Starting to work.

Starting to work.

--- lib/simple-flake8-view.coffee
+++ lib/simple-flake8-view.coffee
@@ -16,12 +16,7 @@
   flake8Path = atom.config.get "flake8.flake8Path"

   if not fs.existsSync(flake8Path)
-    errors.push {
-      "message": "Unable to get report, please check flake8 bin path",
-      "evidence": flake8Path,
-      "position": 1,
-      "line": 1
-    }
+    console.log("Unable to get report, please check flake8 bin path")
     callback errors
     return

@@ -57,16 +52,12 @@

   # Watch for the exit code
   proc.on 'exit', (exit_code, signal) ->
-      if exit_code == 1 and errors.length == 0
-        errors.push {
-          "message": "flake8 is crashing, please check flake8 bin path or reinstall flake8",
-          "evidence": flake8Path,
-          "position": 1,
-          "line": 1
-        }
-      callback errors
+    if exit_code == 1 and errors.length == 0
+      console.log('Flake8 is crashing. Check flake8 bin path.')
+    callback errors

 module.exports =
+

 class SimpleFlake8View extends SelectListView

@@ -88,7 +79,7 @@
     atom.workspaceView.command 'simple-flake8:toggle', => @toggle()

   getFilterKey: ->
-    'eventDescription'
+    'message'

   toggle: ->
     if @hasParent()
@@ -100,43 +91,46 @@
     editor = atom.workspace.getActiveEditor()
     return unless editor?
     return unless editor.getGrammar().name == 'Python'
-    #
-    # @storeFocusedElement()
-    #
-    # if @previouslyFocusedElement[0] and @previouslyFocusedElement[0] isnt document.body
-    #   @eventElement = @previouslyFocusedElement
-    # else
-    #   @eventElement = atom.workspaceView

-    @eventElement = atom.workspaceView
+    @storeFocusedElement()
     filePath = editor.getPath()

-    error_list = []
-    flake filePath, (errors) ->
+    @setLoading('Running Flake8 Lint...')
+    atom.workspaceView.append(this)
+    @focusFilterEditor()
+
+    flake filePath, (errors) =>
+      error_list = []
       if errors.length == 0
         return

       for error in errors
         if error.type
-          message = error.type + " " + error.message
+          message = error.line + ' - ' + error.type + " " + error.message
         else
           message = error.message
-        console.log(message)
+        # console.log(message)
         error_list.push({message, error})

-    @setItems(error_list)
+      @setItems(error_list)

-    atom.workspaceView.append(this)
-    @focusFilterEditor()
+    if @previouslyFocusedElement[0] and @previouslyFocusedElement[0] isnt document.body
+      @eventElement = @previouslyFocusedElement
+    else
+      @eventElement = atom.workspaceView

-  viewForItem: ({eventName, eventDescription}) ->
-    keyBindings = @keyBindings
+  viewForItem: ({message, error}) ->
+    console.log('Processing: ' + message)
+    # data = '

' + message + '
'
+    # console.log('Returning html: ' + data)
+    # return data
+    base_msg = error.type + ' - ' + error.message
     $$ ->
-      @li class: 'event', 'data-event-name': eventName, =>
+      @li class: 'event', 'data-event-name': error.type, =>
         @div class: 'pull-right', =>
-          for binding in keyBindings when binding.command is eventName
-            @kbd _.humanizeKeystroke(binding.keystrokes), class: 'key-binding'
-        @span eventDescription, title: eventName
+            # Use the keybinding class to display line numbers
+            @kbd "Line: " + error.line, class: 'key-binding'
+        @span base_msg[0..65], title: message

   confirmed: ({eventName}) ->
     @cancel()


Fix having to click menu twice in responsive mode

--- client/source/views/layout.coffee
+++ client/source/views/layout.coffee
@@ -19,9 +19,8 @@

   clickMenuNav: ->
     if @$('.hidden-xs:visible').length is 0
-      _.delay =>
-        @$('.nav-primary').css(display: 'block').slideToggle 150
-      , 100
+      @$('.nav-primary').css(display: 'block').slideToggle 150, =>
+        @$('.nav-primary').toggleClass('hidden-xs').toggle()

   initialize: ->
     super


CP: No need a fat arrow here

CP: No need a fat arrow here

--- client/Main/providers/computeprovider.coffee
+++ client/Main/providers/computeprovider.coffee
@@ -40,7 +40,7 @@
     KD.remote.api.ComputeProvider.create options, callback

   @createDefaultStack = ->
-    KD.remote.api.ComputeProvider.createGroupStack (err, stack)=>
+    KD.remote.api.ComputeProvider.createGroupStack (err, stack)->
       return if KD.showError err

       delete ComputeProvider.stacks


Troubleshoot: fixed not visible recover button in fail conditions bug

--- client/Main/troubleshoot/troubleshoot.coffee
+++ client/Main/troubleshoot/troubleshoot.coffee
@@ -150,9 +150,11 @@
         return warn "#{name} is not registered for health checking"  unless item

         item.once "healthCheckCompleted", =>
-          @waitingResponse -= if item.status is "fail" then @getSuccessorCount root else 1
+          @waitingResponse -= 1
+          # no need to wait for child services
+          @waitingResponse -= @getSuccessorCount children  if item.status is "fail"
           @healthChecker children  if children and item.status in ["success", "slow"]
-          @reset()  unless @waitingResponse
+          @reset "troubleshootCompleted"  unless @waitingResponse

         item.run()


IDE: Show user removed modal

IDE: Show user removed modal

--- client/app/lib/notificationcontroller.coffee
+++ client/app/lib/notificationcontroller.coffee
@@ -77,6 +77,12 @@

     @on 'MachineListUpdated', ({machineUId, action}) ->
       envDataProvider.setLastUpdatedMachineUId machineUId
+
+      switch action
+        when 'removed'
+          if ideInstance = envDataProvider.getIDEFromUId machineUId
+            ideInstance.showUserRemovedModal()
+
       kd.singletons.computeController.reset yes

     @on 'UsernameChanged', ({username, oldUsername}) ->


ComputeProvider: huge refactor includes individual stack creation inital requirements

--- workers/social/lib/social/models/computeproviders/computeprovider.coffee
+++ workers/social/lib/social/models/computeproviders/computeprovider.coffee
@@ -30,6 +30,7 @@
       'delete machines'   : ['member','moderator']
       'update machines'   : ['member','moderator']
       'list own machines' : ['member','moderator']
+      'execute templates' : ['member','moderator']
     sharedMethods         :
       static              :
         ping              :
@@ -50,6 +51,8 @@
           (signature Function)
         createGroupStack  :
           (signature Function)
+        createStackFromTemplate :
+          (signature Object, Function)

   @providers      = PROVIDERS
@@ -193,12 +196,94 @@
     provider.remove client, options, callback

+  @createStackFromTemplate = permit 'execute templates', success: revive
+
+    shouldReviveClient   : yes
+    shouldReviveProvider : no
+
+  , (client, options, callback) ->
+
+    callback null, 'Coming soon.'
+
+
+  @generateStackFromTemplate = (data, options, callback) ->
+
+    { account, user, group, template } = data
+
+    stackRevision = template.template?.sum or ''
+
+    JComputeStack = require '../stack'
+
+    # Create a new JComputeStack based on the template details
+    # We will then add machines and other information into it.
+    JComputeStack.create {
+      title         : template.title
+      config        : template.config
+      baseStackId   : template._id
+      groupSlug     : group.slug
+      account, stackRevision
+    }, (err, stack) ->
+
+      return callback err  if err
+
+      queue         = []
+      results       =
+        machines    : []
+
+      # Create JMachine documents based on the data
+      # provided in template's machines array ~ GG
+      template.machines?.forEach (machineInfo) ->
+
+        queue.push ->
+
+          # Set the stack as newly create JComputeStack
+          machineInfo.stack         = stack
+
+          # We are passing the provided credential for the template
+          # Provider implementation can override this value like we
+          # did in Koding Provider ~ GG
+          machineInfo.credential    = template.credentials?.first
+          machineInfo.generatedFrom =
+            templateId : template._id
+            revision   : stackRevision
+
+          # Inline create helper
+          create = (machineInfo) ->
+            ComputeProvider.create client, machineInfo, (err, machine) ->
+              results.machines.push { err, obj: machine }
+              queue.next()
+
+          # This is optional, since for koding group for example
+          # we don't want to add our admins into users machines ~ GG
+          unless options.addGroupAdminToMachines
+            return create machineInfo
+
+          # TODO Do we need all admins or only some of them? ~ GG
+          # Maybe some of them as admin some of them as user etc.
+          group.fetchAdmin (err, admin) ->
+
+            if not err and admin and not admin.getId().equals account.getId()
+              admin.fetchUser (err, adminUser) ->
+                if not err and adminUser
+                  machineInfo.users = [
+                    { id: adminUser.getId(), sudo: yes, owner: yes }
+                  ]
+                create machineInfo
+            else
+              create machineInfo
+
+      queue.push ->
+
+        callback null, {stack, results}
+
+      daisy queue
+

   # Auto create stack operations ###

   @createGroupStack$ = permit 'create machines',

-    success: (client, callback)->
+    success: (client, callback) ->

       ComputeProvider.createGroupStack client, callback

@@ -209,145 +294,24 @@
     callback ?= ->
     options  ?= {}

-    fetchStackTemplate client, (err, res)->
+    fetchGroupStackTemplate client, (err, res) ->

       return callback err  if err

-      { account, user, group, template } = res
-
-      stackRevision = template.template?.sum or ''
-
-      JComputeStack = require '../stack'
-      JComputeStack.create {
-        title         : template.title
-        config        : template.config
-        baseStackId   : template._id
-        groupSlug     : group.slug
-        account, stackRevision
-      }, (err, stack)->
-
+      {template, account} = res
+      checkTemplateUsage template, account, (err) ->
         return callback err  if err

-        queue         = []
-        results       =
-          rules       : []
-          machines    : []
-          domains     : []
-          connections : []
-
-        queue.push ->
-
-          checkTemplateUsage template, account, (err)->
-            return callback err  if err
-
-            account.addStackTemplate template, (err)->
-              if err then callback err else queue.next()
-
-        template.machines?.forEach (machineInfo)->
-
-          queue.push ->
-
-            machineInfo.stack         = stack
-
-            # We are passing the provided credential for the template
-            # Provider implementation can override this value like we
-            # did in Koding Provider ~ GG
-            machineInfo.credential    = template.credentials?.first
-            machineInfo.generatedFrom =
-              templateId : template._id
-              revision   : stackRevision
-
-            create = (machineInfo) ->
-              ComputeProvider.create client, machineInfo, (err, machine)->
-                results.machines.push { err, obj: machine }
-                queue.next()
-
-            # This is optional, since for koding group for example
-            # we don't want to add our admins into users machines ~ GG
-            unless options.addGroupAdminToMachines
-              return create machineInfo
-
-            # TODO Do we need all admins or only some of them? ~ GG
-            # Maybe some of them as admin some of them as user etc.
-            group.fetchAdmin (err, admin)->
-
-              if not err and admin and not admin.getId().equals account.getId()
-                admin.fetchUser (err, adminUser)->
-                  if not err and adminUser
-                    machineInfo.users = [
-                      { id: adminUser.getId(), sudo: yes, owner: yes }
-                    ]
-                  create machineInfo
-              else
-                create machineInfo
-
-
-        template.domains?.forEach (domainInfo)->
-
-          queue.push ->
-            domain = domainInfo.domain.replace "${username}", user.username
-            JProposedDomain.createDomain {
-              domain, account,
-              stack : stack._id
-              group : group.slug
-            }, (err, r)->
-              console.warn err  if err?
-              results.domains.push { err, obj: r }
-              queue.next()
-
-        template.connections?.forEach (c)->
-
-          queue.push ->
-
-            # Assign rule to domain
-            if c.rules? and c.domains?
-
-              rule   = results.rules[c.rules]
-              domain = results.domains[c.domains]
-
-              if not rule?.err and not domain?.err
-                results.connections.push
-                  err : new KodingError "Not implemented"
-                  obj : null
-              else
-                results.connections.push
-                  err : new KodingError "Missing edge"
-                  obj : null
-
-              queue.next()
-
-            # Assign a domain to machine
-            else if c.machines? and c.domains?
-
-              domain  = results.domains[c.domains]
-              machine = results.machines[c.machines]
-
-              if not domain?.err and not machine?.err
-
-                domain.obj.bindMachine machine.obj.getId(), (err)->
-                  results.connections.push { err, obj: ok: !err? }
-                  queue.next()
-
-              else
-                results.connections.push
-                  err : new KodingError "Missing edge"
-                  obj : null
-                queue.next()
-
-            else
-              queue.next()
-
-        queue.push ->
-
-          callback null, {stack, results}
-
-        daisy queue
+        account.addStackTemplate template, (err) ->
+          return callback err  if err
+
+          ComputeProvider.generateStackFromTemplate res, options, callback

   do ->

     JGroup = require '../group'
