:bug: for not passing editor to swrap.clearBlockwiseSelection

--- lib/vim-state.coffee
+++ lib/vim-state.coffee
@@ -117,7 +117,7 @@
     @swrap.getBlockwiseSelectionsOrderedByBufferPosition(@editor)

   clearBlockwiseSelections: ->
-    @swrap.clearBlockwiseSelections(@editor)
+    @getProp('swrap')?.clearBlockwiseSelections(@editor)

   # Other
   # -------------------------
@@ -268,7 +268,7 @@
     @editor.setCursorBufferPosition(@editor.getCursorBufferPosition())

   resetNormalMode: ({userInvocation}={}) ->
-    @getProp('swrap')?.clearBlockwiseSelections()
+    @clearBlockwiseSelections()

     if userInvocation ? false
       switch


Lowered ratio requirements

Lowered ratio requirements

--- server/queues/scoring.coffee
+++ server/queues/scoring.coffee
@@ -202,7 +202,7 @@
       cb null, true
     else
       ratio = (updatedSession.numberOfLosses) / (totalNumberOfGamesPlayed)
-      if ratio > 0.1
+      if ratio > 0.2
         cb null, false
         console.log "Ratio(#{ratio}) is bad, ending simulation"
       else


update specs to match changes in openstax/react-components#46

--- test/exercise/index.spec.coffee
+++ test/exercise/index.spec.coffee
@@ -62,4 +62,5 @@
       input = answer.querySelector('input')
       ReactTestUtils.Simulate.change(input, target:{checked: true})
       Testing.actions.click(input)
-      expect(@props.item.answer_id).equal(step.content.questions[0].answers[0].id)
+      selectedAnswer = _.find(step.content.questions[0].answers, id: @props.item.answer_id)
+      expect(selectedAnswer.content_html).equal(answer.innerText)


Remove unused method in reporter

Remove unused method in reporter

--- lint-reporter.coffee
+++ lint-reporter.coffee
@@ -24,11 +24,6 @@
         return styles.reduce (m, s)  ->
             return "\u001b[" + map[s][0] + "m" + m + "\u001b[" + map[s][1] + "m"
         , message
-
-    hasWarning : () ->
-        for path of @errorReport.paths
-            return true if @errorReport.pathHasWarning(path)
-        return false

     publish : () ->
         paths = @errorReport.paths


<tests> Correcting expected response for incorrect sensor update.

--- tests/controllers/sensor.coffee
+++ tests/controllers/sensor.coffee
@@ -129,12 +129,12 @@
             @client.put "sensors/#{store.sensorId}", update, done

         it 'should return an error', ->
-            expect(@body).to.not.exist
             expect(@response.statusCode).to.equal 500
-            expect(@err).to.equal 'Server error while saving sensor'
+            expect(@body).to.exist
+            expect(@body.error).to.exist

         it 'when we get the targeted Sensor (GET /sensors/:id)', (done) ->
-            @client.get "sensors/#{id}", done
+            @client.get "sensors/#{store.sensorId}", done

         it 'should return the unmodified sensor', ->
             expect(@err).to.not.exist


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/flickerable.coffee
+++ test/flickerable.coffee
@@ -9,7 +9,8 @@
   equals player.I.alpha, 0.9

   player.flicker
-    duration: 20, 5, 0.2
+    duration: 20,
+    5, 0.2

   player.update()


minor

minor

--- test/fs/fsTimeFormatSpec.coffee
+++ test/fs/fsTimeFormatSpec.coffee
@@ -1,4 +1,4 @@
-describe 'fsTime', ->
+describe 'fsTimeFormat', ->
   $scope = null
   $compile = null
   input = null


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/function_extensions.coffee
+++ test/function_extensions.coffee
@@ -28,7 +28,8 @@
       score += 100
     ).once()

-  once
+  onceScore()
+  onc

 test "#returning", ->
   x = 0


sagews: cover another case of redo/undo flakiness

--- src/smc-webapp/sagews.coffee
+++ src/smc-webapp/sagews.coffee
@@ -504,6 +504,7 @@
             catch e
                 console.warn("skipping undo: ",e)
         @process_sage_updates() # reprocess entire buffer -- e.g., output could change in strange ways
+        @set_all_output_line_classes()

     on_redo: (cm, changeObj) =>
         u = cm.getHistory().done
@@ -511,13 +512,12 @@
             try
                 cm.redo()
                 # TODO: having to do this is potentially very bad/slow if document has large number
-                # of outputs.  However, codemirror throws away all the line classes on redo.  So have
                 # to do this.  This is temporary anyways, since we plan to get rid of using codemirror
                 # undo entirely.
-                @set_all_output_line_classes()
             catch e
                 console.warn("skipping redo: ",e)
         @process_sage_updates() # reprocess entire buffer
+        @set_all_output_line_classes()

     interrupt: (opts={}) =>
         opts = defaults opts,


Move the deployments file to /config

Move the deployments file to /config

--- src/hubot-graphite-send.coffee
+++ src/hubot-graphite-send.coffee
@@ -20,14 +20,14 @@
 # Author:
 #   amwelch (https://github.com/amwelch)

-nconf = require("nconf")
-deployments = require './data/deployments.json'
-graphite = require("graphite-udp")
-
 cwd = process.cwd()
 DEFAULTS_FILE = "#{__dirname}/data/defaults.json"
 CONFIG_FILE = "#{cwd}/config/hubot-graphite-send-config.json"
+DEPLOYMENTS_FILE = "#{cwd}/config/deployments.json"

+deployments = require "#{DEPLOYMENTS_FILE}"
+nconf = require("nconf")
+graphite = require("graphite-udp")
 nconf.argv()
     .env()
     .file('config', CONFIG_FILE)


WIP event listeners for Snackbar

WIP event listeners for Snackbar

--- src/javascript/snackbar.coffee
+++ src/javascript/snackbar.coffee
@@ -25,6 +25,14 @@
         text    : ''
         # 動作按鈕的語意。
         emphasis: ''
+    #
+    onVisible: =>
+    #
+    onShow: =>
+    #
+    onHide: =>
+    #
+    onHidden: =>
     # 當點心條關閉時所會呼叫的回呼函式。
     onClose  : =>
     # 當點心條因為放置而自動關閉時所會呼叫的回呼函式。


small test refactor

small test refactor

--- test/phabs_commands_test.coffee
+++ test/phabs_commands_test.coffee
@@ -2771,6 +2771,24 @@
           .eql 'toto is not subscribed to T42'
           expect(hubotResponse(3)).to
           .eql 'Sorry, noproject not found.'
+
+      context 'phab T42 sub user_with_phid unsub toto to backlog not in noproject', ->
+        beforeEach ->
+          nock(process.env.PHABRICATOR_URL)
+            .get('/api/project.search')
+            .query({
+              'names[0]': 'noproject',
+              'api.token': 'xxx'
+            })
+            .reply(200, { result: {
+              'data': [ ],
+              'slugMap': [ ],
+              'cursor': {
+                'limit': 100,
+                'after': null,
+                'before': null
+              }
+            } })

         hubot 'ph T42 sub user_with_phid unsub toto to backlog not in noproject', 'user_with_phid'
         it 'gives a feedback that the assignment went ok', ->


Add prefixes to component names based on NPM module, refs #49

--- src/lib/ComponentLoader.coffee
+++ src/lib/ComponentLoader.coffee
@@ -16,9 +16,14 @@

   constructor: (@baseDir) ->

+  getModulePrefix: (name) ->
+    name.replace 'noflo-', ''
+
   getModuleComponents: (moduleDef) ->
     components = {}
     checked.push moduleDef.name
+
+    prefix = @getModulePrefix moduleDef.name

     # Handle sub-modules
     _.each moduleDef.dependencies, (def) =>
@@ -32,7 +37,7 @@
     return components unless moduleDef.noflo
     return components unless moduleDef.noflo.components
     _.each moduleDef.noflo.components, (cPath, name) ->
-      components[name] = path.resolve moduleDef.realPath, cPath
+      components["#{prefix}/#{name}"] = path.resolve moduleDef.realPath, cPath
     components

   listComponents: (callback) ->


debugging

debugging

--- lib/ObjectTemplate.coffee
+++ lib/ObjectTemplate.coffee
@@ -45,6 +45,7 @@
       else
         binder = (element, index) =>
           key = @chooseKey element
+          console.log('key', key, 'element', element) if key is 'similar'
           value = @chooseValue(element, {})
           formatted = @formatNodes node, value, key
           context[formatted.key] = formatted.value if typeof formatted.value isnt "undefined"


Updated config schema to work with latest API

--- lib/coffee-compile.coffee
+++ lib/coffee-compile.coffee
@@ -5,17 +5,27 @@
 util              = require './util'

 module.exports =
-  configDefaults:
-    grammars: [
-      'source.coffee'
-      'source.litcoffee'
-      'text.plain'
-      'text.plain.null-grammar'
-    ]
-    noTopLevelFunctionWrapper: true
-    compileOnSave: false
-    compileOnSaveWithoutPreview: false
-    focusEditorAfterCompile: false
+  config:
+    grammars:
+      type: 'array'
+      default: [
+        'source.coffee'
+        'source.litcoffee'
+        'text.plain'
+        'text.plain.null-grammar'
+      ]
+    noTopLevelFunctionWrapper:
+      type: 'boolean'
+      default: true
+    compileOnSave:
+      type: 'boolean'
+      default: false
+    compileOnSaveWithoutPreview:
+      type: 'boolean'
+      default: false
+    focusEditorAfterCompile:
+      type: 'boolean'
+      default: false

   activate: ->
     atom.workspaceView.command 'coffee-compile:compile', => @display()


fix for undefined slider bug

fix for undefined slider bug

Former-commit-id: af895f2d26c4f20430e2fd9cbfe1c479cdf98e00 [formerly ee3e71d338ff1746872898aed51551d25a9d1b46] [formerly f8b2a1fc1e385cc90d0847f3d21773f13e5b7f05 [formerly a27eddd720af8d6afbd906b43af3c465650c6262]] [formerly 1d6aaae8840f96c72a10b2cfcb4df9f3e4c70329 [formerly a55852748eda190fc2889cdb317d0c82c55f387e] [formerly 5f548ec2f9ae4209a35692d2b08b38ab151e9c5c [formerly 5f548ec2f9ae4209a35692d2b08b38ab151e9c5c [formerly 265265b592575bce6d68029ff51ca5d399fe2b90]]]]
Former-commit-id: 1532b07ad66108fec01bea81ffd4a33739535805 [formerly 961bf50efbcdac45597797a51ef5c5ac916168c3] [formerly 70f61110dc0398ae22de03c0bc33640f992b3bad [formerly ab7079902b9ffdb79353065b83a507183489c534]]
Former-commit-id: 02a46662cef790b47ceca78f25c83f9acae448ca [formerly d06cb03db012f04f873ee09bd5ffc03358c41148]
Former-commit-id: eded2b4e654a9611c176d44e14402ae95fa07056
Former-commit-id: d9e48ceb6cb82b028b3434b2874c3754112cfad1

--- @client/customizations.coffee
+++ @client/customizations.coffee
@@ -324,10 +324,11 @@
                               "Slightly "

       valence = customization "slider_pole_labels.individual." + \
-                              (if value > 0 then 'agree' else 'disagree'), \
+                              (if value > 0 then 'support' else 'oppose'), \
                               proposal

       "You #{strength_of_opinion} #{valence}"
+

 plus_minus =
   individual:


Clean

Clean

--- lib/non-navigation.coffee
+++ lib/non-navigation.coffee
@@ -1,5 +1,4 @@
 _ = require 'underscore-plus'
-# {Point} = require 'atom'

 module.exports =
 class NonNavigation
@@ -10,7 +9,8 @@
   moveToNextBoundary: ->
     for cursor in @cursors()
       scope = scope or cursor.getScopeDescriptor()
-      if position = cursor.getNextWordBoundaryBufferPosition(@cursorOptions(scope))
+      options = @cursorOptions(scope)
+      if position = cursor.getNextWordBoundaryBufferPosition(options)
         cursor.setBufferPosition(position)

   wordAndNonWordRegExp: (scope, options={}) ->


fix pandoc directory issue

fix pandoc directory issue

--- lib/pandoc-wrapper.coffee
+++ lib/pandoc-wrapper.coffee
@@ -2,6 +2,7 @@
 fs = require 'fs'
 {execFile} = require 'child_process'
 matter = require 'gray-matter'
+{Directory} = require 'atom'

 getFileExtension = (documentType)->
   if documentType == 'pdf_document' or documentType == 'beamer_presentation'
@@ -242,13 +243,17 @@
     args.push('--filter', 'pandoc-citeproc')

   # console.log args.join(' ')
-
-  atom.notifications.addInfo('Your document is being prepared', detail: ':)')
-
-  program = execFile 'pandoc', args, (err)->
-    process.chdir(cwd) # change cwd back
-    throw err if err
-    atom.notifications.addInfo "File #{path.basename(outputFilePath)} was created", detail: "path: #{outputFilePath}"
-  program.stdin.end(text)
+  #
+  # pandoc will cause error if directory doesn't exist,
+  # therefore I will create directory first.
+  directory = new Directory(path.dirname(outputFilePath))
+  directory.create().then (flag)->
+    atom.notifications.addInfo('Your document is being prepared', detail: ':)')
+
+    program = execFile 'pandoc', args, (err)->
+      process.chdir(cwd) # change cwd back
+      throw err if err
+      atom.notifications.addInfo "File #{path.basename(outputFilePath)} was created", detail: "path: #{outputFilePath}"
+    program.stdin.end(text)

 module.exports = pandocConvert


