Add checks for the change event

Add checks for the change event

--- spec/tokenized-buffer-spec.coffee
+++ spec/tokenized-buffer-spec.coffee
@@ -688,7 +688,12 @@
         expect(tokenizedBuffer.lineForScreenRow(10).indentLevel).toBe 2
         expect(tokenizedBuffer.lineForScreenRow(11).indentLevel).toBe 2

+        tokenizedBuffer.on "changed", changeHandler = jasmine.createSpy('changeHandler')
+
         buffer.setTextInRange([[7, 0], [8, 65]], '        one\n        two\n        three\n        four')
+
+        changeEvent = _.pick(changeHandler.mostRecentCall.args[0], 'start', 'end', 'delta')
+        expect(changeEvent).toEqual(start: 5, end: 8, delta: 2)

         expect(tokenizedBuffer.lineForScreenRow(5).indentLevel).toBe 4
         expect(tokenizedBuffer.lineForScreenRow(6).indentLevel).toBe 4
@@ -701,7 +706,12 @@
         buffer.insert([7, 0], '\n\n')
         buffer.insert([5, 0], '\n\n')

+        tokenizedBuffer.on "changed", changeHandler = jasmine.createSpy('changeHandler')
+
         buffer.setTextInRange([[7, 0], [8, 65]], '    ok')
+
+        changeEvent = _.pick(changeHandler.mostRecentCall.args[0], 'start', 'end', 'delta')
+        expect(changeEvent).toEqual(start: 5, end: 8, delta: -1)

         expect(tokenizedBuffer.lineForScreenRow(5).indentLevel).toBe 2
         expect(tokenizedBuffer.lineForScreenRow(6).indentLevel).toBe 2


add echo

add echo

--- scripts/httpd.coffee
+++ scripts/httpd.coffee
@@ -11,6 +11,7 @@
 #   None
 #
 # URLS:
+#   /hubot/echo/:room
 #   /hubot/log/:room
 #
 # Author:
@@ -18,10 +19,20 @@

 module.exports = (robot) ->

+  ## Post a mention message
+  robot.router.post "/hubot/echo/:room", (req, res) ->
+    user = room: "##{req.params.room}"
+    data = req.body
+    message = data.message
+    if data.to?
+      message = "@#{data.to}: " + message
+
+    robot.send user, message
+    res.end "OK\n"
+
   ## Notify a log message
   robot.router.post "/hubot/log/:room", (req, res) ->
     user = room: "##{req.params.room}"
-    console.log(req.body)
     data = req.body
     message = "[#{data.level.toUpperCase()}] #{data.message}"
     if /FATAL/i.test(data.level) or /ERROR/i.test(data.level)


修复如果没有条件出错的bug

修复如果没有条件出错的bug

--- biz/BaseEntity.coffee
+++ biz/BaseEntity.coffee
@@ -14,6 +14,7 @@
       callback = options
       options = {}

+    condition = condition || {}
     #移除掉undefined的查询条件
     for key, value of condition
       delete condition[key] if value is undefined


Application Stack in admin add container class

--- client/packages/admin/views/stack/application/app_admin.coffee
+++ client/packages/admin/views/stack/application/app_admin.coffee
@@ -4,7 +4,8 @@
     "click li#logout": "logout"
     "view_shown #target > div": "page_shown"
   }
-  page_shown: (ev)->
+  className: "container"
+  pageShown: (ev)->
     page_class = ev.currentTarget.className.split(/\s+/).filter((a)->
       return /_page_layout$/.test(a)
     )[0]


Parse inital datetime values.

Parse inital datetime values.

--- core/htdocs_source/src/pods/oxifield-datetime/component.coffee
+++ core/htdocs_source/src/pods/oxifield-datetime/component.coffee
@@ -7,9 +7,11 @@
     options: {}

     setup: (->
-        @set "content.value", "now"
-        if "now" is @get "content.value"
+        value = @get "content.value"
+        if value is "now"
             @set "content.pickvalue", moment().format(@get "format")
+        else if value
+            @set "content.pickvalue", moment.unix(value).format(@get "format")
         Em.run.next =>
             @$().find(".date").datetimepicker()
     ).on "didInsertElement"


Whoops, this shouldn't be there.

Whoops, this shouldn't be there.

--- bootstrap-tour.coffee
+++ bootstrap-tour.coffee
@@ -225,7 +225,7 @@

       tip = $(step.element).data("popover").tip()
       @_reposition(tip, step)
-      @_scrollIntoView(tip) if options.scrollToStep
+      @_scrollIntoView(tip)

     # Prevent popups from crossing over the edge of the window
     _reposition: (tip, step) ->


Slave: Make sure connection socket is still valid before handing off

--- src/streammachine/slave/index.coffee
+++ src/streammachine/slave/index.coffee
@@ -285,18 +285,22 @@
                                     startTime:  l.startTime
                                     minuteTime: l.minuteTime
                                     client:     l.obj.client
-
-                            translator.send "stream_listener", lobj.opts, lobj.socket
-
-                            # mark them as in-flight
-                            @_inflight[ lobj.opts.key ] = lobj
+
+                            # there's a chance that the connection could end
+                            # after we recorded the id but before we get here.
+                            # don't send in that case...
+                            if lobj.socket && !lobj.socket.destroyed
+                                translator.send "stream_listener", lobj.opts, lobj.socket
+
+                                # mark them as in-flight
+                                @_inflight[ lobj.opts.key ] = lobj

-                            # set a timer to check in on them
-                            lobj.timer = setTimeout =>
-                                if !lobj.ack
-                                    console.error "Failed to get ack for #{lobj.opts.key}"
-                                    translator.send "stream_listener", lobj.opts, lobj.socket
-                            , 1000
+                                # set a timer to check in on them
+                                lobj.timer = setTimeout =>
+                                    if !lobj.ack
+                                        console.error "Failed to get ack for #{lobj.opts.key}"
+                                        translator.send "stream_listener", lobj.opts, lobj.socket
+                                , 1000

                             # go on to the next listener
                             slFunc()


Show domain input in create group dialog

--- components/inputs/KDSelectBox.coffee
+++ components/inputs/KDSelectBox.coffee
@@ -3,8 +3,6 @@
   constructor:(options = {})->

     options.type = "select"
-
-
     super options

   setDomElement:(cssClass)->
@@ -34,9 +32,11 @@
     @_$select.val value
     @_$title.text @_$select.find("option[value=\"#{value}\"]").text()
     @inputDefaultValue = value
+
   getDefaultValue:-> @inputDefaultValue

   getValue:-> @_$select.val()
+
   setValue:(value)->
     @_$select.val value
     @change()
@@ -78,4 +78,4 @@

   focus:-> @setClass 'focus'

-  blur:-> @unsetClass 'focus'+  blur:-> @unsetClass 'focus'


added correct console logging handling via settings

--- src/Observatory.coffee
+++ src/Observatory.coffee
@@ -55,11 +55,12 @@
     (s)->
       @_loggers = []
       @emitters = {}
+      #console.log s
       if s?
         @settings.maxSeverity = if s.logLevel? then @LOGLEVEL[s.logLevel] else 3
         @settings.printToConsole = s.printToConsole ? true
       @_consoleLogger = new Observatory.ConsoleLogger 'default'
-      @subscribeLogger @_consoleLogger
+      @subscribeLogger @_consoleLogger if @settings.printToConsole
       @_defaultEmitter = new Observatory.Toolbox 'Toolbox'
       @emitters.Toolbox = @_defaultEmitter
       @emitters.Toolbox.maxSeverity = @settings.maxSeverity


set the ContentType when uploading packages to S3.

--- build/tasks/publish-nylas-build-task.coffee
+++ build/tasks/publish-nylas-build-task.coffee
@@ -62,21 +62,24 @@
         return reject(err) if err
         resolve()

-  put = (localSource, destName) ->
+  put = (localSource, destName, options = {}) ->
     grunt.log.writeln ">> Uploading #{localSource} to S3…"

     write = grunt.log.writeln
     ext = path.extname(destName)
     lastPc = 0

+    params =
+      Key: destName
+      ACL: "public-read"
+      Bucket: "edgehill"
+
+    _.extend(params, options)
+
     new Promise (resolve, reject) ->
       uploader = s3Client.uploadFile
         localFile: localSource
-        s3Params:
-          Key: destName
-          ACL: "public-read"
-          Bucket: "edgehill"
-
+        s3Params: params
       uploader.on "error", (err) ->
         reject(err)
       uploader.on "progress", ->
@@ -158,9 +161,11 @@
         files = fs.readdirSync(buildDir)
         for file in files
           if path.extname(file) is '.deb'
-            uploadPromises.push uploadToS3(file, "#{fullVersion}/#{process.platform}-deb/#{process.arch}/N1.deb")
+            uploadPromises.push uploadToS3(file, "#{fullVersion}/#{process.platform}-deb/#{process.arch}/N1.deb",
+                                           {"ContentType": "application/x-deb"})
           if path.extname(file) is '.rpm'
-            uploadPromises.push uploadToS3(file, "#{fullVersion}/#{process.platform}-rpm/#{process.arch}/N1.rpm")
+            uploadPromises.push uploadToS3(file, "#{fullVersion}/#{process.platform}-rpm/#{process.arch}/N1.rpm",
+                                           {"ContentType": "application/x-rpm"})

       else
         grunt.fail.fatal "Unsupported platform: '#{process.platform}'"


stop the previous animation before animating again

--- src/tip/bootstrap.coffee
+++ src/tip/bootstrap.coffee
@@ -86,6 +86,9 @@
     side = 'left' if side == 'right'

     value = parseInt(element.css(side))
+
+    # stop the previous animation
+    element.stop()

     # set initial position
     css = {}


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/controllable.coffee
+++ test/controllable.coffee
@@ -96,6 +96,6 @@

   equals player.I.y, 23

-""
+test""

 module()


Fix deploy history JSON links (f-ed by a merge)

--- SingularityUI/app/utils.coffee
+++ SingularityUI/app/utils.coffee
@@ -38,7 +38,7 @@
             lookupObject = app.allTasks
         if type is 'request'
             lookupObject = app.allRequests
-        if type is 'deploys'
+        if type is 'deploy'
             lookupObject = app.allDeploys
         if type is 'requestHistory'
             lookupObject = app.allRequestHistories


Drop users' tables first

Drop users' tables first

--- src/block/users.coffee
+++ src/block/users.coffee
@@ -13,10 +13,25 @@
 mssql.execute cmd, (u)->
   x.push u.name

-echo "Удаляем: #{x.join ', '}" if x.length
+if x.length
+  echo "Удаляем: #{x.join ', '}"
+  cmd = mssql.command """
+    Declare @sql nvarchar(max);
+    Select @sql = '';
+
+    Select @sql = @sql + 'Drop Table ' +
+      QUOTENAME(TABLE_SCHEMA) + '.' + QUOTENAME(TABLE_NAME) + ';'
+    From INFORMATION_SCHEMA.TABLES
+    Where TABLE_SCHEMA = ?
+      And TABLE_TYPE ='BASE TABLE';
+
+    exec sp_executesql @sql;
+  """

 for u in x
   try
+    assign cmd, 0, u
+    cmd.Execute()
     mssql.h.sp_dropuser u
   catch e
     echo "##{e.message}"


Ensure that the command line always exits with the correct error code.

In the previous implementation if stdout and stderr drain before the
callback is created it will exit with a 0 status even on a failure.

--- src/commandline.coffee
+++ src/commandline.coffee
@@ -206,9 +206,7 @@
         new Reporter(errorReport, colorize)
     reporter.publish()

-    process.stdout.on 'drain', =>
-        process.exit errorReport.getExitCode()
-    process.stderr.on 'drain', =>
+    process.on 'exit', () ->
         process.exit errorReport.getExitCode()

 # Declare command line options.


more precise storage bucket events

more precise storage bucket events

--- server/events.coffee
+++ server/events.coffee
@@ -30,7 +30,7 @@
   added: (doc) ->
     newSuccessEvent 'appdef', 'added', {name: doc.name, version: doc.version} unless starting

-StorageBuckets.find().observe
+StorageBuckets.find({}, {fields: name: 1, isLocked: 1}).observe
   changed: (doc, oldDoc) ->
     if doc.isLocked and not oldDoc.isLocked
       newInfoEvent 'storage', 'locked', {name: doc.name} unless starting


fix searching for  in builds

fix searching for  in builds

--- plugin/browserify.coffee
+++ plugin/browserify.coffee
@@ -39,7 +39,7 @@

   else # it's a versioned package... doesn't have a packageSource object.
     builds = isopackCache._isopacks[name].unibuilds
-    for build in builds when build.arch is 'os' # the 'os' one has nodeModulesPath
+    for build in builds when build.nodeModulesPath?
       return build.nodeModulesPath[...-12]

 getNpmDir = (file) ->


revert mistaken commit

revert mistaken commit

--- plugin/browserify.coffee
+++ plugin/browserify.coffee
@@ -38,7 +38,7 @@
   catch e
     # output error via CompileStep#error()
     # convert it to a string and then remove the 'Error: ' at the beginning.