-    JGroup.on 'MemberAdded', ({group, member})->
+    JGroup.on 'MemberAdded', ({group, member}) ->

       # No need to try creating group stacks for guests or koding group members
       return  if group.slug in ['guests', 'koding']
@@ -359,7 +323,7 @@

       ComputeProvider.createGroupStack client,
         addGroupAdminToMachines: yes
-      , (err, res = {})->
+      , (err, res = {}) ->

         {stack, results} = res

@@ -369,7 +333,7 @@

     JAccount = require '../account'
-    JAccount.on 'UsernameChanged', ({ oldUsername, username, isRegistration })->
+    JAccount.on 'UsernameChanged', ({ oldUsername, username, isRegistration }) ->

       return  unless oldUsername and username
       return  if isRegistration


link to full record

link to full record

--- shared/ironTableController.coffee
+++ shared/ironTableController.coffee
@@ -280,6 +280,7 @@
           value = @valueFromRecord(key, col, record)
           colData.push
             template     : col.template
+            record       : record  # Link to full record if we need it
             value        : col.display?(value, record, @params) or value
             aLink        : col.link?(value, record)
             title        : col.title?(value, record) or col.title


handle record sub elements

handle record sub elements

--- shared/ironTableController.coffee
+++ shared/ironTableController.coffee
@@ -130,6 +130,16 @@
     select: ->
         @defaultSelect