Better error message

Better error message

--- lib/relations/many.coffee
+++ lib/relations/many.coffee
@@ -131,7 +131,7 @@
     # fetch
     @reverse_model_type.cursor({$ids: load_ids}).toJSON (err, json) =>
       return callback(err) if err
-      return callback(new Error "Failed to load all models. Id #{util.inspect(load_ids)}. Expected: #{load_ids.length}. Actual: #{json.length}", callback) if load_ids.length isnt json.length
+      return callback(new Error "Failed to load all models. Id #{util.inspect(load_ids)}. Actual: #{util.inspect(_.pluck(json, 'id'))}", callback) if load_ids.length isnt json.length

       # update
       collection._orm_loaded = true


easier to see emailing

easier to see emailing

--- models/invite.coffee
+++ models/invite.coffee
@@ -16,7 +16,7 @@

 InviteSchema.method 'send', (force) ->
   if not @sent or force
-    util.log "Sending 'teams_new' to '#{@email}'"
+    util.log "Sending 'teams_new' to '#{@email}'".yellow
     team = @parentArray._parent
     postageapp.apiCall @email, 'teams_new', null, 'all@nodeknockout.com',
       team_id: team.id


Remove dead code.

Remove dead code.

The "else if" part seems to contain keys that are also tested for in the "if"
part.

--- scripts/gh-book/session.coffee
+++ scripts/gh-book/session.coffee
@@ -11,8 +11,9 @@
         if not _.isEmpty _.pick @.changed, ['token', 'id', 'password']
           @_reloadClient()

-        # If any of the repo settings change then check if the user can still collaborate
-        else if not _.isEmpty _.pick @.changed, ['repoUser', 'repoName', 'id', 'token', 'password']
+        # If any of the repo settings change then check if the user can still
+        # collaborate
+        else if not _.isEmpty _.pick @.changed, ['repoUser', 'repoName']
           @checkCanCollaborate()

     authenticate: (config) ->


Remove some extra logging

Remove some extra logging

--- lib/views/terminal.coffee
+++ lib/views/terminal.coffee
@@ -55,7 +55,6 @@
       @term.cursorHidden = true

     @terminal.on 'terminal-session-opened', () =>
-      console.log('OPENED')
       @term.off 'data'
       @term.on 'data', (data) =>
         ipc.send 'terminal-data', data
@@ -68,7 +67,6 @@

   openLab: ->
     if @openPath
-      console.log('open path!')
       ipc.send 'terminal-data', 'learn open ' + @openPath.toString() + '\r'
       @openPath = null


OK... just wanted to make sure you can still test private methods

...in CoffeeScript

--- example/coffeeTest.coffee
+++ example/coffeeTest.coffee
@@ -43,16 +43,36 @@
       when 'function' then return -> @obj.apply(this, arguments)

     return null if @obj is null
-    return @cloneArray() if @obj instanceof Array
-    return @cloneObject()
+    return cloneArray(@obj) if @obj instanceof Array
+    return cloneObject(@obj)

-  cloneArray: ->
-    @obj.slice(0)
+###
+Clones an array

-  cloneObject: ->
-    clone = {}
-    for key of @obj
-      clone[key] = @obj[key]
-    clone
+@private
+@examples
+arr = [1, 2, 3]
+
+cloneArray(arr)         // [1, 2, 3]
+cloneArray(arr).push(4) // arr == [1, 2, 3]
+###
+cloneArray = (arr) ->
+  arr.slice(0)
+
+###
+Clones an object
+
+@private
+@examples
+obj = { foo: 'bar' }
+
+cloneObject(obj)             // { foo: 'bar' }
+cloneObject(obj).foo = 'baz' // obj == { foo: 'bar' }
+###
+cloneObject = (obj) ->
+  clone = {}
+  for key of obj
+    clone[key] = obj[key]
+  clone

 module.exports = C


Alphabetized routes

Alphabetized routes

--- src/server/routes/index.coffee
+++ src/server/routes/index.coffee
@@ -1,15 +1,15 @@
 routes = [
+	require "./get/authorize.js"
+	require "./get/callback.js"
+	require "./get/confirm.js"
+	require "./get/login.js"
+	require "./get/logout.js"
+	require "./get/settings.js"
+	require "./get/teapot.js"
+	require "./post/decision.js"
 	require "./post/login.js"
-	require "./get/teapot.js"
-	require "./get/settings.js"
-	require "./get/authorize.js"
-	require "./get/logout.js"
-	require "./get/login.js"
-	require "./get/confirm.js"
-	require "./get/callback.js"
 	require "./post/settings.js"
 	require "./post/signup.js"
-	require "./post/decision.js"
 	require "./post/token.js"
 ]


Wait for snippets to load before checking snippets

--- spec/installed-package-view-spec.coffee
+++ spec/installed-package-view-spec.coffee
@@ -37,25 +37,29 @@

   it "displays the snippets registered by the package", ->
     snippetsTable = null
+    snippetsModule = null

     waitsForPromise ->
       atom.packages.activatePackage('snippets').then (p) ->
-        return unless p.mainModule.provideSnippets().getUnparsedSnippets?
+        snippetsModule = p.mainModule
+        return unless snippetsModule.provideSnippets().getUnparsedSnippets?

         SnippetsProvider =
-          getSnippets: -> p.mainModule.provideSnippets().getUnparsedSnippets()
+          getSnippets: -> snippetsModule.provideSnippets().getUnparsedSnippets()

     waitsForPromise ->
       atom.packages.activatePackage(path.join(__dirname, 'fixtures', 'language-test'))

+    waitsFor 'snippets to load', (done) ->
+      snippetsModule.onDidLoadSnippets(done)
+
     runs ->
       pack = atom.packages.getActivePackage('language-test')
       view = new PackageDetailView(pack, new SettingsView(), new PackageManager(), SnippetsProvider)
       snippetsTable = view.element.querySelector('.package-snippets-table tbody')

-    waitsFor ->
+    waitsFor 'snippets table children to contain 2 items', ->
       snippetsTable.children.length >= 2
-    , 'Snippets table children to contain 2 items', 5000

     runs ->
       expect(snippetsTable.querySelector('tr:nth-child(1) td:nth-child(1)').textContent).toBe 'b'


fix formatting

fix formatting

--- examples/todo/todo.coffee
+++ examples/todo/todo.coffee
@@ -3,9 +3,11 @@
   load: -> JSON.parse(localStorage.getItem(this._id) or '[]')
   save: (x) -> localStorage.setItem this._id, JSON.stringify(x)

+
 createActions = ->
   to_hash ([name, new Bacon.Bus()] for name in \
     ['creates', 'destroys', 'edits', 'toggles', 'megatoggles', 'clears'])
+

 createModel = (initialTodos, actions) ->
   mutators =
@@ -20,7 +22,7 @@
       _.defaults {completed}, t

   {creates, destroys, edits, toggles, megatoggles, clears} = actions
-  # thanks to Juha Paananen for the stream of functions pattern!
+  # thanks to Juha Paananen for the stream-of-functions pattern!
   mutations = creates.map(mutators.create)
     .merge(destroys.map(mutators.delete))
     .merge(toggles.merge(edits).map(mutators.update))
@@ -32,33 +34,35 @@
   completedTodos = allTodos.map (todos) -> _.where todos, completed: true
   {allTodos, activeTodos, completedTodos}

+
 createView = (model, actions, hash) ->
-  rendactive Handlebars.compile($("#app").html()), (h) ->
+  rendactive Handlebars.compile($('#app').html()), (h) ->
     title = h.inputValue('#new-todo').map((val) -> val.trim())
-    h.enters("#new-todo").onValue -> $("#new-todo").val('')
+    h.enters('#new-todo').onValue -> $('#new-todo').val('')

     # convert ui events into actions
     {creates, destroys, edits, toggles, megatoggles, clears} = actions
-    creates.plug h.enters("#new-todo").map(title).filter(_.identity)
-    destroys.plug h.ids("click [data-action=delete]")
-    edits.plug h.enters(".edit").merge(h.blurs(".edit")).map (e) ->
+    creates.plug h.enters('#new-todo').map(title).filter(_.identity)
+    destroys.plug h.ids('click [data-action=delete]')
+    edits.plug h.enters('.edit').merge(h.blurs('.edit')).map (e) ->
       id: $(e.target).data('id')
       changes: {title: $(e.target).val().trim()}
     destroys.plug edits.filter(({changes}) -> !changes.title).map(({id}) -> id)
-    toggles.plug h.changes("[data-action=toggle]").map (e) ->
+    toggles.plug h.changes('[data-action=toggle]').map (e) ->
       id: $(e.target).data('id')
-      changes: {completed: $(e.target).is(":checked")}
-    megatoggles.plug h.checkboxBooleans("#toggle-all")
-    clears.plug h.clicks("#clear-completed")
+      changes: {completed: $(e.target).is(':checked')}
+    megatoggles.plug h.checkboxBooleans('#toggle-all')
+    clears.plug h.clicks('#clear-completed')

-    editingId = h.ids("dblclick label").merge(edits.map(null)).toProperty(null)
-    h.valueAfterRender editingId, (id) -> $("#edit-#{id}").focus()
+    editingId = h.ids('dblclick label').merge(edits.map(null)).toProperty(null)
+    h.valueAfterRender editingId, (id) -> $('#edit-#{id}').focus()

     {allTodos, activeTodos, completedTodos} = model
     selectedTodos = hash.decode
       '#/': allTodos, '#/active': activeTodos, '#/completed': completedTodos

     {allTodos, completedTodos, activeTodos, selectedTodos, editingId}
+

 createApplication = ->
   hash = Bacon.UI.hash '#/'
@@ -71,5 +75,6 @@
   model.allTodos.onValue storage.save
   {model, view, actions}

+
 app = createApplication()
 $('body').append(app.view.fragment)


fix https://github.com/sagemathinc/cocalc/issues/2382 -- race condition when opening syncstring.

--- src/smc-util/syncstring.coffee
+++ src/smc-util/syncstring.coffee
@@ -593,7 +593,7 @@
             window.syncstrings[@_path] = @
         ###

-        # window.s = @
+        ## window?.s = @

         #dbg = @dbg("constructor(path='#{@_path}')")
         #dbg('connecting...')
@@ -816,8 +816,8 @@
     # The project calls this once it has checked for the file on disk; this
     # way the frontend knows that the syncstring has been initialized in
     # the database, and also if there was an error doing the check.
-    _set_initialized: (error, is_read_only, cb) =>
-        init = {time: misc.server_time()}
+    _set_initialized: (error, is_read_only, size, cb) =>
+        init = {time: misc.server_time(), size:size}
         if error
             init.error = "error - #{JSON.stringify(error)}"  # must be a string!
         else
@@ -939,7 +939,24 @@
                 @_syncstring_table.wait
                     until : (t) => t.get_one()?.get('init')
                     cb    : (err, init) =>
-                        @emit('init', err ? init.toJS().error)
+                        if err
+                            @emit('init', err)
+                            return
+                        init = init.toJS()
+                        err  = init.error
+                        console.log(err, init, @_patch_list.count(), init.size)
+                        if err
+                            @emit('init', err)
+                            return
+                        if @_client.is_user() and @_patch_list.count() == 0 and (init.size ? 0) > 0
+                            # wait for a change -- i.e., project loading the file from
+                            # disk and making available...  Because init.size > 0, we know that
+                            # there must be SOMETHING in the patches table once initialization is done.
+                            # This is the root cause of https://github.com/sagemathinc/cocalc/issues/2382
+                            @_patches_table.once 'change', =>
+                                @emit('init')
+                        else
+                            @emit('init')
                 if err
                     cb(err)
                 else
@@ -992,7 +1009,7 @@
     _load_from_disk_if_newer: (cb) =>
         tm     = @last_changed()
         dbg    = @_client.dbg("syncstring._load_from_disk_if_newer('#{@_path}')")
-        locals = {exists: false, is_read_only: false}
+        locals = {exists: false, is_read_only: false, size:0}
         async.series([
             (cb) =>
                 dbg("check if path exists")
@@ -1018,7 +1035,9 @@
                                 cb(err)
                             else if stats.ctime > tm
                                 dbg("disk file changed more recently than edits, so loading")
-                                @_load_from_disk(cb)
+                                @_load_from_disk (err, size) =>
+                                    locals.size = size
+                                    cb(err)
                             else
                                 dbg("stick with database version")
                                 cb()
@@ -1026,7 +1045,9 @@
                     dbg("never edited before")
                     if locals.exists
                         dbg("path exists, so load from disk")
-                        @_load_from_disk(cb)
+                        @_load_from_disk (err, size) =>
+                            locals.size = size
+                            cb(err)
                     else
                         cb()
             (cb) =>
@@ -1037,7 +1058,7 @@
                 else
                     cb()
         ], (err) =>
-            @_set_initialized(err, locals.is_read_only, cb)
+            @_set_initialized(err, locals.is_read_only, locals.size, cb)
         )

     _patch_table_query: (cutoff) =>
