Value changes rework: WIP - value reporting working some test still have incorrect expectations

--- src/test/runtime/ReactiveFunctionRunnerSpec.coffee
+++ src/test/runtime/ReactiveFunctionRunnerSpec.coffee
@@ -497,7 +497,7 @@

       inputs 20, 30, 40

-      changesFor("allInputs").should.eql [[null], [null, 20], [null, 20, 30], [null, 20, 30, 40]]
+      changesFor("allInputs").should.eql [null, [20], [20, 30], [20, 30, 40]]    #TODO should start with empty array

     it 'finds the total of the values in a stream using all_ function', ->
       providedFunctions
@@ -506,7 +506,7 @@

       inputs 20, 30, 40

-      changesFor("tot").should.eql [0, 20, 50, 90]
+      changesFor("tot").should.eql [null, 20, 50, 90]

     it 'finds the totals of the sequence values in a stream using plain version and their running total', ->
       providedFunctions
@@ -516,8 +516,8 @@

       inputs [2, 3, 4], [5, 6], [7]

-      changesFor("tot").should.eql [0, 9, 11, 7]
-      changesFor("totAll").should.eql [0, 9, 20, 27]
+      changesFor("tot").should.eql [null, 9, 11, 7]
+      changesFor("totAll").should.eql [null, 9, 20, 27]

     it 'finds the squares of all the values in a stream using all function', ->
       providedFunctions
@@ -526,26 +526,26 @@

       inputs 20, 30, 40

-      changesFor("sq").should.eql [[0], [0, 400], [0, 400, 900], [0, 400, 900, 1600]]
+      changesFor("sq").should.eql [null, [400], [400, 900], [400, 900, 1600]]

       #TODO this should not have a zero at start
     it 'applies a transform function to an input stream using all function', ->
       parseUserFunctions 'sq = fromEach(all(theInput), in * in)'
       inputs 20, 30, 40
-      changesFor("sq").should.eql [[0], [0, 400], [0, 400, 900], [0, 400, 900, 1600]]
+      changesFor("sq").should.eql [null, [400], [400, 900], [400, 900, 1600]]

     it 'applies a transform function to the sequence values in a stream using plain value', ->
       parseUserFunctions 'sq = fromEach(theInput, in * in)'
       inputs [2, 3, 4], [5, 6], [7]
-      changesFor("sq").should.eql [[], [4, 9, 16], [25, 36], [49]]       # TODO should first be null or []?
+      changesFor("sq").should.eql [null, [4, 9, 16], [25, 36], [49]]       # TODO should first be null or []?

     it 'finds the squares of the sequence values in a stream using plain version', ->
       providedFunctions
         square: (s) -> s * s
       parseUserFunctions 'sq = fromEach(theInput, square(in))'
       inputs [2, 3, 4], [5, 6], [7]
-      changesFor("sq").should.eql [[], [4, 9, 16], [25, 36], [49]]
+      changesFor("sq").should.eql [null, [4, 9, 16], [25, 36], [49]]

     it 'uses items from unpacked lists but reports change only for last value in each list', ->

@@ -557,7 +557,8 @@
       parseUserFunctions 'doubled = items * 2'
       sendInputs 'itemsIn', [4, 5, 6], 7, [], [8, 9]

-      changesFor("doubled").should.eql([0, 12, 14, 18])
+      valuesFor("doubled").should.eql([12, 14, 18])
+      changesFor("doubled").should.eql([null, 12, 14, 18])

     it 'collects all items from unpacked lists', ->

@@ -569,7 +570,8 @@
       parseUserFunctions 'allItems = all(items)'
       sendInputs 'itemsIn', [4, 5, 6], 7, [], [8, 9]

-      changesFor("allItems").should.eql([[null], [null, 4, 5, 6], [null, 4, 5, 6, 7], [null, 4, 5, 6, 7, 8, 9]])
+      valuesFor("allItems").should.eql([[4, 5, 6], [4, 5, 6, 7], [4, 5, 6, 7], [4, 5, 6, 7, 8, 9]])
+      changesFor("allItems").should.eql([null, [4, 5, 6], [4, 5, 6, 7], [4, 5, 6, 7, 8, 9]])

     it 'totals all items from unpacked lists', ->

@@ -583,7 +585,8 @@
       parseUserFunctions 'tot = total(all(items))'
       sendInputs 'itemsIn', [4, 5], [7], [], [8, 9]

-      changesFor("tot").should.eql([0, 9, 16, 33])
+      valuesFor("tot").should.eql([9, 16, 16, 33])
+      changesFor("tot").should.eql([null, 9, 16, 33])

   describe 'updates dependent expressions and notifies changes', ->
@@ -618,7 +621,7 @@
       parseUserFunctions 'number = theInput(); plusOne = number + 1'
       inputs 10, 20

-      changes.should.eql [{number:null}, {plusOne:1}, {theInput:10}, {number:10}, {plusOne:11}, {theInput:20}, {number:20}, {plusOne:21}]
+      changes.should.eql [{number:null}, {plusOne:null}, {theInput:10}, {number:10}, {plusOne:11}, {theInput:20}, {number:20}, {plusOne:21}]

     it 'when referenced functions defined before', ->
@@ -641,19 +644,19 @@
       inputs 25
       parseUserFunctions 'materials = 50'

-      changes.should.eql [{materials: 35}, {labour:null}, {total: 35}, {theInput: 25}, {labour:25}, {total: 60}, {materials: 50}, {total: 75}]
-
-
-    it 'adds two changing values in function set in between', ->
+      changes.should.eql [{materials: 35}, {labour:null}, {total: null}, {theInput: 25}, {labour:25}, {total: 60}, {materials: 50}, {total: 75}]
+
+
+    it.skip 'adds two changing values in function set in between', ->
       parseUserFunctions 'materials = 35'
       parseUserFunctions 'total = materials + labour'
       parseUserFunctions 'labour = theInput()'

       inputs 25

-      changes.should.eql [{materials: 35}, {labour: unknown 'labour'}, {total: unknown 'labour'}, {labour: null}, {total: 35}, {theInput: 25}, {labour: 25}, {total: 60}]
-
-    it 'on individual named value including initial value', ->
+      changes.should.eql [{materials: 35}, {labour: unknown 'labour'}, {total: unknown 'labour'}, {labour: null}, {total: null}, {theInput: 25}, {labour: 25}, {total: 60}]
+
+    it.skip 'on individual named value including initial value', ->
       parseUserFunctions 'aaa = 10'

       observeNamedChanges 'aaa'


Refactor common code

Refactor common code

--- lib/viewers/atom-pdf-viewer.coffee
+++ lib/viewers/atom-pdf-viewer.coffee
@@ -5,13 +5,15 @@
   _forwardSync = (view, texFile, line) ->
     view?.forwardSync?(texFile, line)

+  _activateItem = (item) ->
+    pane = atom.workspace.paneForItem(item)
+    pane.activate()
+    pane.activateItem(item)
+
   _getActivePaneItem = (pdfFile, opts = {}) ->
     for item of atom.workspace.getPaneItems()
       if item.filePath is pdfFile
-        unless opts.keepFocus
-          pane = atom.workspace.paneForItem(item)
-          pane.activate()
-          pane.activateItem(item)
+        _activateItem(item) unless opts.keepFocus
         return item

   _open = (pdfFile, opts = {}, callback = null) ->
@@ -19,11 +21,9 @@
       cb = callback
       current = atom.workspace.getActivePaneItem()
       callback = (view) ->
-        pane = atom.workspace.paneForItem(current)
-        pane.activate()
-        pane.activateItem(current)
+        _activateItem(current)
         cb(view)
-
+
     atom.workspace.open(
       pdfFile,
       split: 'right',


Update -> replace throw by debug message.

--- lib/enum-validator.coffee
+++ lib/enum-validator.coffee
@@ -34,7 +34,7 @@
           if not @_isEnumKeyPresent(ea, refEnum)
             updateActions.push @_generateUpdateAction(ea, refEnum)
         else
-          throw err "enum attribute name: #{ea.name} not found in Product Type: #{productType.name}", ea
+          debug "enum attribute name: #{ea.name} not found in Product Type: #{productType.name}"
       else
         debug "Skipping #{ea.name} update action generation as already exists."
     updateActions


chang ref to worker url

chang ref to worker url

--- coffeescript/004-webworkers/csmain.coffee
+++ coffeescript/004-webworkers/csmain.coffee
@@ -35,7 +35,7 @@
         )
         $("#params").html(list)

-        worker = new Worker './worker.js'
+        worker = new Worker 'worker.js'
         worker.onmessage = (event) ->
             data = event.data;
             $("#result").html(data);


:bug: Fix Race Condition In FuzzyProvider

--- lib/fuzzy-provider.coffee
+++ lib/fuzzy-provider.coffee
@@ -59,14 +59,14 @@
   # e - The change {Event}
   onChanged: (e) =>
     if e.newText in ["\n", " "]
-      newLine = e.newText is "\n"
-      @addLastWordToList newLine
+      newline = e.newText is "\n"
+      @addLastWordToList e.newRange.start.row, e.newRange.start.column, newline

   # Private: Adds the last typed word to the wordList
   #
   # newLine - {Boolean} Has a new line been typed?
-  addLastWordToList: (newLine) ->
-    lastWord = @lastTypedWord newLine
+  addLastWordToList: (row, column, newline) ->
+    lastWord = @lastTypedWord row, column, newline
     return unless lastWord

     if @wordList.indexOf(lastWord) < 0
@@ -78,21 +78,14 @@
   # newLine - {Boolean} Has a new line been typed?
   #
   # Returns {String} the last typed word
-  lastTypedWord: (newLine) ->
-    selectionRange = @editor.getSelection().getBufferRange()
-    {row} = selectionRange.start
+  lastTypedWord: (row, column, newline) ->
+    # The user pressed enter, check everything until the end
+    if newline
+      maxColumn = column - 1 unless column = 0
+    else
+      maxColumn = column

-    # The user pressed enter, check previous line
-    if newLine
-      row--
-
-    # The user pressed enter, check everything until the end
-    if newLine
-      maxColumn = @editor.lineLengthForBufferRow row
-    else
-      maxColumn = selectionRange.start.column
-
-    lineRange = [[row, 0], [row, maxColumn]]
+    lineRange = [[row, 0], [row, column]]

     lastWord = null
     @currentBuffer.scanInRange @wordRegex, lineRange, ({match, range, stop}) ->


バージョン番号を出力する処理を追加

バージョン番号を出力する処理を追加

--- lib/lamjet_command.coffee
+++ lib/lamjet_command.coffee
@@ -66,8 +66,7 @@
       .then (result)-> self.copyTemplate("index_spec.coffee", path.join("src", "index_spec.coffee"))

   run: ->
-    console.log("lamjet")
-    # TODO: バージョン番号を出力する
+    console.log("lamjet v" + require("../package.json").version)

     if @argv[2] == "init"
       @init()


CLI padding glob pattern bug fixed

CLI padding glob pattern bug fixed

--- cli.coffee
+++ cli.coffee
@@ -12,7 +12,7 @@
 runTestFile = (path) ->
 	require pathUtils.join realpath, path

-if fs.statSync(path).isFile()
+if fs.existsSync(path) and fs.statSync(path).isFile()
 	runTestFile path
 else
 	glob pathUtils.join(path, '/', '**/*.spec.*'), (err, files) ->


FIX: Scroll task into student list on load

--- src/app/projects/states/dashboard/directives/student-task-list/student-task-list.coffee
+++ src/app/projects/states/dashboard/directives/student-task-list/student-task-list.coffee
@@ -11,7 +11,7 @@
     refreshTasks: '=?'
     # Special taskData object (wraps the selectedTask)
     taskData: '='
-  controller: ($scope, $filter, gradeService) ->
+  controller: ($scope, $timeout, $filter, gradeService) ->
     # Check taskSource exists
     unless $scope.taskData?
       throw Error "Invalid taskData provided. Must wrap the selectedTask and selectedTaskAbbr"
@@ -47,7 +47,10 @@
       funcName = if taskEl.scrollIntoViewIfNeeded? then 'scrollIntoViewIfNeeded' else if taskEl.scrollIntoView? then 'scrollIntoView'
       return unless funcName?
       taskEl[funcName]({behavior: 'smooth', block: 'top'})
+    $timeout ->
+      scrollToTaskInList($scope.taskData.selectedTask) if $scope.taskData.selectedTask?
     $scope.isSelectedTask = (task) ->
       # Compare by definition
       task.definition.id == $scope.taskData?.selectedTask?.definition.id
+
 )


filetree fixes

filetree fixes

--- components/tree/treeviewcontroller.coffee
+++ components/tree/treeviewcontroller.coffee
@@ -75,6 +75,7 @@
   ###

   initTree:(nodes)->
+    log nodes
     @addNode node for node in nodes

   logTreeStructure:->
@@ -141,9 +142,11 @@
   ###

   addNode:(nodeData)->
-
+
+    log nodeData, "><AS>>ASD"
     nodeData = @repairIds nodeData
-    return unless nodeData
+    # return unless nodeData
+    log nodeData, "><AS>>ASD"
     @getData().push nodeData
     @addIndexedNode nodeData
     @registerListData nodeData