+    valueFromRecord: (key, col, record) ->
+        if col.dataKey
+            subElements = col.dataKey.split('.')
+            value = record
+            for subElement in subElements
+                value = value?[subElement]
+            value
+        else
+            record[key]
+
     data: ->
         records = @collection()?.find(@select(),
             sort: @sort()
@@ -139,12 +149,14 @@
         recordData = []
         for record in records
             colData = []
-            for col, colObj of @_cols()
-                if not (colObj.hide?() or colObj.hide)
+            for key, col of @_cols()
+                dataKey = col.dataKey or key
+                if not (col.hide?() or col.hide)
+                    value = @valueFromRecord(key, col, record)
                     colData.push
-                        value : colObj.display?(record[col], record) or record[col]
-                        aLink : colObj.link?(record[col], record)
-                        title : colObj.title?(record[col], record) or colObj.title
+                        value : col.display?(value, record) or value
+                        aLink : col.link?(value, record)
+                        title : col.title?(value, record) or col.title

             recordData.push
                 colData: colData
@@ -154,9 +166,7 @@
                 deleteOk: @collection().deleteOk?(record)

         recordDisplayStop = @skip() + recordData.length
-        #if recordDisplayStop > @_sess("recordCount")
-        #    recordDisplayStop = @_sess("recordCount")
-
+
         theData =
             haveData: records? and (records.length > 0 or @_sess("recordCount") > 0)
             tableTitle: @_tableTitle()
@@ -210,6 +220,7 @@
                 col.displayType = col.type
                 col.checkbox = false
                 col.checked = ''
+                value = @valueFromRecord(key, col, record)
                 if col.type is 'boolean'
                     col.displayType = 'checkbox'
                     col.checkbox = true
@@ -218,8 +229,8 @@
                             col.checked = 'checked'
                     else if col.default
                         col.checked = 'checked'
-                else if record?[dataKey]?
-                    col.value = col.display?(record[dataKey], record) or record[dataKey]
+                else if value?
+                    col.value = col.display?(value, record) or value
                 else if col.default?
                     col.value = col.default


convert data to JSON then test if empty

convert data to JSON then test if empty

Fixes bug where data was a string encoded empty object i.e. "{}"

It failed the isEmpty test since it was tested before converting

--- shared/src/api/collections.coffee
+++ shared/src/api/collections.coffee
@@ -134,15 +134,16 @@
   simpleRequest.url = simpleRequest.url.replace(requestConfig.baseURL, '') if requestConfig.baseURL
   simpleRequest.url = trim(simpleRequest.url, '/')

-  if isEmpty(simpleRequest.data)
-    simpleRequest = omit(simpleRequest, 'data')
-  else if isString(simpleRequest.data)
+  if isString(simpleRequest.data)
     try
       simpleRequest.data = JSON.parse(simpleRequest.data)
     catch e

-  simpleRequest.data = omitBy(simpleRequest.data, isUndefined) if simpleRequest.data
+  if isEmpty(simpleRequest.data)
+    delete simpleRequest.data

+  if simpleRequest.data
+    simpleRequest.data = omitBy(simpleRequest.data, isUndefined)
   simpleRequest

 class XHRRecords


restore mathjax

restore mathjax

--- shared/src/helpers/mathjax.coffee
+++ shared/src/helpers/mathjax.coffee
@@ -53,7 +53,7 @@
 startMathJax = ->
   MATHJAX_CONFIG =
     showProcessingMessages: false
-#    extensions: ["[a11y]/explorer.js"]
+    extensions: ["[a11y]/explorer.js"]
     tex2jax:
       displayMath: [[MATH_MARKER_BLOCK, MATH_MARKER_BLOCK]]
       inlineMath:  [[MATH_MARKER_INLINE, MATH_MARKER_INLINE]]
@@ -64,7 +64,7 @@
     window.MathJax.Hub.Configured()

   if window.MathJax?.Hub
-#    window.MathJax.Ajax.config.path["a11y"] = "https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.1/extensions/a11y"
+    window.MathJax.Ajax.config.path["a11y"] = "https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.1/extensions/a11y"
     window.MathJax.Hub.Config(MATHJAX_CONFIG)
     # Does not seem to work when passed to Config
     window.MathJax.Hub.processSectionDelay = 0


Attempt to fix mobile auctions test

Attempt to fix mobile auctions test

--- mobile/apps/auctions/test/template.coffee
+++ mobile/apps/auctions/test/template.coffee
@@ -23,7 +23,8 @@
           pastAuctions: [@closedSale]
           currentAuctions: [@openSale]
           upcomingAuctions: [@previewSale]
-        done()
+        , ->
+          done()

     after ->
       benv.teardown()


remove use of ngettext in discussion jasmine test

--- common/static/coffee/spec/discussion/view/discussion_thread_view_spec.coffee
+++ common/static/coffee/spec/discussion/view/discussion_thread_view_spec.coffee
@@ -146,11 +146,7 @@
             expect(view.$(".js-response-list .discussion-response").length).toEqual(numNonEndorsed)
             assertResponseCountAndPaginationCorrect(
                 view,
-                ngettext(
-                    "#{numNonEndorsed} #{if numEndorsed then "other " else ""}response",
-                    "#{numNonEndorsed} #{if numEndorsed then "other " else ""}responses",
-                    numNonEndorsed
-                )
+                "#{numNonEndorsed} #{if numEndorsed then "other " else ""}#{if numNonEndorsed == 1 then "response" else "responses"}",
                 if numNonEndorsed then "Showing all responses" else null,
                 null
             )


Cosmetic changes - align selectors

Cosmetic changes - align selectors

--- client/test/lib/teams/teamslikeunlike.coffee
+++ client/test/lib/teams/teamslikeunlike.coffee
@@ -49,11 +49,11 @@
       .waitForElementVisible  menuButton,   20000
       .click                  menuButton
       .waitForElementVisible  editButton,   20000
-      .click  		            editButton
+      .click                  editButton
       .waitForElementVisible  chatInput,    20000
       .clearValue             chatInput
       .setValue               chatInput,    editedmessage
-      .setValue		            chatInput,    browser.Keys.ENTER
+      .setValue               chatInput,    browser.Keys.ENTER
       .waitForElementVisible  editedText,   20000
       .assert.containsText    editedText,   editedmessage
       .end()


use https over http (#22)

use https over http (#22)

--- scripts/bungie-data-helper.coffee
+++ scripts/bungie-data-helper.coffee
@@ -20,7 +20,7 @@
       console.log(Object.keys(response.definitions.damageTypes).length)
       console.log("damageType empty for #{itemDefs.itemName}")

-    prefix = 'http://www.bungie.net'
+    prefix = 'https://www.bungie.net'
     iconSuffix = itemDefs.icon
     itemSuffix = '/en/Armory/Detail?item='+hash


Modify visuals

Modify visuals

--- scripts/bungie-data-helper.coffee
+++ scripts/bungie-data-helper.coffee
@@ -146,7 +146,7 @@
       message += '\n'
       for mod in activityDetails.modifiers
         for skull in mod.skulls
-          message += "#{skull.displayName}\n"
+          message += "*#{skull.displayName}*\n"
           message += "_#{skull.description}_\n"

     if message
@@ -160,11 +160,11 @@

     message = ''
     for bountyItem in vendorBountyDetails.bountyItemsDetail
-        message += "#{bountyItem.itemName}\n"
+        message += "*#{bountyItem.itemName}*\n"
         message += "_#{bountyItem.itemDescription}_\n"

     attachment =
-      title: vendorBountyDetails.vendorName
+      author_name: vendorBountyDetails.vendorName + ' Bounties'
       fallback: vendorBountyDetails.vendorName
       text: message
       mrkdwn_in: ['text']


add ml example

add ml example

--- server/scripts/load_scales.coffee
+++ server/scripts/load_scales.coffee
@@ -11,7 +11,7 @@
     #roleRequired: 'root',
     action: () ->

-      ml_scales.remove '*'
+      ml_scales.delete '*'

       sheet = '/home/cloo/scales.csv'
       scales = API.convert.csv2json(undefined,fs.readFileSync(sheet).toString())


now extends auth.AuthContext

now extends auth.AuthContext

--- modules/http/lib/sessions/Sessions.coffee
+++ modules/http/lib/sessions/Sessions.coffee
@@ -6,13 +6,11 @@
     ##
     ##
     ##
-    class HttpSessions extends floyd.Context
+    class HttpSessions extends floyd.auth.AuthContext

         configure: (config)->

             @_hiddenKeys.push 'Registry', 'Session'
-            @_TOKENS = config.tokens
-
             super new floyd.Config

                 data:
@@ -167,13 +165,9 @@
         ##
         ##
         ##
-        authorize: (token, fn)->
-
-            sid = token.substr 40
-
-            #console.log 'session authorize session %s', sid
-
-            if (sess = @_registry.get sid)
+        authorize: (token, fn)->
+
+            if token && (sess = @_registry?.get (sid = token.substr 40))

                 #console.log 'found session', sess, sess.public.user?.login

@@ -203,16 +197,8 @@
                     fn()

-            ## a static token has to be of the length of 40 plus the key in @_TOKENS
-            ## e.g. 8b804dd8-7a88-4142-8b33-913a586d67b4----backend so that the following comes true
-            ## @_TOKENS['backend'] is '8b804dd8-7a88-4142-8b33-913a586d67b4----backend'
-            else if @_TOKENS[sid] is token
-                fn()
-
-            else
-                #console.log 'session failure', sid, sess, sess?.user
-
-                fn new Error 'unauthorized sessionID'
+            else
+                super token, fn

         ##
         ##
@@ -221,38 +207,33 @@

             @logger.debug 'session authenticate identity %s', identity.id

-            identity.token (err, token)=>
-                if err || !token
-                    @logger.debug 'session authenticate NO TOKEN', identity.id
-
-                    return fn(err || new Error 'session authenticate NO TOKEN')
-
-                ##
-                if @_TOKENS && @_TOKENS[identity.id.split('.').shift()] is token
-
-                    @logger.debug 'found known Token, authenticate SUCCESS', identity.id
-
-                    fn()
-
-                else
+            super identity, (err)=>
+                return fn() if !err ## successfully authenticated
+
+                identity.token (err, token)=>
+                    if err || !token
+                        @logger.warning 'session authenticate NO TOKEN', identity.id
+
+                        return fn(err || new Error 'session authenticate NO TOKEN')
+
                     sid = token.substr 40

                     @logger.fine 'session authenticate session %s', sid
-
+
                     @_load sid, (err, sess)=>
                         return fn(err) if err
-
+
                         @logger.finer 'session authenticate hash %s', token.substr 0, 39
-
+
                         if token is sess.token
-
+
                             @logger.debug 'session authenticate SUCCESS', identity.id
-
+
                             fn()
-
+
                         else
                             @logger.debug 'session authenticate FAILURE', identity.id
-
+
                             fn new Error 'session authenticate FAILURE'


Remove `start` method from git-commit-beta return object

--- lib/models/git-commit-beta.coffee
+++ lib/models/git-commit-beta.coffee
@@ -74,16 +74,13 @@
     .then -> showFile filePath
     .then (textEditor) ->
       disposables.add textEditor.onDidSave ->
-        commit(dir(repo), filePath).then -> new GitPush(repo) if andPush
+        commit(dir(repo), filePath).then -> GitPush(repo) if andPush
       disposables.add textEditor.onDidDestroy -> cleanup currentPane, filePath
-    .catch (message) ->
-      notifier.addInfo message
-
-  start: ->
-    if stageChanges
-      git.add(repo, update: stageChanges)
-      .then -> startCommit()
-    else startCommit()
+    .catch (message) -> notifier.addInfo message
+
+  if stageChanges
+    git.add(repo, update: stageChanges).then -> startCommit()
+  else startCommit()

 # class GitCommit
 #   # Public: Helper method to return the current working directory.


Implement listing and individual legislation calls.

--- routes/api/v1/legislation.coffee
+++ routes/api/v1/legislation.coffee
@@ -13,14 +13,70 @@
 #
 #    You should have received a copy of the GNU General Public License
 #    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ObjectId = require('mongodb').ObjectID;
+
 module.exports = (app, db) ->
   # GET /api/v1/legislation - Get all matching legislation for query.
   app.get '/api/v1/legislation', (req, res) ->
-    res.send(501)
+    db.collection("sessions").findOne {current: true}, (err, currentSession) ->
+      unless currentSession?
+        err = "Could not find current session."
+        errorId = Math.random().toString(36).substring(7)
+        console.error("Error " + errorId + ": " + err)
+
+        res.json
+          id: errorId,
+          error: err
+        , 500
+
+        return
+
+      selectedSessionIdStr = req.query.sessionId || currentSession._id.toString()
+      selectedSessionId = new ObjectId(selectedSessionIdStr)
+
+      db.collection("legislation").find({sessionId: selectedSessionId}).sort({number: 1}).toArray (err, results) ->
+        if err
+          errorId = Math.random().toString(36).substring(7)
+          console.error("Error " + errorId + ": " + err)
+
+          res.json
+            id: errorId,
+            error: err
+          , 500
+
+          return
+
+        res.json(results)

   # GET /api/v1/legislation/:id - Get all info on individual legilsation
   app.get '/api/v1/legislation/:id', (req, res) ->
-    res.send(501)
+    try
+      legislationObjectId = new ObjectId req.params.id
+    catch
+      errorOutput =
+        fieldId: "legislationId",
+        error: "invalid",
+        message: "The legislation id you requested was not a valid identifier. Identifiers should conform to MongoDB's ObjectID format."
+
+      res.json errorOutput, 417
+      return
+
+    db.collection("legislation").findOne {_id: legislationObjectId}, (err, legislation) ->
+      if err
+        errorId = Math.random().toString(36).substring(7)
+        console.error("Error " + errorId + ": " + err)
+
+        res.json
+          id: errorId,
+          error: err
+        , 500
+
+        return
+
+      if legislation?
+        res.json legislation
+      else
+        res.send 404

   # GET /api/v1/legislation/:type/:number - Retrieve a legilation type
   # by number


Fix comment

Fix comment

--- lib/provider/provider-base.coffee
+++ lib/provider/provider-base.coffee
@@ -227,9 +227,9 @@
       openOptions.pane = pane
     else
       # NOTE: See #107
-      # Whe Atom is old and workspace.open not accept pane options to open URI.
-      # We need to activate that pane first.
-      # Otherwise, when original pane have item for that URL, it open that item on current pane.
+      # In Atom v1.16.0 or older, `workspace.open` doesn't allow to specify target pane to open file.
+      # So need to activate target pane first.
+      # Otherwise, when original pane have item for same path(URI), it opens on CURRENT pane.
       originalActivePane = atom.workspace.getActivePane() unless activatePane
       pane.activate()


better title for editing

better title for editing

--- shared/ironTableController.coffee
+++ shared/ironTableController.coffee
@@ -386,7 +386,7 @@

     editRecord: (_id) =>
         @_sess("currentRecordId", _id)
-        CoffeeModal.form(@formTemplate, @formData('edit', _id), @saveRecord)
+        CoffeeModal.form(@formTemplate, @formData('edit', _id), @saveRecord, 'Edit ' + @_recordName().capitalize())

     setFilterColumn: (col) ->


Disable rtc plugin because networking

Disable rtc plugin because networking

--- scripts/rtc.coffee
+++ scripts/rtc.coffee
@@ -40,7 +40,7 @@
         msg.send err
     )

-module.exports = (robot) ->
-  robot.hear /rtc #?(\d+)/ig, (msg) ->
-    for bug in distinctBugs msg.match
-      fetchSummary msg, bug
+# module.exports = (robot) ->
+#   robot.hear /rtc #?(\d+)/ig, (msg) ->
+#     for bug in distinctBugs msg.match
+#       fetchSummary msg, bug


Updating mipmaps each video texture frame.

It fixes the "ghost" of the first frame in the video-texture but it must be optimized.

--- engine/fetch_assets.coffee
+++ engine/fetch_assets.coffee
@@ -67,14 +67,14 @@
         # that it can be played without interruption
         video.addEventListener 'canplaythrough', ->
             configure_texture(tex, video, filter, wrap, context).then (tex)->
+
                 # update_texture will be called on each game engine frame (in main_loop)
                 # but it only will update the texture if video.currentTime has been changed.
                 update_texture = ->
                     if video.currentTime != video.lastTime
                         video.lastTime = video.currentTime
-                        gl.bindTexture gl.TEXTURE_2D, tex.tex
-                        gl.pixelStorei gl.UNPACK_FLIP_Y_WEBGL, true
-                        gl.texImage2D gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, video
+                        #TODO: Optimize
+                        configure_texture(tex, video, filter, wrap, context)

                 video.update_texture = update_texture
                 resolve tex


fix what appears to be a regression

fix what appears to be a regression

--- library/membrane/preflight.coffee
+++ library/membrane/preflight.coffee
@@ -207,10 +207,10 @@
     # is going to be implemented. Most of these libraries required
     # by the internal implementations of the various subcomponents.
     # Refer to `BowerSupport` class implementation for information.
-    @bower "platform", "platform.js"
     @bower "async", "lib/async.js"
     @bower "eventemitter2"
     @bower "js-signals"
+    @bower "platform"
     @bower "loglevel"
     @bower "lodash"
     @bower "jquery"


medium counts route

medium counts route

--- node/server.coffee
+++ node/server.coffee
@@ -77,6 +77,9 @@
 gravity.get '/api/v1/search/filtered/fair/:id/options', (req, res) ->
   res.send { medium: { Painting: 'painting' } }

+gravity.get '/api/v1/search/filtered/fair/:id/suggest', (req, res) ->
+  res.send { medium: { painting: 420 } }
+
 gravity.get '/api/v1/profile/alessandra/posts', (req, res) ->
   res.send [fabricate 'post']


Make authorization logic clearer

Make authorization logic clearer

- Taken from meteor-voltage

--- server/boot.coffee
+++ server/boot.coffee
@@ -62,11 +62,13 @@
         return false

       # If role AND group is passed
-      if Blog.settings.adminRole and Blog.settings.adminGroup and not Roles.userIsInRole(Meteor.user(), Blog.settings.adminRole, Blog.settings.adminGroup)
-        return false
+      if Blog.settings.adminRole and Blog.settings.adminGroup
+        if not Roles.userIsInRole(Meteor.user(), Blog.settings.adminRole, Blog.settings.adminGroup)
+          return false

       # If only role is passed
-      if Blog.settings.adminRole and not Roles.userIsInRole(Meteor.user(), Blog.settings.adminRole)
-        return false
+      else if Blog.settings.adminRole
+        if not Roles.userIsInRole(Meteor.user(), Blog.settings.adminRole)
+          return false

       true


attempt to fix create track spec

attempt to fix create track spec

--- app/assets/javascripts/app/views/tracks/new_track_form.js.coffee
+++ app/assets/javascripts/app/views/tracks/new_track_form.js.coffee
@@ -25,17 +25,17 @@
   init_form_validation: ->
     @$('#track_upload_form').validate(
       ignore: 'input[type=hidden]',
-      groups:
-        suit: 'track_file[track_attributes][wingsuit_id] track_file[track_attributes][suit]'
       rules:
         'track_file[track_attributes][name]':
           minlength: 3,
           required: ->
             return $('#newTrackModal input#name')
         'track_file[track_attributes][wingsuit_id]':
-          require_from_group: [1, '.suit-group']
+          required: ->
+            $('[name="track_file[track_attributes][wingsuit_id]"]').is(':visible')
         'track_file[track_attributes][suit]':
-          require_from_group: [1, '.suit-group']
+          required: ->
+            $('[name="track_file[track_attributes][suit]"]').is(':visible')
         'track_file[track_attributes][location]':
           minlength: 3,
           required: true
@@ -116,12 +116,14 @@

   clean_hide_suit_select: ->
     @$('.new-track-wingsuit-select').val(null).trigger('change')
+    @$('.new-track-wingsuit-select').hide()
     @$('.new-track-wingsuit-select + span').hide()

   show_suit_input: ->
     @$('input[name="track_file[track_attributes][suit]"]').show()

   show_suit_select: ->
+    @$('.new-track-wingsuit-select').show()
     @$('.new-track-wingsuit-select + span').show()

   current_suit_mode: ->


remove unused code

remove unused code

--- app/assets/javascripts/backbone/apps/props/list/list_view.coffee
+++ app/assets/javascripts/backbone/apps/props/list/list_view.coffee
@@ -56,9 +56,6 @@
     modelEvents:
       'change' : 'render'

-    onUpvote: (e) ->
-      console.log 'kliknal'
-
     serializeData: ->
       _.extend super,
         created_at: moment(@model.get('created_at')).fromNow()
@@ -72,3 +69,13 @@
     childViewContainer: '.props-list'
     className: 'list-group'
     emptyView: List.EmptyView
+
+    events:
+      'click [data-page]' : 'pageSelected'
+
+    pageSelected: (e) ->
+      if $(e.target).data().page == 'next'
+        @collection.getNextPage()
+      else
+        @collection.getPreviousPage()
+


Don’t break when date picker is not used in group

--- app/assets/javascripts/components/datepicker_component.js.coffee
+++ app/assets/javascripts/components/datepicker_component.js.coffee
@@ -6,18 +6,18 @@
   date: null

   didInsertElement: ->
-    @.get('parentView').registerPicker(@)
+    Ember.tryInvoke(@get('parentView'), 'registerPicker', [@])
     @set('value', @get('date'))
     $picker = @$().datepicker(autoclose: true)

     $picker.on('changeDate', (event) =>
       @set('date', event.format())
-      @get('parentView').dateChanged(@)
+      Ember.tryInvoke(@get('parentView'), 'dateChanged', [@])
     )

     $picker.on('clearDate', (event) =>
       @set('date', null)
-      @get('parentView').dateChanged(@)
+      Ember.tryInvoke(@get('parentView'), 'dateChanged', [@])
     )

     @set('$picker', $picker)


react course rewrite: assignment collect; don't allow until after assigned, etc. -- FLOW

--- salvus/page/editor_course.cjsx
+++ salvus/page/editor_course.cjsx
@@ -24,6 +24,22 @@
 ###
 TODO:

+- [x] (1:00?) (0:12) assignment collect; don't allow until after assigned, etc. -- FLOW
+- [ ] (0:30?) when adding assignments filter out folders contained in existing assignment folders
+- [ ] (0:30?) when searching show how many things are not being shown.
+- [ ] (0:45?) delete student; show deleted students; permanently delete students
+- [ ] (0:45?) delete assignment; show deleted assignments; permanently delete assignment
+- [ ] (1:00?) help page -- integrate info
+- [ ] (1:00?) changing title/description needs to change it for all projects
+- [ ] (1:00?) clean up after flux/react when closing the editor
+- [ ] (1:30?) cache stuff/optimize
+- [ ] (2:00?) make everything look pretty
+        - triangles for show/hide assignment info like for students
+        - error messages in assignment page -- make hidable and truncate-able
+- [ ] (3:00?) bug searching / testing / debugging
+- [ ] (1:00?) (0:19+) fix bugs in opening directories in different projects using actions -- completely busted right now due to refactor of directory listing stuff....
+
+DONE:
 - [x] (0:30?) (0:09) create function to render course in a DOM element with basic rendering; hook into editor.coffee
 - [x] (0:30?) (0:36) create proper 4-tab pages using http://react-bootstrap.github.io/components.html#tabs
 - [x] (0:45?) (1:35) create dynamically created store attached to a project_id and course filename, which updates on sync of file.
@@ -42,21 +58,7 @@
 - [x] (0:45?) (0:20)  counter for each page heading (num students, num assignments)
 - [x] (2:00?) (0:59) links to grade each student; buttons to assign to one student, collect from one, etc.
 - [x] (1:30?) (1:03) display info about each student when they are clicked on (in students page) -- ugly but nicely refactored
-- [ ] (1:00?) assignment collect; don't allow until after assigned, etc. -- FLOW
-- [ ] (0:30?) when adding assignments filter out folders contained in existing assignment folders
-- [ ] (0:30?) when searching show how many things are not being shown.
-- [ ] (0:45?) delete student; show deleted students; permanently delete students
-- [ ] (0:45?) delete assignment; show deleted assignments; permanently delete assignment
-- [ ] (1:00?) show deleted assignments (and purge)
-- [ ] (1:00?) help page -- integrate info
-- [ ] (1:00?) changing title/description needs to change it for all projects
-- [ ] (1:00?) clean up after flux/react when closing the editor
-- [ ] (1:30?) cache stuff/optimize
-- [ ] (2:00?) make everything look pretty
-        - triangles for show/hide assignment info like for students
-        - error messages in assignment page -- make hidable and truncate-able
-- [ ] (3:00?) bug searching / testing / debugging
-- [ ] (1:00?) (0:19+) fix bugs in opening directories in different projects using actions -- completely busted right now due to refactor of directory listing stuff....
+
 ###

 # standard non-SMC libraries
@@ -603,9 +605,9 @@
             student = @get_student(student)
             student_id = student.get('student_id')
             info =
-                last_assignment    : assignment.get('last_assignment')?.get(student_id)?.toJS() ? {}
-                last_collect       : assignment.get('last_collect')?.get(student_id)?.toJS() ? {}
-                last_return_graded : assignment.get('last_return_graded')?.get(student_id)?.toJS() ? {}
+                last_assignment    : assignment.get('last_assignment')?.get(student_id)?.toJS()   # important to be undefined if no info -- assumed in code
+                last_collect       : assignment.get('last_collect')?.get(student_id)?.toJS()
+                last_return_graded : assignment.get('last_return_graded')?.get(student_id)?.toJS()
                 grade              : assignment.get('grades')?.get(student_id)
                 student_id         : student_id
                 assignment_id      : assignment.get('assignment_id')
@@ -923,7 +925,7 @@
     copy: (type, assignment_id, student_id) ->
         @props.flux.getActions(@props.name).copy_assignment(type, assignment_id, student_id)

-    render_last: (name, obj, type, info) ->
+    render_last: (name, obj, type, info, enable_copy) ->
         open = => @open(type, info.assignment_id, info.student_id)
         copy = => @copy(type, info.assignment_id, info.student_id)
         v = [{name+': '}]
@@ -933,22 +935,24 @@
                 v.push(e.preventDefault();open()}>(open))
             if obj.error
                 v.push({obj.error})
-            v.push(e.preventDefault();copy()}>(re-copy))
+            if enable_copy
+                v.push(e.preventDefault();copy()}>(re-copy))
         else
