working on register for enhancing require(), should support auto 'import' resolutions

--- src/index.coffee
+++ src/index.coffee
@@ -93,10 +93,16 @@
 """, wrap: false

 # convenience to add a new YANG module into the Registry
-exports.require = -> exports.Registry.extends arguments...
+exports.require = (filename, opts={}) ->
+  # TODO: enable a special 'import' extension that handles dependent schemas if NOT found

-# enable require to handle .yin and .yang extensions
-exports.register = ->
+  schema = (fs.readFileSync filename, 'utf-8')
+  x = yang schema, wrap: false
+  exports.Registry.extends x
+  return x
+
+# enable require to handle .yang extensions
+exports.register = (opts={}) ->
   require.extensions?['.yang'] = (m, filename) ->
-    m.exports = exports.require (fs.readFileSync filename, 'utf-8')
+    m.exports = exports.require filename, opts
   return exports


Processing unsuccessful express server run

--- src/index.coffee
+++ src/index.coffee
@@ -34,18 +34,21 @@
     else
       server = http.createServer app
     app.use connect.directory(if typeof opt.root == "object" then opt.root[0] else opt.root)
-    server.listen opt.port
-    @log "Server started http://#{opt.host}:#{opt.port}"
-    if opt.livereload
-      tiny_lr.Server::error = ->
-      if opt.https?
-        lr = tiny_lr
-          key: opt.https.key || fs.readFileSync __dirname + '/certs/server.key'
-          cert: opt.https.cert || fs.readFileSync __dirname + '/certs/server.crt'
+    server.listen opt.port, (err) ->
+      if err
+        @log "Error on starting server: #{err}"
       else
-        lr = tiny_lr()
-      lr.listen opt.livereload.port
-      @log "LiveReload started on port #{opt.livereload.port}"
+        @log "Server started http://#{opt.host}:#{opt.port}"
+        if opt.livereload
+          tiny_lr.Server::error = ->
+          if opt.https?
+            lr = tiny_lr
+              key: opt.https.key || fs.readFileSync __dirname + '/certs/server.key'
+              cert: opt.https.cert || fs.readFileSync __dirname + '/certs/server.crt'
+          else
+            lr = tiny_lr()
+          lr.listen opt.livereload.port
+          @log "LiveReload started on port #{opt.livereload.port}"

   middleware: ->
     middleware = if opt.middleware then opt.middleware.call(this, connect, opt) else []


Use a queue for scoring

Use a queue for scoring

--- src/index.coffee
+++ src/index.coffee
@@ -209,16 +209,6 @@
         ], callback
       (results, callback) ->
         [followings, ids] = results
-        downloadAndScore = (id, callback) ->
-          async.waterfall [
-            (callback) ->
-              downloadFullPost id, callback
-            (fullPost, callback) ->
-              if fullPost?.current_user?.voted_for_post || fullPost?.current_user?.commented_on_post
-                callback null, null
-              else
-                scorePost fullPost, callback
-          ], callback
         scorePost = (post, callback) ->
           inputs =
             relatedPostUpvotes: _.filter(post.related_posts, (related) -> related?.current_user?.voted_for_post).length
@@ -235,6 +225,7 @@
             else
               post.score = outputs.score
               callback null, post
+        q = async.queue scorePost, 64
         downloadFullPost = (id, callback) ->
           token = req.token
           headers =
@@ -247,6 +238,16 @@
             else
               results = JSON.parse(body)
               callback null, results.post
+        downloadAndScore = (id, callback) ->
+          async.waterfall [
+            (callback) ->
+              downloadFullPost id, callback
+            (fullPost, callback) ->
+              if fullPost?.current_user?.voted_for_post || fullPost?.current_user?.commented_on_post
+                callback null, null
+              else
+                q.push fullPost, callback
+          ], callback
         async.map ids, downloadAndScore, callback
     ], (err, scored) ->
       if err


LinkView automatically syncs Widget w/ SyncMaster.

--- src/index.coffee
+++ src/index.coffee
@@ -97,6 +97,16 @@
             else
                 return false

+    class LinkView extends SyncView
+        constructor: (syncMaster, render) ->
+            super(syncMaster)
+            @render = render
+
+        onDataUpdated: (source, version, data) ->
+            if super(source, version, data)
+                @render(data)
+
+
     class DropboxView extends SyncView
         constructor: (syncMaster, path) ->
             super(syncMaster)
@@ -223,9 +233,12 @@
     coffeeconsole = new CoffeeConsoleWidget()
     coffeeconsole.title.text = 'CoffeeScript REPL'

-    linkView = new LinkViewWidget(syncMaster)
-    linkView.title.text = 'Link View'
-
+    linkViewWidget = new LinkViewWidget(syncMaster)
+    linkViewWidget.title.text = 'Link View'
+
+    linkView = new LinkView(syncMaster, linkViewWidget.render)
+
+    spy.linkViewWidget = linkViewWidget
     spy.linkView = linkView

@@ -254,7 +267,7 @@

     cmTaskpaper.title.text = 'CodeMirror View'
     panel.insertRight(cmTaskpaper)
-    panel.insertRight(linkView)
+    panel.insertRight(linkViewWidget)
     panel.insertBottom(coffeeconsole, cmTaskpaper)
     panel.attach(document.body)


cloned the options object

cloned the options object

--- src/index.coffee
+++ src/index.coffee
@@ -45,6 +45,14 @@
     callback error, plugins
 #------------------------------------------------------------------------------

+clone = (obj) ->
+  return obj  if null is obj or "object" isnt typeof obj
+  copy = obj.constructor()
+  for attr of obj
+    copy[attr] = obj[attr]  if obj.hasOwnProperty(attr)
+  copy
+
+
 module.exports = class StaticJadeCompiler
   brunchPlugin: yes
   type: 'template'
@@ -54,9 +62,11 @@
     @locals       = @config.plugins?.jade?.locals or () ->
     @extension    = @config.plugins?.static_jade?.extension ? ".jade"
     @relAssetPath = @config.plugins?.static_jade?.asset ? "app/assets"
-    @options      = @config.plugins?.jade?.options \
+    options      = @config.plugins?.jade?.options \
                       or @config.plugins?.jade \
                       or {}
+
+    @options = clone options

     mkdirp.sync @relAssetPath

@@ -99,12 +109,14 @@
     return newpath

   fromJade2Html: (jadeFilePath, callback) ->
+    options = @options
     try
       fs.readFile jadeFilePath, (err,data) =>
         if err
           throw err

         @options.filename = jadeFilePath
+
         fn = jade.compile data,
           @options


white_check_mark: Add traverseTree test

white_check_mark: Add traverseTree test

--- spec/fs-plus-spec.coffee
+++ spec/fs-plus-spec.coffee
@@ -241,6 +241,86 @@
         onPath = (childPath) -> paths.push(childPath)
         fs.symlinkSync(path.join(directory, 'source'), path.join(directory, 'destination'))
         fs.traverseTreeSync(directory, onPath)
+        expect(paths.length).toBe 0
+
+  describe ".traverseTree(path, onFile, onDirectory, onDone)", ->
+    it "calls fn for every path in the tree at the given path", ->
+      paths = []
+      onPath = (childPath) ->
+        paths.push(childPath)
+        true
+      done = false
+      onDone = ->
+        done = true
+      fs.traverseTree fixturesDir, onPath, onPath, onDone
+
+      waitsFor ->
+        done
+
+      runs ->
+        expect(paths).toEqual fs.listTreeSync(fixturesDir)
+
+    it "does not recurse into a directory if it is pruned", ->
+      paths = []
+      onPath = (childPath) ->
+        if childPath.match(/\/dir$/)
+          false
+        else
+          paths.push(childPath)
+          true
+      done = false
+      onDone = ->
+        done = true
+
+      fs.traverseTree fixturesDir, onPath, onPath, onDone
+
+      waitsFor ->
+        done
+
+      runs ->
+        expect(paths.length).toBeGreaterThan 0
+        for filePath in paths
+          expect(filePath).not.toMatch /\/dir\//
+
+    it "returns entries if path is a symlink", ->
+      symlinkPath = path.join(fixturesDir, 'symlink-to-dir')
+      symlinkPaths = []
+
+      onSymlinkPath = (path) -> symlinkPaths.push(path.substring(symlinkPath.length + 1))
+
+      regularPath = path.join(fixturesDir, 'dir')
+      paths = []
+      onPath = (path) -> paths.push(path.substring(regularPath.length + 1))
+
+      symlinkDone = false
+      onSymlinkPathDone = ->
+        symlinkDone = true
+
+      regularDone = false
+      onRegularPathDone = ->
+        regularDone = true
+
+      fs.traverseTree symlinkPath, onSymlinkPath, onSymlinkPath, onSymlinkPathDone
+      fs.traverseTree regularPath, onPath, onPath, onRegularPathDone
+
+      waitsFor ->
+        symlinkDone && regularDone
+
+      runs ->
+        expect(symlinkPaths).toEqual(paths)
+
+    it "ignores missing symlinks", ->
+      directory = temp.mkdirSync('symlink-in-here')
+      paths = []
+      onPath = (childPath) -> paths.push(childPath)
+      fs.symlinkSync(path.join(directory, 'source'), path.join(directory, 'destination'))
+      done = false
+      onDone = ->
+        done = true
+      fs.traverseTree directory, onPath, onPath, onDone
+      waitsFor ->
+        done
+      runs ->
         expect(paths.length).toBe 0

   describe ".traverseTree(path, onFile, onDirectory, onDone)", ->


Fix bug where storage zone are not displayed in reception edit page

--- app/assets/javascripts/cocoon/iceberg.js.coffee
+++ app/assets/javascripts/cocoon/iceberg.js.coffee
@@ -23,8 +23,6 @@
         @_bindSelectorsInitialization()
         @newForm().addClass('hidden')
         @setFormSubmitable()
-
-

     _bindButtons: (form) ->
       # console.log '_bindButtons:this', this
@@ -150,7 +148,8 @@

     interpolateStoring: ->
       zones = []
-      @newForm().find('.storing-fields').each ->
+      form = if @newForm().length > 0 then @newForm() else @oldForm()
+      form.find('.storing-fields').each ->
         zones.push
           quantity: $(this).find('input.storing-quantity').val()
           unit: $(this).find('.item-population-unit-name').html()


Bugfix for setting "edit" button on first drawing

--- app/assets/javascripts/image_question.js.coffee
+++ app/assets/javascripts/image_question.js.coffee
@@ -126,8 +126,8 @@
       # if the form is still open it would make sense to put the error there

   update_display: ->
-    @current_thumbnail = $("#{@form_sel} [name=\"embeddable_image_question_answer[annotated_image_url]\"]").val() ||
-                         $("#{@form_sel} [name=\"embeddable_image_question_answer[image_url]\"]").val()
+    @annotated_url = $("#{@form_sel} [name=\"embeddable_image_question_answer[annotated_image_url]\"]").val()
+    @current_thumbnail = @annotated_url || $("#{@form_sel} [name=\"embeddable_image_question_answer[image_url]\"]").val()
     @current_annotation = $("#image_question_annotation_for_#{@image_question_id}").val()
     @$thumbnail.show()
     @$thumbnail.attr("src", @current_thumbnail)
@@ -142,7 +142,7 @@
     if (@$snapshot_button.length > 0) and (@current_src or @current_thumbnail)
       # This is a shutterbug question, so it's answered if there's a thumbnail
       @show_edit_buttons()
-    else if (@$drawing_button.length > 0) and @current_annotation
+    else if (@$drawing_button.length > 0) and (@current_annotation or @annotated_url)
       # This is a drawing question, so an annotation is needed for it to be answered
       @show_edit_buttons()


only set mobile-layout-inner padding-top after all logo has loaded

--- app/assets/javascripts/mobile/_mobile.js.coffee
+++ app/assets/javascripts/mobile/_mobile.js.coffee
@@ -5,6 +5,11 @@
     if like_count is 1 then 'like' else 'likes'

 $ ->
+    window.$mobile_header = ($ '#mobile_header')
+    window.$menu = ($ '#menu')
+    window.$menu_button = ($ '#menu_button')
+    window.has_touch_events = !($ 'html').hasClass('no-touch')
+    window.toggle_menu = -> $menu.toggle()
     window.forceReflow = (elem = document.documentElement) ->
         #http://stackoverflow.com/a/11478853/382982
         hack = document.createElement("div")
@@ -15,35 +20,35 @@
             hack = null
         , 0

-    window.header_height =  ($ '.mobile-header').height()
-    window.has_touch_events = !($ 'html').hasClass('no-touch')
-    toggle_menu = -> ($ '#menu').toggle()
-
     if has_touch_events
-        ($ '.mobile-layout-inner').css('padding-top', header_height)
-
         # iOS fixed header hacks
         forceReflow()
         ($ 'input, textarea, select')
             .focus(->
-                ($ '.mobile-header').addClass('absolute')
+                $mobile_header.addClass('absolute')
             ).blur(->
-                ($ '.mobile-header').removeClass('absolute')
+                $mobile_header.removeClass('absolute')
             )
         ($ window).scroll -> forceReflow()

-        ($ '#menu_button').tap((e) ->
+        $menu_button.tap((e) ->
             e.preventDefault()
             toggle_menu())
     else
-        ($ '#menu_button').click((e) ->
+        $menu_button.click((e) ->
             e.preventDefault()
             toggle_menu())
+
+# ensure logos have been loaded
+# before calculating header height
+window.addEventListener 'load', ->
+    window.header_height =  $mobile_header.height()
+    if has_touch_events
+        ($ '#mobile_layout_inner').css('padding-top', header_height)

     window.scroll_to_error = ($context) ->
         error_y = $context.find('.unhappy').first().offset().top - header_height - 20
         scrollTo(0, error_y)
-

 if gon.page is 'stores_show' or gon.page is 'products_show' or gon.page is 'products_index' or gon.page is 'stores_new'
     # only reveal product gallery controls if there


Yamon

Yamon

--- app/assets/javascripts/models/message.js.coffee
+++ app/assets/javascripts/models/message.js.coffee
@@ -2,9 +2,7 @@

   sync: railsRestSync

-  initialize: (args) ->
-
-    session.get('users').findOrInitialize(args.user) if args.user
+  initialize: () ->

   url: -> "/v1/#{@get('topic_type')}s/#{@get('topic_id')}/chat/messages"

@@ -16,6 +14,11 @@

   drops: -> new Cloudsdale.Collections.Drops(@get('drops'))

-  user: (args) -> return session.get('users').findOrInitialize({id:@get('author_id')})
+  user: (args) ->
+    return session.get('users').findOrInitialize @get('author'),
+      fetch: false

-  topic: (args) -> return session.get(@get('topic_type') + 's').findOrInitialize(@get('topic_id'))+  topic: (args) ->
+    return session.get(@get('topic_type') + 's').findOrInitialize @get('topic_id'),
+      fetch: false
+


Fixed some more

Fixed some more

--- app/assets/javascripts/routers/clouds.js.coffee
+++ app/assets/javascripts/routers/clouds.js.coffee
@@ -10,6 +10,10 @@

     fail_to_load = (_id) =>
       $(".loading-content.loader-cloud[data-entity-id='#{_id}']").addClass('load-error')
+      setTimeout ->
+        $(".loading-content.loader-cloud[data-entity-id='#{_id}']").remove()
+        Backbone.history.navigate("/",true)
+      , 500

     if session.isLoggedIn()
       $('body').append("<div class='loading-content loader-cloud' data-entity-id='#{id}'/>")


Fixed /checkout/complete route

Fixed /checkout/complete route

--- app/assets/javascripts/sprangular/routes.coffee
+++ app/assets/javascripts/sprangular/routes.coffee
@@ -79,7 +79,7 @@
         order: (Cart) ->
           Cart.reload().then -> Cart.current

-    .when 'checkout/complete',
+    .when '/checkout/complete',
       controller: 'CheckoutCompleteCtrl'
       templateUrl: 'checkout/complete.html'


Proxy message link opened within the iframe, should open in new window

--- app/js/jail_iframe/classes/proxy_message.coffee
+++ app/js/jail_iframe/classes/proxy_message.coffee
@@ -3,7 +3,7 @@

       ×

-      You're looking at this page through Factlink

+      You're looking at this page through Factlink

       (visit original page)

   """