-    #step.error message:e.toString().substring 7
+    step.error message:e.toString().substring 7

 # add our function as the handler for files ending in 'browserify.js'
 Plugin.registerSourceHandler 'browserify.js', processFile


fix(connection): account.id => accountId

--- internal_packages/worker-sync/lib/nylas-long-connection.coffee
+++ internal_packages/worker-sync/lib/nylas-long-connection.coffee
@@ -111,7 +111,7 @@
           res.on 'data', (chunk) =>
             if chunk.toString().indexOf('Invalid cursor') > 0
               error = new Error('Delta Connection: Cursor is invalid. Need to blow away local cache.')
-              NylasEnv.config.removeAtKeyPath("nylas.#{@_account.id}.cursor")
+              NylasEnv.config.removeAtKeyPath("nylas.#{@_accountId}.cursor")
               DatabaseStore._handleSetupError(error)
             @close()
           return


Add deprecation warning for users of up.ready

--- lib/assets/javascripts/up/magic.js.coffee
+++ lib/assets/javascripts/up/magic.js.coffee
@@ -369,6 +369,11 @@
 up.compiler = up.magic.compiler
 up.on = up.magic.on
 up.hello = up.magic.hello
+
+up.ready = (args...) ->
+  up.util.warn("up.ready has been renamed to up.hello and will be removed in a future version")
+  up.hello(args...)
+
 up.awaken = (args...) ->
   up.util.warn("up.awaken has been renamed to up.compiler and will be removed in a future version")
   up.compiler(args...)


editing attributes for existing nodes

editing attributes for existing nodes

--- src/textorum/tinymce/element.coffee
+++ src/textorum/tinymce/element.coffee
@@ -50,14 +50,15 @@
     # Delete a tag
     removeTag: (ui, params) ->

-    attrListElement: (name, params, node) ->
-      el = document.createElement("li")
-      el.appendChild(document.createTextNode(name))
-      el
-
-    attrFormElement: (name, params, node) ->
+    editorNodeFromListNode: (node) ->
       node = $(node)
-      node = @editor.dom.select("##{node.attr('name')}")[0]
+      if node.length
+        return $(@editor.dom.select("##{node.attr('name')}"))
+      else
+        return node
+
+
+    nameWithPrefix: (name, params, editorNode) ->
       if params.ns
         prefix = @editor.plugins.textorum.nsmap[params.ns]
         if prefix is undefined
@@ -67,55 +68,79 @@
           # TODO: Better way to get the actual base document element?
           # TODO: Only set this when necessary, maybe?
           @editor.dom.select("body")[0].firstElementChild.setAttribute("xmlns:#{prefix}", params.ns)
-          attrValue = node.getAttributeNS(params.ns, name)
+          attrValue = editorNode[0].getAttributeNS params.ns, name
           if attrValue
-            node.removeAttributeNS(params.ns, name)
-            node.setAttribute("#{prefix}:#{name}", attrValue)
+            editorNode[0].removeAttributeNS params.ns, name
+            editorNode.attr "#{prefix}:#{name}", attrValue
           else
         if prefix
-          name = "#{prefix}:#{name}"
-          attrValue = node.getAttribute(name)
-      else
-        attrValue = node.getAttribute(name)
+          return "#{prefix}:#{name}"
+      return name
+
+    attrListElement: (name, params, node) ->
+      origname = name
+      node = @editorNodeFromListNode(node)
+      name = @nameWithPrefix(name, params, node)
+      attrValue = node.attr name
+      el = $(document.createElement("li"))
+      el.addClass "attr_#{origname.replace(/:/, '__')}"
+      el.data "target", "attr_#{origname.replace(/:/, '__')}"
+      el.append(document.createTextNode(name))
+      if not params.required
+        el.addClass "optional"
+      if attrValue?
+        el.addClass "visible"
+      el
+
+    attrFormElement: (name, params, node) ->
+      origname = name
+      node = @editorNodeFromListNode(node)
+      name = @nameWithPrefix(name, params, node)
+      attrValue = node.attr name

       if params.required or attrValue?
         display = "block"
       else
         display = "none"
-      out = document.createElement("div")
-      out.setAttribute("style", """display: #{display};""")
-      out.class = "attr_#{name}"
-      label = document.createElement("label")
-      label.appendChild(document.createTextNode("#{name}"))
-      out.appendChild(label)
+      out = $(document.createElement("div"))
+      out.attr "style", """display: #{display};"""
+      out.addClass "attr_#{origname.replace(/:/, '__')}"
+      out.data 'attribute_name', name
+      out.addClass "attrform"
+      label = $(document.createElement("label"))
+      label.append document.createTextNode("#{name}")
+      out.append label

       if params.value?.length
-        sel = document.createElement("select")
+        sel = $(document.createElement("select"))
+        sel.addClass "attrinput"
         sel.name = "attr_#{name}"
         if not params.required
-          opt = document.createElement("option")
-          opt.value = ""
-          opt.appendChild(document.createTextNode(" -- empty -- "))
-          sel.appendChild(opt)
+          opt = $(document.createElement("option"))
+          opt.val ""
+          opt.append document.createTextNode(" -- empty -- ")
+          sel.append opt
         for value in params.value
-          opt = document.createElement("option")
-          opt.value = value
+          opt = $(document.createElement("option"))
+          opt.val value
           if value is attrValue
-            opt.setAttribute("selected", "selected")
-          opt.appendChild(document.createTextNode(value))
-          sel.appendChild(opt)
-        out.appendChild(sel)
+            opt.prop "selected", true
+          opt.append document.createTextNode(value)
+          sel.append opt
+        out.append sel
       else if params.data isnt undefined
-        sel = document.createElement("input")
-        sel.type = "text"
+        sel = $(document.createElement("input"))
+        sel.addClass "attrinput"
+        sel.prop "type", "text"
         if attrValue?
-          sel.value = attrValue
-        out.appendChild(sel)
+          sel.val attrValue
+        out.append sel
       else if params.$?
-        sel = document.createElement("textarea")
+        sel = $(document.createElement("textarea"))
+        sel.addClass "attrinput"
         if attrValue?
-          sel.appendChild(document.createTextNode(attrValue))
-        out.appendChild(sel)
+          sel.append document.createTextNode(attrValue)
+        out.append sel

       out

@@ -123,28 +148,37 @@
       attroptional = {}
       attrrequired = {}
       attrgroups = {}
-      newtagname = $(node).attr("data-xmlel") || params['key']
+      editorNode = @editorNodeFromListNode node
+      newtagname = $(editorNode).attr("data-xmlel") || params['key']
       elementattrs = @editor.plugins.textorum.schema.defs[newtagname]?.attr

-      attrRequiredList = document.createElement("ul")
-      attrlist = document.createElement("ul")
-      attrform = document.createElement("div")
-
-      attrwindow = document.createElement("div")
-      attrlists = document.createElement("div")
-      attrlists.appendChild(attrRequiredList)
-      attrlists.appendChild(attrlist)
-      attrwindow.appendChild(attrlists)
-      attrwindow.appendChild(attrform)
+      attrRequiredList = $(document.createElement("ul"))
+      attrlist = $(document.createElement("ul"))
+      attrform = $(document.createElement("div"))
+
+      attrwindow = $(document.createElement("div"))
+      attrwindow.addClass "textorum_attributewindow"
+      attrlists = $(document.createElement("div"))
+      attrlists.append attrRequiredList
+      attrlists.append attrlist
+      attrwindow.append attrlists
+      attrwindow.append attrform
+
+      attrlists.on 'click', 'li.optional', (e) ->
+        el = $(this)
+        window.foo = el
+        el.parents(".textorum_attributewindow").find("div.#{el.data('target')}").toggle()
+
       for own attr of elementattrs
-        attrform.appendChild(@attrFormElement(attr, elementattrs[attr], node))
+        attrform.append @attrFormElement(attr, elementattrs[attr], node)
         if elementattrs[attr].required
-          attrRequiredList.appendChild(@attrListElement(attr, elementattrs[attr], node))
+          attrRequiredList.append @attrListElement(attr, elementattrs[attr], node)
           attroptional[attr] = elementattrs[attr]
         else
-          attrlist.appendChild(@attrListElement(attr, elementattrs[attr], node))
+          attrlist.append @attrListElement(attr, elementattrs[attr], node)
           attrrequired[attr] = elementattrs[attr]