-            v.push(e.preventDefault();copy()}>(copy))
+            if enable_copy
+                v.push(e.preventDefault();copy()}>(copy))
         return v

     render: ->
         info = @props.flux.getStore(@props.name).student_assignment_info(@props.student, @props.assignment)
         <Row >
             <Col md=4 key='last_assignment'>
-                {@render_last('Assigned', info.last_assignment, 'assigned', info)}
+                {@render_last('Assigned', info.last_assignment, 'assigned', info, true)}
             </Col>
             <Col md=4 key='collect'>
-                {@render_last('Collected', info.last_collect, 'collected', info)}
+                {@render_last('Collected', info.last_collect, 'collected', info, info.last_assignment?)}
             </Col>
             <Col md=4 key='return_graded'>
-                {@render_last('Returned', info.last_return_graded, 'graded', info)}
+                {@render_last('Returned', info.last_return_graded, 'graded', info, info.last_collect?)}
             </Col>
         </Row>

@@ -1060,16 +1064,18 @@
         @props.flux.getActions(@props.name).copy_assignment_from_all_students(@props.assignment)

     render_collect_button: ->
-        <Button onClick={@collect_assignment}>
+        # disable the button if nothing ever assigned
+        <Button onClick={@collect_assignment} disabled={(@props.assignment.get('last_assignment')?.size ? 0) == 0}>
             <Icon name="share-square-o" rotate="180" /> Collect from all...
         </Button>

     return_assignment: ->
-        # assign assignment to all (non-deleted) students
+        # Assign assignment to all (non-deleted) students.
         @props.flux.getActions(@props.name).return_assignment_to_all_students(@props.assignment)

     render_return_button: ->
-        <Button onClick={@return_assignment}>
+        # Disable the button if nothing ever collected.
+        <Button onClick={@return_assignment} disabled={(@props.assignment.get('last_collect')?.size ? 0) == 0}>
             <Icon name="share-square-o" /> Return to all...
         </Button>


fix setDate

fix setDate

--- components/auth_modal/view.coffee
+++ components/auth_modal/view.coffee
@@ -82,7 +82,7 @@
       @showError _.capitalize res.error
     else
       oneDayFromNow = new Date()
-      oneDayFromNow.setDate(sevenDaysFromNow.getDate() + 1)
+      oneDayFromNow.setDate(oneDayFromNow.getDate() + 1)
       Cookies.set('destination', @destination, expires: oneDayFromNow) if @destination

       if @state.get('mode') is 'login'


chorus: minor

chorus: minor

--- scripts-src/reversible_ca.coffee
+++ scripts-src/reversible_ca.coffee
@@ -149,17 +149,17 @@
     throw "Field size must be even, not " + field.width + "x" + field.height  if field.width % 2 isnt 0 or field.height % 2 isnt 0
     @phase = 0

-  transform_from: (x0, y0, rule) ->
+  transform_from: (xy0, rule) ->
     rule_table = rule.table
     field = @field
     data = field.data
     w = field.width
     h = field.height
     a = 0 #cell index
-    for y in [y0 ... h] by 2
+    for y in [xy0 ... h] by 2
       dy = (if (y + 1 < h) then w else (w * (1 - h)))
-      a = y*w+x0
-      for x in [x0 ... w] by 2
+      a = y*w+xy0
+      for x in [xy0 ... w] by 2
         dx = (if (x + 1 < w) then 1 else (1 - w))
         #Code was inlined to increase performance
         b=a + dx; c=a + dy; d=b + dy;
@@ -171,23 +171,19 @@
           data[c] = (Y >> 2) & 1
           data[d] = (Y >> 3) & 1
         a += 2
-    null #Return null to increase performance
+    return

   #Combine given value by XOR with each block of the current phase.
   apply_xor: (value) ->
-    xy0 = @phase
-    @transform_from xy0, xy0, from_list(xor_transposition value)
+    @transform_from @phase, from_list(xor_transposition value)

   transform: (rule)->
-    xy0 = @phase
-    @transform_from xy0, xy0, rule
-    @phase = mod2 @phase+1
+    @transform_from @phase, rule
+    @phase ^= 1

   untransform: (irule) ->
-    xy0 = mod2 @phase-1
-    @transform_from xy0, xy0, irule
-    @phase = mod2 @phase-1
-
+    @phase ^= 1
+    @transform_from @phase, irule

   clear: ->
     @field.fill 0


Add regression tests

Add regression tests

--- modules/titlebar/test/spec.coffee
+++ modules/titlebar/test/spec.coffee
@@ -47,6 +47,25 @@
     titlebar = new hx.TitleBar(hx.select('#fixture').select('.hx-heading').node())
     titlebar.active(0)
     hx.selectAll('.hx-titlebar-link').classed('hx-selected').should.eql([true, false, false, false, false])
+
+  it 'should set the active section by node correctly', ->
+    titlebar = new hx.TitleBar(hx.select('#fixture').select('.hx-heading').node())
+    node = hx.selectAll('.hx-titlebar-link').nodes(3)
+    titlebar.active(node)
+    hx.selectAll('.hx-titlebar-link').classed('hx-selected').should.eql([false, false, false, true, false])
+
+  it 'should set the active section by ID correctly', ->
+    titlebar = new hx.TitleBar(hx.select('#fixture').select('.hx-heading').node())
+    hx.select(hx.selectAll('.hx-titlebar-link').nodes(2))
+      .attr('id', '#titlebar-active-test')
+    titlebar.active('#titlebar-active-test')
+    hx.selectAll('.hx-titlebar-link').classed('hx-selected').should.eql([false, false, true, false, false])
+
+  it 'should set the active section by Selection correctly', ->
+    titlebar = new hx.TitleBar(hx.select('#fixture').select('.hx-heading').node())
+    sel = hx.select(hx.selectAll('.hx-titlebar-link').nodes(2))
+    titlebar.active(sel)
+    hx.selectAll('.hx-titlebar-link').classed('hx-selected').should.eql([false, false, true, false, false])

   it 'should set the active section by index correctly', ->
     titlebar = new hx.TitleBar(hx.select('#fixture').select('.hx-heading').node())


Remove JSXTransformer from src concat

Remove JSXTransformer from src concat

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -25,9 +25,8 @@
         src: [
           'bower_components/rxjs/dist/rx.all.js'
           'bower_components/rxjs/dist/rx.testing.js'
-          'bower_components/ramda/dist/ramda.js'
           'bower_components/jquery/dist/jquery.js'
-          'node_modules/react/dist/JSXTransformer.js'
+          'node_modules/ramda/dist/ramda.js'
           'node_modules/react/dist/react.js'
           'public/assets/analyzer.js'
         ]


Fix buid task, create a new task `grunt archive` to create old design archive

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -126,6 +126,9 @@
         command: "jekyll serve --watch"

       dist:
+        command: "jekyll build"
+
+      archive:
         command: "jekyll build --baseurl <%= core.cfg.base %>/ -d <%= core.cfg.destination %><%= core.cfg.base %>/"

       sync:
@@ -150,6 +153,7 @@
   grunt.registerTask "server", ["less:server", "concurrent"]
   grunt.registerTask "test", ["coffeelint", "recess"]
   grunt.registerTask "build", ["clean", "test", "less:dist", "shell:dist", "concurrent:dist"]
+  grunt.registerTask "archive", ["build", "shell:archive", "concurrent:dist"]
   grunt.registerTask "sync", ["build", "shell:sync"]
   grunt.registerTask "s3", ["shell:s3"]
   grunt.registerTask "default", ["build"]


Took out accidental change to Gruntfile.

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -777,7 +777,7 @@
       options: {
         outdir: 'build/dev/uproxy/integration/'
         # Uncomment this for debugging
-        keepRunner: true,
+        # keepRunner: true,
       }
     }
   }


Ignore reveal.js plugin leap

