Require the root of the project in the test

--- test/base58_test.coffee
+++ test/base58_test.coffee
@@ -1,6 +1,6 @@
 require 'should'

-Base58 = require '../lib/base58'
+Base58 = require '..'

 examples = require './examples'


editor resize

editor resize

--- js/editor.coffee
+++ js/editor.coffee
@@ -232,11 +232,17 @@

     @editor.on 'change', @on.change
     height = window.innerHeight
-    console.log height
     @editor.setSize null, "#{height - 100}px"
     @elems.preview.style.maxHeight = "#{height - 50}px"
     @editor.setValue Sample

+    window.addEventListener 'resize', @on.resize
+
+   @listen 'resize', ->
+    height = window.innerHeight
+    @editor.setSize null, "#{height - 100}px"
+    @elems.preview.style.maxHeight = "#{height - 50}px"
+
    render: ->
     @elems.container = document.body
     Weya elem: @elems.container, context: this, @template


Add no click delay support.

Add no click delay support.

--- js/flight.coffee
+++ js/flight.coffee
@@ -7,6 +7,7 @@
   pageHistory: [""]
   os: {}
   hideUrlBar: false
+  noClickDelay: true
   # options
   mainMenu: '#main-menu'
   menuOpen: false
@@ -42,8 +43,8 @@

     if @hideUrlBar is true then @hideUrlBar()

-    if NoClickDelay?
-      new NoClickDelay(document.getElementById('flight'));
+    if @noClickDelay is true
+      console.log new NoClickDelay(document.getElementById('flight'));

   # Goes to page, transitionAnimation defines if transition happens or not
   goTo: (targetPage, options) =>


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/flickerable.coffee
+++ test/flickerable.coffee
@@ -35,6 +35,7 @@
   player.update()
   equals player.I.alpha, 0.2

+  # returns to original alpha when flicker duration hits 0
   player.update()
   equals player.I.alpha, 0.9


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/flickerable.coffee
+++ test/flickerable.coffee
@@ -17,6 +17,6 @@
   4.times ->
     player.update()

-  equ
+  equals player.I.alpha, 0.9

 module()


Make sure it is the two install specs failing

--- spec/install-spec.coffee
+++ spec/install-spec.coffee
@@ -8,12 +8,12 @@
 apm = require '../lib/apm-cli'
 Install = require '../lib/install'

-fdescribe 'apm install', ->
+describe 'apm install', ->
   [atomHome, resourcePath] = []

   beforeEach ->
     spyOnToken()
-    silenceOutput(true)
+    silenceOutput()

     atomHome = temp.mkdirSync('apm-home-dir-')
     process.env.ATOM_HOME = atomHome
@@ -485,7 +485,7 @@
           modPath = path.join(process.env.ATOM_HOME, 'packages', 'test-git-repo', 'node_modules', dep)
           expect(fs.existsSync(modPath)).toBeTruthy()

-    describe 'when installing a Git URL and --json is specified', ->
+    fdescribe 'when installing a Git URL and --json is specified', ->
       [cloneUrl, pkgJsonPath] = []

       beforeEach ->
@@ -513,7 +513,7 @@
           source: cloneUrl
           sha: sha

-    describe 'when installing a registred package and --json is specified', ->
+    fdescribe 'when installing a registred package and --json is specified', ->
       beforeEach ->
         callback = jasmine.createSpy('callback')
         apm.run(['install', "test-module", "test-module2", "--json"], callback)


Define functions onFocus and onBlur in clearField

--- theme_src/scripts/coffee/functions.coffee
+++ theme_src/scripts/coffee/functions.coffee
@@ -25,10 +25,10 @@
   # Used to clear the search form text
   ###
   $.fn.clearField = ->
-    onFocus ->
+    onFocus = ->
       this.value = '' if this.defaultValue is this.value
       return
-    onBlur ->
+    onBlur = ->
       this.value = this.defaultValue if not this.value.length
       return
     this.focus(onFocus).blur(onBlur)


:lipstick: Sort requires

:lipstick: Sort requires

--- src/theme-manager.coffee
+++ src/theme-manager.coffee
@@ -4,10 +4,10 @@
 {Emitter} = require 'emissary'
 fs = require 'fs-plus'

+{$} = require './space-pen-extensions'
+AtomPackage = require './atom-package'
 File = require './file'
 Package = require './package'
-AtomPackage = require './atom-package'
-{$} = require './space-pen-extensions'

 # Private: Handles discovering and loading available themes.
 #


none of the current callers actually have those properties

--- resources/assets/coffee/_classes/beatmap-discussion-helper.coffee
+++ resources/assets/coffee/_classes/beatmap-discussion-helper.coffee
@@ -122,13 +122,7 @@

   @moderationGroup: (user) =>
-    if user.groups?
-      _.intersection(user.groups, ['admin', 'qat', 'bng'])[0]
-    else
-      switch
-        when user.is_admin then 'admin'
-        when user.is_qat then 'qat'
-        when user.is_bng then 'bng'
+    _.intersection(user.groups, ['admin', 'qat', 'bng'])[0]

   @previewMessage = (message) =>


Fix for PATH update under new FS

Fix for PATH update under new FS

--- src/tools/addpath.coffee
+++ src/tools/addpath.coffee
@@ -15,7 +15,7 @@

 module.exports = (add)->
   orig = env key
-  bin = junction.$()
+  bin = "#{junction.$()}"
   x = for x in orig.split ';' when x != bin
     x
   x.unshift bin if add


fix skip logic selectors

fix skip logic selectors

--- jsapp/xlform/src/view.rowDetail.SkipLogic.coffee
+++ jsapp/xlform/src/view.rowDetail.SkipLogic.coffee
@@ -254,6 +254,7 @@
     attach_to: (target) ->
       target.find('.skiplogic__responseval').remove()
       super(target)
+      return

     bind_event: (handler) ->
       @$el.on 'blur', handler
@@ -294,8 +295,8 @@
     className: 'skiplogic__responseval'

     attach_to: (target) ->
-      super(target)
       target.find('.skiplogic__responseval').remove()
+      super(target)
       # workaround for missing elements when toggling skiplogic back and forth
       target.find('.skiplogic__responseval.select2-container').show()


removes debugger

removes debugger

--- app/assets/javascripts/hours_report/timesheets/edit.js.coffee
+++ app/assets/javascripts/hours_report/timesheets/edit.js.coffee
@@ -31,13 +31,11 @@
       $("#timesheet_calendar_events").attr("checked", true)

     $("form.edit_timesheet").submit (event) ->
-      debugger
       totalHours = 0
       for day in $(".edit_timesheet fieldset")
         dayType = $(day).find(".day-type").val()
         totalHours += parseFloat($(day).find(".day-value").val()) if dayType == "workday"

-      debugger
       if totalHours == 0 and !confirm("There are no hours in the timesheet, are you sure you want to continue?")
         event.preventDefault()


Save reference to sortable product list container

--- backend/app/assets/javascripts/spree/backend/taxons.js.coffee
+++ backend/app/assets/javascripts/spree/backend/taxons.js.coffee
@@ -1,19 +1,22 @@
 $(document).ready ->
-  $('#taxon_products').sortable();
-  $('#taxon_products').on "sortstop", (event, ui) ->
-    Spree.ajax
-      url: Spree.routes.classifications_api,
-      method: 'PUT',
-      data:
-        product_id: ui.item.data('product-id'),
-        taxon_id: $('#taxon_id').val(),
-        position: ui.item.index()
-
   window.productTemplate = Handlebars.compile($('#product_template_sortable').text());

   productListTemplate = (products) ->
     products.map(productTemplate).join('') ||
     "<h4>#{Spree.translations.no_results}</h4>"
+
+  saveSort = (event, ui) ->
+   Spree.ajax
+     url: Spree.routes.classifications_api,
+     method: 'PUT',
+     data:
+       product_id: ui.item.data('product-id'),
+       taxon_id: $('#taxon_id').val(),
+       position: ui.item.index()
+
+  sortable = $('#taxon_products').sortable()
+    .on
+      sortupdate: saveSort

   $('#taxon_id').select2
     dropdownCssClass: "taxon_select_box",
@@ -35,9 +38,8 @@
       taxon.pretty_name;

   $('#taxon_id').on "change", (e) ->
-    el = $('#taxon_products')
     Spree.ajax
       url: Spree.routes.taxon_products_api,
       data: { id: e.val }
       success: (data) ->
-        el.html productListTemplate(data.products)
+        sortable.html productListTemplate(data.products)


Convert to using the JS props system

Convert to using the JS props system

--- bokehjs/src/coffee/models/transforms/step_interpolator.coffee
+++ bokehjs/src/coffee/models/transforms/step_interpolator.coffee
@@ -1,14 +1,18 @@
 _ = require "underscore"
 Transform = require "./transform"
 Interpolator = require "./interpolator"
+p = require "../../core/properties"
+

 class StepInterpolator extends Interpolator.Model

   initialize: (attrs, options) ->
     super(attrs, options)

-  defaults: ->
-    return _.extend({}, super())
+  props: ->
+    return _.extend {}, super(), {
+      mode: [ p.String, "after"]
+    }

   compute: (x) ->
     # Apply the transform to a single value


Add new logget sections

Add new logget sections

--- packages/rocketchat-logger/server.coffee
+++ packages/rocketchat-logger/server.coffee
@@ -73,6 +73,7 @@
 			level: 0

 	constructor: (@name, config={}) ->
+		self = @
 		@config = {}

 		_.extend @config, config
@@ -82,16 +83,18 @@
 			return LoggerManager.loggers[@name]

 		for type, typeConfig of @defaultTypes
-			do (type, typeConfig) =>
-				@[type] = (args...) =>
-					@_log
+			do (type, typeConfig) ->
+				self[type] = (args...) ->
+					self._log.call self,
+						section: this.__section
 						type: type
 						level: typeConfig.level
 						method: typeConfig.name
 						arguments: args

-				@[type+"_box"] = (args...) =>
-					@_log
+				self[type+"_box"] = (args...) ->
+					self._log.call self,
+						section: this.__section
 						type: type
 						box: true
 						level: typeConfig.level
@@ -100,33 +103,58 @@

 		if @config.methods?
 			for method, typeConfig of @config.methods
-				do (method, typeConfig) =>
-					if @[method]?
-						@warn "Method", method, "already exists"
-
-					if not @defaultTypes[typeConfig.type]?
-						@warn "Method type", typeConfig.type, "doest not exists"
-
-					@[method] = (args...) =>
-						@_log
+				do (method, typeConfig) ->
+					if self[method]?
+						self.warn "Method", method, "already exists"
+
+					if not self.defaultTypes[typeConfig.type]?
+						self.warn "Method type", typeConfig.type, "doest not exists"
+
+					self[method] = (args...) ->
+						self._log.call self,
+							section: this.__section
 							type: typeConfig.type
-							level: if typeConfig.level? then typeConfig.level else @defaultTypes[typeConfig.type]?.level
+							level: if typeConfig.level? then typeConfig.level else self.defaultTypes[typeConfig.type]?.level
 							method: method
 							arguments: args

-					@[method+"_box"] = (args...) =>
-						@_log
+					self[method+"_box"] = (args...) ->
+						self._log.call self,
+							section: this.__section
 							type: typeConfig.type
 							box: true
-							level: if typeConfig.level? then typeConfig.level else @defaultTypes[typeConfig.type]?.level
+							level: if typeConfig.level? then typeConfig.level else self.defaultTypes[typeConfig.type]?.level
 							method: method
 							arguments: args

+		if @config.sections?
+			for section, name of @config.sections
+				do (section, name) ->
+					self[section] = {}
+					for type, typeConfig of self.defaultTypes
+						do (type, typeConfig) =>
+							self[section][type] = =>
+								self[type].apply {__section: name}, arguments
+
+							self[section][type+"_box"] = =>
+								self[type+"_box"].apply {__section: name}, arguments
+
+					for method, typeConfig of self.config.methods
+						do (method, typeConfig) =>
+							self[section][method] = =>
+								self[method].apply {__section: name}, arguments
+
+							self[section][method+"_box"] = =>
+								self[method+"_box"].apply {__section: name}, arguments
+
 		LoggerManager.register @
 		return @

 	getPrefix: (options) ->
-		prefix = "#{@name} ➔ #{options.method}"
+		if options.section?
+			prefix = "#{@name} ➔ #{options.section}.#{options.method}"
+		else
+			prefix = "#{@name} ➔ #{options.method}"

 		details = @_getCallerDetails()


Fix display of annotations when switching files in ace editor

The annotations (error markers) were not preserved when switching
between files.  The setAnnotations method was being called before the
document is ready -- when the document is attached to ace the content
is deleted and reinserted, which caused the annotations to be removed.
To fix the problem we set the annotations after attaching.

