Mis-referenced var

Mis-referenced var

--- src/coffee/dbHandler.coffee
+++ src/coffee/dbHandler.coffee
@@ -1,33 +1,33 @@
 fs = require 'fs'
 errorHandler = require './errorHandler.js'

-dbHandler =
+dbHandler =
    beginMigration: (credentials, filesToProcess) ->

       if !fs.existsSync __dirname+'/orms/'+credentials.type+'.js'
          process.stdout.write '\nDatabase Type: '.red + credentials.type + ' not supported.\n Supported types: mysql, pgsql\n'
          process.exit 1
-
+
       @currentFile = 0
       @files = filesToProcess
       @database = require __dirname+'/orms/'+credentials.type+'.js'

-      process.stdout.write '\nConnecting as '.green + credentials.username + ' ... '
+      process.stdout.write '\nConnecting as '.green + credentials.username + ' ... '

-      @database.connect credentials,
+      @database.connect credentials,
          (dbClient) ->
             # Success
             if credentials.schema
-                  dbHandler.database.setSchema dbClient, credentials.schema,
-                     (client) ->
-                        dbHandler.database.beginTransaction client
+                  dbHandler.database.setSchema dbClient, credentials.schema,
+                     (client) ->
+                        dbHandler.database.beginTransaction client
                            , ->
                               # Success
                               dbHandler.processFiles client
                            , errorHandler.handleDbError
-
+
             else
-               dbHandler.database.beginTransaction client
+               dbHandler.database.beginTransaction dbClient
                   , ->
                      # Success
                      dbHandler.processFiles dbClient
@@ -36,14 +36,14 @@
          , (err) ->
             # Failure
             errorHandler.onErr err
-
+
    processFiles: (dbClient) ->
       currFile = @files[@currentFile]

       process.stdout.write '\nProcessing file: '.green + currFile.yellow+' ...'.green
       sqlFile = fs.readFileSync(currFile).toString()
-
-      @database.processFile dbClient, sqlFile,
+
+      @database.processFile dbClient, sqlFile,
          (dbClient)->
             # Success
             ++dbHandler.currentFile;
@@ -54,16 +54,16 @@
          , (err, client, dbInterface) ->
             # Failure
             errorHandler.handleDbError err, client, dbInterface, currFile
-
+
    migrationComplete:
       (dbClient) ->
-         dbHandler.database.commit dbClient,
-            ->
+         dbHandler.database.commit dbClient,
+            ->
                # Success
                console.log '\nMigration Complete'.green
                process.exit 0
             , (err, client) ->
                # Failure
                errorHandler.handleDbError err, client
-
-module.exports = dbHandler+
+module.exports = dbHandler


Use product projections

Use product projections

--- src/coffee/pricesync.coffee
+++ src/coffee/pricesync.coffee
@@ -47,19 +47,17 @@
       throw new Error "Cannot find customer group '#{CUSTOMER_GROUP_SALE}' in master" unless masterCustomerGroup
       throw new Error "Cannot find customer group '#{CUSTOMER_GROUP_SALE}' in retailer" unless retailerCustomerGroup

-      @retailerClient.products
+      @retailerClient.productProjections
       .sort('id')
       .last("#{@fetchHours}h")
-      .where("masterData(published=\"true\")")
+      .where("published=\"true\"") # TODO: is staged=false enough?
       .perPage(100)
       .process (payload) =>
         retailerProductsBatch = payload.body.results

         Promise.map retailerProductsBatch, (retailerProduct) =>
           @logger?.debug retailerProduct, 'Processing retailer product'
-          current = retailerProduct.masterData.current
-          current.variants or= []
-          variants = [current.masterVariant].concat(current.variants)
+          variants = [retailerProduct.masterVariant].concat(retailerProduct.variants or [])

           Promise.map variants, (retailerVariant) =>
             @_processVariant retailerVariant, retailerCustomerGroup, masterCustomerGroup, retailerChannelInMaster, retailerProduct.id


Object.create doesn't work well with CustomEvent in Chrome 43

--- src/command-registry.coffee
+++ src/command-registry.coffee
@@ -175,11 +175,14 @@
   # * `commandName` {String} indicating the name of the command to dispatch.
   dispatch: (target, commandName, detail) ->
     event = new CustomEvent(commandName, {bubbles: true, detail})
-    eventWithTarget = Object.create event,
+    eventWithTarget = Object.create {},
       target: value: target
       preventDefault: value: ->
       stopPropagation: value: ->
       stopImmediatePropagation: value: ->
+    # In Chrome 43, Object.create doesn't work well with CustomEvent.
+    for k, v of event when k not in eventWithTarget
+      eventWithTarget[k] = v
     @handleCommandEvent(eventWithTarget)

   # Public: Invoke the given callback before dispatching a command event.
@@ -214,7 +217,7 @@
     matched = false
     currentTarget = originalEvent.target

-    syntheticEvent = Object.create originalEvent,
+    syntheticEvent = Object.create {},
       eventPhase: value: Event.BUBBLING_PHASE
       currentTarget: get: -> currentTarget
       preventDefault: value: ->
@@ -228,6 +231,9 @@
         immediatePropagationStopped = true
       abortKeyBinding: value: ->
         originalEvent.abortKeyBinding?()
+    # In Chrome 43, Object.create doesn't work well with CustomEvent.
+    for k, v of originalEvent when k not in syntheticEvent
+      syntheticEvent[k] = v

     @emitter.emit 'will-dispatch', syntheticEvent


Only trigger the toggle event once

Only trigger the toggle event once

--- src/scripts/modules/media/body/body.coffee
+++ src/scripts/modules/media/body/body.coffee
@@ -40,8 +40,8 @@

     events:
       'click a': 'changePage'
-      'click [data-type="solution"] > .ui-toggle-wrapper > .ui-toggle': 'toggleSolution'
-      'click .solution > .ui-toggle-wrapper > .ui-toggle': 'toggleSolution'
+      'click [data-type="solution"] > .ui-toggle-wrapper > .ui-toggle,
+        .solution > .ui-toggle-wrapper > .ui-toggle': 'toggleSolution'
       'keydown .media-body': 'checkKeySequence'
       'keyup .media-body': 'resetKeySequence'

@@ -384,7 +384,7 @@
         @parent.scrollToTop()

     toggleSolution: (e) ->
-      $solution = $(e.currentTarget).closest('.solution, [data-type="solution"]').eq(0)
+      $solution = $(e.currentTarget).closest('.solution, [data-type="solution"]')
       $solution.toggleClass('ui-solution-visible')

     onEditable: () -> @$el.find('.media-body').addClass('draft')


split lines

split lines

--- src/scripts/pages/contents/contents.coffee
+++ src/scripts/pages/contents/contents.coffee
@@ -45,7 +45,8 @@
             # Check for updates on the content as well as the current Page (if it exists)
             promises = [view.model.fetch({skipDownloads:true, doNotRerender:true})]
             page = view.model.asPage()
-            promises.push(page.fetch({skipDownloads:true, doNotRerender:true})) if view.model isnt page and page.isDraft()
+            if view.model isnt page and page.isDraft()
+              promises.push(page.fetch({skipDownloads:true, doNotRerender:true}))

             $.when(promises)
             .then () =>


feat(timer): add more events handling

feat(timer): add more events handling

--- src/common/countdown.coffee
+++ src/common/countdown.coffee
@@ -8,16 +8,28 @@
     start = $attrs.start
     interval = undefined

-    $scope.countdown = start
+    # Main method of the directive, start the countdown using $interval
+    startCountdown = ->
+      $scope.countdown = start

-    tick = ->
-      $scope.countdown = $scope.countdown - 1
-      if $scope.countdown == 0
-        $interval.cancel interval
-        $rootScope.$broadcast 'countdown:over'
+      tick = ->
+        $scope.countdown = $scope.countdown - 1
+        if $scope.countdown == 0
+          $interval.cancel interval
+          $rootScope.$broadcast 'countdown:over'

-    interval = $interval(tick, 1000)
+      interval = $interval(tick, 1000)

+    # Called from a controller to restart the countdown
+    $scope.$on 'countdown:restart', ->
+      $interval.cancel interval
+      startCountdown()
+
+    # Called from a controller to stop the countdown
+    $scope.$on 'countdown:stop', ->
+      $interval.cancel interval
+
+    startCountdown()
   ]

 ]


More console.log

More console.log

--- _source/lib/routes/server/api.coffee
+++ _source/lib/routes/server/api.coffee
@@ -1,5 +1,6 @@
 _jsonContentType = "application/JSON"
 _authenticationError = 401
+_unexpectedError = 500

 ###

@@ -21,6 +22,8 @@
   sentToken = @.requestHeaders[Apollos.api.tokenName]

   for name, details of Apollos.api.platforms
+    console.log "Testing request against registered platform:", details
+
     token = details.token
     collections = details.collections

@@ -103,27 +106,43 @@
   method[url] =

     post: (data) ->
-      Apollos.debug "Got POST #{url} id=#{@.params.id}"
-      platform = authenticatePlatform.call @, collection
+      try
+        Apollos.debug "Got POST #{url} id=#{@.params.id}"
+        platform = authenticatePlatform.call @, collection

-      if platform
-        Apollos.debug "POST authenticated from #{platform.name}"
+        if platform
+          Apollos.debug "POST authenticated from #{platform.name}"

-      if not platform
-        Apollos.debug "Dropping request because of unknown platform"
-        return
+        if not platform
+          Apollos.debug "Dropping request because of unknown platform"
+          return

-      return upsertResource.call @, data, Apollos[singular].update, platform.name
+        handler = Apollos[singular].update
+        return upsertResource.call @, data, handler, platform.name
+
+      catch error
+        @.setStatusCode _unexpectedError
+        Apollos.debug "EXCEPTION from POST handling:"
+        Apollos.debug error
+        return error

     delete: (data) ->
-      Apollos.debug "Got DELETE for #{url} id=#{@.params.id}"
-      platform = authenticatePlatform.call @, collection
+      try
+        Apollos.debug "Got DELETE for #{url} id=#{@.params.id}"
+        platform = authenticatePlatform.call @, collection

-      if not platform
-        Apollos.debug "Dropping request because of unknown platform"
-        return
+        if not platform
+          Apollos.debug "Dropping request because of unknown platform"
+          return

-      return deleteResource.call @, Apollos[singular].delete, platform.name
+        handler = Apollos[singular].delete
+        return deleteResource.call @, handler, platform.name
+
+      catch error
+        @.setStatusCode _unexpectedError
+        Apollos.debug "EXCEPTION from DELETE handling:"
+        Apollos.debug error
+        return error

   HTTP.methods method
   return url
@@ -158,6 +177,7 @@
   if not token
     Apollos.debug "Cannot add #{name} to API as platform because there is no
       token assigned in the Meteor settings"
+    return

   Apollos.api.platforms[name] =
     token: token


:lipstick:

:lipstick:

--- spec/module-cache-spec.coffee
+++ spec/module-cache-spec.coffee
@@ -46,7 +46,7 @@

     indexPath = path.join(packagePath, 'index.js')
     fs.writeFileSync indexPath, """
-      exports.load = function() {require('underscore-plus');};
+      exports.load = function() { require('underscore-plus'); };
     """

     packageMain = require(indexPath)


fix for travisCI test failure

fix for travisCI test failure

--- spec/src/debugger-spec.coffee
+++ spec/src/debugger-spec.coffee
@@ -22,7 +22,7 @@

       atomStub =
         project:
-          resolvePath: (file) -> '/path/to/' + file
+          resolvePath: (file) -> '/path/to/file.js'
         workspace:
           getActiveTextEditor: ->
             getPath: -> '/path/to/file.js'


remove custom row transform

remove custom row transform