@@ -155,7 +158,9 @@
       list = @createList(parentId).getListView()
       @addSubList @nodes[parentId], parentId

-    list.addItem nodeData
+    node = list.addItem nodeData
+    log node, ">>>>"
+    node

   removeNode:(id)->


Correct typo in function name

Correct typo in function name

--- tapestry-core/src/main/coffeescript/org/apache/tapestry5/corelib/modulejs/pubsub.coffee
+++ tapestry-core/src/main/coffeescript/org/apache/tapestry5/corelib/modulejs/pubsub.coffee
@@ -83,7 +83,7 @@
   # Adds a responder for a stimulus, but the responder is added first in the list.
   # Otherwise, the same as respondsTo().
   # Returns this module's exports, for easy chaining of calls.
-  exports.responderFirst = exports.first = (stimulusName, responder) ->
+  exports.respondFirst = exports.first = (stimulusName, responder) ->
     addSub stimulusName, true, responder

     return exports


Fix another issue with $.i18n.t and this

--- ozaria/site/views/play/level/LevelPlaybackView.coffee
+++ ozaria/site/views/play/level/LevelPlaybackView.coffee
@@ -50,14 +50,13 @@
     ua = navigator.userAgent.toLowerCase()
     if /safari/.test(ua) and not /chrome/.test(ua)
       @$el.find('.toggle-fullscreen').hide()
-    t = $.i18n.t
-    @second = t 'units.second'
-    @seconds = t 'units.seconds'
-    @minute = t 'units.minute'
-    @minutes = t 'units.minutes'
-    @goto = t 'play_level.time_goto'
-    @current = t 'play_level.time_current'
-    @total = t 'play_level.time_total'
+    @second = $.i18n.t 'units.second'
+    @seconds = $.i18n.t 'units.seconds'
+    @minute = $.i18n.t 'units.minute'
+    @minutes = $.i18n.t 'units.minutes'
+    @goto = $.i18n.t 'play_level.time_goto'
+    @current = $.i18n.t 'play_level.time_current'
+    @total = $.i18n.t 'play_level.time_total'
     @$el.find('#play-button').css('visibility', 'hidden') if @options.level.get 'hidesPlayButton'  # Don't show for first few levels, confuses new players.

   # These functions could go to some helper class


added a test

added a test

--- test/compiler/vds_test.coffee
+++ test/compiler/vds_test.coffee
@@ -127,6 +127,8 @@
       check_nomatch "black", "black && <number>", 1, "number expected but '' found"
     it "fails for second", ->
       check_nomatch "3.3", "black && <number>", 1, "'black' expected but '' found"
+    it "fails for empty", ->
+      check_nomatch "", "black && <number>", 0, "'black' or number expected but '' found"

   describe "annotations", ->


fix omission of 'length'

fix omission of 'length'

--- services/web/app/coffee/Features/Authentication/AuthenticationController.coffee
+++ services/web/app/coffee/Features/Authentication/AuthenticationController.coffee
@@ -191,7 +191,7 @@

 	_setRedirectInSession: (req, value) ->
 		if !value?
-			value = if Object.keys(req.query) > 0 then "#{req.path}?#{querystring.stringify(req.query)}" else req.path
+			value = if Object.keys(req.query).length > 0 then "#{req.path}?#{querystring.stringify(req.query)}" else req.path
 		if req.session?
 			req.session.postLoginRedirect = value


Sometimes project configuration is null.

--- app/pages/project/index.cjsx
+++ app/pages/project/index.cjsx
@@ -99,7 +99,7 @@
   render: ->
     projectPath = "/projects/#{@props.project.slug}"

-    currentWorkflow = @props.preferences?.preferences.selected_workflow ? @props.project.configuration.default_workflow
+    currentWorkflow = @props.preferences?.preferences.selected_workflow ? @props.project.configuration?.default_workflow

     pages = [{}, @state.pages...].reduce (map, page) =>
       map[page.url_key] = page


Remove useless option

Remove useless option

--- lib/violation-view.coffee
+++ lib/violation-view.coffee
@@ -25,7 +25,6 @@
       title: HTML || @violation.message
       html: HTML?
       container: @lintView
-      viewport: @lintView

   trackEdit: ->
     @marker = @editor.markScreenRange(@getCurrentScreenRange(), invalidation: 'inside')


Missed a spot

Missed a spot

--- lib/zombie/browser.coffee
+++ lib/zombie/browser.coffee
@@ -772,7 +772,7 @@
   selectOption: (selector)->
     option = @query(selector)
     if option && !option.getAttribute("selected")
-      select = @xpath("./ancestor::select", option).value[0]
+      select = @xpath("./ancestor::select", option).iterateNext()
       option.setAttribute("selected", "selected")
       select.focus()
       @fire(select, "change")
@@ -800,7 +800,7 @@
   # Without callback, returns this.
   unselectOption: (option)->
     if option && option.getAttribute("selected")
-      select = @xpath("./ancestor::select", option).value[0]
+      select = @xpath("./ancestor::select", option).iterateNext()
       unless select.multiple
         throw new Error("Cannot unselect in single select")
       option.removeAttribute("selected")


Decrease datasetIndex when removing a dataset

Signed-off-by: Marko Locher <9efe9a5a1da5f41a4eb7599f2715dc24abf5bbc8@locher.at>

--- src/d3.timeslider.coffee
+++ src/d3.timeslider.coffee
@@ -294,6 +294,7 @@

         i = @data[id].index
         delete @data[id]
+        @options.datasetIndex--
         d3.select(@element).select("g.dataset#dataset-#{id}").remove()

         # repaint the datasets


WIP coffee structure

WIP coffee structure

--- app/static/coffee/all.coffee
+++ app/static/coffee/all.coffee
@@ -7,16 +7,13 @@
         @model()
         return
     view: () =>
-        [
-            #try m("div", ["object1" ,@pageNumber.view()])
-            #try m("div", ["object2" ,@pageNumber2.view()])
-            #try m("div", ["object1" ,@pageNumber.view()])
+        return [
             header.view()
             @table.view()
         ]

 class Data
-    model: () =>
+    model: (json = {}) =>
         meta=(response)=>
             @headers = response.head.vars
             @genomes = response.results.bindings
@@ -24,12 +21,14 @@
         m.request(
             method: "POST",
             url: 'http://10.139.14.121:5000/mithril/meta',
-            data: {}
+            data: json
             datatype: 'json'
             type: meta)
     #controller
-    constructor: () ->
-        @model()
+    request: (json = {}) =>
+    	@model(json)
+    constructor: (json = {}) ->
+        @model(json)
     #view
     response: () =>
         return {
@@ -164,10 +163,12 @@
             ])
         ])

+
+#Singleton?
 header = new Header()
 meta = new App
+home = new Home