--- services/web/public/coffee/ide/editor/directives/aceEditor.coffee
+++ services/web/public/coffee/ide/editor/directives/aceEditor.coffee
@@ -170,8 +170,6 @@
 					session = editor.getSession()
 					session.setUseWrapMode(true)
 					session.setMode("ace/mode/latex")
-					session.setAnnotations scope.annotations
-

 				updateCount = 0
 				onChange = () ->
@@ -193,6 +191,9 @@
 						undoManager.nextUpdateIsRemote = true

 					sharejs_doc.attachToAce(editor)
+					# need to set annotations after attaching because attaching
+					# deletes and then inserts document content
+					session.setAnnotations scope.annotations

 					editor.focus()


Load all views with editor

Load all views with editor

--- scripts/views/workspace/content/layouts/editor.coffee
+++ scripts/views/workspace/content/layouts/editor.coffee
@@ -20,4 +20,4 @@
     onRender: () ->
       @metadata.show(new MetadataView({model: @model}))
       @roles.show(new RolesView({model: @model}))
-      #@edit.show(new ContentEditView())
+      @edit.show(new ContentEditView({model: @model}))


Refactored to use a DDP connection instead of executeAsync in browser

--- tests/cucumber/features/support/hooks.coffee
+++ tests/cucumber/features/support/hooks.coffee
@@ -12,9 +12,9 @@

     @.Before =>
         next = arguments[arguments.length - 1]
-        @.world.browser.url @.world.cucumber.mirror.rootUrl
-            .executeAsync (done) -> Meteor.call("/fixtures/reloadpatients", done)
-            .call next
+        connection = DDP.connect @.world.cucumber.mirror.host
+        connection.call "/fixtures/reloadpatients", (err, res) ->
+            if err then next.fail(err) else next()

     @.After =>
         next = arguments[arguments.length - 1]


Pass scope as trailing option to config.get

--- lib/autocomplete-view.coffee
+++ lib/autocomplete-view.coffee
@@ -56,8 +56,8 @@
     false

   getCompletionsForCursorScope: ->
-    cursorScope = @editor.scopeDescriptorForBufferPosition(@editor.getCursorBufferPosition())
-    completions = atom.config.get(cursorScope, 'editor.completions')
+    scope = @editor.scopeDescriptorForBufferPosition(@editor.getCursorBufferPosition())
+    completions = atom.config.get('editor.completions', {scope})
     _.uniq(_.flatten(completions))

   buildWordList: ->


Fix tests

Fix tests

--- spec/NamedFunction.coffee
+++ spec/NamedFunction.coffee
@@ -1,73 +1,25 @@

-{ createImpl } = require "../src/NamedFunction"
+{createImpl} = require ".."

 describe "NamedFunction", ->

+  it "creates a new Function when in __DEV__ mode", ->
+    NamedFunction = createImpl yes
+    name = "Foo"
+    fn = do -> -> 1
+    result = NamedFunction name, fn
+    expect(fn.name).toBe ""
+    expect(result).not.toBe fn
+    expect(result()).toBe 1
+    expect(result.name).toBe name
+    expect(result.getName()).toBe name
+
   it "does not override 'name' when not in __DEV__ mode", ->
-
-    NamedFunction = createImpl yes, no
-
+    NamedFunction = createImpl no
     name = "Foo"
-
-    fn = -> 1
-
+    fn = do -> -> 1
     result = NamedFunction name, fn
-
-    expect result
-      .toBe fn
-
-    expect result()
-      .toBe 1
-
-    expect result.name
-      .toBe ""
-
-    expect result.getName()
-      .toBe name
-
-  it "overrides 'name' when it is configurable", ->
-
-    NamedFunction = createImpl yes, yes
-
-    name = "Foo"
-
-    fn = -> 1
-
-    result = NamedFunction name, fn
-
-    expect result
-      .toBe fn
-
-    expect result()
-      .toBe 1
-
-    expect result.name
-      .toBe name
-
-    expect result.getName()
-      .toBe name
-
-  it "creates a new Function when 'name' is not configurable", ->
-
-    NamedFunction = createImpl no, yes
-
-    name = "Foo"
-
-    fn = -> 1
-
-    result = NamedFunction name, fn
-
-    expect result
-      .not.toBe fn
-
-    expect result()
-      .toBe 1
-
-    expect result.name
-      .toBe name
-
-    expect result.getName()
-      .toBe name
-
-    expect fn.name
-      .toBe ""
+    expect(result).toBe fn
+    expect(result()).toBe 1
+    expect(result.name).toBe ""
+    expect(result.getName()).toBe name


Sending Zero rather than false prevents NaN error.

Sending Zero rather than false prevents NaN error in client side rendering of page counts.

--- lib/datatables.client.coffee
+++ lib/datatables.client.coffee
@@ -470,11 +470,11 @@

 # ##### getTotalCount()
 Template.dataTable.getTotalCount = ->
-  return @getCountCollection().findOne( "#{ @getSubscription() }" ).count or false
+  return @getCountCollection().findOne( "#{ @getSubscription() }" ).count or 0

 # ##### getFilteredCount()
 Template.dataTable.getFilteredCount = ->
-  return @getCountCollection().findOne( "#{ @getSubscription() }_filtered" ).count or false
+  return @getCountCollection().findOne( "#{ @getSubscription() }_filtered" ).count or 0

 # ### Cursor
 #   The reactive cursor responsible for keeping the client in sync
@@ -658,4 +658,4 @@
         ]
       }]

-  return options+  return options


hub/test: fix some timestamp jitter -- https://travis-ci.org/sagemathinc/cocalc/jobs/467108704

--- src/smc-hub/test/postgres/postgres-user-queries-syncstring.coffee
+++ src/smc-hub/test/postgres/postgres-user-queries-syncstring.coffee
@@ -626,7 +626,14 @@
                         query : {syncstrings:{project_id:projects[0], path:path0, last_active:time1}}
                         cb    : cb
                 (x, cb) ->
-                    expect(x).toEqual({action:'delete', old_val:{last_active:time3, project_id:projects[0], string_id:string_id0}})
+                    expect(x.action).toEqual('delete')
+                    expect(x.old_val.project_id).toEqual(projects[0])
+                    expect(x.old_val.string_id).toEqual(string_id0)
+                    # the last_active time might jitter a bit. checking if inside a bracket...
+                    time3.setSeconds(time3.getSeconds() - 1)
+                    expect(x.old_val.last_active.getTime()).toBeGreaterThan(time3.getTime())
+                    time3.setSeconds(time3.getSeconds() + 2)
+                    expect(x.old_val.last_active.getTime()).toBeLessThan(time3.getTime())

                     db.user_query_cancel_changefeed(id:changefeed_id, cb:cb)
                 (x, cb) ->


FIX: remove comments, checnge hash to name

--- app/assets/javascripts/media_lib/inserter_factory.js.coffee
+++ app/assets/javascripts/media_lib/inserter_factory.js.coffee
@@ -71,7 +71,7 @@

     $uploaderVideo = $uploader.find('.js-uploader-video')
     if $uploaderVideo.length
-      $player = $("")
+      $player = $("")
       $uploaderVideo.html $player

 # Стратегия вставки видео-модели
@@ -218,14 +218,10 @@
           src: src
           description: @model.description
       when 'video'
-        template = _.template '<iframe src="<%= host %>/embed/<%= hash %>">' # '<video controls poster="<%= poster %>" src="<%= src %>" alt="<%= description %>"></video>'
-        # content = template
-        #   poster: @model.player_image
-        #   src: @model.static_url
-        #   description: @model.description
+        template = _.template '<iframe src="<%= host %>/embed/<%= hash %>">'
         content = template
           host: @host
-          hash: @model.hash
+          hash: @model.name
       when 'audio'
         template = _.template '
           <audio class="audioPlayer" controls>
@@ -251,14 +247,10 @@
           src: src
           description: @model.description
       when 'video'
-        template = _.template '<iframe src="<%= host %>/embed/<%= hash %>">' # '<div class="jw-video js-jw-video" data-image="<%= image %>" data-file="<%= file %>" data-description="<%= description %>">Video loading...
'
-        # content = template
-        #   image: @model.player_image
-        #   file: @model.static_url
-        #   description: @model.description
+        template = _.template '<iframe src="<%= host %>/embed/<%= hash %>">'
         content = template
           host: @host
-          hash: @model.hash
+          hash: @model.name
       when 'audio'
         template = _.template '<div class="jw-audio js-jw-audio" data-file="<%= file %>" data-description="<%= description %>">Audio loading...
'
         content = template


drag'n'drop events in opera

drag'n'drop events in opera

--- app/assets/javascripts/uploadcare/widget/dragdrop.js.coffee
+++ app/assets/javascripts/uploadcare/widget/dragdrop.js.coffee
@@ -29,13 +29,25 @@
             upload('url', uris) if uris
           false

+    onDelay = 0
+    offDelay = if $.browser.opera then 200 else 0
+
     # Trigger an event on watched elements when dragging
     active = false
-    $(window).on 'mouseenter dragend', => dragState off
-    $('body').on 'dragenter', (e) => dragState on
+    $(window).on 'mouseenter dragend', => delayedDragState off, offDelay
+    $('body').on 'dragenter', (e) => delayedDragState on, onDelay
     $('body').on 'dragleave', (e) =>
       return unless e.target == e.currentTarget
-      dragState off
+      delayedDragState off, offDelay
+
+    delayedDragState = (newActive, delay) ->
+      if delayedDragState.timeout?
+        clearTimeout delayedDragState.timeout
+        delayedDragState.timeout = null
+      if delay > 0
+        delayedDragState.timeout = setTimeout (-> dragState newActive), delay
+      else
+        dragState newActive

     dragState = (newActive) ->
       if active != newActive


fixed bug when query string passed

fixed bug when query string passed

--- lib/asset.coffee
+++ lib/asset.coffee
@@ -41,7 +41,7 @@
     responds_to:  ['coffee', 'styl']

     valid: (url) ->
-      return true if url == '/'
+      return true if @_root(url)
       file_extension = url.split('.').reverse()[0]
       @responds_to.include(file_extension)

@@ -58,12 +58,16 @@
       extension = url.split('.').reverse()[0]
       path = url.split('/')
       dir = path[1]; file = path[2]
-      if url == '/'
+      if @_root(url)
         {name: 'app.jade', extension: 'jade'}
       else if extension == 'coffee' and self.client_dirs.include(dir)
         {name: "#{dir}/#{file}", extension: extension}
       else
         {name: file, extension: extension}
+
+    # Determins if we're looking for the root of the site, ignoring any hashes or anything in the query string
+    _root: (url) ->
+       url.split('?')[0].split('/').length == 2

   # Asset Compiler - Transforms lovely languages into ancient text


tweak error handling

tweak error handling

--- lib/index.coffee
+++ lib/index.coffee
@@ -31,6 +31,9 @@
   # check for `plugins`, if there are some, then 'require' them and load them
   # in for use.
   chain = buildPluginChain builderOptions
+
+  if chain.error?
+    return error:'Failed to build plugin chain: '+chain.error, reason:chain

   # build socket builder function
   builder = (options = {}, isServer) ->
@@ -74,7 +77,8 @@
           else 'connect'

     # if the chain failed then return an error back along with info
-    if result.error? then return error:'Failed to configure socket', reason:result
+    if result.error?
+      return error:'Failed to configure socket with plugin chain', reason:result

     # 4. add their listeners, if they exist
     if options.onSecureConnect?


不要なタスクを削除

不要なタスクを削除

--- lib/index.coffee
+++ lib/index.coffee
@@ -108,12 +108,6 @@
         ["deploy-to-aws-lambda"],
         callback)

-    # TODO: 削除予定。
-    gulp.task "js", ->
-      gulp.src("./src/*.coffee")
-        .pipe(coffee()).on("error", gutil.log)
-        .pipe(gulp.dest("./dist"))
-
     # TODO: サブディレクトリも含める。ディレクトリ名を変更する。
     gulp.task "test", ->
       gulp.src("./dist/*_spec.js")


ref mincer

ref mincer

--- lib/index.coffee
+++ lib/index.coffee
@@ -1 +1,3 @@
+Mincer = require 'mincer'
+
 module.exports = {};


only create one observer redis client; fix selected_db prefixing

--- lib/index.coffee
+++ lib/index.coffee
@@ -10,12 +10,14 @@
   streams = {}
   nextStreamId = 0

+  redisObserver = redisLib.createClient redis.port, redis.host, redis.options
+  redisObserver.auth redis.auth_pass if redis.auth_pass
+  redisObserver.setMaxListeners 0
+
   # Redis has different databases, which are namespaced separately. We need to
   # make sure our pubsub messages are constrained to the database where we
   # published the op.
