Use new tooltips

Use new tooltips

--- lib/find-view.coffee
+++ lib/find-view.coffee
@@ -53,7 +53,8 @@
     @updateOptionsLabel()

   destroy: ->
-    @subscriptions.dispose()
+    @subscriptions?.dispose()
+    @tooltipSubscriptions?.dispose()

   setPanel: (@panel) ->
     @subscriptions.add @panel.onDidChangeVisible (visible) =>
@@ -61,16 +62,35 @@

   didShow: ->
     atom.workspaceView.addClass('find-visible')
-    # unless @tooltipsInitialized
-    #   @regexOptionButton.setTooltip("Use Regex", command: 'find-and-replace:toggle-regex-option', commandElement: @findEditor)
-    #   @caseOptionButton.setTooltip("Match Case", command: 'find-and-replace:toggle-case-option', commandElement: @findEditor)
-    #   @selectionOptionButton.setTooltip("Only In Selection", command: 'find-and-replace:toggle-selection-option', commandElement: @findEditor)
-    #
-    #   @nextButton.setTooltip("Find Next", command: 'find-and-replace:find-next', commandElement: @findEditor)
-    #
-    #   @replaceNextButton.setTooltip("Replace Next", command: 'find-and-replace:replace-next', commandElement: @replaceEditor)
-    #   @replaceAllButton.setTooltip("Replace All", command: 'find-and-replace:replace-all', commandElement: @replaceEditor)
-    #   @tooltipsInitialized = true
+    return if @tooltipSubscriptions?
+
+    @tooltipSubscriptions = subs = new CompositeDisposable
+    subs.add atom.tooltips.add @regexOptionButton,
+      title: "Use Regex"
+      keyBindingCommand: 'find-and-replace:toggle-regex-option',
+      keyBindingTarget: @findEditor[0]
+    subs.add atom.tooltips.add @caseOptionButton,
+      title: "Match Case",
+      keyBindingCommand: 'find-and-replace:toggle-case-option',
+      keyBindingTarget: @findEditor[0]
+    subs.add atom.tooltips.add @selectionOptionButton,
+      title: "Only In Selection",
+      keyBindingCommand: 'find-and-replace:toggle-selection-option',
+      keyBindingTarget: @findEditor[0]
+
+    subs.add atom.tooltips.add @nextButton,
+      title: "Find Next",
+      keyBindingCommand: 'find-and-replace:find-next',
+      keyBindingTarget: @findEditor[0]
+
+    subs.add atom.tooltips.add @replaceNextButton,
+      title: "Replace Next",
+      keyBindingCommand: 'find-and-replace:replace-next',
+      keyBindingTarget: @replaceEditor[0]
+    subs.add atom.tooltips.add @replaceAllButton,
+      title: "Replace All",
+      keyBindingCommand: 'find-and-replace:replace-all',
+      keyBindingTarget: @replaceEditor[0]

   didHide: ->
     @hideAllTooltips()
@@ -78,14 +98,8 @@
     atom.workspaceView.removeClass('find-visible')

   hideAllTooltips: ->
-  #   @regexOptionButton.hideTooltip()
-  #   @caseOptionButton.hideTooltip()
-  #   @selectionOptionButton.hideTooltip()
-  #
-  #   @nextButton.hideTooltip()
-  #
-  #   @replaceNextButton.hideTooltip()
-  #   @replaceAllButton.hideTooltip()
+    @tooltipSubscriptions.dispose()
+    @tooltipSubscriptions = null

   handleEvents: ->
     @handleFindEvents()


added some clarifying comments

added some clarifying comments

--- lib/languages.coffee
+++ lib/languages.coffee
@@ -60,10 +60,12 @@
     Uses the the below defined syntax, without preceding `#` per line. This is
     the syntax for what the definition is actually meant for !
     ###
+    # This syntax produces arkward comments in the compiled javascript, if the
+    # pull-request _[Format block-comments better]
+    # (<https://github.com/jashkenas/coffee-script/pull/3132)_ has not
+    # been applied to coffee-script's codebase …
     ### }
     # Variant 3
-    #
-    # **This syntax is nonsense and strongly disencouraged !**
     #
     # The block-comment line-matcher `'#'` also works on lines not starting
     # with `'#'`, because we add unmatched lines to the comments once we are


Fixes multi-line-comment array.

Fixes multi-line-comment array.

--- lib/languages.coffee
+++ lib/languages.coffee
@@ -165,7 +165,7 @@
   LiveScript:
     nameMatchers:       ['.ls', 'Slakefile']
     pygmentsLexer:      'livescript'
-    multiLineComment:   ['/*' '*', '*/']
+    multiLineComment:   ['/*', '*', '*/']
     singleLineComment:  ['#']
     ignorePrefix:       '}'
     foldPrefix:         '^'


Add signature param only if present

Add signature param only if present

--- src/actions_manager.coffee
+++ src/actions_manager.coffee
@@ -70,7 +70,7 @@
               type: item[1]
               data: item[2]
             }
-            action.sig = item[3] if item[0] is api.ecommerce.key
+            action.sig = item[3] if item[0] is api.ecommerce.key and item[3]

             @actions.push action


remove double "if" condition

remove double "if" condition

--- src/mixins/validatable-http-message.coffee
+++ src/mixins/validatable-http-message.coffee
@@ -159,28 +159,27 @@
     @validation.body.expectedType = null
     @validation.body.results ?= []

-    if @expected.bodySchema? and
-      @expected.bodySchema?
-        if typeof @expected.bodySchema == 'string'
-          try
-            parsed = JSON.parse @expected.bodySchema
-            if typeof parsed != 'object' or Array.isArray parsed
-              message = {
-                message: 'Expected body: JSON Schema provided, but it is not an Object'
-                severity: 'error'
-              }
-              @validation.body.results.push message
-            else
-              @validation.body.expectedType = 'application/schema+json'
-          catch error
+    if @expected.bodySchema?
+      if typeof @expected.bodySchema == 'string'
+        try
+          parsed = JSON.parse @expected.bodySchema
+          if typeof parsed != 'object' or Array.isArray parsed
             message = {
-              message: 'Expected body: JSON Schema provided, but it is not a parseable JSON'
+              message: 'Expected body: JSON Schema provided, but it is not an Object'
               severity: 'error'
             }
             @validation.body.results.push message
-            return
-        else
-          @validation.body.expectedType = 'application/schema+json'
+          else
+            @validation.body.expectedType = 'application/schema+json'
+        catch error
+          message = {
+            message: 'Expected body: JSON Schema provided, but it is not a parseable JSON'
+            severity: 'error'
+          }
+          @validation.body.results.push message
+          return
+      else
+        @validation.body.expectedType = 'application/schema+json'
     else

       expectedContentType = @expected.headers?['content-type']


simplified show button manager

simplified show button manager

--- app/js/models/fact.coffee
+++ app/js/models/fact.coffee
@@ -4,32 +4,15 @@

 class ShowButtonManager
-  constructor: (fact, dom_events) ->
-    @dom_events = dom_events
-    @fact = fact
+  constructor: (@dom_events) ->
     @show_button = new Factlink.ShowButton @dom_events

   set_coordinates: (@top, @left) =>

-  openShowButton:  =>
-    return if @opening_timeout
-    @opening_timeout = setTimeout =>
-      @_stopOpening()
-      @_realOpenShowButton(@top, @left)
-    , delay_between_highlight_and_show_button_open
-
-  _realOpenShowButton: (top, left) =>
-    @show_button.show(top, left)
-
-  closeShowButton: ->
-    @_stopOpening()
-    @show_button?.hide()
+  openShowButton:  ->  @show_button.show(@top, @left)
+  closeShowButton: ->  @show_button.hide()

   startLoading: -> @show_button?.startLoading()
-
-  _stopOpening: ->
-    clearTimeout @opening_timeout
-    @opening_timeout = null

   destroy: -> @show_button.destroy()

@@ -73,8 +56,7 @@

     @highlighter = new Highlighter $(@elements)

-
-    @show_button_manager = new ShowButtonManager this,
+    @show_button_manager = new ShowButtonManager
       mouseenter: => @onFocus()
       mouseleave: => @onBlur()
       click:      => @openFactlinkModal()


added parenthesis to sound.play

added parenthesis to sound.play

--- Framework/SoundCache.coffee
+++ Framework/SoundCache.coffee
@@ -18,6 +18,6 @@

       if preload
         sound.volume = 0
-        sound.play
+        sound.play()
         sound.volume = 1
     sound


fix(menu): set correct style property

fix(menu): set correct style property

--- src/components/Menu.coffee
+++ src/components/Menu.coffee
@@ -18,13 +18,13 @@
         submenu =
           for key2,v2 of v.items when typeof v.items is "object" then do
             (key2,v2) ->
-              li { key2, className: if v2.active then 'active' },
-                a { onClick: onClick(key2) }, v2.label
+              li { key: key2, className: if v2.active then 'active' },
+                a { onClick: onClick key2 }, v2.label
         li { key, className: if v.active then 'active' },
-          a { onClick: onClick(key) }, v.label
-          if (submenu and submenu.length)
-            i className: "fa fa-angle-down", style: {"padding-left": "5px"}
-          if (submenu and submenu.length)
+          a { onClick: onClick key }, v.label
+          if submenu and submenu.length
+            i className: "fa fa-angle-down", style: {paddingLeft: "5px"}
+          if submenu and submenu.length
             ul className:"submenu",
               submenu
     ul className:"menu", buttons


Apply removes from @updating

Apply removes from @updating

--- src/concepts/Update.coffee
+++ src/concepts/Update.coffee
@@ -138,6 +138,7 @@
     other = @domains[to]
     probs = @problems[from]

+    # Apply removes from parent update
     if parent
       globals = parent.domains.indexOf(null, parent.index + 1)
       if !domain.MAYBE
@@ -145,8 +146,17 @@
           globs = parent.problems[globals]
           if globs[0] == 'remove'
             domain.remove.apply(domain, globs.slice(1))
-
-
+
+    # Apply removes from global update
+    if @engine.updating
+      globals = @engine.updating.domains.indexOf(null, @engine.updating.index + 1)
+      if !domain.MAYBE
+        if globals > -1# && globals < from
+          globs = @engine.updating.problems[globals]
+          if globs[0] == 'remove'
+            domain.remove.apply(domain, globs.slice(1))
+
+    # Apply removes scheduled for exported domain
     while prob = probs[i++]
       if prob[0] == 'remove'
         domain.remove.apply(domain, prob.slice(1))
@@ -159,7 +169,7 @@
     @domains.splice(from, 1)
     @problems.splice(from, 1)
     for constraint in domain.constraints by -1
-      domain.unconstrain(constraint)
+      domain.unconstrain(constraint, undefined, true)
     if (i = @engine.domains.indexOf(domain)) > -1
       @engine.domains.splice i, 1
     return true


Disabled keys updating

Disabled keys updating

--- src/core/view-model.coffee
+++ src/core/view-model.coffee
@@ -11,6 +11,7 @@

 # @nodoc
 updateObservables = (model) ->
+  return
   return if kb.wasReleased(@) or not model

   # NOTE: this does not remove keys that are different between the models
@@ -20,6 +21,8 @@
   keys = _.difference(keys, @__kb.statics) if @__kb.statics  # remove statics
   missing = _.difference(keys, _.keys(@__kb.model_keys))
   @createObservables(model, missing) if missing.length
+
+KEYS_OPTIONS = ['internals', 'excludes', 'statics', 'static_defaults']

 # Base class for ViewModels for Models.
 #
@@ -109,7 +112,7 @@
     @__kb.vm_keys = {}
     @__kb.model_keys = {}
     @__kb.view_model = if _.isUndefined(view_model) then this else view_model
-    @__kb[key] = options[key] for key in ['internals', 'excludes', 'statics', 'static_defaults'] when options.hasOwnProperty(key)
+    @__kb[key] = options[key] for key in KEYS_OPTIONS when options.hasOwnProperty(key)

     # always use a store to ensure recursive view models are handled correctly
     kb.Store.useOptionsOrCreate(options, model, @)


Using Elyssa.serialize instead of JSON.stringif

--- src/foundation/node.coffee
+++ src/foundation/node.coffee
@@ -13,7 +13,7 @@
     draw: ->

-    toString: -> JSON.stringify
+    toString: -> Elyssa.serialize
       position: @position.toString()
       color: @color.toString()
       scale: @scale.toString()


add daysBetween()

add daysBetween()

--- src/github-calendar.coffee
+++ src/github-calendar.coffee
@@ -31,6 +31,10 @@
 			paintGrids i % 7, Math.floor(i / 7), grid
 )

+daysBetween = (date1, date2)->
+	ms = Math.abs( date1 - date2 )
+	floor ms / 1000 / 60 / 60 / 24
+
 isSameDay = (date1, date2)->
 	date1.getYear() is date2.getYear() and date1.getMonth() is date2.getMonth() and date1.getDate() is date2.getDate()


refactor(encryption): removed all old encryption functionality, for decoupling from main modelize code