--- src/config/inspector.coffee
+++ src/config/inspector.coffee
@@ -30,12 +30,8 @@
   _AUTHORITY: {
     fill: my.color.row
     scale: my.row.size
-    transform: (world, args) ->
-      scale = world.get('scale')
-      margin = world.get('margin')
-      x = margin
-      y = world.index * scale + margin
-      "translate(#{x},#{y})" # TODO: Refactor
+    x: my.margin
+    y: (world, args) -> world.index * world.get('scale') + my.margin
     height: (world) -> world.get('scale') - 2 * world.get('margin')
     width: (world) -> world.get('size') - 2 * world.get('margin')
     name_style: (world) ->


adjust iron form

adjust iron form

--- src/webcomponents/cmi-form/cmi-form.coffee
+++ src/webcomponents/cmi-form/cmi-form.coffee
@@ -7,49 +7,181 @@
   extends: 'form'

   properties:
-    contentType:
-      type: String
-      value: "application/x-www-form-urlencoded"
-    disableNativeValidationUi:
-      type: Boolean
-      value: false
-    withCredentials:
-      type: Boolean
-      value: false
+
+    ###*
+    * Content type to use when sending data. If the `contentType` property
+    * is set and a `Content-Type` header is specified in the `headers`
+    * property, the `headers` property value will take precedence.
+    * If Content-Type is set to a value listed below, then
+    * the `body` (typically used with POST requests) will be encoded accordingly.
+    *
+    *    * `content-type="application/json"`
+    *      * body is encoded like `{"foo":"bar baz","x":1}`
+    *    * `content-type="application/x-www-form-urlencoded"`
+    *      * body is encoded like `foo=bar+baz&x=1`
+    ###
+    contentType: { type: String, value: "application/x-www-form-urlencoded" }
+
+    ###*
+    * By default, the form will display the browser's native validation
+    * UI (i.e. popup bubbles and invalid styles on invalid fields). You can
+    * manually disable this; however, if you do, note that you will have to
+    * manually style invalid *native* HTML fields yourself, as you are
+    * explicitly preventing the native form from doing so.
+    ###
+    disableNativeValidationUi: { type: Boolean, value: false }
+
+    ###*
+    * Set the withCredentials flag when sending data.
+    ###
+    withCredentials: { type: Boolean, value: false }
+
+    ###*
+    * HTTP request headers to send.
+    *
+    * Note: setting a `Content-Type` header here will override the value
+    * specified by the `contentType` property of this element.
+    ###
+    headers:
+      type: Object
+      value: -> {}
+
+    ###*
+    * iron-ajax request object used to submit the form.
+    ###
+    request: { type: Object }
+
+  ###*
+  * Fired if the form cannot be submitted because it's invalid.
+  *
+  * @event iron-form-invalid
+  ###
+
+  ###*
+  * Fired if the form cannot be submitted because it's invalid.
+  *
+  * @event cmi-form-invalid
+  ###
+
+  ###*
+  * Fired before the form is submitted.
+  *
+  * @event iron-form-presubmit
+  ###
+
+  ###*
+  * Fired before the form is submitted.
+  *
+  * @event cmi-form-presubmit
+  ###
+
+  ###*
+  * Fired after the form is submitted.
+  *
+  * @event iron-form-submit
+  ###
+
+  ###*
+  * Fired after the form is submitted.
+  *
+  * @event cmi-form-submit
+  ###
+
+  ###*
+  * Fired after the form is reset.
+  *
+  * @event iron-form-reset
+  ###
+
+  ###*
+  * Fired after the form is reset.
+  *
+  * @event cmi-form-reset
+  ###
+
+  ###*
+  * Fired after the form is submitted and a response is received. An
+  * IronRequestElement is included as the event.detail object.
+  *
+  * @event iron-form-response
+  ###
+
+  ###*
+  * Fired after the form is submitted and a response is received. An
+  * IronRequestElement is included as the event.detail object.
+  *
+  * @event cmi-form-response
+  ###
+
+  ###*
+  * Fired after the form is submitted and an error is received. An
+  * IronRequestElement is included as the event.detail object.
+  *
+  * @event iron-form-error
+  ###
+
+  ###*
+  * Fired after the form is submitted and an error is received. An
+  * IronRequestElement is included as the event.detail object.
+  *
+  * @event cmi-form-error
+  ###

   listeners:
-    'iron-form-element-register': '_registerElement'
+    'iron-form-element-register':   '_registerElement'
     'iron-form-element-unregister': '_unregisterElement'
-    'submit': '_onSubmit'
+    'cmi-form-element-register':    '_registerElement'
+    'cmi-form-element-unregister':  '_unregisterElement'
+    'submit':                       '_onSubmit'
+    'reset':                        '_onReset'

   ready: ->
     @_requestBot = document.createElement('iron-ajax')
     @_requestBot.addEventListener('response', @_handleFormResponse.bind(@))
     @_requestBot.addEventListener('error', @_handleFormError.bind(@))

-    @_customElements = []
-
+    @_customElements              = []
+    @_customElementsInitialValues = []
+
+  ###*
+  * Submits the form
+  ###
   submit: ->
     if !@noValidate && !@validate()
       @_doFakeSubmitForValidation() if !@disableNativeValidationUi
       @fire('iron-form-invalid')
+      @fire('cmi-form-invalid')
       return

     json = @serialize()

-    @_requestBot.url = @action
-    @_requestBot.method = @method.toUpperCase()
-    @_requestBot.contentType = @contentType
-    @_requestBot.withCredentials = @withCredentials
+    # Native forms can also index elements magically by their name (can't make
+    # this up if I tried) so we need to get the correct attributes, not the
+    # elements with those names.
+    @request.url              = @getAttribute('action')
+    @request.method           = @getAttribute('method')
+    @request.contentType      = @contentType
+    @request.withCredentials  = @withCredentials
+    @request.headers          = @headers

     if @method.toUpperCase() == 'POST'
-      @_requestBot.body = json
+      @request.body = json
     else
-      @_requestBot.params = json
-
-    @_requestBot.generateRequest()
-    @fire('iron-form-submit', json)
-
+      @request.params = json
+
+    # Allow for a presubmit hook
+    event = @fire('iron-form-presubmit', {}, { cancelable: true })
+    event = @fire('cmi-form-presubmit', {}, { cancelable: true })
+
+    if !event.defaultPrevented
+      @request.generateRequest()
+      @fire('iron-form-submit', json)
+
+  ###*
+  * Handler that is called when the native form fires a `submit` event
+  *
+  * @param {Event} event A `submit` event.
+  ###
   _onSubmit: (event) ->
     @submit()

@@ -57,34 +189,75 @@

     return false

+  ###*
+  * Handler that is called when the native form fires a `reset` event
+  *
+  * @param {Event} event A `reset` event.
+  ###
+  _onReset: (event) ->
+    @_resetCustomElements()
+
+  ###*
+  * Returns a json object containing name/value pairs for all the registered
+  * custom components and native elements of the form. If there are elements
+  * with duplicate names, then their values will get aggregated into an
+  * array of values.
+  *
+  * @return {!Object}
+  ###
   serialize: ->
     json = {}

-    addSerializedElement = (el) =>
-      if !json[el.name]
-        json[el.name] = el.value
+    addSerializedElement = (name, value) =>
+      # If the name doesn't exist, add it. Otherwise, serialize it to
+      # an array,
+      if !json[name]
+        json[name] = value
       else
-        json[el.name] = [json[el.name]] if !Array.isArray(json[el.name])
-        json[el.name].push(el.value)
-
+        json[name] = [json[name]] if !Array.isArray(json[name])
+        json[name].push(value)
+
+    # Go through all of the registered custom components.
     for el in @_customElements
-      addSerializedElement(el) if @_useValue(el)
-
+      addSerializedElement(el.name, el.value) if @_useValue(el)
+
+    # Also go through the form's native elements.
     for el in @elements
+      # Checkboxes and radio buttons should only use their value if they're checked.
+      # Also, custom elements that extend native elements (like an
+      # `<input is="fancy-input">`) will appear in both lists. Since they
+      # were already added as a custom element, they don't need
+      # to be re-added.
       continue if !@_useValue(el) || (el.hasAttribute('is') && json[el.name])
-      addSerializedElement(el)
+
+      if el.tagName.toLowerCase() == 'select' && el.multiple
+        # A <select multiple> has an array of values.
+        for option in el.options
+          addSerializedElement(el.name, option.value) if option.selected
+
+      else
+        addSerializedElement(el.name, el.value)

     return json

   _handleFormResponse: (event) ->
-    @fire('iron-form-response', event.detail.response)
+    @fire('iron-form-response', event.detail)
+    @fire('cmi-form-response', event.detail)

   _handleFormError: (event) ->
     @fire('iron-form-error', event.detail)
+    @fire('cmi-form-error', event.detail)

   _registerElement: (e) ->
-    e.target._parentForm = @
-    @_customElements.push(e.target)
+    element = e.target
+    element._parentForm = @
+    @_customElements.push(element)
+
+    # Save the original value of this input.
+    if @_usesCheckedInsteadOfValue(element)
+      @_customElementsInitialValues.push(element.checked)
+    else
+      @_customElementsInitialValues.push(element.value)

   _unregisterElement: (e) ->
     target = e.detail.target
@@ -92,34 +265,58 @@
     if target
       index = @_customElements.indexOf(target)

-      @_customElements.splice(index, 1) if index > -1
-
+      if index > -1
+        @_customElements.splice(index, 1)
+        @_customElementsInitialValues.splice(index, 1)
+
+  ###*
+  * Validates all the required elements (custom and native) in the form.
+  * @return {boolean} True if all the elements are valid.
+  ###
   validate: ->
     valid = true

+    # Validate all the custom elements.
     for el in @_customElements
-      if el.required && @_useValue(el)
-        validatable = (el)
-
+      if el.required && !el.disabled
+        validatable = (el) # @type {{validate: (function() : boolean)}}
+
+        # Some elements may not have correctly defined a validate method.
         valid = !!validatable.validate() && valid if validatable.validate

+    # Validate the form's native elements.
     for el in @elements
-      if !el.hasAttribute('is') && el.willValidate && el.checkValidity && el.name
+      # Custom elements that extend a native element will also appear in
+      # this list, but they've already been validated.
+      if !el.hasAttribute('is') && el.willValidate && el.checkValidity
         valid = el.checkValidity() && valid

     return valid

+  ###*
+  * Returns whether the given element is a radio-button or a checkbox.
+  * @return {boolean} True if the element has a `checked` property.
+  ###
+  _usesCheckedInsteadOfValue: (el) ->
+    return true if el.type == 'checkbox' || el.type == 'radio'
+    return true if el.getAttribute('role') == 'checkbox'
+    return true if el.getAttribute('role') == 'radio'
+    return true if el['_hasIronCheckedElementBehavior']
+
+    return false
+
   _useValue: (el) ->
+    # Skip disabled elements or elements that don't have a `name` attribute.
     return false if el.disabled || !el.name

-    if el.type == 'checkbox' || el.getAttribute('role') == 'checkbox'
+    # Checkboxes and radio buttons should only use their value if they're
+    # checked. Custom paper-checkbox and paper-radio-button elements
+    # don't have a type, but they have the correct role set.
+    if @_usesCheckedInsteadOfValue(el)
       if el.boolean == true
         return true
       else
         return el.checked
-
-    if el.type == 'radio' || el.getAttribute('role') == 'radio'
-      return el.checked

     return true

@@ -133,5 +330,28 @@

     @removeChild(fakeSubmit)

-
-
+  ###*
+  * Resets all non-disabled form custom elements to their initial values.
+  ###
+  _resetCustomElements: () ->
+    # Reset all the registered custom components. We need to do this after
+    # the native reset, since programmatically changing the `value` of some
+    # native elements (iron-input in particular) does not notify its
+    # parent `paper-input`, which will now display the wrong value.
+    @this.async ->
+      for el, i in @_customElements
+        continue if el.disabled
+
+        if @_usesCheckedInsteadOfValue(el)
+          el.checked = @_customElementsInitialValues[i]
+        else
+          el.value = @_customElementsInitialValues[i]
+
+        el.invalid = false
+
+      @fire('iron-form-reset')
+      @fire('cmi-form-reset')
+    , 1
+
+
+


Add Object.assign polyfill.

Add Object.assign polyfill.

--- engine/init.coffee
+++ engine/init.coffee
@@ -1,3 +1,34 @@
+```
+if (typeof Object.assign != 'function') {
+  // Must be writable: true, enumerable: false, configurable: true
+  Object.defineProperty(Object, "assign", {
+    value: function assign(target, varArgs) { // .length of function is 2
+      'use strict';
+      if (target == null) { // TypeError if undefined or null
+        throw new TypeError('Cannot convert undefined or null to object');
+      }
+
+      var to = Object(target);
+
+      for (var index = 1; index < arguments.length; index++) {
+        var nextSource = arguments[index];
+
+        if (nextSource != null) { // Skip over if undefined or null
+          for (var nextKey in nextSource) {
+            // Avoid bugs when hasOwnProperty is shadowed
+            if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) {
+              to[nextKey] = nextSource[nextKey];
+            }
+          }
+        }
+      }
+      return to;
+    },
+    writable: true,
+    configurable: true
+  });
+}
+```

 if not window.fetch?
     require 'whatwg-fetch'


properly handling mouseleave events for resize tool

--- static/coffee/tools/eventgenerators.coffee
+++ static/coffee/tools/eventgenerators.coffee
@@ -83,7 +83,7 @@
         @_stop_drag(e)
         return false)
     @plotview.canvas_wrapper.bind('mouseleave', (e) =>
-      if @button_activated and e.target == this
+      if @button_activated
         @_stop_drag(e)
         return false)

@@ -158,15 +158,15 @@
       if e.keyCode == 27
         eventSink.trigger("clear_active_tool"))

-    @mouseover_count = 0
-    #waiting 500 ms and testing mouseover countmakes sure that
-    #mouseouts that occur because of going over element borders don't
-    #trigger the mouseout
-    @plotview.$el.bind("mouseout", (e) =>
-      @mouseover_count -=1
-      _.delay((=>
-        if @mouseover_count == 0
-          eventSink.trigger("clear_active_tool")), 500))
+    # @mouseover_count = 0
+    # #waiting 500 ms and testing mouseover countmakes sure that
+    # #mouseouts that occur because of going over element borders don't
+    # #trigger the mouseout
+    # @plotview.$el.bind("mouseout", (e) =>
+    #   @mouseover_count -=1
+    #   _.delay((=>
+    #     if @mouseover_count == 0
+    #       eventSink.trigger("clear_active_tool")), 500))

     @plotview.$el.bind("mousein", (e) =>
       eventSink.trigger("clear_active_tool"))