@@ -1614,7 +1635,7 @@
                 cb()
         ], (err) => cb?(err))

-    _load_from_disk: (cb) =>
+    _load_from_disk: (cb) =>   # cb(err, size)
         path = @get_path()
         dbg = @_client.dbg("syncstring._load_from_disk('#{path}')")
         dbg()
@@ -1622,24 +1643,24 @@
             cb?('lock')
             return
         @_load_from_disk_lock = true
-        exists = undefined
+        locals = {exists: undefined, size:0}
         async.series([
             (cb) =>
                 @_client.path_exists
                     path : path
-                    cb   : (err, x) =>
-                        exists = x
+                    cb   : (err, exists) =>
+                        locals.exists = exists
                         if not exists
                             dbg("file no longer exists")
                             @from_str('')
                         cb(err)
             (cb) =>
-                if exists
+                if locals.exists
                     @_update_if_file_is_read_only(cb)
                 else
                     cb()
             (cb) =>
-                if not exists
+                if not locals.exists
                     cb()
                     return
                 @_client.path_read
@@ -1650,7 +1671,8 @@
                             dbg("failed -- #{err}")
                             cb(err)
                         else
-                            dbg("got it -- length=#{data?.length}")
+                            locals.size = data?.length ? 0
+                            dbg("got it -- length=#{locals.size}")
                             @from_str(data)
                             # we also know that this is the version on disk, so we update the hash
                             @_set_save(state:'done', error:false, hash:misc.hash_string(data))
@@ -1660,7 +1682,7 @@
                 @_save(cb)
         ], (err) =>
             @_load_from_disk_lock = false
-            cb?(err)
+            cb?(err, locals.size)
         )

     _set_save: (x) =>


logs: minor fixes

logs: minor fixes

--- workers/social/lib/social/models/errorlog.coffee
+++ workers/social/lib/social/models/errorlog.coffee
@@ -27,11 +27,13 @@
       _.extend record, params

       rawCurr = new Date
+
+      year    = rawCurr.getFullYear()
+      month   = rawCurr.getMonth() + 1
       date    = rawCurr.getDate()
-      month   = rawCurr.getMonth() + 1
-      year    = rawCurr.getFullYear()
       hour    = rawCurr.getHours()
       min     = Math.round(rawCurr.getMinutes()/uniqInt)*uniqInt # round to nearest time
+
       timeStr = "#{year}#{month}#{date}#{hour}#{min}"

       {error, reason} = params
@@ -39,7 +41,7 @@

       # to reduce noise in logs, we don't log if there was the same error from
       # same user within `uniqInt`; es enforces unique constraint on `_id`
-      idStr = "#{error}#{reason}#{username}#{year}#{month}#{date}#{hour}#{min}"
+      idStr = "#{error}#{reason}#{username}#{timeStr}"
       _id   = checksum idStr

       # _.extends modifies source object, so we clone to keep them seperate


remove config from specs

remove config from specs

--- spec/new-post-view-spec.coffee
+++ spec/new-post-view-spec.coffee
@@ -47,7 +47,7 @@
       runs ->
         dialog = $(atom.workspace.getModalPanels()[0].getItem()).view()
         titleName = Utils.generateFileName('Jekyll New Post')
-        fileName = atom.config.get('jekyll.postsDir') + titleName + atom.config.get('jekyll.postsType')
+        fileName = path.join('_posts', titleName + '.markdown')
         pathToCreate = atom.project.getDirectories()[0]?.resolve(fileName)

         fs.unlinkSync(pathToCreate) if fs.existsSync(pathToCreate)
@@ -88,7 +88,7 @@
       runs ->
         dialog = $(atom.workspace.getModalPanels()[0].getItem()).view()
         titleName = Utils.generateFileName('Jekyll New Post')
-        fileName = atom.config.get('jekyll.postsDir') + titleName + atom.config.get('jekyll.postsType')
+        fileName = path.join('_posts', titleName + '.markdown')
         pathToCreate = atom.project.getDirectories()[0]?.resolve(fileName)

         fs.unlinkSync(pathToCreate) if fs.existsSync(pathToCreate)


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/floating_text_effect.coffee
+++ src/floating_text_effect.coffee
@@ -3,7 +3,7 @@
 to move the text upward and fade it out over 0.5 seconds.

     # adds a
-    engine.add 'FlTextEffect'
+    engine.add 'FloatingTextEffect'
       x: 50
       y: 50


Default to auto height being true.

Default to auto height being true.

Otherwise TextEditorElements created through the tag wouldn’t have a
setting and be wrong.

--- src/text-editor-element.coffee
+++ src/text-editor-element.coffee
@@ -18,6 +18,7 @@
   hasTiledRendering: true
   logicalDisplayBuffer: true
   scrollPastEnd: true
+  autoHeight: true

   createdCallback: ->
     # Use globals when the following instance variables aren't set.


destroy buffer

destroy buffer

--- lib/atom-ternjs-reference.coffee
+++ lib/atom-ternjs-reference.coffee
@@ -70,6 +70,7 @@
       item.position = buffer.positionForCharacterIndex(item.start)
       item.lineText = buffer.lineForRow(item.position.row)
       item.lineText = item.lineText.replace(data.name, "<strong>#{data.name}</strong>")
+      buffer.destroy()
     data

   hide: ->


Run curl independently

Run curl independently

--- actions/curl.coffee
+++ actions/curl.coffee
@@ -37,6 +37,7 @@
 ###
 module.exports.push (ctx) ->
   require('./users').configure ctx
+  require('./proxy').configure ctx
   ctx.config.curl ?= {}
   {curl} = ctx.config
   curl.merge ?= true
@@ -57,7 +58,7 @@
 ###
 module.exports.push name: 'Curl # User Configuration', callback: (ctx, next) ->
   ok = false
-  {merge, proxy, users, config} = ctx.config.curl
+  {merge, users, config} = ctx.config.curl
   work = (user, file, next)->
     ctx.log "Write config into #{file}"
     ctx.ini


send email from mail@nodeknockout.com

send email from mail@nodeknockout.com

--- models/models.coffee
+++ models/models.coffee
@@ -93,7 +93,7 @@
         The Node.js Knockout Organizers
         """
       http.post 'http://www.postalgone.com/mail', {
-        sender: '"Node.js Knockout" <mail@postalgone.com>',
+        sender: '"Node.js Knockout" <mail@nodeknockout.com>',
         from: 'all@nodeknockout.com',
         to: @email,
         subject: "Password reset for Node.js Knockout",
@@ -117,7 +117,7 @@
       The Node.js Knockout Organizers
       """
     http.post 'http://www.postalgone.com/mail', {
-      sender: '"Node.js Knockout" <mail@postalgone.com>',
+      sender: '"Node.js Knockout" <mail@nodeknockout.com>',
       from: 'all@nodeknockout.com',
       to: @email,
       subject: "You've been invited to Node.js Knockout",


Added method for creating map of collections based on their names.

--- Collections.coffee
+++ Collections.coffee
@@ -121,3 +121,13 @@
     # TODO(aramk) Temporary solution to prevent refreshing due to added callback firing for all
     # existing docs.
     isObserving = true
+
+# @param {Array.<Meteor.Collection>} collections
+# @returns {Object.<String, Meteor.Collection>} A map of collection name to object for the given
+# collections.
+  getMap: (collections) ->
+    collectionMap = {}
+    _.each collections, (collection) =>
+      name = @getName(collection)
+      collectionMap[name] = collection
+    collectionMap


Fix spawn leak

Fix spawn leak

--- lib/ghc-modi-process-real.coffee
+++ lib/ghc-modi-process-real.coffee
@@ -9,8 +9,8 @@
 module.exports =
 class GhcModiProcessReal
   constructor: (@caps) ->
-    @processMap = new WeakMap
-    @bufferDirMap = new WeakMap #TextBuffer -> Directory
+    @processMap = new Map #FilePath -> InteractiveProcess
+    @bufferDirMap = new WeakMap #TextBuffer -> FilePath
     @disposables = new CompositeDisposable
     @disposables.add @emitter = new Emitter

@@ -66,7 +66,7 @@
   spawnProcess: (rootDir, options) =>
     return unless @processMap?
     return unless atom.config.get('haskell-ghc-mod.enableGhcModi')
-    proc = @processMap.get(rootDir)
+    proc = @processMap.get(rootDir.getPath())
     debug "Checking for ghc-modi in #{rootDir.getPath()}"
     if proc?
       debug "Found running ghc-modi instance for #{rootDir.getPath()}"
@@ -82,8 +82,8 @@
         new InteractiveProcess(modiPath, [], options, @caps)
     proc.onExit (code) =>
       debug "ghc-modi for #{rootDir.getPath()} ended with #{code}"
-      @processMap?.delete(rootDir)
-    @processMap.set rootDir, proc
+      @processMap?.delete(rootDir.getPath())
+    @processMap.set rootDir.getPath(), proc
     return proc

   runModCmd: ({options, command, text, uri, args}) ->
@@ -165,14 +165,14 @@
   killProcess: =>
     return unless @processMap?
     debug "Killing all ghc-modi processes"
-    atom.workspace.getTextEditors().forEach (editor) =>
-      @killProcessForDir @getRootDir(editor.getBuffer())
+    @processMap.forEach (proc) ->
+      proc.kill()

   killProcessForDir: (dir) =>
     return unless @processMap?
     debug "Killing ghc-modi process for #{dir.getPath()}"
-    @processMap.get(dir)?.kill?()
-    @processMap.delete(dir)
+    @processMap.get(dir.getPath())?.kill?()
+    @processMap.delete(dir.getPath())

   destroy: =>
     return unless @processMap?


For the special case of the "URN" field, we want to construct the value.

--- src/js/collection-editor.coffee
+++ src/js/collection-editor.coffee
@@ -7,6 +7,12 @@

 google_oauth_url =
   "https://accounts.google.com/o/oauth2/auth?#{$.param(google_oauth_parameters_for_fusion_tables)}"
+
+cite_urn = (namespace, collection, row, version) ->
+  urn = "urn:cite:#{namespace}:#{collection}.#{row}"
+  if arguments.length == 4
+    urn += ".#{version}"
+  return urn

 disable_collection_form = ->
   $('#collection_form').children().prop('disabled',true)
@@ -27,7 +33,13 @@
       else
         $('<input>').attr('style','width:100%')
     when 'citeurn', 'citeimg'
-      $('<input>').attr('style','width:100%')
+      # for the special case of the "URN" field, we want to construct the value
+      if $(property).attr('name') == 'URN'
+        namespace = $(property).parent().find('namespaceMapping').attr('abbr')
+        collection = $(property).parent().attr('name')
+        $('<input>').attr('style','width:100%').prop('disabled',true).attr('value',cite_urn(namespace,collection,1))
+      else
+        $('<input>').attr('style','width:100%')
     when 'datetime'
       $('<input>').attr('type','date')
     else


Add Block()

Add Block()

--- src/layouts/default.html.coffee
+++ src/layouts/default.html.coffee
@@ -1,9 +1,15 @@
+Block = (name, fn)=>
+  z = @getBlock name
+  z = fn.call z if 'function'==typeof fn
+  raw z.toHTML()
+
 (tag "!DOCTYPE", true) html: true
 html ->
   head ->
     title @getPreparedTitle()
-    raw @getBlock("meta").toHTML()
-    raw @getBlock("styles").add(["/styles/style.css"]).toHTML()
+    Block "meta"
+    Block "styles", ->
+      @add ["/styles/style.css"]
   body ->
     img src: "/images/logo.gif"

@@ -17,4 +23,5 @@

     h1 @document.title
     raw @content
-    raw @getBlock("scripts").add(["/vendor/jquery.js", "/scripts/script.js"]).toHTML()
+    Block "scripts", ->
+      @add ["/vendor/jquery.js", "/scripts/script.js"]


ident as root if connecting from office...

--- code/serv.coffee
+++ code/serv.coffee
@@ -215,8 +215,15 @@
           return res.send {error: "Unable to create box"} if err? or any_stderr
           return res.send {status: "ok"}

+myCheckIdent = (req, res, next) ->
+  if req.ip is "88.211.55.91"
+    req.ident = 'root'
+    next()
+  else
+    checkIdent req, res, next
+
 # Add an SSH key to a box
-app.post "/:boxname/sshkeys/?", checkIP, checkIdent, (req, res) ->
+app.post "/:boxname/sshkeys/?", checkIP, myCheckIdent, (req, res) ->
   if req.ident != 'root'
     return res.send 400,
       error: "Only custard running as root can contact me"


deal with synchronization problem

deal with synchronization problem

--- views/akashic-senka-server.cjsx
+++ views/akashic-senka-server.cjsx
@@ -41,7 +41,7 @@
                "タウイタウイ泊地", "パラオ泊地", "ブルネイ泊地", "単冠湾泊地", "幌筵泊地",
                "宿毛湾泊地", "鹿屋基地", "岩川基地", "佐伯湾泊地", "柱岛泊地"]

-sync = async (memberId, serverId, isDownloading) ->
+sync = async (memberId, serverId, serverSelectedVersion, isDownloading) ->
   time = dateToString()
   senkaList = {}
   #serverList 1-100/500/990
@@ -49,9 +49,11 @@
     json: true
   if response.statusCode == 200
     senkaList = JSON.stringify(body)