--- src/lib/encryptable.coffee
+++ src/lib/encryptable.coffee
@@ -1,40 +0,0 @@
-Encryptable = (self, encrypted_container, encrypted_editable) ->
-  # Decrypt given encrypted_container and populate fields with decrypted values
-  #
-  self.decrypt = (encrypted_container) =>
-    if typeof encrypted_container == 'string' && encrypted_container != null && encrypted_container != ''
-      dencdata = decryptData sessionStorage.getItem('appKey'), encrypted_container
-
-      encrypted_container = JSON.parse(dencdata)
-
-      for index, value of encrypted_container
-        self[index] = value
-
-  if typeof encrypted_container == 'string'
-    self.decrypt self[encrypted_container]
-  else if typeof encrypted_container == 'object'
-    for container in encrypted_container
-      self.decrypt self[container]
-
-  self.enc_update = (data, callback) =>
-    for index, value of data
-      if index in encrypted_editable
-        # encrypt
-        if typeof self[encrypted_container] != 'object' || self[encrypted_container] == null
-          self[encrypted_container] = {}
-
-        self[encrypted_container][index] = value
-
-    self.save_encrypted_container()
-
-    if callback?
-      callback()
-
-  self.save_encrypted_container = =>
-    encdata = encryptData(sessionStorage.getItem('appKey'), JSON.stringify(self[encrypted_container]))
-
-    edit_value = {}
-    edit_value[encrypted_container] = encdata
-    self.update edit_value
-
-  return self


fail on unknown protocols in urls

fail on unknown protocols in urls

--- src/package-fetcher.coffee
+++ src/package-fetcher.coffee
@@ -40,7 +40,7 @@
         @_fetchFromGit name, spec, registry, parsed
       when 'http:', 'https:'
         @_fetchFromHTTP name, spec, registry, parsed
-      else
+      when null
         if semver.validRange spec, true
           @_fetchFromRegistry name, spec, registry
         else
@@ -48,6 +48,9 @@
             @_fetchFromGithub name, spec, registry
           else
             @emit 'error', "Unknown spec #{spec}", name, spec
+      else
+        @emit 'error', "Unknown protocol #{parsed.protocol}", name, spec
+

 PackageFetcher.prototype._fetchFromGithub = (name, spec, registry) ->
   new_spec = "git://github.com/" + spec.replace "#", ".git#"


fix tests

fix tests

--- src/protomixin.spec.coffee
+++ src/protomixin.spec.coffee
@@ -104,22 +104,23 @@
         expect(args...).toEqual(['arg1', 'arg2'])

       it 'should invoke the pre-mixing hook before mixing in properties', ->
+        mix_opts = {premixing_hook: ->}
         error = new Error
         threw = false

-        spyOn(@mixin, 'premixing_hook').and.throwError(error)
+        spyOn(mix_opts, 'premixing_hook').and.throwError(error)
         expect(@mixin.foo).toBeDefined()

         class Example

         try
-          Example.mixinto_proto @mixin, null, ['arg1', 'arg2']
+          Example.mixinto_proto @mixin, mix_opts, ['arg1', 'arg2']
         catch error
           threw = true
         finally
           expect(threw).toBe true
           expect(Example::foo).toBeUndefined() # wasn't mixed in!
-          expect(@mixin.premixing_hook).toHaveBeenCalledWith(['arg1', 'arg2'])
+          expect(mix_opts.premixing_hook).toHaveBeenCalledWith(['arg1', 'arg2'])

     describe 'post-mixing hooks', ->

@@ -127,30 +128,32 @@
         @mixin = MIXINS.default_protomixin()

       it 'should invoke a post-mixing hook with the prototype context', ->
-        spyOn(@mixin, 'postmixing_hook').and.callThrough()
-
-        class Example
-        Example.mixinto_proto @mixin, null, ['arg1', 'arg2']
-
-        expect(@mixin.postmixing_hook.calls.count()).toBe 1
-
-        {object, args} = @mixin.postmixing_hook.calls.first()
+        mix_opts = {postmixing_hook: ->}
+        spyOn(mix_opts, 'postmixing_hook').and.callThrough()
+
+        class Example
+        Example.mixinto_proto @mixin, mix_opts, ['arg1', 'arg2']
+
+        expect(mix_opts.postmixing_hook.calls.count()).toBe 1
+
+        {object, args} = mix_opts.postmixing_hook.calls.first()

         # test that the right context was used
         expect(object).toBe(Example::)
         expect(args...).toEqual(['arg1', 'arg2'])

       it 'should invoke the post-mixing hook after mixing in properties', ->
+        mix_opts = {postmixing_hook: ->}
         error = new Error
         threw = false

-        spyOn(@mixin, 'postmixing_hook').and.throwError(error)
+        spyOn(mix_opts, 'postmixing_hook').and.throwError(error)
         expect(@mixin.bar).toBe 1

         class Example

         try
-          Example.mixinto_proto @mixin, null, ['arg1', 'arg2']
+          Example.mixinto_proto @mixin, mix_opts, ['arg1', 'arg2']
         catch error
           threw = true
         finally


Remove meses vazios antes de passá-los ao dataTables

--- app/assets/javascripts/angular_app/directives/table_graph.js.coffee
+++ app/assets/javascripts/angular_app/directives/table_graph.js.coffee
@@ -36,7 +36,7 @@
       pago: entity.pago.total
       rppago: entity.rppago.total
       pagamentos: entity.pagamentos.total
-    months.splice(1).map (month, i) ->
+    months.filter((month) -> month).map (month, i) ->
       [monthFilter(month.label)
         currencyFilter(month.autorizado, '')
         currencyFilter(month.pago, '')


Correctly set the typeFilter when the user clicks "show more"

--- app/assets/javascripts/discourse/views/search/search_view.js.coffee
+++ app/assets/javascripts/discourse/views/search/search_view.js.coffee
@@ -92,8 +92,8 @@
     count
   ).property('content')

-  moreOfType: (e) ->
-    @set('typeFilter', e.context)
+  moreOfType: (type) ->
+    @set('typeFilter', type)
     false

   cancelType: ->


Turbolinks 5 support

Turbolinks 5 support

--- app/assets/javascripts/effective_assets/asset_box_sorting.js.coffee
+++ app/assets/javascripts/effective_assets/asset_box_sorting.js.coffee
@@ -6,3 +6,4 @@

 $ -> applySortable()
 $(document).on 'page:change', -> applySortable()
+$(document).on 'turbolinks:load', -> applySortable()


[refactor] Used underscore's sortBy rather than sugar.js's method (1m)

--- app/assets/javascripts/houston/scheduler/views/sequence_view.coffee
+++ app/assets/javascripts/houston/scheduler/views/sequence_view.coffee
@@ -14,7 +14,7 @@

     @unresolvedTickets = @tickets.unresolved().pluck('number')
     @sortedTickets = @tickets.sorted()
-    @unsortedTickets = @tickets.unresolved().unsorted().sortBy (ticket)-> ticket.get('summary')
+    @unsortedTickets = _.sortBy @tickets.unresolved().unsorted(), (ticket)-> ticket.get('summary')

   render: ->
     template = HandlebarsTemplates['houston/scheduler/tickets/sequence']


prevent keypress event propagation in properties editor

--- src/scripts/editor/PropertiesEditor.coffee
+++ src/scripts/editor/PropertiesEditor.coffee
@@ -26,6 +26,9 @@

       @selectionManager.onSelect.add(@onSelect)

+      # Stop event propagation to no play by accident.
+      @$el.keypress (e) -> e.stopPropagation()
+
     onKeyAdded: () =>
       console.log "on key added"
       @keyAdded.dispatch()


Change sim link class name

Change sim link class name

--- src/scripts/modules/media/body/body.coffee
+++ src/scripts/modules/media/body/body.coffee
@@ -45,7 +45,7 @@
         .solution > .ui-toggle-wrapper > .ui-toggle': 'toggleSolution'
       'keydown .media-body': 'checkKeySequence'
       'keyup .media-body': 'resetKeySequence'
-      'click .os-simulation': 'simLink'
+      'click .os-interactive': 'simLink'

     initialize: () ->
       super()
@@ -73,7 +73,7 @@
       try
         if @model.get('loaded') and @model.asPage()?.get('loaded') and @model.asPage()?.get('active')

-          if $temp.find('.os-simulation')
+          if $temp.find('.os-interactive .os-embed')
             @model.set('sims', true)

           # Remove the module title and abstract TODO: check if it is still necessary


- adding mouse click drag interaction - fixed an issue related to the nodes freezing on double click

--- bookmarks/js/source.coffee
+++ bookmarks/js/source.coffee
@@ -1,26 +1,45 @@
 $(document).ready ->

+  nodes = []
+  links = []
+  sx = null
+  sy = null
+  cx = 0
+  cy = 0
+
   class Node
-
     @ids = 0

-    constructor: () ->
+    constructor: ->
       @id = Node.ids++
       @x = 0
       @y = 0
       @parent = null
       @text = ''
       @children = []
+      nodes.push @

     setParent: (parent) ->
       if @parent
         @parent.remove @
       @parent = parent
+      parent.children.push @
+      @x = parent.x - Math.random() * 20 + 10
+      @y = parent.y - Math.random() * 20 + 10
+      links.push new Link parent, @
       @

     remove: (child) ->
       @children.splice @children.indexOf child, 1
       child
+
+  class Link
+    @ids = 0
+
+    constructor: (source, target) ->
+      @id = Link.ids++
+      @source = source
+      @target = target

   randomColor = ->
     '#'+Math.floor(Math.random()*16777215).toString(16);
@@ -39,35 +58,63 @@
     svg.attr 'width', w
     svg.attr 'height', h

+  $(window).on 'mousedown', (e) ->
+    sx = e.clientX
+    sy = e.clientY
+
+  $(window).on 'mouseup', (e) ->
+    sx = null
+    sy = null
+
+  $(window).on 'mousemove', (e) ->
+    if sx? and sy?
+      dx = e.clientX - sx
+      dy = e.clientY - sy
+      sx = e.clientX
+      sy = e.clientY
+      cx += dx
+      cy += dy
+
   head = new Node()
-  nodes = [head]
-
-  child = new Node()
-  child.setParent head
-  nodes.push child
-
   head.x = w / 2
   head.y = h / 2

-  child.x = head.x - 1
-  child.y = head.y - 1
+  link = null
+  node = null

   circles = null
   rects = null
   texts = null

-  # link = svg.selectAll('.link')
-  #   .data(links)
-  #   .enter().append('line')
+  new Node().setParent head
+  new Node().setParent head
+  new Node().setParent head
+  new Node().setParent head
+  new Node().setParent head
+  new Node().setParent head
+  new Node().setParent head

-  node = svg.selectAll('.node')
-    .data(nodes)
-    .enter().append('circle').classed('node', true)
+  refresh = ->
+    svg.selectAll('.link')
+      .data(links, (d) -> d.id)
+      .enter()
+        .insert('line', ':first-child')
+        .classed('link', true)

-  console.log node
+    svg.selectAll('.node')
+      .data(nodes, (d) -> d.id)
+      .enter()
+        .append('circle')
+        .classed('node', true)
+        .on('dblclick', (nodeClicked) ->
+          new Node().setParent nodeClicked
+          refresh()
+        )
+  refresh()

   update = ->
-    MAX = 100
+    MAX = 200
+    PUSH = 5

     for n1, i in nodes
       sum = V 0, 0
@@ -75,39 +122,30 @@
       for n2, j in nodes
         continue if n1 == n2
         outer = V n2.x, n2.y
-        sum = sum.add inner.sub outer
-
-      if sum.x isnt 0 or sum.y isnt 0
-        mag = sum.mag()
+        dif = inner.sub outer
+        mag = dif.mag()
         norm = 1.0 - Math.min(mag, MAX) / MAX
-        dir = sum.unit()
-        inner = inner.add dir.mult norm * 5
-        n1.x = inner.x
-        n1.y = inner.y
-
-      # .on('dblclick', (nodeClicked) ->
-      #   newNode = new Node
-      #   newNode.setParent nodeClicked
-      #   nodes.push newNode
-      #   update()
-      # )
+        dir = dif.unit()
+        sum = sum.add dir.mult norm * PUSH
+      inner = inner.add sum
+      n1.x = inner.x
+      n1.y = inner.y

   render = ->
-    # links
-    #   .attr('x1', (d) -> d.source.x)
-    #   .attr('y1', (d) -> d.source.y)
-    #   .attr('x2', (d) -> d.target.x)
-    #   .attr('y2', (d) -> d.target.y)
+    svg.selectAll('.link')
+      .attr('x1', (d) -> d.source.x + cx)
+      .attr('y1', (d) -> d.source.y + cy)
+      .attr('x2', (d) -> d.target.x + cx)
+      .attr('y2', (d) -> d.target.y + cy)

-    node
-      .attr('cx', (d) -> d.x)
-      .attr('cy', (d) -> d.y)
+    svg.selectAll('.node')
+      .attr('cx', (d) -> d.x + cx)
+      .attr('cy', (d) -> d.y + cy)
       .attr('r', 40)
-

   setInterval ->
     update()
-  , 1
+  , 10

   window.requestAnimationFrame = do ->
     window.requestAnimationFrame ||
@@ -122,7 +160,6 @@
     render()
     requestAnimationFrame recursiveAnim
   )()
-

 # rects = nodes


Show login popup to download replay

Show login popup to download replay

--- resources/assets/lib/play-detail-menu.coffee
+++ resources/assets/lib/play-detail-menu.coffee
@@ -126,7 +126,7 @@
         className: 'simple-menu simple-menu--play-detail-menu'
         if @props.score.replay
           a