@@ -231,15 +231,15 @@
       if e.keyCode == 27
         eventSink.trigger("clear_active_tool"))

-    @mouseover_count = 0
-    #waiting 500 ms and testing mouseover countmakes sure that
-    #mouseouts that occur because of going over element borders don't
-    #trigger the mouseout
-    @plotview.$el.bind("mouseout", (e) =>
-      @mouseover_count -=1
-      _.delay((=>
-        if @mouseover_count == 0
-          eventSink.trigger("clear_active_tool")), 500))
+    # @mouseover_count = 0
+    # #waiting 500 ms and testing mouseover countmakes sure that
+    # #mouseouts that occur because of going over element borders don't
+    # #trigger the mouseout
+    # @plotview.$el.bind("mouseout", (e) =>
+    #   @mouseover_count -=1
+    #   _.delay((=>
+    #     if @mouseover_count == 0
+    #       eventSink.trigger("clear_active_tool")), 500))

     @plotview.$el.bind("mouseover", (e) =>
       @mouseover_count += 1)


Add comments in the code

Add comments in the code

--- src/data/base-entity.coffee
+++ src/data/base-entity.coffee
@@ -79,6 +79,10 @@

 			defer.promise
 		typedKeys: () ->
+# 		Disable caching when looking up providers
+# 		If the cache is not disabled then 'Platform Settings' section of OAuth.io
+# 		which is used to configure OAuth server will not work,
+# 		i.e., settings are saved by cannot be retrieved properly unless the application is redeployed
 #			if @constructor._cachedTypedKeys
 #				return Q(@constructor._cachedTypedKeys)
 			defer = Q.defer()


Updated copyright year

Updated copyright year

--- ackee.coffee
+++ ackee.coffee
@@ -2,7 +2,7 @@
 @name			Ackee
 @description	This script is a wrapper to easily run Ackee
 @author			Tobias Reich
-@copyright		2014 by Tobias Reich
+@copyright		2015 by Tobias Reich
 ###

 init = require('./node/init')()


Fix api doc literals.

Fix api doc literals.

--- src/date/date-helper.coffee
+++ src/date/date-helper.coffee
@@ -15,12 +15,21 @@
 ###
 DateHelper =
   ###*
+   * @method isDate
+   * @param value the value to check
+   * @return whether the value is a javascript `Date` or
+   *   moment object
+  ###
+  isDate: (value) ->
+    moment.isMoment(value) or moment.isDate(value)
+
+  ###*
    * Converts the input to a moment. The input can be a date string,
-   * e.g. '07 Sep 1986', or the number of milliseconds since
+   * e.g. `07 Sep 1986`, or the number of milliseconds since
    * the Unix Epoch (Jan 1 1970 12AM UTC).
    *
    * A string input must contain the date in the format
-   * DD MMM YYYY, e.g. 'Tue, 03 Feb 2012 00:00:00 GMT'.
+   * DD MMM YYYY, e.g. `Tue, 03 Feb 2012 00:00:00 GMT`.
    * Otherwise, an error is thrown. Note that the time in
    * the example input is ignored.
    *


allow navigation on squat map via url hash

--- app/assets/javascripts/squats.coffee
+++ app/assets/javascripts/squats.coffee
@@ -25,17 +25,24 @@

 popup = L.popup()

-openSquatFromHash = ->
+opening_popup = false
+$(window).on "hashchange", ->
   slug = document.location.hash.replace '#', ''
-  for squat in squats when squat.slug == slug
-    popup.setLatLng squat.latlng
-    popup.setContent squat.popup
-    popup.openOn map
-    map.panTo squat.latlng
-
-$(window).on "hashchange", openSquatFromHash
+  if slug.length == 0
+    map.closePopup()
+  if slug.length > 0
+    for squat in squats when squat.slug == slug
+      popup.setLatLng squat.latlng
+      popup.setContent squat.popup
+      opening_popup = true
+      popup.openOn map
+      opening_popup = false

 $(->
+  map.on "popupclose", (e) ->
+    if !opening_popup
+      document.location.hash = ""
+
   squats_url = $("#map").data("squats-url")
   if squats_url
     $.ajax(squats_url).success (squats) ->


Minor fix to last while switching views

Minor fix to last while switching views

--- app/assets/volume/spreadsheet.coffee
+++ app/assets/volume/spreadsheet.coffee
@@ -580,7 +580,7 @@
           fill()
           if Rows.length > Order.length
             for r in Rows.splice(Order.length)
-              TBody.removeChild(r)
+              TBody.removeChild(r) if r.parentNode
           return

         ################################# Place DOM elements


Added top position options.

Added top position options.

--- src/directives/modal.coffee
+++ src/directives/modal.coffee
@@ -29,7 +29,7 @@

         # Update opened modal object
         @opened = {id, element, options}
-        @resize element
+        @resize @opened

         options.callback?()

@@ -39,8 +39,11 @@
       else
         @waiting = {id, options}

-    resize: (element) ->
-      return unless element?
+    resize: (modalObject) ->
+      return unless modalObject?
+
+      element = modalObject.element
+      options = modalObject.options

       modal  = $(".modal", element).attr "style", ""
       height = modal.outerHeight()
@@ -48,7 +51,7 @@
         if $(window).height() > height
           marginTop: -height / 2
         else
-          top: 20
+          top: options.topOffset
       )

     hide: (callback) ->
@@ -110,6 +113,7 @@
         overlayClose: false
         resize:       true
         open:         null
+        topOffset:    20

       opts = util.extendAttributes "", defaults, attrs

@@ -122,7 +126,7 @@
       $scope.escapeKeyHandler = (event) ->
         modal.hide() if event.which is keys.ESCAPE

-      $scope.resizeHandler =  (event) -> modal.resize element
+      $scope.resizeHandler =  (event) -> modal.resize modal.opened
       $scope.overlayHandler = (event) -> $scope.closeModal()

       $scope.bindingEvents = (action = "bind") ->


Removed an unnecessary console log

Removed an unnecessary console log

--- app/views/core/SubscribeModal.coffee
+++ app/views/core/SubscribeModal.coffee
@@ -48,7 +48,6 @@
     popoverTitle = $.i18n.t 'subscribe.parent_email_title'
     popoverTitle += '<button type="button" class="close" onclick="$('.parent-button').popover('hide');">×</button>'
     popoverContent = ->
-      console.log 'found html', $('.parent-button-popover-content').html()
       $('.parent-button-popover-content').html()
     @$el.find('.parent-button').popover(
       animation: true


KD.extend: enableLogs issues fixed, and now we have disableLogs too. Also we stopped to suspending console.* so PLEASE use log, error etc. global ones. And if you set enableLogs cookie as true you can force to see logs even on prod.

--- client/Main/KD.extend.coffee
+++ client/Main/KD.extend.coffee
@@ -106,20 +106,22 @@

   resetAppScripts    :-> @appScripts = {}

-  enableLogs:do->
-    oldConsole = window.console
-    window.console = {}
-    console[method] = noop  for method in ['log','warn','error','trace','time','timeEnd']
-
-    enableLogs =->
-      window.console = oldConsole
-      KD.log     = log     = if console?.log     then console.log.bind(console)     else noop
-      KD.warn    = warn    = if console?.warn    then console.warn.bind(console)    else noop
-      KD.error   = error   = if console?.error   then console.error.bind(console)   else noop
-      KD.time    = time    = if console?.time    then console.time.bind(console)    else noop
-      KD.timeEnd = timeEnd = if console?.timeEnd then console.timeEnd.bind(console) else noop
-      KD.logsEnabled = yes
-      return "Logs are enabled now."
+  disableLogs:->
+    for method in ['log','warn','error','trace','time','timeEnd']
+      window[method] = noop
+      KD[method]     = noop
+    delete KD.logsEnabled
+    return "Logs are disabled now."
+
+  enableLogs:(state = yes)->
+    return KD.disableLogs()  unless state
+    KD.log     = window.log     = console.log.bind     console
+    KD.warn    = window.warn    = console.warn.bind    console
+    KD.error   = window.error   = console.error.bind   console
+    KD.time    = window.time    = console.time.bind    console
+    KD.timeEnd = window.timeEnd = console.timeEnd.bind console
+    KD.logsEnabled = yes
+    return "Logs are enabled now."

   impersonate : (username)->
     KD.remote.api.JAccount.impersonate username, (err)->
@@ -284,4 +286,4 @@
   get:->
     if KD.isGuest() then 'guests' else 'koding'

-KD.enableLogs() if not KD.config?.suppressLogs+KD.enableLogs ($.cookie 'enableLogs') or !KD.config?.suppressLogs


members will be able to see build logs page

--- client/home/lib/index.coffee
+++ client/home/lib/index.coffee
@@ -24,7 +24,7 @@
     { title : 'Payment History', viewClass : HomePaymentHistory }
     { title : 'Koding Utilities', viewClass : HomeUtilities, role: 'member' }
     { title : 'My Account', viewClass : HomeAccount, role: 'member' }
-    { title : 'Build Logs', viewClass : HomeBuildLogs }
+    { title : 'Build Logs', viewClass : HomeBuildLogs, role: 'member' }
   ]


Crap removal

Crap removal

--- client/modules/errors.coffee
+++ client/modules/errors.coffee
@@ -46,10 +46,6 @@
 	output

 exports.stack = (error) ->
-	try
-		throw new Error
-	catch error
-		console.log error.stack
 	formatStack = error.stack
 	formatStack = if formatStack?
 		formatStack = formatStack.split '\n'


Better feedback on problems in inventory updater.

--- src/coffee/updater/inventory-updater.coffee
+++ src/coffee/updater/inventory-updater.coffee
@@ -53,7 +53,8 @@
           else if response.statusCode is 201
             deferred.resolve body
           else
-            deferred.reject 'Error on creating channel: ' + body
+            humanReadable = JSON.stringify body, null, 2
+            deferred.reject 'Problem on creating channel: ' + humanReadable
     deferred.promise

   allInventoryEntries: (rest, queryString) ->
@@ -65,7 +66,8 @@
         if response.statusCode is 200
           deferred.resolve body.results
         else
-          deferred.reject "Error on fetching all inventory entries: #{body}"
+          humanReadable = JSON.stringify body, null, 2
+          deferred.reject "Problem on fetching all inventory entries: #{humanReadable}"
     , (progress) -> deferred.notify progress
     deferred.promise

@@ -116,7 +118,8 @@
         else if response.statusCode is 304
           deferred.resolve 'Inventory entry update not neccessary.'
         else
-          deferred.reject 'Error on updating existing inventory entry: ' + body
+          humanReadable = JSON.stringify body, null, 2
+          deferred.reject 'Problem on updating existing inventory entry: ' + humanReadable
     deferred.promise

   create: (stock) ->
@@ -129,7 +132,8 @@
         if response.statusCode is 201
           deferred.resolve 'New inventory entry created.'
         else
-          deferred.reject 'Error on creating new inventory entry: ' + body
+          humanReadable = JSON.stringify body, null, 2
+          deferred.reject 'Problem on creating new inventory entry: ' + humanReadable
     deferred.promise

 ###


auto load all scripts if "*" is passed in the array

--- index.coffee
+++ index.coffee
@@ -6,7 +6,7 @@
   fs.exists scriptsPath, (exists) ->
     if exists
       for script in fs.readdirSync(scriptsPath)
-        if scripts?
+        if scripts? and '*' not in scripts
           robot.loadFile(scriptsPath, script) if script in scripts
         else
           robot.loadFile(scriptsPath, script)


more integration specs

more integration specs

--- test/specs/integration/migrate_test.coffee
+++ test/specs/integration/migrate_test.coffee
@@ -118,6 +118,14 @@
           CustomAssertions.assertTableColumnCount(@AdapterWrapper, @tableName, 2, done)
         )

+    context 'when an invalid targetVersion is given', ->
+      it 'should throw an error', (done)->
+        @tableName = 'many_migrations'
+        migrateScope(@adapter, migrationsPath, @tableName, 2220141, (err, versions)=>
+          assert.equal(err, 'Unknown migration version error 2220141')
+          done()
+        )
+
   describe 'db:rollback', ->
     context 'default behavior', ->
       it 'should be able rollback one migration', (done)->
@@ -143,3 +151,20 @@
           assert.equal(versions.length, 4)
           CustomAssertions.assertTableColumnCount(@AdapterWrapper, @tableName, 2, done)
         )
+
+    context 'when a valid steps argument is given', ->
+      it 'should be able rollback two steps', (done)->
+        @tableName = 'many_migrations'
+        rollbackScope(@adapter, migrationsPath, @tableName, 2, (err, versions)=>
+          return done(err) if err
+          assert.equal(versions.length, 3)
+          CustomAssertions.assertTableColumnCount(@AdapterWrapper, @tableName, 3, done)
+        )
+
+      it 'should be able rollback many steps', (done)->
+        @tableName = 'many_migrations'
+        rollbackScope(@adapter, migrationsPath, @tableName, 4, (err, versions)=>
+          return done(err) if err
+          assert.equal(versions.length, 1)
+          CustomAssertions.assertTableColumnCount(@AdapterWrapper, @tableName, 3, done)
+        )


