Update InformaticsUser.coffee

Update InformaticsUser.coffee

--- server/informatics/InformaticsUser.coffee
+++ server/informatics/InformaticsUser.coffee
@@ -80,7 +80,7 @@
         @name = a.innerHTML
         if not id or id.length < 2
             throw "Can't detect id, href=" + a.href + " username=" + @username
-        logger.info "Logged in user #{username} href=#{a.href}"
+        logger.info "Logged in user #{@username} href=#{a.href}"
         @id = id[1]
         return
             id: @id,


Change urls of messages from Array of String to Array of Objects

--- server/methods/sendMessage.coffee
+++ server/methods/sendMessage.coffee
@@ -15,7 +15,7 @@
 		message.u = Meteor.users.findOne Meteor.userId(), fields: username: 1

 		if urls = message.msg.match /([A-Za-z]{3,9}):\/\/([-;:&=\+\$,\w]+@{1})?([-A-Za-z0-9\.]+)+:?(\d+)?((\/[-\+=!:~%\/\.@\,\w]+)?\??([-\+=&!:;%@\/\.\,\w]+)?#?([\w]+)?)?/g
-			message.urls = urls
+			message.urls = urls.map (url) -> url: url

 		message = RocketChat.callbacks.run 'beforeSaveMessage', message


Cleanups.

Cleanups.

--- framer/Extras/Preloader.coffee
+++ framer/Extras/Preloader.coffee
@@ -38,7 +38,7 @@

 			do layout = =>
 				screen = Framer.Device?.screen
-				scale = screen?.frame.width / screen?.canvasFrame.width
+				scale = screen?.frame.width / screen?.canvasFrame.width
 				@progressIndicator.scale = scale
 				@brand.scale = scale

@@ -143,8 +143,7 @@
 		@end()

 exports.enable = ->
-	return if Framer.Preloader
-	Framer.Preloader = new Preloader()
+	Framer.Preloader ?= new Preloader()

 exports.disable = ->
 	return unless Framer.Preloader
@@ -152,5 +151,4 @@
 	Framer.Preloader = null

 exports.addImage = (url) ->
-	return unless Framer.Preloader
-	Framer.Preloader.addImage url
+	Framer.Preloader?.addImage url


fix object returned by defaultValues and initMove

--- app/assets/javascripts/components/mark/tools/text-row-tool/index.cjsx
+++ app/assets/javascripts/components/mark/tools/text-row-tool/index.cjsx
@@ -19,13 +19,15 @@

   statics:
     defaultValues: ({x, y}) ->
-      {x, y} # this will be the initial click position (not super useful as of yet)
+      x: x
+      y: y # x and y will be the initial click position (not super useful as of yet)
       yUpper: y - DEFAULT_HEIGHT/2
       yLower: y + DEFAULT_HEIGHT/2

     initMove: ({x, y}) ->
-      {x, y}
-      yUpper: y - DEFAULT_HEIGHT/2 # not sure if these are needed here
+      x: x
+      y: y
+      yUpper: y - DEFAULT_HEIGHT/2 # not sure if these are
       yLower: y + DEFAULT_HEIGHT/2

   getDeleteButtonPosition: ->


fixed incorrect prototyping

fixed incorrect prototyping

--- script/helix.coffee
+++ script/helix.coffee
@@ -2,7 +2,7 @@

 helix.config = {}
-helix.config.localStream = "/"
+helix.config.localStream = "http://localhost:9000/"
 helix.config.remoteStream = "http://stream.helix.to/"

@@ -76,6 +76,34 @@
         load.reject()

     return load
+
+
+helix.smartLoad = (url, loaded, formats=['html', 'js'], i=0) ->
+    """multi format loader
+    """
+    if not loaded?
+        loaded = new $.Deferred()
+
+    splitURL = url.split('.')
+    if splitURL.length > 1
+        fullURL = url
+    else
+        fullURL = url + "." + formats[i]
+
+    formatLoaded = helix.loadURL(fullURL)
+    $.when(formatLoaded).then(() ->
+        loaded.resolve()
+        console.log "#{fullURL} successfully loaded")
+
+    formatLoaded.fail(() ->
+        i = i + 1
+        if i <= formats.length
+            helix.smartLoad(url, loaded, formats, i)
+        else
+            loaded.reject()
+            console.log "#{url} couldn't be located using #{formats.toString()}")
+
+    return loaded

 helix._loadFileURL = (url, direct=false) ->
@@ -132,8 +160,7 @@
     return helix.loadedFiles[path]

-helix.loadedBases = {}
-helix.loadedFamilies = {}
+helix.definedBases = {}
 helix.loadBase = (base) ->
     """attempts a load of the specified base

@@ -161,29 +188,26 @@
     if splitTag.length <= 1
         return ''

-    if not helix.loadedBases[baseName]?
-        helix.loadedBases[baseName] = new $.Deferred()
+    if not helix.definedBases[baseName]?
+        helix.definedBases[baseName] = new $.Deferred()
         if baseName isnt 'helix-base'
             helix.loadCount.inc()

-        ## base of family should be a base.html or base.js file
-        baseFamily = splitTag[0] + "-base"
-        if baseName isnt baseFamily
-            familyBaseLoaded = helix.loadBase(baseFamily)
+        familyBase = splitTag[0] + "-base"
+        if baseName isnt familyBase
+            familyBaseLoaded = helix.loadBase(familyBase)

             helix.loadCount.inc()
             $.when(familyBaseLoaded).then(() =>
                 helix.loadCount.dec()
-                mappedPath = helix.bases[baseFamily].prototype.mapPath(baseName)
+                mappedPath = helix.bases[familyBase].prototype.mapPath(baseName)
                 if mappedPath isnt false and typeof mappedPath is 'string'
-                    baseLoaded = helix.loadFile(mappedPath)
-                    $.when(baseLoaded).then(() =>
-                        helix.loadedBases[baseName].resolve()))
-        else
-            baseFamilyPath = baseFamily.replace(/\-/g, '/')
-            helix.loadFile(baseFamilyPath)
-
-    return helix.loadedBases[baseName]
+                    helix.loadFile(mappedPath))
+        else
+            familyBasePath = familyBase.replace(/\-/g, '/')
+            helix.loadFile(familyBasePath)
+
+    return helix.definedBases[baseName]

 helix.bases = {}
@@ -253,9 +277,9 @@
             if $.isFunction(value) # if key not in excludedKeys
                 elPrototype[key] = value

-            ## extend parent attributes
-            else if key is 'properties' and elPrototype.properties?
-                if key is 'properties'
+            else if key in ['properties', 'template', 'class', 'wildcard']
+                ## extend parent attributes
+                if key is 'properties' and elPrototype.properties?
                     extendedProperties = $.extend({}, elPrototype.properties)
                     for k, v of value
                         if v?
@@ -263,9 +287,6 @@

                     value = $.extend({}, extendedProperties)

-            ## only allow a subset of non-function values to be set at
-            ## at the prototype level
-            else if key in ['template', 'class', 'wildcard']
                 Object.defineProperty(elPrototype, key, {
                     value: value
                     writable: true
@@ -276,11 +297,11 @@

         ## allow access to the prototype
         helix.bases["#{tagName}"] = CustomElement
-        if not helix.loadedBases["#{tagName}"]?
-            helix.loadedBases["#{tagName}"] = new $.Deferred()
+        if not helix.definedBases["#{tagName}"]?
+            helix.definedBases["#{tagName}"] = new $.Deferred()

         ## let everyone know the base has been loaded
-        helix.loadedBases["#{tagName}"].resolve()
+        helix.definedBases["#{tagName}"].resolve()
         if tagName isnt 'helix-base'
             helix.loadCount.dec()
     )


Wait for activation

Wait for activation

--- spec/symbols-view-spec.coffee
+++ spec/symbols-view-spec.coffee
@@ -258,6 +258,9 @@
         atom.workspace.getActiveTextEditor().setCursorBufferPosition([13,4])
         atom.commands.dispatch(getEditorView(), 'symbols-view:go-to-declaration')

+      waitsForPromise ->
+        activationPromise
+
       waitsFor ->
         SymbolsView::moveToPosition.callCount is 1

@@ -291,6 +294,11 @@
           editor = atom.workspace.getActiveTextEditor()
           editor.setCursorBufferPosition([6,0])
           atom.commands.dispatch(getEditorView(), 'symbols-view:return-from-declaration')
+
+        waitsForPromise ->
+          activationPromise
+
+        runs ->
           expect(editor.getCursorBufferPosition()).toEqual [6,0]

       it "returns to previous row and column", ->
@@ -302,6 +310,9 @@
           editor.setCursorBufferPosition([6,24])
           spyOn(SymbolsView.prototype, "moveToPosition").andCallThrough()
           atom.commands.dispatch(getEditorView(), 'symbols-view:go-to-declaration')
+
+        waitsForPromise ->
+          activationPromise

         waitsFor ->
           SymbolsView::moveToPosition.callCount is 1


:lipstick: remove non-essential noise.

:lipstick: remove non-essential noise.

--- spec/text-objects-spec.coffee
+++ spec/text-objects-spec.coffee
@@ -1,6 +1,6 @@
 helpers = require './spec-helper'

-fdescribe "TextObjects", ->
+describe "TextObjects", ->
   [editor, editorElement, vimState] = []

   beforeEach ->
@@ -18,533 +18,469 @@
     options.element ?= editorElement
     helpers.keydown(key, options)

+  keystroke = (keys) ->
+    for key in keys.split('')
+      if key.match(/[A-Z]/)
+        keydown(key, shift: true)
+      else
+        keydown(key)
+
   normalModeInputKeydown = (key, opts = {}) ->
     editor.normalModeInputView.editorElement.getModel().setText(key)

+  text = (text=null) ->
+    if text
+      editor.setText text
+    else
+      expect editor.getText()
+
+  addCursor = (point) ->
+    editor.addCursorAtBufferPosition point
+
+  cursor = (point=null) ->
+    if point
+      editor.setCursorScreenPosition point
+    else
+      expect editor.getCursorScreenPosition()
+
+  cursorBuffer = (point=null) ->
+    if point
+      editor.setCursorBufferPosition point
+    else
+      expect editor.getCursorBufferPosition()
+
+  selectedScreenRange = ->
+      expect editor.getSelectedScreenRange()
+
+  selectedBufferRange = ->
+      expect editor.getSelectedBufferRange()
+
+  selectedBufferRanges = ->
+      expect editor.getSelectedBufferRanges()
+
+  register = (name, value) ->
+    if value
+      vimState.register.set(name, value)
+    else
+      expect vimState.register.get(name).text
+
+  classListContains = (klass) ->
+    expect editorElement.classList.contains(klass)
+
   describe "the 'iw' text object", ->
     beforeEach ->
-      editor.setText("12345 abcde ABCDE")
-      editor.setCursorScreenPosition([0, 9])
-
-    it "applies operators inside the current word in operator-pending mode", ->
-      keydown('d')
-      keydown('i')
-      keydown('w')
-
-      expect(editor.getText()).toBe "12345  ABCDE"
-      expect(editor.getCursorScreenPosition()).toEqual [0, 6]
-      expect(vimState.register.get('"').text).toBe "abcde"
-      expect(editorElement.classList.contains('operator-pending-mode')).toBe(false)
-      expect(editorElement.classList.contains('normal-mode')).toBe(true)
+      text "12345 abcde ABCDE"
+      cursor [0, 9]
+
+    it "applies operators inside the current word in operator-pending mode", ->
+      keystroke 'diw'
+
+      text().toBe "12345  ABCDE"
+      cursor().toEqual [0, 6]
+      register('"').toBe 'abcde'
+      classListContains('operator-pending-mode').toBe(false)
+      classListContains('normal-mode').toBe(true)

     it "selects inside the current word in visual mode", ->
-      keydown('v')
-      keydown('i')
-      keydown('w')
-
-      expect(editor.getSelectedScreenRange()).toEqual [[0, 6], [0, 11]]
+      keystroke 'viw'
+      selectedScreenRange().toEqual [[0, 6], [0, 11]]

     it "works with multiple cursors", ->
-      editor.addCursorAtBufferPosition([0, 1])
-      keydown("v")
-      keydown("i")
-      keydown("w")
-      expect(editor.getSelectedBufferRanges()).toEqual [
+      addCursor [0, 1]
+      keystroke 'viw'
+      selectedBufferRanges().toEqual [
         [[0, 6], [0, 11]]
         [[0, 0], [0, 5]]
       ]

   describe "the 'iW' text object", ->
     beforeEach ->
-      editor.setText("12(45 ab'de ABCDE")
-      editor.setCursorScreenPosition([0, 9])
+      text "12(45 ab'de ABCDE"
+      cursor [0, 9]

     it "applies operators inside the current whole word in operator-pending mode", ->
-      keydown('d')
-      keydown('i')
-      keydown('W', shift: true)
-
-      expect(editor.getText()).toBe "12(45  ABCDE"
-      expect(editor.getCursorScreenPosition()).toEqual [0, 6]
-      expect(vimState.register.get('"').text).toBe "ab'de"
-      expect(editorElement.classList.contains('operator-pending-mode')).toBe(false)
-      expect(editorElement.classList.contains('normal-mode')).toBe(true)
+      keystroke 'diW'
+
+      text().toBe "12(45  ABCDE"
+      cursor().toEqual [0, 6]
+      register('"').toBe "ab'de"
+      classListContains('operator-pending-mode').toBe(false)
+      classListContains('normal-mode').toBe(true)

     it "selects inside the current whole word in visual mode", ->
-      keydown('v')
-      keydown('i')
-      keydown('W', shift: true)
-
-      expect(editor.getSelectedScreenRange()).toEqual [[0, 6], [0, 11]]
+      keystroke 'viW'
+      selectedScreenRange().toEqual [[0, 6], [0, 11]]

   describe "the 'i(' text object", ->
     beforeEach ->
-      editor.setText("( something in here and in (here) )")
-      editor.setCursorScreenPosition([0, 9])
-
-    it "applies operators inside the current word in operator-pending mode", ->
-      keydown('d')
-      keydown('i')
-      keydown('(')
-      expect(editor.getText()).toBe "()"
-      expect(editor.getCursorScreenPosition()).toEqual [0, 1]
-      expect(editorElement.classList.contains('operator-pending-mode')).toBe(false)
-      expect(editorElement.classList.contains('normal-mode')).toBe(true)
+      text "( something in here and in (here) )"
+      cursor [0, 9]
+
+    it "applies operators inside the current word in operator-pending mode", ->
+      keystroke 'di('
+      text().toBe "()"
+      cursor().toEqual [0, 1]
+      classListContains('operator-pending-mode').toBe(false)
+      classListContains('normal-mode').toBe(true)

     it "applies operators inside the current word in operator-pending mode (second test)", ->
-      editor.setCursorScreenPosition([0, 29])
-      keydown('d')
-      keydown('i')
-      keydown('(')
-      expect(editor.getText()).toBe "( something in here and in () )"
-      expect(editor.getCursorScreenPosition()).toEqual [0, 28]
-      expect(editorElement.classList.contains('operator-pending-mode')).toBe(false)
-      expect(editorElement.classList.contains('normal-mode')).toBe(true)
+      cursor [0, 29]
+      keystroke 'di('
+      text().toBe "( something in here and in () )"
+      cursor().toEqual [0, 28]
+      classListContains('operator-pending-mode').toBe(false)
+      classListContains('normal-mode').toBe(true)

     it "works with multiple cursors", ->
-      editor.setText("( a b ) cde ( f g h ) ijk")
-      editor.setCursorBufferPosition([0, 2])
-      editor.addCursorAtBufferPosition([0, 18])
-
-      keydown("v")
-      keydown("i")
-      keydown("(")
-
-      expect(editor.getSelectedBufferRanges()).toEqual [
+      text "( a b ) cde ( f g h ) ijk"
+      cursorBuffer [0, 2]
+      addCursor [0, 18]
+      keystroke 'vi('
+      selectedBufferRanges().toEqual [
         [[0, 1],  [0, 6]]
         [[0, 13], [0, 20]]
       ]

   describe "the 'i{' text object", ->
     beforeEach ->
-      editor.setText("{ something in here and in {here} }")
-      editor.setCursorScreenPosition([0, 9])
-
-    it "applies operators inside the current word in operator-pending mode", ->
-      keydown('d')
-      keydown('i')
-      keydown('{')
-      expect(editor.getText()).toBe "{}"
-      expect(editor.getCursorScreenPosition()).toEqual [0, 1]
-      expect(editorElement.classList.contains('operator-pending-mode')).toBe(false)
-      expect(editorElement.classList.contains('normal-mode')).toBe(true)
+      text "{ something in here and in {here} }"
+      cursor [0, 9]
+
+    it "applies operators inside the current word in operator-pending mode", ->
+      keystroke 'di{'
+      text().toBe "{}"
+      cursor().toEqual [0, 1]
+      classListContains('operator-pending-mode').toBe(false)
+      classListContains('normal-mode').toBe(true)

     it "applies operators inside the current word in operator-pending mode (second test)", ->
-      editor.setCursorScreenPosition([0, 29])
-      keydown('d')
-      keydown('i')
-      keydown('{')
-      expect(editor.getText()).toBe "{ something in here and in {} }"
-      expect(editor.getCursorScreenPosition()).toEqual [0, 28]
-      expect(editorElement.classList.contains('operator-pending-mode')).toBe(false)
-      expect(editorElement.classList.contains('normal-mode')).toBe(true)
+      cursor [0, 29]
+      keystroke 'di{'
+      text().toBe "{ something in here and in {} }"
+      cursor().toEqual [0, 28]
+      classListContains('operator-pending-mode').toBe(false)
+      classListContains('normal-mode').toBe(true)

   describe "the 'i<' text object", ->
     beforeEach ->
-      editor.setText("< something in here and in <here> >")
-      editor.setCursorScreenPosition([0, 9])
-
-    it "applies operators inside the current word in operator-pending mode", ->
-      keydown('d')
-      keydown('i')
-      keydown('<')
-      expect(editor.getText()).toBe "<>"
-      expect(editor.getCursorScreenPosition()).toEqual [0, 1]
-      expect(editorElement.classList.contains('operator-pending-mode')).toBe(false)
-      expect(editorElement.classList.contains('normal-mode')).toBe(true)
+      text "< something in here and in <here> >"
+      cursor [0, 9]
+
+    it "applies operators inside the current word in operator-pending mode", ->
+      keystroke 'di<'
+      text().toBe "<>"
+      cursor().toEqual [0, 1]
+      classListContains('operator-pending-mode').toBe(false)
+      classListContains('normal-mode').toBe(true)

     it "applies operators inside the current word in operator-pending mode (second test)", ->
-      editor.setCursorScreenPosition([0, 29])
-      keydown('d')
-      keydown('i')
-      keydown('<')
-      expect(editor.getText()).toBe "< something in here and in <> >"
-      expect(editor.getCursorScreenPosition()).toEqual [0, 28]
-      expect(editorElement.classList.contains('operator-pending-mode')).toBe(false)
-      expect(editorElement.classList.contains('normal-mode')).toBe(true)
+      cursor [0, 29]
+      keystroke 'di<'
+      text().toBe "< something in here and in <> >"
+      cursor().toEqual [0, 28]
+      classListContains('operator-pending-mode').toBe(false)
+      classListContains('normal-mode').toBe(true)

   describe "the 'it' text object", ->
     beforeEach ->
-      editor.setText("<something>here</something><again>")
-      editor.setCursorScreenPosition([0, 5])
+      text "<something>here</something><again>"
+      cursor [0, 5]

     it "applies only if in the value of a tag", ->
-      keydown('d')
-      keydown('i')
-      keydown('t')
-      expect(editor.getText()).toBe "<something></something><again>"
-      expect(editor.getCursorScreenPosition()).toEqual [0, 11]
-      expect(editorElement.classList.contains('operator-pending-mode')).toBe(false)
-      expect(editorElement.classList.contains('normal-mode')).toBe(true)
-
-    it "applies operators inside the current word in operator-pending mode", ->
-      editor.setCursorScreenPosition([0, 13])
-      keydown('d')
-      keydown('i')
-      keydown('t')
-      expect(editor.getText()).toBe "<something></something><again>"
-      expect(editor.getCursorScreenPosition()).toEqual [0, 11]
-      expect(editorElement.classList.contains('operator-pending-mode')).toBe(false)
-      expect(editorElement.classList.contains('normal-mode')).toBe(true)
+      keystroke 'dit'
+      text().toBe "<something></something><again>"
+      cursor().toEqual [0, 11]
+      classListContains('operator-pending-mode').toBe(false)
+      classListContains('normal-mode').toBe(true)
+
+    it "applies operators inside the current word in operator-pending mode", ->
+      cursor [0, 13]
+      keystroke 'dit'
+      text().toBe "<something></something><again>"
+      cursor().toEqual [0, 11]
+      classListContains('operator-pending-mode').toBe(false)
+      classListContains('normal-mode').toBe(true)

   describe "the 'ip' text object", ->
     beforeEach ->
-      editor.setText("\nParagraph-1\nParagraph-1\nParagraph-1\n\n")
-      editor.setCursorScreenPosition([2, 2])
+      text "\nParagraph-1\nParagraph-1\nParagraph-1\n\n"
+      cursor [2, 2]

     it "applies operators inside the current paragraph in operator-pending mode", ->
-
-      keydown('y')
-      keydown('i')
-      keydown('p')
-
-      expect(editor.getText()).toBe "\nParagraph-1\nParagraph-1\nParagraph-1\n\n"
-      expect(editor.getCursorScreenPosition()).toEqual [1, 0]
-      expect(vimState.register.get('"').text).toBe "Paragraph-1\nParagraph-1\nParagraph-1\n"
-      expect(editorElement.classList.contains('operator-pending-mode')).toBe(false)
-      expect(editorElement.classList.contains('normal-mode')).toBe(true)
+      keystroke 'yip'
+      text().toBe "\nParagraph-1\nParagraph-1\nParagraph-1\n\n"
+      cursor().toEqual [1, 0]
+      register('"').toBe "Paragraph-1\nParagraph-1\nParagraph-1\n"
+      classListContains('operator-pending-mode').toBe(false)
+      classListContains('normal-mode').toBe(true)

     it "selects inside the current paragraph in visual mode", ->
-      keydown('v')
-      keydown('i')
-      keydown('p')
-
-      expect(editor.getSelectedScreenRange()).toEqual [[1, 0], [4, 0]]
+      keystroke 'vip'
+      selectedScreenRange().toEqual [[1, 0], [4, 0]]

   describe "the 'ap' text object", ->
     beforeEach ->
-      editor.setText("text\n\nParagraph-1\nParagraph-1\nParagraph-1\n\nmoretext")
-      editor.setCursorScreenPosition([3, 2])
+      text "text\n\nParagraph-1\nParagraph-1\nParagraph-1\n\nmoretext"
+      cursor [3, 2]

     it "applies operators around the current paragraph in operator-pending mode", ->
-
-      keydown('y')
-      keydown('a')
-      keydown('p')
-
-      expect(editor.getText()).toBe "text\n\nParagraph-1\nParagraph-1\nParagraph-1\n\nmoretext"
-      expect(editor.getCursorScreenPosition()).toEqual [2, 0]
-      expect(vimState.register.get('"').text).toBe "Paragraph-1\nParagraph-1\nParagraph-1\n\n"
-      expect(editorElement.classList.contains('operator-pending-mode')).toBe(false)
-      expect(editorElement.classList.contains('normal-mode')).toBe(true)
+      keystroke 'yap'
+      text().toBe "text\n\nParagraph-1\nParagraph-1\nParagraph-1\n\nmoretext"
+      cursor().toEqual [2, 0]
+      register('"').toBe "Paragraph-1\nParagraph-1\nParagraph-1\n\n"
+      classListContains('operator-pending-mode').toBe(false)
+      classListContains('normal-mode').toBe(true)

     it "selects around the current paragraph in visual mode", ->
-      keydown('v')
-      keydown('a')
-      keydown('p')
-
-      expect(editor.getSelectedScreenRange()).toEqual [[2, 0], [6, 0]]
+      keystroke 'vap'
+      selectedScreenRange().toEqual [[2, 0], [6, 0]]

   describe "the 'i[' text object", ->
     beforeEach ->
-      editor.setText("[ something in here and in [here] ]")
-      editor.setCursorScreenPosition([0, 9])
-
-    it "applies operators inside the current word in operator-pending mode", ->
-      keydown('d')
-      keydown('i')
-      keydown('[')
-      expect(editor.getText()).toBe "[]"
-      expect(editor.getCursorScreenPosition()).toEqual [0, 1]
-      expect(editorElement.classList.contains('operator-pending-mode')).toBe(false)
-      expect(editorElement.classList.contains('normal-mode')).toBe(true)
+      text "[ something in here and in [here] ]"
+      cursor [0, 9]
+
+    it "applies operators inside the current word in operator-pending mode", ->
+      keystroke 'di['
+      text().toBe "[]"
+      cursor().toEqual [0, 1]
+      classListContains('operator-pending-mode').toBe(false)
+      classListContains('normal-mode').toBe(true)

     it "applies operators inside the current word in operator-pending mode (second test)", ->
-      editor.setCursorScreenPosition([0, 29])
-      keydown('d')
-      keydown('i')
-      keydown('[')
-      expect(editor.getText()).toBe "[ something in here and in [] ]"
-      expect(editor.getCursorScreenPosition()).toEqual [0, 28]
-      expect(editorElement.classList.contains('operator-pending-mode')).toBe(false)
-      expect(editorElement.classList.contains('normal-mode')).toBe(true)
+      cursor [0, 29]
+      keystroke 'di['
+      text().toBe "[ something in here and in [] ]"
+      cursor().toEqual [0, 28]
+      classListContains('operator-pending-mode').toBe(false)
+      classListContains('normal-mode').toBe(true)

   describe "the 'i\'' text object", ->
     beforeEach ->
-      editor.setText("' something in here and in 'here' ' and over here")
-      editor.setCursorScreenPosition([0, 9])
+      text "' something in here and in 'here' ' and over here"
+      cursor [0, 9]

     it "applies operators inside the current string in operator-pending mode", ->
-      keydown('d')
-      keydown('i')
-      keydown('\'')
-      expect(editor.getText()).toBe "''here' ' and over here"
-      expect(editor.getCursorScreenPosition()).toEqual [0, 1]
-      expect(editorElement.classList.contains('operator-pending-mode')).toBe(false)
-      expect(editorElement.classList.contains('normal-mode')).toBe(true)
+      keystroke "di'"
+      text().toBe "''here' ' and over here"
+      cursor().toEqual [0, 1]
+      classListContains('operator-pending-mode').toBe(false)
+      classListContains('normal-mode').toBe(true)

     # I don't like old behavior, that was not in Vim and furthermore, this is counter intuitive.
     # Simply selecting area between quote is that normal user expects.
     # it "applies operators inside the next string in operator-pending mode (if not in a string)", ->
     it "[Changed behavior] applies operators inside area between quote", ->
-      editor.setCursorScreenPosition([0, 29])
-      keydown('d')
-      keydown('i')
-      keydown('\'')
-      expect(editor.getText()).toBe "' something in here and in '' ' and over here"
-      expect(editor.getCursorScreenPosition()).toEqual [0, 28]
-      expect(editorElement.classList.contains('operator-pending-mode')).toBe(false)
-      expect(editorElement.classList.contains('normal-mode')).toBe(true)
+      cursor [0, 29]
+      keystroke "di'"
+      text().toBe "' something in here and in '' ' and over here"
+      cursor().toEqual [0, 28]
+      classListContains('operator-pending-mode').toBe(false)
+      classListContains('normal-mode').toBe(true)

     it "makes no change if past the last string on a line", ->
-      editor.setCursorScreenPosition([0, 39])
-      keydown('d')
-      keydown('i')
-      keydown('\'')
-      expect(editor.getText()).toBe "' something in here and in 'here' ' and over here"
-      expect(editor.getCursorScreenPosition()).toEqual [0, 39]
-      expect(editorElement.classList.contains('operator-pending-mode')).toBe(false)
-      expect(editorElement.classList.contains('normal-mode')).toBe(true)
+      cursor [0, 39]
+      keystroke "di'"
+      text().toBe "' something in here and in 'here' ' and over here"
+      cursor().toEqual [0, 39]
+      classListContains('operator-pending-mode').toBe(false)
+      classListContains('normal-mode').toBe(true)

   describe "the 'i\"' text object", ->
     beforeEach ->
-      editor.setText("\" something in here and in \"here\" \" and over here")
-      editor.setCursorScreenPosition([0, 9])
+      text '" something in here and in "here" " and over here'
+      cursor [0, 9]

     it "applies operators inside the current string in operator-pending mode", ->
-      keydown('d')
-      keydown('i')
-      keydown('"')
-      expect(editor.getText()).toBe "\"\"here\" \" and over here"
-      expect(editor.getCursorScreenPosition()).toEqual [0, 1]
-      expect(editorElement.classList.contains('operator-pending-mode')).toBe(false)
-      expect(editorElement.classList.contains('normal-mode')).toBe(true)
+      keystroke 'di"'
+      text().toBe '""here" " and over here'
+      cursor().toEqual [0, 1]
+      classListContains('operator-pending-mode').toBe(false)
+      classListContains('normal-mode').toBe(true)

     it "[Changed behavior] applies operators inside area between quote", ->
-      editor.setCursorScreenPosition([0, 29])
-      keydown('d')
-      keydown('i')
-      keydown('"')
-      expect(editor.getText()).toBe '" something in here and in "" " and over here'
-      expect(editor.getCursorScreenPosition()).toEqual [0, 28]
-      expect(editorElement.classList.contains('operator-pending-mode')).toBe(false)
-      expect(editorElement.classList.contains('normal-mode')).toBe(true)
+      cursor [0, 29]
+      keystroke 'di"'
+      text().toBe '" something in here and in "" " and over here'
+      cursor().toEqual [0, 28]
+      classListContains('operator-pending-mode').toBe(false)
+      classListContains('normal-mode').toBe(true)

     it "makes no change if past the last string on a line", ->
-      editor.setCursorScreenPosition([0, 39])
-      keydown('d')
-      keydown('i')
-      keydown('"')
-      expect(editor.getText()).toBe "\" something in here and in \"here\" \" and over here"
-      expect(editor.getCursorScreenPosition()).toEqual [0, 39]
-      expect(editorElement.classList.contains('operator-pending-mode')).toBe(false)
+      cursor [0, 39]
+      keystroke 'di"'
+      text().toBe '" something in here and in "here" " and over here'
+      cursor().toEqual [0, 39]
+      classListContains('operator-pending-mode').toBe(false)

   describe "the 'aw' text object", ->
     beforeEach ->
-      editor.setText("12345 abcde ABCDE")
-      editor.setCursorScreenPosition([0, 9])
+      text "12345 abcde ABCDE"
+      cursor [0, 9]

     it "applies operators from the start of the current word to the start of the next word in operator-pending mode", ->
-      keydown('d')
-      keydown('a')
-      keydown('w')
-
-      expect(editor.getText()).toBe "12345 ABCDE"
-      expect(editor.getCursorScreenPosition()).toEqual [0, 6]
-      expect(vimState.register.get('"').text).toBe "abcde "
-      expect(editorElement.classList.contains('operator-pending-mode')).toBe(false)
-      expect(editorElement.classList.contains('normal-mode')).toBe(true)
+      keystroke 'daw'
+      text().toBe "12345 ABCDE"
+      cursor().toEqual [0, 6]
+      register('"').toBe "abcde "
+      classListContains('operator-pending-mode').toBe(false)
+      classListContains('normal-mode').toBe(true)

     it "selects from the start of the current word to the start of the next word in visual mode", ->
-      keydown('v')
-      keydown('a')
-      keydown('w')
-
-      expect(editor.getSelectedScreenRange()).toEqual [[0, 6], [0, 12]]
+      keystroke 'vaw'
+      selectedScreenRange().toEqual [[0, 6], [0, 12]]

     it "doesn't span newlines", ->
-      editor.setText("12345\nabcde ABCDE")
-      editor.setCursorBufferPosition([0, 3])
-
-      keydown("v")
-      keydown("a")
-      keydown("w")
-
-      expect(editor.getSelectedBufferRanges()).toEqual [[[0, 0], [0, 5]]]
+      text "12345\nabcde ABCDE"
+      cursor [0, 3]
+      keystroke 'vaw'
+      selectedBufferRange().toEqual [[0, 0], [0, 5]]

     it "doesn't span special characters", ->
-      editor.setText("1(345\nabcde ABCDE")
-      editor.setCursorBufferPosition([0, 3])
-
-      keydown("v")
-      keydown("a")
-      keydown("w")
-
-      expect(editor.getSelectedBufferRanges()).toEqual [[[0, 2], [0, 5]]]
+      text "1(345\nabcde ABCDE"
+      cursor [0, 3]
+      keystroke 'vaw'
+      selectedBufferRange().toEqual [[0, 2], [0, 5]]

   describe "the 'aW' text object", ->
     beforeEach ->
-      editor.setText("12(45 ab'de ABCDE")
-      editor.setCursorScreenPosition([0, 9])
+      text "12(45 ab'de ABCDE"
+      cursor [0, 9]

     it "applies operators from the start of the current whole word to the start of the next whole word in operator-pending mode", ->
-      keydown('d')
-      keydown('a')
-      keydown('W', shift: true)
-
-      expect(editor.getText()).toBe "12(45 ABCDE"
-      expect(editor.getCursorScreenPosition()).toEqual [0, 6]
-      expect(vimState.register.get('"').text).toBe "ab'de "
-      expect(editorElement.classList.contains('operator-pending-mode')).toBe(false)
-      expect(editorElement.classList.contains('normal-mode')).toBe(true)
+      keystroke 'daW'
+      text().toBe "12(45 ABCDE"
+      cursor().toEqual [0, 6]
+      register('"').toBe "ab'de "
+      classListContains('operator-pending-mode').toBe(false)
+      classListContains('normal-mode').toBe(true)

     it "selects from the start of the current whole word to the start of the next whole word in visual mode", ->
-      keydown('v')
-      keydown('a')
-      keydown('W', shift: true)
-
-      expect(editor.getSelectedScreenRange()).toEqual [[0, 6], [0, 12]]
+      keystroke 'vaW'
+      selectedScreenRange().toEqual [[0, 6], [0, 12]]

     it "doesn't span newlines", ->
-      editor.setText("12(45\nab'de ABCDE")
-      editor.setCursorBufferPosition([0, 4])
-
-      keydown('v')
-      keydown('a')
-      keydown('W', shift: true)
-
-      expect(editor.getSelectedBufferRanges()).toEqual [[[0, 0], [0, 5]]]
+      text "12(45\nab'de ABCDE"
+      cursor [0, 4]
+      keystroke 'vaW'
+      selectedBufferRange().toEqual [[0, 0], [0, 5]]

   describe "the 'a(' text object", ->
     beforeEach ->
-      editor.setText("( something in here and in (here) )")
-      editor.setCursorScreenPosition([0, 9])
+      text "( something in here and in (here) )"
+      cursor [0, 9]

     it "applies operators around the current parentheses in operator-pending mode", ->
-      keydown('d')
-      keydown('a')
-      keydown('(')
-      expect(editor.getText()).toBe ""
-      expect(editor.getCursorScreenPosition()).toEqual [0, 0]
-      expect(editorElement.classList.contains('operator-pending-mode')).toBe(false)
-      expect(editorElement.classList.contains('normal-mode')).toBe(true)
+      keystroke 'da('
+      text().toBe ""
+      cursor().toEqual [0, 0]
+      classListContains('operator-pending-mode').toBe(false)
+      classListContains('normal-mode').toBe(true)

     it "applies operators around the current parentheses in operator-pending mode (second test)", ->
-      editor.setCursorScreenPosition([0, 29])
-      keydown('d')
-      keydown('a')
-      keydown('(')
-      expect(editor.getText()).toBe "( something in here and in  )"
-      expect(editor.getCursorScreenPosition()).toEqual [0, 27]
-      expect(editorElement.classList.contains('operator-pending-mode')).toBe(false)
-      expect(editorElement.classList.contains('normal-mode')).toBe(true)
+      cursor [0, 29]
+      keystroke 'da('
+      text().toBe "( something in here and in  )"
+      cursor().toEqual [0, 27]
+      classListContains('operator-pending-mode').toBe(false)
+      classListContains('normal-mode').toBe(true)

   describe "the 'a{' text object", ->
     beforeEach ->
-      editor.setText("{ something in here and in {here} }")
-      editor.setCursorScreenPosition([0, 9])
+      text "{ something in here and in {here} }"
+      cursor [0, 9]

     it "applies operators around the current curly brackets in operator-pending mode", ->
-      keydown('d')
-      keydown('a')
-      keydown('{')
-      expect(editor.getText()).toBe ""
-      expect(editor.getCursorScreenPosition()).toEqual [0, 0]
-      expect(editorElement.classList.contains('operator-pending-mode')).toBe(false)
-      expect(editorElement.classList.contains('normal-mode')).toBe(true)
+      keystroke 'da{'
+      text().toBe ""
+      cursor().toEqual [0, 0]
+      classListContains('operator-pending-mode').toBe(false)
+      classListContains('normal-mode').toBe(true)

     it "applies operators around the current curly brackets in operator-pending mode (second test)", ->
-      editor.setCursorScreenPosition([0, 29])
-      keydown('d')
-      keydown('a')
-      keydown('{')
-      expect(editor.getText()).toBe "{ something in here and in  }"
-      expect(editor.getCursorScreenPosition()).toEqual [0, 27]
-      expect(editorElement.classList.contains('operator-pending-mode')).toBe(false)
-      expect(editorElement.classList.contains('normal-mode')).toBe(true)
+      cursor [0, 29]
+      keystroke 'da{'
+      text().toBe "{ something in here and in  }"
+      cursor().toEqual [0, 27]
+      classListContains('operator-pending-mode').toBe(false)
+      classListContains('normal-mode').toBe(true)

   describe "the 'a<' text object", ->
     beforeEach ->
-      editor.setText("< something in here and in <here> >")
-      editor.setCursorScreenPosition([0, 9])
+      text "< something in here and in <here> >"
+      cursor [0, 9]

     it "applies operators around the current angle brackets in operator-pending mode", ->
-      keydown('d')
-      keydown('a')
-      keydown('<')
-      expect(editor.getText()).toBe ""
-      expect(editor.getCursorScreenPosition()).toEqual [0, 0]
-      expect(editorElement.classList.contains('operator-pending-mode')).toBe(false)
-      expect(editorElement.classList.contains('normal-mode')).toBe(true)
+      keystroke 'da<'
+      text().toBe ""
+      cursor().toEqual [0, 0]
+      classListContains('operator-pending-mode').toBe(false)
+      classListContains('normal-mode').toBe(true)

     it "applies operators around the current angle brackets in operator-pending mode (second test)", ->
-      editor.setCursorScreenPosition([0, 29])
-      keydown('d')
-      keydown('a')
-      keydown('<')
-      expect(editor.getText()).toBe "< something in here and in  >"
-      expect(editor.getCursorScreenPosition()).toEqual [0, 27]
-      expect(editorElement.classList.contains('operator-pending-mode')).toBe(false)
-      expect(editorElement.classList.contains('normal-mode')).toBe(true)
+      cursor [0, 29]
+      keystroke 'da<'
+      text().toBe "< something in here and in  >"
+      cursor().toEqual [0, 27]
+      classListContains('operator-pending-mode').toBe(false)
+      classListContains('normal-mode').toBe(true)

   describe "the 'a[' text object", ->
     beforeEach ->
-      editor.setText("[ something in here and in [here] ]")
-      editor.setCursorScreenPosition([0, 9])
+      text "[ something in here and in [here] ]"
+      cursor [0, 9]

     it "applies operators around the current square brackets in operator-pending mode", ->
-      keydown('d')
-      keydown('a')
-      keydown('[')
-      expect(editor.getText()).toBe ""
-      expect(editor.getCursorScreenPosition()).toEqual [0, 0]
-      expect(editorElement.classList.contains('operator-pending-mode')).toBe(false)
-      expect(editorElement.classList.contains('normal-mode')).toBe(true)
+      keystroke 'da['
+      text().toBe ""
+      cursor().toEqual [0, 0]
+      classListContains('operator-pending-mode').toBe(false)
+      classListContains('normal-mode').toBe(true)

     it "applies operators around the current square brackets in operator-pending mode (second test)", ->
-      editor.setCursorScreenPosition([0, 29])
-      keydown('d')
-      keydown('a')
-      keydown('[')
-      expect(editor.getText()).toBe "[ something in here and in  ]"
-      expect(editor.getCursorScreenPosition()).toEqual [0, 27]
-      expect(editorElement.classList.contains('operator-pending-mode')).toBe(false)
-      expect(editorElement.classList.contains('normal-mode')).toBe(true)
+      cursor [0, 29]
+      keystroke 'da['
+      text().toBe "[ something in here and in  ]"
+      cursor().toEqual [0, 27]
+      classListContains('operator-pending-mode').toBe(false)
+      classListContains('normal-mode').toBe(true)

   describe "the 'a\'' text object", ->
     beforeEach ->
-      editor.setText("' something in here and in 'here' '")
-      editor.setCursorScreenPosition([0, 9])
+      text "' something in here and in 'here' '"
+      cursor [0, 9]

     it "applies operators around the current single quotes in operator-pending mode", ->
-      keydown('d')
-      keydown('a')
-      keydown('\'')
-      expect(editor.getText()).toBe "here' '"
-      expect(editor.getCursorScreenPosition()).toEqual [0, 0]
-      expect(editorElement.classList.contains('operator-pending-mode')).toBe(false)
-      expect(editorElement.classList.contains('normal-mode')).toBe(true)
+      keystroke "da'"
+      text().toBe "here' '"
+      cursor().toEqual [0, 0]
+      classListContains('operator-pending-mode').toBe(false)
+      classListContains('normal-mode').toBe(true)

     it "applies operators around the current single quotes in operator-pending mode (second test)", ->
-      editor.setCursorScreenPosition([0, 29])
-      keydown('d')
-      keydown('a')
-      keydown('\'')
-      expect(editor.getText()).toBe "' something in here and in  '"
-      expect(editor.getCursorScreenPosition()).toEqual [0, 27]
-      expect(editorElement.classList.contains('operator-pending-mode')).toBe(false)
-      expect(editorElement.classList.contains('normal-mode')).toBe(true)
+      cursor [0, 29]
+      keystroke "da'"
+      text().toBe "' something in here and in  '"
+      cursor().toEqual [0, 27]
+      classListContains('operator-pending-mode').toBe(false)
+      classListContains('normal-mode').toBe(true)

   describe "the 'a\"' text object", ->
     beforeEach ->
-      editor.setText("\" something in here and in \"here\" \"")
-      editor.setCursorScreenPosition([0, 9])
+      text '" something in here and in "here" "'
+      cursor [0, 9]

     it "applies operators around the current double quotes in operator-pending mode", ->
-      keydown('d')
-      keydown('a')
-      keydown('""')
-      expect(editor.getText()).toBe 'here" "'
-      expect(editor.getCursorScreenPosition()).toEqual [0, 0]
-      expect(editorElement.classList.contains('operator-pending-mode')).toBe(false)
-      expect(editorElement.classList.contains('normal-mode')).toBe(true)
+      keystroke 'da"'
+      text().toBe 'here" "'
+      cursor().toEqual [0, 0]
+      classListContains('operator-pending-mode').toBe(false)
+      classListContains('normal-mode').toBe(true)

     it "applies operators around the current double quotes in operator-pending mode (second test)", ->
-      editor.setCursorScreenPosition([0, 29])
-      keydown('d')
-      keydown('a')
-      keydown('"')
-      expect(editor.getText()).toBe '" something in here and in  "'
-      expect(editor.getCursorScreenPosition()).toEqual [0, 27]
-      expect(editorElement.classList.contains('operator-pending-mode')).toBe(false)
-      expect(editorElement.classList.contains('normal-mode')).toBe(true)
+      cursor [0, 29]
+      keystroke 'da"'
+      text().toBe '" something in here and in  "'
+      cursor().toEqual [0, 27]
+      classListContains('operator-pending-mode').toBe(false)
+      classListContains('normal-mode').toBe(true)


removing duplicate declaration of page number

--- src/scripts/modules/media/tabs/contents/toc/page.coffee
+++ src/scripts/modules/media/tabs/contents/toc/page.coffee
@@ -11,7 +11,7 @@
     template: template
     templateHelpers:
       page: () -> @content.getPageNumber(@model)
-      url: () -> linksHelper.getPath('contents', {model: @content, page: @model.getPageNumber()})
+      url: () -> linksHelper.getPath('contents', {model: @content})
       editable: () -> @editable

     tagName: 'li'
@@ -26,7 +26,6 @@

       @content = @model.get('book')
       @editable = @content.get('editable')
-      @bookTitle = @content.get('title').substring(0,30)

       # If this is the active page, update the URL bar to the correct page number
       if @model.get('active')


Increase hype decay to 5 minutes

Increase hype decay to 5 minutes

--- scripts/hype.coffee
+++ scripts/hype.coffee
@@ -1,5 +1,5 @@
 module.exports = (robot) ->
-  timeDecay = 60000
+  timeDecay = 5 * 60 * 1000
   hypeIncrement = 15
   maxHype = 100
   total = 0


Update maps.coffee

Update maps.coffee

Adds a dot to the extension to fool (some) clients into showing the image inline

--- scripts/maps.coffee
+++ scripts/maps.coffee
@@ -14,7 +14,7 @@
                 "&size=400x400&maptype=" +
                 mapType +
                 "&sensor=false" +
-                "&format=png" # So campfire knows it's an image
+                "&format=.png" # So campfire knows it's an image
     url      = "http://maps.google.com/maps?q=" +
                escape(location) +
               "&hl=en&sll=37.0625,-95.677068&sspn=73.579623,100.371094&vpsrc=0&hnear=" +


Unit details: sort period subjects alphabetically

--- src/views/unit-details.coffee
+++ src/views/unit-details.coffee
@@ -186,7 +186,7 @@

         _serviceDetailsToPeriods: (services) ->
             periods = _.filter services, (s) -> s.period isnt null
-            sorted = _.sortBy periods, (p) -> p.period[0]
+            sorted = _.sortBy periods, (p) -> [p.period?[0], p.name?[p13n.getLanguage()]]
             iteratee = (cum, s) ->
                 key = "#{s.period[0]}—#{s.period[1]}"
                 cum[key] = cum[key] or []


Support testError and error alert.

Support testError and error alert.

--- views/filter_selector.cjsx
+++ views/filter_selector.cjsx
@@ -4,42 +4,58 @@
 cloneByJson = (o) -> JSON.parse(JSON.stringify(o))

 AlertDismissable = React.createClass
+  getInitialState: ->
+    show: false
+    text: null
+
+  componentWillReceiveProps: (nextProps) ->
+    if @state.text != nextProps.text
+      @setState
+        show: true
+        text: nextProps.text
+
   render: ->
-    if @props.show
-      options = @props.options
-      <Alert onDismiss={@handleAlertDismiss} {...options}>
-        @props.children
-      </Alert>
+    <div>
+     {
+      if @state.show
+        options = @props.options
+        <Alert onDismiss={@handleAlertDismiss} {...options}>
+          {@state.text}
+        </Alert>
+     }
+    </div>

   handleAlertDismiss: ->
-    @props.onDismiss?()
+    @setState
+      show: false
+      text: null

 # PROTOCAL
 #   All menu properties will be accumulated to its children except sub.
 #   See accumulateMenu
 # MENU :=
 #   title:
-#       The text shown in the selection input in its parent
+#       The text shown in the dropdown input in its parent
 #   value:
 #       If exists, this item is a terminate and will return this as raw_value.
 #   sub:
 #       {MENU_ID: MENUITEM}
-#       If exists, this item is a selection input.
+#       If exists, this item is a dropdown input.
 #       Otherwise, this item is a text input.
 #       Use the form of '_abcd' as MENU_ID to distinguish it from menu properties.
 #   applyEnabledFunc:
 #       (path, raw_value) -> Boolean
 #       Return true if the "Apply" button is enabled
-#   testError:
-#       (path, raw_value) -> String | undefined
-#       The raw_value is valid if the input return undefined.
-#       Otherwise, return the error prompt.
-#       Like applyEnabledFunc, but more of runtime check.
 #   preprocess:
 #       (path, raw_value) -> pre_value
 #       Change the raw input to make it easier to store and process afterwards.
 #       Will be called the first time adding this rule.
 #       Result must be JSONisable.
+#   testError:
+#       (path, pre_value) -> String | undefined
+#       The raw_value is valid if the input return undefined.
+#       Otherwise, return the error prompt.
+#       Like applyEnabledFunc, but more of runtime check.
 #   postprocess:
 #       (path, pre_value) -> post_value
 #       Change the value returned by preprocess to make easier to process.
@@ -128,8 +144,8 @@
          '_id':
            title: 'By ship id'
            testError: (path, value) ->
-             if !_ships[value]?
-               'You have no ship with id #{value}'
+             if !_ships?[value]?
+               "You have no ship with id #{value}"
            func: (path, value, record) ->
              record.fleet.concat(record.fleet2 || []).filter(
                (sh) -> sh.id?.toString() == value.toString())
@@ -155,8 +171,9 @@
     nowLastMenu: menuTree['_root']
     filterValue: null
     applyEnabled: false
-
-  handleInputSelectChange: (level, e) ->
+    errorText: null
+
+  handleDropdownChange: (level, e) ->
     path = @state.nowMenuPath[0..level]
     path.push e.target.value
     totalDetails = accumulateMenu path
@@ -166,7 +183,7 @@
       applyEnabled: totalDetails.value?
       filterValue: totalDetails.value

-  handleInputTextChange: (e) ->
+  handleTextChange: (e) ->
     path = @state.nowMenuPath
     nowMenu = @state.nowLastMenu
     value = e.target.value
@@ -178,9 +195,14 @@
     console.log 'nmp', @state.nowMenuPath
     path = @state.nowMenuPath.slice()
     console.log path
-    preprocess = @state.nowLastMenu.preprocess || ((path, value) -> value)
+    menu = @state.nowLastMenu
+    preprocess = menu.preprocess || ((path, value) -> value)
     value = cloneByJson(preprocess(path, @state.filterValue))
-    @props.onAddFilter? path, value
+    if (errorText = menu.testError path, value)?
+      console.log errorText
+      @setState {errorText}
+    else
+      @props.onAddFilter? path, value

   render: ->
     <Panel collapsible defaultExpanded header="Filter">
@@ -202,7 +224,7 @@
               # A selection input
               if nowMenu.sub?
                 <Input type='select'
-                  onChange={@handleInputSelectChange.bind(this, level)}
+                  onChange={@handleDropdownChange.bind(this, level)}
                   {...options} >
                   <option value='none'>Select...</option>
                   {
@@ -215,10 +237,16 @@

               # A text input
               else
-                <Input type="text" onChange={@handleInputTextChange}
+                <Input type="text" onChange={@handleTextChange}
                   {...options} />
          }
         </ListGroup>
+        {
+          <AlertDismissable text={@state.errorText} options={
+            dismissAfter: 4000
+            bsStyle: 'warning'
+          }/>
+        }
         {
           lastMenu = @state.nowLastMenu
           console.log 'last', lastMenu
@@ -241,6 +269,9 @@
       menu.func.bind(this, path, value)
     (record) ->
       funcs.every (f) -> f(record)
+
+  handleAddError: (errorText) ->
+    @set

   handleAddFilter: (path, value) ->
     nowFilterList = @state.nowFilterList


start move to search endpoint for languages


Switch from Sass gem to node-sass

Switch from Sass gem to node-sass

--- src/sass.plugin.coffee
+++ src/sass.plugin.coffee
@@ -4,6 +4,7 @@
   safeps = require('safeps')
   fs = require('fs')
   {TaskGroup} = require('taskgroup')
+  sass = require('node-sass')

   # Define Plugin
   class SassPlugin extends BasePlugin
@@ -13,22 +14,13 @@
     # Plugin config
     # Only on the development environment use expanded, otherwise use compressed
     config:
-      sassPath: null
-      scssPath: null
-      compass: null
       debugInfo: false
-      sourcemap: false
-      outputStyle: 'compressed'
-      requireLibraries: null
+      #sourcemap: false
+      #outputStyle: 'compressed'
       renderUnderscoreStylesheets: false
       environments:
         development:
-          outputStyle: 'expanded'
-
-    # Locale
-    locale:
-      sassNotInstalled: 'SASS does not appear to be available on your system'
-      scssNotInstalled: 'SCSS does not appear to be available on your system'
+          debugInfo: 'normal'

     # Generate Before
     generateBefore: (opts,next) ->
@@ -37,21 +29,6 @@

       # Group
       tasks = new TaskGroup().setConfig(concurrency:0).once('complete',next)
-
-      # Determine if compass is installed
-      unless config.compass?
-        tasks.addTask (complete) ->
-          safeps.getExecPath 'compass', (err,path) ->
-            config.compass = path?
-            return complete()
-
-      # Determine sass executable path
-      ['sass','scss'].forEach (thing) ->
-        unless config[thing+'Path']?
-          tasks.addTask (complete) ->
-            safeps.getExecPath thing, (err,path) ->
-              config[thing+'Path'] = path ? false
-              return complete()

       # Fire tasks
       tasks.run()
@@ -75,7 +52,6 @@
     render: (opts,next) ->
       # Prepare
       config = @config
-      locale = @locale
       {inExtension,outExtension,file} = opts

       # If SASS/SCSS then render
@@ -83,42 +59,30 @@
         # Fetch useful paths
         fullDirPath = file.get('fullDirPath')

+        # Define callback fn
+        callback = (css) ->
+          opts.content = css
+          return next()
+
         # Prepare the command and options
-        commandOpts = {stdin:opts.content}
-        execPath = config[inExtension+'Path']
+        commandOpts =
+          success: callback
+          error: (err)->
+            return next(new Error(err))

-        # Check if we have the executable for that extension
-        return next(new Error(locale[inExtension+'NotInstalled']))  unless execPath
-
-        # Build our command
-        if config.sourcemap
-          command = [execPath, file.attributes.fullPath + ':' + file.attributes.outPath, '--no-cache', '--update', '--sourcemap']
-          commandOpts = {}
+        if fullDirPath
+          commandOpts.includePaths = [fullDirPath]
+        if config.debugInfo
+          commandOpts.sourceComments = config.debugInfo
+          commandOpts.file = file.attributes.fullPath
         else
-          command = [execPath, '--stdin', '--no-cache']
-        if fullDirPath
-          command.push('--load-path')
-          command.push(fullDirPath)
-        if config.compass
-          command.push('--compass')
-        if config.debugInfo
-          command.push('--debug-info')
-        if config.outputStyle
-          command.push('--style')
-          command.push(config.outputStyle)
-        if config.requireLibraries
-          for name in config.requireLibraries
-            command.push('--require')
-            command.push(name)
+          commandOpts.data = opts.content
+        # outputStyle is not currently supported by libsass
+        #if config.outputStyle
+          #commandOpts.outputStyle = config.outputStyle

         # Spawn the appropriate process to render the content
-        safeps.spawn command, commandOpts, (err,stdout,stderr,code,signal) ->
-          return next(err)  if err
-          if config.sourcemap
-            opts.content = fs.readFileSync(file.attributes.outPath).toString()
-          else
-            opts.content = stdout
-          return next()
+        sass.render commandOpts

       else
         # Done, return back to DocPad


spanish subdomains

spanish subdomains

Former-commit-id: ea6905d695757727b97a3fcff7ad444036182ccd [formerly b70f57e6e26f586a7a73c731d45300483c577263] [formerly fd993a40cd53fbb8a769d5ac67d41f4eb85b4f13 [formerly 0720c7f5e9e49f6c29a73cbd6aee1af34c42f363]] [formerly f5d08e4c36a48ea305e5422d0e297a14bf0f723e [formerly 6735ff288db4e08a01153230672f1c3cb77006f5] [formerly 81e9b0fffa991dfceb09042c8630d2d2b9f40200 [formerly 81e9b0fffa991dfceb09042c8630d2d2b9f40200 [formerly 198d0058d01e66e20d44e1db92a734dd400ac6e9]]]]
Former-commit-id: f2cf6fef8d69da6f90d64670d57e3fbc11018e26 [formerly bf3dc9a05e8cd5dc8c90ef6217d424d923543bf2] [formerly b1195e138df885594dbcb24d13ddc30a5231f931 [formerly 7cd1bff6030f1e8c68c845f8ae0684233e80c15a]]
Former-commit-id: 24fcbf83565b701c10dc1b3c894a2a5efb2f3da2 [formerly 422e60e909e43ffe1ec020f6d36e3f8c5626474f]
Former-commit-id: 73f4639f6e8fa60c7c1b311251bb50899a2b6db0
Former-commit-id: f4b2eed4c7ecb449cbf2ce0dc8906b7e7fd1db8d

--- @client/customizations.coffee
+++ @client/customizations.coffee
@@ -541,15 +541,20 @@
   slider_pole_labels : effective_ineffective

-customizations['villagb'] = customizations['citysens'] = customizations['iniciativasciudadanas'] =
-  lang: 'spa'
-  point_labels : pros_contras
-  slider_pole_labels : desacuerdo_acuerdo
-  homie_histo_title: "Opiniones"
-  show_slider_feedback: false
-
-
-customizations['alcala'] = _.extend {}, customizations['citysens'],
+
+spanish = ['alcala', 'villagb', 'citysens', 'iniciativasciudadanas', \
+           'movilidadcdmx']
+
+for spa in spanish
+  customizations[spa] =
+    lang: 'spa'
+    point_labels : pros_contras
+    slider_pole_labels : desacuerdo_acuerdo
+    homie_histo_title: "Opiniones"
+    show_slider_feedback: false
+
+
+customizations['alcala'] = _.extend {}, customizations['alcala'],
   HomepageHeader : ReactiveComponent
     displayName: 'HomepageHeader'


cleanup

cleanup

--- examples/node/notebook.coffee
+++ examples/node/notebook.coffee
@@ -1,7 +1,6 @@
 fs = require 'fs'
 lead = require 'lead.js'
 lead.enable_codemirror()
-#console.log lead.require 'cm/codemirror'
 notebook = lead.require 'notebook'
 notebook.init_codemirror()

@@ -14,4 +13,4 @@
   setTimeout ->
     console.log nb.$document.html()
     process.exit()
-  , 5000
+  , 500


test for not activating channel

test for not activating channel

--- spec/coffeescripts/danthes_spec.js.coffee
+++ spec/coffeescripts/danthes_spec.js.coffee
@@ -85,6 +85,7 @@
   it "adds a signed channel to subscribe later", ->
     pub.fayeClient = 'string'
     [faye, options] = signToChannel('somechannel')
+    expect(pub.subscriptions.somechannel.activated).toBeUndefined()
     expect(pub.subscriptions.somechannel).toBeDefined()
     expect(pub.subscriptions.somechannel.opts.signature).toEqual('1234567890')
     expect(pub.subscriptions.somechannel.opts.timestamp).toEqual(1234567890)


update gm test for new file input

update gm test for new file input


Remove unneeded attachToDom() call

Remove unneeded attachToDom() call

--- src/packages/command-logger/spec/command-logger-spec.coffee
+++ src/packages/command-logger/spec/command-logger-spec.coffee
@@ -9,7 +9,6 @@
     atom.loadPackage 'command-logger'
     editor = rootView.getActiveEditor()
     commandLogger = CommandLogger.instance
-    rootView.attachToDom()

   afterEach ->
     rootView.deactivate()


Renaming variable and function

Renaming variable and function

--- src/scripts/modules/role-acceptance/role-acceptances.coffee
+++ src/scripts/modules/role-acceptance/role-acceptances.coffee
@@ -29,7 +29,7 @@

     events:
       'click .submit': 'acceptOrRejectRoles'
-      'click input[type="radio"]': 'acceptedOrRejectedValue'
+      'click input[type="radio"]': 'onClickSetHasAcceptedForRole'

     initialize: () ->
@@ -41,7 +41,7 @@
       @showAcceptedAndRejected()

-    acceptedOrRejectedValue: (e) ->
+    onClickSetHasAcceptedForRole: (e) ->
       model = @collection.at(0)
       current = $(e.currentTarget)
       roles = model?.get('roles')
@@ -57,24 +57,24 @@

       _.each rows, (row) ->
         requestedRole = $(row).attr('data-requested-role')
-        hasAccepted = _.find roles, (role) -> role.role is requestedRole
+        userPermissions = _.find roles, (role) -> role.role is requestedRole
         selectedRow = $(row).closest('tr')

-        if hasAccepted.role is requestedRole
-          if hasAccepted.hasAccepted is null
+        if userPermissions.role is requestedRole
+          if userPermissions.hasAccepted is null
             selectedRow.find(':radio[value=true]').attr('checked', 'checked')
-          else if hasAccepted.hasAccepted is true
+          else if userPermissions.hasAccepted is true
             selectedRow.find(':radio[value=true]').prop({'checked': 'checked'})
             selectedRow.find(':radio').prop({'disabled': 'disabled'})
             $(row).addClass('gray')
             $('.btn').prop('disabled', true)
-          else if hasAccepted.hasAccepted is false
+          else if userPermissions.hasAccepted is false
             selectedRow.find(':radio[value=false]').prop({'checked': 'checked'})
             selectedRow.find(':radio').prop({'disabled': 'disabled'})
             $(row).addClass('gray')
             $('.btn').prop('disabled', true)

-
+
     acceptLicense: (model) ->
       if $('.licenseCheckbox').is(':checked')
         model.set('hasAcceptedLicense', true)


unit tests are passing again with fixed precision svg units

--- test/plotter_plot_test.coffee
+++ test/plotter_plot_test.coffee
@@ -3,6 +3,9 @@
 GerberReader = require '../src/gerber-reader'
 GerberParser = require '../src/gerber-parser'
 fs = require 'fs'
+
+# svg coord factor
+factor = require('../src/svg-coord').factor

 describe 'the plot method of the Plotter class', ->
   it 'should plot example 1 from the gerber spec', ->
@@ -14,16 +17,16 @@
         {
           path: {
             d: [
-              'M', 0,       0
-              'L', 500000,  0
-              'L', 500000,  500000
-              'L', 0,       500000
-              'L', 0,       0
-              'M', 600000,  0
-              'L', 1100000, 0
-              'L', 1100000, 500000
-              'L', 600000,  500000
-              'L', 600000,  0
+              'M', 0*factor,  0*factor
+              'L', 5*factor,  0*factor
+              'L', 5*factor,  5*factor
+              'L', 0*factor,  5*factor
+              'L', 0*factor,  0*factor
+              'M', 6*factor,  0*factor
+              'L', 11*factor, 0*factor
+              'L', 11*factor, 5*factor
+              'L', 6*factor,  5*factor
+              'L', 6*factor,  0*factor
             ]
           }
         }


Load chat messages in reasonable sized chunks

--- services/web/public/coffee/ide/chat/services/chatMessages.coffee
+++ services/web/public/coffee/ide/chat/services/chatMessages.coffee
@@ -3,7 +3,7 @@
 ], (App) ->
 	App.factory "chatMessages", ($http, ide) ->
 		MESSAGES_URL = "/project/#{ide.project_id}/messages"
-		MESSAGE_LIMIT = 3
+		MESSAGE_LIMIT = 50
 		CONNECTED_USER_URL = "/project/#{ide.project_id}/connected_users"

 		chat = {


only run participate test for sign up if user has no projects

--- services/web/public/coffee/main/project-list/project-list.coffee
+++ services/web/public/coffee/main/project-list/project-list.coffee
@@ -11,12 +11,14 @@
 		$scope.predicate = "lastUpdated"
 		$scope.reverse = true

-
-		sixpack.participate 'first_sign_up', ['default', 'minimial'], (chosenVariation, rawResponse)->
-			$scope.first_sign_up = chosenVariation
-			$timeout () ->
-				recalculateProjectListHeight()
-			, 10
+		if $scope.projects.length > 0
+			$scope.first_sign_up = "default"
+		else
+			sixpack.participate 'first_sign_up', ['default', 'minimial'], (chosenVariation, rawResponse)->
+				$scope.first_sign_up = chosenVariation
+				$timeout () ->
+					recalculateProjectListHeight()
+				, 10

 		recalculateProjectListHeight = () ->
 			topOffset = $(".project-list-card")?.offset()?.top


Make once work with gestures

Make once work with gestures

--- framer/GestureManager.coffee
+++ framer/GestureManager.coffee
@@ -10,9 +10,22 @@
 	constructor: (@layer) ->
 		@_manager = Hammer(@layer._element)

-	addListener: (eventName, listener) ->
-
+
+	once: (eventName, listener) =>
 		super(eventName, listener)
+		@_addListener eventName, (e) =>
+			@removeListener(eventName, listener)
+			listener.apply(@layer, [e, @layer])
+
+	addListener: (eventName, listener) =>
+		super(eventName, listener)
+		@_addListener(eventName, listener)
+
+	removeListener: (eventName, listener) ->
+		super(eventName, listener)
+		@_removeListener(eventName, listener)
+
+	_addListener: (eventName, listener) ->

 		# Make sure we have a hammer instance and layer listeners enabled
 		@layer.ignoreEvents = false
@@ -37,8 +50,7 @@

 		@_manager.on(eventName, listener._actual)

-	removeListener: (eventName, listener) ->
-		super(eventName, listener)
+	_removeListener: (eventName, listener) ->
 		@_manager.off(eventName, listener._actual)

 	destroy: ->


add some more fields related to public paths...

--- src/smc-util/db-schema.coffee
+++ src/smc-util/db-schema.coffee
@@ -890,6 +890,19 @@
         disabled    :
             type : 'boolean'
             desc : 'if true then disabled'
+        created :
+            type : 'timestamp'
+            desc : 'when this path was created'
+        last_edited :
+            type : 'timestamp'
+            desc : 'when this path was last edited'
+        last_saved :
+            type : 'timestamp'
+            desc : 'when this path was last saved (or deleted if disabled) by manage-storage'
+        counter :
+            type : 'number'
+            desc : 'the number of times this public path has been accessed'
+
     pg_indexes : ['project_id']
     user_query:
         get :


locale name

locale name

--- src/store/countryStore.coffee
+++ src/store/countryStore.coffee
@@ -25,15 +25,21 @@
     store.addCountry countries
     store

+localeName =
+    en_US: 'English'
+    th_TH: 'ภาษาไทย'
+    zh_HK: '中文'
+
 countries = [
     {
         key: 'th'
-        locale: ['en_US', 'th_TH']
+        locale: ['en_US', 'th_TH'].map (l)-> localeName[l]
         en_US: 'Thailand'
     }
     {
         key: 'hk'
-        locale: ['en_US', 'zh_HK']
+        locale: ['en_US', 'zh_HK'].map (l)-> localeName[l]
         en_US: 'Hong Kong'
+        name: '中文'
     }
 ]


fix tests

fix tests

--- components/filter/artworks/test/view.coffee
+++ components/filter/artworks/test/view.coffee
@@ -11,6 +11,7 @@
     benv.setup =>
       benv.expose { $: benv.require 'jquery' }
       Backbone.$ = $
+      sinon.stub _, 'defer'
       FilterArtworksView = benv.require resolve __dirname, '../view'
       for klass in ['ArtworkColumnsView', 'FilterSortCount', 'FilterFixedHeader', 'FilterRouter']
         @[klass] = (opts) -> _.extend @, opts
@@ -27,6 +28,7 @@

   afterEach ->
     Backbone.sync.restore()
+    _.defer.restore()
     benv.teardown()

   describe '#initialize', ->


Refactor video caption to use binary search

--- templates/coffee/src/modules/video/video_caption.coffee
+++ templates/coffee/src/modules/video/video_caption.coffee
@@ -1,6 +1,5 @@
 class VideoCaption
   constructor: (@player, @youtubeId) ->
-    @index = []
     @render()
     @bind()

@@ -29,6 +28,12 @@
     @$('.subtitles').css maxHeight: @$('.video-wrapper').height() - 5
     @fetchCaption()

+  fetchCaption: ->
+    $.getWithPrefix @captionURL(), (captions) =>
+      @captions = captions.text
+      @start = captions.start
+      @renderCaption()
+
   renderCaption: ->
     container = $('
')

@@ -44,28 +49,28 @@
     @$('.subtitles').prepend($('
').height(@topSpacingHeight()))
       .append($('
').height(@bottomSpacingHeight()))

-  fetchCaption: ->
-    $.getWithPrefix @captionURL(), (captions) =>
-      @captions = captions.text
-      @start = captions.start
-      for index in [0...captions.start.length]
-        for time in [captions.start[index]..captions.end[index]]
-          @index[time] ||= []
-          @index[time].push(index)
-      @renderCaption()
+  search: (time) ->
+    min = 0
+    max = @start.length - 1
+
+    while min < max
+      index = Math.ceil((max + min) / 2)
+      if time < @start[index]
+        max = index - 1
+      if time >= @start[index]
+        min = index
+
+    return min

   onUpdatePlayTime: (event, time) =>
     # This 250ms offset is required to match the video speed
     time = Math.round(Time.convert(time, @player.currentSpeed(), '1.0') * 1000 + 250)
-    newIndex = @index[time]
+    newIndex = @search time

     if newIndex != undefined && @currentIndex != newIndex
       if @currentIndex
-        for index in @currentIndex
-          @$(".subtitles li[data-index='#{index}']").removeClass('current')
-
-      for index in newIndex
-        @$(".subtitles li[data-index='#{newIndex}']").addClass('current')
+        @$(".subtitles li.current").removeClass('current')
+      @$(".subtitles li[data-index='#{newIndex}']").addClass('current')

       @currentIndex = newIndex
       @scrollCaption()


Add additional controls to the index page Map

--- app/assets/javascripts/map.js.coffee
+++ app/assets/javascripts/map.js.coffee
@@ -7,6 +7,8 @@
   mapOptions =
     zoom: 2,
     scrollwheel: false,
+    draggable: true,
+    panControl: true,
     mapTypeId: google.maps.MapTypeId.ROADMAP,
     center: map_center


Add strut session initial data, for fixing first-load bug

--- app/assets/javascripts/ppt.js.coffee
+++ app/assets/javascripts/ppt.js.coffee
@@ -18,6 +18,7 @@
                #]
       #}
     #]
+  localStorage['Strut_sessionMeta'] = '{"generator_index":0,"lastPresentation":"presentation-unnamed.strut"}'
   localStorage['strut-presentation-unnamed.strut'] = JSON.stringify(pptData)
   $('#editor').attr('src', '/editor/')


split out setRemoteDescription

split out setRemoteDescription

--- app/assets/javascripts/webrtc.coffee
+++ app/assets/javascripts/webrtc.coffee
@@ -286,44 +286,46 @@
       console.log('[WARN] - could not locate peer for peer id: '+peer_id)
       console.log('this must be the offer...')
       @doGetUserMedia( =>
-        peer = @addPeer({uuid:peer_id, sendOffer:false})
+        @addPeer({uuid:peer_id, sendOffer:false})
         # now we have the peer so lets try this again.
-        @sessionDescription(config)
+        @setRemoteDescription(config)
       )
     else
-      remote_description = config.session_description
-      console.log(config.session_description)
-
-      desc = new RTCSessionDescription(remote_description)
-      console.log("Description Object: ", desc)
-
-
-      peer.setRemoteDescription(desc, =>
-        console.log("setRemoteDescription succeeded")
-        if (remote_description.type == "offer")
-          console.log("Creating answer");
-          peer.createAnswer((local_description) =>
-            console.log("Answer description is: ", local_description);
-            peer.setLocalDescription(local_description
-            , =>
-              @send('relay',
-                {
-                  'type':'sessionDescription'
-                  'actors': [peer_id],
-                  'session_description': local_description
-                }
-              )
-              console.log("Answer setLocalDescription succeeded")
-            , ->
-              alert("Answer setLocalDescription failed!")
+      @setRemoteDescription(config, peer, peer_id)
+
+  setRemoteDescription: (config, peer, peer_id) ->
+    remote_description = config.session_description
+    console.log(config.session_description)
+
+    desc = new RTCSessionDescription(remote_description)
+    console.log("Description Object: ", desc)
+
+    peer.setRemoteDescription(desc, =>
+      console.log("setRemoteDescription succeeded")
+      if (remote_description.type == "offer")
+        console.log("Creating answer");
+        peer.createAnswer((local_description) =>
+          console.log("Answer description is: ", local_description);
+          peer.setLocalDescription(local_description
+          , =>
+            @send('relay',
+              {
+                'type':'sessionDescription'
+                'actors': [peer_id],
+                'session_description': local_description
+              }
             )
-          , (error) ->
-            console.log("Error creating answer: ", error)
-            console.log(peer)
-          ,sdpConstraints)
-      ,(error) ->
-        console.log("setRemoteDescription error: ", error)
-      )
+            console.log("Answer setLocalDescription succeeded")
+          , ->
+            alert("Answer setLocalDescription failed!")
+          )
+        , (error) ->
+          console.log("Error creating answer: ", error)
+          console.log(peer)
+        ,sdpConstraints)
+    ,(error) ->
+      console.log("setRemoteDescription error: ", error)
+    )

@@ -369,13 +371,13 @@

   doGetUserMedia: (callback) ->
-    if( @localStream != null)
+    if( @localStream? )
       if( callback )
         callback()
-        return
-    constraints = {"audio": true, "video": {"mandatory": {}, "optional": []}};
+      return
+    constraints = {"audio": true, "video": {"mandatory": {}, "optional": []}}
     try
-      console.log("Requested access to local media with mediaConstraints:\n \"" + JSON.stringify(constraints) + "\"");
+      console.log("Requested access to local media with mediaConstraints:\n \"" + JSON.stringify(constraints) + "\"")
       getUserMedia(constraints, (stream) =>
         @localStream = stream
         local_media = if @USE_VIDEO


Fix bug with LinkedEvents v1 compatiblity

--- src/views/search-input.coffee
+++ src/views/search-input.coffee
@@ -97,8 +97,9 @@
                 source: search.linkedeventsEngine.ttAdapter(),
                 displayKey: (c) -> c.name[p13n.getLanguage()]
                 templates:
-                    suggestion: (ctx) -> jade.template 'typeahead-suggestion', ctx
-
+                    suggestion: (ctx) ->
+                        ctx.object_type = 'event'
+                        jade.template 'typeahead-suggestion', ctx

             # A hack needed to ensure the header is always rendered.
             fullDataset =
@@ -131,9 +132,11 @@
             # TODO: re-enable in a compatible way
             #$('.search-container input').blur()
             model = null
-            objectType = data.object_type
+            objectType = data.object_type or data["@type"]
             if objectType == 'address'
                 return
+            if objectType == "Event/LinkedEvent"
+                objectType = 'event'
             @$searchEl.typeahead 'val', ''
             app.request 'clearSearchResults', navigate: false
             $('.search-container input').val('')


Fix for tutorial show/hide

Fix for tutorial show/hide

--- app/controllers/classify_page.coffee
+++ app/controllers/classify_page.coffee
@@ -58,7 +58,9 @@
     else
       if @tutorial?
         @startTutorial()
-        @tutorial.hide() unless @el.hasClass 'active'
+        unless @el.hasClass 'active'
+          @tutorial.dialog.close()
+          @tutorial.dialog.el.css display: 'none'
       else
         @selectNextSubject()

@@ -99,11 +101,11 @@
   activate: ->
     super
     html.addClass 'on-classify'
-    @tutorial.show() if Subject.current?.metadata.tutorial
+    @tutorial.dialog.open() if Subject.current?.metadata.tutorial

   deactivate: ->
     super
     html.removeClass 'on-classify'
-    @tutorial.hide() if Subject.current?.metadata.tutorial
+    @tutorial.dialog.close() if Subject.current?.metadata.tutorial

 module.exports = ClassifyPage


Fixed bug where arrangedContent did not recompute at init

--- packages/emberella/lib/controllers/multi_array_controller.coffee
+++ packages/emberella/lib/controllers/multi_array_controller.coffee
@@ -23,9 +23,11 @@
 ###
 Emberella.MultiArrayController = Ember.ArrayController.extend
   init: ->
-    ret = @_super()
     @_subArraysDidChange()
-    ret
+    Ember.run.scheduleOnce('sync', @, ->
+      @notifyPropertyChange 'subArrays'
+    )
+    @_super()

   ###
     An array of strings describing the names of array controllers to include
@@ -53,8 +55,7 @@
         children: controller
       })
     ))
-  .property('content', 'sortProperties.@each', 'needs.@each', 'subArrays.@each')
-
+  .property('content', 'sortProperties.@each', 'subArrays.@each')

   ###
     Assembles array of objects contained in this mixed array controller and all
@@ -80,7 +81,6 @@

     flatten arrangedContent

-
   ###
     @private


FIX: Ensure a single teaching period get is mapped with active func

--- src/app/api/models/teaching-period.coffee
+++ src/app/api/models/teaching-period.coffee
@@ -11,8 +11,8 @@
     unless tp.name?
       tp.name = () ->
         "#{tp.period} #{tp.year}"
-      tp.active = () ->
-        moment(tp.active_until).diff(moment()) > 0
+    tp.active = () ->
+      moment(tp.active_until).diff(moment()) > 0
     tp

   TeachingPeriod = {
@@ -53,7 +53,9 @@
     get: (id, onSuccess, onFailure) ->
       resource.get( {id: id}
         (success) ->
-          result = _.find data.loadedPeriods, (tp) -> tp.id == id
+          success = injectFunctionalityInTeachingPeriod(success)
+          result = _.find data.loadedPeriods, (tp) ->
+            tp.id == id
           # Fetched teaching period details... update teaching period in loadedPeriods
           if result?
             _.extend result, success


attempt to fix filters select

attempt to fix filters select

--- js/index.coffee
+++ js/index.coffee
@@ -36,20 +36,20 @@
         @filter_names.push {value: d, color: 'color-' + i }
         i += 1

-    filters = []
+    @filters = []

     # populate the filters from the dataset
     @data.forEach (d) =>
       $.each d, (k, v) =>
         if k != 'id' && k != 'name'
           filter_obj = {filter: k, value: v}
-          filter_exists = $.grep filters, (e) =>
+          filter_exists = $.grep @filters, (e) =>
             return e.filter == k && e.value == v
           if filter_exists.length == 0
-            filters.push(filter_obj)
+            @filters.push(filter_obj)

     # add the filters to the select
-    d3.select("#filter-select").selectAll('option').data(filters).enter()
+    d3.select("#filter-select").selectAll('option').data(@filters).enter()
       .append("option")
       # unfortunately value is the only thing passed to select2
       # so we have to hack together this string param
@@ -67,6 +67,9 @@
           val = e.added.id.split(':')
           this.add_nodes(val[0], val[1])
           this.add_filter(val[0], val[1])
+
+#          @filters = $.grep(@filters, (e) => e.filter != val[0] || e.value != val[1])
+#          d3.select("#filter-select").selectAll('option').data(@filters).exit().remove()
     )

   add_filter: (field, val) =>


rename VM to CircleVM on outer; prevents onerror from triggering setErrorMessage

--- js/outer.coffee
+++ js/outer.coffee
@@ -12,7 +12,7 @@
   constructor: ->
     @ab = (new ABTests(ab_test_definitions)).ab_tests

-window.VM = new CircleViewModel
+window.CircleVM = new CircleViewModel

 circle = $.sammy "body", ->

@@ -32,7 +32,7 @@
       # Fetch page-specific libraries
       @lib() if @lib?

-      ko.applyBindings(VM)
+      ko.applyBindings(CircleVM)

     render: (cx) =>
       $("body").attr("id","#{@name}-page").html HAML['header'](renderContext)


fix artwork spec

fix artwork spec

--- apps/artwork/test/client/view.coffee
+++ apps/artwork/test/client/view.coffee
@@ -5,6 +5,7 @@
 sinon           = require 'sinon'
 { resolve }     = require 'path'
 { fabricate }   = require 'antigravity'
+{ stubChildClasses }  = require '../../../../test/helpers/stubs'

 Artist        = require '../../../../models/artist'
 Artwork       = require '../../../../models/artwork'
@@ -43,13 +44,15 @@
       artist: @artist
       artwork: @artwork
     }, =>
-      @ArtworkView = benv.requireWithJadeify(
+      @ArtworkView = mod = benv.requireWithJadeify(
         (resolve __dirname, '../../client/view'),
         ['detailTemplate', 'auctionPlaceholderTemplate']
       )
       @ArtworkView.__set__ 'analytics', { abTest: sinon.stub(), delta: sinon.stub(), track: { click: sinon.stub() } }
       @ArtworkView.__set__ 'ShareView', (@shareViewStub = sinon.stub())
       @ArtworkView.__set__ 'acquireArtwork', (@acquireArtworkStub = sinon.stub())
+
+      stubChildClasses mod, @, ['BlurbView'], []

       @artworkColumnsView = { appendArtworks: sinon.stub() }
       @ArtworkColumnsView = sinon.stub()


Arrange code format

Arrange code format

--- source/mylist_atom.coffee
+++ source/mylist_atom.coffee
@@ -7,11 +7,18 @@

     @b = $(@xml)

-    @title = @b.find('title').eq(0).text().substring("マイリスト　".length).slice(0, -("‐ニコニコ動画".length))
+    @title    = @b.find('title').eq(0)
+                  .text()
+                  .substring("マイリスト　".length)
+                  .slice(0, -("‐ニコニコ動画".length))
+
     @subtitle = @b.find('subtitle').text()
-    @mylistId = Number @b.find('link').eq(0).attr('href').split('/')[4]
-    @updated = @b.find('updated').eq(0).text()
-    @author = @b.find('author name').text()
+
+    @mylistId = Number @b
+                  .find('link').eq(0)
+                  .attr('href').split('/')[4]
+
+    @updated  = @b.find('updated').eq(0).text()
+    @author   = @b.find('author name').text()

     delete @b
-


Cosmetic change.

Cosmetic change.

--- test/unit/servicesSpec.coffee
+++ test/unit/servicesSpec.coffee
@@ -4,7 +4,7 @@
     # Fake the services module.
     angular.mock.module('qiprofile.services')

-  describe 'The Image Service', ->
+  describe 'Image Service', ->
     # The qiprofile Image factory.
     Image = null
     # The mock Angular $http service provider.


removed temp files

removed temp files


Add 'this course has no x' row to lists

Add 'this course has no x' row to lists

--- app/assets/javascripts/components/common/list.cjsx
+++ app/assets/javascripts/components/common/list.cjsx
@@ -24,6 +24,16 @@
       )
       className = @props.table_key + ' list'

+    elements = @props.elements
+    if elements.length == 0
+      elements = (
+

+
+            This course has no {@props.table_key}
+
+

+      )
+

@@ -37,7 +47,7 @@
         enterTimeout={500}
         leaveTimeout={500}
       >
-        {@props.elements}
+        {elements}
       </TransitionGroup>

 )


Change default sort to character_sum for Articles

--- app/assets/javascripts/stores/article_store.coffee
+++ app/assets/javascripts/stores/article_store.coffee
@@ -1,8 +1,7 @@
 StockStore = require './stock_store.coffee'

 ArticleStore = new StockStore(
-  sortKey: 'title'
-  sortAsc: true
+  sortKey: 'character_sum'
   descKeys:
     character_sum: true
     view_count: true


リファクタリング

リファクタリング

--- lib/lamjet_command.coffee
+++ lib/lamjet_command.coffee
@@ -141,9 +141,7 @@
     config = null
     return Promise.resolve()
       .then (result)-> self.configuration(self.stdout, self.stdin, defaultConfig)
-      .then (result)->
-        # console.log(JSON.stringify({config: result}, null, 2))
-        config = result
+      .then (result)-> config = result
       .then (result)-> self.makePackageJson(config)
       .then (result)-> self.makeAwsLambdaConfigJs(config)
       .then (result)-> self.copyTemplate("gitignore", ".gitignore")
@@ -157,7 +155,6 @@
     if self.argv[2] == "init"
       self.init()
         .then (result)->
-          # self.print(JSON.stringify({then: result}, null, 2))
           self.print("initialized")
         .catch (result)->
           self.print(result)


Let's try not to break so many things.

Let's try not to break so many things.

--- lib/linter-crystal.coffee
+++ lib/linter-crystal.coffee
@@ -21,8 +21,6 @@

   constructor: (@editor)->
     super(@editor)
-    @cmdListen = atom.config.observe 'linter-crystal.command', =>
-      @cmd = atom.config.get 'linter-crystal.command'
     @lintLiveListen = atom.config.observe 'linter-crystal.liveLinting', =>
       @lintLive = atom.config.get 'linter-crystal.liveLinting'
     @colorListen = atom.config.observe 'linter-crystal.colorOutput', =>
@@ -31,6 +29,7 @@
       @buildOutput = atom.config.get 'linter-crystal.buildArtifacts'

   lintFile: (filePath, callback) ->
+    @cmd = atom.config.get 'linter-crystal.command'
     {command, args} = @getCmdAndArgs(filePath)
     unless @colorBuild
       @cmd = "#{@cmd} --no-color"
@@ -44,7 +43,6 @@
       console.log "linter-crystal running command: #{@cmd}"

   destroy: ->
-    @cmdListen.dispose()
     @lintLiveListen.dispose()
     @colorListen.dipose()
     @buildOutputListen.dispose()


removed console.log

removed console.log

--- lib/models/vehicle.coffee
+++ lib/models/vehicle.coffee
@@ -54,7 +54,6 @@
     @get('journeys').pushObject(journey)
     journey
   createStop: (message) ->
-    console.log "createing stop", message.get('usn')
     stop = Sensor.Stop.create arriveMessage: message
     @get('_stops').pushObject stop
     stop


Almost totally correct cursor behavior

Almost totally correct cursor behavior

--- lib/views/terminal.coffee
+++ lib/views/terminal.coffee
@@ -17,6 +17,9 @@

     @term.open(this.get(0))
     #@term.write('Connecting...\r')
+
+    ipc.on 'remote-open-event', (file) =>
+      @term.blur()

     if !!process.platform.match(/darwin/)
       this.on 'keydown', (e) =>
@@ -44,11 +47,8 @@
     #@on 'mousedown', '.terminal-view-resize-handle', (e) =>
       #@resizeStarted(e)

-    #$('.terminal').on 'focus', =>
-      #@term.focus()
-
-    #$('.terminal').on 'blur', =>
-      #@term.blur()
+    $('.terminal').on 'focus', (e) =>
+      @term.focus()

     @term.on 'data', (data) =>
       ipc.send 'terminal-data', data


Exclude linting on files prefixed with 'vendor-'

--- src/tasks/_util.coffee
+++ src/tasks/_util.coffee
@@ -44,9 +44,9 @@
 	files.inline          = "#{dir.inline}/**/*.{gif,jpg,png,svg}"
 	files.raw             = "#{dir.raw}/**/*"
 	files.scripts         = "#{dir.scripts}/**/*.js"
-	files.scripts_lint    = [files.bundles_scripts, files.scripts, "!#{dir.scripts_nolint}/**/*"]
+	files.scripts_lint    = [files.bundles_scripts, files.scripts, "!#{dir.scripts_nolint}/**/*", "!#{dir.scripts}/**/vendor-*"]
 	files.styles          = "#{dir.styles}/**/*.scss"
-	files.styles_lint     = [files.bundles_styles, files.styles, "!#{dir.styles_nolint}/**/*"]
+	files.styles_lint     = [files.bundles_styles, files.styles, "!#{dir.styles_nolint}/**/*", "!#{dir.styles}/**/vendor-*"]
 	files.templates       = "#{dir.templates}/**/*.jshtml"
 	files.bower_scripts   = "#{dir.bower}/**/*.js"
 	files.bower_styles    = "#{dir.bower}/**/*.{css,less,scss}"


console.appender support auto-append '/n'

--- appender/console_appender.coffee
+++ appender/console_appender.coffee
@@ -9,10 +9,11 @@

 	append : (loggerName, level, msg)->
 		if util.isArray(msg)
-			msg = msg.join(',')+'\n'
-		else
+			msg = msg.join(',')
+		if msg? and msg.slice(msg.length-1, msg.length) != '\n'
 			msg += '\n'
 		# do layout
+		# @see layout_attachable.coffee
 		msg = @format?(loggerName, level, msg)
 		if LevelUtil.isError(level)
 			process.stderr.write(msg)


namespaced Movable

namespaced Movable

--- src/text_effect.coffee
+++ src/text_effect.coffee
@@ -37,7 +37,7 @@
     alpha: 1
     rotation: 0
     rotationalVelocity: 0
-    velocity: Point(0, -1)
+    velocity: Point(0, 0)

   self = GameObject(I)


Alts: Use in-battle names for the challenge vs message

--- server/index.coffee
+++ server/index.coffee
@@ -186,8 +186,9 @@

         battleId = server.acceptChallenge(user, challengerId, team, altName)
         if battleId
+          playerNames = server.findBattle(battleId).getPlayerNames()
           lobby.message("""Challenge: #{challengerId} vs. #{user.id}!""")
+          data-battle-id="#{battleId}">#{playerNames[0]} vs. #{playerNames[1]}!""")

     'rejectChallenge': (user, challengerId, team) ->
       server.rejectChallenge(user, challengerId)


fix unit test

fix unit test

--- services/document-updater/test/unit/coffee/UpdateManager/UpdateManagerTests.coffee
+++ services/document-updater/test/unit/coffee/UpdateManager/UpdateManagerTests.coffee
@@ -194,9 +194,9 @@
 					.calledWith(@project_id, @doc_id, @updatedDocLines, @version, @appliedOps, @updated_ranges)
 					.should.equal true

-			it "shoould add metadata to the ops" , ->
+			it "should add metadata to the ops" , ->
 				@UpdateManager._addProjectHistoryMetadataToOps
-					.calledWith(@appliedOps, @pathname, @updatedDocLines)
+					.calledWith(@appliedOps, @pathname, @lines)
 					.should.equal true

 			it "should push the applied ops into the history queue", ->


cleanup

cleanup

--- gulp-nav.coffee
+++ gulp-nav.coffee
@@ -19,13 +19,10 @@
 ###

 path    = require 'path'
+through = require 'through2'
 webPath = require './web-path'
-through = require 'through2'
-  .obj

-# global vars easier for this
-root     = null
-rootName = null
+root = rootName = null                            # global vars easier for this

 module.exports = ({sources, targets, titles, orders, skips, hrefExtension,
   demoteTopIndex}={}) ->
@@ -53,7 +50,7 @@
       title: null
     orderGen = 9999

-    through (file, encoding, transformCallback) ->
+    through.obj (file, encoding, transformCallback) ->
       # if vinyl objects have different properties, take first that exists
       source = (file[source] for source in sources).reduce (x, y) -> x ?= y
       source ?= file         # just look for properties on the vinyl obj itself
@@ -94,17 +91,17 @@
       # set properties of vinyl object XXX does this need error handling?
       for target in targets
         obj = file
-        [properties..., last] = target.split '.'    # for nested target props
+        [properties..., last] = target.split '.'      # for nested target props
         obj = obj[property] ?= {} for property in properties
         obj[last] = nav
       # delay until we've seen them all...
       files.push file
       transformCallback()
-    , (flushCallback) ->
-      for name, child of navTree.children                  # there's only one
+    , (flushCallback) ->                   # (still in the call to through.obj)
+      for name, child of navTree.children                    # there's only one
         root = child
         rootName = name
-      if demoteTopIndex     # top-level index becomes sibling of its children
+      if demoteTopIndex       # top-level index becomes sibling of its children
         for name, child of root.children
           navTree.children[webPath.resolve rootName, name] = child
           child.parent = navTree


Changed optimization level for imagemin

Changed optimization level for imagemin

3 might be the “magic” number - but let’s test the performance.

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -77,7 +77,7 @@
   gulp.src('build/media/images/**/*')
   .pipe(
     imagemin
-      optimizationLevel: 4
+      optimizationLevel: 2
       progressive: true
       use: [pngquant()])
   .pipe gulp.dest('build/media/images')


update gulpfile

update gulpfile

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -12,6 +12,25 @@
   compiledTest: './.tmp/test/**/*.js'
   compiledTestDir: './.tmp/test/'
   buildDir: './lib/'
+
+hubotHelp =
+  init: ->
+    through = require 'through2'
+    through.obj (file, encoding, next) ->
+      help = for line in file.contents.toString().split('\n')
+        break unless line[0] is '#' or line.substring(0, 2) is '//'
+        line.replace(new RegExp('^#(\\s*)'), '//$1')
+      file.hubotHelp = help.join('\n') + '\n'
+      next null, file
+  write: ->
+    through = require 'through2'
+    through.obj (file, encoding, next) ->
+      return next(null, file) unless file.hubotHelp?
+      file.contents = Buffer.concat [
+        new Buffer(file.hubotHelp)
+        file.contents
+      ]
+      next null, file

 gulp.task 'clean', (done) ->
   del = require 'del'
@@ -31,7 +50,9 @@
   coffee = require 'gulp-coffee'
   gulp
     .src paths.src
+    .pipe hubotHelp.init()
     .pipe coffee(bare: true).on('error', gutil.log)
+    .pipe hubotHelp.write()
     .pipe gulp.dest(paths.buildDir)

 gulp.task 'compile-src', ->
@@ -40,7 +61,9 @@
   gulp
     .src paths.src
     .pipe sourcemaps.init()
+    .pipe hubotHelp.init()
     .pipe coffee(bare: true).on('error', gutil.log)
+    .pipe hubotHelp.write()
     .pipe sourcemaps.write()
     .pipe gulp.dest(paths.compiledSrcDir)


Remove bower.json from bump files list

Remove bower.json from bump files list

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -202,7 +202,7 @@
 gulp.task 'bump', ['test'], ->
   bumpType = $.util.env.type || 'patch'

-  return gulp.src(['./bower.json', './package.json', './smart.json'])
+  return gulp.src(['./package.json', './smart.json'])
     .pipe $.bump(type: bumpType)
     .pipe gulp.dest('./')


Updates gulp.coffee to remove deprecated methods

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -1,24 +1,25 @@
 "use strict"

 # Gulp
-gulp	 	= require 'gulp'
-watch	 	= require 'gulp-watch'
+gulp	 		= require 'gulp'
+util 			= require 'gulp-util'
+watch	 		= require 'gulp-watch'
 rename 		= require 'gulp-rename'
 header 		= require 'gulp-header'

 # Preprocessors
-coffee	 	= require 'gulp-coffee'
-react 		= require 'gulp-react'
-browserify 	= require 'gulp-browserify'
-stylus	 	= require 'gulp-stylus'
-jade	 	= require 'gulp-jade'
+coffee	 	 = require 'gulp-coffee'
+react 		 = require 'gulp-react'
+browserify = require 'gulp-browserify'
+stylus	 	 = require 'gulp-stylus'
+jade	 	   = require 'gulp-jade'

 # Server, livereload
-ecstatic 	= require('ecstatic')
-http 		= require('http')
-embedlr 	= require("gulp-embedlr")
-livereload 	= require('gulp-livereload')
-server 		= require('tiny-lr')()
+ecstatic 	 = require('ecstatic')
+http 		   = require('http')
+embedlr 	 = require("gulp-embedlr")
+livereload = require('gulp-livereload')
+server 		 = require('tiny-lr')()

 ###
 # Styles
@@ -35,9 +36,9 @@
 # Scripts
 ###
 gulp.task 'scripts', ->
-	gulp.run 'coffee', ->
-		gulp.run 'jsx', ->
-			gulp.run 'browserfy', ->
+	gulp.start 'coffee', ->
+		gulp.start 'jsx', ->
+			gulp.start 'browserfy', ->

 gulp.task 'coffee', ->

@@ -56,13 +57,13 @@
 	gulp.src( '.tmp/jsx/**/*.jsx' )
 		.pipe(react())
 		.pipe(gulp.dest( '.tmp/scripts/' ))
-
+
 gulp.task 'browserfy', ->

 	gulp.src( '.tmp/scripts/main.js' )
 		.pipe(browserify({
 			insertGlobals: true
-			debug: !gulp.env.production
+			debug: !util.env.production
         }))
 		.pipe(gulp.dest( 'dist/scripts/' ))
 		.pipe(livereload(server))
@@ -85,15 +86,15 @@
 ###
 gulp.task 'default', ->

-	gulp.run 'styles', 'templates', 'scripts'
+	gulp.start 'styles', 'templates', 'scripts'

 ###
-# Static server, livereload and watch changes
+# Static server, livereload and watch changes
 ###
 gulp.task "watch", ->

-	gulp.run 'default'
+	gulp.start 'default'

 	# Create static server
 	http.createServer(
@@ -107,14 +108,14 @@

 		# Watch .styl files
 		gulp.watch "app/styles/**/*.styl", (e)->
-			gulp.run 'styles'
+			gulp.start 'styles'

 		# Watch .coffee files
-		gulp.watch "app/scripts/**/*.coffee", (e)->
-			gulp.run 'scripts'
+		gulp.watch "app/scripts/**/*.coffee", (e)->
+			gulp.start 'scripts'

 		# Watch .jade files
-		gulp.watch ["app/*.jade","app/templates/**/*.jade"], (e)->
-			gulp.run 'templates'
+		gulp.watch ["app/*.jade","app/templates/**/*.jade"], (e)->
+			gulp.start 'templates'


Build: added watch to playground styl file

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -103,6 +103,7 @@

   watcher = gulp.watch [
     './playground/**/*.coffee'
+    './playground/**/*.styl'
     './playground/**/*.html'
   ], ['play']


remove unnecessary require in gulpfile.coffee

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -1,5 +1,4 @@
 gulp = require 'gulp'
-coffeeify = require 'coffeeify'
 browserify = require 'browserify'
 source = require 'vinyl-source-stream'


initial brain save for env set

initial brain save for env set

--- src/tf.coffee
+++ src/tf.coffee
@@ -169,6 +169,13 @@
     unless fs.existsSync("#{basepath}/#{projname}")
       return msg.send {room: msg.message.user.name}, "Invalid project name: `#{projname}`"

+    brainloc = "hubot-tf_#{projname}"
+    localstorage = JSON.parse(robot.brain.get brainloc) or {}
+    localstorage[ekey] = evalue
+    robot.brain.set brainloc, JSON.stringify(localstorage)
+    robot.brain.save()
+
+    msg.send {room: msg.message.user.name}, "```\n#{JSON.stringify(localstorage)}\n```"
     return msg.send {room: msg.message.user.name}, "`#{projname}` env set: `#{ekey}` = `#{evalue}`"

   robot.respond /tf env ([^\s]+) unset ([^\s]+)$/i, (msg) ->


Test that deferred packages don't require their main module

As opposed to asserting that the main module isn't present, which
might not be true if a previous spec required it.

--- spec/app/atom-spec.coffee
+++ spec/app/atom-spec.coffee
@@ -49,10 +49,12 @@
             beforeEach ->
               mainModule = require 'package-with-activation-events/index'
               spyOn(mainModule, 'activate').andCallThrough()
+              AtomPackage = require 'atom-package'
+              spyOn(AtomPackage.prototype, 'requireMainModule').andCallThrough()
               pack = atom.activatePackage('package-with-activation-events')

             it "defers requiring/activating the main module until an activation event bubbles to the root view", ->
-              expect(pack.mainModule).toBeNull()
+              expect(pack.requireMainModule).not.toHaveBeenCalled()
               expect(mainModule.activate).not.toHaveBeenCalled()
               rootView.trigger 'activation-event'
               expect(mainModule.activate).toHaveBeenCalled()


Fix deprecation location reporting in spec runner

--- spec/atom-reporter.coffee
+++ spec/atom-reporter.coffee
@@ -117,7 +117,7 @@
           @div class: 'result-message fail deprecation-message', =>
             @raw marked(deprecation.message)

-          for stack in deprecation.stacks
+          for stack in deprecation.getStacks()
             fullStack = stack.map ({functionName, location}) ->
               if functionName is '<unknown>'
                 "  at #{location}"


cleanup/rework

cleanup/rework

--- imports/ui/pages/AppsPage.cjsx
+++ imports/ui/pages/AppsPage.cjsx
@@ -1,35 +1,19 @@
 { Meteor }          = require 'meteor/meteor'
 React               = require 'react'
-
-{ Article, Box, Header, Heading, Search, Title, Box, List, ListItem, Split } = require 'grommet'
+AppsList = require '../lists/AppsList.cjsx'
+{ Article, Box, Header, Heading, Search, Title} = require 'grommet'

 module.exports = React.createClass
   displayName: 'AppsPage'

-  getInitialState: ->
-    splitFlex: 'left'
-
-  listItemSelected: (idx) ->
-    @props.appNameSelected @props.appNames[idx]
-
-  userSearch: (evt) ->
-    @props.appsSearchEntered evt.srcElement.value
+  onSearch: (evt) ->
+    @props.onAppSearchEntered evt.srcElement.value

   render: ->
-    splitFlex = if @props.selectedAppName then 'right' else 'left'

         <Title responsive=true truncate=true>Apps</Title>
-        <Search onDOMChange={@userSearch} placeHolder='Search...' inline=true fill=true size='medium' />
+        <Search onDOMChange={@onSearch} placeHolder='Search...' inline=true fill=true size='medium' />

-      <List selectable=true onSelect={@listItemSelected}>
-        {@props.appNames.map (app) ->
-          <ListItem key={app._id} pad='medium' justify='between' align='center'>
-            <Box direction='column' pad='none'>
-              {app.name}
-              {app.version}
-            </Box>
-          </ListItem>
-        }
-      </List>
+      <AppsList apps={@props.apps} onAppNameSelected={@props.onAppNameSelected} />