Remove watcher.

Remove watcher.

--- src/watcher.coffee
+++ src/watcher.coffee
@@ -1,38 +0,0 @@
-path      = require 'path'
-vm        = require 'vm'
-{patcher} = require './utils'
-{watch}   = require 'fs'
-
-module.exports = (callback) ->
-  hooks = {}
-
-  updateHooks = ->
-    for ext, handler of require.extensions
-      do (ext, handler) ->
-        if hooks[ext] != require.extensions[ext]
-          hooks[ext] = require.extensions[ext] = (module, filename) ->
-            # Watch module if it hasn't been loaded before
-            callback module.filename unless module.loaded
-
-            # Invoke original handler
-            handler module, filename
-
-            # Make sure the module did not hijack the handler
-            updateHooks()
-
-  # Hook 'em.
-  updateHooks()
-
-  # Patch VM module.
-  {patch} = patcher vm
-  methods =
-    createScript: 1
-    runInThisContext: 1
-    runInNewContext: 2
-    runInContext: 2
-
-  for method, idx of methods
-    patch method, (original) ->
-      ->
-        watch(file) if file = arguments[idx]
-        original arguments


Renderer test: fix render not called

Renderer test: fix render not called

--- test/src/Renderer.coffee
+++ test/src/Renderer.coffee
@@ -16,7 +16,7 @@

     describe '#render()', ->
         it 'should render to null', ->
-            expect(new Renderer(null).render).null
+            expect(new Renderer(null).render()).null
             expect(new Renderer('abc').render()).null
         it 'basic render', ->
             r = new Renderer(['abc', {rb: '今日', rt: 'きょう'}, 'xyz'])


Test creating multiple process connections

--- test/test_process.coffee
+++ test/test_process.coffee
@@ -82,6 +82,52 @@

         process.quit()

+exports.testCreateMultipleConnections = (test) ->
+  test.expect 8
+
+  process = createProcess config
+
+  process.once 'ready', ->
+    test.ok true
+
+  process.on 'exit', ->
+    test.ok true
+    test.done()
+
+  count = 2
+  quit = ->
+    count--
+    if count is 0
+      process.quit()
+
+  openConnections = 0
+
+  process.createConnection (client) ->
+    openConnections++
+    client.on 'close', -> openConnections--
+    test.same 1, openConnections
+
+    test.ok client
+    request = client.request 'GET', '/foo', {}
+    request.end()
+
+    request.on 'response', (response) ->
+      test.ok response
+      response.on 'end', -> quit()
+
+  process.createConnection (client) ->
+    openConnections++
+    client.on 'close', -> openConnections--
+    test.same 1, openConnections
+
+    test.ok client
+    request = client.request 'GET', '/bar', {}
+    request.end()
+
+    request.on 'response', (response) ->
+      test.ok response
+      response.on 'end', -> quit()
+
 exports.testCreateConnectionWithClientException = (test) ->
   test.expect 5


sending all events to particles

sending all events to particles

--- test/timed_events.coffee
+++ test/timed_events.coffee
@@ -1,6 +1,6 @@
 module "TimedEvents"

-test "#every", ->
+test "#every", 4, ->
   gameObject = GameObject()

   gameObject.every 1, ->
@@ -8,7 +8,7 @@

   4.times ->
     gameObject.trigger "update", 1
-    gameObject.trigger "after"
+    gameObject.trigger "afterUpate", 1

 # Clear out the module
 module()


registerResourceTypes common helper to be used by standard gint-* modules

--- server/common/index.coffee
+++ server/common/index.coffee
@@ -3,8 +3,24 @@
     object[key] = val
   object

+registerResourceTypes = (mongoose, models) ->
+  mongoose.models['System'].find {}, (err, systems) ->
+    systems.forEach (system) ->
+      for key, val of models
+        resourceType =
+          name: val.name
+          systemId: system._id
+
+        mongoose.models['Resource'].update resourceType
+        , resourceType
+        , {upsert: true}
+        , (err) ->
+          if err
+            console.log err
+
 module.exports =
   extend: extend
   rest: require './rest'
   timePatterns: require '../../common/timePatterns'
-  mongo: require './mongo'+  mongo: require './mongo'
+  registerResourceTypes: registerResourceTypes


Added Delicious, Magicscroll and Tumblr commands

--- commands.coffee
+++ commands.coffee
@@ -2,6 +2,7 @@

 module.exports =
   "Add to Backtick": "8dde812886f949e241e0"
+  "Add to Delicious": "7881195"
   "Amazon Wish List": "7699000"
   "App.net Share": "5a543b6f98460f5e5c8b"
   "BeeLine Reader": "9f6c45105b28e53b8d3e"
@@ -20,8 +21,10 @@
   "Instapaper Read Later": "7f14f0119b48d777637a"
   "Is it Down?": "7361458"
   "Kern.js": "10256b190ed5006e894c"
+  "Magicscroll Web Reader": "7880657"
   "Markup": "c96d3e5e2bfa2a587f73"
   "Pinterest Share": "3e50b60e77fb8283c00e"
+  "Post to Tumblr": "7880974"
   "Readability Read Later": "26152acc6cd5137df078"
   "Readability Read Now": "9b3edb25051030f6a8b2"
   "Reddit Submit": "9685429b0bc17b9b2df0"


ApiGenerator: add operationId for better client generation

--- scripts/api-generator.coffee
+++ scripts/api-generator.coffee
@@ -206,6 +206,7 @@
       post:
         tags: [ name ]
         consumes: [ 'application/json' ]
+        operationId: "#{name}.#{method}"
         parameters: parameters ? []
         security: [ { Bearer: [] } ]
         description: docs[name]['static'][method]?.description ? ''
@@ -246,6 +247,7 @@
     paths["/remote.api/#{name}.#{method}/{id}"] =
       post:
         tags: [ name ]
+        operationId: "#{name}.#{method}"
         security: [ { Bearer: [] } ]
         consumes: [ 'application/json' ]
         description: docs[name]['instance'][method]?.description ? ''


Minor fixes in modals

Minor fixes in modals

--- scripts/views/profile.coffee
+++ scripts/views/profile.coffee
@@ -92,10 +92,11 @@
                                             $resendConfirmationForm.trigger 'submit'

                                         $resendConfirmationModal.on 'show.bs.modal', (e) ->
+                                            $('#resend-confirmation-email').val team.email
                                             $resendConfirmationSubmitError.text ''

                                         $resendConfirmationModal.on 'shown.bs.modal', (e) ->
-                                            $('#resend-confirmation-email').val(team.email).focus()
+                                            $('#resend-confirmation-email').focus()

                                         $resendConfirmationForm.on 'submit', (e) ->
                                             e.preventDefault()
@@ -137,10 +138,11 @@
                                             $changeEmailForm.trigger 'submit'

                                         $changeEmailModal.on 'show.bs.modal', (e) ->
+                                            $('#change-email-new').val ''
                                             $changeEmailSubmitError.text ''

                                         $changeEmailModal.on 'shown.bs.modal', (e) ->
-                                            $('#change-email-new').val('').focus()
+                                            $('#change-email-new').focus()

                                         $changeEmailForm.on 'submit', (e) ->
                                             e.preventDefault()
@@ -183,12 +185,13 @@
                                         $editProfileForm.trigger 'submit'

                                     $editProfileModal.on 'show.bs.modal', (e) ->
-                                        $editProfileSubmitError.text ''
-
-                                    $editProfileModal.on 'shown.bs.modal', (e) ->
                                         $('#edit-profile-country').val(team.country).focus()
                                         $('#edit-profile-locality').val team.locality
                                         $('#edit-profile-institution').val team.institution
+                                        $editProfileSubmitError.text ''
+
+                                    $editProfileModal.on 'shown.bs.modal', (e) ->
+                                        $('#edit-profile-country').focus()

                                     $editProfileForm.on 'submit', (e) ->
                                         e.preventDefault()
@@ -231,6 +234,9 @@
                                         $changePasswordForm.trigger 'submit'

                                     $changePasswordModal.on 'show.bs.modal', (e) ->
+                                        $('#change-pwd-current').val ''
+                                        $('#change-pwd-new').val ''
+                                        $('#change-pwd-confirm-new').val ''
                                         $changePasswordSubmitError.text ''

                                     $changePasswordModal.on 'shown.bs.modal', (e) ->


Whoops

Whoops

--- app/assets/javascripts/farmbot_app/services/device_service.js.coffee
+++ app/assets/javascripts/farmbot_app/services/device_service.js.coffee
@@ -86,13 +86,10 @@

   send: (msg, cb = (d) -> console.log(d)) ->
     if !!@connection
-      doIt = (d) ->
-        cb(d)
-        console.log d
       @connection.message
         devices: @current.uuid
         payload: msg
-        , => @$rootScope.$apply(doIt)
+        , => @$rootScope.$apply(cb)
     else
       console.warn("Already connected to Meshblu")


Close open navigation when sidebar link is clicked on mobile

--- app/assets/javascripts/tenon/features/hamburger_navigation.js.coffee
+++ app/assets/javascripts/tenon/features/hamburger_navigation.js.coffee
@@ -6,19 +6,20 @@
     @openClass = 'mobile-open'

     # listeners
-    @$toggle.on('click', @toggleNavigation)
+    @$toggle.on('click', @toggleNav)
+    $(document).on('click', '.sidebar-container.mobile-open a', @_closeNav)

-  toggleNavigation: (e) =>
+  toggleNav: (e) =>
     e.preventDefault()
     if @$toggle.hasClass(@openClass)
-      @_closeNavigation()
+      @_closeNav()
     else
-      @_openNavigation()
+      @_openNav()

-  _closeNavigation: () =>
+  _closeNav: () =>
     @$toggle.removeClass(@openClass)
     @$navigation.removeClass(@openClass)

-  _openNavigation: () =>
+  _openNav: () =>
     @$toggle.addClass(@openClass)
     @$navigation.addClass(@openClass)


add plain text link to email address confirmation emails

--- services/web/app/coffee/Features/Email/EmailBuilder.coffee
+++ services/web/app/coffee/Features/Email/EmailBuilder.coffee
@@ -97,6 +97,8 @@
 	title: () -> "Confirm Email"
 	message: () -> "Please confirm your email on #{settings.appName}."
 	ctaText: () -> "Confirm Email"
+	secondaryMessage: (opts) ->
+		"If the button does not appear, open this link in your browser: #{opts.confirmEmailUrl}"
 	ctaURL: (opts) -> opts.confirmEmailUrl
 })


Get rid of conflicting view

Get rid of conflicting view

--- core/app/backbone/views/facts/top_fact_evidence_view.coffee
+++ core/app/backbone/views/facts/top_fact_evidence_view.coffee
@@ -1,3 +0,0 @@
-class window.TopFactEvidenceView extends Backbone.Marionette.Layout
-  template: 'facts/top_fact_evidence'
-  className: -> 'top-fact-evidence'


Client: Activity: Added handleFollowChannelSuccess/handleUnfollowChannelSuccess event handler tests

--- client/activity/lib/flux/test/channelparticipantidsstore.coffee
+++ client/activity/lib/flux/test/channelparticipantidsstore.coffee
@@ -60,3 +60,37 @@
       expect(storeState.foo).to.eql {'bar'}

+  describe '#handleFollowChannelSuccess', ->
+
+    it 'adds given accountId to the channel of ChannelParticipantIdsStore when follow channel action succeed', ->
+
+      @reactor.dispatch actions.FOLLOW_CHANNEL_SUCCESS, {
+        channelId: 'foo', accountId: 'bar'
+      }
+
+      storeState = @reactor.evaluateToJS [ChannelParticipantIdsStore.getterPath]
+
+      expect(storeState.foo).to.eql {'bar'}
+
+
+  describe '#handleUnfollowChannelSuccess', ->
+
+    it 'removes given accountId to the channel of ChannelParticipantIdsStore when unfollow channel action succeed', ->
+
+      @reactor.dispatch actions.FOLLOW_CHANNEL_SUCCESS, {
+        channelId: 'testchannel_1', accountId: 'testAccount_1'
+      }
+
+      @reactor.dispatch actions.FOLLOW_CHANNEL_SUCCESS, {
+        channelId: 'testchannel_2', accountId: 'testAccount_2'
+      }
+
+      @reactor.dispatch actions.UNFOLLOW_CHANNEL_SUCCESS, {
+        channelId: 'testchannel_1', accountId: 'testAccount_1'
+      }
+
+      storeState = @reactor.evaluateToJS [ChannelParticipantIdsStore.getterPath]
+
+      expect(storeState.testchannel_2).to.eql {'testAccount_2'}
+      expect(storeState.testchannel_1).to.eql {}
+


[fix] Stopped replacing the New Feedback form with the search tips (5m)

--- app/assets/javascripts/houston/feedback/views/conversations_view.coffee
+++ app/assets/javascripts/houston/feedback/views/conversations_view.coffee
@@ -157,6 +157,8 @@
             conversations: (conversation.toJSON() for conversation in @sortedConversations.slice(@offset, @offset + 50))
           promise

+    @editNothing()
+

   resultFocused: (e)->
@@ -328,8 +330,6 @@
     $('#tags_report').html @renderTagCloud
       topTags: tags.slice(0, MAX_TAGS)
       extraTags: tags.slice(MAX_TAGS)