add alt and title text for bars correct and incorrect percent

--- src/components/task-plan/reading-stats.cjsx
+++ src/components/task-plan/reading-stats.cjsx
@@ -47,7 +47,9 @@
                 bsStyle={bsStyles[correctOrIncorrect]}
                 label='%(percent)s%'
                 now={percent}
-                key="page-progress-#{type}-#{data.id}-#{correctOrIncorrect}" />
+                key="page-progress-#{type}-#{data.id}-#{correctOrIncorrect}"
+                title="#{percent}% #{correctOrIncorrect}"
+                alt="#{percent}% #{correctOrIncorrect}"/>

   renderPercentBars: (data, type) ->
     percents =


more test data

more test data


Use interpolated string for setting left offset

--- src/extensions/wrap-guide/wrap-guide.coffee
+++ src/extensions/wrap-guide/wrap-guide.coffee
@@ -26,5 +26,4 @@
     @rootView.on 'font-size-change', => @updateGuide(@editor)

   updateGuide: (editor) ->
-    width = editor.charWidth * @column
-    @css("left", width + "px")
+    @css('left', "#{editor.charWidth * @column}px")


feat($build): Verboser logging

feat($build): Verboser logging

--- lib/grunt/utils.coffee
+++ lib/grunt/utils.coffee
@@ -50,6 +50,7 @@
   concat: (formulae) ->
     src = ''
     _.map(formulae, (f) ->
+      grunt.log.writeln "File added: " + f
       src = grunt.file.read f
       src
     ).join(grunt.util.linefeed)
@@ -89,6 +90,8 @@
       _.each formulae, (mod) ->
         if mod in _.keys(includes)
           includes[mod] = true
+        else
+            grunt.log.error "Module: #{mod} not found in modules"
     else if _.isObject formulae
       includes = _.defaults includes, formulae


Update gulp_bridge.coffee

Update gulp_bridge.coffee

--- lib/gulp_bridge.coffee
+++ lib/gulp_bridge.coffee
@@ -21,8 +21,8 @@
       if vinyl.isNull()
         done()
         return
-
-      // make path separators consistent
+
+      # make path separators consistent
       filename = vinyl.relative.replace(/\\/g, "/")

       if utf8(vinyl.contents)


Reini done var when settings is closed

Reini done var when settings is closed

--- lib/preferences.coffee
+++ lib/preferences.coffee
@@ -39,6 +39,13 @@

       window.FrenchMenu.pref.done = true

+      # Restore the flag when the settings panel is closed
+      editorPane = atom.workspace.getActivePane()
+      if editorPane
+        handler = editorPane.onDidRemoveItem (event) =>
+          if event.item.uri == 'atom://config'
+            window.FrenchMenu.pref.done = false
+          handler.dispose()
     catch e
       console.error "Echec de traduction en Francais", e


Get latest changes from master.

Get latest changes from master.

--- src/coffee/orderexport.coffee
+++ src/coffee/orderexport.coffee
@@ -81,7 +81,6 @@
   syncOrder: (xmlOrder, filename) ->
     @orderService.addSyncInfo xmlOrder.id, xmlOrder.version, @channel, filename

-  mapOrder: (order, customer) ->
   mapOrder: (order, paymentInfo, customer) ->
     xml = builder.create('order',
       { 'version': '1.0', 'encoding': 'UTF-8', 'standalone': true })


Send file out line-by-line

Send file out line-by-line

--- components/fileReader.coffee
+++ components/fileReader.coffee
@@ -8,7 +8,7 @@

 readFile = (fileName, socket) ->
     socket.on "connect", ->
-        fs.readFile fileName, (err, data) ->
+        fs.readFile fileName, "utf-8", (err, data) ->
             if err
                 if errSocket
                     errSocket.on "connect", ->
@@ -17,8 +17,8 @@
                     errSocket.connect()
                 return

-            # TODO: Split by line
-            socket.send data
+            data.split("\n").forEach (line) ->
+                socket.send line
             socket.disconnect()

     socket.connect()


remove log

remove log

--- app/lib/sub-nav.coffee
+++ app/lib/sub-nav.coffee
@@ -6,8 +6,8 @@
 1. Create a nav with class="sub-nav-PAGE_NAME"
     ex: "science", "education"

-2. Add buttons to the nav with name="SECTION_SECTION"
-    ex: "science", "more-info", etc.
+2. Add buttons to the nav with name="SECTION_NAME"
+    ex: "planets", "more-info", etc.

 3. Add content sections with class="sub-nav-PAGE_NAME-SECTION_NAME"

@@ -25,8 +25,7 @@

       $(".sub-nav-#{pageName}-#{initialClassName}:nth-child(1)").siblings().hide()

-      $(".sub-nav-#{pageName} button").on "click", (e) =>
-        console.log "clicked", e.target.name
+      $(".sub-nav-#{pageName} button").on "click", (e) =>
         $(".sub-nav-#{pageName}-#{e.target.name}").show().siblings().hide()
         $(".sub-nav-#{pageName} button[name=#{e.target.name}").addClass("active").siblings().removeClass("active")


clone component to set key

clone component to set key

--- app/react_abuse.coffee
+++ app/react_abuse.coffee
@@ -1,10 +1,8 @@
-React = require 'react'
+React = require 'react/lib/ReactWithAddons'
 _ = require 'underscore'
 Bacon = require 'bacon.model'

 component_id = 1
-assign_key = (component) ->
-  component.props.key ?= "component_#{component_id++}"

 createIdentityClass = (args...) ->
   cls = React.createClass args...
@@ -41,7 +39,8 @@
   model: model
   component: SimpleObservableComponent observable: model
   add_component: (c) ->
-    assign_key c
+    unless c.props.key?
+      c = React.addons.cloneWithProps c, key: "#{c.constructor.displayName ? 'component'}_#{component_id++}"
     components.push c
     model.set components.slice()
   empty: ->


Issue #1: Fixed issue where jenkins server manager could not find servers for job names containing spaces

--- src/jenkins-enhanced.coffee
+++ src/jenkins-enhanced.coffee
@@ -81,6 +81,7 @@
     @_jobs.length > 0

   hasJobByName: (jobName) =>
+    jobName = jobName.replace('%20', ' ')
     @_jobs.where(name: jobName).length > 0


Fix caching of textures.

Fix caching of textures.

--- src/js/text_renderer.coffee
+++ src/js/text_renderer.coffee
@@ -8,9 +8,11 @@
             size: 30
             font: "Times Roman"
             color: "#ff0000"
-        id = {text: text, font: options.font, color: options.color}
+        id = JSON.stringify({text: text, font: options.font, color: options.color})
+        console.log("render text called")
         return cache[id] if id of cache
         canvas = document.createElement('canvas')
+        $("#container").append(canvas)
         context = canvas.getContext('2d')
         context.font = "#{options.size}px #{options.font}"
         metrics = context.measureText(text)


refactor

refactor

git-svn-id: 00f01ee4f729ccc52c3e336777c3e81b5adc88db@248934 ef392f78-ee14-0410-84e5-9db46baae5bb

--- app/scripts/app.coffee
+++ app/scripts/app.coffee
@@ -76,26 +76,22 @@

     searches.infoDef.then () ->
         corpus = $location.search().corpus
+        currentCorpora = []
         if corpus
-            corp_array = corpus.split(",")
-            processed_corp_array = []
-            $.each corp_array, (key, val) ->
-                processed_corp_array = [].concat(processed_corp_array, getAllCorporaInFolders(settings.corporafolders, val))
-            settings.corpusListing.select(processed_corp_array)
-            corpusChooserInstance.corpusChooser "selectItems", processed_corp_array
-            $("#select_corpus").val corpus
+            _.map corpus.split(","), (val) ->
+                currentCorpora = [].concat(currentCorpora, getAllCorporaInFolders(settings.corporafolders, val))
         else
             if not settings.preselected_corpora?.length
-                all_default_corpora = _.pluck settings.corpusListing.corpora, "id"
+                currentCorpora = _.pluck settings.corpusListing.corpora, "id"
             else
-                all_default_corpora = []
                 for pre_item in settings.preselected_corpora
                     pre_item = pre_item.replace /^__/g, ''
-                    all_default_corpora.push.apply(all_default_corpora, getAllCorporaInFolders(settings.corporafolders, pre_item))
+                    currentCorpora = [].concat(currentCorpora, getAllCorporaInFolders(settings.corporafolders, pre_item))

-            settings.preselected_corpora = all_default_corpora
-            settings.corpusListing.select all_default_corpora
-            corpusChooserInstance.corpusChooser "selectItems", all_default_corpora
+            settings.preselected_corpora = currentCorpora
+
+        settings.corpusListing.select currentCorpora
+        corpusChooserInstance.corpusChooser "selectItems", currentCorpora

 korpApp.controller "headerCtrl", ($scope, $location, $uibModal, utils) ->
     s = $scope


Trying to get the text to wrap.

Trying to get the text to wrap.

--- src/game/states/script.coffee
+++ src/game/states/script.coffee
@@ -84,9 +84,10 @@
     @bluey_x = 160
     @bluey_y = 395

-    @captainTxt =  @add.text(@pxyis_x, @pxyis_y, "", { font: "18px Arial", fill: "#FFFF00", align: "center" })
-    @dogTxt =  @add.text(@dog_x, @dog_y, "", { font: "18px Arial", fill: "#FFFF00", align: "center" })
-    @blueyTxt =  @add.text(@bluey_x, @bluey_y, "", { font: "18px Arial", fill: "#FFFF00", align: "center" })
+    @style = { font: "18px sans-serif", fill: "#FFFF00", align: "center", wordWrap: true, wordWrapWidth: 650}
+    @captainTxt =  @add.text(@pxyis_x, @pxyis_y, "", @style)
+    @dogTxt =  @add.text(@dog_x, @dog_y, "", @style)
+    @blueyTxt =  @add.text(@bluey_x, @bluey_y, "", @style)
     @index = 0
     @nextDialog()
     @index = 1
@@ -106,12 +107,12 @@
     @speaker = @level.dialogArray[@index].speaker
     if @speaker == 0
       @captainTxt.destroy()
-      @captainTxt =  @add.text(@pxyis_x, @pxyis_y, @level.dialogArray[@index].speech, { font: "18px Arial", fill: "#FFFF00", align: "center" })
+      @captainTxt =  @add.text(@pxyis_x, @pxyis_y, @level.dialogArray[@index].speech, @style)
     else if @speaker == 1
       @dogTxt.destroy()
-      @dogTxt =  @add.text(@dog_x, @dog_y, @level.dialogArray[@index].speech, { font: "18px Arial", fill: "#FFFF00", align: "center" })
+      @dogTxt =  @add.text(@dog_x, @dog_y, @level.dialogArray[@index].speech, @style)
     else
       @blueyTxt.destroy()
-      @blueyTxt =  @add.text(@bluey_x, @bluey_y, @level.dialogArray[@index].speech, { font: "18px Arial", fill: "#FFFF00", align: "center" })
+      @blueyTxt =  @add.text(@bluey_x, @bluey_y, @level.dialogArray[@index].speech, @style)

 module.exports = Script


some edits to the interpolation path

some edits to the interpolation path

--- lexer.coffee
+++ lexer.coffee
@@ -71,6 +71,10 @@
 LAST_DENT  : /\n([ \t]*)/
 ASSIGNMENT : /^(:|=)$/

+# Interpolation matching regexes.
+INTERPOLATED_EXPRESSION: /(^|[\s\S]*?(?:[\\]|\\\\)?)(\${[\s\S]*?(?:[^\\]|\\\\)})/
+INTERPOLATED_IDENTIFIER: /(^|[\s\S]*?(?:[\\]|\\\\)?)(\$([a-zA-Z_]\w*))/
+
 # Token cleaning regexes.
 JS_CLEANER      : /(^`|`$)/g
 MULTILINER      : /\n/g
@@ -112,7 +116,8 @@
 exports.Lexer: class Lexer

   # Scan by attempting to match tokens one at a time. Slow and steady.
-  tokenize: (code, rewrite) ->
+  tokenize: (code, options) ->
+    options  ||= {}
     @code    : code  # The remainder of the source code.
     @i       : 0     # Current character position we're parsing.
     @line    : 0     # The current line.
@@ -123,8 +128,8 @@
       @chunk: @code.slice(@i)
       @extract_next_token()
     @close_indentation()
-    return (new Rewriter()).rewrite @tokens if (rewrite ? true)
-    return @tokens
+    return @tokens if options.rewrite is no
+    (new Rewriter()).rewrite @tokens

   # At every position, run through this list of attempted matches,
   # short-circuiting if any of them succeed.
@@ -166,8 +171,7 @@
   # Matches strings, including multi-line strings.
   string_token: ->
     return false unless string: @match STRING, 1
