Moving toward a subsequent plan.

Moving toward a subsequent plan.

--- src/lib/strata.coffee
+++ src/lib/strata.coffee
@@ -5,6 +5,7 @@
   constructor: (@strata, @object, @fields, initial, subsequent, swap, penultimate, @callback) ->
     @levels = []
     @decisions = { initial, subsequent, swap, penultimate }
+    @exclusive = false

 class Level
   # Construct a new level. The `exclusive` pararameter determines how this level
@@ -12,25 +13,27 @@
   constructor: (@exclusive) ->
     @operations = []
     @locks = []
-  lock: (strata, mutation, tier, callback) ->
-    locks = (strata.locks[tier.address] or= [ [] ])
-    if @exclusive
-      throw new Error "already locked" if @locks.length
-      lock = [ callback, strata, mutation ]
-      lock.address = tier.address
-      @locks.push lock
-      locks.push [ lock ]
-      locks.push []
-      if locks[0].length is 0
-        locks.shift()
-        callback.call strata, mutation
-    else
-      lock = [ callback, strata, mutation ]
-      lock.address = tier.address
-      @locks.push lock
-      locks[locks.length - 1].push lock
-      if locks.length is 1
-        callback.call strata, mutation
+  lock: (strata, mutation, address, callback) ->
+    # TODO Will it matter if we load before we lock? We probably have to.
+    strata.io.load strata, mutation, address, (mutation, tier) =>
+      locks = (strata.locks[tier.address] or= [ [] ])
+      if @exclusive
+        throw new Error "already locked" if @locks.length
+        lock = [ callback, strata, mutation, tier ]
+        lock.address = tier.address
+        @locks.push lock
+        locks.push [ lock ]
+        locks.push []
+        if locks[0].length is 0
+          locks.shift()
+          callback.call strata, mutation, tier
+      else
+        lock = [ callback, strata, mutation, tier ]
+        lock.address = tier.address
+        @locks.push lock
+        locks[locks.length - 1].push lock
+        if locks.length is 1
+          callback.call strata, mutation, tier

   # Remove the lock callback from the callback list..
   release: (strata) ->
@@ -107,9 +110,8 @@

     address

-  load: (strata, splat..., address, callback) ->
-    splat.push @tiers[address]
-    callback.apply strata, splat
+  load: (strata, mutation, address, callback) ->
+    callback.apply strata, [ mutation, @tiers[address] ]

   dirty: ->

@@ -151,52 +153,41 @@
   #
   # This generalized mutation will insert or remove a single item.
   _generalized: (mutation) ->
-    mutation.levels.push new Level(false)
-    @io.load @, mutation, @rootAddress, @_rootLoaded
-
+    mutation.levels.unshift new Level
+    mutation.levels[0].lock @, mutation, @rootAddress, @_rootLoaded
+
+  # Perform the root decision with only a read lock obtained.
   _rootLoaded: (mutation, root) ->
-    mutation.parentLevel = new Level(false)
-    mutation.parentLevel.tier = root
-    mutation.levels.push mutation.parentLevel
-    mutation.parentLevel.lock @, mutation, mutation.parentLevel.tier, @_initialTest
-
-  # Perform the root decision with only a read lock obtained. If it decides to
-  # mutate the root, we will
-  _initialTest: (mutation) ->
-    mutation.childLevel = new Level(false)
-    mutation.levels.push mutation.childLevel
-    if mutation.decisions.initial.call @, mutation
-      mutation.parentLevel.operations.clear()
-      mutation.parentLevel.upgrade mutation.childLevel, @_initialTestAgain
-    else
-      @_tierDecision mutation
-
-  _initialTestAgain: (mutation) ->
-    if not mutation.initial.call @, mutation
-      mutation.rewind 0
+    mutation.levels[0].tier = root
+    mutation.decisions.initial.call @, mutation
     @_tierDecision mutation

   # Determine if we need to lock a tier. We have different criteria for inner
   # tiers with inner tier children then for penultimate inner tiers with leaf
   # tier children.
-  #
-  # When we encounter a penultimate tier, we continue on to peform the
-  # operations. Otherwise, we make another decision using the next inner tier
-  # child.
   _tierDecision: (mutation) ->
-    if mutation.parentLevel.tier.penultimate
-      @_testInnerTier mutation, mutation.decisions.penultimate, 1, @_operate
+    if mutation.level[0].tier.penultimate
+      @_testInnerTier mutation, mutation.decisions.penultimate, @_operate
     else
-      @_testInnerTier mutation, mutation.decisions.subsequent, 0, @_tierDescend
+      @_testInnerTier mutation, mutation.decisions.subsequent, @_tierDescend
+
+  # Perform decisions related to the inner tier.
+  _testInnerTier: (mutation, decision, next) ->
+    mutation.decisions.swap.call @, mutation
+    if not decision.call @, mutation
+      for level in mutation.levels
+        level.operations = level.operations.filter (operation) ->
+          operation isnt "swap"
+    next.call @, mutation

   _tierDescend: (mutation) ->
     branch = @_find(mutation.parentLevel.tier, mutation.sought)
     @io.load mutation.parent, branch, (child) =>
       mutation.parent = child
       mutation.parentLevel = mutation.childLevel
-      mutation.childLevel = new Level(mutation.parentLevel.exclusive)
+      mutation.childLevel = new Level()
       mutation.levels.push mutation.childLevel
-      mutation.shift @_tierDecision
+      @_tierDecision mutation

   # When `_operate` is invoked, all the necessary locks have been obtained and
   # we are able to mutate the tree with impunity.
@@ -281,52 +272,12 @@
         low--
       return low
     return low - 1
-
-  _testInnerTier: (mutation, decision, leaveExclusive, next) ->
-    tiers = decision.call @, mutation
-    keys = mutation.decisions.swap.call @, mutation
-    if tiers or keys
-      if not (mutation.parentLevel.exclusive and mutation.childLevel.exclusive)
-        mutation.parentLevel.upgrade mutation.childLevel, =>
-          mutation.parentLevel.operations.clear()
-          mutation.childLevel.operations.clear()
-          @_testInnerTier mutation, leaveExclusive, next
-      else if not tiers
-        mutation.rewind leaveExclusive
-        next.call @, mutation
-      else
-        next.call @, mutation
-    else
-      @_rewind mutation, leaveExclusive
-      next.call @, mutation
-
-  _rewind: (mutation, leaveExclusive) ->
-    count = mutation.levels.length - leaveExclusive
-    if count > 0
-      unlock = true
-      index = 0
-      for ignored in  [0...count]
-        level = mutation.levels[index]
-        level.operations = level.operations.filter (operation) ->
-          not operation.isSplitOrMerge
-        unlock = unlock or level.operations.length is 0
-        if unlock
-          if mutation.levels.length <= 3
-            level.downgrade(@)
-            index++
-          else
-            level.release()
-            levels.splice(index, 1)
-        else
-          index++

   # If the root is full, we add a root split operation to the operation stack.
   _shouldDrainRoot: (mutation) ->
-    if @innerSize is mutation.parentLevel.tier.size()
-      mutation.parentLevel.operations.add @_splitRoot
-      true
-    else
-      false
+    console.log "_shouldDrainRoot"
+    if @innerSize is mutation.levels[0].tier.size()
+      mutation.levels[0].operations.push "splitRoot"

   # Shorthand to allocate a new inner tier.
   _newInnerTier: (mutation, penultimate) ->
@@ -572,6 +523,7 @@
     return false

   _shouldSplitInner: (mutation) ->
+    console.log "_shouldSplitInner"
     structure = mutation.getStructure()
     branch = parent.find(mutation.getComparable())
     child = structure.getStorage().load(mutation.getStash(), parent.getChildAddress(branch))
@@ -586,13 +538,14 @@
   # Determine if the leaf that will hold the inserted value is full and ready
   # to split, if it is full and part of linked list of b+tree leaves of
   # duplicate index values, or it it can be inserted without splitting.
+  #
+  # TODO Now there is a chance that this might already be in a split state. What
+  # do we do? Do we create a new plan as we decend to test the current plan?
   _howToInsertLeaf: (mutation) ->
-    # By default, return false.
-    split = true

     # Find the branch that navigates to the leaf child.
     branch = @_find mutation.parentLevel.tier, mutation.fields
-    leaf = @io.load @, mutation, mutation.parentLevel.tier.get(branch).address, @_inspectLeafForSplitLoaded
+    @io.load @, mutation, mutation.parentLevel.tier.get(branch).address, @_inspectLeafForSplitLoaded

   # After load, lock the leaf exclusively.
   _inspectLeafForSplitLoaded: (mutation, leaf) ->


Change direction relative to last position moved

--- game.coffee
+++ game.coffee
@@ -87,7 +87,7 @@
       @key = keyCode

     handle_input: (time) ->
-      lastpos = @player0.lastPos()
+      lastpos = @player0.currentLinePos()
       player = @player0
       switch @key
         when 37


Work with literal authors

Work with literal authors

Updated icon in auto-completion

--- lib/provider.coffee
+++ lib/provider.coffee
@@ -48,8 +48,8 @@
                 text: word.key
                 displayText: word.label
                 leftLabel: word.key
-                type: "constant"
-                iconHTML: ''
+                type: "value"
+                iconHTML: ''
               }
               suggestions = suggestions.concat suggestion
             resolve(suggestions)


:lipstick: Rename e to event

:lipstick: Rename e to event

--- lib/snippets.coffee
+++ lib/snippets.coffee
@@ -95,19 +95,19 @@

   enableSnippetsInEditor: (editorView) ->
     editor = editorView.getEditor()
-    editorView.command 'snippets:expand', (e) =>
+    editorView.command 'snippets:expand', (event) =>
       if editor.getSelection().isEmpty()
-        e.abortKeyBinding() unless @expandSnippetUnderCursor(editor)
+        event.abortKeyBinding() unless @expandSnippetUnderCursor(editor)
       else
-        e.abortKeyBinding()
+        event.abortKeyBinding()

-    editorView.command 'snippets:next-tab-stop', (e) ->
+    editorView.command 'snippets:next-tab-stop', (event) ->
       unless editor.snippetExpansion?.goToNextTabStop()
-        e.abortKeyBinding()
+        event.abortKeyBinding()

-    editorView.command 'snippets:previous-tab-stop', (e) ->
+    editorView.command 'snippets:previous-tab-stop', (event) ->
       unless editor.snippetExpansion?.goToPreviousTabStop()
-        e.abortKeyBinding()
+        event.abortKeyBinding()

   # Get a RegExp of all the characters used in the snippet prefixes
   getWordRegex: (snippets) ->


Use Workspace::addOpener

Use Workspace::addOpener

--- lib/snippets.coffee
+++ lib/snippets.coffee
@@ -13,7 +13,7 @@
   loaded: false

   activate: ->
-    atom.workspace.registerOpener (uri) =>
+    atom.workspace.addOpener (uri) =>
       if uri is 'atom://.atom/snippets'
         atom.workspace.open(@getUserSnippetsPath())


Close socket on terminal reset

Close socket on terminal reset

--- lib/terminal.coffee
+++ lib/terminal.coffee
@@ -41,7 +41,8 @@
     "#{protocol}://#{@host}:#{@port}/#{@path}?token=#{@token}"

   reset: ->
-    @socket.connect()
+    @socket.close()
+    setTimeout(@connect.bind(this), 100)

   send: (data) ->
     if @socket


Tiny bug

Tiny bug

--- templates/example-aerix/javascript/invoice-view.coffee
+++ templates/example-aerix/javascript/invoice-view.coffee
@@ -63,7 +63,7 @@
     # Table footer part
     footerAggregate = {}
     footerAggregate.subTotal = @model.subTotal()