-home = new Home
 m.route(document.body, "/", {
     "/": home
     "/home": home


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/engine_levels.coffee
+++ src/engine_levels.coffee
@@ -1,5 +1,5 @@
 ###*
-This module clears or fills the canvas before drawing the scene.
+This module provides methods for transitioning

 @name Clear
 @fieldOf Engine


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/engine_levels.coffee
+++ src/engine_levels.coffee
@@ -59,7 +59,7 @@
     loadLevel level

   ###*
-  Load map named level
+  Reload the

   engine.goToLevel 'bossFight'


hi @dzucconi

hi @dzucconi

--- apps/gene/test/routes.coffee
+++ apps/gene/test/routes.coffee
@@ -35,7 +35,7 @@
         @res.render.args[0][0].should.equal 'index'
         done()

-    it 'redirects to the correct URL if the gene slug has been updated', ->
+    it 'redirects to the correct URL if the gene slug has been updated', (done) ->
       routes.index @req, @res
       _.first(Backbone.sync.args)[2].success fabricate 'gene', id: 'not-foo'
       _.last(Backbone.sync.args)[2].success fabricate2 'filter_artworks'


Add player lines to canvas at game start

--- game.coffee
+++ game.coffee
@@ -43,6 +43,10 @@
       @player3 = new Player("Player3", new Position([0, Game.HEIGHT/2], Game.EAST, 0))

       @players = [@player0, @player1, @player2, @player3]
+
+      if @browser
+        @canvas.add(p.current_line) for p in @players
+

       @old_time = SynchronizedTime.getTime()


Fix IE: supress error on media modal close

--- MediaAdminBundle/Resources/public/coffee/view/MediaModalView.coffee
+++ MediaAdminBundle/Resources/public/coffee/view/MediaModalView.coffee
@@ -37,7 +37,6 @@
       currentModal.modal "show"
       currentModal.on 'hidden.bs.modal', ->
         currentModal = null
-        this.remove()
         return

   initMenu: (activeNode) ->


Working compiling via ace workers

Working compiling via ace workers

--- src/js/app/editor.coffee
+++ src/js/app/editor.coffee
@@ -114,44 +114,40 @@
   "ph", -> history.print(log)
 ]

-compileAndRun = ->
-  # TODO use prelude trim
-  source = $.trim commandArea.getValue()
-  return if source.length is 0
-
-  timeline.push source
-  hideError "command", "runtime"
-  try
-    if pref = source.match(/^(< |:)/)
-      source = source[pref[0].length..]
-      for command in commands
-        match = command.match source
-        break if match?
-      saveTimeline()
-      outputScrollTop()
-    else
-      command = compiler.compile source, getCompilerOptions()
-      try
-        log execute compiledJS + command
-        saveTimeline()
-        outputScrollTop()
-      catch error
-        showErrorMessage "runtime", "Runtime: #{error}"
-  catch error
-    console.log error
-    showErrorMessage "command", "Command Line: #{error.message}"
-  return
+# compileAndRun = ->
+#   # TODO use prelude trim
+#   source = $.trim commandArea.getValue()
+#   return if source.length is 0
+
+#   timeline.push source
+#   hideError "command", "runtime"
+#   try
+#     if pref = source.match(/^(< |:)/)
+#       source = source[pref[0].length..]
+#       for command in commands
+#         match = command.match source
+#         break if match?
+#       saveTimeline()
+#       outputScrollTop()
+#     else
+#       command = compiler.compile source, getCompilerOptions()
+#       try
+#         log execute compiledJS + command
+#         saveTimeline()
+#         outputScrollTop()
+#       catch error
+#         showErrorMessage "runtime", "Runtime: #{error}"
+#   catch error
+#     console.log error
+#     showErrorMessage "command", "Command Line: #{error.message}"
+#   return

 modes =
   CoffeeScript:
     id: "coffeescript"
-    options:
-      bare: true

   IcedCoffeeScript:
     id: "icedcoffeescript"
-    options:
-      bare: true

   Ometa:
     id: "ometa"
@@ -167,33 +163,28 @@
   addMessage modesList(), "modesList"

 setNewMode = (name, callback) ->
+  name = 'CoffeeScript' unless name?.length
   if name isnt currentMode
     setMode name, callback
   else
     callback?()

 setMode = (name, callback) ->
-  mode = modes[name]
-  if mode?
-    id = mode.id
-    require [
-      "compilers/" + id + "/compiler"
-      "compilers/" + id + "/highlighter"
-    ], (compilerClass, highlighter) ->
-      compiler = compilerClass
-      compilerOptions = mode.options
-      sourceArea.setOption "mode", id
-      commandArea.setOption "mode", id
-      mode.init?()
-      currentMode = name
+  config = modes[name]
+  if config?
+    modeId = config.id
+    sourceArea.session.setMode "compilers/#{modeId}/mode"
+    commandArea.session.setMode "compilers/#{modeId}/mode"
+    currentMode = name
+    commandArea.on 'changeMode', ->
+      compiler = sourceArea.session.getMode()
       if isCurrentMessage "modesList"
         setCurrentMessage modesList(), "modesList"
       else
-        log "#{name} compiler loaded"
-      # compileCode()
+        showFileMessage "#{name} compiler loaded"
       callback?()
-    , (error) ->
-      log "#{name} loading failed"
+  # , (error) ->
+  #   log "#{name} loading failed"
   else
     log "Wrong mode name, choose from:\n\n#{modesList()}"

@@ -208,37 +199,37 @@
   # TODO: use prelude
   $.extend {}, compilerOptions

-compileCode = ->
-  startColor = "#151515"
-  endColor = "#ccc"
-  normalColor = "#050505"
-  indicator = $ "#compilationIndicator"
-  indicateBy = (color) ->
-    indicator.animate
-      color: color
-    ,
-      complete: ->
-        indicator.css color: color
-
-  compileSource ->
-    indicateBy startColor
-  , ->
-    indicateBy endColor
-    indicateBy normalColor
-
-compileSource = (start, finish) ->
-  start()
-  source = sourceArea.getValue()
-  compiledJS = ""
-  saveCurrent()
-  try
-    compiledJS = compiler.compile source, getCompilerOptions()
-    hideError "compiler", "runtime"
-  catch error
-    showErrorMessage "compiler", "Compiler: #{error.message ? error}"
-  sourceCompiled = true
-  finish()
-  $("#repl_permalink").attr "href", "##{sourceFragment}#{encodeURIComponent(source)}"
+# compileCode = ->
+#   startColor = "#151515"
+#   endColor = "#ccc"
+#   normalColor = "#050505"
+#   indicator = $ "#compilationIndicator"
+#   indicateBy = (color) ->
+#     indicator.animate
+#       color: color
+#     ,
+#       complete: ->
+#         indicator.css color: color
+
+#   compileSource ->
+#     indicateBy startColor
+#   , ->
+#     indicateBy endColor
+#     indicateBy normalColor
+
+# compileSource = (start, finish) ->
+#   start()
+#   source = sourceArea.getValue()
+#   compiledJS = ""
+#   saveCurrent()
+#   try
+#     compiledJS = compiler.compile source, getCompilerOptions()
+#     hideError "compiler", "runtime"
+#   catch error
+#     showErrorMessage "compiler", "Compiler: #{error.message ? error}"
+#   sourceCompiled = true
+#   finish()
+#   $("#repl_permalink").attr "href", "##{sourceFragment}#{encodeURIComponent(source)}"

 execute = (code) ->
   eval (compiler.preExecute? code) ? code
@@ -249,12 +240,12 @@
   sourceChanged = true
   history.add e
   return #unless autoCompile
-  DELAY = 700
-  lastHit = +new Date
-  setTimeout ->
-    if +new Date() - lastHit > DELAY
-      compileCode() unless sourceCompiled
-  , 2 * DELAY
+  # DELAY = 700
+  # lastHit = +new Date
+  # setTimeout ->
+  #   if +new Date() - lastHit > DELAY
+  #     compileCode() unless sourceCompiled
+  # , 2 * DELAY

 # TODO: this is not used, it is connected to History
 printHistory = ->
@@ -349,7 +340,7 @@
   return unless name?
   fileCookie saveName, null
   ammendClientTable name
-  showFileMessage "" + name + " deleted"
+  showFileMessage "#{name} deleted"

 ammendClientTable = (exclude, addition) ->
   addition = null unless addition?
@@ -358,7 +349,6 @@
   if oldTable?
     for pair in oldTable.split ";"
       [name, lines] = pair.split ","
-      console.log name, exclude
       table.push pair if name isnt exclude

   table.push addition if addition
@@ -472,7 +462,6 @@

 sourceArea = ace.edit 'sourceArea'
 sourceArea.setTheme "ace/theme/cobalt"
-sourceArea.session.setMode "compilers/coffeescript/mode"
 sourceArea.session.on 'changeMode', ->
   sourceArea.session.$worker.on 'ok', ({data: {result}}) ->
     saveCurrent()
@@ -498,18 +487,48 @@

 commandArea = ace.edit 'commandArea'
 commandArea.setTheme "ace/theme/cobalt"
-commandArea.session.setMode "compilers/coffeescript/mode"
+commandArea.session.setUseWorker false
 commandArea.setHighlightActiveLine false
 commandArea.setShowPrintMargin false
 commandArea.renderer.setShowGutter false
+commandWorker = null
+
+commandArea.session.on 'changeMode', ->
+  commandWorker = commandArea.session.getMode().createWorker()
+
+  commandWorker.on 'ok', ({data: {result, type}}) ->
+    # TODO use prelude trim
+    source = $.trim commandArea.getValue()
+    return if source.length is 0
+
+    timeline.push source
+    hideError "command", "runtime"
+    if type is 'command'
+      for command in commands
+        match = command.match result
+        break if match?
+      saveTimeline()
+      outputScrollTop()
+    else
+      try
+        log execute compiledJS + result
+        saveTimeline()
+        outputScrollTop()
+      catch error
+        showErrorMessage "runtime", "Runtime: #{error}"
+
+    commandArea.setValue ""
+
+  commandWorker.on 'error', ({data: {text}}) ->
+    showErrorMessage "command", "Command Line: #{text}"

 # Execute on enter
 commandArea.commands.addCommand
   name: 'execute'
   bindKey: win: 'Enter', mac: 'Enter'
   exec: ->
-    compileAndRun()
-    commandArea.setValue ""
+    commandWorker.call 'setValue', [commandArea.getValue()]
+    commandWorker.call 'onUpdate', []

 commandArea.commands.addCommand
   name: 'previous'


Replaced main KS view

Replaced main KS view

--- src/ks/views/main.coffee
+++ src/ks/views/main.coffee
@@ -1,31 +1,16 @@
-styles = require('styles').ui
+module.exports = class Window extends Backbone.View

-TabGroup = require 'views/ui/tabs/group'
+  viewName: 'Window'

-module.exports = class MainView extends TabGroup
+  attributes:
+    backgroundColor: '#eee'

-  initialize: ->
+  render: ->

-    layout =
-      title: 'Layout',
-      viewClass: require 'ks/views/layout'
-      icon: 'ks/layout.png'
+    Backbone.$('<Label>')
+      .attr('text', 'Installation was successful!')
+      .appendTo @$el

-    tables =
-      title: 'Tables'
-      viewClass: require 'ks/views/tables'
-      icon: 'ks/tables.png'
+    @

-    forms =
-      title: 'Forms'
-      viewClass: require 'ks/views/forms'
-      icon: 'ks/forms.png'
-
-    github =
-      title: 'GitHub'
-      viewClass: require 'ks/views/github'
-      icon: 'ks/github.png'
-
-    @options.items = [ tables, layout, forms, github ]
-
-    super
+  open: -> @render().el.open()


fixed indentation typo

fixed indentation typo

--- app/assets/javascripts/action_assets/jquery_action_assets.js.coffee
+++ app/assets/javascripts/action_assets/jquery_action_assets.js.coffee
@@ -2,7 +2,7 @@
     @.each ->
       params =
         controller: $(@).attr('data-controller')
-    		 action: $(@).attr('data-action')
+        action: $(@).attr('data-action')
         layout: $(@).attr('data-layout')
         event: event
         selector: @


Fixed text modules.

Fixed text modules.

--- app/assets/javascripts/app/controllers/widget/text_module.js.coffee
+++ app/assets/javascripts/app/controllers/widget/text_module.js.coffee
@@ -20,24 +20,25 @@

   reload: (data) =>
     return if !data
+    @data = data
     @update()

   update: =>
     allRaw = App.TextModule.all()
     all = []
-    ui = @data || @
+    data = @data || @
     for item in allRaw
       if item.active is true
         attributes = item.attributes()
         attributes.content = attributes.content.replace( /<%=\s{0,2}(.+?)\s{0,2}%>/g, ( index, key ) ->
-          key = key.replace( /@/g, 'ui.data.' )
+          key = key.replace( /@/g, 'data.' )
           varString = "#{key}" + ''
-#          console.log( "tag replacement env: ", ui.data)
+          #console.log( "tag replacement env: ", data)
           try
-#            console.log( "tag replacement: " + key, varString )
+            #console.log( "tag replacement: " + key, varString )
             key = eval (varString)
           catch error
-#            console.log( "tag replacement error: " + error )
+            #console.log( "tag replacement error: " + error )
             key = ''
           return key
         )


Added tooltip on points showing the 'size' value for the bubble

--- app/assets/javascripts/views/reports/report_scatter_chart.js.coffee
+++ app/assets/javascripts/views/reports/report_scatter_chart.js.coffee
@@ -12,12 +12,13 @@
 			chart.color(d3.scale.category10().range())
 			chart.xAxis.tickFormat(d3.format ',').axisLabel(colX)
 			chart.yAxis.tickFormat(d3.format ',').axisLabel(colY)
+			chart.tooltipContent((key, x, y, e) -> e.point.size)
 			chart.showControls(false)
 			d3.fisheye = false # strange fix for fisheye causing issues with selecting legend elements
 			d3.select('#scatter_chart svg').datum(data).transition().duration(350).call(chart)
 			nv.utils.windowResize(chart.update)
 			chart
-
+
 	prepareData: ->

 		data = @getData()


filetree: Fixed "Open terminal from here"

--- client/app/MainApp/filetree/controllers/findertreecontroller.coffee
+++ client/app/MainApp/filetree/controllers/findertreecontroller.coffee
@@ -446,9 +446,9 @@

   openTerminalFromHere: (nodeView) ->
     appManager.open "WebTerm", (appInstance) =>
-      path        = nodeView.getData().path
-      webTermView = KD.getSingleton('mainView').mainTabView.getActivePane().mainView
-      #TODO: webTermView != appInstance.getView() so should simplify the line above
+      path          = nodeView.getData().path
+      appManager    = @getSingleton "appManager"
+      {webTermView} = appManager.getFrontApp().getView().tabView.getActivePane().getOptions()

       webTermView.on "WebTermConnected", (server) =>
         server.input "cd #{path}\n"


Use real templates in forum NewPostView tests

--- common/static/coffee/spec/discussion/view/new_post_view_spec.coffee
+++ common/static/coffee/spec/discussion/view/new_post_view_spec.coffee
@@ -1,57 +1,7 @@
 # -*- coding: utf-8 -*-
 describe "NewPostView", ->
     beforeEach ->
-        setFixtures(
-            """
-

-

-

-

-

-
-
-
-
-
-
-            """
-        )
+        DiscussionSpecHelper.setUnderscoreFixtures()
         window.$$course_id = "edX/999/test"
         spyOn(DiscussionUtil, "makeWmdEditor")
         @discussion = new Discussion([], {pages: 1})
@@ -77,10 +27,11 @@
             "entries": {}
           },
           "allow_anonymous": true,
-          "allow_anonymous_to_peers": true
+          "allow_anonymous_to_peers": true,
+          "is_cohorted": true
         })
         @view = new NewPostView(
-          el: $(".new-post-article"),
+          el: $("#fixture-element"),
           collection: @discussion,
           course_settings: @course_settings,
           mode: "tab"
@@ -144,6 +95,7 @@
               }
             }
           )
+          DiscussionSpecHelper.makeModerator()
           view.render()

         expectCohortSelectorEnabled = (view, enabled) ->