-  redisDb = redis.selected_db || 0
-
-  prefixChannel = (channel) -> "#{redisDb} #{channel}"
+  prefixChannel = (channel) -> "#{redis.selected_db || 0} #{channel}"
   getOpLogKey = (cName, docName) -> "#{cName}.#{docName} ops"
   getDocOpChannel = (cName, docName) -> "#{cName}.#{docName}"

@@ -188,7 +190,8 @@
             trySubmit()

-    _subscribeChannels: (channels, callback) -> # Subscribe to a redis pubsub channel and get a nodejs stream out
+    # Subscribe to a redis pubsub channel and get a nodejs stream out
+    _subscribeChannels: (channels, callback) ->
       # TODO: 2 refactors:
       #        - Make the redis observer we use here reusable
       #        - Reuse listens on channels
@@ -200,9 +203,6 @@
       # that is any better than the buffer implementation in nodejs streams themselves.
       stream._read = ->

-      redisObserver = redisLib.createClient redis.port, redis.host, redis.options
-      redisObserver.auth redis.auth_pass if redis.auth_pass
-
       open = true

       stream._id = nextStreamId++
@@ -214,32 +214,41 @@
         stream.push null
         open = false
         delete streams[stream._id]
-        redisObserver.unsubscribe()
-        redisObserver.quit()
+        redisObserver.unsubscribe channels
+        # redisObserver.quit()
+        redisObserver.removeListener 'message', onMessage

         stream.emit 'close'
         stream.emit 'end'

-      redisObserver.on 'message', (_channel, msg) ->
-        # We shouldn't get messages after we tell it to unsubscribe, but its happened.
-        return unless open
-
-        data = JSON.parse msg
-        # we need to attach the channel name to the data, except its prefixed. So unprefix it.
-        data.channel = _channel[_channel.indexOf(' ') + 1..] if Array.isArray channels
-        stream.push data
-
       if Array.isArray channels
         channels[i] = prefixChannel c for c, i in channels
       else
         channels = prefixChannel channels

+      if Array.isArray channels
+        onMessage = (msgChannel, msg) ->
+          # We shouldn't get messages after unsubscribe, but it's happened.
+          return if !open || channels.indexOf(msgChannel) == -1
+
+          data = JSON.parse msg
+          # Unprefix database name from the channel
+          data.channel = msgChannel.slice msgChannel.indexOf(' ') + 1
+          stream.push data
+      else
+        onMessage = (msgChannel, msg) ->
+          # We shouldn't get messages after unsubscribe, but it's happened.
+          return if !open || msgChannel isnt channels
+          data = JSON.parse msg
+          stream.push data
+
+      redisObserver.on 'message', onMessage
+
       redisObserver.subscribe channels, (err) ->
         if err
-          stream.destroy() if open
-          callback err, null
-        else
-          callback null, stream
+          stream.destroy()
+          return callback err
+        callback null, stream

     # Callback called with (err, op stream). v must be in the past or present. Behaviour
     # with a future v is undefined (because I don't think thats an interesting case).


Clean disconnect

Clean disconnect

--- lib/pusht.coffee
+++ lib/pusht.coffee
@@ -44,8 +44,8 @@
     @subscriptions.dispose()

   disconnect: ->
-    # @pairingChannel.trigger 'client-disconnected', {colour: @markerColour}
-    @pusher.disconnect()
+    @pairingChannel.trigger 'client-disconnected', {colour: @markerColour}
+    setTimeout((=> @pusher.disconnect()),500)
     _.each @friendColours, (colour) => @clearMarkers(colour)
     atom.views.getView(@editor).removeAttribute('id')
     @hidePanel()
@@ -207,14 +207,12 @@
       buffer.append(chunk)
       @triggerPush = true

-    @pairingChannel.bind 'client-change', (events) =>
-      console.log(events);
-      _.each(events, (event) => @changeBuffer(event) )
-
-    # @pairingChannel.bind 'client-disconnected', (data) =>
-    #   @clearMarkers(data.colour)
-    #   disconnectView = new AlertView "Your pair buddy has left the session."
-    #   atom.workspace.addModalPanel(item: disconnectView, visible: true)
+    @pairingChannel.bind 'client-change', (events) => _.each(events, (event) => @changeBuffer(event) )
+
+    @pairingChannel.bind 'client-disconnected', (data) =>
+      @clearMarkers(data.colour)
+      disconnectView = new AlertView "Your pair buddy has left the session."
+      atom.workspace.addModalPanel(item: disconnectView, visible: true)

     @triggerEventQueue()
     @listenToBufferChanges()
@@ -224,7 +222,6 @@
     @buffer.onDidChange (event) =>
       return unless @triggerPush
       deletion = !(event.newText is "\n") and (event.newText.length is 0)
-      console.log(deletion)
       @events.push({deletion: deletion, event: event, colour: @markerColour})

   triggerEventQueue: ->
@@ -238,7 +235,6 @@
     newRange = Range.fromObject(data.event.newRange)
     oldRange = Range.fromObject(data.event.oldRange)
     newText = data.event.newText
-    console.log(data.deletion)
     @triggerPush = false

     @clearMarkers(data.colour)


nested routes inherit default params from parents

--- lib/route.coffee
+++ lib/route.coffee
@@ -47,6 +47,8 @@

       # create a new route instance
       nested_route = new Route router, prefix+path, method, @optional
+      # add local default params
+      nested_route.default_params = @default_params
       # apply local conditions
       nested_route.where @conditions if @conditions
       # return the new awesomeness
@@ -58,6 +60,7 @@

     # base properties
     @params = {}
+    @default_params = {}
     @parts = []
     @route_name = null
     @path = path
@@ -131,7 +134,7 @@

-  # route.to( endpoint [, extra_params ] )
+  # route.to( endpoint [, default_params ] )
   # --------------------------------------
   # defines the endpoint & mixes in optional params
   #
@@ -141,10 +144,12 @@
   #
   # returns the route for chaining
   #
-  to: ( endpoint, extra_params )->
-
-    if !extra_params && typeof endpoint != 'string'
-      [ extra_params, endpoint ] = [ endpoint, undefined ]
+  to: ( endpoint, default_params )->
+
+    if !default_params && typeof endpoint != 'string'
+      [ default_params, endpoint ] = [ endpoint, undefined ]
+
+    mixin @default_params, default_params

     # TODO: make endpoint optional, since you can have the
     # controller & action in the URL utself,
@@ -155,7 +160,7 @@
         throw new Error 'syntax should be in the form: controller.action'
       [ @params.controller, @params.action ] = endpoint.split '.'

-    mixin @params, extra_params || {}
+    mixin @params, @default_params

     this # chainable


DRY up prefixes-spec.coffee

DRY up prefixes-spec.coffee

--- spec/prefixes-spec.coffee
+++ spec/prefixes-spec.coffee
@@ -17,30 +17,33 @@
     options.element ?= editor[0]
     helpers.keydown(key, options)

-  describe "the Repeat prefix", ->
-    it "repeats the following operation N times", ->
-      editor.setText('12345')
-      editor.setCursorScreenPosition([0, 1])
+  describe "Repeat", ->
+    describe "with operations", ->
+      beforeEach ->
+        editor.setText("123456789abc")
+        editor.setCursorScreenPosition([0, 0])

-      keydown('3')
-      keydown('x')
+      it "repeats N times", ->
+        keydown('3')
+        keydown('x')

-      expect(editor.getText()).toBe '15'
+        expect(editor.getText()).toBe '456789abc'

-      editor.setText('123456789abc')
-      editor.setCursorScreenPosition([0, 0])
-      keydown('1')
-      keydown('0')
-      keydown('x')
+      it "repeats NN times", ->
+        keydown('1')
+        keydown('0')
+        keydown('x')

-      expect(editor.getText()).toBe 'bc'
+        expect(editor.getText()).toBe 'bc'

-    it "repeats the following motion N times", ->
-      editor.setText('one two three')
-      editor.setCursorScreenPosition([0, 0])
+    describe "with motions", ->
+      beforeEach ->
+        editor.setText('one two three')
+        editor.setCursorScreenPosition([0, 0])

-      keydown('d')
-      keydown('2')
-      keydown('w')
+      it "repeats N times", ->
+        keydown('d')
+        keydown('2')
+        keydown('w')

-      expect(editor.getText()).toBe 'three'
+        expect(editor.getText()).toBe 'three'


MachineStateModal: Remove defer.

MachineStateModal: Remove defer.

--- client/app/lib/providers/environmentsmachinestatemodal.coffee
+++ client/app/lib/providers/environmentsmachinestatemodal.coffee
@@ -616,10 +616,7 @@
       @machine = m
       @setData m

-      # TODO: temp fix for snapshot resurrection, ide should wait for IdeIsReallyReady event -SY
-      kd.utils.defer =>
-        kd.warn 'needs IdeIsReallyReady event!, @acet'
-        @emit 'IDEBecameReady', m
+      @emit 'IDEBecameReady', m

   verifyAccount: ->


client: remove debugger

client: remove debugger

--- client/app/lib/providers/environmentsmachinestatemodal.coffee
+++ client/app/lib/providers/environmentsmachinestatemodal.coffee
@@ -52,8 +52,6 @@

       kd.warn err  if err?

-      debugger
-
       if not verified
         @buildVerifyView()
       else if not @machine.isApproved()


Make Entity#findByUid fail when instance if not found

--- lib/entity.coffee
+++ lib/entity.coffee
@@ -96,7 +96,13 @@
     deferred = Q.defer()
     deferred.promise.nodeify callback
     if uid?
-      @$domainRepository.findEntityByUid @, uid, deferred.makeNodeResolver()
+      @$domainRepository.findEntityByUid @, uid, (err, entity) ->
+        if err?
+          deferred.reject err
+        else if not entity
+          deferred.reject new Error "Could not find entity with UID " + uid
+        else
+          deferred.resolve entity
     else
       deferred.reject(new Error 'Please provide a UID to find')
     deferred.promise


Restore previous config-manager.coffee

Restore previous config-manager.coffee

--- lib/config-manager.coffee
+++ lib/config-manager.coffee
@@ -9,31 +9,18 @@
     fileStoragePath: path.join(__dirname, '..', 'kernel-configs')

     writeConfigFile: (onCompleted) ->
-        customKernelConnectionPath = path.join atom.project.rootDirectories[0].path, 'hydrogen', 'connection.json'
-        fs.access customKernelConnectionPath, (err) =>
-            if err?
-                try
-                    console.log "fileStoragePath: ", @fileStoragePath
-                    fs.mkdirSync(@fileStoragePath)
-                catch e
-                    if e.code != 'EEXIST'
-                        throw e
-                filename = 'kernel-' + uuid.v4() + '.json'
-                portfinder.findMany 5, (ports) =>
-                    config = @buildConfiguration ports
-                    configString = JSON.stringify config
-                    filepath = path.join(@fileStoragePath, filename)
-                    fs.writeFileSync filepath, configString
-                    console.log "Creating kernel connection: ", filepath
-                    onCompleted filepath, config
-            else
-                fs.readFile customKernelConnectionPath, 'utf8', (err, data) ->
-                    unless err?
-                        config = JSON.parse data
-                        console.log "Using custom kernel connection: ", customKernelConnectionPath
-                        atom.notifications.addInfo 'Custom kernel connection:',
-                            detail: "Make sure to manually start the custom kernel that serves the connection in #{customKernelConnectionPath}", dismissable: true
-                        onCompleted customKernelConnectionPath, config, true
+        try
+            fs.mkdirSync(@fileStoragePath)
+        catch e
+            if e.code != 'EEXIST'
+                throw e
+        filename = 'kernel-' + uuid.v4() + '.json'
+        portfinder.findMany 5, (ports) =>
+            config = @buildConfiguration ports
+            configString = JSON.stringify config
+            filepath = path.join(@fileStoragePath, filename)
+            fs.writeFileSync filepath, configString
+            onCompleted filepath, config

     buildConfiguration: (ports) ->
         config =


small fix comment

small fix comment

--- lib/client/javascripts/nali/model.js.coffee
+++ lib/client/javascripts/nali/model.js.coffee
@@ -253,7 +253,7 @@
     params

   setRelations: ->
-    # запускает установку у модели
+    # запускает установку связей у модели
     @setRelationsType type for type in [ 'belongsTo', 'hasOne', 'hasMany', 'hasOneThrough', 'hasManyThrough' ]
     @


inherit stdin for inquirer support

inherit stdin for inquirer support

--- lib/utils.coffee
+++ lib/utils.coffee
@@ -120,7 +120,7 @@
       verify: false
       detached: false
       xvfb: os.platform() is "linux"
-      stdio: ["ignore", process.stdout, "ignore"]
+      stdio: [process.stdin, process.stdout, "ignore"]

     spawn = =>
       @verifyCypress().then (pathToCypress) =>


Small whitespace fix

Small whitespace fix

--- lib/utils.coffee
+++ lib/utils.coffee
@@ -6,7 +6,7 @@
     manifestRequest.send(null)
     JSON.parse(manifestRequest.responseText).version