-
-    @editNothing()

   focusSearch: ->
     @selectNone()


Take into account the lb unit name

Take into account the lb unit name

--- app/assets/javascripts/admin/line_items/controllers/line_items_controller.js.coffee
+++ app/assets/javascripts/admin/line_items/controllers/line_items_controller.js.coffee
@@ -165,7 +165,7 @@
   $scope.getGroupBySizeFormattedValueWithUnitName = (value, unitsProduct, unitsVariant) ->
     scale = $scope.getScale(unitsProduct, unitsVariant)
     if scale
-      value = value / scale if scale != 28.35 && scale != 1 # divide by scale if not smallest unit
+      value = value / scale if scale != 28.35 && scale != 1 && scale != 453.6 # divide by scale if not smallest unit
       $scope.getFormattedValueWithUnitName(value, unitsProduct, unitsVariant, scale)
     else
       ''
@@ -183,7 +183,7 @@
       $scope.selectedUnitsProduct.hasOwnProperty("variant_unit") &&
       ( $scope.selectedUnitsProduct.variant_unit == "weight" || $scope.selectedUnitsProduct.variant_unit == "volume" )
         scale = $scope.getScale($scope.selectedUnitsProduct, $scope.selectedUnitsVariant)
-        sumOfUnitValues = sumOfUnitValues / scale if scale == 28.35 # divide by scale if smallest unit
+        sumOfUnitValues = sumOfUnitValues / scale if scale == 28.35 || scale == 453.6 # divide by scale if smallest unit
         $scope.roundToThreeDecimals(sumOfUnitValues / $scope.selectedUnitsProduct.group_buy_unit_size * $scope.selectedUnitsVariant.unit_value)
     else
       ''


refactor pastemanager for testing

refactor pastemanager for testing

--- src/modules/paste-manager.coffee
+++ src/modules/paste-manager.coffee
@@ -14,27 +14,25 @@
         'position': 'absolute'
         'top': '50%'
     )
-    this.initListeners()
+    DOM.addEventListener(@editorContainer.ownerDocument, 'paste', _.bind(this._paste, this))

-  initListeners: ->
-    DOM.addEventListener(@editorContainer.ownerDocument, 'paste', =>
-      oldDocLength = @quill.getLength()
-      range = @quill.getSelection()
-      return unless range?
-      @container.innerHTML = ""
-      @container.focus()
-      _.defer( =>
-        doc = new Document(@container, @quill.options)
-        delta = doc.toDelta()
-        lengthAdded = delta.endLength
-        delta.ops.unshift(new Tandem.RetainOp(0, range.start)) if range.start > 0
-        delta.ops.push(new Tandem.RetainOp(range.end, oldDocLength)) if range.end < oldDocLength
-        delta.endLength += (@quill.getLength() - (range.end - range.start))
-        delta.startLength = oldDocLength
-        @quill.updateContents(delta, 'user')
-        @quill.focus()
-        @quill.setSelection(range.start + lengthAdded, range.start + lengthAdded)
-      )
+  _paste: ->
+    oldDocLength = @quill.getLength()
+    range = @quill.getSelection()
+    return unless range?
+    @container.innerHTML = ""
+    @container.focus()
+    _.defer( =>
+      doc = new Document(@container, @quill.options)
+      delta = doc.toDelta()
+      lengthAdded = delta.endLength
+      delta.ops.unshift(new Tandem.RetainOp(0, range.start)) if range.start > 0
+      delta.ops.push(new Tandem.RetainOp(range.end, oldDocLength)) if range.end < oldDocLength
+      delta.endLength += (@quill.getLength() - (range.end - range.start))
+      delta.startLength = oldDocLength
+      @quill.updateContents(delta, 'user')
+      @quill.focus()
+      @quill.setSelection(range.start + lengthAdded, range.start + lengthAdded)
     )


typo in homeloginbar

typo in homeloginbar

--- client/app/Applications/Home.kdapplication/Views/homeloginbar.coffee
+++ client/app/Applications/Home.kdapplication/Views/homeloginbar.coffee
@@ -49,7 +49,7 @@
         {entryPoint} = KD.config
         if entryPoint
           requiresLogin =>
-            @appManager.tell 'groups', "showRequestAccessModal", @group, @policy, (err)=>
+            @appManager.tell 'Groups', "showRequestAccessModal", @group, @policy, (err)=>
               unless err
                 @request.hide()
                 @requested.show()
@@ -91,7 +91,7 @@
         href      : "#"
       click       : (event)=>
         @utils.stopDOMEvent event
-        requiresLogin => @appManager.tell 'groups', "joinGroup", @group
+        requiresLogin => @appManager.tell 'Groups', "joinGroup", @group

     @requested    = new CustomLinkView
       tagName     : "a"
@@ -122,7 +122,7 @@
                   color    : "#ffffff"
                   diameter : 16
                 callback   : =>
-                  @appManager.tell 'groups', 'cancelGroupRequest', @group, (err)=>
+                  @appManager.tell 'Groups', 'cancelGroupRequest', @group, (err)=>
                     modal.buttons.Cancel.hideLoader()
                     @handleBackendResponse err, 'Successfully cancelled request!'
                     modal.destroy()


webterm: Disabled tab title change.

webterm: Disabled tab title change.

--- client/landing/app/Applications/WebTerm.kdapplication/AppView.coffee
+++ client/landing/app/Applications/WebTerm.kdapplication/AppView.coffee
@@ -40,7 +40,12 @@
     @terminal = new WebTerm.Terminal @container.$()
     @terminal.sessionEndedCallback = (sessions) =>
       @getSingleton('mainView').mainTabView.removePaneByView this
+      #@propagateEvent
+      #  KDEventType  : "ApplicationWantsToClose"
+      #  globalEvent  : yes
+      #, {options: null, data: this}
       return
+
       @server.getSessions (sessions) =>
         keys = Object.keys sessions
         keys.sort (a, b) ->
@@ -58,7 +63,7 @@
             mainView: this
         @sessionBox.show()
     @terminal.setTitleCallback = (title) =>
-      @tabPane.setTitle title
+      #@tabPane.setTitle title

     @listenWindowResize()


Remove autoplay limit

Remove autoplay limit


fix help for `hubot datadog graph <graph> <range>`

--- src/scripts/datadog-graph.coffee
+++ src/scripts/datadog-graph.coffee
@@ -7,7 +7,7 @@
 #   HUBOT_DATADOG_GRAPH_USE_SLACK
 #
 # Commands:
-#   hubot datadog graph <graph> - take a graph snapshot using the Datadog API
+#   hubot datadog graph <graph> <range> - take a graph snapshot using the Datadog API
 #   hubot datadog graph config add <graph> <query>
 #   hubot datadog graph config list
 #   hubot datadog graph config remove <graph>


rewrite email tests

rewrite email tests

--- src/test/validators/email.coffee
+++ src/test/validators/email.coffee
@@ -10,21 +10,35 @@

   beforeEach(module('CoolFormValidators'))

+  fields =
+    f1:
+      value: 'fred.delbos@gmail.com'
+    f2:
+      value: 'fred@mail.oko.xasxa.com'
+    f3:
+      value: null
+    f4: {}
+    f5:
+      value: 42
+    f6:
+      'daasdasd.com'
+    f7:
+      'dasda@asda'
+    f8:
+      '@asda.com'
+
+
   it 'validate a correct email', inject((emailValidator) ->
-    expect(emailValidator.validator('f', {'f':'fred.delbos@gmail.com'})).toEqual true
-    expect(emailValidator.validator('f', {'f':'fred@mail.oko.xasxa.com'})).toEqual true
+    expect(emailValidator.validator('f1', fields)).toEqual true
+    expect(emailValidator.validator('f2', fields)).toEqual true
   )

   it 'wrong email', inject((emailValidator) ->
-    expect(emailValidator.validator('f', {'f': null})).toEqual false
-    expect(emailValidator.validator('f', {})).toEqual false
-    expect(emailValidator.validator('f', {'f': '  '})).toEqual false
-    expect(emailValidator.validator('f', {'f': 42})).toEqual false
+    expect(emailValidator.validator('f3', fields)).toEqual false
+    expect(emailValidator.validator('f4', fields)).toEqual false
+    expect(emailValidator.validator('f5', fields)).toEqual false
+    expect(emailValidator.validator('f6', fields)).toEqual false
+    expect(emailValidator.validator('f7', fields)).toEqual false
+    expect(emailValidator.validator('f8', fields)).toEqual false
+
   )
-
-  it 'incorrect emails', inject((emailValidator) ->
-    expect(emailValidator.validator('f', {'f': "dadsa.com"})).toEqual false
-    expect(emailValidator.validator('f', {'f': "dadsa@com"})).toEqual false
-    expect(emailValidator.validator('f', {'f': "@dasda.com"})).toEqual false
-  )
-


Publish skype id.

Publish skype id.

--- server/publications.coffee
+++ server/publications.coffee
@@ -5,7 +5,7 @@
   Notifications.find {userId: this.userId}

 Meteor.publish 'usernames', ->
-  Meteor.users.find {}, {fields: {'username': 1, '_id': 1, 'show_tip': 1, 'telephone_nr': 1}}
+  Meteor.users.find {}, {fields: {'username': 1, '_id': 1, 'show_tip': 1, 'telephone_nr': 1, 'skype_id', 1}}

 Meteor.publish 'photos', (projectId) ->
   if projectId?


Fix /teachers/demo to work when not logged in

--- server/routes/index.coffee
+++ server/routes/index.coffee
@@ -42,7 +42,7 @@

   TrialRequest = require '../models/TrialRequest'
   app.get('/db/trial.request', mw.trialRequests.fetchByApplicant, mw.auth.checkHasPermission(['admin']), mw.rest.get(TrialRequest))
-  app.post('/db/trial.request', mw.auth.checkLoggedIn(), mw.trialRequests.post)
+  app.post('/db/trial.request', mw.trialRequests.post)
   app.get('/db/trial.request/:handle', mw.auth.checkHasPermission(['admin']), mw.rest.getByHandle(TrialRequest))
   app.put('/db/trial.request/:handle', mw.auth.checkHasPermission(['admin']), mw.trialRequests.put)


fix live announcement

fix live announcement

--- app/coffee/modules/events.coffee
+++ app/coffee/modules/events.coffee
@@ -204,7 +204,6 @@
     onOpen: ->
         @.connected = true
         @.startHeartBeatMessages()
-        @.notifications()

         @log.debug("WebSocket connection opened")
         token = @auth.getToken()
@@ -215,6 +214,7 @@
         }

         @.sendMessage(message)
+        @.notifications()

     onMessage: (event) ->
         @.log.debug "WebSocket message received: #{event.data}"


Use same git server for dev and live.

Use same git server for dev and live.

--- server/code/model/box.coffee
+++ server/code/model/box.coffee
@@ -28,8 +28,9 @@
   , callback

 getGitURL = (tool, server) ->
+  # :todo: Maybe use a different server for dev.scraperwiki.com
   if /dev/.test server
-    origin = "https://git-dev.scraperwiki.com"
+    origin = "https://git.scraperwiki.com"
   else
     origin = "https://git.scraperwiki.com"

@@ -55,7 +56,7 @@
           user: arg.user
           boxName: @name
           boxServer: @server
-          cmd: "rm -r http && git clone #{gitURL} tool && ln -s tool/http http"
+          cmd: "rm -fr http && git clone #{gitURL} tool && ln -s tool/http http"
         , (err, res, body) ->
           if err?
             callback err


inversion of logic

inversion of logic

--- server/lib/abstracter.coffee
+++ server/lib/abstracter.coffee
@@ -74,7 +74,7 @@
                 if previous == 'bankTransfer'
                     concrete.push s: '?bankoperation', p: 'pdta:isOutbound', o: 'true'
                 else
-                    abstract.push s: '?x', p: 'pdta:isOutbound', o: 'false'
+                    abstract.push s: '?x', p: 'pdta:isOutbound', o: 'true'

             when 'float'
                 # Check if it's a price


[bugfix] favorite finding was broken

[bugfix] favorite finding was broken

--- server/models/account.coffee
+++ server/models/account.coffee
@@ -388,7 +388,7 @@

     # if we dont have our 4 favorites, pick at random
     for box in boxes when changes.favorites.length < 4
-        if box.id not in changesfavorites and box.isSelectable()
+        if box.id not in changes.favorites and box.isSelectable()
             changes.favorites.push box.id

     @updateAttributes changes, callback


Add guard for non-existing Blaze namespace

--- emails.coffee
+++ emails.coffee
@@ -93,7 +93,8 @@
   )

   settings = _.extend({}, Mailer.settings, options.settings)
-  globalHelpers = _.extend({}, Helpers, Blaze._globalHelpers, options.helpers)
+  blazeHelpers = if typeof Blaze isnt 'undefined' then Blaze._globalHelpers else {}
+  globalHelpers = _.extend({}, Helpers, blazeHelpers, options.helpers)

   Utils.setupLogger(settings.logger, suppressInfo: settings.silent)


testing auth

testing auth

--- app/controllers/fb/FbDemo.coffee
+++ app/controllers/fb/FbDemo.coffee
@@ -36,7 +36,7 @@
   # @return [None]
   #
   indexAction: ->