-      if attrform.childNodes.length
+      if attrform.children().length
+        window.attrform = attrform
         wm = @editor.windowManager
         w = wm.open {
           inline: true
@@ -154,7 +188,12 @@
           buttons: [{
             text: 'Ok'
             click: (e) ->
-              console.log "OK button clicked:", e, w
+              attrform.find("div.attrform:hidden").each (e) ->
+                console.log "removing", $(this).data('attribute_name')
+                editorNode.removeAttr $(this).data('attribute_name')
+              attrform.find("div.attrform:visible").each (e) ->
+                console.log "setting", $(this).data('attribute_name'), "to", $(this).find('.attrinput').val()
+                editorNode.attr $(this).data('attribute_name'), $(this).find('.attrinput').val()
               wm.close(null, w.id)
           }, {
             text: 'Cancel'
@@ -165,10 +204,6 @@
         }
       else
         console.log "no attributes"
-      console.log "attrform", attrform
-      console.log "Required:", attrrequired
-      console.log "Optional:", attroptional
-

   init = (editor) ->
     return new ElementHandler(editor)


Partial fix for explicit type w/ctors import

--- lib/completion-backend/buffer-info.coffee
+++ lib/completion-backend/buffer-info.coffee
@@ -40,7 +40,7 @@
         ([\w.]+) #name
         (?:\s+as\s+([\w.]+))? #alias
         (?:\s+(hiding))?
-        (?:\s+\(([^)]+)\))? #import list
+        (?:\s*\((.+)\)\s*$)? #import list
         ///gm
       @buffer.scan regex, ({match}) ->
         modules.push


Update chrome hack to use position: fixed

For reasons unknown, we were experiencing UI bugginess with the drag image
after updating jQuery.

Chaging the position to fixed instead of absolute fixed the problem.

--- src/utilities/set_drag_image.coffee
+++ src/utilities/set_drag_image.coffee
@@ -9,7 +9,7 @@
       # Chrome hack; position the drag element where the UA are suppose to position it.
       # This since UA's requires it to be in the DOM and Chrome requires it to be in view…
       iconAndSize[0] = image = image.cloneNode true
-      image.style.position = "absolute"
+      image.style.position = "fixed"
       image.style.left = "#{e.clientX - iconAndSize[1]}px"
       image.style.top = "#{e.clientY - iconAndSize[2]}px"
       document.body.appendChild image


Enable the reading of the OS release number

--- lib/grammar-utils/operating-system.coffee
+++ lib/grammar-utils/operating-system.coffee
@@ -12,3 +12,6 @@

   platform: ->
     process.platform
+
+  release: ->
+    process.release


创建模块时限制模块标识的非法字符输入

创建模块时限制模块标识的非法字符输入

--- lib/module/create-module-info-view.coffee
+++ lib/module/create-module-info-view.coffee
@@ -31,6 +31,8 @@
             @subview 'moduleName', new TextEditorView(mini: true)
         @div class: 'form-row msg clearfix hide', =>
           @div desc.createModuleErrorMsg, class: 'text-warning', outlet: 'errorMsg'
+        @div class: 'form-row msg clearfix hide', =>
+          @div '模块标识长度必须在6-32个字符范围内,只能输入数字，字母，下划线', class: 'text-warning', outlet: 'errorMsg2'

   initialize: ->
     # @modulePath.getModel().onDidChange => @checkPath()
@@ -116,6 +118,11 @@
   checkPath: ->
     path = @moduleId.getText().trim()
     if path isnt ""
+      regEx = /^\w{6,32}$/
+      if regEx.test path
+        @errorMsg2.parent().addClass('hide')
+      else
+        @errorMsg2.parent().removeClass('hide')
       projectPath = @modulePath.html().trim()
       path = pathM.join projectPath,path
       console.log path


remove unnecessary row

remove unnecessary row

--- app/assets/javascripts/linkus.coffee
+++ app/assets/javascripts/linkus.coffee
@@ -3,4 +3,3 @@

 $(window).on 'page:load page:restore page:change', ->
   $('.loading-container').hide()
-


Minor change to MapNodeView

Minor change to MapNodeView

--- app/coffeescripts/MapNodeView.coffee
+++ app/coffeescripts/MapNodeView.coffee
@@ -33,7 +33,7 @@
     else
       @marker.setIcon(MapNodeView.__super__.getIcon.apply(@, [icon]) )

-  _getIconName: () ->
+  _getIconName: () ->
     tokens = @marker.get('icon').url.split "/"
     lastIndex =  tokens.length - 1
     tokens[lastIndex]


fix: show newScan tour only in appknox

fix: show newScan tour only in appknox

--- app/components/submission-box.coffee
+++ app/components/submission-box.coffee
@@ -10,14 +10,15 @@
   cookies: Ember.inject.service()

   didInsertElement: ->
-    cookies = @get 'cookies'
-    name = tourName(ENV.TOUR.newScan)
-    try
-      readCookies = cookies.read()
-      cookieKey = readCookies[name]
-      if cookieKey isnt "true"
-        this.set('onboard.activeTour', ENV.TOUR.newScan)
-        cookies.write(name, true)
+    if ENV.isAppknox
+      cookies = @get 'cookies'
+      name = tourName(ENV.TOUR.newScan)
+      try
+        readCookies = cookies.read()
+        cookieKey = readCookies[name]
+        if cookieKey isnt "true"
+          this.set('onboard.activeTour', ENV.TOUR.newScan)
+          cookies.write(name, true)

   actions:


vm: simplify syntax

vm: simplify syntax

--- client/Main/VirtualizationController.coffee
+++ client/Main/VirtualizationController.coffee
@@ -276,7 +276,7 @@
       @info vm, callback? rest...

   fetchDiskUsage:(vmName, callback = noop)->
-    command = """df | grep aufs | awk '{print $2, $3}'"""
+    command = "df | grep aufs | awk '{print $2, $3}'"
     @run { vmName, withArgs:command }, (err, res)->
       if err or not res then [max, current] = [0, 0]
       else [max, current] = res.trim().split " "


bugfix: remove bad merge artifact

bugfix: remove bad merge artifact

--- client/Main/VirtualizationController.coffee
+++ client/Main/VirtualizationController.coffee
@@ -268,7 +268,6 @@
         .then(-> resolve kite)
         .catch warn

-
   handleFetchedVms: (vms, callback) ->
     if KD.useNewKites
       Promise.cast(vms).map (vm) =>
@@ -288,9 +287,6 @@
     else
       @registerKite vm  for vm in vms
       KD.utils.defer -> callback null
->>>>>>> 9b97aa7513573d6ee591a07b59d34017e1ec2ace
-
-

   fetchGroupVMs:(force, callback = noop)->
     if @groupVms.length > 0 and not force


Activity: re-implemented lost keyboard navigation to the activity sidebar

--- client/Social/Activity/AppController.coffee
+++ client/Social/Activity/AppController.coffee
@@ -4,6 +4,15 @@
   KD.registerAppClass this,
     name         : 'Activity'
     searchRoute  : '/Activity?q=:text:'
+    commands     :
+      'next tab'     : 'goToNextTab'
+      'previous tab' : 'goToPreviousTab'
+    keyBindings: [
+      { command: 'next tab',      binding: 'alt+]',    global: yes }
+      { command: 'next tab',      binding: 'alt+down', global: yes }
+      { command: 'previous tab',  binding: 'alt+up',   global: yes }
+      { command: 'previous tab',  binding: 'alt+[',    global: yes }
+    ]

   firstFetch = yes

@@ -85,6 +94,20 @@
     firstFetch = yes

+  getActiveChannel: -> @getView().sidebar.selectedItem.getData()
+
+
+  goToNextTab: (event) ->
+
+    KD.utils.stopDOMEvent event
+    @getView().openNext()
+
+
+  goToPreviousTab: (event) ->
+
+    KD.utils.stopDOMEvent event
+    @getView().openPrev()
+
   #
   # LEGACY
   #


Collaboration: Pick machine from latest workspace

--- client/ide/routes.coffee
+++ client/ide/routes.coffee
@@ -153,8 +153,11 @@
     # we assume that if machineLabel is all numbers it is the channelId - SY
     return loadCollaborativeIDE machineLabel  if machineLabel and /^[0-9]+$/.test machineLabel

-    machine = KD.userMachines.first
-    return putVMInWorkspace machine  if machine
+    localStorage   = KD.getSingleton("localStorageController").storage "IDE"
+    {machineLabel} = localStorage.getValue 'LatestWorkspace'
+
+    machine = m for m in KD.userMachines when m.label is machineLabel
+    return putVMInWorkspace (machine or KD.userMachines.first)

     KD.singletons.computeController.fetchMachines (err, machines)->


cleanup

cleanup

--- private/js/wizard.coffee
+++ private/js/wizard.coffee
@@ -19,20 +19,20 @@
   return $('input', "#wizard-p-#{ index }")

-onFinished = (event, index) ->
+onFinished = ->
   $overlay.css visibility: 'visible'
   return

-onInit = (event, index) ->
+onInit = ->
   $formats = $('#formats')

   $formats.append $("<option value='#{ f }'>#{ f }</option>") for f in ['2Ad', '3Ad', 'Angel ad', 'APTO', 'Banderole', 'Banner', 'Banner + Layer', 'Billboard', 'Billboard slider', 'Billboard XL', 'Black-Out', 'Careta', 'CornerAd', 'Cross device', 'Cross device billboard', 'Cube', 'Divide Ad', 'Double Sidekick', 'Double Slider', 'Emag', 'Expandable', 'Expandable leaderbord', 'Expandable rectangle', 'Extreme header', 'Fatboy + video', 'Filmstrip', 'Fixed position', 'Fixed scroll banner', 'Flip', 'FloorAd', 'FrameAd', 'FrontKick', 'FullScreen', 'Fullscreen attention layer', 'Fullscreen FloorAd', 'Gecko', 'Group Take Over', 'Halfpage', 'Halfpage + video', 'Header', 'HPTO', 'HPTO layer', 'IN CONTENT', 'in-app header', 'Inarticle', 'Insite', 'Intent message', 'Intent takeover (ITO)', 'Interactive preroll', 'Interscroller', 'Interstitial', 'inview', 'iTV leaderboard', 'Layer', 'Leaderboard', 'Lightbox', 'MastHead', 'Mobile banner', 'Mobile banner + fullscreen layer', 'Mobile double banner', 'Mobile halfpage', 'Mobile halfpage + fullscreen layer', 'Mobile slider', 'MPU', 'MSN custom header', 'Portrait', 'Post Click', 'Preroll', 'Pushdown', 'Rectangle', 'Rectangle to fullscreen', 'Responsive homepage ad', 'Responsive HPTO', 'Roadblock', 'Rubrieks Takeover', 'Screenad', 'Sidebox', 'Sidekick', 'Skin', 'Skybox', 'Skyscraper', 'Slider', 'Slides', 'Slingshot', 'Splashpage', 'SuperExpanding', 'SuperLayer', 'Tablet halfpage', 'Takeover', 'U WallPaper', 'Video banner', 'Video rectangle', 'Video2Halfpage', 'VideoSkin', 'Videostrip', 'VPAID', 'VPAID Ad Bar', 'Vpaid Expand Ad', 'Vpaid Filmstrip', 'Vpaid Freestyle', 'Vpaid Share View', 'WallPaper', 'Windows live', 'WoW preroll', 'Wow Videobox']

-  $(window).keydown (e) ->
+  $(window).keydown (event) ->
     prevent = yes

-    switch e.which
+    switch event.which
       when 13
         index = $wizard.steps('getCurrentIndex')
         $wizard.steps if (index is steps) then 'finish' else 'next'
@@ -43,7 +43,7 @@
       else
         prevent = no

-    e.preventDefault() if prevent
+    event.preventDefault() if prevent
     return

   $overlay.css visibility: 'hidden'
@@ -51,7 +51,7 @@
   return

-onStepChanging = (event, index, newIndex) ->
+onStepChanging = (event, index) ->
   switch index
     when 0
       $input = input(index)
@@ -81,7 +81,7 @@
   return yes

-onStepChanged = (event, index, lastIndex) ->
+onStepChanged = (event, index) ->
   input(index).focus().select()
   return


Only send JSON content type if there is data

--- web/scripts/api.coffee
+++ web/scripts/api.coffee
@@ -345,10 +345,10 @@
                     addAuth r, @login, @password
                     addVersion r
                     options.beforeSend? r
-                data: JSON.stringify(options.data)
+                data: JSON.stringify options.data
                 success: options.success
                 error: ajaxError options.error
                 dataType: 'json'
                 type: options.method ? 'GET'
-                contentType: 'application/json; charset=utf-8'
+                contentType: 'application/json; charset=utf-8' if options.data?
             return


KDEventEmitter: removed unsubscribe method

- it was unused, redundant and confusing

--- client/Framework/core/KDEventEmitter.coffee
+++ client/Framework/core/KDEventEmitter.coffee
@@ -20,7 +20,7 @@
   _unregisterEvent = (registry, eventName, callback)->
     if eventName is "*"
       registry = {}
-    # reset the listener container so no event
+    # reset the listener container so no event3
     # will be propagated to previously registered
     # listener callbacks.
     else if callback and registry[eventName]
@@ -70,7 +70,7 @@

   constructor:->
     @KDEventEmitterEvents  = {}
-    @_e = @KDEventEmitterEvents[@constructor.name] = {}
+    @_e = {}

   emit:(eventName, args...)->
     @_e[eventName] ?= []
@@ -90,7 +90,6 @@

   on  :(eventName, callback) -> _on  @_e, eventName, callback
   off :(eventName, callback) -> _off @_e, eventName, callback
-  unsubscribe:(eventName, callback) -> _off @_e, eventName, callback

   once:(eventName, callback) ->
     _callback = =>


Payment: fix the expiration date on cancelled subscriptions

--- client/Main/payment/subscriptionview.coffee
+++ client/Main/payment/subscriptionview.coffee
@@ -30,7 +30,7 @@
             when 'active'
               "Will renew on #{dateFormat renewAt, datePattern}"
             when 'canceled'
-              "Will be available till #{dateFormat expires, datePattern}"
+              "Will be available till #{dateFormat (expires or renewAt), datePattern}"
             when 'future'
               "Will become available on #{dateFormat startsAt, datePattern}"
         else ''


Fixed bug with TrustStatus collections

Fixed bug with TrustStatus collections

--- server/server.coffee
+++ server/server.coffee
@@ -1,4 +1,3 @@
-@PendingTrust = new Meteor.Collection "pendtrust"
 Fiber = Npm.require 'fibers'

 ###
@@ -12,10 +11,10 @@
 ###

 #remove temp stuff on start
-PendingTrust.remove({})
+TrustStatus.remove({})

 Meteor.publish "trust", ()->
-  PendingTrust.find({})
+  TrustStatus.find({})

 Meteor.publish "characters", (hostHash)->
   Characters.find({hostid: hostHash})
@@ -118,14 +117,14 @@
       host = @request.connection.remoteAddress
       trusted = @request.headers["eve_trusted"] is "Yes"
       hostHash = @request.headers["ident"]
-      trustStatus = PendingTrust.findOne({ident: hostHash})
+      trustStatus = TrustStatus.findOne({ident: hostHash})
       if !trustStatus?
         trustStatus = {ident: hostHash, status: trusted}
-        trustStatus._id = PendingTrust.insert(trustStatus)
+        trustStatus._id = TrustStatus.insert(trustStatus)
       if trustStatus.status isnt trusted
         console.log "user "+(if trusted then @request.headers["eve_charname"] else hostHash)+" is now "+(if trusted then "trusted" else "not trusted")
         trustStatus.status = trusted
-        PendingTrust.update({_id: trustStatus._id}, {$set: {status: trusted}})
+        TrustStatus.update({_id: trustStatus._id}, {$set: {status: trusted}})
       @response.writeHead 200, {'Content-Type': 'text/html'}
       @response.end ""+hostHash


minor change for destroy pane modal

minor change for destroy pane modal

--- client/ide/lib/views/tabview/ideview.coffee
+++ client/ide/lib/views/tabview/ideview.coffee
@@ -159,17 +159,21 @@
     { frontApp } = kd.singletons.appManager

     modal = new ContentModal
-      title         : 'Would you like us to remove the pane when there are no tabs left?'
+      width         : 600
+      title         : 'Remove Pane'
       cssClass      : 'content-modal'
       content       : '''
+        <h>Would you like us to remove the pane when there are no tabs left?</h>

You can always change this setting on preferences.

       '''
       overlay       : yes
       buttons       :
         'No'        :
+          title     : 'No'
           style     : 'solid medium'
           callback  : -> modal.destroy()
         'Yes'       :
+          title     : 'Yes'
           style     : 'solid medium'
           callback  : =>
             frontApp.settingsPane.emit 'EnableAutoRemovePane'


Login: minor copy change

Login: minor copy change

--- client/landing/Login/Views/loginform.coffee
+++ client/landing/Login/Views/loginform.coffee
@@ -39,7 +39,7 @@
             required  : "Please enter your password."

     @button = new KDButtonView
-      title       : "SIGN ME IN"
+      title       : "SIGN IN"
       style       : "solid green"
       type        : 'submit'
       loader      :


dont require args

dont require args

--- src/superflux.coffee
+++ src/superflux.coffee
@@ -2,7 +2,7 @@

 module.exports = class SuperFlux
-    constructor: (args) ->
+    constructor: (args={}) ->
         @socket = args.socket or ->
         @requestAuthHeaderKey = args.requestAuthHeaderKey or null
         @requestAuthHeaderValue = args.requestAuthHeaderValue or null


Updated tag input to work with other directives.

--- src/tag_input.coffee
+++ src/tag_input.coffee
@@ -27,10 +27,9 @@
       element.attr "data-placeholder", placeholder

       for tag in tagsList
-        element.append $("<option>").attr(tag).text tag
+        element.append $("<option>").attr("value", tag).text tag

-      ($scope, element, attr) ->
-        options.no_results_text = noResult if noResult?
+      options.no_results_text = noResult if noResult?

-        element.chosen options
+      element.chosen options
 ]