-  map: (l, fn)    -> fn x for x in l
+  map: (l, fn) -> fn x for x in l

   # Takes a dot-notation object string and call the function
   # that it points to with the correct value for 'this'.


#382 fix incorrect naming

#382 fix incorrect naming

--- lib/motion.coffee
+++ lib/motion.coffee
@@ -669,7 +669,7 @@
     duration = settings.get('smoothScrollOnScrollMotionDuration')
     jQuery(topPixelFrom).animate(topPixelTo, {step, done, duration})

-  flashRow: (screenRow) ->
+  highlightScreenRow: (screenRow) ->
     screenRange = new Range([screenRow, 0], [screenRow, Infinity])
     marker = @editor.markScreenRange(screenRange)
     @editor.decorateMarker(marker, type: 'highlight', class: 'vim-mode-plus-flash')
@@ -690,7 +690,7 @@
       newTopRow = fromRow + @getAmountOfRows()

       if settings.get('smoothScrollOnScrollMotion')
-        marker = @flashRow(cursor.getScreenRow())
+        marker = @highlightScreenRow(cursor.getScreenRow())
         destroyMaker = -> marker.destroy()
         @withScroll fromRow, newTopRow, =>
           setTimeout(destroyMaker, 100)


Fix #82

Fix #82

--- lib/motion.coffee
+++ lib/motion.coffee
@@ -101,16 +101,6 @@
     if getVimLastScreenRow(@editor) isnt cursor.getScreenRow()
       cursor.moveDown()

-  moveCursorRight: (cursor, allowWrap=false) ->
-    {row, column} = cursor.getScreenPosition()
-    column++
-    column++ if (allowWrap and pointIsAtEndOfLine(@editor, [row, column]))
-    point = Point.min([row, column], getVimEofScreenPosition(@editor))
-    cursor.setScreenPosition point,
-      clip: 'forward',
-      wrapBeyondNewlines: allowWrap
-      wrapAtSoftNewlines: true
-
   # Utils
   # -------------------------
   # Calling stop() in callback stop remaining processing.
@@ -183,7 +173,10 @@
   moveCursor: (cursor) ->
     @countTimes =>
       unfoldAtCursorRow(cursor)
-      @moveCursorRight(cursor, @canWrapToNextLine(cursor))
+      allowWrap = @canWrapToNextLine(cursor)
+      moveCursorRight(cursor)
+      if cursor.isAtEndOfLine() and allowWrap and not @cursorIsAtVimEndOfFile(cursor)
+        moveCursorRight(cursor, {allowWrap})

 class MoveUp extends Motion
   @extend()


Get closer to the algorithm outlined in #465

--- src/lib/Component.coffee
+++ src/lib/Component.coffee
@@ -140,49 +140,46 @@
       # If port is non-triggering, we can skip the process function call
       return

-    if ip.type is 'openBracket'
-      # Prepare bracket context for packet scope as needed
-      @bracketContext[ip.scope] = [] unless @bracketContext[ip.scope]
-
-      # Initialize the context for this bracket
-      context =
-        ip: ip
-        ports: []
-        source: port.name
-      @bracketContext[ip.scope].push context
-
-      if @bracketContext[ip.scope].length is 1 and @autoOrdering is null
-        # Switch component to ordered mode when receiving a stream unless
-        # auto-ordering is disabled
-        debug "#{@nodeId} port #{port.name} entered auto-ordering mode"
-        @autoOrdering = true
-
-    if ip.type is 'closeBracket' and @isOrdered()
-      # Clear this scope
-      context = @bracketContext[ip.scope].pop()
-      if context.pending
-        debug "#{@nodeId} bracket context #{context.ip.data} is pending, skipping close"
-        @bracketContext[ip.scope].push context
-        contexts = @bracketContext[ip.scope].slice 0
-        contexts.reverse()
-        unclosed = contexts.filter (ctx) -> not ctx.closeIp
-        unclosed[0].closeIp = ip
+    if ip.type is 'openBracket' and @autoOrdering is null
+      # Switch component to ordered mode when receiving a stream unless
+      # auto-ordering is disabled
+      debug "#{@nodeId} port #{port.name} entered auto-ordering mode"
+      @autoOrdering = true
+
+    # Initialize the result object for situations where output needs
+    # to be queued to be kept in order
+    result = {}
+
+    if @isForwardingInport port
+      # For bracket-forwarding inports we need to initialize a bracket context
+      # so that brackets can be sent as part of the output, and closed after.
+      if ip.type is 'openBracket'
+        # For forwarding ports openBrackets don't fire
         return
-      context.closeIp = ip
-      result =
-        __resolved: true
-        __closeBracketContext: context
-      @outputQ.push result
-      do @processOutputQueue
-
-    if @isForwardingInport(port) and ip.type in ['openBracket', 'closeBracket']
-      # Bracket forwarding enabled on port. In this case we don't call processing
-      # functions on brackets, only on data.
-      return
-
-    bracketContext = if @bracketContext[ip.scope] then @bracketContext[ip.scope].slice(0) else []
-    result =
-      __bracketContext: bracketContext
+
+      if ip.type is 'closeBracket'
+        # For forwarding ports closeBrackets don't fire
+        # However, we need to handle several different scenarios:
+        # A. There are closeBrackets in queue before current packet
+        # B. There are closeBrackets in queue after current packet
+        # C. We're no longer processing anything and closeBracket arrives
+        if @load is 0 and @outputQ.length is 0
+          buf = port.getBuffer ip.scope
+          return unless buf[0] is ip
+          # Remove from buffer
+          port.get ip.scope
+          context = @bracketContext[ip.scope].pop()
+          context.closeIp = ip
+          return unless context.ports.length
+          debug "#{@nodeId} closeBracket-C #{ip.data} #{context.ports}"
+          result =
+            __resolved: true
+          for outport in context.ports
+            result[outport] = [ip.clone()]
+          @outputQ.push result
+          do @processOutputQueue
+        return
+
     # Prepare the input/output pair
     input = new ProcessInput @inPorts, ip, @, port, result
     output = new ProcessOutput @outPorts, ip, @, result
@@ -191,7 +188,6 @@

     unless input.activated
       debug "#{@nodeId} #{ip.type} packet on #{port.name} didn't match preconditions"
-      bracketContext[bracketContext.length - 1].pending = true if @isForwardingInport port
       return

     # Component fired
@@ -202,40 +198,54 @@
     return

   addBracketForwards: (result) ->
-    # First see if there are any brackets to forward
-    return unless result.__bracketContext
-    for outport, ips of result
-      continue if outport.indexOf('__') is 0
-      unforwarded = result.__bracketContext.filter (ctx) =>
-        return false unless @isForwardingOutport ctx.source, outport
-        ctx.ports.indexOf(outport) is -1
-      continue unless unforwarded.length
-      unforwarded.reverse()
-      for ctx in unforwarded
-        ips.unshift ctx.ip.clone()
-        debug "#{@nodeId} forwarding #{ctx.ip.type} to #{outport}"
-        if ctx.closeIp
-          @outputQ.push
-            __resolved: true
-            __closeBracketContext: ctx
+    if result.__bracketClosingBefore?.length
+      for context in result.__bracketClosingBefore
+        debug "#{@nodeId} closeBracket-A #{context.closeIp.data} #{context.ports}"
+        continue unless context.ports.length
+        for port in context.ports
+          result[port] = [] unless result[port]
+          result[port].unshift context.closeIp.clone()
+
+    if result.__bracketContext?.length
+      # First see if there are any brackets to forward
+      for outport, ips of result
+        continue if outport.indexOf('__') is 0
+        unforwarded = result.__bracketContext.filter (ctx) =>
+          return false unless @isForwardingOutport ctx.source, outport
+          ctx.ports.indexOf(outport) is -1
+        continue unless unforwarded.length
+        unforwarded.reverse()
+        for ctx in unforwarded
+          ips.unshift ctx.ip.clone()
+          ctx.ports.push outport
+
+    if result.__bracketClosingAfter?.length
+      for context in result.__bracketClosingAfter
+        debug "#{@nodeId} closeBracket-B #{context.closeIp.data} #{context.ports}"
+        continue unless context.ports.length
+        for port in context.ports
+          result[port] = [] unless result[port]
+          result[port].push context.closeIp.clone()
+
+    delete result.__bracketClosingBefore
+    delete result.__bracketContext
+    delete result.__bracketClosingAfter

   processOutputQueue: ->
     while @outputQ.length > 0
       result = @outputQ[0]
       break unless result.__resolved
-
-      if result.__closeBracketContext
-        # Handle closing bracket forwarding
-        for outport in result.__closeBracketContext.ports
-          debug "#{@nodeId} forwarding closeBracket to #{outport}"
-          result[outport] = [result.__closeBracketContext.closeIp.clone()]
-        delete result.__closeBracketContext
-
+      @addBracketForwards result
       for port, ips of result
         continue if port.indexOf('__') is 0
         continue unless @outPorts.ports[port].isAttached()
-        @addBracketForwards result
         for ip in ips
+          if ip.type is 'openBracket'
+            debug "#{@nodeId} sending < #{ip.data} to #{port}"
+          else if ip.type is 'closeBracket'
+            debug "#{@nodeId} sending > #{ip.data} to #{port}"
+          else
+            debug "#{@nodeId} sending DATA to #{port}"
           @outPorts[port].sendIP ip
       @outputQ.shift()

@@ -255,8 +265,8 @@
     @activated = true

     if @nodeInstance.isOrdered()
-      # We're handling packets in order. Set the result as non-resolved so that it can
-      # be send when the order comes up
+      # We're handling packets in order. Set the result as non-resolved
+      # so that it can be send when the order comes up
       @result.__resolved = false

   # ## Input preconditions
@@ -308,10 +318,58 @@
     args = ['in'] unless args.length
     res = []
     for port in args
+      if @nodeInstance.isForwardingInport port
+        ip = @__getForForwarding port
+        res.push ip
+        continue
       ip = @ports[port].get @scope
       res.push ip

     if args.length is 1 then res[0] else res
+
+  __getForForwarding: (port) ->
+    # Ensure we have a bracket context for the current scope
+    @nodeInstance.bracketContext[@scope] = [] unless @nodeInstance.bracketContext[@scope]
+    prefix = []
+    dataIp = null
+    # Read IPs until we hit data
+    loop
+      # Read next packet
+      ip = @ports[port].get @scope
+      # Stop at the end of the buffer
+      break unless ip
+      if ip.type is 'data'
+        # Hit the data IP, stop here
+        dataIp = ip
+        break
+      # Keep track of bracket closings and openings before
+      prefix.push ip
+
+    # Forwarding brackets that came before data packet need to manipulate context
+    # and be added to result so they can be forwarded correctly to ports that
+    # need them
+    for ip in prefix
+      if ip.type is 'closeBracket'
+        # Bracket closings before data should remove bracket context
+        @result.__bracketClosingBefore = [] unless @result.__bracketClosingBefore
+        context = @nodeInstance.bracketContext[@scope].pop()
+        context.closeIp = ip
+        @result.__bracketClosingBefore.push context
+        continue
+      if ip.type is 'openBracket'
+        # Bracket openings need to go to bracket context
+        @nodeInstance.bracketContext[@scope].push
+          ip: ip
+          ports: []
+          source: port
+        continue
+
+    # Add current bracket context to the result so that when we send
+    # to ports we can also add the surrounding brackets
+    @result.__bracketContext = @nodeInstance.bracketContext[@scope].slice 0
+    # Bracket closings that were in buffer after the data packet need to
+    # be added to result for done() to read them from
+    return dataIp

   # Fetches `data` property of IP object(s) for given port(s)
   getData: (args...) ->
@@ -432,11 +490,9 @@
     ip.scope = @scope if @scope isnt null and ip.scope is null

     if @nodeInstance.isOrdered()
-      debug "#{@nodeInstance.nodeId} is in ordered mode, deferred send of #{ip.type} to #{port}"
       @result[port] = [] unless port of @result
       @result[port].push ip
       return
-    debug "#{@nodeInstance.nodeId} direct send of #{ip.type} to #{port}"
     @nodeInstance.outPorts[port].sendIP ip

   # Sends packets for each port as a key in the map
@@ -479,6 +535,22 @@
   done: (error) ->
     @error error if error

+    if @nodeInstance.isOrdered() and @nodeInstance.bracketContext[@scope]?.length and @nodeInstance.load is 1
+      # We're doing bracket forwarding. See if there are
+      # dangling closeBrackets in buffer
+      nodeContext = @nodeInstance.bracketContext[@scope]
+      processContext = @result.__bracketContext
+      context = nodeContext[nodeContext.length - 1]
+      buf = @nodeInstance.inPorts[context.source].getBuffer context.ip.scope
+      loop
+        break unless buf.length
+        break unless buf[0].type is 'closeBracket'
+        ip = @nodeInstance.inPorts[context.source].get context.ip.scope
+        ctx = @nodeInstance.bracketContext[ip.scope].pop()
+        ctx.closeIp = ip
+        @result.__bracketClosingAfter = [] unless @result.__bracketClosingAfter
+        @result.__bracketClosingAfter.push ctx
+
     debug "#{@nodeInstance.nodeId} finished processing #{@nodeInstance.load}"
     @nodeInstance.load--