-    console.log 'ENCRYPTED: ' + @req.connection.encrypted
+    console.log 'ENCRYPTED: ' + @req.protocol
     accessToken = @req.session.access_token
     if not accessToken
       console.log @req.session


Physics: Use bound_box instead of dimensions.


Change charge switch

Change charge switch

--- web/source/javascripts/angular-app.js.coffee
+++ web/source/javascripts/angular-app.js.coffee
@@ -82,7 +82,7 @@
         .outerRadius(outerRadius)
       force
         .linkDistance((l) -> l.distance)
-        .charge((n) -> if n.active then -600 else -30) # fush nodes away from selected node
+        .charge((n) -> if n.active then -150 else -30) # fush nodes away from selected node
         # TODO try friction based on mouse distance
         .nodes(nodes)
         .links(links)


fix for order cover dialog - displayed wrong quantity

--- app/js/controllers/market.coffee
+++ app/js/controllers/market.coffee
@@ -349,8 +349,9 @@
                 scope.market = current_market.actual_market or current_market
                 original_order = order
                 #console.log order
-                if !current_market.inverted
-                    order = order.invert()
+                order = angular.copy(order)
+#                if !current_market.inverted
+#                    order = order.invert()
                 scope.v = {quantity: order.quantity, total: order.quantity}
                 scope.cancel = ->
                     modalInstance.dismiss "cancel"


attempted filter fix

attempted filter fix

--- shared/IdleController.coffee
+++ shared/IdleController.coffee
@@ -54,7 +54,7 @@

     $scope.$watch 'editing', (newVal, oldVal) ->
       return if newVal is oldVal
-      $scope.editing.statName = _.findWhere($scope.filterableStats, {key: $scope.editing.stat}).name
+      $scope.editing.statName = _.findWhere($scope.filterableStats, {key: $scope.editing.stat})?.name
       Filters.setFilters newVal
     , yes
   ]
@@ -62,7 +62,7 @@
   ngMeteor.controller 'Idle', ['$scope', '$collection', 'IdleFilterData', ($scope, $collection, Filters) =>

     $scope._filters = Filters
-    $scope.filters = {stat: "level.__current", statName: "Level"}
+    $scope.filters = {stat: "level.__current", statName: "Level", name: "", }

     $collection IdlePlayers, {}, sort: 'level.__current': -1
     .bind $scope, 'players'
@@ -77,7 +77,7 @@

     $scope.decompose = (player, key) ->
       try
-        _.reduce (key.split "."), ((prev, cur) -> prev?[cur]), player
+        _.reduce (key.split "."), ((prev, cur) -> prev[cur]), player
       catch e
         return 0
   ]


Fix array lookup

Fix array lookup

--- src/eventtarget.coffee
+++ src/eventtarget.coffee
@@ -9,12 +9,15 @@
 class EventTarget extends UnJSONable
   null

-  ###*
-  @private
-  @property {Function[]}
-    Array of event listeners
-  ###
-  #_listeners: []
+  #--------------------------------------------------------------------------------
+  # Private variables / constants
+  #
+
+  ITEM_EQUAL = (i) -> return @[0] == i[0] and @[1] == i[1]
+
+  #--------------------------------------------------------------------------------
+  # Public methods
+  #

   ###*
   @method constructor
@@ -34,7 +37,7 @@
   ###
   addEventListener: (listener, thisObject = null) ->
     item = [listener, thisObject]
-    @_listeners.push(item) unless @_listeners.includes(item)
+    @_listeners.push(item) unless @_listeners.find(ITEM_EQUAL, item)
     return

   ###*
@@ -48,7 +51,7 @@
   ###
   removeEventListener: (listener, thisObject = null) ->
     item = [listener, thisObject]
-    index = @_listeners.indexOf(item)
+    index = @_listeners.findIndex(ITEM_EQUAL, item)
     @_listeners.splice(index, 1) if index >= 0
     return


Don't assume invalid time values are zero

--- src/fields/time.coffee
+++ src/fields/time.coffee
@@ -11,9 +11,9 @@
     "#{@getValue().hours || '00'}:#{@getValue().minutes || '00'}:#{@getValue().seconds || '00'} #{@getValue().am_pm}"

   validateType: ->
-    hours = parseInt(@get('value.hours'), 10) || 0
-    minutes = parseInt(@get('value.minutes'), 10) || 0
-    seconds = parseInt(@get('value.seconds'), 10) || 0
+    hours = parseInt(@get('value.hours'), 10)
+    minutes = parseInt(@get('value.minutes'), 10)
+    seconds = parseInt(@get('value.seconds'), 10)

     unless (1 <= hours <= 12) && (0 <= minutes <= 59) && (0 <= seconds <= 59)
       'time'


Gets and posts all work… it's a start...

--- src/scripts/search-virus-total.coffee
+++ src/scripts/search-virus-total.coffee
@@ -16,19 +16,19 @@
 #   Scott J Roberts - @sroberts

 VIRUS_TOTAL_API_KEY = process.env.VIRUS_TOTAL_API_KEY
-vt_file_report_url = "https://www.virustotal.com/vtapi/v2/file/report"
+vt_url = "https://www.virustotal.com"
+
+vt_file_report_url = vt_url + "/vtapi/v2/file/report"
+vt_url_report_url = vt_url + "/vtapi/v2/url/report"
+vt_ip_report_url = vt_url + "/vtapi/v2/ip-address/report"

 module.exports = (robot) ->
-  robot.respond /virustotal (.*)/i, (msg) ->
+  robot.respond /virustotal hash (.*)/i, (msg) ->
     # First paramater is the file hash to look for
-    search_resource = msg.match[1].toLowerCase()
+    hash = msg.match[1].toLowerCase()
+    msg.send "Test: Hash #{hash}"

-    parameters = {
-      "apikey": VIRUS_TOTAL_API_KEY,
-      "resource": search_resource
-    }
-
-    data = "apikey=#{encodeURIComponent VIRUS_TOTAL_API_KEY}&resource=#{encodeURIComponent search_resource}"
+    data = "apikey=#{encodeURIComponent VIRUS_TOTAL_API_KEY}&resource=#{encodeURIComponent hash}"

     robot.http(vt_file_report_url)
       .post(data) (err, res, body) ->
@@ -41,3 +41,47 @@
         """

         msg.send summary
+
+  robot.respond /virustotal url (.*)/i, (msg) ->
+    url = msg.match[1].toLowerCase()
+    msg.send "Test: URL #{url}"
+
+    data = "apikey=#{encodeURIComponent VIRUS_TOTAL_API_KEY}&resource=#{encodeURIComponent url}"
+
+    robot.http(vt_url_report_url)
+      .post(data) (err, res, body) ->
+        msg.send body
+        # vt_json = JSON.parse(body)
+        #
+        # summary = """ VirusTotal URL Result: #{vt_json.resource}
+        # - Scanned at: #{vt_json.scan_date}
+        # - Results:    #{vt_json.positives}/#{vt_json.total}
+        # - Link:       #{vt_json.permalink}
+        # """
+
+
+
+  robot.respond /virustotal ip (.*)/i, (msg) ->
+    ip = msg.match[1].toLowerCase()
+
+    robot.http(vt_ip_report_url)
+      .query("apikey": VIRUS_TOTAL_API_KEY, "ip": ip)
+      .get() (err, res, body) ->
+        #vt_json = JSON.parse(body)
+        msg.send body
+
+        #if vt_json.response_code == 1
+
+          # summary = """ VirusTotal IP Result: #{vt_json.resource}
+          # - Scanned at: #{vt_json.scan_date}
+          # - Results:    #{vt_json.positives}/#{vt_json.total}
+          # - Link:       #{vt_json.permalink}
+          # """
+
+          # for resolution of vt_json.resolutions
+          #   console.log("- #{resolution.last_resolved} : #{resolution.hostname}")
+
+        #vt_json
+
+        #else
+          #msg.send vt_json.verbose_msg


Fixing spec for merge and intersect

Fixing spec for merge and intersect

--- spec/leaderboard_spec.coffee
+++ spec/leaderboard_spec.coffee
@@ -392,6 +392,7 @@
   it 'should allow you to merge leaderboards', (done) ->
     foo = new Leaderboard('foo')
     bar = new Leaderboard('bar')
+    foobar = new Leaderboard('foobar')

     foo.rankMember('foo_1', 1, null, (reply) ->
       foo.rankMember('foo_2', 2, null, (reply) ->
@@ -400,11 +401,14 @@
             bar.rankMember('bar_3', 3, null, (reply) ->
               foo.mergeLeaderboards('foobar', ['bar'], null, (reply) ->
                 reply.should.equal(5)
-                done()))))))
+                foobar.totalMembers((numMembers) ->
+                  numMembers.should.equal(5)
+                  done())))))))

   it 'should allow you to intersect leaderboards', (done) ->
     foo = new Leaderboard('foo')
     bar = new Leaderboard('bar')
+    foobar = new Leaderboard('foobar')

     foo.rankMember('foo_1', 1, null, (reply) ->
       foo.rankMember('foo_2', 2, null, (reply) ->
@@ -414,4 +418,6 @@
               bar.rankMember('bar_3', 5, null, (reply) ->
                 foo.intersectLeaderboards('foobar', ['bar'], null, (reply) ->
                   reply.should.equal(2)
-                  done())))))))
+                  foobar.totalMembers((numMembers) ->
+                    numMembers.should.equal(2)
+                    done()))))))))


reconcile create property from inspect data

--- imports/server/mqtt/docker/inspect.coffee
+++ imports/server/mqtt/docker/inspect.coffee
@@ -23,6 +23,8 @@
       reconciler.updateContainerName name[1..], labels
     if id = msg.Id
       reconciler.updateContainerId id, labels
+    if created = msg.Created
+      reconciler.updateCreated created, labels

 ###
 {


Added gameserver stress test

Added gameserver stress test


Coffeescript registerer.

Coffeescript registerer.

--- filemanager/static/script/thumbloader.coffee
+++ filemanager/static/script/thumbloader.coffee
@@ -1,7 +1,7 @@

 handleViewedItem= (data) ->
-    console.log $.parseJSON(data)[0].pk
+    console.log $.parseJSON(data)
     console.log $.parseJSON(data)[0].loading
     console.log $.parseJSON(data)[0].size[0]
     console.log $.parseJSON(data)[0].size[1]
@@ -18,8 +18,8 @@

-class thumbloader
+class window.thumbloader
     datalist: []

-    register: (pk) (gallery) ->
-        datalist.push([pk,gallery])
+    register: (pk, gallery) ->
+        this.datalist.push([pk,gallery])


Correctly set "max" attribute

Correctly set "max" attribute

--- src/trix/views/attachment_view.coffee
+++ src/trix/views/attachment_view.coffee
@@ -17,7 +17,7 @@

     if @attachment.isPending()
       data.trixSerialize = false
-      progressElement = makeElement(tagName: "progress", max: 100, data: { trixMutable: true })
+      progressElement = makeElement("progress", max: 100, "data-trix-mutable": true)
       figure.appendChild(progressElement)

     if href = @attachment.getHref()


[test] ignore helpers/ for test coverage

--- test/iced-coffee-coverage.coffee
+++ test/iced-coffee-coverage.coffee
@@ -16,6 +16,7 @@
     "/node_modules"
     "/.git"
     "/*.coffee"
+    "/helpers"
   ]
   coverageVar:  coverageVar
   writeOnExit:  writeOnExit


pipe webview console messages to window console


fix tool_rpc integration test

fix tool_rpc integration test

--- test/integration/tool_rpc.coffee
+++ test/integration/tool_rpc.coffee
@@ -128,7 +128,7 @@
         browser.get @toolURL, ->
           wd40.switchToTopFrame ->
             # we have to scroll the toolbar left, so that we see the "More tools" link
-            browser.execute 'document.getElementById("dataset-tools").scrollLeft = 999999', (err, result) ->
+            browser.eval 'document.getElementById("dataset-tools").scrollLeft = 999999', (err, result) ->
               wd40.click '.new-view', (err) ->
                 browser.waitForElementByCss '#chooser .tool', 4000, done

@@ -144,7 +144,7 @@
           before (done) ->
             wd40.switchToBottomFrame ->
               wd40.click '#getDatasetName', done
-
+
           it 'shows the parent dataset name', (done) ->
             wd40.getText '#datasetName', (err, text) =>
               text.should.equal "Test Dataset (renamed)"


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/color2.coffee
+++ test/color2.coffee
@@ -185,6 +185,8 @@

   hsl = color.toHsl()

+  log hsl
+
   equal hsl[0], 22
   equal hsl[1].toFixed(2), 0.38
   equal hsl[2].toFixed(2), 0.13


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/color2.coffee
+++ test/color2.coffee
@@ -183,5 +183,15 @@
   equal hsl[2].toFixed(2), 0.13
   equal hsl[3], 1.0

+test "#hue", ->
+  color = Color2(34, 54, 239)
+
+  color2 = color.hue(60)
+
+  equal color2.r, 218
+  equal color2.g, 37
+  equal color2.b, 239
+  equal color2.a, 1.0
+
 module()


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/engine.coffee
+++ test/engine.coffee
@@ -188,6 +188,8 @@
     velocity: Point(1, 0)

   engine.frameAdvance()
+
+  object.I.x

 test "#setState", 3, ->
   engine = Engine()