comment out unnecessary code

comment out unnecessary code

--- app/scripts/resources/budgets.coffee
+++ app/scripts/resources/budgets.coffee
@@ -7,6 +7,7 @@
   all: () ->
     Restangular.all('budgets').getList()

+###
   myBudgets: ->
     #TODO restrict to only getting budgets visible by this user
     @allBudgets().$object
@@ -15,7 +16,7 @@
     state ||= "open"
     limit ||= ""
     Restangular.one('budgets', budget_id).customGET('buckets', {state: state, limit: limit})
-
+
   getBudgetContributors: (budget_id, state, limit) ->
     #console.log(budget_id)
     #state ||= "open"
@@ -25,6 +26,7 @@
     Restangular.oneUrl('contributors', 'http://api.cobudget.enspiral.info/cobudget/list_by_budget_accounts?budget_id='+budget_id).get()

     #contributors = {name: "charlie ablett", allocation_rights_cents: 178093}
+###

 #  createBudget: (budget_data)->
 #    budgets.post('budgets', budget_data)


added noc_members alert

added noc_members alert

--- scripts/name-expander.coffee
+++ scripts/name-expander.coffee
@@ -13,6 +13,8 @@
 justin = "Justin Campbell"
 trotter = "Trotter Cashion"
 afeng = "Aaron Feng"
+
+noc_members = ["Carter1", "mrobinson"]

 dert_mermbers = ["CDERCKERSERN", "MAHCHERLERVER", "JERS HERERTH", "JKERL", "JERMAH WERNSER", "CHIN YERN", "KERLERN", "ERNDRERW GERSERN", "JERSTEN CERMPBELL"]

@@ -71,5 +73,8 @@
   robot.hear /^(mashion)( team| dudes| guys)*:(.+)/i, (msg) ->
     msg.send mashion_devs.join(", ") + ": ^^"

+  robot.hear /^@?(noc)( team| dudes| guys)*(:| )(.+)/i, (msg) ->
+    msg.send noc_members.join(", ") + ": ^^"
+
   robot.hear /^mergers:(.+)/i, (msg) ->
     msg.send chef_repo_mergers.join(", ") + ": ^^"


handle when registered course isnt the passed enroll code course

--- coach/src/concept-coach/launcher/index.cjsx
+++ coach/src/concept-coach/launcher/index.cjsx
@@ -73,6 +73,7 @@
     height: @getHeight()
     isEnrollmentCodeValid: false
     isEnrollmentTargetPast: false
+    isCoursePast: false

   componentWillReceiveProps: (nextProps) ->
     @setState(height: @getHeight(nextProps)) if @props.isLaunching isnt nextProps.isLaunching
@@ -89,10 +90,17 @@

   setIsEnrollmentCodeValid: (eventChannel, data) ->
     if eventChannel.event is 'validated.failure'
-      @setState(isEnrollmentTargetPast: includes(map(data, 'code'), 'course_ended'))
+      if includes(map(data, 'code'), 'course_ended')
+        @setState(isEnrollmentTargetPast: true)
+        if @getUser().getCourse(@props.collectionUUID).id is @getUser().getCourse(@props.collectionUUID, @props.enrollmentCode)?.id
+          @setState(isCoursePast: true)
     else if eventChannel.event is 'validated.success'
       @props.setIsEnrollmentCodeValid?(true)
       @setState(isEnrollmentCodeValid: true)
+
+  isEnrolled: ->
+    {enrollmentCode} = @props if not @state.isEnrollmentTargetPast and @state.isEnrollmentCodeValid
+    @getUser().isEnrolled(@props.collectionUUID, enrollmentCode)

   mixins: [UserStatusMixin]

@@ -103,13 +111,13 @@

   render: ->
     {isLaunching, defaultHeight} = @props
-    {height, isEnrollmentCodeValid, isEnrollmentTargetPast} = @state
+    {height, isEnrollmentCodeValid, isCoursePast} = @state
     user = @getUser()
-    isEnrolled =  user.isEnrolled(@props.collectionUUID, @props.enrollmentCode)
+    isEnrolled =  @isEnrolled()
     isLoggedIn = user.isLoggedIn()

     finePrint = if isEnrolled
-      if isEnrollmentTargetPast
+      if isCoursePast
         <SemesterIsPast {...@props}/>
       else
         <SwitchSections {...@props}/>


Reduce limit to actuall 1Mb, not 1Gb

Reduce limit to actuall 1Mb, not 1Gb

--- services/real-time/app/coffee/DocumentUpdaterController.coffee
+++ services/real-time/app/coffee/DocumentUpdaterController.coffee
@@ -3,7 +3,7 @@
 redis = require("redis-sharelatex")
 rclient = redis.createClient(settings.redis.web)

-MESSAGE_SIZE_LOG_LIMIT = 1024 * 1024 * 1024 # 1Mb
+MESSAGE_SIZE_LOG_LIMIT = 1024 * 1024 # 1Mb

 module.exports = DocumentUpdaterController =
 	# DocumentUpdaterController is responsible for updates that come via Redis


Add comments and remove some code that we don't use anymore

--- admin/static/coffee/namespaces/database.coffee
+++ admin/static/coffee/namespaces/database.coffee
@@ -1,16 +1,18 @@
 # Database view
 module 'DatabaseView', ->
+    # Class in case the database is not found (propably a deleted database). Links to this view can be found in old logs.
     class @NotFound extends Backbone.View
         className: 'section'
         template: Handlebars.compile $('#element_view-not_found-template').html()
-        initialize: (id) ->
+        initialize: (id) =>
             @id = id
+
         render: =>
             @.$el.html @template
                 id: @id
                 type: 'database'
-                type_url: 'databases'
-                type_all_url: 'tables'
+                type_url: 'databases' # Use to build a url to a specific database
+                type_all_url: 'tables' # Use to build a url to the list of all databases
             return @

     # Container for the entire database view
@@ -20,7 +22,6 @@
         alert_tmpl: Handlebars.compile $('#modify_shards-alert-template').html()

         events: ->
-            'click .tab-link': 'change_route'
             'click .close': 'close_alert'
             # operations in the dropdown menu
             'click .operations .rename': 'rename_database'
@@ -42,6 +43,7 @@

             databases.on 'remove', @check_if_still_exists

+        # If we were on this view and the database has been deleted somewhere else, we redirect the user to the list of databases
         check_if_still_exists: =>
             exist = false
             for database in databases.models
@@ -53,11 +55,8 @@
                 window.app.index_namespaces
                     alert_message: "The database #{@model.get('name')} could not be found and was probably deleted."

-        change_route: (event) =>
-            # Because we are using bootstrap tab. We should remove them later.
-            window.router.navigate @.$(event.target).attr('href')
-
-        render: (tab) =>
+        # Function to render the view
+        render: =>
             log_render '(rendering) database view: container'

             @.$el.html @template
@@ -75,21 +74,26 @@

             return @

+        # Method to close an alert/warning/arror
         close_alert: (event) ->
             event.preventDefault()
             $(event.currentTarget).parent().slideUp('fast', -> $(this).remove())

+        # Create a modal to renane the database
         rename_database: (event) =>
             event.preventDefault()
             rename_modal = new UIComponents.RenameItemModal @model.get('id'), 'database'
             rename_modal.render()

+        # Create a modal to delete the databse
         delete_database: (event) ->
             event.preventDefault()
             remove_database_dialog = new DatabaseView.RemoveDatabaseModal
             remove_database_dialog.render @model

-        destroy: =>
+        # Remove listeners
+        destroy: =>
+            databases.off 'remove', @check_if_still_exists
             @title.destroy()
             @profile.destroy()
             @performance_graph.destroy()
@@ -98,20 +102,25 @@
     class @Title extends Backbone.View
         className: 'database-info-view'
         template: Handlebars.compile $('#database_view_title-template').html()
-        initialize: ->
+
+        # Bind listeners
+        initialize: =>
             @name = @model.get('name')
             @model.on 'change:name', @update

+        # Update the title if the database has a new name
         update: =>
             if @name isnt @model.get('name')
                 @name = @model.get('name')
                 @render()

+        # Render the view for the title
         render: =>
             @.$el.html @template
                 name: @name
             return @

+        # Unbind listeners
         destroy: =>
             @model.off 'change:name', @update

@@ -119,21 +128,25 @@
     class @Profile extends Backbone.View
         className: 'database-profile'
         template: Handlebars.compile $('#database_view-profile-template').html()
-
-        initialize: ->
-            @data = {}
-            namespaces.on 'all', @render
-
-        render: =>
+
+        # Bind listeners and initialize the data of this view
+        initialize: =>
+            @data = {} # This will be used to know whether we need to rerender our view or not
+            namespaces.on 'all', @render # We listen to all namespaces since they can be moved while the user is on this view
+
+        # Render the view
+        render: =>
+            # Initialize the data for this view
             data =
-                num_namespaces: 0
-                num_live_namespaces: 0
-                reachability: true
-                stats_up_to_date: true
-                nshards: 0
-                nreplicas: 0
-                ndatacenters: 0
-
+                num_namespaces: 0 # Number of namespaces
+                num_live_namespaces: 0 # Number of namespaces alived
+                reachability: true # Status of the table
+                nshards: 0 # Number of shards
+                nreplicas: 0 # Number of replicas (numbers of copies of each shards)
+                ndatacenters: 0 # Number of datacenter
+                #stats_up_to_date: true # Not display for now. We should put it back. Issue #1286
+
+            # Compute the data for this view
             for namespace in namespaces.models
                 if namespace.get('database') is @model.get('id')
                     data.num_namespaces++
@@ -142,14 +155,17 @@
                         data.num_live_namespaces++
                     data.nshards += namespace_status.nshards
                     data.nreplicas += data.nshards + namespace_status.nreplicas
+                    # We should use _.uniq on the datacenters for all the tables, not on each table
                     data.ndatacenters += namespace_status.ndatacenters

+            # We update only if the data has changed
             if not _.isEqual @data, data
                 @data = data
                 @.$el.html @template data

             return @

+        # Unbind listeners
         destroy: =>
             namespaces.off 'all', @render


fix(win): When windows are removed, keep ivars in sync

--- src/browser/window-manager.coffee
+++ src/browser/window-manager.coffee
@@ -422,6 +422,10 @@
   # Public: Removes the {AtomWindow} from the global window list.
   removeWindow: (window) ->
     @_windows.splice @_windows.indexOf(window), 1
+    if window is @_mainWindow
+      @_mainWindow = null
+    if window is @_workWindow
+      @_workWindow = null
     @applicationMenu?.enableWindowSpecificItems(false) if @_windows.length == 0
     @windowClosedOrHidden()


Fix delete and minimize

Fix delete and minimize

--- client/message.coffee
+++ client/message.coffee
@@ -2167,7 +2167,6 @@
   onDelete = (e) ->
     e.preventDefault()
     e.stopPropagation()
-    messageFolded.set message, not message.deleted or message.minimized or images[message._id]?.count > 0
     ## Stop editing if we are deleting.
     if not message.deleted and editing
       Meteor.call 'messageEditStop', message._id
@@ -2189,7 +2188,6 @@
   onMinimize = (e) ->
     e.preventDefault()
     e.stopPropagation()
-    messageFolded.set message, message.deleted or not message.minimized or images[message._id]?.count > 0
     Meteor.call 'messageUpdate', message._id,
       minimized: not message.minimized
       finished: true


Migrate config

Migrate config

--- src/main-process/atom-application.coffee
+++ src/main-process/atom-application.coffee
@@ -84,6 +84,12 @@
   initialize: (options) ->
     global.atomApplication = this