-            className: 'simple-menu__item'
+            className: 'simple-menu__item js-login-required--click'
             href: laroute.route 'users.replay',
                     beatmap: @props.score.beatmap.id
                     mode: @props.score.beatmap.mode


follow default keymap to rename one

follow default keymap to rename one

--- keymaps/vim-mode-plus.cson
+++ keymaps/vim-mode-plus.cson
@@ -139,7 +139,7 @@
   'g -': 'vim-mode-plus:dash-case'
   'g space': 'vim-mode-plus:compact-spaces'
   'g |': 'vim-mode-plus:trim-string'
-  'g cmd-d': 'vim-mode-plus:add-selection'
+  'g cmd-d': 'vim-mode-plus:select-occurrence'
   # 'g _': 'vim-mode-plus:snake-case'
   # 'g r':   'vim-mode-plus:reverse'
   # 'ctrl-s': 'vim-mode-plus:transform-string-by-select-list'
@@ -276,12 +276,12 @@
   '%': 'vim-mode-plus:move-to-line-by-percent'

 'atom-text-editor.vim-mode-plus.with-range-marker:not(.insert-mode)':
-  'cmd-d': 'vim-mode-plus:add-selection'
+  'cmd-d': 'vim-mode-plus:select-occurrence-in-a-range-marker'
   'ctrl-cmd-g': 'vim-mode-plus:a-range-marker'

 'atom-text-editor.vim-mode-plus.visual-mode.selection-only-mode':
   'ctrl-cmd-c': 'vim-mode-plus:change-occurrence'
-  'cmd-d': 'vim-mode-plus:add-selection'
+  'cmd-d': 'vim-mode-plus:select-occurrence'

 # normal
 # -------------------------


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/engine_selector.coffee
+++ src/engine_selector.coffee
@@ -59,7 +59,7 @@
   @param {String} selector
   @returns {Array} An array of the objects found
   ###
-  each:
+  each: (selector)

   find: (selector) ->
     results = []


Get editor working again

Get editor working again

--- editor/server/index.coffee
+++ editor/server/index.coffee
@@ -1,22 +1,31 @@
 # Dependencies
 global.DebugMode = true
-Log = require('../../app/log')('Editor', 'white')
-database = require('../../app/database')
-express = require('express')
+
+folder = '../../app/'
+
+express  = require 'express'
+config   = require folder + 'config'
+Log      = require folder + 'utils/log'
+database = require folder + 'controllers/database'
+connect  = require folder + 'controllers/connect'
+
+log = Log 'Editor', 'white'

 # Config
 PORT = 8001
+config.use 'testing'

 # Create a new express web server
 app = express()

 # Connect to database
-database.connect()
+connect.init()

 # Serve up static files in the client folder
 app.configure ->
   app.use express.static(__dirname + '/../client')
-  app.use express.bodyParser()
+  app.use express.urlencoded()
+  app.use express.json()

 app.get '/read/all', (req, res) ->
   database.user.all()


adding default time zone

adding default time zone

--- src/google-calendar.coffee
+++ src/google-calendar.coffee
@@ -106,10 +106,10 @@
             format = 'M/D h:mm'

           start = moment(start)
-          start = start.tz(timeZone || item.start.timeZone) if item.start.timeZone
+          start = start.tz(timeZone || item.start.timeZone || 'America/New_York')

           end = moment(end)
-          end = end.tz(timeZone || item.end.timeZone) if item.end.timeZone
+          end = end.tz(timeZone || item.end.timeZone || 'America/New_York')

           entry =  "[#{start.format(format)}-#{end.format(format)}]  #{item.summary}\n"
           # entry += "[#{start.toString()}-#{end.toString()}]\n"


Ensure hash element exists

Ensure hash element exists

--- src/scripts/modules/media/body/body.coffee
+++ src/scripts/modules/media/body/body.coffee
@@ -149,7 +149,7 @@
           window.location.hash = hash

       $target = $(window.location.hash)
-      if $target.prop('tagName').toLowerCase() is 'iframe'
+      if $target.prop('tagName')?.toLowerCase() is 'iframe'
         $target.on('load', jumpToHash)
       else
         jumpToHash()


removed debug statements

removed debug statements

--- src/scripts/modules/media/body/body.coffee
+++ src/scripts/modules/media/body/body.coffee
@@ -51,7 +51,6 @@
       if @owner.get('loaded') and @model?.get('loaded') and @model?.get('active')
         # Converts a TERP link to an OST-hosted iframe
         $temp.find('a[href*="#terp-"]').each () ->