button for deleting logs added, each log has a timestamp now

--- lib/atom-python-test-view.coffee
+++ lib/atom-python-test-view.coffee
@@ -11,12 +11,27 @@
         @div class: 'btn-toolbar', outlet:'toolbar', =>
           @button outlet: 'closeBtn', class: 'btn inline-block-tight right', click: 'destroy', style: 'float: right', =>
             @span class: 'icon icon-x'
+          @button outlet: 'clearBtn', class: 'btn inline-block-tight right', click: 'clear', style: 'float: right', =>
+            @span class: 'icon icon-zap'
         @pre class: 'output', outlet: 'output'

     initialize: ->
       @panel ?= atom.workspace.addBottomPanel(item: this)
       @message = ""
       @panel.hide()
+
+    createTimestamp: ->
+      today = new Date
+      dd = today.getDate()
+      #The value returned by getMonth is an integer between 0 and 11, referring 0 to January, 1 to February, and so on.
+      mm = today.getMonth() + 1
+      yyyy = today.getFullYear()
+      if dd < 10
+        dd = '0' + dd
+      if mm < 10
+        mm = '0' + mm
+      today = mm + '-' + dd + '-' + yyyy
+      return today

     addLine: (line) ->
       if  true == /.*\d+.failed.*(passed)?.in.*seconds.*/i.test(line)
@@ -36,6 +51,10 @@

     clear: ->
       @message = ''
+      virtual_console = @find(".output")[0]
+      while virtual_console.firstChild
+        virtual_console.removeChild(virtual_console.firstChild)
+

     finish: ->
       console.log('finish')
@@ -47,6 +66,6 @@

     toggle: ->
       @find(".output").height(300)
-      @find(".output").innerHTML = ""
-      @addLine 'Running tests... \n \n'
+      @addLine @createTimestamp()
+      @addLine '\nRunning tests... \n \n'
       @panel.show()


model: return promise from connect()

model: return promise from connect()

--- lib/debugger-model.coffee
+++ lib/debugger-model.coffee
@@ -46,6 +46,7 @@
     if @isActive then throw new Error('Already connected.')
     @onPause ?= ->
     @onResume ?= ->
+    @openScript ?= ->

     debug('starting debugger', wsUrl)
     # the current list of breakpoints is from before this
@@ -57,14 +58,11 @@

     @api.once 'connect', =>
       @isActive = true
-      @api.debugger.enable(null, (err, result)->
-        debug('enabled returned', err, result)
-        if(err) then console.error err
-        else debug('debugger enabled')
-      )
-      @api.page.getResourceTree(null, (err, result)->
-        debug('getResourceTree returned!', err, result)
-      )
+
+      debuggerEnabledPromise = q.all [
+        q.ninvoke @api.debugger, 'enable', null
+        q.ninvoke @api.page, 'getResourceTree', null
+      ]

     @api.once 'close', => @close()

@@ -95,6 +93,8 @@
     )

     @api.on 'scriptParsed', (scriptObject)=>@addScript(scriptObject)
+
+    debuggerEnabledPromise

   close: ->


add console.log alias

add console.log alias

--- lib/client/javascripts/nali/extensions.js.coffee
+++ lib/client/javascripts/nali/extensions.js.coffee
@@ -16,3 +16,5 @@
 String::underscore = ->
   str = @replace /([A-Z])/g, ( match ) -> '_' + match.lower()
   if str[ 0...1 ] is '_' then str[ 1.. ] else str
+
+window.__ = ( args... ) -> console.log args...


WebSocket object is release after close.

--- lib/generators/nyoibo/templates/nyoibo.js.coffee
+++ lib/generators/nyoibo/templates/nyoibo.js.coffee
@@ -64,6 +64,7 @@
           ws.send("JSON: " + JSON.stringify(params))
         when 'OK Bye'
           ws.close()
+          ws = null
         when 'EMPTY'
           ws.send("QUIT")
         when 'NEXT'


Add check to see if body tag is there to extract the messages.

--- lib/guard/jasmine/phantomjs/guard-jasmine.coffee
+++ lib/guard/jasmine/phantomjs/guard-jasmine.coffee
@@ -84,7 +84,7 @@
 # Error message for when jasmine never loaded asynchronously
 #
 jasmineMissing = ->
-  text = page.evaluate -> document.getElementsByTagName('body')[0].innerText
+  text = page.evaluate -> document.getElementsByTagName('body')[0]?.innerText

   if text
     error = """
@@ -104,7 +104,7 @@
 # Error message for when specs time out
 #
 specsTimedout = ->
-  text = page.evaluate -> document.getElementsByTagName('body')[0].innerText
+  text = page.evaluate -> document.getElementsByTagName('body')[0]?.innerText
   if text
     error = """
             Timeout waiting for the Jasmine test results!


Update Yocto version in coffeescript

Update Yocto version in coffeescript

Signed-off-by: Will Newton <9ae5e71ad61fb3eb3f972773b758e0d4f6c5e573@resin.io>

--- intel-corei7-64.coffee
+++ intel-corei7-64.coffee
@@ -90,7 +90,7 @@
 		machine: 'intel-corei7-64'
 		image: 'resin-image-flasher'
 		fstype: 'resinos-img'
-		version: 'yocto-morty'
+		version: 'yocto-pyro'
 		deployArtifact: 'resin-image-flasher-intel-corei7-64.resinos-img'
 		compressed: true


Add parent dir of file, if atom opened with only file.

See `https://github.com/atom/tree-view/issues/471`

--- lib/runner.coffee
+++ lib/runner.coffee
@@ -1,4 +1,6 @@
 {Emitter, BufferedProcess} = require 'atom'
+fs = require 'fs'
+path = require 'path'

 module.exports =
 class Runner
@@ -50,7 +52,7 @@
     workingDirectoryProvided = cwd? and cwd isnt ''
     paths = atom.project.getPaths()
     if not workingDirectoryProvided and paths?.length > 0
-      cwd = paths[0]
+      cwd = if fs.statSync(paths[0]).isDirectory() then paths[0] else path.join(paths[0], '..')

     cwd


Removed uncoffee-like function parentheses.

--- lib/server.coffee
+++ lib/server.coffee
@@ -14,5 +14,5 @@
   res.status(200).send response

 server = app.listen PORT, () ->
-  console.log('server started')
+  console.log 'server started'


lib: Log request url with path

lib: Log request url with path

--- lib/server.coffee
+++ lib/server.coffee
@@ -29,10 +29,10 @@

           # Found it in our repo
           check.on 'success', (data) ->
-              console.log "INFO: Found #{req.url} in child npm!"
+              req.url = child_npm.path + req.url
+              console.info "INFO: Found #{req.url} in child npm!"

               req.headers['host'] = child_npm.host
-              req.url = child_npm.path + req.url
               proxyOpts = child_npm
               proxyOpts['buffer'] = buffer

@@ -43,20 +43,20 @@

               req.url = strip_url_for_npm_vhost req.url

+              req.url = parent_npm.path + req.url
               console.info "INFO: proxying to parent npm #{req.url}"

               req.headers['host'] = parent_npm.host
-              req.url = parent_npm.path + req.url
               proxyOpts = parent_npm
               proxyOpts['buffer'] = buffer

               proxy.proxyRequest req, res, proxyOpts
       else
           # Update our repo
+          req.url = child_npm.path + req.url
           console.info "INFO: Updating #{req.url}"

           req.headers['host'] = child_npm.host
-          req.url = child_npm.path + req.url
           proxyOpts = child_npm
           proxyOpts['buffer'] = buffer


release

release

--- webpack/release.coffee
+++ webpack/release.coffee
@@ -15,26 +15,6 @@
 if process.env.CIRCLE_BRANCH.startsWith('@')
   console.log("Not releasing #{process.env.CIRCLE_BRANCH}")
   process.exit(1)