+    # DEPRECATED: This can be removed at some point (added in 1.13)
+    # It converts `useCustomTitleBar: true` to `titleBar: "custom"`
+    if process.platform is 'darwin' and @config.get('core.useCustomTitleBar')
+      @config.unset('core.useCustomTitleBar')
+      @config.set('core.titleBar', 'custom')
+
     @config.onDidChange 'core.titleBar', @promptForRestart.bind(this)

     @autoUpdateManager = new AutoUpdateManager(


extend Sprite report method

extend Sprite report method

--- src/aniSprite.coffee
+++ src/aniSprite.coffee
@@ -147,6 +147,23 @@

             #restart the timer
             @_ani.timer.restart()
+
+    #debugging
+    report: ->
+        """
+        #{super()}
+        animation:
+            frameRate: #{@_ani.frameRate}
+            cellWidth: #{@_ani.cellWidth}
+            cellHeight: #{@_ani.cellHeight}
+            orientation: #{@_ani.orientation}
+            current cycle:
+                frame: #{@_ani.current.frame}
+                index: #{@_ani.current.index}
+                start: #{@_ani.current.start}
+                stop: #{@_ani.current.stop}
+                name: #{@_ani.current.name}
+        """

 #add to namespace object
-gh.AniSprite = AniSprite+gh.AniSprite = AniSprite


Support hide handler on click event

Support hide handler on click event

This commit support hide handler for onCick event, useful for Ui
interaction elements such as buttons or links.
This should fix #4.

--- src/aria-tips.coffee
+++ src/aria-tips.coffee
@@ -223,6 +223,7 @@
         @addEventListener 'mouseout', hide
         @addEventListener 'blur', hide
         @addEventListener 'keyup', onKeyUp
+        @addEventListener 'click', hide

         # Since a tooltip management is scoped (there is one pair of handlers
         # for onMouseOver/onMouseOut events for each trigger), we return an
@@ -236,6 +237,7 @@
             @removeEventListener 'mouseout', hide
             @removeEventListener 'blur', hide
             @removeEventListener 'keyup', onKeyUp
+            @removeEventListener 'click', hide

         return unbindEvents


derp... broke something

derp... broke something

--- lib/job.coffee
+++ lib/job.coffee
@@ -19,10 +19,11 @@
     delete @gearman.currentJobs[@handle] if @handle
     return if @aborted
     @abort()
+    error = new Error "Timeout exceed for the job"
     if typeof @timeoutCallback is "function"
       @timeoutCallback error
     else
-      @emit "timeout", handle
+      @emit "timeout", error, @handle

   abort: () ->
     clearTimeout @timeoutTimer


Added use of Geometry class.

Added use of Geometry class.

--- coffee/surface.coffee
+++ coffee/surface.coffee
@@ -18,6 +18,8 @@

     update: (axes) ->

+        # TODO: replace by SCIWIS.Geometry
+
         # geometry
         geometry = new THREE.BufferGeometry()
         geometry.addAttribute('position', new THREE.BufferAttribute(@vertex.data, 3));
@@ -32,4 +34,4 @@

 module.exports =
-    Surface : Surface+    Surface : Surface


Updated context reference

Updated context reference

--- src/bodyguard.coffee
+++ src/bodyguard.coffee
@@ -3,7 +3,7 @@
   noop = ->

   bodyguard = (definition, obj, context) ->
-    result = {}
+    context or= {}

     if Array.isArray definition
       for method in definition


Client add api tab

Client add api tab

--- client/admin/lib/index.coffee
+++ client/admin/lib/index.coffee
@@ -1,6 +1,7 @@
 kd                         = require 'kd'
 AppController              = require 'app/appcontroller'

+AdminApiView               = require './views/api/adminapiview'
 AdminAppView               = require './views/customviews/adminappview'
 TeamInviteView             = require './views/koding-admin/teaminviteview'
 AdminMembersView           = require './views/members/adminmembersview'
@@ -44,6 +45,7 @@
           ]
         }
         { slug : 'Stacks',         title : 'Compute Stacks',    viewClass : GroupStackSettings       }
+        { slug : 'Api',            title : 'Api Settings',      viewClass : AdminApiView             }
       ]
     koding     :
       title    : 'Koding Administration'


small refactor to chat

small refactor to chat

--- src/chat/chat.coffee
+++ src/chat/chat.coffee
@@ -46,11 +46,6 @@
     @userCommands.listenTo commandInput
     commandInput.on 'switch_window', (winNum) =>
       @switchToWindowByIndex winNum
-
-  switchToWindowByIndex: (winNum) ->
-      winNum = 10 if winNum is 0
-      win = @winList.get winNum - 1
-      @switchToWindow win if win?

   listenToScriptEvents: (@scriptHandler) ->
     # TODO - allow scripts to create notifications and plain text
@@ -106,7 +101,7 @@

   _determineWindow: (conn, e) ->
     chan = e.context.channel
-    if irc.util.nicksEqual chan, conn.irc.nick
+    if irc.isOwnNick chan
       return chat.NO_WINDOW unless e.name is 'privmsg'
       from = e.args[0]
       conn.windows[from] ?= @_createWindowForChannel conn, from
@@ -201,6 +196,11 @@
     statusList.push 'Welcome!' if statusList.length is 0
     $('#status').text(statusList.join ' ')

+  switchToWindowByIndex: (winNum) ->
+      winNum = 10 if winNum is 0
+      win = @winList.get winNum - 1
+      @switchToWindow win if win?
+
   switchToWindow: (win) ->
     throw new Error("switching to non-existant window") if not win?
     @currentWindow.detach() if @currentWindow


Clampable#clampToBounds

Clampable#clampToBounds

--- src/clampable.coffee
+++ src/clampable.coffee
@@ -1,4 +1,12 @@
+###*
+The Tween module provides a method to tween object properties.

+@name Tween
+@module
+@constructor
+@param {Object} I Instance variables
+@param {Core} self Reference to including object
+###
 Clampable = (I={}, self) ->
   Object.reverseMerge I,
     clampData: {}


Tests: Start runtime for example tests

Tests: Start runtime for example tests

Someone's got to do it...

--- spec/examples.coffee
+++ spec/examples.coffee
@@ -19,16 +19,22 @@
 runtimeInfo =
   protocol: 'websocket'
   address: "ws://localhost:3335"
+  command: "python2 protocol-examples/python/runtime.py --port 3335"

 describe 'Examples', ->
   schema = fbpspec.getSchema 'testsfile'
   runner = null
+  runtime = null
   before (done) ->
+    @timeout 4000
     tv4.addSchema schema.id, schema
     runner = new fbpspec.runner.Runner runtimeInfo
-    runner.connect done
+    runtime = fbpspec.subprocess.start runtimeInfo.command, {}, (err) ->
+      return done err if err
+      runner.connect done
   after (done) ->
     tv4.reset()
+    runtime.kill() if runtime
     runner.disconnect done

   listExamples().forEach (name) ->


Fix KD.showError to show correct error messages if there is user-defined ones, fix whitespace typo in groupbundle

--- client/landing/app/MainApp/KD.extend.coffee
+++ client/landing/app/MainApp/KD.extend.coffee
@@ -89,18 +89,22 @@
   showError:(err, messages)->
     return  unless err

-    messages or=
-      AccessDenied : 'Permission denied'
-      KodingError  : 'Something went wrong'
-
     if 'string' is typeof err
       message = err
       err     = {message}

+    defaultMessages =
+      AccessDenied : 'Permission denied'
+      KodingError  : 'Something went wrong'
+
     err.name or= 'KodingError'
+    content    = ''

-    content    = ''
-    errMessage = err.message or messages[err.name] or messages.KodingError
+    if messages
+      errMessage = messages[err.name] or messages.KodingError \
+                                      or defaultMessages.KodingError
+    messages or= defaultMessages
+    errMessage or= err.message or messages[err.name] or messages.KodingError

     if errMessage?
       if 'string' is typeof errMessage


editor: show tooltip when user gets credential related error

--- client/stack-editor/lib/editor/index.coffee
+++ client/stack-editor/lib/editor/index.coffee
@@ -390,7 +390,9 @@
           .add 'Checking provided credentials...'

         @checkAndBootstrapCredentials (err, credentials) =>
-          return @saveButton.hideLoader()  if err
+          if err
+            @credentialWarning.tooltip.show()
+            return @saveButton.hideLoader()

           @outputView
             .add 'Credentials are ready!'


Fix for teams stacks

Fix for teams stacks

--- client/test/lib/helpers/teamshelpers.coffee
+++ client/test/lib/helpers/teamshelpers.coffee
@@ -258,16 +258,14 @@
   startStackCreate: (browser) ->

     welcomePageSelector = '.content-page.welcome'
-    stackSelector       = "#{welcomePageSelector} a[href='/Admin/Stacks']"
-    overlaySelector     = '.AppModal--admin'
-    getstartedSelector  = "#{overlaySelector} .stack-onboarding.get-started"
+    stackSelector       = '.SidebarStackWidgets .SidebarSection-body a[href="/Stacks/Group-Stack-Templates"]'
+    getstartedSelector  = '.AppModal--admin .stack-onboarding.get-started'
     buttonSelector      = "#{getstartedSelector} .header button"

     browser
       .waitForElementVisible  welcomePageSelector, 20000
       .waitForElementVisible  stackSelector, 20000
       .click                  stackSelector
-      .waitForElementVisible  overlaySelector, 20000
       .waitForElementVisible  getstartedSelector, 20000
       .waitForElementVisible  buttonSelector, 20000
       .click                  buttonSelector


update 2

update 2

--- lms/static/coffee/src/discussion/utils.coffee
+++ lms/static/coffee/src/discussion/utils.coffee
@@ -250,7 +250,7 @@
       else if RE_DISPLAYMATH.test(text)
         text = text.replace RE_DISPLAYMATH, ($0, $1, $2, $3) ->
           #processedText += $1 + processor("$$" + $2 + "$$", 'display')
-          bug fix, ordering is off
+          #bug fix, ordering is off
           processedText =  processor("$$" + $2 + "$$", 'display') + processedText
           processedText = $1 + processedText
           $3


fix #1239: wrong label for groups appearance

--- jsapp/xlform/src/view.rowDetail.coffee
+++ jsapp/xlform/src/view.rowDetail.coffee
@@ -104,8 +104,8 @@
     textbox: (cid, key, key_label = key, input_class = '') ->
       @field """<input type="text" name="#{key}" id="#{cid}" class="#{input_class}" />""", cid, key_label

-    checkbox: (cid, key, key_label = key, input_label = 'Yes') ->
-      input_label = _t('Yes')
+    checkbox: (cid, key, key_label = key, input_label = _t('Yes')) ->
+      input_label = input_label
       @field """<input type="checkbox" name="#{key}" id="#{cid}"/> <label for="#{cid}">#{input_label}</label>""", cid, key_label

     dropdown: (cid, key, values, key_label = key) ->


fixed bug with tests

fixed bug with tests

--- spec/all.spec.coffee
+++ spec/all.spec.coffee
@@ -1,4 +1,4 @@
-scrape = require 'lib/scrape'
+scrape = require '../lib/scrape'

 describe 'The source file', ->
     it 'has a function named useXPath', ->


run: remove unused code

run: remove unused code

--- lib/run.coffee
+++ lib/run.coffee
@@ -11,8 +11,6 @@
 nikita = require 'nikita'
 constraints = require './constraints'
 context = require './context'
-
-normalize_node = (node) ->

 normalize_service = (service) ->
   if typeof service is 'string'


Util: Added a method 'copyToClipboard'.

Util: Added a method 'copyToClipboard'.

--- src/base/util.coffee
+++ src/base/util.coffee
@@ -579,6 +579,21 @@
 				CUI.util.mergeMap(targetMap[k], v)
 		targetMap

+	@copyToClipboard: (text) ->
+		if not text
+			return
+
+		textarea = CUI.dom.element("textarea",
+			style:
+				position: 'absolute'
+				left: '-9999px'
+		)
+		textarea.value = text
+		CUI.dom.append(document.body, textarea)
+		textarea.select()
+		document.execCommand('copy')
+		CUI.dom.remove(textarea)
+		return

 CUI.util.moment = moment
 CUI.util.marked = marked


implemented genomes

implemented genomes

--- lingsim.coffee
+++ lingsim.coffee
@@ -50,18 +50,29 @@

 Utils = new Utilities()

+class Genome
+  constructor: (size, space) ->
+    @genome = []
+    _.times size, => @genome.push Utils.randomAlpha(space)
+
+  randomGene: =>
+    Utils.randomAlpha(GENOME_SPACE)
+
+  mutate: =>
+    index = Utils.randomIndex @genome
+    @genome[index] = @randomGene()
+
+
 class Language
   GENOME_SIZE: 6
+  GENOME_SPACE: 4

   constructor: ->
-    @genome = @generateGenome()
-
-  generateGenome: =>
-    @genome = []
-    _.times GENOME_SIZE, => @genome.push Utils.randomAlpha()
+    @genome = new Genome()

   distance: (otherLanguage) =>
-    Utils.editDistance @genome, otherLanguage.genome
+    @genome.distance otherLanguage.genome
+

 class Agent
   NAT_FITNESS_WEIGHT: 0.8


More descriptive msg upon 500 response status

--- src/circle-ci.coffee
+++ src/circle-ci.coffee
@@ -123,7 +123,7 @@
       when 401
         msg.send 'Not authorized.  Did you set HUBOT_CIRCLECI_TOKEN correctly?'
       when 500
-        msg.send 'Yikes!  I turned that circle into a square' # Don't send body since we'll get HTML back from Circle
+        msg.send 'Yikes!  I turned that circle into a square (CircleCI responded 500)' # Don't send body since we'll get HTML back from Circle
       when 200
         response = JSON.parse(body)
         handler response


added support for CE-HTML doctype

added support for CE-HTML doctype