Fix autolinking to include path

Fix autolinking to include path

--- assets/javascripts/helpers/formatting.js.coffee
+++ assets/javascripts/helpers/formatting.js.coffee
@@ -15,6 +15,9 @@

   formatUrl: (url='') ->
     url.replace(/^\w+:\/\/([^\/]+).*?$/, '$1')
+
+  formatUrlWithPath: (url = '') ->
+    url.replace(/^\w+:\/\/(.*)$/, '$1')

   replaceIndexRange: (start_index, end_index, string, replacement) ->
     string.substr(0, start_index) + replacement + string.substr(end_index, string.length-1)
@@ -41,7 +44,7 @@
       start_index = i.indices[0] + offset
       end_index = i.indices[1] + offset
       original_text = text.substring(start_index, end_index)
-      replace_text = "#{TentStatus.Helpers.formatUrl original_text}"
+      replace_text = "#{TentStatus.Helpers.formatUrlWithPath original_text}"
       delta = replace_text.length - original_text.length
       offset += delta
       text = TentStatus.Helpers.replaceIndexRange(start_index, end_index, text, replace_text)


has_properties.coffee: Delete unused url and rpc methods

--- bokehjs/src/coffee/common/has_properties.coffee
+++ bokehjs/src/coffee/common/has_properties.coffee
@@ -308,48 +308,11 @@
       @_base = require('./base')
     return @_base

-  url: () ->
-    # ### method HasProperties::url
-    # model where our API processes this model
-    doc = @get('doc')
-    if not doc?
-      logger.error("unset 'doc' in #{@}")
-
-    url = @get_base().Config.prefix + "bokeh/bb/" + doc + "/" + @type + "/"
-    if (@isNew())
-      return url
-    return url + @get('id') + "/"
-
   sync: (method, model, options) ->
-    # this should be fixed via monkey patching when extended by an
-    # environment that implements the model backend,
-    # to enable normal beaviour, add this line
-    #
-    # HasProperties.prototype.sync = Backbone.sync
+    # make this a no-op, we sync the whole document never individual models
     return options.success(model.attributes, null, {})

   defaults: -> {}
-
-  rpc: (funcname, args, kwargs) =>
-    prefix = @get_base().Config.prefix
-    doc = @get('doc')
-    if not doc?
-      throw new Error("Unset 'doc' in " + this)
-    id = @get('id')
-    type = @type
-    url = "#{prefix}bokeh/bb/rpc/#{doc}/#{type}/#{id}/#{funcname}/"
-    data =
-      args: args
-      kwargs: kwargs
-    resp = $.ajax(
-      type: 'POST'
-      url: url,
-      data: JSON.stringify(data)
-      contentType: 'application/json'
-      xhrFields:
-        withCredentials: true
-    )
-    return resp

   # true if this class can be serialized and synced with the server-side
   # Document. We should refactor HasProperties to avoid this boolean


beziers now work

beziers now work

--- bokehjs/src/coffee/renderer/glyph/bezier.coffee
+++ bokehjs/src/coffee/renderer/glyph/bezier.coffee
@@ -27,7 +27,6 @@
         @selected_mask[i] = false
       @have_new_data = true

-
     _render: () ->
       [@sx0,  @sy0]  = @plot_view.map_to_screen(@x0,  @glyph_props.x0.units,  @y0, @glyph_props.y0.units)
       [@sx1,  @sy1]  = @plot_view.map_to_screen(@x1,  @glyph_props.x1.units,  @y1, @glyph_props.y1.units)
@@ -39,16 +38,6 @@
       @do_fill = true
       @_render_core()

-    _fast_path: (ctx) ->
-      if @do_stroke
-        @glyph_props.line_properties.set(ctx, @glyph_props)
-        ctx.beginPath()
-        for i in [0..@sx0.length-1]
-          if isNaN(@sx0[i] + @sy0[i] + @sx1[i] + @sy1[i] + @scx0[i] + @scy0[i] + @scx1[i] + @scy1[i])
-            continue
-          ctx.moveTo(@sx0[i], @sy0[i])
-          ctx.bezierCurveTo(@scx0[i], @scy0[i], @scx1[i], @scy1[i], @sx1[i], @sy1[i])
-        ctx.stroke()

     _full_path: (ctx, glyph_props) ->
       if @do_stroke


:beer: when narrow-editor opened from narrow-editor, provider.bindEditor solve problem abstractly

- and fix bug for editor scrollTop was not restored on cancel.

--- lib/ui.coffee
+++ lib/ui.coffee
@@ -176,6 +176,7 @@

   constructor: (@provider, {@query, @activate, @pending}={}) ->
     @titleNumber = @constructor.getNextTitleNumber()
+    @query ?= ''
     @pending ?= false
     @activate ?= true
     @disposables = new CompositeDisposable
@@ -209,48 +210,62 @@
       @disposables.add @onDidMoveToItemArea =>
         @setReadOnly(true)

+    @disposables.add(@registerCommands())
+
+    # Depends on ui.grammar and commands bound to @editorElement, so have to come last
+    @providerPanel = new ProviderPanel(this, showSearchOption: @provider.showSearchOption)
+
+    @constructor.register(this)
+    @disposables.add new Disposable =>
+      @constructor.unregister(this)
+
+  start: ->
+    # When initial getItems() take very long time, it means refresh get delayed.
+    # In this case, user see modified icon(mark) on tab.
+    # Explicitly setting modified start here prevent this
+    @setModifiedState(false)
+
+    if isNarrowEditor(@provider.editor)
+      exNarrowEditor = @provider.editor
+      # Invoked from another narrow-editor(= ex-narrow-editor).
+      # Rebind provider's editor to behaves like it invoked from normal-editor.
+      @provider.bindEditor(@constructor.get(exNarrowEditor).provider.editor)
+
+    pane = getAdjacentPaneOrSplit(@provider.getPane(), split: settings.get('directionToOpen'))
+    # pane.activate() unless pane.isActive()
+
+    # [NOTE] When new item is activated, existing PENDING item is destroyed.
+    # So existing PENDING narrow-editor is destroyed at this timing.
+    # And PENDING narrow-editor's provider's editor have foucsed.
+    # So pane.activate must be called AFTER activateItem
+    pane.activateItem(@editor, {@pending})
+    pane.activate()
+
+    @grammar.activate()
+    @insertQuery(@query)
+    @providerPanel.show()
+
     @disposables.add(
       @registerCommands()
       @observeChange()
       @observeCursorMove()
       @observeStopChangingActivePaneItem()
     )
-    # Depends on ui.grammar and commands bound to @editorElement, so have to come last
-    @providerPanel = new ProviderPanel(this, showSearchOption: @provider.showSearchOption)
-
-    @constructor.register(this)
-    @disposables.add new Disposable =>
-      @constructor.unregister(this)
-
-  start: ->
-    # When initial getItems() take very long time, it means refresh get delayed.
-    # In this case, user see modified icon(mark) on tab.
-    # Explicitly setting modified start here prevent this
-    @setModifiedState(false)
-
-    providerPane = @provider.getPane()
-    if isNarrowEditor(@provider.editor)
-      # If narrow is invoked from narrow-editor, open in same pane.
-      pane = providerPane
-    else
-      pane = getAdjacentPaneOrSplit(providerPane, split: settings.get('directionToOpen'))
-
-    pane.activate() unless pane.isActive()
-    pane.activateItem(@editor, {@pending})
-
-    @grammar.activate()
-    if @query
-      @insertQuery(@query)
-    else
-      @withIgnoreChange => @insertQuery()
-    @providerPanel.show()
+
     @moveToPrompt()
     @refresh().then =>
-      @activateProviderPane() unless @activate
+      if @activate
+        @preview() if @query and @autoPreview
+      else
+        @activateProviderPane()

   observeStopChangingActivePaneItem: ->
     atom.workspace.onDidStopChangingActivePaneItem (item) =>
-      @provider.needRestoreEditorState = false
+      if item isnt @editor
+        # When item other than narrow-editor was activated,
+        # No longer restore editor's state.
+        # This guard is necessary since initial narrow-editor open fire this event.
+        @provider.needRestoreEditorState = false
       return if not isTextEditor(item) or isNarrowEditor(item)
       return if paneForItem(item) is @getPane()
       @startSyncToEditor(item)
@@ -370,7 +385,7 @@
     @previewItemForDirection('previous')

   getQuery: ->
-    @lastQuery = @editor.lineTextForBufferRow(0)
+    @editor.lineTextForBufferRow(0)

   excludeFile: ->
     return if @provider.boundToEditor
@@ -412,7 +427,7 @@
         @cachedItems = items if @provider.supportCacheItems
         items

-    filterSpec = getFilterSpecForQuery(@getQuery())
+    filterSpec = getFilterSpecForQuery(@lastQuery = @getQuery())
     if @provider.updateGrammarOnQueryChange
       @grammar.update(filterSpec.include) # No need to highlight excluded items

@@ -528,7 +543,6 @@

   syncToEditor: (editor) ->
     return if @preventSyncToEditor
-    return if @isActive()
     if item = @findClosestItemForEditor(editor)
       @selectItem(item)
       {row} = @editor.getCursorBufferPosition()
@@ -726,24 +740,23 @@

     @syncSubcriptions?.dispose()
     @syncSubcriptions = new CompositeDisposable
-
-    syncToEditor = @syncToEditor.bind(this, editor)
-    syncToEditor()
+    @syncToEditor(editor)

     ignoreColumnChange = @provider.ignoreSideMovementOnSyncToEditor

-    @syncSubcriptions.add editor.onDidChangeCursorPosition (event) ->
+    @syncSubcriptions.add editor.onDidChangeCursorPosition (event) =>
       return unless isActiveEditor(editor)
       return if event.textChanged
       return if ignoreColumnChange and (event.oldBufferPosition.row is event.newBufferPosition.row)