engine#add

engine#add

--- test/engine.coffee
+++ test/engine.coffee
@@ -59,7 +59,8 @@
   engine.add
     test: "test"

-test "#add",
+test "#add", ->
+  engine

 test "zSort", ->
   engine = Engine


Fix wrong nav title

Fix wrong nav title

--- views/tabarea.cjsx
+++ views/tabarea.cjsx
@@ -34,7 +34,7 @@
 ControlledTabArea = React.createClass
   getInitialState: ->
     key: 0
-    pluginKey: -2
+    pluginKey: if config.get 'poi.tabarea.double', false then 2 else -2
     dropdownOpen: false
     nowTime: 0
     plugins: []


schema

schema

--- app/graphite.coffee
+++ app/graphite.coffee
@@ -213,7 +213,7 @@
     if settings.get('type') == 'lead'
       unless function_names
         functions_promise = http.get(graphite.url 'functions').then (functions) ->
-          function_names = _.filter functions, (f) -> f.indexOf('-') == -1
+          function_names = _.filter Object.keys(functions), (f) -> f.indexOf('-') == -1
     else
       function_names = graphite_function_names


Refactor isSwipe method

Refactor isSwipe method

--- source/quo.gestures.coffee
+++ source/quo.gestures.coffee
@@ -75,9 +75,7 @@
       fingers = touches.length
       if fingers == GESTURE.fingers
         CURRENT_TOUCH = _fingersPosition(touches, fingers)
-        is_swipe = _isSwipe(event)
-        if is_swipe then GESTURE.prevSwipe = true
-        _trigger "swiping" if (is_swipe or GESTURE.prevSwipe is true)
+        _trigger "swiping" if _isSwipe(event)
         if fingers == 2
           _captureRotation()
           _capturePinch()
@@ -92,6 +90,7 @@
       move_horizontal = Math.abs(FIRST_TOUCH[0].x - CURRENT_TOUCH[0].x) > 30
       move_vertical = Math.abs(FIRST_TOUCH[0].y - CURRENT_TOUCH[0].y) > 30
       it_is = GESTURE.el and (move_horizontal or move_vertical)
+      GESTURE.prevSwipe = it_is
     it_is

   _onTouchEnd = (event) ->


TextMate syntax highlighting for Coffeescript is still broken. Adding spaces here to make my life easier.

--- spec/draggable_spec.coffee
+++ spec/draggable_spec.coffee
@@ -145,7 +145,7 @@

         # Move it by less than the drag distance
         lessThanDistance = options.distanceConfig.distance - 0.1
-        delta = Math.floor Math.sqrt((lessThanDistance*lessThanDistance)/2)
+        delta = Math.floor Math.sqrt((lessThanDistance*lessThanDistance) / 2)
         $(document).simulate 'mousemove',
           clientX: center.x + delta
           clientY: center.y + delta
@@ -167,7 +167,7 @@

         # Move it by at least the drag distance
         distance = options.distanceConfig.distance
-        @delta = Math.ceil Math.sqrt((distance*distance)/2)
+        @delta = Math.ceil Math.sqrt((distance*distance) / 2)
         $(document).simulate 'mousemove',
           clientX: center.x + @delta
           clientY: center.y + @delta


:lipstick:

:lipstick:

--- spec/find-view-spec.coffee
+++ spec/find-view-spec.coffee
@@ -198,7 +198,7 @@
         $(document.activeElement).trigger 'core:confirm'
         expect(editor.getSelectedBufferRange()).toEqual [[2, 0], [2, 5]]

-      it "toggles case sensitiviAty via a button and finds text matching the pattern", ->
+      it "toggles case sensitivity via a button and finds text matching the pattern", ->
         findView.findEditor.setText 'WORDs'
         $(document.activeElement).trigger 'core:confirm'
         expect(editor.getSelectedBufferRange()).toEqual [[1, 0], [1, 5]]


Fix dk spec that would fail, even if dk worked

--- spec/operators-spec.coffee
+++ spec/operators-spec.coffee
@@ -316,7 +316,7 @@
         it "deletes the bottom two lines", ->
           keydown('d')
           keydown('k')
-          expect(editor.getText()).toBe("ABCDE")
+          expect(editor.getText()).toBe("12345")

       describe "on the beginning of the file", ->
         beforeEach ->
@@ -332,7 +332,7 @@
         it "deletes the first two lines", ->
           keydown('d')
           keydown('k')
-          expect(editor.getText()).toBe("12345")
+          expect(editor.getText()).toBe("ABCDE")

   describe "the D keybinding", ->


enable storage bucket refresh

enable storage bucket refresh


Add selection-line class to old editor gutter for forward-compatibility

The React editor does not apply the .cursor-line-no-selection class,
but instead relies on a combination of .cursor-line and .selection-line
for selectors that want to target the cursor line when there is no
selection.

This allows themes to be upgraded to support the React editor and the
old editor at the same time.

--- src/gutter-view.coffee
+++ src/gutter-view.coffee
@@ -237,6 +237,7 @@
     return unless @highlightedLineNumbers
     for line in @highlightedLineNumbers
       line.classList.remove('cursor-line')
+      line.classList.remove('selection-line')
       line.classList.remove('cursor-line-no-selection')
     @highlightedLineNumbers = null

@@ -245,7 +246,10 @@
     @highlightedLineNumbers ?= []
     if highlightedLineNumber = @lineNumbers[0].children[row - @firstScreenRow]
       highlightedLineNumber.classList.add('cursor-line')
-      highlightedLineNumber.classList.add('cursor-line-no-selection') if emptySelection
+      if emptySelection
+        highlightedLineNumber.classList.add('cursor-line-no-selection')
+      else
+        highlightedLineNumber.classList.add('selection-line')
       @highlightedLineNumbers.push(highlightedLineNumber)

   highlightLines: ->


Fix for a bug in the surround helper where leading and trailing whitespace would not be stripped and 's' characters would.

--- src/haml-coffee.coffee
+++ src/haml-coffee.coffee
@@ -482,7 +482,7 @@
       if @options.customSurround
         fn += "surround = (start, end, fn) => #{ @options.customSurround }.call(@, start, end, fn)\n"
       else
-        fn += "surround = (start, end, fn) => start + fn.call(@)?.replace(/^\s+|\s+$/g, '') + end\n"
+        fn += "surround = (start, end, fn) => start + fn.call(@)?.replace(/^\\s+|\\s+$/g, '') + end\n"

     # Succeed helper
     if code.indexOf('succeed') isnt -1


StacksCustomViews: updateStackTemplate helper

--- client/admin/lib/views/stacks/stackscustomviews.coffee
+++ client/admin/lib/views/stacks/stackscustomviews.coffee
@@ -2,6 +2,7 @@
 kd                              = require 'kd'
 hljs                            = require 'highlight.js'
 globals                         = require 'globals'
+remote                          = require('app/remote').getInstance()
 dateFormat                      = require 'dateformat'
 CustomViews                     = require 'app/commonviews/customviews'
 CredentialListItem              = require './credentiallistitem'
@@ -39,6 +40,22 @@
       .catch   (err) ->
         console.log 'Failed >>', err

+
+  updateStackTemplate = (data, callback) ->
+
+    { template, credential, title, stackTemplate } = data
+
+    { JCredential, JStackTemplate } = remote.api
+
+    credentials = [credential.publicKey]
+
+    if stackTemplate
+      stackTemplate.update {title, machines, template, credentials}, callback
+    else
+      JStackTemplate.create {
+        title : title or "Default stack template"
+        template, machines, credentials
+      }, callback

   fetchAndShowCredentialData = (credential, outputView) ->


StacksCustomViews: remove credentialForm we will use credentialList's form

--- client/admin/lib/views/stacks/stackscustomviews.coffee
+++ client/admin/lib/views/stacks/stackscustomviews.coffee
@@ -73,19 +73,6 @@
       return container

-    credentialForm: (provider) =>
-
-      form = ComputeController_UI.generateAddCredentialFormFor provider
-      form.on "Cancel", ->
-        console.log 'Form Cancelled'
-
-      form.on "CredentialAdded", (credential) =>
-        # credential.owner = yes
-        console.log 'Credential added', credential
-
-      return form
-
-
     credentialList: (provider) =>

       listView   = new AccountCredentialList
@@ -107,7 +94,7 @@
       container = @views.container 'step-creds'
       @addTo container,
         stepsHeaderView : 2
-        credentialForm  : provider
+        credentialList  : provider
         navButton_prev  :
           callback      : cancelCallback
         navButton_next  :


Remove client-side detection of special mailboxes

Client-side detection of special mailboxes lead the client to believe they are
defined even when they are not, leading to the user having errors (e.g. when
sending an email) it can't understand.

--- client/app/utils/translators/account_translator.coffee
+++ client/app/utils/translators/account_translator.coffee
@@ -9,30 +9,6 @@
         last = {}
         weight1 = 900
         weight2 = 400
-        # Try to detect special mailboxes, first with flags, then by name
-        # Already done on server, except for TEST accounts
-        raw.mailboxes ?= []
-        if not raw.draftMailbox? or
-           not raw.sentMailbox? or
-           not raw.trashMailbox?
-            raw.mailboxes.forEach (box) ->
-                # If needed, use mailboxes attribs to set draft, sent and trash
-                if not raw.draftMailbox? and MailboxFlags.DRAFT in box.attribs
-                    raw.draftMailbox = box.id
-                if not raw.sentMailbox? and MailboxFlags.SENT in box.attribs
-                    raw.sentMailbox = box.id
-                if not raw.trashMailbox? and MailboxFlags.TRASH in box.attribs
-                    raw.trashMailbox = box.id
-        if not raw.draftMailbox? or
-           not raw.sentMailbox? or
-           not raw.trashMailbox?
-            raw.mailboxes.forEach (box) ->
-                if not raw.draftMailbox? and /draft/i.test box.label
-                    raw.draftMailbox = box.id
-                if not raw.sentMailbox? and /sent/i.test box.label
-                    raw.sentMailbox = box.id
-                if not raw.trashMailbox? and /trash/i.test box.label
-                    raw.trashMailbox = box.id

         # Creates Immutable OrderedMap of mailboxes
         mailboxes = Immutable.Sequence raw.mailboxes


app-updates: Changed update required modal title and content.

--- client/landing/app/MainApp/kodingappscontroller.coffee
+++ client/landing/app/MainApp/kodingappscontroller.coffee
@@ -626,12 +626,13 @@
     console.warn error.message, error

   showUpdateRequiredModal: (manifest) ->
-    modal = new KDModalView
-      title          : "App Update Available"
+    {name} = manifest
+    modal  = new KDModalView
+      title          : "Update Required for #{name}"
       content        : """

-
An update available for #{manifest.name}. You can update the app now or you can continue to use old version you have.

-
Warning: Updating the app will delete it's current folder to install new version. This cannot be undone. If you have updated files, back up them now.

+
Author of #{name} made this update required. You must update to keep on using the app.

+
Warning: Updating an app will delete it's current folder to install new version. This cannot be undone. If you have updated files, back up them now.

       """
       overlay        : yes
@@ -645,13 +646,9 @@
             @updateUserApp manifest, ->
               modal.buttons.Update.hideLoader()
               modal.destroy()
-        "Use This Version" :
-          style      : "modal-clean-gray"
-          callback   : =>
-            @skipUpdate = yes
-            @appManager.open manifest.name
-            modal.destroy()
-            @skipUpdate = no
+        "Close" :
+          style      : "modal-cancel"
+          callback   : => modal.destroy()

   defaultManifest = (type, name)->
     {profile} = KD.whoami()


apps: Used someWithRelationship instead of some when fetching published apps.

--- client/landing/app/MainApp/kodingappscontroller.coffee
+++ client/landing/app/MainApp/kodingappscontroller.coffee
@@ -170,9 +170,10 @@
           callback err, script

   getPublishedApps: ->
-    KD.remote.api.JApp.some {}, {}, (err, apps) =>
+    return unless KD.isLoggedIn()
+    KD.remote.api.JApp.someWithRelationship {}, {}, (err, apps) =>
       @publishedApps = map = {}
-      map[app.manifest.name] = app for app in apps when app.approved
+      map[app.manifest.name] = app for app in apps

   isAppUpdateAvailable: (appName, appVersion) ->
     if @publishedApps[appName]
@@ -190,7 +191,7 @@
       # TODO: Error handling
       @refreshApps =>
         notification.notificationSetTitle "Updating #{appName}: Fetching new app details"
-        KD.remote.api.JApp.some { "manifest.name": appName }, {}, (err, app) =>
+        KD.remote.api.JApp.someWithRelationship { "manifest.name": appName }, {}, (err, app) =>
           notification.notificationSetTitle "Updating #{appName}: Updating app to latest version"
           @installApp app[0], "latest", =>
             @refreshApps()


Only stopPropagation if has srcElement attribute

--- source/quo.gestures.coffee
+++ source/quo.gestures.coffee
@@ -32,7 +32,7 @@

   # Private methods
   _start = (ev) ->
-    return ev.stopPropagation() if ev.srcElement.tagName.toLowerCase() in _disabled_tags
+    return ev.stopPropagation() if ev.srcElement?.tagName.toLowerCase() in _disabled_tags
     _started = true
     _originalEvent = ev or event
     _fingers = _getFingers(ev)


