Add explicit call to setSoftWrap in specs

This is now required now that setting the soft wrap
column does not cause wrapping unless soft wrap is enabled.

--- spec/edit-session-spec.coffee
+++ spec/edit-session-spec.coffee
@@ -102,6 +102,7 @@

       describe "when soft-wrap is enabled and code is folded", ->
         beforeEach ->
+          editSession.setSoftWrap(true)
           editSession.setSoftWrapColumn(50)
           editSession.createFold(2, 3)

@@ -325,6 +326,7 @@
     describe ".moveCursorToBeginningOfLine()", ->
       describe "when soft wrap is on", ->
         it "moves cursor to the beginning of the screen line", ->
+          editSession.setSoftWrap(true)
           editSession.setSoftWrapColumn(10)
           editSession.setCursorScreenPosition([1, 2])
           editSession.moveCursorToBeginningOfLine()
@@ -344,6 +346,7 @@
     describe ".moveCursorToEndOfLine()", ->
       describe "when soft wrap is on", ->
         it "moves cursor to the beginning of the screen line", ->
+          editSession.setSoftWrap(true)
           editSession.setSoftWrapColumn(10)
           editSession.setCursorScreenPosition([1, 2])
           editSession.moveCursorToEndOfLine()
@@ -363,6 +366,7 @@
     describe ".moveCursorToFirstCharacterOfLine()", ->
       describe "when soft wrap is on", ->
         it "moves to the first character of the current screen line or the beginning of the screen line if it's already on the first character", ->
+          editSession.setSoftWrap(true)
           editSession.setSoftWrapColumn(10)
           editSession.setCursorScreenPosition [2,5]
           editSession.addCursorAtScreenPosition [8,7]
@@ -1778,6 +1782,7 @@
       describe ".cutToEndOfLine()", ->
         describe "when soft wrap is on", ->
           it "cuts up to the end of the line", ->
+            editSession.setSoftWrap(true)
             editSession.setSoftWrapColumn(10)
             editSession.setCursorScreenPosition([2, 2])
             editSession.cutToEndOfLine()
@@ -2234,6 +2239,7 @@

     describe "when soft wrap is enabled", ->
       it "deletes the entire line that the cursor is on", ->
+        editSession.setSoftWrap(true)
         editSession.setSoftWrapColumn(10)
         editSession.setCursorBufferPosition([6])


Remove unneeded calls to rootView.deactivate

--- spec/fuzzy-finder-spec.coffee
+++ spec/fuzzy-finder-spec.coffee
@@ -15,9 +15,6 @@
     rootView.enableKeymap()
     finderView = window.loadPackage("fuzzy-finder").packageMain.createView()

-  afterEach ->
-    rootView.deactivate()
-
   describe "file-finder behavior", ->
     describe "toggling", ->
       describe "when the root view's project has a path", ->


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/engine_tilemap.coffee
+++ src/engine_tilemap.coffee
@@ -11,8 +11,7 @@
 Engine.Tilemap = (I, self) ->
   Object.extend I,
     map: null
-
-  map = null
+
   updating = false
   clearObjects = false


strange handling of null language for when a grammar we don't support is debugged

--- src/beautify.coffee
+++ src/beautify.coffee
@@ -322,6 +322,26 @@

   # Language
   language = beautifier.getLanguage(grammarName, filePath)
+
+  if not language?
+    # if the grammar is Null Grammar we probably should not be debugging it
+    title = "Atom Beautify is unable to identify a language"
+    detail = "Atom Beautify is unable to identify a language for provided grammar of: `#{grammarName}`. "
+    if grammarName == 'Null Grammar'
+      detail += "Please select a grammar before running Atom Beautify: Help Debug Editor."
+    else
+      detail += """
+                This very likely means that this grammar is not yet supported. If you would like to request \
+                support for this file and its language, please create an issue \
+                for Atom Beautify at #{pkg.bugs.url}
+                """
+    atom?.notifications.addWarning(title, {
+      detail
+      dismissable : true
+    })
+    # if we don't know the language stop trying to debug
+    return ""
+
   addInfo('Original File Language', language.name)

   # Get current editor's text


Moved atom.loadPackage to a spec-only helper function

--- spec/symbols-view-spec.coffee
+++ spec/symbols-view-spec.coffee
@@ -8,7 +8,7 @@

   beforeEach ->
     rootView = new RootView(require.resolve('fixtures'))
-    atom.loadPackage("symbols-view")
+    window.loadPackage("symbols-view")

     rootView.attachToDom()
     setArraySpy = spyOn(SymbolsView.prototype, 'setArray').andCallThrough()


Fix indent in campfire @reply

Fix indent in campfire @reply

--- src/hubot/campfire.coffee
+++ src/hubot/campfire.coffee
@@ -9,7 +9,7 @@
         console.log "campfire error: #{err}" if err

   reply: (user, strings...) ->
-      @send user, "#{user.name}: #{str}" for str in strings
+    @send user, "#{user.name}: #{str}" for str in strings

   run: ->
     self = @


UI Copy: proccesess => processes

UI Copy: proccesess => processes

--- client/Main/VirtualizationController.coffee
+++ client/Main/VirtualizationController.coffee
@@ -409,7 +409,7 @@
     switch command
       when 'vm.stop', 'vm.shutdown'
         content = """
Turning off your VM will stop running Terminal
-                     instances and all running proccesess that you have on
+                     instances and all running processes that you have on
                      your VM. Do you want to continue?
"""
         button  =
           title : "Turn off"


groups: show "group is out of vm quota" notification in group context

--- client/Main/VirtualizationController.coffee
+++ client/Main/VirtualizationController.coffee
@@ -356,7 +356,10 @@
         @provisionVm {subscription, productData: {pack}}, (err, nonce) =>
           return  unless err
           if err.message is "quota exceeded"
-          then @showUpgradeModal()
+            if KD.getGroup().slug is "koding"
+              @showUpgradeModal()
+            else
+              new KDNotificationView title: "Your group is out of VM quota"
           else KD.showError err

   showUpgradeModal: ->


junction: logical grouping

junction: logical grouping

--- client/app/MainApp/junction/junction.coffee
+++ client/app/MainApp/junction/junction.coffee
@@ -119,8 +119,6 @@
   @All = class All extends this
     # All is like Junction.

-  @all = (fields...) -> new All fields
-
   @Any = class Any extends this
     # Any is like Junction, with a couple tweaks.

@@ -130,4 +128,6 @@
     # Any#createSatisfier returns a singleton satisfier :)
     createSatisfier: -> @satisfier ?= super

+  @all = (fields...) -> new All fields
+
   @any = (fields...) -> new Any fields


Minor fixes to mailbox config/new form

Minor fixes to mailbox config/new form

--- client/app/components/mailbox-config.coffee
+++ client/app/components/mailbox-config.coffee
@@ -12,12 +12,6 @@
     ]

     render: ->
-        # width depends on layout: one panel or full width
-        inputClass = if @props.layout is 'full' then 'col-sm-3' else 'col-sm-6'
-        portClass = if @props.layout is 'full' then 'col-sm-1' else 'col-sm-2'
-        labelClass = if @props.layout is 'full' then 'col-sm-2 col-sm-offset-2' else 'col-sm-3'
-        labelClass = "#{labelClass} control-label"
-
         titleLabel = if @props.initialMailboxConfig? then 'Edit mailbox' else 'New mailbox'

         if @props.isWaiting then buttonLabel = 'Saving...'
@@ -32,41 +26,42 @@

             form className: 'form-horizontal',
                 div className: 'form-group',
-                    label htmlFor: 'mailbox-label', className: labelClass, 'Label'
-                    div className: inputClass,
+                    label htmlFor: 'mailbox-label', className: 'col-sm-2 col-sm-offset-2 control-label', 'Label'
+                    div className: 'col-sm-3',
                         input id: 'mailbox-label', valueLink: @linkState('label'), type: 'text', className: 'form-control', placeholder: 'A short mailbox name'
                 div className: 'form-group',
-                    label htmlFor: 'mailbox-name', className: labelClass, 'Your name'
-                    div className: inputClass,
+                    label htmlFor: 'mailbox-name', className: 'col-sm-2 col-sm-offset-2 control-label', 'Your name'
+                    div className: 'col-sm-3',
                         input id: 'mailbox-name', valueLink: @linkState('name'), type: 'text', className: 'form-control', placeholder: 'Your name, as it will be displayed'
                 div className: 'form-group',
-                    label htmlFor: 'mailbox-email-address', className: labelClass, 'Email address'
-                    div className: inputClass,
+                    label htmlFor: 'mailbox-email-address', className: 'col-sm-2 col-sm-offset-2 control-label', 'Email address'
+                    div className: 'col-sm-3',
                         input id: 'mailbox-email-address', valueLink: @linkState('email'), type: 'email', className: 'form-control', placeholder: 'Your email address'
                 div className: 'form-group',
-                    label htmlFor: 'mailbox-password', className: labelClass, 'Password'
-                    div className: inputClass,
+                    label htmlFor: 'mailbox-password', className: 'col-sm-2 col-sm-offset-2 control-label', 'Password'
+                    div className: 'col-sm-3',
                         input id: 'mailbox-password', valueLink: @linkState('password'), type: 'password', className: 'form-control'

                 div className: 'form-group',
-                    label htmlFor: 'mailbox-smtp-server', className: labelClass, 'Sending server'
-                    div className: inputClass,
+                    label htmlFor: 'mailbox-smtp-server', className: 'col-sm-2 col-sm-offset-2 control-label', 'Sending server'
+                    div className: 'col-sm-3',
                         input id: 'mailbox-smtp-server', valueLink: @linkState('smtpServer'), type: 'text', className: 'form-control', placeholder: 'smtp.provider.tld'
                     label htmlFor: 'mailbox-smtp-port', className: 'col-sm-1 control-label', 'Port'
-                        div className: portClass,
+                        div className: 'col-sm-1',
                             input id: 'mailbox-smtp-port', valueLink: @linkState('smtpPort'), type: 'text', className: 'form-control'

                 div className: 'form-group',
-                    label htmlFor: 'mailbox-imap-server', className: labelClass, 'Receiving server'
-                    div className: inputClass,
+                    label htmlFor: 'mailbox-imap-server', className: 'col-sm-2 col-sm-offset-2 control-label', 'Receiving server'
+                    div className: 'col-sm-3',
                         input id: 'mailbox-imap-server', valueLink: @linkState('imapServer'), type: 'text', className: 'form-control', placeholder: 'imap.provider.tld'
                     label htmlFor: 'mailbox-imap-port', className: 'col-sm-1 control-label', 'Port'
-                    div className: portClass,
+                    div className: 'col-sm-1',
                         input id: 'mailbox-imap-port', valueLink: @linkState('imapPort'), type: 'text', className: 'form-control'

                 div className: 'form-group',
-                    div className: 'col-sm-offset-3 col-sm-6 text-right',
-                        button className: 'btn btn-cozy', onClick: @onRemove, 'Remove'
+                    div className: 'col-sm-offset-2 col-sm-5 text-right',
+                        if @props.initialMailboxConfig?
+                            button className: 'btn btn-cozy', onClick: @onRemove, 'Remove'
                         button className: 'btn btn-cozy', onClick: @onSubmit, buttonLabel
     onSubmit: (event) ->
         # prevents the page from reloading


Nix the slug hiding effect, janky

Nix the slug hiding effect, janky

--- client/source/views/base/mixins/form.coffee
+++ client/source/views/base/mixins/form.coffee
@@ -16,17 +16,6 @@
         $input = $slug.prev()

         $inputs = $slug.parent().find('input')
-
-        unless $input.is(':focus')
-          $slug.css display: 'none'
-
-        closeTimeout = null
-        $inputs.focus ->
-          $slug.slideDown 80
-          clearTimeout closeTimeout if closeTimeout
-        $inputs.blur -> closeTimeout = setTimeout ->
-          $slug.slideUp 80
-        , 50

   formParams: ->
     # Uses jQuery formParams, but don't try to convert number values to numbers, etc.


Turn @expectedFormSections into a property, not function.

--- src/checkout.coffee
+++ src/checkout.coffee
@@ -47,23 +47,23 @@
 		@stateRequestHashToResponseMap = {}
 		@subjectToJqXHRMap = {}

-	expectedFormSections: ->
-		[
-			'items'
-			'totalizers'
-			'clientProfileData'
-			'shippingData'
-			'paymentData'
-			'sellers'
-			'messages'
-			'marketingData'
-			'clientPreferencesData'
-			'storePreferencesData'
-			'giftRegistryData'
-			'ratesAndBenefitsData'
-		]
-
-	getOrderForm: (expectedFormSections = @expectedFormSections()) =>
+		@expectedFormSections =
+			[
+				'items'
+				'totalizers'
+				'clientProfileData'
+				'shippingData'
+				'paymentData'
+				'sellers'
+				'messages'
+				'marketingData'
+				'clientPreferencesData'
+				'storePreferencesData'
+				'giftRegistryData'
+				'ratesAndBenefitsData'
+			]
+
+	getOrderForm: (expectedFormSections = @expectedFormSections) =>
 		checkoutRequest = { expectedOrderFormSections: expectedFormSections }
 		@ajax
 			url: @_getOrderFormURL()