-    footerAggregate.vat = @model.VAT(footerAggregate.subTotal)
+    footerAggregate.vat = @model.VAT(footerAggregate.subtotal)
     footerAggregate.total = footerAggregate.subtotal + footerAggregate.vat
     @_mapObjToDOM footerAggregate, @$("div#invoice-body table tfoot")
     @


resolve field aliases to fieldnrs (not rangekeys)

--- src/mkts-table.coffee
+++ src/mkts-table.coffee
@@ -203,18 +203,14 @@
   list of cellkeys and / or cellrange literals. ###
   return @_resolve_aliases me, selector.split /\s*,\s*/ if CND.isa_text selector
   R = new Set()
-  for alias in selector
-    continue unless alias.startsWith '@'
-    unless ( fieldnrs = me.fieldnrs_by_aliases[ alias ] )?
-      ### TAINT error or failure? ###
-      throw new Error "(MKTS/TABLE µ5446) unknown alias #{rpr alias}"
-    ### TAINT avoid to compute rangeref only to parse it afterwards ###
-    ### TAINT this should be an INTERGRID method ###
-    for fieldnr in fieldnrs
-      d           = me.fieldcells[ fieldnr ]
-      topleft     = IG.CELLS.get_cellkey me.grid, { colnr: d.left_colnr,  rownr: d.top_rownr,     }
-      bottomright = IG.CELLS.get_cellkey me.grid, { colnr: d.right_colnr, rownr: d.bottom_rownr,  }
-      R.add "#{topleft}..#{bottomright}"
+  for term in selector
+    if ( CND.isa_text term ) and ( term.startsWith '@' )
+      unless ( fieldnrs = me.fieldnrs_by_aliases[ term ] )?
+        ### TAINT error or failure? ###
+        throw new Error "(MKTS/TABLE µ5446) unknown alias #{rpr term}"
+      R.add fieldnr for fieldnr in fieldnrs
+    else
+      R.add term
   return [ R... ]

 #-----------------------------------------------------------------------------------------------------------
@@ -726,16 +722,26 @@
 @_walk_fails_and_fieldnrs_from_selector = ( me, selector ) ->
   ### TAINT this will have to be changed to allow for named fields ###
   count                   = 0
-  seen_field_designations = new Set()
+  seen_fieldnrs           = new Set()
   selector                = @_resolve_aliases me, selector
   #.........................................................................................................
-  for cell from IG.GRID.walk_cells_from_selector me.grid, selector
-    continue unless ( field_designations = me.cellfields[ cell.cellkey ] )?
-    for field_designation in field_designations
-      continue if seen_field_designations.has field_designation
-      seen_field_designations.add field_designation
+  for term in selector
+    if CND.isa_text term
+      for cell from IG.GRID.walk_cells_from_selector me.grid, selector
+        continue unless ( fieldnrs = me.cellfields[ cell.cellkey ] )?
+        for fieldnr in fieldnrs
+          ### TAINT code duplication ###
+          continue if seen_fieldnrs.has fieldnr
+          seen_fieldnrs.add fieldnr
+          count += +1
+          yield [ null, fieldnr, ]
+    else
+      fieldnr = term
+      ### TAINT code duplication ###
+      continue if seen_fieldnrs.has fieldnr
+      seen_fieldnrs.add fieldnr
       count += +1
-      yield [ null, field_designation, ]
+      yield [ null, fieldnr, ]
   #.........................................................................................................
   if count is 0
     yield [ ( _fail me, 'µ5131', "selector #{rpr selector} does not match any field" ), null ]


add test

add test

--- spec/motions-spec.coffee
+++ spec/motions-spec.coffee
@@ -1744,6 +1744,25 @@
       keydown(',')
       expect(editor.getCursorScreenPosition()).toEqual [0, 2]

+    it "gets state from GlobalVimState", ->
+      keydown('f')
+      normalModeInputKeydown('c')
+      expect(editor.getCursorScreenPosition()).toEqual [0, 2]
+
+      # store the current find for 'c'
+      findFC = vimState.globalVimState.currentFind
+
+      keydown('t')
+      normalModeInputKeydown('b')
+      expect(editor.getCursorScreenPosition()).toEqual [0, 3]
+      keydown(';')
+      expect(editor.getCursorScreenPosition()).toEqual [0, 6]
+
+      # simulate a find for 'c' being done elsewhere
+      vimState.globalVimState.currentFind = findFC
+      keydown(';')
+      expect(editor.getCursorScreenPosition()).toEqual [0, 8]
+
   describe 'the % motion', ->
     beforeEach ->
       editor.setText("( ( ) )--{ text in here; and a function call(with parameters) }\n")


Use project.getDirectories()

Use project.getDirectories()

--- spec/project-spec.coffee
+++ spec/project-spec.coffee
@@ -175,25 +175,25 @@
       it "sets its path to the files parent directory and updates the root directory", ->
         atom.project.setPaths([require.resolve('./fixtures/dir/a')])
         expect(atom.project.getPaths()[0]).toEqual path.dirname(require.resolve('./fixtures/dir/a'))
-        expect(atom.project.getRootDirectory().path).toEqual path.dirname(require.resolve('./fixtures/dir/a'))
+        expect(atom.project.getDirectories()[0].path).toEqual path.dirname(require.resolve('./fixtures/dir/a'))

     describe "when path is a directory", ->
       it "sets its path to the directory and updates the root directory", ->
         directory = fs.absolute(path.join(__dirname, 'fixtures', 'dir', 'a-dir'))
         atom.project.setPaths([directory])
         expect(atom.project.getPaths()[0]).toEqual directory
-        expect(atom.project.getRootDirectory().path).toEqual directory
+        expect(atom.project.getDirectories()[0].path).toEqual directory

     describe "when path is null", ->
       it "sets its path and root directory to null", ->
         atom.project.setPaths([])
         expect(atom.project.getPaths()[0]?).toBeFalsy()
-        expect(atom.project.getRootDirectory()?).toBeFalsy()
+        expect(atom.project.getDirectories()[0]?).toBeFalsy()

     it "normalizes the path to remove consecutive slashes, ., and .. segments", ->
       atom.project.setPaths(["#{require.resolve('./fixtures/dir/a')}#{path.sep}b#{path.sep}#{path.sep}.."])
       expect(atom.project.getPaths()[0]).toEqual path.dirname(require.resolve('./fixtures/dir/a'))
-      expect(atom.project.getRootDirectory().path).toEqual path.dirname(require.resolve('./fixtures/dir/a'))
+      expect(atom.project.getDirectories()[0].path).toEqual path.dirname(require.resolve('./fixtures/dir/a'))

   describe ".replace()", ->
     [filePath, commentFilePath, sampleContent, sampleCommentContent] = []


Moved the bulk of the test into waitsForPromise to eliminate some extra variables.

--- spec/project-spec.coffee
+++ spec/project-spec.coffee
@@ -201,31 +201,23 @@

   describe ".repositoryForDirectory(directory)", ->
     it "resolves to null when the directory does not have a repository", ->
-      theResult = "dummy_value"
-
       waitsForPromise ->
         directory = new Directory("/tmp")
-        atom.project.repositoryForDirectory(directory).then (result) -> theResult = result
-
-      runs ->
-        expect(atom.project.repositoryProviders.length).toBeGreaterThan 0
-        expect(theResult).toBe null
-        expect(atom.project.repositoryPromisesByPath.size).toBe 0
+        atom.project.repositoryForDirectory(directory).then (result) ->
+          expect(result).toBeNull()
+          expect(atom.project.repositoryProviders.length).toBeGreaterThan 0
+          expect(atom.project.repositoryPromisesByPath.size).toBe 0

     it "resolves to a GitRepository and is cached when the given directory is a Git repo", ->
-      theResult = "dummy_value"
-      directory = new Directory(path.join(__dirname, '..'))
-
-      waitsForPromise ->
-        atom.project.repositoryForDirectory(directory).then (result) -> theResult = result
-
-      runs ->
-        dirPath = directory.getRealPathSync()
-        expect(theResult).toBeInstanceOf GitRepository
-        expect(theResult.getPath()).toBe path.join(dirPath, '.git')
-
-        # Verify that the result is cached.
-        expect(atom.project.repositoryForDirectory(directory)).toBe(atom.project.repositoryForDirectory(directory))
+      waitsForPromise ->
+        directory = new Directory(path.join(__dirname, '..'))
+        atom.project.repositoryForDirectory(directory).then (result) ->
+          expect(result).toBeInstanceOf GitRepository
+          dirPath = directory.getRealPathSync()
+          expect(result.getPath()).toBe path.join(dirPath, '.git')
+
+          # Verify that the result is cached.
+          expect(atom.project.repositoryForDirectory(directory)).toBe(atom.project.repositoryForDirectory(directory))

   describe ".setPaths(path)", ->
     describe "when path is a file", ->


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/active_bounds.coffee
+++ src/active_bounds.coffee
@@ -4,7 +4,7 @@
     y: 0
     width: 8
     height: 8
-    activeBounds: Rectangle(0, 0)
+    activeBounds: Rectangle(0, 0, App.width, App,)

   position: (newPosition) ->


Corrected circle drawing in Sketch.

Corrected circle drawing in Sketch.

--- src/client/sketch.coffee
+++ src/client/sketch.coffee
@@ -78,6 +78,7 @@
   ###
   isFillEnabled = true
   isStrokeEnabled = false
+  TWOPI = Math.PI * 2

   computeStyle = (args) ->
     switch args.length
@@ -148,7 +149,7 @@

   circle: (x, y, r) ->
     @g.beginPath()
-    @g.arc x, y, r, 0, 360
+    @g.arc x, y, r, 0, TWOPI, false
     @g.fill() if isFillEnabled
     @g.stroke() if isStrokeEnabled
     @g.closePath()


fix(tests): Fix failing tests

fix(tests): Fix failing tests

--- src/coffee/export.coffee
+++ src/coffee/export.coffee
@@ -244,8 +244,6 @@

   _parse: (csvString) =>
     new Promise (resolve, reject) =>
-      # remove delimiter at the end/beggining of header if there is any
-      csvString = _.trim(csvString, @options.templateDelimiter)

       Csv().from.string(csvString, {delimiter: @options.templateDelimiter})
       .to.array (data, count) ->


:art: pull the MathJax Queue check up

:art: pull the MathJax Queue check up

--- src/components/html.cjsx
+++ src/components/html.cjsx
@@ -48,7 +48,8 @@
     hasMath = root.querySelector('math')

     # Return immediatly if no [data-math] or <math> elements are present
-    return unless window.MathJax and (_.any(nodes) or hasMath)
+    # TODO: If the MathJax Queue is not available then MathJax has not loaded yet. Add a load callback to enqueue.
+    return unless window.MathJax.Hub?.Queue? and (_.any(nodes) or hasMath)

     for node in nodes
       formula = node.getAttribute('data-math')
@@ -57,18 +58,17 @@
         node.textContent = "\u200c\u200c\u200c#{formula}\u200c\u200c\u200c"
       else
         node.textContent = "\u200b\u200b\u200b#{formula}\u200b\u200b\u200b"
-      # TODO: If the MathJax Queue is not available then MathJax has not loaded yet. Add a load callback to enqueue.
-      window.MathJax.Hub?.Queue?(['Typeset', MathJax.Hub, node])
+      window.MathJax.Hub.Queue(['Typeset', MathJax.Hub, node])
       # mark node as processed
       node.classList.add('math-rendered')

     # render MathML with MathJax