-
-if process.env.CIRCLE_BRANCH.startsWith('nr-')
-
-do Bluebird.coroutine(->
-  console.log('finding releases')
-  release = {
-    static: 'static-files',
-    current: "v#{pkg.version}",
-    builds: 'builds',
-  }
-
-  for id, tag of release
-    try
-      release[id] = yield github("/releases/tags/#{tag}")
-      console.log("#{tag} found")
-
-  xpi = "zotero-better-bibtex-#{version}.xpi"
-
-  if process.env.CIRCLE_TAG
-    if release.current

 do Bluebird.coroutine(->
   console.log('finding releases')


Add timestamp to enter/leave logs.

Add timestamp to enter/leave logs.

--- lib/worker.coffee
+++ lib/worker.coffee
@@ -197,9 +197,14 @@
   # Attempt to run the runner function.
   run: ->
     @fiber = Fiber =>
+      console.log "ENTER (#{@timestamp()}): #{@key}"
       @clear()
       @process()
+      console.log "LEAVE (#{@timestamp()}): #{@key}"
     @fiber.run()
+
+  timestamp: ->
+    new Date().toJSON()

   # Reset information about this run, including:
   #


* add important comment

* add important comment

--- src/queuficator.coffee
+++ src/queuficator.coffee
@@ -24,6 +24,7 @@
 _done_cb_builder = (queue_cache, key) ->
   (data...) ->
     while step = queue_cache[key].shift()
+      # prevent ANY interaction cased by result sharing (it may slow down execution, but safety first!)
       step (_.cloneDeep data)...

 queueSomeRequest = (methodBody) ->


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/cooldown.coffee
+++ src/cooldown.coffee
@@ -32,7 +32,7 @@
     # Turbo Cooldown
     player = GameObject()

-    # turboTimer starts at 1000
+    # turboTimer starts at 1000 and approach
     player.cooldown "turboTimer",
       approachBy: 5
       value: 1000


cooldown finilazation

cooldown finilazation

--- src/cooldown.coffee
+++ src/cooldown.coffee
@@ -1,7 +1,7 @@
 ###*
 The Cooldown module provides a declarative way to manage cooldowns on
 GameObject's properties.
-collisions so is probably best suited to particle effect like things.
+

 player = GameObject


Commit of initial version of ng-paste-from

--- src/ng-paste-from.coffee
+++ src/ng-paste-from.coffee
@@ -1,29 +1,88 @@
 ngPasteFrom = angular.module "ngPasteFrom", []

-ngPasteFrom.directive "ngPasteFrom", ->
-	restrict: "E"
-	replace: true
-	template: '
I like turtles
'
-	scope: {
-		time: "="
-		disabled: "="
-	}
-
-	link: ($scope, element, attrs)->
-		$scope.minutes 	= [0..59]
-		$scope.hours 	= [0..23]
+ngPasteFrom
+	.constant "ngPasteFromErrors",
+		invalidColumnLength: "NGPASTEFROM_INVALID_COLUMN_LENGTH"
+		failedValidation: "NGPASTEFROM_FAILED_VALIDATION"
+	.directive "ngPasteFrom", ->
+		restrict: "A"
+		scope:
+			ngPasteFrom: "="
+			ngPasteFromFormat: "="
+			ngPasteFromOnValidate: "="
+			ngPasteFromOnError: "="
+
+		link: ($scope, element, attrs) ->
+			if not $scope.ngPasteFromFormat?
+				console.error "Missing required attribute ngPasteFromFormat."

-	controller: ($scope, $filter) ->
-		$scope.hour 	= "00"
-		$scope.minute 	= "00"
+			# NOTE: This solution with paste + keyup kind of works, but it is not optimal.
+			# It causes the data to show and not get processed until key up.
+			element.on "paste", (event) ->
+				element.val("")
+				$scope.hasPasted = true
+
+			element.on "keyup", (event) ->
+				if $scope.hasPasted
+					$scope.$apply ->
+						$scope.pasteData = element.val()
+
+					$scope.hasPasted = false

-		if $scope.time?
-			split 			= $scope.time.split(":")
-			$scope.hour 	= split[0]
-			$scope.minute 	= split[1]
+				element.val("")
+
+		controller: ($scope, $filter, ngPasteFromErrors) ->
+			defaultOnError = (error, index) ->
+				console.error "ngPasteFromError: index " + index + " error: " + error

-		$scope.updateScope = ->
-
+			defaultOnValidate = (object, index) ->
+				return true

-		$scope.$watch "hour", $scope.updateScope
-		$scope.$watch "minute", $scope.updateScope+			# Assign default error and validation handler in case they are not set.
+			if not $scope.ngPasteFromOnError? then $scope.ngPasteFromOnError = defaultOnError
+			if not $scope.ngPasteFromOnValidate? then $scope.ngPasteFromOnValidate = defaultOnValidate
+
+			splitToRows = (data) ->
+				lineEndingsRegExp = /\r\n|\n\r|\n|\r/g;
+				lineEnding = "\n"
+
+				return data.replace(lineEndingsRegExp, lineEnding).split(lineEnding)
+
+			splitToColumns = (row) ->
+				separatorChar = "\t"
+
+				return row.split(separatorChar)
+
+			columnsToObject = (columns) ->
+				o = {}
+				format = $scope.ngPasteFromFormat
+
+				for c, i in columns
+					o[format[i]] = c
+
+				return o
+
+			$scope.processPasteData = (data) ->
+				rows = splitToRows data
+				result = []
+
+				for r, i in rows
+					columns = splitToColumns r
+
+					if columns.length isnt $scope.ngPasteFromFormat.length
+						$scope.ngPasteFromOnError ngPasteFromErrors.invalidColumnLength, i
+						continue
+
+					o = columnsToObject columns
+
+					if $scope.ngPasteFromOnValidate o, i
+						result.push o
+					else
+						$scope.ngPasteFromOnError ngPasteFromErrors.failedValidation, i
+
+				$scope.ngPasteFrom = result
+
+			$scope.$watch "pasteData", ->
+				if $scope.pasteData? and 0 < $scope.pasteData.length
+					$scope.processPasteData($scope.pasteData)
+					$scope.pasteData = null


Add Event Class snippet.

Add Event Class snippet.

--- ide-snippets/StructureJS.Atom.cson
+++ ide-snippets/StructureJS.Atom.cson
@@ -13,15 +13,12 @@
 # Method: method
 # Import: import
 # Extend: extendClass
+# Event: eventClass

 ".source.js":
   "View":
    prefix: "viewClass"
    body: """
-       /**
-        * @fileOverview
-        * ${1:Class}
-        */
         define(function(require, exports, module) { // jshint ignore:line
             'use strict';

@@ -106,10 +103,6 @@
   "Value Object":
    prefix: "valueObject"
    body: """
-      /**
-       * @fileOverview
-       * ${1:Class}
-       */
         define(function(require, exports, module) { // jshint ignore:line
             'use strict';

@@ -155,10 +148,6 @@
   "Stage":
    prefix: "stageClass"
    body: """
-       /**
-        * @fileOverview
-        * ${1:Class}
-        */
         define(function(require, exports, module) { // jshint ignore:line
             'use strict';

@@ -304,3 +293,45 @@

         });
     """
+      "Event":
+        prefix: "eventClass"
+        body: """
+            define(function (require, exports, module) { // jshint ignore:line
+                'use strict';
+
+                // Imports
+                var Extend = require('structurejs/util/Extend');
+                var BaseEvent = require('structurejs/event/BaseEvent');
+
+                /**
+                 * TODO: YUIDoc_comment
+                 *
+                 * @class ${1:Class}
+                 * @extends ${1}
+                 * @constructor
+                 **/
+                var ${1:Class} = (function () {
+
+                    /**
+                     * This is an example of an event type.
+                     * Go ahead and rename or remove this.
+                     *
+                     * @event EXAMPLE
+                     * @type {string}
+                     * @static
+                     */
+                    ${1:Class}.EXAMPLE = '${1:Class}.example';
+
+                  var _super = Extend(${1:Class}, ${1});
+
+                      function ${1:Class}() {
+                          _super.call(this);
+                      }
+
+                      return ${1:Class};
+                })();
+
+                module.exports = ${1:Class};
+
+            });
+        """


attach jasmine to window

attach jasmine to window

--- jasmine-harness.coffee
+++ jasmine-harness.coffee
@@ -1,6 +1,6 @@
-{jasmine} = require './spec/lib/jasmine.js'
-require './spec/lib/jasmine-html.js'
-require './spec/lib/jasmine.tap_reporter.js'
+window.jasmine = require('./spec/lib/jasmine.js').jasmine
+html = require './spec/lib/jasmine-html.js'
+tap = require './spec/lib/jasmine.tap_reporter.js'

 # insert test files here
 ###


Instantiate Redis client when needed only

--- lib/event_store/redis.coffee
+++ lib/event_store/redis.coffee
@@ -2,21 +2,21 @@
 async       = require "async"
 Event       = require "../event"

-client      = redis.createClient()
+class EventStore
+  constructor: ->
+    @client      = redis.createClient()
+    @client.on 'error', (error) ->
+      throw error

-client.on 'error', (error) ->
-  throw error
-
-class EventStore
   createNewUid: (callback) ->
-    client.incr 'uid', callback
+    @client.incr 'uid', callback

   findAllByAggregateUid: (aggregateUid, callback) ->
-    client.lrange "aggregate-timeline:#{aggregateUid}", 0, -1, (err, eventIds) =>
+    @client.lrange "aggregate-timeline:#{aggregateUid}", 0, -1, (err, eventIds) =>

-      findEventById = (eventUid, callback) ->
-        client.get "event:#{eventUid}", (err, eventName) ->
-          client.hgetall "event:#{eventUid}:data", (err, eventData) ->
+      findEventById = (eventUid, callback) =>
+        @client.get "event:#{eventUid}", (err, eventName) =>
+          @client.hgetall "event:#{eventUid}:data", (err, eventData) ->
             if err
               callback err
             else
@@ -26,10 +26,10 @@
       async.map eventIds, findEventById, callback

   saveEvent: (event, callback) ->
-    @createNewUid (err, eventUid) ->
+    @createNewUid (err, eventUid) =>
       event.uid = eventUid

-      transaction = client.multi [
+      transaction = @client.multi [
         ["set", "event:#{eventUid}", event.name],
         ["hmset", "event:#{eventUid}:data", event.data],
         ["rpush", "event-timeline", eventUid],


:lipstick:

:lipstick:

--- lib/models/git-commit.coffee
+++ lib/models/git-commit.coffee
@@ -96,22 +96,19 @@
         new StatusView(type: 'success', message: data)
         # Set @isAmending to false since it succeeded.
         @isAmending = false
-
         # Destroying the active EditorView will trigger our cleanup method.
         @destroyActiveEditorView()
-
         # Refreshing the atom repo status to refresh things like TreeView and
         # diff gutter.
         atom.project.getRepo()?.refreshStatus()
-
         # Activate the former active pane.
         @currentPane.activate()
-
         # Refresh git index to prevent bugs on our methods.
         git.refresh()

       stderr: (err) =>
-        if @isAmending then @undoAmend() else @cleanup()
+        # Destroying the active EditorView will trigger our cleanup method.
+        @destroyActiveEditorView()

   # Public: Destroys the active EditorView to trigger our cleanup method.
   destroyActiveEditorView: ->


When publishing, ensure we don't mutate the subscriptions array as we iterate

--- lib/monarch/util/node.coffee
+++ lib/monarch/util/node.coffee
@@ -9,7 +9,7 @@
     @publishArgs(arguments)

   publishArgs: (args) ->
-    for subscription in @subscriptions
+    for subscription in new Array(@subscriptions...)
       subscription.publish(args)

   subscribe: (callback, context) ->


test pipe

test pipe

--- src/test/render.coffee
+++ src/test/render.coffee
@@ -1,8 +1,38 @@
 { Template } = require '../dynamictemplate'
-render = require '../render'
+{ render, pipe } = require '../render'

 module.exports =
+
+    pipe: (æ) ->
+        output = pipe new Template schema:5, doctype:on, ->
+            @$html ->
+                @$head ->
+                    @$title "holla"
+                @$body ->
+                    @$p "hello world"
+
+
+        output.on 'data', (tag) -> æ.equal results.shift(), tag.toString()
+        output.on 'end', ->
+            æ.equal 0, results.length
+            æ.done()
+        results = [
+            '<!DOCTYPE html>'
+            ''
+            ''
+            '<title>'
+            'holla'
+            '</title>'
+            ''
+            ''
+            '
'
+            'hello world'
+            '
'
+            ''
+            ''
+        ]
+

     simple: (æ) ->
         output = render new Template schema:5, doctype:on, ->
@@ -31,4 +61,4 @@
             '
'
             ''
             ''
-        ]+        ]


Move getSaveDialogOptions near other save methods

--- src/text-editor.coffee
+++ src/text-editor.coffee
@@ -593,16 +593,6 @@
   # Essential: Returns the {String} path of this editor's text buffer.
   getPath: -> @buffer.getPath()

-  # Private: Return [save options](http://electron.atom.io/docs/v0.27.0/api/di
-  # alog/#dialog.showsavedialog(%5Bbrowserwindow%5D,-%5Boptions%5D,-%5Bcallbac
-  # k%5D)) to be used when displaying the save dialog.
-  #
-  # Default empty options are returned now. In the future this would be the
-  # place to start implementing things like: https://discuss.atom.io/t
-  # /request-saving- file-with-correct-extension/17521
-  getSaveDialogOptions: ->
-    {}
-
   # Extended: Returns the {String} character set encoding of this editor's text
   # buffer.
   getEncoding: -> @buffer.getEncoding()
@@ -647,6 +637,10 @@
       @isModified()
     else
       @isModified() and not @buffer.hasMultipleEditors()
+
+  # Returns an {Object} to configure dialog shown when this editor is saved
+  # via {Pane::saveItemAs}.
+  getSaveDialogOptions: -> {}

   checkoutHeadRevision: ->
     if filePath = this.getPath()


Use list.map instead of _.map list

Use list.map instead of _.map list

--- src/text-editor.coffee
+++ src/text-editor.coffee
@@ -2523,7 +2523,7 @@
   logCursorScope: ->
     scopeDescriptor = @getLastCursor().getScopeDescriptor()
     list = scopeDescriptor.scopes.toString().split(',')
-    list = _.map list, (item) -> "* #{item}"
+    list = list.map (item) -> "* #{item}"
     content = "Scopes at Cursor\n#{list.join('\n')}"

     atom.notifications.addInfo(content, dismissable: true)


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/text_effect.coffee
+++ src/text_effect.coffee
@@ -1,5 +1,5 @@
 ###*
-The Text Effect class provides a method to display
+The Text Effect class provides a method to display

 By default, images are loaded asynchronously. A proxy object is
 returned immediately. Even though it has a draw method it will not


levels and default game states

levels and default game states

--- src/text_screen.coffee
+++ src/text_screen.coffee
@@ -5,7 +5,6 @@

   self = GameState(I).extend
     drawTitleText: (canvas, titleText, subtitleText) ->
-      canvas.fillColor
       canvas.font I.font

       canvas.centerText


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/text_screen.coffee
+++ src/text_screen.coffee
@@ -17,7 +17,7 @@

     screen = TextScreen()
-    screen.centerText
+    screen.centerText(canvas, )
     enemy.follow(player)

     # => The enemy now has it's velocity attribute set in


levels and default game states

levels and default game states

--- src/text_screen.coffee
+++ src/text_screen.coffee
@@ -5,7 +5,7 @@
   self = GameState(I).extend
     drawTitleText: (canvas, titleText, subtitleText) ->
       canvas.fillColor 'white'
-      canvas.font '24px Helvetica'
+      canvas.font ''

       canvas.centerText
         y: App.height / 2 - 20


Added tooltips to navigation icons

Added tooltips to navigation icons

--- client/Main/navigation/navigationlink.coffee
+++ client/Main/navigation/navigationlink.coffee
@@ -7,6 +7,10 @@
     data.type        or= ''
     options.tagName  or= 'a'
     options.type     or= 'main-nav'
+    options.tooltip    =
+      title            : "#{data.title}"
+      placement        : "bottom"
+      arrow            : "top"
     options.draggable  = yes
       # axis             : 'xy'
       # containment      : 'parent' #KD.getSingleton('DockController').getView()


activity: locked state of `ActivityLikeLink` view (fixes #76629854)

--- client/Social/Activity/views/likelink.coffee
+++ client/Social/Activity/views/likelink.coffee
@@ -15,12 +15,17 @@

     KD.utils.stopDOMEvent event

+    return  if @locked
+    @locked = yes
+
     {id}           = data = @getData()
     {isInteracted} = data.interactions.like
     {like, unlike} = KD.singletons.socialapi.message

     fn = if isInteracted then unlike else like
-    fn {id}, (err) => @showError err  if err
+    fn {id}, (err) =>
+      @locked = no
+      @showError err  if err

   update: ->


Teamwork: TabView is now navigable.

Teamwork: TabView is now navigable.

--- client/Teamwork/Views/teamworktabview.coffee
+++ client/Teamwork/Views/teamworktabview.coffee
@@ -80,7 +80,10 @@
       delegate                  : this
       lastTabHandleMargin       : 80
       tabHandleContainer        : @tabHandleHolder
+      enableMoveTabHandle       : yes
+      resizeTabHandles          : no
       closeAppWhenAllTabsClosed : no
+      minHandleWidth            : 128

     @tabView.on "PaneAdded", (pane) =>
       pane.getHandle().on "click", =>


Collaboration: Use native JS instead of jQ.

--- client/ide/workspace/panes/editorpane.coffee
+++ client/ide/workspace/panes/editorpane.coffee
@@ -159,7 +159,8 @@
     if oldWidget
       widgetManager.removeLineWidget oldWidget

-    $(".ace-line-widget-#{KD.nick()}").remove()
+    elements = document.getElementsByClassName "ace-line-widget-#{KD.nick()}"
+    elements[0].parentNode.removeChild elements[0]  if elements.length

     lineWidgetOptions =
       row        : row


Fix sorting of columns with undefined values. PP-131

--- client/src/scripts/scoring-sheet-view.coffee
+++ client/src/scripts/scoring-sheet-view.coffee
@@ -890,10 +890,8 @@
     [ columnHeader, scoringLink, swatch, checkbox, selectAllCheckbox, filterButton, filterOutButton] = geyser.generate [ "a.y-header data-variable-id='$id'", "a.y-scoring-link data-scoring-id='$id'", ".y-legend-swatch style='background-color:$color'", "input.y-select-one-checkbox type='checkbox' data-scoring-id='$id'", "input.y-select-all-checkbox type='checkbox'", 'button.btn.y-filter-button', "button.btn.y-filter-out-button style='margin-left:7px'"]

     # Sort
-    _filteredMetrics.sort (metricA, metricB) ->
-      a = _sortByVariable.read metricA
-      b = _sortByVariable.read metricB
-      if _sortAscending then a > b else b > a
+    _filteredMetrics = sortBy _filteredMetrics, (metric) -> _sortByVariable.read metric
+    _filteredMetrics.reverse() unless _sortAscending

     columnVariables = clone _filteredMetricVariables
     columnVariables.splice.apply columnVariables, flatten [ 8, 0, _filteredInputVariables ]


Use express v3.0.0beta4. [Fix #1]

Use express v3.0.0beta4. [Fix #1]

--- lib/skeleton/template.coffee
+++ lib/skeleton/template.coffee
@@ -20,7 +20,7 @@
           "name": "#{@appName}"
         , "version": "0.0.1"
         , "dependencies": {
-              "express": "3.0.x"
+              "express": "3.0.0beta4"
             , "connect-assets": "2.1.x"
             #{this.getTemplateModule()}
             #{this.getCssModule()}


Let findMatchingOpeningBracket return non-reversed ranges

As it should have done from the start.

--- lib/text-manipulation.coffee
+++ lib/text-manipulation.coffee
@@ -72,7 +72,7 @@
             # TODO: For now, disallow this, as it requires some changes to the model to accomodate the whitespace in an empty list
             []
           else
-            nonNestedIxRanges = leftIxRanges.reverse().concat rightIxRanges
+            nonNestedIxRanges = leftIxRanges.concat rightIxRanges
             elementRanges = @getElementRangesFromNonNested bufferText, listStartIx, listEndIx, nonNestedIxRanges
             # @showIxRanges bufferText, elementRanges

@@ -104,11 +104,11 @@

       if (closingBracket? && (currentChar == @getOpeningBracketFor closingBracket)) ||
          @isOpeningBracket currentChar
-        @addRange ranges, ix, rangeEnd, isNested
+        @unshiftRange ranges, ix, rangeEnd, isNested
         return {bracketIx: ix, ranges: ranges}

       if @isClosingBracket currentChar
-        @addRange ranges, ix, rangeEnd, isNested
+        @unshiftRange ranges, ix, rangeEnd, isNested
         res = @findMatchingOpeningBracket bufferText, ix-1, true, currentChar
         break if not res?
         ix = res.bracketIx
@@ -116,7 +116,10 @@

       ix--

-    return null # syntax error in list (or no list)
+    return null # list not well formed, or no list
+
+  unshiftRange: (ranges, rangeStart, rangeEnd, isNested) ->
+    ranges.unshift [rangeStart,rangeEnd] if not isNested && rangeStart != rangeEnd

   findMatchingClosingBracket: (bufferText, startIx, isNested, openingBracket) ->
     # console.log "findMatchingClosingBracket: " + startIx + (if openingBracket? then openingBracket else "any opening bracket")
@@ -128,11 +131,11 @@

       if (openingBracket? && (currentChar == @getClosingBracketFor openingBracket)) ||
          @isClosingBracket currentChar
-        @addRange ranges, rangeStart, ix, isNested
+        @pushRange ranges, rangeStart, ix, isNested
         return {bracketIx: ix, ranges: ranges}

       if @isOpeningBracket currentChar
-        @addRange ranges, rangeStart, ix, isNested
+        @pushRange ranges, rangeStart, ix, isNested
         res = @findMatchingClosingBracket bufferText, ix+1, true, currentChar
         break if not res?
         ix = res.bracketIx
@@ -140,9 +143,9 @@

       ix++

-    return null # syntax error in list (or no list)
-
-  addRange: (ranges, rangeStart, rangeEnd, isNested) ->
+    return null # list not well formed, or no list)
+
+  pushRange: (ranges, rangeStart, rangeEnd, isNested) ->
     ranges.push [rangeStart,rangeEnd] if not isNested && rangeStart != rangeEnd

   # Convert list of ranges that cover the entire list except its sublists, to ranges for its elements.


removed some logs

removed some logs

--- src/ubret/table.coffee
+++ src/ubret/table.coffee
@@ -76,10 +76,8 @@
       else
         return @dimensions[@selectedKey].filter([top[@selectedKey], bottom[@selectedKey]])[@sortOrder](Infinity)
     else if number < @pages
-      console.log @currentPage, number
       top = @dimensions[@selectedKey].filterAll()[@sortOrder](number * @numRows)[(number * @numRows) - 1]
       bottom = @dimensions[@selectedKey][@sortOrder]((number + 1) * @numRows)[((number + 1) * @numRows) - 1]
-      console.log top, bottom
       return @dimensions[@selectedKey].filter([bottom[@selectedKey], top[@selectedKey]])[@sortOrder](Infinity)
     else if number >= @pages
       @page number % @pages


Fixed bulk action.

Fixed bulk action.

--- app/assets/javascripts/app/controllers/agent_ticket_view.js.coffee
+++ app/assets/javascripts/app/controllers/agent_ticket_view.js.coffee
@@ -95,7 +95,8 @@
     pages_total =  parseInt( ( @tickets_count / @overview.view[@view_mode].per_page ) + 0.99999 ) || 1

     # render init page
-    edit = true
+    checkbox = true
+    edit     = true
     if @isRole('Customer')
       checkbox = false
       edit     = false


Interactive commands buffering and timeout

--- lib/ghc-modi-process-base.coffee
+++ lib/ghc-modi-process-base.coffee
@@ -36,10 +36,11 @@
       else
         modiPath = atom.config.get('haskell-ghc-mod.ghcModiPath')
         CP.spawn(modiPath, [], options)
-    proc.stderr.pause()
+    proc.stdout.setEncoding 'utf-8'
+    proc.stderr.on 'data', (data) ->
+      console.error "Ghc-modi said: #{data}"
     proc.on 'exit', (code) =>
       debug "ghc-modi for #{rootDir.getPath()} ended with #{code}"
-      console.error "Ghc-modi said: #{proc.stderr.read()}"
       @processMap?.delete(rootDir)
       @spawnProcess(rootDir, legacyInteractive, options) if code != 0
     @processMap.set rootDir,
@@ -99,79 +100,84 @@
         dismissable: true
       return []

+  waitForAnswer: (proc, cmd) ->
+    new Promise (resolve, reject) ->
+      savedLines = []
+      exitCallback = null
+      parseData = null
+      cleanup = ->
+        proc.stdout.removeListener 'data', parseData
+        proc.removeListener 'exit', exitCallback
+      parseData = (data) ->
+        debug "Got response from ghc-modi:#{EOL}#{data}"
+        lines = data.split(EOL)
+        savedLines = savedLines.concat lines
+        result = savedLines[savedLines.length - 2]
+        if result is 'OK'
+          cleanup()
+          lines = savedLines.slice(0, -2)
+          resolve lines.map (line) ->
+            line.replace /\0/g, EOL
+      exitCallback = ->
+        cleanup()
+        console.error "#{savedLines}"
+        reject "Ghc-modi crashed on command #{cmd} with message #{savedLines}"
+      proc.stdout.on 'data', parseData
+      proc.on 'exit', exitCallback
+      setTimeout (->
+        cleanup()
+        console.error "#{savedLines}"
+        reject "Timeout on ghc-modi command #{cmd}; message so far: #{savedLines}"
+        ), 60000
+
+  interact: (proc, command) ->
+    resultP = @waitForAnswer proc, command
+    debug "Running ghc-modi command #{command.split(EOL)[0]}"
+    proc.stdin.write command
+    return resultP
+
   runModiCmd: (o) =>
     {dir, options, command, text, uri, args, legacyInteractive} = o
     debug "Trying to run ghc-modi in #{dir.getPath()}"
-    process = @spawnProcess(dir, legacyInteractive, options)
-    unless process
+    proc = @spawnProcess(dir, legacyInteractive, options)
+    unless proc
       debug "Failed. Falling back to ghc-mod"
       return @runModCmd o
     @interactiveAction =
-    @interactiveAction.then ->
-      process.stdout.pause()
+    @interactiveAction.then =>
       if text?
         debug "Loading file text for ghc-modi"
-        new Promise (resolve, reject) ->
-          process.stdout.once 'readable', ->
-            data = process.stdout.read()?.toString?()
-            if data is "OK#{EOL}"
-              debug "Successfully loaded file text for ghc-modi"
-              resolve()
-            else
-              debug "Failed to load file text for ghc-modi: #{data}"
-              reject "Failed to load file text for ghc-modi: #{data}"
-          process.stdin.write "map-file #{uri}#{EOL}#{text}#{EOT}"
-    .then ->
-      new Promise (resolve, reject) ->
-        savedLines = []
-        parseData = ->
-          data = process.stdout.read()?.toString?()
-          unless data?
-            console.error savedLines
-            return reject "Haskell-ghc-mod: ghc-modi crashed on
-              #{command} with message #{savedLines.join(EOL)}
-              in #{dir.getPath()}"
-          debug "Got response from ghc-modi:#{EOL}#{data}"
-          lines = data.split(EOL)
-          savedLines = savedLines.concat lines
-          result = savedLines[savedLines.length - 2]
-          if result?.match?(/^OK/)
-            lines = savedLines.slice(0, -2)
-            return resolve lines.map (line) ->
-              line.replace /\0/g, EOL
-          else
-            process.stdout.once 'readable', parseData
-        process.stdout.once 'readable', parseData
-        if uri?
-          cmd = [command, uri].concat args
-        else
-          cmd = [command].concat args
-        debug "Running ghc-modi command #{cmd}"
-        process.stdin.write cmd.join(' ').replace(EOL, ' ') + EOL
-    .then (res) ->
+        @interact proc, "map-file #{uri}#{EOL}#{text}#{EOT}"
+        .then ->
+          debug "Successfully loaded file text for ghc-modi"
+        .catch (err) ->
+          debug "Failed to load file text for ghc-modi"
+          throw err
+    .then =>
+      if uri?
+        cmd = [command, uri].concat args
+      else
+        cmd = [command].concat args
+
+      @interact proc, cmd.join(' ').replace(EOL, ' ') + EOL
+    .then (res) =>
       if text?
-        new Promise (resolve, reject) ->
-          debug "Unloading file text from ghc-modi"
-          process.stdout.once 'readable', ->
-            data = process.stdout.read()?.toString?()
-            if data is "OK#{EOL}"
-              debug "Successfully unloaded file text for ghc-modi"
-              resolve(res)
-            else
-              debug "Failed to unload file text for ghc-modi: #{data}"
-              reject "Failed to unload file text for ghc-modi: #{data}"
-          process.stdin.write "unmap-file #{uri}#{EOL}"
+        debug "Unloading file text from ghc-modi"
+        @interact proc, "unmap-file #{uri}#{EOL}"
+        .then ->
+          debug "Successfully unloaded file text for ghc-modi"
+          return res
+        .catch (err) ->
+          debug "Failed to unload file text for ghc-modi: #{err}"
+          throw err
       else
         res
     .catch (err) ->
-      atom.notifications.addError 'Looks like ghc-modi crashed',
+      atom.notifications.addError 'Looks like something went wrong',
         detail: "#{err}"
         dismissable: true
-      try process.stdin.write "unmap-file #{uri}#{EOL}"
+      try proc.stdin.write "unmap-file #{uri}#{EOL}"
       return []
-    .then (res) ->
-      try process.stdout.resume()
-      res

   killProcess: =>
     return unless @processMap?


Port wage schedule calculation over to server-side

--- src/routes/survey.coffee
+++ src/routes/survey.coffee
@@ -82,6 +82,9 @@

   intervals = []

+  debug "Schedule: #{schedule}"
+  debug "Schedule: #{wageSchedules[schedule]}"
+
   for wageInterval in [0..wageSchedules[schedule].length] by 1
     debug "Wage Interval #{wageInterval}"
     current = wageSchedules[schedule][wageInterval]


surgery ops to the old coding

surgery ops to the old coding

--- library/nucleus/watch.coffee
+++ library/nucleus/watch.coffee
@@ -49,6 +49,7 @@
 # the modules that contain services. It can be configured to watch
 # only specific directories. It also features automatic reloading
 # that takes care of unregistering and registering the services.
+# Take a look at the `GraniteKernel` for the usage information.
 module.exports.Watcher = class Watcher extends Archetype

     # This defines the set of filename extensions that will be


Add keyboard events and some comments

Add keyboard events and some comments

--- src/runners/event.coffee
+++ src/runners/event.coffee
@@ -1,15 +1,20 @@
 ### LIST OF EVENTS TO TRIGGER ###

 EventList = [
+  # Mouse events
   "click", "dblclick"
   "mouseenter", "mouseleave", "mouseout"
   "mouseover", "mousedown", "mouseup"
   "drag", "dragstart", "dragenter", "dragleave", "dragover", "dragend", "drop"
+  # General
   "load"
   "focus", "focusin", "focusout", "select", "blur"
   "submit"
   "scroll"
+  # Touch events
   "touchstart", "touchend", "touchmove", "touchenter", "touchleave", "touchcancel"
+  # Keys events
+  "keyup", "keydown", "keypress"
 ]

 class EventRunner extends Neck.Controller


Pass atom.styles when initializing <atom-styles>

--- lib/markdown-preview-view.coffee
+++ lib/markdown-preview-view.coffee
@@ -188,13 +188,10 @@
     document.styleSheets

   getTextEditorStyles: ->
-
     textEditorStyles = document.createElement("atom-styles")
+    textEditorStyles.initialize(atom.styles)
     textEditorStyles.setAttribute "context", "atom-text-editor"
     document.body.appendChild textEditorStyles
-
-    # Force styles injection
-    textEditorStyles.initialize()

     # Extract style elements content
     Array.prototype.slice.apply(textEditorStyles.childNodes).map (styleElement) ->


Fix my promise usage

Fix my promise usage

--- lib/project/results-model.coffee
+++ lib/project/results-model.coffee
@@ -28,18 +28,8 @@
     @pattern = ''
     @emit('cleared')

-  search: (pattern, paths, onlyRunIfChanged = false)->
-    if onlyRunIfChanged and pattern? and paths? and pattern == @pattern and _.isEqual(paths, @searchedPaths)
-      # Returning a promise that is already resolved (i.e. Q()) is problematic.
-      # Produces undefined behavior. (Maybe a Q bug?) Example:
-      #
-      # prom = @search('items', '', true).then =>
-      #   @replace('items', 'blah')
-      #
-      # prom can (and sometimes does) resolve _before_ the @replace promise!
-      deferred = Q.defer()
-      setImmediate -> deferred.resolve()
-      deferred.promise
+  search: (pattern, paths, onlyRunIfChanged = false) ->
+    return Q() if onlyRunIfChanged and pattern? and paths? and pattern == @pattern and _.isEqual(paths, @searchedPaths)

     @clear()
     @active = true
@@ -53,11 +43,8 @@
     promise = atom.project.scan @regex, {paths, onPathsSearched}, (result) =>
       @setResult(result.filePath, result.matches)

-    promise.done => @emit('finished-searching')
-
     @emit('search', promise)
-
-    promise
+    promise.then => @emit('finished-searching')

   replace: (pattern, replacementText, paths) ->
     regex = @getRegex(pattern)
@@ -71,13 +58,10 @@
         replacements += result.replacements
       @emit('path-replaced', result)

-    promise.done =>
+    @emit('replace', promise)
+    promise.then =>
       @clear()
       @emit('finished-replacing', {pathsReplaced, replacements})
-
-    @emit('replace', promise)
-
-    promise

   toggleUseRegex: ->
     @useRegex = not @useRegex


trigger needsSave when HXL tags field is changed in form builder, fixes #1525

--- jsapp/xlform/src/model.base.coffee
+++ jsapp/xlform/src/model.base.coffee
@@ -158,7 +158,7 @@
       # when attributes change, register changes with parent survey
       if @key in ["name", "label", "hint", "required",
                   "calculation", "default", "appearance",
-                  "constraint_message"]
+                  "constraint_message", "tags"]
         @on "change", (changes)=>
           @getSurvey().trigger "change", changes


app: Add post login events.

app: Add post login events.

--- oghma/app.coffee
+++ oghma/app.coffee
@@ -26,6 +26,10 @@
 # @author Christopher Alfeld (calfeld@calfeld.net)
 # @copyright 2013 Christopher Alfeld
 class Oghma.App
+  # Callbacks
+  callbacks:
+    post_login: jQuery.Callbacks()
+
   # Constructor
   #
   # Sets up server connections for {Heron.Comet} and {Heron.Dictionary} and
@@ -156,6 +160,19 @@
       document.title,
       encodeURI( "?user=#{username}" )
     )
+    @callbacks.post_login.fire( @me() )
+    this
+
+  # Register callback.
+  #
+  # Available events:
+  # - post_login: Called with user thingy on successful login.
+  #
+  # @param [string] which Which event.
+  # @param [function] f Function to call on specified event.
+  # @return [Oghma.App] this
+  on: ( which, f ) ->
+    @callbacks[ which ].add( f )
     this

   # @return [userverse.user] Thingy for current user.


Minor implementation change

Minor implementation change

--- library/membrane/describe.coffee
+++ library/membrane/describe.coffee
@@ -98,8 +98,8 @@
         noDescriptor = "The #{descriptor} is not a descriptor"
         assert _.isFunction(method), noMethod
         assert _.isFunction(descriptor), noDescriptor
+        previous = method.document?.descriptor
         method.document ?= new Document
-        previous = method.document.descriptor
         method.document.descriptor = ->
             previous?.apply this, arguments
             descriptor.apply this, arguments


Fix tests

Fix tests

--- test/index-spec.coffee
+++ test/index-spec.coffee
@@ -97,7 +97,7 @@
     describe 'with a valid query', ->
       beforeEach (done) ->
         @request.get.yields null, null, foo: 'bar'
-        @sut.devices 'octoblu:test', (@error, @body) => done()
+        @sut.devices type: 'octoblu:test', (@error, @body) => done()

       it 'should call get', ->
         expect(@request.get).to.have.been.calledWith 'https://meshblu.octoblu.com:443/devices',


Removing files is now possible but doesn't show the initial message if files length is 0.

#36

--- src/dropzone.coffee
+++ src/dropzone.coffee
@@ -51,6 +51,7 @@
     "dragleave"
     "selectedfiles"
     "addedfile"
+    "removedfile"
     "thumbnail"
     "error"
     "processingfile"
@@ -131,8 +132,8 @@
     dragover: (e) -> @element.addClass "drag-hover"
     dragleave: (e) -> @element.removeClass "drag-hover"

+    # Called whenever files are dropped or selected
     selectedfiles: (files) ->
-    # Called whenever files are dropped or selected
       @element.addClass "started"

@@ -144,6 +145,10 @@
       file.previewTemplate.find(".filename span").text file.name
       file.previewTemplate.find(".details").append o """
#{@filesize file.size}
"""

+
+    # Called whenever a file is removed.
+    removedfile: (file) ->
+      file.previewTemplate.remove()

     # Called when a thumbnail has been generated
     # Receives `file` and `dataUrl`
@@ -375,6 +380,13 @@
       else
         @files.queue.push file
         @processQueue()
+
+  # Can be called by the user to remove a file
+  removeFile: (file) ->
+    throw new Error "Can't remove file currently processing" if file.processing
+    @files = without @files, file
+
+    @emit "removedfile", file

   createThumbnail: (file) ->

@@ -442,6 +454,7 @@
   # Loads the file, then calls finishedLoading()
   processFile: (file) ->
     @files.processing.push file
+    file.processing = yes

     @emit "processingfile", file

@@ -498,6 +511,7 @@
   # Individual callbacks have to be called in the appropriate sections.
   finished: (file, responseText, e) ->
     @files.processing = without(@files.processing, file)
+    file.processing = no
     @emit "success", file, responseText, e
     @emit "finished", file, responseText, e # For backwards compatibility
     @emit "complete", file
@@ -508,6 +522,7 @@
   # Individual callbacks have to be called in the appropriate sections.
   errorProcessing: (file, message) ->
     @files.processing = without(@files.processing, file)
+    file.processing = no
     @emit "error", file, message
     @emit "complete", file
     @processQueue()


grammar fix

grammar fix

--- client/stacks/lib/views/stacks/onboarding/configurationview.coffee
+++ client/stacks/lib/views/stacks/onboarding/configurationview.coffee
@@ -38,7 +38,7 @@

     if tabLength is 2
       return new kd.NotificationView
-        title    : 'This is a preview mode. You can add more server manually in next steps.'
+        title    : 'This is a preview mode. You can add more servers manually in the next steps.'
         duration : 3663

     name = "Server #{tabLength + 1}"


Client: Activity: Added renderButton to render follow/unfollow buttons by account is participant of channel or not

--- client/activity/lib/components/channellistitem/index.coffee
+++ client/activity/lib/components/channellistitem/index.coffee
@@ -33,12 +33,26 @@

     channel.unfollowChannel channelId, whoami()._id

+
+  renderButton: ->
+
+    { channel } = @props
+
+    return  unless channel.get('typeConstant') is 'topic'
+
+    if channel.get 'isParticipant'
+      <button className="ChannelListItem-unfollow button" onClick={@bound 'unfollowChannel'}>UNFOLLOW</button>
+    else
+      <button className="ChannelListItem-follow button" onClick={@bound 'followChannel'}>FOLLOW</button>
+
+
   render: ->
+
     { channel } = @props
     channelName = channel.get 'name'

       {channelName}
-      <button className="ChannelListItem-unfollowButton" onClick={@bound 'unfollowChannel'}>UNFOLLOW</button>
+      {@renderButton()}


app-settings-menu: Improved code quality to get app menu.

--- client/app/MainApp/maincontroller/mainviews/mainview.coffee
+++ client/app/MainApp/maincontroller/mainviews/mainview.coffee
@@ -104,8 +104,9 @@
     ,null

     @mainTabView.on "PaneDidShow", =>
-      appManifest = KD.getSingleton("appManager").getFrontAppManifest()
-      menu = appManifest?.menu or appManager.get(@mainTabView.getActivePane()?.name)?.getOptions().menu
+      appManager  = KD.getSingleton "appManager"
+      appManifest = appManager.getFrontAppManifest()
+      menu = appManifest?.menu or KD.getAppOptions(appManager.getFrontApp().getOptions().name)?.menu
       if menu?.length
         @appSettingsMenuButton.setData menu
         @appSettingsMenuButton.show()


1tik asagi alindi :P

1tik asagi alindi :P

--- client/landing/app/MainApp/book/embedded/socialshare.coffee
+++ client/landing/app/MainApp/book/embedded/socialshare.coffee
@@ -6,7 +6,7 @@
       tagName       : "iframe"
       attributes    :
         width       : 90
-        height      : 21
+        height      : 20
         frameborder : 0
         scrolling   : "no"
         allowtransparency: "true"


rewriter: detectEnd no longer passes `undefined` as token, fixing #750

--- rewriter.coffee
+++ rewriter.coffee
@@ -46,9 +46,9 @@
     true

   detectEnd: (i, condition, action) ->
+    {tokens} = this
     levels = 0
-    loop
-      token = @tokens[i]
+    while token = tokens[i]
       return action.call this, token, i     if levels is 0 and condition.call this, token, i
       return action.call this, token, i - 1 if not token or levels < 0
       if include EXPRESSION_START, token[0]


insert after last core step

insert after last core step

--- tutor/src/components/task/crumb-mixin.cjsx
+++ tutor/src/components/task/crumb-mixin.cjsx
@@ -99,9 +99,11 @@

     # insert spacer panel/crumb for reading task that have spaced practices or personalized problems
     if task.type is 'reading'
-      notCore = _.last _.filter crumbs, (crumb) ->
-        (crumb.type is 'step') and not TaskStepStore.isCore(crumb.data.id)

+      notCoreIndex = _.findLastIndex crumbs, (crumb) ->
+        (crumb.type is 'step') and TaskStepStore.isCore(crumb.data.id)
+      if notCoreIndex isnt -1
+        notCore = crumbs[notCoreIndex]
       if notCore?
         crumbType = 'spacer'
         spacerCrumb =