@@ -76,7 +76,7 @@
 	# @param attachmentId
 	# @param serializedAttachment stringified serializedAttachment
 	# @param expectedOrderFormSections
-	sendAttachment: (attachmentId, serializedAttachment, expectedOrderFormSections = @expectedFormSections(), options = {}) =>
+	sendAttachment: (attachmentId, serializedAttachment, expectedOrderFormSections = @expectedFormSections, options = {}) =>
 		if attachmentId is undefined or serializedAttachment is undefined
 			d = $.Deferred()
 			d.reject("Invalid arguments")
@@ -108,7 +108,7 @@
 		updateItemsRequest =
 			id: offeringId
 			info: offeringInfo
-			expectedOrderFormSections: expectedOrderFormSections ? @expectedFormSections()
+			expectedOrderFormSections: expectedOrderFormSections ? @expectedFormSections

 		@ajax
 			url: @_getAddOfferingsURL(itemIndex)
@@ -123,7 +123,7 @@
 	removeOffering: (offeringId, itemIndex, expectedOrderFormSections) =>
 		updateItemsRequest =
 			Id: offeringId
-			expectedOrderFormSections: expectedOrderFormSections ? @expectedFormSections()
+			expectedOrderFormSections: expectedOrderFormSections ? @expectedFormSections

 		@ajax
 			url: @_getRemoveOfferingsURL(itemIndex, offeringId)
@@ -132,7 +132,7 @@
 			dataType: 'json'
 			data: JSON.stringify(updateItemsRequest)

-	updateItems: (itemsJS, expectedOrderFormSections = @expectedFormSections()) =>
+	updateItems: (itemsJS, expectedOrderFormSections = @expectedFormSections) =>
 		updateItemsRequest =
 			orderItems: itemsJS
 			expectedOrderFormSections: expectedOrderFormSections
@@ -161,7 +161,7 @@
 	addDiscountCoupon: (couponCode, expectedOrderFormSections) =>
 		couponCodeRequest =
 			text: couponCode
-			expectedOrderFormSections: expectedOrderFormSections ? @expectedFormSections()
+			expectedOrderFormSections: expectedOrderFormSections ? @expectedFormSections

 		@ajax
 			url: @_getAddCouponURL()
@@ -173,7 +173,7 @@
 	removeDiscountCoupon: (expectedOrderFormSections) =>
 		return @addDiscountCoupon('', expectedOrderFormSections)

-	removeGiftRegistry: (expectedFormSections = @expectedFormSections()) =>
+	removeGiftRegistry: (expectedFormSections = @expectedFormSections) =>
 		checkoutRequest = { expectedOrderFormSections: expectedFormSections }
 		@ajax
 			url: "/api/checkout/pub/orderForm/giftRegistry/#{@_getOrderFormId()}/remove"
@@ -193,14 +193,13 @@
 			type: 'GET'
 			timeout : 20000

-	# Aceita um address com propriedades postalCode e country
 	getProfileByEmail: (email, salesChannel) =>
 		@ajax
 			url: @_getProfileURL()
 			type: 'GET'
 			data: {email: email, sc: salesChannel}

-	startTransaction: (value, referenceValue, interestValue, savePersonalData = false, optinNewsLetter = false, expectedOrderFormSections = @expectedFormSections()) =>
+	startTransaction: (value, referenceValue, interestValue, savePersonalData = false, optinNewsLetter = false, expectedOrderFormSections = @expectedFormSections) =>
 		transactionRequest = {
 			referenceId: @_getOrderFormId()
 			savePersonalData: savePersonalData


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/cooldown.coffee
+++ src/cooldown.coffee
@@ -17,6 +17,19 @@
     # => 51

     # Shoot Timeout
+    player = GameObject()
+
+    # by default the cooldown
+    # approaches 0 by 1 each update
+    player.cooldown "shootTimer"
+
+    player.I.shootTimer = 10 # => Pew! Pew!
+
+    player.update(1)
+
+    player.I.shootTimer # => 9
+
+      #
     player = GameObject()

     # by default the cooldown


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/cooldown.coffee
+++ src/cooldown.coffee
@@ -21,7 +21,7 @@

 player.I.shootTimer = 10 # => Pew! Pew!

-player.I.update()
+player.update()

 player.I.shootTimer # => 9
 </pre></code>


Retain scroll position on touch devices

Retain scroll position on touch devices

--- src/creative.coffee
+++ src/creative.coffee
@@ -16,9 +16,12 @@
     window.open(@creative.content.location)

   _append_embedded: ->
+    # Remember scroll position and make body fixed
+    @scrollTop = Infeedl.$(window).scrollTop()
+    Infeedl.$("html, body").addClass("infeedl--no-scroll")
+
     @loader = document.createElement("div")
     @loader.setAttribute("class", "infeedl--embedded--loader")
-    Infeedl.$("html, body").addClass("infeedl--no-scroll")
     Infeedl.$("body")[0].appendChild(@loader)

     @wrapper = document.createElement("div")
@@ -69,6 +72,9 @@
     Infeedl.$(".infeedl--embedded-close").remove()
     Infeedl.$(".infeedl--embedded-wrapper").remove()
     Infeedl.$("html, body").removeClass("infeedl--no-scroll")
+
+    # Return to where it was
+    setTimeout((=> Infeedl.$(window).scrollTop(@scrollTop)), 0)
     # console?.log("[INFEEDL] Creative ##{@creative.id}: embedded removed")

   _interpolations: ->


Forward groups

Forward groups

--- src/components/SplitStr.coffee
+++ src/components/SplitStr.coffee
@@ -8,6 +8,7 @@
     constructor: ->
         @delimiterString = "\n"
         @strings = []
+        @groups = []

         @inPorts =
             in: new noflo.Port()
@@ -17,13 +18,20 @@

         @inPorts.delimiter.on "data", (data) =>
             @delimiterString = data
+        @inPorts.in.on "begingroup", (group) =>
+            @groups.push(group)
         @inPorts.in.on "data", (data) =>
             @strings.push data
         @inPorts.in.on "disconnect", (data) =>
+            for group in @groups
+              @outPorts.out.beginGroup(group)
             @strings.join(@delimiterString).split(@delimiterString).forEach (line) =>
                 @outPorts.out.send line
+            for group in @groups
+              @outPorts.out.endGroup()
             @outPorts.out.disconnect()
             @strings = []
+            @groups = []

 exports.getComponent = ->
     new SplitStr()


fixed nested deprecated error message

fixed nested deprecated error message

--- src/core/methods/attach.coffee
+++ src/core/methods/attach.coffee
@@ -19,7 +19,7 @@
     # Create the main set/get function.
     vars.self[method] = (createFunction)(vars, method)

-initialize = (vars, obj, method) ->
+initialize = (vars, obj, method, p) ->

   # Initialize a few globals.
   obj.previous    = false
@@ -44,7 +44,9 @@
           (x) ->
             str = vars.format.locale.value.dev.deprecated
             dep = "." + dep + "()"
-            print.error stringFormat(str, dep, "." + n + "()"), n
+            rec = if p then "\"" + n + "\" in ." + p + "()" else "." + n + "()"
+            doc = p or n
+            print.error stringFormat(str, dep, rec), doc
             vars.self
         )(d, method)

@@ -52,7 +54,7 @@
       vars[method] = [] unless method of vars

     else if o isnt "value"
-      initialize vars, obj[o], o if validObject(obj[o])
+      initialize vars, obj[o], o, method if validObject(obj[o])

   true


Document public subscription methods

Document public subscription methods

--- src/keymap-manager.coffee
+++ src/keymap-manager.coffee
@@ -63,32 +63,6 @@
 # the previous keystrokes are replayed. If there is ambiguity again during the
 # replay, the next longest bindings are disabled and the keystrokes are replayed
 # again.
-#
-# ## Events
-#
-# ### matched
-#
-# Emitted when keystrokes match a binding.
-#
-# * `keystrokes` The keystroke {String} that matched the binding
-# * `binding` The {KeyBinding} that was used
-# * `keyboardEventTarget` The target element of the keyboard event
-#
-# ### matched-partially
-#
-# Emitted when keystrokes partially match one or more bindings.
-#
-# * `keystrokes` The keystroke {String} that partially match some bindings
-# * `partiallyMatchedBindings` The {KeyBinding}s that partially matched
-# * `keyboardEventTarget` The target element of the keyboard event
-#
-# ### match-failed
-#
-# Emitted when keystrokes don't match any bindings.
-#
-# * `keystrokes` The keystroke {String} that matched no bindings
-# * `keyboardEventTarget` The target element of the keyboard event
-#
 module.exports =
 class KeymapManager
   EmitterMixin.includeInto(this)
@@ -130,12 +104,47 @@
     @queuedKeystrokes = []
     @watchSubscriptions = {}

+  # Public: Invoke the given callback when one or more keystrokes completely
+  # match a key binding.
+  #
+  # * `callback` {Function} to be called when keystrokes match a binding.
+  #   * `event` {Object} with the following keys:
+  #     * `keystrokes` {String} of keystrokes that matched the binding.
+  #     * `binding` {KeyBinding} that the keystrokes matched.
+  #     * `keyboardEventTarget` DOM element that was the target of the most
+  #        recent keyboard event.
+  #
+  # Returns a {Disposable} on which `.dispose()` can be called to unsubscribe.
   onDidMatchBinding: (callback) ->
     @emitter.on 'did-match-binding', callback

-  onDidPartiallyMatchBinding: (callback) ->
+  # Public: Invoke the given callback when one or more keystrokes partially
+  # match a binding.
+  #
+  # * `callback` {Function} to be called when keystrokes partially match a
+  #   binding.
+  #   * `event` {Object} with the following keys:
+  #     * `keystrokes` {String} of keystrokes that matched the binding.
+  #     * `partiallyMatchedBindings` {KeyBinding}s that the keystrokes partially
+  #       matched.
+  #     * `keyboardEventTarget` DOM element that was the target of the most
+  #       recent keyboard event.
+  #
+  # Returns a {Disposable} on which `.dispose()` can be called to unsubscribe.
+  onDidPartiallyMatchBindings: (callback) ->
     @emitter.on 'did-partially-match-binding', callback

+  # Public: Invoke the given callback when one or more keystrokes fail to match
+  # any bindings.
+  #
+  # * `callback` {Function} to be called when keystrokes fail to match any
+  #   bindings.
+  #   * `event` {Object} with the following keys:
+  #     * `keystrokes` {String} of keystrokes that matched the binding.
+  #     * `keyboardEventTarget` DOM element that was the target of the most
+  #        recent keyboard event.
+  #
+  # Returns a {Disposable} on which `.dispose()` can be called to unsubscribe.
   onDidFailToMatchBinding: (callback) ->
     @emitter.on 'did-fail-to-match-binding', callback


Logging notification in all events

Logging notification in all events

--- src/observers/logger.observer.coffee
+++ src/observers/logger.observer.coffee
@@ -2,17 +2,17 @@

 module.exports =
   listenTo: (observable) ->
-    observable.on "started", ({ context: { log }, notification })->
-      log "A new message has been received", notification
+    observable.on "started", ({ context: { log }, notification }) ->
+      log.info "A new message has been received", { notification }

-    observable.on "successful", ({ context: { log } }) ->
-      log "Process successful"
+    observable.on "successful", ({ context: { log }, notification }) ->
+      log.info "Process successful", { notification }

-    observable.on "unsuccessful", ({ context: { log }, error }) ->
-      log "Process unsuccessful", errorToJson(error)
+    observable.on "unsuccessful", ({ context: { log }, notification, error }) ->
+      log.error "Process unsuccessful", { notification, error: errorToJson(error) }

-    observable.on "finished", ({ context: { log } }) ->
-      log "Process finished"
+    observable.on "finished", ({ context: { log }, notification }) ->
+      log.info "Process finished", { notification }

     observable.on "ignored", ({ context: { log }, notification }) ->
-      log "Message ignored", notification
+      log.verbose "Message ignored", { notification }


Compensate for lack of topological sorting in dependency tracking

--- models.coffee
+++ models.coffee
@@ -426,7 +426,13 @@
             throw new Meteor.Error "#{modelClass}.reactives.#{reactiveName} missing val function"

         modelClass.prototype[reactiveName] ?= do (reactiveName, reactiveSpec) -> ->
-            if reactiveSpec.denorm
+            # If the server is in reactive-recalculation mode and
+            # J.denorm._watchingQueries is true, then we can't use
+            # the cached value of the reactive because right now
+            # our dependency tracking only works with nodes in the
+            # Normalized Kernel.
+
+            if reactiveSpec.denorm and not (Meteor.isServer and J.denorm._watchingQueries)
                 if @attached
                     # Record that the current computation uses the current reactive
                     projection = {}


_id: $$.key saving bugfix

_id: $$.key saving bugfix

--- models.coffee
+++ models.coffee
@@ -200,7 +200,7 @@
         unless @alive
             throw new Meteor.Error "Can't save dead #{@modelClass.name} instance"