--- src/coffeekup.coffee
+++ src/coffeekup.coffee
@@ -30,6 +30,7 @@
     '1.1': '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">',
     'basic': '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML Basic 1.1//EN" "http://www.w3.org/TR/xhtml-basic/xhtml-basic11.dtd">'
     'mobile': '<!DOCTYPE html PUBLIC "-//WAPFORUM//DTD XHTML Mobile 1.2//EN" "http://www.openmobilealliance.org/tech/DTD/xhtml-mobile12.dtd">'
+    'ce': '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "ce-html-1.0-transitional.dtd">'

   ck_self_closing = ['area', 'base', 'basefont', 'br', 'col', 'frame', 'hr', 'img', 'input', 'link', 'meta', 'param']


Tidy up indentations

Tidy up indentations

--- src/coffeepad.coffee
+++ src/coffeepad.coffee
@@ -1,11 +1,11 @@

-CoffeeEditor         = require './coffeeeditor'
-JSEditor             = require './jseditor'
-Storage              = require './storage'
-SaveFileModal        = require './savemodal'
-CoffeePadHeader      = require './coffeepad-header'
-CPFileItem           = require './fileitem'
-CPMultipleChoice     = require './multiplechoice'
+CoffeeEditor     = require './coffeeeditor'
+JSEditor         = require './jseditor'
+Storage          = require './storage'
+SaveFileModal    = require './savemodal'
+CoffeePadHeader  = require './coffeepad-header'
+CPFileItem       = require './fileitem'
+CPMultipleChoice = require './multiplechoice'

 module.exports = class CoffeePad extends KDView


collision with regular bounds

collision with regular bounds

--- src/collision.coffee
+++ src/collision.coffee
@@ -218,7 +218,7 @@
       unless target.xw? and target.yw?
         if target.width? and target.height?
           xw = target.width/2
-          y
+          yw = target.height/2
           return Collision.rayRectangle source, direction,
             xw:
             yw:


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/collision.coffee
+++ src/collision.coffee
@@ -218,7 +218,7 @@
       unless target.xw? and target.yw?
         return Collision.rayRectangle source, direction,
           xw: target.width/2
-          yw: target.height
+          yw: target.height/2

       xw = target.xw
       yw = target.yw


Global apply on initial load

Global apply on initial load

And some refactors including no class removal in case turbolinks happen.

--- app/assets/javascripts/_classes/timeago.coffee
+++ app/assets/javascripts/_classes/timeago.coffee
@@ -3,19 +3,36 @@

 class @Moe.Timeago
   constructor: ->
+    $ @initialize
+
+
+  initialize: =>
+    @run()
+    @observe()
+
+
+  run: (nodes = document.body) =>
+    $(nodes)
+      .find(".js-timeago")
+      .addBack(".js-timeago")
+      .timeago()
+      .each (_i, el) ->
+        return if el.timeagoApplied
+
+        el.title = new Date(el.dateTime).toString()
+        el.timeagoApplied = true
+
+
+  observe: =>
     @observer = new MutationObserver @onMutate
+
     config =
       childList: true
       subtree: true
+
     @observer.observe document, config

   onMutate: (mutations) =>
     for mutation in mutations
-      $(mutation.addedNodes)
-        .find(".js-timeago")
-        .addBack(".js-timeago")
-        .timeago()
-        .each (_i, el) ->
-          el.title = new Date(el.dateTime).toString()
-        .removeClass(".js-timeago")
+      @run mutation.addedNodes


More activity feed pages.

More activity feed pages.

--- app/assets/javascripts/activity-feed.js.coffee
+++ app/assets/javascripts/activity-feed.js.coffee
@@ -90,6 +90,7 @@
     @collection.bind 'remove', @remove
     @loadedAll = false
     @fetchInProgress = false
+    @page = 1
   add: (story) ->
     @views[story.cid] = new StoryView
       model: story
@@ -105,15 +106,15 @@
     $(".activity-feed").append @$el
   fetchMore: (baseURL) ->
     unless @loadedAll or @fetchInProgress
-      page = 1 + Math.floor(@collection.length / 20)
       @fetchInProgress = true
       that = this
-      $.ajax baseURL + "page=" + page,
+      $.ajax baseURL + "page=" + @page,
         dataType: "json"
         error: -> that.fetchInProgress = false
         success: (feedItems) ->
+          that.page += 1
           that.fetchInProgress = false
-          if feedItems.length < 20
+          if feedItems.length == 0
             that.loadedAll = true
             $(".activity-feed-spinner").hide()
           that.addStories feedItems


Bug fix

Bug fix

--- static/script.coffee
+++ static/script.coffee
@@ -10,23 +10,23 @@
   # FIXME: clean this quick and dirty code
   $('#Guangzhou').click () ->
     utils.setCookie 'city', 'Guangzhou', 7
-    utils.renderChart 'Guangzhou'
+    charts.renderChart 'Guangzhou'
     return false
   $('#Beijing').click () ->
     utils.setCookie 'city', 'Beijing', 7
-    utils.renderChart 'Beijing'
+    charts.renderChart 'Beijing'
     return false
   $('#Shanghai').click () ->
     utils.setCookie 'city', 'Shanghai', 7
-    utils.renderChart 'Shanghai'
+    charts.renderChart 'Shanghai'
     return false
   $('#Shenyang').click () ->
     utils.setCookie 'city', 'Shenyang', 7
-    utils.renderChart 'Shenyang'
+    charts.renderChart 'Shenyang'
     return false
   $('#Chengdu').click () ->
     utils.setCookie 'city', 'Chengdu', 7
-    utils.renderChart 'Chengdu'
+    charts.renderChart 'Chengdu'
     return false
   $(".ui.dropdown").dropdown
     on: 'hover'


Move scopeName and name to top

Move scopeName and name to top


better

better

--- api/apps/articles/model.coffee
+++ api/apps/articles/model.coffee
@@ -140,7 +140,7 @@
     query.$or = [
       { primary_featured_artist_ids: ObjectId(input.artist_id) }
       { featured_artist_ids: ObjectId(input.artist_id) }
-      { biography_for_artist_id: ObjectId(input.biography_for_artist_id) }
+      { biography_for_artist_id: ObjectId(input.artist_id) }
     ] if input.artist_id
     query.featured_artwork_ids = ObjectId input.artwork_id if input.artwork_id
     # Allow regex searching through the q param


Fix: _reconnectTimer treated as a browser timer object, not Timer class

--- src/connector.coffee
+++ src/connector.coffee
@@ -42,7 +42,7 @@
     return if @_isSocketConnected()

     # prepare for a new connection
-    clearTimeout @_reconnectTimer if @_reconnectTimer
+    @_reconnectTimer.stop()
     @_disconnectionReason = 'cannot-connect'
     @protocolParser.reset()


restores merge request coffee file

restores merge request coffee file

--- app/assets/javascripts/merge_request.js.coffee
+++ app/assets/javascripts/merge_request.js.coffee
@@ -22,7 +22,6 @@

     if $("a.btn-close").length
       @initTaskList()
-      @initMergeRequestBtnEventListeners()

   # Local jQuery finder
   $: (selector) ->
@@ -35,54 +34,6 @@
     else
       # Show the first tab (Commits)
       $('.merge-request-tabs a[data-toggle="tab"]:first').tab('show')
-
-  initMergeRequestBtnEventListeners: ->
-    _this = @
-    mergeRequestFailMessage = 'Unable to update this merge request at this time.'
-    $('a.btn-close, a.btn-reopen').on 'click', (e) ->
-      e.preventDefault()
-      e.stopImmediatePropagation()
-      $this = $(this)
-      isClose = $this.hasClass('btn-close')
-      shouldSubmit = $this.hasClass('btn-comment')
-      if shouldSubmit
-        _this.submitNoteForm($this.closest('form'))
-      $this.prop('disabled', true)
-      url = $this.attr('href')
-      $.ajax
-        type: 'PUT',
-        url: url,
-        error: (jqXHR, textStatus, errorThrown) ->
-          mergeRequestStatus = if isClose then 'close' else 'open'
-          new Flash(mergeRequestFailMessage, 'alert')
-        success: (data, textStatus, jqXHR) ->
-          if data.saved
-            if isClose
-              $('a.btn-close').addClass('hidden')
-              $('a.issuable-edit').addClass('hidden')
-              $('a.btn-reopen').removeClass('hidden')
-              $('div.status-box-closed').removeClass('hidden')
-              $('div.status-box-open').addClass('hidden')
-
-              $('div.mr-state-widget-closed').removeClass('hidden')
-              $('div.mr-state-widget-opened').addClass('hidden')
-            else
-              $('a.btn-reopen').addClass('hidden')
-              $('a.issuable-edit').removeClass('hidden')
-              $('a.btn-close').removeClass('hidden')
-              $('div.status-box-closed').addClass('hidden')
-              $('div.status-box-open').removeClass('hidden')
-
-              $('div.mr-state-widget-closed').addClass('hidden')
-              $('div.mr-state-widget-opened').removeClass('hidden')
-          else
-            new Flash(mergeRequestFailMessage, 'alert')
-          $this.prop('disabled', false)
-
-  submitNoteForm: (form) =>
-    noteText = form.find("textarea.js-note-text").val()
-    if noteText.trim().length > 0
-      form.submit()

   showAllCommits: ->
     this.$('.first-commits').remove()


重複してlog storeに情報が保存される不具合を修正

重複してlog storeに情報が保存される不具合を修正

--- app/assets/javascripts/stores/log_store.coffee
+++ app/assets/javascripts/stores/log_store.coffee
@@ -1,5 +1,4 @@
 logs = {}
-id = 1

 @LogStore = new EventEmitter2()
 $.extend @LogStore,
@@ -21,5 +20,6 @@
     when AbilitysheetConstants.RECEIVED_LOG_DATA
       for log in payload.logs
         logs[log.created_date] ||= []
+        continue unless logs[log.created_date].indexOf(log.title) is -1
         logs[log.created_date].push log.title
       LogStore.emitChange()


Use valueList to construct a select, if present.

--- js/collection-editor.coffee
+++ js/collection-editor.coffee
@@ -1,9 +1,18 @@
+build_input_for_valuelist = (valuelist) ->
+  select = $('<select>')
+  values = $(valuelist).find('value')
+  select.append $('<option>').append($(value).text()) for value in values
+  return select
+
 build_input_for_property = (property) ->
   input = switch $(property).attr('type')
     when 'markdown'
       $('<textarea>').attr('style','width:100%;height:20em')
     when 'string'
-      $('<input>').attr('style','width:100%')
+      if $(property).find('valueList').length > 0
+        build_input_for_valuelist $(property).find('valueList')[0]
+      else
+        $('<input>').attr('style','width:100%')
     when 'citeurn', 'citeimg'
       $('<input>').attr('style','width:100%')
     when 'datetime'


mp4: Fix crash when file is audio-only or video-only

--- mp4.coffee
+++ mp4.coffee
@@ -116,6 +116,12 @@
       process.stdout.write box.dump 0, 2
     return

+  hasVideo: ->
+    return @videoTrakBox?
+
+  hasAudio: ->
+    return @audioTrakBox?
+
   getSPS: ->
     avcCBox = @videoTrakBox.find 'avcC'
     return avcCBox.sequenceParameterSets[0]
@@ -143,6 +149,10 @@
       logger.debug "[mp4:#{@filename}] already paused"

   sendVideoPacketsSinceLastKeyFrame: (endSeconds, callback) ->
+    if not @videoTrakBox?  # video trak does not exist
+      callback? null
+      return
+
     # Get next sample number
     stblBox = @videoTrakBox.child('mdia').child('minf').child('stbl')
     sttsBox = stblBox.child('stts') # TimeToSampleBox
@@ -423,22 +433,34 @@
     callback?()

   getNumVideoSamples: ->
-    sttsBox = @videoTrakBox.find 'stts'
-    return sttsBox.getTotalSamples()
+    if @videoTrakBox?
+      sttsBox = @videoTrakBox.find 'stts'
+      return sttsBox.getTotalSamples()
+    else
+      return 0

   getNumAudioSamples: ->
-    sttsBox = @audioTrakBox.find 'stts'
-    return sttsBox.getTotalSamples()
+    if @audioTrakBox?
+      sttsBox = @audioTrakBox.find 'stts'
+      return sttsBox.getTotalSamples()
+    else
+      return 0

   # Returns the timestamp of the last sample in the file
   getLastTimestamp: ->
-    numVideoSamples = @getNumVideoSamples()
-    sttsBox = @videoTrakBox.find 'stts'
-    videoLastTimestamp = sttsBox.getDecodingTime(numVideoSamples).seconds
-
-    numAudioSamples = @getNumAudioSamples()
-    sttsBox = @audioTrakBox.find 'stts'
-    audioLastTimestamp = sttsBox.getDecodingTime(numAudioSamples).seconds
+    if @videoTrakBox?
+      numVideoSamples = @getNumVideoSamples()
+      sttsBox = @videoTrakBox.find 'stts'
+      videoLastTimestamp = sttsBox.getDecodingTime(numVideoSamples).seconds
+    else
+      videoLastTimestamp = 0
+
+    if @audioTrakBox?
+      numAudioSamples = @getNumAudioSamples()
+      sttsBox = @audioTrakBox.find 'stts'
+      audioLastTimestamp = sttsBox.getDecodingTime(numAudioSamples).seconds
+    else
+      audioLastTimestamp = 0

     if audioLastTimestamp > videoLastTimestamp
       return audioLastTimestamp