-      syncToEditor()
-
-    @syncSubcriptions.add @onDidRefresh(syncToEditor)
-
+      @syncToEditor(editor)
+
+    @syncSubcriptions.add @onDidRefresh =>
+      @syncToEditor(editor)
+
+    # Avoid refresh while active, important to update-real-file don't cause auto-refresh.
     refresh = => @refresh(force: true) unless @isActive()

     if @provider.boundToEditor
-      # Surppress refreshing while editor is active to avoid auto-refreshing while direct-edit.
       @syncSubcriptions.add editor.onDidStopChanging(refresh)
     else
       @syncSubcriptions.add editor.onDidSave(refresh)


KDFramework: KDInputView setName implemented/taken out of setDomElement + some code cleanup

--- client/Framework/components/inputs/KDInputView.coffee
+++ client/Framework/components/inputs/KDInputView.coffee
@@ -31,6 +31,7 @@
     @inputValidationNotifications = {}
     @valid = yes
     @inputCallback = null
+    @setName options.name
     @setLabel()
     @setCallback()
     @setDefaultValue options.defaultValue
@@ -60,7 +61,6 @@
           @setValue o.selectOptions[0].value unless o.defaultValue

   setDomElement:(cssClass = "")->
-    @inputName = @options.name
     name = "name='#{@options.name}'"
     @domElement = switch @getType()
       when "text"     then $ "<input #{name} type='text' class='kdinput text #{cssClass}'/>"
@@ -81,17 +81,16 @@
       @getDomElement().trigger "focus"
       @getDomElement().trigger "click"

-  getLabel:()-> @inputLabel
-
-  setCallback:()-> @inputCallback = @options.callback
-
-  getCallback:()-> @inputCallback
-
-  setType:(type = "text")-> @inputType = type
-
-  getType:()-> @inputType
-
-  getName:()-> @inputName
+  getLabel:-> @inputLabel
+
+  setCallback:-> @inputCallback = @getOptions().callback
+  getCallback:-> @inputCallback
+
+  setType:(@inputType = "text")->
+  getType:-> @inputType
+
+  setName:(@inputName)->
+  getName:-> @inputName

   setFocus:()->
     (@getSingleton "windowController").setKeyView @


AvatarView: avatarImage implementation

AvatarView: avatarImage implementation

--- client/Main/CommonViews/avatarviews/avatarview.coffee
+++ client/Main/CommonViews/avatarviews/avatarview.coffee
@@ -41,7 +41,7 @@

   setAvatar:(uri)->
     if @bgImg isnt uri
-      @$().css "background-image", uri
+      @$().css "background-image", "url(#{uri})"
       @bgImg = uri

   render:->
@@ -55,10 +55,13 @@

     height = width unless height

-    avatarURI = "url(//gravatar.com/avatar/#{profile.hash}?size=#{width}&d=#{encodeURIComponent @fallbackUri})"
+    avatarURI = if profile.hash
+    then "//gravatar.com/avatar/#{profile.hash}?size=#{width}&d=#{encodeURIComponent @fallbackUri}"
+    else "#{@fallbackUri}"
+
     if profile.avatar?.match /^https?:\/\//
       resizedAvatar = KD.utils.proxifyUrl profile.avatar, {crop: yes, width, height}
-      avatarURI = "url(#{resizedAvatar})"
+      avatarURI = "#{resizedAvatar}"

     @setAvatar avatarURI