-        doc = Tracker.nonreactive => @toDoc()
+        doc = Tracker.nonreactive => @toDoc(true)

         doc._id ?= Random.hexString(10)

@@ -239,8 +239,8 @@
         doc = J.Model.toSubdoc(@_fields.tryToObj(), denormalize)

         if denormalize and @modelClass.idSpec is J.PropTypes.key
-            key = @key()
-            J.assert not @_id? or @_id is key
+            key = @key() # Might throw a legitimate VALUE_NOT_READY
+            J.assert key? and (@_id is key or not @_id?)
             doc._id = key
         else
             doc._id = @_id


Refactor regEx

Refactor regEx

--- timestampable.coffee
+++ timestampable.coffee
@@ -37,10 +37,12 @@

       addAfDefs def if af?

+    regEx = new RegExp "(#{SimpleSchema.RegEx.Id.source})|^0$"
+
     if createdBy
       def = definition[createdBy] =
         optional: true
-        regEx: new RegExp "(#{SimpleSchema.RegEx.Id.source})|^0$"
+        regEx: regEx
         type: String

       addAfDefs def if af?
@@ -57,7 +59,7 @@
     if updatedBy
       def = definition[updatedBy] =
         optional: true
-        regEx: new RegExp "(#{SimpleSchema.RegEx.Id.source})|^0$"
+        regEx: regEx
         type: String

       addAfDefs def if af?


bug fix

bug fix

--- src/server/services/messagers.coffee
+++ src/server/services/messagers.coffee
@@ -158,7 +158,7 @@
   chatContext.status = null
   chatContext.save (err) -> if err? then console.log err

-  if message.text.toLowerCase() is 'cancel'
+  if message.text?.toLowerCase() is 'cancel'
     bot.sendMessage
       chat_id: message.chat.id
       text: "Fine. So what can I do for you #{user.firstName}?"
@@ -212,7 +212,7 @@
     when 'WAIT_CANCEL_REMINDER'
       chatContext.status = null
       chatContext.save (err) -> if err? then console.log err
-      if message.text.toLowerCase() is 'cancel'
+      if message.text?.toLowerCase() is 'cancel'
         cancelReminder message, user
         return
     when 'WAIT_FEEDBACK'


Update geometry.coffee

Update geometry.coffee

use Math.round() instead of Number.toFixed(), as it returns a string and using Math.round() allows for a greater margin of error

--- src/geometry.coffee
+++ src/geometry.coffee
@@ -11,13 +11,13 @@

     get: (what) ->
         if what is 'x'
-            @x.toFixed 2
+            Math.round @x
         else if what is 'y'
-            @y.toFixed 2
+            Math.round @y
         else if what is 'org_x'
-            @org_x.toFixed 2
+            Math.round @org_x
         else if what is 'org_y'
-            @org_y.toFixed 2
+            Math.round @org_y
         else if what is 'a'
             Math.atan2 @y - @org_y, @x - @org_x
         else if what is 'dist'
@@ -84,23 +84,17 @@
         else if @p1.get('x') == @p2.get('x') and @p1.get('y') > @p2.get('y')
             [@p1, @p2] = [@p2, @p1]

-    get: (what, roundOff = true) ->
+    get: (what) ->
         if what is 'slope' or 'm'
             if @p1.get('x') == @p2.get('x')
                 undefined
             else
-                if roundOff
-                    ((@p2.y - @p1.y) / (@p2.x - @p1.x)).toFixed 2
-                else
-                    (@p2.y - @p1.y) / (@p2.x - @p1.x)
+                (@p2.y - @p1.y) / (@p2.x - @p1.x)
         else if what is 'y-intercept' or 'b'
             if @p1.get('x') == @p2.get('x')
                 undefined
             else
-                if roundOff
-                    -((@p2.y - @p1.y) / (@p2.x - @p1.x) * @p1.x + @p1.y).toFixed 2
-                else
-                    -(@p2.y - @p1.y) / (@p2.x - @p1.x) * @p1.x + @p1.y
+                -@get('m') * @p1.x + @p1.y
         else
             throw new Error "#{what} is not a get-able Line attribute"

@@ -116,20 +110,20 @@
             if @p1.get('y') <= pt.get('y') <= @p2.get('y')
                 return true
         else if @p1.get('x') <= pt.get('x') <= @p2.get('x')
-            if (@get('m', false) * pt.x + @get('b', false)).toFixed(2) == pt.get('y')
+            if Math.round(@get('m') * pt.x + @get('b')) == pt.get('y')
                 return true
         return false

     _intersection: (other) ->
-        if @get('m') == other.get('m')
+        if Math.round(@get('m')) == Math.round(other.get('m'))
             return undefined
         else if @get('m') is undefined
             _x = @p1.x
-            _y = other.get('m', false) * _x + other.get('b', false)
+            _y = other.get('m') * _x + other.get('b')
         else if other.get('m') is undefined
             _x = other.p1.x
-            _y = @get('m', false) * _x + @get('b', false)
+            _y = @get('m') * _x + @get('b')
         else
-            _x = (other.get('b', false) - @get('b', false)) / (@get('m', false) - other.get('m', false))
-            _y = @get('m', false) * _x + @get('b', false)
+            _x = (other.get('b') - @get('b')) / (@get('m') - other.get('m'))
+            _y = @get('m') * _x + @get('b')
         return new Point _x, _y, 0, 0


Tweak Diplomat prompt: ask for native speakers of target language

--- app/locale/en.coco.coffee
+++ app/locale/en.coco.coffee
@@ -223,7 +223,7 @@
   diplomat_suggestion:
     title: "Help translate CodeCombat!"  # This shows up when a player switches to a non-English language using the language selector.
     sub_heading: "We need your language skills."
-    pitch_body: "We develop CodeCombat in English, but we already have players all over the world. Many of them want to play in {English} but don't speak English, so if you can speak both, please consider signing up to be a Diplomat and help translate both the CodeCombat website and all the levels into {English}."
+    pitch_body: "We develop CodeCombat in English, but we already have players all over the world. Many of them want to play in {English} but don't speak English, so if you can speak both and are a native speaker of {English}, please consider signing up to be a Diplomat and help translate both the CodeCombat website and all the levels into {English}." # {change}
     missing_translations: "Until we can translate everything into {English}, you'll see English when {English} isn't available."
     learn_more: "Learn more about being a Diplomat"
     subscribe_as_diplomat: "Subscribe as a Diplomat"


/today UTC

/today UTC