prevent infinite rerendering when point is open

--- @client/franklin.coffee
+++ @client/franklin.coffee
@@ -205,7 +205,7 @@
     has_community_points = community_points.length > 0

-    if get_selected_point()
+    if get_selected_point() && !@local.show_all_points
       @local.show_all_points = true
       save @local


remove stale prefix

remove stale prefix

--- assets/scripts/app/models/job.coffee
+++ assets/scripts/app/models/job.coffee
@@ -93,7 +93,7 @@
     return if @get('subscribed')
     @set('subscribed', true)
     if Travis.pusher
-      Travis.pusher.subscribe "#{prefix}job-#{@get('id')}",
+      Travis.pusher.subscribe "job-#{@get('id')}",

   unsubscribe: ->
     return unless @get('subscribed')


Simpler to destructure methods

Simpler to destructure methods

--- components/util/device.coffee
+++ components/util/device.coffee
@@ -1,5 +1,5 @@
 module.exports =
-  isIPad: ->
+  isIPad: isIPad = ->
     navigator.userAgent.toLowerCase()
       .indexOf('ipad') > -1

@@ -8,14 +8,14 @@
       .indexOf('Artsy-Mobile') > -1

   # http://stackoverflow.com/questions/4817029/whats-the-best-way-to-detect-a-touch-screen-device-using-javascript
-  isTouchDevice: ->
+  isTouchDevice: isTouchDevice = ->
     'ontouchstart' of window or 'onmsgesturechange' of window

   isPhoneLike: ->
-    @isTouchDevice() and not @isIPad()
+    isTouchDevice() and not isIPad()

   autofocus: ->
-    if @isTouchDevice() then undefined else true
+    if isTouchDevice() then undefined else true

   isRetina: ->
     (window.devicePixelRatio or 1) > 1


Updated district selector to work after contact update in editing mode.

--- src/javascripts/contact_us.coffee
+++ src/javascripts/contact_us.coffee
@@ -1,4 +1,32 @@
 jQuery ($) ->