-    escaped: string.replace STRING_NEWLINES, " \\\n"
-    @interpolate_string escaped
+    @interpolate_string string.replace STRING_NEWLINES, " \\\n"
     @line += count string, "\n"
     @i += string.length
     true
@@ -341,30 +345,34 @@
   assignment_error: ->
     throw new Error 'SyntaxError: Reserved word "' + @value() + '" on line ' + @line + ' can\'t be assigned'

-  # Replace variables and expressions inside double-quoted strings.
-  interpolate_string: (escaped) ->
-    if escaped.length < 3 or escaped.indexOf('"') isnt 0
-      @token 'STRING', escaped
+  # Expand variables and expressions inside double-quoted strings using
+  # [ECMA Harmony's interpolation syntax](http://wiki.ecmascript.org/doku.php?id=strawman:string_interpolation).
+  #
+  #     "Hello $name."
+  #     "Hello ${name.capitalize()}."
+  #
+  interpolate_string: (str) ->
+    if str.length < 3 or str.substring(0, 1) isnt '"'
+      @token 'STRING', str
     else
-      lexer: null
+      lexer:  new Lexer()
       tokens: []
-      quote: escaped.substring(0, 1)
-      escaped: escaped.substring(1, escaped.length - 1)
-      while escaped.length
-        expression_match: escaped.match /(^|[\s\S]*?(?:[\\]|\\\\)?)(\${[\s\S]*?(?:[^\\]|\\\\)})/
+      quote:  str.substring(0, 1)
+      str:    str.substring(1, str.length - 1)
+      while str.length
+        expression_match: str.match INTERPOLATED_EXPRESSION
         if expression_match
           [group, before, expression]: expression_match
           if before.substring(before.length - 1) is '\\'
             tokens.push ['STRING', quote + before.substring(0, before.length - 1) + expression + quote] if before.length
           else
             tokens.push ['STRING', quote + before + quote] if before.length
-            lexer: new Lexer() if not lexer?
-            nested: lexer.tokenize '(' + expression.substring(2, expression.length - 1) + ')', rewrite: no
+            nested: lexer.tokenize '(' + expression.substring(2, expression.length - 1) + ')', {rewrite: no}
             nested.pop()
             tokens.push ['TOKENS', nested]
-          escaped: escaped.substring(group.length)
+          str: str.substring(group.length)
         else
-          identifier_match: escaped.match /(^|[\s\S]*?(?:[\\]|\\\\)?)(\$([a-zA-Z_]\w*))/
+          identifier_match: str.match INTERPOLATED_IDENTIFIER
           if identifier_match
             [group, before, identifier]: identifier_match
             if before.substring(before.length - 1) is '\\'
@@ -372,15 +380,16 @@
             else
               tokens.push ['STRING', quote + before + quote] if before.length
               tokens.push ['IDENTIFIER', identifier.substring(1)]
-            escaped: escaped.substring(group.length)
+            str: str.substring(group.length)
           else
-            tokens.push ['STRING', quote + escaped + quote]
-            escaped: ''
+            tokens.push ['STRING', quote + str + quote]
+            str: ''
       if tokens.length > 1
         for i in [tokens.length - 1..1]
-          if tokens[i][0] is 'STRING' and tokens[i - 1][0] is 'STRING'
-            tokens.splice i - 1, 2, ['STRING', quote + tokens[i - 1][1].substring(1, tokens[i - 1][1].length - 1) +
-                                                       tokens[i][1].substring(1, tokens[i][1].length - 1) + quote]
+          [prev, tok]: [tokens[i - 1], tokens[i]]
+          if tok[0] is 'STRING' and prev[0] is 'STRING'
+            contents: quote + prev[1].substring(1, prev[1].length - 1) + tok[1].substring(1, tok[1].length - 1) + quote
+            tokens.splice i - 1, 2, ['STRING', contents]
       for each, i in tokens
         if each[0] is 'TOKENS'
           @token nested[0], nested[1] for nested in each[1]


enhance: define property for status

enhance: define property for status

--- app/models/file.coffee
+++ app/models/file.coffee
@@ -28,10 +28,11 @@
   report: DS.attr 'string'
   manual: DS.attr 'number'

-  tdeviceBooting: t("deviceBooting")
-  tdeviceIsReady: t("deviceIsReady")
-  tdeviceNotFound: t("deviceNotFound")
-  tdeviceShuttingDown: t("deviceShuttingDown")
+  tDeviceBooting: t("deviceBooting")
+  tDeviceDownloading: t("deviceDownloading")
+  tDeviceInstalling: t("deviceInstalling")
+  tDeviceLaunching: t("deviceLaunching")
+  tDeviceHooking: t("deviceHooking")

   analysesSorting: ['risk:desc']
   sortedAnalyses: Ember.computed.sort 'analyses', 'analysesSorting'
@@ -79,18 +80,38 @@
     status is ENUMS.DYNAMIC_STATUS.READY
   ).property 'dynamicStatus'

-  isBooting: (->
-    status = @get 'dynamicStatus'
-    status is ENUMS.DYNAMIC_STATUS.BOOTING
-  ).property 'dynamicStatus'
-
   isShuttingDown: (->
     status = @get 'dynamicStatus'
     status is ENUMS.DYNAMIC_STATUS.SHUTTING_DOWN
   ).property 'dynamicStatus'

-  setBooting: ->
-    @set "dynamicStatus", ENUMS.DYNAMIC_STATUS.BOOTING
+  isOtherStatus: (->
+    status = @get 'dynamicStatus'
+    status not in [ENUMS.DYNAMIC_STATUS.READY, ENUMS.DYNAMIC_STATUS.NONE, ENUMS.DYNAMIC_STATUS.SHUTTING_DOWN]
+  ).property 'dynamicStatus'
+
+  otherStatus: (->
+    tDeviceBooting = @get "tDeviceBooting"
+    tDeviceDownloading = @get "tDeviceDownloading"
+    tDeviceInstalling = @get "tDeviceInstalling"
+    tDeviceLaunching = @get "tDeviceLaunching"
+    tDeviceHooking = @get "tDeviceHooking"
+    switch @get "dynamicStatus"
+      when ENUMS.DYNAMIC_STATUS.BOOTING
+        tDeviceBooting
+      when ENUMS.DYNAMIC_STATUS.DOWNLOADING
+        tDeviceDownloading
+      when ENUMS.DYNAMIC_STATUS.INSTALLING
+        tDeviceInstalling
+      when ENUMS.DYNAMIC_STATUS.LAUNCHING
+        tDeviceLaunching
+      when ENUMS.DYNAMIC_STATUS.HOOKING
+        tDeviceHooking
+  ).property 'dynamicStatus'
+
+
+  setOtherStatus: ->
+    @set "dynamicStatus", [ENUMS.DYNAMIC_STATUS.BOOTING, ENUMS.DYNAMIC_STATUS.DOWNLOADING, ENUMS.DYNAMIC_STATUS.INSTALLING, ENUMS.DYNAMIC_STATUS.LAUNCHING, ENUMS.DYNAMIC_STATUS.HOOKING]

   setShuttingDown: ->
     @set "dynamicStatus", ENUMS.DYNAMIC_STATUS.SHUTTING_DOWN
@@ -101,23 +122,5 @@
   setReady: ->
     @set "dynamicStatus", ENUMS.DYNAMIC_STATUS.READY

-  humanizedStatus: (->
-    tdeviceBooting = @get "tdeviceBooting"
-    tdeviceIsReady = @get "tdeviceIsReady"
-    tdeviceShuttingDown = @get "tdeviceShuttingDown"
-    tdeviceNotFound = @get "tdeviceNotFound"
-
-    switch @get "dynamicStatus"
-      when ENUMS.DYNAMIC_STATUS.UNKNOWN
-        "UNKNOWN: " + tdeviceNotFound
-      when ENUMS.DYNAMIC_STATUS.NONE
-        "NONE: " + tdeviceNotFound
-      when ENUMS.DYNAMIC_STATUS.BOOTING
-        tdeviceBooting
-      when ENUMS.DYNAMIC_STATUS.READY
-        tdeviceIsReady
-      when ENUMS.DYNAMIC_STATUS.SHUTTING_DOWN
-        tdeviceShuttingDown
-  ).property "dynamicStatus"

 `export default File`


Added create package skeleton

Added create package skeleton


remove latin blurgh add some real text

remove latin blurgh add some real text

--- app/pages/sign-in.cjsx
+++ app/pages/sign-in.cjsx
@@ -7,7 +7,7 @@
 counterpart.registerTranslations 'en',
   signIn:
     withZooniverse: 'Sign in with your Zooniverse account'
-    whyHaveAccount: 'Signed-in volunteers lorem ipsum dolor sit amet blah blah blah.'
+    whyHaveAccount: 'Signed-in volunteers can keep track of their work and will be credited in any resulting publications.'
     signIn: 'Sign in'
     register: 'Register'
     orThirdParty: 'Or sign in with another service'


nodes: refactored and removed the notion of `o.top` in favor of LVL_TOP

--- nodes.coffee
+++ nodes.coffee
@@ -37,20 +37,15 @@
   # the top level of a block (which would be unnecessary), and we haven't
   # already been asked to return the result (because statements know how to
   # return results).
-  #
-  # If a Node is *topSensitive*, that means that it needs to compile differently
-  # depending on whether it's being used as part of a larger expression, or is a
-  # top-level statement within the function body.
   compile: (o, lvl) ->
-    o       = if o then extend {}, o else {}
-    o.level = lvl if lvl
-    top     = if @topSensitive then o.top else del o, 'top'
-    @tab    = o.indent
-    if top or o.asStatement or this instanceof Comment or
-       @isPureStatement() or not @isStatement(o)
-      @compileNode o
+    o        = if o then extend {}, o else {}
+    o.level  = lvl if lvl?
+    node     = @unfoldSoak(o) or this
+    node.tab = o.indent
+    if o.level is LVL_TOP or node.isPureStatement() or not node.isStatement(o)
+      node.compileNode o
     else
-      @compileClosure o
+      node.compileClosure o

   # Statements converted into expressions via closure-wrapping share a scope
   # object with their parent closure, to preserve the expected lexical scope.
@@ -74,7 +69,7 @@

   # Compile to a source/variable pair suitable for looping.
   compileLoopReference: (o, name) ->
-    src = tmp = @compile o, LVL_ASSIGN
+    src = tmp = @compile o, LVL_LIST
     unless NUMBER.test(src) or IDENTIFIER.test(src) and o.scope.check(src, immediate: on)
       src = "#{ tmp = o.scope.freeVariable name } = #{src}"
     [src, tmp]
@@ -147,7 +142,6 @@
   isPureStatement : NO
   isComplex       : YES
   isChainable     : NO
-  topSensitive    : no
   unfoldSoak      : NO

   # Is this node used to assign a certain variable?
@@ -162,7 +156,7 @@

   children: ['expressions']

-  isStatement:  YES
+  isStatement: YES

   constructor: (nodes) ->
     super()
@@ -170,12 +164,12 @@

   # Tack an expression on to the end of this expression list.
   push: (node) ->
-    @expressions.push(node)
+    @expressions.push node
     this

   # Add an expression at the beginning of this expression list.
   unshift: (node) ->
-    @expressions.unshift(node)
+    @expressions.unshift node
     this

   # If this Expressions consists of just a single node, unwrap it by pulling
@@ -201,7 +195,8 @@
     if o.scope then super o, lvl else @compileRoot o

   compileNode: (o) ->
-    (@compileExpression node, merge o for node in @expressions).join '\n'
+    @tab = o.indent
+    (@compileExpression node, o for node in @expressions).join '\n'

   # If we happen to be the top-level **Expressions**, wrap everything in
   # a safety closure, unless requested not to.
@@ -218,20 +213,23 @@
   # Compile the expressions body for the contents of a function, with
   # declarations of all inner variables pushed up to the top.
   compileWithDeclarations: (o) ->
-    code = @compileNode o
-    if o.scope.hasAssignments this
-      code = "#{@tab}var #{ o.scope.compiledAssignments().replace /\n/g, '$&' + @tab };\n#{code}"
+    code    = @compileNode o
+    {scope} = o
+    if scope.hasAssignments this
+      code = "#{@tab}var #{ multident scope.compiledAssignments(), @tab };\n#{code}"
     if not o.globals and o.scope.hasDeclarations this
-      code = "#{@tab}var #{ o.scope.compiledDeclarations() };\n#{code}"
+      code = "#{@tab}var #{ scope.compiledDeclarations() };\n#{code}"
     code

   # Compiles a single expression within the expressions body. If we need to
   # return the result, and it's an expression, simply return it. If it's a
   # statement, ask the statement to do so.
   compileExpression: (node, o) ->
-    o.top = node.tags.front = on
-    @tab  = o.indent
-    code  = node.compile o
+    while node isnt node = node.unwrap() then
+    node = node.unfoldSoak(o) or node
+    node.tags.front = on
+    o.level = LVL_TOP
+    code    = node.compile o
     if node.isStatement o then code else @tab + code + ';'

   # Wrap up the given nodes as an **Expressions**, unless it already happens