-    window.MathJax.Hub?.Queue?(['Typeset', MathJax.Hub, root]) if hasMath
+    window.MathJax.Hub.Queue(['Typeset', MathJax.Hub, root]) if hasMath

     # Once MathJax finishes processing, cleanup the MathJax message nodes to prevent
     # React "Invariant Violation" exceptions.
     # MathJax calls queued events in order, so this will be called after processing completes
-    window.MathJax.Hub?.Queue?([ ->
+    window.MathJax.Hub.Queue([ ->
       for nodeId in ['MathJax_Message', 'MathJax_Hidden', 'MathJax_Font_Test']
         el = document.getElementById(nodeId)
         break unless el # the elements won't exist if MathJax didn't do anything


Account for future ok

Account for future ok

--- server/calculations/updateResults.coffee
+++ server/calculations/updateResults.coffee
@@ -62,9 +62,10 @@
     for submit in submits
         lastSubmitId = submit._id
         lastSubmitTime = submit.time
-        if submit.outcome == "IG"
-            ignored = 1
-            ok = 0
+        if submit.outcome == "IG"
+            if solved == 0
+                ignored = 1
+                ok = 0
             continue
         # any other result resets ignored flag
         ignored = 0
@@ -76,11 +77,11 @@
         else if submit.outcome == "AC"
             solved = 1
             ok = 0
-            break
+	    continue  # we might have a future OK
         else if submit.outcome == "OK"
             ok = 1
             continue  # we might have a future AC
-        else  if submit.outcome != "CE"
+        else if submit.outcome != "CE"
             attempts++
     logger.debug "updated result ", userId, problemId, solved, ok, attempts, ignored, lastSubmitId
     result = new Result


Referral: add campaign start date

Referral: add campaign start date

--- workers/social/lib/social/models/referral/index.coffee
+++ workers/social/lib/social/models/referral/index.coffee
@@ -278,6 +278,7 @@
   CAMPAIGN_NAME                   = "100_TB_CAMPAIGN"
   CAMPAIGN_TOTAL_DISK_SIZE_IN_MB  = 1024*1024*100 # 100TB
   CAMPAIGN_DISK_SIZE_IN_MB        = 1024
+  CAMPAIGN_START_DATE             = new Date("Jan 28 2014 16:00:00 GMT")
   CAMPAIGN_END_DATE               = new Date("Feb 04 2014 16:00:00 GMT")
   OLD_DISK_SIZE_IN_MB             = 250

@@ -365,8 +366,9 @@
         cmp = new JStorage
           name: CAMPAIGN_NAME
           content:
-            diskSpaceLeftMB: CAMPAIGN_TOTAL_DISK_SIZE_IN_MB
-            endDate: CAMPAIGN_END_DATE
+            diskSpaceLeftMB : CAMPAIGN_TOTAL_DISK_SIZE_IN_MB
+            endDate         : CAMPAIGN_END_DATE
+            startDate       : CAMPAIGN_START_DATE

         cmp.save (err)->
           return callback err if err


Added Jester and Saboteur to actionPriority.

--- basicAI.coffee
+++ basicAI.coffee
@@ -250,6 +250,7 @@
     "Library" if my.hand.length <= 3
     "Expand"
     "Remodel"
+    "Jester"
     "Militia"
     "Cutpurse"
     "Bridge"
@@ -294,6 +295,7 @@
     "Ironworks" # should have higher priority if condition can see it will gain an Action card
     "Workshop"
     "Coppersmith"
+    "Saboteur"
     "Library" if my.hand.length <= 7
     "Watchtower" if my.hand.length <= 6
     # Eighth priority: cards that have become useless. Maybe they'll decrease


added support for complete cross product in buildht

--- src/data/ops/util.coffee
+++ src/data/ops/util.coffee
@@ -103,12 +103,15 @@
   #
   # build hash table based on equality of columns
   # @param cols columns to use for equality test
+  # @param complete create partition for cross product of
+  #        partitioned columns?
   # @return [ht, keys]  where
   #   ht = JSON.stringify(key) -> rows
   #   keys: JSON.stringify(key) -> key
-  @buildHT: (t, cols) ->
+  @buildHT: (t, cols, complete=no) ->
     getkey = @createKeyF cols, t.schema

+    uniqvals = {}
     ht = {}
     t.each (oldrow) ->
       row = oldrow.shallowClone()
@@ -117,5 +120,20 @@
       unless strkey of ht
         ht[strkey] = { str: strkey, key: key, rows: [] }
       ht[strkey].rows.push row
+
+      if complete
+        for col, idx in cols
+          uniqvals[col] = {} unless col of uniqvals
+          uniqvals[col][key[idx]] = yes
+
+    if complete
+      for key, o of uniqvals
+        uniqvals[key] = _.keys(o)
+
+      for row in _.cross(uniqvals)
+        [key, strkey] = getkey row
+        unless strkey of ht
+          ht[strkey] = { str: strkey, key: key, rows: [] }
+
     ht


Fix formatting of point in exception message

--- src/display-layer.coffee
+++ src/display-layer.coffee
@@ -657,8 +657,9 @@

       if comparePoints(decorationIterator.getPosition(), bufferStart) < 0
         bufferRow = decorationIterator.getPosition().row
+        iteratorPosition = decorationIterator.getPosition()
         throw new Error("""
-          Invalid text decoration iterator position: #{decorationIterator.getPosition()}.
+          Invalid text decoration iterator position: (#{iteratorPosition.row}, #{iteratorPosition.column}).
           Buffer row #{bufferRow} has length #{@buffer.lineLengthForRow(bufferRow)}.
         """)


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/engine_levels.coffee
+++ src/engine_levels.coffee
@@ -16,7 +16,6 @@
       I.currentLevelName = level
       engine.setState levelState

-  # We must always return self as the last line
   nextLevel: ->
     unless I.transitioning
       I.currentLevel += 1


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/engine_levels.coffee
+++ src/engine_levels.coffee
@@ -38,7 +38,7 @@
       I.currentLevel += 1

       if level = I.levels[I.currentLevel]
-        loadLevel level
+        loadLevel level, offset
       else
         engine.setState GameOver()


Sidebar: Eliminate `MainController::ready` listener

--- client/app/lib/activity/sidebar/activitysidebar.coffee
+++ client/app/lib/activity/sidebar/activitysidebar.coffee
@@ -477,10 +477,9 @@

     super

-    kd.getSingleton('mainController').ready =>
-      @addMachineList()
-      @addFollowedTopics()
-      @addMessages()
+    @addMachineList()
+    @addFollowedTopics()
+    @addMessages()

   initiateFakeCounter: ->


ContentSearch: Code clean.

ContentSearch: Code clean.

--- client/ide/views/contentsearch/idecontentsearch.coffee
+++ client/ide/views/contentsearch/idecontentsearch.coffee
@@ -72,14 +72,14 @@
     searchText      = @searchText

     unless isRegExp
-      splitText  = searchText.split "\\n"
-      splitText  = splitText.map @grepEscapeRegExp
-      searchText = splitText.join "\\n"
+      splitText     = searchText.split '\\n'
+      splitText     = splitText.map @grepEscapeRegExp
+      searchText    = splitText.join '\\n'

-    searchText   = searchText.replace (new RegExp "\\\'", "g"), "'\\''"
-    searchText   = searchText.replace /-/g, "\\-"
+    searchText      = searchText.replace (new RegExp "\\\'", 'g'), "'\\''"
+    searchText      = searchText.replace /-/g, '\\-'

-    flags        = [
+    flags           = [
       '-s'                           # Silent mode
       '-r'                           # Recursively search subdirectories listed.
       '-n'                           # Each output line is preceded by its relative line number in the file
@@ -103,13 +103,13 @@
       @formatOutput res.stdout, @bound 'createResultsView'

   escapeRegExp: (str) ->
-    str.replace(/([.*+?\^${}()|\[\]\/\\])/g, "\\$1");
+    str.replace /([.*+?\^${}()|\[\]\/\\])/g, '\\$1'

   escapeShell: (str) ->
-    str.replace /([\\"'`$\s\(\)<>])/g, "\\$1"
+    str.replace /([\\"'`$\s\(\)<>])/g, '\\$1'

   grepEscapeRegExp: (str) ->
-    str.replace(/[[\]{}()*+?.,\\^$|#\s"']/g, "\\$&");
+    str.replace /[[\]{}()*+?.,\\^$|#\s"']/g, '\\$&'

   formatOutput: (output, callback = noop) ->
     # Regexes
@@ -122,11 +122,9 @@
       numberOfSearchedFiles : 0

     formatted = lines
-    .map (line) ->
-      # Remove erronous whitespace
+    .map (line) -> # Remove erroneous whitespace
       return line.trimLeft()
-    .filter (line) ->
-      # Skip lines that aren't one of these
+    .filter (line) -> # Skip lines that aren't one of these
       return mainLineRegex.test(line) or contextLineRegex.test(line)
     .map (line) ->
       # Get the matches


maintabs: hiddenhandle missing option added

--- client/landing/app/MainApp/maintabs/maintabview.coffee
+++ client/landing/app/MainApp/maintabs/maintabview.coffee
@@ -117,10 +117,11 @@
     # this is a temporary hack
     # for reviving the main tabs
     # a better solution tbdl - SY
-    o.domId    = "maintabpane-#{@utils.slugify options.name}"
-    o.domId   += "-#{@utils.getRandomNumber()}" if document.getElementById o.domId
-    o.name     = options.name
-    o.behavior = options.behavior
+    o.domId         = "maintabpane-#{@utils.slugify options.name}"
+    o.domId        += "-#{@utils.getRandomNumber()}" if document.getElementById o.domId
+    o.name          = options.name
+    o.behavior      = options.behavior
+    o.hiddenHandle  = options.hiddenHandle

     paneInstance = new MainTabPane o


teams: /Teams route reverted

teams: /Teams route reverted

--- client/landing/site.landing/coffee/teams/routes.coffee
+++ client/landing/site.landing/coffee/teams/routes.coffee
@@ -36,5 +36,5 @@

   KD.registerRoutes 'Teams',

-    '/Teams'       : -> KD.singletons.router.handleRoute '/'
+    '/Teams'       : handleRoute
     '/Teams/:token': handleInvitation


check all form's fields when clicking on a submit button

--- src/frontend/home.coffee
+++ src/frontend/home.coffee
@@ -42,8 +42,6 @@
             elemName = $(elem).attr 'id'
             if elemName isnt @name and elemName isnt (do @father._jqFirstStep.get).attr 'id'
                 do $(elem).hide
-        for btn in (do @_jqElem.get)
-            $(btn).attr 'disabled', off
         do @father.hide
         (do @_jqDiv.get).fadeTo 0.01
         (do @_jqDiv.get).animate {
@@ -52,8 +50,6 @@
         }, 600

     hide : (event) =>
-        for btn in (do @_jqElem.get)
-            $(btn).attr 'disabled', on
         do HideAllStates
         do @father.show
         (do @_jqDiv.get).fadeTo 0.99
@@ -69,9 +65,13 @@
         (do @_jqForm.get).submit @onSubmit
         for input in ((do @_jqForm.get).find 'input')
             ($ input).bind 'blur', @onFieldBlur
+        console.log ((do @_jqElem.get).find 'button[type=submit]')
+        ((do @_jqElem.get).find 'button[type=submit]').click (event) =>
+            ((do @_jqElem.get).find 'input').each () ->
+                ($ @).trigger 'blur'

     display : () =>
-        do ($ '#msg').empty
+        ($ document).trigger 'hideAllForms'
         ($ 'div#choicesContainer div.displayed').hide 0, () ->
             do HideAllStates
             ($ this).removeClass 'displayed'
@@ -121,8 +121,6 @@
         ]

     hide : () =>
-        for btn in (do @_jqBtn.get)
-            $(btn).attr 'disabled', on
         (do @_jqFirstStep.get).fadeTo 0.99
         (do @_jqFirstStep.get).animate {
             opacity : 0
@@ -130,8 +128,6 @@
         }, 600

     show : () =>
-        for btn in (do @_jqBtn.get)
-            $(btn).attr 'disabled', off
         (do @_jqFirstStep.get).fadeTo 0.01
         (do @_jqFirstStep.get).animate {
             opacity : 1
@@ -180,5 +176,9 @@
         '#logIn'    : new Choice 'logIn'
         '#signUp'   : new Choice 'signUp', SignUpForm

+    ($ document).on 'hideAllForms', (event) =>
+        for c of choices
+            do (do (do choices[c]._form.get)._jqElem.get).hide
+
     if window.location.hash? and choices[window.location.hash]?
         do choices[window.location.hash].onClick


[FIX]: Animation time and arrows position

--- src/frontend/room.coffee
+++ src/frontend/room.coffee
@@ -105,6 +105,29 @@
             i++
         $('div#endMessage').delay(time).fadeIn speed
         $('div#endMessage').delay(time).fadeOut speed
+        i = 0
+        time = interval * 5
+        while i < elems.length
+            $(elems[i]).delay(time).fadeOut speed
+            i++
+        $('div#endMessage').delay(time).fadeIn speed
+        $('div#endMessage').delay(time).fadeOut speed
+        @startAnimation $("div[id$='Arrow']"), 1000, 400
+
+    startAnimation       : (elems, interval, speed) =>
+        i = elems.length
+        fadeInTime = interval * 5
+        while i >= 0
+            $(elems[i]).delay(fadeInTime).fadeIn speed
+            fadeInTime -= interval
+            i--
+        i = 0
+        fadeOutTime = interval * 5
+        while i < elems.length
+            $(elems[i]).delay(fadeOutTime).fadeOut speed
+            i++
+        $('div#endMessage').delay(fadeOutTime).fadeIn speed
+        $('div#endMessage').delay(3000).fadeOut speed

     start               : () =>
         do @networkManager.connection


fix up units in gerber-to-svg

fix up units in gerber-to-svg

--- src/gerber-to-svg.coffee
+++ src/gerber-to-svg.coffee
@@ -50,13 +50,14 @@
     p.bbox.yMax = 0
     height = 0
   # create an xml object
+  unitDivisor = 10 ** p.parser?.format?.places?[1] || 1
   xml = {
     svg: {
       xmlns: 'http://www.w3.org/2000/svg'
       version: '1.1'
       'xmlns:xlink': 'http://www.w3.org/1999/xlink'
-      width: "#{width}#{p.units}"
-      height: "#{height}#{p.units}"
+      width: "#{width/unitDivisor}#{p.units}"
+      height: "#{height/unitDivisor}#{p.units}"
       viewBox: [ p.bbox.xMin, p.bbox.yMin, width, height ]
       _: []
     }


Add LOAD CSV highlighting to the browser

--- community/browser/app/scripts/codemirror-cypher.coffee
+++ community/browser/app/scripts/codemirror-cypher.coffee
@@ -73,7 +73,7 @@
   curPunc = undefined
   funcs = wordRegexp(["str", "min", "labels", "max", "type", "lower", "upper", "length", "type", "id", "coalesce", "head", "last", "nodes", "relationships", "extract", "filter", "tail", "range", "reduce", "abs", "round", "sqrt", "sign", "replace", "substring", "left", "right", "ltrim", "rtrim", "trim", "collect", "distinct"])
   preds = wordRegexp(["all", "any", "none", "single", "not", "in", "has", "and", "or"])
-  keywords = wordRegexp(["start", "merge", "using", "on create", "on match", "match", "index on", "drop", "where", "with", "limit", "skip", "order", "by", "return", "create", "delete", "set", "unique"])
+  keywords = wordRegexp(["start", "merge", "load csv", "using", "on create", "on match", "match", "index on", "drop", "where", "with", "limit", "skip", "order", "by", "return", "create", "delete", "set", "unique"])
   operatorChars = /[*+\-<>=&|~]/
   startState: (base) ->
     tokenize: tokenBase


Trap kill signals

Trap kill signals

--- src/pow/server.coffee
+++ src/pow/server.coffee
@@ -14,6 +14,10 @@

   listen: (port) ->
     @server.listen port
+
+    process.on 'SIGINT',  () => @close()
+    process.on 'SIGTERM', () => @close()
+    process.on 'SIGQUIT', () => @close()

   close: ->
     for config, app of @applications


Log stream env var

Log stream env var

--- src/pushbullet.coffee
+++ src/pushbullet.coffee
@@ -24,6 +24,7 @@
 pushbullet = new PushBullet apiKey

 module.exports = (robot) ->
+  console.log "HUBOT_PUSHBULLET_STREAM is #{if process.env.HUBOT_PUSHBULLET_STREAM then 'on' else 'off'}"
   if process.env.HUBOT_PUSHBULLET_STREAM
     pbStream = pushbullet.stream()
     pbStream.on 'push', (push) ->


Fix merge master

Fix merge master

--- core/app/backbone/contrib/default_click_handler.coffee
+++ core/app/backbone/contrib/default_click_handler.coffee
@@ -11,10 +11,10 @@
       else
         $link.attr("target", "_blank") # all unhandled clicks: new window.

-
-
 navigateTo = (url) ->
   status = Backbone.history.navigate(url, true)
   if status == undefined #means url unchanged; nothing happened.
     Backbone.history.loadUrl(url) #reload on navigate to current location.
+  else
+    status
 $(document).on "click", defaultClickHandler


executeOne

executeOne

--- app/server.coffee
+++ app/server.coffee
@@ -214,6 +214,9 @@

   fn 'batch', 'Executes a batch of DSL expressions with a promise for each', (ctx) ->
     Context.value(server.batch(ctx.options()))
+
+  fn 'executeOne', 'Executes a single DSL expression and returns a promise', (ctx, target, params) ->
+    Context.value(server.executeOne(target, params, ctx.options()))

   init: ->
     if settings.get('type') == 'lead'
@@ -370,6 +373,12 @@
     promise.fail (response) ->
       Q.reject new ServerError(server.parse_error_response response)

+  executeOne: (target, params={}, default_options) ->
+    request = server.batch(default_options)
+    result = request.add(target)
+    request.execute(params)
+    result
+
   batch: (default_options) ->
     items = []


Fixed a bug with regeneration

Fixed a bug with regeneration

--- lib/docpad.coffee
+++ lib/docpad.coffee
@@ -9,6 +9,7 @@
 async = require 'async'
 watch = require 'watch'
 util = require 'bal-util'
+sys = require 'sys'
 mongoose = require 'mongoose'
 Schema = mongoose.Schema
 SchemaTypes = Schema.Types
@@ -129,20 +130,27 @@

 	# Clean the database
 	generateClean: (next) ->
+		console.log 'Cleaning files'
+
+		timeoutCallback = ->
+			throw new Error 'Could not connect to the mongod'
+		timeout = setTimeout timeoutCallback, 1500
+
 		async.parallel [
 			(callback) ->
 				docpad.LayoutModel.remove {}, (err) ->
 					throw err if err
-					console.log 'Cleaned Layouts'
+					console.log 'Cleaned layouts'
 					callback()
 			(callback) ->
 				docpad.DocumentModel.remove {}, (err) ->
 					throw err if err
-					console.log 'Cleaned Documents'
+					console.log 'Cleaned documents'
 					callback()
 		],
 		->
-			console.log 'Cleaned Files'
+			clearTimeout timeout
+			console.log 'Cleaned files'
 			next()

 	# Parse the files
@@ -443,17 +451,28 @@
 	# Generate
 	generate: (next) ->
 		# Check
-		if docpad.genetating then return
-		else docpad.generating = true
-
+		if docpad.generating
+			console.log 'Generate request received, but we are already busy generating...'
+			return
+		else
+			console.log 'Generating...'
+			docpad.generating = true
+
 		# Continue
 		path.exists docpad.options.srcPath, (exists) ->
 			# Check
-			if not exists then throw Error 'Cannot generate website as the src dir was not found'
-
+			if exists is false
+				throw new Error 'Cannot generate website as the src dir was not found'
+
+			# Log
+			console.log 'Cleaning the out path'
+
 			# Continue
-			util.rmdir docpad.options.outPath, (err) ->
-				throw err if err
+			util.rmdir docpad.options.outPath, (err,list,tree) ->
+				if err
+					console.log 'Failed to clean the out path '+docpad.options.outPath
+					throw err
+				console.log 'Cleaned the out path'
 				docpad.generateClean ->
 					docpad.generateParse ->
 						docpad.generateRelations ->
@@ -465,9 +484,14 @@

 	# Watch
 	watch: (next) ->
+		# Log
+		console.log 'Setting up watching...'
+
 		# Watch the src directory
 		watch.createMonitor docpad.options.srcPath, (monitor) ->
-
+			# Log
+			console.log 'Set up watching'
+
 			# File Changed
 			monitor.on 'changed', (fileFullPath,newStat,oldStat) ->
 				docpad.generate ->


Removed unnecessary fat arrows and improv variable names

--- lib/loofah.coffee
+++ lib/loofah.coffee
@@ -4,10 +4,7 @@
 module.exports =

   default: ->
-    bk = bad_keys()
-    ue = url_encode()
-    pt = plain_text()
-    _.compose(pt, ue, bk)
+    _.compose(plain_text(), url_encode(), bad_keys())

   bad_keys: bad_keys = (b_keys = ['password', 'user', 'email', 'api', 'secret']) ->
     return (object) ->
@@ -18,10 +15,10 @@
           val = if (b_key.test key) then '[REDACTED]' else val
         val

-  bad_vals: (substrings) ->
-    return (object) =>
+  bad_vals: bad_vals = (substrings) ->
+    return (object) ->
       return _bad_vals object, substrings if not _.isObject object
-      return obj = _.deepMapValues object, (val) =>
+      return obj = _.deepMapValues object, (val) ->
         _bad_vals val, substrings

   _bad_vals: _bad_vals = (string, substrings) ->
@@ -32,9 +29,9 @@
     string

   url_encode: url_encode = (query_params = ['client_id', 'client_secret', 'refresh_token']) ->
-    return (object) =>
+    return (object) ->
       return _url_encode object, query_params if not _.isObject object
-      return obj = _.deepMapValues object, (val) =>
+      return obj = _.deepMapValues object, (val) ->
         _url_encode val, query_params

   _url_encode: _url_encode = (string, query_params) ->
@@ -47,9 +44,9 @@
     val.join('')

   plain_text: plain_text = (keywords = ['user', 'username', 'password', 'email']) ->
-    return (object) =>
+    return (object) ->
       return _plain_text object, keywords if not _.isObject object
-      return obj = _.deepMapValues object, (val) =>
+      return obj = _.deepMapValues object, (val) ->
         _plain_text val, keywords

   _plain_text: _plain_text = (string, keywords) ->
@@ -61,12 +58,11 @@
         val[i + 2] = '[REDACTED]' if val[i + 1] isnt '=' and val[i + 2]? and keyword.test v
     val.join('')

-  _splitter: _splitter = (string, d) ->
+  _splitter: _splitter = (string, delims) ->
     list = []
-    i = if d[0].test string[0] then 1 else 0
-
+    i = if delims[0].test string[0] then 1 else 0
     while true
-      next = string.search d[i]
+      next = string.search delims[i]
       if next is -1
         list.push string
         return list


Change 'live' view default from 50 to 10

I think the live view is mostly used to watch the latest 2-3 messages.
If someone wants more than 10, then they can change it.
50 seems like a fair amount to load and render.

--- lib/routes.coffee
+++ lib/routes.coffee
@@ -95,7 +95,7 @@
     since: @params.since ? defaultSince
   fastRender: true

-defaultLiveLimit = 50
+defaultLiveLimit = 10

 Router.route '/:group/live/:limit?',
   name: 'live'


Bug fix

Bug fix

--- lib/schema.coffee
+++ lib/schema.coffee
@@ -47,7 +47,7 @@
         sync: relation.model_type.createSync(JoinTable)
     catch
       class JoinTable extends Backbone.Model
-        @model_name: Schema.joinTableModelName(relation)
+        urlRoot: "/#{Schema.joinTableModelName(relation)}"
         @schema: schema
         sync: relation.model_type.createSync(JoinTable)


Removed old code

Removed old code

--- src/js/app/editor.coffee
+++ src/js/app/editor.coffee
@@ -115,34 +115,6 @@
   "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
-
 modes =
   CoffeeScript:
     id: "coffeescript"
@@ -239,13 +211,7 @@
   sourceCompiled = false
   sourceChanged = true
   history.add e
-  return #unless autoCompile
-  # DELAY = 700
-  # lastHit = +new Date
-  # setTimeout ->
-  #   if +new Date() - lastHit > DELAY
-  #     compileCode() unless sourceCompiled
-  # , 2 * DELAY
+  return

 # TODO: this is not used, it is connected to History
 printHistory = ->
@@ -422,6 +388,7 @@
   $("#rightColumn").width winSize.w - $("#leftColumn").width() - 60
   $("#rightColumn").css "max-height", (winSize.h - 25) + "px"
   setMaxPreWidth $("#output pre")
+  sourceArea.resize()

 selectLastOutput = ->
   (getMessage(0)).selectText()


PR #364: Removed braces

PR #364: Removed braces

--- src/koaMiddleware.coffee
+++ src/koaMiddleware.coffee
@@ -27,10 +27,9 @@
 compress = require 'koa-compress'

 rawBodyReader = (next) ->
-  body = yield getRawBody this.req, {
+  body = yield getRawBody this.req,
     length: this.length,
     encoding: this.charset
-  }

   this.body = body if body
   yield next


fixed undefined precursor character in delimiter expansion

--- src/mist-resolver.coffee
+++ src/mist-resolver.coffee
@@ -215,7 +215,7 @@
   dict = MistResolver.generateDict pathname

   template.replace MistResolver.delimiterPattern, (m, p, c)->
-    if c of dict then p + dict[c]
+    if c of dict then (p || '') + dict[c]
     else throw "unknown file delimiter: #{c}"

 ###


Better align transcribe tooling

Better align transcribe tooling


Remove page reload on popstate event

Remove page reload on popstate event

This is to get round the collision with the technique and the dfp async ads request.
To be reimplemented...

--- app/assets/javascripts/lib/extends/controller.js.coffee
+++ app/assets/javascripts/lib/extends/controller.js.coffee
@@ -67,10 +67,10 @@
         # Modern browsers
         # WebKit fires a popstate event on document load
         # https://code.google.com/p/chromium/issues/detail?id=63040
-        setTimeout(( =>
-          $(window).bind 'popstate', =>
-            @setUrl(@getUrl()) unless history.state is null
-        ), 1)
+        # setTimeout(( =>
+        #   $(window).bind 'popstate', =>
+        #     @setUrl(@getUrl()) unless history.state is null
+        # ), 1)

       else if @_supportsHash()
         #ie8 and ie9


stateful model re-binding for development mode. bugfix

--- app/assets/javascripts/luca/concerns/state_model.coffee
+++ app/assets/javascripts/luca/concerns/state_model.coffee
@@ -1,5 +1,8 @@
 Luca.concerns.StateModel =
   __onModelChange: (args...)->
+    statefulView = @
+    state = statefulView.state
+
     @trigger.call(statefulView, "state:change", args... )

     for changed, value of state.changedAttributes()


fix links click in wysiwyg

fix links click in wysiwyg

--- app/modules/components/wysiwyg/wysiwyg.directive.coffee
+++ app/modules/components/wysiwyg/wysiwyg.directive.coffee
@@ -490,13 +490,7 @@
                 e.stopPropagation()

                 if e.target.href
-                    window.open(e.target.href)
-
-            mediumInstance.subscribe 'focus', (event) ->
-                $scope.$applyAsync () ->
-                    if !$scope.editMode
-                        setEditMode(true)
-                        refreshCodeBlocks(mediumInstance)
+                    window.open(e.target.href)

             mediumInstance.subscribe 'editableDrop', (event) ->
                 $scope.onUploadFile({files: event.dataTransfer.files, cb: uploadEnd})
@@ -523,6 +517,16 @@
             $scope.$applyAsync () ->
                 wysiwygCodeHightlighterService.addHightlighter(mediumInstance.elements[0])
                 refreshCodeBlocks(mediumInstance)
+
+        $(editorMedium[0]).on 'mousedown', (e) ->
+            if e.target.href
+                e.preventDefault()
+                e.stopPropagation()
+            else
+                $scope.$applyAsync () ->
+                    if !$scope.editMode
+                        setEditMode(true)
+                        refreshCodeBlocks(mediumInstance)

         $(editorMedium[0]).on 'dblclick', 'pre', (e) ->
             $scope.$applyAsync () ->
@@ -561,6 +565,7 @@

         $scope.$on "$destroy", () ->
             if mediumInstance
+                $(editorMedium[0]).off() if editorMedium.length
                 mediumInstance.destroy()

     return {


payment: set default value of payment method id to `null`

--- client/Main/payment/paymentcontroller.coffee
+++ client/Main/payment/paymentcontroller.coffee
@@ -74,10 +74,8 @@
       when 'user'
         JPaymentPlan.fetchAccountDetails callback

-  updatePaymentInfo: (paymentMethodId, paymentMethod, callback) ->
-
-    { JPayment } = KD.remote.api
-
+  updatePaymentInfo: (paymentMethodId = null, paymentMethod, callback) ->
+    {JPayment} = KD.remote.api
     JPayment.setPaymentInfo paymentMethodId, paymentMethod, callback

   createPaymentInfoModal: -> new PaymentFormModal


WorkspaceTabView: Fixed typo.

WorkspaceTabView: Fixed typo.

--- client/ide/workspace/workspacetabview.coffee
+++ client/ide/workspace/workspacetabview.coffee
@@ -16,8 +16,8 @@
       delegate      : this

   createTabView: ->
-    TabViewClas = @getOption('tabViewClass') or ApplicationTabView
-    @tabView    = new TabViewClas
+    TabViewClass = @getOption('tabViewClass') or ApplicationTabView
+    @tabView     = new TabViewClass
       tabHandleContainer        : @holderView
       closeAppWhenAllTabsClosed : no


Reverse instanceof condition in GlyphRenderer

--- bokehjs/src/coffee/renderer/glyph/glyph_renderer.coffee
+++ bokehjs/src/coffee/renderer/glyph/glyph_renderer.coffee
@@ -106,8 +106,8 @@

       @glyph._map_data()

-      if @_mask_data? and @plot_view.x_range instanceof FactorRange.Model \
-                      and @plot_view.y_range instanceof FactorRange.Model
+      if @_mask_data? and not (@plot_view.x_range instanceof FactorRange.Model) \
+                      and not (@plot_view.y_range instanceof FactorRange.Model)
         indices = @_mask_data()
       else
         indices = @all_indices


Added loading indicator

Added loading indicator

--- core/app/backbone/views/react_profile.coffee
+++ core/app/backbone/views/react_profile.coffee
@@ -67,7 +67,9 @@
     _div [],
       ReactUserTabs model: @model(), page: 'about'

-      if @model().get('deleted')
+      if @model().isNew()
+        _img [src: Factlink.Global.ajax_loader_image]
+      else if @model().get('deleted')
         @_deletedUser()
       else
         @_activeUser()


Fix bug on resolve environment variables

--- PostmanImporter.coffee
+++ PostmanImporter.coffee
@@ -70,6 +70,8 @@
         return pawRequest

     @expandEnvironmentVariables = (context, string) ->
+        if string == null
+            string = ''
         rx = /\{\{([^\n\}]+)\}\}/g;
         items = string.split(rx)
         if items.length < 2


saving url shouldn't be needed anymore

saving url shouldn't be needed anymore

--- resources/assets/coffee/react/beatmaps/main.coffee
+++ resources/assets/coffee/react/beatmaps/main.coffee
@@ -45,11 +45,9 @@
   constructor: (props) ->
     super props

-    @url = location.href
-
     prevState = JSON.parse(props.container.dataset.reactState ? '{}')

-    @state = prevState.state if prevState.url == location.href
+    @state = prevState.state unless _.isEmpty(prevState)
     @state ?= _.extend
       beatmaps: @props.beatmaps
       paging:
@@ -85,7 +83,6 @@
     $(document).on 'beatmap:load_more.beatmaps', @loadMore
     $(document).on 'beatmap:search:start.beatmaps', @search
     $(document).on 'beatmap:search:filtered.beatmaps', @updateFilters
-    $(document).on 'turbolinks:before-visit.beatmaps', @recordUrl
     $(document).on 'turbolinks:before-cache.beatmaps', @saveState
     $(window).on 'resize.beatmaps', @updateColumnCount

@@ -225,12 +222,8 @@
       @setState newState

-  recordUrl: =>
-    @url = location.href
-
-
   saveState: =>
-    @props.container.dataset.reactState = JSON.stringify({@state, @url})
+    @props.container.dataset.reactState = JSON.stringify({@state})
     @componentWillUnmount()


add 'Connect to Remote Kernel' to menus

add 'Connect to Remote Kernel' to menus

--- menus/hydrogen.cson
+++ menus/hydrogen.cson
@@ -24,6 +24,9 @@
         }, {
           'label': 'Run All Above'
           'command': 'hydrogen:run-all-above'
+        }, {
+          'label': 'Connect to Remote Kernel'
+          'command': 'hydrogen:connect-to-remote-kernel'
         }
       ]
     ]


set more explicit error messages on engine

--- src/render/Engine.coffee
+++ src/render/Engine.coffee
@@ -20,7 +20,7 @@

     getContext: ->
-        throw new Error "[Rotten.Engine] abstract method should be implemented"
+        throw new Error "[Rotten.Engine] Your render should override getContext() method"

     getWidth: ->
         @width
@@ -29,28 +29,28 @@
         @height

     setBackgroundColor: (color) ->
-        throw new Error "[Rotten.Engine] abstract method should be implemented"
+        throw new Error "[Rotten.Engine] Your render should override setBackgroundColor() method"

     clearRect: ->
-        throw new Error "[Rotten.Engine] abstract method should be implemented"
+        throw new Error "[Rotten.Engine] Your render should override clearRect() method"

     save: ->
-        throw new Error "[Rotten.Engine] abstract method should be implemented"
+        throw new Error "[Rotten.Engine] Your render should override save() method"

     restore: ->
-        throw new Error "[Rotten.Engine] abstract method should be implemented"
+        throw new Error "[Rotten.Engine] Your render should override restore() method"

     translate: (x,y) ->
-        throw new Error "[Rotten.Engine] abstract method should be implemented"
+        throw new Error "[Rotten.Engine] Your render should override translate() method"

     rotate: (angle) ->
-        throw new Error "[Rotten.Engine] abstract method should be implemented"
+        throw new Error "[Rotten.Engine] Your render should override rotate() method"

     setAlpha: (alpha) ->
-        throw new Error "[Rotten.Engine] abstract method should be implemented"
+        throw new Error "[Rotten.Engine] Your render should override setAlpha() method"

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

     drawSubImage: (image, sub_x, sub_y, sub_width, sub_height, x, y, width, height) ->
-        throw new Error "[Rotten.Engine] abstract method should be implemented"
+        throw new Error "[Rotten.Engine] Your render should override drawSubImage() method"


Removed whitespace, added new line after dependencies and taking care if no deal available

--- src/scripts/steam.coffee
+++ src/scripts/steam.coffee
@@ -5,6 +5,7 @@
 #   "htmlparser": "1.8.0"
 #   "soupselect": "0.2.0"
 #   "validator" : "0.4.20"
+#
 # Configuration:
 #   None
 #
@@ -33,7 +34,7 @@
 getDeals = (msg, callback) ->
     location = "http://store.steampowered.com"
     msg.http(location).get() (error,response, body) ->
-      return msg.send "Something went worng..." if error
+      return msg.send "Something went wrong..." if error
       deal = parseDeals body, ".dailydeal a"
       callback deal

@@ -42,10 +43,12 @@
   parser = new HTMLParser.Parser handler
   parser.parseComplete body
   dealObj = Select(handler.dom, selector)[0]
-  originalPrice = Select(handler.dom, '.dailydeal_content .discount_original_price')[0]
-  finalPrice = Select(handler.dom, '.dailydeal_content .discount_final_price')[0]
-  deal = "From #{sanitize(originalPrice.children[0].data).entityDecode().trim()} to #{sanitize(finalPrice.children[0].data).entityDecode().trim()}  #{dealObj.children[0].attribs.src}  #{dealObj.attribs.href}"
-
-
+  if dealObj?
+    originalPrice = Select(handler.dom, '.dailydeal_content .discount_original_price')[0]
+    finalPrice = Select(handler.dom, '.dailydeal_content .discount_final_price')[0]
+    deal = "From #{sanitize(originalPrice.children[0].data).entityDecode().trim()} to #{sanitize(finalPrice.children[0].data).entityDecode().trim()}  #{dealObj.children[0].attribs.src}  #{dealObj.attribs.href}"
+  else
+    msg.send "No daily deal found"

+


Activity: two socialapi endpoints added

Activity: two socialapi endpoints added

--- client/Main/socialapicontroller.coffee
+++ client/Main/socialapicontroller.coffee
@@ -394,6 +394,15 @@
       fnName             : 'follow'
       validateOptionsWith: ['channelId']

+    addParticipant       : channelRequesterFn
+      fnName             : 'follow'
+      validateOptionsWith: ['channelId']
+
+    addParticipants      : (callback)->
+
+      callback message : 'Cihangir needs to create the endpoint first :)'
+
+
     unfollow             : channelRequesterFn
       fnName             : 'unfollow'
       validateOptionsWith: ['channelId']


Social: map unread replies count to social message

--- client/Main/socialapicontroller.coffee
+++ client/Main/socialapicontroller.coffee
@@ -71,6 +71,10 @@
     m.replies      = mapActivities data.replies or []
     m.repliesCount = data.repliesCount
     m.isFollowed   = data.isFollowed
+
+    # this is sent by the server when
+    # response for pinned messages
+    m.unreadRepliesCount = data.unreadRepliesCount

     m.interactions    = interactions or
       like            :


client: fix typo

client: fix typo

--- client/app/lib/searchcontroller.coffee
+++ client/app/lib/searchcontroller.coffee
@@ -121,7 +121,7 @@
       hitsPerPage                  : 10
       restrictSearchableAttributes : [ "nick" ]

-    opts = kd.utils.extend opt, options
+    opts = kd.utils.extend opts, options

     seed = seed.replace /[^-\w]/g, ''


fixing multiple click bug

fixing multiple click bug

--- client/controllers/curatorInbox.coffee
+++ client/controllers/curatorInbox.coffee
@@ -110,7 +110,8 @@

 Template.curatorInboxSection.onRendered ->
   # select the first item in the inbox
-  $(".curator-inbox-table tbody tr:first").click()
+  if $(".details-open").length == 0
+    $(".curator-inbox-table tbody tr:first").click()

 Template.curatorInboxSection.onCreated ->
   @curatorInboxFields = [
@@ -193,7 +194,6 @@
       filterArray.push _.object(Template.instance().data.reviewFilter.fields.map((field)->
         [field, reviewFilter]
       ))
-    console.log filterArray
     filter = {
               $and: filterArray
             }
@@ -236,8 +236,6 @@
   @contentIsOpen = new ReactiveVar(false)
   @reviewed = new ReactiveVar @data.reviewed or false

-# Template.curatorSourceDetails.onRendered ->
-
 Template.curatorSourceDetails.helpers
   post: ->
     return CuratorSources.findOne({_id: @_id})


Improve error handling for createWorkItem   If create work item fails call the callback with the error

--- src/vso-client.coffee
+++ src/vso-client.coffee
@@ -357,7 +357,10 @@
   createWorkItem: (item, callback) ->
     path = @buildApiPath 'wit/workitems'
     @client.post path, item, (err, res, body) ->
-      parseReplyData err, res,  body, callback
+      if res.statusCode == 400
+        callback body.exception?.Message or "Error Creating work item", body
+      else
+        parseReplyData err, res,  body, callback

   updateWorkItem: (id, item, callback) ->
     path = @buildApiPath 'wit/workitems/' + id


Remove unused variable

Remove unused variable

--- src/wrap-guide.coffee
+++ src/wrap-guide.coffee
@@ -15,7 +15,6 @@
   @content: ->
     @div class: 'wrap-guide'

-  getGuideColumn: null
   defaultColumn: 80

   initialize: (@rootView, @editor) =>


a little bit more complex template text

a little bit more complex template text

--- src/test/template.coffee
+++ src/test/template.coffee
@@ -1,4 +1,5 @@
-
+path = require 'path'
+{ readFile } = require 'fs'
 { Template } = require '../async-xml'

 module.exports =
@@ -14,11 +15,14 @@
         content = ["a", "b", "c"]
         xml = new Template schema:'html5', ->
             @$html ->
-                @$body ->
-                    @$div class:'test', "lala"
-                    @$ul ->
-                        content.forEach (data) =>
-                            @$li(null, data)
+                @body ->
+                    readFile path.join(__dirname,"filename"), (err, filedata) =>
+                        @$div class:'test', filedata
+                        @$ul ->
+                            content.forEach (data) =>
+                                li = @$li({'data-content':data}, data)
+                        @end()
+

         xml.on 'end', æ.done
         xml.on 'data', (tag) -> æ.equal results.shift(), tag
@@ -26,16 +30,16 @@
             ''
             ''
             '
'
-            'lala'
+            'hello world\n'
             '
'
             '
'
-            '
'
+            '
'
             'a'
             '
'
-            '
'
+            '
'
             'b'
             '
'
-            '
'
+            '
'
             'c'
             '
'
             '
'


social bugs

social bugs

--- components/buttons/KDButtonView.coffee
+++ components/buttons/KDButtonView.coffee
@@ -106,8 +106,9 @@
         range       : loader.range    || 0.4
         speed       : loader.speed    || 1.5
         FPS         : loader.FPS      || 24
+
     @addSubView @loader, null, yes
-    @loader.$().css position : "absolute", left : 5, top : 5
+    @loader.$().css position : "absolute", left : loader.left or 5, top : loader.top or 5
     @loader.hide()

   showLoader:->
@@ -143,7 +144,9 @@
   triggerClick:()-> @doOnSubmit()

 class KDToggleButton extends KDButtonView
+
   constructor:(options = {},data)->
+
     options = $.extend
       dataPath : null          # a JsPath String
       states   : []            # an Array of Objects in form of stateName : callback key/value pairs
@@ -156,12 +159,14 @@
     @attachListener()

   attachListener:->
+
     {dataPath} = @getOptions()
     if dataPath
       @getData().on dataPath, =>
         @setState dataPath

   setDefaultStates:->
+
     {states} = @getOptions()
     if states.length < 3
       @options.states = ['default',-> warn "no state options passed" ]
@@ -169,6 +174,7 @@
       return

   getStateIndex:(name)->
+
     {states} = @getOptions()
     unless name
       return 0
@@ -178,6 +184,7 @@
           return index

   setState:(name)->
+
     {states} = @getOptions()
     @stateIndex = index = @getStateIndex name
     @state      = states[index]
@@ -186,6 +193,7 @@
     @setCallback states[index + 1].bind @, @toggleState.bind @

   toggleState:(err)->
+
     {states} = @getOptions()
     nextState = states[@stateIndex + 2] or states[0]
     unless err


add negative tests for constructor

add negative tests for constructor

--- matrixSpec.coffee
+++ matrixSpec.coffee
@@ -38,6 +38,14 @@
               4, 5, 6 ], 3
       expect(B.height).toBe 2
       expect(B.width).toBe 3
+
+    it 'should throw on wrong width', ->
+
+      expect(-> M [ 1, 2, 3 ], 2).toThrow()
+
+    it 'should throw on on param call with not square array size', ->
+
+      expect(-> M [ 1, 2, 3 ]).toThrow()

   describe 'clone() function', ->


Listen on stylesheet file

Listen on stylesheet file

--- src/theme-manager.coffee
+++ src/theme-manager.coffee
@@ -257,9 +257,9 @@
       @userStylesheetFile = new File(userStylesheetPath)
       @userStylsheetSubscriptions = new CompositeDisposable()
       reloadStylesheet = => @loadUserStylesheet()
-      @userStylsheetSubscriptions.add(@userStylesheetPath.onDidChange(reloadStylesheet))
-      @userStylsheetSubscriptions.add(@userStylesheetPath.onDidRename(reloadStylesheet))
-      @userStylsheetSubscriptions.add(@userStylesheetPath.onDidDelete(reloadStylesheet))
+      @userStylsheetSubscriptions.add(@userStylesheetFile.onDidChange(reloadStylesheet))
+      @userStylsheetSubscriptions.add(@userStylesheetFile.onDidRename(reloadStylesheet))
+      @userStylsheetSubscriptions.add(@userStylesheetFile.onDidDelete(reloadStylesheet))
     catch error
       message = """
         Unable to watch path: `#{path.basename(userStylesheetPath)}`. Make sure


Fix stupid typo

Fix stupid typo

--- src/tools/addpath.coffee
+++ src/tools/addpath.coffee
@@ -13,10 +13,9 @@
 ###
 assign = new Function 'o,k,v', 'o(k)=v'

-z=
 module.exports = (add)->
   orig = env key
-  tail = "#{folder PACKAGE.mingi, junction.$.n()}"
+  tail = "#{folder PACKAGE.mingzi, junction.$.n()}"
   other = (s)->
     s.slice(-tail.length) != tail or /\w$/.test s.slice 0, -tail.length
   x = for x in orig.split ';' when other x


N -> n to follow the other option

N -> n to follow the other option

--- src/ui/views/menu.coffee
+++ src/ui/views/menu.coffee
@@ -81,7 +81,7 @@
            submenu: [
              {
                 type: 'checkbox'
-                label: 'Show Notifications'
+                label: 'Show notifications'
                 checked: viewstate.showPopUpNotifications
                 enabled: viewstate.loggedin
                 click: (it) -> action 'showpopupnotifications', it.checked
@@ -342,7 +342,7 @@
           submenu: [
             {
                type: 'checkbox'
-               label: 'Show Notifications'
+               label: 'Show notifications'
                checked: viewstate.showPopUpNotifications
                enabled: viewstate.loggedin
                click: (it) -> action 'showpopupnotifications', it.checked


add menu entry

add menu entry

--- src/ui/views/menu.coffee
+++ src/ui/views/menu.coffee
@@ -312,6 +312,12 @@
             enabled: not viewstate.hidedockicon
             checked:  viewstate.showtray
             click: -> action 'toggleshowtray'
+        }, {
+          label: 'Escape key hides YakYak to tray'
+          type: 'checkbox'
+          enabled: viewstate.showtray
+          checked: viewstate.escapeClosesWindow
+          click: -> action 'toggleescapecloseswindow'
         }
         if isDarwin
             {


don't lie about convert to sagews

don't lie about convert to sagews

--- src/smc-webapp/jupyter/nbconvert.cjsx
+++ src/smc-webapp/jupyter/nbconvert.cjsx
@@ -127,7 +127,10 @@

     render_cmd: ->
-        cmd = "jupyter nbconvert #{@args().join(' ')} '#{misc.path_split(@props.path)?.tail}'"
+        if @props.nbconvert_dialog.get('to') == 'sagews'
+            cmd = "smc-ipynb2sagews '#{misc.path_split(@props.path)?.tail}'"
+        else
+            cmd = "jupyter nbconvert #{@args().join(' ')} '#{misc.path_split(@props.path)?.tail}'"

{cmd}

     render_started: ->


print out the stat to the console, it helps to work out whether the analytics are working or not

--- src/tasks/watch-analytics-ftdi.coffee
+++ src/tasks/watch-analytics-ftdi.coffee
@@ -22,12 +22,14 @@
     if startTime == null
       startTime = moment().subtract(timeInSeconds, 'seconds')

-    startTime.add(timeInSeconds, 'seconds').toDate()
+    startTime.clone().add(timeInSeconds, 'seconds').toDate()

   save = (name, timeInSeconds, value) ->
     point =
       time: calculateTime(timeInSeconds)
       value: value
+
+    console.log name, value, calculateTime(timeInSeconds)

     persistence.writePoint name, point, {}, (error) ->
       if error


Doc fix

Doc fix

--- packages/truss-dox/dynamic/index.coffee
+++ packages/truss-dox/dynamic/index.coffee
@@ -20,7 +20,7 @@
 # Gather all source files.
 sourceFilesPromise = new Promise (resolve, reject) ->

-  # #### Invoke hook `trussDoxSources`.
+  # #### Invoke hook `trussDoxSourceList`.
   sourceFiles = _.flatten pkgman.invokeFlat 'trussDoxSourceList'
   glob "{#{sourceFiles.join ','}}", (error, files) ->
     return reject error if error?


add twitter search streaming

add twitter search streaming

--- oretamaBot/scripts/twitter-search.coffee
+++ oretamaBot/scripts/twitter-search.coffee
@@ -5,10 +5,10 @@
 #   "twit": "1.1.x"
 #
 # Configuration:
-#   HUBOT_TWITTER_CONSUMER_KEY
-#   HUBOT_TWITTER_CONSUMER_SECRET
-#   HUBOT_TWITTER_ACCESS_TOKEN
-#   HUBOT_TWITTER_ACCESS_TOKEN_SECRET
+#   HUBOT_TWITTER_KEY
+#   HUBOT_TWITTER_SECRET
+#   HUBOT_TWITTER_TOKEN
+#   HUBOT_TWITTER_TOKEN_SECRET
 #
 # Commands:
 #   hubot ツイート流して <query> - Continue to search Twitter for a query and to tweet
@@ -18,100 +18,40 @@
 #   aha-oretama <sekine_y_529@msn.com>
 #

-Twit = require "twit"
+Twit = require 'twit'

 config =
-  consumer_key: process.env.HUBOT_TWITTER_CONSUMER_KEY
-  consumer_secret: process.env.HUBOT_TWITTER_CONSUMER_SECRET
-  access_token: process.env.HUBOT_TWITTER_ACCESS_TOKEN
-  access_token_secret: process.env.HUBOT_TWITTER_ACCESS_TOKEN_SECRET
+  consumer_key: process.env.HUBOT_TWITTER_KEY
+  consumer_secret: process.env.HUBOT_TWITTER_SECRET
+  access_token: process.env.HUBOT_TWITTER_TOKEN
+  access_token_secret: process.env.HUBOT_TWITTER_TOKEN_SECRET

-twit = undefined

 getTwit = ->
   unless twit
     twit = new Twit config
   return twit

-doSearch = (msg) ->
-  query = msg.match[2]
-  return if !query
+module.exports = (robot) ->
+  twit = undefined
+  stream = undefined

-  twit = getTwit()
-  count = 5
-  searchConfig =
-    q: "#{query}",
-    count: count,
-    lang: 'en',
-    result_type: 'recent'
+  robot.respond /ツイート流して (\S+)$/i, (msg) ->
+    query = msg.match[1]
+    twit = getTwit()

-  twit.get 'search/tweets', searchConfig, (err, reply) ->
-    return msg.send "Error retrieving tweets!" if err
-    return msg.send "No results returned!" unless reply?.statuses?.length
+    stream.stop() if stream
+    stream = twit.stream('statuses/filter', { track: query,language: 'ja' })

-    statuses = reply.statuses
-    response = ''
-    i = 0
-    for status, i in statuses
-      response += "**@#{status.user.screen_name}**: #{status.text}"
-      response += "\n" if i != count-1
+    msg.send 'ツイート流すよー'

-    return msg.send response
-
-doUser = (msg) ->
-  username = msg.match[2]
-  return if !username
-
-  twit = getTwit()
-  count = 5
-  searchConfig =
-    screen_name: username,
-    count: count
-
-  twit.get 'statuses/user_timeline', searchConfig, (err, statuses) ->
-    return msg.send "Error retrieving tweets!" if err
-    return msg.send "No results returned!" unless statuses?.length
-
-    response = ''
-    i = 0
-    msg.send "Recent tweets from #{statuses[0].user.screen_name}"
-    for status, i in statuses
-      response += "#{status.text}"
-      response += "\n" if i != count-1
-
-    return msg.send response
-
-doTweet = (msg, tweet) ->
-  return if !tweet
-  tweetObj = status: tweet
-  twit = getTwit()
-  twit.post 'statuses/update', tweetObj, (err, reply) ->
-    if err
-      msg.send "Error sending tweet!"
-    else
-      username = reply?.user?.screen_name
-      id = reply.id_str
-      if (username && id)
-        msg.send "https://www.twitter.com/#{username}/status/#{id}"
-
-module.exports = (robot) ->
-  robot.respond /ツイート流して (\S+)$/i, (msg) ->
-
-    screen_name = msg.match[1]
-    query = msg.match[2]
-
-    username = reply?.user?.screen_name
-
-    subscriptionManager.ensureSubscribedTo msg.message.user.reply_to, screen_name, (err) ->
-      if err?
-        msg.reply "Something went wrong: #{err}"
-      else
-        msg.reply "Great! Anytime @#{screen_name} tweets, I'll post it here."
+    stream.on 'tweet', (tweet) ->
+      msg.send "#{query}見つかったよー\n\nhttps://twitter.com/#{tweet.user.screen_name}/status/#{tweet.id_str}"
+    stream.on "disconnect", (disconnectMessage) ->
+      msg.send "ツイート流すが切れたー。理由: #{disconnectMessage}"
+    stream.on "reconnect", (request, response, connectInterval) ->
+      msg.send "ちょっと切れちゃった。再開は #{connectInterval}ミリ秒後"

   robot.respond /ツイート止めて/i, (msg) ->
-    screen_name = msg.match[1]
-    subscriptionManager.ensureUnsubscribedFrom msg.message.user.reply_to, screen_name, (err) ->
-      if err?
-        msg.reply "Something went wrong: #{err}"
-      else
-        msg.reply "Roger that! I won't post tweets from @#{screen_name} anymore."
+    stream.stop() if stream
+    msg.send 'ツイート止めたよー'


added danger function

added danger function

--- src/components/cs-alert/cs-alert-service.js.coffee
+++ src/components/cs-alert/cs-alert-service.js.coffee
@@ -38,6 +38,9 @@
   @warning = (msgType) ->
     @addAlert(@getText(msgType), 'warning')

+  @danger = (msgType) ->
+    @addAlert(customMessage || @getText(msgType), 'danger')
+
   @getText = (type) ->
     @i18n.t('alert.' + type) || 'translation missing'


Add quoted symbol

Add quoted symbol

--- lisp.coffee
+++ lisp.coffee
@@ -1,6 +1,6 @@
 class Symbol
-  constructor: (@name, @pos) ->
-  toString: -> @name
+  constructor: (@name, @quoted, @pos) ->
+  toString: -> "#{if @quoted then "'" else ""}#{@name}"

 class Nil
   toString: -> 'nil'
@@ -84,7 +84,7 @@
   forwards_if: (pattern) ->
     @forwards pattern if @expects pattern

-  atom: ->
+  atom: (quoted) ->
     #number
     if @expects /[0-9]/
       num = ''
@@ -102,7 +102,7 @@
     return t if @forwards_if 't'

     #var
-    return new Symbol(@symbol(), @currentPos())
+    return new Symbol(@symbol(), quoted, @currentPos())

   symbol: ->
     ret = ''
@@ -136,12 +136,12 @@
       @forwards "'"
       if @expects '(' #list
         return @list()
-      else #atom
-        return @atom()
+      else #quoted atom
+        return @atom(true)
     else if @expects '(' #calling function
       return @call_fun()
     else #atom
-      return @atom()
+      return @atom(false)

   program: ->
     ret = []
@@ -224,6 +224,9 @@
           else
             error NotFunctionError, "#{JSON.stringify(funname)}(#{funname.constructor.name}) is not a function", funname.pos
       when 'Symbol'
+        if expr.quoted
+          return expr
+
         value = currentEnv().get(expr.name)
         unless value
           error NameError, "undefined valiable \"#{expr.name}\"", expr.pos


support lambda

support lambda

--- lisp.coffee
+++ lisp.coffee
@@ -32,6 +32,12 @@

 class Lambda
   constructor: (@params, @body) ->
+
+class Environment
+  constructor: (@variables) ->
+  get: (name) -> @variables[name]
+
+envstack = []

 class @Parser
   constructor: ->
@@ -153,9 +159,18 @@
             new Lambda(args[0], args[1])
       when 'CallFun'
         args = expr.args.map (arg) => @eval_expr(arg)
-        switch expr.funname.constructor.name
+        funname = if expr.funname instanceof SpecialForm then @eval_expr(expr.funname) else expr.funname
+        switch funname.constructor.name
+          when 'Lambda'
+            lambda = funname
+            envstack.push new Environment(lambda.params.values.reduce(
+              ((env, param, index) -> env[param.name] = args[index]; env), {}))
+            [name, args...] = lambda.body.values
+            ret = @eval_expr(new CallFun(name, args)) #とりあえず
+            envstack.pop()
+            return ret
           when 'Symbol'
-            switch expr.funname
+            switch funname.name
               when '+'
                 new Atom args.reduce(((sum, n) -> sum + n.value), 0)
               when 'car'
@@ -170,6 +185,8 @@
                 if args[0].value == args[1].value then t else nil
               when 'atom'
                 if args[0] instanceof Atom then t else nil
+      when 'Symbol'
+        envstack[envstack.length - 1].get(expr.name)
       else
         expr


add analytics to Facebook events

add analytics to Facebook events

--- templates/facebook/facebook.js.coffee
+++ templates/facebook/facebook.js.coffee
@@ -6,11 +6,40 @@
     xfbml      : true
     cookie: true

+  # track facebook events with analytics
+  FB.Event.subscribe "message.send", (href) ->
+    _gaq.push(['_trackEvent', 'facebook', 'send', href]) if _gaq?
+    _gaq.push(['_trackSocial', 'facebook', 'send', href]) if _gaq?
+
+  FB.Event.subscribe "edge.create", (href, widget) ->
+    _gaq.push(['_trackEvent', 'facebook', 'like', href]) if _gaq?
+    _gaq.push(['_trackSocial', 'facebook', 'like', href]) if _gaq?
+
+  FB.Event.subscribe "edge.remove", (href, widget) ->
+    _gaq.push(['_trackEvent', 'facebook', 'unlike', href]) if _gaq?
+    _gaq.push(['_trackSocial', 'facebook', 'unlike', href]) if _gaq?
+
 $ ->
-  $('a#facebook-login').click (event) ->
+  # Share button
+  # Usage: share
+  # title, description, picture etc is read from og-metadata
+  $('[data-fb-feed]').click (event) ->
+    obj =
+      method: 'feed'
+      link: $(this).data('fb-feed')
+
+    FB.ui obj, (resp) ->
+      if resp
+        _gaq.push(['_trackEvent', 'facebook', 'feed', window.location.protocol + '//' + window.location.hostname]) if _gaq?
+        _gaq.push(['_trackSocial', 'facebook', 'feed', window.location.protocol + '//' + window.location.hostname]) if _gaq?
+
+  # Login button
+  # Usage: login
+  $('[data-fb-login]').click (event) ->
+    $btn = $(this)
     event.preventDefault()
-    unless $('a#facebook-login').hasClass 'submitting'
-      $('a#facebook-login').addClass 'submitting'
+    unless $btn.hasClass 'disabled'
+      $btn.addClass 'disabled'
       FB.login (response) ->
         if response.authResponse
           $.ajax '/users',
@@ -22,7 +51,7 @@
             success: (response) ->
               window.location = response.redirect
             error: (event) ->
-              $('a#facebook-login').removeClass 'submitting'
+              $btn.removeClass 'disabled'
               alert "Er is iets mis gegaan, probeer het later nog eens!"
       , scope: 'email'
       false


No need this.

No need this.

--- client/landing/app/Applications/Inbox.kdapplication/Controllers/InboxMessageListController.coffee
+++ client/landing/app/Applications/Inbox.kdapplication/Controllers/InboxMessageListController.coffee
@@ -6,7 +6,6 @@

   loadMessages:(callback)->
     @removeAllItems()
-    @utils.wait 7000, => callback?()
     KD.whoami().fetchMail
       # limit       : 20
       sort        :


Fix a typo: `prefxi` => `prefix`

Fix a typo: `prefxi` => `prefix`

--- services/web/public/coffee/ide/editor/directives/aceEditor/auto-complete/AutoCompleteManager.coffee
+++ services/web/public/coffee/ide/editor/directives/aceEditor/auto-complete/AutoCompleteManager.coffee
@@ -42,7 +42,7 @@

 			labelsManager = @labelsManager
 			LabelsCompleter =
-				getCompletions: (editor, session, pos, prefxi, callback) ->
+				getCompletions: (editor, session, pos, prefix, callback) ->
 					upToCursorRange = new Range(pos.row, 0, pos.row, pos.column)
 					lineUpToCursor = editor.getSession().getTextRange(upToCursorRange)
 					commandFragment = getLastCommandFragment(lineUpToCursor)


Fixed issue with Profile sync

Fixed issue with Profile sync

--- server/methods/box/syncProfile.coffee
+++ server/methods/box/syncProfile.coffee
@@ -4,7 +4,7 @@
     @unblock()

     lastSync = Meteor.user().profile.lastSync
-    if Date.now() - lastSync - 43200000 > 0
+    if lastSync == undefined || Date.now() - lastSync - 43200000 > 0

       console.log("Checking token expiration...")
       tokenExpiration = Meteor.user().services.box.expiresAt


Ext/Table: Stop listening to normal Kinetic events while dropper is loaded.

--- Oghma/Ext/Table.coffee
+++ Oghma/Ext/Table.coffee
@@ -68,18 +68,19 @@
   # @param [function(x, y, event)] down Function to load for mouse down.
   # @param [function(x, y, event)] move  Function to load for mouse move.
   # @param [function()] unload Function to load for unload.
-  # @return [Oghma.App] this
+  # @return [Oghma.Table] this
   load_dropper: ( up, down = null, move = null, unload = null ) ->
     @dropper_up     = up
     @dropper_down   = down
     @dropper_move   = move
     @dropper_unload = unload
     document.body.style.cursor = 'crosshair'
+    @stopListening()
     this

   # Unload the dropper.
   #
-  # @return [Oghma.App] this
+  # @return [Oghma.Table] this
   unload_dropper: ->
     @dropper_unload?()
     @dropper_up     = null
@@ -87,14 +88,32 @@
     @dropper_move   = null
     @dropper_unload = null
     document.body.style.cursor = 'default'
-    this
+    @startListening()
+    this
+
+  # Prevent object bindings.
+  #
+  # @return [Oghma.Table] this]
+  stopListening: ->
+    for child in @stage.children
+      child.listening(false)
+    null
+
+  # Allow object bindings.
+  #
+  # @return [Oghma.Table] this]
+  startListening: ->
+    for child in @stage.children
+      child.listening(true)
+    null
+

   # Mouse down the dropper.
   #
   # @param [numeric] x X stage location.
   # @param [numeric] y Y stage location.
   # @param [Object] e Event
-  # @return [Oghma.App] this
+  # @return [Oghma.Table] this
   down_dropper: ( x, y, e = null ) ->
     @dropper_down?( x, y, e )
     this
@@ -104,7 +123,7 @@
   # @param [numeric] x X stage location.
   # @param [numeric] y Y stage location.
   # @param [Object] e Event
-  # @return [Oghma.App] this
+  # @return [Oghma.Table] this
   move_dropper: ( x, y, e = null ) ->
     @dropper_move?( x, y, e )
     this
@@ -114,7 +133,7 @@
   # @param [numeric] x X stage location.
   # @param [numeric] y Y stage location.
   # @param [Object] e Event
-  # @return [Oghma.App] this
+  # @return [Oghma.Table] this
   up_dropper: ( x, y, e = null ) ->
     original_dropper_up = @dropper_up
     if ! e? || ! e.altKey


remove karma-coverage plugin from karma task


fix conversation name editing

fix conversation name editing

--- src/ui/models/convsettings.coffee
+++ src/ui/models/convsettings.coffee
@@ -58,6 +58,7 @@
                 properties:
                     photo_url: p.photo_url
                     display_name: p.display_name or p.fallback_name
+        @group = @selectedEntities.length > 1
         @id = c.conversation_id?.id or c.id?.id
         @initialName = @name = c.name or ""
         @initialSearchQuery = ""


added default uncountable 'music'

added default uncountable 'music'

--- src/underscore.inflections.coffee
+++ src/underscore.inflections.coffee
@@ -4,7 +4,7 @@
   # Common uncountable words
   defaultUncountables: [
     'equipment', 'information', 'rice', 'money', 'species', 'series',
-    'fish', 'sheep', 'jeans', 'moose', 'deer', 'news'
+    'fish', 'sheep', 'jeans', 'moose', 'deer', 'news', 'music'
   ]

   # Plural rules


Update console log.

Update console log.

--- main.coffee
+++ main.coffee
@@ -238,11 +238,11 @@
         # sheets

         @sheet = {}
-        console.log "sheetJsn?", JSON.parse(gist.files["sheet.json"].content)
+        console.log "A", JSON.parse(gist.files["sheet.json"].content)
         fn = (x) -> x
+
         sheetJson = JSON.parse(gist.files["sheet.json"].content)
-
-        console.log "????", sheetJson
+        console.log "B", sheetJson

         for sym of sheetJson
             @sheet[sym] = new toolbox["sheet"] sheetJson[sym]


fixed js bug

fixed js bug

--- static/coffeescript/issues.coffee
+++ static/coffeescript/issues.coffee
@@ -2,7 +2,7 @@
   $("#create_issue").submit ->
     $.post this.action, $(this).serialize(), (response) ->
         issue = response.data
-        document.location.href = "/projects/#{issue.project.id}/issues/#{issue.id}"
+        document.location.href = "/projects/#{issue.project_id}/issues/#{issue.id}"

     return false


Add id back in

Add id back in

git-svn-id: f0ad932f837d50b52f73eb632e8f284266842b45@122 46e4088a-c2b5-4d24-90db-838aeec2e325

--- resources/static/brunch/src/app/views/home_view.coffee
+++ resources/static/brunch/src/app/views/home_view.coffee
@@ -24,7 +24,7 @@
     txtVal = txtAddRule.val()

     # TODO: Make this actually work
-    newRule = new Rule({rule: txtVal})
+    newRule = new Rule({id: "", rule: txtVal})

     if newRule.validate()
       res = app.collections.rulesList.find(


Fix tests.

Fix tests.

--- test/rails_app/app/assets/javascripts/admin.coffee
+++ test/rails_app/app/assets/javascripts/admin.coffee
@@ -130,7 +130,7 @@
           _position:    { type: 'float'  }
           body_html:    { type: 'text'   }

-  chr.start(config)
+  chr.start('Test', config)


Removing this coffescript file that isn't having any effect; the flash message js is also in main.js.coffee.


fix state-change listener func

fix state-change listener func

--- game.coffee
+++ game.coffee
@@ -103,7 +103,7 @@
         @encounterManager = new EncounterManager(trajJSON);
         @locationManager = new LocationManager(trajJSON);
         @eventManager = new EventManager();
-        @eventManager.on(EVENTS.SWITCH_STATE, (data)->
+        @eventManager.on(EVENTS.SWITCH_STATE, (event, data)->
             console.log('CHANGING GAME STATE TO ', data.newState);
             globalData.game.state.start(data.newState);
         );


[Defaults] Testing plugins support

[Defaults] Testing plugins support

--- tests/src/app/features/defaults/module.spec.coffee
+++ tests/src/app/features/defaults/module.spec.coffee
@@ -18,12 +18,13 @@
     sinon              = require 'sinon'
     chai               = require 'chai'
     assert             = chai.assert
+    expect             = chai.expect

     ###*
      * Tests
     ###
-    describe 'Provides a default error handler: ', ->
+    describe 'Provides a default error handler... ', ->
       beepStup = null
       logStub  = null

@@ -72,4 +73,63 @@

-      +
+
+    describe 'Provides node plugins to use as option...', ->
+
+      it 'Being an object as API', ->
+        expect(defaultOptions.plugins).to.be.a('object')
+
+      it 'Contains all gulp plugins in basebuild (gulp-* pattern loaded for gulp-load-plugins)', ->
+        gulpPlugins = [
+          "angularFilesort"
+          "angularTemplatecache"
+          "cjsx"
+          "coffee"
+          "coffeelint"
+          "concat"
+          "csso"
+          "filter"
+          "flatten"
+          "if"
+          "inject"
+          "jshint"
+          "minifyHtml"
+          "ngAnnotate"
+          "protractor"
+          "rename"
+          "replace"
+          "rev"
+          "revReplace"
+          "sass"
+          "size"
+          "sourcemaps"
+          "uglify"
+          "useref"
+          "util"
+          "watch"
+        ]
+
+        for plugin in gulpPlugins then expect(defaultOptions.plugins).to.have.property(plugin)
+
+
+      it 'Contains all third-party plugins loaded in basebuild', ->
+        thirdPartyPlugins = [
+          "browserSync"
+          "browserSyncSpa"
+          "concatStream"
+          "del"
+          "httpProxy"
+          "jshintStylish"
+          "lodash"
+          "mainBowerFiles"
+          "mergeStream"
+          "protractor"
+          "requireDir"
+          "shelljs"
+          "uglifySaveLicense"
+          "wiredep"
+          "chalk"
+        ]
+
+        for plugin in thirdPartyPlugins then expect(defaultOptions.plugins).to.have.property(plugin)