+
+  # The form
+  $chooseDistrict = $("#choose-district")
+
+  showDistrictContact = (district) ->
+        # The form
+        $chooseDistrict = $("#choose-district")
+
+        # Hide all contact cards
+        $("aside.contact-us.multi-district .vcard").hide()
+        $("aside.contact-us.multi-district .no-district-available").remove()
+
+        # Show selected contact card
+        $chosen = $("#district-#{district}")
+        if($chosen.length)
+          $chosen.show()
+        else
+          $chooseDistrict.after('
Kontaktinformation för valt stadsområde saknas.
')
+
+        # Set district in select menu
+        $selectDistrict.val district
+
+        # Set selected district in cookie
+        $.cookie('city-district', district, { expires: 365, path: '/' } )
+
+  $(document.body).on "change", $chooseDistrict.find("select"), ->
+    showDistrictContact $chooseDistrict.find("select").val()
+
   $("aside.contact-us .write-to-us").click (event) ->
     event.preventDefault()
     $trigger = $(@)
@@ -20,7 +48,7 @@
           url: $trigger.attr('data-action')
           data: $form.serialize() + "&contactid=#{$trigger.attr('data-contact-id')}"
           success: (data) ->
-            $form.replaceWith(data)
+            $form.replaceWith(data)
             $that.find('form').show()
           error: (x, y, z) ->
             $form.after('
Ett fel inträffade, vänligen försök senare eller skicka ditt meddelande till nedanstående e-postadress.
')
@@ -34,32 +62,11 @@
   if $("aside.contact-us.multi-district").length
     $.cookie.json = true

-    # The form
-    $chooseDistrict = $("#choose-district")
-
     # Prevent the form for being submited
     $chooseDistrict.submit -> event.preventDefault()

     # Selectbox
     $selectDistrict = $chooseDistrict.find("select")
-
-    showDistrictContact = (district) ->
-      # Hide all contact cards
-      $("aside.contact-us.multi-district .vcard").hide()
-      $("aside.contact-us.multi-district .no-district-available").remove()
-
-      # Show selected contact card
-      $chosen = $("#district-#{district}")
-      if($chosen.length)
-        $chosen.show()
-      else
-        $chooseDistrict.after('
Kontaktinformation för valt stadsområde saknas.
')
-
-      # Set district in select menu
-      $selectDistrict.val district
-
-      # Set selected district in cookie
-      $.cookie('city-district', district, { expires: 365, path: '/' } )

     # Select district from cookie on load
     storedDistrict = $.cookie('city-district')
@@ -67,10 +74,6 @@
       showDistrictContact storedDistrict
     else
       $("aside.contact-us.multi-district .vcard").hide()
-
-    # District selector is changed by user or address search
-    $selectDistrict.change ->
-      showDistrictContact $(@).val()

     # Autocomplete for street addresses
     # Get address suggestions w/ districts from SBK's map service
@@ -89,4 +92,4 @@
               district: item.towndistrict
       minLength: 2
       select: (event, ui) ->
-        showDistrictContact ui.item.district.toLowerCase()+        showDistrictContact ui.item.district.toLowerCase()


Require controllers in CoffeeScript MVC, and fix bug that was preventing HTTP responses

--- app/templates/mvc-coffee/config/express.coffee
+++ app/templates/mvc-coffee/config/express.coffee
@@ -1,4 +1,6 @@
 express = require 'express'
+fs = require 'fs'
+path = require 'path'

 favicon = require 'serve-favicon'
 logger = require 'morgan'
@@ -13,14 +15,19 @@

     # app.use(favicon(config.root + '/public/img/favicon.ico'));
     app.use logger 'dev'
-    app.use bodyParser.json
+    app.use bodyParser.json()
     app.use bodyParser.urlencoded(
         extended: true
     )
-    app.use cookieParser
-    app.use compress
+    app.use cookieParser()
+    app.use compress()
     app.use express.static config.root + '/public'
-    app.use methodOverride
+    app.use methodOverride()
+
+    controllersPath = path.join __dirname, '../app/controllers'
+    fs.readdirSync(controllersPath).forEach (file) ->
+        if file.indexOf('.coffee') >= 0
+            require(controllersPath + '/' + file)(app)

     app.use (req, res, next) ->
         err = new Error 'Not Found'


Implement feature request of issue #19 (#33)

--- lib/firepad-share.coffee
+++ lib/firepad-share.coffee
@@ -1,6 +1,7 @@
 {CompositeDisposable} = require 'atom'
 {Emitter} = require 'event-kit'
 Crypto = require 'crypto'
+os = require 'os'
 Firebase = require 'firebase'
 Firepad = require './firepad-lib'

@@ -14,18 +15,39 @@
     @handleEditorEvents()

     hash = Crypto.createHash('sha256').update(@shareIdentifier).digest('base64')
+    @userId = Math.random().toString(36).slice(2, 10)
+
     @firebase = new Firebase(atom.config.get('firepad.firebaseUrl')).child(hash)
+    @firebaseUsers = @firebase.child('users')
+    @firebaseContent = @firebase.child('content')

-    @firebase.once 'value', (snapshot) =>
-      options = {sv_: Firebase.ServerValue.TIMESTAMP}
+    @firebaseUsers.on 'value', (snapshot) =>
+      @attachDecoration(snapshot)
+
+    @firebaseUserSelf = @firebaseUsers.push
+      userId: @userId
+      displayName: os.hostname()
+      pos: @editor.getCursorBufferPosition()
+      color: '#' + Math.floor(Math.random() * 256 * 256 * 256).toString(16)
+
+    @firebaseCursor = @firebaseUserSelf.child('pos')
+
+    @firebaseContent.once 'value', (snapshot) =>
+      options =
+        sv_: Firebase.ServerValue.TIMESTAMP
+        userId: @userId
       if not snapshot.val() and @editor.getText() isnt ''
         options.overwrite = true
       else
         @editor.setText ''
-      @firepad = Firepad.fromAtom @firebase, @editor, options
+      @firepad = Firepad.fromAtom @firebaseContent, @editor, options
+
     #   @shareview.show(@shareIdentifier)

   handleEditorEvents: ->
+    @subscriptions.add @editor.onDidChangeCursorPosition (event) =>
+      @updateCursorPosition(event)
+
     @subscriptions.add @editor.onDidDestroy =>
       @remove()

@@ -44,10 +66,48 @@
   getShareIdentifier: ->
     @shareIdentifier

+  attachDecoration: (userSnapshot) ->
+    # reset markers
+    @markers?.forEach (marker) =>
+      marker.destroy()
+    @markers = []
+
+    # attach decorations
+    users = userSnapshot.val()
+    for uid, user of users
+      # uncomment below if you want to hide the cursor of self
+      # continue if user.userId is @userId
+      marker = @editor.markBufferRange([
+        [user.pos.row, 0]
+        [user.pos.row, 0]
+      ], invalidate: 'never')
+      @markers.push marker
+      decoration = @editor.decorateMarker marker,
+        type: 'overlay',
+        item: @getCursorElement user
+
+  getCursorElement: (user) ->
+    element = document.createElement('div')
+    element.setAttribute 'class', 'popover-list'
+    element.setAttribute 'style',
+      """
+      background-color: #{user.color};
+      display: block;
+      padding: 3px;
+      font-size: 12px;
+      color: white;
+      """
+    element.textContent = user.displayName
+    element
+
+  updateCursorPosition: (event) ->
+    @firebaseCursor.set(event.newBufferPosition)
+
   remove: ->
     # @firepad.dispose() # Won't work #4
     # @subscriptions.dispose()
     # @emitter.emit 'did-destroy'
+    @firebaseUserSelf.remove()
     atom.notifications.addWarning('The "unshare" function is due dependencies buggy, and wont work. Please close the pane, to stop sharing!')

   onDidDestroy: (callback) ->


Added setting to modify the per project configuration file path

--- lib/flex-tool-bar.coffee
+++ lib/flex-tool-bar.coffee
@@ -17,6 +17,9 @@
     toolBarConfigurationFilePath:
       type: 'string'
       default: atom.getConfigDirPath()
+    toolBarProjectConfigurationFilePath:
+      type: 'string'
+      default: '.'
     reloadToolBarWhenEditConfigFile:
       type: 'boolean'
       default: true
@@ -99,13 +102,18 @@

   resolveProjectConfigPath: ->
     @projectToolbarConfigPath = null
+    relativeProjectConfigPath = atom.config.get 'flex-tool-bar.toolBarProjectConfigurationFilePath'
     editor = atom.workspace.getActivePaneItem()
     file = editor?.buffer?.file or editor?.file

     if file?.getParent()?.path?
       for pathToCheck in atom.project.getPaths()
         if file.getParent().path.includes(pathToCheck)
-          @projectToolbarConfigPath = fs.resolve pathToCheck, 'toolbar', ['cson', 'json5', 'json', 'js', 'coffee']
+          pathToCheck = path.join pathToCheck, relativeProjectConfigPath
+          if fs.isFileSync(pathToCheck)
+            @projectToolbarConfigPath = pathToCheck
+          else
+            @projectToolbarConfigPath = fs.resolve pathToCheck, 'toolbar', ['cson', 'json5', 'json', 'js', 'coffee']

     if @projectToolbarConfigPath is @configFilePath
       @projectToolbarConfigPath = null


Add uri to search for packages in InstallPanel

--- lib/install-panel.coffee
+++ lib/install-panel.coffee
@@ -75,25 +75,49 @@

     @searchPackagesButton.on 'click', =>
       unless @searchPackagesButton.hasClass('selected')
-        @searchType = 'packages'
-        @searchPackagesButton.addClass('selected')
-        @searchThemesButton.removeClass('selected')
-        @searchEditorView.getModel().setPlaceholderText('Search packages')
-        @publishedToText.text('Packages are published to ')
-        @atomIoURL = 'https://atom.io/packages'
-        @loadFeaturedPackages()
+        @setSearchType('package')
         @performSearch()

     @searchThemesButton.on 'click', =>
       unless @searchThemesButton.hasClass('selected')
-        @searchType = 'themes'
-        @searchThemesButton.addClass('selected')
-        @searchPackagesButton.removeClass('selected')
-        @searchEditorView.getModel().setPlaceholderText('Search themes')
-        @publishedToText.text('Themes are published to ')
-        @atomIoURL = 'https://atom.io/themes'
-        @loadFeaturedPackages(true)
+        @setSearchType('theme')
         @performSearch()
+
+  setSearchType: (searchType) ->
+    if searchType is 'theme'
+      @searchType = 'themes'
+      @searchThemesButton.addClass('selected')
+      @searchPackagesButton.removeClass('selected')
+      @searchEditorView.getModel().setPlaceholderText('Search themes')
+      @publishedToText.text('Themes are published to ')
+      @atomIoURL = 'https://atom.io/themes'
+      @loadFeaturedPackages(true)
+    else if searchType is 'package'
+      @searchType = 'packages'
+      @searchPackagesButton.addClass('selected')
+      @searchThemesButton.removeClass('selected')
+      @searchEditorView.getModel().setPlaceholderText('Search packages')
+      @publishedToText.text('Packages are published to ')
+      @atomIoURL = 'https://atom.io/packages'
+      @loadFeaturedPackages()
+
+  beforeShow: (options) ->
+    return unless options?.uri?
+    query = @extractQueryFromURI(options.uri)
+    if query?
+      {searchType, packageName} = query
+      @setSearchType(searchType)
+      @searchEditorView.setText(packageName)
+      @performSearch()
+
+  PackageNameRegex = /config\/install\/(package|theme):([a-z0-9-_]+)/i
+  extractQueryFromURI: (uri) ->
+    matches = PackageNameRegex.exec(uri)
+    if matches?
+      [__, searchType, packageName] = matches
+      {searchType, packageName}
+    else
+      null

   performSearch: ->
     if query = @searchEditorView.getText().trim()


Tweak Enter at the beginning of line

Tweak Enter at the beginning of line

--- src/js/esarea.coffee
+++ src/js/esarea.coffee
@@ -44,6 +44,7 @@
 handleEnterKey = (e) ->
   return if e.metaKey || e.ctrlKey || e.shiftKey # for cmd + enter
   return unless currentLine = getCurrentLine(e)
+  return if currentLine.start == currentLine.caret
   if match = currentLine.text.match(/^(\s*(?:-|\+|\*) (?:\[(?:x| )\] )?)\s*\S/)
     # smart indent with list
     if currentLine.text.match(/^(\s*(?:-|\+|\*) (?:\[(?:x| )\] ))\s*$/)


Refactored, added some empty returns to reduce the number of return statements in generated js

--- src/ki-router.coffee
+++ src/ki-router.coffee
@@ -68,6 +68,7 @@
         console.log("ki-router: " + JSON.stringify(arguments))
       else
         console.log(arguments)
+    return

   add: (urlPattern, fn, metadata) =>
     @routes.push({route: new SinatraRouteParser(urlPattern), fn: fn, urlPattern: urlPattern, metadata: metadata})
@@ -113,6 +114,7 @@
   historyApiRouting: () =>
     @hashchangeSupport = false
     @transparentRouting()
+    return

   transparentRouting: () =>
     @init = true
@@ -120,6 +122,7 @@
       @attachClickListener()
       @attachLocationChangeListener()
       @renderInitialView()
+      return
     finally
       @init = false
       @initDone = true
@@ -129,6 +132,7 @@
     if !@hashchangeSupport
       throw new Error("No hashchange support!")
     @transparentRouting()
+    return

   attachClickListener: =>
     if @pushStateSupport || @hashchangeSupport
@@ -141,18 +145,23 @@
           if @checkIfOkToRender(event, aTag)
             href = aTag.attributes.href.nodeValue
             @log("Click event passed all checks")
-            if @checkIfHashBaseUrlRedirectNeeded()
-              @log("Using hashbang change to trigger rendering for", href)
-              @disableEventDefault(event)
-              window.location.href = @hashBaseUrl + "#!" + href
-            else if @exec(href)
-              @log("Rendered", href)
-              @disableEventDefault(event)
-              @clickCount += 1
-              @updateUrl(href)
-            else
-              @log("Letting browser render url because no matching route", href)
-            return
+            @renderUrlOrRedirect(href, event)
+        return
+    return
+
+  renderUrlOrRedirect: (href, event) =>
+    if @checkIfHashBaseUrlRedirectNeeded()
+      @log("Using hashbang change to trigger rendering for", href)
+      @disableEventDefault(event)
+      window.location.href = @hashBaseUrl + "#!" + href
+    else if @exec(href)
+      @log("Rendered", href)
+      @disableEventDefault(event)
+      @clickCount += 1
+      @updateUrl(href)
+    else
+      @log("Letting browser render url because no matching route", href)
+    return

   checkIfHashBaseUrlRedirectNeeded: () =>
     !@pushStateSupport && @hashchangeSupport && @hashBaseUrl && @hashBaseUrl != window.location.pathname
@@ -165,10 +174,12 @@
     @blog("- Link host same as current window", @targetHostSame(aTag))

   disableEventDefault: (ev) =>
-    if ev.preventDefault
-      ev.preventDefault()
-    else
-      ev.returnValue = false
+    if ev
+      if ev.preventDefault
+        ev.preventDefault()
+      else
+        ev.returnValue = false
+    return

   blog: (str, v) =>
     @log(str + ", result: " + v)
@@ -229,6 +240,7 @@
           href = window.location.pathname
           @log("Rendering popstate", href)
           @renderUrl(href)
+        return
     else
       if @hashchangeSupport
         @addListener window, "hashchange", (event) =>
@@ -236,6 +248,8 @@
             href = window.location.hash.substring(2)
             @log("Rendering hashchange", href)
             @renderUrl(href)
+          return
+    return

   renderInitialView: =>
     initialUrl = window.location.pathname
@@ -248,6 +262,7 @@
           initialUrl = window.location.hash.substring(2)
     @log("Rendering initial page")
     @renderUrl(initialUrl)
+    return

   renderUrl: (url) =>
     if ret = @exec(url)
@@ -265,6 +280,7 @@
       else
         if @hashchangeSupport
           window.location.hash = "!" + href
+    return

   addListener: (element, event, fn) =>
     if element.addEventListener  # W3C DOM


final code cleanup for popup.coffee

final code cleanup for popup.coffee

--- src/lib/popup.coffee
+++ src/lib/popup.coffee
@@ -28,11 +28,9 @@
     document.body.innerHTML = ext.popupHtml
     # Calculate the widest text used by the `div` elements in the popup and
     # assign it to all of the others.
-    textItems = document.querySelectorAll '.text'
-    width     = 0
-    for textItem in textItems when textItem.scrollWidth > width
-      width = textItem.scrollWidth
-    textItem.style.width = "#{width}px" for textItem in textItems
+    items = document.querySelectorAll '.text'
+    width = item.scrollWidth for item in items when item.scrollWidth > width
+    item.style.width = "#{width}px" for item in items
     log.debug "Widest textual item in popup is #{width}px"
     width = document.querySelector('#itemList li').scrollWidth
     document.querySelector('#loadDiv .progress').style.width = "#{width + 2}px"


fixed typo in shell in normalizePath function declaration

--- src/lib/shell.coffee
+++ src/lib/shell.coffee
@@ -27,7 +27,7 @@
 app.network = null
 app.filename = null

-normalizePath: (path) ->
+normalizePath = (path) ->
     return path if path.substr(0, 1) is "/"
     "#{process.cwd()}/#{path}"


handle auth routes in force

handle auth routes in force

--- lib/middleware/proxy_to_gravity.coffee
+++ lib/middleware/proxy_to_gravity.coffee
@@ -15,7 +15,7 @@
   '/oauth2*', '/sitemap*', "/auction-registration/*", '/assets/common-e1*.js', '/unsubscribe*'
 ]
 AUTH_ROUTES = [
-  '/post', "/users/sign_in", "/users/sign_out", "/users/auth/facebook", "/users/auth/twitter", "/user/delete", "/profile/edit", "/user/edit"
+  '/post', "/profile/edit", "/user/edit"
 ]
 for route in ROUTES
   app.all route, (req, res) ->


kite: add more instrumentation and logging

--- src/kite/kite.coffee
+++ src/kite/kite.coffee
@@ -9,6 +9,8 @@

   wrapApi = require './wrap-api.coffee'
   handleIncomingMessage = require '../incoming-message-handler.coffee'
+
+  enableLogging = require '../logging.coffee'

   # ready states:
   [ NOTREADY, READY, CLOSED ] = [0,1,3]
@@ -32,6 +34,8 @@
     @options.autoConnect   ?= yes
     @options.autoReconnect ?= yes

+    enableLogging @options.name, this, @options.logLevel
+
     @readyState = NOTREADY

     @initBackoff()  if @options.autoReconnect
@@ -40,7 +44,7 @@

     @proto.on 'request', (req) =>
       @ready => @ws.send JSON.stringify req
-      @emit 'info', "proto request", req
+      @emit 'debug', "Sending: ", JSON.stringify req
       return

     @connect()  if @options.autoConnect
@@ -60,7 +64,7 @@
   disconnect: (reconnect = false) ->
     @options.autoReconnect = !!reconnect
     @ws.close()
-    @emit 'info', "Disconnecting from #{ @options.url }"
+    @emit 'notice', "Disconnecting from #{ @options.url }"
     return

   # event handlers:
@@ -68,7 +72,7 @@
     @readyState = READY
     @emit 'connected'
     @emit 'ready'
-    @emit 'info', "Connected to Kite: #{ @options.url }"
+    @emit 'notice', "Connected to Kite: #{ @options.url }"
     @clearBackoffTimeout()
     return

@@ -83,13 +87,11 @@
     return

   onMessage: ({ data }) ->
-    @emit 'info', "onMessage", data
     handleIncomingMessage.call this, @proto, data
     return

   onError: (err) ->
     @emit 'error', "Websocket error!"
-    @emit 'info', "#{ @options.url } error: #{ err.data }"
     return

   unwrapMessage: (message) ->


Removing file

Removing file

--- src/matchhost.coffee
+++ src/matchhost.coffee
@@ -16,7 +16,7 @@
     .forEach (host) ->
       diffLength = diffChars(host, string).length
       differences = diffLength unless differences
-      if diffLength <= differences and match.password
+      if diffLength <= differences
         match = host
         differences = diffLength


override dnode.Bridge for cheerio local

override dnode.Bridge for cheerio local

--- modules/gui/platforms/node/tools/index.coffee
+++ modules/gui/platforms/node/tools/index.coffee
@@ -38,7 +38,11 @@

         tools.page route, new floyd.Config

+            local:
+                type: 'Context'
+
             remote:
                 type: 'dnode.Bridge'

-        , config+        , config
+


Subscribe with new event methods in PaneView

--- src/pane-view.coffee
+++ src/pane-view.coffee
@@ -1,6 +1,7 @@
 {$, View} = require './space-pen-extensions'
 Delegator = require 'delegato'
 {deprecate} = require 'grim'
+{CompositeDisposable} = require 'event-kit'
 PropertyAccessors = require 'property-accessors'

 Pane = require './pane'
@@ -33,6 +34,8 @@
   previousActiveItem: null

   initialize: (args...) ->
+    @disposables = new CompositeDisposable
+
     if args[0] instanceof Pane
       @model = args[0]
     else
@@ -44,13 +47,13 @@
     @handleEvents()

   handleEvents: ->
-    @subscribe @model.$activeItem, @onActiveItemChanged
-    @subscribe @model, 'item-added', @onItemAdded
-    @subscribe @model, 'item-removed', @onItemRemoved
-    @subscribe @model, 'item-moved', @onItemMoved
-    @subscribe @model, 'before-item-destroyed', @onBeforeItemDestroyed
-    @subscribe @model, 'activated', @onActivated
-    @subscribe @model.$active, @onActiveStatusChanged
+    @disposables.add @model.onDidChangeActiveItem(@onActiveItemChanged)
+    @disposables.add @model.onDidAddItem(@onItemAdded)
+    @disposables.add @model.onDidRemoveItem(@onItemRemoved)
+    @disposables.add @model.onDidMoveItem(@onItemMoved)
+    @disposables.add @model.onWillDestroyItem(@onBeforeItemDestroyed)
+    @disposables.add @model.onDidActivate(@onActivated)
+    @disposables.add @model.onDidChangeActive(@onActiveStatusChanged)

     @subscribe this, 'focusin', => @model.focus()
     @subscribe this, 'focusout', => @model.blur()
@@ -219,6 +222,7 @@
     @closest('.panes').view()

   beforeRemove: ->
+    @disposables.dispose()
     @model.destroy() unless @model.isDestroyed()

   remove: (selector, keepData) ->


Properly emit item argument in pane:before-item-destroyed legacy event

--- src/pane-view.coffee
+++ src/pane-view.coffee
@@ -149,7 +149,7 @@
   onItemMoved: ({item, newIndex}) =>
     @trigger 'pane:item-moved', [item, newIndex]

-  onBeforeItemDestroyed: (item) =>
+  onBeforeItemDestroyed: ({item}) =>
     @unsubscribe(item) if typeof item.off is 'function'
     @trigger 'pane:before-item-destroyed', [item]


.css('opacity', 0) fix

.css('opacity', 0) fix

You can't set i.e. opacity to 0 due to `if value` will return false for 0.

Fixed by chaining `if value` to `if typeof value != "undefined"`.

--- src/quo.style.coffee
+++ src/quo.style.coffee
@@ -27,7 +27,7 @@
         _existsClass name, this[0].className

     $$.fn.style = (property, value) ->
-        if value
+        if typeof value != "undefined"
             @each -> @style[property] = value
         else
             this[0].style[property] or _computedStyle(this[0], property)