FIX: Fix showing student ID step for staff

--- src/app/home/states/new-user-wizard/new-user-wizard.coffee
+++ src/app/home/states/new-user-wizard/new-user-wizard.coffee
@@ -22,10 +22,12 @@
       $scope.isStaff = true
       $scope.user.student_id = null
       delete $scope.steps.studentIdStep
-      for step in ['emailStep', 'targetGradeStep', 'optInToResearchStep']
+      # NOTE: Must add step to below
+      for step in ['emailStep', 'targetGradeStep', 'avatarStep', 'optInToResearchStep']
         $scope.steps[step].seq -= 1
       $scope.steps.nicknameStep.subtitle = $scope.steps.nicknameStep.subtitle.replace('tutor', 'students')
   # Define steps for wizard
+  # MUST ADD TO ABOVE NOTE!
   $scope.steps = {
     nameStep: {
       title:    "What's your name?"


Add integer definition

Add integer definition


Handle error from glob

Handle error from glob


don't trigger on page:load because jquery.turbolinks will handle this

--- lib/assets/javascripts/appitude.js.coffee
+++ lib/assets/javascripts/appitude.js.coffee
@@ -9,9 +9,6 @@
 viewObjects = {}

 $(document).ready ()->
-  autoloadViews()
-
-$(document).on 'page:load', ()->
   autoloadViews()

 getView = (name)->


Refactor operators-spec.coffee

Refactor operators-spec.coffee

--- spec/operators-spec.coffee
+++ spec/operators-spec.coffee
@@ -18,60 +18,55 @@
     helpers.keydown(key, options)

   describe "the x keybinding", ->
-    it "deletes a character", ->
-      editor.setText("012345")
-      editor.setCursorScreenPosition([0, 4])
-
-      keydown('x')
-      expect(editor.getText()).toBe '01235'
-      expect(editor.getCursorScreenPosition()).toEqual [0, 4]
-      expect(vimState.getRegister('"').text).toBe '4'
-
-      keydown('x')
-      expect(editor.getText()).toBe '0123'
-      expect(editor.getCursorScreenPosition()).toEqual [0, 3]
-      expect(vimState.getRegister('"').text).toBe '5'
-
-      keydown('x')
-      expect(editor.getText()).toBe '012'
-      expect(editor.getCursorScreenPosition()).toEqual [0, 2]
-      expect(vimState.getRegister('"').text).toBe '3'
-
-    it "deletes nothing when cursor is on empty line", ->
-      editor.getBuffer().setText "012345\n\nabcdef"
-      editor.setCursorScreenPosition [1, 0]
-
-      keydown('x')
-      expect(editor.getText()).toBe "012345\n\nabcdef"
+    describe "on a line with content", ->
+      beforeEach ->
+        editor.setText("012345")
+        editor.setCursorScreenPosition([0, 4])
+
+      it "deletes a character", ->
+        keydown('x')
+        expect(editor.getText()).toBe '01235'
+        expect(editor.getCursorScreenPosition()).toEqual [0, 4]
+        expect(vimState.getRegister('"').text).toBe '4'
+
+        keydown('x')
+        expect(editor.getText()).toBe '0123'
+        expect(editor.getCursorScreenPosition()).toEqual [0, 3]
+        expect(vimState.getRegister('"').text).toBe '5'
+
+        keydown('x')
+        expect(editor.getText()).toBe '012'
+        expect(editor.getCursorScreenPosition()).toEqual [0, 2]
+        expect(vimState.getRegister('"').text).toBe '3'
+
+    describe "on an empty line", ->
+      beforeEach ->
+        editor.setText("012345\n\nabcdef")
+        editor.setCursorScreenPosition([1, 0])
+        keydown('x')
+
+      it "deletes nothing when cursor is on empty line", ->
+        expect(editor.getText()).toBe "012345\n\nabcdef"

   describe "the s keybinding", ->
     beforeEach ->
       editor.setText('012345')
       editor.setCursorScreenPosition([0, 1])
+      keydown('s')

     it "deletes the character to the right and enters insert mode", ->
-      keydown('s')
-
       expect(editor).toHaveClass 'insert-mode'
       expect(editor.getText()).toBe '02345'
       expect(editor.getCursorScreenPosition()).toEqual [0, 1]
       expect(vimState.getRegister('"').text).toBe '1'

-    it "deletes count characters to the right and enters insert mode", ->
-      keydown('3')
-      keydown('s')
-
-      expect(editor).toHaveClass 'insert-mode'
-      expect(editor.getText()).toBe '045'
-      expect(editor.getCursorScreenPosition()).toEqual [0, 1]
-      expect(vimState.getRegister('"').text).toBe '123'
-
   describe "the S keybinding", ->
-    it "deletes the entire line and enters insert mode", ->
+    beforeEach ->
       editor.setText("12345\nabcde\nABCDE")
       editor.setCursorScreenPosition([1, 3])
-
       keydown('S', shift: true)
+
+    it "deletes the entire line and enters insert mode", ->
       expect(editor).toHaveClass 'insert-mode'
       expect(editor.getText()).toBe "12345\n\nABCDE"
       expect(editor.getCursorScreenPosition()).toEqual [1, 0]
@@ -101,7 +96,7 @@
         expect(editor.getText()).toBe "12345\nabcde"
         expect(editor.getCursorScreenPosition()).toEqual [1, 0]

-    describe "when the second d is prefixed by a count", ->
+    describe "undo behavior", ->
       beforeEach ->
         editor.setText("12345\nabcde\nABCDE\nQWERT")
         editor.setCursorScreenPosition([1,1])
@@ -110,33 +105,10 @@
         keydown('2')
         keydown('d')

-      it "deletes n lines, starting from the current", ->
-        expect(editor.getText()).toBe "12345\nQWERT"
-        expect(editor.getCursorScreenPosition()).toEqual [1, 0]
-
-      describe "with undo", ->
-        beforeEach ->
-          keydown('u')
-
-        it "undoes both lines", ->
-          expect(editor.getText()).toBe "12345\nabcde\nABCDE\nQWERT"
-
-    describe "when followed by an h", ->
-      it "deletes the previous letter on the current line", ->
-        editor.setText("abcd\n01234")
-        editor.setCursorScreenPosition([1, 1])
-
-        keydown('d')
-        keydown('h')
-
-        expect(editor.getText()).toBe "abcd\n1234"
-        expect(editor.getCursorScreenPosition()).toEqual [1, 0]
-
-        keydown('d')
-        keydown('h')
-
-        expect(editor.getText()).toBe "abcd\n1234"
-        expect(editor.getCursorScreenPosition()).toEqual [1, 0]
+        keydown('u')
+
+      it "undoes both lines", ->
+        expect(editor.getText()).toBe "12345\nabcde\nABCDE\nQWERT"

     describe "when followed by a w", ->
       it "deletes the next word until the end of the line", ->
@@ -169,35 +141,13 @@
         expect(editor.getText()).toBe 'four'
         expect(editor.getCursorScreenPosition()).toEqual [0, 0]

-    describe "when followed by a b", ->
-      it "deletes to the beginning of the previous word", ->
-        editor.setText("abcd efg")
-        editor.setCursorScreenPosition [0, 2]
-
-        keydown('d')
-        keydown('b')
-
-        expect(editor.getText()).toBe 'cd efg'
-        expect(editor.getCursorScreenPosition()).toEqual [0, 0]
-
-        editor.setText('one two three four')
-        editor.setCursorScreenPosition([0, 11])
-
-        keydown('d')
-        keydown('3')
-        keydown('b')
-
-        expect(editor.getText()).toBe 'ee four'
-        expect(editor.getCursorScreenPosition()).toEqual [0, 0]
-
   describe "the D keybinding", ->
     beforeEach ->
       editor.getBuffer().setText("012\n")
       editor.setCursorScreenPosition([0, 1])
+      keydown('D', shift: true)

     it "deletes the contents until the end of the line", ->
-      keydown('D', shift: true)
-
       expect(editor.getText()).toBe "0\n"

   describe "the c keybinding", ->
@@ -227,95 +177,13 @@
         expect(editor).not.toHaveClass 'command-mode'
         expect(editor).toHaveClass 'insert-mode'

-    describe "when the second c is prefixed by a count", ->
-      it "deletes n lines, starting from the current, and enters insert mode", ->
-        editor.setText("12345\nabcde\nABCDE\nQWERT")
-        editor.setCursorScreenPosition([1, 1])
-
-        keydown('c')
-        keydown('2')
-        keydown('c')
-
-        expect(editor.getText()).toBe "12345\nQWERT"
-        expect(editor.getCursorScreenPosition()).toEqual [1, 0]
-        expect(editor).not.toHaveClass 'command-mode'
-        expect(editor).toHaveClass 'insert-mode'
-
-    describe "when followed by an h", ->
-      it "deletes the previous letter on the current line and enters insert mode", ->
-        editor.setText("abcd\n01234")
-        editor.setCursorScreenPosition([1, 1])
-
-        keydown('c')
-        keydown('h')
-
-        expect(editor.getText()).toBe "abcd\n1234"
-        expect(editor.getCursorScreenPosition()).toEqual [1, 0]
-        expect(editor).not.toHaveClass 'command-mode'
-        expect(editor).toHaveClass 'insert-mode'
-
-    describe "when followed by a w", ->
-      it "deletes to the beginning of the next word and enters insert mode", ->
-        editor.setText('abcd efg')
-        editor.setCursorScreenPosition([0, 2])
-
-        keydown('c')
-        keydown('w')
-
-        expect(editor.getText()).toBe 'ab efg'
-        expect(editor.getCursorScreenPosition()).toEqual [0, 2]
-        expect(editor).not.toHaveClass 'command-mode'
-        expect(editor).toHaveClass 'insert-mode'
-
-        keydown('escape')
-
-        editor.setText('one two three four')
-        editor.setCursorScreenPosition([0, 0])
-
-        keydown('c')
-        keydown('3')
-        keydown('w')
-
-        expect(editor.getText()).toBe 'four'
-        expect(editor.getCursorScreenPosition()).toEqual [0, 0]
-        expect(editor).not.toHaveClass 'command-mode'
-        expect(editor).toHaveClass 'insert-mode'
-
-    describe "when followed by a b", ->
-      it "deletes to the beginning of the previous word and enters insert mode", ->
-        editor.setText('abcd efg')
-        editor.setCursorScreenPosition [0, 2]
-
-        keydown('c')
-        keydown('b')
-
-        expect(editor.getText()).toBe 'cd efg'
-        expect(editor.getCursorScreenPosition()).toEqual [0,0]
-        expect(editor).not.toHaveClass 'command-mode'
-        expect(editor).toHaveClass 'insert-mode'
-
-        keydown('escape')
-
-        editor.setText('one two three four')
-        editor.setCursorScreenPosition([0, 11])
-
-        keydown('c')
-        keydown('3')
-        keydown('b')
-
-        expect(editor.getText()).toBe 'ee four'
-        expect(editor.getCursorScreenPosition()).toEqual [0, 0]
-        expect(editor).not.toHaveClass 'command-mode'
-        expect(editor).toHaveClass 'insert-mode'
-
   describe "the C keybinding", ->
     beforeEach ->
       editor.getBuffer().setText("012\n")
       editor.setCursorScreenPosition([0, 1])
+      keydown('C', shift: true)

     it "deletes the contents until the end of the line and enters insert mode", ->
-      keydown('C', shift: true)
-
       expect(editor.getText()).toBe "0\n"
       expect(editor.getCursorScreenPosition()).toEqual [0, 1]
       expect(editor).not.toHaveClass 'command-mode'
@@ -326,140 +194,113 @@
       editor.getBuffer().setText("012 345\nabc\n")
       editor.setCursorScreenPosition([0, 4])

+    describe "when followed by a second y ", ->
+      beforeEach ->
+        keydown('y')
+        keydown('y')
+
+      it "saves the line to the default register", ->
+        expect(vimState.getRegister('"').text).toBe "012 345\n"
+        expect(editor.getCursorScreenPosition()).toEqual [0, 4]
+
+    describe "when followed with a repeated y", ->
+      beforeEach ->
+        keydown('y')
+        keydown('2')
+        keydown('y')
+
+      it "copies n lines, starting from the current", ->
+        expect(vimState.getRegister('"').text).toBe "012 345\nabc\n"
+
+    describe "with a register", ->
+      beforeEach ->
+        keydown('"')
+        keydown('a')
+        keydown('y')
+        keydown('y')
+
+      it "saves the line to the a register", ->
+        expect(vimState.getRegister('a').text).toBe "012 345\n"
+
+    describe "with a motion", ->
+      beforeEach ->
+        keydown('y')
+        keydown('w')
+
+      it "saves the first word to the default register", ->
+        expect(vimState.getRegister('"').text).toBe '345'
+
+  describe "the Y keybinding", ->
+    beforeEach ->
+      editor.getBuffer().setText "012 345\nabc\n"
+      editor.setCursorScreenPosition [0, 4]
+
     it "saves the line to the default register", ->
-      keydown('y')
-      keydown('y')
+      keydown('Y', shift: true)

       expect(vimState.getRegister('"').text).toBe "012 345\n"
       expect(editor.getCursorScreenPosition()).toEqual [0, 4]

-    describe "when the second y is prefixed by a count", ->
-      it "copies n lines, starting from the current", ->
-        keydown('y')
-        keydown('2')
-        keydown('y')
-
-        expect(vimState.getRegister('"').text).toBe "012 345\nabc\n"
-
-    it "saves the line to the a register", ->
-      keydown('"')
-      keydown('a')
-      keydown('y')
-      keydown('y')
-
-      expect(vimState.getRegister('a').text).toBe "012 345\n"
-
-    it "saves the first word to the default register", ->
-      keydown('y')
-      keydown('w')
-
-      expect(vimState.getRegister('"').text).toBe '345'
-
-  describe "the Y keybinding", ->
-    beforeEach ->
-      editor.getBuffer().setText "012 345\nabc\n"
-      editor.setCursorScreenPosition [0, 4]
-
-    it "saves the line to the default register", ->
-      keydown('Y', shift: true)
-
-      expect(vimState.getRegister('"').text).toBe "012 345\n"
-      expect(editor.getCursorScreenPosition()).toEqual [0, 4]
-
-    describe "when prefixed by a count", ->
-      it "copies n lines, starting from the current", ->
-        keydown('2')
-        keydown('Y', shift: true)
-
-        expect(vimState.getRegister('"').text).toBe "012 345\nabc\n"
-
-    it "saves the line to the a register", ->
-      keydown('"')
-      keydown('a')
-      keydown('Y', shift: true)
-
-      expect(vimState.getRegister('a').text).toBe "012 345\n"
-
   describe "the p keybinding", ->
-    describe "character", ->
+    describe "with character contents", ->
       beforeEach ->
         editor.getBuffer().setText "012\n"
         editor.setCursorScreenPosition [0, 0]
         vimState.setRegister('"', text: '345')
         vimState.setRegister('a', text: 'a')

-      it "inserts the contents of the default register", ->
-        keydown('p')
-
-        expect(editor.getText()).toBe "034512\n"
-        expect(editor.getCursorScreenPosition()).toEqual [0, 4]
-
-      it "inserts the contents of the 'a' register", ->
-        keydown('"')
-        keydown('a')
-        keydown('p')
-
-        expect(editor.getText()).toBe "0a12\n"
-        expect(editor.getCursorScreenPosition()).toEqual [0, 2]
-
-    describe "linewise", ->
+      describe "from the default register", ->
+        beforeEach -> keydown('p')
+
+        it "inserts the contents", ->
+          expect(editor.getText()).toBe "034512\n"
+          expect(editor.getCursorScreenPosition()).toEqual [0, 4]
+
+      describe "from a specified register", ->
+        beforeEach ->
+          keydown('"')
+          keydown('a')
+          keydown('p')
+
+        it "inserts the contents of the 'a' register", ->
+          expect(editor.getText()).toBe "0a12\n"
+          expect(editor.getCursorScreenPosition()).toEqual [0, 2]
+
+    describe "with linewise contents", ->
       beforeEach ->
         editor.getBuffer().setText("012\n")
         editor.setCursorScreenPosition([0, 1])
         vimState.setRegister('"', text: " 345\n", type: 'linewise')
+        keydown('p')

       it "inserts the contents of the default register", ->
-        keydown('p')
-
         expect(editor.getText()).toBe "012\n 345\n"
         expect(editor.getCursorScreenPosition()).toEqual [1, 1]

     describe "undo behavior", ->
-      it "handles repeats", ->
+      beforeEach ->
         editor.setText("12345\nabcde\nABCDE\nQWERT")
         editor.setCursorScreenPosition([1, 1])
         vimState.setRegister('"', text: '123')
-
         keydown('2')
         keydown('p')
-
         keydown('u')

+      it "handles repeats", ->
         expect(editor.getText()).toBe "12345\nabcde\nABCDE\nQWERT"

   describe "the P keybinding", ->
-    describe "character", ->
+    describe "with character contents", ->
       beforeEach ->
         editor.getBuffer().setText("012\n")
         editor.setCursorScreenPosition([0, 0])
         vimState.setRegister('"', text: '345')
         vimState.setRegister('a', text: 'a')
-
-      it "inserts the contents of the default register", ->
         keydown('P', shift: true)

+      it "inserts the contents of the default register above", ->
         expect(editor.getText()).toBe "345012\n"
         expect(editor.getCursorScreenPosition()).toEqual [0, 3]
-
-      it "inserts the contents of the 'a' register", ->
-        keydown('"')
-        keydown('a')
-        keydown('P', shift: true)
-
-        expect(editor.getText()).toBe "a012\n"
-        expect(editor.getCursorScreenPosition()).toEqual [0, 1]
-
-    describe "linewise", ->
-      beforeEach ->
-        editor.getBuffer().setText("012\n")
-        editor.setCursorScreenPosition([0, 1])
-        vimState.setRegister('"', text: " 345\n", type: 'linewise')
-
-      it "inserts the contents of the default register", ->
-        keydown('P', shift: true)
-
-        expect(editor.getText()).toBe " 345\n012\n"
-        expect(editor.getCursorScreenPosition()).toEqual [0, 1]

   describe "the O keybinding", ->
     beforeEach ->
@@ -469,10 +310,9 @@

       editor.getBuffer().setText("  abc\n  012\n")
       editor.setCursorScreenPosition([1, 1])
+      keydown('O', shift: true)

     it "switches to insert and adds a newline above the current one", ->
-      keydown('O', shift: true)
-
       expect(editor.getText()).toBe "  abc\n  \n  012\n"
       expect(editor.getCursorScreenPosition()).toEqual [1, 2]
       expect(editor).toHaveClass 'insert-mode'
@@ -485,10 +325,9 @@

       editor.getBuffer().setText("abc\n  012\n")
       editor.setCursorScreenPosition([1, 2])
+      keydown('o')

     it "switches to insert and adds a newline above the current one", ->
-      keydown('o')
-
       expect(editor.getText()).toBe "abc\n  012\n  \n"
       expect(editor).toHaveClass 'insert-mode'
       expect(editor.getCursorScreenPosition()).toEqual [2, 2]
@@ -497,89 +336,103 @@
     beforeEach ->
       editor.getBuffer().setText("012\n")

-    it "switches to insert mode and shifts to the right", ->
-      editor.setCursorScreenPosition([0, 0])
-
-      keydown('a')
-
-      expect(editor.getCursorScreenPosition()).toEqual [0, 1]
-      expect(editor).toHaveClass 'insert-mode'
-
-    it "doesn't linewrap", ->
-      editor.setCursorScreenPosition([0, 3])
-
-      keydown('a')
-
-      expect(editor.getCursorScreenPosition()).toEqual [0, 3]
+    describe "at the beginning of the line", ->
+      beforeEach ->
+        editor.setCursorScreenPosition([0, 0])
+        keydown('a')
+
+      it "switches to insert mode and shifts to the right", ->
+        expect(editor.getCursorScreenPosition()).toEqual [0, 1]
+        expect(editor).toHaveClass 'insert-mode'
+
+    describe "at the end of the line", ->
+      beforeEach ->
+        editor.setCursorScreenPosition([0, 3])
+        keydown('a')
+
+      it "doesn't linewrap", ->
+        expect(editor.getCursorScreenPosition()).toEqual [0, 3]

   describe "the J keybinding", ->
     beforeEach ->
       editor.getBuffer().setText("012\n    456\n")
       editor.setCursorScreenPosition([0, 1])

-    it "deletes the contents until the end of the line", ->
-      keydown('J', shift: true)
-
-      expect(editor.getText()).toBe "012 456\n"
-
-    describe "undo behavior", ->
-      it "handles repeats", ->
+    describe "without repeating", ->
+      beforeEach -> keydown('J', shift: true)
+
+      it "joins the contents of the current line with the one below it", ->
+        expect(editor.getText()).toBe "012 456\n"
+
+    describe "with repeating", ->
+      beforeEach ->
         editor.setText("12345\nabcde\nABCDE\nQWERT")
         editor.setCursorScreenPosition([1, 1])
-
         keydown('2')
         keydown('J', shift: true)

-        keydown('u')
-
-        expect(editor.getText()).toBe "12345\nabcde\nABCDE\nQWERT"
+      describe "undo behavior", ->
+        beforeEach -> keydown('u')
+
+        it "handles repeats", ->
+          expect(editor.getText()).toBe "12345\nabcde\nABCDE\nQWERT"

   describe "the > keybinding", ->
+    beforeEach ->
+      editor.setText("12345\nabcde\nABCDE")
+      editor.setCursorScreenPosition([0, 0])
+
     describe "when followed by a >", ->
-      it "indents the current line", ->
-        editor.setText("12345\nabcde\nABCDE")
-        editor.setCursorScreenPosition([1, 1])
-
+      beforeEach ->
         keydown('>')
         keydown('>')

-        expect(editor.getText()).toBe "12345\n  abcde\nABCDE"
-        expect(editor.getCursorScreenPosition()).toEqual [1, 2]
-
-      it "indents multiple lines at once", ->
-        editor.setText("12345\nabcde\nABCDE")
-        editor.setCursorScreenPosition([0, 0])
-
+      it "indents the current line", ->
+        expect(editor.getText()).toBe "  12345\nabcde\nABCDE"
+        expect(editor.getCursorScreenPosition()).toEqual [0, 2]
+
+    describe "when followed by a repeating >", ->
+      beforeEach ->
         keydown('3')
         keydown('>')
         keydown('>')

+      it "indents multiple lines at once", ->
         expect(editor.getText()).toBe "  12345\n  abcde\n  ABCDE"
         expect(editor.getCursorScreenPosition()).toEqual [0, 2]

-        keydown('u')
-        expect(editor.getText()).toBe "12345\nabcde\nABCDE"
+      describe "undo behavior", ->
+        beforeEach -> keydown('u')
+
+        it "outdents all three lines", ->
+          expect(editor.getText()).toBe "12345\nabcde\nABCDE"

   describe "the < keybinding", ->
+    beforeEach ->
+      editor.setText("  12345\n  abcde\nABCDE")
+      editor.setCursorScreenPosition([0, 0])
+
     describe "when followed by a <", ->
-      it "indents the current line", ->
-        expect(editor.setText("12345\n  abcde\nABCDE"))
-        editor.setCursorScreenPosition([1, 2])
-
+      beforeEach ->
         keydown('<')
         keydown('<')
-        expect(editor.getText()).toBe "12345\nabcde\nABCDE"
-        expect(editor.getCursorScreenPosition()).toEqual [1, 0]
-
-      it "indents multiple lines at once", ->
-        editor.setText("  12345\n  abcde\n  ABCDE")
-        editor.setCursorScreenPosition([0, 0])
-
-        keydown('3')
+
+      it "indents the current line", ->
+        expect(editor.getText()).toBe "12345\n  abcde\nABCDE"
+        expect(editor.getCursorScreenPosition()).toEqual [0, 0]
+
+    describe "when followed by a repeating <", ->
+      beforeEach ->
+        keydown('2')
         keydown('<')
         keydown('<')
+
+      it "indents multiple lines at once", ->
         expect(editor.getText()).toBe "12345\nabcde\nABCDE"
         expect(editor.getCursorScreenPosition()).toEqual [0, 0]

-        keydown('u')
-        expect(editor.getText()).toBe "  12345\n  abcde\n  ABCDE"
+      describe "undo behavior", ->
+        beforeEach -> keydown('u')
+
+        it "indents both lines", ->
+          expect(editor.getText()).toBe "  12345\n  abcde\nABCDE"


Completely removed tree-view dependency

Completely removed tree-view dependency

--- spec/spark-ide-spec.coffee
+++ spec/spark-ide-spec.coffee
@@ -3,7 +3,6 @@

 describe 'Main Tests', ->
   activationPromise = null
-  treeViewPromise = null
   loginView = null
   sparkIde = null
   originalProfile = null
@@ -13,7 +12,6 @@
     activationPromise = atom.packages.activatePackage('spark-ide').then ({mainModule}) ->
       sparkIde = mainModule
       loginView = mainModule.loginView
-    treeViewPromise = atom.packages.activatePackage('tree-view')

     originalProfile = SettingsHelper.getProfile()
     # For tests not to mess up our profile, we have to switch to test one...
@@ -52,14 +50,10 @@

   describe 'when the spark-ide:rename-core event is triggered', ->
     it 'calls renameCore() method', ->
-      waitsForPromise ->
-        treeViewPromise
-
-      runs ->
-        spyOn sparkIde, 'renameCore'
-        atom.workspaceView.trigger 'spark-ide:rename-core'
-        expect(sparkIde.renameCore).toHaveBeenCalled()
-        jasmine.unspy sparkIde, 'renameCore'
+      spyOn sparkIde, 'renameCore'
+      atom.workspaceView.trigger 'spark-ide:rename-core'
+      expect(sparkIde.renameCore).toHaveBeenCalled()
+      jasmine.unspy sparkIde, 'renameCore'

   describe 'when the spark-ide:remove-core event is triggered', ->
@@ -144,11 +138,7 @@

   describe 'when the spark-ide:claim-core-manually event is triggered', ->
     it 'calls claimCoreManually() method', ->
-      waitsForPromise ->
-        treeViewPromise
-
-      runs ->
-        spyOn sparkIde, 'claimCoreManually'
-        atom.workspaceView.trigger 'spark-ide:claim-core-manually'
-        expect(sparkIde.claimCoreManually).toHaveBeenCalled()
-        jasmine.unspy sparkIde, 'claimCoreManually'
+      spyOn sparkIde, 'claimCoreManually'
+      atom.workspaceView.trigger 'spark-ide:claim-core-manually'
+      expect(sparkIde.claimCoreManually).toHaveBeenCalled()
+      jasmine.unspy sparkIde, 'claimCoreManually'


remove mistakenly added describe.only

remove mistakenly added describe.only

--- spec/specs/holdwhen.coffee
+++ spec/specs/holdwhen.coffee
@@ -69,7 +69,7 @@
         Bacon.fromArray([new Bacon.Error(), "2"]).
           holdWhen(later(20, false).startWith(true))
       [error(), [20, "2"]])
-  describe.only "Sends the entire buffer even if valve ends", ->
+  describe "Sends the entire buffer even if valve ends", ->
     expectStreamEvents(
       ->
         left = series(1, [1, 2, 3, 4, 5])


Removing <show>chat</show> stanza to prevent Google Talk from setting default Hubot status to "Chatty".

--- src/hubot/gtalk.coffee
+++ src/hubot/gtalk.coffee
@@ -28,7 +28,6 @@

   online: =>
     @client.send new Xmpp.Element('presence')
-      .c('show').t('chat')

     # send raw whitespace for keepalive
     setInterval =>


only show download button after item selected

--- ui/app/components/ItemExplorer.cjsx
+++ ui/app/components/ItemExplorer.cjsx
@@ -78,7 +78,6 @@
     leaf = this.props.items

     inventory = _.map path, (item_id,i) ->
-      console.log("inventory: #{item_id}")
       item = _.find leaf, (e) -> e.item_id == item_id
       leaf = item.children

@@ -89,7 +88,7 @@
         onClick={onClick}
       />

-    downloadButton = if inventory
+    downloadButton = if path.length > 0
       <DownloadButton
         className='btn download-button'
         genFile={this.generateItemSet}


Fixed a bug causing the old doc to be used as the latest doc when merging. Prevented dropdown from executing submit unless the docPromise is resolved.

--- src/Forms.coffee
+++ src/Forms.coffee
@@ -181,7 +181,9 @@
       # event manually.
       $submit = Form.getSubmitButton(@)
       if $submit.closest('form').length == 0
-        $submit.click => $form.submit()
+        $submit.click =>
+          Logger.debug('Submitting form...', formArgs)
+          $form.submit()

       schemaInputs = Form.getSchemaInputs(@)

@@ -210,7 +212,6 @@
           hasRequiredField = true

       Form.setUpFields(@)
-      Form.setUpAutosave(@)

       if hasRequiredField
         @$('.ui.form.segment').append($('

Required field
'))
@@ -250,7 +251,12 @@
       # if Form.isBulk()
       #   Form.setUpBulkFields()

-      if @data?.docPromise? then Q.when(@data.docPromise).then => Form.mergeLatestDoc(@)
+      if @data?.docPromise?
+        Q.when(@data.docPromise).then =>
+          Form.mergeLatestDoc(@)
+          Form.setUpAutosave(@)
+      else
+        Form.setUpAutosave(@)

       formArgs.onRender?.apply(@, arguments)

@@ -458,9 +464,6 @@
       template.formDoc = Form.getInputValues(template)

     Form.setUpAutosave = (template) ->
-      # TODO(aramk) This is causing the form to submit and remove fields. Disabled for now.
-      return
-
       return unless formArgs.autosave
       template = getTemplate(template)
       $form = Form.getElement(template)
@@ -540,7 +543,8 @@
       _.each docs, (doc) ->
         doc = collection.findOne(doc._id)
         if doc? then latestDocs.push(doc)
-      latestValues = if docs.length > 1 then Form.getBulkValues(template, latestDocs) else docs[0]
+      latestValues =
+        if docs.length > 1 then Form.getBulkValues(template, latestDocs) else latestDocs[0]
       latestValues ?= {}
       latestValues = Objects.flattenProperties(latestValues)
       mergedValues = {}


* [bug] free/end should call the _end to release resource.

--- src/abstract-iterator.coffee
+++ src/abstract-iterator.coffee
@@ -183,12 +183,12 @@
   _endKeys: ->
     delete @_resultOfKeys
     @_indexOfKeys = -2
-    @_ended = true
+    # @_ended = true

   freeSync: ->
     if @_indexOfKeys?
-      return @_endKeys()
-    else if @_endSync
+      @_endKeys()
+    if @_endSync
       @_ended = true
       return @_endSync()
     else
@@ -255,8 +255,6 @@
     return callback(new AlreadyEndError("end() already called on iterator"))  if @_ended
     if @_indexOfKeys?
       @_endKeys()
-      setImmediate callback
-    else
-      @_ended = true
-      @_end callback
+    @_ended = true
+    @_end callback
   end: @::free


QUALITY: Adjust groups in tutorial filter

--- src/app/common/filters/filters.coffee
+++ src/app/common/filters/filters.coffee
@@ -192,9 +192,9 @@
 .filter('groupsInTutorials', ->
   (input, unitRole, kind) ->
     return unless input? && unitRole? && kind?
-    return input if kind == 'all'
     if kind == 'mine'
-      _.filter(input, (group) -> group.tutorial().tutor.name == unitRole.name)
+      return _.filter(input, (group) -> group.tutorial().tutor.id == unitRole.user_id)
+    return input
 )

 .filter('groupsForStudent', ->


Add an event for after the user keymap has loaded.

--- src/keymap-extensions.coffee
+++ src/keymap-extensions.coffee
@@ -7,6 +7,9 @@

 KeymapManager::onDidLoadBundledKeymaps = (callback) ->
   @emitter.on 'did-load-bundled-keymaps', callback
+
+KeymapManager::onDidLoadUserKeymap = (callback) ->
+  @emitter.on 'did-load-user-keymap', callback

 KeymapManager::loadBundledKeymaps = ->
   keymapsPath = path.join(@resourcePath, 'keymaps')
@@ -32,8 +35,10 @@
   return unless fs.isFileSync(userKeymapPath)

   try
+    errorLoading = false
     @loadKeymap(userKeymapPath, watch: true, suppressErrors: true, priority: 100)
   catch error
+    errorLoading = true
     if error.message.indexOf('Unable to watch path') > -1
       message = """
         Unable to watch path: `#{path.basename(userKeymapPath)}`. Make sure you
@@ -49,6 +54,9 @@
       stack = error.stack
       @notificationManager.addFatalError(error.message, {detail, stack, dismissable: true})

+  @emitter.emit 'did-load-user-keymap' unless errorLoading
+
+
 KeymapManager::subscribeToFileReadFailure = ->
   @onDidFailToReadFile (error) =>
     userKeymapPath = @getUserKeymapPath()


Tumblr queries now pull tags from a random time within the past year.

--- src/app/rounds/rounds.coffee
+++ src/app/rounds/rounds.coffee
@@ -34,7 +34,7 @@
 rounds.factory "RoundsRes", [
   "$resource"
   ($resource) ->
-    return $resource("http://api.tumblr.com/v2/tagged?api_key=iI6dl4tEgEt96yvRl1urojakH0Wk86544k2ooTuNxHxVGysBMm&tag=:tag&callback=JSON_CALLBACK", {},
+    return $resource("http://api.tumblr.com/v2/tagged?api_key=iI6dl4tEgEt96yvRl1urojakH0Wk86544k2ooTuNxHxVGysBMm&tag=:tag&before=:before&callback=JSON_CALLBACK", {},
       jsonp_query:
         tag: @tag
         method: "JSONP"
@@ -50,7 +50,7 @@
     $scope.correct = false
     $scope.guess = ""
     tag_regex = new RegExp('^#'+tag+'$', "i")
-    RoundsRes.jsonp_query tag: tag, (response) ->
+    RoundsRes.jsonp_query tag: "adventure time", before: (Date.now() - (Math.floor(Math.random() * 2678400 * 12))) / 1000, (response) ->
       $scope.message = response.meta
       $scope.posts = response.response


There is no KD.singleton() in framework

There is no KD.singleton() in framework

--- src/components/tooltip/tooltip.coffee
+++ src/components/tooltip/tooltip.coffee
@@ -32,7 +32,7 @@
     if @getOptions().animate then @setClass 'out' else @hide()

     @addListeners()
-    KD.singleton("windowController").on "ScrollHappened", @bound "hide"
+    KD.singletons["windowController"].on "ScrollHappened", @bound "hide"

     @once "viewAppended", =>
       o = @getOptions()
@@ -64,7 +64,7 @@
     return unless @visible
     super
     @getDomElement().remove()
-    KD.singleton("windowController").removeLayer this
+    KD.singletons["windowController"].removeLayer this
     @visible = no

   update:(o = @getOptions(), view = null)->
@@ -126,7 +126,7 @@

     # converts NESW-Values to topbottomleftright and retains them in @getOptions
     @appendToDomBody()
-    KD.singleton("windowController").addLayer this
+    KD.singletons["windowController"].addLayer this
     o = @translateCompassDirections o if o.gravity
     o.gravity = null


Do not output time in production env

Do not output time in production env

--- src/moon/logger.coffee
+++ src/moon/logger.coffee
@@ -79,7 +79,7 @@
       sp2 = "      ".split(" ").slice(type.length).join(" ")
       output = [ ("\ " + time + "").grey, "".grey + @name.white + sp1 + type[@colors[index]] + sp2 + ">".grey ].concat(message)
     else
-      output = [ ("\ " + time + ""), "" + @name + "." + type + " >" ].concat(message)
+      output = [ @name + "." + type + " >" ].concat(message)
     unless console[type]
       message = type
       type = "log"


fix(auth): Fix missing var. This is why we should go ES6!

--- internal_packages/onboarding/lib/account-settings-page.cjsx
+++ internal_packages/onboarding/lib/account-settings-page.cjsx
@@ -319,7 +319,7 @@
     data.provider = @state.provider.name

     # if there's an account with this email, get the ID for it to notify the backend of re-auth
-    account = AccountStore.accountForEmail(email)
+    account = AccountStore.accountForEmail(data.email)
     reauthParam = if account then "&reauth=#{account.id}" else ""

     # handle special case for exchange/outlook/hotmail username field


Storing destinations only for simple paths

--- src/eager-runtime/EagerRuntime.coffee
+++ src/eager-runtime/EagerRuntime.coffee
@@ -174,9 +174,8 @@
 		newPath.initialPath?.from = hash
 		newPath.loopingPath?.from = hash

+		# only simple paths lead somewhere
 		newPath.path?.to = getHash newPath.path.getEndPoint()
-		newPath.initialPath?.to = getHash newPath.initialPath.getEndPoint()
-		newPath.loopingPath?.to = getHash newPath.loopingPath.getEndPoint()

 		if newPath.type != 'simple'
 			# cyclic path


some stuff

some stuff

--- src/ngram-robot.coffee
+++ src/ngram-robot.coffee
@@ -1,23 +1,27 @@
 ngram = require('simple-ngram-markov')
+ngramLength = 3

 record = (resp, robot) ->
   if resp.envelope.user.model == undefined
-    resp.envelope.user.model = ngram.createModel()
+    resp.envelope.user.model = ngram.createModel(ngramLength)
   ngram.addSentenceToModel(resp.envelope.user.model, resp.match.toString())
   robot.brain.save()

 generate = (resp, robot) ->
   name = resp.match[1].toLowerCase().trim()
-  num = Number(resp.match[2].toLowerCase().trim())
+  if resp.match[2]
+    num = Number(resp.match[2].trim())
+
+  num = if num > 0 and num < 140 then num else Math.floor(Math.random()*30+5)
   user = robot.brain.userForName name

   unless user
-    return resp.send "There is no model for #{name}"
+    return resp.send "User named #{name} not found."

   model = user.model

   unless model
-    return resp.send "There is no model for #{name}"
+    return resp.send "There is no model for #{name}."

   sentence = ngram.generateSentence(model, num)
   resp.send "#{user.name}: #{sentence}"
@@ -27,5 +31,5 @@
   robot.hear /^[^!|\/].+/g, (resp) ->
     record(resp, robot)

-  robot.hear /^!talklike\s(\w+)\s(\d+)/i, (resp) ->
+  robot.hear /^!talklike\s(\w+)\s?(\d*)/i, (resp) ->
     generate(resp, robot)


Show interceptors when reusing, fixes #490

--- src/resources/forms/orbeon/builder/resources/section-grid-repeat/section-editor.coffee
+++ src/resources/forms/orbeon/builder/resources/section-grid-repeat/section-editor.coffee
@@ -155,6 +155,7 @@
                 _.each _.range(sections.length), (pos) ->
                     title = f$.find '.fr-section-label a', $ sections[pos]
                     interceptor = labelClickInterceptors[pos]
+                    interceptor.show()                                          # Might be an interceptor that was previously hidden, and is now reused
                     interceptor.offset title.offset()
                     interceptor.height title.height()
                     interceptor.width title.width()


Improved the robustness of the glsl literals api

--- src/generator/compile.glsl.api.coffee
+++ src/generator/compile.glsl.api.coffee
@@ -253,18 +253,18 @@
         throw "Operands passed to the max operation have incorrect types."

     literal: (a) ->
-      if typeof a == 'string'
-        a
-      else if typeof a == 'number'
+      if typeof a == 'number'
         glsl.floatLit a
       else if Array.isArray a
         glsl.vecLit a
+      else
+        a

     floatLit: (a) ->
-      if typeof a == 'string'
-        a
-      else
-        if (a | 0) == a then a + '.0' else a
+      if typeof a == 'number' and (a | 0) == a
+        a + '.0'
+      else
+        a

     vecLit: (a) ->
       if a.length > 1 and a.length < 5
@@ -273,26 +273,26 @@
         throw "Cannot create vector literal with length #{a.length}."

     vec2Lit: (a) ->
-      if typeof a == 'string'
-        a
-      else if typeof a == 'number'
+      if typeof a == 'number'
         "vec2(#{glsl.floatLit a})"
-      else
+      else if Array.isArray a
         "vec2(#{glsl.floatLit a[0]},#{glsl.floatLit a[1]})"
+      else
+        a

     vec3Lit: (a) ->
-      if typeof a == 'string'
-        a
-      else if typeof a == 'number'
+      if typeof a == 'number'
         "vec3(#{glsl.floatLit a})"
-      else
+      else if Array.isArray a
         "vec3(#{glsl.floatLit a[0]},#{glsl.floatLit a[1]},#{glsl.floatLit a[2]})"
+      else
+        a

     vec4Lit: (a) ->
-      if typeof a == 'string'
-        a
-      else if typeof a == 'number'
+      if typeof a == 'number'
         "vec4(#{glsl.floatLit a})"
-      else
+      else if Array.isArray a
         "vec4(#{glsl.floatLit a[0]},#{glsl.floatLit a[1]},#{glsl.floatLit a[2]},#{glsl.floatLit a[3]})"
-
+      else
+        a
+


bongo: uncomment error path

bongo: uncomment error path

--- node_modules_koding/bongo/lib/bongo.coffee
+++ node_modules_koding/bongo/lib/bongo.coffee
@@ -126,11 +126,11 @@
     @registry.forEach (path) =>
       try model = require path
       catch e
-        # console.error e
-        # err = new Error
-        # err.originalError = e
-        # err.message = "Error loading a class: #{path}"
-        # return @emit 'error', err
+        console.error e
+        err = new Error
+        err.originalError = e
+        err.message = "Error loading a class: #{path}"
+        return @emit 'error', err
       if model.name? and ('function' is typeof model)
         @defineModel model.name, model
       else
@@ -327,7 +327,7 @@
     {method, sessionToken, userArea} = message
     scrubber = new Scrubber @localStore
     unscrubbed = scrubber.unscrub message, (callbackId)=>
-      storeKey = secretName+callbackId
+      storeKey = "#{secretName}#{callbackId}"
       unless @remoteStore.has storeKey
         @remoteStore.add storeKey, (args...)=>
           @handleResponse secretName, callbackId, args


:art: Remove bind calls in favour of coffee script syntax

--- src/integrations/atom/projects.coffee
+++ src/integrations/atom/projects.coffee
@@ -17,10 +17,10 @@

   createItemsProp: ->
     projectsPaths       = atoms.projectsPaths()
-    watchPathsStream    = projectsPaths.openStream.map(@createWatchPathTask.bind(this))
+    watchPathsStream    = projectsPaths.openStream.map @createWatchPathTask
     addItemsStream      = watchPathsStream.flatMap (task) -> Bacon.fromEvent(task, 'add')
     removeItemsStream   = watchPathsStream.flatMap (task) -> Bacon.fromEvent(task, 'unlink')
-    closeProjectsStream = projectsPaths.closeStream.map(@destroyWatchPathTask.bind(this))
+    closeProjectsStream = projectsPaths.closeStream.map @destroyWatchPathTask

     return Bacon.update [],
       [addItemsStream], @concatNewItem
@@ -31,7 +31,7 @@

   concatNewItem: R.flip(R.invoker(1, 'concat'))

-  createWatchPathTask: (path) ->
+  createWatchPathTask: (path) =>
     @tasks[path] = task = new Task(require.resolve('./watch-project-task.coffee'))
     task.projectPath = path # projectPath to match src/atom/watch-project-task.coffee definition
     task.start(path,
@@ -40,7 +40,7 @@
     )
     return task

-  destroyWatchPathTask: (path) ->
+  destroyWatchPathTask: (path) =>
     task = @tasks[path]
     task.send('dispose')
     return task


Add missing require's

Add missing require's

--- src/postversion.coffee
+++ src/postversion.coffee
@@ -1,3 +1,4 @@
+readPackage = require('./steps/read-package')
 verifyBranch = require('./steps/verify-branch')
 verifyClean = require('./steps/verify-clean')
 reattach = require('./steps/reattach')


fire off register to newsletter on user registration as it can be slow

--- services/web/app/coffee/Features/User/UserRegistrationHandler.coffee
+++ services/web/app/coffee/Features/User/UserRegistrationHandler.coffee
@@ -53,7 +53,9 @@
 				async.series [
 					(cb)-> User.update {_id: user._id}, {"$set":{holdingAccount:false}}, cb
 					(cb)-> AuthenticationManager.setUserPassword user._id, userDetails.password, cb
-					(cb)-> NewsLetterManager.subscribe user, cb
+					(cb)->
+						NewsLetterManager.subscribe user, ->
+						cb() #this can be slow, just fire it off
 					(cb)->
 						emailOpts =
 							first_name:user.first_name


Use autosized textarea

Use autosized textarea

--- resources/assets/coffee/react/_components/comment-editor.coffee
+++ resources/assets/coffee/react/_components/comment-editor.coffee
@@ -16,7 +16,9 @@
 #    along with osu!web.  If not, see <http://www.gnu.org/licenses/>.
 ###

-{button, div, span, textarea} = ReactDOMFactories
+{button, div, span} = ReactDOMFactories
+
+el = React.createElement

 bn = 'comment-editor'

@@ -24,7 +26,7 @@
   constructor: (props) ->
     super props

-    @textareaRef = React.createRef()
+    @textarea = null

     @state =
       message: @props.message ? ''
@@ -32,8 +34,8 @@

   componentDidMount: =>
-    @textareaRef.current.selectionStart = -1
-    @textareaRef.current.focus() if (@props.focus ? true)
+    @textarea.selectionStart = -1
+    @textarea.focus() if (@props.focus ? true)

   componentWillUnmount: =>
@@ -45,9 +47,9 @@
     blockClass += " #{bn}--compact" if @mode() == 'new'

     div className: blockClass,
-      textarea
+      el TextareaAutosize,
         className: "#{bn}__message"
-        ref: @textareaRef
+        innerRef: @setTextarea
         value: @state.message
         placeholder: osu.trans("comments.placeholder.#{@mode()}")
         onChange: @onChange
@@ -129,3 +131,7 @@
     .fail (xhr, status) =>
       @setState posting: false
       osu.ajaxError(xhr, status)
+
+
+  setTextarea: (ref) =>
+    @textarea = ref


refactored

refactored

--- src/Store.coffee
+++ src/Store.coffee
@@ -31,6 +31,19 @@
      console.error e
      cb e

+saveObjectToFile = (o, file, cb) ->
+  try
+    json = JSON.stringify o, null, 2
+    if cb?
+      fs.writeFile file, json, 'utf8', cb
+    else
+      try
+        fs.writeFileSync file, json, 'utf8'
+      catch e
+        e
+  catch e
+    if cb? then cb e else e
+
 id2fileName = (id, dir) -> path.join dir,"#{id}.json"

 save = (id, o, cb) ->
@@ -52,13 +65,9 @@
       if cb? then cb err else err
     else
       @_cache[id] = o
-      if cb? then  cb null, id else id
-  try
-    json = JSON.stringify data, null, 2
-    if cb? then fs.writeFile file, json, 'utf8', done
-    else done fs.writeFileSync file, json, 'utf8'
-  catch e
-    if cb? then cb e else e
+      if cb? then cb null, id else id
+  if cb? then saveObjectToFile data, file, done
+  else done saveObjectToFile data, file

 get = (id, cb) ->
   o = clone @_cache[id]
@@ -83,23 +92,16 @@
   if @_single
     backup = @_cache[id]
     delete @_cache[id]
-    try
-      json = JSON.stringify @_cache, null, 2
-    catch e
-      @_cache[id] = backup
-      return if cb? then cb e else e
     done = (err)=>
-        if err?
-          @_cache[id] = backup
-          if cb? then cb err else err
-        else cb? null
+      if err?
+        @_cache[id] = backup
+        if cb? then cb err else err
+      else cb? null
     if cb?
-      fs.writeFile file, json, 'utf8', done
+      saveObjectToFile @_cache, file, done
     else
-      try
-        done fs.writeFileSync file, json, 'utf8'
-      catch e
-        done e
+      err = (o = saveObjectToFile @_cache, file) instanceof Error
+      done err, o
   else
     done = (err) =>
       return (if cb? cb err else err) if err?
@@ -151,12 +153,7 @@

   all: (cb=->) ->
     if @_single
-      fs.readFile @_getFileName(), "utf8", (err, content) ->
-        return cb err if err?
-        try
-          cb null, JSON.parse content
-        catch e
-          cb e
+      getObjectFromFile.call @, undefined, cb
     else
       readIDs @_dir, (err, ids) =>
         return cb err if err?


[fix] RedisModel##remove should take a callback

--- src/redis-model.coffee
+++ src/redis-model.coffee
@@ -147,7 +147,7 @@
   ]

   #Removes one or more jobs by ID, also removes the job from any state list it's in
-  remove: (jobs) ->
+  remove: (jobs, callback) ->
     return unless jobs
     #Expects {id: 123, queue: "video transcoding"}
     multi = []
@@ -157,7 +157,7 @@
       multi.push ["del", "#{prefix}#{id}"]
       multi.concat @commandRemoveFromStateLists prefix, id

-    (@redis.multi multi).exec()
+    (@redis.multi multi).exec callback

   #Makes all jobs in a specific state pending
   makePendingByState: (queue = "*", state, callback) ->


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/color.coffee
+++ src/color.coffee
@@ -89,7 +89,7 @@
     else if args.length == 1 && Object.isArray(args.first())
       arr = args.first()

-      rgbMap = arr.splice(0, 3).map (channel) ->
+      rgbMap = arr.slice(0, 3).map (channel) ->
         parseFloat channel

       alpha = if arr[0]? then parseFloat arr[0] else 1.0


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/color.coffee
+++ src/color.coffee
@@ -201,7 +201,7 @@
     shiftHue$: (degrees) ->
       hsl = @toHsl()

-      hsl[0] = (hsl[0] + degrees).mod 360
+      hsl[0] = (hsl[0] + degrees.round()).mod 360

       [@r, @g, @b, @a] = hslToRgb(hsl)


the best javascript color library

the best javascript color library

--- src/color.coffee
+++ src/color.coffee
@@ -63,7 +63,7 @@
     else
       parsedColor = lookup[normalizeKey(arguments[0])] || parseHex(arguments[0]) || parseRGB(arguments[0])

-    return unless parsedColor
+    return channels = [0, 0, 0, 0] unless parsedColor

     alpha = parsedColor[3]


Change bower.json resource URI

Change bower.json resource URI

--- app/src/services/services.coffee
+++ app/src/services/services.coffee
@@ -14,7 +14,7 @@
 myAppServices.service 'myApp.manifestService', [
   '$resource'
   ($resource) ->
-    manifestResource = $resource 'assets/data/bower.json'
+    manifestResource = $resource 'bower.json'
     assetsResource = $resource 'assets/assets.json'

     @getManifest = ->


still not working...

still not working...

--- lib/assets/javascripts/attachinary.js.coffee
+++ lib/assets/javascripts/attachinary.js.coffee
@@ -17,7 +17,7 @@
                 <img
                   src="<%= $.cloudinary.url(files[i].public_id, { "version": files[i].version, "format": 'jpg'}) %>"
                   alt=""/>
-
+

               <% } %>
               <a href="#" data-remove="<%= files[i].public_id %>">Remove


Remove duplicated method definition

Remove duplicated method definition

--- src/text-editor.coffee
+++ src/text-editor.coffee
@@ -436,9 +436,6 @@
   onDidRemoveDecoration: (callback) ->
     @displayBuffer.onDidRemoveDecoration(callback)

-  onDidUpdateDecorations: (callback) ->
-    @displayBuffer.onDidUpdateDecorations(callback)
-
   # Extended: Calls your `callback` when the placeholder text is changed.
   #
   # * `callback` {Function}


cleanup

cleanup

--- apps/articles/client/show.coffee
+++ apps/articles/client/show.coffee
@@ -17,7 +17,6 @@

   initialize: (options) ->
     { @article } = options
-    window.view = this
     new ShareView el: @$('#articles-social')
     @setupSlideshow()
     @renderArtworks()


Using elapsed time in delayed events

Using elapsed time in delayed events

--- src/engine_game_state.coffee
+++ src/engine_game_state.coffee
@@ -41,8 +41,10 @@
       self.trigger "afterAdd", object

       return object
+
     camera: (n=0) ->
       self.cameras()[n]
+
     cameras: (newCameras) ->
       if newCameras?
         I.currentState.cameras(newCameras)


refactor

refactor