code indention for literate coffee

code indention for literate coffee

--- src/components.coffee
+++ src/components.coffee
@@ -1,209 +1,209 @@
-class State
-
-    constructor: (@done = false) ->
-
-
-class Point
-
-    constructor: (@x = 0, @y = 0) ->
-
-    toString: -> "#{@x}-#{@y}"
-
-
-class Range
-
-    constructor: (@top = 0, @bottom = 0, @left = 0, @right = 0) ->
-
-    unitX: -> @left + @right + 1
-
-    unitY: -> @top + @bottom + 1
-
-    markX: (offset) -> ((1 << @unitX()) - 1) << offset - @left
-
-    markY: (offset) -> ((1 << @unitY()) - 1) << offset - @top
-
-
-class CellModel
-
-    constructor: ->
-        @typeHash = {}
-        @nameHash = {}
-
-    add: (instance) ->
-        @typeHash[instance.constructor] = instance
-
-    get: (clazz) ->
-        @typeHash[clazz]
-
-    gets: (types...) ->
-
-        obj = {}
-
-        for type in types when type of @typeHash
-
-            unless type of @nameHash
-                key = type.toString().match(/^function (.)([^\(]+)/)
-                key = key[1].toLowerCase() + key[2]
-                @nameHash[type] = key
-
-            obj[@nameHash[type]] = @get type
-
-        obj
-
-
-class GridModel
-
-    constructor: (width = 0, height = 0, cellClass = Object) ->
-
-        @row = []
-        @col = []
-
-        for i in [0...height]
-            @row[i] = []
-            for j in [0...width]
-                @row[i][j] = new cellClass
-
-        for i in [0...width]
-            @col[i] = []
-            for j in [0...height]
-                @col[i][j] = @row[j][i]
-
-    height: -> @row.length
-    width: -> @col.length
-
-    getRow: (index) -> @row[index]
-    getCol: (index) -> @col[index]
-
-    getCell: (x, y) ->
-
-        return null if x < 0 or x >= @width()
-        return null if y < 0 or y >= @height()
-
-        @getRow(y)[x]
-
-
-class Calculate
-
-    constructor: (@grid) ->
-
-    hasMatch: (foo, bar) ->
-
-        return if foo is bar
-
-        [foo, bar] = for item in [foo, bar]
-            item.gets Point, Range
-
-        [top, bottom] = `foo.point.y < bar.point.y ? [foo, bar] : [bar, foo]`
-        [left, right] = `foo.point.x < bar.point.x ? [foo, bar] : [bar, foo]`
-
-        absX = right.point.x - left.point.x
-        absY = bottom.point.y - top.point.y
-
-        if foo.point.y is bar.point.y and absX is left.range.right + 1
-            return true
-
-        if foo.point.x is bar.point.x and absY is top.range.bottom + 1
-            return true
-
-        if top is left
-            if top.range.bottom >= absY and bottom.range.left >= absX
+    class State
+
+        constructor: (@done = false) ->
+
+
+    class Point
+
+        constructor: (@x = 0, @y = 0) ->
+
+        toString: -> "#{@x}-#{@y}"
+
+
+    class Range
+
+        constructor: (@top = 0, @bottom = 0, @left = 0, @right = 0) ->
+
+        unitX: -> @left + @right + 1
+
+        unitY: -> @top + @bottom + 1
+
+        markX: (offset) -> ((1 << @unitX()) - 1) << offset - @left
+
+        markY: (offset) -> ((1 << @unitY()) - 1) << offset - @top
+
+
+    class CellModel
+
+        constructor: ->
+            @typeHash = {}
+            @nameHash = {}
+
+        add: (instance) ->
+            @typeHash[instance.constructor] = instance
+
+        get: (clazz) ->
+            @typeHash[clazz]
+
+        gets: (types...) ->
+
+            obj = {}
+
+            for type in types when type of @typeHash
+
+                unless type of @nameHash
+                    key = type.toString().match(/^function (.)([^\(]+)/)
+                    key = key[1].toLowerCase() + key[2]
+                    @nameHash[type] = key
+
+                obj[@nameHash[type]] = @get type
+
+            obj
+
+
+    class GridModel
+
+        constructor: (width = 0, height = 0, cellClass = Object) ->
+
+            @row = []
+            @col = []
+
+            for i in [0...height]
+                @row[i] = []
+                for j in [0...width]
+                    @row[i][j] = new cellClass
+
+            for i in [0...width]
+                @col[i] = []
+                for j in [0...height]
+                    @col[i][j] = @row[j][i]
+
+        height: -> @row.length
+        width: -> @col.length
+
+        getRow: (index) -> @row[index]
+        getCol: (index) -> @col[index]
+
+        getCell: (x, y) ->
+
+            return null if x < 0 or x >= @width()
+            return null if y < 0 or y >= @height()
+
+            @getRow(y)[x]
+
+
+    class Calculate
+
+        constructor: (@grid) ->
+
+        hasMatch: (foo, bar) ->
+
+            return if foo is bar
+
+            [foo, bar] = for item in [foo, bar]
+                item.gets Point, Range
+
+            [top, bottom] = `foo.point.y < bar.point.y ? [foo, bar] : [bar, foo]`
+            [left, right] = `foo.point.x < bar.point.x ? [foo, bar] : [bar, foo]`
+
+            absX = right.point.x - left.point.x
+            absY = bottom.point.y - top.point.y
+
+            if foo.point.y is bar.point.y and absX is left.range.right + 1
                 return true
-            if bottom.range.top >= absY and top.range.right >= absX
+
+            if foo.point.x is bar.point.x and absY is top.range.bottom + 1
                 return true
-        else
-            if top.range.bottom >= absY and bottom.range.right >= absX
-                return true
-            if bottom.range.top >= absY and top.range.left >= absX
-                return true
-
-        [fooOuter, barOuter] = for item in [foo, bar]
-            getOuterBits item, @grid.width(), @grid.height()
-
-        return true if fooOuter & barOuter
-
-        [fooMarks, barMarks] = (getMarkBits item for item in [foo, bar])
-
-        if result = getBitAdd fooMarks.x, barMarks.x
-            for i in [0...result.length]
-                list = @grid.getCol i + result.offset
-                for j in [top.point.y...bottom.point.y]
-                    break unless list[j].get(State).done
-                return true if j is bottom.point.y
-
-        if result = getBitAdd fooMarks.y, barMarks.y
-            for i in [0...result.length]
-                list = @grid.getRow i + result.offset
-                for j in [left.point.x...right.point.x]
-                    break unless list[j].get(State).done
-                return true if j is right.point.x
-
-        false
-
-    markMatch: (cell) ->
-
-        state = cell.get State
-        state.done = true
-
-        range = cell.get Range
-
-        cellRange = getCellRange @grid, cell
-
-        cellRange.bottom?.top = range.unitY()
-        cellRange.top?.bottom = range.unitY()
-
-        cellRange.right?.left = range.unitX()
-        cellRange.left?.right = range.unitX()
-
-    getCellRange = (grid, cell) ->
-
-        point = cell.get Point
-        range = cell.get Range
-
-        list =
-            top:    grid.getCell point.x, point.y - range.top - 1
-            bottom: grid.getCell point.x, point.y + range.bottom + 1
-            left:   grid.getCell point.x - range.left - 1, point.y
-            right:  grid.getCell point.x + range.right + 1, point.y
-
-        for key, value of list
-            list[key] = value?.get Range
-
-        list
-
-    getOuterBits = (item, width, height) ->
-        result   = 0
-        result  += item.point.y is item.range.top
-        result <<= 1
-        result  += item.point.y + item.range.bottom + 1 is height
-        result <<= 1
-        result  += item.point.x is item.range.left
-        result <<= 1
-        result  += item.point.x + item.range.right + 1 is width
-        result
-
-    getMarkBits = (item) ->
-        x: item.range.markX item.point.x
-        y: item.range.markY item.point.y
-
-    getBitAdd = (a, b) ->
-
-        result = a & b
-
-        @hash or= 0: false
-
-        return @hash[result] if result of @hash
-
-        list = result.toString(2).split('0')
-
-        @hash[result] =
-            offset: list.length - 1
-            length: list[0].length
-
-
-
-exports = {
-    State, Point, Range, CellModel, GridModel, Calculate
-}
-
-unless module?.exports = exports
-    @components = exports
+
+            if top is left
+                if top.range.bottom >= absY and bottom.range.left >= absX
+                    return true
+                if bottom.range.top >= absY and top.range.right >= absX
+                    return true
+            else
+                if top.range.bottom >= absY and bottom.range.right >= absX
+                    return true
+                if bottom.range.top >= absY and top.range.left >= absX
+                    return true
+
+            [fooOuter, barOuter] = for item in [foo, bar]
+                getOuterBits item, @grid.width(), @grid.height()
+
+            return true if fooOuter & barOuter
+
+            [fooMarks, barMarks] = (getMarkBits item for item in [foo, bar])
+
+            if result = getBitAdd fooMarks.x, barMarks.x
+                for i in [0...result.length]
+                    list = @grid.getCol i + result.offset
+                    for j in [top.point.y...bottom.point.y]
+                        break unless list[j].get(State).done
+                    return true if j is bottom.point.y
+
+            if result = getBitAdd fooMarks.y, barMarks.y
+                for i in [0...result.length]
+                    list = @grid.getRow i + result.offset
+                    for j in [left.point.x...right.point.x]
+                        break unless list[j].get(State).done
+                    return true if j is right.point.x
+
+            false
+
+        markMatch: (cell) ->
+
+            state = cell.get State
+            state.done = true
+
+            range = cell.get Range
+
+            cellRange = getCellRange @grid, cell
+
+            cellRange.bottom?.top = range.unitY()
+            cellRange.top?.bottom = range.unitY()
+
+            cellRange.right?.left = range.unitX()
+            cellRange.left?.right = range.unitX()
+
+        getCellRange = (grid, cell) ->
+
+            point = cell.get Point
+            range = cell.get Range
+
+            list =
+                top:    grid.getCell point.x, point.y - range.top - 1
+                bottom: grid.getCell point.x, point.y + range.bottom + 1
+                left:   grid.getCell point.x - range.left - 1, point.y
+                right:  grid.getCell point.x + range.right + 1, point.y
+
+            for key, value of list
+                list[key] = value?.get Range
+
+            list
+
+        getOuterBits = (item, width, height) ->
+            result   = 0
+            result  += item.point.y is item.range.top
+            result <<= 1
+            result  += item.point.y + item.range.bottom + 1 is height
+            result <<= 1
+            result  += item.point.x is item.range.left
+            result <<= 1
+            result  += item.point.x + item.range.right + 1 is width
+            result
+
+        getMarkBits = (item) ->
+            x: item.range.markX item.point.x
+            y: item.range.markY item.point.y
+
+        getBitAdd = (a, b) ->
+
+            result = a & b
+
+            @hash or= 0: false
+
+            return @hash[result] if result of @hash
+
+            list = result.toString(2).split('0')
+
+            @hash[result] =
+                offset: list.length - 1
+                length: list[0].length
+
+
+
+    exports = {
+        State, Point, Range, CellModel, GridModel, Calculate
+    }
+
+    unless module?.exports = exports
+        @components = exports


add transifex lang codes for chinese

add transifex lang codes for chinese

--- tasks/translations.coffee
+++ tasks/translations.coffee
@@ -157,13 +157,18 @@
   all_translations = {}
   source = JSON.parse fs.readFileSync('./i18n/_source.json')

+  transifex_langs = {
+    'zh-CN': 'zh-Hans',
+    'zh-TW': 'zh-Hant'
+  }
+
   async.series [
     (step) ->
       async.each supported_languages, (lang, next) ->
         return next() if lang == 'en'

         previous_translations = JSON.parse fs.readFileSync("./i18n/#{lang}.json")
-        url = "https://#{process.env.TRANSIFEX_KEY}@www.transifex.com/api/2/project/championify/resource/english-source/translation/#{lang}/?mode=default&file"
+        url = "https://#{process.env.TRANSIFEX_KEY}@www.transifex.com/api/2/project/championify/resource/english-source/translation/#{transifex_langs[lang] or lang}/?mode=default&file"
         request url, (err, res, body) ->
           return next(err) if err

@@ -221,6 +226,7 @@

               fs.writeFile "./i18n/#{lang}.json", JSON.stringify(translations, null, 2), {encoding: 'utf8'}, next
             else
+              console.log 'Translation not saved...'.bold.red
               return next()

       , (err) ->


default dialog urls to http

default dialog urls to http

--- src/sc/dialog.coffee
+++ src/sc/dialog.coffee
@@ -62,12 +62,12 @@
           ), 1
         else if window.top
           window.top.setTimeout (->
-            console.log("1");
             window.top.SC.Dialog._handleDialogReturn(window)
           ), 1

     buildUrlForDialog: (dialogName, options={}) ->
       url = new SC.URI(SC._baseUrl)
+      url.scheme = "http"
       url.fragment = SC.Helper.merge options,
         redirect_uri: SC.options.redirect_uri
       switch(dialogName)


:memo: Doc `fullLine` parameter to Selection::{copy,cut}

--- src/selection.coffee
+++ src/selection.coffee
@@ -546,6 +546,7 @@
   # Public: Copies the selection to the clipboard and then deletes it.
   #
   # * `maintainClipboard` {Boolean} (default: false) See {::copy}
+  # * `fullLine` {Boolean} (default: false) See {::copy}
   cut: (maintainClipboard=false, fullLine=false) ->
     @copy(maintainClipboard, fullLine)
     @delete()
@@ -556,6 +557,9 @@
   #   is created to store each content copied to the clipboard. The clipboard
   #   `text` still contains the concatenation of the clipboard with the
   #   current selection. (default: false)
+  # * `fullLine` {Boolean} if `true`, the copied text will always be pasted
+  #   at the beginning of the line containing the cursor, regardless of the
+  #   cursor's horizontal position. (default: false)
   copy: (maintainClipboard=false, fullLine=false) ->
     return if @isEmpty()
     selectionText = @editor.buffer.getTextInRange(@getBufferRange())


Position editor based on paletteElement.offsetWidth.

If the CSS limits paletteElementWidth (e.g., using max-width), the offsetWidth
may be different from the .style.width.  Throughout the code we position the
editor to the right of the palette by trusing the offsetWidth, but this one used
style.width, resulting in slighly-off layouts when max-width applied.

--- src/controller.coffee
+++ src/controller.coffee
@@ -3209,7 +3209,7 @@
         # Translate the ACE editor div into frame.
         @aceElement.style.top = '0px'
         if @showPaletteInTextMode and @paletteEnabled
-          @aceElement.style.left = @paletteWrapper.style.width
+          @aceElement.style.left = "#{@paletteWrapper.offsetWidth}px"
         else
           @aceElement.style.left = '0px'


Single change event, and sync with ace.

Single change event, and sync with ace.

--- src/controller.coffee
+++ src/controller.coffee
@@ -438,10 +438,12 @@

         # Update the ace editor value to match,
         # but don't trigger a resize event.
-        @suppressAceChangeEvent = true; oldScroll = @aceEditor.session.getScrollTop()
+        @suppressAceChangeEvent = true
+        oldScroll = @aceEditor.session.getScrollTop()
         console.log 'SET ACE @changeEventVersion'
         @aceEditor.setValue @getValue(), -1
-        @suppressAceChangeEvent = false; @aceEditor.session.setScrollTop oldScroll
+        @suppressAceChangeEvent = false
+        @aceEditor.session.setScrollTop oldScroll

         @fireEvent 'change', []

@@ -2649,8 +2651,17 @@
   # ================================

   Editor::copyAceEditor = ->
+    clearTimeout @changeFromAceTimer
+    @changeFromAceTimer = null
     @gutter.style.width = @aceEditor.renderer.$gutterLayer.gutterWidth + 'px'
     @resizeBlockMode()
+    return @setValue_raw @aceEditor.getValue()
+
+  Editor::changeFromAceEditor = ->
+    if @changeFromAceTimer then return
+    @changeFromAceTimer = setTimeout (=>
+      @copyAceEditor()
+    ), 0

   hook 'populate', 0, ->
     @aceElement = document.createElement 'div'
@@ -2666,8 +2677,11 @@
     @aceEditor.getSession().setTabSize 2

     @aceEditor.on 'change', =>
-      if @currentlyUsingBlocks and not @suppressAceChangeEvent
-        @copyAceEditor()
+      if @suppressAceChangeEvent then return
+      if @currentlyUsingBlocks
+        @changeFromAceEditor()
+      else
+        @fireEvent 'change', []

     @currentlyUsingBlocks = true
     @currentlyAnimating = false
@@ -2949,11 +2963,7 @@

   Editor::performFreezeAnimation = (fadeTime = 500, translateTime = 500, cb = ->)->
     if not @currentlyUsingBlocks and not @currentlyAnimating
-      @copyAceEditor()
-
-      @fireEvent 'statechange', [true]
-
-      setValueResult = @setValue_raw @aceEditor.getValue()
+      setValueResult = @copyAceEditor()

       unless setValueResult.success
         return setValueResult
@@ -2965,6 +2975,7 @@

       @currentlyUsingBlocks = true
       @currentlyAnimating = true
+      @fireEvent 'statechange', [true]

       setTimeout (=>
         # Hide scrollbars and increase width
@@ -3344,7 +3355,6 @@

     oldScrollTop = @aceEditor.session.getScrollTop()

-    console.log 'SET ACE @setValue'
     @aceEditor.setValue value, -1
     @aceEditor.resize true


Add text/line transformation shortcuts

Add text/line transformation shortcuts

Borrowed from Aptana's keyboard shortcuts that I'm too used to using.

--- .config/keymap.cson
+++ .config/keymap.cson
@@ -24,8 +24,21 @@
 # If you are unfamiliar with CSON, you can read more about it here:
 # https://github.com/bevry/cson#what-is-cson

+
+# Display
 'body':
 	'cmd-alt-w':	'window:toggle-invisibles'

+
+# Editing
 'atom-text-editor:not([mini])':
-	'cmd-d':		'editor:delete-line'+	'cmd-d':		'editor:delete-line'
+	'alt-up':		'editor:move-line-up'
+	'alt-down':		'editor:move-line-down'
+	'cmd-alt-up':	'editor:duplicate-lines'
+
+
+# Character transformation
+'.platform-darwin atom-text-editor':
+	'cmd-shift-U':	'editor:upper-case'
+	'cmd-u':		'editor:lower-case'


修复Model和DataModel的整体重构后遗留的BUG

修复Model和DataModel的整体重构后遗留的BUG

--- src/core/model.coffee
+++ src/core/model.coffee
@@ -935,26 +935,22 @@
 		return

 	getProperty: (path) ->
-		i = path.indexOf(".")
-		if i > 0
-			path1 = path.substring(0, i)
-			path2 = path.substring(i + 1)
-		else
-			path1 = null
-			path2 = path
-
-		dataModel = @
-		while dataModel
-			rootDataType = dataModel._rootDataType
-			if rootDataType
-				if path1
-					dataType = rootDataType.getProperty(path1)?.get("dataType")
-				else
-					dataType = rootDataType
-				if dataType then break
-			dataModel = dataModel.model.parent?.data
-
-		return dataType?.getProperty(path2)
+		if @_rootDataType
+			i = path.indexOf(".")
+			if i > 0
+				path1 = path.substring(0, i)
+				path2 = path.substring(i + 1)
+			else
+				path1 = null
+				path2 = path
+
+			if path1
+				dataType = @_rootDataType.getProperty(path1)?.get("dataType")
+			else
+				dataType = @_rootDataType
+			return dataType?.getProperty(path2)
+		else
+			return @parent?.getProperty(path)

 	getDataType: (path) ->
 		property = @getProperty(path)
@@ -1095,11 +1091,11 @@
 		if i > 0
 			holder = @_aliasMap[path.substring(0, i)]
 			if holder?.path
-				path = holder.path + path.substring(i + 1)
+				path = holder.path + path.substring(i)
 		else
 			holder = @_aliasMap[path]
 			if holder?.path
-				path = holder.alias
+				path = holder.path
 		return @parent.getProperty(path)

 	getDataType: (path) ->
@@ -1107,11 +1103,11 @@
 		if i > 0
 			holder = @_aliasMap[path.substring(0, i)]
 			if holder?.path
-				path = holder.path + path.substring(i + 1)
+				path = holder.path + path.substring(i)
 		else
 			holder = @_aliasMap[path]
 			if holder?.path
-				path = holder.alias
+				path = holder.path
 		return @parent.getDataType(path)

 	_isExBindingPath: (path) ->


new version of homepage with video player with custom controls

Former-commit-id: c3d937955f0804198bccd1310fced722013bca54 [formerly a4250bdbd7258829e47a1bda781e3cbd9c38ac03] [formerly c7f01b06b9ea5aded5d5226e413a9b8e9aa9b1c7 [formerly 7d0a91ad8b076211d4ec7142e7e128a176180436]] [formerly 5fbd1f424e59f5ab94ff38b421e49d98b4b5066a [formerly c867fb2aadfc570ba9483c8531fddb621e6a3114] [formerly 40184a6b5940d7541165dcf1939ff6a2136ccf44 [formerly 40184a6b5940d7541165dcf1939ff6a2136ccf44 [formerly 264695b53e642e5f121508d215bf0547f5947601]]]]
Former-commit-id: 0865ce0be434fb9e6c18d93544c078d47e5e428f [formerly 8e669e3f9937b1a36eb59b7f2ed16b48254d7873] [formerly 367d668e5f87ed560a66f4ba97235c3025d214d0 [formerly 06d51f6437e0b4a77fa672a022dbffa077b0358e]]
Former-commit-id: 8554ae550c32d6e5f4a1c7d4bb7da2de87138f03 [formerly 6729be47394451a6d7e9233d86fd622dca04be4a]
Former-commit-id: 491d80cee758b1093ee386508dbd535a4e483acc
Former-commit-id: 7819c0bb9c24b1284dc5111b0810a3c2b79e6909

--- @client/saas_landing_page.coffee
+++ @client/saas_landing_page.coffee
@@ -5,111 +5,231 @@
 require './browser_location'
 require './tooltip'
 require './shared'
+require './slider'
+require './development'
+require './state_dash'
+
+SAAS_PAGE_WIDTH = 1000
+TEXT_WIDTH = 730

 base_text =
   fontSize: 24
-  fontWeight: 200
+  fontWeight: 400
   fontFamily: "'Avenir Next W01', 'Avenir Next', 'Lucida Grande', 'Helvetica Neue', Helvetica, Verdana, sans-serif"

 h1 = _.extend {}, base_text,
-  fontSize: 72
+  fontSize: 48
   fontWeight: 400
+  textAlign: 'center'

 h2 = _.extend {}, base_text,
-  fontSize: 48
-  fontWeight: 700
-
-h3 = _.extend {}, base_text,
   fontSize: 36
   fontWeight: 300
+  textAlign: 'center'

 strong = _.extend {}, base_text,
   fontWeight: 600

-bullet = (props) ->
-  DIV
-    style:
-      paddingLeft: 30
-      position: "relative"
-      margin: "30px 0"
-
-    DIV
-      style:
-        position: "absolute"
-        left: 0
-
-      switch props.point_style
-        when "bullet"
-          "•"
-        when "pro"
-          "+"
-        when "con"
-          "–"
-
-    P null,
-      SPAN
-        style: strong
-        props.strong
-      SPAN null,
-        props.body
+nav_link_style = _.extend {}, base_text,
+  fontWeight: 600
+  fontSize: 24
+  color: 'white'
+  marginLeft: 40
+  cursor: 'pointer'
+
+a = _.extend {}, base_text,
+  color: logo_red
+  cursor: 'pointer'
+  textDecoration: 'underline'
+

 SaasHomepage = ReactiveComponent
   displayName: "SaasHomepage"
   render: ->
-    DIV null, title(), Videos(), usedFor(), pricing(), contact(), story()
-
-title = ->
-  chapter = fetch("video_chapter")
-
-  DIV
-    style:
-      marginTop: 50
-    H1
-      style: _.extend {}, h1,
-        textAlign: "center"
-      "Think better together"
-
-    H3
-      style: _.extend {}, h3,
-        textAlign: "center"
-
-      if chapter.text
-        chapter.text
-      else
-        "Sometimes a proposal bears careful thought"
-
-
-Videos = ReactiveComponent
-  displayName: "videos"
+    DIV null,
+      Video()
+      usedFor()
+      pricing()
+      contact(@local)
+      story()
+
+
+Header = ReactiveComponent
+  displayName: "Header"
+  render: ->
+    current_user = fetch("/current_user")
+
+    DIV null,
+      DIV
+        style:
+          position: "relative"
+          margin: "0 auto"
+          backgroundColor: logo_red
+          height: 75
+
+        IMG
+          src: asset("saas_landing_page/considerit_logo.svg")
+          style:
+            position: "relative"
+            top: 23
+            left: 12
+            height: 68
+
+      # nav menu
+      DIV
+        style:
+          width: SAAS_PAGE_WIDTH
+          margin: 'auto'
+          position: 'relative'
+          top: -34
+
+
+        DIV
+          style:
+            position: 'absolute'
+            right: 0
+
+          A
+            style: nav_link_style
+            href: "#pricing"
+            'pricing'
+          A
+            style: nav_link_style
+            href: "#contact"
+            'contact'
+          A
+            style: nav_link_style
+            href: "#story"
+            'story'
+
+      DIV
+        style:
+          fontSize: 24
+          fontWeight: 300
+          color: logo_red
+          position: 'relative'
+          left: 16
+          top: 4
+        "for thinking together"
+
+
+
+VIDEO_SLIDER_WIDTH = 250
+Video = ReactiveComponent
+  displayName: "video"
   render: ->

-
-    DIV null,
-
-      STYLE
-        type: "text/css"
-        "#homepage_video video::cue { visibility: hidden; }"
-
+    chapter = fetch("video_chapter")
+    controls = fetch('video_controls')
+
+    if !controls.playing?
+      controls.playing = true
+      save controls
+
+    DIV null,
       DIV
         id: "homepage_video"
         style:
-          marginTop: 40
-          width: 1000
+          width: SAAS_PAGE_WIDTH

         VIDEO
           preload: "auto"
           loop: true
           autoPlay: true
-          width: 1000
+          width: SAAS_PAGE_WIDTH
           ref: "video"
-
+
           SOURCE
-            src: asset("saas_landing_page/deathstar_cam.mp4")
+            src: asset("saas_landing_page/with_screen.mp4")
             type: "video/mp4"

           SOURCE
-            src: asset("saas_landing_page/deathstar_cam.webm")
-            type: "video/webm"
+            src: asset("saas_landing_page/with_screen.webm")
+            type: "video/webm"
+
+
+      DIV
+        style:
+          marginTop: 10
+          position: 'relative'
+
+
+        H2
+          style: _.extend {}, h2,
+            textAlign: "center"
+
+          if chapter.text
+            chapter.text
+          else
+            "Sometimes a proposal bears careful thought"
+
+        DIV
+          onMouseEnter: (ev) => @local.hover_player = true; save @local
+          onMouseLeave: (ev) => @local.hover_player = false; save @local
+
+          style:
+            width: VIDEO_SLIDER_WIDTH + 80
+            margin: 'auto'
+            #padding: '15px 10px'
+            position: 'relative'
+            opacity: if @local.hover_player then 1 else .5
+            top: -10
+          I
+            className: "fa fa-#{if controls.playing then 'pause' else 'play'}"
+            onClick: (ev) =>
+              controls = fetch('video_controls')
+              controls.playing = !controls.playing
+              save controls
+              if controls.playing
+                @refs.video.getDOMNode().play()
+              else
+                @refs.video.getDOMNode().pause()
+
+            style:
+              fontSize: 10
+              color: '#ccc'
+              #position: 'relative'
+              padding: '12px 12px'
+              cursor: 'pointer'
+              visibility: if @local.hover_player then 'visible' else 'hidden'
+              left: -12
+
+          Slider
+            key: 'video_controls'
+            width: VIDEO_SLIDER_WIDTH
+            handle_height: if @local.hover_player then 20 else 4
+            base_height: 4
+            base_color: '#ECECEC'
+            slider_style:
+              margin: 'auto'
+              position: 'absolute'
+              top: '50%'
+              marginTop: -2
+              left: 40
+            handle_props:
+              color: logo_red
+
+            onMouseDownCallback: (ev) =>
+              video = @refs.video.getDOMNode()
+              video.pause()
+
+            onMouseMoveCallback: (ev) =>
+              controls = fetch('video_controls')
+              video = @refs.video.getDOMNode()
+              video.currentTime = controls.value * video.duration
+
+            onMouseUpCallback: (ev) =>
+              controls = fetch('video_controls')
+              video = @refs.video.getDOMNode()
+              video.play() if controls.playing
+
+            onClickCallback: (ev) =>
+              controls = fetch('video_controls')
+              video = @refs.video.getDOMNode()
+              video.pause()
+              video.currentTime = controls.value * video.duration
+              video.play() if controls.playing
+

   componentDidMount: ->
@@ -120,10 +240,11 @@
     v = @refs.video.getDOMNode()
     v.addEventListener 'timeupdate', (ev) ->
       chapter = fetch("video_chapter")
+      controls = fetch('video_controls')

       text =
         if v.currentTime < 5
-          "Sometimes a proposal bears careful thought"
+          "Proposals often require careful thought"
         else if v.currentTime < 10
           "The Pro/Con list encourages thinking about tradeoffs"
         else if v.currentTime < 14.5
@@ -139,7 +260,7 @@
         else if v.currentTime < 41
           "Histogram shows the spectrum of opinion"
         else if v.currentTime < 46
-          "Points ranked by salience across whole population"
+          "Points ranked by salience across all individuals"
         else if v.currentTime < 53
           "Now explore patterns of thought!"
         else if v.currentTime < 58
@@ -153,195 +274,249 @@
         else
           "For small and large groups alike"

+      controls.value = v.currentTime / v.duration
+
+      save controls
+
       if chapter.text != text
         chapter.text = text
         save chapter
     , false

-usedFor = ->
+
+bullet = (props) ->
   DIV
     style:
-      marginTop: 20
-      marginBottom: 50
-  , H3(
-    style: _.extend({}, h3,
-      marginTop: 30
-      marginBottom: 10
-    )
-  , "Consider.it is good for…"), bullet(
-    point_style: "bullet"
-    strong: "Leading change. "
-    body: "Leadership can communicate with their constituents/employees/customers to get feedback on issues, or respond to common points, addressing hundreds of individuals at once. Get the ship pointed in the same direction!"
-  ), bullet(
-    point_style: "bullet"
-    strong: "Distributing decision making. "
-    body: "Make decisions as a whole, without resorting to centralization. The will of a crowd, and the thoughts behind that will, become visible."
-  ), bullet(
-    point_style: "bullet"
-    strong: "Teaching critical thinking. "
-    body: "Students learn how to develop a considered opinion while engaging with other’s ideas; teachers achieve Common Core goals."
-  ), H3(
-    style: _.extend({}, h3,
-      marginTop: 30
-      marginBottom: 10
-    )
-  , "Consider.it focuses dialogue to create clarity in many minds"), bullet(
-    point_style: "pro"
-    strong: "Produces a summary overview of group thought "
-    body: "that incorporates the unique opinion of each person. Patterns can be identified across all people. Perhaps 80% of opposers have a single con point that can be addressed!"
-  ), bullet(
-    point_style: "pro"
-    strong: "Fosters considerate interactions "
-    body: "by making it easy to consider all sides, find common ground, and work through sticking points. Personal attacks and trolling don’t make sense and can easily be identified."
-  ), bullet(
-    point_style: "pro"
-    strong: "Scales to hundreds of people "
-    body: "deliberating an issue together, without becoming overwhelming or requiring strong central leadership/facilitation."
-  ), bullet(
-    point_style: "con"
-    strong: "Limited to proposals. "
-    body: "Consider.it collects opinions on a “yesable” question, such as hypotheticals, proposals, ideas, or plans of action. Open ended questions like “How should we…” aren’t supported as well."
-  ), bullet(
-    point_style: "con"
-    strong: "Mediocre mobile quality. "
-    body: "The current design poorly translates to mobile, thus we cannot claim to be mobile optimized."
-  )
+      paddingLeft: 30
+      position: "relative"
+      margin: "30px auto"
+      width: TEXT_WIDTH
+
+    DIV
+      style:
+        position: "absolute"
+        left: -15
+
+      switch props.point_style
+        when "bullet"
+          "•"
+        when "pro"
+          "+"
+        when "con"
+          "–"
+
+    P null,
+      SPAN
+        style: strong
+        props.strong
+      SPAN
+        style: _.extend {}, base_text,
+          fontWeight: 300
+        props.body
+
+
+usedFor = ->
+
+  DIV
+    style:
+      marginTop: 60
+
+    H1 style: h1,
+      'Consider.it creates focused discussion by:'
+
+    bullet
+      point_style: 'bullet'
+      strong: "Collecting opinions on specific ideas"
+      body: ", proposals, plans of action, or hypotheticals. Even job candidates, products, or designs."
+
+    bullet
+      point_style: 'bullet'
+      strong: "Fostering considerate interactions"
+      body: ". The interface makes it easy to consider all sides without putting people in direct conflict. Personal attacks and trolling don’t fit the format."
+
+    bullet
+      point_style: 'bullet'
+      strong: "Producing an interactive summary of group thought"
+      body: ". Patterns of thought across the whole group can be identified. Perhaps 80% of opposers have a single con point that can be addressed!"
+
+    bullet
+      point_style: 'bullet'
+      strong: "Scaling to hundreds of people"
+      body: " deliberating an issue together, without becoming overwhelming or requiring strong central leadership/facilitation."
+

 pricing = ->

-story = ->
-
-contact = ->
-  teamMember = undefined
+contact = (local) ->
+
   teamMember = (props) ->
     DIV
       style:
         display: "inline-block"
         margin: "20px 15px"
         textAlign: "center"
-    , IMG(
-      src: props.src
-      style:
-        borderRadius: "50%"
-        display: "inline-block"
-        width: 125
-        textAlign: "center"
-    ), DIV(
-      style:
-        textAlign: "center"
-    , props.name), A(
-      style:
-        textAlign: "center"
-        color: focus_blue
-        textDecoration: "underline"
-        fontWeight: 400
-    , props.email)
-
-  DIV null, DIV(null, teamMember(
-    src: asset("saas_landing_page/mike.jpg")
-    name: "Michael Toomim"
-    email: "toomim@consider.it"
-  ), teamMember(
-    src: asset("saas_landing_page/kevin.jpg")
-    name: "Kevin Miniter"
-    email: "kevin@consider.it"
-  ), teamMember(
-    src: asset("saas_landing_page/travis.jpg")
-    name: "Travis Kriplean"
-    email: "travis@consider.it"
-  )), H2(
-    style: _.extend({}, h2)
-  , "Get in touch with us"), DIV(null, "We would love to get to know you!"), DIV(null, "Write us a ", A(
-    href: "mailto:admin@consider.it"
+      IMG
+        src: props.src
+        style:
+          borderRadius: "50%"
+          display: "inline-block"
+          width: 200
+          textAlign: "center"
+
+      DIV
+        style:
+          textAlign: "center"
+        props.name
+
+      A
+        href: "mailto:#{props.email}"
+        style: _.extend {}, a,
+          textAlign: "center"
+        props.email
+
+  DIV
+    id: 'contact'
     style:
-      color: logo_red
-      textDecoration: "underline"
-  , "nice electronic letter"), ". Or we can reach out to you:", FORM(
-    action: "http://chalkboard.us7.list-manage1.com/subscribe/post?u=9cc354a37a52e695df7b580bd&id=d4b6766b00"
-    id: "mc-embedded-subscribe-form"
-    method: "post"
-    name: "mc-embedded-subscribe-form"
-    novalidate: "true"
-    target: "_blank"
-    style:
-      position: "relative"
-      left: 50
-      display: "inline-block"
-      margin: "10px 0 30px 0"
-  , INPUT(
-    id: "mce-EMAIL"
-    name: "EMAIL"
-    placeholder: "Email address"
-    type: "email"
-    defaultValue: ""
-    style:
-      fontSize: 24
-  ), BUTTON(
-    name: "subscribe"
-    type: "submit"
-    style:
-      fontSize: 24
-      padding: "3px 5px"
-      marginLeft: 8
-      display: "inline-block"
-  , "Send"))), DIV(null, "We work out of Seattle, WA, Portland, OR, and the Internet.")
-
-Header = ReactiveComponent(
-  displayName: "Header"
-  render: ->
-    current_user = undefined
-    current_user = fetch("/current_user")
-    DIV
-      style:
-        position: "relative"
-        margin: "0 auto"
-        backgroundColor: logo_red
-        height: 52
-    , IMG(
-      src: asset("saas_landing_page/considerit_logo.svg")
-      style:
-        position: "relative"
-        top: 8
-        left: 12
-    )
-)
-Page = ReactiveComponent(
+      marginTop: 60
+
+    H1
+      style: _.extend {}, h1,
+        marginBottom: 30
+
+      "Get in touch with us,"
+      BR null
+      "We'd love to get to know you!"
+
+    DIV
+      style:
+        margin: 'auto'
+        width: TEXT_WIDTH
+        textAlign: 'center'
+      "Write us a "
+      A
+        href: "mailto:admin@consider.it"
+        style: a
+        "nice electronic letter"
+
+      ". Or we can reach out to you:"
+
+      FORM
+        action: "http://chalkboard.us7.list-manage1.com/subscribe/post?u=9cc354a37a52e695df7b580bd&id=d4b6766b00"
+        id: "mc-embedded-subscribe-form"
+        method: "post"
+        name: "mc-embedded-subscribe-form"
+        novalidate: "true"
+        target: "_blank"
+        style:
+          display: "inline-block"
+          margin: "10px 0 20px 0"
+
+        INPUT
+          id: "mce-EMAIL"
+          name: "EMAIL"
+          placeholder: "email address"
+          type: "email"
+          defaultValue: ""
+          style:
+            fontSize: 24
+            padding: "8px 12px"
+            width: 380
+            border: '1px solid #999'
+
+        BUTTON
+          name: "subscribe"
+          type: "submit"
+          style:
+            fontSize: 24
+            marginLeft: 8
+            display: "inline-block"
+            backgroundColor: if local.hover_contactme then logo_red else 'white'
+            color: if local.hover_contactme then 'white' else '#999'
+            fontWeight: 500
+            border: "1px solid #{if local.hover_contactme then 'transparent' else '#999'}"
+            borderRadius: 16
+            padding: '8px 18px'
+          onMouseEnter: => local.hover_contactme = true; save local
+          onMouseLeave: => local.hover_contactme = false; save local
+          "Contact me"
+
+      DIV null,
+        "We work out of Seattle, WA, Portland, OR, and the Internet."
+
+    DIV
+      style:
+        textAlign: 'center'
+        marginTop: 30
+
+      teamMember
+        src: asset("saas_landing_page/travis.jpg")
+        name: "Travis Kriplean"
+        email: "travis@consider.it"
+
+      teamMember
+        src: asset("saas_landing_page/kevin.jpg")
+        name: "Kevin Miniter"
+        email: "kevin@consider.it"
+
+      teamMember
+        src: asset("saas_landing_page/mike.jpg")
+        name: "Michael Toomim"
+        email: "toomim@consider.it"
+
+story = ->
+
+Page = ReactiveComponent
+
   displayName: "Page"
   mixins: [ AccessControlled ]
+
   render: ->
-    auth = undefined
-    loc = undefined
     loc = fetch("location")
     auth = fetch("auth")
-    DIV null, (->
+
+    DIV null,
       if auth.form
         Auth()
       else unless @accessGranted()
         SPAN null
       else
         switch loc.url
-          when "/"
-            SaasHomepage key: "homepage"
           when "/dashboard/create_subdomain"
             CreateSubdomain key: "/page/dashboard/create_subdomain"
-    ).call(this)
-)
-Root = ReactiveComponent(
+          else
+            SaasHomepage key: "homepage"
+
+
+Root = ReactiveComponent
   displayName: "Root"
+
   render: ->
-    loc = undefined
-    loc = fetch("location")
+    loc = fetch 'location'
+    app = fetch '/application'
+
     DIV
       style: base_text
-    , BrowserLocation(), Header(), DIV(
-      style:
-        width: 1000
-        backgroundColor: "white"
-        margin: "auto"
-        paddingBottom: 20
-    , BrowserHacks(), Page(key: "/page" + loc.url), Tooltip())
-)
+
+      BrowserLocation()
+      StateDash()
+      Header()
+      DIV
+        style:
+          width: 1000
+          backgroundColor: "white"
+          margin: "auto"
+          paddingBottom: 20
+        BrowserHacks()
+        Page(key: "/page" + loc.url)
+
+      Tooltip()
+
+      if app.dev
+        Development()
+
+
+
 window.Saas = Root

 require './application_loader'


No need to load this twice

No need to load this twice

--- test/property.spec.coffee
+++ test/property.spec.coffee
@@ -1,6 +1,6 @@
 require './spec_helper'

-{extend, defineProperty} = Build
+{extend} = Build
 {defineProperty} = Serenade

 describe 'Serenade.defineProperty', ->


Null guard atom global so the specs pass

--- src/space-pen.coffee
+++ src/space-pen.coffee
@@ -445,7 +445,7 @@

 JQueryTrigger = $.fn.trigger
 $.fn.trigger = (eventName, data) ->
-  if typeof eventName is 'string' and atom.commands.registeredCommands[eventName]
+  if typeof eventName is 'string' and atom?.commands.registeredCommands[eventName]?
     throw new Error """
       `trigger` is no longer available for emitting events as it will not
       correctly route the command to its handlers. Please use


Revert "don't close the tree view on cmd-w"

This reverts commit ef48c355fbabbb668d723dfea74a696382828d81.

--- src/tree-view.coffee
+++ src/tree-view.coffee
@@ -48,7 +48,7 @@
     @on 'click', '.entry', (e) => @entryClicked(e)
     @command 'core:move-up', => @moveUp()
     @command 'core:move-down', => @moveDown()
-    @command 'core:close', => false
+    @command 'core:close', => @detach(); false
     @command 'tree-view:expand-directory', => @expandDirectory()
     @command 'tree-view:collapse-directory', => @collapseDirectory()
     @command 'tree-view:open-selected-entry', => @openSelectedEntry(true)


including removed items in the padding adjustment calculations

--- src/ui-scroll.coffee
+++ src/ui-scroll.coffee
@@ -309,6 +309,10 @@
 										promises = promises.concat (builder.insertElementAnimated wrapper.element, buffer[i-1].element)
 									wrapper.op = 'none'
 								when 'remove' then toBeRemoved.push wrapper
+
+						for wrapper in toBeRemoved
+							promises = promises.concat (removeItem wrapper)
+
 						builder.bottomPadding(Math.max(0,builder.bottomPadding() - (builder.topOffset() - topOffset)))

 						if toBePrepended.length
@@ -324,9 +328,6 @@
 							else
 								# if not, increment scrollTop
 								viewport.scrollTop(viewport.scrollTop() + heightIncrement)
-
-						for wrapper in toBeRemoved
-							promises = promises.concat (removeItem wrapper)

 						# re-index the buffer
 						item.scope.$index = first + i for item,i in buffer


changed test namespaces

changed test namespaces

--- tests/coffeescript.coffee
+++ tests/coffeescript.coffee
@@ -1,4 +1,4 @@
-prefixTest = (name) -> "[Coffeescript] Document-Classes - #{name}"
+prefixTest = (name) -> "Document-Classes - Coffeescript - #{name}"

 Tinytest.add prefixTest('transform'), (test) ->
   Posts = new Mongo.Collection null


Remove extra double quotes in tests

Remove extra double quotes in tests

--- tests/request.spec.coffee
+++ tests/request.spec.coffee
@@ -125,7 +125,7 @@
 						url: '/foo'
 						body: 'Qux'
 					.get('body')
-					m.chai.expect(promise).to.eventually.equal('The body is: "Qux"')
+					m.chai.expect(promise).to.eventually.equal('The body is: Qux')

 			describe 'given simple read only endpoints', ->


Holy shit no. Just fucking no. Not even a PR no.

Fuck.

--- scripts/httpd.coffee
+++ scripts/httpd.coffee
@@ -1,42 +0,0 @@
-# Description:
-#   A simple interaction with the built in HTTP Daemon
-#
-# Dependencies:
-#   None
-#
-# Configuration:
-#   None
-#
-# Commands:
-#   None
-#
-# URLS:
-#   /hubot/version
-#   /hubot/ping
-#   /hubot/time
-#   /hubot/info
-#   /hubot/ip
-
-spawn = require('child_process').spawn
-
-module.exports = (robot) ->
-
-  robot.router.get "/hubot/version", (req, res) ->
-    res.end robot.version
-
-  robot.router.post "/hubot/ping", (req, res) ->
-    res.end "PONG"
-
-  robot.router.get "/hubot/time", (req, res) ->
-    res.end "Server time is: #{new Date()}"
-
-  robot.router.get "/hubot/info", (req, res) ->
-    child = spawn('/bin/sh', ['-c', "echo I\\'m $LOGNAME@$(hostname):$(pwd) \\($(git rev-parse HEAD)\\)"])
-
-    child.stdout.on 'data', (data) ->
-      res.end "#{data.toString().trim()} running node #{process.version} [pid: #{process.pid}]"
-      child.stdin.end()
-
-  robot.router.get "/hubot/ip", (req, res) ->
-    robot.http('http://ifconfig.me/ip').get() (err, r, body) ->
-      res.end body


spaces and regular expressions... fml

spaces and regular expressions... fml

--- scripts/karma.coffee
+++ scripts/karma.coffee
@@ -84,7 +84,7 @@
   karma = new Karma robot
   allow_self = process.env.KARMA_ALLOW_SELF or "true"

-  robot.hear /(?:[\(]([\S\s]+)[\)]|([\S]+[^()])[^ ])\+\+(\s|$)/, (msg) ->
+  robot.hear /(?:[\(]([\S\s]+)[\)]|([\S]+[^() ]))\+\+(\s|$)/, (msg) ->
     subject = (msg.match[1] || msg.match[2]).toLowerCase()
     if allow_self is true or msg.message.user.name.toLowerCase() != subject
       karma.increment subject


Fixed 'update' typo in syncer

Fixed 'update' typo in syncer

--- shared/syncer.coffee
+++ shared/syncer.coffee
@@ -57,7 +57,7 @@
   # attributes if we don't pass any in explicitly.
   # This gets screwed up because we append the locale
   # and currency, so let's replicate that behavior.
-  if model and _.isEqual(params, {}) and (method is 'create' or method is 'udpate')
+  if model and _.isEqual(params, {}) and (method is 'create' or method is 'update')
     _.extend ret, model.toJSON()

   ret


valid CSON

valid CSON

--- snippets/fa-css.cson
+++ snippets/fa-css.cson
@@ -26,7 +26,7 @@
 	"content: '\\f0f9';":
 		prefix: "fa-ambulance"
 		body: "content: '\\\\f0f9';"
-	"content: '\\\\f2a3';"
+	"content: '\\f2a3';":
     prefix: "american-sign-language-interpreting"
     body: "content: '\\\\f2a3';"
 	"content: '\\f13d';":
@@ -119,10 +119,10 @@
 	"content: '\\f07d';":
 		prefix: "fa-arrows-v"
 		body: "content: '\\\\f07d';"
-	"content: '\\f2a3';"
+	"content: '\\f2a3';":
     prefix: "asl-interpreting"
     body: "content: '\\\\f2a3';"
-	"content: '\\f2a2';"
+	"content: '\\f2a2';":
     prefix: "assistive-listening-systems"
     body: "content: '\\\\f2a2';"
 	"content: '\\f069';":
@@ -131,7 +131,7 @@
 	"content: '\\f1fa';":
 		prefix: "fa-at"
 		body: "content: '\\\\f1fa';"
-	"content: '\\f29e';"
+	"content: '\\f29e';":
     prefix: "audio-description"
     body: "content: '\\\\f29e';"
 	"content: '\\f1b9';":
@@ -236,7 +236,7 @@
 	"content: '\\f27e';":
 		prefix: "fa-black-tie"
 		body: "content: '\\\\f27e';"
-	"content: '\\f29d';"
+	"content: '\\f29d';":
     prefix: "fa-blind"
     body: "content: '\\\\f29d';"
 	"content: '\\f293';":
@@ -263,7 +263,7 @@
 	"content: '\\f097';":
 		prefix: "fa-bookmark-o"
 		body: "content: '\\\\f097';"
-	"content: '\\f2a1';"
+	"content: '\\f2a1';":
     prefix: "fa-braille"
     body: "content: '\\\\f2a1';"
 	"content: '\\f0b1';":
@@ -572,10 +572,10 @@
 	"content: '\\f1c0';":
 		prefix: "fa-database"
 		body: "content: '\\\\f1c0';"
-	"content: '\\f2a4';"
+	"content: '\\f2a4';":
     prefix: "fa-deaf"
     body: "content: '\\\\f2a4';"
-"content: '\\f2a4';"
+"content: '\\f2a4';":
     prefix: "fa-deafness"
     body: "content: '\\\\f2a4';"
 	"content: '\\f03b';":
@@ -641,7 +641,7 @@
 	"content: '\\f199';":
 		prefix: "fa-envelope-square"
 		body: "content: '\\\\f199';"
-	"content: '\\f299';"
+	"content: '\\f299';":
     prefix: "fa-envira"
     body: "content: '\\\\f299';"
 	"content: '\\f12d';":
@@ -893,7 +893,7 @@
 	"content: '\\f092';":
 		prefix: "fa-github-square"
 		body: "content: '\\\\f092';"
-	"content: '\\f296';"
+	"content: '\\f296';":
     prefix: "fa-gitlab"
     body: "content: '\\\\f296';"
 	"content: '\\f184';":
@@ -902,10 +902,10 @@
 	"content: '\\f000';":
 		prefix: "fa-glass"
 		body: "content: '\\\\f000';"
-	"content: '\\f2a5';"
+	"content: '\\f2a5';":
     prefix: "fa-glide"
     body: "content: '\\\\f2a5';"
-	"content: '\\f2a6';"
+	"content: '\\f2a6';":
     prefix: "fa-glide-g"
     body: "content: '\\\\f2a6';"
 	"content: '\\f0ac';":
@@ -977,7 +977,7 @@
 	"content: '\\f256';":
 		prefix: "fa-hand-stop-o"
 		body: "content: '\\\\f256';"
-	"content: '\\f2a4';"
+	"content: '\\f2a4';":
     prefix: "fa-hard-of-hearing"
     body: "content: '\\\\f2a4';"
 	"content: '\\f292';":
@@ -1196,7 +1196,7 @@
 	"content: '\\f176';":
 		prefix: "fa-long-arrow-up"
 		body: "content: '\\\\f176';"
-	"content: '\\f2a8';"
+	"content: '\\f2a8';":
     prefix: "fa-low-vision"
     body: "content: '\\\\f2a8';"
 	"content: '\\f0d0';":
@@ -1478,7 +1478,7 @@
 	"content: '\\f059';":
 		prefix: "fa-question-circle"
 		body: "content: '\\\\f059';"
-	"content: '\\f29c';"
+	"content: '\\f29c';":
     prefix: "fa-question-circle-o"
     body: "content: '\\\\f29c';"
 	"content: '\\f10d';":
@@ -1652,10 +1652,10 @@
 	"content: '\\f215';":
 		prefix: "fa-simplybuilt"
 		body: "content: '\\\\f215';"
-	"content: '\\f2a7';"
+	"content: '\\f2a7';":
     prefix: "fa-sign-language"
     body: "content: '\\\\f2a7';"
-	"content: '\\f2a7';"
+	"content: '\\f2a7';":
     prefix: "fa-signing"
     body: "content: '\\\\f2a7';"
 	"content: '\\f0e8';":
@@ -1679,13 +1679,13 @@
 	"content: '\\f118';":
 		prefix: "fa-smile-o"
 		body: "content: '\\\\f118';"
-	"content: '\\f2ab';"
+	"content: '\\f2ab';":
     prefix: "fa-snapchat"
     body: "content: '\\\\f2ab';"
-	"content: '\\f2ac';"
+	"content: '\\f2ac';":
     prefix: "fa-snapchat-ghost"
     body: "content: '\\\\f2ac';"
-	"content: '\\f2ad';"
+	"content: '\\f2ad';":
     prefix: "fa-snapchat-square"
     body: "content: '\\\\f2ad';"
 	"content: '\\f1e3';":
@@ -1991,7 +1991,7 @@
 	"content: '\\f0e2';":
 		prefix: "fa-undo"
 		body: "content: '\\\\f0e2';"
-	"content: '\\f29a';"
+	"content: '\\f29a';":
     prefix: "fa-universal-access"
     body: "content: '\\\\f29a';"
 	"content: '\\f19c';":
@@ -2048,10 +2048,10 @@
 	"content: '\\f237';":
 		prefix: "fa-viacoin"
 		body: "content: '\\\\f237';"
-	"content: '\\f2a9';"
+	"content: '\\f2a9';":
     prefix: "fa-viadeo"
     body: "content: '\\\\f2a9';"
-	"content: '\\f2aa';"
+	"content: '\\f2aa';":
     prefix: "fa-viadeo-square"
     body: "content: '\\\\f2aa';"
 	"content: '\\f03d';":
@@ -2069,7 +2069,7 @@
 	"content: '\\f189';":
 		prefix: "fa-vk"
 		body: "content: '\\\\f189';"
-	"content: '\\f2a0';"
+	"content: '\\f2a0';":
     prefix: "fa-volume-control-phone"
     body: "content: '\\\\f2a0';"
 	"content: '\\f027';":
@@ -2099,7 +2099,7 @@
 	"content: '\\f193';":
 		prefix: "fa-wheelchair"
 		body: "content: '\\\\f193';"
-	"content: '\\f29b';"
+	"content: '\\f29b';":
     prefix: "fa-wheelchair-alt"
     body: "content: '\\\\f29b';"
 	"content: '\\f1eb';":
@@ -2117,10 +2117,10 @@
 	"content: '\\f19a';":
 		prefix: "fa-wordpress"
 		body: "content: '\\\\f19a';"
-	"content: '\\f297';"
+	"content: '\\f297';":
     prefix: "fa-wpbeginner"
     body: "content: '\\\\f297';"
-	"content: '\\f298';"
+	"content: '\\f298';":
     prefix: "fa-wpforms"
     body: "content: '\\\\f298';"
 	"content: '\\f0ad';":


Updating os_env resource snippet

Updating os_env resource snippet

--- snippets/os_env.cson
+++ snippets/os_env.cson
@@ -1,4 +1,4 @@
 '.source.inspec':
   'Describe os_env':
     'prefix': 'os_env'
-    'body': "# http://inspec.io/docs/reference/resources/os_env/\ndescribe os_env('${1:name}') do\n  it { should${2:_not} ${3:exist} }${4}\nend"
+    'body': "# http://inspec.io/docs/reference/resources/os_env/\ndescribe os_env('${1:name}') do\n  its('content') { should${2:_not} eq '${3:value}' }${4}\nend"


Remove unused PUT inteface

Remove unused PUT inteface

--- sample.coffee
+++ sample.coffee
@@ -10,8 +10,7 @@
 myData = new FileCollection('images', {
    resumable: true,     # Enable the resumable.js compatible chunked file upload interface
    http: [
-      { method: 'get', path: '/id/:_id', lookup: (params, query) -> return { _id: params._id }},
-      { method: 'put', path: '/put/:_id', lookup: (params, query) -> return { _id: params._id }}
+      { method: 'get', path: '/id/:_id', lookup: (params, query) -> return { _id: params._id }}
    ]}
 )

@@ -451,9 +450,10 @@

       # Create a job to make a thumbnail for each newly uploaded image
       addedFileJob = (file) ->
-         # Don't make new jobs for files tha already have them in process...
+         # Don't make new jobs for files which already have them in process...
          # findAndModify is atomic, so in a multi-server environment,
-         # only one server can succeed and go on to create a job
+         # only one server can succeed and go on to create a job.
+         # Too bad Meteor has no built-in atomic DB update...
          myData.rawCollection().findAndModify(
             { _id: new MongoInternals.NpmModule.ObjectID(file._id.toHexString()), 'metadata._Job': {$exists: false}},
             [],
@@ -555,7 +555,8 @@
                      stdout.pipe(outStream)

       workers = myJobs.processJobs 'makeThumb', { concurrency: 2, prefetch: 2, pollInterval: 1000000000 }, worker
+
       myJobs.find({ type: 'makeThumb', status: 'ready' })
-             .observe
-               added: (doc) ->
-                  workers.trigger()
+         .observe
+            added: (doc) ->
+               workers.trigger()


safer browser check

safer browser check

--- browser.coffee
+++ browser.coffee
@@ -3,7 +3,7 @@
 CoffeeScript = require './coffee-script'
 CoffeeScript.require = require

-isModernBrowser = if window? then -> true if btoa and JSON else ->
+isModernBrowser = if window? then -> true if btoa? and JSON? else ->

 compile = (code, options = {}) ->
   res = undefined


sort option list

sort option list

--- script.coffee
+++ script.coffee
@@ -19,13 +19,14 @@

   draw: (d) =>
     @graph = new Dygraph($("##{@div}")[0], d,
+      dateWindow: [(new Date().valueOf())-86400000,new Date().valueOf()]
+      delimiter: ';'
+      digitsAfterDecimal: 3
       includeZero: true
-      delimiter: ';'
+      labels: ['Time', 'value']
       rollPeriod: 1
       showRoller: true
-      digitsAfterDecimal: 3
       strokeWidth: 1
-      labels: ['Time', 'value']
     )

   create: ->


simplify menu creation, no need to store data in DOM

--- script.coffee
+++ script.coffee
@@ -47,16 +47,14 @@
     $("##{@div}").width($(window).width() - $("##{@div}").css('margin').replace(/[^-\d\.]/g, '')*4)

 class Stat
-  @get_stats: ->
-    $('#js-datastore').data('stats')
-
   @loadFile: -> $.ajax { url: '/stats', dataType: 'html' }

   @parseHtml: (htmlDoc) =>
+    stats = {}
     $(htmlDoc).find('li>a').map ->
       url = $(this).attr('href')
       urlRe = /([0-9a-f-]{35})\.([0-9]{6})\.xml/
-      source = url.replace(urlRe, '$1')
+      sourceId = url.replace(urlRe, '$1')
       period = url.replace(urlRe, '$2')
       year = period.slice 0,4
       month = period.slice -2
@@ -64,43 +62,39 @@
       title = $(this).text().replace(titleRe, '$1')
       categoryAndRoom = $(this).text().replace(titleRe, '$2')

-      if source of Stat.get_stats() # CoffeeScript 'of' equals JS 'in'
-        data = Stat.get_stats()[source]
-        data.urls.push {url, year, month}
-        $('#js-datastore').data('stats', obj)
+      if sourceId of stats # CoffeeScript 'of' equals JS 'in'
+        stats[sourceId].urls.push {url, year, month}
       else
-        obj = Stat.get_stats()
-        obj[source] = {title, categoryAndRoom, urls: [{url, year, month}]}
-        $('#js-datastore').data('stats', obj)
+        stats[sourceId] = {title, categoryAndRoom, urls: [{url, year, month}]}

-  @createMenu: ->
+    return stats
+
+  @createMenu: (stats) ->
     $('#menu-container').append('

')
-    for k, v of Stat.get_stats()
-      $('#menu-container>ul').append("
#{v.title}

")
-      for u in v.urls
-        divid = u.url.replace(/\./g, '_') # '.' in id breaks jQuery!
-        $("#menu-container>ul>li[id=#{k}]>ul")
+    for sourceId, source of stats
+      $('#menu-container>ul').append("
#{source.title}

")
+      for u in source.urls
+        divId = "#{sourceId}_#{u.year}#{u.month}"
+        $("#menu-container>ul>li[id=#{sourceId}]>ul")
           .append("
#{u.year}-#{u.month}
")
+            data-title=\"#{source.title}: #{u.year}-#{u.month}\">#{u.year}-#{u.month}
")
     $('#menu-container a').click ->
       # TODO move remaining code into Graph method
-      anchor = this
-      divid = $(this).attr('data-div-id')
+      divId = $(this).attr('data-div-id')
       if $(this).hasClass('selected')
-        #$(this).parent().remove()
-        $("##{divid}").parent().remove()
+        $("##{divId}").parent().remove()
       else
         $('#graph-container').append("

-          x
-
loading

+          x
+
loading

@@ -111,10 +105,7 @@

       $(this).toggleClass('selected')

-
-
   @go: ->
-    $('#js-datastore').data('stats', {})
     @loadFile()
       .then(@parseHtml)
       .then(@createMenu)


simple maybe specs

simple maybe specs

--- spec/Thenable.coffee
+++ spec/Thenable.coffee
@@ -328,6 +328,52 @@
         chai.expect(res).to.equal 3
         done()

+  describe 'with simple positive maybe', ->
+    it 'should resolve', (done) ->
+      y1 = (c, data) ->
+        Root()
+        .deliver data
+        .then 'yep-1', ->
+          1
+      t = Root()
+      .deliver {}
+      .maybe [y1]
+      .else (choice, e) ->
+        throw new Error "ignored"
+      .finally (choice, res) ->
+        chai.expect(res).to.eql [1]
+        done()
+
+  describe 'with simple negative maybe via throw', ->
+    it 'should resolve', (done) ->
+      n = (c, data) ->
+        Root()
+        .deliver data
+        .then 'nope', ->
+          throw "nope"
+      t = Root()
+      .deliver 'hello'
+      .maybe [n]
+      .else (choice, data) ->
+        data
+      .finally (choice, res) ->
+        chai.expect(res).to.equal "hello"
+        done()
+
+  describe 'with simple negative maybe via abort', ->
+    it 'should resolve', (done) ->
+      n = (c, data) ->
+        Root()
+        .deliver data
+        .then 'nope', (n) ->
+          n.abort "nope"
+      t = Root()
+      .deliver 'hello'
+      .maybe [n]
+      .finally (choice, res) ->
+        chai.expect(res).to.equal "hello"
+        done()
+
   describe 'with maybe & return values', ->
     it 'should resolve', (done) ->
       multiply = (multiplier, orig, data) ->


fix force domain

fix force domain

--- server.coffee
+++ server.coffee
@@ -9,7 +9,7 @@
 app.set('view engine', 'ejs')
 app.set('views', "#{__dirname}/app/views")
 app.use(express.static('./public'))
-if(!process.env.NODE_ENV isnt 'production')
+if(process.env.NODE_ENV isnt 'production')
   app.disable('view cache')

 app.use(bodyParser.json())


Move history tab from shared to backend

Move history tab from shared to backend

--- server.coffee
+++ server.coffee
@@ -53,7 +53,9 @@
 		newVersion = 6
 		if not(Db.shared.peek('game', 'eventlist', 'maxId')?)
 			Db.shared.set 'game', 'eventlist', 'maxId', 0
-
+	if version < 7
+		newVersion=7
+		Db.shared.remove 'history'
 	# Write new version to the database
 	if newVersion isnt version
 		log '[onUpgrade] Upgraded from version '+version+' to '+newVersion+'.'
@@ -702,8 +704,8 @@
 #Move all data to history tab
 moveData = ->
 	current = Db.shared.peek('gameNumber')
-	Db.shared.set 'history', current,'game', Db.shared.peek('game')
-	Db.shared.set 'history', current, 'gameState', Db.shared.peek('gameState')
+	Db.backend.set 'history', current,'game', Db.shared.peek('game')
+	Db.backend.set 'history', current, 'gameState', Db.shared.peek('gameState')


--- command.coffee
+++ command.coffee
@@ -67,7 +67,7 @@
     opts.literals = sources.splice(1).concat opts.literals
   process.ARGV = process.argv = process.argv.slice(0, 2).concat opts.literals
   process.argv[0] = 'coffee'
-  process.execPath = process.mainModule.filename
+  process.execPath = require.main.filename
   compileScripts()

 # Asynchronously read in each CoffeeScript in a list of source files and


Got --watch with directory watching and addition / removal of inner subdirectories working...

--- command.coffee
+++ command.coffee
@@ -51,6 +51,7 @@
 sources      = []
 sourceCode   = []
 notSources   = {}
+watchers     = {}
 optionParser = null

 # Run `coffee` by parsing passed options and determining what action to take.
@@ -74,7 +75,7 @@
   process.argv[0] = 'coffee'
   process.execPath = require.main.filename
   for source in sources
-    compilePath source, yes, path.join source
+    compilePath source, yes, path.normalize source

 # Compile a path, which could be a script or a directory. If a directory
 # is passed, recursively compile all '.coffee' extension source files in it
@@ -105,7 +106,7 @@
           compileScript(source, code.toString(), base)
       else
         notSources[source] = yes
-        removeSource source
+        removeSource source, base

 # Compile a single source script, containing the given code, according to the
@@ -149,6 +150,7 @@
 # If all of the source files are done being read, concatenate and compile
 # them together.
 compileJoin = ->
+  return unless opts.join
   unless sourceCode.some((code) -> code is null)
     compileScript opts.join, sourceCode.join('\n'), opts.join

@@ -187,31 +189,42 @@
             compile()
             watcher = fs.watch source, callback
           else
-            removeSource source
-            if opts.join
-              compileJoin()
-            else
-              fs.unlink outputPath(source, base), (err) ->
-                throw err if err
-            timeLog "removed #{source}"
+            removeSource source, base, yes
+            compileJoin()
       , 250

 # Watch a directory of files for new additions.
 watchDir = (source, base) ->
   watcher = fs.watch source, ->
-    fs.readdir source, (err, files) ->
-      throw err if err
-      files = files.map (file) -> path.join source, file
-      for file in files when not notSources[file] and sources.indexOf(file) < 0
-        sources.push file
-        sourceCode.push null
-        compilePath file, no, base
+    path.exists source, (exists) ->
+      if exists
+        fs.readdir source, (err, files) ->
+          throw err if err
+          files = files.map (file) -> path.join source, file
+          for file in files when not notSources[file]
+            continue if sources.some((s) -> s.indexOf(file) >= 0)
+            sources.push file
+            sourceCode.push null
+            compilePath file, no, base
+      else
+        watcher.close()
+        toRemove = (file for file in sources when file.indexOf(source) >= 0)
+        removeSource file, base, yes for file in toRemove
+        compileJoin()

-# Remove a file from our source list, and source code cache.
-removeSource = (source) ->
+# Remove a file from our source list, and source code cache. Optionally remove
+# the compiled JS version as well.
+removeSource = (source, base, removeJs) ->
   index = sources.indexOf source
   sources.splice index, 1
   sourceCode.splice index, 1
+  if removeJs and not opts.join
+    jsPath = outputPath source, base
+    path.exists jsPath, (exists) ->
+      if exists
+        fs.unlink jsPath, (err) ->
+          throw err if err
+          timeLog "removed #{source}"

 # Get the corresponding output JavaScript path for a source file.
 outputPath = (source, base) ->


added error message on 'notReady'

added error message on 'notReady'

--- command.coffee
+++ command.coffee
@@ -31,3 +31,6 @@

   app.listen port, =>
     console.log "listening at localhost:#{port}"
+
+conn.on 'notReady', ->
+  console.error "Unable to establish a connection to meshblu"


wtfdebuggershowdotheyworklolbbq

wtfdebuggershowdotheyworklolbbq

--- tasks/spec-ci.coffee
+++ tasks/spec-ci.coffee
@@ -20,7 +20,6 @@
         output += chunk

       child.on "exit", (code, signal) ->
-        debugger
         if code != 0
           grunt.warn("Spec execution appears to have failed.")
           done(false)


this check is enough

this check is enough

--- core/KD.coffee
+++ core/KD.coffee
@@ -107,7 +107,7 @@

     {callback, onFailMsg, onFail, silence, tryAgain} = options

-    unless KD.whoami() instanceof KD.remote.api.JAccount and KD.isLoggedIn()
+    unless KD.isLoggedIn()

       if onFailMsg
         new KDNotificationView


Add new gulp files to tests

Add new gulp files to tests

--- test/app.spec.coffee
+++ test/app.spec.coffee
@@ -16,6 +16,8 @@
       LICENSE
       .travis.yml
       .gitignore
+      gulpfile.js
+      gulpfile.coffee
       test/mocha.opts
       test/test-module.spec.coffee
       src/test-module.coffee


cooldown finilazation

cooldown finilazation

--- test/cooldown.coffee
+++ test/cooldown.coffee
@@ -111,7 +111,7 @@
   5.times ->
     obj.update()

-  equals obj.I.health, 5
+  equals obj.I.health, 8

 test "initialize property to 0 if no current value", ->
   obj = GameObject()


cooldown finilazation

cooldown finilazation

--- test/cooldown.coffee
+++ test/cooldown.coffee
@@ -101,6 +101,11 @@
 # TODO: tests to make sure value isn't overwritten if the instance variable already exists
 # TODO: tests to make sure value is defaulted to 0, but only if the instance variable doesn't exist
 test "initialize property to 0 if no current value", ->
+    obj = GameObject()

+  obj.include(Cooldown)
+
+  obj.cooldown 'health',
+    value: 100

 module()


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/tweening.coffee
+++ test/tweening.coffee
@@ -6,6 +6,7 @@

   o.tween 10,
     x: 10
+    easing: "sinusoidal"

   11.times (i) ->
     o.update()


Add note about the field guide

Add note about the field guide

--- src/scripts/tutorialSteps.coffee
+++ src/scripts/tutorialSteps.coffee
@@ -145,6 +145,7 @@
       content: [
         'You can use Talk to discuss images with other volunteers if you have questions or find something interesting.'
         'This concludes the tutorial. Now you\'re ready to dive in and complete some classifications on your own!'
+        'If you\'re ever unsure of what to mark, you can always consult the field guide on the "About" page for descriptions of the ground covers and species. You can then return to the "Classify" page when you\'re ready.'
       ]
       attach: x: 'right', to: '.talk p', at: x: 'left'
       style: width: 400


Trim styles

Trim styles

--- src/smc-webapp/projects_nav.cjsx
+++ src/smc-webapp/projects_nav.cjsx
@@ -183,7 +183,7 @@
         >
             <SortableNav
                 className            = "smc-project-tab-sorter"
-                style                = {display:'flex', overflow: 'hidden', height:'40px', margin:'0'}
+                style                = {display:'flex', overflow: 'hidden'}
                 helperClass          = {'smc-project-tab-floating'}
                 onSortEnd            = {@on_sort_end}
                 axis                 = {'x'}


'Updating for version 0.12.6dev6'

'Updating for version 0.12.6dev6'

--- bokehjs/src/coffee/version.coffee
+++ bokehjs/src/coffee/version.coffee
@@ -1 +1 @@
-export version = '0.12.6dev5'
+export version = '0.12.6dev6'


Fix lint error

Fix lint error

--- src/text-editor-component.coffee
+++ src/text-editor-component.coffee
@@ -139,7 +139,7 @@

     measureWindowSize = @measureWindowSize.bind(this)
     window.addEventListener('resize', measureWindowSize)
-    @disposables.add(new Disposable => window.removeEventListener('resize', measureWindowSize))
+    @disposables.add(new Disposable -> window.removeEventListener('resize', measureWindowSize))

   getDomNode: ->
     @domNode


Remove unnecessary onDidChangeSoftWrapped subscription

--- src/text-editor-presenter.coffee
+++ src/text-editor-presenter.coffee
@@ -38,7 +38,6 @@
       @updateLinesState()
       @updateGutterState()
       @updateLineNumbersState()
-    @disposables.add @model.onDidChangeSoftWrapped(@updateState.bind(this))
     @disposables.add @model.onDidChangeGrammar(@updateContentState.bind(this))
     @disposables.add @model.onDidChangePlaceholderText(@updateContentState.bind(this))
     @disposables.add @model.onDidChangeMini =>


Update hubot-cah.coffee

Update hubot-cah.coffee

--- src/hubot-cah.coffee
+++ src/hubot-cah.coffee
@@ -192,17 +192,21 @@

 module.exports = (robot) ->

-  db = robot.brain.get 'cah'
-  if (!db)
-    db = {
-      scores:         {},                   # {<name>: <score>, ...}
-      activePlayers:  [],                   # [<player name>, ...]
-      blackCard:      random_black_card(),  # <card text>
-      czar:           null,                 # <player name>
-      hands:          {},                   # {<name>: [<card text>, <card text>, ...], ...}
-      answers:        [],                   # [ [<player name>, [<card text>, ...]], ... ]
-    }
-  robot.brain.set 'cah', db
+  brainLoaded = () ->
+    db = robot.brain.get 'cah'
+    if (!db)
+      db = {
+        scores:         {},                   # {<name>: <score>, ...}
+        activePlayers:  [],                   # [<player name>, ...]
+        blackCard:      random_black_card(),  # <card text>
+        czar:           null,                 # <player name>
+        hands:          {},                   # {<name>: [<card text>, <card text>, ...], ...}
+        answers:        [],                   # [ [<player name>, [<card text>, ...]], ... ]
+      }
+    robot.brain.set 'cah', db
+
+  robot.brain.on 'loaded', brainLoaded
+  brainLoaded() # just in case

   robot.hear /^cah help$/i, (msg) ->
     msg.send helpSummary
@@ -290,6 +294,7 @@
             msg.reply "You cannot submit a single card more than once."
             return
       submit_answer(sender(msg), nums)
+      robot.brain.save()
       msg.reply "Submission accepted."

   robot.hear /^cah answers$/i, (msg) ->
@@ -314,9 +319,11 @@
         msg.reply "That is not an valid choice, try again."
       else
         msg.send czar_choose_winner num
+        robot.brain.save()

   robot.hear /^cah (status|question)$/i, (msg) ->
     msg.send game_state_string()

   robot.hear /^cah skip$/i, (msg) ->
     msg.send czar_choose_winner -1
+    robot.brain.save()


Revert a change made during testing

Revert a change made during testing

Signed-off-by: Wyatt Calandro <96804f3875bc364d25340989c9f5960c99a00fd9@arm1stice.com>

--- src/intervals.coffee
+++ src/intervals.coffee
@@ -5,7 +5,7 @@
 currentMiniSeries = null;
 module.exports.setupIntervals = (lolClient, ipcMain, config, mainWindow, db) ->
   # Check to see if the person is in game
-  inGame = true;
+  inGame = false;
   ranked = false;
   flex = false;
   lpInterval = null
@@ -37,7 +37,6 @@
                                 'TEAM_BUILDER_DRAFT_RANKED_5x5'],
                   beginTime: (Number(docs[0].timestamp) + 1)
               }, (err, data) ->
-                util.log "Got new matchlist with #{data.matches.length} match(es)"
                 if err
                   throw err
                 else


Dock: minor removal

Dock: minor removal

--- client/Main/maincontroller/dockcontroller.coffee
+++ client/Main/maincontroller/dockcontroller.coffee
@@ -6,7 +6,6 @@
     { title : "Terminal",  path : "/Terminal", order : 30, type :"persistent" }
     { title : "Editor",    path : "/Ace",      order : 40, type :"persistent" }
     { title : "Apps",      path : "/Apps",     order : 50, type :"persistent" }
-    # { title : "About",     path : "/About",    order : 70, type :"persistent" }
     { title : "DevTools",  path : "/DevTools", order : 60, type :"persistent" }
   ]


Graph: Also send port name for changeInport/Outport

Note: this is an API incompatible change, but only to unreleased API.
Needed in journal to have enough info to call set*PortMetadata() for undo/replay.

--- src/lib/Graph.coffee
+++ src/lib/Graph.coffee
@@ -168,7 +168,7 @@
     @inports[publicPort].metadata = {} unless @inports[publicPort].metadata
     for item, val of metadata
       @inports[publicPort].metadata[item] = val
-    @emit 'changeInport', @inports[publicPort], before
+    @emit 'changeInport', publicPort, @inports[publicPort], before
     @checkTransactionEnd()

   addOutport: (publicPort, nodeKey, portKey, metadata) ->
@@ -214,7 +214,7 @@
     @outports[publicPort].metadata = {} unless @outports[publicPort].metadata
     for item, val of metadata
       @outports[publicPort].metadata[item] = val
-    @emit 'changeOutport', @outports[publicPort], before
+    @emit 'changeOutport', publicPort, @outports[publicPort], before
     @checkTransactionEnd()

   # ## Grouping nodes in a graph


pass options when loading graph file

pass options when loading graph file

--- src/lib/NoFlo.coffee
+++ src/lib/NoFlo.coffee
@@ -143,7 +143,7 @@

   exports.graph.loadFile file, (net) ->
     net.baseDir = options.baseDir if options.baseDir
-    exports.createNetwork net, callback
+    exports.createNetwork net, callback, options

 # ### Saving a network definition
 #


chore: shortened capitalize definition

chore: shortened capitalize definition

--- src/lib/utils.coffee
+++ src/lib/utils.coffee
@@ -1,2 +1,2 @@
-String.prototype.capitalize = ->
+String::capitalize = ->
   this.charAt(0).toUpperCase() + this.slice(1)


use better names if the javascript is minimised

--- core/app/backbone/modules/memory_profiler.coffee
+++ core/app/backbone/modules/memory_profiler.coffee
@@ -50,10 +50,21 @@

     comparator: (model) -> -model.get('count')

+  view_name_for = (view) ->
+    classname = view.__proto__.constructor.name
+    templatename = view.template
+
+    if classname.length > 3
+      classname
+    else if typeof(templatename) is 'string'
+      templatename
+    else
+      "unknown"
+
   openView = (view)->
     console.info('+CID ', view.cid)
     MemoryProfiler.views[view.cid] = view
-    view_name = view.__proto__.constructor.name
+    view_name = view_name_for(view)

     MemoryProfiler.view_counts[view_name] ||= 0
     MemoryProfiler.view_counts[view_name] = MemoryProfiler.view_counts[view_name] + 1
@@ -63,7 +74,7 @@
   closeView = (view)->
     console.info('-CID', view.cid)
     delete MemoryProfiler.views[view.cid]
-    view_name = view.__proto__.constructor.name
+    view_name = view_name_for(view)

     MemoryProfiler.view_counts[view_name] = MemoryProfiler.view_counts[view_name] - 1


don't auto focus because IE sucks

don't auto focus because IE sucks

Former-commit-id: 59532566454d0ffaa1d65a5499a01174ce4fcb73 [formerly 70cf1cbca76f044d08e84def8364ded87475adcd] [formerly 8facba388bb5a88c3c1d3303264be261a8091e0f [formerly 8facba388bb5a88c3c1d3303264be261a8091e0f [formerly 77b74fe0e2ffb265f0f3e12e709b5c1fb1a6f2e0]]]
Former-commit-id: deae4c98faa756c90d359c296277305ed0177f85 [formerly 097841c0946530239967943572a298bb3bb43f46]
Former-commit-id: ebd7564f7dbd6508f243bc875c34a6f653d97819
Former-commit-id: 3dc4c917b5f6b19f0aeee1285fbe49bce180434b


event: only store valid results in cache

--- models/event.coffee
+++ models/event.coffee
@@ -41,10 +41,11 @@
       InfluxService.find uncachedQueries.join '\n'
       .tap (response) ->
         redisCacheable = _.flatten _.filter _.map uncachedQueries, (query) ->
-          if isCacheable query
+          result = response.results[uncachedQueries.indexOf(query)]
+          if isCacheable(query) and result? and not result.error
             [
               "#{PREFIX}:#{query}"
-              JSON.stringify response.results[uncachedQueries.indexOf(query)]
+              JSON.stringify result
             ]
         unless _.isEmpty redisCacheable
           redis.msetAsync redisCacheable


[Poll] await between reactions

[Poll] await between reactions

--- modules/poll.coffee
+++ modules/poll.coffee
@@ -16,6 +16,6 @@
       for answer,i in poll.answers
         embed.description += "#{options[i]} - #{answer}\n"
       m = await msg.channel.send l.gen(l.poll.pollStarted, msg.author), { embed }
-      m.react(options[i]) for i in [0..poll.answers.length-1]
+      await m.react(options[i]) for i in [0..poll.answers.length-1]

 module.exports = PollModule


Give focus to primary button for ConfirmationModal

--- admin/static/coffee/ui_components/modals.coffee
+++ admin/static/coffee/ui_components/modals.coffee
@@ -125,6 +125,7 @@
                 modal_title: 'Confirmation'
                 btn_secondary_text: 'No'
                 btn_primary_text: 'Yes'
+            @.$('.btn-primary').focus()

         on_submit: ->
             super


don't collapse many questions

don't collapse many questions

--- app/assets/javascripts/components/question.cjsx
+++ app/assets/javascripts/components/question.cjsx
@@ -20,7 +20,11 @@
 Tooltip = require("components/tooltip")

 class Question extends React.Component
-  ALWAYS_OPEN_QUESTIONS = ["quantities", "multi", "subspecies"]
+  ALWAYS_OPEN_QUESTIONS = [
+    "quantities", "multi", "subspecies",
+    "gps", "body_parts", "form", "text",
+    "numeric", "file", "decimal_numeric"
+  ]

   constructor: (props, context) ->
     super(props, context)


adds base link to runner IDs.

adds base link to runner IDs.

--- src/netrunner.coffee
+++ src/netrunner.coffee
@@ -73,6 +73,9 @@
   if card.type == "Identity"
     text += "*Minimum Deck Size*: #{card.minimumdecksize}\n"
     text += "*Influence Limit*: #{card.influencelimit}\n"
+    #add runner base link
+    if card.baselink
+      text += ":linknr: : #{card.baselink}\n"

 #add trash costs for assets and upgrades
   if card.type == "Asset" || card.type == "Upgrade"


fixed "left of undefined" error; fixes #332

--- app/assets/javascripts/oxalis/controller.coffee
+++ app/assets/javascripts/oxalis/controller.coffee
@@ -86,18 +86,18 @@
       #TODO trigger on resize
       # set width / height for the right-side menu
       _.defer =>
-
-        menuPosition = $("#right-menu").position()
-        MARGIN = 40
-        width = window.innerWidth - menuPosition.left - MARGIN
-        height = window.innerHeight - menuPosition.top - MARGIN
-        tabHeight = height - $('#right-menu .nav').height() - 30
-
-        $("#right-menu").width(width).height(height)
-        @annotationController?.abstractTreeController?.setDimensions({
-          width : width
-          height : tabHeight
-        })
+        if $("#right-menu").length
+          menuPosition = $("#right-menu").position()
+          MARGIN = 40
+          width = window.innerWidth - menuPosition.left - MARGIN
+          height = window.innerHeight - menuPosition.top - MARGIN
+          tabHeight = height - $('#right-menu .nav').height() - 30
+
+          $("#right-menu").width(width).height(height)
+          @annotationController?.abstractTreeController?.setDimensions({
+            width : width
+            height : tabHeight
+          })

       @sceneController = new SceneController(
         @model.upperBoundary, @model.flycam, @model)


Fixed requires in sprangular/chimpy.coffee

--- app/assets/javascripts/sprangular/chimpy.coffee
+++ app/assets/javascripts/sprangular/chimpy.coffee
@@ -1,3 +1,3 @@
-#= require chimpy/module
-#= require_tree chimpy/controllers
-#= require_tree chimpy/services
+#= require ./chimpy/module
+#= require_tree ./chimpy/controllers
+#= require_tree ./chimpy/services


Documenting Route class

Documenting Route class

--- www/src/theoricus/core/route.coffee
+++ www/src/theoricus/core/route.coffee
@@ -1,23 +1,107 @@
+###*
+  Core module
+  @module core
+###
+
+
+###*
+
+  Responsible for manipulating and validating url state.
+
+  @class Route
+###
 module.exports = class Route

-  # static regex for reuse
+  ###*
+
+    Match named params
+
+    @static
+    @property named_param_reg {RegExp}
+    @example
+      "works/:id".match Route.named_param_reg # matchs ':id'
+  ###
   @named_param_reg: /:\w+/g
+
+  ###*
+
+    @static
+    @property splat_param_reg {RegExp}
+  ###
   @splat_param_reg: /\*\w+/g

-  # regex matcher
+  ###*
+
+    Regex responsible for parsing the url state.
+
+    @property matcher {RegExp}
+  ###
   matcher: null

-  # construct variables
+  ###*
+
+    Url state.
+
+    @property match {String}
+  ###
   match: null
+
+  ###*
+
+    Controller '/' action to which the route will be sent.
+
+    @property to {String}
+  ###
   to: null
+
+  ###*
+
+    Route to be called as a dependency.
+
+    @property at {String}
+  ###
   at: null
+
+  ###*
+
+    CSS selector to define where the template will be rendered.
+
+    @property el {String}
+  ###
   el: null

-  # api info
+  ###*
+
+    Store the controller name extracted from url.
+
+    @property controller_name {String}
+  ###
   controller_name: null
+
+  ###*
+
+    Store the controllers' action name extracted from url.
+
+    @property action_name {String}
+  ###
   action_name: null
+
+  ###*
+
+    Store the controllers' action parameters extracted from url.
+
+    @property param_names {String}
+  ###
   param_names: null

+  ###*
+    @class Route
+    @constructor
+    @param @match {String} Url state.
+    @param @to {String} Controller '/' action to which the route will be sent.
+    @param @at {String} Route to be called as a dependency.
+    @param @el {String} CSS selector to define where the template will be rendered.
+  ###
   constructor:( @match, @to, @at, @el )->

     # prepare regex matcher str for reuse
@@ -28,7 +112,14 @@
     # fitlers controller and action name
     [@controller_name, @action_name] = to.split '/'

+  ###*
+
+    Extract the url named parameters.

+    @method extract_params
+    @param url {String}
+
+  ###
   extract_params:( url )->
     # initialize empty params object
     params = {}
@@ -51,12 +142,25 @@

     return params

+  ###*
+
+    @method rewrite_url_with_parms
+    @param url {String}
+    @param params {Object}
+
+  ###
   rewrite_url_with_parms:( url, params )->
     for key, value of params
       reg = new RegExp "[:\\*]+#{key}", 'g'
       url = url.replace reg, value
     return url

-  # test given url against the route
+  ###*
+
+    Test given url against the route
+
+    @method test
+    @param url {String} Url to be tested.
+  ###
   test:( url )->
     @matcher.test url


remove old toggleStatus test method

remove old toggleStatus test method

--- app/components/project-page/project-page.coffee
+++ app/components/project-page/project-page.coffee
@@ -33,13 +33,6 @@

     $scope.showLess = ->
       $scope.showFullDescription = false
-
-    $scope.statuses = ['draft', 'live', 'funded', 'done']
-    $scope.statusIndex = 0
-
-    $scope.toggleStatus = ->
-      $scope.statusIndex++
-      $scope.status = $scope.statuses[$scope.statusIndex % $scope.statuses.length]

     $scope.newComment = Records.comments.build(bucketId: projectId)


renamed index to quadrants

renamed index to quadrants

--- src/quad_tree.coffee
+++ src/quad_tree.coffee
@@ -68,20 +68,20 @@
       top = item.y <= y_midpoint
       bottom = item.y + item.height > y_midpoint

-      index = []
+      quadrants = []

       if left
         if top
-          index.push(TOP_LEFT)
+          quadrants.push(TOP_LEFT)
         if bottom
-          index.push(BOTTOM_LEFT)
+          quadrants.push(BOTTOM_LEFT)
       else
         if top
-          index.push(TOP_RIGHT)
+          quadrants.push(TOP_RIGHT)
         if bottom
-          index.push(BOTTOM_RIGHT)
+          quadrants.push(BOTTOM_RIGHT)

-      return index
+      return quadrants

     halfWidth = -> (I.bounds.width / 2).floor()
     halfHeight = -> (I.bounds.height / 2).floor()


re-impl Bacon.later not to use setInterval

--- src/scheduled.coffee
+++ src/scheduled.coffee
@@ -9,7 +9,10 @@
     -> Bacon.scheduler.clearInterval(id)), poll)))

 Bacon.later = (delay, value) ->
-  withDescription(Bacon, "later", delay, value, Bacon.fromPoll(delay, -> [value, endEvent()]))
+  withDescription Bacon, "later", delay, value, Bacon.fromBinder (sink) ->
+    sender = -> sink [value, endEvent()]
+    id = Bacon.scheduler.setTimeout(sender, delay)
+    -> Bacon.scheduler.clearTimeout(id)

 Bacon.sequentially = (delay, values) ->
   index = 0


Add Quick Launch icon

Add Quick Launch icon

--- src/setup/pif.coffee
+++ src/setup/pif.coffee
@@ -1,5 +1,13 @@
 ###
 Link to shortcuts
 ###
-module.exports = for f in ['Desktop', 'StartMenu']
-  file sh.SpecialFolders(f), "#{PACKAGE.mingzi}.lnk"
+module.exports = exports = ['Desktop', 'StartMenu', 'AppData']
+
+for f, i in exports by -1
+  exports[i] = sh.SpecialFolders f
+
+if (f = folder exports.pop(), 'Microsoft/Internet Explorer/Quick Launch').y()
+  exports.push f
+
+for f, i in exports by -1
+  exports[i] = file f, "#{PACKAGE.mingzi}.lnk"


refactored DrmAlerts

refactored DrmAlerts

--- drm-alerts.coffee
+++ drm-alerts.coffee
@@ -1,10 +1,34 @@
 ###############################################################################
 # Displays removable alerts for web apps
 ###############################################################################
+"use strict"

 ( ($) ->
+    class window.DrmAlert
+        constructor: (@alertClass = "drm-dismissable-alert", @speed = 300) ->
+            self = @

-	$('.drm-dismissable-alert').on 'click', '.close', ->
-		$(@).parent().fadeOut 300
-
+            $('html').on 'click', "div.#{@alertClass} button.close", -> self.clearAlert.call @, self.speed
+
+        showAlert: (type, message, holder) ->
+            className = "#{type}-alert #{@alertClass}"
+            newAlert = $ '

',
+                text: message,
+                class: className
+
+            close = $ '<button></button>',
+                text: 'x'
+                class: 'close'
+
+            newAlert.prependTo holder
+            close.prependTo newAlert
+
+            newAlert
+
+        clearAlert: (speed) ->
+            $(@).parent().fadeOut speed, ->
+                $(@).remove()
+
+    new DrmAlert()
+
 ) jQuery


bugging style 3

bugging style 3

--- apis/v0.coffee
+++ apis/v0.coffee
@@ -156,8 +156,17 @@
   ###
   res.send {
     success: true
-    newsHTML: "ATTENTION!
THIS VERSION WILL STOP FUNCTIONING VERY SOON. UPGRADE NOW!
Click Here to Download Last Version.
The new version shortcut is labeled SVU Helper, while the old (this) one is labeled Login.
For technical reasons, the new version does not replace this one. Instead, you will have to un-install this one.
Download Last Version
-
<big><big><big><big>Download Last Version</big></big></big></big>
+    newsHTML: "ATTENTION!
THIS VERSION WILL STOP FUNCTIONING VERY SOON. UPGRADE NOW!

+    <big><big><big><big><big>
+    Click Here to Download Last Version.
+    </big></big></big></big></big>
+
The new version shortcut is labeled SVU Helper, while the old (this) one is labeled Login.
For technical reasons, the new version does not replace this one. Instead, you will have to un-install this one.
+

+    <big><big><big><big>Download Last Version</big></big></big></big>
+

+    <big><big><big><big>Download Last Version</big></big></big></big>
+

+    <big><big><big><big>Download Last Version</big></big></big></big>
     "
   }


Corrupted rule is case insensitive.

Corrupted rule is case insensitive.

--- grammars/poe.cson
+++ grammars/poe.cson
@@ -111,7 +111,7 @@
       }
       {
         'name': 'filter.corrupted.poe'
-        'begin': '^\\s*(Corrupted)\\s'
+        'begin': '(?i)^\\s*(Corrupted)\\s'
         'beginCaptures':
           '1':
             'name': 'keyword.cond.corrupted.poe'


Cool style ifs on args

Cool style ifs on args

--- gulp-serve.coffee
+++ gulp-serve.coffee
@@ -38,8 +38,8 @@

 # Task to start a serve-static server on port 8989
 gulp.task 'webserver', ->
-  serverName = args.n || '127.0.0.1'
-  serverPort = +args.p || 8989
+  serverName = args.n ? '127.0.0.1'
+  serverPort = +args.p ? 8989
   http = require 'http'
   finalhandler = require 'finalhandler'
   serveStatic = require 'serve-static'


Fix globs again

Fix globs again

--- gulp-tasks.coffee
+++ gulp-tasks.coffee
@@ -3,10 +3,12 @@
 filelog = require 'gulp-filelog'

 files =
+  # NOTE: The underscores here only exist so that the glob starts at the right place.
+  # This keeps the output paths the way they should be.
   html: './html/**/*.ect'
-  components: './{components,}/**/*.html'
-  js: ['./{js,}/main.coffee', './{js,}/project.coffee']
-  css: ['./{css,}/main.styl']
+  components: './{components,_}/**/*.html'
+  js: ['./{js,_}/main.coffee', './{js,_}/project.coffee']
+  css: ['./{css,_}/main.styl']

 translations = ['en-us', 'es-mx']


authenticate instead of crash

authenticate instead of crash

--- job-registry.cson
+++ job-registry.cson
@@ -50,10 +50,8 @@
       filter: 'HandleDeliverMessage'

 DeliverBroadcastSent:
-  start: 'crash'
-  tasks:
-    'crash':
-      task: 'crash'
+  start: 'authenticate'
+  tasks:
     'authenticate':
       filter: 'Authenticate'
       on:


Only import `notImplemented` when necessary

--- lib/balena.coffee
+++ lib/balena.coffee
@@ -42,7 +42,6 @@
 	BalenaAuth = require('balena-auth')['default']
 	getPine = require('balena-pine')
 	errors = require('balena-errors')
-	{ notImplemented } = require('./util')
 	{ PubSub } = require('./util/pubsub')

 	###*
@@ -81,6 +80,7 @@
 		apiVersion: 'v5'

 	if opts.isBrowser
+		{ notImplemented } = require('./util')
 		settings =
 			get: notImplemented
 			getAll: notImplemented


load dotenv first

load dotenv first

--- lib/config.coffee
+++ lib/config.coffee
@@ -1,8 +1,8 @@
+require('dotenv').config()
 { log, pjson } = require 'lightsaber'
 debug = require('debug')('config')
 global.Promise = require 'bluebird'
 Airbrake = require('airbrake')
-require('dotenv').config()

 nodeEnv = process.env.NODE_ENV or 'development'
 debug {nodeEnv}


add proper eventListener for cordova auth based on InAppBrowser

--- src/api/auth.coffee
+++ src/api/auth.coffee
@@ -89,6 +89,15 @@
           onCompleteAuthentication(hash)
       authHelper: (path)->
         w = window.open(path, "_auth", 'location=0,status=0,width=990,height=600')
+
+        # Support for cordova events
+        if window.device?.cordova
+          w?.addEventListener 'loadstart', (event)->
+            hash = try JSON.parse(atob(event.url.split('#')[1]))
+            if hash
+              window.__hull_login_status__(hash)
+              w.close()
+
         _popupInterval = w? && setInterval ->
           if w?.closed
             onCompleteAuthentication({ success: false, error: { reason: 'window_closed' } })


Use absolute path instead of uri when opening save-as dialog

--- src/app/pane.coffee
+++ src/app/pane.coffee
@@ -223,7 +223,7 @@
   saveItemAs: (item, nextAction) ->
     return unless item.saveAs?

-    itemPath = item.getUri?()
+    itemPath = item.getPath?()
     itemPath = dirname(itemPath) if itemPath
     path = atom.showSaveDialogSync(itemPath)
     if path


fix search output with configured prefix

--- src/factoids.coffee
+++ src/factoids.coffee
@@ -83,7 +83,8 @@
     factoids = @factoids.search msg.match[1]

     if factoids.length > 0
-      msg.reply "Matched the following factoids: *!#{factoids.join '*, *!'}*"
+      found = prefix + factoids.join("*, *#{prefix}")
+      msg.reply "Matched the following factoids: *#{found}*"
     else
       msg.reply 'No factoids matched'


テストは落ちるが、頭がこんがらかったので中間セーブ

テストは落ちるが、頭がこんがらかったので中間セーブ

--- src/calc-box.coffee
+++ src/calc-box.coffee
@@ -12,7 +12,7 @@
   ###
   辺の長さでソートしたのを返す
   ###
-  _sortSide: (parcel)->
+  _sortSide: (parcel, sort)->
     sides = [
       side: 'width'
       value: parcel.width
@@ -23,8 +23,13 @@
       side: 'depth'
       value: parcel.depth
     ]
-    sides = sides.sort (a, b)->
-      b.value - a.value
+
+    if sort is 'asc'
+      sides = sides.sort (a, b)->
+        a.value - b.value
+    else
+      sides = sides.sort (a, b)->
+        b.value - a.value

     sides
   ###
@@ -146,16 +151,48 @@

   _pushParcel: (parcel, table)->

-    longestSideOfBox = @_longestSide @
-
-    debug "longestSideOfBox.side: #{longestSideOfBox.side}"
-    debug "table[longestSideOfBox.side]: #{table[longestSideOfBox.side]}"
-    debug "parcel[table[longestSideOfBox.side]]: #{parcel[table[longestSideOfBox.side]]}"
-    @[longestSideOfBox.side] -= parcel[table[longestSideOfBox.side]]
+    shortestSideOfBox = @_shortestSide @
+
+    debug "shortestSideOfBox.side: #{shortestSideOfBox.side}"
+    debug "table[shortestSideOfBox.side]: #{table[shortestSideOfBox.side]}"
+    debug "parcel[table[shortestSideOfBox.side]]: #{parcel[table[shortestSideOfBox.side]]}"
+    @[shortestSideOfBox.side] -= parcel[table[shortestSideOfBox.side]]
+
+    # longestSideOfBox = @_longestSide @
+    #
+    # debug "longestSideOfBox.side: #{longestSideOfBox.side}"
+    # debug "table[longestSideOfBox.side]: #{table[longestSideOfBox.side]}"
+    # debug "parcel[table[longestSideOfBox.side]]: #{parcel[table[longestSideOfBox.side]]}"
+    # @[longestSideOfBox.side] -= parcel[table[longestSideOfBox.side]]

   pushParcel: (parcel)->
     unless @canContain parcel
       return false
+
+    # 箱に商品を入れる向きを決定し、商品分箱のサイズを小さくする
+    #
+    # ・箱に最大限に商品を入れるため、
+    # 　荷物の最長辺が入る箱の最短長の辺を探し、それらの辺を合わせる向きで商品を箱に入れる。
+    # ・商品の向きが決まったら、商品分箱のサイズを小さくして処理終了。
+
+    BoxSides = @_sortSide @, 'asc'
+    longestSideOfParcel = @_longestSide parcel
+    # 箱の最短辺と荷物の長辺を比較して
+    for BoxSide in BoxSides
+      debug "box[#{BoxSide.side}] = #{parcel[BoxSide.side]}"
+      if longestSideOfParcel.value <= BoxSide.value
+        debug "longestSideOfParcel.side = #{longestSideOfParcel.side}"
+        debug "BoxSide.side = #{BoxSide.side}"
+        table = @_adjustSide longestSideOfParcel, BoxSide
+        for k,v of table
+          debug "box[#{k}]@#{@[k]} : parcel[#{v}]@#{parcel[v]}"
+
+        @_pushParcel(parcel, table)
+        break
+
+
+    true
+

     # 箱を最大限に活かすために，
     # 箱の最短辺と荷物の長辺を比較．
@@ -163,20 +200,20 @@
     # 入らなければ次に短い荷物の辺を比較
     # 以下繰り返し

-    sides = @_sortSide parcel
-    shortestSideOfBox = @_shortestSide @
-    # 箱の最短辺と荷物の長辺を比較して
-    for side in sides
-      debug "parcel[#{side.side}] = #{parcel[side.side]}"
-      if shortestSideOfBox.value > side.value
-        debug "shortestSideOfBox.side = #{shortestSideOfBox.side}"
-        debug "side.side = #{side.side}"
-        table = @_adjustSide shortestSideOfBox, side
-        for k,v of table
-          debug "box[#{k}]@#{@[k]} : parcel[#{v}]@#{parcel[v]}"
-
-        @_pushParcel(parcel, table)
-        break
-
-
-    true
+    # sides = @_sortSide parcel
+    # shortestSideOfBox = @_shortestSide @
+    # # 箱の最短辺と荷物の長辺を比較して
+    # for side in sides
+    #   debug "parcel[#{side.side}] = #{parcel[side.side]}"
+    #   if shortestSideOfBox.value > side.value
+    #     debug "shortestSideOfBox.side = #{shortestSideOfBox.side}"
+    #     debug "side.side = #{side.side}"
+    #     table = @_adjustSide shortestSideOfBox, side
+    #     for k,v of table
+    #       debug "box[#{k}]@#{@[k]} : parcel[#{v}]@#{parcel[v]}"
+    #
+    #     @_pushParcel(parcel, table)
+    #     break
+    #
+    #
+    # true


forgot to require moment

forgot to require moment

--- src/incident.coffee
+++ src/incident.coffee
@@ -20,7 +20,7 @@
 #   Aaron Blythe
 #   Levi Smith

-
+moment = require('moment')
 #GitHubApi = require "github"

 module.exports = (robot) ->


tweak scroll centering

tweak scroll centering

--- lib/motion.coffee
+++ lib/motion.coffee
@@ -30,7 +30,6 @@
   setBufferColumn
   limitNumber
   getIndex
-  smartScrollToBufferPosition
 } = require './utils'

 swrap = require './selection-wrapper'
@@ -1070,10 +1069,11 @@
         when 'next' then @getCount(-1)
         when 'previous' then -@getCount(-1)
       range = @ranges[getIndex(index + offset, @ranges)]
-      point = range.start
-      @editor.unfoldBufferRow(point.row) if cursor.isLastCursor()
-      smartScrollToBufferPosition(@editor, point)
-      cursor.setBufferPosition(point, autoscroll: false)
+
+      cursor.setBufferPosition(range.start, autoscroll: false)
+      if cursor.isLastCursor()
+        @editor.unfoldBufferRow(range.start.row)
+        cursor.autoscroll(center: true)
       @vimState.flash(range, type: 'search')

   getIndex: (fromPoint) ->


Dont sort everytime

Dont sort everytime

--- src/entities.coffee
+++ src/entities.coffee
@@ -102,6 +102,7 @@
         @renderable.addAnimation('walk', [0,1,2,3])
         @renderable.addAnimation('stand', [0])
         @renderable.setCurrentAnimation('stand')
+        @firstUpdate = true

     _doStep: (dt) ->
         @body.update(dt)
@@ -128,8 +129,10 @@
             # set the jumping flag
             @body.jumping = true
     update: (dt) ->
-        @z = 1000000
-        me.game.world.sort()
+        if @firstUpdate
+            @z = 1000000
+            me.game.world.sort()
+            @firstUpdate = false
         if me.input.isKeyPressed('block')
             [x, y] = [@getRx(), @getRy()]
             # {x, y} = @pos


make sure all writes get read in the same order

--- lib/stream.coffee
+++ lib/stream.coffee
@@ -137,6 +137,12 @@
   readyForWriting = false
   encoding = null
   emitter = new EventEmitter()
+  writeQueue = []
+  waitingOnPreviousWrite = false
+  doNextWrite = ->
+    if writeQueue.length and not waitingOnPreviousWrite
+      waitingOnPreviousWrite = true
+      writeQueue.shift()()
   stream =
     readable: true
     writeable: true
@@ -151,18 +157,20 @@
     setEncoding: (e) ->
       encoding = e
     write: (data) ->
-      performWrite = ->
-        req = http.request({port: phantomServerPort, method: "POST"})
+      writeQueue.push ->
+        req = http.request {port: phantomServerPort, method: "POST"}, ->
+          waitingOnPreviousWrite = false
+          doNextWrite()
         req.on "error", (err) ->
           logger.error?(err)
         req.setHeader("Content-Length", data.length)
         req.end(data)
       if readyForWriting
-        performWrite()
+        doNextWrite()
       else
         emitter.on "ready", ->
           readyForWriting = true
-          performWrite()
+          doNextWrite()
   return stream

 exports.open = open
@@ -178,6 +186,7 @@

   console.info("CLIENT: SEND: hello world")
   stream.write("hello world")
+  stream.write("hello world2")
   stream.setEncoding("utf-8")
   stream.on "data", (data) ->
     console.info("CLIENT: RECV: #{data}")