-          debugger; alert 'hi'
           terpCode = $(this).attr('href').match(/#terp\-(.*)/)[1]
           $(this).replaceWith("<iframe class='terp'
                                        src='https://openstaxtutor.org/terp/#{terpCode}/quiz_start'


Remove empty catch

Remove empty catch

--- src/scripts/modules/media/body/body.coffee
+++ src/scripts/modules/media/body/body.coffee
@@ -130,7 +130,6 @@
                 if $el.text() is '[link]' and tag
                   tag = tag.charAt(0).toUpperCase() + tag.substring(1)
                   $el.text("#{tag}") if tag isnt 'undefined'
-              catch

           # Convert links to maintain context in a book, if appropriate
           if @model.isBook()


v1.0.2

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -160,6 +160,6 @@
     grunt.loadNpmTasks 'grunt-shell'

     grunt.registerTask 'build',     [ 'clean', 'stylus', 'salt', 'pepper', 'bower_concat', 'coffee', 'shell:kill', 'shell:build', 'shell:start' ]
-    grunt.registerTask 'release',   [ 'clean', 'stylus', 'salt', 'pepper', 'bower_concat', 'coffee', 'shell:release', 'githubAsset' ]
+    grunt.registerTask 'release',   [ 'clean', 'stylus', 'salt', 'pepper', 'bower_concat', 'coffee', 'shell:build', 'shell:release', 'githubAsset' ]
     grunt.registerTask 'test',      [ 'clean', 'stylus', 'salt', 'pepper', 'bower_concat', 'coffee', 'shell:kill', 'shell:test' ]
     grunt.registerTask 'default',   [ 'test' ]


Add the banner to the compiled css files.

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -5,10 +5,12 @@
     pkg: grunt.file.readJSON('package.json')
     meta:
       banner: """
-              // <%= pkg.name %> v<%= pkg.version %>
-              //
-              // Copyright (c) 2013 <%= pkg.author %>
-              // Distributed under the <%= pkg.license %> license
+              /**
+               * <%= pkg.name %> v<%= pkg.version %>
+               *
+               * Copyright (c) 2013 <%= pkg.author %>
+               * Distributed under the <%= pkg.license %> license
+               */

               """
     coffee:
@@ -34,6 +36,8 @@
         files:
           'js/ruban.min.js': 'js/ruban.js'
     cssmin:
+      options:
+        banner: '<%= meta.banner %>'
       dist:
         files:
           'css/ruban.min.css': 'css/ruban.css'


Exclude test files when copying

Exclude test files when copying

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -32,6 +32,7 @@
     IMG_FILES:      '**/*.{png,gif,jpg,jpeg}'
     JS_FILES:       '**/*.js'
     LESS_FILES:     '**/*.less'
+    TEST_FILES:     '**/test/**'

     copy:
@@ -40,7 +41,7 @@
         files:      [
           expand:   true
           cwd:      '<%= CLIENT_DIR %>'
-          src:      '<%= ALL_FILES %>'
+          src:      [ '<%= ALL_FILES %>', '!<%= TEST_FILES %>' ]
           dest:     '<%= BUILD_DIR %>'
         ,
           src:      '<%= CLIENT_DIR %>/index.html'
@@ -57,7 +58,7 @@
         files:      [
           expand:   true
           cwd:      '<%= CLIENT_DIR %>'
-          src:      '<%= JS_FILES %>'
+          src:      [ '<%= JS_FILES %>', '!<%= TEST_FILES %>' ]
           dest:     '<%= BUILD_DIR %>'
         ]

@@ -66,7 +67,7 @@
         files:      [
           expand:   true
           cwd:      '<%= CLIENT_DIR %>'
-          src:      '<%= HTML_FILES %>'
+          src:      [ '<%= HTML_FILES %>', '!<%= TEST_FILES %>' ]
           dest:     '<%= BUILD_DIR %>'
         ,
           src:      '<%= CLIENT_DIR %>/index.html'


add make-release task

add make-release task

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -27,7 +27,7 @@

   grunt.registerTask('dist', ['clean', 'lodash', 'browserify', 'uglify', 'stylus', 'concat'])
   grunt.registerTask('release', ['dist', 'examples', 'copy', 'compress'])
-
+  grunt.registerTask('make-release', ['grunt-release', 'release', 'github-release'])
   grunt.registerTask('server', ['connect:server:keepalive'])

   grunt.registerTask('test', ['test:unit'])


Better commit message for gh-pages task

Better commit message for gh-pages task

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -152,6 +152,7 @@
       options:
         base: 'dist'
         clone: 'gh-pages'
+        message: 'Updating web version to <%= pkg.version %>'
       src: '**/*'


Fixes inline CSS in html not compressed bug

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -108,6 +108,12 @@
         files:
           "<%= core.dist %>/css/a.css": ["<%= core.dist %>/css/*.css"]

+      html:
+        expand: true
+        cwd: "<%= core.dist %>"
+        src: "**/*.html"
+        dest: "<%= core.dist %>"
+
     shell:
       options:
         stdout: true


get rid of some old wonky bullshit

get rid of some old wonky bullshit

--- core/KDObject.coffee
+++ core/KDObject.coffee
@@ -15,12 +15,6 @@
     @listeningTo = []
     super
     @once 'ready', => @readyState = READY
-
-  if KD.MODE is 'development'
-    interfere:(o)-> o
-    o:(o)-> @interfere o
-  else
-    o:(o)->o

   bound: Bongo.bound


Test on Safari and IE11 too

Test on Safari and IE11 too

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -261,13 +261,21 @@
         options:
           urls: ['http://127.0.0.1:9999/spec/runner.html']
           browsers: [
-            browserName: 'chrome'
+            browserName: 'googlechrome'
             version: '34'
+          ,
+            browserName: 'safari'
+            platform: 'OS X 10.8'
+            version: '6'
+          ,
+            browserName: 'internet explorer'
+            platform: 'Windows 8.1',
+            version: '11'
           ]
           build: process.env.TRAVIS_JOB_ID
           testname: 'NoFlo UI browser tests'
           tunnelTimeout: 5
-          concurrency: 3
+          concurrency: 1
           detailedError: true

   # Grunt plugins used for building


updated Gruntfile

updated Gruntfile

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -11,7 +11,7 @@

     concat:
       typesc:
-        src: ['src/qflick/*.js']
+        src: ['src/qflick/**/*.js']
         dest: 'public/qflick.js'

       lib:


Added Jasmine

Added Jasmine

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -22,9 +22,11 @@
       output      : 'source/quo.output.coffee'
       query       : 'source/quo.query.coffee'
       style       : 'source/quo.style.coffee'
+      spec        : 'spec/*.coffee'

     concat:
       core        : files: '<%=meta.build%>/<%=meta.file%>.coffee': '<%= source.core %>'
+      spec        : files: '<%=meta.build%>/<%=meta.file%>.spec.coffee': '<%= source.spec %>'

     coffee:
       core        : files: '<%= meta.build %>/<%= meta.file %>.js': '<%=meta.build%>/<%=meta.file%>.coffee'
@@ -36,6 +38,7 @@
       output      : files: '<%= meta.build %>/<%= meta.file %>.output.js': '<%=source.output%>'
       query       : files: '<%= meta.build %>/<%= meta.file %>.query.js': '<%=source.query%>'
       style       : files: '<%= meta.build %>/<%= meta.file %>.style.js': '<%=source.style%>'
+      spec        : files: '<%= meta.build %>/<%= meta.file %>.spec.js': '<%=meta.build%>/<%=meta.file%>.spec.coffee'

     uglify:
       options: mangle: false, banner: "<%= meta.banner %>"#, report: "gzip"
@@ -50,10 +53,20 @@
       style       : files: '<%= meta.bower %>/<%= meta.file %>.style.js': '<%= meta.build %>/<%= meta.file %>.style.js'

+    jasmine:
+      pivotal:
+        src: [
+          '<%=meta.bower%>/<%= meta.file %>.js']
+        options:
+          # vendor: 'spec/components/jquery/jquery.min.js'
+          specs: '<%=meta.build%>/<%=meta.file%>.spec.js',
+          # outfile: 'spec.html'
+          # keepRunner: true
+
     watch:
       core:
         files: ['<%= source.core %>']
-        tasks: ["concat:core", "coffee:core", "uglify:core"]
+        tasks: ["concat:core", "coffee:core", "uglify:core", "jasmine"]
       ajax:
         files: ['<%= source.ajax %>']
         tasks: ["coffee:ajax", "uglify:ajax"]
@@ -78,11 +91,17 @@
       style:
         files: ['<%= source.style %>']
         tasks: ["coffee:style", "uglify:style"]
+      spec:
+        files: ['<%= source.spec %>']
+        tasks: ["concat:spec", "coffee:spec", "jasmine"]
+
+

   grunt.loadNpmTasks "grunt-contrib-concat"
   grunt.loadNpmTasks "grunt-contrib-coffee"
   grunt.loadNpmTasks "grunt-contrib-uglify"
+  grunt.loadNpmTasks "grunt-contrib-jasmine"
   grunt.loadNpmTasks "grunt-contrib-watch"

   # Default task.
-  grunt.registerTask 'default', ["concat", "coffee", "uglify"]
+  grunt.registerTask 'default', ["concat", "coffee", "uglify", "jasmine"]


Disabled imagemin temporarily from Grunt tasks (it blocks other tasks from running after it is executed)

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -3,7 +3,7 @@
         dist: 'public/dist'
         src: 'public/src'
         pkg: grunt.file.readJSON 'package.json'
-        clean: ['<%= dist %>/css', '<%= dist %>/js', '<%= dist %>/img']
+        clean: ['<%= dist %>/css', '<%= dist %>/js']
         coffeelint:
             app: ['Enactus.coffee']
             grunt: ['Gruntfile.coffee']
@@ -78,7 +78,7 @@
     grunt.loadNpmTasks 'grunt-contrib-copy'

     grunt.registerTask 'default', [
-        'clean', 'copy', 'imagemin', 'uglify', 'compass:dist'
+        'clean', 'uglify', 'compass:dist'
     ]
     grunt.registerTask 'dev', ['default', 'test', 'compass:dev']
     grunt.registerTask 'test', ['coffeelint']


Revert "Compile nu.nl javascript and css"

This reverts commit 6d74cff23935349a09a1e0a3d5b98cffd127a184.

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -69,14 +69,10 @@
           'build/js/loader/loader_bookmarklet.js'
         ]
         dest: 'build/factlink_loader_bookmarklet.js'
-      publisher:
-        src:  'build/js/publisher_poc/nu.js'
-        dest: 'build/nu.js'
     less:
       build:
         files:
           'build/css/basic.css': 'app/css/basic.less'
-          'build/css/publisher_poc/nu.nl.css': 'app/css/publisher_poc/nu.nl.less'
     cssmin:
       build:
         options:


Lints all coffee files inside src

Lints all coffee files inside src

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -37,7 +37,7 @@
       tasks: ['default']

     coffeelint:
-      files: ['Gruntfile.coffee', 'src/*.coffee']
+      files: ['Gruntfile.coffee', 'src/**/*.coffee']
       options:
         max_line_length:
           level: 'ignore'


merging

merging

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -32,7 +32,7 @@
           livereload: true
     hogan:
       main:
-        src: 'src/templates/templates.js'
+        dest: 'src/templates/templates.js'
         src: 'src/templates/**/*.mustache'
       lib:
         dest: 'lib/templates/templates.js'


minify fontawesome css

minify fontawesome css

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -122,6 +122,9 @@
       theme:
         src: themeFile + ".css"
         dest: themeFile + ".min.css"
+      fontawesome:
+        src: "compiled/customfont/fontawesome.css"
+        dest:"compiled/customfont/fontawesome.min.css"

     # config for grunt-contrib-compress
     compress: readCoffee "build/compress.coffee"


Add "default" task for grunt

Add "default" task for grunt

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -11,13 +11,13 @@
         DS      = path.sep
         bin     = PATHS[os.type()]
         proc    = null
-
+
         try
             if bin?
                 proc = spawn bin, ["--debug", "src" + DS], {detached: true}
             else
                 grunt.fail.fatal "Unsupported environment (#{os.type()})"
-
+
         catch e
             console.error e

@@ -39,3 +39,4 @@
     # regist Tasks
     grunt.registerTask "debug", debug
     grunt.registerTask "release", ["nodewebkit"]
+    grunt.registerTask "default", ["debug"]


Ignoring max line length until the HTML strings move to files

The CoffeeScript multiline strings preserve whitespace which is causing issues (e.g. spaces in long URLs) when keeping to the max line length limit.

This limit will be enforced again when there are no more lengthy HTML strings in the code, i.e. they are all extracted to files of their own.

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -32,6 +32,9 @@
           port: 8000

     coffeelint:
+      options:
+        max_line_length:
+          "level": "ignore"
       files: [
         "src/app/**/*.coffee"
         "src/coffee/**/*.coffee"


abstract counter-like moves

abstract counter-like moves

--- data/bw/moves.coffee
+++ data/bw/moves.coffee
@@ -260,6 +260,28 @@
     "#{pokemon.name}'s #{stat} won't go any higher!"
   else if !wasBoosted && amount < 0
     "#{pokemon.name}'s #{stat} won't go any lower!"
+
+makeCounterMove = (name, multiplier, applies) ->
+  extendMove name, ->
+    @getTargets = (battle, user) ->
+      # Return the last pokemon who hit this one, if it's alive.
+      pokemon = user.lastHitBy?.pokemon
+      return [ pokemon ]  if pokemon? && !pokemon.isFainted()
+
+      # Return a random target (or none).
+      pokemon = battle.getOpponentPokemon(battle.getOwner(user).id)
+      pokemon = pokemon.filter((p) -> !p.isFainted())
+      if pokemon.length == 0
+        []
+      else
+        [ battle.rng.choice(pokemon) ]
+
+    @use = (battle, user, target) ->
+      hit = user.lastHitBy
+      if hit? && applies(hit.move) && hit.turn == battle.turn
+        target.damage(multiplier * hit.damage)
+      else
+        @fail(battle)

 extendWithDrain 'absorb'
 extendWithSecondaryBoost 'acid', 'target', .1, specialDefense: -1
@@ -557,26 +579,10 @@
     else
       @fail(battle)

-extendMove 'counter', ->
-  @getTargets = (battle, user) ->
-    # Return the last pokemon who hit this one, if it's alive.
-    pokemon = user.lastHitBy?.pokemon
-    return [ pokemon ]  if pokemon? && !pokemon.isFainted()
-
-    # Return a random target (or none).
-    pokemon = battle.getOpponentPokemon(battle.getOwner(user).id)
-    pokemon = pokemon.filter((p) -> !p.isFainted())
-    if pokemon.length == 0
-      []
-    else
-      [ battle.rng.choice(pokemon) ]
-
-  @use = (battle, user, target) ->
-    hit = user.lastHitBy
-    if hit? && hit.move.isPhysical() && hit.turn == battle.turn
-      target.damage(2 * hit.damage)
-    else
-      @fail(battle)
+
+makeCounterMove('counter', 2, (move) -> move.isPhysical())
+makeCounterMove('mirror-coat', 2, (move) -> move.isSpecial())
+makeCounterMove('metal-burst', 1.5, (move) -> move.isPhysical() || move.isSpecial())

 extendMove 'crush-grip', ->
   @basePower = (battle, user, target) ->


Fix slot updateRange in editing mode

Fix slot updateRange in editing mode

--- app/assets/volume/slot.coffee
+++ app/assets/volume/slot.coffee
@@ -48,8 +48,8 @@
       l = Infinity
       u = -Infinity
       for t in $scope.tracks.concat(records, $scope.consents)
-        l = t.segment.l if isFinite(t.segment.l) && t.segment.l < l
-        u = t.segment.u if isFinite(t.segment.u) && t.segment.u > u
+        l = t.segment.l if isFinite(t.segment?.l) && t.segment.l < l
+        u = t.segment.u if isFinite(t.segment?.u) && t.segment.u > u
       fullRange.l = finite(slot.segment.l, l, 0)
       fullRange.u = finite(slot.segment.u, u, 0)
       return


clean up comments and remove console.logs

--- app/coffee/PackManager.coffee
+++ app/coffee/PackManager.coffee
@@ -21,17 +21,20 @@
 	#   {
 	#     "doc_id" : 549dae9e0a2a615c0c7f0c98,
 	#     "project_id" : 549dae9c0a2a615c0c7f0c8c,
-	#     "pack" : [ D1, D2, D3, ....],
+	#     "pack" : [ U1, U2, U3, ...., UN],
 	#     "meta" : {	"user_id" : 52933..., "start_ts" : 1422310693931,	"end_ts" : 1422310693931 },
 	#     "v" : 17082
 	#   }
 	#
-	#  where D1, D2, D3, .... are single updates stripped of their
+	#  where U1, U2, U3, .... are single updates stripped of their
 	#  doc_id and project_id fields (which are the same for all the
-	#  updates in the pack).  The meta and v fields of the pack itself
-	#  are those of the first entry in the pack D1 (this makes it
-	#  possible to treat packs and single updates in the same way).
-
+	#  updates in the pack).
+	#
+	#  The pack itself has v and meta fields, this makes it possible to
+	#  treat packs and single updates in the same way.
+	#
+	#  The v field of the pack itself is from the first entry U1
+	#  The meta.end_ts field of the pack itself is from the last entry UN.

 	findDocResults: (collection, query, limit, callback) ->
 		# query - the mongo query selector, includes both the doc_id/project_id and
@@ -113,7 +116,7 @@

 	findProjectResults: (collection, query, limit, callback) ->
 		# query - the mongo query selector, includes both the doc_id/project_id and
-		# the range on v or meta.end_ts
+		# the range on meta.end_ts
 		# limit - the mongo limit, we need to apply it after unpacking any
 		# packs

@@ -146,13 +149,9 @@
 			# results up to this, and then all the changes at that time
 			# (without imposing a limit) and any overlapping packs
 			cutoff = if unpackedSet.length then unpackedSet[unpackedSet.length-1].meta.end_ts else null
-			#console.log 'before is', before
-			#console.log 'cutoff is', cutoff
-			#console.log 'limit  is', limit

 			filterFn = (item) ->
 				ts = item?.meta?.end_ts
-				#console.log 'checking', ts, before, cutoff
 				return false if before? && ts >= before
 				return false if cutoff? && ts < cutoff
 				return true
@@ -166,7 +165,6 @@
 				if x > y then 1 else if x < y then -1 else 0

 			updates = PackManager._filterAndLimit(updates, unpackedSet, filterFn, limit)
-			#console.log 'initial updates are', updates

 			# get all elements on the lower bound (cutoff)
 			tailQuery = _.clone(query)
@@ -174,8 +172,6 @@
 			tail = collection
 				.find(tailQuery, projection)
 				.sort(sort)
-
-			#console.log 'tailQuery is', tailQuery

 			# now find any packs that overlap with the time window
 			overlapQuery = _.clone(query)
@@ -195,8 +191,6 @@
 				.find(overlapQuery, projection)
 				.sort(sort)

-			#console.log 'overlapQuery is', overlapQuery
-
 			# we don't specify a limit here, as there could be any number of overlaps
 			# NB. need to catch items in original query and followup query for duplicates

@@ -206,7 +200,6 @@
 				# need all the updates at the final time to avoid breaking
 				# the changeset into parts
 				updates = PackManager._filterAndLimit(updates, extraSet, filterFn, null)
-				#console.log 'extra updates after filterandlimit', updates
 			tail.toArray (err, result2) ->
 				if err?
 					return callback err, updates.sort timeOrder
@@ -249,7 +242,6 @@
 		seen = {}
 		newResults = newResults.filter (item) ->
 			key = item.doc_id + ' ' + item.v
-			#console.log 'key is', key
 			if seen[key]
 				return false
 			else


Add the secondary key fields.

Add the secondary key fields.

--- app/javascripts/routes.coffee
+++ app/javascripts/routes.coffee
@@ -96,9 +96,10 @@
               charting: (Subject, project, collection) ->
                 # The selection criterion is the project and collection.
                 condition = {project: project, collection: collection}
-                # The id is always fetched. In addition, the number and encounters
-                # are fetched. No other field is fetched.
-                fields = ['number', 'encounters']
+                # The id is always fetched. In addition, the
+                # project/collection/number secondary key and
+                # encounters are fetched. No other field is fetched.
+                fields = ['project', 'collection', 'number', 'encounters']
                 # Delegate to the resource.
                 Subject.query(condition, fields)
             views:


Sidebar: navigation items dnd commented out until reimplemented horizontally

--- client/Main/navigation/navigationlist.coffee
+++ client/Main/navigation/navigationlist.coffee
@@ -5,73 +5,73 @@

     @viewWidth = 55

-    @on 'ItemWasAdded', (view)=>
+    # @on 'ItemWasAdded', (view)=>

-      view.once 'viewAppended', =>
+    #   view.once 'viewAppended', =>

-        if view.data.type is 'persistent'
-          view.options.draggable = axis : 'x'
+    #     if view.data.type is 'persistent'
+    #       view.options.draggable = axis : 'x'

-        view._index ?= @getItemIndex view
-        view.setX view._index * @viewWidth
-        @_width = @viewWidth * (@items.length + 1)
-        @setWidth @_width - @viewWidth
-        KD.utils.defer -> view.unsetClass 'no-anim'
+    #     view._index ?= @getItemIndex view
+    #     view.setX view._index * @viewWidth
+    #     @_width = @viewWidth * (@items.length + 1)
+    #     @setWidth @_width - @viewWidth
+    #     KD.utils.defer -> view.unsetClass 'no-anim'

-      lastChange = 0
+    #   lastChange = 0

-      view.on 'DragStarted', =>
-        @_dragStarted = yes
+    #   view.on 'DragStarted', =>
+    #     @_dragStarted = yes

-      view.on 'DragInAction', (x, y)=>
+    #   view.on 'DragInAction', (x, y)=>

-        if @_dragStarted and y > 15 and view.data.type isnt 'persistent'
-          dock = KD.singletons.dock.mainView
-          dock.setClass 'remove-app-state'
-          delete @_dragStarted
+    #     if @_dragStarted and y > 15 and view.data.type isnt 'persistent'
+    #       dock = KD.singletons.dock.mainView
+    #       dock.setClass 'remove-app-state'
+    #       delete @_dragStarted

-        return  if x + view._x > @_width or x + view._x < 0
+    #     return  if x + view._x > @_width or x + view._x < 0

-        if view.data.type isnt 'persistent' and y > 25
-        then view.setClass 'remove'
-        else view.unsetClass 'remove'
+    #     if view.data.type isnt 'persistent' and y > 25
+    #     then view.setClass 'remove'
+    #     else view.unsetClass 'remove'

-        if x > @viewWidth
-          current = Math.floor x / @viewWidth
-        else if x < -@viewWidth
-          current = Math.ceil  x / @viewWidth
-        else
-          current = 0
+    #     if x > @viewWidth
+    #       current = Math.floor x / @viewWidth
+    #     else if x < -@viewWidth
+    #       current = Math.ceil  x / @viewWidth
+    #     else
+    #       current = 0

-        if current > lastChange
-          @moveItemToIndex view, view._index+1
-          lastChange = current
-        else if current < lastChange
-          @moveItemToIndex view, view._index-1
-          lastChange = current
+    #     if current > lastChange
+    #       @moveItemToIndex view, view._index+1
+    #       lastChange = current
+    #     else if current < lastChange
+    #       @moveItemToIndex view, view._index-1
+    #       lastChange = current

-      view.on 'DragFinished', =>
+    #   view.on 'DragFinished', =>

-        view.unsetClass 'no-anim remove'
+    #     view.unsetClass 'no-anim remove'

-        if view.data.type isnt 'persistent' and view.getRelativeY() > 25
+    #     if view.data.type isnt 'persistent' and view.getRelativeY() > 25

-          view.setClass 'explode'
-          KD.utils.wait 500, => @removeApp view
+    #       view.setClass 'explode'
+    #       KD.utils.wait 500, => @removeApp view

-        else
+    #     else

-          KD.utils.wait 200, -> view.unsetClass 'on-top'
-          view.setX view._index * @viewWidth
-          view.setY 0
+    #       KD.utils.wait 200, -> view.unsetClass 'on-top'
+    #       view.setX view._index * @viewWidth
+    #       view.setY 0

-          KD.singletons.dock.saveItemOrders @items
+    #       KD.singletons.dock.saveItemOrders @items

-        lastChange  = 0
+    #     lastChange  = 0

-        KD.utils.wait 200, =>
-          delete @_dragStarted
-          KD.singletons.dock.mainView.unsetClass 'remove-app-state'
+    #     KD.utils.wait 200, =>
+    #       delete @_dragStarted
+    #       KD.singletons.dock.mainView.unsetClass 'remove-app-state'

   removeApp:(view)->


client: fix calculation of trial period

client: fix calculation of trial period

--- client/app/lib/redux/services/payment.coffee
+++ client/app/lib/redux/services/payment.coffee
@@ -37,7 +37,7 @@

 exports.createSubscription = createSubscription = pickData (params = {}) ->
   if not params.trialEnd
-    params.trialEnd = (new Date()).getTime() + (30 * 24 * 60 * 60 * 1000)
+    params.trialEnd = Math.round ((new Date()).getTime() + (30 * 24 * 60 * 60 * 1000)) / 1000
   client.post Endpoints.SubscriptionCreate, params

 exports.deleteSubscription = deleteSubscription = pickData (params = {}) ->


Passed delegate to ApplicationTabHandleHolder instance.

--- client/ide/workspace/workspacetabview.coffee
+++ client/ide/workspace/workspacetabview.coffee
@@ -13,6 +13,7 @@
   createTabHolderView: ->
     @holderView     = new ApplicationTabHandleHolder
       addPlusHandle : @getOption "addPlusHandle"
+      delegate      : this

   createTabView: ->
     @tabView = new ApplicationTabView


Build: fixed relative path

Build: fixed relative path

--- client/landing/gulptasks/task.sprites.coffee
+++ client/landing/gulptasks/task.sprites.coffee
@@ -2,14 +2,16 @@
 spritesmith = require 'gulp.spritesmith'
 argv        = require('minimist') process.argv
 site        = argv.site or 'landing'
+base        = "#{__dirname}/.."

 { BUILD_PATH } = require './helper.constants'
+{ log } = require './helper.logger'

 module.exports = (pixelRatio = 1) ->

   suffix = if pixelRatio > 1 then "__#{pixelRatio}x" else ''

-  stream = gulp.src "./../#{site}/sprites@#{pixelRatio}x/**/*.png"
+  stream = gulp.src "#{base}/#{site}/sprites@#{pixelRatio}x/**/*.png"
     .pipe spritesmith
       imgName   : "sprite@#{pixelRatio}x.png"
       cssName   : "sprite@#{pixelRatio}x.styl"


comment out synctex for moment

comment out synctex for moment

--- services/clsi/app/coffee/DockerRunner.coffee
+++ services/clsi/app/coffee/DockerRunner.coffee
@@ -108,8 +108,8 @@
 	_getContainerOptions: (command, image, volumes, timeout, environment) ->
 		timeoutInSeconds = timeout / 1000

-		if Settings.path?.synctexBinHostPath?
-			volumes[Settings.path.synctexBinHostPath] = "/opt/synctex:ro"
+		# if Settings.path?.synctexBinHostPath?
+		# 	volumes[Settings.path.synctexBinHostPath] = "/opt/synctex:ro"

 		dockerVolumes = {}
 		for hostVol, dockerVol of volumes


log user_id not user

log user_id not user

--- services/web/app/coffee/Features/User/UserController.coffee
+++ services/web/app/coffee/Features/User/UserController.coffee
@@ -30,7 +30,7 @@

 	updateUserSettings : (req, res)->
 		user_id = AuthenticationController.getLoggedInUserId(req)
-		logger.log user: user_id, "updating account settings"
+		logger.log user_id: user_id, "updating account settings"
 		User.findById user_id, (err, user)->
 			if err? or !user?
 				logger.err err:err, user_id:user_id, "problem updaing user settings"


Run benchmark against coffee code

Run benchmark against coffee code

--- benchmark/benchmark.coffee
+++ benchmark/benchmark.coffee
@@ -1,4 +1,4 @@
-{specificity} = require '../lib/main'
+{specificity} = require '../src/main'

 count = 0


changeActiveControl in EVENT is cancelled, if the control was already active, which may happen in code outside EVENT itself calling changeActiveControl

--- js/foundation/eventmanager/eventmanager.coffee
+++ js/foundation/eventmanager/eventmanager.coffee
@@ -775,6 +775,7 @@
   #
   # Sets the active control
   changeActiveControl: (_ctrl)->
+    return if _ctrl != null and @_views[@_listeners.active[0]] == _ctrl
     _prevActive = @delActiveControl(_ctrl)
     @addActiveControl(_ctrl, _prevActive) if _ctrl != null
   #


activity event binding fix

activity event binding fix

--- client/landing/app/Applications/Activity.kdapplication/AppController.coffee
+++ client/landing/app/Applications/Activity.kdapplication/AppController.coffee
@@ -64,7 +64,6 @@
     @lastTo    = null
     @lastFrom  = Date.now()
     @isLoading = no
-    @clearPopulateActivityBindings()
     @listController.resetList()
     @listController.removeAllItems()

@@ -100,6 +99,7 @@

       @resetAll()

+      @clearPopulateActivityBindings()
       if data.type in ["Public", "Followed"]
       then @setFeedFilter data.type
       else @setActivityFilter data.type
@@ -114,8 +114,9 @@

   clearPopulateActivityBindings:->
     eventSuffix = "#{@getFeedFilter()}_#{@getActivityFilter()}"
-    @off "activitiesFetched_#{eventSuffix}"
-    @off "cacheFetched_#{eventSuffix}"
+    @off "followingFeedFetched_#{eventSuffix}"
+    @off "publicFeedFetched_#{eventSuffix}"
+    # log "------------------ bindingsCleared", dateFormat(@lastFrom, "mmmm dS HH:mm:ss"), @_e

   populateActivity:(options = {}, callback=noop)->
     return  if @isLoading
@@ -165,7 +166,7 @@

         @fetchFollowingActivities options

-      # log "------------------    populateActivity", @getActivityFilter(), @lastFrom
+      # log "------------------ populateActivity", dateFormat(@lastFrom, "mmmm dS HH:mm:ss"), @_e

     if isReady
     then fetch()
@@ -193,16 +194,20 @@
       then @emit "activitiesCouldntBeFetched", err
       else @emit "followingFeedFetched_#{eventSuffix}", activities

+  setLastTimestamps:(from, to)->
+    # debugger
+    @lastTo   = to
+    @lastFrom = from
+
+
   # Store first & last cache activity timestamp.
   extractCacheTimeStamps: (cache)->
-    @lastTo   = (new Date cache.to).getTime()
-    @lastFrom = (new Date cache.from).getTime()
+    @setLastTimestamps (new Date cache.from).getTime(), (new Date cache.to).getTime()

   # Store first & last activity timestamp.
   extractTeasersTimeStamps:(teasers)->
     return unless teasers.first
-    @lastTo   = new Date(teasers.first.meta.createdAt).getTime()
-    @lastFrom = new Date(teasers.last.meta.createdAt).getTime()
+    @setLastTimestamps new Date(teasers.last.meta.createdAt).getTime(), new Date(teasers.first.meta.createdAt).getTime()

   sanitizeCache:(cache, callback)->
     activities = clearQuotes cache.activities


Catch compile errors in file tree too

Catch compile errors in file tree too

--- client/landing/app/MainApp/filetree/controllers/findertreecontroller.coffee
+++ client/landing/app/MainApp/filetree/controllers/findertreecontroller.coffee
@@ -389,14 +389,15 @@

     manifest = KodingAppsController.getManifestFromPath folder.path

-    kodingAppsController.compileApp manifest.name, =>
+    kodingAppsController.compileApp manifest.name, (err)=>
       folder.emit "fs.compile.finished"
-      @notify "App compiled!", "success"
-      callback?()
-      @utils.wait 500, =>
-        @refreshFolder nodeView, =>
-          @utils.wait =>
-            @selectNode @nodes["#{folder.path}/index.js"]
+      if not err
+        @notify "App compiled!", "success"
+        @utils.wait 500, =>
+          @refreshFolder nodeView, =>
+            @utils.wait =>
+              @selectNode @nodes["#{folder.path}/index.js"]
+      callback? err

   runApp:(nodeView, callback)->


Translate to Spanish System section

Translate to Spanish System section

--- def/es/settings.cson
+++ def/es/settings.cson
@@ -11,7 +11,7 @@
     }
     {
       _label: "System"
-      value: "System"
+      value: "Sistema"
     }
     {
       _label: "Keybindings"
@@ -38,7 +38,7 @@
   sectionHeadings: [
     {_label: "Core Settings", value: "Ajustes del Core"}
     {_label: "Editor Settings", value: "Ajustes del Editor"}
-    {_label: "System Settings", value: "System Settings"}
+    {_label: "System Settings", value: "Ajustes del Sistema"}
     {_label: "Keybindings", value: "Atajos"}
     {_label: "Installed Packages", value: "Paquetes Instalados"}
     {_label: "Choose a Theme", value: "Elige un Tema"}
@@ -373,23 +373,23 @@
       }
       {
         _id: 'system.windows.file-handler'
-        title: "Register as file handler"
-        desc: "Show Atom in the \"Open with\" application list for easy association with file types."
+        title: "Registrar como administrador de archivos"
+        desc: "Mostar a Atom en la lista de aplicaciones del menú \"Abrir con\" para fácil asociación con algunos tipos de archivos."
       }
       {
         _id: 'system.windows.shell-menu-files'
-        title: "Show in file context menus"
-        desc: "Add \"Open with Atom\" to the File Explorer context menu for files."
+        title: "Mostrar en el menú contextual de archivos"
+        desc: "Agregar \"Abrir con Atom\" al menú contextual del Explorador de Archivos (sólo archivos)."
       }
       {
         _id: 'system.windows.shell-menu-folders'
-        title: "Show in folder context menus"
-        desc: "Add \"Open with Atom\" to the File Explorer context menu for folders."
+        title: "Mostrar en el menú contextual de carpetas"
+        desc: "Agregar \"Abrir con Atom\" al menú contextual del Explorador de Archivos (sólo carpetas)."
       }
     ]
   }
   system: {
-    notes: "These settings determine how Atom integrates with your operating system."
+    notes: "Estas configuraciones determinan cómo Atom se integra con tu sistema operativo."
   }
   keymaps: {
     notes: {


add select2 call after loading new images Fixes #5385

--- backend/app/assets/javascripts/spree/backend/images/index.js.coffee
+++ backend/app/assets/javascripts/spree/backend/images/index.js.coffee
@@ -13,3 +13,4 @@
       )
       success: (r) ->
         ($ '#images').html r
+        ($ '.select2').select2()


remote-extension: add comment

remote-extension: add comment

--- client/app/lib/remote-extensions/index.coffee
+++ client/app/lib/remote-extensions/index.coffee
@@ -36,6 +36,10 @@
         # RemoteExtensions will decide to keep or drop them away.
         RemoteExtensions.addInstance data._id, this

+      # small helper to check instance ownership
+      # if originId is provided in the modal
+      # please be aware this will return false all the time
+      # if modal doesn't have originId field in its schema
       remote.api[model]::isMine = -> @originId is whoami()._id


Moar defensive code

Moar defensive code

--- client/ide/lib/collaborationcontroller.coffee
+++ client/ide/lib/collaborationcontroller.coffee
@@ -146,7 +146,7 @@

   onRealtimeParticipantJoined: (data) ->

-    return  unless @stateMachine.state is 'Active'
+    return  unless @stateMachine?.state is 'Active'

     {sessionId} = data.collaborator

@@ -166,7 +166,7 @@

   onRealtimeParticipantLeft: (data) ->

-    return  unless @stateMachine.state is 'Active'
+    return  unless @stateMachine?.state is 'Active'

     {sessionId} = data.collaborator


collaboration: controller listen rtm for error events

--- client/ide/lib/collaborationcontroller.coffee
+++ client/ide/lib/collaborationcontroller.coffee
@@ -188,6 +188,8 @@
   activateRealtimeManager: (doc) ->

     @rtm.setRealtimeDoc doc
+    @bindRealtimeErrorEvents()
+
     @setCollaborativeReferences()
     @addParticipant whoami()
     @registerCollaborationSessionId()
@@ -313,6 +315,16 @@

       else if map is @permissions
         @handlePermissionMapChange event
+
+
+  bindRealtimeErrorEvents: ->
+
+    @on 'ErrorRealtimeFileMissing',   throwError
+    @on 'ErrorRealtimeServer',        throwError
+    @on 'ErrorRealtimeUserForbidden', throwError
+    @on 'ErrorRealtimeTokenExpired',  throwError
+    @on 'ErrorGoogleDriveApiClient',  throwError
+    @on 'ErrorHappened',              throwError

   removeParticipant: (nickname) ->


collaboration: change nick util name to prevent name collisions

--- client/ide/lib/collaborationcontroller.coffee
+++ client/ide/lib/collaborationcontroller.coffee
@@ -46,7 +46,7 @@
   initPrivateMessage: (callback) ->

     {message} = kd.singletons.socialapi
-    nick      = nick()
+    nickname  = nick()

     message.initPrivateMessage
       body       : "@#{nick} initiated the IDE session."
@@ -682,7 +682,7 @@
     return callback msg : 'no social channel'  unless @socialChannel

     {message} = kd.singletons.socialapi
-    nick      = nick()
+    nickname  = nick()

     message.sendPrivateMessage
       body       : "@#{nick} activated collaboration."
@@ -710,9 +710,9 @@
     return callback msg : 'no social channel'  unless @socialChannel

     {message} = kd.singletons.socialapi
-    nick      = nick()
-
-    @broadcastMessages.push origin: nick(), type: 'SessionEnded'
+    nickname  = nick()
+
+    @broadcastMessages.push origin: nickname, type: 'SessionEnded'

     @rtm.once 'FileDeleted', =>
       @statusBar.emit 'CollaborationEnded'


Collaboration: Make everybody read only at beginning.

--- client/ide/lib/collaborationcontroller.coffee
+++ client/ide/lib/collaborationcontroller.coffee
@@ -308,11 +308,13 @@

   addParticipant: (account) ->

-    {hash, nickname} = account.profile
-
-    val = {nickname, hash}
+    { hash, nickname } = account.profile
+
+    val   = { nickname, hash }
     index = @participants.indexOf val, (a, b) -> a.nickname is b.nickname
+
     @participants.push val  if index is -1
+    @setMyPermission 'edit' if @amIHost

   watchParticipant: (nickname) -> @myWatchMap.set nickname, nickname
@@ -408,7 +410,7 @@
       @statusBar.createParticipantAvatar nickname, no

       if @amIHost
-        @setParticipantPermission nickname
+        @setParticipantPermission nickname, 'read'
         @setMachineUser [nickname]

@@ -876,6 +878,7 @@

           @stateMachine.transition 'Active'
           @updateSessionStartingProgress 90
+          @makeReadOnly()  if @getMyPermission() isnt 'edit'

           kd.utils.wait 2000, =>
             @updateSessionStartingProgress 100
@@ -1342,17 +1345,21 @@

   setParticipantPermission: (nickname, permission) ->

-    return  if (not permission?) and @permissions.get nickname
-
-    permission ?= if @settings.get 'readOnly' then 'read' else 'edit'
     @permissions.set nickname, permission

   removeParticipantPermissions: (nickname) ->

-    return  unless @permissions.get nickname
-
     @permissions.delete nickname
+
+
+  getMyPermission: -> @permissions.get nick()
+
+
+  setMyPermission: (permission) ->
+
+    permission = 'edit'  if @amIHost # override for host
+    @setParticipantPermission nick(), permission

   getMyWatchers: ->


list hidden for non-admins, some code cleanup

--- client/landing/app/MainApp/monitorview.coffee
+++ client/landing/app/MainApp/monitorview.coffee
@@ -10,7 +10,7 @@
     @ledList = new KDListView
       itemClass : StatusLEDItemView

-    @ledList.hide() unless KD.checkFlag "super-admin", KD.whoami()
+    @ledList.hide() unless KD.checkFlag "super-admin"

     @ledLabel = new KDView
       cssClass : 'status-label'
@@ -23,13 +23,7 @@
     monitorController.on 'ServiceWentOnline', (key,serviceData={})=>
       for item in @ledList.items
         if item.getData() is key
-          # if @utils.getRandomNumber(2) is 1
-          #   item.setOffline()
-          # else
-          #   item.setOnline()
-
           item.setOnline()
-
           if serviceData.count
             item.setCount serviceData.count


detect if enter is pressed.

detect if enter is pressed.

--- cf/spell.coffee
+++ cf/spell.coffee
@@ -25,3 +25,8 @@
   update_question = (data) ->
     word = random_question(data)
     $("p#hint").empty().append(word['hint'])
+
+  $("#answer").keypress((e) ->
+    if e.which == 13
+      alert("Enter is pressed.")
+  )


HUB-153 : Add sessionType properties for a Gateway actor

--- lib/client_connector/socketio_connector.coffee
+++ lib/client_connector/socketio_connector.coffee
@@ -103,7 +103,8 @@
       data.trackInbox = inboundAdapters
       data.actor = actor
       data.inboundAdapters
-      client.hClient.createChild "hsession", "inproc", data, (child) =>
+      session_type = @owner.properties.sessionType or "hsession"
+      client.hClient.createChild session_type, "inproc", data, (child) =>
         #Relay all server status messages
         child.initListener(client)
         client.child = child.actor


add missing components to parser

add missing components to parser

--- lib/ical_helpers.coffee
+++ lib/ical_helpers.coffee
@@ -76,6 +76,28 @@
     addAlarm: (date) ->
         @add new VAlarm date

+module.exports.VEvent = VEvent = class VEvent extends VComponent
+    name = 'VEVENT'
+module.exports.VJournal = VJournal = class VJournal extends VComponent
+    name = 'VJOURNAL'
+module.exports.VFreeBusy = VFreeBusy = class VFreeBusy extends VComponent
+    name = 'VFREEBUSY'
+module.exports.VTimezone = VTimezone = class VTimezone extends VComponent
+    name = 'VTIMEZONE'
+module.exports.VStandard = VStandard = class VStandard extends VComponent
+    name = 'VSTANDARD'
+module.exports.VDaylight = VDaylight = class VDaylight extends VComponent
+    name = 'VDAYLIGHT'
+
+components =
+    VCALENDAR: VCalendar
+    VTODO: VTodo
+    VALARM: VAlarm
+    VEVENT: VEvent
+    VJOURNAL: VJournal
+    VFREEBUSY: VFreeBusy
+    VTIMEZONE: VTimezone
+    VSTANDARD: VStandard

 module.exports.ICalParser = class ICalParser

@@ -120,11 +142,8 @@
                         component = new VCalendar()
                         result = component

-                    else if value is "VTODO"
-                        component = new VTodo()
-
-                    else if value is "VALARM"
-                        component = new VAlarm()
+                    else if value in Object.keys(components)
+                        component = new components[value]()

                     else
                         sendError "Malformed ical file (line #{lineNumber})"


Fix order of arguments passed to clang. Fix #57

--- lib/linter-clang.coffee
+++ lib/linter-clang.coffee
@@ -44,6 +44,9 @@
     @cmd = [ atom.config.get 'linter-clang.clangCommand' ]

     {command, args} = @getCmdAndArgs(filePath)
+
+    # Remove file path from args, it should be the last argument
+    args.shift()

     args.push '-fsyntax-only'
     args.push '-fno-caret-diagnostics'
@@ -134,6 +137,9 @@

     searchDirectory projectPath

+    # Add file path as last argument
+    args.push filePath
+
     # add file to regex to filter output to this file,
     # need to change filename a bit to fit into regex
     @regex = filePath.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&") +


add code suggestion if its in the message

--- lib/linter-views.coffee
+++ lib/linter-views.coffee
@@ -82,6 +82,7 @@
           html: msg.html
           range: msg.range
           trace: msg.trace
+          suggestion: msg.suggestion
       }
     @renderedMessages = @linter.messenger.manyMessages prepared


Metrics API tests: More robust date comparisons

--- test/integration/metricsAPITests.coffee
+++ test/integration/metricsAPITests.coffee
@@ -11,6 +11,7 @@
 authorisation = require "../../lib/api/authorisation"
 config = require "../../lib/config/config"
 server = require "../../lib/server"
+moment = require "moment"

 describe "API Metrics Tests", ->

@@ -173,7 +174,8 @@
             res.body.should.have.length 2
             res.body[0].load.should.equal 1
             res.body[0].avgResp.should.equal 149
-            res.body[0].timestamp.should.equal '2014-07-18T13:00:00+00:00'
+            ts = moment(res.body[0].timestamp)
+            ts.format().should.equal moment('2014-07-18T13:00:00+00:00').format()
             done()

     describe '*getGlobalStatusMetrics()', ->
@@ -220,7 +222,8 @@
             res.body.should.have.length 1
             res.body[0].load.should.equal 1
             res.body[0].avgResp.should.equal 149
-            res.body[0].timestamp.should.equal '2014-07-18T00:00:00+00:00'
+            ts = moment(res.body[0].timestamp)
+            ts.format().should.equal moment('2014-07-18T00:00:00+00:00').format()
             done()

     describe '*getStatusMetrics()', ->


remove misleading tests

remove misleading tests

--- test/javascripts/turbolinks_test.coffee
+++ test/javascripts/turbolinks_test.coffee
@@ -185,33 +185,6 @@
           assertLinks(['foo.css'])
           done()

-    it 'many subsequent navigations do not break head asset tracking', (done) ->
-      requestsToDo = 10
-
-      recursiveVisit = ->
-        visit url: 'twoLinksInHead', ->
-          visit url: 'singleLinkInHead', ->
-            assertLinks(['foo.css'])
-            requestsToDo -=1
-            return recursiveVisit() if requestsToDo > 0
-            done()
-
-      recursiveVisit()
-
-      it 'many subsequent synchronous navigations do not break head asset tracking', (done) ->
-        requestsToDo = 10
-
-        recursiveVisit = ->
-          visit url: 'twoLinksInHead'
-          visit url: 'singleLinkInHead'
-          visit url: 'twoLinksInHead', ->
-              assertLinks(['foo.css', 'bar.css'])
-              requestsToDo -=1
-              return recursiveVisit() if requestsToDo > 0
-              done()
-
-        recursiveVisit()
-
     describe 'transforms the current head to have the same links in the same order as the upstream document with minimal moves', ->
       it 'maintains order when moving from an empty head to a page with link nodes.', (done) ->
         linkTagInserted = sinon.spy()


Remove unused argument -w in http

Remove unused argument -w in http

--- lib/plugins/http.coffee
+++ lib/plugins/http.coffee
@@ -21,8 +21,8 @@
         detached = not shell.isShell or settings.detach
         pipeStdout = settings.stdout and not detached
         pipeStderr = settings.stderr and not detached
-        args.push '-w'
-        args.push settings.workspace
+        #args.push '-w'
+        #args.push settings.workspace
         if not pipeStdout
             args.push '>'
             args.push settings.stdout


Atom config updates

Atom config updates

--- atom/atom.symlink/config.cson
+++ atom/atom.symlink/config.cson
@@ -10,7 +10,7 @@
   editor:
     fontSize: 12
     showIndentGuide: true
-    preferredLineLength: 120
+    preferredLineLength: 110
     tabLength: 4
     softWrap: true
     softWrapAtPreferredLineLength: true
@@ -25,38 +25,29 @@
       "atom-light-ui"
       "tomorrow"
     ]
+    disabledPackages: [
+      "go-plus"
+      "spell-check"
+    ]
   rspec:
     command: "bundle exec rspec"
   "cucumber-runner":
     command: "bundle exec cucumber"
-".js.rails.source source.jquery.js":
-  editor: {}
-".rails.rjs.ruby.source":
-  editor: {}
+  "file-types":
+    $debug: true
+    "^Podfile$": "source.ruby"
+  "autocomplete-plus": {}
+  "spell-check": {}
 ".basic.html.text":
   editor:
     preferredLineLength: 110
     tabLength: 2
-".rspec.ruby.source":
-  editor: {}
-".ruby.source.sql":
-  editor: {}
 ".erb.html.text":
   editor:
     tabLength: 2
-".html.ruby.text":
-  editor: {}
-".rails.ruby.source":
-  editor: {}
-".objc.source ++ *":
-  editor: {}
 ".coffee.source":
   editor:
     tabLength: 2
-".source.strings":
-  editor: {}
-".objc.source":
-  editor: {}
 ".shell.source":
   editor:
     tabLength: 2
@@ -66,7 +57,6 @@
 ".litcoffee.source":
   editor:
     tabLength: 2
-".gfm.source":
-  editor: {}
-".source.swift":
-  editor: {}
+".diff.source":
+  editor:
+    preferredLineLength: 110


Readd timeout

Readd timeout

--- lib/sublime-tabs.coffee
+++ lib/sublime-tabs.coffee
@@ -99,9 +99,13 @@

   disableStockPackages: ->
     if atom.packages.isPackageLoaded('tabs')
-        atom.packages.deactivatePackage('tabs')
         atom.packages.disablePackage('tabs')
+        setTimeout ->
+          atom.packages.deactivatePackage('tabs')
+        , 2000

     if atom.packages.isPackageLoaded('tree-view')
-        atom.packages.deactivatePackage('tree-view')
         atom.packages.disablePackage('tree-view')
+        setTimeout ->
+          atom.packages.deactivatePackage('tree-view')
+        , 2000


Fix typo that used the wrong function to reset the buffer range

--- lib/utils/editor.coffee
+++ lib/utils/editor.coffee
@@ -9,7 +9,7 @@

   # reset the selection to what it was before calling
   # this function
-  editor.getSelectedBufferRange bufferRange
+  editor.setSelectedBufferRange bufferRange

   selectedText.trim() == ''


removed test that is failing in travis (due to logs not existing)

--- test/_supertest/Api-Logs.super.test.coffee
+++ test/_supertest/Api-Logs.super.test.coffee
@@ -35,6 +35,7 @@
       data.assert_Size_Is_Bigger_Than 1
       data.first().assert_Contains 'logs-'

-  it '/logs/file', ()->
+  # not working on travis because there are no logs in the file system
+  xit '/logs/file', ()->
     check_Path '/logs/file/0', (data)->
       data.assert_Contains 'GET'


fix obvious mistake in storage query

fix obvious mistake in storage query

--- src/smc-hub/storage.coffee
+++ src/smc-hub/storage.coffee
@@ -1700,7 +1700,7 @@
             # Projects that had a storage request recently (in the last age_m minutes)...
             # and we only want the ignored requests...
             # And the ones that haven't started and haven't finished
-            query = "SELECT project_id storage_request storage host state FROM projects WHERE "
+            query = "SELECT project_id,storage_request,storage,host,state FROM projects WHERE "
             params = [misc.minutes_ago(opts.age_m)]
             query += " storage_request#>'{requested}' >= $1 AND storage_request#>'{started}' IS NULL AND storage_request#>'{finished}' IS NULL "
             if not opts.all


use the proper component for exercise test

--- test/components/exercise/index.spec.coffee
+++ test/components/exercise/index.spec.coffee
@@ -1,6 +1,6 @@
 {Testing, expect, sinon, _, ReactTestUtils} = require 'test/helpers'

-Exercise = require 'components/exercise'
+{Exercise} = require 'components/exercise'
 STEP = require './step-data'
 CHOICES = STEP.content.questions[0].answers


Fix specs to reflect new dom classes and layout

--- test/components/reference-book.spec.coffee
+++ test/components/reference-book.spec.coffee
@@ -91,10 +91,10 @@
       .to.contain('menu-open')

   it 'navigates forward and back between pages', ->
-    prevControl = @state.div.querySelector('.page-wrapper > .prev')
+    prevControl = @state.div.querySelector('.page-navigation.prev')
     expect(prevControl).to.not.exist # on first page

-    nextControl = @state.div.querySelector('.page-wrapper > .next')
+    nextControl = @state.div.querySelector('.page-navigation.next')
     expect(nextControl).to.exist
     expect(nextControl.href).to.contain(SECOND_PAGE)
     page = ReactTestUtils.findRenderedComponentWithType(@state.book, Page)
@@ -106,15 +106,15 @@
     expect(page.context.router.getCurrentParams().section)
       .to.equal(SECOND_PAGE)

-    expect(@state.div.querySelector('.page-wrapper > .next').href)
+    expect(@state.div.querySelector('.page-navigation.next').href)
       .to.contain(THIRD_PAGE)

-    expect(@state.div.querySelector('.page-wrapper > .prev').href)
+    expect(@state.div.querySelector('.page-navigation.prev').href)
       .to.contain(FIRST_PAGE)

   it 'preserves menu toggle when navigating forward and back between pages', ->
     toggle = @state.div.querySelector('.menu-toggle')
-    nextControl = @state.div.querySelector('.page-wrapper > .next')
+    nextControl = @state.div.querySelector('.page-navigation.next')

     page = ReactTestUtils.findRenderedComponentWithType(@state.book, Page)
     ReferenceBookPageActions.loaded(PAGE, SECOND_PAGE_ID)
@@ -136,13 +136,13 @@
     expect(_.toArray(@state.div.querySelector('.reference-book').classList))
       .to.contain('menu-open')

-    prevControl = @state.div.querySelector('.page-wrapper > .prev')
+    prevControl = @state.div.querySelector('.page-navigation.prev')
     commonActions.click(prevControl, {button: 0})
     expect(_.toArray(@state.div.querySelector('.reference-book').classList))
       .to.contain('menu-open')

   it 'sets the menu item to be active based on the current page', ->
-    selection = @state.div.querySelector(".toc [data-section='#{FIRST_PAGE}'] a")
+    selection = @state.div.querySelector(".menu [data-section='#{FIRST_PAGE}'] a")
     expect(selection).not.to.be.null
     expect(_.toArray(selection.classList)).to.include('active')

@@ -150,7 +150,7 @@
     expect(_.toArray(@state.div.querySelector('.reference-book').classList))
       .to.contain('menu-open')

-    nextSelection = @state.div.querySelector(".toc [data-section='#{SECOND_PAGE}'] a")
+    nextSelection = @state.div.querySelector(".menu [data-section='#{SECOND_PAGE}'] a")
     ReferenceBookPageActions.loaded(PAGE, SECOND_PAGE_ID)

     commonActions.click(nextSelection)
@@ -176,15 +176,15 @@
       .to.match(/1/)

   it 'sets the menu item to be active based on the current page', ->
-    selection = @state.div.querySelector(".toc [data-section='#{ECO_FIRST_PAGE}'] a")
+    selection = @state.div.querySelector(".menu [data-section='#{ECO_FIRST_PAGE}'] a")
     expect(selection).not.to.be.null
     expect(_.toArray(selection.classList)).to.include('active')

   it 'navigates forward and back between pages, maintaining ecosystem id', ->
-    prevControl = @state.div.querySelector('.page-wrapper > .prev')
+    prevControl = @state.div.querySelector('.page-navigation.prev')
     expect(prevControl).to.not.exist # on first page

-    nextControl = @state.div.querySelector('.page-wrapper > .next')
+    nextControl = @state.div.querySelector('.page-navigation.next')
     expect(nextControl).to.exist
     expect(nextControl.href).to.contain(ECO_SECOND_PAGE)
     page = ReactTestUtils.findRenderedComponentWithType(@state.book, Page)
@@ -199,14 +199,14 @@
     expect(page.context.router.getCurrentQuery())
       .to.have.property('ecosystemId').and.equal(ECO_ID)

-    expect(@state.div.querySelector('.page-wrapper > .next').href)
+    expect(@state.div.querySelector('.page-navigation.next').href)
       .to.contain("?ecosystemId=#{ECO_ID}")

-    expect(@state.div.querySelector('.page-wrapper > .prev').href)
+    expect(@state.div.querySelector('.page-navigation.prev').href)
       .to.contain("?ecosystemId=#{ECO_ID}")

   it 'keeps ecosystem id in table of contents links', ->
-    tocLinks = _.toArray(@state.div.querySelectorAll('.toc a'))
+    tocLinks = _.toArray(@state.div.querySelectorAll('.menu a'))
     linkAddresses = _.pluck(tocLinks, 'href')

     _.each(linkAddresses, (linkAddress) ->


Give focus to input/text when we click on edit

--- admin/static/coffee/namespaces/shards.coffee
+++ admin/static/coffee/namespaces/shards.coffee
@@ -359,6 +359,7 @@
             @.$('.edit-shards').html @edit_template
                 num_shards: @model.get('shards').length
                 max_shards: max_shards
+            @.$('.num-shards').focus()

         render_data_repartition: =>
             $('.tooltip').remove()


sorting answers by id in edit view

sorting answers by id in edit view

--- src/stores/question.coffee
+++ src/stores/question.coffee
@@ -5,6 +5,9 @@

 QuestionConfig = {
   _loaded: (obj, id) ->
+    obj.answers = _.sortBy obj?.answers, (answer) ->
+      parseInt(answer.id)
+
     for answer in obj?.answers
       AnswerActions.loaded(answer, answer.id)


log commented

log commented

--- test/test-cohtml-to-html5-converter.coffee
+++ test/test-cohtml-to-html5-converter.coffee
@@ -21,5 +21,5 @@

     html5Scope = converter.convertScope scope

-    console.log (require 'util').inspect html5Scope, {depth:50, colors: true}
+    # console.log (require 'util').inspect html5Scope, {depth:50, colors: true}


Test the position of the indicator on the bar.

--- spec/config/status-icon-position-spec.coffee
+++ spec/config/status-icon-position-spec.coffee
@@ -1,12 +1,49 @@
+BottomTab = require '../../lib/views/bottom-tab'
+
 describe "The Status Icon Configuration Option", ->
-
+  [statusBar, workspaceElement, dummyView] = []
   configString = "linter.statusIconPosition"

   beforeEach ->
+    workspaceElement = atom.views.getView(atom.workspace)
+    dummyView = document.createElement("div")
+    statusBar = null
+
     waitsForPromise ->
       atom.packages.activatePackage('linter')
+    waitsForPromise ->
+      atom.packages.activatePackage('status-bar')
+
+    runs ->
+      statusBar = workspaceElement.querySelector("status-bar")

   it "is 'Left' by default.", ->
-    packageSetting = atom.config.get configString
-    expectedDefault = "Left"
-    expect(packageSetting).toEqual expectedDefault
+    expect(atom.config.get(configString)).toEqual "Left"
+
+  describe "when set to 'Left'", ->
+    beforeEach ->
+      atom.config.set configString, 'Left'
+
+    it "is set to 'Left.'", ->
+      expect(atom.config.get(configString)).toEqual "Left"
+
+    it "is on the left side of the Status Bar.", ->
+      jasmine.attachToDOM(workspaceElement)
+      [linterHighlight] = statusBar.getLeftTiles().map (tile) -> tile.getItem()
+      expect(linterHighlight).toBeDefined
+      [linterHighlight] = statusBar.getRightTiles().map (tile) -> tile.getItem()
+      expect(linterHighlight).not.toBeDefined
+
+  describe "when set to 'Right'", ->
+    beforeEach ->
+      atom.config.set configString, 'Right'
+
+    it "is set to 'Right.'", ->
+      expect(atom.config.get(configString)).toEqual "Right"
+
+    it "is on the right side of the Status Bar.", ->
+      jasmine.attachToDOM(workspaceElement)
+      [linterHighlight] = statusBar.getRightTiles().map (tile) -> tile.getItem()
+      expect(linterHighlight).toBeDefined
+      [linterHighlight] = statusBar.getRightTiles().map (tile) -> tile.getItem()
+      expect(linterHighlight).not.toBeDefined


Remove from annotations groups which are removed.

--- client/publication.coffee
+++ client/publication.coffee
@@ -32,6 +32,17 @@

 Meteor.startup ->
   Session.setDefault 'annotationDefaults', ANNOTATION_DEFAULTS
+
+getAnnoationDefaults = ->
+  _.defaults Session.get('annotationDefaults'), ANNOTATION_DEFAULTS
+
+Deps.autorun ->
+  # We have to keep list of default groups updated if user is removed from a group
+  Group.documents.find(_id: $in: _.pluck Meteor.person()?.inGroups, '_id').observeChanges
+    removed: (id) ->
+      defaults = getAnnoationDefaults()
+      defaults.groups = _.without defaults.groups, id
+      Session.set 'annotationDefaults', defaults

 class @Publication extends Publication
   @Meta
@@ -1292,7 +1303,7 @@
   'change .access input:radio': (e, template) ->
     access = Annotation.ACCESS[$(template.findAll '.access input:radio:checked').val().toUpperCase()]

-    defaults = _.defaults Session.get('annotationDefaults'), ANNOTATION_DEFAULTS
+    defaults = getAnnoationDefaults()
     defaults.access = access
     Session.set 'annotationDefaults', defaults

@@ -1336,14 +1347,14 @@

 Template.contextMenuGroups.events
   'click .add-to-working-in': (e, template) ->
-    defaults = _.defaults Session.get('annotationDefaults'), ANNOTATION_DEFAULTS
+    defaults = getAnnoationDefaults()
     defaults.groups = _.union defaults.groups, [@_id]
     Session.set 'annotationDefaults', defaults

     return # Make sure CoffeeScript does not return anything

   'click .remove-from-working-in': (e, template) ->
-    defaults = _.defaults Session.get('annotationDefaults'), ANNOTATION_DEFAULTS
+    defaults = getAnnoationDefaults()
     defaults.groups = _.without defaults.groups, @_id
     Session.set 'annotationDefaults', defaults


Add support for caching cabins from Nasjonal Turbase

--- coffee/model/cabin.coffee
+++ coffee/model/cabin.coffee
@@ -1,7 +1,9 @@
 "use strict"

-cache = './cache'
+cache = require './cache'
+mongo = require './mongo'
 request = require 'request'
+ObjectID = require('mongodb').ObjectID

 api_key = process.env.NTB_API_KEY

@@ -13,13 +15,53 @@

     cb null, body.documents

+
+getCabinFromNtb = (id, after, cb) ->
+  opts =
+    url: "http://api.nasjonalturbase.no/steder/#{id}?api_key=#{api_key}"
+    headers: "If-Modified-Since": after if after
+    json: true
+  request opts, (err, res, body) ->
+    cb err, res.statusCode, body
+
+getCabinFromCache = (id, cb) ->
+  cache.hgetall "cabin:#{id}", cb
+
+getCabinFromDb = (id, cb) ->
+  mongo.db.collection('cabin').findOne _id: new ObjectID(id), cb
+
+cacheCabin = (id, cabin, cb) ->
+  cache.hmset "cabin:#{id}",
+    name    : cabin.navn
+    checksum: cabin.checksum
+    endret  : cabin.endret
+    url     : cabin.url
+    groups  : cabin.grupper
+
+  cabin._id = new ObjectID cabin._id
+  mongo.db.collection('cabin').save cabin, (err) ->
+    cb err, cabin
+
 exports.getCabin = (id, cb) ->
-  url = "http://api.nasjonalturbase.no/steder/#{id}?api_key=#{api_key}"
-  request url: url, json: true, (err, res, body) ->
-    if err or res.statusCode isnt 200 or not body
-      return cb new Error('Not Found')
+  getCabinFromCache id, (err, c) ->

-    cb null, body
+    return cb null, null if c and c.status is 'Slettet'
+
+    getCabinFromNtb id, c?.endret, (err, code, cabin) ->
+      if code is 304
+        return getCabinFromDb id, (err, doc) ->
+          return cb null, doc if doc
+          return getCabinFromNtb id, null, (err, code, cabin) ->
+            return cacheCabin id, cabin, cb if code is 200
+            return cb new Error('NTB Failed')
+
+      else if code is 200
+        return cacheCabin id, cabin, cb
+
+      else
+        cache.hset "cabin:#{id}", "status", "Slettet"
+        cache.expire "cabin:#{id}", 600
+        return cb null, null

 exports.getImage = (id, cb) ->
   url = "http://api.nasjonalturbase.no/bilder/#{id}?api_key=#{api_key}"


Added GA back in. Keeping track of user navigation between tabs, form success & errors.

--- src/coffee/donations-form-model.coffee
+++ src/coffee/donations-form-model.coffee
@@ -113,6 +113,7 @@
     self.visibleInputSet = ko.observable(0)

     self.incrementInputSet = ->
+      gaDonations('send', 'event', 'next', 'click', "from-#{self.visibleInputSet()}-to-#{self.visibleInputSet()+1}")
       self.visibleInputSet(self.visibleInputSet() + 1)

     self.isAdvanceAllowed = (index) ->
@@ -129,6 +130,7 @@

     self.setInputSet = (index) ->
       if self.isAdvanceAllowed(index)
+        gaDonations('send', 'event', 'navigation', 'click', "from-#{self.visibleInputSet()}-to-#{index}")
         self.visibleInputSet(index)

     self.firstName = ko.observable().extend({ required: { message: "Can't be blank" } })
@@ -232,6 +234,7 @@
           $('.donation-loading-overlay').hide()
           pusher.disconnect()
           if data.status == "success"
+            gaDonations('send', 'event', 'form', 'success')
             $("#donation-script").trigger("donations:success")
             if !!config['redirectto']
               unless /^https?:\/\//.test(config['redirectto'])
@@ -240,6 +243,7 @@
             $("#donation-form").hide()
             $(".donations-callback-flash").show(0).delay(8000).hide(0)
           else
+            gaDonations('send', 'event', 'form', 'error')
             $(".donation-payment-errors").text(data.message or "Something went wrong.").show()

         customer = {}
@@ -263,9 +267,8 @@
           contentType: 'application/json'
           data: JSON.stringify(formPost)
           success: (response, textStatus, jqXHR) ->
-            gaDonations('send', 'event', 'advance-button', 'click#success', 'submit', 1)
           error: (response, textStatus, errorThrown) ->
-            gaDonations('send', 'event', 'advance-button', 'click#with-errors', 'submit', 1)
+            gaDonations('send', 'event', 'form', 'error', 'charge-error')
             $form.find(".donation-payment-errors").text(response.responseText or "Something went wrong.").show()
             $('.donation-loading-overlay').hide()
             $form.find("button").prop "disabled", false
@@ -275,8 +278,8 @@
     stripeResponseHandler = (status, response) ->
       $form = $("#donation-form")
       if response.error
+        gaDonations('send', 'event', 'form', 'error', 'stripe-error')
         # Show the errors on the form
-        gaDonations('send', 'event', 'advance-button', 'click#with-errors', 'submit', 1)
         $form.find("button").prop "disabled", false
         self.stripeMessage(response.error.message)
         $('.donation-loading-overlay').hide()
@@ -285,7 +288,7 @@
         false

     self.submitForm = ->
-      gaDonations('send', 'event', 'advance-button', 'click#submit', 'submit', 1)
+      gaDonations('send', 'event', 'submit', 'click')
       $form = $("#donation-form")
       $('.donation-loading-overlay').show()
       # Disable the submit button to prevent repeated clicks


SplitPanel: instance props simplified min/max props deprecated

--- src/components/split/splitpanel.coffee
+++ src/components/split/splitpanel.coffee
@@ -4,19 +4,17 @@

   constructor:(options = {}, data)->

-    options.fixed    ?= no
-    options.minimum or= null
-    options.maximum or= null
-    options.view    or= null
+    options.fixed  ?= no
+    options.view  or= null

-    super options,data
+    super options, data

-    @isVertical = @getOptions().type.toLowerCase() is "vertical"
-    @isFixed    = @getOptions().fixed
+    {type} = @getOptions()

-    {@size, @minimum, @maximum} = @options
+    @vertical = type.toLowerCase() is "vertical"

-  _getIndex:-> @parent.getPanelIndex @
+    {@fixed, @size, @index} = @getOptions()
+
   _getSize:-> if @vertical then @getWidth() else @getHeight()


Require the right query string lib

Require the right query string lib

Per @mzikherman, `qs` is the cool one.

--- src/desktop/apps/home/client/auth_router.coffee
+++ src/desktop/apps/home/client/auth_router.coffee
@@ -1,7 +1,7 @@
 Backbone = require 'backbone'
 _ = require 'underscore'
 mediator = require '../../../lib/mediator.coffee'
-qs = require 'querystring'
+qs = require 'qs'

 module.exports = class HomeAuthRouter extends Backbone.Router


Problem sending parameters via POST

Problem sending parameters via POST

Jquery seems to fail posting data as POST params if contentType is overwritten. At least in chrome

--- src/main/coffeescript/view/OperationView.coffee
+++ src/main/coffeescript/view/OperationView.coffee
@@ -78,7 +78,7 @@
         complete: (data) =>
           @showCompleteStatus(data)

-      obj.contentType = "application/json" if (obj.type.toLowerCase() == "post" or obj.type.toLowerCase() == "put" or obj.type.toLowerCase() == "patch")
+      obj.contentType = "application/json" if (obj.type.toLowerCase() == "put" or obj.type.toLowerCase() == "patch")

       jQuery.ajax(obj)
       false


add xtend

add xtend

--- app/vis.coffee
+++ app/vis.coffee
@@ -1,7 +1,9 @@
 window.THREE = require("three")
 createGeometry = require('three-bmfont-text')
 Shader = require "./shaders/sdf"
+xtend = require 'xtend'
 createOrbitViewer = require('three-orbit-viewer')(THREE)
+

 class Main
     SCALE_TEXT = 0.005
@@ -116,12 +118,14 @@
         lineObject

     addChain: (text) =>
-        chain = processChain(text)
-
-        lineObjects = chain.map @getLineObject
+        lineObjects = processChain(text)
+            .map @getLineObject

         chainObject = new THREE.Object3D()
         chainObject.add.apply(chainObject, lineObjects)
+
+        # chainObject.children.forEach (line) ->
+            # debugger

         chainObject.scale.multiplyScalar(SCALE_TEXT)


make sure log is loaded first

make sure log is loaded first

--- manager/app/assets/javascripts/bixby.js.coffee
+++ manager/app/assets/javascripts/bixby.js.coffee
@@ -1,5 +1,6 @@

 #= require "utils"
+#= require "stark/log"
 #= require_tree "./stark"

 # setup our namespaces


improve logging and interrupt handling

improve logging and interrupt handling

--- migrations/3_pack_docHistory_collection.coffee
+++ migrations/3_pack_docHistory_collection.coffee
@@ -3,7 +3,7 @@
 mongojs = require("mongojs")
 ObjectId = mongojs.ObjectId
 db = mongojs(Settings.mongo.url, ['docs','docHistory', 'docHistoryStats'])
-_ = require("lodash")
+_ = require("underscore")
 async = require("async")
 exec = require("child_process").exec
 BSON = db.bson.BSON
@@ -12,6 +12,14 @@
 	log: ->
 	err: ->
 }
+
+needToExit = false
+handleExit = () ->
+	needToExit = true
+	console.log('Got signal.  Shutting down.')
+
+process.on 'SIGINT', handleExit
+process.on 'SIGHUP', handleExit

 finished_docs_path = "/tmp/finished-docs-3"
 all_docs_path = "/tmp/all-docs-3"
@@ -56,23 +64,24 @@
 	markDocAsProcessed doc_id, (err)->
 		fs.appendFile unmigrated_docs_path, "#{doc_id}\n", callback

-printProgress = ->
-	count = Object.keys(finished_docs).length
-	console.log "completed", count
-
 checkIfDocHasBeenProccessed = (doc_id, callback)->
 	callback(null, finished_docs[doc_id])

 processNext = (doc_id, callback)->
 	if !doc_id? or doc_id.length == 0
 		return callback()
+	if needToExit
+		return callback(new Error("graceful shutdown"))
 	checkIfDocHasBeenProccessed doc_id, (err, hasBeenProcessed)->
 		if hasBeenProcessed
 			console.log "#{doc_id} already processed, skipping"
 			return callback()
-		console.log "#{doc_id} processing"
 		PackManager._packDocHistory doc_id, {}, (err) ->
-			markDocAsProcessed doc_id, callback
+			if err?
+				console.log "error processing #{doc_id}"
+				markDocAsUnmigrated doc_id, callback
+			else
+				markDocAsProcessed doc_id, callback

 updateIndexes = (callback) ->
 	async.series [
@@ -91,15 +100,27 @@

 exports.migrate = (client, done = ->)->
 	getDocIds (err, ids)->
+		totalDocCount = ids.length
+		alreadyFinishedCount = Object.keys(finished_docs).length
+		t0 = Date.now()
+		printProgress = () ->
+			count = Object.keys(finished_docs).length
+			processedFraction = (count-alreadyFinishedCount)/totalDocCount
+			remainingFraction = (totalDocCount-count)/totalDocCount
+			t = Date.now()
+			dt = (t-t0)*remainingFraction/processedFraction
+			estFinishTime = new Date(t + dt)
+			console.log "completed #{count}/#{totalDocCount} processed=#{processedFraction.toFixed(2)} remaining=#{remainingFraction.toFixed(2)} elapsed=#{(t-t0)/1000} est Finish=#{estFinishTime}"
 		interval = setInterval printProgress, 3*1000
-		jobs = _.map ids, (id)->
+
+		jobs = _.map _.filter(ids, (id) -> not finished_docs[id]), (id)->
 			return (cb)->
 				processNext(id, cb)
 		async.series jobs, (err)->
 			if err?
 				console.error err, "at end of jobs"
 			else
-				console.log "finished"
+				console.log "finished at #{new Date}"
 			clearInterval interval
 			done(err)


Text element visible synchronization fixed

--- style.coffee
+++ style.coffee
@@ -10,7 +10,8 @@

 module.exports = (File, data) -> class Style
 	{windowStyle, styles, queries} = data
-	{Tag, Text} = File.Element
+	{Element} = File
+	{Tag, Text} = Element

 	@__name__ = 'Style'
 	@__path__ = 'File.Style'
@@ -710,11 +711,10 @@
 			arr

 	# synchronize visibility
-	{Tag} = File.Element
 	opts = utils.CONFIGURABLE
-	getter = utils.lookupGetter Tag::, 'visible'
-	setter = utils.lookupSetter Tag::, 'visible'
-	utils.defineProperty Tag::, 'visible', opts, getter, do (_super = setter) ->
+	getter = utils.lookupGetter Element::, 'visible'
+	setter = utils.lookupSetter Element::, 'visible'
+	utils.defineProperty Element::, 'visible', opts, getter, do (_super = setter) ->
 		updateVisibility = (node) ->
 			if style = node._documentStyle
 				hasItem = !!style.item