-    fs.ensureDirSync(path.join(APPDATA_PATH, 'akashic-records', "#{memberId}", 'senkaList', "#{serverId}", '500'))
-    fs.appendFile(path.join(APPDATA_PATH, 'akashic-records', "#{memberId}", 'senkaList', "#{serverId}", '500', time), "#{senkaList}", 'utf8', (err)->
-      error "Write senkaList file error!" if err)
+    try
+      fs.ensureDirSync path.join(APPDATA_PATH, 'akashic-records', "#{memberId}", 'senkaList', "#{serverId}", '500')
+      fs.writeFileSync path.join(APPDATA_PATH, 'akashic-records', "#{memberId}", 'senkaList', "#{serverId}", '500', time), "#{senkaList}", 'utf8'
+    catch e
+      error "Write senkaList file error!#{e}"
     log "save server:#{serverId} senkaList(500) from senkame[#{time}]"
   else
     log response.statusCode
@@ -60,13 +62,15 @@
     json: true
   if response.statusCode == 200
     senkaList = JSON.stringify(body)
-    fs.ensureDirSync(path.join(APPDATA_PATH, 'akashic-records', "#{memberId}", 'senkaList', "#{serverId}", '990'))
-    fs.appendFile(path.join(APPDATA_PATH, 'akashic-records', "#{memberId}", 'senkaList', "#{serverId}", '990', time), "#{senkaList}", 'utf8', (err)->
-      error "Write senkaList file error!" if err)
+    try
+      fs.ensureDirSync path.join(APPDATA_PATH, 'akashic-records', "#{memberId}", 'senkaList', "#{serverId}", '990')
+      fs.writeFileSync path.join(APPDATA_PATH, 'akashic-records', "#{memberId}", 'senkaList', "#{serverId}", '990', time), "#{senkaList}", 'utf8'
+    catch e
+      error "Write senkaList file error!#{e}"
     log "save server:#{serverId} senkaList(990) from senkame[#{time}]"
   else
     log response.statusCode
-  isDownloading()
+  isDownloading(serverSelectedVersion)
   return

 AkashicSenkaServer = React.createClass
@@ -76,17 +80,17 @@
     server: "镇守府"
     date: "日期"
     serverId: config.get "plugin.Akashic.senka.serverId", 0
-    downloadingFlag: ""
+    serverSelectedVersion: 0
+    downloadingFlag: true

-  updateSenkaList: (showAmount, serverId) ->
+  updateSenkaList: (showAmount, serverId, serverSelectedVersion) ->
     time = dateToString()
     senkalist = {}
     senkaList = glob.sync(path.join(APPDATA_PATH, 'akashic-records', "#{@props.memberId}", 'senkaList', "#{serverId}", "#{showAmount}", "#{time}"))
     if senkaList.length == 0
-      sync @props.memberId, serverId, isDownloading=() =>
-        @updateSenkaList @state.showAmount, @state.serverId
-        @setState
-        	downloadingFlag: false
+      sync @props.memberId, serverId, serverSelectedVersion, isDownloading=(serverSelectedVersion) =>
+        if serverSelectedVersion is @state.serverSelectedVersion
+          @updateSenkaList @state.showAmount, @state.serverId, @state.serverSelectedVersion
       @setState
         downloadingFlag: true
     else
@@ -104,6 +108,7 @@
         tableData: list["list"]
         server: list["name"]
         date: dateStringFormat(list["date"])
+        downloadingFlag: false

   shouldComponentUpdate: (nextProps, nextState) ->
     refreshFlag = false
@@ -117,29 +122,37 @@
 #    if @state.tableData.length < 10 and @props.memberId > 0
 #      @updateSenkaList @state.showAmount, @state.serverId

+  componentWillMount: () ->
+    @updateSenkaList @state.showAmount, @state.serverId, @state.serverSelectedVersion
+
   componentWillReceiveProps: (nextProps) ->
     time = dateToString()
     if @props.memberId > 0 and @state.tableData.length < 10
-      @updateSenkaList @state.showAmount, @state.serverId
+      @updateSenkaList @state.showAmount, @state.serverId, @state.serverSelectedVersion

   handleCustomClick: ->
     showAmount = 500
-    @updateSenkaList showAmount, @state.serverId
-    @setState
-      showAmount: showAmount
-    config.set "plugin.Akashic.senka.table.showAmount", showAmount
+    if not @state.downloadingFlag
+      @updateSenkaList showAmount, @state.serverId, @state.serverSelectedVersion
+      @setState
+        showAmount: showAmount
+      config.set "plugin.Akashic.senka.table.showAmount", showAmount
   handleMoreClick: ->
     showAmount = 990
-    @updateSenkaList showAmount, @state.serverId
-    @setState
-      showAmount: showAmount
-    config.set "plugin.Akashic.senka.table.showAmount", showAmount
+    if not @state.downloadingFlag
+      @updateSenkaList showAmount, @state.serverId, @state.serverSelectedVersion
+      @setState
+        showAmount: showAmount
+      config.set "plugin.Akashic.senka.table.showAmount", showAmount

   handleFilterSelect: (e) ->
     serverId = parseInt e.target.value
-    @updateSenkaList @state.showAmount, serverId
+    {serverSelectedVersion} = @state
+    serverSelectedVersion += 1
+    @updateSenkaList @state.showAmount, serverId, serverSelectedVersion
     @setState
       serverId: serverId
+      serverSelectedVersion: serverSelectedVersion
     config.set "plugin.Akashic.senka.serverId", serverId

   render: ->