Ignore reveal.js plugin leap

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -88,7 +88,7 @@
                 options:
                     timestamp: true
                     exclude: [
-                      '/bower_components/reveal.js/plugin/leap/'
+                      'bower_components/reveal.js/plugin/leap'
                     ]
                 src: [
                     'css/**'


Add coffeelint Grunt task

Add coffeelint Grunt task

Include it in default task

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -8,6 +8,7 @@
   grunt.loadNpmTasks 'grunt-contrib-handlebars'
   grunt.loadNpmTasks 'grunt-casperjs'
   grunt.loadNpmTasks 'grunt-contrib-watch'
+  grunt.loadNpmTasks 'grunt-coffeelint'

   # Project configuration.
   grunt.initConfig
@@ -106,8 +107,15 @@
         files: 'css/**/*'
         tasks: ['cssmin']

+    coffeelint:
+      all:
+        files: '**/*.coffee'
+        options:
+          no_tabs: false
+          no_empty_param_list: true
+
   # Default task.
-  grunt.registerTask 'default', ['coffee', 'handlebars', 'concat', 'uglify', 'cssmin', 'replace']
+  grunt.registerTask 'default', ['coffeelint', 'coffee', 'handlebars', 'concat', 'uglify', 'cssmin', 'replace']

   grunt.registerTask 'test', 'casperjs'
-  grunt.registerTask 'all', ['coffee', 'handlebars', 'concat', 'uglify', 'cssmin', 'replace', 'test']
+  grunt.registerTask 'all', ['default', 'test']


watch for test changes too

watch for test changes too

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -5,14 +5,17 @@
             grunt.file.readJSON 'package.json'

         coffee:
-          dist:
-            src: ['lib/**/*.coffee']
-            dest: 'dist/jsmegahal.js'
+            dist:
+                src: ['lib/**/*.coffee']
+                dest: 'dist/jsmegahal.js'

         watch:
             dist:
                 files: '<%= coffee.dist.src %>'
                 tasks: [ 'default' ]
+            test:
+                files: '<%= mochaTest.dist.src %>'
+                tasks: [ 'mochaTest' ]

         mochaTest:
             dist:


Monkeypatch jscoverage design flaw.

Monkeypatch jscoverage design flaw.

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -101,8 +101,8 @@
 				bare: true

 		clean:
-			coffee: [
-				'lib/validation.js'
+			shit: [
+				'lib-cov/*.coffee'
 			]

 		coffeelint:
@@ -128,7 +128,6 @@
 			options:
 				inputDirectory: 'lib'
 				outputDirectory: 'lib-cov'
-				exclude: 'locparse.coffee,strings.coffee'

 	# These plugins provide necessary tasks.
@@ -139,14 +138,14 @@
 	grunt.registerTask 'check', ['checkstrict', 'checklicense', 'coffeelint', 'jshint:all']
 	grunt.registerTask 'build', ['browserify', 'uglify']
 	grunt.registerTask 'test', [
-		'jscoverage', 'coffeeCoverage',    # order is important
+		'jscoverage', 'clean:shit', 'coffeeCoverage',  # order is important
 		'nodeunit:js', 'nodeunit:coffee',  # order is important
 		'jscoverage_report'
 	]
 	if grunt.option('single')?
 		grunt.config.set 'nodeunit.one', [ 'tests_node/'+grunt.option('single') ]
 		grunt.registerTask 'test', [
-			'jscoverage', 'coffeeCoverage',    # order is important
+			'jscoverage', 'clean:shit', 'coffeeCoverage',  # order is important
 			'nodeunit:one',
 			'jscoverage_report'
 		]


chore(Grunt): load release grunt task

chore(Grunt): load release grunt task

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -62,6 +62,7 @@
   grunt.loadNpmTasks 'grunt-coffeelint'
   grunt.loadNpmTasks 'grunt-contrib-coffee'
   grunt.loadNpmTasks 'grunt-contrib-nodeunit'
+  grunt.loadNpmTasks 'grunt-release'

   grunt.registerTask 'default', ['clean', 'coffeelint', 'coffee']
   grunt.registerTask 'test', ['clean', 'coffeelint', 'coffee', 'jade', 'nodeunit']


Add shareID in shared documents at the share creation

Also returns the shareID in the body result

--- server/controllers/sharing.coffee
+++ server/controllers/sharing.coffee
@@ -115,8 +115,13 @@
             next err
         else
             share.shareID = res._id
-            req.share = share
-            next()
+
+            # Add the shareID for each shared document
+            addShareIDDocs share.rules, share.shareID, (err) ->
+                return next err if err?
+
+                req.share = share
+                next()

 # Delete an existing sharing, on the sharer side
@@ -237,7 +242,7 @@
         if err?
             next err
         else
-            res.status(200).send success: true
+            res.status(200).send success: true, shareID: share.shareID

 # Send a sharing revocation for each target defined in the share object
@@ -452,17 +457,13 @@
         db.merge doc._id, doc, (err, result) ->
             return next err if err?

-            # Add the shareID for each shared document
-            addShareIDDocs doc.rules, doc._id, (err) ->
-                return next err if err?
-
-                # Params structure for the replication
-                share =
-                    target : target
-                    doc    : doc
-
-                req.share = share
-                next()
+            # Params structure for the replication
+            share =
+                target : target
+                doc    : doc
+
+            req.share = share
+            next()

 # Replicate documents to the target url


Fix issue with extra new line when rendering the content

--- lib/written/app/assets/javascripts/written/parsers/block/code.coffee
+++ lib/written/app/assets/javascripts/written/parsers/block/code.coffee
@@ -57,7 +57,7 @@
     if @matches[0][4]?
       code.insertAdjacentHTML('beforebegin', "<header>#{@matches[0][4]}</header>")

-    code.appendChild(document.createTextNode(@content))
+    code.appendChild(document.createTextNode(@content.slice(1, -1)))

     node


Improve data fetching

Improve data fetching

--- server/konnectors/linkedin.coffee
+++ server/konnectors/linkedin.coffee
@@ -143,9 +143,9 @@
     processRetrievingContactData = (contactId, done) ->
         contacts = """https://www.linkedin.com/contacts/api/contacts/\
         #{contactId}/?fields=name,first_name,last_name,\
-        #emails_extended,phone_numbers,sites,addresses,\
-        #company,title,location,profiles,twitter,\
-        #display_sources&_=1444864592009"""
+        emails_extended,phone_numbers,sites,addresses,\
+        company,title,geo_location,profiles,twitter,tag,\
+        secure_profile_image_url"""

         opts =
             url: contacts
@@ -171,13 +171,14 @@
             finalContact = new Contact
                 n: "#{data.last_name};#{data.first_name}"
                 fn: data.name
-                title: contact.title || undefined
+                title: data.title || undefined
                 org: data.company?.name || undefined
                 title: data.title || undefined
                 tags: ['linkedin']
                 datapoints: datapoints || undefined

-            finalContact.imageUrl = contact.secure_profile_image_url || undefined
+            console.log data
+            finalContact.imageUrl = data.secure_profile_image_url || undefined
             ContactHelper.setAccount finalContact,
                 type: ACCOUNT_TYPE
                 name: entries.accountName
@@ -213,7 +214,7 @@
 getPhoneNumber = (data) ->
     listPhones = []

-    data.phone_numbers.forEach (number) ->
+    data.phone_numbers?.forEach (number) ->
         listPhones.push
             name: 'tel'
             type: number.type.toLowerCase()
@@ -224,7 +225,7 @@
 getEmails = (data) ->
     listEmails = []

-    data.emails_extended.forEach (email) ->
+    data.emails_extended?.forEach (email) ->
         listEmails.push
             name: 'email'
             value: email.email
@@ -236,17 +237,17 @@
 getUrls = (data) ->
     listUrls = []

-    data.sites.forEach (site) ->
+    data.sites?.forEach (site) ->
         listUrls.push
             name: 'url'
             value: site.url
             type: site.name
-    data.profiles.forEach (profile) ->
+    data.profiles?.forEach (profile) ->
         listUrls.push
             name: 'url'
             value: profile.url
             type: 'linkedin'
-    data.twitter.forEach (twitter) ->
+    data.twitte?r.forEach (twitter) ->
         listUrls.push
             name: 'url'
             value: twitter.url
@@ -264,7 +265,7 @@
         #region = segmentAddress[1] || ''
         #locality = segmentAddress[2] || ''

-    data.addresses.forEach (address) =>
+    data.addresses?.forEach (address) =>
         addressArray = ContactHelper.adrStringToArray address.raw
         addressArray[6] = country

@@ -296,8 +297,6 @@

         if urlImage?
             opts = url.parse(urlImage)
-            opts.headers =
-                'User-Agent': agent
             https.get opts, (stream) ->
                 stream.on 'error', (err) -> log.error err


removed accounts

removed accounts

--- server/lib/config/accounts.coffee
+++ server/lib/config/accounts.coffee
@@ -1,7 +0,0 @@
-configureService = (service, client, secret) ->
-  ServiceConfiguration.configurations.upsert {service}, $set: _.extend client, {secret}
-
-Meteor.startup ->
-  configureService 'facebook', appId: 'XXXXXXXXXXXXXXX', 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'
-  configureService 'google', clientId: 'XXXXXXXXXXXX-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX.apps.googleusercontent.com', 'XXXXXXXXXXXXXXXXX-XXXXXX'
-  configureService 'twitter', consumerKey: 'XXXXXXXXXXXXXXXXXXXXXXXXX', 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'


Fix typos

Fix typos

--- server/materials/data/news.coffee
+++ server/materials/data/news.coffee
@@ -3,11 +3,11 @@

 export default allNews = () ->
     return news([
-        newsItem("Очных заннятий до 10 апреля не будет. С 10 апреля планируется возобновление очных занятий.", String.raw"""
-            Очных заннятий до 10 апреля не будет. Занимайтесь из дома. Если хотите, я готов организовать консультации по zoom, пишите.
+        newsItem("Очных занятий до 10 апреля не будет. С 10 апреля планируется возобновление очных занятий.", String.raw"""
+            Очных занятий до 10 апреля не будет. Занимайтесь из дома. Если хотите, я готов организовать консультации по zoom, пишите.
             С 10 апреля планируется возобновление очных занятий.
         """),
-
+
         newsItem("Опрос про алгопрог", String.raw"""
             Ответьте, пожалуйста, на несколько вопросов. Тем, кто уже отвечал — я там добавил несколько вопросов, можете ответить еще раз.
         """),


Jax.Mesh.Data#refresh

Jax.Mesh.Data#refresh

--- lib/assets/javascripts/jax/mesh/data.js.coffee
+++ lib/assets/javascripts/jax/mesh/data.js.coffee
@@ -65,17 +65,23 @@
       descriptor.gl.deleteBuffer descriptor.buffer
       delete @_glBuffers.id

-  bind: (context) ->
+  bind: () ->
+    id = @_context.id
     gl = @_context.gl
-    unless buffer = @_glBuffers[context.id]?.buffer
-      @_glBuffers[context.id] =
+    unless buffer = @_glBuffers[id]?.buffer
+      @_glBuffers[id] =
         gl: gl
         buffer: gl.createBuffer()
-      gl.bindBuffer GL_ARRAY_BUFFER, @_glBuffers[context.id].buffer
+      gl.bindBuffer GL_ARRAY_BUFFER, @_glBuffers[id].buffer
       gl.bufferData GL_ARRAY_BUFFER, @_array_buffer, GL_STATIC_DRAW
     else
       gl.bindBuffer GL_ARRAY_BUFFER, buffer
     @_bound = true
+
+  refresh: (context) ->
+    @context = context if context
+    @bind()
+    @_context.gl.bufferData GL_ARRAY_BUFFER, @_array_buffer, GL_STATIC_DRAW

   set: (vars, mapping) ->
     throw new Error "Jax context for this pass is not set" unless @_context


Tidy up

Tidy up

--- server/app/bongo/models/app.coffee
+++ server/app/bongo/models/app.coffee
@@ -29,8 +29,11 @@

     sharedMethods   :
       instance      : [
-        'update', 'follow', 'unfollow', 'remove', 'like', 'checkIfLikedBefore', 'fetchLikedByes',
-        'fetchFollowersWithRelationship', 'fetchFollowingWithRelationship', 'fetchCreator', 'install'
+        'update', 'follow', 'unfollow', 'remove', 'review',
+        'like', 'checkIfLikedBefore', 'fetchLikedByes',
+        'fetchFollowersWithRelationship', 'install',
+        'fetchFollowingWithRelationship', 'fetchCreator',
+        'fetchRelativeReviews'
       ]
       static        : [
         "one","on","some","create"


add cb in case of error

add cb in case of error

--- server/controllers/accounts.coffee
+++ server/controllers/accounts.coffee
@@ -16,7 +16,7 @@
 apps = []

 restartApp = (app, cb) =>
-    homeClient = new Client 'http://localhost:9103'
+    homeClient = new Client 'http://localhost:9103'
     homeClient.post "api/applications/#{app}/stop", {}, (err, res) ->
         console.log err if err?
         db.view 'application/byslug', {key: app}, (err, appli) ->
@@ -35,6 +35,8 @@
                 homeClient.post "api/applications/#{app}/start", {start: descriptor}, (err, res) ->
                     console.log err
                     cb()
+            else
+                cb()

 module.exports.addApp = (app) =>
     unless app in apps


Add mod to server launch

Add mod to server launch

--- server/serversystem/manager.coffee
+++ server/serversystem/manager.coffee
@@ -29,7 +29,7 @@
   servers.update {_id: serv._id}, {$set: {activeLobbies: serv.activeLobbies}}
   lobbies.update {_id: lobby}, {$set: {status: 2, serverIP: serv.ip+":"+port}}
   theLob = lobbies.findOne({_id: lobby})
-  sockets[serv._id].send "launchServer|"+id+"|"+port+"|"+(if theLob.devMode then "True" else "False")
+  sockets[serv._id].send "launchServer|"+id+"|"+port+"|"+(if theLob.devMode then "True" else "False")+"|"+lobby.mod
   console.log "server launched, id: "+id

 queueProc = ->


fixed.  Forgot this is an action that's only triggered from admin panel.

--- server/methods/deleteUser.coffee
+++ server/methods/deleteUser.coffee
@@ -2,12 +2,12 @@
 	deleteUser: (userId) ->
 		if not Meteor.userId()
 			throw new Meteor.Error('invalid-user', "[methods] deleteUser -> Invalid user")
-
-		user = Meteor.users.findOne userId

+		user = Meteor.users.findOne Meteor.userId()
 		unless user?.admin is true
 			throw new Meteor.Error 'not-authorized', '[methods] deleteUser -> Not authorized'

+		user = Meteor.users.findOne userId
 		unless user?
 			throw new Meteor.Error 'not-found', '[methods] deleteUser -> User not found'

@@ -18,6 +18,10 @@
 			if room.t isnt 'c' and room.usernames.length is 1
 				ChatRoom.remove subscription.rid # Remove non-channel rooms with only 1 user (the one being deleted)

+
+
+
+
 		ChatSubscription.remove { "u._id": userId } # Remove user subscriptions

 		rooms = ChatRoom.find({ "u._id": userId }).fetch()
@@ -27,4 +31,4 @@
 		ChatRoom.update {}, { $pull: { usernames: user.username } }, { multi: true } # Remove user from all other rooms
 		Meteor.users.remove { _id: userId } # Remove user from users database

-		return true
+		return true


remove dead code in help controller

remove dead code in help controller

--- server/controllers/help.coffee
+++ server/controllers/help.coffee
@@ -15,7 +15,6 @@

         CozyUser.first (err, user) ->
             CozyInstance.first (err, instance) ->
-                selectedApp = req.body.app or 'home'

                 {locale, domain} = instance
                 {email, public_name} = user


[LoadingFree] send same parameters into kodinghome static page generation

--- server/lib/server/index.coffee
+++ server/lib/server/index.coffee
@@ -371,12 +371,12 @@
           console.error err
         else if loggedIn
           # go to koding activity
-          JGroup.render.loggedIn.kodingHome {client, account}, (err, activityPage)->
+          JGroup.render.loggedIn.kodingHome {client, account, bongoModels}, (err, activityPage)->
             return next()  if err
             serve activityPage, res
         else
           # go to koding home
-          JGroup.render.loggedOut.kodingHome {client}, (err, homePage)->
+          JGroup.render.loggedOut.kodingHome {client, account, bongoModels}, (err, homePage)->
             return next()  if err
             serve homePage, res


Add debug log

Add debug log

--- server/metrics/graphite.coffee
+++ server/metrics/graphite.coffee
@@ -16,4 +16,5 @@
     metricsWithPrefix = {}
     for key, value of metrics
         metricsWithPrefix["#{prefix}.#{key}"] = value
+    console.log "Sending metrics ", metricsWithPrefix
     client.write(metricsWithPrefix, (err) -> if err? then logger.error("Can't send metrics to graphite: #{err}"))


:white_check_mark: Add tests

:white_check_mark: Add tests

Tests for the `remove matching brackets` feature

--- spec/bracket-matcher-spec.coffee
+++ spec/bracket-matcher-spec.coffee
@@ -357,6 +357,65 @@
         editor.setCursorBufferPosition([6,6])
         editorView.trigger "bracket-matcher:select-inside-brackets"
         expect(editor.getSelectedBufferRange()).toEqual [[4, 29], [7, 4]]
+
+  describe "when bracket-matcher:remove-matching-brackets is triggered", ->
+    describe "when the cursor is not in front of any pair", ->
+      it "performs a regular backspace action", ->
+        editor.setCursorBufferPosition([0,1])
+        editorView.trigger "bracket-matcher:remove-matching-brackets"
+        expect(editor.lineForBufferRow(0)).toEqual('ar quicksort = function () {')
+        expect(editor.getCursorBufferPosition()).toEqual([0,0])
+
+    describe "when the cursor is at the beginning of a line", ->
+      it "performs a regular backspace action", ->
+        editor.setCursorBufferPosition([12,0])
+        editorView.trigger "bracket-matcher:remove-matching-brackets"
+        expect(editor.lineForBufferRow(11)).toEqual('  return sort(Array.apply(this, arguments));};')
+        expect(editor.getCursorBufferPosition()).toEqual([11,44])
+
+    describe "when the cursor is on the left side of a starting pair", ->
+      it "performs a regular backspace action", ->
+        editor.setCursorBufferPosition([0,28])
+        editorView.trigger "bracket-matcher:remove-matching-brackets"
+        expect(editor.lineForBufferRow(0)).toEqual('var quicksort = function (){')
+        expect(editor.getCursorBufferPosition()).toEqual([0,27])
+
+    describe "when the cursor is on the left side of an ending pair", ->
+      it "performs a regular backspace action", ->
+        editor.setCursorBufferPosition([7,4])
+        editorView.trigger "bracket-matcher:remove-matching-brackets"
+        expect(editor.lineForBufferRow(7)).toEqual('  }')
+        expect(editor.getCursorBufferPosition()).toEqual([7,2])
+
+    describe "when the cursor is on the right side of a starting pair, the ending pair on another line", ->
+      it "removes both pairs", ->
+        editor.setCursorBufferPosition([0,29])
+        editorView.trigger "bracket-matcher:remove-matching-brackets"
+        expect(editor.lineForBufferRow(0)).toEqual('var quicksort = function () ')
+        expect(editor.lineForBufferRow(12)).toEqual(';')
+        expect(editor.getCursorBufferPosition()).toEqual([0,28])
+
+    describe "when the cursor is on the right side of an ending pair, the starting pair on another line", ->
+      it "removes both pairs", ->
+        editor.setCursorBufferPosition([7,5])
+        editorView.trigger "bracket-matcher:remove-matching-brackets"
+        expect(editor.lineForBufferRow(4)).toEqual('    while(items.length > 0) ')
+        expect(editor.lineForBufferRow(7)).toEqual('    ')
+        expect(editor.getCursorBufferPosition()).toEqual([7,4])
+
+    describe "when the cursor is on the right side of a starting pair, the ending pair on the same line", ->
+      it "removes both pairs", ->
+        editor.setCursorBufferPosition([11,14])
+        editorView.trigger "bracket-matcher:remove-matching-brackets"
+        expect(editor.lineForBufferRow(11)).toEqual('  return sortArray.apply(this, arguments);')
+        expect(editor.getCursorBufferPosition()).toEqual([11,13])
+
+    describe "when the cursor is on the right side of an ending pair, the starting pair on the same line", ->
+      it "removes both pairs", ->
+        editor.setCursorBufferPosition([11,43])
+        editorView.trigger "bracket-matcher:remove-matching-brackets"
+        expect(editor.lineForBufferRow(11)).toEqual('  return sortArray.apply(this, arguments);')
+        expect(editor.getCursorBufferPosition()).toEqual([11,41])

   describe "matching bracket deletion", ->
     beforeEach ->


fix linter error

fix linter error

--- spec/fast-activation-spec.coffee
+++ spec/fast-activation-spec.coffee
@@ -135,7 +135,7 @@
     it "calling service.registerCommandFromSpec doesn't load base.js", ->
       withCleanActivation (pack) ->
         service = pack.mainModule.provideVimModePlus()
-        service.registerCommandFromSpec("SampleCommand", {prefix: 'vim-mode-plus-user', getClass: () => "SampleCommand"})
+        service.registerCommandFromSpec("SampleCommand", {prefix: 'vim-mode-plus-user', getClass: -> "SampleCommand"})
         ensureRequiredFiles(shouldRequireFilesInOrdered)

   describe "command-table", ->


Update deprecated require

Update deprecated require

--- spec/package-manager-spec.coffee
+++ spec/package-manager-spec.coffee
@@ -1,5 +1,5 @@
 path = require 'path'
-process = require 'process'
+{process} = require 'electron'
 PackageManager = require '../lib/package-manager'

 describe "package manager", ->


Be defensive about checking for an ok response in JSON page

--- components/json_page/index.coffee
+++ components/json_page/index.coffee
@@ -28,7 +28,7 @@
       request
         .get "http://#{@bucket}.s3.amazonaws.com#{@path()}"
         .end (err, res) =>
-          if res.ok
+          if res?.ok
             try
               @data = JSON.parse res.text
               resolve @data


Be defensive about checking for error on response object

--- components/json_page/index.coffee
+++ components/json_page/index.coffee
@@ -30,13 +30,13 @@
         .end (err, res) =>
           if res?.ok
             try
-              @data = JSON.parse res.text
+              @data = JSON.parse res?.text
               resolve @data
               callback? null, @data
             catch e
               error e
           else
-            error res.error
+            error res?.error

   set: (data, callback) ->
     buffer = new Buffer JSON.stringify(data)


Forward args for function calls to the current player, fixes resuming CompoundPlayer scripts.

--- application/chrome/content/wesabe/download/CompoundPlayer.coffee
+++ application/chrome/content/wesabe/download/CompoundPlayer.coffee
@@ -80,8 +80,8 @@

     startNextPlayer()

-  resume: ->
-    @currentPlayer?.resume()
+  resume: (args...) ->
+    @currentPlayer?.resume(args...)

-  onLastGoalFinished: ->
-    @currentPlayer?.onLastGoalFinished?()
+  onLastGoalFinished: (args...) ->
+    @currentPlayer?.onLastGoalFinished?(args...)


Set also runtime @headerWidth after @onColResizeMouseup(), to used current @headerWidth e. g. on change sort order.

--- app/assets/javascripts/app/controllers/_application_controller_table.coffee
+++ app/assets/javascripts/app/controllers/_application_controller_table.coffee
@@ -447,6 +447,7 @@

   onColResizeMouseup: =>
     $(document).off 'mousemove.resizeCol'
+
     # switch to percentage
     resizeBaseWidth = @resizeTargetLeft.parents('table').width()
     leftWidth = @resizeTargetLeft.outerWidth()
@@ -462,15 +463,20 @@
     ]

     @log 'debug', @table_id, 'leftColumnKey', leftColumnKey, leftWidth, 'rightColumnKey', rightColumnKey, rightWidth
+
+    # update store and runtime @headerWidth
     @preferencesStore('headerWidth', leftColumnKey, leftWidth)
-
+    @headerWidth[leftColumnKey] = leftWidth
+
+    # update store and runtime @headerWidth
     if rightColumnKey
       @preferencesStore('headerWidth', rightColumnKey, rightWidth)
+      @headerWidth[rightColumnKey] = rightWidth

   sortByColumn: (event) =>
     column = $(event.currentTarget).closest('[data-column-key]').attr('data-column-key')

-    # sort
+    # sort, update runtime @orderBy and @orderDirection
     if @orderBy isnt column
       @orderBy = column
       @orderDirection = 'ASC'
@@ -481,6 +487,8 @@
         @orderDirection = 'ASC'

     @log 'debug', @table_id, 'sortByColumn', @orderBy, 'direction', @orderDirection
+
+    # update store
     @preferencesStore('order', 'orderBy', @orderBy)
     @preferencesStore('order', 'orderDirection', @orderDirection)
     @render()


 Merge branch 'master.accountSettingsFix' into 'master'

Master.Account Settings Fix

--- client/Account/AppController.coffee
+++ client/Account/AppController.coffee
@@ -65,7 +65,7 @@

     for item in @navController.itemsOrdered when section is item.getData().slug
       @tabView.addPane @createTab item.getData()
-      @navController.selectSingleItem item
+      @navController.selectItem item
       break


Turn off Evernote updater.

Turn off Evernote updater.

I'm in the process of migrating away from Evernote.

--- add-note-count-to-focus-task.coffee
+++ add-note-count-to-focus-task.coffee
@@ -87,9 +87,11 @@
       taskBasename: 'Process TextExpander suggestions'
       count: @_countTextExpanderSuggestions()
     )
-    @_updateOmniFocusTask(
-      taskBasename: 'Process Evernote inbox'
-      count: @_countNotesInInbox()
-    )
+    # MRF 2016-03-31
+    # In the process of migrating away from Evernote. Turning this off.
+    # @_updateOmniFocusTask(
+    #   taskBasename: 'Process Evernote inbox'
+    #   count: @_countNotesInInbox()
+    # )

 new UpdateFocusTasks().run()


Server: added missing fake endpoints

Server: added missing fake endpoints

--- client/landing/server/server.coffee
+++ client/landing/server/server.coffee
@@ -19,7 +19,18 @@

   app.use bodyParser.urlencoded({ extended: false })

-  app.post '/FetchGravatarInfo', (req, res) ->
+  app.post '/WFGH/Apply', (req, res) ->
+    res.status(200).send {
+      totalApplicants    : 14812
+      approvedApplicants : 5613
+      isApplicant        : yes
+      deadline           : new Date '20 November 2014'
+      prize              : 10000
+      cap                : 50000
+    }
+
+
+  app.post '/Gravatar', (req, res) ->
     {email} = req.body
     console.log "Gravatar info request for: #{email}"

@@ -41,8 +52,16 @@

     # data = '{"entry":[{"id":"5390782","hash":"02e68fe6ba0d08c7ea5b63320beedc46","requestHash":"dyasar","profileUrl":"http:\/\/gravatar.com\/dyasar","preferredUsername":"dyasar","thumbnailUrl":"http:\/\/0.gravatar.com\/avatar\/02e68fe6ba0d08c7ea5b63320beedc46","photos":[{"value":"http:\/\/0.gravatar.com\/avatar\/02e68fe6ba0d08c7ea5b63320beedc46","type":"thumbnail"}],"name":{"givenName":"Devrim","familyName":"Yasar","formatted":"Devrim Yasar"},"displayName":"Devrim","urls":[]}]}'
+  app.post '/Validate/Username/:username?', (req, res) ->

-    # res.status(400).send(JSON.parse data)
+    return res.status(200).send
+      forbidden  : no
+      kodingUser : no
+
+
+  app.post '/Validate/Email/:email?', (req, res) ->
+
+    return res.status(200).send yes

   app.post '/Login', (req, res) ->


Minor changes in the preflight impls

Minor changes in the preflight impls

--- library/membrane/preflight.coffee
+++ library/membrane/preflight.coffee
@@ -96,19 +96,19 @@
     installation: (kernel, targets, options, next) ->
         install = bower.commands.install
         bowerings = @constructor.bowerings ?= []
-        installer = install(targets, {}, options)
+        installer = install targets, {}, options
         installer.on "error", (error) ->
             reason = "failed Bower package installation"
-            logger.error error.message.red, error
-            kernel.shutdownKernel reason
+            logger.error error.message.toString().red, error
+            kernel.shutdownKernel reason.toString()
         installer.on "end", (installed) =>
             bowerings.installed = installed
             message = "Get Bower lib %s@%s at %s"
             for packet in _.values(installed or {})
-                what = packet.pkgMeta?.name.underline
-                vers = packet.pkgMeta?.version.underline
+                name = packet.pkgMeta?.name.underline
+                version = packet.pkgMeta?.version.underline
                 where = @constructor.identify().underline
-                logger.debug message.cyan, what, vers, where
+                logger.debug message.cyan, name, version, where
             return next()

     # This server side method is called on the context prior to the


update JSTextView textarea size

update JSTextView textarea size

--- JSKit/04_JSTextView.coffee
+++ JSKit/04_JSTextView.coffee
@@ -46,6 +46,7 @@
             else
                 disp = @_text.replace(/\n/g, "
")
                 $(@_viewSelector+"_textarea").html(disp)
+            @setFrame(@_frame)

     setTextSize: (@_textSize) ->
         if ($(@_viewSelector+"_textarea").length)
@@ -85,14 +86,14 @@
             if (@_editable == true) # モード変更前が編集可能モード

                 if (!$(@_viewSelector+"_textarea").length)
-                    tag = "<textarea id='"+@_objectID+"_textarea' style='position:absolute;overflow:auto;word-break:break-all;z-index:1;width:"+@_frame.size.width+"px;height:"+@_frame.size.height+"px;'>"+@_text+"</textarea>"
+                    tag = "<textarea id='"+@_objectID+"_textarea' style='position:absolute;overflow:auto;word-break:break-all;z-index:1;'>"+@_text+"</textarea>"
                 else
                     @_text = $(@_viewSelector+"_textarea").val()
                     return

             else # モード変更前が編集不可モード

-                tag = "<textarea id='"+@_objectID+"_textarea' style='position:absolute;overflow:auto;word-break:break-all;z-index:1;width:"+@_frame.size.width+"px;height:"+@_frame.size.height+"px;'>"+@_text+"</textarea>"
+                tag = "<textarea id='"+@_objectID+"_textarea' style='position:absolute;overflow:auto;word-break:break-all;z-index:1;'>"+@_text+"</textarea>"

             x = -2
             y = -2
@@ -108,7 +109,7 @@
                 text = JSEscape(@_text)
                 disp = text.replace(/\n/g, "
")

-            tag = "
"+disp+"
"
+            tag = "
"+disp+"
"
             x = 0
             y = 0


Make specs compatible with empty panes

Make specs compatible with empty panes

--- spec/markdown-preview-spec.coffee
+++ spec/markdown-preview-spec.coffee
@@ -107,13 +107,15 @@
             it "updates the preview but does not make it active", ->
               atom.workspaceView.getActiveView().trigger 'markdown-preview:show'
               [pane1, pane2] = atom.workspaceView.getPanes()
-              pane2.moveItemToPane(pane2.activeItem, pane1, 1)
+              preview = pane2.activeItem
+              pane2.moveItemToPane(preview, pane1, 1)
+
               pane1.showItemAtIndex(1)
               pane1.showItemAtIndex(0)
               preview = pane1.itemAtIndex(1)

               waitsFor ->
-                pane2.activeItem.buffer
+                preview.buffer

               runs ->
                 preview.renderMarkdown.reset()
@@ -156,6 +158,5 @@
           [pane1, pane2] = atom.workspaceView.getPanes()
           preview = pane2.activeItem
           pane2.destroyActiveItem()
-          expect(atom.workspaceView.getPanes().length).toBe 1
           atom.workspaceView.trigger 'pane:reopen-closed-item'
           expect(atom.workspaceView.getActivePaneItem().constructor.name).toBe 'MarkdownPreviewView'


Trigger ‘blur’ on select list editor instead of ‘focusut’ on its input

--- spec/select-list-view-spec.coffee
+++ spec/select-list-view-spec.coffee
@@ -184,7 +184,7 @@
   describe "when the mini editor loses focus", ->
     it "triggers the cancelled hook and detaches the select list", ->
       spyOn(selectList, 'detach')
-      filterEditorView.hiddenInput.trigger 'focusout'
+      filterEditorView.trigger 'blur'
       expect(selectList.cancelled).toHaveBeenCalled()
       expect(selectList.detach).toHaveBeenCalled()


config: remove unneeded creds

config: remove unneeded creds

--- config/credentials.default.coffee
+++ config/credentials.default.coffee
@@ -26,17 +26,7 @@
     worker_tunnelproxymanager_route53: kodingdev_master_2016_05
     #Encryption and Storage on S3
     worker_sneakerS3 : kodingdev_master_2016_05
-    vm_vmwatcher:     # vm_vmwatcher_dev
-      accessKeyId: ""
-      secretAccessKey: ""
-    vm_kloud:         # vm_kloud_dev
-      accessKeyId: ""
-      secretAccessKey: ""
-
-  slKeys =
-    vm_kloud:
-      username: ""
-      apiKey: ""
+
   mongo = "#{options.serviceHost}:27017/koding"
   redis =
     host: "#{options.serviceHost}"
@@ -198,7 +188,6 @@
   return {
     kiteHome
     awsKeys
-    slKeys
     mongo
     redis
     monitoringRedis


Ram usage bar: Minor fix about KD.utils.curryCssClass's usage

--- client/Framework/components/progressbar/KDProgressBarView.coffee
+++ client/Framework/components/progressbar/KDProgressBarView.coffee
@@ -1,6 +1,6 @@
 class KDProgressBarView extends KDCustomHTMLView
   constructor:(options = {})->
-    options.cssClass = KD.utils.curryCssClass "progressbar-container"
+    options.cssClass = KD.utils.curryCssClass "progressbar-container", options.cssClass

     super options


Applications: some fixes on applicationtabview

--- client/app/CommonViews/applicationview/applicationtabview.coffee
+++ client/app/CommonViews/applicationview/applicationtabview.coffee
@@ -13,28 +13,27 @@
     super options, data

     @isSessionEnabled = options.saveSession and options.sessionName
+    appManager        = KD.getSingleton 'appManager'

-    appView = @getDelegate()
-
-    @on 'PaneRemoved', =>
-      if @panes.length is 0
-        appView.emit 'AllViewsClosed'
-        if options.closeAppWhenAllTabsClosed
-          appManager = KD.getSingleton 'appManager'
-          appManager.quit appManager.frontApp
-
-      @tabHandleContainer.repositionPlusHandle @handles
-      @removeFromSession yes
-
-    @on 'PaneAdded', =>
+    @on 'PaneAdded', (pane)=>
       @tabHandleContainer.repositionPlusHandle @handles
       @initSession @panes.last, @bound "updateSession" if @isSessionEnabled
+
+      tabView = this
+      pane.on "KDTabPaneDestroy", ->
+        # -1 because the pane is still there but will be destroyed after this event
+        if tabView.panes.length - 1 is 0 and options.closeAppWhenAllTabsClosed
+          appManager.quit appManager.getFrontApp()
+        tabView.tabHandleContainer.repositionPlusHandle tabView.handles
+        tabView.removeFromSession yes

     @on 'SaveSession', (data) =>
       @appStorage.setValue @getOptions().sessionKey, data

-    # appView.on "AceAppDidQuit", => @removeFromSession no

+
+  # FIXME: @fatihacet ace related stuff can not live here in this file
+  # please generalize this - SY
   # session related methods

   fetchStorage: (callback) ->


Fixed issue #1340 - Relative and absolute time bug.

--- app/assets/javascripts/app/controllers/_ui_element/ticket_selector.coffee
+++ app/assets/javascripts/app/controllers/_ui_element/ticket_selector.coffee
@@ -156,7 +156,7 @@
       elementRow = $(e.target).closest('.js-filterElement')
       groupAndAttribute = elementRow.find('.js-attributeSelector option:selected').attr('value')
       return if !groupAndAttribute
-      @buildOperator(item, elementRow, groupAndAttribute, elements, {}, attribute, false)
+      @buildOperator(item, elementRow, groupAndAttribute, elements, {}, attribute)
     )

     # bind for preview
@@ -244,9 +244,9 @@
     if groupAndAttribute
       elementRow.find('.js-attributeSelector select').val(groupAndAttribute)

-    @buildOperator(elementFull, elementRow, groupAndAttribute, elements, meta, attribute, true)
-
-  @buildOperator: (elementFull, elementRow, groupAndAttribute, elements, meta, attribute, buildValue) ->
+    @buildOperator(elementFull, elementRow, groupAndAttribute, elements, meta, attribute)
+
+  @buildOperator: (elementFull, elementRow, groupAndAttribute, elements, meta, attribute) ->
     currentOperator = elementRow.find('.js-operator option:selected').attr('value')

     name = "#{attribute.name}::#{groupAndAttribute}::operator"
@@ -284,9 +284,9 @@

     elementRow.find('.js-operator select').replaceWith(selection)

-    @buildPreCondition(elementFull, elementRow, groupAndAttribute, elements, meta, attribute, buildValue)
-
-  @buildPreCondition: (elementFull, elementRow, groupAndAttribute, elements, meta, attributeConfig, buildValue = true) ->
+    @buildPreCondition(elementFull, elementRow, groupAndAttribute, elements, meta, attribute)
+
+  @buildPreCondition: (elementFull, elementRow, groupAndAttribute, elements, meta, attributeConfig) ->
     currentOperator = elementRow.find('.js-operator option:selected').attr('value')
     currentPreCondition = elementRow.find('.js-preCondition option:selected').attr('value')

@@ -318,7 +318,6 @@
     if !preCondition
       elementRow.find('.js-preCondition select').html('')
       elementRow.find('.js-preCondition').addClass('hide')
-      return if !buildValue
       toggleValue()
       @buildValue(elementFull, elementRow, groupAndAttribute, elements, meta, attribute)
       return
@@ -351,7 +350,6 @@
       toggleValue()
     )

-    return if !buildValue
     @buildValue(elementFull, elementRow, groupAndAttribute, elements, meta, attribute)
     toggleValue()


Update journal_thumbnail_controller.js.coffee

--- app/assets/javascripts/controllers/journal_thumbnail_controller.js.coffee
+++ app/assets/javascripts/controllers/journal_thumbnail_controller.js.coffee
@@ -3,7 +3,7 @@
   currentUser: Ember.computed.alias 'controllers.application.currentUser'
   isEditing: (-> @get 'model.isDirty').property()
   thumbnailId: (-> "journal-logo-#{@get 'model.id'}").property()
-  logoUploadUrl: (-> "/admin/journals/#{@get 'id'}/upload_logo").property()
+  logoUploadUrl: (-> "/admin/journals/#{@get 'model.id'}/upload_logo").property()
   nameErrors: null
   descriptionErrors: null


Ace: Refactored removeModifiedFromTab method to get rid of the unnecessary argument.

--- client/Ace/ace.coffee
+++ client/Ace/ace.coffee
@@ -424,12 +424,13 @@

     for path, aceView of aceViews when aceView.data.parentPath isnt 'localfile:'
       aceView.ace.requestSave()
-      aceView.ace.once 'FileContentRestored', -> @removeModifiedFromTab aceView
-
-  removeModifiedFromTab:(aceView)->
+      aceView.ace.once 'FileContentRestored', @bound 'removeModifiedFromTab'
+
     {name} = aceView.ace.data
     for handle in aceView.delegate.tabView.handles when handle.options.title is name
       handle.unsetClass 'modified'
+  removeModifiedFromTab: ->
+    aceView      = @parent

   showGotoLine: ->
     unless @gotoLineModal


Give control to the developer over if and when to set a username.

--- client/dialogs.coffee
+++ client/dialogs.coffee
@@ -1,4 +1,7 @@
-# Dialogs to possibly show after page loaded
+###
+  Dialogs to possibly show after page loaded
+###
+
 Meteor.startup ->
   ###
     Disconnect warning
@@ -20,10 +23,10 @@
             Please check your Internet connection.</h3>""")
       return

+TurkServer.ensureUsername = ->
   ###
     Capture username after logging in
   ###
-  # TODO only do this for experiments that require it
   usernameDialog = null

   Deps.autorun ->


don't use @ when calling global function

--- client/general.coffee
+++ client/general.coffee
@@ -105,7 +105,7 @@
       remove_driver_line x

     save: (info, template) ->
-      @save_driver_line info, template
+      save_driver_line info, template

     insert_template_name: "driver_line_insert"
     delete_template_name: "driver_line_show_brief"
@@ -119,7 +119,7 @@
       remove_neuron_type x

     save: (info, template) ->
-      @save_neuron_type info, template
+      save_neuron_type info, template

     insert_template_name: "neuron_type_insert"
     delete_template_name: "neuron_type_show_brief"
@@ -133,7 +133,7 @@
       remove_neuropil x

     save: (info, template) ->
-      @save_neuropil info, template
+      save_neuropil info, template

     insert_template_name: "neuropil_insert"
     delete_template_name: "neuropil_show_brief"


Fix error reporting on login.

Fix error reporting on login.

--- client/helpers.coffee
+++ client/helpers.coffee
@@ -46,7 +46,7 @@
     # submit the HIT
     TurkServer.submitHIT()
   else
-    bootbox.dialog("
Unable to login:
" + e.message)
+    bootbox.alert("
Unable to login:
" + e.message)

 mturkLogin = (args) ->
   Accounts.callLoginMethod


Restore autohide/autounhide functionality of images

--- client/message.coffee
+++ client/message.coffee
@@ -239,6 +239,7 @@

 messageRaw = new ReactiveDict
 export messageFolded = new ReactiveDict
+defaultFolded = new ReactiveDict
 messageHistory = new ReactiveDict
 messageKeyboard = new ReactiveDict
 messagePreview = new ReactiveDict
@@ -290,6 +291,12 @@
   @editBody = new ReactiveVar null
   @lastTitle = null
   @savedTitles = []
+
+  ## Fold naturally folded (minimized and deleted) messages
+  ## by default on initial load.
+  defaultFolded.set @data._id, natural = naturallyFolded @data
+  messageFolded.set @data._id, true if natural
+
   @autorun =>
     data = Template.currentData()
     return unless data?
@@ -389,11 +396,14 @@
   ## Image gets unnaturally folded if it's referenced at least once
   ## and doesn't have any children (don't want to hide children, and this
   ## can also lead to infinite loop if children has the image reference).
-  messageFolded.set id, true if images[id].naturallyFolded or
+  newDefault = images[id].naturallyFolded or
     (not images[id].children and
      (images[id].count > 0 or
       (imagesInternal[images[id].file]? and
        imagesInternal[images[id].file].count > 0)))
+  if newDefault != defaultFolded.get id
+    defaultFolded.set id, newDefault
+    messageFolded.set id, newDefault
   ## No longer care about this image if it's not referenced and doesn't have
   ## a rendered template.
   if images[id].count == 0 and id not of id2template
@@ -451,10 +461,6 @@
   ## Scroll to this message if it's been requested.
   if scrollToLater == @data._id
     scrollToMessage @data._id
-
-  ## Fold naturally folded (minimized and deleted) messages
-  ## by default on initial load.
-  messageFolded.set @data._id, true if natural = naturallyFolded @data

   ## Fold referenced attached files by default on initial load.
   #@$.children('.panel').children('.panel-body').find('a[href|="/file/"]')


Add specs for toggling the gutter when the editor is hidden

--- spec/editor-component-spec.coffee
+++ spec/editor-component-spec.coffee
@@ -1589,6 +1589,36 @@
       expect(horizontalScrollbarNode.scrollWidth).toBe editor.getScrollWidth()
       expect(horizontalScrollbarNode.style.left).toBe gutterNode.offsetWidth + 'px'

+    describe "when the editor is hidden", ->
+      hideEditorView = ->
+        wrapperNode.style.display = 'none'
+        expect(component.isVisible()).toBe false
+
+      showEditorView = ->
+        wrapperNode.style.display = 'block'
+        expect(component.isVisible()).toBe true
+
+      it "updates the position of the horizontal scrollbar only when the editor is visible", ->
+        # toggling gutter off
+        hideEditorView()
+        atom.config.set("editor.showLineNumbers", false)
+
+        showEditorView()
+        component.forceUpdate()
+        runSetImmediateCallbacks()
+        gutterNode = componentNode.querySelector('.gutter')
+        expect(horizontalScrollbarNode.style.left).toBe '0px'
+
+        # toggling gutter back on
+        hideEditorView()
+        atom.config.set("editor.showLineNumbers", true)
+
+        showEditorView()
+        component.forceUpdate()
+        runSetImmediateCallbacks()
+        gutterNode = componentNode.querySelector('.gutter')
+        expect(horizontalScrollbarNode.style.left).toBe gutterNode.offsetWidth + 'px'
+
   describe "mousewheel events", ->
     beforeEach ->
       atom.config.set('editor.scrollSensitivity', 100)


Update keymaps.

Update keymaps.

--- keymap/script.cson
+++ keymap/script.cson
@@ -3,11 +3,11 @@

 # Only bind the keys in the editor
 '.platform-darwin atom-text-editor':
-  'cmd-c': 'script:compile'
-  'cmd-g': 'script:generate'
-  'cmd-m': 'script:generateWithMigrate'
+  'cmd-alt-c': 'script:compile'
+  'cmd-alt-g': 'script:generate'
+  'cmd-alt-m': 'script:generateWithMigrate'

 '.platform-win32 atom-text-editor, .platform-linux atom-text-editor':
-  'shift-ctrl-c': 'sifu:compile'
-  'shift-ctrl-g': 'sifu:generate'
-  'shift-ctrl-m': 'sifu:generateWithMigrate'+  'ctrl-shift-c': 'sifu:compile'
+  'ctrl-shift-g': 'sifu:generate'
+  'ctrl-shift-m': 'sifu:generateWithMigrate'


Update tooltip.coffee

Update tooltip.coffee

--- coffee/tooltip.coffee
+++ coffee/tooltip.coffee
@@ -117,7 +117,7 @@
         switch align
           when 'top'
             position.left = targetOffset.left + (targetSize.width / 2)
-            position.top = targetOffset.top - distance
+            position.top = targetOffset.top - distance + arrowSize
           when 'right'
             position.left = targetOffset.left + targetSize.width + distance - arrowSize
             position.top = targetOffset.top + (targetSize.height / 2)


Removes geojson and adds owner to list projection

--- coffee/turbase.coffee
+++ coffee/turbase.coffee
@@ -9,48 +9,48 @@
 db = null

 database.connect null, (err, db_ref) ->
-  throw err if err
-  db = db_ref
+throw err if err
+db = db_ref

 exports.getTypes = (req, res) ->
-  res.jsonp
-    types: ['aktiviteter', 'bilder', 'områder', 'steder', 'turer']
-    count: 5
+res.jsonp
+  types: ['aktiviteter', 'bilder', 'områder', 'steder', 'turer']
+  count: 5

 exports.get = (req, res) ->
-  db.collection req.params.object, (err, collection) ->
+db.collection req.params.object, (err, collection) ->
+  # @TODO move to propper error handling
+  return res.jsonp "{'error':#{err}}" if err
+  return res.jsonp "{'error': 'Mangler id'}" if not req.params.id
+
+  collection.findOne _id: ObjectID.createFromHexString(req.params.id), (err, doc) ->
     # @TODO move to propper error handling
-    return res.jsonp "{'error':#{err}}" if err
-    return res.jsonp "{'error': 'Mangler id'}" if not req.params.id

-    collection.findOne _id: ObjectID.createFromHexString(req.params.id), (err, doc) ->
-      # @TODO move to propper error handling
+    delete doc.privat if req.eier.toLowerCase() isnt doc?.eier?.toLowerCase()

-      delete doc.privat if req.eier.toLowerCase() isnt doc?.eier?.toLowerCase()
-
-      return res.jsonp err if err
-      return res.jsonp doc if doc
-      # @TODO this should 404 Not Found
-      return res.jsonp {}
+    return res.jsonp err if err
+    return res.jsonp doc if doc
+    # @TODO this should 404 Not Found
+    return res.jsonp {}

 exports.list = (req, res) ->
-  # @TODO parse arguments in server.coffee
-  limit  = Math.min(parseInt(req?.query?.limit) || 10, 50)
-  offset = parseInt(req?.query?.offset) || 0
-  query  = {endret:{$gt:req.query.after}} if req?.query?.after
+# @TODO parse arguments in server.coffee
+limit  = Math.min(parseInt(req?.query?.limit) || 10, 50)
+offset = parseInt(req?.query?.offset) || 0
+query  = {endret:{$gt:req.query.after}} if req?.query?.after

-  db.collection req.params.object, (err, collection) ->
-    # @TODO move to propper error handling
-    return res.jsonp "{'error':#{err}}" if err
+db.collection req.params.object, (err, collection) ->
+  # @TODO move to propper error handling
+  return res.jsonp "{'error':#{err}}" if err

-    opts =
-      limit: limit
-      skip: offset
-      sort: "endret"
-
-    # @TODO add endret paramter
-    # @TODO add support for queries
-    collection.find(query,{navn:1,endret:1,geojson:1},opts).toArray (err, result) ->
+  opts =
+    limit: limit
+    skip: offset
+    sort: "endret"
+
+  # @TODO add endret paramter
+  # @TODO add support for queries
+  collection.find(query,{eier:1,navn:1,endret:1},opts).toArray (err, result) ->
       # @TODO move to propper error handling
       return res.jsonp err if err
       return res.jsonp documents: result, count: result.length if result


Remove pluralize inflection

Remove pluralize inflection

--- lib/generators/nas_extjs/templates/public/app/model/SingleAssociation.coffee
+++ lib/generators/nas_extjs/templates/public/app/model/SingleAssociation.coffee
@@ -32,7 +32,7 @@
         model[this.instanceName]?

     setFromStore: (model,fk)->
-        name = Util.baseClassName( this.associatedModel.getName() ).pluralize()
+        name = Util.pluralize( Util.baseClassName( this.associatedModel.getName() ) )
         store = Application.getStore(name)
         if store
             rec = store.getById( parseInt(fk) )


Use correct github auth behaviour in test

--- server/controllers/deploy.coffee
+++ server/controllers/deploy.coffee
@@ -15,8 +15,8 @@

 exports.index = (req, res) ->
   remoteIp = getIpFromRequest(req)
-  envIsProduction = process.env.NODE_ENV is 'production'
-  if !ipIsFromGithub(remoteIp) and envIsProduction
+  envIsNotDevelopment = process.env.NODE_ENV isnt 'development'
+  if !ipIsFromGithub(remoteIp) and envIsNotDevelopment
     return res.send 401

   parsedPayload = req.body