@@ -247,24 +245,19 @@
 # `true`, `false`, `null`...
 exports.Literal = class Literal extends Base

-  constructor: (@value) ->
-    super()
-
-  makeReturn: ->
-    if @isStatement() then this else super()
+  constructor: (@value) -> super()
+
+  makeReturn: -> if @isStatement() then this else super()

   # Break and continue must be treated as pure statements -- they lose their
   # meaning when wrapped in a closure.
-  isStatement    : -> @value in ['break', 'continue', 'debugger']
-  isPureStatement: Literal::isStatement
+  isPureStatement: -> @value in ['break', 'continue', 'debugger']

   isComplex: NO

   assigns: (name) -> name is @value

-  compileNode: ->
-    val = if @value.reserved then "\"#{@value}\"" else @value
-    if @isStatement() then @tab + val + ';' else val
+  compile: -> if @value.reserved then "\"#{@value}\"" else @value

   toString: -> ' "' + @value + '"'

@@ -279,8 +272,7 @@
   isStatement    : YES
   isPureStatement: YES

-  constructor: (@expression) ->
-    super()
+  constructor: (@expression) -> super()

   makeReturn: THIS

@@ -289,11 +281,8 @@
     if expr and expr not instanceof Return then expr.compile o, lvl else super o, lvl

   compileNode: (o) ->
-    expr = ''
-    if @expression
-      o.asStatement = true if @expression.isStatement o
-      expr = ' ' + @expression.compile o, LVL_PAREN
-    @tab + 'return' + expr + ';'
+    o.level = LVL_PAREN
+    @tab + "return#{ if @expression then ' ' + @expression.compile o else '' };"

 #### Value

@@ -311,7 +300,7 @@

   # Add a property access to the list.
   push: (prop) ->
-    @properties.push(prop)
+    @properties.push prop
     this

   hasProperties: ->
@@ -346,7 +335,7 @@

   # Values are considered to be statements if their base is a statement.
   isStatement: (o) ->
-    @base.isStatement(o) and not @properties.length
+    not @properties.length and @base.isStatement o

   isSimpleNumber: ->
     @base instanceof Literal and SIMPLENUM.test @base.value
@@ -369,21 +358,16 @@
       nref = new Index nref
     [base.push(name), new Value(bref or base.base, [nref or name])]

-  # Override compile to unwrap the value when possible.
-  compile: (o, lvl) ->
-    @base.tags.front = @tags.front
-    if not o.top or @properties.length then super o, lvl else @base.compile o, lvl
-
   # We compile a value to JavaScript by compiling and joining each property.
   # Things get much more insteresting if the chain of properties has *soak*
   # operators `?.` interspersed. Then we have to take care not to accidentally
   # evaluate anything twice when building the soak chain.
   compileNode: (o) ->
-    return ifn.compile o if ifn = @unfoldSoak o
+    @base.tags.front = @tags.front
     props = @properties
-    code  = @base.compile o, props.length and LVL_ACCESS
+    code  = @base.compile o, if props.length then LVL_ACCESS else null
     code  = "(#{code})" if props[0] instanceof Accessor and @isSimpleNumber()
-    (code += prop.compileNode o) for prop in props
+    (code += prop.compile o) for prop in props
     code

   # Unfold a soak into an `If`: `a?.b` -> `a.b if a?`
@@ -410,15 +394,13 @@
 # at the same position.
 exports.Comment = class Comment extends Base

-  isStatement: YES
-
-  constructor: (@comment) ->
-    super()
+  isPureStatement: YES
+
+  constructor: (@comment) -> super()

   makeReturn: THIS

-  compileNode: (o) ->
-    @tab + '/*' + @comment.replace(/\n/g, '\n' + @tab) + '*/'
+  compileNode: (o) -> @tab + '/*' + multident(@comment, @tab) + '*/'

 #### Call

@@ -488,7 +470,6 @@

   # Compile a vanilla function call.
   compileNode: (o) ->
-    return ifn.compile o if ifn = @unfoldSoak o
     @variable?.tags.front = @tags.front
     for arg in @args when arg instanceof Splat
       return @compileSplat o
@@ -514,10 +495,10 @@
       base = Value.wrap @variable
       if (name = base.properties.pop()) and base.isComplex()
         ref = o.scope.freeVariable 'this'
-        fun = "(#{ref} = #{ base.compile o, LVL_ASSIGN })#{ name.compileNode o }"
+        fun = "(#{ref} = #{ base.compile o, LVL_LIST })#{ name.compile o }"
       else
         fun = ref = base.compile o, LVL_ACCESS