@@ -171,7 +123,7 @@
           {always: ->}
       )
       view = new NewPostView(
-        el: $(".new-post-article"),
+        el: $("#fixture-element"),
         collection: @discussion,
         course_settings: new DiscussionCourseSettings({
           allow_anonymous: false,


Don't prepopulate recipient email for example polls

--- angular/core/components/poll/common/action_panel/poll_common_action_panel.coffee
+++ angular/core/components/poll/common/action_panel/poll_common_action_panel.coffee
@@ -4,14 +4,15 @@
   controller: ($scope, Records, Session) ->

     $scope.init = ->
-      invitation_token = $location.search().invitation_token
-      $scope.invitation = Records.invitations.find(token: invitation_token)[0] || {}
+      token      = $location.search().invitation_token
+      invitation = _.first(Records.invitations.find(token: token)) unless $scope.poll.example
       $scope.stance = PollService.lastStanceBy(Session.user(), $scope.poll) or
                       Records.stances.build(
                         pollId:    $scope.poll.id,
                         userId:    AppConfig.currentUserId,
-                        token:     invitation_token
-                        visitorAttributes: {email: $scope.invitation.recipientEmail}
+                        token:     token
+                        visitorAttributes:
+                          email: (invitation or {}).recipientEmail
                       ).choose($location.search().poll_option_id)

     $scope.$on 'refreshStance', $scope.init


Comments and slightly better status output.

--- prod/src/coffee-script/node/camparser.coffee
+++ prod/src/coffee-script/node/camparser.coffee
@@ -1,3 +1,7 @@
+# Parse traffic camera KML into MongoDB documents and save them with
+# geospatial indexing.  This will allow for $near queries to return traffic
+# cameras around a mobile device.
+
 sys = require 'sys'
 fs = require 'fs'
 xml2js = require 'xml2js'
@@ -69,7 +73,8 @@

 Error: #{err}"""

+            # Give some status and close the connection.
             if isLast
                 CameraModel.count {}, (err, count) ->
-                    console.log "Saved #{count} cameras successfully."
+                    console.log "Saved #{count} of #{result.Document.Folder.Placemark.length} cameras successfully."
                     mongoose.disconnect()


Stack: grant group admin rights to modify a stack

--- workers/social/lib/social/models/stack.coffee
+++ workers/social/lib/social/models/stack.coffee
@@ -485,6 +485,7 @@
   modify: permit

     advanced: [
+      { permission: 'update stack', validateWith: Validators.group.admin }
       { permission: 'update own stack', validateWith: Validators.own }
     ]


JComputeStack: update model schema, remove deprecated rules, domains and extras fields. Add default value for machines field

--- workers/social/lib/social/models/stack.coffee
+++ workers/social/lib/social/models/stack.coffee
@@ -72,10 +72,9 @@
       baseStackId        : ObjectId
       stackRevision      : String

-      rules              : [ ObjectId ]
-      domains            : [ ObjectId ]
-      machines           : [ ObjectId ]
-      extras             : [ ObjectId ]
+      machines           :
+        type             : [ ObjectId ]
+        default          : -> []

       config             : Object


Refactor the addHook function to be more readable

--- src/hook-manager.coffee
+++ src/hook-manager.coffee
@@ -44,32 +44,53 @@
   # * `options` {Object} containing the following options:
   #     * `room` Room name {String} to send events to for this hook.
   addHook: (user, repo, options = {}) ->
+    try
+      url = @buildHookUrl(user, repo, options)
+      token = @getToken()
+
+      data =
+        name: 'web'
+        active: true
+        config:
+          content_type: 'json'
+          url: url
+        events: ALL_EVENTS
+
+      @robot.http("https://api.github.com/repos/#{user}/#{repo}/hooks")
+        .header('Accept', 'application/json')
+        .header('Authorization', "token #{token}")
+        .post(data)
+    catch e
+      @msg.reply e.message
+
+  # Private: Builds the URL to use for receiving the web hooks.
+  #
+  # * `user` {String} containing the GitHub user name.
+  # * `repo` {String} containing the GitHub repository name.
+  # * `options` {Object} containing the following options:
+  #     * `room` Room name {String} to send events to for this hook.
+  #
+  # Returns a {String} containing the URL.
+  buildHookUrl: (user, repo, options = {}) ->
     host = process.env.HEROKU_URL ? process.env.HUBOT_GITHUB_EVENT_BASE_URL

     unless host
-      @msg.reply("Neither HEROKU_URL nor HUBOT_GITHUB_EVENT_BASE_URL are set, cannot add hook")
-      return
-
-    token = process.env.HUBOT_GITHUB_EVENT_HOOK_TOKEN
-
-    unless token
-      @msg.reply('HUBOT_GITHUB_EVENT_HOOK_TOKEN is not set, cannot add hook')
-      return
+      throw new Error('Neither HEROKU_URL nor HUBOT_GITHUB_EVENT_BASE_URL are set, cannot add hook')

     url = "#{host}/hubot/github-events"
     url += "?room=#{options.room}" if options.room

-    data =
-      name: 'web'
-      active: true
-      config:
-        content_type: 'json'
-        url: url
-      events: ALL_EVENTS
+    url

-    @robot.http("https://api.github.com/repos/#{user}/#{repo}/hooks")
-      .header('Accept', 'application/json')
-      .header('Authorization', "token #{token}")
-      .post(data)
+  # Private: Gets the security token to use for authorization to access web hooks.
+  #
+  # Returns a {String} containing the token text.
+  getToken: ->
+    token = process.env.HUBOT_GITHUB_EVENT_HOOK_TOKEN
+
+    unless token
+      throw new Error('HUBOT_GITHUB_EVENT_HOOK_TOKEN is not set, cannot add hook')
+
+    token

 module.exports = HookManager


[✖] Removed “polyfills.js”.

[✖] Removed “polyfills.js”.

--- vendor/assets/javascripts/polyfills.js.coffee
+++ vendor/assets/javascripts/polyfills.js.coffee
@@ -1,6 +0,0 @@
-# This is the manifest for “polyfills.js”.
-
-# polyfill (n): A JavaScript shim that replicates the standard API for older browsers.
-
-#= require ./polyfills/console
-#= require ./polyfills/modernizr


Number#degrees and Number#rotations

Number#degrees and Number#rotations

--- test/number_extensions.coffee
+++ test/number_extensions.coffee
@@ -97,5 +97,13 @@
   equals 1.second, 1000
   equals 3.seconds, 3000

+test "#degrees", ->
+  equals 180.degrees, Math.PI
+  equals 1.degree, Math.TAU / 360
+
+test "#rotations", ->
+  equals 1.rotation, Math.TAU
+  equals 0.5.rotations, Math.TAU / 2
+
 module undefined


Update EC2 test

Update EC2 test

--- test/services/ec2.spec.coffee
+++ test/services/ec2.spec.coffee
@@ -30,7 +30,7 @@
             "%26X-Amz-Algorithm%3DAWS4-HMAC-SHA256%26X-Amz-Credential%3Dakid%252F19700101" +
             "%252Fsrc-region%252Fec2%252Faws4_request%26X-Amz-Date%3D19700101T000000Z" +
             "%26X-Amz-Expires%3D3600%26X-Amz-Security-Token%3Dsession" +
-            "%26X-Amz-Signature%3D59730f8fcb14eab2257c698dd4d2037beb215a0792d489b2e1a9f8aa2a9e277a" +
+            "%26X-Amz-Signature%3D59789a2b46cde824a9e2e4fae739605ec3b8796f8c5266c4f4023bdc9a6ba15e" +
             "%26X-Amz-SignedHeaders%3Dhost",
           'SourceRegion=src-region',
           'SourceSnapshotId=snap-123456789'


Added test for $router.href()

Added test for $router.href()

--- test/specs/router-spec.coffee
+++ test/specs/router-spec.coffee
@@ -46,6 +46,25 @@
             $router.reload yes
             expect(routerProvider.renderViews).toHaveBeenCalledWith yes, yes

+        it '$router.href() with undefined namespace.', inject ($router) ->
+            expect(-> $router.href('undefined')).toThrow new Error('Can\'t find the rule undefined.')
+        it '$router.href() without search argument.', inject ($router) ->
+            routerProvider.rules =
+                home:
+                    hrefTemplate: '/{key}'
+            result = $router.href 'home',
+                key: 'test'
+                index: 0
+            expect(result).toBe '/test?index=0'
+        it '$router.href() with search argument.', inject ($router) ->
+            routerProvider.rules =
+                home:
+                    hrefTemplate: '/{key}'
+            search = {}
+            result = $router.href 'home', key: 'test', index: 0, search
+            expect(result).toBe '/test'
+            expect(search).toEqual index: 0
+
         it '$router.oldState and $routerProvider.oldState are the same object', inject ($router) ->
             expect($router.oldState).not.toBeNull()
             expect($router.oldState).toBe routerProvider.oldState


More progress bar support

More progress bar support

--- web/doppio-jvm/scripts/demo/doppio-api.coffee
+++ web/doppio-jvm/scripts/demo/doppio-api.coffee
@@ -7,7 +7,7 @@
         (this is usually accomplished with jQuery(document).onReady)
     ###

-    constructor: (@stdout, done_cb2) ->
+    constructor: (@stdout, done_cb2, progress_cb) ->
         ###
             Sets up Doppio environment.
             @stdout (msg) ->
@@ -25,7 +25,7 @@
             jvm.set_classpath '/sys/vendor/classes', '/tmp/'
             @rs = new runtime.RuntimeState(@output, stdin, @bs_cl)
             done_cb2?()
-        @load_mini_rt(done_cb1)
+        @load_mini_rt(done_cb1,progress_cb)
         return

     setOutputFunctions: (stdout, @log) ->
@@ -43,7 +43,7 @@
             @stdout msg
         return

-    load_mini_rt: (done_cb)->
+    load_mini_rt: (done_cb,progress_cb)->
         ###
             Loads the compressed pre-compiled java classes for Doppio
         ###
@@ -54,6 +54,7 @@
           xhrfs = node.fs.getRootFS().mntMap["/sys"]

           write_one_file = (percent, path, file) ->
+            progress_cb?(percent)
             path = "/#{path}" if path[0] != '/'
             try
                 xhrfs.preloadFile(path,file) if file.length > 0


Use config for iamport parameter

Use config for iamport parameter

--- public/assets/coffee/order-id-payment.coffee
+++ public/assets/coffee/order-id-payment.coffee
@@ -1,6 +1,6 @@
 $ ->
   IMP = window.IMP
-  IMP.init('imp77873889')
+  IMP.init(CONFIG.iamport.id)

   STATUS =
     choose_address: 49  # 주소선택
@@ -43,7 +43,7 @@
       dataType: 'json'
       success: (data, textStatus, jqXHR) ->

-        IMP.request_pay
+        imp_params =
           pg:           'html5_inicis'
           pay_method:   pay_method
           merchant_uid: data.cid
@@ -53,8 +53,9 @@
           buyer_name:   name
           buyer_tel:    $info.data('phone')
           buyer_addr:   $info.data('address1')
-          notice_url:   'https://test-share.theopencloset.net/webhooks/iamport'
-        , (res) ->
+        imp_params.notice_url = CONFIG.iamport.notice_url if CONFIG.iamport.notice_url?
+
+        IMP.request_pay imp_params, (res) ->
           payment_status = res.status
           unless res.success
             payment_status = "cancelled"


refactored delimiter dictinoary creation

--- src/mist-resolver.coffee
+++ src/mist-resolver.coffee
@@ -200,14 +200,27 @@
 ###
 # Delimits a template given a pathname
 #
-# Memoized!
-#
 # pathname:
 #   The pathname to use when expanding the templates
 # template:
 #   A delimited template
 ###
 MistResolver.delimitPath = (pathname, template)->
+  dict = MistResolver.generateDict pathname
+
+  template.replace MistResolver.delimiterPattern, (m, p, c)->
+    if c of dict then p + dict[c]
+    else throw "unknown file delimiter: #{c}"
+
+###
+# Generates a delimiter dictionary for a given pathname.
+#
+# Implemented with memoization for slightly better performance.
+#
+# pathname;
+#   The pathname for which to build up a dictionary
+###
+MistResolver.generateDict = (pathname)->
   dict = {}
   if pathname of @
     dict = @[pathname]
@@ -216,11 +229,8 @@
     dict['o'] = '%o'
     dict['b'] = path.basename pathname
     dict['B'] = dict['b'].replace /\..+$/, ''
-
-  template.replace MistResolver.delimiterPattern, (m, p, c)->
-    if c of dict then p + dict[c]
-    else throw "unknown file delimiter: #{c}"
-MistResolver.delimitPath = MistResolver.delimitPath.bind {}
+  return dict
+MistResolver.generateDict = MistResolver.generateDict.bind {}

 ###
 # Delimits a command


JComputeStack: some: ignore stacks that are in Destroying state

We are marking them as Destroying as well when remove requested

--- workers/social/lib/social/models/stack.coffee
+++ workers/social/lib/social/models/stack.coffee
@@ -187,10 +187,10 @@
     { delegate } = client.connection
     { group }    = client.context

-    selector ?= {}
-    selector.originId = delegate.getId()
-    selector.status   = { $ne: 'Terminated' }
-    selector.group    = group
+    selector                ?= {}
+    selector.group           = group
+    selector.originId        = delegate.getId()
+    selector['status.state'] = { $ne: 'Destroying' }

     return selector

@@ -287,8 +287,7 @@

       return callback err  if err

-      # TODO Implement delete methods.
-      @update { $set: { status: 'Terminating' } }
+      @update { $set: { status: { state: 'Destroying' } } }

       JProposedDomain  = require './domain'
       JMachine = require './computeproviders/machine'


Fixing touch tests to pass in IE

Fixing touch tests to pass in IE

--- test/touch-events.spec.coffee
+++ test/touch-events.spec.coffee
@@ -18,13 +18,18 @@
     test 'tap textnode', (done) ->
         element = $('.a').get(0)
         text = element.childNodes[0]
+        counter = new Number.Counter

         events.addListener element, 'tap', (event) ->
             assert.equal event.target, element
+            counter.step()
+        down(text, 10, 10)
+        up(text)
+
+        setTimeout ->
             events.removeListener element, '*'
             done()
-        down(text, 10, 10)
-        up(text)
+        , 0

     test 'tap twice', (done) ->
         element = $('.a').get(0)


less longer line

less longer line

--- client/app/Applications/Environments.kdapplication/views/DomainMapperView.coffee
+++ client/app/Applications/Environments.kdapplication/views/DomainMapperView.coffee
@@ -21,13 +21,15 @@
     KD.remote.api.JVM.fetchVmsWithHostnames (err, vms)=>
       if vms

+        hostnameAliases = domain.hostnameAlias
+
         @vmListViewController = new VMListViewController
           viewOptions :
             cssClass  : 'vm-list'

         @vmListViewController.getListView().setData
           domain          : domain
-          hostnameAliases : if domain.hostnameAlias then (hostnameAlias for hostnameAlias in domain.hostnameAlias) else []
+          hostnameAliases : if hostnameAliases then (alias for alias in hostnameAliases) else []

         @vmListViewController.instantiateListItems vms
         @addSubView @vmListViewController.getView()


ProfileView: Remove user presence on avatar

--- client/app/Applications/Members.kdapplication/ContentDisplays/profileview.coffee
+++ client/app/Applications/Members.kdapplication/ContentDisplays/profileview.coffee
@@ -615,13 +615,6 @@

   putNick: (nick) -> "@#{nick}"

-  putPresence: (state) ->
-    """
-

-    #{state or 'offline'}
-

-    """
-
   updateUserHomeLink: ->
     return  unless @userHomeLink

@@ -648,7 +641,6 @@
     {{> @avatar}}
     {{> @followButton}}
     {cite{ @putNick #(profile.nickname)}}
-    {div{ @putPresence #(onlineStatus)}}

     {{> @trollSwitch}}


changed getData to getFormData

changed getData to getFormData

--- client/landing/app/Applications/Account.kdapplication/account/accKiteList.coffee
+++ client/landing/app/Applications/Account.kdapplication/account/accKiteList.coffee
@@ -70,11 +70,12 @@
     @emit "KiteUpdated", kite

   createKite : (form)->
-    data = form.getData()
+    data = form.getFormData()
     KD.remote.api.JKite.create
       description  : data.description
       kiteName     : data.kiteName
-      #count        : data.kiteCallLimit
+      #count        : data.kiteCallLimit
+      #isPublic     : data.publicPrivate
       (err, kite) =>
         if err
           @notify err.message, "fail"
@@ -112,7 +113,7 @@
       overlay   : yes
       cssClass  : "kite-kdmodal"
       width     : 500
-      height    : 187
+      height    : 233
       view      : form
       buttons   :
         "Create New Kite" :
@@ -134,6 +135,11 @@
     @addSubView kiteNameView  = new KDView
       cssClass : "modalformline"
     kiteNameView.addSubView kiteName  = new KDInputView name : "kiteName", placeholder : "Running Kite name..."
+
+    # @addSubView publicOrPrivate   = new KDView
+    #   cssClass : "modalformline"
+    # publicOrPrivate.addSubView yesNo  = new KDOnOffSwitch {name : "publicPrivate", labels : ['Public', 'Private'] }
+

     # @addSubView kiteApiCallLimitView  = new KDView
     #   cssClass : "modalformline"


options and complex binding

options and complex binding

--- src/json-pointer.coffee
+++ src/json-pointer.coffee
@@ -7,6 +7,104 @@
     @name = @constructor.name

 class JsonPointer
+  constructor: (obj, pointer, value) ->
+    return switch arguments.length
+      when 3 then @set(obj, pointer, value)
+      when 2 then @get(obj, pointer)
+      when 1 then @bind({ obj: obj })
+      else null
+
+  @bind: (bindings) ->
+    # object
+    # pointer
+    # value
+
+    obj = bindings.object
+    ptr = bindings.pointer
+    opt = bindings.options
+
+    hasObj = obj != undefined
+    hasPtr = ptr?
+    hasOpt = opt?
+
+    if typeof ptr == 'string'
+      ptr = @parse(ptr)
+
+    api = @constructor.bind(@)
+
+    for own key, val of @
+      api[key] = @[key]
+
+    mergeOptions = (override) ->
+      o = {}
+
+      o.hasOwnProp = override.hasOwnProp ? opt.hasOwnProp
+      o.getProp = override.getProp ? opt.getProp
+      o.setProp = override.setProp ? opt.setProp
+      o.getNotFound = override.getNotFound ? opt.getNotFound
+      o.setNotFound = override.setNotFound ? opt.setNotFound
+      o.delNotFound = override.delNotFound ? opt.delNotFound
+
+      return o
+
+    if hasObj and hasPtr and hasOpt
+      api.set = (value, override) => @set.call(api, obj, ptr, value, mergeOptions(override))
+      api.get = (override) => @get.call(api, obj, ptr, mergeOptions(override))
+      api.has = (override) => @has.call(api, obj, ptr, mergeOptions(override))
+      api.del = (override) => @del.call(api, obj, ptr, mergeOptions(override))
+    else if hasObj and hasPtr
+      api.set = @set.bind(api, obj, ptr)
+      api.get = @get.bind(api, obj, ptr)
+      api.has = @has.bind(api, obj, ptr)
+      api.del = @del.bind(api, obj, ptr)
+    else if hasObj and hasOpt
+      api.set = (ptr, value, override) => @set.call(api, obj, ptr, value, mergeOptions(override))
+      api.get = (ptr, override) => @get.call(api, obj, ptr, mergeOptions(override))
+      api.has = (ptr, override) => @has.call(api, obj, ptr, mergeOptions(override))
+      api.del = (ptr, override) => @del.call(api, obj, ptr, mergeOptions(override))
+    else if hasPtr and hasOpt
+      api.set = (obj, value, override) => @set.call(api, obj, ptr, value, mergeOptions(override))
+      api.get = (obj, override) => @get.call(api, obj, ptr, mergeOptions(override))
+      api.has = (obj, override) => @has.call(api, obj, ptr, mergeOptions(override))
+      api.del = (obj, override) => @del.call(api, obj, ptr, mergeOptions(override))
+    else if hasOpt
+      api.set = (obj, ptr, value, override) => @set.call(api, obj, ptr, value, mergeOptions(override))
+      api.get = (obj, ptr, override) => @get.call(api, obj, ptr, mergeOptions(override))
+      api.has = (obj, ptr, override) => @has.call(api, obj, ptr, mergeOptions(override))
+      api.del = (obj, ptr, override) => @del.call(api, obj, ptr, mergeOptions(override))
+    else if hasObj
+      api.set = @set.bind(api, obj)
+      api.get = @get.bind(api, obj)
+      api.has = @has.bind(api, obj)
+      api.del = @del.bind(api, obj)
+    else if hasPtr
+      api.set = (obj, value, options) => @set.call(api, obj, ptr, value, options)
+      api.get = (obj, options) => @get.call(api, obj, ptr, options)
+      api.has = (obj, options) => @has.call(api, obj, ptr, options)
+      api.del = (obj, options) => @del.call(api, obj, ptr, options)
+    else
+      return @
+
+    api.bind = (override) ->
+      o = {}
+
+      if {}.hasOwnProperty.call(override, 'object')
+        o.object = override.object
+      else if hasObj
+        o.object = obj
+
+      if {}.hasOwnProperty.call(override, 'pointer')
+        o.pointer = override.pointer
+      else if hasObj
+        o.pointer = ptr
+
+      if {}.hasOwnProperty.call(override, 'options')
+        o.options = merge(override.options)
+      else if hasObj
+        o.options = opt
+
+    return api
+
   @escape: (segment) ->
     segment.replace(/~/g, '~0').replace(/\//g, '~1')

@@ -14,16 +112,28 @@
     segment.replace(/~1/g, '/').replace(/~0/g, '~')

   @parse: (str) ->
+    if str == ''
+      return []
+
     if str.charAt(0) != '/'
       throw new JsonPointerError("Invalid JSON pointer: #{str}")

-    return str.substring(1).split('/').map(JsonPointer.escape)
+    return str.substring(1).split('/').map(JsonPointer.unescape)

   @compile: (segments) ->
     segents.map((segment) -> '/' + segment).join()

+  @hasOwnProp: (obj, key) ->
+    {}.hasOwnProperty.call(obj, key)
+
   @hasProp: (obj, key) ->
-    typeof obj == 'object' and {}.hasOwnProperty.call(obj, key)
+    key of obj
+
+  @getProp: (obj, key) ->
+    obj[key]
+
+  @setProp: (obj, key, value) ->
+    obj[key] = value

   @getNotFound: (root, segments, node, iSegment) ->
     undefined
@@ -36,13 +146,17 @@
     else
       return obj[segment] = {}

+  @delNotFound: (root, segments, node, iSegment) ->
+    @

   @set: (obj, pointer, value, options) ->
     if typeof pointer == 'string'
       pointer = JsonPointer.parse(pointer)

-    hasProp = options?.hasProp ? JsonPointer.hasProp
-    setNotFound = options?.setNotFound ? JsonPointer.setNotFound
+    hasProp = options?.hasProp ? JsonPointer.hasOwnProp
+    getProp = options?.getProp ? JsonPointer.getProp.bind(@)
+    setProp = options?.setProp ? JsonPointer.setProp.bind(@)
+    setNotFound = options?.setNotFound ? JsonPointer.setNotFound.bind(@)

     root = obj
     iSegment = 0
@@ -56,12 +170,12 @@
         segment = obj.length

       if iSegment == len
-        obj[segment] = value
+        setProp(obj, segment, value)
         break
       else if not hasProp(obj, segment)
         obj = setNotFound(root, pointer, obj, pointer)
       else
-        obj = obj[segment]
+        obj = getProp(obj, segment)

     return @

@@ -69,8 +183,9 @@
     if typeof pointer == 'string'
       pointer = JsonPointer.parse(pointer)

-    hasProp = options?.hasProp ? JsonPointer.hasProp
-    getNotFound = options?.getNotFound ? JsonPointer.getNotFound
+    hasProp = options?.hasProp ? JsonPointer.hasOwnProp
+    getProp = options?.getProp ? JsonPointer.getProp.bind(@)
+    getNotFound = options?.getNotFound ? JsonPointer.getNotFound.bind(@)

     root = obj
     iSegment = 0
@@ -85,16 +200,17 @@
       if not hasProp(obj, segment)
         return getNotFound(root, pointer, obj, iSegment - 1)
       else
-        obj = obj[segment]
+        obj = getProp(obj, segment)

     return obj

-  @remove: (obj, pointer, options) ->
-    if typeof pointer == 'string'
-      pointer = JsonPointer.parse(pointer)
-
-    hasProp = options?.hasProp ? JsonPointer.hasProp
-    getNotFound = options?.getNotFound ? JsonPointer.getNotFound
+  @del: (obj, pointer, options) ->
+    if typeof pointer == 'string'
+      pointer = JsonPointer.parse(pointer)
+
+    hasProp = options?.hasProp ? JsonPointer.hasOwnProp
+    getProp = options?.getProp ? JsonPointer.getProp.bind(@)
+    delNotFound = options?.delNotFound ? JsonPointer.delNotFound.bind(@)

     root = obj
     iSegment = 0
@@ -110,9 +226,31 @@
         delete obj[segment]
         break
       else if not hasProp(obj, segment)
-        return getNotFound(root, pointer, obj, iSegment - 1)
+        delNotFound(root, pointer, obj, iSegment - 1)
+        break
       else
-        obj = obj[segment]
+        obj = getProp(obj, segment)

     return @

+  @has: (obj, pointer, options) ->
+    if typeof pointer == 'string'
+      pointer = JsonPointer.parse(pointer)
+
+    hasProp = options?.hasProp ? JsonPointer.hasOwnProp
+    getProp = options?.getProp ? JsonPointer.getProp
+
+    iSegment = 0
+    len = pointer.length
+    while iSegment != len
+      segment = pointer[iSegment]
+      ++iSegment
+
+      if not hasProp(obj, segment)
+        return false
+
+      obj = getProp(obj, segment)
+
+    return true
+
+


fix data-route test; it was accidentally overwritten

--- tests/batman/view_test.coffee
+++ tests/batman/view_test.coffee
@@ -1009,10 +1009,10 @@
     QUnit.start()

 unless IN_NODE
-  asyncTest 'should set model instance', 1, ->
+  asyncTest 'should set corresponding href for model and action', 1, ->
     class @App extends Batman.App
       @layout: null
-      @route 'tweet/:id', 'tweets#show', resource: 'tweet'
+      @resources 'tweets'
     class @App.Tweet extends Batman.Model
     class @App.TweetsController extends Batman.Controller
       show: (params) ->
@@ -1022,7 +1022,10 @@
     tweet = new @App.Tweet(id: 1)
     @App.set 'tweet', tweet

-    source = 'click'
+    source = 'index' +
+      'show' +
+      'edit' +
+      'destroy'
     node = document.createElement 'div'
     node.innerHTML = source

@@ -1030,7 +1033,7 @@
       contexts: []
       node: node
     view.ready ->
-      node = $(view.get('node').children[0])
-      equal node.attr('href'), '#!/tweet/1'
+      urls = ($(a).attr('href') for a in view.get('node').children)
+      deepEqual urls, ['#!/tweets', '#!/tweets/1', '#!/tweets/1/edit', '#!/tweets/1/destroy']
       QUnit.start()
     view.get 'node'


Do not serialize range when serializing snapshots

Ranges will be automatically serialized to objects with a structure similar to
`{start: {row: y1, column: x1}, end: {row: y2, column: x2}}`, which we can
easily deserialize by calling `Range.fromObject`. Since marker snapshots are, by
design, immutable data structures, we can avoid the performance drag of cloning
them for serialization purposes.

--- src/marker-layer.coffee
+++ src/marker-layer.coffee
@@ -19,13 +19,7 @@
     store

   @serializeSnapshot: (snapshot) ->
-    result = {}
-    for layerId, markerSnapshots of snapshot
-      result[layerId] = {}
-      for markerId, markerSnapshot of markerSnapshots
-        result[layerId][markerId] = clone(markerSnapshot)
-        result[layerId][markerId].range = markerSnapshot.range.serialize()
-    result
+    snapshot

   @deserializeSnapshot: (snapshot) ->
     result = {}
@@ -33,7 +27,7 @@
       result[layerId] = {}
       for markerId, markerSnapshot of markerSnapshots
         result[layerId][markerId] = clone(markerSnapshot)
-        result[layerId][markerId].range = Range.deserialize(markerSnapshot.range)
+        result[layerId][markerId].range = Range.fromObject(markerSnapshot.range)
     result

   ###


:art: API docs

:art: API docs

--- src/marker-layer.coffee
+++ src/marker-layer.coffee
@@ -74,26 +74,28 @@
     not @destroyed

   ###
-  Section: Public interface
+  Section: Querying
   ###

   # Public: Get an existing marker by its id.
+  #
+  # Returns a {Marker}.
   getMarker: (id) ->
     @markersById[id]

-  # Public: Get all existing markers on the buffer.
+  # Public: Get all existing markers on the marker layer.
   #
   # Returns an {Array} of {Marker}s.
   getMarkers: ->
     marker for id, marker of @markersById

-  # Public: Get the number of markers in the buffer.
+  # Public: Get the number of markers in the marker layer.
   #
   # Returns a {Number}.
   getMarkerCount: ->
     Object.keys(@markersById).length

-  # Public: Find markers conforming to the given parameters.
+  # Public: Find markers in the layer conforming to the given parameters.
   #
   # See the documentation for {TextBuffer::findMarkers}.
   findMarkers: (params) ->
@@ -182,7 +184,7 @@
   # with layers that could contain large numbers of markers.*
   #
   # * `callback` A {Function} that will be called with a {Marker} whenever a
-  #   new {Marker} is created.
+  #   new marker is created.
   #
   # You should prefer {onDidUpdate} when synchronous notifications aren't
   # absolutely necessary.


update missing abstract arguments

update missing abstract arguments

--- src/render/Engine.coffee
+++ src/render/Engine.coffee
@@ -31,7 +31,7 @@
     setBackgroundColor: (color) ->
         throw new Error "[Rotten.Engine] Your render should override setBackgroundColor() method"

-    clearRect: ->
+    clearRect: (x, y, width, height) ->
         throw new Error "[Rotten.Engine] Your render should override clearRect() method"

     save: ->


Merging with what is and returning sounds.

- Merged with whatis.coffee
 - added "what is <term>?" syntax
 - not found messages
- showing example sounds if found

--- src/scripts/urban.coffee
+++ src/scripts/urban.coffee
@@ -8,6 +8,7 @@
 #   None
 #
 # Commands:
+#   hubot what is <term>?         - Searches Urban Dictionary and returns definition
 #   hubot urban me <term>         - Searches Urban Dictionary and returns definition
 #   hubot urban define me <term>  - Searches Urban Dictionary and returns definition
 #   hubot urban example me <term> - Searches Urban Dictionary and returns example
@@ -15,19 +16,40 @@
 # Author:
 #   Travis Jeffery (@travisjeffery)
 #   Robbie Trencheny (@Robbie)
-
-# FIXME merge with whatis.coffee
+#
+# Contributors:
+#   Benjamin Eidelman (@beneidel)

 module.exports = (robot) ->
+
+  robot.respond /what ?is ([^\?]*)[\?]*/i, (msg) ->
+    urbanDict msg, msg.match[1], (found, entry, sounds) ->
+      if !found
+        msg.send "I don't know what \"#{msg.match[1]}\" is"
+        return
+      msg.send "#{entry.definition}"
+      if sounds and sounds.length
+        msg.send "#{sounds.join(' ')}"
+
+
   robot.respond /(urban)( define)?( example)?( me)? (.*)/i, (msg) ->
-    urbanDict msg, msg.match[5], (entry) ->
+    urbanDict msg, msg.match[5], (found, entry, sounds) ->
+      if !found
+        msg.send "\"#{msg.match[5]}\" not found"
+        return
       if msg.match[3]
         msg.send "#{entry.example}"
       else
         msg.send "#{entry.definition}"
+      if sounds and sounds.length
+        msg.send "#{sounds.join(' ')}"

 urbanDict = (msg, query, callback) ->
   msg.http("http://api.urbandictionary.com/v0/define?term=#{escape(query)}")
     .get() (err, res, body) ->
-      callback(JSON.parse(body).list[0])
+      result = JSON.parse(body)
+      if result.list.length
+        callback(true, result.list[0], result.sounds)
+      else
+        callback(false)


Update route

Update route

--- src/routes/index.coffee
+++ src/routes/index.coffee
@@ -17,12 +17,12 @@

 router.get '/wage_theft/report_info', (req, res) ->
   res.json({
-    post_url: "https://docs.google.com/forms/d/1ArpKfNDrrsdNl05VLkT3tECjA0vctk8wnVrMGQSIqIE/formResponse"
+    post_url: "https://docs.google.com/forms/d/12rKqdkVtQTcckc3ZyNp6AY7sZIKlj11k-QVFaAwKjCk/formResponse"
     mapping:
-      "employer[name]": "entry.1223479717"
-      "employee[name]": "entry.1255459603"
-      "employee[email]": "entry.570498028"
-      "employee[phone]": "entry.229881285"
+      "employerName": "entry.1223479717"
+      "employeeName": "entry.1255459603"
+      "employeeEmail": "entry.570498028"
+      "employeePhone": "entry.229881285"
   }).end()

 router.use '/employer_size', employer_size_router


Account: unfinished items commented out

Account: unfinished items commented out

--- client/Account/AppController.coffee
+++ client/Account/AppController.coffee
@@ -17,25 +17,25 @@
         { slug : 'Profile',   title : "User profile",        listType: "username",           listHeader: "Here you can edit your account information." }
         { slug : 'Email',     title : "Email Notifications", listType: "emailNotifications", listHeader: "Email Notifications" }
         { slug : 'Externals', title : "Linked accounts",     listType: "linkedAccounts",     listHeader: "Your Linked Accounts" }
-        { slug : 'Referrals', title : "Referrals",           listType: "referralSystem",     listHeader: "Referrals " }
+        # { slug : 'Referrals', title : "Referrals",           listType: "referralSystem",     listHeader: "Referrals " }
       ]
-    billing :
-      title : "Billing"
-      items : [
-        { slug : "Payment",       title : "Payment methods",    listHeader: "Your Payment Methods",       listType: "methods" }
-        { slug : "Subscriptions", title : "Your subscriptions", listHeader: "Your Active Subscriptions",  listType: "subscriptions" }
-        { slug : "Billing",       title : "Billing history",    listHeader: "Billing History",            listType: "history" }
-      ]
-    develop :
-      title : "Develop"
-      items : [
-        { slug : 'SSH',  title : "SSH keys",    listHeader: "Your SSH Keys",    listType: "keys" }
-      ]
-    danger  :
-      title : "Danger"
-      items : [
-        { slug: 'Delete', title : "Delete Account", listHeader: "Danger Zone", listType: "delete" }
-      ]
+    # billing :
+    #   title : "Billing"
+    #   items : [
+    #     { slug : "Payment",       title : "Payment methods",    listHeader: "Your Payment Methods",       listType: "methods" }
+    #     { slug : "Subscriptions", title : "Your subscriptions", listHeader: "Your Active Subscriptions",  listType: "subscriptions" }
+    #     { slug : "Billing",       title : "Billing history",    listHeader: "Billing History",            listType: "history" }
+    #   ]
+    # develop :
+    #   title : "Develop"
+    #   items : [
+    #     { slug : 'SSH',  title : "SSH keys",    listHeader: "Your SSH Keys",    listType: "keys" }
+    #   ]
+    # danger  :
+    #   title : "Danger"
+    #   items : [
+    #     { slug: 'Delete', title : "Delete Account", listHeader: "Danger Zone", listType: "delete" }
+    #   ]

   constructor:(options={}, data)->


Provide KD.defaultSlug as property which returns 'koding' for loggedIn users and  'guests' for guests.

--- client/app/MainApp/KD.extend.coffee
+++ client/app/MainApp/KD.extend.coffee
@@ -67,12 +67,11 @@

   whoami:-> KD.getSingleton('mainController').userAccount

-  isGuest:-> KD.whoami().type is 'unregistered'
-
   logout:->
     mainController = KD.getSingleton('mainController')
     delete mainController?.userAccount

+  isGuest:-> not KD.isLoggedIn()
   isLoggedIn:-> KD.whoami().type isnt 'unregistered'

   isMine:(account)-> KD.whoami().profile.nickname is account.profile.nickname
@@ -114,3 +113,7 @@
     new KDNotificationView {title, content, duration}

     warn "KodingError:", err.message  unless err.name is 'AccessDenied'
+
+Object.defineProperty KD, "defaultSlug",
+  get:->
+    if KD.isGuest() then 'guests' else 'koding'


Reset fileInput after a submit attempt (fix #170)

--- client/components/SubmitForm.coffee
+++ client/components/SubmitForm.coffee
@@ -125,9 +125,11 @@
                     message: "Неопознанная ошибка"
         newState = {
             @state...
+            wasFile: false
             submit: data.submit
         }
         @setState(newState)
+        document.getElementById("file").value = ""

     render: () ->
         if not @props.myUser?._id


Add notifaction if browser-plus is not installed.

--- lib/flex-tool-bar.coffee
+++ lib/flex-tool-bar.coffee
@@ -1,7 +1,6 @@
 shell = require 'shell'
 path = require 'path'
 UrlReplace = require './url-replace'
-
 module.exports =
   toolBar: null
   configFilePath: null
@@ -17,12 +16,11 @@
     reloadToolBarWhenEditConfigFile:
       type: 'boolean'
       default: true
-    useBrowserPlus:
+    useBrowserPlusWhenItIsActive:
       type: 'boolean'
-      default: false
+      default: true

   activate: ->
-
     @storeGrammar()
     @resolveConfigPath()

@@ -83,8 +81,14 @@
               callback: (url) =>
                 urlReplace = new UrlReplace()
                 url = urlReplace.replace url
-                if ( atom.config.get('flex-tool-bar.useBrowserPlus') ) and atom.packages.isPackageActive('browser-plus')
-                  atom.workspace.open url, split:'right'
+                if atom.config.get('flex-tool-bar.useBrowserPlusWhenItIsActive')
+                  if atom.packages.isPackageActive('browser-plus')
+                    atom.workspace.open url, split:'right'
+                  else
+                    warning = "Package browser-plus is not active. Using default browser instead!"
+                    options = detail: "Use apm install browser-plus to install the needed package."
+                    atom.notifications.addWarning warning, options
+                    shell.openExternal url
                 else
                   shell.openExternal url
               tooltip: btn.tooltip


application first opening compile fix

application first opening compile fix

--- client/app/MainApp/kodingappscontroller.coffee
+++ client/app/MainApp/kodingappscontroller.coffee
@@ -174,8 +174,7 @@
     else
       @fetchCompiledAppSource manifest, (err, script)=>
         if err
-          @compileApp name, (err)->
-            callback err, script
+          @compileApp name, callback
         else
           @defineApp name, script
           callback err, script
@@ -322,7 +321,7 @@
               @defineApp name, res
               loader.notificationSetTitle "App compiled successfully"
               loader.notificationSetTimer 2000
-            callback? err
+            callback? err, res
         else
           loader.destroy()


video: model add state management

video: model add state management

--- client/app/lib/videocollaboration/model.coffee
+++ client/app/lib/videocollaboration/model.coffee
@@ -3,6 +3,14 @@
 OpenTokService = require './services/opentok'

 module.exports = class VideoCollaborationModel extends kd.Object
+
+  defaultState:
+    video              : on
+    audio              : on
+    publishing         : off
+    connectedToSession : no
+    maxConnectionCount : 999
+    activeParticipant  : null

   # @param {SocialChannel} options.channel
   constructor: (options = {}, data) ->
@@ -10,6 +18,7 @@
     super options, data

     @_service = OpenTokService.getInstance()
+    @state = _.assign {}, options.state, @defaultState

     { @channel, @view } = options

@@ -175,7 +184,11 @@

   ###*
+   * Merges instance state with given state.
    *
+   * @param {object} _state
+   * @return {object} state
   ###
+  setState: (state) -> @state = _.assign {}, @state, state


kite: update browser client

kite: update browser client

--- client/landing/app/MainApp/kite/kontrol.coffee
+++ client/landing/app/MainApp/kite/kontrol.coffee
@@ -17,11 +17,6 @@

     @kite = new NewKite kite, authentication
     @kite.connect()
-    @watchKites {}, (kite)=>
-      @emit "kiteRegistered", kite
-    , (error)->
-      log "Cannot watch Kites:", error
-

   # Calls the callback function with the list of NewKite instances.
   # The returned kites are not connected. You must connect with NewKite.connect().
@@ -36,31 +31,24 @@
   #   hostname    string
   #   id          string
   #
-  getKites: (query={}, onKites, onError)->
+  getKites: (query={}, onKites, onError, onEvent)->
     if not query.username
       query.username = "#{KD.nick()}"
+    if not query.environment
+      query.environment = "production"

-    @kite.tell "getKites", query, (err, kites)=>
+    eventCB = (e)=>
+      log "kite event: ", {e}
+      if onEvent
+        onEvent e.action, @_createKite e.kite
+
+    @kite.tell "getKites", [query, eventCB], (err, kites)=>
       log "getKites result: ", {err}, {kites}
       if err
         onError err
       else
         onKites (@_createKite k for k in kites)

-  # Takes the same query parameters as getKites but calls the callback function
-  # when a Kite matching the query is registered.
-  watchKites: (query={}, onKite, onError)->
-    if not query.username
-      query.username = "#{KD.nick()}"
-
-    @kite.tell "watchKites", [query, (kite)=>
-      log "watchKites kite: ", {kite}
-      onKite @_createKite kite
-    ], (err, result)=>
-      log "watchKites result: ", {err}, {result}
-      if err
-        onError err
-
   # Returns a new NewKite instance from Kite data structure coming from
   # getKites() and watchKites() methods.
   _createKite: (k)->


add default option

add default option

--- src/simple-select.coffee
+++ src/simple-select.coffee
@@ -2,7 +2,7 @@
 	list = options.list || {'Not specified': null}

 	result = $('<div>')
-	result.text 'Select...'
+	result.text options.default || 'Select...'
 	ul = $('<ul>')
 	result.on 'click', ->
 		ul.toggleClass 'show'


Add javascript Ising Model as project

Add javascript Ising Model as project

--- atom/dot-atom/projects.glasbren-oryxpro.cson
+++ atom/dot-atom/projects.glasbren-oryxpro.cson
@@ -21,10 +21,10 @@
     ]
   }
   {
-    title: "JS Random Walk"
-    group: "Development"
+    title: "ising.js"
+    group: "teaching"
     paths: [
-      "~/Development/js-random-walk"
+      "~/Courses/CDS-101/simulations/ising-model"
     ]
   }
 ]


new article router

new article router

--- index.coffee
+++ index.coffee
@@ -34,6 +34,19 @@
     res.end()
     db.close()

+addArticle = (req, res) ->
+  db = new sqlite3.Database dbFile
+  aid = '5'
+  title = 'title 555'
+  content = 'content 55555555555555555555555555555555'
+  db.run "INSERT INTO articles values ('#{aid}', '#{title}', '#{content}', datetime('now','localtime'))", (err) ->
+    if err
+      console.log err, 'reeeeeeerrrrrr'
+      res.end();
+    res.writeHead 200, {'Content-Type': 'text/html'}
+    res.end("insert success")
+    db.close()
+
 onRequest = (req, res) ->

   if req.method is 'GET'
@@ -42,6 +55,7 @@
     switch  pathname
       when "/" then listArticles req, res
       when "/article" then showArticle req, res
+      when "/new_article" then addArticle req, res
       else
         if /\.(css)$/.test(pathname)
           res.writeHead 200, {'Content-Type': 'text/css'}


Coffee version of Unit 1 Final Quiz

Coffee version of Unit 1 Final Quiz

--- cs101/unit1.coffee
+++ cs101/unit1.coffee
@@ -0,0 +1,9 @@
+# 1.39 Final quiz
+page ='

'
+start_link = page.indexOf('<a href=')
+start_quote = page.indexOf('"', start_link)
+end_quote = page.indexOf('"', start_quote+1)
+
+url = page[start_quote+1..end_quote-1]
+
+# Note that we have to place with indexes differently here in JavaScript strings! Otherwise, it is basically the same.


删除package下的chameleon菜单

删除package下的chameleon菜单

--- menus/atom-chameleon.cson
+++ menus/atom-chameleon.cson
@@ -8,34 +8,6 @@
   ]

 'menu': [
-  {
-    'label': 'Packages'
-    'submenu': [
-      'label': 'Chameleon'
-      'submenu': [
-        {
-          'label': 'Create Project',
-          'command': 'atom-chameleon:create-project'
-        }
-        {
-          'label': 'Framework Install/Upgrade'
-          'command': 'atom-chameleon:install'
-        }
-        {
-          'label': 'Start Debug Server'
-          'command': 'atom-chameleon:run-on-server'
-        }
-        {
-          'label': 'Launch Emulator'
-          'command': 'atom-chameleon:emulator'
-        }
-        {
-          'label': 'Package'
-          'command': 'atom-chameleon:list-package'
-        }
-      ]
-    ]
-  }
   {
     'label': 'Chameleon'
     'submenu': [


Completed VM

Completed VM

--- vm.coffee
+++ vm.coffee
@@ -21,7 +21,8 @@
       switch code[pc]
         when op.HALT then return 0
         when op.POP then stack.pop()
-        when op.LDCONST then stack.push @consts[code[++pc]]
+        when op.LDCONST
+          stack.push @consts[code[++pc]]
         when op.LDLOCAL
           _local = localContext[code[++pc]]
           if typeof _local == 'undefined'
@@ -59,30 +60,38 @@
             ), _args
         when op.RET then return stack.pop()
         when op.JT
-          if stack.pop() then pc = code[++pc] else ++pc
+          ++pc
+          if stack.pop()
+            pc = code[pc]
+            continue
         when op.JF
-          unless stack.pop() then pc = code[++pc] else ++pc
-        when op.JMP then pc = code[++pc]
+          ++pc
+          unless stack.pop()
+            pc = code[pc]
+            continue
+        when op.JMP
+          pc = code[++pc]
+          continue
         when op.ADD
+          _y = stack.pop()
           _x = stack.pop()
-          _y = stack.pop()
           stack.push _x + _y
         when op.SUB
+          _y = stack.pop()
           _x = stack.pop()
-          _y = stack.pop()
           stack.push _x - _y
         when op.MUL
+          _y = stack.pop()
           _x = stack.pop()
-          _y = stack.pop()
           stack.push _x * _y
         when op.DIV
           # x / 0 = Infinity
+          _y = stack.pop()
           _x = stack.pop()
-          _y = stack.pop()
           stack.push _x / _y
         when op.MOD
+          _y = stack.pop()
           _x = stack.pop()
-          _y = stack.pop()
           stack.push _x % _y
         when op.DELLOCAL
           delete localContext[code[++pc]]
@@ -97,64 +106,64 @@
         when op.NEG
           stack[stack.length - 1] = -(stack[stack.length - 1])
         when op.LSHIFT
+          _y = stack.pop()
           _x = stack.pop()
-          _y = stack.pop()
           stack.push _x << _y
         when op.URSHIFT
+          _y = stack.pop()
           _x = stack.pop()
-          _y = stack.pop()
           stack.push _x >>> _y
         when op.RSHIFT
+          _y = stack.pop()
           _x = stack.pop()
-          _y = stack.pop()
           stack.push _x >> _y
         when op.LTE
+          _y = stack.pop()
           _x = stack.pop()
-          _y = stack.pop()
           stack.push _x <= _y
         when op.GTE
+          _y = stack.pop()
           _x = stack.pop()
-          _y = stack.pop()
           stack.push _x >= _y
         when op.LT
+          _y = stack.pop()
           _x = stack.pop()
-          _y = stack.pop()
           stack.push _x < _y
         when op.GT
+          _y = stack.pop()
           _x = stack.pop()
-          _y = stack.pop()
           stack.push _x > _y
         when op.EQ
+          _y = stack.pop()
           _x = stack.pop()
-          _y = stack.pop()
           stack.push _x == _y
         when op.NEQ
+          _y = stack.pop()
           _x = stack.pop()
-          _y = stack.pop()
           stack.push _x != _y
         when op.NOT
           stack[stack.length - 1] = !(stack[stack.length - 1])
         when op.BNEG
           stack[stack.length - 1] = ~(stack[stack.length - 1])
         when op.BAND
+          _y = stack.pop()
           _x = stack.pop()
-          _y = stack.pop()
           stack.push _x & _y
         when op.BXOR
+          _y = stack.pop()
           _x = stack.pop()
-          _y = stack.pop()
           stack.push _x ^ _y
         when op.BOR
+          _y = stack.pop()
           _x = stack.pop()
-          _y = stack.pop()
           stack.push _x | _y
         when op.AND
+          _y = stack.pop()
           _x = stack.pop()
-          _y = stack.pop()
           stack.push _x && _y
         when op.OR
+          _y = stack.pop()
           _x = stack.pop()
-          _y = stack.pop()
           stack.push _x || _y
         when op.ROT
           _x = stack.pop()


fix unit tests

fix unit tests

timekeeper must be called before SandboxedModule.require

--- services/document-updater/test/unit/coffee/RedisManager/RedisManagerTests.coffee
+++ services/document-updater/test/unit/coffee/RedisManager/RedisManagerTests.coffee
@@ -13,6 +13,7 @@
 			auth: () ->
 			exec: sinon.stub()
 		@rclient.multi = () => @rclient
+		tk.freeze(new Date())
 		@RedisManager = SandboxedModule.require modulePath,
 			requires:
 				"logger-sharelatex": @logger = { error: sinon.stub(), log: sinon.stub(), warn: sinon.stub() }
@@ -50,6 +51,9 @@
 				"./Errors": Errors
 			globals:
 				JSON: @JSON = JSON
+
+		afterEach ->
+			tk.reset()

 		@doc_id = "doc-id-123"
 		@project_id = "project-id-123"
@@ -323,12 +327,8 @@

 		describe "with a consistent version", ->
 			beforeEach ->
-				tk.freeze(new Date())
 				@RedisManager.getDocVersion.withArgs(@doc_id).yields(null, @version - @ops.length)
 				@RedisManager.updateDocument @doc_id, @lines, @version, @ops, @ranges, @callback
-
-			afterEach ->
-				tk.reset()

 			it "should get the current doc version to check for consistency", ->
 				@RedisManager.getDocVersion


Improve logging for debugging

Improve logging for debugging

--- services/web/app/coffee/Features/Collaborators/CollaboratorsInviteHandler.coffee
+++ services/web/app/coffee/Features/Collaborators/CollaboratorsInviteHandler.coffee
@@ -11,11 +11,12 @@
 module.exports = CollaboratorsInviteHandler =

 	getAllInvites: (projectId, callback=(err, invites)->) ->
-		logger.log {projectId}, "fetching invites from mongo"
+		logger.log {projectId}, "fetching invites for project"
 		ProjectInvite.find {projectId: projectId}, (err, invites) ->
 			if err?
 				logger.err {err, projectId}, "error getting invites from mongo"
 				return callback(err)
+			logger.log {projectId, count: invites.length}, "found invites for project"
 			callback(null, invites)

 	getInviteCount: (projectId, callback=(err, count)->) ->


Add arc() drawing method.

Add arc() drawing method.

--- lib/mixins/vector.coffee
+++ lib/mixins/vector.coffee
@@ -102,6 +102,60 @@
   circle: (x, y, radius) ->
     @ellipse x, y, radius

+  arc: (x, y, radius, startAngle, endAngle, anticlockwise = false) ->
+    TWO_PI = 2.0 * Math.PI
+    HALF_PI = 0.5 * Math.PI
+
+    deltaAng = endAngle - startAngle
+
+    if Math.abs(deltaAng) > TWO_PI
+      #draw only full circle if more than that is specified
+      deltaAng = TWO_PI
+
+    else if (deltaAng != 0 && anticlockwise != (deltaAng < 0))
+      #necessary to flip direction of rendering
+      dir = if anticlockwise then -1 else 1
+      deltaAng = dir * TWO_PI + deltaAng
+
+    numSegs = Math.ceil(Math.abs(deltaAng) / HALF_PI)
+    segAng = deltaAng / numSegs
+    handleLen = (segAng / HALF_PI) * KAPPA * radius
+    curAng = startAngle
+
+    # component distances between anchor point and control point
+    deltaCx = -Math.sin(curAng) * handleLen
+    deltaCy = Math.cos(curAng) * handleLen
+
+    # anchor point
+    ax = x + Math.cos(curAng) * radius
+    ay = y + Math.sin(curAng) * radius
+
+    # calculate and render segments
+    @moveTo ax, ay
+
+    for segIdx in [0...numSegs]
+      # starting control point
+      cp1x = ax + deltaCx
+      cp1y = ay + deltaCy
+
+      # step angle
+      curAng += segAng
+
+      # next anchor point
+      ax = x + Math.cos(curAng) * radius
+      ay = y + Math.sin(curAng) * radius
+
+      # next control point delta
+      deltaCx = -Math.sin(curAng) * handleLen
+      deltaCy = Math.cos(curAng) * handleLen
+
+      # ending control point
+      cp2x = ax - deltaCx
+      cp2y = ay - deltaCy
+
+      # render segment
+      @bezierCurveTo cp1x, cp1y, cp2x, cp2y, ax, ay
+
   polygon: (points...) ->
     @moveTo points.shift()...
     @lineTo point... for point in points


adding TODOs

adding TODOs

--- lib/python-indent.coffee
+++ lib/python-indent.coffee
@@ -1,4 +1,9 @@
 {CompositeDisposable} = require 'atom'
+
+# TODO: handle hanging indents
+# TODO: handle hard indents, which CAN also have spaces in them (!!!)
+#       see "stopRange" in "properlyIndent"
+# TODO: unindent back to previous level after all brackets have been closed

 module.exports = PythonIndent =
   config:


Remove duplicate of Theme creation

Remove duplicate of Theme creation

--- src/theme-manager.coffee
+++ src/theme-manager.coffee
@@ -42,7 +42,7 @@
   loadTheme: (name) ->
     try
       theme = new Theme(name)
-      @loadedThemes.push(new Theme(name))
+      @loadedThemes.push(theme)
       @trigger('theme-loaded', theme)
     catch error
       console.warn("Failed to load theme #{name}", error.stack ? error)


Give the focus to the text editor when clicking on it

Otherwise it’s the table that get the focus and things get messy.

--- lib/table-element.coffee
+++ lib/table-element.coffee
@@ -687,6 +687,13 @@
         e.stopImmediatePropagation()
         return false

+    @textEditorSubscriptions.add @subscribeTo @editorElement,
+      'click': (e) =>
+        e.stopPropagation()
+        e.stopImmediatePropagation()
+        e.preventDefault()
+        @editorElement.focus()
+
   subscribeToColumnTextEditor: (editorView) ->
     @textEditorSubscriptions = new CompositeDisposable
     @textEditorSubscriptions.add atom.commands.add 'atom-table-editor atom-text-editor:not([mini])',


Remember how to alphabetize in the language scopes

--- lib/tag-generator.coffee
+++ lib/tag-generator.coffee
@@ -34,8 +34,8 @@
       when 'source.css.less' then 'Css'
       when 'source.css.scss' then 'Css'
       when 'source.elixir'   then 'Elixir'
+      when 'source.fountain' then 'Fountain'
       when 'source.gfm'      then 'Markdown'
-      when 'source.fountain' then 'Fountain'
       when 'source.go'       then 'Go'
       when 'source.java'     then 'Java'
       when 'source.js'       then 'JavaScript'


Add note to add google analytics

Add note to add google analytics

--- client/app/initialize.coffee
+++ client/app/initialize.coffee
@@ -4,6 +4,8 @@
 cookie = require('./modules/cookie')

 {route} = require('./modules/route_actions')
+
+# TODO-1 Add google analytics

 # Require all tasks
 require('./tasks/card')


Re-enabled installation's wizard

Re-enabled installation's wizard

--- client/app/views/main.coffee
+++ client/app/views/main.coffee
@@ -24,7 +24,7 @@
     subscriptions:
         'backgroundChanged': 'changeBackground'

-    wizards: ['quicktour']
+    wizards: ['install', 'quicktour']

     constructor: ->
         @apps          = new AppCollection(window.applications)


--- client/startup/unread.coffee
+++ client/startup/unread.coffee
@@ -53,6 +53,8 @@

 	Tracker.autorun ->
 		siteName = RocketChat.settings.get('Site_Name') or ''
+		# KM EDIT: Hard coded siteName
+		siteName = "Komed Health Demo"

 		unread = Session.get 'unread'
 		fireGlobalEvent 'unread-changed', unread


test #19

test #19

--- lib/touch-gesture.coffee
+++ lib/touch-gesture.coffee
@@ -38,7 +38,9 @@
       @startTouches = event.touches
     else if event.type =='touchend' and event.touches.lenght>0
       @startTouches = null
-    currentTouch = event.touches[0]
+
+    @currentTouch = event.touches[0] if event.touches.lenght>0
+
     event.left = currentTouch.clientX
     event.top = currentTouch.clientY


Fixed a couple of minor merge problems

Fixed a couple of minor merge problems

--- client/views/app/room.coffee
+++ client/views/app/room.coffee
@@ -84,7 +84,7 @@

 		if roomData.t is 'd'
 			username = _.without roomData.usernames, Meteor.user().username
-			userData = getUserData(username)
+			userData = getUserData(username, roomData)
 			if Meteor.user()?.admin is true
 				userData = _.extend userData, Meteor.users.findOne { username: String(username) }

@@ -811,25 +811,6 @@
 	allUsers = RoomManager.allUsers.get()
 	allUsers[username]

-getUserData = (username) ->
-	message = ''
-	status = Session.get 'user_' + username + '_status'
-	if status in ['online', 'away', 'busy']
-		statusMessages = Session.get('user_' + username + '_statusMessages')
-		if (statusMessages?)
-			message = ': ' + statusMessages[status]
-	else
-		status = 'offline'
-	userData = {
-		name: getUser(username)?.name || username
-		# not used, but if wanted, need to set in RoomManager
-		#emails: Session.get('user_' + username + '_emails') || []
-		#phone: Session.get('user_' + username + '_phone')
-		username: String(username)
-		customMessage: message
-		status: status
-	}
-	return userData

 getUserData = (username,room) ->
 	message = ''
@@ -846,7 +827,7 @@
 		#emails: Session.get('user_' + username + '_emails') || []
 		#phone: Session.get('user_' + username + '_phone')
 		username: String(username)
-		isOwner: username[0] is room?.u?._id
+		isOwner: String(username) is room?.u?._id
 		customMessage: message
 		status: status
 	}


handle confirmation not set case

handle confirmation not set case

--- workers/social/lib/social/models/user/index.coffee
+++ workers/social/lib/social/models/user/index.coffee
@@ -1429,7 +1429,7 @@
       JVerificationToken = require '../verificationtoken'
       JVerificationToken.createNewPin _options, (err, confirmation) ->
         console.warn 'Failed to send verification token:', err  if err
-        callback err, confirmation.pin
+        callback err, confirmation?.pin

   validateConvert = (options, callback) ->


SocialWorker: take options argument in JUser.createJWT method

--- workers/social/lib/social/models/user/index.coffee
+++ workers/social/lib/social/models/user/index.coffee
@@ -1690,14 +1690,13 @@
     daisy queue

-  @createJWT: (data) ->
+  @createJWT: (data, options = {}) ->
     { secret, confirmExpiresInMinutes } = KONFIG.jwt

     jwt = require 'jsonwebtoken'

     # uses 'HS256' as default for signing
-    options =
-      expiresInMinutes : confirmExpiresInMinutes
+    options.expiresInMinutes ?= confirmExpiresInMinutes

     return jwt.sign data, secret, options


Remove unnecessary code.

Remove unnecessary code.

--- coffee/visualizer/visualizer.coffee
+++ coffee/visualizer/visualizer.coffee
@@ -106,24 +106,12 @@
         @graphics.fill settings.colors.greenLight
       else
         @graphics.fill settings.colors.greenLightOff
-#        @graphics.drawTriangle(
-#          new Point(0.3, -0.1),
-#          new Point(0.5, 0),
-#          new Point(0.3, 0.1)
-#        )
-#        @graphics.fill settings.colors.greenLight
       # 红灯
       @graphics.drawCircle(new Point(0.2, 0.3), 0.1)
       if not lights[1]
         @graphics.fill settings.colors.redLight
       else
         @graphics.fill settings.colors.redLightOff
-#        @graphics.drawTriangle(
-#          new Point(0.1, 0.2),
-#          new Point(0.2, 0.4),
-#          new Point(0.3, 0.2)
-#        )
-#        @graphics.fill settings.colors.greenLight
       @ctx.restore()

   drawRoad: (road, alpha) ->


Trigger Discourse SSO when signing in a user

--- server/auth.coffee
+++ server/auth.coffee
@@ -25,8 +25,8 @@

   authenticate req, (body) ->
     if !body
-      redirectURL = "https://pokebattle.com/accounts/login"
-      redirectURL += "?next=/sim"
+      redirectURL = "forum.pokebattle.com/session/sso"
+      redirectURL += "?return_path=https://sim.pokebattle.com"
       return res.redirect(redirectURL)
     # The remote URL returns `username`, but we'd like a more unified interface,
     # so we replace `username` with `name`.


activity: remove deprecated call to `ActivityLikeCount::setCount` method

--- client/Social/Activity/views/likeview.coffee
+++ client/Social/Activity/views/likeview.coffee
@@ -48,8 +48,6 @@

     @_currentState = not @_currentState

-    @likeCount.setCount()
-
     if @_currentState
     then @decorateLike()
     else @decorateUnlike()


app: improve Modal component

app: improve Modal component

--- client/app/lib/components/modal/index.coffee
+++ client/app/lib/components/modal/index.coffee
@@ -5,8 +5,11 @@

 class ModalOverlay extends React.Component

+  @defaultProps =
+    onClick: kd.noop
+
   render: ->
-

+

 module.exports = class Modal extends React.Component
@@ -30,11 +33,14 @@

     return null  unless @props.isOpen

-    <Portal {...@getPortalProps()}>
+    <Portal {...@getPortalProps()} ref="modal">

         {@props.children}

     </Portal>
+
+
+  closePortal: -> @props.onClose?()

   renderOverlay: ->
@@ -42,7 +48,7 @@
     return null  unless @props.isOpen and @props.hasOverlay

     return \
-      <Portal isOpened={@props.isOpen}>
+      <Portal ref="overlay" isOpened={@props.isOpen} onClick={@bound 'closePortal'}>
         <ModalOverlay />
       </Portal>


Break logic out into a CheckBox component

--- client/lanes/components/grid/Selections.cjsx
+++ client/lanes/components/grid/Selections.cjsx
@@ -1,14 +1,31 @@
+class CheckBox extends Lanes.React.BaseComponent
+    d: -> @props.query.results.xtraData(@props.row)
+
+    onChange: (ev) -> #, me, results, index) ->
+        @d().selected = ev.target.checked
+        @props.selections.onChange?(@props)
+        @forceUpdate()
+
+    render: ->
+        selected = @d().selected
+        selected = @props.selections.selectionDefault unless selected?
+        <input type="checkbox" checked={selected} onChange={@onChange} />
+
+
 class Lanes.Components.Grid.Selections
     id: 'selected'
     query: false
     textAlign: 'center'
     fixedWidth: 90
-    defaultClicked: true
+    selectionDefault: true
+    sortBy: (row, indx, all) ->
+        false == this.xtraData(indx)?.selected
+
     constructor: (options) ->
         @onChange = options.onChange
         @choices = {}
         _.bindAll(@, 'onColumnClick')
-        @render = _.partial(@render, _, @)
+        @component = _.partial(@component, _, @)

     onColumnClick: (ev, {rowNum, field, query}) ->
         unless ev.target.tagName is 'INPUT'
@@ -17,14 +34,5 @@
             @select({target: input}, @, query.results, rowNum)
         ev.stopPropagation()

-    select: (ev, me, results, index) ->
-        x = results.xtraData(index)
-        x.selected = ev.target.checked
-        me.onChange?(index)
-
-    render: (props, me) ->
-        id = props.row[props.query.idIndex]
-        x = props.query.results.xtraData(props.index)
-        x.selected = me.defaultClicked unless x.selected?
-        <input type="checkbox" defaultChecked={x.selected}
-            onChange={_.partial(me.select, _, me, props.query.results, props.index)} />
+    component: (props, me) ->
+        <CheckBox {...props} selections={me} />


Format visualisation data in tests

Format visualisation data in tests

--- client/test/src/views/line_chart_view.coffee
+++ client/test/src/views/line_chart_view.coffee
@@ -20,7 +20,7 @@

 test('.formatData converts sub indicator data in to datasets suitable
   for the LineChart View', ->
-  indicatorData =
+  indicatorData =
     results: [
       {
         date: "2013-01-01T01:00:00.000Z",
@@ -63,10 +63,12 @@
       yAxis: 'value'
   )

+  console.log '# creating  # vis # for # formatData'
   visualisation = Factory.visualisation(
     indicator: indicator
-    data: indicatorData
   )
+
+  visualisation.set('data', visualisation.formatData(indicatorData))

   lineChartView = new Backbone.Views.LineChartView(visualisation: visualisation)


removing sessions' dependency

removing sessions' dependency

--- client/views/app/chatMessageDashboard.coffee
+++ client/views/app/chatMessageDashboard.coffee
@@ -24,9 +24,9 @@

 	message: ->
 		switch this.t
-			when 'r' then t('Room_name_changed', { room_name: this.msg, user_by: Session.get('user_' + this.u._id + '_name') }) + '.'
-			when 'au' then t('User_added_by', { user_added: this.msg, user_by: Session.get('user_' + this.u._id + '_name') })
-			when 'ru' then t('User_removed_by', { user_removed: this.msg, user_by: Session.get('user_' + this.u._id + '_name') })
+			when 'r' then t('Room_name_changed', { room_name: this.msg, user_by: this.u.username })
+			when 'au' then t('User_added_by', { user_added: this.msg, user_by: this.u.username })
+			when 'ru' then t('User_removed_by', { user_removed: this.msg, user_by: this.u.username })
 			when 'ul' then t('User_left', this.msg)
 			when 'nu' then t('User_added', this.msg)
 			when 'wm' then t('Welcome', this.msg)
@@ -66,4 +66,4 @@
 	parent = message.parent().children().last()

 	if message.get(0) is parent.get(0)
-		ScrollListener.toBottom(false)+		ScrollListener.toBottom(false)


Fix clear process when redrawing map image

--- server/app/assets/javascript/map_image.coffee
+++ server/app/assets/javascript/map_image.coffee
@@ -201,6 +201,7 @@
     arrow(@ctx, x1, y1, x2, y2, 20, 40, 30, 7)

   clear: () ->
+    @ctx.clearRect(0, 0, @tag[0].width, @tag[0].height)
     @setImage()
     @setLayers()


Validator.match() improved to accept fields that should be ignored.

--- server/src/Component/Builtin/Validator.coffee
+++ server/src/Component/Builtin/Validator.coffee
@@ -82,17 +82,16 @@

         # check schema field presence
         for key of data
+            # if field must not be ignored
+            if (expression + key) in skipMatch
+                continue
             # schema for this field was not set, block
             if validate[expression + key] is undefined
-                # if field must not be ignored
-                if (expression + key) in skipMatch
-                    continue
-                else
-                    return (
-                        field: expression + key
-                        level: level
-                        error: 'denied'
-                    )
+                return (
+                    field: expression + key
+                    level: level
+                    error: 'denied'
+                )

             # validate set and it is an object: recursive
             if data[key] isnt null and typeof data[key] is 'object'


tests FTW

tests FTW

--- api/apps/users/model.coffee
+++ api/apps/users/model.coffee
@@ -111,7 +111,7 @@

       # Denormalize user's data into articles they authored in the background
       db.articles.update(
-        { author_id: userId }
+        { author_id: newUser._id }
         { $set: author: denormalizedForArticle(newUser) }
         (err) -> debug err if err
       )