--- app/scripts/routes.coffee
+++ app/scripts/routes.coffee
@@ -81,7 +81,7 @@
     .when("/today",
       redirectTo: do () ->
         d = new Date()
-        "/#{d.getMonth()+1}-#{d.getDate()}-#{d.getFullYear()}"
+        "/#{d.getUTCMonth()+1}-#{d.getUTCDate()}-#{d.getUTCFullYear()}"
     )

     .when("/:rootId?",


Remove compile:babel task

Remove compile:babel task


Update link to docs that cobalt give

Update link to docs that cobalt give

--- code/serv.coffee
+++ code/serv.coffee
@@ -142,7 +142,7 @@

 docs = (req, res) ->
   res.header('Content-Type', 'application/json')
-  res.send "See https://x.scraperwiki.com/docs", 200
+  res.send "See https://beta.scraperwiki.com/help/developer/", 200

 app.get "/", docs
 app.get "/:boxname/?", docs


enable autoreconnect for cobalt

enable autoreconnect for cobalt

--- code/serv.coffee
+++ code/serv.coffee
@@ -56,7 +56,11 @@
   app.use express.logger()
   app.use express.errorHandler()

-mongoose.connect process.env['CU_DB']
+mongoose.connect process.env.CU_DB,
+  server:
+    auto_reconnect: true
+    socketOptions:
+      keepAlive: 1

 server_hostname = ''
 root_url = ''


Remove extraneous cruft, clean up a little, handle errors in repoll

--- source/coffee/index.coffee
+++ source/coffee/index.coffee
@@ -72,27 +72,8 @@
       pin keyed if pin? # Update pin with keyed
       children: root # Return in format appropriate for mapping

-    mapping =
-      #children:
-      #  key: (data) ->
-      #    ko.utils.unwrapObservable data.id
-      #  create: (options) ->
-      #    createNode options
-        #update: (options) ->
-        #  createNode options
-
-    createNode = (options) ->
-      #node = options.data
-      #console.log "Extending: ", node
-      #$.extend node,
-      #  servers: v for k,v of node?.children ? {} when "agent" in v.facts.backends
-      #  containers: v for k,v of node?.children ? {} when "container" in v.facts.backends
-      #  actions: []
-      #  status: "unknown"
-      #ko.mapping.fromJS node, mapping
-
     @updateNodes = (data, pin, keys) =>
-      @mapData @parseNodes(data, pin, keys), pin #, mapping
+      @mapData @parseNodes(data, pin, keys), pin

     @getNodes = (url, pin, keys) =>
       @getData url, (data) =>
@@ -106,16 +87,19 @@
     @wsKeys = {}

     @updateTransaction = (data) =>
-      if data?
+      if data?.transaction?
         @sKey = data.transaction.session_key
         @txID = data.transaction.txid
-        #console.log "Updating transaction: ", @sKey, @txID

     @poll = (url, cb, timeout=@config?.timeout?.long) =>
-      rePoll = (data) =>
-        @updateTransaction data
-        # Restart long-poller for new transaction
-        setTimeout (=> @poll "/roush/nodes/updates/#{@sKey}/#{@txID}?poll", cb, timeout), 1
+      rePoll = (throttle=false, update=false) =>
+        doit = (=> @poll "/roush/nodes/updates/#{@sKey}/#{@txID}?poll", cb, timeout)
+        if update?
+          @getData "/roush/updates", (data) =>
+            @updateTransaction data
+            setTimeout doit, 1 unless throttle else 1000
+        else
+          setTimeout doit, 1 unless throttle else 1000

       $.ajax
         url: url
@@ -124,14 +108,10 @@
         error: (jqXHR, textStatus, errorThrown) =>
           switch jqXHR.status
             when 410 # Gone, cycle txID
-              @getData "/roush/updates", (data) =>
-                rePoll data
-            when 0 # Timeout
-              #console.log "Retrying"
-              rePoll()
+              rePoll false, true
             else # Other errors
               console.log "Error (#{jqXHR.status}): #{errorThrown}"
-              setTimeout (=> @poll url, cb, timeout), 1000 # Throttle retry
+              rePoll true
         dataType: "json"
         timeout: timeout ? 30000

@@ -144,7 +124,6 @@
         @updateTransaction data
         # Start long-poller
         @poll "/roush/nodes/updates/#{@sKey}/#{@txID}?poll", (data, cb) =>
-          #console.log "Got update: ", data
           pnodes = []
           resolver = (stack) =>
             id = stack.pop()
@@ -201,7 +180,7 @@
             @wsPlans().node = object.id()
             $("#indexInputModal").modal "show"
           else
-            console.log "Error (#{jqXHR.status}): ", errorThrown
+            console.log "Error (#{jqXHR.status}): errorThrown"

     $('#inputForm').validate
       focusCleanup: true
@@ -224,7 +203,7 @@
           $("#indexInputModal").modal "hide"
         , (jqXHR, textStatus, errorThrown) ->
           $("#indexInputModal").modal "hide"
-          console.log "Error: ", jqXHR.status, textStatus, errorThrown
+          console.log "Error (#{jqXHR.status}): errorThrown"

     @ # Return ourself

@@ -233,7 +212,7 @@
     delay: 0
     trigger: "hover"
     animation: true
-    #placement: ntrapy.getPopoverPlacement
+    placement: ntrapy.getPopoverPlacement

   ko.bindingHandlers.popper =
     init: (el, data) ->
@@ -265,6 +244,7 @@
         .popover "hide"
     stop: (event, ui) ->
       null
+      # TODO: Do something on sort stop?

   ko.bindingHandlers.sortable.afterMove = (options) ->
     parent = options.sourceParentNode.attributes["data-id"].value
@@ -274,8 +254,7 @@
         value: parent
         node_id: options.item.id()
     , (data) ->
-      null
-      #console.log "Success: ", data
+      null # TODO: Do something with success?
     , (jqXHR) ->
       console.log "Error: ", jqXHR


Try isEnabled observable

Try isEnabled observable

--- source/coffee/index.coffee
+++ source/coffee/index.coffee
@@ -48,13 +48,19 @@
         # Stub if missing
         node.actions ?= []
         node.status ?= "unknown"
+        node.isEnabled ?= ko.observable true
         node.children ?= {}
         keyed[nid] = node # Add/update node

       # Step through IDs
       for id of keyed
         node = keyed[id]
-        if node.task_id? then node.status = "alert" else node.status = "unknown"
+        if node.task_id?
+          node.status = "alert"
+          node.isEnabled = false
+        else
+          node.status = "unknown"
+          node.isEnabled = true
         pid = node.facts?.parent_id
         if pid? # Has parent ID?
           pnode = keyed?[pid]


change order of loading variables

change order of loading variables

--- packages/rocketchat-integrations/server/api/api.coffee
+++ packages/rocketchat-integrations/server/api/api.coffee
@@ -71,9 +71,9 @@
 						error: 'invalid-channel-type'

 		message =
-			avatar: integration.avatar or @bodyParams.avatar or @bodyParams.icon_url
-			emoji: integration.emoji or @bodyParams.emoji or @bodyParams.icon_emoji
-			alias: integration.alias or @bodyParams.alias or @bodyParams.username
+			avatar: @bodyParams.icon_url or @bodyParams.avatar or integration.avatar
+			emoji: @bodyParams.icon_emoji or @bodyParams.emoji or integration.emoji
+			alias: @bodyParams.username or @bodyParams.alias or integration.alias
 			msg: @bodyParams.text or ''
 			attachments: @bodyParams.attachments
 			parseUrls: false


tiny bit clearer

tiny bit clearer

--- test/expander_test.coffee
+++ test/expander_test.coffee
@@ -36,25 +36,25 @@

                 fs.stub readdir: (dir, cb) ->

-                    if dir == '/once/upon/a/time/there'     then return cb null, ['file1', 'was', 'is']
-                    if dir == '/once/upon/a/time/there/was' then return cb null, ['file2', 'dir1', 'dir2']
-                    if dir == '/once/upon/a/time/there/is'  then return cb null, ['file3', 'dir3', 'dir4']
+                    if dir == '/once/upon/a/time/there'     then return cb null, ['file1', 'waS', 'iS']
+                    if dir == '/once/upon/a/time/there/waS' then return cb null, ['file2', 'dir1', 'dir2']
+                    if dir == '/once/upon/a/time/there/iS'  then return cb null, ['file3', 'dir3', 'dir4']

                     throw new Error 'Gone too far!'

                 @includeDirs = true
                 @includeFiles = true

-                Expander.perform @In, './there/*s/*i*', @includeFiles, @includeDirs
-
-                .then (r) ->
-                    r.should.eql [
-                        "./there/was/file2"
-                        "./there/was/dir1"
-                        "./there/was/dir2"
-                        "./there/is/file3"
-                        "./there/is/dir3"
-                        "./there/is/dir4"
+                Expander.perform @In, './there/*S/*i*', @includeFiles, @includeDirs
+
+                .then (r) ->
+                    r.should.eql [
+                        "./there/waS/file2"
+                        "./there/waS/dir1"
+                        "./there/waS/dir2"
+                        "./there/iS/file3"
+                        "./there/iS/dir3"
+                        "./there/iS/dir4"
                     ]
                     done()


Update use of AssetHost.SERVER to work with included protocol

--- app/assets/javascripts/asset_gallery/models.js.coffee
+++ app/assets/javascripts/asset_gallery/models.js.coffee
@@ -2,7 +2,7 @@
     constructor: ->

     @Asset: Backbone.Model.extend
-        urlRoot: "http://#{AssetHost.SERVER}/api/assets/"
+        urlRoot: "#{AssetHost.SERVER}/api/assets/"

         #----------


Update fetched data with yet to sync data

--- app/assets/javascripts/offline/synchronizer.js.coffee
+++ app/assets/javascripts/offline/synchronizer.js.coffee
@@ -3,20 +3,39 @@

 Backbone.sync = (method, model, options) ->
   url = _.result(model, 'url')
+
+  synclist = JSON.parse(localStorage['toSync'] || '[]')
+
   if method is 'read'
-    if navigator.onLine
-      # Store the fetched result also in our local cache
-      Backbone.backendSync(arguments...).then (data) ->
-        localStorage[url] = JSON.stringify(data)
-    else
-      # Return the result from our local cache
+
+    getData = (method, model, options) ->
       defer = $.Deferred()
-      data = JSON.parse(localStorage[url])
-      setTimeout(
-        => defer.resolveWith(this, [data])
-        0
-      )
-      defer.promise().then (data) ->
-        options.success(data)
+      if navigator.onLine
+        # Store the fetched result also in our local cache
+        #console.log 'data from backend'
+        Backbone.backendSync(method, model).then (data) ->
+          localStorage[url] = JSON.stringify(data)
+          defer.resolveWith(this, [data])
+      else
+        #console.log 'data from cache'
+        # Return the result from our local cache
+        data = JSON.parse(localStorage[url])
+        setTimeout(
+          => defer.resolveWith(this, [data])
+          0
+        )
+        defer.promise()
+
+    getData(method, model, options).then (data) ->
+      # synchronize data with synclist
+      for sync in synclist
+        if sync.url is url
+          data.push sync.data
+
+      options.success(data)
+
   else
-    Backbone.backendSync(arguments...)
+    synclist.push { url: url, data: model.toJSON(), method: method }
+    localStorage['toSync'] = JSON.stringify synclist
+
+    #Backbone.backendSync(arguments...)


Change card.type to card.brand to match Stripe API change. Fixes #170

--- app/assets/javascripts/store/gateway/stripe.js.coffee
+++ app/assets/javascripts/store/gateway/stripe.js.coffee
@@ -44,7 +44,7 @@
   else
     Spree.stripePaymentMethod.find('#card_number, #card_expiry, #card_code').prop("disabled" , true)
     Spree.stripePaymentMethod.find(".ccType").prop("disabled", false)
-    Spree.stripePaymentMethod.find(".ccType").val(mapCC(response.card.type))
+    Spree.stripePaymentMethod.find(".ccType").val(mapCC(response.card.brand))

     # token contains id, last4, and card type
     token = response['id'];


fix analytics spec

fix analytics spec

--- test/lib/analytics.coffee
+++ test/lib/analytics.coffee
@@ -13,11 +13,9 @@

     before ->
       sinon.stub(analytics, 'getUserAgent').returns "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/32.0.1700.102 Safari/537.36"
-      @clock = sinon.useFakeTimers();

     after ->
       analytics.getUserAgent.restore()
-      @clock.restore()

     beforeEach ->
       sd.MIXPANEL_ID = 'mix that panel'
@@ -39,6 +37,12 @@
         @gaStub.args[0][1].should.equal 'goog that analytics'

     describe '#trackPageview', ->
+
+      beforeEach ->
+        @clock = sinon.useFakeTimers();
+
+      afterEach ->
+        @clock.restore()

       it 'sends a google pageview', ->
         analytics.trackPageview()


Fixed ticket creation.

Fixed ticket creation.

--- app/assets/javascripts/app/controllers/agent_ticket_create.js.coffee
+++ app/assets/javascripts/app/controllers/agent_ticket_create.js.coffee
@@ -150,25 +150,26 @@

       # disable form
       @formDisable(e)
-
+      ui = @
       object.save(
-        success: (r) =>
+        success: ->

           # notify UI
-          @notify
+          ui.notify
             type:    'success',
-            msg:     T('Ticket %s created!', r.number),
-            link:    "#ticket/zoom/#{r.id}"
+            msg:     T('Ticket %s created!', @.number),
+            link:    "#ticket/zoom/#{@.id}"
             timeout: 12000,

           # create new create screen
-          @render()
+          ui.render()

           # scroll to top
-          @scrollTo()
-
-        error: =>
-          @log 'save failed!'
+          ui.scrollTo()
+
+        error: ->
+          ui.log 'save failed!'
+          ui.formEnable(e)
       )

@@ -209,17 +210,19 @@
       return

     # save user
+    ui = @
     user.save(
-      success: (r) =>
-        @modalHide()
-        realname = r.firstname + ' ' + r.lastname
-        $('#create_customer_id').val(r.id)
+      success: ->
+        console.log('-------', @, ui)
+        ui.modalHide()
+        realname = @.firstname + ' ' + @.lastname
+        $('#create_customer_id').val(@.id)
         $('#create_customer_id_autocompletion').val(realname)

         # start customer info controller
-        @userInfo( user_id: r.id )
-      error: =>
-        @modalHide()
+        ui.userInfo( user_id: @.id )
+      error: ->
+        ui.modalHide()
     )

 Config.Routes['ticket_create'] = Index


remove console.logs

remove console.logs

--- src/models/octoblu-oauth.coffee
+++ src/models/octoblu-oauth.coffee
@@ -33,7 +33,6 @@
     callback()

   getAuthCode: (authCode, callback) =>
-    console.log atob(authCode)
     [client_id, uuid, token] = atob(authCode).split ':'
     debug 'getAuthCode', [client_id, uuid]
     callback null, {
@@ -44,7 +43,6 @@

   _generateHash: ({client_id, uuid, token}) =>
     hasher = crypto.createHash 'sha256'
-    console.log {client_id, uuid, @pepper}
     hasher.update @meshbluConfig.uuid
     hasher.update uuid
     hasher.update @pepper
@@ -69,7 +67,6 @@
     tokenOptions = {tag, client_id}
     meshblu.revokeTokenByQuery uuid, {tag}, (error, response) =>
       return callback error if error?
-      console.log {response}
       meshblu.generateAndStoreTokenWithOptions uuid, tokenOptions, (error, response) =>
         debug 'generateAndStoreToken error: ', error if error?
         return callback error if error?


Individual portal fetch is disabled currently

--- spec/GetPortalState.coffee
+++ spec/GetPortalState.coffee
@@ -26,7 +26,7 @@
     c.outPorts.error.attach error
     portalConfig = JSON.parse fs.readFileSync path.resolve(__dirname, '../portals.json'), 'utf-8'

-  describe 'getting portal information', ->
+  describe.skip 'getting portal information', ->
     it 'should be able to retrieve it', (done) ->
       return done() unless process.env.API_HOST
       return done() unless process.env.API_USERNAME


nof...again

nof...again

--- spec/find-view-spec.coffee
+++ spec/find-view-spec.coffee
@@ -484,7 +484,7 @@
             expect(findView.descriptionLabel).not.toHaveClass 'text-error'
             expect(findView.descriptionLabel.text()).toContain "6 results"

-        fdescribe "when the search string is too large", ->
+        describe "when the search string is too large", ->
           beforeEach ->
             largeText = "x".repeat(50000)
             findView.findEditor.setText largeText


Use quotes for spec descriptions

Use quotes for spec descriptions

--- spec/operators-spec.coffee
+++ spec/operators-spec.coffee
@@ -381,7 +381,7 @@
       expect(vimState.getRegister('a').text).toBe "012 345\n"

   describe "the p keybinding", ->
-    describe 'character', ->
+    describe "character", ->
       beforeEach ->
         editor.getBuffer().setText "012\n"
         editor.setCursorScreenPosition [0, 0]
@@ -402,7 +402,7 @@
         expect(editor.getText()).toBe "0a12\n"
         expect(editor.getCursorScreenPosition()).toEqual [0, 2]

-    describe 'linewise', ->
+    describe "linewise", ->
       beforeEach ->
         editor.getBuffer().setText("012\n")
         editor.setCursorScreenPosition([0, 1])
@@ -428,7 +428,7 @@
         expect(editor.getText()).toBe "12345\nabcde\nABCDE\nQWERT"

   describe "the P keybinding", ->
-    describe 'character', ->
+    describe "character", ->
       beforeEach ->
         editor.getBuffer().setText("012\n")
         editor.setCursorScreenPosition([0, 0])
@@ -449,7 +449,7 @@
         expect(editor.getText()).toBe "a012\n"
         expect(editor.getCursorScreenPosition()).toEqual [0, 1]

-    describe 'linewise', ->
+    describe "linewise", ->
       beforeEach ->
         editor.getBuffer().setText("012\n")
         editor.setCursorScreenPosition([0, 1])


Remove fsUtils.makeDirectory()

Remove fsUtils.makeDirectory()

fsUtils.makeTree() was sufficient for all current uses.

--- spec/tree-view-spec.coffee
+++ spec/tree-view-spec.coffee
@@ -601,8 +601,8 @@

       dirPath = path.join(rootDirPath, "test-dir")
       filePath = path.join(dirPath, "test-file.txt")
-      fsUtils.makeDirectory(rootDirPath)
-      fsUtils.makeDirectory(dirPath)
+      fsUtils.makeTree(rootDirPath)
+      fsUtils.makeTree(dirPath)
       fsUtils.writeSync(filePath, "doesn't matter")

       project.setPath(rootDirPath)
@@ -703,7 +703,7 @@
           describe "when a file or directory already exists at the given path", ->
             it "shows an error message and does not close the dialog", ->
               newPath = path.join(dirPath, "new-dir")
-              fsUtils.makeDirectory(newPath)
+              fsUtils.makeTree(newPath)
               addDialog.miniEditor.insertText("new-dir/")
               addDialog.trigger 'core:confirm'


Verify expansion state

Verify expansion state

--- spec/tree-view-spec.coffee
+++ spec/tree-view-spec.coffee
@@ -2669,8 +2669,19 @@
       fs.removeSync(entriesPath)
       treeView.roots[0].reload()

+      expect(treeView.roots[0].directory.serializeExpansionState()).toEqual
+        isExpanded: true
+        entries: {}
+
       fs.mkdirSync(path.join(projectPath, 'other'))
       treeView.roots[0].reload()
+
+      expect(treeView.roots[0].directory.serializeExpansionState()).toEqual
+        isExpanded: true
+        entries:
+          other:
+            isExpanded: false
+            entries: {}

   describe "Dragging and dropping files", ->
     beforeEach ->


Suppress false coffeelint error

Suppress false coffeelint error

--- spec/workspace-spec.coffee
+++ spec/workspace-spec.coffee
@@ -661,7 +661,7 @@
   describe "::isTextEditor(obj)", ->
     it "returns true when the passed object is an instance of `TextEditor`", ->
       expect(workspace.isTextEditor(atom.workspace.buildTextEditor())).toBe(true)
-      expect(workspace.isTextEditor({getText: ->})).toBe(false)
+      expect(workspace.isTextEditor({getText: -> null})).toBe(false)
       expect(workspace.isTextEditor(null)).toBe(false)
       expect(workspace.isTextEditor(undefined)).toBe(false)


Update tests

Update tests

--- test/pager-me-test.coffee
+++ test/pager-me-test.coffee
@@ -6,7 +6,7 @@

 describe 'pagerduty', ->
   before ->
-    @triggerRegex = /(pager|major)( me)? (?:trigger|page) ((["'])([^]*?)\4|([\.\w\-]+)) (.+)$/i
+    @triggerRegex = /(pager|major)( me)? (?:trigger|page) ((["'])([^\4]*?)\4|“([^”]*?)”|‘([^’]*?)’|([\.\w\-]+)) (.+)$/i
     @schedulesRegex = /(pager|major)( me)? schedules( ((["'])([^]*?)\5|(.+)))?$/i
     @whosOnCallRegex = /who(?:’s|'s|s| is|se)? (?:on call|oncall|on-call)(?:\?)?(?: (?:for )?((["'])([^]*?)\2|(.*?))(?:\?|$))?$/i

@@ -82,23 +82,23 @@

   it 'trigger handles users with dots', ->
     msg = @triggerRegex.exec('pager trigger foo.bar baz')
-    expect(msg[6]).to.equal('foo.bar')
-    expect(msg[7]).to.equal('baz')
+    expect(msg[8]).to.equal('foo.bar')
+    expect(msg[9]).to.equal('baz')

   it 'trigger handles users with spaces', ->
     msg = @triggerRegex.exec('pager trigger "foo bar" baz')
     expect(msg[5]).to.equal('foo bar')
-    expect(msg[7]).to.equal('baz')
+    expect(msg[9]).to.equal('baz')

   it 'trigger handles users with spaces and single quotes', ->
     msg = @triggerRegex.exec("pager trigger 'foo bar' baz")
     expect(msg[5]).to.equal('foo bar')
-    expect(msg[7]).to.equal('baz')
+    expect(msg[9]).to.equal('baz')

   it 'trigger handles users without spaces', ->
     msg = @triggerRegex.exec('pager trigger foo bar baz')
-    expect(msg[6]).to.equal('foo')
-    expect(msg[7]).to.equal('bar baz')
+    expect(msg[8]).to.equal('foo')
+    expect(msg[9]).to.equal('bar baz')

   it 'schedules handles names with quotes', ->
     msg = @schedulesRegex.exec('pager schedules "foo bar"')


Fixed grammar

Fixed grammar

--- test.coffee
+++ test.coffee
@@ -72,25 +72,25 @@
     .post "/"
     .set "content-type", t.context.multipartHeaderMock

-  t.true isPlainObject(body), "Request should always returns a plain object"
+  t.true isPlainObject(body), "Request should always return a plain object"

-test "Should return files and fields in two other objects", (t) ->
+test "Should return files and fields in two different objects", (t) ->
   {body} = await request t.context.serverMock on
     .post "/"
     .set "content-type", t.context.multipartHeaderMock

   t.deepEqual body, {fields: {}, files: {}}

-test "Should return a correct string field", (t) ->
+test "Should return a valid string field", (t) ->
   {body} = await request do t.context.serverMock
     .post "/"
     .set "content-type", t.context.multipartHeaderMock
     .field "someValue", "In Soviet Moon, landscape see binoculars through you."

   t.true "someValue" of body, "Result should contain someValue field"
-  t.is typeof body.someValue, "string", "someValue should be a string"
+  t.is typeof body.someValue, "string", "someValue field should be a string"
   t.is body.someValue, "In Soviet Moon, landscape see binoculars through you.",
-    "someValue should contain a valid message in string"
+    "someValue field should contain a valid message in the string"

 test "Should rescue types", (t) ->
   t.plan 4


Rewrite of richtext-test.coffee

Rewrite of richtext-test.coffee

--- test/richtext-test.coffee
+++ test/richtext-test.coffee
@@ -9,416 +9,20 @@
 chai.use(sinonChai)
 chai.config.includeStack = true

-Y_richText = require '../lib/y-richtext.coffee'
-Y =
-  RichText: Y_richText.YRichText
-  Selection: Y_richText.Selection
-  Word: Y_richText.Word
-  misc:
-    absoluteFromRelative: Y_richText.absoluteFromRelative
-    relativeFromAbsolute: Y_richText.relativeFromAbsolute
+Y = require '../../yjs/lib/y.coffee'
+Connector = require '../../y-test/lib/y-test.coffee'
+Y.RichText = require '../lib/y-richtext.coffee'
+TestEditor = (require '../lib/editor-abstraction.coffee').TestEditor

-describe 'Rich Text type should', ->
-  richText1 = RichText2 = null
-  ar = ["This", "is", "a ", "test"]
-  it 'initialize correctly [constructor, val]', ->
-    richText1 = new Y.RichText "Test"
-    richText1.val().should.equal "Test"
-
-  it 'split the words correctly [push]', ->
-    richText1 = new Y.RichText "This is a  test"
-    richText1.val().should.equal "This is a  test"
-    richText1 = new Y.RichText "   "
-    richText1.val().should.equal "   "
-
-  it 'get word correctly [getWord]', ->
-    richText1 = new Y.RichText "This is a  test"
-    richText1.getWord(0).word.should.equal "This "
-    richText1.getWord(1).word.should.equal "is "
-    richText1.getWord(2).word.should.equal "a  "
-    richText1.getWord(3).word.should.equal "test"
-
-  it 'set word correctly [setWord]', ->
-    richText1 = new Y.RichText "This is a  test"
-    richText1.setWord(2, 'a ')
-    richText1.val().should.equal "This is a test"
-
-  it 'insert words correctly [insertWords]', ->
-    richText1 = new Y.RichText "is"
-    richText1.insertWords(0, ["This "])
-    richText1.val().should.equal "This is"
-    richText1.insertWords(2, [" sparta ", "!"])
-    richText1.val().should.equal "This is sparta !"
-
-  it 'delete words correctly [deleteWords]', ->
-    richText1 = new Y.RichText "This There is a mistake in this sentence!"
-    richText1.deleteWords(0, 1)
-    richText1.val().should.equal "There is a mistake in this sentence!"
-
-    richText1 = new Y.RichText "This are There is a mistake in this sentence!"
-    richText1.deleteWords(0, 2)
-    richText1.val().should.equal "There is a mistake in this sentence!"
-
-    richText1 = new Y.RichText "There is a mistake in this sentence! there"
-    richText1.deleteWords(7, 8)
-    richText1.val().should.equal "There is a mistake in this sentence! "
-
-  it 'not delete space after word when selection ends at last character
-   [deleteSel]', ->
-    richText1 = new Y.RichText "I am yjs here!"
-    sel = new Y.Selection(5, 8, richText1)
-    richText1.deleteSel(sel)
-    richText1.val().should.equal "I am  here!"
-
-  it 'delete space and merge (if necessary) [deleteSel]', ->
-    richText1 = new Y.RichText "y jjs is here!"
-    sel = new Y.Selection(1, 3, richText1)
-    richText1.deleteSel(sel)
-    richText1.val().should.equal "yjs is here!"
-
-  it 'merge words when no space anymore [deleteSel]', ->
-    richText1 = new Y.RichText "yjs is is here!"
-    sel = new Y.Selection(4, 7, richText1)
-    richText1.deleteSel(sel)
-    richText1.val().should.equal "yjs is here!"
-
-  it 'merge the two words [merge]', ->
-    richText1 = new Y.RichText "Hel lo"
-    richText1.merge(0)
-    richText1.val().should.equal "Hello"
-
-  it 'insert correctly [insert]', ->
-    richText1 =  new Y.RichText ""
-
-    richText1.insert(0, "Two words")
-    richText1.val().should.equal "Two words"
-    richText1._richText.words.length.should.equal 2
-
-    richText1.insert(4, "insertd ")
-    richText1.val().should.equal "Two insertd words"
-    richText1._richText.words.length.should.equal 3
-
-   it 'accept deltas (insert) [delta]', ->
-    delta = { ops:[
-      { insert: 'Gandalf', attributes: { bold: true } },
-      { insert: ' the ' },
-      { insert: 'Grey', attributes: { color: '#ccc' } },
-      { insert: '.'}
-      ] }
-    richText1 = new Y.RichText()
-
-    richText1.delta delta
-
-    richText1.val().should.equal "Gandalf the Grey."
-    richText1._richText.words.length.should.equal 4
-    richText1._richText.words[0].word.should.equal "Gandalf "
-    richText1._richText.selections[0].should.have.property 'left', richText1.getWord(0)
-    richText1._richText.selections.length.should.equal 2
-
-  it 'accept styles [delta]', ->
-    delta = { ops:[
-      { insert: 'Gandalf', attributes: { bold: true } },
-      { insert: ' the ' },
-      { insert: 'Grey', attributes: { bold: true } }
-      ] }
-    richText1 = new Y.RichText()
-    richText1.delta delta
-    delta1 = {ops: [
-      {retain: 7},
-      {retain: 5, attributes: {bold: true}}
-      ]}
-    richText1.delta delta1
-    richText1.getSelections().length.should.equal 1
-
-  it 'should accept deltas (retain & delete) [delta]', ->
-    delta = { ops:[
-      { retain: 7, attributes: { bold: true } },
-      { delete: 4},
-      ] }
-    richText1 = new Y.RichText "Gandalf the Grey"
-    richText1.delta delta
-    richText1.val().should.equal "Gandalf Grey"
-
-describe 'Utilities', ->
-  it 'should convert correctly [relativeFromAbsolute]', ->
-    richText = new Y.RichText "Zero One two three four five"
-    pos = Y.misc.relativeFromAbsolute 0, richText
-    pos.should.have.property 'word', 0
-    pos.should.have.property 'pos', 0
-
-    pos = Y.misc.relativeFromAbsolute 7, richText
-    pos.should.have.property 'word', 1
-    pos.should.have.property 'pos', 2
-
-  it 'should convert correctly [absoluteFromRelative]', ->
-    richText = new Y.RichText "Zero One two three four five"
-    pos = Y.misc.absoluteFromRelative 0, 2, richText
-    pos.should.equal 2
-
-    pos = Y.misc.absoluteFromRelative 1, 2, richText
-    pos.should.equal 7
-
-describe 'y-richText.Selection object should', ->
-  sel = sel2 = word = null
-  richText = new Y.RichText "Zero One two three four five"
-
-  it 'initialized with three parameters [constructor]', ->
-    sel = new Y.Selection 1, 7, richText
-
-  it 'converichText positions correctly [_relativeFromAbsolute]', ->
-    sel = new Y.Selection 1, 7, richText
-    sel.should.have.property 'left', (richText.getWord 0)
-    sel.should.have.property 'leftPos', 1
-
-    sel.should.have.property 'right', (richText.getWord 1)
-    sel.should.have.property 'rightPos', 2
-
-  it 'have an order relation [gt]', ->
-    sel0 = new Y.Selection 1, 7, richText
-    (sel0.gt((richText.getWord 0), 0, "left")).should.be.true
-    (sel0.gt((richText.getWord 0), 0, "right")).should.be.true
-
-    (sel0.gt((richText.getWord 1), 0, "left")).should.be.false
-    (sel0.gt((richText.getWord 1), 0, "right")).should.be.true
-
-    (sel0.gt((richText.getWord 1), 2, "left")).should.be.false
-    (sel0.gt((richText.getWord 1), 2, "right")).should.be.true
-
-    (sel0.gt((richText.getWord 1), 3, "left")).should.be.false
-    (sel0.gt((richText.getWord 1), 3, "right")).should.be.false
-
-  it 'have an order relation [lt]', ->
-    sel0 = new Y.Selection 1, 7, richText
-    (sel0.lt((richText.getWord 0), 0, "left")).should.be.false
-    (sel0.lt((richText.getWord 0), 0, "right")).should.be.false
-
-    (sel0.lt((richText.getWord 1), 0, "left")).should.be.true
-    (sel0.lt((richText.getWord 1), 0, "right")).should.be.false
-
-    (sel0.lt((richText.getWord 1), 2, "left")).should.be.true
-    (sel0.lt((richText.getWord 1), 2, "right")).should.be.true
-
-    (sel0.lt((richText.getWord 1), 3, "left")).should.be.true
-    (sel0.lt((richText.getWord 1), 3, "right")).should.be.true
-
-  it 'have working functions [equals,notEquals,in,contains,overlaps,atLeftOf]', ->
-    sel0 = new Y.Selection 0, 1, richText
-    sel1 = new Y.Selection 1, 2, richText
-    sel2 = new Y.Selection 0, 2, richText
-    sel3 = new Y.Selection 2, 3, richText
-    sel0.equals(sel0).should.be.true
-    sel0.equals(sel1).should.be.false
-
-    sel0.notEquals(sel0).should.be.false
-    sel0.notEquals(sel1).should.be.true
-
-    sel0.in(sel1).should.be.false
-    sel0.in(sel2).should.be.true
-
-    sel0.contains(sel1).should.be.false
-    sel0.contains(sel2).should.be.false
-    sel2.contains(sel0).should.be.true
-
-    sel0.atLeftOf(sel1).should.be.true
-    sel0.atLeftOf(sel2).should.be.true
-    sel0.atLeftOf(sel3).should.be.false
-
-  it 'return good values for [isValid]', ->
-    sel0 = new Y.Selection 1, 0, richText
-    sel1 = new Y.Selection 0, 1, richText
-
-    sel0.isValid().should.be.false
-    sel1.isValid().should.be.true
+describe 'a', ->
+  richText = null
+  beforeEach () ->
+    con = new Connector "abc"
+    y = new Y con
+    editor = new TestEditor()
+    y.val("test", new Y.RichText(editor))
+    richText = y.val("test")

-  it 'split correcly two selections [split]', ->
-    richText = new Y.RichText "Zero one two three"
-    sel0 = new Y.Selection 0, 10, richText, {style: {foo: "bar"}}
-    sel1 = new Y.Selection 2, 8, richText, {style: {foo: "ping pong is awesome"}}
-
-    sel0.split sel1 # nothing happens!
-    richText.getSelections().length.should.equal 2
-
-    sel1.split sel0 # should create a new selection at right of sel1
-    sels = richText.getSelections()
-
-    sels.length.should.equal 3
-    sels[0].should.equal sel0
-    sels[1].should.equal sel1
-    sels[2].leftPos.should.equal(sels[1].rightPos)
-    sels[2].left.word.should.equal(sels[1].right.word)
-
-  it 'unbind correctly [unbind]', ->
-    richText = new Y.RichText "Zero One two three four five"
-    sel = new Y.Selection 0, 1, richText
-    sel2 = new Y.Selection 0, 5, richText
-    sel.unbind()
-    (sel.left == null).should.be.true
-    (sel.right == null).should.be.true
-    richText.getWord(0).left.length.should.equal 1
-    richText.getWord(0).right.length.should.equal 0
-
-describe 'Selection.merge ', ->
-  it 'not merging', ->
-    richText = new Y.RichText "Zero One two three four five"
-    sel0 = new Y.Selection 0, 1, richText, {style: {foo: "bar"}}
-    sel1 = new Y.Selection 0, 1, richText, {style: {mergeMe: "I'm famous"}}
-    sel2 = new Y.Selection 2, 10, richText, {style: {foo: "bar"}}
-
-    sel0.merge sel0 # same selection
-    richText._richText.selections.length.should.equal 3
-
-    sel0.merge sel1 # not same style
-    richText._richText.selections.length.should.equal 3
-
-    sel0.merge sel2 # not merging
-    richText._richText.selections.length.should.equal 3
-
-    sel0.merge() # no argument
-    richText._richText.selections.length.should.equal 3
-
-  it 'at right', ->
-    richText = new Y.RichText "Zero One two three four five"
-    sel0 = new Y.Selection 0, 1, richText, {style: {foo: "bar"}}
-    sel1 = new Y.Selection 0, 1, richText, {style: {mergeMe: "I'm famous"}}
-    sel2 = new Y.Selection 1, 10, richText, {style: {foo: "bar"}}
-    sel0.merge sel2
-    richText._richText.selections.length.should.equal 2
-    richText._richText.selections.indexOf(sel2).should.equal -1
-
-    leftWord = richText.getWord 0
-    rightWord = richText.getWord 2
-
-    sel0.should.have.deep.property 'left', leftWord
-    sel0.should.have.deep.property 'leftPos', 0
-    sel0.should.have.deep.property 'right', rightWord
-    sel0.should.have.deep.property 'rightPos', 1
-
-  it 'at left', ->
-    richText = new Y.RichText "Zero One two three four five"
-    sel0 = new Y.Selection 0, 1, richText, {style: {foo: "bar"}}
-    sel2 = new Y.Selection 1, 10, richText, {style: {foo: "bar"}}
-
-    sel2.merge sel0, richText
-    richText._richText.selections.length.should.equal 1
-    richText._richText.selections.indexOf(sel0).should.equal -1
-
-  it 'in', ->
-    richText = new Y.RichText "Zero One two three four five"
-    sel0 = new Y.Selection 0, 10, richText
-    sel2 = new Y.Selection 2, 5, richText
-
-    console.log sel2.in sel0
-    sel2.merge sel0, richText
-    richText._richText.selections.length.should.equal 1
-    richText._richText.selections.indexOf(sel0).should.equal -1
-
-  it 'contains', ->
-    richText = new Y.RichText "Zero One two three four five"
-    sel0 = new Y.Selection 0, 10, richText
-    sel2 = new Y.Selection 2, 5, richText
-
-    sel0.merge sel2, richText
-    richText._richText.selections.length.should.equal 1
-    richText._richText.selections.indexOf(sel2).should.equal -1
-
-
-describe 'Word objects should', ->
-  sel = sel2 = richText = word = null
-  it 'be linked correctly to selections [constructor]', ->
-    richText = new Y.RichText "This is a test"
-    sel = new Y.Selection 0, 6, richText
-    word = richText.getWord(0)
-    word.left[0].equals(sel).should.be.true
-    word.right.length.should.equals 0
-
-    word = richText.getWord(1)
-    word.left.length.should.equals 0
-    word.right[0].equals(sel).should.be.true
-
-  it 'remove selection correctly [removeSel]', ->
-    richText = new Y.RichText "This is a test"
-    sel = new Y.Selection 0, 6, richText
-    word = richText.getWord(0)
-    word.removeSel sel, "left"
-    word.left.length.should.equals 0
-
-    word = richText.getWord(1)
-    word.right.length.should.equals 1
-
-  it 'be able to get its index in RTE instance [index]', ->
-    richText = new Y.RichText "This is a test"
-    richText.val()
-    word = richText.getWord( 3)
-    word.index(richText).should.equal 3
-  it '[diff]', ->
-      # create a test !
-#    richText1 = new Y.Word "Something"
-#    ret = richText1.diffToDelta "machin", "Machierie"
-#    console.log ret
-
-describe 'y-richtext.Selections objects should get updated when', ->
-  it 'deleting words [deleteWords]', ->
-    richText = new Y.RichText "This is a test with many words."
-    sel1 = new Y.Selection 8, 10, richText # will be deleted
-    sel2 = new Y.Selection 0, 6, richText # will be updated
-    sel3 = new Y.Selection 10, 18, richText # will be updated
-    richText.deleteWords 1, 4 # deleting "is ", "a " and "test "
-    # richText is now "This with many words."
-
-    richText.getSelections().indexOf(sel1).should.equal -1
-
-    sel2.left.should.equal richText.getWord(0)
-    sel2.right.should.equal richText.getWord(1)
-
-    sel3.left.should.equal richText.getWord(1)
-    sel3.leftPos.should.equal 0
-    sel3.right.should.equal richText.getWord(1)
-    sel3.rightPos.should.equal 3
-
-  it 'merging words [merge]', ->
-    richText = new Y.RichText "Long sword is a kind of sword."
-    sel1 = new Y.Selection 2, 15, richText # will point on Longsword at left
-    sel2 = new Y.Selection 4, 6, richText # will point on Longsword at left
-    richText.merge 0
-    sel1.left.should.equal richText.getWord(0)
-    sel1.right.should.equal richText.getWord(2)
-
-    sel2.left.should.equal richText.getWord(0)
-    sel2.right.should.equal richText.getWord(0)
-
-  it 'deleting a selection [deleteSel]', ->
-    richText = new Y.RichText "I'm the real test"
-    sel = new Y.Selection 2, 8, richText
-    sel2 = new Y.Selection 0, 2, richText, {bind: false}
-    richText.deleteSel sel2
-    sel.left.should.equal (richText.getWord 0)
-    sel.leftPos.should.equal 0
-    sel.right.should.equal (richText.getWord 2)
-    sel.rightPos.should.equal 0
-
-  it 'inserting content [insert]', ->
-    richText = new Y.RichText "Th is a test"
-    sel = new Y.Selection 2, 8, richText
-
-    richText.insert 2, 'is'
-    sel.left.should.equal richText.getWord(0)
-    sel.leftPos.should.equal 4
-
-    sel.right.should.equal richText.getWord(3)
-    sel.rightPos.should.equal 0
-
-  it 'inserting words [insertWords]', ->
-    richText = new Y.RichText "This is a test"
-
-    sel = new Y.Selection 2, 8, richText
-    [leftPos, rightPos, this_word, a_word] = [sel.leftPos, sel.rightPos,
-                                              sel.left, sel.right]
-
-    toInsert = 'is really cool, ain\'t it? Because this'.split(' ')
-    richText.insertWords 1, toInsert
-    sel.left.should.equal this_word
-    sel.right.should.equal a_word
-
-    sel.leftPos.should.equal leftPos
-    sel.rightPos.should.equal rightPos
+  it 'b', ->
+    richText.passDeltas [{insert: "abc"}]


tests for listing Youtube resources

tests for listing Youtube resources

--- test/spec/GAPISpec.coffee
+++ test/spec/GAPISpec.coffee
@@ -222,10 +222,20 @@
     it 'should refer to the correct url', ->
       expect(Youtube.url).toBe 'https://www.googleapis.com/youtube/v3/'

-    it 'should list activities'
+    it 'should list activities', ->
+      url = "#{Youtube.url}activities?home=true&part=snippet"
+      $httpBackend.expectGET(url, getHeaders).respond null
+      Youtube.listActivities({part:'snippet', home:true})
+      $httpBackend.flush()
+
     it 'should insert activities'

-    it 'should list channels'
+    it 'should list channels', ->
+      url = "#{Youtube.url}channels?mine=true&part=snippet"
+      $httpBackend.expectGET(url, getHeaders).respond null
+      Youtube.listChannels({part:'snippet', mine:true})
+      $httpBackend.flush()
+
     it 'should update channels'

     it 'should list guide categories'
@@ -243,17 +253,35 @@
     it 'should update live streams'
     it 'should delete live streams'

-    it 'should list playlist items'
+    it 'should list playlist items', ->
+      url = "#{Youtube.url}playlistItems?maxResults=50&part=snippet&playlistId=1234"
+      $httpBackend.expectGET(url, getHeaders).respond null
+      Youtube.listPlaylistItems
+          part: 'snippet'
+          maxResults: 50
+          playlistId: '1234'
+      $httpBackend.flush()
+
     it 'should insert playlist items'
     it 'should update playlist items'
     it 'should delete playlist items'

-    it 'should list playlists'
+    it 'should list playlists', ->
+      url = "#{Youtube.url}playlists?mine=true&part=snippet"
+      $httpBackend.expectGET(url, getHeaders).respond null
+      Youtube.listPlaylists({part:'snippet', mine:true})
+      $httpBackend.flush()
+
     it 'should insert playlists'
     it 'should update playlists'
     it 'should delete playlists'

-    it 'should list subscriptions'
+    it 'should list subscriptions', ->
+      url = "#{Youtube.url}subscriptions?mine=true&part=snippet"
+      $httpBackend.expectGET(url, getHeaders).respond null
+      Youtube.listSubscriptions({part:'snippet', mine:true})
+      $httpBackend.flush()
+
     it 'should insert subscriptions'
     it 'should delete subscriptions'

@@ -261,7 +289,12 @@

     it 'should list video categories'

-    it 'should list videos'
+    it 'should list videos', ->
+      url = "#{Youtube.url}videos?myRating=like&part=snippet"
+      $httpBackend.expectGET(url, getHeaders).respond null
+      Youtube.listVideos({part:'snippet', myRating:'like'})
+      $httpBackend.flush()
+
     it 'should insert videos'
     it 'should update videos'
     it 'should delete videos'


Error flash message is now not closable, resolves #260

--- wzk/ui/Flash.coffee
+++ wzk/ui/Flash.coffee
@@ -27,6 +27,8 @@
   addMessage: (text, severity = 'info', fadeOut = undefined, closable = true) ->
     notError = severity isnt 'error'
     fadeOut = notError if fadeOut is undefined
+    unless notError
+      closable = false

     msgs = if goog.isArray(text) then text else [text]
     flashes = []


Fixed #1

Fixed #1

--- src/modules/notification.coffee
+++ src/modules/notification.coffee
@@ -7,7 +7,7 @@

 		@confirm = (message, callback, alert, buttons) ->
 			$cordova.ready().then () ->
-				$window.navigator.notification.confirm message, callback, alert, button
+				$window.navigator.notification.confirm message, callback, alert, buttons

 		return


Eliminate PaneContainer prototype properties

--- src/pane-container.coffee
+++ src/pane-container.coffee
@@ -5,19 +5,18 @@
 ItemRegistry = require './item-registry'
 PaneContainerElement = require './pane-container-element'

+SERIALIZATION_VERSION = 1
+STOPPED_CHANGING_ACTIVE_PANE_ITEM_DELAY = 100
+
 module.exports =
 class PaneContainer
-  serializationVersion: 1
-  root: null
-  stoppedChangingActivePaneItemDelay: 100
-  stoppedChangingActivePaneItemTimeout: null
-
   constructor: (params) ->
     {@config, applicationDelegate, notificationManager, deserializerManager, @viewRegistry, @location} = params
     @emitter = new Emitter
     @subscriptions = new CompositeDisposable
     @itemRegistry = new ItemRegistry
     @alive = true
+    @stoppedChangingActivePaneItemTimeout = null

     @setRoot(new Pane({container: this, @config, applicationDelegate, notificationManager, deserializerManager, @viewRegistry}))
     @setActivePane(@getRoot())
@@ -44,12 +43,12 @@

   serialize: (params) ->
     deserializer: 'PaneContainer'
-    version: @serializationVersion
+    version: SERIALIZATION_VERSION
     root: @root?.serialize()
     activePaneId: @activePane.id

   deserialize: (state, deserializerManager) ->
-    return unless state.version is @serializationVersion
+    return unless state.version is SERIALIZATION_VERSION
     @setRoot(deserializerManager.deserialize(state.root))
     activePane = find @getRoot().getPanes(), (pane) -> pane.id is state.activePaneId
     @setActivePane(activePane ? @getPanes()[0])
@@ -237,7 +236,7 @@
         @stoppedChangingActivePaneItemTimeout =
           setTimeout(
             stoppedChangingActivePaneItemCallback,
-            @stoppedChangingActivePaneItemDelay)
+            STOPPED_CHANGING_ACTIVE_PANE_ITEM_DELAY)

       @subscriptions.add(childSubscription)


Only pass @hash to super constructor

Only pass @hash to super constructor

This fixes an issue with setting the navigator as a param

--- src/routing/params.coffee
+++ src/routing/params.coffee
@@ -2,7 +2,7 @@

 class Batman.Params extends Batman.Hash
   constructor: (@hash, @navigator) ->
-    super
+    super(@hash)

     @url = new Batman.UrlParams({}, @navigator, this)

@@ -10,9 +10,10 @@

 class Batman.UrlParams extends Batman.Hash
   constructor: (@hash, @navigator, @params) ->
-    super
+    super(@hash)

     @replace(@paramsFromUri())
+    @updateParams()

     @on 'change', (obj) =>
       obj.updateUrl()


Make sure removed all nodes completely

Make sure removed all nodes completely

--- client/Framework/components/tree/treeviewcontroller.coffee
+++ client/Framework/components/tree/treeviewcontroller.coffee
@@ -248,9 +248,17 @@

   removeAllNodes:->

-    if @listControllers["0"]
-      @listControllers["0"].itemsOrdered.forEach (itemView)=>
-        @removeNodeView itemView
+    for id, listController of @listControllers
+      listController.itemsOrdered.forEach @bound 'removeNodeView'
+      listController?.getView().destroy()
+      delete @listControllers[id]
+      delete @listData[id]
+
+    @nodes           = {}
+    @listData        = {}
+    @indexedNodes    = []
+    @selectedNodes   = []
+    @listControllers = {}

   removeChildNodes:(id)->


FinderController: Make fetchContents optional.

--- client/finder/filetree/controllers/findercontroller.coffee
+++ client/finder/filetree/controllers/findercontroller.coffee
@@ -102,7 +102,7 @@

   mountMachine: (machine, options = {}) ->
-    options.fetchContent = yes
+    options.fetchContent ?= yes

     unless machine.status.state is Machine.State.Running
       return warn "Machine '#{machine.getName()}' was not ready, I skipped it."


HomeAccountIntegrations: on unlink check and remove org. token from team

Follow-up for unlink request flow we will check and ask to admin if they
want to remove the existing integration with the team and it will be
removed once approved.

--- client/home/lib/account/homeaccountintegrationsview.coffee
+++ client/home/lib/account/homeaccountintegrationsview.coffee
@@ -176,8 +176,11 @@
     @loaders[provider].show()

     me = whoami()
-    me.unlinkOauth provider, (err) =>
-      return showError err  if err
+    unlink = => me.unlinkOauth provider, (err) =>
+      if err
+        @switches[provider].setOn no
+        @loaders[provider].hide()
+        return showError err

       new kd.NotificationView
         title: "Your #{@providers[provider]} integration is now disabled."
@@ -185,6 +188,37 @@
       @linked[provider] = no
       @scopes[provider].unsetTooltip()
       @scopes[provider].hide()
+
+      @tokens[provider].updatePartial "#{@providers[provider]} Integration"
+
+      @loaders[provider].hide()
+
+    if provider is 'github' and isAdmin()
+
+      group = getGroup()
+      group.fetchDataAt ORG_TOKEN, (err, token) =>
+        return unlink()  if err or not token
+
+        if token is @linkedData.github.token
+
+          cc = kd.singletons.computeController
+          cc.ui.askFor 'disableTeamOAuth', {}, (status) =>
+
+            if status.confirmed
+              data = {}
+              data[ORG_TOKEN] = null
+              group.modifyData data, (err) ->
+                console.log 'Removing team organization key failed:', err  if err
+              do unlink
+
+            else
+              @switches[provider].setOn no
+              @loaders[provider].hide()
+        else
+          do unlink
+
+    else
+      do unlink

   getLoaderView: ->


change double quotes to single quotes in test/operators.coffee

--- test/operators.coffee
+++ test/operators.coffee
@@ -52,14 +52,14 @@
     ok new Number not instanceof String
     ok new Array not instanceof Boolean

-  test "use `::` operator on keyword `this`", ->
+  test 'use `::` operator on keyword `this`', ->
     obj = prototype: prop: nonce = {}
     eq nonce, (-> this::prop).call obj

   suite 'Existential Operator (Binary)', ->

-    test "binary existential operator", ->
+    test 'binary existential operator', ->
       nonce = {}

       b = a ? nonce
@@ -78,13 +78,13 @@
       b = a ? nonce
       eq 0, b

-    test "binary existential operator conditionally evaluates second operand", ->
+    test 'binary existential operator conditionally evaluates second operand', ->
       i = 1
       func = -> i -= 1
       result = func() ? func()
       eq result, 0

-    test "binary existential operator with negative number", ->
+    test 'binary existential operator with negative number', ->
       a = null ? - 1
       eq -1, a

@@ -103,36 +103,36 @@

   suite 'Existential Operator (Unary)', ->

-    test "postfix existential operator", ->
+    test 'postfix existential operator', ->
       ok (if nonexistent? then false else true)
       defined = true
       ok defined?
       defined = false
       ok defined?

-    test "postfix existential operator only evaluates its operand once", ->
+    test 'postfix existential operator only evaluates its operand once', ->
       semaphore = 0
       fn = ->
         ok false if semaphore
         ++semaphore
       ok(if fn()? then true else false)

-    test "negated postfix existential operator", ->
+    test 'negated postfix existential operator', ->
       ok !nothing?.value

-    test "postfix existential operator on expressions", ->
+    test 'postfix existential operator on expressions', ->
       eq true, (1 or 0)?, true

   suite '`is`,`isnt`,`==`,`!=`', ->

-    test "`==` and `is` should be interchangeable", ->
+    test '`==` and `is` should be interchangeable', ->
       a = b = 1
       ok a is 1 and b == 1
       ok a == b
       ok a is b

-    test "`!=` and `isnt` should be interchangeable", ->
+    test '`!=` and `isnt` should be interchangeable', ->
       a = 0
       b = 1
       ok a isnt 1 and b != 0
@@ -144,7 +144,7 @@
     # - `in` should check if an array contains a value using `indexOf`
     # - `of` should check if a property is defined on an object using `in`

-    test "in, of", ->
+    test 'in, of', ->
       arr = [1]
       ok 0 of arr
       ok 1 in arr
@@ -154,7 +154,7 @@
       ok 1 not of arr
       ok 0 not in arr

-    test "`in` should be able to operate on an array literal", ->
+    test '`in` should be able to operate on an array literal', ->
       ok 2 in [0, 1, 2, 3]
       ok 4 not in [0, 1, 2, 3]
       arr = [0, 1, 2, 3]
@@ -169,13 +169,13 @@
       ok val++ of arr
       ok val++ not of arr

-    test "`in` with cache and `__indexOf` should work in argument lists", ->
+    test '`in` with cache and `__indexOf` should work in argument lists', ->
       eq 1, [Object() in Array()].length

-    test "jashkenas/coffee-script#737: `in` should have higher precedence than logical operators", ->
+    test 'jashkenas/coffee-script#737: `in` should have higher precedence than logical operators', ->
       eq 1, 1 in [1] and 1

-    test "jashkenas/coffee-script#768: `in` should preserve evaluation order", ->
+    test 'jashkenas/coffee-script#768: `in` should preserve evaluation order', ->
       share = 0
       a = -> share++ if share is 0
       b = -> share++ if share is 1
@@ -183,23 +183,23 @@
       ok a() not in [b(),c()]
       eq 3, share

-    test "jashkenas/coffee-script#1099: empty array after `in` should compile to `false`", ->
+    test 'jashkenas/coffee-script#1099: empty array after `in` should compile to `false`', ->
       eq 1, [5 in []].length
       eq false, do -> return 0 in []

-    test "jashkenas/coffee-script#1354: optimized `in` checks should not happen when splats are present", ->
+    test 'jashkenas/coffee-script#1354: optimized `in` checks should not happen when splats are present', ->
       a = [6, 9]
       eq 9 in [3, a...], true

-    test "jashkenas/coffee-script#1100: precedence in or-test compilation of `in`", ->
+    test 'jashkenas/coffee-script#1100: precedence in or-test compilation of `in`', ->
       ok 0 in [1 and 0]
       ok 0 in [1, 1 and 0]
       ok not (0 in [1, 0 or 1])

-    test "jashkenas/coffee-script#1630: `in` should check `hasOwnProperty`", ->
+    test 'jashkenas/coffee-script#1630: `in` should check `hasOwnProperty`', ->
       ok undefined not in {length: 1}

-    #test "jashkenas/coffee-script#1714: lexer bug with raw range `for` followed by `in`", ->
+    #test 'jashkenas/coffee-script#1714: lexer bug with raw range `for` followed by `in`', ->
     #  0 for [1..2]
     #  ok not ('a' in ['b'])
     #
@@ -208,45 +208,45 @@
     #  0 for [1..10] # comment ending
     #  ok not ('a' in ['b'])

-    test "jashkenas/coffee-script#1099: statically determined `not in []` reporting incorrect result", ->
+    test 'jashkenas/coffee-script#1099: statically determined `not in []` reporting incorrect result', ->
       ok 0 not in []

   # Chained Comparison

-  test "chainable operators", ->
+  test 'chainable operators', ->
     ok 100 > 10 > 1 > 0 > -1
     ok -1 < 0 < 1 < 10 < 100

-  test "`is` and `isnt` may be chained", ->
+  test '`is` and `isnt` may be chained', ->
     ok true is not false is true is not false
     ok 0 is 0 isnt 1 is 1

-  test "different comparison operators (`>`,`<`,`is`,etc.) may be combined", ->
+  test 'different comparison operators (`>`,`<`,`is`,etc.) may be combined', ->
     ok 1 < 2 > 1
     ok 10 < 20 > 2+3 is 5

-  test "some chainable operators can be negated by `unless`", ->
+  test 'some chainable operators can be negated by `unless`', ->
     ok (true unless 0==10!=100)

-  test "operator precedence: `|` lower than `<`", ->
+  test 'operator precedence: `|` lower than `<`', ->
     eq 1, 1 | 2 < 3 < 4

-  test "preserve references", ->
+  test 'preserve references', ->
     a = b = c = 1
     # `a == b <= c` should become `a === b && b <= c`
     # (this test does not seem to test for this)
     ok a == b <= c

-  test "chained operations should evaluate each value only once", ->
+  test 'chained operations should evaluate each value only once', ->
     a = 0
     ok 1 > a++ < 1

-  #test "jashkenas/coffee-script#891: incorrect inversion of chained comparisons", ->
+  #test 'jashkenas/coffee-script#891: incorrect inversion of chained comparisons', ->
   #  ok (true unless 0 > 1 > 2)
   #  ok (true unless (NaN = 0/0) < 0/0 < NaN)

-  test "jashkenas/coffee-script#1234: Applying a splat to :: applies the splat to the wrong object", ->
+  test 'jashkenas/coffee-script#1234: Applying a splat to :: applies the splat to the wrong object', ->
     nonce = {}
     class C
       method: -> @nonce
@@ -255,37 +255,37 @@
     arr = []
     eq nonce, C::method arr... # should be applied to `C::`

-  test "jashkenas/coffee-script#1102: String literal prevents line continuation", ->
+  test 'jashkenas/coffee-script#1102: String literal prevents line continuation', ->
     eq "': '", '' +
     "': '"

-  test "jashkenas/coffee-script#1703, ---x is invalid JS", ->
+  test 'jashkenas/coffee-script#1703, ---x is invalid JS', ->
     x = 2
     eq (- --x), -1

-  #test "Regression with implicit calls against an indented assignment", ->
+  #test 'Regression with implicit calls against an indented assignment', ->
   #  eq 1, a =
   #    1
   #  eq a, 1

-  test "jashkenas/coffee-script#2155: conditional assignment to a closure", ->
+  test 'jashkenas/coffee-script#2155: conditional assignment to a closure', ->
     x = null
     func = -> x ?= (-> if true then 'hi')
     func()
     eq x(), 'hi'

-  test "jashkenas/coffee-script#2197: Existential existential double trouble", ->
+  test 'jashkenas/coffee-script#2197: Existential existential double trouble', ->
     counter = 0
     func = -> counter++
     func()? ? 100
     eq counter, 1

-  test "#85: operands of ExistsOp must be coerced to expressions", ->
+  test '#85: operands of ExistsOp must be coerced to expressions', ->
     f = ->
     f (a ? a?.b())
     f (a ? while 0 then)

-  test "#89: extends operator has side effects and should not be optimised away", ->
+  test '#89: extends operator has side effects and should not be optimised away', ->
     class A
     class B
     B extends A
@@ -293,7 +293,7 @@

   # Loop Operators

-  test "#195: 'until' keyword should negate loop condition", ->
+  test '#195: "until" keyword should negate loop condition', ->
     x = 0
     x++ until x > 10
     eq x, 11


Updated spacing

Updated spacing

--- test/webserver.coffee
+++ test/webserver.coffee
@@ -71,6 +71,7 @@
     client.get '/users', (err, req, res, data) ->
       assert.ifError err
       assert.deepEqual data, { users: ['silly_user', 'test_user'] }
+
       client.del '/users/test_user', (err, req, res, data) ->
         assert.ifError err
         client.get '/users', (err, req, res, data) ->


remove old test

remove old test

--- test_optimized.coffee
+++ test_optimized.coffee
@@ -1,220 +0,0 @@
-#!/usr/bin/env coffee
-
-cc = require "./src/coffeecup"
-
-tests =
-  'Literal text':
-    template: "text 'Just text'"
-    expected: 'Just text'
-
-  'Default DOCTYPE':
-    template: "doctype()"
-    expected: '<!DOCTYPE html>'
-
-  'DOCTYPE':
-    template: "doctype 'xml'"
-    expected: '<?xml version="1.0" encoding="utf-8" ?>'
-
-  'Custom tag':
-    template: "tag 'custom'"
-    expected: '<custom></custom>'
-
-  'Custom tag with attributes':
-    template: "tag 'custom', foo: 'bar', ping: 'pong'"
-    expected: '<custom foo="bar" ping="pong"></custom>'
-
-  'Custom tag with attributes and inner content':
-    template: "tag 'custom', foo: 'bar', ping: 'pong', -> 'zag'"
-    expected: '<custom foo="bar" ping="pong">zag</custom>'
-
-  'Self-closing tags':
-    template: "img src: 'icon.png', alt: 'Icon'"
-    expected: ''
-
-  'Common tag':
-    template: "p 'hi'"
-    expected: '
hi
'
-
-  'Attributes':
-    template: "a href: '/', title: 'Home'"
-    expected: ''
-
-  'HereDocs':
-    template: '''
-      script """
-        $(document).ready(function(){
-          alert('test');
-        });
-      """
-    '''
-    expected: ""
-
-  'CoffeeScript helper (function)':
-    template: "coffeescript -> alert 'hi'"
-    expected: ""
-
-  'CoffeeScript helper (string)':
-    template: "coffeescript \"alert 'hi'\""
-    expected: ""
-
-  'Context vars':
-    template: "h1 @foo"
-    expected: '
bar
'
-    params: {foo: 'bar'}
-
-  'Local vars, hardcoded':
-    template: 'h1 "harcoded: " + obj.foo'
-    run: ->
-      obj = {foo: 'bar'}
-      @compiled = cc.compile(@template, hardcode: {obj})
-      @expected = '
harcoded: bar
'
-      @result = @compiled()
-      @success = @result is @expected
-      if @success
-        obj.foo = 'baz'
-        @result = @compiled()
-        @success = @result is @expected
-
-  'Local vars, hard-coded (functions)':
-    template: "h1 \"The sum is: \#{sum 1, 2}\""
-    expected: '
The sum is: 3
'
-    params: {hardcode: {sum: (a, b) -> a + b}}
-
-  'Local vars, hard-coded ("helpers")':
-    template: "textbox id: 'foo'"
-    expected: '<input id="foo" name="foo" type="text" />'
-    params:
-      hardcode:
-        textbox: (attrs) ->
-          tag 'input',
-            id: attrs.id
-            name: attrs.id
-            type: 'text'
-
-  'Local vars':
-    template: 'h1 "dynamic: " + obj.foo'
-    run: ->
-      obj = {foo: 'bar'}
-      @expected = '
dynamic: bar
'
-      @result = render(@template, locals: {obj: obj})
-      @success = @result is @expected
-      if @success
-        obj.foo = 'baz'
-        @expected = '
dynamic: baz
'
-        @result = render(@template, locals: {obj: obj})
-        @success = @result is @expected
-
-  'Comments':
-    template: "comment 'Comment'"
-    expected: ''
-
-  'Escaping':
-    template: "h1 h(\"\")"
-    expected: "
<script>alert('"pwned" by c&a &copy;')</script>
"
-
-  'Autoescaping':
-    template: "h1 \"\""
-    expected: "
<script>alert('"pwned" by c&a &copy;')</script>
"
-    params:
-      autoescape: yes
-
-  'ID/class shortcut (combo)':
-    template: "div '#myid.myclass1.myclass2', 'foo'"
-    expected: '
foo
'
-
-  'ID/class shortcut (ID only)':
-    template: "div '#myid', 'foo'"
-    expected: '
foo
'
-
-  'ID/class shortcut (one class only)':
-    template: "div '.myclass', 'foo'"
-    expected: '
foo
'
-
-  'ID/class shortcut (multiple classes)':
-    template: "div '.myclass.myclass2.myclass3', 'foo'"
-    expected: '
foo
'
-
-  'ID/class shortcut (no string contents)':
-    template: "img '#myid.myclass', src: '/pic.png'"
-    expected: ''
-
-  'Attribute values':
-    template: "br vrai: yes, faux: no, undef: @foo, nil: null, str: 'str', num: 42, arr: [1, 2, 3].join(','), obj: {foo: 'bar'}, func: ->"
-    expected: '
'
-
-  'IE conditionals':
-    template: """
-      html ->
-        head ->
-          title 'test'
-          ie 'gte IE8', ->
-            link href: 'ie.css', rel: 'stylesheet'
-    """
-    expected: '<title>test</title>'
-
-  'yield':
-    template: "p \"This text could use \#{yield -> strong -> a href: '/', 'a link'}.\""
-    expected: '
This text could use a link.
'
-
-cc = require './src/coffeecup'
-render = cc.render
-
-@run = ->
-  {print} = require 'sys'
-  colors = {red: "\033[31m", redder: "\033[91m", green: "\033[32m", normal: "\033[0m"}
-  printc = (color, str) -> print colors[color] + str + colors.normal
-
-  [total, passed, failed, errors] = [0, [], [], []]
-
-  for name, test of tests
-    total++
-    try
-      if not test.params?
-        test.params =
-          optimize: true
-          cache: on unless total is 1
-      else
-        test.params.optimize = true
-        test.params.cache = true
-      test.original_params = JSON.stringify test.params
-
-      if test.run
-        test.run()
-      else
-        test.result = cc.render(test.template, test.params)
-        test.success = test.result is test.expected
-      if test.success
-        passed.push name
-        print "[Passed] #{name}\n"
-      else
-        failed.push name
-        printc 'red', "[Failed] #{name}\n"
-    catch ex
-      test.result = ex
-      errors.push name
-      printc 'redder', "[Error]  #{name}\n"
-
-  print "\n#{total} tests, #{passed.length} passed, #{failed.length} failed, #{errors.length} errors\n\n"
-
-  if failed.length > 0
-    printc 'red', "FAILED:\n\n"
-
-    for name in failed
-      t = tests[name]
-      print "- #{name}:\n"
-      print t.template + "\n"
-      print t.original_params + "\n" if t.params
-      printc 'green', t.expected + "\n"
-      printc 'red', t.result + "\n\n"
-
-  if errors.length > 0
-    printc 'redder', "ERRORS:\n\n"
-
-    for name in errors
-      t = tests[name]
-      print "- #{name}:\n"
-      print t.template + "\n"
-      printc 'green', t.expected + "\n"
-      printc 'redder', t.result.stack + "\n\n"
-
-@run()


newdatamodel

newdatamodel

--- src/js/bokeh.coffee
+++ src/js/bokeh.coffee
@@ -80,6 +80,15 @@

 class Range1d extends HasProperties
   type : 'Range1d'
+  defaults :
+    start : 0
+    end : 1
+
+class Range1ds extends Backbone.Collection
+  model : Range1d
+
+class DataRange1d extends Range1d
+  type : 'DataRange1d'
   defaults :
     start : 0
     end : 1
@@ -431,20 +440,40 @@
     'width', 'height', 'border_space']
   initialize : (attrs, options) ->
     super(attrs, options)
+    if 'xrange' not in attrs
+      @set('xrange', Collections['Range1d'].create({'start' : 0, 'end' : 1})
+    if 'yrange' not in attrs
+      @set('yrange', Collections['Range1d'].create({'start' : 0, 'end' : 1})
+    @register_property('width', ['xrange'], (xrange) ->
+          if xrange
+            return xrange.get('end') - xrange.get('start')
+          else
+            return 0
+      , false
+      , (width) ->
+        xrange = @get('xrange')
+        xrange.set('end', xrange.get('start') + width)
+    )
+    @register_property('height', ['xrange'], (yrange) ->
+          if yrange
+            return yrange.get('end') - yrange.get('start')
+          else
+            return 0
+      , false
+      , (height) ->
+        yrange = @get('xrange')
+        yrange.set('end', yrange.get('start') + height)
+    )
     @register_property('outerwidth', ['width', 'border_space'],
       () -> @get('width') + 2 * @get('border_space')
       false)
     @register_property('outerheight', ['height', 'border_space'],
       () -> @get('height') + 2 * @get('border_space')
       false)
-    @xrange = Collections['Range1d'].create({'start' : 0, 'end' : @get('height')})
-    @yrange = Collections['Range1d'].create({'start' : 0, 'end' : @get('width')})
-    @on('change:width', =>
-      @xrange.set('end', @get('width')))
-    @on('change:height', =>
-      @yrange.set('end', @get('height')))

 _.extend(Plot::defaults , {
+  'xrange' : null,
+  'yrange' : null,
   'data_sources' : {},
   'renderers' : [],
   'axes' : [],


docs(build options): tweak test option

docs(build options): tweak test option

--- docs/src/client/scripts/constants/build-opts-constant.coffee
+++ docs/src/client/scripts/constants/build-opts-constant.coffee
@@ -653,8 +653,9 @@
 	test:
 		opts: [
 			name: 'client.browsers'
-			define: "@type array of strings (browser names)"
-			description: [
+			define: "@type array of strings"
+			description: [
+				"Array of browser names."
 				"Phantomjs will run by default."
 			]
 		]


Fix container heights to support multiple instances better

--- src/jquery.superslides.coffee
+++ src/jquery.superslides.coffee
@@ -53,9 +53,10 @@
       margin: 0

     $(el).css
+      position: 'relative'
       overflowX: 'hidden'
       width: '100%'
-      height: '100%'
+      height: height

     $control.css
       position: 'relative'
@@ -81,7 +82,6 @@
   setupContainers = =>
     $control.css
       width: width * multiplier
-      height: height
       left: -width

     if @options.scrollable


modified

modified

--- src/jsonsina.coffee
+++ src/jsonsina.coffee
@@ -70,7 +70,7 @@

       if arr?.length > 0 # 必須這樣寫,不能簡化為: for each in arr?
         for each in arr
-          #each.date = each.day
+          each.date = each.day
           each.day = new Date each.day
           each.open = Number each.open
           each.low = Number each.low


Support showing POS frequencies

Support showing POS frequencies

--- app/assets/javascripts/rglossa/components/centre/results/results_toolbar.jsx.coffee
+++ app/assets/javascripts/rglossa/components/centre/results/results_toolbar.jsx.coffee
@@ -1,5 +1,6 @@
 #= require rglossa/utils
 #= require ./results_paginator
+#= require rglossa/models/corpus

 ###* @jsx React.DOM ###

@@ -49,6 +50,7 @@

Word forms

Lemmas

+
Parts-of-speech


Focus filter editor upon show

Focus filter editor upon show