-        fun += name.compileNode o if name
+        fun += name.compile o if name
       return "#{fun}.apply(#{ref}, #{splatargs})"
     idt = @idt 1
     """
@@ -537,11 +518,10 @@

   children: ['child', 'parent']

-  constructor: (@child, @parent) ->
-    super()
+  constructor: (@child, @parent) -> super()

   # Hooks one constructor into another's prototype chain.
-  compileNode: (o) ->
+  compile: (o) ->
     new Call(new Value(new Literal utility 'extends'), [@child, @parent]).compile o

 #### Accessor
@@ -557,7 +537,7 @@
     @proto    = if tag is 'prototype' then '.prototype' else ''
     @soakNode = tag is 'soak'

-  compileNode: (o) ->
+  compile: (o) ->
     name = @name.compile o
     @proto + if IS_STRING.test name then "[#{name}]" else ".#{name}"

@@ -570,10 +550,9 @@

   children: ['index']

-  constructor: (@index) ->
-    super()
-
-  compileNode: (o) ->
+  constructor: (@index) -> super()
+
+  compile: (o) ->
     (if @proto then '.prototype' else '') + "[#{ @index.compile o, LVL_PAREN }]"

   isComplex: -> @index.isComplex()
@@ -590,7 +569,6 @@
     @objects = @properties = props or []

   compileNode: (o) ->
-    top = del o, 'top'
     o.indent = @idt 1
     nonComments = prop for prop in @properties when prop not instanceof Comment
     lastNoncom  = last nonComments
@@ -660,7 +638,7 @@

   children: ['variable', 'parent', 'properties']

-  isStatement:  YES
+  isStatement: YES

   # Initialize a **Class** with its name, an optional superclass, and a
   # list of prototype property assignments.
@@ -682,7 +660,6 @@
     variable   = new Literal o.scope.freeVariable 'ctor' if variable.value is '__temp__'
     extension  = @parent and new Extends variable, @parent
     props      = new Expressions
-    o.top      = true
     me         = null
     className  = variable.compile o
     constScope = null
@@ -730,11 +707,12 @@

     constructor.className = className.match /[$\w]+$/
     constructor.body.unshift new Literal "#{me} = this" if me
-    construct = @idt() + new Assign(variable, constructor).compile(merge o, sharedScope: constScope) + ';'
-    props     = if !props.empty() then '\n' + props.compile(o)                    else ''
-    extension = if extension      then '\n' + @idt() + extension.compile(o) + ';' else ''
-    returns   = if @returns       then '\n' + new Return(variable).compile(o) else ''
-    construct + extension + props + returns
+    o.sharedScope = constScope
+    construct  = @tab + new Assign(variable, constructor).compile(o) + ';'
+    construct += '\n' + @tab + extension.compile(o) + ';' if extension
+    construct += '\n' + props.compile o                   if !props.empty()
+    construct += '\n' + new Return(variable).compile o    if @returns
+    construct

 #### Assign

@@ -749,10 +727,12 @@

   children: ['variable', 'value']

-  topSensitive: yes
-
-  constructor: (@variable, @value, @context) ->
-    super()
+  constructor: (@variable, @value, @context) -> super()
+
+  assigns: (name) ->
+    @[if @context is 'object' then 'value' else 'variable'].assigns name
+
+  unfoldSoak: (o) -> If.unfoldSoak o, this, 'variable'

   # Compile an assignment, delegating to `compilePatternMatch` or
   # `compileSplice` if appropriate. Keep track of the name of the base object
@@ -760,31 +740,24 @@
   # has not been seen yet within the current scope, declare it.
   compileNode: (o) ->
     if isValue = @variable instanceof Value
-      return @compilePatternMatch(o) if @variable.isArray() or @variable.isObject()
-      if ifn = If.unfoldSoak o, this, 'variable'
-        delete o.top
-        return ifn.compile o
-      return @compileConditional o if @context in @CONDITIONAL
-    top  = del o, 'top'
-    stmt = del o, 'asStatement'
-    name = @variable.compile o, LVL_ASSIGN
+      return @compilePatternMatch o if @variable.isArray() or @variable.isObject()
+      return @compileConditional  o if @context in @CONDITIONAL
+    name = @variable.compile o, LVL_LIST
     if @value instanceof Code and match = @METHOD_DEF.exec name
       @value.name  = match[2]
       @value.klass = match[1]
-    val = @value.compile o, LVL_ASSIGN
+    val = @value.compile o, LVL_LIST
     return "#{name}: #{val}" if @context is 'object'
     o.scope.find name unless isValue and (@variable.hasProperties() or @variable.namespaced)
     val = name + " #{ @context or '=' } " + val
-    return "#{@tab}#{val};" if stmt
-    if top or o.level <= LVL_ASSIGN then val else "(#{val})"
+    if o.level <= LVL_LIST then val else "(#{val})"

   # Brief implementation of recursive pattern matching, when assigning array or
   # object literals to a value. Peeks at their properties to assign inner names.
   # See the [ECMAScript Harmony Wiki](http://wiki.ecmascript.org/doku.php?id=harmony:destructuring)
   # for details.
   compilePatternMatch: (o) ->
-    top  = del o, 'top'
-    otop = merge o, top: yes
+    top       = o.level is LVL_TOP
     {value}   = this
     {objects} = @variable.base
     return value.compile o unless olength = objects.length
@@ -796,11 +769,12 @@
       else
         idx = if isObject
           if obj.tags.this then obj.properties[0].name else obj
-        else new Literal 0
+        else
+          new Literal 0
       accessClass = if IDENTIFIER.test idx.value then Accessor else Index
       (value = Value.wrap value).properties.push new accessClass idx
-      return new Assign(obj, value).compile otop
-    valVar  = value.compile o
+      return new Assign(obj, value).compile o
+    valVar  = value.compile o, LVL_LIST
     assigns = []
     splat   = false
     if not IDENTIFIER.test(valVar) or @variable.assigns(valVar)
@@ -823,12 +797,13 @@
         val   = new Literal obj.compileValue o, valVar, i, olength - i - 1
         splat = true
       else
-        idx = new Literal(if splat then "#{valVar}.length - #{olength - idx}" else idx) if typeof idx isnt 'object'
+        if typeof idx isnt 'object'
+          idx = new Literal(if splat then "#{valVar}.length - #{ olength - idx }" else idx)
         val = new Value new Literal(valVar), [new accessClass idx]
-      assigns.push new Assign(obj, val).compile otop
+      assigns.push new Assign(obj, val).compile o, LVL_LIST
     assigns.push valVar unless top
     code = assigns.join ', '
-    if top or o.level <= LVL_PAREN then code else "(#{code})"
+    if o.level < LVL_LIST then code else "(#{code})"

   # When compiling a conditional assignment, take care to ensure that the
   # operands are only evaluated once, even though we have to reference them
@@ -837,9 +812,6 @@
     [left, rite] = @variable.cacheReference o
     return new Op(@context.slice(0, -1), left, new Assign(rite, @value)).compile o

-  assigns: (name) ->
-    @[if @context is 'object' then 'value' else 'variable'].assigns name
-
 #### Code

 # A function definition. This is the only node that creates a new Scope.
@@ -863,9 +835,7 @@
   # a closure.
   compileNode: (o) ->
     sharedScope = del o, 'sharedScope'
-    top         = del o, 'top'
-    o.scope     = sharedScope or new Scope(o.scope, @body, this)
-    o.top       = true
+    o.scope     = scope = sharedScope or new Scope o.scope, @body, this
     o.indent    = @idt 1
     empty       = @body.expressions.length is 0
     delete o.bare
@@ -881,28 +851,34 @@
       else
         if param.attach
           {value} = param
-          [param, param.splat] = [new Literal(o.scope.freeVariable 'arg'), param.splat]
+          [param, param.splat] = [new Literal(scope.freeVariable 'arg'), param.splat]
           @body.unshift new Assign new Value(new Literal('this'), [new Accessor value]), param
         if param.splat
           splat           = new Splat param.value
           splat.index     = i
           splat.trailings = []
           splat.arglength = @params.length
-          @body.unshift(splat)
+          @body.unshift splat
         else
           params.push param
-    o.scope.startLevel()
-    params = param.compile(o) for param in params
+    scope.startLevel()
     @body.makeReturn() unless empty or @noReturn
-    o.scope.parameter param for param in params
+    params = for param in params
+      scope.parameter param = param.compile o
+      param
     comm  = if @comment then @comment.compile(o) + '\n' else ''
     o.indent = @idt 2 if @className
-    code  = if @body.expressions.length then "\n#{ @body.compileWithDeclarations(o) }\n" else ''
-    open  = if @className then "(function() {\n#{comm}#{@idt(1)}function #{@className}(" else "function("
-    close = if @className then "#{code and @idt(1)}};\n#{@idt(1)}return #{@className};\n#{@tab}})()" else "#{code and @tab}}"
-    func  = "#{open}#{ params.join(', ') }) {#{code}#{close}"
-    o.scope.endLevel()
-    return "#{utility 'bind'}(#{func}, #{@context})" if @bound
+    idt  = @idt 1
+    code = if @body.expressions.length then "\n#{ @body.compileWithDeclarations o }\n" else ''
+    if @className
+      open  = "(function() {\n#{comm}#{idt}function #{@className}("
+      close = "#{ code and idt }};\n#{idt}return #{@className};\n#{@tab}})()"
+    else
+      open  = "function("
+      close = "#{ code and @tab }}"
+    func = "#{open}#{ params.join ', ' }) {#{code}#{close}"
+    scope.endLevel()
+    return "#{ utility 'bind' }(#{func}, #{@context})" if @bound
     if @tags.front then "(#{func})" else func

   # Short-circuit `traverseChildren` method to prevent it from crossing scope boundaries
@@ -922,13 +898,12 @@
     super()
     @value = new Literal @name

-  compileNode: (o) ->
-    @value.compile o, LVL_LIST
+  compile: (o) -> @value.compile o, LVL_LIST

   toString: ->
-    {name} = @
-    name = '@' + name if @attach
-    name += '...'     if @splat
+    {name} = this
+    name   = '@' + name if @attach
+    name  += '...'      if @splat
     new Literal(name).toString()

 #### Splat
@@ -945,7 +920,7 @@

   assigns: (name) -> @name.assigns name

-  compileNode: (o) ->
+  compile: (o) ->
     if @index? then @compileParam o else @name.compile o

   # Compiling a parameter splat means recovering the parameters that succeed
@@ -1004,8 +979,7 @@

   children: ['condition', 'guard', 'body']

-  topSensitive: yes
-  isStatement : YES
+  isStatement: YES

   constructor: (condition, opts) ->
     super()
@@ -1024,22 +998,23 @@
   # *while* can be used as a part of a larger expression -- while loops may
   # return an array containing the computed result of each iteration.
   compileNode: (o) ->
-    top      = del(o, 'top') and not @returns
     o.indent = @idt 1
-    cond     = @condition.compile o, LVL_PAREN
-    o.top    = true
     set      = ''
-    unless top
-      rvar  = o.scope.freeVariable 'result'
-      set   = "#{@tab}#{rvar} = [];\n"
-      @body = Push.wrap(rvar, @body) if @body
-    pre     = "#{set}#{@tab}while (#{cond})"
-    @body   = Expressions.wrap [new If @guard, @body] if @guard
+    {body}   = this
+    if o.level > LVL_TOP or @returns
+      rvar = o.scope.freeVariable 'result'
+      set  = "#{@tab}#{rvar} = [];\n"
+      body = Push.wrap rvar, body if body
+    body = Expressions.wrap [new If @guard, body] if @guard
+    code = set + @tab + """
+      while (#{ @condition.compile o, LVL_PAREN }) {
+      #{ body.compile o, LVL_TOP }
+      #{@tab}}
+    """
     if @returns
-      post = '\n' + new Return(new Literal rvar).compile merge o, indent: @idt()
-    else
-      post = ''
-    "#{pre} {\n#{ @body.compile(o) }\n#{@tab}}#{post}"
+      o.indent = @tab
+      code += '\n' + new Return(new Literal rvar).compile o
+    code

 #### Op

@@ -1102,9 +1077,11 @@
   toString: (idt) ->
     super idt, @constructor.name + ' ' + @operator

+  unfoldSoak: (o) ->
+    @operator in @MUTATORS and If.unfoldSoak o, this, 'first'
+
   compileNode: (o) ->
     if @isUnary()
-      return ifn.compile o if @operator in @MUTATORS and ifn = If.unfoldSoak o, this, 'first'
       return @compileUnary o
     return @compileChain o     if @isChainable() and @first.unwrap().isChainable()
     return @compileExistence o if @operator is '?'
@@ -1127,7 +1104,7 @@
     else
       fst = @first
       ref = fst.compile o
-    new Existence(fst).compile(o) + " ? #{ref} : #{ @second.compile o, LVL_ASSIGN }"
+    new Existence(fst).compile(o) + " ? #{ref} : #{ @second.compile o, LVL_LIST }"

   # Compile a unary **Op**.
   compileUnary: (o) ->
@@ -1140,8 +1117,7 @@

   children: ['object', 'array']

-  constructor: (@object, @array) ->
-    super()
+  constructor: (@object, @array) -> super()

   invert: ->
     @negated = not @negated
@@ -1181,8 +1157,7 @@

   isStatement: YES

-  constructor: (@attempt, @error, @recovery, @ensure) ->
-    super()
+  constructor: (@attempt, @error, @recovery, @ensure) -> super()

   makeReturn: ->
     @attempt  = @attempt .makeReturn() if @attempt
@@ -1192,15 +1167,17 @@
   # Compilation is more or less as you would expect -- the *finally* clause
   # is optional, the *catch* is not.
   compileNode: (o) ->
-    o.indent    = @idt 1
-    o.top       = true
-    attemptPart = @attempt.compile(o)
-    errorPart   = if @error then " (#{ @error.compile o }) " else ' '
-    catchPart   = if @recovery
-      " catch#{errorPart}{\n#{ @recovery.compile o }\n#{@tab}}"
-    else unless @ensure or @recovery then ' catch (_e) {}' else ''
-    finallyPart = (@ensure or '') and ' finally {\n' + @ensure.compile(merge o) + "\n#{@tab}}"
-    "#{@tab}try {\n#{attemptPart}\n#{@tab}}#{catchPart}#{finallyPart}"
+    o.indent  = @idt 1
+    errorPart = if @error then " (#{ @error.compile o }) " else ' '
+    catchPart = if @recovery
+      " catch#{errorPart}{\n#{ @recovery.compile o, LVL_TOP }\n#{@tab}}"
+    else unless @ensure or @recovery
+      ' catch (_e) {}'
+    """
+    #{@tab}try {
+    #{ @attempt.compile o, LVL_TOP }
+    #{@tab}}#{ catchPart or '' }
+    """ + if @ensure then " finally {\n#{ @ensure.compile o, LVL_TOP }\n#{@tab}}" else ''

 #### Throw

@@ -1211,14 +1188,12 @@

   isStatement: YES

-  constructor: (@expression) ->
-    super()
+  constructor: (@expression) -> super()

   # A **Throw** is already a return, of sorts...
   makeReturn: THIS

-  compileNode: (o) ->
-    "#{@tab}throw #{ @expression.compile o };"
+  compileNode: (o) -> @tab + "throw #{ @expression.compile o };"

 #### Existence

@@ -1229,8 +1204,7 @@

   children: ['expression']

-  constructor: (@expression) ->
-    super()
+  constructor: (@expression) -> super()

   compileNode: (o) ->
     code = @expression.compile o
@@ -1251,29 +1225,20 @@

   children: ['expression']

-  topSensitive: yes
-
-  constructor: (@expression) ->
-    super()
-
-  isStatement: (o) ->
-    @expression.isStatement o
-  isComplex: ->
-    @expression.isComplex()
-
-  makeReturn: ->
-    @expression.makeReturn()
-
-  compileNode: (o) ->
-    top  = del o, 'top'
+  constructor: (@expression) -> super()
+
+  unwrap    : -> @expression
+  isComplex : -> @expression.isComplex()
+  makeReturn: -> @expression.makeReturn()
+
+  compileNode: (o) ->
     expr = @expression
     if expr instanceof Value and expr.isAtomic()
       expr.tags.front = @tags.front
       return expr.compile o
+    bare = o.level < LVL_OP and (expr instanceof Op or expr instanceof Call)
     code = expr.compile o, LVL_PAREN
-    return code if o.level < LVL_OP and (expr instanceof Op or expr instanceof Call)
-    return (if top then @tab + code + ';' else code) if expr.isStatement o
-    "(#{code})"
+    if bare then code else "(#{code})"

 #### For

@@ -1288,8 +1253,7 @@

   children: ['body', 'source', 'guard', 'step', 'from', 'to']

-  topSensitive: yes
-  isStatement : YES
+  isStatement: YES

   constructor: (@body, head) ->
     if head.index instanceof Value
@@ -1316,7 +1280,6 @@
   # some cannot.
   compileNode: (o) ->
     {scope} = o
-    top     = del(o, 'top') and not @returns
     name    = not @pattern and @name?.compile o
     index   = @index?.compile o
     ivar    = if not index then scope.freeVariable 'i' else index
@@ -1338,9 +1301,9 @@
       if name
         [sourcePart, svar] = @source.compileLoopReference o, 'ref'
       else
-        sourcePart = svar = @source.compile o
+        sourcePart = svar = @source.compile o, LVL_PAREN
       namePart = if @pattern
-        new Assign(@name, new Literal "#{svar}[#{ivar}]").compile merge o, top: on
+        new Assign(@name, new Literal "#{svar}[#{ivar}]").compile o, LVL_TOP
       else if name
         "#{name} = #{svar}[#{ivar}]"
       unless @object
@@ -1351,28 +1314,29 @@
           lvar = scope.freeVariable 'len'
           vars = "#{ivar} = 0, #{lvar} = #{svar}.length"
           cond = "#{ivar} < #{lvar}"
-    defPart   = ''
+    defPart = ''
     if @object
       forPart   = "#{ivar} in #{sourcePart}"
       guardPart = not @raw and
         "#{idt}if (!#{ utility 'hasProp' }.call(#{svar}, #{ivar})) continue;\n"
     else
-      vars += ", #{step}" if step isnt pvar
+      vars   += ", #{step}" if step isnt pvar
       defPart = "#{@tab}#{sourcePart};\n" if svar isnt sourcePart
-      forPart = "#{vars}; #{cond}; " + switch +pvar
-        when  1 then ivar + '++'
-        when -1 then ivar + '--'
-        else ivar + if pvar < 0 then ' -= ' + pvar.slice 1 else ' += ' + pvar
-    unless top
+      forPart = "#{vars}; #{cond}; " + ivar + switch +pvar
+        when  1 then '++'
+        when -1 then '--'
+        else (if pvar < 0 then ' -= ' + pvar.slice 1 else ' += ' + pvar)
+    if o.level > LVL_TOP or @returns
       rvar      = scope.freeVariable 'result'
       defPart  += "#{@tab}#{rvar} = [];\n"
       resultRet = @compileReturnValue rvar, o
       body      = Push.wrap rvar, body
-    body = Expressions.wrap [new If @guard, body] if @guard
-    varPart = "#{idt}#{namePart};\n" if namePart
+    body     = Expressions.wrap [new If @guard, body] if @guard
+    varPart  = "#{idt}#{namePart};\n" if namePart
+    o.indent = idt
     """
-    #{ defPart or '' }#{@tab}for (#{forPart}) {
-    #{ guardPart or '' }#{varPart}#{ body.compile merge o, indent: idt, top: on }
+    #{ defPart   or '' }#{@tab}for (#{forPart}) {
+    #{ guardPart or '' }#{varPart}#{ body.compile o, LVL_TOP }
     #{@tab}}#{ resultRet or '' }
     """

@@ -1385,29 +1349,28 @@

   isStatement: YES

-  constructor: (@subject, @cases, @otherwise) ->
-    super()
+  constructor: (@subject, @cases, @otherwise) -> super()

   makeReturn: ->
     pair[1].makeReturn() for pair in @cases
-    @otherwise.makeReturn() if @otherwise
+    @otherwise?.makeReturn()
     this

   compileNode: (o) ->
-    idt1  = @idt 1
-    idt2  = o.indent = @idt 2
-    o.top = yes
-    code  = "#{ @tab }switch (#{ @subject?.compile(o) or true }) {"
-    for [conditions, block] in @cases
-      for condition in flatten [conditions]
-        condition = condition.invert().invert() unless @subject
-        code += "\n#{ idt1 }case #{ condition.compile o }:"
-      code += "\n#{ block.compile o }"
-      code += "\n#{ idt2 }break;" unless last(block.expressions) instanceof Return
-    if @otherwise
-      code += "\n#{ idt1 }default:\n#{ @otherwise.compile o }"
-    code += "\n#{ @tab }}"
-    code
+    idt1 = @idt 1
+    idt2 = o.indent = @idt 2
+    code = @tab + "switch (#{ @subject?.compile(o, LVL_PAREN) or true }) {\n"
+    for [conditions, block], i in @cases
+      for cond in flatten [conditions]
+        cond  = cond.invert().invert() unless @subject
+        code += idt1 + "case #{ cond.compile o, LVL_PAREN }:\n"
+      code += block.compile(o, LVL_TOP) + '\n'
+      break if i is @cases.length - 1 and not @otherwise
+      for expr in block.expressions by -1 when expr not instanceof Comment
+        code += idt2 + 'break;\n' unless expr instanceof Return
+        break
+    code += idt1 + "default:\n#{ @otherwise.compile o, LVL_TOP }\n" if @otherwise
+    code +  @tab + '}'

 #### If

@@ -1419,8 +1382,6 @@
 exports.If = class If extends Base

   children: ['condition', 'body', 'elseBody']
-
-  topSensitive: yes

   constructor: (condition, @body, tags) ->
     @tags      = tags or= {}
@@ -1444,7 +1405,7 @@
   # The **If** only compiles into a statement if either of its bodies needs
   # to be a statement. Otherwise a conditional operator is safe.
   isStatement: (o) ->
-    @statement or= o?.top or @bodyNode().isStatement(o) or @elseBodyNode()?.isStatement(o)
+    o?.level is LVL_TOP or @bodyNode().isStatement(o) or @elseBodyNode()?.isStatement(o)

   compileNode: (o) ->
     if @isStatement o then @compileStatement o else @compileExpression o
@@ -1463,24 +1424,23 @@
   # Compile the **If** as a regular *if-else* statement. Flattened chains
   # force inner *else* bodies into statement form.
   compileStatement: (o) ->
-    top      = del o, 'top'
     child    = del o, 'chainChild'
-    condO    = merge o
+    cond     = @condition.compile o, LVL_PAREN
     o.indent = @idt 1
-    o.top    = true
-    ifPart   = "if (#{ @condition.compile condO, LVL_PAREN }) {\n#{ @body.compile o }\n#{@tab}}"
+    body     = @ensureExpressions(@body).compile o
+    ifPart   = "if (#{cond}) {\n#{body}\n#{@tab}}"
     ifPart   = @tab + ifPart unless child
     return ifPart unless @elseBody
     ifPart + ' else ' + if @isChain
        @elseBodyNode().compile merge o, indent: @tab, chainChild: true
     else
-      "{\n#{ @elseBody.compile o }\n#{@tab}}"
+      "{\n#{ @elseBody.compile o, LVL_TOP }\n#{@tab}}"

   # Compile the If as a conditional operator.
   compileExpression: (o) ->
-    code = @condition .compile(o, LVL_COND)   + ' ? ' +
-           @bodyNode().compile(o, LVL_ASSIGN) + ' : ' +
-           @elseBodyNode()?.compile o, LVL_ASSIGN
+    code = @condition .compile(o, LVL_COND) + ' ? ' +
+           @bodyNode().compile(o, LVL_LIST) + ' : ' +
+           @elseBodyNode()?.compile o, LVL_LIST
     if o.level >= LVL_COND then "(#{code})" else code

   unfoldSoak: -> @soakNode and this
@@ -1543,7 +1503,7 @@
   # Correctly set up a prototype chain for inheritance, including a reference
   # to the superclass for `super()` calls. See:
   # [goog.inherits](http://closure-library.googlecode.com/svn/docs/closureGoogBase.js.source.html#line1206).
-  extends:  '''
+  extends: '''
     function(child, parent) {
       function ctor() { this.constructor = child; }
       ctor.prototype = parent.prototype;
@@ -1570,16 +1530,15 @@

   # Shortcuts to speed up the lookup time for native functions.
   hasProp: 'Object.prototype.hasOwnProperty'
-  slice:   'Array.prototype.slice'
+  slice  : 'Array.prototype.slice'

 # Levels indicates a node's position in the AST.
 LVL_TOP    = 0  # ...;
 LVL_PAREN  = 1  # (...)
 LVL_LIST   = 2  # [...]
-LVL_ASSIGN = 3  # x = ...
-LVL_COND   = 4  # ... ? a : b
-LVL_OP     = 5  # +...
-LVL_ACCESS = 6  # ...[0]
+LVL_COND   = 3  # ... ? x : y
+LVL_OP     = 4  # !...
+LVL_ACCESS = 5  # ...[0]

 # Tabs are two spaces for pretty printing.
 TAB = '  '
@@ -1603,3 +1562,5 @@
   ref = "__#{name}"
   Scope.root.assign ref, UTILITIES[name]
   ref
+
+multident = (code, tab) -> code.replace /\n/g, '$&' + tab


reminder comment code

reminder comment code

--- bootstrap-magic.coffee
+++ bootstrap-magic.coffee
@@ -203,6 +203,8 @@
           data.step = 1
           data.min = 0
           data.max = 100
+
+        #get values for obj.reference.value

     console.log "filtered it: ", allPx


Include telegram room data as part of the user object.

--- src/telegram.coffee
+++ src/telegram.coffee
@@ -89,14 +89,16 @@
     # Create a new user in relation with a chat_id
     #
     # @param object user
-    # @param int    chat_id
+    # @param object chat
     #
     # @return object
     ###
-    createUser: (user, chat_id) ->
+    createUser: (user, chat) ->
         opts = user
         opts.name = opts.username
-        opts.room = chat_id
+        opts.room = chat.id
+        opts.telegram_chat = chat
+
         result = @robot.brain.userForId user.id, opts
         current = result.first_name + result.last_name + result.username
         update = user.first_name + user.last_name + user.username
@@ -179,24 +181,24 @@

             @robot.logger.debug "Received message: " + message.from.username + " said '" + text + "'"

-            user = @createUser message.from, message.chat.id
+            user = @createUser message.from, message.chat
             @receive new TextMessage user, text, message.message_id

         # Join event
         else if message.new_chat_participant
-            user = @createUser message.new_chat_participant, message.chat.id
+            user = @createUser message.new_chat_participant, message.chat
             @robot.logger.info "User " + user.id + " joined chat " + message.chat.id
             @receive new EnterMessage user, null, message.message_id

         # Exit event
         else if message.left_chat_participant
-            user = @createUser message.left_chat_participant, message.chat.id
+            user = @createUser message.left_chat_participant, message.chat
             @robot.logger.info "User " + user.id + " left chat " + message.chat.id
             @receive new LeaveMessage user, null, message.message_id

         # Chat topic event
         else if message.new_chat_title
-            user = @createUser message.from, message.chat.id
+            user = @createUser message.from, message.chat
             @robot.logger.info "User " + user.id + " changed chat " + message.chat.id + " title: " + message.new_chat_title
             @receive new TopicMessage user, message.new_chat_title, message.message_id


fixed some pathing heuristics

fixed some pathing heuristics

--- src/tile-map.coffee
+++ src/tile-map.coffee
@@ -132,18 +132,43 @@
       return false
     return hash

+Directions =
+  Left: 0
+  BottomLeft: 1
+  Bottom: 2
+  BottomRight: 3
+  Right: 4
+  TopRight: 5
+  Top: 6
+  TopLeft: 7
+  All: 8
+
+getNeighbors = (status)->
+  tile = status.tile
+  neighbors = []
+
+  neighbors.push(tile.top) if tile.top?
+  neighbors.push(tile.topLeft) if tile.topLeft?
+  neighbors.push(tile.left) if tile.left?
+  neighbors.push(tile.bottomLeft) if tile.bottomLeft?
+  neighbors.push(tile.bottom) if tile.bottom?
+  neighbors.push(tile.bottomRight) if tile.bottomRight?
+  neighbors.push(tile.right) if tile.right?
+  neighbors.push(tile.topRight) if tile.topRight?
+
+  return neighbors
+
 TileMap.AStar = (startTile, endTile, cost)->
   statuses = {}

-  lastTile = null
-
   startStatus = statuses[startTile.id] =
     tile: startTile
-    parent: lastTile
+    parent: null
     closed: false
     cost: 0
     heuristicCost: 0
     predictedCost: 0
+    direction: Directions.All
     opened: true

   openList = new Heap
@@ -161,22 +186,13 @@
         tile = statuses[tile.id].parent
       return waypoints.reverse()

-    neighbors = []
-    neighbors.push(tile.top) if tile.top?
-    neighbors.push(tile.topLeft) if tile.topLeft?
-    neighbors.push(tile.left) if tile.left?
-    neighbors.push(tile.bottomLeft) if tile.bottomLeft?
-    neighbors.push(tile.bottom) if tile.bottom?
-    neighbors.push(tile.bottomRight) if tile.bottomRight?
-    neighbors.push(tile.right) if tile.right?
-    neighbors.push(tile.topRight) if tile.topRight?
+    neighbors = getNeighbors(status)

     for neighbor in neighbors
       neighborStatus = statuses[neighbor.id]
       if !neighborStatus?
         neighborStatus = statuses[neighbor.id] =
           tile: neighbor
-          parent: tile
           opened: false
       if !neighborStatus.closed
         coef = if neighbor.position.x != tile.position.x && neighbor.position.y != tile.position.y then SQRT2 else 1
@@ -190,6 +206,7 @@
           newCost = status.cost + moveCost

           if !neighborStatus.opened || newCost < neighborStatus.cost
+            neighborStatus.parent = tile
             heuristicCost = neighbor.position.sub(neighbor.position).length()
             predictedCost = newCost + heuristicCost


trigger folioAntiCacheDone in anti-cache JS

--- app/assets/javascripts/folio/anti-cache.coffee
+++ app/assets/javascripts/folio/anti-cache.coffee
@@ -18,9 +18,15 @@
     $item.replaceWith(replacement)

   if immediate
-    $old.addClass('folio-anti-cache--done')
+    $old
+      .addClass('folio-anti-cache--done')
+      .trigger('folioAntiCacheDone')
   else
-    setTimeout (-> $old.addClass('folio-anti-cache--done')), 0
+    setTimeout (->
+      $old
+        .addClass('folio-anti-cache--done')
+        .trigger('folioAntiCacheDone')
+    ), 0

   saveAntiCacheHtml($old)


refs #102: Fixes issue where page scrolls on ajax responses

--- app/assets/javascripts/housing_forms.js.coffee
+++ app/assets/javascripts/housing_forms.js.coffee
@@ -4,7 +4,7 @@
 if window.location.pathname.match(/\/form$/)
 	$ ->

-		changeSection = ->
+		changeSection = (e, noscroll)->
 			hash = window.location.hash
 			sectionId = hash.substring 1
 			$('.section').each ()->
@@ -20,20 +20,20 @@
 					parent.toggleClass 'active', true
 				else
 					parent.toggleClass 'active', false
-			window.scrollTo 0, 0
+			window.scrollTo 0, 0 unless noscroll

 		$(window).on 'hashchange', changeSection

 		showDownloadWarning = ->
 			$('#download-warning').modal()

-		initialize = ->
+		initialize = (e)->
 			$('.download-forms').click(showDownloadWarning)
 			firstSectionHash = $('.wizard-nav a').eq(0).attr 'href'
 			if window.location.hash.length <= 1
 				window.location.hash = firstSectionHash
 			else
-				changeSection()
+				changeSection null, !!e

 		$(document).ajaxComplete initialize
 		initialize()


ignore uncaught promise if caused by abort

--- resources/assets/coffee/react/beatmaps/main.coffee
+++ resources/assets/coffee/react/beatmaps/main.coffee
@@ -197,6 +197,8 @@
         more: data.cursor? && data.total > beatmaps.length
         url: @state.paging.url
       recommendedDifficulty: data.recommended_difficulty
+    .catch (error) ->
+      throw error unless error.readyState == 0

   fetchResults: (newQuery) =>


Typo auditor -> audit.auditor

Typo auditor -> audit.auditor

--- routes/postreceive.coffee
+++ routes/postreceive.coffee
@@ -34,7 +34,7 @@
         __Discuss on the original commit's diff: #{audit.commit.id.slice(0, 10)}__
         /cc @#{audit.commit.author.username}
         """
-      assignee: auditor
+      assignee: audit.auditor
       labels: ['audit']

     requestCb = (err, _res, body) ->


Delete default-config.cson

Delete default-config.cson

--- default-config.cson
+++ default-config.cson
@@ -1,21 +0,0 @@
-poi:
-  theme: "paperdark"
-  layout: "horizontal"
-  window:
-    x: 60
-    y: 60
-    width: 1274
-    height: 616
-  webview:
-    width: 800
-  shortcut:
-    bosskey: "CmdOrCtrl+i"
-  useSVGIcon: false
-proxy:
-  use: "none"
-  socks5:
-    host: "127.0.0.1"
-    port: 1080
-  http:
-    host: "127.0.0.1"
-    port: 8099


Allow to hide original mail when editing a draft, refs #79

--- client/app/components/compose.coffee
+++ client/app/components/compose.coffee
@@ -530,8 +530,16 @@
                     , 0
             )
             # Allow to hide original message
-            jQuery('#email-compose .rt-editor .originalToggle').on('click', ->
-                jQuery('#email-compose .rt-editor').toggleClass('folded')
+            if document.querySelector('.rt-editor blockquote') and not document.querySelector('.rt-editor .originalToggle')
+                try
+                    header = jQuery('.rt-editor blockquote').eq(0).prev()
+                    header.text(header.text().replace('…', ''))
+                    header.append('<span class="originalToggle">…</>')
+                catch e
+                    console.log e
+            jQuery('.rt-editor .originalToggle').on('click', ->
+                console.log 'click'
+                jQuery('.rt-editor').toggleClass('folded')
             )
         else
             # Text message