@@ -244,3 +247,26 @@
         </div>
     </div>
     """
+
+class AvatarImage extends AvatarView
+
+  constructor:(options = {},data)->
+
+    options.tagName  or= "img"
+    options.cssClass or= ""
+    options.size     or=
+      width            : 50
+      height           : 50
+    options.cssClass   = KD.utils.curry "avatarimage", options.cssClass
+
+    super options, data
+
+    @bgImg = null
+    @fallbackUri = "#{KD.apiUri}/images/defaultavatar/default.avatar.#{options.size.width}.png"
+
+  setAvatar:(uri)->
+    if @bgImg isnt uri
+      @setAttribute "src", uri
+      @bgImg = uri
+
+  pistachio:-> ''


Sidebar: vmlist changed to machinelist

Sidebar: vmlist changed to machinelist

--- client/Social/Activity/sidebar/activitysidebar.coffee
+++ client/Social/Activity/sidebar/activitysidebar.coffee
@@ -332,16 +332,16 @@
     KD.singletons.dock.getView().addSubView new GroupDescription

-  listVMs: (vms) ->
-
-    @vmTree.addNode vm  for vm in vms
+  listMachines: (machines) ->
+
+    @machineTree.addNode machine  for machine in machines

   addVMTree: ->

     @addSubView section = new KDCustomHTMLView tagName : 'section'

-    @vmTree = new JTreeViewController
+    @machineTree = new JTreeViewController
       type                : 'main-nav'
       treeItemClass       : NavigationMachineItem

@@ -350,7 +350,8 @@
       cssClass : 'sidebar-title'
       partial  : 'VMs'

-    section.addSubView @vmTree.getView()
+    section.addSubView @machineTree.getView()
+
     @machineTree.on 'NodeWasAdded', (machineItem) ->

       machineItem.on 'click', (event) ->
@@ -360,8 +361,8 @@

     if KD.userMachines.length
-    then @listVMs KD.userMachines
-    else @fetchVMs @bound 'listVMs'
+    then @listMachines KD.userMachines
+    else @fetchMachines @bound 'listMachines'

   addFollowedTopics: ->


Client: Activity: Change menulist class name and updated button onclick event to remove unnnecessary lines

--- client/app/lib/components/buttonwithmenu/index.coffee
+++ client/app/lib/components/buttonwithmenu/index.coffee
@@ -16,17 +16,16 @@

   onButtonClick: (event) ->
-    event.preventDefault()
-    event.stopPropagation()
+    kd.utils.stopDOMEvent event
     @setState showSettingsMenu: yes
     @props.showMenuForMouseAction = yes

   render: ->
-    settingsMenuListClassName = if @state.showSettingsMenu and @props.showMenuForMouseAction then 'SettingsMenuList' else 'SettingsMenuList hidden'
+    menuListClassName = if @state.showSettingsMenu and @props.showMenuForMouseAction then 'MenuList' else 'MenuList hidden'

       <button type="button" onClick={@bound 'onButtonClick'} ></button>
-

+

         {@renderListMenu()}


Prevent race condition for close event

Prevent race condition for close event

--- client/app/lib/environment/environmentlistitem.coffee
+++ client/app/lib/environment/environmentlistitem.coffee
@@ -61,8 +61,8 @@

   handleMachineRequest: (provider) ->

+      @getDelegate().emit 'ModalDestroyRequested'
       ComputeHelpers.handleNewMachineRequest { provider }
-      @getDelegate().emit 'ModalDestroyRequested'

   createExtraViews: ->


computemodal: Use direct calls to makeDisable/Enable

--- client/app/lib/providers/computeplansmodalpaid.coffee
+++ client/app/lib/providers/computeplansmodalpaid.coffee
@@ -62,7 +62,7 @@

     # Set the default region passed in as an option
     if region
-      @disableRegion()
+      @regionSelector.makeDisabled()
       @regionSelector.setValue region

     regionContainer.addSubView @regionTextView = new KDView
@@ -91,9 +91,9 @@
         if snapshotId
           @regionSelector.setValue @snapshots[snapshotId].region
           @updateRegionText()
-          @disableRegion()
+          @regionSelector.makeDisabled()
         else
-          @enableRegion()
+          @regionSelector.makeEnabled()

     content.addSubView storageContainer = new KDView
       cssClass : "storage-container"
@@ -277,11 +277,3 @@
       @destroy()

-  disableRegion: ->
-    @regionSelector.makeDisabled()
-
-
-  enableRegion: ->
-    @regionSelector.makeEnabled()
-
-


Add error callbacks

Add error callbacks

--- src/js/cts-cite-driver.coffee
+++ src/js/cts-cite-driver.coffee
@@ -140,6 +140,8 @@
   fusion_tables_query "SELECT * FROM #{cts_cite_collection_driver_config['cite_table_id']}", (fusion_tables_result) ->
     cite_collection = fusion_tables_result
     callback() if callback?
+  , ->
+    $('#translation_container').append $('<div>').attr('class','alert alert-danger').text('Error in response from Google Fusion Tables for translation collection.')

 get_valid_reff_xml_to_urn_list = (xml) ->
   leaf_nodes = $(xml).find('chunk').filter (index) -> (($(this).children('chunk').length) == 0)
@@ -153,12 +155,14 @@
     valid_urns = (urn[0] for urn in fusion_tables_result.rows)
     # console.log(valid_urns)
     callback() if callback?
+  , ->
+    $('#translation_container').append $('<div>').attr('class','alert alert-danger').text('Error in response from Google Fusion Tables for text collection.')

 # wrap values in single quotes and backslash-escape single-quotes
 fusion_tables_escape = (value) ->
   "'#{value.replace(/'/g,"\\\'")}'"

-fusion_tables_query = (query, callback) ->
+fusion_tables_query = (query, callback, error_callback) ->
   console.log "Query: #{query}"
   switch query.split(' ')[0]
     when 'SELECT'
@@ -169,6 +173,7 @@
         crossDomain: true
         error: (jqXHR, textStatus, errorThrown) ->
           console.log "AJAX Error: #{textStatus}"
+          error_callback() if error_callback?
         success: (data) ->
           # console.log data
           if callback?


Remove login notify.

Remove login notify.

--- app/assets/javascripts/app/controllers/login.js.coffee
+++ app/assets/javascripts/app/controllers/login.js.coffee
@@ -89,12 +89,6 @@
     # rebuild navbar with ticket overview counter
     App.WebSocket.send( event: 'navupdate_ticket_overview' )

-    # add notify
-    @notify
-      type:      'success'
-      msg:       App.i18n.translateContent('Login successfully! Have a nice day!')
-      removeAll: true
-
     # redirect to #
     requested_url = @Config.get( 'requested_url' )
     if requested_url && requested_url isnt '#login'


calculates according to days, ignore hours

--- app/assets/javascripts/models/expense_report.js.coffee
+++ app/assets/javascripts/models/expense_report.js.coffee
@@ -69,7 +69,7 @@

       numberOfDays: ->
         if @startTime and @endTime
-          parseFloat(moment.duration(moment(@endTime) - moment(@startTime)).as("days").toPrecision(3))
+          moment(@endTime).diff(moment(@startTime), "days") + 1
         else
           0
 ]


fix ie 8 conversations loading bug, fixes #9627

ie 8 and below don't gracefully handle coffeescript trying to splat
something that isn't an array. specifically, "{}..." results in a
"[].slice.call({})", which gives "Number expected". because modelize
didn't correctly handle empty arrays, we would splat an object when there
were no conversations to load, resulting in a js error and a spinner that
would never go away

this affected people on ie 8 with no conversations, or who had selected
a conversation filter (e.g. "Unread") that resulted in no matches

test plan:
1. in ie 8, go to conversations
2. if you have conversations, they should load correctly
3. if you have no conversations, "You have no messages" should appear
4. if you select a filter/scope with no results (e.g. "Unread"), "You
   have no [filter] messages" should appear
5. there should be no js errors

Change-Id: I3376460694293c4c2204157400322cf29b370253
Reviewed-on: https://gerrit.instructure.com/12627
Tested-by: Jenkins <d95b56ce41a2e1ac4cecdd398defd7414407cc08@instructure.com>
Reviewed-by: Zach Pendleton <514dbb4d54d4f29e454990df7d075678f43f92aa@instructure.com>

--- app/coffeescripts/util/ScrollableListDataSource.coffee
+++ app/coffeescripts/util/ScrollableListDataSource.coffee
@@ -71,7 +71,7 @@
         item.list = @list
         item

-      if data.length
+      if data.length?
         (modelize(item) for item in data)
       else
         modelize(data)


Testing

Testing

--- widgets/cusi_progress_bars/cusi_progress_bars.coffee
+++ widgets/cusi_progress_bars/cusi_progress_bars.coffee
@@ -150,7 +150,7 @@
       barvalue = 100
     else if (barvalue < 0)
       barvalue = 0
-	  value = 0
+      value = 0
 #    element.textContent = Math.floor(value) + "%"
     element.textContent = Math.floor(value)
     element.style.width = barvalue + "%"


bundle: fix initialization logic

bundle: fix initialization logic

--- workers/social/lib/social/models/bundle/index.coffee
+++ workers/social/lib/social/models/bundle/index.coffee
@@ -6,6 +6,7 @@

   {dash} = require 'bongo'

+  {extend} = require 'underscore'
   # @create = ()

   @setLimits = (limits) ->
@@ -39,18 +40,24 @@
       { limits_: defaultLimits } = @constructor

       @once 'save', =>
-        console.log 'does this happen?'
+        JLimit = require '../limit'

         queue = Object.keys(defaultLimits).map (limitName) => =>
+          defaultOptions = defaultLimits[limitName]
+
           limitOptions = limits[limitName]
+
           limitOptions = { unit: limit }  if 'string' is typeof limitOptions
-          limit = new Limit limitOptions
+
+          limitOptions = extend { title: limitName }, defaultOptions, limitOptions
+
+          limit = new JLimit limitOptions
           limit.save (err)=>
             return next err  if err
-            @addLimit limit, limitName, next
+            @addLimit limit, limitName, fin

         dash queue, => @emit 'limitsAreSet'

-        next = queue.next.bind queue
+        fin = queue.fin.bind queue

         return


payment-base: cast quantities to numeric values before saving them

--- workers/social/lib/social/models/payment/base.coffee
+++ workers/social/lib/social/models/payment/base.coffee
@@ -54,6 +54,9 @@
     planCodes = Object.keys quantities
     productSelector = planCode: $in: planCodes

+    for planCode in planCodes
+      quantities[planCode] = +quantities[planCode]
+
     JPaymentProduct.some productSelector, { limit: 20 }, (err, products) =>
       return callback err  if err


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/active_bounds.coffee
+++ src/active_bounds.coffee
@@ -3,13 +3,12 @@
 are outside of the specified bounds. The default bounds are
 the dimensions of your game. Useful for bullet type objects.

-    player = Game
+    player = GameObject
       x: 10
       y: 50
       width: 20
       height: 20
-      other: "stuff"
-      more: "properties"
+

 @see GameObject


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/active_bounds.coffee
+++ src/active_bounds.coffee
@@ -24,7 +24,7 @@
         x: 30
         y: 20
         width: 0
-
+        height

 ActiveBounds module
 @name ActiveBounds


file access logging -- at most once per minute

--- salvus/hub.coffee
+++ salvus/hub.coffee
@@ -1464,10 +1464,17 @@
                     if err
                         @error_to_client(id:mesg.id, error:"Problem getting file editing session -- #{err}")
                     else
-                        database.log_file_access
-                            project_id : mesg.project_id
-                            account_id : @account_id
-                            filename   : mesg.path
+                        if not @_file_access?
+                            @_file_access = {}
+                        if not @_file_access[mesg.project_id + mesg.path]?
+                            database.log_file_access
+                                project_id : mesg.project_id
+                                account_id : @account_id
+                                filename   : mesg.path
+                            @_file_access[mesg.project_id + mesg.path] = true
+                            f = () =>
+                                delete @_file_access[mesg.path]
+                            setTimeout(f, 60000)
                         # It is critical that we initialize the
                         # diffsync objects on both sides with exactly
                         # the same document.
@@ -1482,12 +1489,6 @@
                                 path         : session.path
                                 content      : snapshot
                             @push_to_client(mesg)
-                            ###
-                            database.file_access_log
-                                account_id : ?
-                                project_id : mesg.project_id
-                                filename   : session.path
-                            ###

     get_codemirror_session: (mesg, cb) =>


temporarily disable notifications

temporarily disable notifications

--- salvus/hub.coffee
+++ salvus/hub.coffee
@@ -2893,8 +2893,6 @@
                                 break
                         cb()
         (cb) ->
-            if not is_new_activity
-                cb(); return
             #dbg("record new activity and notification")
             where = {project_id:opts.project_id, path:path, timestamp:now_time}
             async.parallel([
@@ -2920,6 +2918,8 @@
                         set   : {project_id:opts.project_id, path:path, fullname:opts.fullname, project_title:opts.project_title}
                         cb    : cb
                 (cb) ->
+                    # DISABLE temporarily; probably causing trouble.
+                    cb(); return
                     #dbg('add to notifications')
                     async.series([
                         (cb) ->


Better error message when module is not found

--- bokehjs/src/coffee/common/base.coffee
+++ bokehjs/src/coffee/common/base.coffee
@@ -175,7 +175,10 @@
   mod = mod_cache[typename]

   if not mod?
-    throw Error("module `#{typename}' does not exists")
+    throw new Error("Module `#{typename}' does not exists. The problem may be two fold. Either
+                     a model was requested that's available in an extra bundle, e.g. a widget,
+                     or a custom model was requested, but it wasn't registered before first
+                     usage.")

   if not mod.Collection?
     mod.Collection = make_collection(mod.Model)


eliminated _join in favor of path.join

eliminated _join in favor of path.join

--- platforms/node/tools/files.coffee
+++ platforms/node/tools/files.coffee
@@ -17,6 +17,13 @@
     ##
     path: path

+    ##
+    ##
+    appdir: (args...)->
+
+        normpath args
+
+
     ##
     ##
     mkdir: (dir)->
@@ -101,9 +108,6 @@
     ##
     rm: (name, recursive)->

-        if floyd.tools.objects.isArray name
-            name = _join name
-
         name = normpath name

         if files.exists name
@@ -155,7 +159,7 @@
                 persistent: false

         fs.watch normpath(name), fn
-
+

 ##
 ## private
@@ -167,17 +171,12 @@
 normpath = (dir)->

     if typeof dir is 'object'
-        dir = _join dir
-
+        dir = path.join.apply path, dir
+
     dir = dir.replace floyd.system.appdir, ''

     path.join floyd.system.appdir, path.normalize(dir)

-##
-##
-##
-_join = (list)->
-    list.join '/'

 ##


sidebar: fetch messages with utils

sidebar: fetch messages with utils

--- client/app/lib/activity/sidebar/activitysidebar.coffee
+++ client/app/lib/activity/sidebar/activitysidebar.coffee
@@ -16,11 +16,13 @@
 ActivitySideView          = require './activitysideview'
 KDCustomHTMLView          = kd.CustomHTMLView
 SidebarTopicItem          = require './sidebartopicitem'
+fetchChatChannels         = require 'activity/util/fetchchatchannels'
 SidebarPinnedItem         = require './sidebarpinneditem'
 KDNotificationView        = kd.NotificationView
 SidebarMessageItem        = require './sidebarmessageitem'
 JTreeViewController       = kd.JTreeViewController
 MoreWorkspacesModal       = require './moreworkspacesmodal'
+fetchChatChannelCount     = require 'activity/util/fetchchatchannelcount'
 isChannelCollaborative    = require '../../util/isChannelCollaborative'
 SidebarOwnMachinesList    = require './sidebarownmachineslist'
 environmentDataProvider   = require 'app/userenvironmentdataprovider'
@@ -622,11 +624,9 @@
         title    : ' '
         href     : groupifyLink '/Activity/Message/New'
       dataSource : (callback) ->
-        kd.singletons.socialapi.message.fetchPrivateMessages
-          limit  : limit
-        , callback
+        fetchChatChannels { limit }, callback
       countSource: (callback) ->
-        remote.api.SocialMessage.fetchPrivateMessageCount {}, callback
+        fetchChatChannelCount {}, callback

     @sections.messages.on 'DataReady', @bound 'handleWorkspaceUnreadCounts'


NavigationMachineItem: Fixed reference error.

:facepalm:

--- client/app/lib/navigation/navigationmachineitem.coffee
+++ client/app/lib/navigation/navigationmachineitem.coffee
@@ -285,7 +285,7 @@
   click: (e) ->

     m = @machine
-    hasInvitation = _kd.singletons.machineShareManager.get m.uid
+    hasInvitation = kd.singletons.machineShareManager.get m.uid

     if not m.isMine() and not m.isApproved() and hasInvitation
       kd.utils.stopDOMEvent e


FindNodesModal: PR fixes, get views from container when needed

--- client/app/lib/providers/managed/findnodesmodal.coffee
+++ client/app/lib/providers/managed/findnodesmodal.coffee
@@ -44,8 +44,7 @@

       listKites: (data) =>

-        {loader, message, list,
-         button_assign, button_reload} = view.addTo @container,
+        container       = view.addTo @container,
           message_warn  :
             text        : "We are unable to reach your managed vm
                           (#{@machine.ipAddress}), but we've found following
@@ -56,12 +55,15 @@
             title       : 'Use Selected Kite'
             cssClass    : 'green'
             callback    : =>
+
+              {message, list} = container
+
               kite = list.controller.selectedItems.first.getData()
-              if kite.machine
-                message.show()
-              else
-                message.hide()
-                @assignKite kite
+              return message.show()  if kite.machine
+
+              message.hide()
+              @assignKite kite
+
           button_delete :
             title       : 'Delete VM'
             cssClass    : "red #{if @getOption 'reassign' then 'hidden'}"
@@ -70,22 +72,27 @@
             iconOnly    : yes
             cssClass    : 'retry'
             callback    : =>
+
+              {button_reload, message, loader} = container
+
               button_reload.hide()
               message.hide()
               loader.show()
+
               @fetchKites()
+
           loader        :
             cssClass    : 'inline'
           message       :
             text        : 'This kite is in use.'
             cssClass    : 'inline hidden'

+        {list, button_assign, message} = container
+
         list.controller
-
           .on 'ItemDeselectionPerformed', ->
             button_assign.disable()
             message.hide()
-
           .on 'ItemSelectionPerformed', button_assign.bound 'enable'


Fixes captions

Fixes captions

--- client/apps/edit/components/section_image/index.coffee
+++ client/apps/edit/components/section_image/index.coffee
@@ -102,6 +102,7 @@
             className: 'esi-caption bordered-input'
             ref: 'editable'
             onKeyUp: @onEditableKeyup
+            dangerouslySetInnerHTML: __html: @props.section.get('caption')
           }
       (
         if @state.progress


Use new fetchAllAppsData helper instead someWithRelationship, faster.

--- client/landing/app/MainApp/kodingappscontroller.coffee
+++ client/landing/app/MainApp/kodingappscontroller.coffee
@@ -262,11 +262,11 @@
     # return unless KD.isLoggedIn()
     appNames = (appName for appName, manifest of @getManifests()) or []
     query    = "manifest.name": "$in": appNames
-
-    KD.remote.api.JApp.someWithRelationship query, {}, (err, apps) =>
+    {JApp}   = KD.remote.api
+    JApp.fetchAllAppsData query, (err, apps)=>
       @publishedApps = map = {}
       apps?.forEach (app) =>
-        map[app.manifest.name] = app
+        map[app.manifest.name] = new JApp app
       @emit "UserAppModelsFetched", map
       callback? map


testrunner: use a fakesocket to satisfy forwardEvent contract

--- client/testrunner/lib/server/serversocketrunner.coffee
+++ client/testrunner/lib/server/serversocketrunner.coffee
@@ -5,16 +5,23 @@

   constructor: (socket) ->

+    super
+
     @socket = socket
+    @fakeSocket = new EventEmitter
     @bindSocketEvents()

   forwardFromSocket: (events, reducer) ->

-    forwardEvents @socket, this, events, reducer
+    forwardEvents @fakeSocket, this, events, reducer

   bindSocketEvents: ->
+
+    @socket.on 'result', (payload) =>
+      events = JSON.parse payload
+      events.map (e) => @fakeSocket.emit e...

     @forwardFromSocket ['start', 'end', 'suite end']


Support toggling dev tools from event palette

--- src/app/root-view.coffee
+++ src/app/root-view.coffee
@@ -69,7 +69,7 @@
         true

   handleEvents: ->
-    @on 'toggle-dev-tools', => atom.toggleDevTools()
+    @command 'toggle-dev-tools', => atom.toggleDevTools()
     @on 'focus', (e) => @handleFocus(e)
     $(window).on 'focus', (e) =>
       @handleFocus(e) if document.activeElement is document.body


:lipstick:

:lipstick:

--- src/app/selection.coffee
+++ src/app/selection.coffee
@@ -4,10 +4,12 @@

 module.exports =
 class Selection
-  wordwise: false
+  cursor: null
+  marker: null
   editSession: null
   initialScreenRange: null
   goalBufferRange: null
+  wordwise: false
   needsAutoscroll: null

   constructor: ({@cursor, @marker, @editSession, @goalBufferRange}) ->


Change install panel help text when switching contexts

--- lib/install-panel.coffee
+++ lib/install-panel.coffee
@@ -22,7 +22,7 @@

           @div class: 'text native-key-bindings', tabindex: -1, =>
             @span class: 'icon icon-question'
-            @span 'Packages are published to  '
+            @span outlet: 'publishedToText'
             @a class: 'link', outlet: "openAtomIo", "atom.io"
             @span " and are installed to #{path.join(fs.getHomeDirectory(), '.atom', 'packages')}"

@@ -47,14 +47,16 @@
   initialize: (@packageManager) ->
     client = $('.settings-view').view()?.client
     @client = @packageManager.getClient()
+    @atomIoURL = 'https://atom.io/packages'
     @openAtomIo.on 'click', =>
-      require('shell').openExternal('https://atom.io/packages')
+      require('shell').openExternal(@atomIoURL)
       false

     @searchMessage.hide()

     @searchEditorView.getModel().setPlaceholderText('Search packages')
     @searchType = 'packages'
+    @publishedToText.text("#{@searchType[0].toUpperCase() + @searchType.slice(1)} are published to ")
     @handleSearchEvents()

     @loadFeaturedPackages()
@@ -78,6 +80,8 @@
         @searchPackagesButton.addClass('selected')
         @searchThemesButton.removeClass('selected')
         @searchEditorView.getModel().setPlaceholderText('Search packages')
+        @publishedToText.text("#{@searchType[0].toUpperCase() + @searchType.slice(1)} are published to ")
+        @atomIoURL = 'https://atom.io/packages'
         @loadFeaturedPackages()
         @performSearch()

@@ -88,6 +92,8 @@
         @searchThemesButton.addClass('selected')
         @searchPackagesButton.removeClass('selected')
         @searchEditorView.getModel().setPlaceholderText('Search themes')
+        @publishedToText.text("#{@searchType[0].toUpperCase() + @searchType.slice(1)} are published to ")
+        @atomIoURL = 'https://atom.io/themes'
         @loadFeaturedPackages(true)
         @performSearch()


Revised scxml interpreter to use different API for parameterized send.

--- src/main/coffeescript/scxml/SCXML.coffee
+++ src/main/coffeescript/scxml/SCXML.coffee
@@ -230,17 +230,13 @@
 					log = @_eval action,datamodelForNextStep,eventSet
 					if @opts.printTrace then logger.info(log)	#the one place where we use straight logger.info
 				when "send"
-					#FIXME: if send is not defined
-					#we should provide a simple send handler which gets setup by default, and can be overridden.
-					#this is what node- and browser-specific classes should do... is setup default delayed send
 					data = if action.contentexpr then @_eval(action,datamodelForNextStep,eventSet) else null
 					if @_send then @_send(
-						action.target,
 						{
+							target : action.target
 							name : action.event
 							data : data	#TODO: handle namelist,content,params
 						},
-						this,
 						{
 							delay : action.delay
 							sendId : action.id
@@ -431,7 +427,7 @@
 			#set up send and cancel
 			#these may be passed in as options if, e.g., we 're using an external communication layer
 			#these are the defaults if an external communication layer is not being used.
-			@_send = opts.send or (target,event,caller,options) ->
+			@_send = opts.send or (event,options) ->
 				if @opts.setTimeout
 					if @opts.printTrace then logger.trace "sending event",event.name,"with content",event.data,"after delay",options.delay

@@ -444,7 +440,7 @@
 				else
 					throw new Error("setTimeout function not set")

-			@_cancel = opts.canel or (sendid) ->
+			@_cancel = opts.cancel or (sendid) ->
 				if @opts.clearTimeout
 					if sendid of @_timeoutMap
 						if @opts.printTrace then logger.trace "cancelling ",sendid," with timeout id ",@_timeoutMap[sendid]


Refactoring.

Refactoring.

--- lib/linter-pylama.coffee
+++ lib/linter-pylama.coffee
@@ -8,99 +8,69 @@

 class LinterPylama extends Linter
-  @enabled: false
+  regex: ':(?<line>\\d+):(?<col>\\d+):\\s+((((?<error>E)|(?<warning>[CDFNW]))(?<code>\\d+)(:\\s+|\\s+))|(.*?))(?<message>.+)(\r)?\n'
+
+  linterName: 'pylama'
   @syntax: 'source.python'
   @cmd: ''
-  @ignoreErrors: ''
   @pylamaPath: ''
-  @skipFiles: ''
-  @useMcCabe: false
-  @usePEP8: false
-  @usePyFlakes: false
-  @usePyLint: false
-  @usePEP257: false
-  linterName: 'pylama'
-  regex: ':(?<line>\\d+):(?<col>\\d+):\\s+((((?<error>E)|(?<warning>[CDFNW]))(?<code>\\d+)(:\\s+|\\s+))|(.*?))(?<message>.+)(\r)?\n'

   constructor: (@editor) ->
     super @editor
+    @pylamaVersion_ = atom.config.observe 'linter-pylama.pylamaVersion', =>
+      do @initPylama
+    @executablePath_ = atom.config.observe 'linter-pylama.executablePath', =>
+      do @initPylama
+    @ignoreErrorsAndWarnings_ = atom.config.observe 'linter-pylama.ignoreErrorsAndWarnings', =>
+      do @initCmd
+    @skipFiles_ = atom.config.observe 'linter-pylama.skipFiles', =>
+      do @initCmd
+    @useMcCabe_ = atom.config.observe 'linter-pylama.useMccabe', =>
+      do @initCmd
+    @usePEP8_ = atom.config.observe 'linter-pylama.usePep8', =>
+      do @initCmd
+    @usePEP257_ = atom.config.observe 'linter-pylama.usePep257', =>
+      do @initCmd
+    @usePyFlakes_ = atom.config.observe 'linter-pylama.usePyflakes', =>
+      do @initCmd
+    @usePyLint_ = atom.config.observe 'linter-pylama.usePylint', =>
+      do @initCmd
     do @initPythonPath
-    atom.config.observe 'linter-pylama.ignoreErrorsAndWarnings', =>
-      ignoreErrors = atom.config.get('linter-pylama.ignoreErrorsAndWarnings')
-      if ignoreErrors and ignoreErrors.length > 0
-        @ignoreErrors = "-i #{ignoreErrors}"
-      else
-        @ignoreErrors = ''
-      do @initCmd
-    atom.config.observe 'linter-pylama.usePylint', =>
-      usePyLint = atom.config.get 'linter-pylama.usePylint'
-      @usePyLint = if usePyLint then 'pylint' else ''
-      do @initCmd
-    atom.config.observe 'linter-pylama.useMccabe', =>
-      useMcCabe = atom.config.get 'linter-pylama.useMccabe'
-      @useMcCabe = if useMcCabe then 'mccabe' else ''
-      do @initCmd
-    atom.config.observe 'linter-pylama.usePep8', =>
-      usePEP8 = atom.config.get 'linter-pylama.usePep8'
-      @usePEP8 = if usePEP8 then 'pep8' else ''
-      do @initCmd
-    atom.config.observe 'linter-pylama.usePep257', =>
-      usePEP257 = atom.config.get 'linter-pylama.usePep257'
-      @usePEP257 = if usePEP257 then 'pep257' else ''
-      do @initCmd
-    atom.config.observe 'linter-pylama.usePyflakes', =>
-      usePyFlakes = atom.config.get 'linter-pylama.usePyflakes'
-      @usePyFlakes = if usePyFlakes then 'pyflakes' else ''
-      do @initCmd
-    atom.config.observe 'linter-pylama.skipFiles', =>
-      skipFiles = atom.config.get 'linter-pylama.skipFiles'
-      if skipFiles
-        @skipFiles = "--skip #{skipFiles}"
-      else
-        @skipFiles = ''
-      do @initCmd
+    do @initPylama

-    atom.config.observe 'linter-pylama.executablePath', =>
-      pylamaVersion = atom.config.get 'linter-pylama.pylamaVersion'
-      if pylamaVersion is 'external'
-        @enabled = false
-        @pylamaPath = atom.config.get 'linter-pylama.executablePath'
-        exec "#{@pylamaPath} --version", @executionCheckHandler

-    atom.config.observe 'linter-pylama.pylamaVersion', =>
-      pylamaVersion = atom.config.get 'linter-pylama.pylamaVersion'
-      if pylamaVersion is 'internal'
-        @pylamaPath = path.join path.dirname(__dirname), 'bin', 'pylama.py'
-        @enabled = true
-        @pylamaVersion = pylamaVersion
-        do @initCmd
-      else
-        pylamaPath = atom.config.get 'linter-pylama.executablePath'
-        if @pylamaPath != pylamaPath
-          @enabled = false
-          @pylamaPath = pylamaPath
-          exec "#{@pylamaPath} --version", @executionCheckHandler
+  destroy: ->
+    super
+    do @pylamaVersion_.dispose
+    do @executablePath_.dispose
+    do @ignoreErrorsAndWarnings_.dispose
+    do @skipFiles_.dispose
+    do @useMcCabe_.dispose
+    do @usePEP8_.dispose
+    do @usePEP257_.dispose
+    do @usePyFlakes_.dispose
+    do @usePyLint_.dispose
+

   executionCheckHandler: (error, stdout, stderr) =>
     pylamaVersion = ''
-    if not @enabled
-      versionRegEx = /pylama(.exe)? ([\d\.]+)/
-      if versionRegEx.test(stderr)
-        pylamaVersion = versionRegEx.exec(stderr)[0]
-      else if versionRegEx.test(stdout)
-        pylamaVersion = versionRegEx.exec(stdout)[0]
-      if not pylamaVersion
-        result = if error? then '#' + error.code + ': ' else ''
-        result += 'stdout: ' + stdout if stdout.length > 0
-        result += 'stderr: ' + stderr if stderr.length > 0
-        result = result.replace(/\r\n|\n|\r/, '')
-        console.error "Linter-Pylama: \"#{@pylamaPath}\" \
-        was not executable: \"#{result}\". \
-        Please, check executable path in the linter settings."
-        return
-      @enabled = true
+    versionRegEx = /pylama(.exe)? ([\d\.]+)/
+    if versionRegEx.test(stderr)
+      pylamaVersion = versionRegEx.exec(stderr)[0]
+    else if versionRegEx.test(stdout)
+      pylamaVersion = versionRegEx.exec(stdout)[0]
+    if not pylamaVersion
+      result = if error? then '#' + error.code + ': ' else ''
+      result += 'stdout: ' + stdout if stdout.length > 0
+      result += 'stderr: ' + stderr if stderr.length > 0
+      result = result.replace(/\r\n|\n|\r/, '')
+      console.error "Linter-Pylama: \"#{@pylamaPath}\" \
+      was not executable: \"#{result}\". \
+      Please, check executable path in the linter settings."
+      @pylamaPath = ''
+      return
     log "Linter-Pylama: found " + pylamaVersion
-    do @initCmd
+

   initPythonPath: =>
     pythonPath = if process.env['PYTHONPATH'] then process.env.PYTHONPATH else ''
@@ -110,30 +80,45 @@
     if pythonPath
       process.env.PYTHONPATH = "#{process.env.PYTHONPATH}#{sep}#{pythonPath}"

-  initCmd: =>
-      if @enabled
-        @cmd = "#{@pylamaPath}"
-        if @ignoreErrors
-          @cmd = "#{@cmd} #{@ignoreErrors}"
-        if @skipFiles
-          @cmd = "#{@cmd} #{@skipFiles}"
-        linters = [
-          @usePyFlakes
-          @useMcCabe
-          @usePEP8
-          @usePyLint
-          @usePEP257
-        ].join()
-        linters = linters.replace /(,+)/g, ','
-        linters = linters.replace /(^,+)|(,+$)/g, ''
-        if not linters
-          linters = 'none'
-        @cmd = "#{@cmd} -l #{linters}"
-        log 'Linter-Pylama: initialization completed'
+
+  initPylama: =>
+    pylamaVersion = atom.config.get 'linter-pylama.pylamaVersion'
+    pylamaPath = atom.config.get 'linter-pylama.executablePath'
+
+    if pylamaVersion is 'external' and pylamaPath isnt @pylamaPath
+      @pylamaPath = pylamaPath
+      exec "#{@pylamaPath} --version", @executionCheckHandler
+    else
+      @pylamaPath = path.join path.dirname(__dirname), 'bin', 'pylama.py'
+
+    do @initCmd

+  initCmd: =>
+    if not @pylamaPath
+      @cmd = ''
+      return
+    cmd = [@pylamaPath]
+
+    ignoreEW = atom.config.get 'linter-pylama.ignoreErrorsAndWarnings'
+    if ignoreEW then cmd.push ['-i', ignoreEW]
+
+    skipFiles = atom.config.get 'linter-pylama.skipFiles'
+    if skipFiles then cmd.push ['--skip', skipFiles]
+
+    usePyLint = if atom.config.get 'linter-pylama.usePylint' then 'pylint' else ''
+    useMcCabe = if atom.config.get 'linter-pylama.useMccabe' then 'mccabe' else ''
+    usePEP8 = if atom.config.get 'linter-pylama.usePep8' then 'pep8' else ''
+    usePEP257 = if atom.config.get 'linter-pylama.usePep257' then 'pep257' else ''
+    usePyFlakes = if atom.config.get 'linter-pylama.usePyflakes' then 'pyflakes' else ''
+
+    linters = [usePyLint, useMcCabe, usePEP8, usePEP257, usePyFlakes].filter (e) -> e isnt ''
+    if linters.length then cmd.push ['-l', do linters.join] else ['-l', 'none']
+
+    @cmd = cmd
+
   lintFile: (filePath, callback) =>
-    if @enabled
+    if @cmd
       super filePath, callback

   formatMessage: (match) ->


Remove underscores in variable names

Remove underscores in variable names

--- lib/linter-pylama.coffee
+++ lib/linter-pylama.coffee
@@ -18,41 +18,41 @@
     @subscriptions = new CompositeDisposable
     @subscriptions.add atom.config.observe 'linter-pylama.pylamaVersion',
     (pylamaVersion) =>
-      if @pylamaVersion_
-        @pylamaVersion_ = pylamaVersion
+      if @pylamaVersion
+        @pylamaVersion = pylamaVersion
         do @initPylama
       else
-        @pylamaVersion_ = pylamaVersion
+        @pylamaVersion = pylamaVersion

     @subscriptions.add atom.config.observe 'linter-pylama.executablePath',
     (executablePath) =>
-      @executablePath_ = executablePath
+      @executablePath = executablePath
       do @initPylama

     @subscriptions.add atom.config.observe 'linter-pylama.ignoreErrorsAndWarnings',
     (ignoreErrorsAndWarnings) =>
       ignoreErrorsAndWarnings = ignoreErrorsAndWarnings.replace /\s+/g, '' if ignoreErrorsAndWarnings
-      @ignoreErrorsAndWarnings_ = ignoreErrorsAndWarnings
+      @ignoreErrorsAndWarnings = ignoreErrorsAndWarnings

     @subscriptions.add atom.config.observe 'linter-pylama.skipFiles',
     (skipFiles) =>
-      @skipFiles_ = skipFiles
+      @skipFiles = skipFiles

     @subscriptions.add atom.config.observe 'linter-pylama.useMccabe',
     (useMcCabe) =>
-      @useMcCabe_ = useMcCabe
+      @useMcCabe = useMcCabe

     @subscriptions.add atom.config.observe 'linter-pylama.usePep8',
     (usePEP8) =>
-      @usePEP8_ = usePEP8
+      @usePEP8 = usePEP8

     @subscriptions.add atom.config.observe 'linter-pylama.usePep257',
     (usePEP257) =>
-      @usePEP257_ = usePEP257
+      @usePEP257 = usePEP257

     @subscriptions.add atom.config.observe 'linter-pylama.usePyflakes',
     (usePyFlakes) =>
-      @usePyFlakes_ = usePyFlakes
+      @usePyFlakes = usePyFlakes

     @subscriptions.add atom.config.observe 'linter-pylama.usePylint',
     (usePyLint) =>
@@ -68,7 +68,7 @@

     @subscriptions.add atom.config.observe 'linter-pylama.configFileName',
     (configFileName) =>
-      @configFileName_ = configFileName
+      @configFileName = configFileName

   destroy: ->
     do @subscriptions.dispose
@@ -94,10 +94,8 @@

   initPylama: =>
-    pylamaVersion = atom.config.get 'linter-pylama.pylamaVersion'
-    pylamaPath = atom.config.get 'linter-pylama.executablePath'
-
-    if pylamaVersion is 'external' and pylamaPath isnt @pylamaPath
+    pylamaPath = @executablePath
+    if @pylamaVersion is 'external' and pylamaPath isnt @pylamaPath
       if /^(pylama|pylama\.exe)$/.test pylamaPath
         processPath = process.env.PATH or process.env.Path
         processPath.split(path.delimiter).forEach (dir) =>
@@ -126,18 +124,18 @@
     args = ['-F']

     if @configFileLoad_[0] is 'U' # 'Use pylama config'
-      configFilePath = helpers.findCached curDir, @configFileName_
+      configFilePath = helpers.findCached curDir, @configFileName

     if configFilePath then args.push.apply args, ['--options', configFilePath]
     else
-      if @ignoreErrorsAndWarnings_ then args.push.apply args, ['--ignore', @ignoreErrorsAndWarnings_]
-      if @skipFiles_ then args.push.apply args, ['--skip', @skipFiles_]
+      if @ignoreErrorsAndWarnings then args.push.apply args, ['--ignore', @ignoreErrorsAndWarnings]
+      if @skipFiles then args.push.apply args, ['--skip', @skipFiles]

       usePyLint = if @usePyLint_ then 'pylint' else ''
-      useMcCabe = if @useMcCabe_ then 'mccabe' else ''
-      usePEP8 = if @usePEP8_ then 'pep8' else ''
-      usePEP257 = if @usePEP257_ then 'pep257' else ''
-      usePyFlakes = if @usePyFlakes_ then 'pyflakes' else ''
+      useMcCabe = if @useMcCabe then 'mccabe' else ''
+      usePEP8 = if @usePEP8 then 'pep8' else ''
+      usePEP257 = if @usePEP257 then 'pep257' else ''
+      usePyFlakes = if @usePyFlakes then 'pyflakes' else ''

       linters = [usePEP8, usePEP257, usePyLint, usePyFlakes, useMcCabe].filter (e) -> e isnt ''
       args.push '--linters'


:bug: #14 guard in case undo stack was empty.

--- lib/misc-commands.coffee
+++ lib/misc-commands.coffee
@@ -98,7 +98,7 @@
       else
         firstAdded or lastRemoved

-    fn(range)
+    fn(range) if range?
     if settings.get('flashOnUndoRedo')
       @onDidOperationFinish =>
         timeout = settings.get('flashOnUndoRedoDuration')


Add isSelectorMatch() method

Add isSelectorMatch() method

- Added a method to check whether a selector matches a doc.

--- src/Objects.coffee
+++ src/Objects.coffee
@@ -83,3 +83,14 @@
       unless !value? or ((Types.isObjectLiteral(value) or Types.isArray()) and _.isEmpty(value))
         flatTrimmed[key] = value
     @unflattenProperties(flatTrimmed)
+
+  # Returns a boolean for whether the given selector object matches the given doc object.
+  isSelectorMatch: (doc, selector) ->
+    _.all selector, (value, field) ->
+      docValue = Objects.getModifierProperty(doc, field)
+      # TODO(aramk) Perform this with Mongo.
+      if Types.isObject(value) and value.$exists?
+        docValue? == value.$exists
+      else
+        # Allow undefined/null values for the comparison value.
+        docValue == value or (!docValue? and !value?)


replace _.contains call with _.includes

replace _.contains call with _.includes

--- assets/js/mutations.coffee
+++ assets/js/mutations.coffee
@@ -38,7 +38,7 @@
     # It is sufficient to check if the row is an ancestor of the new parent,
     # because if there was a clone underneath the row which was an ancestor of 'parent',
     # then 'row' would also be an ancestor of 'parent'.
-    if _.contains (view.data.allAncestors parent_id, { inclusive: true }), id
+    if _.includes (view.data.allAncestors parent_id, { inclusive: true }), id
       view.showMessage "Cloned rows cannot be nested under themselves", {text_class: 'error'}
       return false
     return true


use the same local version of identifiers

--- lib/optimizations.coffee
+++ lib/optimizations.coffee
@@ -1,14 +1,13 @@
 esprima = require 'esprima'
 escodegen = require 'escodegen'

-syntax = esprima.Syntax
-_BinaryExpression = syntax.BinaryExpression
-_UnaryExpression = syntax.UnaryExpression
-_Literal = syntax.Literal
-_AssignmentExpression = syntax.AssignmentExpression
-_Identifier = syntax.Identifier
-_UpdateExpression = syntax.UpdateExpression
-_VariableDeclaration = syntax.VariableDeclaration
+{ BinaryExpression,
+  UnaryExpression,
+  Literal,
+  AssignmentExpression,
+  Identifier,
+  UpdateExpression,
+  VariableDeclaration } = esprima.Syntax;

 debug = require 'debug'
 { TreeVisitor } = require 'nodevisitor'
@@ -39,10 +38,10 @@
 # ==/!= of constants null or undefined
 #
 class EqIdioms extends TreeVisitor
-        is_typeof = (e) -> e.type is _UnaryExpression and e.operator is "typeof"
-        is_string_literal = (e) -> e.type is _Literal and typeof e.value is "string"
-        is_undefined_literal = (e) -> e.type is _Literal and e.value is undefined
-        is_null_literal = (e) -> e.type is _Literal and e.value is null
+        is_typeof = (e) -> e.type is UnaryExpression and e.operator is "typeof"
+        is_string_literal = (e) -> e.type is Literal and typeof e.value is "string"
+        is_undefined_literal = (e) -> e.type is Literal and e.value is undefined
+        is_null_literal = (e) -> e.type is Literal and e.value is null

         visitBinaryExpression: (exp) ->
                 return super if exp.operator isnt "==" and exp.operator isnt "===" and exp.operator isnt "!=" and exp.operator isnt "!=="
@@ -74,7 +73,7 @@
                         rv = create_intrinsic intrinsic, [typeofarg]
                         if exp.operator[0] is '!'
                                 rv = {
-                                        type: _UnaryExpression
+                                        type: UnaryExpression
                                         operator: "!"
                                         argument: rv
                                 }
@@ -88,7 +87,7 @@
                                 rv = create_intrinsic isNullOrUndefined_id, [checkarg]
                                 if exp.operator is "!="
                                         rv = {
-                                                type: _UnaryExpression
+                                                type: UnaryExpression
                                                 operator: "!"
                                                 argument: rv
                                         }
@@ -102,7 +101,7 @@
                                 rv = create_intrinsic isNull_id, [checkarg]
                                 if exp.operator is "!=="
                                         rv = {
-                                                type: _UnaryExpression
+                                                type: UnaryExpression
                                                 operator: "!"
                                                 argument: rv
                                         }
@@ -114,7 +113,7 @@
                                 rv = create_intrinsic isNull_id, [checkarg]
                                 if exp.operator is "!=="
                                         rv = {
-                                                type: _UnaryExpression
+                                                type: UnaryExpression
                                                 operator: "!"
                                                 argument: rv
                                         }
@@ -125,7 +124,7 @@
         constructor: -> super

         visitUnaryExpression: (n) ->
-                if n.operator is "void" and n.argument.type is _Literal and n.argument.value is 0
+                if n.operator is "void" and n.argument.type is Literal and n.argument.value is 0
                         return create_intrinsic builtinUndefined_id, []
                 n

@@ -156,40 +155,40 @@
 class LoopCounter extends TreeVisitor
         visitFor: (exp) ->
                 # we only handle update and assignment expressions
-                if exp.update.left.type isnt _Identifier
+                if exp.update.left.type isnt Identifier
                         return super

                 update_identifier_name = exp.update.left.name

-                if exp.update.type isnt _UpdateExpression and exp.update.type isnt _AssignmentExpression
-                        return super
-
-                if exp.update.type is _AssignmentExpression
+                if exp.update.type isnt UpdateExpression and exp.update.type isnt AssignmentExpression
+                        return super
+
+                if exp.update.type is AssignmentExpression
                         update_right = exp.update.right

                         # of the +=|-=|*=|/= operators, we only handle + and -
                         if exp.update.operator.length is 2
                                 if exp.update.operator[0] is '+' or exp.update.operator[0] is '-'
-                                        if update_right.type isnt _Literal or (typeof update_right.value) isnt 'number'
+                                        if update_right.type isnt Literal or (typeof update_right.value) isnt 'number'
                                                 return super
                                         # else we win and the update expression is ok
                                 else
                                         return super
                         # and for normal assignments, the rhs must be a binary expression with operator of + or - with the same identifier and an integer literal
                         else
-                                if update_right.type isnt _BinaryExpression
+                                if update_right.type isnt BinaryExpression
                                         return super

                                 if update_right.operator isnt '+' and update_right.operator isnt '-'
                                         return super

-                                if update_right.left.type is _Identifier and update_right.right.type is _Literal
+                                if update_right.left.type is Identifier and update_right.right.type is Literal
                                         if typeof update_right.right.value isnt 'number'
                                                 return super
                                         else if update_right.left.name isnt update_identifier_name
                                                 return super
                                         # else we win and the update expression is ok
-                                else if update_right.left.type is _Literal and update_right.right.type is _Identifier
+                                else if update_right.left.type is Literal and update_right.right.type is Identifier
                                         if typeof update_right.left.value isnt 'number'
                                                 return super
                                         else if update_right.right.name isnt update_identifier_name
@@ -198,8 +197,8 @@
                                 else
                                         return super

-                else if exp.update.type is _UpdateExpression
-                        if update_right.type isnt _Literal or typeof update_right.value isnt 'number'
+                else if exp.update.type is UpdateExpression
+                        if update_right.type isnt Literal or typeof update_right.value isnt 'number'
                                 return super
                         # else we win and the update expression is ok
                 else
@@ -207,18 +206,18 @@

                 # now make sure the init block initializes update_identifier_name

-                if exp.init.type isnt _VariableDeclaration and exp.init.type isnt _AssignmentExpression
-                        return super
-
-                if exp.init.type is _VariableDeclaration
+                if exp.init.type isnt VariableDeclaration and exp.init.type isnt AssignmentExpression
+                        return super
+
+                if exp.init.type is VariableDeclaration
                         found = false
                         # check for var/let $update_identifier_name = ...
-                        found = found || (decl.id.name is update_identifier_name and decl.init.type is _Literal and typeof decl.init.value is 'number') for decl in exp.init.declarations
+                        found = found || (decl.id.name is update_identifier_name and decl.init.type is Literal and typeof decl.init.value is 'number') for decl in exp.init.declarations
                         if not found
                                 return super
                         # else we win and the initializer is ok
                 else
-                        if exp.init.left.type isnt _Identifier or exp.init.left.name isnt update_identifier_name
+                        if exp.init.left.type isnt Identifier or exp.init.left.name isnt update_identifier_name
                                 return super
                         # else we win and the initializer is ok


Sort available packages by name

Sort available packages by name

--- lib/package-panel.coffee
+++ lib/package-panel.coffee
@@ -110,6 +110,7 @@
         @availablePackages.append errorView
         @logApmError(error)
       else
+        @packages = _.sortBy(@packages, 'name')
         for pack in @packages
           view = new PackageView(pack, @packageEventEmitter)
           if pack.theme


fixing error

fixing error

Former-commit-id: e8ae20e6db5029bc0260c9038111c111eb38a0d5 [formerly f81026a29cb1708e831d52d40feee5ba21102e16] [formerly 0e4cc0c3f76a05be68c2c6ba9beb2cd923bc30f5 [formerly 0e4cc0c3f76a05be68c2c6ba9beb2cd923bc30f5 [formerly 2780e9d7b98c516d397b85782f3671e77d4c62c6]]]
Former-commit-id: 31c254121989ae5d5d6d5d6a5a9faeee14cddb02 [formerly 03e5609578f8f725eb5269aade225ff7beb02fe8]
Former-commit-id: 8eb13450cb1a28b0185fa5af9231190da98c2ccd
Former-commit-id: e2f5808d023a015d9dd1135e5732b524e2d68453


Remove superfluous slash

Remove superfluous slash

--- app/assets/javascripts/data/adapter.coffee
+++ app/assets/javascripts/data/adapter.coffee
@@ -11,7 +11,7 @@

   buildURL: (record, suffix) ->
     url = @_super(record, suffix)
-    url = "/search_types/#{url}" unless record.search(/_search$/) is -1
+    url = "/search_types#{url}" unless record.search(/_search$/) is -1
     url


Add operator chaining.

Add operator chaining.

--- src/nodes.coffee
+++ src/nodes.coffee
@@ -1376,8 +1376,8 @@
   compileChain: (o) ->
     [@first.second, shared] = @first.second.cache o
     fst = @first.compile o, LEVEL_OP
-    code = "#{fst} #{if @invert then '&&' else '||'} #{ shared.compile o } #{@operator} #{ @second.compile o, LEVEL_OP }"
-    "(#{code})"
+    code = "(#{if @invert then '&&' else '||'} #{fst} (#{@operator} #{ shared.compile o } #{ @second.compile o, LEVEL_OP }))"
+    "#{code}"

   compileExistence: (o) ->
     if @first.isComplex()


A new version of chat script - more robust and competent.

--- scripts/chat.coffee
+++ scripts/chat.coffee
@@ -1,4 +1,4 @@
-# Description:
+#### Description:
 #   Respond to custom answers
 #
 # Dependencies:
@@ -10,27 +10,16 @@
 # Command:
 #
 # Author:
-#   NNA
+#   Robert Carroll

 moment = require 'moment'
+_ = require('underscore')

-question_bank = [
-		{
-			id: 0
-			question: "What's the quietest thing you can hear?"
-			answers: []
-		}
-		{
-			id: 1
-			question: "What makes me unique? "
-			answers: []
-		}
-		{
-			id: 2
-			question: "What one word would you use to describe me?"
-			answers: []
-		}
-	]
+initial_question_bank = {
+	1: {question: "What's the quietest thing you can hear?", my_answer: '', answers: []},
+	2: {question: "What one word would you use to describe me?", my_answer: '', answers: []}
+	3: {question: "What makes me unique?", my_answer: '', answers: []}
+}

 # Varied response
 response_to_answer = ["I see. ", "Interesting. "]
@@ -41,10 +30,13 @@
 module.exports = (robot) ->
 	robot.brain.on 'loaded', =>
 		robot.logger.info "Loading knowledge"
-		robot.brain.data.questions ?= []
+		robot.brain.data.questions ?= {}

-		robot.brain.data.questions = question_bank unless robot.brain.data.questions.length
+		if _.isUndefined(robot.brain.data.questions) then robot.brain.data.questions = initial_question_bank
+		question_bank = robot.brain.data.questions
 		current_question = null
+		question_ids = Object.keys(question_bank)
+		used_questions = []

 		# Fisher-Yates shuffle in Coffeescript https://gist.github.com/smrchy/3098096
 		shuffle = (a) ->
@@ -56,62 +48,28 @@
         a[i] = t
     a

-		getQuestion = (cb) ->
-			# get the IDs of all the questions
-			question_id = (get_question.id for get_question in robot.brain.data.questions)
-			robot.logger.info question_id
-			# generate a random question ID
-			shuffle (question_id)
-			current_question = question_id[0]
-			robot.logger.info current_question
-			cb current_question
+		getQuestion = (cb) ->
+			shuffle (question_ids)
+			current_question = question_ids[0]
+			if _.isUndefined(current_question)
+				question_text = "Thanks for talking to me. "
+			else
+				used_questions.push current_question
+				robot.logger.info used_questions
+				question_ids = _.difference(question_ids, used_questions)
+				question_text = question_bank[current_question].question
+			cb question_text

 		robot.hear /chat/i, (msg) ->
-			getQuestion (current_question) ->
-				question_text = robot.brain.data.questions[current_question].question
+			getQuestion (question_text) ->
 				msg.send question_text
-
-			# need to remove the current question id from the question_id so it isn't repeated

 		robot.hear /answer (.*)$/i, (msg) ->
-			date = moment().unix()
 			answer = msg.match[1]
-			new_answer = {date: date, answer: answer}
-
-			for key,value of robot.brain.data.questions
-				if current_question == value.id
-					value.answers.push new_answer
-					robot.logger.info robot.brain.data.questions[current_question].answers
-			answers_without_current = robot.brain.data.questions[current_question].answers.filter (exclude_current) -> exclude_current isnt new_answer
-			shuffled_answer = shuffle (answers_without_current)
-			robot.logger.info shuffled_answer
-
-			getQuestion (current_question) ->
-				question_text = robot.brain.data.questions[current_question].question
-				msg.send [msg.random response_to_answer] + [if shuffled_answer.length > 1 then 'Someone else told me ' + "'" + shuffled_answer[0].answer + "'. "] + [msg.random conjunction] + question_text
-
-		# Add a new question via Hubot
-		robot.hear /question4 (.*)$/i, (msg) ->
-			question_value = msg.match[1]
-			next_id = robot.brain.data.questions.length
-			new_question = {id:next_id, question: question_value, answers: []}
-			robot.brain.data.questions.push new_question
-			robot.logger.info
-			msg.send "OK, I've added \"#{question_value}\" as a new question"
-
-		# Remove an existing question via Hubot
-		robot.hear /remove4 (.*)$/i, (msg) ->
-			question_cull = msg.match[1]
-			robot.logger.info question_cull
-			robot.brain.data.questions.splice(question_cull, 1)
-			msg.send "OK, I've removed question ID \"#{question_cull}\" from the question bank"
-
-		# see questions
-		robot.hear /view4/i, (msg) ->
-			robot.logger.info robot.brain.data.questions
-			msg.send "Questions displayed in log."
-
-
-
-
-
+			current_question_answers = question_bank[current_question].answers
+			current_question_answers.push answer
+			answers_without_current = current_question_answers.filter (exclude_current) -> exclude_current isnt answer
+			shuffled_answer = shuffle (answers_without_current)
+			getQuestion (question_text) ->
+				robot.logger.info robot.brain.data.questions
+				msg.send [msg.random response_to_answer] + [if shuffled_answer.length > 0 then 'Someone else told me ' + "'" + shuffled_answer[0] + "'. "] + question_text


request uses buildQuery method

request uses buildQuery method

--- src/Request.coffee
+++ src/Request.coffee
@@ -26,18 +26,18 @@

 	constructor: (@url, @type = 'GET', @data = null) ->
+		url = @url
 		@type = @type.toUpperCase()
 		if @type not in ['GET', 'POST', 'PUT', 'DELETE']
 			throw new Error 'Http request: type must be GET, POST, PUT or DELETE, ' + @type + ' given'

 		if @data != null
-			@data = Request.parseData(@data)
+			data = @Http.buildQuery(@data)
 			if @type != 'POST'
-				@url = if @url.indexOf('?') != -1 then @url + '&' + @data else @url + '?' + @data
-				@data = null
+				url = if @url.indexOf('?') != -1 then @url + '&' + data else @url + '?' + data

 		@xhr = Request.createRequestObject()
-		@xhr.open(@type, @url, true)
+		@xhr.open(@type, url, true)

 		if @type == 'POST'
 			@setHeader('Content-type', 'application/x-www-form-urlencoded')
@@ -56,7 +56,7 @@
 					if @success != null then @success(@response)
 					Request.callHttpEvent(@response, @, 'success')
 				else
-					error = new Error 'Can not load ' + @url + ' address'
+					error = new Error 'Can not load ' + url + ' address'
 					if @error != null then @error(error)
 					Request.callHttpEvent(@response, @, 'error', [error])

@@ -114,13 +114,6 @@
 			return new ActiveXObject("Microsoft.XMLHTTP")

-	@parseData: (data) ->
-		result = []
-		for name, value of data
-			result.push(name + '=' + encodeURIComponent(value))
-		return result.join('&')
-
-
 	@callHttpEvent: (response, request, event, args = []) ->
 		if Request.Http == null
 			Request.Http = require './Http'


tiny description fix

tiny description fix

--- scripts/dice.coffee
+++ scripts/dice.coffee
@@ -1,5 +1,5 @@
 # Description:
-#   Allows Hubot to roll dice usgin Invisible Castle grammar
+#   Allows Hubot to roll dice
 #
 # Dependencies:
 #   None


callback on verb.render method

callback on verb.render method

--- app/assets/javascripts/zvg/verbatim.coffee
+++ app/assets/javascripts/zvg/verbatim.coffee
@@ -6,7 +6,7 @@
     @_perPage = 15
     @question_id = question_id

-  render: (options = {}) ->
+  render: (options = {}, callback) ->
     dataFilter = (d) ->
       s = options.series_1 if options.series_1 && options.series_1 != '[all]'
       f = options.series_2 if options.series_2 && options.series_2 != '[all]'
@@ -37,6 +37,7 @@
       .append('span').attr('class', 'tag')

     tag_spans.text((d) -> d)
+    callback(@) if callback

   initializeQuestionTable: ->
@@ -60,7 +61,7 @@
   getData: (source) =>
     d3.json(source, (error, json) =>
       @data(json)
-      @render()
+      @render({}, (chart) -> console.log('getData callback, chart: ', chart))
     )

   seriesDomain: (d) ->


:haircut:

:haircut:

--- scripts/help.coffee
+++ scripts/help.coffee
@@ -80,7 +80,6 @@

 renamedHelpCommands = (robot) ->
-  robot_name = robot.alias or robot.name
   help_commands = robot.helpCommands().map (command) ->
-    command.replace /^hubot/i, robot_name
+    command.replace /^hubot/i, robot.name
   help_commands.sort()


Downcase the names

Downcase the names

--- scripts/rc.coffee
+++ scripts/rc.coffee
@@ -8,7 +8,7 @@

 module.exports = (robot) ->
   robot.respond /(?:rc)\s+for\s([^\s]+)$/i, (msg) ->
-    who = msg.match[1]
+    who = msg.match[1].toLowerCase()
     new RCScore(who).fetch (err, info, self) =>
       {score: score, when: stamp} = info
       if score == null


Provide street2 field from business address

--- src/address.coffee
+++ src/address.coffee
@@ -13,7 +13,8 @@
         dom = new DOMParser().parseFromString(body)
         [address] = xpath.select('/feed/company-info/addresses/address[@type=\'business\']', dom)
         street1 = xpath.select('street1/text()', address).toString()
+        street2 = xpath.select('street2/text()', address).toString()
         city = xpath.select('city/text()', address).toString()
         state = xpath.select('state/text()', address).toString()
         zip = xpath.select('zip/text()', address).toString()
-        callback(null, {street1, city, state, zip})
+        callback(null, {street1, street2, city, state, zip})


reduce grid noise

reduce grid noise

--- scripts/moca.coffee
+++ scripts/moca.coffee
@@ -7,8 +7,8 @@

 module.exports = (robot) ->

-   robot.hear /astro/i, (res) ->
-     res.send "We love Astronomy!"
+   # robot.hear /astro/i, (res) ->
+   #   res.send "We love Astronomy!"

    coffeeReplies = ['I feel the need for caffeine...', 'I love the smell of caffeine in the morning', 'Count me in', 'Caffeine me up!', 'Can we have tea?', 'I have been waiting all morning for this']

@@ -18,11 +18,11 @@
    robot.hear /pizza/i, (res) ->
      res.send "Did somebody mention pizza?"

-   robot.hear /seminar/i, (res) ->
-     res.send "Did somebody mention free lunch?"
+   # robot.hear /seminar/i, (res) ->
+   #   res.send "Did somebody mention free lunch?"

-   robot.hear /email/i, (res) ->
-     res.send "We don't use email anymore!"
+   # robot.hear /email/i, (res) ->
+   #   res.send "We don't use email anymore!"

-   robot.hear /There are no events today/i, (res) ->
-     res.send "SAD"
+   # robot.hear /There are no events today/i, (res) ->
+   #   res.send "SAD"


add neta

add neta

--- scripts/neta.coffee
+++ scripts/neta.coffee
@@ -32,7 +32,7 @@
   robot.respond /まだ$/, (msg) ->
     msg.send "http://livedoor.blogimg.jp/news4wide/imgs/1/5/15e0b96e-s.jpg?399313"

-  robot.respond /夢見させる$/, (msg) ->
+  robot.respond /夢$/, (msg) ->
     msg.send "http://livedoor.blogimg.jp/news4wide/imgs/3/c/3c12a3cc-s.jpg?399634"

   robot.respond /悪い予感$/, (msg) ->
@@ -65,4 +65,37 @@
   robot.respond /不運$/, (msg) ->
     msg.send "http://livedoor.blogimg.jp/sonisoku/imgs/3/c/3cb24eef.jpg"

+  robot.respond /タブー$/, (msg) ->
+    msg.send "http://imgcc.naver.jp/kaze/mission/USER/20120825/13/1153443/13/322x400x0cae962ba07b5bef57baded2.jpg"

+  robot.respond /もう十分$/, (msg) ->
+    msg.send "http://d13n9ry8xcpemi.cloudfront.net/photo/odai/400/8c459ebff3a7a3e37747c10cbc8759ae_400.jpg"
+
+  robot.respond /火$/, (msg) ->
+    msg.send "http://imgcc.naver.jp/kaze/mission/USER/20120729/15/1022545/6/640x544xc7ef58dcdf6dd36603ce0055.jpg"
+
+  robot.respond /欲しい$/, (msg) ->
+    msg.send "http://imgcc.naver.jp/kaze/mission/USER/20120804/15/1022545/62/320x355x77a0adf42a12f335b0e524c8.jpg"
+
+  robot.respond /ふあー$/, (msg) ->
+    msg.send "http://imgcc.naver.jp/kaze/mission/USER/20120804/15/1022545/23/300x325x47c2f5e4fdd0858cb51e912e.jpg"
+
+  robot.respond /殺$/, (msg) ->
+    msg.send "http://imgcc.naver.jp/kaze/mission/USER/20120825/15/1022545/14/299x441x9fbb0fb0afde641c9dbb8ee0.jpg"
+
+  robot.respond /いつもの$/, (msg) ->
+    msg.send "http://imgcc.naver.jp/kaze/mission/USER/20120804/15/1022545/20/183x202x05386c35c340d3c395201c8d.jpg"
+
+  robot.respond /古い$/, (msg) ->
+    msg.send "http://imgcc.naver.jp/kaze/mission/USER/20120804/15/1022545/18/150x226x04b971fa01c597cb75ad6d94.jpg"
+
+  robot.respond /りくつ$/, (msg) ->
+    msg.send "http://imgcc.naver.jp/kaze/mission/USER/20120804/15/1022545/2/214x214x04dc0980a33b9e4cc48b557b.jpg"
+
+  robot.respond /あほか$/, (msg) ->
+    msg.send "http://imgcc.naver.jp/kaze/mission/USER/20120804/15/1022545/19/240x320xc9757cc0590510d012293dbc.jpg"
+
+  robot.respond /無駄な時間$/, (msg) ->
+    msg.send "http://cdn-ak.f.st-hatena.com/images/fotolife/t/takashi0123/20100507/20100507231438.jpg"
+
+


ignore tmp files

ignore tmp files

--- lib/runnable/sync.coffee
+++ lib/runnable/sync.coffee
@@ -17,7 +17,7 @@

     watcher = chokidar.watch conf.local,
         ignored: (item) ->
-            ignored_extensions = /\.(less|git|coffee|tmpl|DS_Store)/i
+            ignored_extensions = /\.(less|git|coffee|tmpl|tmp|DS_Store)/i
             ignored_directories = /node_modules/i
             ignored_files = /grunt.js|cakefile/i


clean up

clean up

--- lib/cli.coffee
+++ lib/cli.coffee
@@ -38,7 +38,7 @@
   if result
     if options.raw
       process.stderr.write "Serializing JSON output...\n"  if options.verbose
-      process.stdout.write JSON.stringify(result, null, 2) + '\n'
+      process.stdout.write JSON.stringify(result, null, 2)
     else
       process.stderr.write "Producing colored output...\n"  if options.verbose
       process.stdout.write colorize(result, color: options.color)


Adding disable/enable methods. I needed a way to turn on/off the panning behaviour, so it would not interfere with a jQuery UI draggable

--- lib/assets/javascripts/zui53/tools/pan_tool.js.coffee
+++ lib/assets/javascripts/zui53/tools/pan_tool.js.coffee
@@ -5,6 +5,7 @@
     constructor: (zui)->
       @vp = zui
       @eventDispatcher = zui.viewport #window
+      @disabled = false

     attach: ()=>
       # console.log "Attaching PAN"
@@ -22,6 +23,9 @@
       $(@eventDispatcher).unbind 'touchstart', @touch_start

     start: (e)=>
+      if @disabled
+        return
+
       # console.log "start panning"
       $('body').addClass('panning')

@@ -31,6 +35,12 @@
       window.addEventListener 'mouseup', @stop, true

       @stopEvent(e)
+
+    disable: ->
+      @disabled = true
+
+    enable: ->
+      @disabled = false

     pan: (e)=>
       @_pan_with(e.screenX, e.screenY)
@@ -48,6 +58,9 @@
       @stopEvent(e)

     touch_start: (e)=>
+      if @disabled
+        return
+
       # console.log "start panning (touch)"
       @_start_with(e.originalEvent.touches[0].clientX, e.originalEvent.touches[0].clientY)
       @eventDispatcher.addEventListener 'touchmove', @touch_move, true


sync available packages rather then loaded packages

--- lib/sync-settings.coffee
+++ lib/sync-settings.coffee
@@ -190,10 +190,25 @@

   getPackages: ->
     packages = []
-    for own name, info of atom.packages.getLoadedPackages()
-      {name, version, theme, apmInstallSource} = info.metadata
+    for i, metadata of @_getAvailablePackageMetadataWithoutDuplicates()
+      {name, version, theme, apmInstallSource} = metadata
       packages.push({name, version, theme, apmInstallSource})
     _.sortBy(packages, 'name')
+
+  _getAvailablePackageMetadataWithoutDuplicates: ->
+    path2metadata = {}
+    package_metadata = atom.packages.getAvailablePackageMetadata()
+    for path, i in atom.packages.getAvailablePackagePaths()
+      path2metadata[fs.realpathSync(path)] = package_metadata[i]
+
+    packages = []
+    for i, pkg_name of atom.packages.getAvailablePackageNames()
+      pkg_path = atom.packages.resolvePackagePath(pkg_name)
+      if path2metadata[pkg_path]
+        packages.push(path2metadata[pkg_path])
+      else
+        console.error('could not correlate package name, path, and metadata')
+    packages

   restore: (cb=null) ->
     @createClient().gists.get


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/bounded.coffee
+++ src/bounded.coffee
@@ -32,9 +32,7 @@
 @param {Core} self Reference to including object
 ###

-Bounded = (I, self) ->
-  I ||= {}
-
+Bounded = (I={}, self) ->
   Object.reverseMerge I,
     x: 0
     y: 0


add polarToVector helper, reorganize a bit

--- app/js/main.coffee
+++ app/js/main.coffee
@@ -7,9 +7,9 @@

 QUARTER_PI = Math.PI / 4

-MAX_THRUST = 2000
 SHIP_MASS = 10
 SHIP_SIZE = 50
+MAX_THRUST = 2000

 Vec =
   add: ([x1, y1], [x2, y2]) ->
@@ -18,6 +18,8 @@
     [x1 - x2, y1 - y2]
   mul: ([x, y], c) ->
     [x * c, y * c]
+  polarToVector: (theta, length) ->
+    [ cos(theta) * length, sin(theta) * length ]

 class Ship
   constructor: (x, y, @theta= HALF_PI * 3) ->
@@ -31,10 +33,8 @@
       @theta -= PI/30

     if @thrust = controls.up
-      a = MAX_THRUST / SHIP_MASS
-      deltaX = a * cos(@theta) * dt
-      deltaY = a * sin(@theta) * dt
-      @velocity = Vec.add @velocity, [deltaX, deltaY]
+      acceleration = Vec.polarToVector(@theta, MAX_THRUST / SHIP_MASS)
+      @velocity = Vec.add @velocity, Vec.mul(acceleration, dt)

     @position = Vec.add @position, Vec.mul(@velocity, dt)

@@ -110,16 +110,9 @@
   left: false
   right: false

-ctx.update = ->
-  ship.update ctx.dt / 1000, controls
+drawWrapped = ([xEdge,yEdge], drawFn) ->
+  drawFn()

-ctx.draw = ->
-  ship.draw(ctx)
-  drawWrappedEdges ship.nearEdges(ctx), -> ship.draw(ctx)
-
-  stats.update()
-
-drawWrappedEdges = ([xEdge,yEdge], drawFn) ->
   if xEdge isnt 0
     ctx.translate xEdge * ctx.width, 0
     drawFn()
@@ -129,6 +122,13 @@
     ctx.translate 0, yEdge * ctx.height
     drawFn()
     ctx.translate 0, -yEdge * ctx.height
+
+ctx.update = ->
+  ship.update ctx.dt / 1000, controls
+
+ctx.draw = ->
+  drawWrapped ship.nearEdges(ctx), -> ship.draw(ctx)
+  stats.update()

 ctx.keyup = (e) ->
   switch e.keyCode


Activity: making sure that we always have a ref to sidebar

--- client/Social/Activity/AppView.coffee
+++ client/Social/Activity/AppView.coffee
@@ -19,6 +19,12 @@
       mainView
     }            = KD.singletons
     {entryPoint} = KD.config
+    # this is also assigned in viewAppended
+    # when you land on a privateMessage directly
+    # sidebar here becomes undefined
+    # and once view is appended we make sure
+    # that the sidebar property is set.
+    # a terrible hack, should be addressed later. - SY
     @sidebar     = mainView.activitySidebar
     @appStorage  = appStorageController.storage 'Activity', '2.0'
     @panePathMap = {}
@@ -48,6 +54,8 @@

   viewAppended: ->

+    # see above for the terrible hack note - SY
+    @sidebar ?= KD.singletons.mainView.activitySidebar
     @addSubView @tabs

     @parent.on 'KDTabPaneActive', =>


Cleanup template

Cleanup template

--- src/control.coffee
+++ src/control.coffee
@@ -3,7 +3,7 @@
   T.div {class: "controls"}, [
     T.div {class: "controls"}, [
       T.button {
-        init: -> @click =>
+        click: ->
           console.log(me.get("j"))
           me.put("j", me.get("j") - 1)
           console.log(me.get("j"))


Move controls

Move controls

--- src/control.coffee
+++ src/control.coffee
@@ -9,23 +9,19 @@
     }, label
   draw_caller = (key, dir) ->
     label = key[0].toUpperCase() + key[1..-1].toLowerCase()
+    if dir?
+      label += if dir == -1 then ' Right' else ' Left'
     T.button {
       class: ["control", key, label]
       click: -> me.call(key, {dir: dir})
-    }, "#{label} #{dir}"
+    }, label
   T.div {
     class: "controls"
     style: "margin-top: -#{size}px; margin-left: #{size}px;"
   }, [
-    T.div {class: "instructions absolute"}, [
-      draw_button("West", "i", -1)
-      draw_button("North", "j", -1)
-      draw_button("South", "j", 1)
-      draw_button("East", "i", 1)
-    ]
     T.div {class: "instructions relative"}, [
+      draw_caller('turn',  1)
       draw_caller('go')
-      draw_caller('turn',  1)
       draw_caller('turn', -1)
     ]
     T.p "More coming soon..."


Add findRelative method

Add findRelative method

--- client/lanes/lib/utilFunctions.coffee
+++ client/lanes/lib/utilFunctions.coffee
@@ -38,13 +38,25 @@
         else
             file.namespace[subPath]?[name] || Lanes[subPath]?[name] || Lanes.u.getPath(name)

+    #
+    findRelative: (name, file)->
+        parts = _.map(file.path.slice(0, file.path.length - 1), (comp)->
+            _.classify(comp);
+        )
+        obj = _.reduce( parts, ( ( memo, val )-> return if memo then memo[ val ] else null ), Lanes )
+        obj?[name]
+
     # Can be called one of two ways:
     # With ns being a string, which will attempt to deref it then deref name inside it
     # or with ns being an object, which will dref name inside it
     getPath: ( name, ns='Lanes' ) ->
         return name unless _.isString(name)
         ns = "Lanes.#{ns}" if _.isString(ns) && !ns.match("Lanes")
-        distillTypes(name, window) || distillTypes(name, if _.isObject(ns) then ns else distillTypes(ns, window) )
+        object = distillTypes(name, window)
+        if ! object
+            ns=if _.isObject(ns) then ns else distillTypes(ns, window)
+            object=distillTypes(name, ns )
+        object

     # Like underscore's results but allows passing
     # arguments to the function if it's called