translated some of the phrases (#3741)

translated some of the phrases (#3741)

--- app/locale/fr.coffee
+++ app/locale/fr.coffee
@@ -79,7 +79,7 @@
     teachers: "Enseignants!"
     teachers_and_educators: "Enseignants et éducateurs"
     class_in_box: "Apprenez comment notre plateforme classe-tout-inclus s'adapte à votre curriculum."
-#    get_started: "Get Started"
+    get_started: "Commencer"
     students: "Étudiants:"
     join_class: "Joindre une classe"
     role: "Votre rôle:"
@@ -515,9 +515,9 @@
     tip_nemo: "Quand la vie vous rabaisse, vous savez ce que vous devenez faire ? Juste continuer de nager, juste continuer de nager. - Dory, Finding Nemo"
     tip_internet_weather: "Just move to the internet, it's great here. We get to live inside where the weather is always awesome. - John Green"
 #    tip_nerds: "Nerds are allowed to love stuff, like jump-up-and-down-in-the-chair-can't-control-yourself love it. - John Green"
-#    tip_self_taught: "I taught myself 90% of what I've learned. And that's normal! - Hank Green"
-#    tip_luna_lovegood: "Don't worry, you're just as sane as I am. - Luna Lovegood"
-#    tip_good_idea: "The best way to have a good idea is to have a lot of ideas. - Linus Pauling"
+    tip_self_taught: "Je me suis enseigné 90% de ce que j'ai appris. Et c'est normal! - Hank Green"
+    tip_luna_lovegood: "Ne t'en fais pas, tu es aussi sain que moi. - Luna Lovegood"
+    tip_good_idea: "La meilleure façon d'avoir une bonne idée est d'avoir beaucoup d'idées. - Linus Pauling"
 #    tip_programming_not_about_computers: "Computer Science is no more about computers than astronomy is about telescopes. - Edsger Dijkstra"
 #    tip_mulan: "Believe you can, then you will. - Mulan"

@@ -585,7 +585,7 @@
     feature5: "Tutoriels vidéo"
     feature6: "Assitance par e-mail dédiée"
     feature7: "Clans privés"
-#    feature8: "No ads!"
+    feature8: "Sans pubs!"
     free: "Gratuit"
     month: "mois"
     must_be_logged: "Vous devez être identifié. Veuillez créer un compte ou vous identifier depuis le menu ci-dessus."
@@ -725,16 +725,16 @@
     editor_config_behaviors_description: "Ferme automatiquement les accolades, parenthèses, et chaînes de caractères."

   about:
-#    main_title: "If you want to learn to program, you need to write (a lot of) code."
-#    main_description: "At CodeCombat, our job is to make sure you're doing that with a smile on your face."
-#    mission_link: "Mission"
-#    team_link: "Team"
-#    story_link: "Story"
-#    press_link: "Press"
-#    mission_title: "Our mission: make programming accessible to every student on Earth."
-#    mission_description_1: "Programming is magic. It's the ability to create things from pure imagination. We started CodeCombat to give learners the feeling of wizardly power at their fingertips by using typed code."
+    main_title: "Si tu veux apprendre la programmation, tu dois écrire beaucoup de code."
+    main_description: "Chez CodeCombat, notre but est d'assurer que tu le fasses avec un sourire."
+    mission_link: "Mission"
+    team_link: "Equipe"
+    story_link: "Histoire"
+    press_link: "Presse"
+    mission_title: "Notre mission: faire en sorte que la programmation soit accessible à chaque élève sur la Terre."
+#    mission_description_1: "Programming is magic.. It's the ability to create things from pure imagination. We started CodeCombat to give learners the feeling of wizardly power at their fingertips by using typed code."
 #    mission_description_2: "As it turns out, that enables them to learn faster too. WAY faster. It's like having a conversation instead of reading a manual. We want to bring that conversation to every school and to every student, because everyone should have the chance to learn the magic of programming."
-#    team_title: "Meet the CodeCombat team"
+    team_title: "Rencontrez l'équipe CodeCombat."
 #    team_values: "We value open and respectful dialog, where the best idea wins. Our decisions are grounded in customer research and our process is focused on delivering tangible results for them. Everyone is hands-on, from our CEO to our Github contributors, because we value growth and learning in our team."
     nick_title: "Programmeur" # {change}
     nick_blurb: "Gourou de Motivation"
@@ -975,7 +975,7 @@
     social_facebook: "Aimer CodeCombat sur Facebook"
     social_twitter: "Suivre CodeCombat sur Twitter"
     social_gplus: "Rejoindre CodeCombat sur Google+"
-#    social_slack: "Chat with us in the public CodeCombat Slack channel"
+    social_slack: "Bavardez avec nous sur la chaîne publique Slack de CodeCombat."
     contribute_to_the_project: "Contribuer au projet"

   clans:
@@ -1027,15 +1027,15 @@
 #    track_concepts1: "Track concepts"
     track_concepts2a: "appris par chaque élèves"
     track_concepts2b: "appris par chaque membres"
-    track_concepts3a: "Suivre les niveaux complétés par chaque élèves"
-    track_concepts3b: "Suivre les niveaux complétés par chaque membres"
-#    track_concepts4a: "See your students'"
-#    track_concepts4b: "See your members'"
-    track_concepts5: "solutions"
+    track_concepts3a: "Suivre les niveaux complétés par chaque élève"
+    track_concepts3b: "Suivre les niveaux complétés par chaque membre"
+    track_concepts4a: "Voir vos élèves"
+    track_concepts4b: "Voir vos membres'"
+    track_concepts5: "Solutions"
     track_concepts6a: "Classer les élèves par nom ou avancement"
     track_concepts6b: "Classer les membres par nom ou avancement"
     track_concepts7: "Nécessite une invitation"
-#    track_concepts8: "to join"
+    track_concepts8: "Joindre"
     private_require_sub: "Les clans privés nécessitent un abonnement pour être créés ou rejoins."

   courses:
@@ -1089,7 +1089,7 @@
     purchasing_for: "Vous achetez une licence pour"
     creating_for: "Vous créez une classe pour"
     for: "pour" # Like in 'for 30 students'
-#    receive_code: "Afterwards you will receive an unlock code to distribute to your students, which they can use to enroll in your class."
+    receive_code: "Après, vous recevrez un code à distribuer à vos élèves, pour qu'ils puissent s'inscrir dans votre cours."
     free_trial: "Essai gratuit pour les professeurs !"
     get_access: "pour obtenir un accès individuel à tous les cours pour évaluation."
     questions: "Questions?"
@@ -1101,8 +1101,8 @@
     no_experience: "Aucune expérience en développement requise"
     easy_monitor: "Suivez facilement la progression des élèves"
     purchase_for_class: "Achetez un cours pour votre classe. C'est très simple de rajouter vos élèves !"
-#    see_the: "See the"
-    more_info: "pour plus d'informations."
+    see_the: "Voir"
+    more_info: "Pour plus d'informations."
     choose_course: "Choisissez votre cours:"
     enter_code: "Entrez un code de déverouillage pour rejoindre une classe existante"
     enter_code1: "Entrez le code de déverouillage"
@@ -1125,7 +1125,7 @@
     change_language: "Changez la langue du cours"
     keep_using: "Continuer à utiliser"
     switch_to: "Changer"
-#    greetings: "Greetings!"
+    greetings: "Salutations!"
     back_classrooms: "Retour à mes classes"
     back_courses: "Retour à mes cours"
     edit_details: "Modifier les informations de la classe"
@@ -1162,7 +1162,7 @@
     play_now_learn_3: "des chaines de caractères & des variables pour personnaliser des actions"
     play_now_learn_4: "comment battre un ogre (compétence importante dans la vie !)"
     welcome_to_page: "Bienvenu sur la page des Cours !"
-#    completed_hoc: "Amazing! You've completed the Hour of Code course!"
+    completed_hoc: "Génial! Tu as fini le cours Heur de Codage!"
     ready_for_more_header: "Motivé pour plus ? Jouez au mode campagne !"
     ready_for_more_1: "Utilisez des gemmes pour débloquer de nouveaux acessoires !"
     ready_for_more_2: "Jouez à travers de nouveaux mondes et challenges"
@@ -1182,7 +1182,7 @@
     play_arena: "Jouer à l'Arene"
     start: "Démarrer"
     last_level: "Dernier Niveau"
-#    welcome_to_hoc: "Adventurers, welcome to our Hour of Code!"
+    welcome_to_hoc: "Aventuriers, bienvenu à note heur de codage!"
     logged_in_as: "Connecté en tant que :"
     not_you: "Pas vous ?"
     welcome_back: "Salut aventurier, content de te revoir !"
@@ -1259,17 +1259,17 @@
     add_students1: "ajouter des élèves"
     view_edit: "voir/modifier"
     students_enrolled: "élèves inscrits"
-#    students_assigned: "students assigned"
+    students_assigned: "élèves attribués"
     length: "Durée:"
     title: "Titre" # Flat style redesign
 #    subtitle: "Review course guidelines, solutions, and levels"
 #    changelog: "View latest changes to course levels."
-#    select_language: "Select language"
-#    select_level: "Select level"
+    select_language: "Selectionner langue"
+    select_level: "Selectionner niveau"
 #    play_level: "Play Level"
     concepts_covered: "Conceptes Couverts"
-#    print_guide: "Print Guide (PDF)"
-#    view_guide_online: "View Guide Online (PDF)"
+    print_guide: "Imprimer Guide (PDF)"
+    view_guide_online: "Voir Guide En Ligne (PDF)"
 #    last_updated: "Last updated:"
 #    grants_lifetime_access: "Grants access to all Courses."
 #    enrollment_credits_available: "Enrollment Credits Available:"
@@ -1311,15 +1311,15 @@
 #    what_is_a_teacher_account: "What's a Teacher Account?"
 #    teacher_account_explanation: "A CodeCombat Teacher account allows you to set up classrooms, monitor students’ progress as they work through courses, manage enrollments and access resources to aid in your curriculum-building."
 #    current_classes: "Current Classes"
-#    archived_classes: "Archived Classes"
+    archived_classes: "Cours Archivés"
 #    archived_classes_blurb: "Classes can be archived for future reference. Unarchive a class to view it in the Current Classes list again."
-#    view_class: "view class"
-#    archive_class: "archive class"
-#    unarchive_class: "unarchive class"
-#    unarchive_this_class: "Unarchive this class"
-#    no_students_yet: "This class has no students yet."
-#    add_students: "Add Students"
-#    create_new_class: "Create a New Class"
+    view_class: "Voir Cours"
+    archive_class: "Archiver Cours"
+    unarchive_class: "Désarchiver cour"
+    unarchive_this_class: "Désarchiver ce cours"
+    no_students_yet: "Ce cours n'a pas encore d'élèves."
+    add_students: "Ajourter Elèves"
+    create_new_class: "Créer une Nouveau Cour"
 #    class_overview: "Class Overview" # View Class page
 #    avg_playtime: "Average level playtime"
 #    total_playtime: "Total play time"
@@ -1398,7 +1398,7 @@
     ambassador_title: "Ambassadeur"
     ambassador_title_description: "(Aide)"
     ambassador_summary: "Domptez les membres du forum, et guidez ceux qui ont besoin d'aide. Nos ambassadeurs représentent CodeCombat face au monde."
-#    teacher_title: "Teacher"
+    teacher_title: "Professeur"

   editor:
     main_title: "Éditeurs CodeCombat"


Uncomment an header from fr.coffee

Uncomment an header from fr.coffee

--- app/locale/fr.coffee
+++ app/locale/fr.coffee
@@ -1294,7 +1294,7 @@
 #    class_name: "Class Name"
 #    teacher_account_restricted: "Your account is a teacher account, and so cannot access student content."

-#  teacher:
+  teacher:
 #    teacher_dashboard: "Teacher Dashboard" # Navbar
 #    my_classes: "My Classes"
 #    courses: "Courses"


Add translations (#3650)

Add translations (#3650)

--- app/locale/uk.coffee
+++ app/locale/uk.coffee
@@ -42,7 +42,7 @@
 #    wanted_to_do: " Coding is something I've always wanted to do, and I never thought I would be able to learn it in school."
 #    why_games: "Why is learning through games important?"
 #    games_reward: "Games reward the productive struggle."
-#    encourage: "Gaming is a medium that encourages interaction, discovery, and trial-and-error. A good game challenges the player to master skills over time, which is the same critical process students go through as they learn."
+    encourage: "Гра заохочує спілкуватись, відкривати щось нове і вчитись на помилках. Хороша гра змушує гравця весь час покращувати власні навички, що подібно до процесу роботи учнів під час навчання." # Gaming is a medium that encourages interaction, discovery, and trial-and-error. A good game challenges the player to master skills over time, which is the same critical process students go through as they learn.
 #    excel: "Games excel at rewarding"
 #    struggle: "productive struggle"
 #    kind_of_struggle: "the kind of struggle that results in learning that’s engaging and"


Send requests to pivotal in batches

Send requests to pivotal in batches

--- scripts/pivotal-story-tagger.coffee
+++ scripts/pivotal-story-tagger.coffee
@@ -13,63 +13,97 @@
 # URLS:
 #   None

+request = require 'request-promise'
+PIVOTAL_ENDPOINT = "https://www.pivotaltracker.com/services/v5/projects/"
+TOKEN = process.env.PIVOTAL_TRACKER_API_TOKEN
+resultsPerPage = 500
+
+getPage = (page, projectId) =>
+  request(
+    url: "#{PIVOTAL_ENDPOINT}#{projectId}/stories?limit=#{resultsPerPage}&offset=#{resultsPerPage * page}",
+    headers: 'X-TrackerToken': TOKEN)
+
+sortStories = (stories) =>
+  stories.sort (a, b) ->
+    return 0 if a['id'] == b['id']
+    if a['id'] < b['id']
+      return -1
+    else
+      return 1
+
+wait = (delay) =>
+  return new Promise((resolve, reject) =>
+    console.log('Waiting...', delay)
+    setTimeout(resolve, delay)
+  )
+
+parseJson = (responses) =>
+  stories = []
+  for response in responses
+    stories.push(JSON.parse(response)...)
+  return stories
+
+generateNewStoryNames = (project, stories) =>
+  STORY_PREFIX = project.projectPrefix
+  sendToPivotal = []
+  taggedStoryCount = 0
+  maxStoryId = 0
+  for story in stories
+    storyId = story["id"]
+    storyName = story["name"]
+    pattern = "^#{STORY_PREFIX}(\\d+)"
+    regex = new RegExp(pattern)
+    currentStoryId = null
+    friendlyStoryId = storyName.match(regex)
+    if friendlyStoryId
+      currentStoryId = parseInt(friendlyStoryId[1])
+    if currentStoryId
+      maxStoryId = currentStoryId if currentStoryId > maxStoryId
+      console.log "Skipping story with existing ID: #{storyName}"
+      continue
+
+    taggedStoryCount += 1
+    maxStoryId += 1
+    newStoryName = "#{STORY_PREFIX}#{maxStoryId} - #{storyName}"
+    data = { id: storyId, name: newStoryName }
+    sendToPivotal.push(data)
+  return sendToPivotal
+
 module.exports = (robot) ->
   robot.on 'story_create_activity', (project) ->
     PROJECT_ID = project.projectId
-    STORY_PREFIX = project.storyPrefix
-    TOKEN = process.env.PIVOTAL_TRACKER_API_TOKEN
-    PIVOTAL_ENDPOINT = "https://www.pivotaltracker.com/services/v5/projects/"
-    maxStoryId = 0
-    taggedStoryCount = 0
+    requests = [0..2].map (n) -> getPage(n, PROJECT_ID)
+    robot.logger.info "Requesting pivotal stories..."
+    Promise.all(requests)
+    .then(parseJson)
+    .then(sortStories)
+    .then (stories) =>
+      sendToPivotal = generateNewStoryNames(project, stories)
+      batchSize = 5

-    robot.logger.info "Requesting all pivotal stories..."
-    robot
-      .http("#{PIVOTAL_ENDPOINT}#{PROJECT_ID}/stories?limit=20000")
-      .header('X-TrackerToken', TOKEN)
-      .get() (err, res, body) ->
-        if err
-          robot.logger.error "Error making request to pivotal for project: #{PROJECT_ID}"
-          robot.logger.error "Error: #{err}"
-          return
-        if body?
-          stories = JSON.parse(body)
-          stories.sort (a, b) ->
-            return 0 if a['id'] == b['id']
-            if a['id'] < b['id']
-              return -1
-            else
-              return 1
+      sendNextBatch = () =>
+        console.log("Sending next batch:", sendToPivotal.length)
+        if sendToPivotal.length == 0
+          return Promise.resolve()
+        batch = sendToPivotal.splice(0, batchSize)
+        return Promise.all(batch.map(updateStory))
+          .then(() =>
+            return wait(1000)
+          ).then(sendNextBatch)

-          for story in stories
-            robot.logger.info "#{story.name}"
-            storyId = story["id"]
-            storyName = story["name"]
-            pattern = "^#{STORY_PREFIX}(\\d+)"
-            regex = new RegExp(pattern)
-            currentStoryId = null
-            friendlyStoryId = storyName.match(regex)
-            if friendlyStoryId
-              currentStoryId = parseInt(friendlyStoryId[1])
-            if currentStoryId
-              maxStoryId = currentStoryId if currentStoryId > maxStoryId
-              robot.logger.info "Skipping story with existing ID: #{storyName}"
-              continue
+      updateStory = (story) =>
+        endpoint = "#{PIVOTAL_ENDPOINT}#{PROJECT_ID}/stories/#{story.id}"
+        console.log("Sending story: ", story)
+        delete story.id
+        return request(
+          method: 'PUT',
+          url: endpoint,
+          body: JSON.stringify(story),
+          headers:
+            'X-TrackerToken': TOKEN,
+            'Content-Type': 'application/json')

-            taggedStoryCount += 1
-            maxStoryId += 1
-
-            newStoryName = "#{STORY_PREFIX}#{maxStoryId} - #{storyName}"
-            endpoint = "#{PIVOTAL_ENDPOINT}#{PROJECT_ID}/stories/#{storyId}"
-            data = JSON.stringify({name: newStoryName})
-
-            robot.http(endpoint)
-                 .header('X-TrackerToken', TOKEN)
-                 .header('Content-Type', 'application/json')
-                 .put(data) (err, res, body) ->
-                   if err
-                     robot.logger.error "Error updating name: #{err}"
-                     return
-                   if res.statusCode >= 200 && res.statusCode < 300
-                     robot.logger.info "Adding new ID to story: #{newStoryName}"
-
-          robot.logger.info "Tagged #{taggedStoryCount} new stories out of #{stories.length} total."
+      return sendNextBatch()
+        .catch (err) =>
+          console.error(err)
+          process.exit()


Uses the success handler to handle cancel.

--- lib/project/results-model.coffee
+++ lib/project/results-model.coffee
@@ -73,11 +73,12 @@
       @setResult(result.filePath, Result.create(result))

     @emit('search', @inProgressSearchPromise)
-    @inProgressSearchPromise.then =>
-      @inProgressSearchPromise = null
-      @emit('finished-searching', @getResultsSummary())
-    , (reason) =>
-      @emit('cancelled-searching') if reason == 'cancelled'
+    @inProgressSearchPromise.then (message) =>
+      if message == 'cancelled'
+        @emit('cancelled-searching')
+      else
+        @inProgressSearchPromise = null
+        @emit('finished-searching', @getResultsSummary())

   replace: (pattern, searchPaths, replacementPattern, replacementPaths) ->
     regex = @getRegex(pattern)


Save a pattern in the results model. Fixes #65

--- lib/project/results-model.coffee
+++ lib/project/results-model.coffee
@@ -30,6 +30,7 @@
     @clear()
     @active = true
     @regex = @getRegex(pattern)
+    @pattern = pattern

     onPathsSearched = (numberOfPathsSearched) =>
       @emit('paths-searched', numberOfPathsSearched)
@@ -56,7 +57,7 @@
     @matchCount

   getPattern: ->
-    if @regex? then @regex.source else ''
+    @pattern or ''

   getPaths: (filePath) ->
     @paths


remove dead code

remove dead code

--- web/app/source/nvwrapper.coffee
+++ web/app/source/nvwrapper.coffee
@@ -3,23 +3,7 @@
 require 'nvd3/build/nv.d3.js'

-duration = 1000
-
-
 parseDateStr = d3.time.format.utc('%Y-%m-%dT%H:%M:%SZ').parse
-
-
-redraw = (target, chart, data) ->
-  d3.select(target).datum(data).call chart
-
-
-getNvChart = (name) ->
-  chart = nv.models[name]()
-  chart = chart.useInteractiveGuideline?(true) ? chart
-  chart.xAxis.axisLabel('Time').tickFormat (d) -> d3.time.format('%H:%M') new Date(d)
-  chart.yAxis.axisLabel('Temp (C)').tickFormat d3.format('.1f')
-  nv.utils.windowResize(chart.update)
-  return chart

 getColor = (name) ->
@@ -67,10 +51,12 @@
       new ChartDrawer target, chart

-
-
 ChartDrawer = class ChartDrawer

+
+  redraw: (data) ->
+    d3.select(@target).datum(data).call @chart
+
   withinCapacity: (n) -> n

   __TS_FIELDS: do ((n) -> ('field' + n) for n in [1..8])
@@ -90,7 +76,7 @@
     last = field1.values[field1.values.length-1]
     if last isnt @oldlast # if updated
       @oldlast = last
-      redraw @target, @chart, newdata
+      @redraw newdata

 BarChartDrawer = class BarChartDrawer extends ChartDrawer
@@ -108,7 +94,7 @@
   drawChart: (data) ->
     newdata = @__structure(data)
     console.log newdata
-    redraw @target, @chart, newdata
+    @redraw newdata

 EggChartDrawer = class EggChartDrawer


Cleanup

Cleanup

--- server/AbstractSocketFactory.coffee
+++ server/AbstractSocketFactory.coffee
@@ -23,10 +23,10 @@
 				spec
 		)

-		winston.info 'END loading socket middleware:'
+		winston.info 'END loading socket middleware.'

 	for method in [
-		'emitToChannel', 'listen'
+		'listen', 'socketsInChannel'
 	]
 		@::[method] = -> throw new ReferenceError(
 			"AbstractSocket#{method} is a pure virtual method!"


Updated ss4 controller init

Updated ss4 controller init

--- completions.cson
+++ completions.cson
@@ -296,9 +296,9 @@
    */
   class ${1}Controller extends ${2:Page}Controller
   {
-  \tpublic function init()
+  \tpublic function doInit()
   \t{
-  \t\tparent::init();
+  \t\tparent::doInit();
   \t}
   }
   """


Add comments

Add comments

--- bin/heyu-run.coffee
+++ bin/heyu-run.coffee
@@ -6,6 +6,7 @@
 load('lib/statemachine.js')
 load('lib/timedevent.js')

+# File names to store state machine and events
 state_db_fn = 'heyu-run.db'
 event_db_fn = 'heyu-events.db'

@@ -51,11 +52,16 @@
     parse_opts(set,args2) if args2.length > 0
     set

-# ---- Main program
-args = clone(@arguments)  # don't need to do this, just for fun
-set = parse_opts({},args)
+# ---- Main program, parse command line
+set = parse_opts({},@arguments)
+
+# ---- Fetch state machine
 appliances = read_json(state_db_fn)

+# ---- Fetch timed events and update state
+# FIXME
+
+# ---- Update state machine from command line
 if set?.id? and set.id
   if set.display_state
     print "# Display current state of",set.id
@@ -70,6 +76,8 @@
       appl2 = new HeyuAppliance(set.id, states: ['OFF', 'ON'])
       appl2.changeState(appl2.currentState(),set.event)
       appliances[appl2.name] = appl2
+
+# ---- Write state machine to file
 state_changed = false
 for name,appl of appliances
   state_changed = true if appl.changed


change the ref in the grouper

change the ref in the grouper

--- library/semantic/formular.coffee
+++ library/semantic/formular.coffee
@@ -64,8 +64,8 @@
     groupTwoFields: (fieldOne, fieldTwo) ->
         selectorOne = fieldOne and _.isString fieldOne
         selectorTwo = fieldTwo and _.isString fieldTwo
-        fieldOne += ".field".toString() if selectorOne
-        fieldTwo += ".field".toString() if selectorTwo
+        fieldOne = ".field.#{fieldOne}" if selectorOne
+        fieldTwo = ".field.#{fieldTwo}" if selectorTwo
         fieldOne = @container.find fieldOne if selectorOne
         fieldTwo = @container.find fieldTwo if selectorTwo
         assert _.isObject(fieldOne), "invalid object A given"


don't need this anymore

don't need this anymore

--- library/semantic/formular.coffee
+++ library/semantic/formular.coffee
@@ -53,8 +53,7 @@
         @container = $ "<div>", class: "ui form segment"
         @container.append @errors, @warnings # invisible
         @container.appendTo(hosting); scoped @container
-        this.emit "configure-form", @container, @payload
-        @hosting = hosting; @reference = reference; this
+        @hosting = hosting; @reference = reference; @

     # Group the two previously created fields (passed by either as
     # direct object or the selectors) into a one horizontal field


Updated test to use require API

Updated test to use require API

--- test/node/exportable1.coffee
+++ test/node/exportable1.coffee
@@ -4,12 +4,12 @@
 describe 'udefine commonjs exportable', ->
   it 'module.exports', ->

-    exportResult = udefine 'export1', ->
+    udefine 'export1', ->
       a: 5
       b: 4
       c: 3

-    module.exports = exportResult
+    udefine.require 'export1', (export1) -> module.exports = export1

     expect(module.exports).to.be.a('object')
     expect(module.exports.a).to.be.a('number', 'Property "a" is not a number')


Improving soaks to avoid uncessesary parentheses.

--- nodes.coffee
+++ nodes.coffee
@@ -63,6 +63,7 @@
 Node::compile: (o) ->
   @options: merge o or {}
   @indent:  o.indent
+  del @options, 'operation' unless @operation_sensitive()
   top:      if @top_sensitive() then @options.top else del @options, 'top'
   closure:  @is_statement() and not @is_statement_only() and not top and
             not @options.returns and not (this instanceof CommentNode) and
@@ -95,11 +96,12 @@
   '\n' + idt + @type + (child.toString(idt + TAB) for child in @children).join('')

 # Default implementations of the common node methods.
-Node::unwrap:             -> this
-Node::children:           []
-Node::is_statement:       -> false
-Node::is_statement_only:  -> false
-Node::top_sensitive:      -> false
+Node::unwrap:               -> this
+Node::children:             []
+Node::is_statement:         -> false
+Node::is_statement_only:    -> false
+Node::top_sensitive:        -> false
+Node::operation_sensitive:  -> false

 # A collection of nodes, each one representing an expression.
 Expressions: exports.Expressions: inherit Node, {
@@ -239,6 +241,9 @@
     @children.push(prop)
     this

+  operation_sensitive: ->
+    true
+
   has_properties: ->
     !!@properties.length

@@ -264,6 +269,7 @@
   compile_node: (o) ->
     soaked:   false
     only:     del(o, 'only_first')
+    op:       del(o, 'operation')
     props:    if only then @properties[0...@properties.length - 1] else @properties
     baseline: @base.compile o
     baseline: '(' + baseline + ')' if @base instanceof ObjectNode and @has_properties()
@@ -285,8 +291,7 @@
     @last: parts[parts.length - 1]
     @source: if parts.length > 1 then parts[0...(parts.length - 1)].join('') else null
     code: parts.join('').replace(/\)\(\)\)/, '()))')
-    return code unless soaked
-    '(' + code + ')'
+    if op and soaked then '(' + code + ')' else code

 }

@@ -757,6 +762,7 @@
     @CHAINABLE.indexOf(@operator) >= 0

   compile_node: (o) ->
+    o.operation: true
     return @compile_chain(o)      if @is_chainable() and @first.unwrap() instanceof OpNode and @first.unwrap().is_chainable()
     return @compile_assignment(o) if @ASSIGNMENT.indexOf(@operator) >= 0
     return @compile_unary(o)      if @is_unary()


Revert "Passing the arguments object directly when generating a closure."

This reverts commit 7b887f065d8c865a4e7f0eca7fd9f417f3c4f107.

--- nodes.coffee
+++ nodes.coffee
@@ -1480,13 +1480,14 @@
   # then make sure that the closure wrapper preserves the original values.
   wrap: (expressions, statement, noReturn) ->
     return expressions if expressions.containsPureStatement()
-    mentionsArgs = expressions.contains @literalArgs
-    func = new Parens new Code (if mentionsArgs then [new Literal 'arguments'] else []), Expressions.wrap [expressions]
+    func = new Parens new Code [], Expressions.wrap [expressions]
     args = []
-    if mentionsArgs or expressions.contains @literalThis
+    if (mentionsArgs = expressions.contains @literalArgs) or
+       (               expressions.contains @literalThis)
+      meth = new Literal if mentionsArgs then 'apply' else 'call'
       args = [new Literal 'this']
       args.push new Literal 'arguments' if mentionsArgs
-      func = new Value func, [new Accessor new Literal 'call']
+      func = new Value func, [new Accessor meth]
       func.noReturn = noReturn
     call = new Call func, args
     if statement then Expressions.wrap [call] else call


adding test for .getProperties({some_map})

--- test/spec/models/node.coffee
+++ test/spec/models/node.coffee
@@ -51,6 +51,10 @@
             it "should accept parameters [property, value], and set @'s property to that value", ->
                 testNode.setProperty("caption", "ChemistryPy")
                 testNode.getProperties("caption").should.equal "ChemistryPy"
+            it "should accept parameters {property1: value1, property2: value2}, and set multiple properties", ->
+                testNode.setProperty({"foo": "bar", "this": "that"})
+                testNode.getProperties("foo").should.equal "bar"
+                testNode.getProperties("this").should.equal "that"

         describe "removeProperty", ->
             it "should accept parameter [property] and remove said property/properties if exist", ->


system limits: skip tests if os isn’t linux

--- test/core/system_limits.coffee
+++ test/core/system_limits.coffee
@@ -1,4 +1,5 @@

+os = require 'os'
 mecano = require '../../src'
 they = require 'ssh2-they'
 test = require '../test'
@@ -9,126 +10,132 @@
   scratch = test.scratch @

   they 'do nothing without any limits', (ssh, next) ->
+    mecano
+      ssh: ssh
+    .system_limits
+      destination: "#{scratch}/me.conf"
+      user: 'me'
+    , (err, status) ->
+      return next err if err
+      status.should.be.false()
+      fs.exists ssh, "#{scratch}/me.conf", (err, exists) ->
+        exists.should.be.false() unless err
+        next err
+
+  they 'nofile and noproc accept int', (ssh, next) ->
+    return next() unless os.platform() is 'linux'
+    mecano
+      ssh: ssh
+    .system_limits
+      destination: "#{scratch}/me.conf"
+      user: 'me'
+      nofile: 2048
+      nproc: 2048
+    , (err, status) ->
+      return next err if err
+      status.should.be.true()
+      fs.readFile ssh, "#{scratch}/me.conf", 'ascii', (err, content) ->
+        content.should.eql """
+        me    -    nofile   2048
+        me    -    nproc   2048
+
+        """ unless err
+        next err
+
+  they 'detect changes', (ssh, next) ->
+    return next() unless os.platform() is 'linux'
+    mecano
+      ssh: ssh
+    .system_limits
+      destination: "#{scratch}/me.conf"
+      user: 'me'
+      nofile: 2048
+      nproc: 2048
+      shy: true
+    .system_limits
+      destination: "#{scratch}/me.conf"
+      user: 'me'
+      nofile: 2047
+      nproc: 2047
+    .then (err, status) ->
+      status.should.be.true() unless err
+      next err
+
+  they 'detect no change', (ssh, next) ->
+    return next() unless os.platform() is 'linux'
+    mecano
+      ssh: ssh
+    .system_limits
+      destination: "#{scratch}/me.conf"
+      user: 'me'
+      nofile: 2048
+      nproc: 2048
+      shy: true
+    .system_limits
+      destination: "#{scratch}/me.conf"
+      user: 'me'
+      nofile: 2048
+      nproc: 2048
+    .then (err, status) ->
+      status.should.be.false() unless err
+      next err
+
+  they 'nofile and noproc default to 75% of kernel limits', (ssh, next) ->
+    return next() unless os.platform() is 'linux'
+    nproc = null
+    nofile = null
+    fs.exists ssh, '/proc/sys/fs/file-max', (err, exists) ->
+      return next() unless exists # Not linux
       mecano
         ssh: ssh
+      .execute
+        cmd: 'cat /proc/sys/fs/file-max'
+      , (err, status, stdout) ->
+        return next err if err
+        nofile = stdout.trim()
+        nofile = Math.round parseInt(nofile)*0.75
+      .execute
+        cmd: 'cat /proc/sys/kernel/pid_max'
+      , (err, status, stdout) ->
+        return next err if err
+        nproc = stdout.trim()
+        nproc = Math.round parseInt(nproc)*0.75
       .system_limits
         destination: "#{scratch}/me.conf"
         user: 'me'
-      , (err, status) ->
-        return next err if err
-        status.should.be.false()
-        fs.exists ssh, "#{scratch}/me.conf", (err, exists) ->
-          exists.should.be.false() unless err
-          next err
-
-  they 'nofile and noproc accept int', (ssh, next) ->
-      mecano
-        ssh: ssh
-      .system_limits
-        destination: "#{scratch}/me.conf"
-        user: 'me'
-        nofile: 2048
-        nproc: 2048
+        nofile: true
+        nproc: true
       , (err, status) ->
         return next err if err
         status.should.be.true()
         fs.readFile ssh, "#{scratch}/me.conf", 'ascii', (err, content) ->
           content.should.eql """
-          me    -    nofile   2048
-          me    -    nproc   2048
+          me    -    nofile   #{nofile}
+          me    -    nproc   #{nproc}

           """ unless err
           next err

-  they 'detect changes', (ssh, next) ->
-      mecano
-        ssh: ssh
-      .system_limits
-        destination: "#{scratch}/me.conf"
-        user: 'me'
-        nofile: 2048
-        nproc: 2048
-        shy: true
-      .system_limits
-        destination: "#{scratch}/me.conf"
-        user: 'me'
-        nofile: 2047
-        nproc: 2047
-      .then (err, status) ->
-        status.should.be.true() unless err
-        next err
-
-  they 'detect no change', (ssh, next) ->
-      mecano
-        ssh: ssh
-      .system_limits
-        destination: "#{scratch}/me.conf"
-        user: 'me'
-        nofile: 2048
-        nproc: 2048
-        shy: true
-      .system_limits
-        destination: "#{scratch}/me.conf"
-        user: 'me'
-        nofile: 2048
-        nproc: 2048
-      .then (err, status) ->
-        status.should.be.false() unless err
-        next err
-
-    they 'nofile and noproc default to 75% of kernel limits', (ssh, next) ->
-      nproc = null
-      nofile = null
-      fs.exists ssh, '/proc/sys/fs/file-max', (err, exists) ->
-        return next() unless exists # Not linux
-        mecano
-          ssh: ssh
-        .execute
-          cmd: 'cat /proc/sys/fs/file-max'
-        , (err, status, stdout) ->
-          return next err if err
-          nofile = stdout.trim()
-          nofile = Math.round parseInt(nofile)*0.75
-        .execute
-          cmd: 'cat /proc/sys/kernel/pid_max'
-        , (err, status, stdout) ->
-          return next err if err
-          nproc = stdout.trim()
-          nproc = Math.round parseInt(nproc)*0.75
-        .system_limits
-          destination: "#{scratch}/me.conf"
-          user: 'me'
-          nofile: true
-          nproc: true
-        , (err, status) ->
-          return next err if err
-          status.should.be.true()
-          fs.readFile ssh, "#{scratch}/me.conf", 'ascii', (err, content) ->
-            content.should.eql """
-            me    -    nofile   #{nofile}
-            me    -    nproc   #{nproc}
-
-            """ unless err
-            next err
-
   they 'raise an error if nofile is too high', (ssh, next) ->
-      mecano
-        ssh: ssh
-      .system_limits
-        destination: "#{scratch}/me.conf"
-        user: 'me'
-        nofile: 1000000000
-      , (err, status) ->
-        err.message.should.match /^Invalid nofile options.*$/
-        next()
+    return next() unless os.platform() is 'linux'
+    mecano
+      ssh: ssh
+    .system_limits
+      destination: "#{scratch}/me.conf"
+      user: 'me'
+      nofile: 1000000000
+    , (err, status) ->
+      err.message.should.match /^Invalid nofile options.*$/
+      next()

   they 'raise an error if nproc is too high', (ssh, next) ->
-      mecano
-        ssh: ssh
-      .system_limits
-        destination: "#{scratch}/me.conf"
-        user: 'me'
-        nproc: 1000000000
-      , (err, status) ->
-        err.message.should.match /^Invalid nproc options.*$/
-        next()
+    return next() unless os.platform() is 'linux'
+    mecano
+      ssh: ssh
+    .system_limits
+      destination: "#{scratch}/me.conf"
+      user: 'me'
+      nproc: 1000000000
+    , (err, status) ->
+      err.message.should.match /^Invalid nproc options.*$/
+      next()


add try/catch for json parse

add try/catch for json parse

--- poetry-chains-java/index.coffee
+++ poetry-chains-java/index.coffee
@@ -24,7 +24,11 @@
       reject(error) if (error)
       stdout = replaceEmDash stdout
       # console.log stdout
-      json = JSON.parse(stdout)
+      try
+        json = JSON.parse(stdout)
+      catch e
+        console.error e
+
       resolve json
     )


Redirect user to `/oauth/error/404` instead of `/oauth/404`

--- oauth.coffee
+++ oauth.coffee
@@ -49,7 +49,7 @@

 		@app.get '/oauth/authorize', debugMiddleware, Meteor.bindEnvironment (req, res, next) ->
 			if not self.model.Clients.findOne({ active: true, clientId: req.query.client_id })?
-				return res.redirect '/oauth/404'
+				return res.redirect '/oauth/error/404'

 			next()


Spec Processor->Reporter continuation in Client

--- test/unit/client_test.coffee
+++ test/unit/client_test.coffee
@@ -107,7 +107,7 @@

   describe "captureException", ->
     processor = { process: sinon.spy() }
-    reporter = { report: -> }
+    reporter = { report: sinon.spy() }
     getProcessor = -> processor
     getReporter = -> reporter

@@ -126,6 +126,22 @@

       expect(processor.process).to.have.been.called

+    it "reports with reporter", ->
+      client = new Client(getProcessor, getReporter)
+      client.captureException(exception)
+
+      # Reporter is not called until Processor invokes the
+      # callback provided
+      expect(reporter.report).not.to.have.been.called
+
+      # The first argument passed the processor is the error to be handled
+      # The second is the continuation handed off to the reporter
+      continueFromProcessor = processor.process.lastCall.args[1]
+      processed_error = sinon.spy()
+      continueFromProcessor(processed_error)
+
+      expect(reporter.report).to.have.been.calledWith(processed_error)
+
     it "ignores errors thrown by processor", ->
       processor = { process: -> throw(new Error("Processor Error")) }
       getProcessor = -> processor


add a todo

add a todo

--- tests/cache-warm.test.coffee
+++ tests/cache-warm.test.coffee
@@ -10,6 +10,7 @@
     .end (err, res) ->
       # File caching happens _after_ the response is sent so we need to delay
       # before checking on the presence of the files.
+      # TODO: Maybe there is a better way to wait for this to complete?
       delay 100, ->
         # Check that the body returned is correct
         res_json = res.body


Show contet menu item only if selection is made

--- js/background.coffee
+++ js/background.coffee
@@ -9,7 +9,7 @@
 #generates a context menu
 chrome.contextMenus.create(
   title:  'Multitran: "%s"'
-  contexts: ["page", "frame", "editable", "image", "video", "audio", "link", "selection"]
+  contexts: ["editable", "selection"]
   onclick:  (data) ->
     data.silent = false
     tran.click(data)


Add a description for each package

Add a description for each package

--- package.cson
+++ package.cson
@@ -1 +1,2 @@
 'main': './lib/whitespace'
+'description': 'Strip trailing whitespace and add a trailing newline when the current editor is saved.'


avoid redraw of the container for auto fitting

--- js/msa/zoomer.coffee
+++ js/msa/zoomer.coffee
@@ -6,6 +6,8 @@
       @maxLabelLength = 20
       @setZoomLevel 2
       @len = 0
+
+      @_setWidth()

     setZoomLevel: (value) ->
       @level = value
@@ -34,25 +36,30 @@
       stepSize = 20  if @columnWidth is 1
       stepSize

+    _setWidth: ->
+      # totally slow - draws the entire container
+      _rect = @msa.container.getBoundingClientRect()
+      @_width = _rect.right - _rect.left
+
+
     guessZoomLevel:(tSeqs) ->
-      width = @msa.container.offsetWidth
       @len = @getMaxLength tSeqs
       @maxLabelLength =  @getMaxLabelLength tSeqs

       level = 2

-      if @len > width
+      if @len > @_width
         # go to minzoom
         return 1
       else
         # increase as long as possible
         @setZoomLevel level
-        while @msa.stage.width(@len) < width or level is 200
+        while @msa.stage.width(@len) < @_width or level is 200
           level++
           @setZoomLevel level

         if level is 2
-          console.log "len: #{@len} - width: #{width}"
+          console.log "len: #{@len} - width: #{@_width}"
           console.log "stage: #{@msa.stage.width(@len)}"
           console.log "stage: #{@msa.container.id}"
         return level - 1


Update deprecated require

Update deprecated require

--- spec/default-file-icons-spec.coffee
+++ spec/default-file-icons-spec.coffee
@@ -1,6 +1,6 @@
 fs = require 'fs-plus'
 path = require 'path'
-process = require 'process'
+{process} = require 'electron'
 temp = require('temp').track()

 DefaultFileIcons = require '../lib/default-file-icons'


pagination xs-width fixed

pagination xs-width fixed

--- public/views/widget_view.coffee
+++ public/views/widget_view.coffee
@@ -13,7 +13,7 @@

-

+

 """


Fix test description

Fix test description

--- spec/javascripts/sample_spec.coffee
+++ spec/javascripts/sample_spec.coffee
@@ -1,3 +1,3 @@
 describe "A test suite", ->
-  it 'should fail', ->
+  it 'should pass', ->
     expect(true).toBe true


Test window.Store offline methods

Test window.Store offline methods

--- spec/localstorage_store_spec.coffee
+++ spec/localstorage_store_spec.coffee
@@ -1,19 +1,19 @@
 window = require('./spec_helper').window

 describe 'window.Store', ->
+  {store, model} = {}
+  beforeEach ->
+    window.localStorage.clear()
+    window.localStorage.setItem 'cats', '3'
+    window.localStorage.setItem 'cats3', '{"id": "3", "color": "burgundy"}'
+    store = new window.Store 'cats'
+
   describe 'creation', ->
     it 'takes a name in its constructor', ->
       store = new window.Store 'convenience store'
       expect(store.name).toBe 'convenience store'

   describe 'persistence', ->
-    {store, model} = {}
-    beforeEach ->
-      window.localStorage.clear()
-      window.localStorage.setItem 'cats', '3'
-      window.localStorage.setItem 'cats3', '{"id": "3", "color": "burgundy"}'
-      store = new window.Store 'cats'
-
     it 'fetches records by id with find', ->
       expect(store.find(id: 3)).toEqual id: '3', color: 'burgundy'

@@ -49,6 +49,29 @@
       expect(window.localStorage.getItem 'cats3').toBeUndefined()

   describe 'offline', ->
-    xit 'marks records dirty and clean', ->
-    xit 'marks records destroyed', ->
-    xit 'reports if it hasDirtyOrDestroyed records', ->
+    it 'on a clean slate, hasDirtyOrDestroyed returns false', ->
+      expect(store.hasDirtyOrDestroyed()).toBeFalsy()
+
+    it 'marks records dirty and clean, and reports if it hasDirtyOrDestroyed records', ->
+      store.dirty id: 3
+      expect(store.hasDirtyOrDestroyed()).toBeTruthy()
+      store.clean id: 3, 'dirty'
+      expect(store.hasDirtyOrDestroyed()).toBeFalsy()
+
+    it 'marks records destroyed and clean from destruction, and reports if it hasDirtyOrDestroyed records', ->
+      store.destroyed id: 3
+      expect(store.hasDirtyOrDestroyed()).toBeTruthy()
+      store.clean id: 3, 'destroyed'
+      expect(store.hasDirtyOrDestroyed()).toBeFalsy()
+
+    it 'cleans the list of dirty or destroyed models out of localStorage after saving or destroying', ->
+      collection = new window.Backbone.Collection [{id: 2, color: 'auburn'}, {id: 3, color: 'burgundy'}]
+      collection.url = 'cats'
+      store.dirty id: 2
+      store.destroyed id: 3
+      expect(store.hasDirtyOrDestroyed()).toBeTruthy()
+      collection.get(2).save()
+      collection.get(3).destroy()
+      expect(store.hasDirtyOrDestroyed()).toBeFalsy()
+      expect(window.localStorage.getItem('cats_dirty').length).toBe 0
+      expect(window.localStorage.getItem('cats_destroyed').length).toBe 0


Stop anchoring the Bad Good headers

Stop anchoring the Bad Good headers

--- scripts.coffee
+++ scripts.coffee
@@ -4,9 +4,7 @@
 $ ->
   currentMenuItem = null
   anchor_selector = ".markdown-body h3,
-    .markdown-body h4,
-    .markdown-body h5,
-    .markdown-body h6"
+    .markdown-body h4"
   contentHeaders = $(anchor_selector)
   if anchors
     anchors.options =


add isParent

add isParent

--- tutor/src/helpers/dom.coffee
+++ tutor/src/helpers/dom.coffee
@@ -6,6 +6,14 @@
     method = el.matches or el.mozMatchesSelector or el.msMatchesSelector or el.oMatchesSelector or el.webkitMatchesSelector
     method?.call(el, selector)

+  isParent: (parent, child, options = { matchSame: true }) ->
+    return false unless parent and child
+    return true if options.matchSame and parent is child
+    node = child.parentNode
+    while node
+      return true if node is parent
+      node = node.parentNode
+    return false

   closest: (el, selector) ->
     return null unless el


fix: adapt poincare-drawing code from Pentagrid app

--- hyperbolic_tessellation.coffee
+++ hyperbolic_tessellation.coffee
@@ -1,5 +1,7 @@
 {CenteredVonDyck} = require "./triangle_group_representation.coffee"
 M = require "./matrix3.coffee"
+
+len2 = (x,y) -> x*x + y*y

 exports.Tessellation = class Tessellation
   constructor: (n,m) ->
@@ -45,36 +47,66 @@
         context.moveTo xx0, yy0

       #@_drawCircularArc context, xx0, yy0, xx1, yy1, xc, yc
-      context.lineTo xx1, yy1
+      #context.lineTo xx1, yy1
+      @drawPoincareCircleTo context, xx0, yy0, xx1, yy1
+
       #else
       #  context.lineTo xx, yy
     context.closePath()

-  _drawCircularArc1: (context, x1, y1, x4, y4, xc, yc)->
-    a1 = Math.atan2(y1-yc, x1-xc)
-    a2 = Math.atan2(y4-yc, x4-xc)
-    r = Math.sqrt((y1-yc)**2 + (x1-xc)**2)
-    context.arc xc, yc, r, a1, a2
+  drawPoincareCircleTo: (context, x0, y0, x1, y1) ->
+    #Calculate radius of the circular arc.
+    sq_l0 = len2( x0, y0 )
+    sq_l1 = len2( x1, y1 )

-  _drawCircularArc: (context, x1, y1, x4, y4, xc, yc)->
-    #http://itc.ktu.lt/itc354/Riskus354.pdf
-    ax = x1 - xc
-    ay = y1 - yc
-    bx = x4 - xc
-    by_ = y4 - yc
-    q1 = ax**2 + ay ** 2
-    q2 = q1 + ax*bx + ay*by_
-    k2 = 4.0/3.0*(Math.sqrt(2*q1*q2)-q2) / (ax*by_ - ay*bx)
-
-    x2 = xc+x1 - k2*y1
-    y2 = yc+y1 + k2*x1
-
-    x3 = xc+x4 - k2*y4
-    y3 = yc + y4 + k2*x4
-
-    context.bezierCurveTo( x2, y2, x3, y3, x4, y4 )
+    k0 = (1+1/sq_l0) * 0.5
+    k1 = (1+1/sq_l1) * 0.5

-
+    delta2 = len2( x0*k0 - x1*k1, y0*k0 - y1*k1 )
+
+    if delta2 < 1e-4 # 0.01^2 lenght of a path too small, create straight line instead to make it faster.
+      context.lineTo( x1, y1 )
+      return
+
+    cross = (x0*y1 - x1*y0)
+    r2 = ( sq_l0 * sq_l1 * delta2 ) / (cross*cross) - 1
+
+    r = - Math.sqrt( r2 )
+    if cross < 0
+      r = -r
+
+    if Math.abs(r) < 100
+      @drawBezierApproxArcTo( context, x0, y0, x1, y1, r, r<0 )
+    else
+      context.lineTo x1, y1
+
+  drawBezierApproxArcTo: (context, x0, y0, x1, y1, r, reverse) ->
+      d2 = (x0-x1)**2 +(y0-y1)**2
+      d  = Math.sqrt( d2 )
+
+      ct = Math.sqrt( 4*r*r - d2)*0.5
+      if reverse
+        ct = -ct
+
+      #Main formulas for calculating bezier points. Math was used to get them.
+      kx = 4.0/3.0*(r-ct)/d
+      ky = 8.0/3.0*r*(ct - r)/d2 + 1.0/6.0
+
+      #Get the bezier control point positions
+      #vx is a perpendicular vector, vy is parallel
+      vy_x = x1-x0
+      vy_y = y1-y0
+      vx_x = vy_y
+      vx_y = -vy_x # #rotated by Pi/2
+
+      p11x = (x0+x1)*0.5 + vx_x * kx + vy_x * ky
+      p11y = (y0+y1)*0.5 + vx_y * kx + vy_y * ky
+
+      p12x = (x0+x1)*0.5 + vx_x * kx - vy_x * ky
+      p12y = (y0+y1)*0.5 + vx_y * kx - vy_y * ky
+
+      context.bezierCurveTo p11x, p11y, p12x, p12y, x1, y1
+
   makeCellShape: (cellTransformMatrix, context) ->
     #each cell is n-gon.


add possibility to pass params to list milestones

--- src/Models/ProjectMilestones.coffee
+++ src/Models/ProjectMilestones.coffee
@@ -2,9 +2,12 @@
 Utils = require '../Utils'

 class ProjectMilestones extends BaseModel
-  list: (projectId, fn = null) =>
+  list: (projectId, query, fn = null) =>
     @debug "Projects::milestones()"
-    @get "projects/#{Utils.parseProjectId projectId}/milestones", (data) => fn data if fn
+     if 'function' is typeof query
+      fn = query
+      query = {}
+    @get "projects/#{Utils.parseProjectId projectId}/milestones", query, (data) => fn data if fn

   show: (projectId, milestoneId, fn = null) =>
     @debug "Projects::milestone()"


fixbug

fixbug

--- core/plan.coffee
+++ core/plan.coffee
@@ -7,8 +7,8 @@
 mAccount = require './model/account'

 exports.joinPlan = (account, plan, callback) ->
-  mAccount.joinPlan account, req.body.plan, ->
-    async.each config.plans[req.body.plan].services, (serviceName, callback) ->
+  mAccount.joinPlan account, plan, ->
+    async.each config.plans[plan].services, (serviceName, callback) ->
       if serviceName in account.attribute.services
         return callback()

@@ -23,11 +23,11 @@
           callback()

 exports.leavePlan = (account, plan, callback) ->
-  mAccount.leavePlan account, req.body.plan, ->
-    async.each config.plans[req.body.plan].services, (serviceName, callback) ->
+  mAccount.leavePlan account, plan, ->
+    async.each config.plans[plan].services, (serviceName, callback) ->
       stillInService = do ->
-        for item in _.without(account.attribute.plans, req.body.plan)
-          if serviceName in config.plans[req.body.plan].services
+        for item in _.without(account.attribute.plans, plan)
+          if serviceName in config.plans[plan].services
             return true

         return false


fixed typo

fixed typo

--- @client/edit_page.coffee
+++ @client/edit_page.coffee
@@ -421,7 +421,7 @@
     page_types = [
       {id: PAGE_TYPES.DEFAULT, label: "Standard page",    description: "Displays the proposal lists configured above."}
       {id: PAGE_TYPES.ABOUT,   label: 'Background page',  description: "Give supplementary background information about the engagement."}
-      {id: PAGE_TYPES.ALL,     label: '"Show all" page"', description: "Aggregate and show all proposal lists from other pages."}
+      {id: PAGE_TYPES.ALL,     label: '"Show all" page', description: "Aggregate and show all proposal lists from other pages."}
     ]

     DIV


Forgot k multiplier parameter for SRP6, added now

--- src/srp.coffee
+++ src/srp.coffee
@@ -72,7 +72,7 @@

 		return result

-	# B = (v + g^b) % N
+	# B = (kv + g^b) % N
 	B: (options) ->
 		# Expects v, which should be the client verifier BigInteger, and b,
 		# which should be a random BigInteger
@@ -81,7 +81,7 @@

 		# This actually ends up being (v + (g^b % N)) % N, this is the way
 		# everyone else is doing it, so it's the way we're gonna do it.
-		result = v.add(@params.g.modPow(b, @params.N), @params.N).mod(@params.N)
+		result = @k().multiply(v).add(@params.g.modPow(b, @params.N)).mod(@params.N)
 		result = transform.pad.toN result, @params

 		return result
@@ -113,7 +113,9 @@

 		result = B
 			.subtract(
-				@params.g.modPow(x, @params.N)
+				@k().multiply(
+					@params.g.modPow(x, @params.N)
+				)
 			)
 			.modPow(
 				a.add(
@@ -141,6 +143,22 @@
 		result = transform.pad.toN result, @params
 		return result

+	# SRP-6 multiplier
+	# Returns BigInteger
+	k: ->
+		result = crypto
+			.createHash(@params.hash)
+			.update(
+				transform.pad.toN(@params.N, @params)
+			)
+			.update(
+				transform.pad.toN(@params.g, @params)
+			)
+			.digest()
+
+		result = transform.buffer.toBigInteger result
+		return result
+
 	K: (options) ->
 		S = options.S # Buffer


Left and right tool-panels don't close when escape is pressed.

--- keymaps/default.cson
+++ keymaps/default.cson
@@ -164,6 +164,8 @@
 '.tool-panel':
   'escape': 'core:close'

+'.tool-panel.panel-left, .tool-panel.panel-right':
+  'escape': 'tool-panel:unfocus'

 '.editor !important, .editor.mini !important':
   'escape': 'editor:consolidate-selections'


Change keybinding for executing query

Change keybinding for executing query

--- keymaps/existdb.cson
+++ keymaps/existdb.cson
@@ -11,7 +11,7 @@
     'cmd-ctrl-e': 'existdb:toggle-tree-view'

 '.platform-darwin atom-text-editor[data-grammar="source xq"]':
-    'ctrl-enter': 'existdb:run'
+    'ctrl-cmd-enter': 'existdb:run'
     'cmd-ctrl-r': 'existdb:file-symbols'
     'cmd-alt-g': 'existdb:goto-definition'
     'cmd-alt-v': 'existdb:rename-variable'


make transparent images visible

make transparent images visible

--- src/smc-webapp/media-viewer/viewer.cjsx
+++ src/smc-webapp/media-viewer/viewer.cjsx
@@ -31,7 +31,7 @@
     render_media: (url) ->
         switch @get_mode()
             when 'image'
-
+
             when 'video'
                 <video
                     src      = {url}


Clear -> cancel

Clear -> cancel

--- lib/autocomplete-manager.coffee
+++ lib/autocomplete-manager.coffee
@@ -214,7 +214,7 @@
     clearTimeout(@delayTimeout)
     @delayTimeout = setTimeout(@runAutocompletion, delay)

-  clearNewSuggestionsRequest: ->
+  cancelNewSuggestionsRequest: ->
     clearTimeout(@delayTimeout)

   # Private: Gets called when the cursor has moved. Cancels the autocompletion if
@@ -235,7 +235,7 @@
   # event - The change {Event}
   bufferChanged: ({newText, oldText}) =>
     return if @suggestionList.compositionInProgress
-    @clearNewSuggestionsRequest()
+    @cancelNewSuggestionsRequest()
     if atom.config.get('autocomplete-plus.enableAutoActivation') and (newText.trim().length is 1 or oldText.trim().length is 1)
       @requestNewSuggestions()
     else


fix nupe when closing non git file

fix nupe when closing non git file

--- lib/git-diff-staged-view.coffee
+++ lib/git-diff-staged-view.coffee
@@ -46,7 +46,7 @@
     @timeoutId = setTimeout(@updateDiffs, timeout)

   updateDiffs: =>
-    return if @editor.isDestroyed()
+    return unless @editor

     @removeDecorations()
     if @repository and path = @editor?.getPath()


Add a better message when no packages installed

--- lib/notification-element.coffee
+++ lib/notification-element.coffee
@@ -261,10 +261,10 @@

       ```coffee
       # User
-      #{installedPackages.user.join('\n')}
+      #{installedPackages.user.join('\n') or 'No installed packages'}

       # Dev
-      #{installedPackages.dev.join('\n')}
+      #{installedPackages.dev.join('\n') or 'No dev packages'}
       ```

       #{copyText}


DONE: fix space only exclusion for selection HL

--- lib/quick-highlight-view.coffee
+++ lib/quick-highlight-view.coffee
@@ -38,14 +38,14 @@

     needSelectionHighlight: (selection) ->
       editorElement = @editor.element
-      scopes = settings.get('highlightSelectionExcludeScopes')
+      excludeScopes = settings.get('highlightSelectionExcludeScopes')
       switch
         when (not settings.get('highlightSelection'))
             , selection.isEmpty()
-            , (scopes.some (scope) -> matchScope(editorElement, scope))
+            , (excludeScopes.some (scope) -> matchScope(editorElement, scope))
             , not selection.getBufferRange().isSingleLine()
             , selection.getText().length < settings.get('highlightSelectionMinimumLength')
-            , /[^\S]/.test(selection.getText())
+            , (not /\S/.test(selection.getText()))
           false
         else
           true
@@ -58,13 +58,9 @@

     highlight: (keyword, color) ->
       markerLayer = @editor.addMarkerLayer()
-      decorationOptions = {type: 'highlight', class: "quick-highlight #{color}"}
-      @editor.decorateMarkerLayer(markerLayer, decorationOptions)
-      markerOptions = {invalidate: 'inside'}
-
-      pattern = ///#{_.escapeRegExp(keyword)}///g
-      @editor.scan pattern, ({range}) ->
-        markerLayer.markBufferRange(range, markerOptions)
+      @editor.decorateMarkerLayer(markerLayer, type: 'highlight', class: "quick-highlight #{color}")
+      @editor.scan ///#{_.escapeRegExp(keyword)}///g, ({range}) ->
+        markerLayer.markBufferRange(range, invalidate: 'inside')
       markerLayer

     clear: ->


fixing tabs being not active

fixing tabs being not active

--- lib/sublime-tab-bar-view.coffee
+++ lib/sublime-tab-bar-view.coffee
@@ -16,5 +16,5 @@
     for tab in @getTabs()
       @closeTab(tab) if tab.is('.temp')

-    tab.updateTitle() for tab in @getTabs()
     @insertTabAtIndex(new SublimeTabView(item, @pane), index)
+    @updateActiveTab()


:gift: finally full support duplicate(overwrite, non-overwrite) every direction!


Fix name parsing on sign up

Fix name parsing on sign up


Small indentation fix

Small indentation fix

--- library/vending/describe.coffee
+++ library/vending/describe.coffee
@@ -75,7 +75,7 @@
             methods = _.object filtered, _.map(filtered, doc)
             args = (method) => [method, service, @kernel]
             doc.descriptor? args(m)... for m, doc of methods
-        return service: service, methods: methods
+            return service: service, methods: methods

     # Describe the supplied method of arbitrary service, in a structured
     # and expected way, so that it can later be used to programmatically