combineAsArray: indirection via markEnd and setValue doesnt really add clarity

--- src/Bacon.coffee
+++ src/Bacon.coffee
@@ -162,10 +162,10 @@
           reply = sink end()
           unsubAll() if reply == Bacon.noMore
       initialSent = false
-      combiningSink = (markEnd, setValue, stream) => (unsubAll) ->
+      combiningSink = (stream, index) => (unsubAll) ->
         stream.subscribeInternal (event) =>
           if (event.isEnd())
-            markEnd()
+            ends[index] = true
             checkEnd(unsubAll)
             Bacon.noMore
           else if event.isError()
@@ -173,7 +173,7 @@
             unsubAll() if reply == Bacon.noMore
             reply
           else
-            setValue(event.value)
+            values[index] = event.value
             if _.all(values, ((x) -> x != None))
               if initialSent and event.isInitial()
                 # don't send duplicate Initial
@@ -186,15 +186,10 @@
                 reply
             else
               Bacon.more
-      sinkFor = (index, stream) ->
-        combiningSink(
-          (-> ends[index] = true)
-          ((x) -> values[index] = x)
-          stream)
       subs = for stream, index in streams
         do (stream, index) ->
           stream = Bacon.constant(stream) if not (stream instanceof Observable)
-          sinkFor index, stream
+          combiningSink stream, index
       compositeUnsubscribe subs...
   else
     Bacon.constant([])


call `console.error` on internal errors

call `console.error` on internal errors


Add tooltip specs for copying path into the clipboard when clicked

--- spec/built-in-tiles-spec.coffee
+++ spec/built-in-tiles-spec.coffee
@@ -65,6 +65,47 @@
         runs ->
           fileInfo.currentPath.click()
           expect(atom.clipboard.read()).toBe 'untitled'
+
+    describe "when buffer's path is not clicked", ->
+      it "doesn't display a path tooltip", ->
+        jasmine.attachToDOM(workspaceElement)
+        waitsForPromise ->
+          atom.workspace.open()
+
+        runs ->
+          expect(document.querySelector('.tooltip')).not.toExist()
+
+    describe "when buffer's path is clicked", ->
+      it "it displays path tooltip and the tooltip disappears after 2 seconds", ->
+        jasmine.attachToDOM(workspaceElement)
+        waitsForPromise ->
+          atom.workspace.open()
+
+        runs ->
+          fileInfo.currentPath.click()
+          expect(document.querySelector('.tooltip')).toBeVisible()
+          advanceClock(2000)
+          expect(document.querySelector('.tooltip')).not.toExist()
+
+    describe "when saved buffer's path is clicked", ->
+      it "it displays a tooltip containing text 'Copied:' and an absolute path", ->
+        jasmine.attachToDOM(workspaceElement)
+        waitsForPromise ->
+          atom.workspace.open('sample.txt')
+
+        runs ->
+          fileInfo.currentPath.click()
+          expect(document.querySelector('.tooltip')).toHaveText "Copied: #{fileInfo.getActiveItem().getPath()}"
+
+    describe "when unsaved buffer's path is clicked", ->
+      it "it displays a tooltip containing text 'Copied: untitled", ->
+        jasmine.attachToDOM(workspaceElement)
+        waitsForPromise ->
+          atom.workspace.open()
+
+        runs ->
+          fileInfo.currentPath.click()
+          expect(document.querySelector('.tooltip')).toHaveText "Copied: untitled"

     describe "when the associated editor's buffer's content changes", ->
       it "enables the buffer modified indicator", ->


Refactor fromList.

Refactor fromList.

--- src/jnoid.coffee
+++ src/jnoid.coffee
@@ -3,18 +3,13 @@
 Jnoid.fromDOM = ($, e)->

 Jnoid.fromList = (list)->
-  new EventStream( (sink)->
-    schedule = (xs) ->
-      if empty xs
-        sink Jnoid.end()
-      else
-        push xs
-    push = (xs) ->
-      reply = sink Jnoid.next head xs
-      unless reply == Jnoid.noMore
-        schedule tail xs
-    schedule list
-  )
+  new EventStream(sendWrapped(list, toEvent))
+
+sendWrapped = (values, wrapper) ->
+  (sink) ->
+    sink (wrapper value) for value in values
+    sink (Jnoid.end())
+    nop

 # Dummy objects for asserting.
 # Should not be equal.
@@ -26,7 +21,6 @@
 Jnoid.end = -> new End()

 class Event
-  isEvent: -> true
   isEnd: -> false
   isInitial: -> false
   isNext: -> false
@@ -66,7 +60,8 @@
   toEventStream: -> new EventStream(@subscribe)
   toString: -> "Dispatcher"

+nop = ->
 empty = (xs) -> xs.length == 0
 head = (xs) -> xs[0]
 tail = (xs) -> xs[1...xs.length]
-
+toEvent = (x) -> if x instanceof Event then x else Jnoid.next x


Fixed the version number

Fixed the version number

--- src/DivSugar.coffee
+++ src/DivSugar.coffee
@@ -1,7 +1,7 @@
 DivSugar =
   _initialize: ->
     # initialize constants
-    @VERSION = '1.0.5'
+    @VERSION = '1.2.0'
     @EPSILON = 0.0001
     @NUM_OF_DIGITS = 4
     @DEG_TO_RAD = Math.PI / 180


use the first time it appears instead

use the first time it appears instead

--- utils/media_time_fix.coffee
+++ utils/media_time_fix.coffee
@@ -13,7 +13,7 @@

 Message.find {}
-.sort {time: 1}
+.sort {time: -1}
 .select 'medias time'
 .lean()
 .stream()


Add IntensityChartCtrl controller.

Add IntensityChartCtrl controller.

--- app/javascripts/controllers.coffee
+++ app/javascripts/controllers.coffee
@@ -366,6 +366,9 @@
           # (cf. https://github.com/krispo/angular-nvd3/issues/316).
           # The work-around is to surgically intervene in the
           # defective ngnvd3 code.
+          # TODO - periodically check whether the ngnvd3 bug is
+          #   fixed and remove the work-around if possible.
+          #scope.isReady = false
           if _.isUndefined(scope.isBroken)
             broken = scope.api.refresh
             fixed = ->
@@ -374,13 +377,24 @@
               # to force the issue by calling the decorate function
               # directly in the refresh rather than indirectly via the
               # watcher.
-              # TODO - periodically check whether the ngnvd3 bug is
-              #   fixed and remove the work-around if possible.
-              #scope.isReady = false
+              # TODO - remove this work-around to a work-around bug when
+              #   the original work-around is fixed.
               broken()
               $scope.decorate(scope, element)
             scope.api.refresh = fixed
             scope.isBroken = false
+    ]
+
+
+    ctlrs.controller 'IntensityChartCtrl', [
+      '$scope', 'IntensityChart',
+      ($scope, IntensityChart) ->
+        # The chart {options, data} configuration.
+        chartConfig = IntensityChart.configure($scope.scan)
+        $scope.options = chartConfig.options
+        $scope.data = chartConfig.data
+        # The global configuration. See ModelingChartCtrl comment.
+        $scope.config = deepWatchData: false
     ]

@@ -403,18 +417,6 @@
           # objects with a circular object graph reference
           # (paramResult -> parent modelingResult -> child paramResult).
           $scope.config = deepWatchData: false
-    ]
-
-
-    ctlrs.controller 'IntensityChartCtrl', [
-      '$scope', 'IntensityChart',
-      ($scope, IntensityChart) ->
-        # The chart {options, data} configuration.
-        chartConfig = IntensityChart.configure($scope.session)
-        $scope.options = chartConfig.options
-        $scope.data = chartConfig.data
-        # The global configuration. See ModelingChartCtrl comment.
-        $scope.config = deepWatchData: false
     ]


job reload change

job reload change

--- server/job.coffee
+++ server/job.coffee
@@ -449,6 +449,9 @@
   future.wait()
   API.log {msg: 'Starting job runner with interval ' + interval, _cid: process.env.CID, _appid: process.env.APP_ID, function: 'API.job.start', level: 'debug'}
   job_limit.remove '*'
+  job_process.remove 'STUCK'
+  job_processing.remove 'STUCK'
+  job_result.remove 'STUCK'
   API.job.reload() # TODO this will be risky if multiple cluster machines try to run it - add a limit checker or something so it only runs on the first machine
   #job_process.remove 'TEST'
   #job_processing.remove 'TEST'


Revert "adding findUser"

Revert "adding findUser"

This reverts commit 9ee01a30f42c939957b44bf3622dbaf8eba8778f.

--- src/scripts/working-on.coffee
+++ src/scripts/working-on.coffee
@@ -15,15 +15,6 @@
 #   beezee

 module.exports = (robot) ->
-
-  findUser = (name) ->
-    users = robot.usersForFuzzyName name
-    if users.length is 1
-      users[0]
-    else if users.length > 1
-      users
-    else
-      false

   robot.respond /(what\'s|what is) (everyone|everybody) working on(\?)?/i, (msg) ->


use today's date in snippet filter

use today's date in snippet filter

--- app/scripts/filters/snippet.coffee
+++ app/scripts/filters/snippet.coffee
@@ -1,4 +1,7 @@
 'use strict';
+
+_MONTHS = ['january', 'february', 'march', 'april', 'may', 'june', 'july',
+  'august', 'september', 'october', 'november', 'december']

 angular.module("brokenPromisesApp").filter "snippet", ->
   (string, type) ->
@@ -11,14 +14,15 @@
     parts = string.split(".").slice(0, 50)
     i = 0
     end = parts.length
+    today = new Date
     while i < end
       switch type
         when "day"
-          theMatch = "7 october 2013"
+          theMatch = "#{do today.getDate} #{_MONTHS[do today.getMonth]} #{do today.getFullYear}"
         when "month"
-          theMatch = "october 2013"
+          theMatch = "#{_MONTHS[do today.getMonth]} #{do today.getFullYear}"
         when "year"
-          theMatch = "2013"
+          theMatch = "#{do today.getFullYear}"
       targetIndex = parts[i].indexOf(theMatch)
       return parts[i].replace(theMatch, "" + theMatch + "")  if targetIndex isnt -1
       i++


Change default behaviour of moveTo to viewport

--- app/scripts/lib/LazerScript.coffee
+++ app/scripts/lib/LazerScript.coffee
@@ -160,26 +160,31 @@
       @enemies[label] = constructor()

   moveTo: (settings) ->
-    defaults = {}
     =>
       @enemies ||= {}
+      enemy = @enemies[settings.enemy ? 'default']
+      return unless enemy?
+
+      defaults =
+        x: enemy.x + Crafty.viewport.x
+        y: enemy.y + Crafty.viewport.y
+        speed: enemy.speed
+

       # TODO: Verify Choreography element
-      enemy = @enemies[settings.enemy ? 'default']
-      defaults.speed = enemy?.speed
       settings = _.defaults(settings, defaults)
-      return unless enemy?
-      delta = if settings.x? then (Math.abs(settings.x)) else 0
-      delta += if settings.y? then (Math.abs(settings.y)) else 0
+
+      delta = if settings.x? then Math.abs(settings.x - (enemy.x + Crafty.viewport.x)) else 0
+      delta += if settings.y? then Math.abs(settings.y - (enemy.y + Crafty.viewport.y)) else 0

       defer = WhenJS.defer()
       enemy.choreography(
         [
-          type: 'linear'
+          type: 'viewport'
           x: settings.x
           y: settings.y
           maxSpeed: settings.speed
-          duration: (delta / settings.speed)
+          duration: (delta / (settings.speed / 50.0)) # FPS
         ]
       ).bind('ChoreographyEnd', ->
         @unbind('ChoreographyEnd')


Enable core specs when run outside of clone repo

The failing specs have been updated so that specs
can now run without the requirement of a Git repository
at the root.

--- spec/spec-suite.coffee
+++ spec/spec-suite.coffee
@@ -27,11 +27,6 @@
 runAllSpecs = ->
   {resourcePath} = atom.getLoadSettings()

-  # Only run core specs when resource path is the Atom repository
-  if Git.exists(resourcePath)
-    requireSpecs(path.join(resourcePath, 'spec'))
-    setSpecType('core')
-
   fixturesPackagesPath = path.join(__dirname, 'fixtures', 'packages')
   packagePaths = atom.packages.getAvailablePackageNames().map (packageName) ->
     atom.packages.resolvePackagePath(packageName)


Add support for fetching one issue from project

--- src/Models/ProjectIssues.coffee
+++ src/Models/ProjectIssues.coffee
@@ -28,4 +28,18 @@
       @get "projects/#{Utils.parseProjectId projectId}/issues", params, cb
     ).bind(@)

+  show: (projectId, issueId, params = {}, fn = null) =>
+    @debug "ProjectIssues::show()"
+    if projectId.toString().indexOf("/") isnt -1
+      projectId = encodeURIComponent(projectId)
+    else
+      projectId = parseInt(projectId)
+
+    if issueId.toString().indexOf("/") isnt -1
+      issueId = encodeURIComponent(issueId)
+    else
+      issueId = parseInt(issueId)
+
+    @get "projects/#{projectId}/issues/#{issueId}", params, (data) -> fn data if fn
+
 module.exports = (client) -> new ProjectIssues client


Remove TODO regarding validation of method_name argument to expects().

Although this is illegal:

  o = {}
  o.1a = -> "a function"

you can still do this:

  o = {}
  o["1a"] = -> "a function"
  o["1a"]()                  # => "a function"

--- src/TinyMock.coffee
+++ src/TinyMock.coffee
@@ -65,7 +65,6 @@
         test_function = args[1]
     expects_method_name ?= "expects"
     mock_count ?= 5
-    # TODO: what happens if expects_method_name is not a legal method name?

   check_expects_method_name = ->
     fail(messages.ExpectsMethodAlreadyExists, expects_method_name) if Object.prototype[expects_method_name]?


Use the verb 'uninstall' instead of 'restore'

--- src/TinyMock.coffee
+++ src/TinyMock.coffee
@@ -51,7 +51,7 @@
     fail( errors.join("\n") + "\n" ) if errors.length > 0

   cleanup_environment: ->
-    @_mock_methods.restore_original_methods()
+    @_mock_methods.uninstall_mock_methods()
     @uninstall_expects_method()

   check_usage: (args) ->
@@ -133,17 +133,17 @@
     expectation._called = yes
     throw expectation._throws if expectation._throws
     expectation._returns
+
+  mock_method.uninstall = ->
+    if original_method?
+      object[method_name] = original_method
+    else
+      delete object[method_name]

   mock_method.create_expectation = ->
     expectation = new Expectation()
     expectations.register(expectation)
     expectation
-
-  mock_method.restore_original_method = ->
-    if original_method?
-      object[method_name] = original_method
-    else
-      delete object[method_name]

   mock_method.find_errors = ->
     expectations.find_errors(method_name)
@@ -248,8 +248,8 @@
   find_errors: ->
     @_list.reduce ( (errors, mock_method) -> errors.concat(mock_method.find_errors()) ), []

-  restore_original_methods: ->
-    mock_method.restore_original_method() for mock_method in @_list
+  uninstall_mock_methods: ->
+    mock_method.uninstall() for mock_method in @_list

 #


Translated IFC Videira description to English, and added an image.

--- app/views/play/MainPlayView.coffee
+++ app/views/play/MainPlayView.coffee
@@ -323,8 +323,8 @@
     name: "IFC - Videira"
     difficulty: 3
     id: 'ifc-videira'
-    image: ''
-    description: 'Nível inspirado em IFC - Videira.'
+    image: '/file/db/level/52602ecb026e8481e7000001/generic_1.png'
+    description: 'A level inspired by IFC Videira. - by Leonardo Meneguzzi.'
   }
 ]


Fix typos

Fix typos

--- src/panel-container.coffee
+++ src/panel-container.coffee
@@ -8,7 +8,7 @@
     @panels = []

   destroy: ->
-    pane.destroy() for pane in @getPanels()
+    panel.destroy() for panel in @getPanels()
     @subscriptions.dispose()
     @emitter.emit 'did-destroy', this
     @emitter.dispose()
@@ -39,7 +39,7 @@
   getPanels: -> @panels

   addPanel: (panel) ->
-    @subscriptions.add panel.onDidDestroy(@panelDestoryed.bind(this))
+    @subscriptions.add panel.onDidDestroy(@panelDestroyed.bind(this))

     index = @getPanelIndex(panel)
     if index is @panels.length
@@ -50,7 +50,7 @@
     @emitter.emit 'did-add-panel', {panel, index}
     panel

-  panelDestoryed: (panel) ->
+  panelDestroyed: (panel) ->
     index = @panels.indexOf(panel)
     if index > -1
       @panels.splice(index, 1)


fix doc

fix doc

--- lib/assets/javascripts/up/flow.js.coffee
+++ lib/assets/javascripts/up/flow.js.coffee
@@ -144,8 +144,8 @@
   @param {String} [options.title]
   @param {String} [options.source]
   @param {Object} [options.transition]
-  @param {String] [options.history]
-  @param {String] [options.historyMethod='push']
+  @param {String} [options.history]
+  @param {String} [options.historyMethod='push']
   ###
   implant = (selector, html, options) ->


Add support for max_line_length

Add support for max_line_length

--- src/brave-mouse/validate.coffee
+++ src/brave-mouse/validate.coffee
@@ -5,6 +5,7 @@
 detectNewline = require 'detect-newline'
 detectTrailingWhitespace = require 'detect-trailing-whitespace'
 trailingNewline = require 'trailing-newline'
+maxLineLength = require 'max-line-length'

 module.exports = (filePath, callback) ->
 	editorconfig.parse(filePath)
@@ -60,6 +61,14 @@
 						expected: editorconfigProperties.insert_final_newline
 						is: hasFinalNewline

+			if editorconfigProperties.max_line_length
+				maximumLineLength = maxLineLength fileContents
+
+				if maximumLineLength > editorconfigProperties.max_line_length
+					results.max_line_length =
+						expected: editorconfigProperties.max_line_length
+						is: maximumLineLength
+
 			results = true if Object.keys(results).length is 0

 			callback null, results


检验是否有builderconfig

检验是否有builderconfig

--- lib/rapid-dev/rapid-dev-mode-view.coffee
+++ lib/rapid-dev/rapid-dev-mode-view.coffee
@@ -81,6 +81,7 @@
   readConfig: (path,type) ->
     configFileName = if type is 'module' then Desc.moduleConfigFileName else Desc.projectConfigFileName
     configPath = PathM.join path,configFileName
+
     try
       config = Util.readJsonSync configPath
     catch err
@@ -113,12 +114,19 @@

   addModuleItem: (projectPath,modules) ->
     htmlStr = ''
-    for module,version of modules
-      modulePath = PathM.join projectPath,Desc.moduleLocatFileName,module
-      moduleConfig = @readConfig modulePath, 'module'
-      htmlStr += @getModuleItemHtmlStr moduleConfig if moduleConfig?
-    htmlStr = "

#{Desc.noModules}

" if htmlStr is ''
-    @codePackList.append htmlStr
+    modulesDir = PathM.join projectPath,Desc.moduleLocatFileName
+    Util.readDir modulesDir, (err,files) =>
+      return console.error err if err?
+      console.log files
+      for module,version of modules
+        modulePath = PathM.join modulesDir,module
+        if Util.isFileExist PathM.join modulePath, Desc.builderConfigFileName
+          moduleConfig = @readConfig modulePath, 'module'
+          htmlStr += @getModuleItemHtmlStr moduleConfig if moduleConfig?
+
+      htmlStr = "

#{Desc.noModules}

" if htmlStr is ''
+      @codePackList.append htmlStr
+

   menuClick: (target) ->
     return @openFolder() if target is @other[0]


Editor#toJSON includes only the serializable parts of the document

--- src/trix/models/editor.coffee
+++ src/trix/models/editor.coffee
@@ -21,7 +21,7 @@
     @composition.loadDocument(@document)

   toJSON: ->
-    document: @document
+    document: @document.toSerializableDocument()
     locationRange: @locationRange

   # Forward attachment manager


Update fetch-subject-sets-mixin.cjsx

Update fetch-subject-sets-mixin.cjsx

--- app/assets/javascripts/lib/fetch-subject-sets-mixin.cjsx
+++ app/assets/javascripts/lib/fetch-subject-sets-mixin.cjsx
@@ -64,7 +64,7 @@
   fetchSubjectSets: (params, callback) ->
     # Apply defaults to unset params:
     _params = $.extend({
-      limit: 2
+      limit: 1 #10 # temporary fix for large subject_sets -STI
       workflow_id: @getActiveWorkflow().id
       random: true
     }, params)


removed empty spacings. Run jenkins, run!

--- app/assets/javascripts/oxalis/view/arbitrary_view.coffee
+++ app/assets/javascripts/oxalis/view/arbitrary_view.coffee
@@ -28,7 +28,7 @@

     _.extend(this, new EventMixin())

-    # The "render" div serves as a container for the canvas, that is
+    # The "render" div serves as a container for the canvas, that is
     # attached to it once a renderer has been initalized.
     @container = $(canvas)
     @width  = @container.width()
@@ -42,7 +42,7 @@
     @camera = camera = new THREE.PerspectiveCamera(45, @width / @height, 50, 1000)
     camera.matrixAutoUpdate = false
     camera.aspect = @width / @height
-
+
     @cameraPosition = [0, 0, @CAM_DISTANCE]

     @group = new THREE.Object3D
@@ -96,10 +96,10 @@
         geometry.update()

       m = @dataCam.getZoomedMatrix()
-
-      camera.matrix.set m[0], m[4], m[8],  m[12],
-                        m[1], m[5], m[9],  m[13],
-                        m[2], m[6], m[10], m[14],
+
+      camera.matrix.set m[0], m[4], m[8],  m[12],
+                        m[1], m[5], m[9],  m[13],
+                        m[2], m[6], m[10], m[14],
                         m[3], m[7], m[11], m[15]

       camera.matrix.multiply( new THREE.Matrix4().makeRotationY( Math.PI ))
@@ -119,14 +119,14 @@
       @trigger("finishedRender")

     window.requestAnimationFrame => @animate()
-

-  draw : ->
+
+  draw : ->

     @forceUpdate = true

-  addGeometry : (geometry) ->
+  addGeometry : (geometry) ->
     # Adds a new Three.js geometry to the scene.
     # This provides the public interface to the GeometryFactory.


Don't return early in case where last arg is not a function

--- libraries/metrics/timeAsyncMethod.coffee
+++ libraries/metrics/timeAsyncMethod.coffee
@@ -14,7 +14,6 @@

 		if !callback? || typeof callback != 'function'
 			logger.log "[Metrics] expected wrapped method '#{methodName}' to be invoked with a callback"
-			return

 		timer = new metrics.Timer(key)


fix line numbers after heregexps

fix line numbers after heregexps

--- src/lexer.coffee
+++ src/lexer.coffee
@@ -189,7 +189,11 @@
   # JavaScript and Ruby.
   regexToken: ->
     return 0 if @chunk.charAt(0) isnt '/'
-    return @heregexToken match if match = HEREGEX.exec @chunk
+    if match = HEREGEX.exec @chunk
+      length = @heregexToken match
+      @line += count match[0], '\n'
+      return length
+
     prev = last @tokens
     return 0 if prev and (prev[0] in (if prev.spaced then NOT_REGEX else NOT_SPACED_REGEX))
     return 0 unless match = REGEX.exec @chunk


Allow to rotate photos immediately after upload

--- client/app/views/galery.coffee
+++ client/app/views/galery.coffee
@@ -123,6 +123,12 @@
         parts = url.split('/')
         id = parts[parts.length - 1]
         id = id.split('.')[0]
+        # if collection has not been saved, we must search by url
+        if not @collection.get(id)?
+            photo = @collection.find (e) ->
+                return e.attributes.src.split('/').pop().split('.')[0] is id
+            if photo?
+                id = photo.cid
         return id

     # Rotate 90° left the picture by updating css and orientation.


pushstate navigation for datasets breadrumb

--- client/code/view/titles.coffee
+++ client/code/view/titles.coffee
@@ -20,6 +20,7 @@
 class Cu.View.DataSetTitle extends Cu.View.Title
   tagName: 'h2'
   events:
+    'click a': 'datasetsClicked'
     'click .editable': 'nameClicked'
     'blur input': 'editableNameBlurred'
     'keypress input': 'keypressOnEditableName'
@@ -37,6 +38,10 @@
     """
     @$el.html tpl
     @
+
+  datasetsClicked: (e) ->
+    e.preventDefault()
+    window.app.navigate "/", {trigger: true}

   nameClicked: (e) ->
     e.preventDefault()


Open chocolate when DOM ready but not when loading finish. Change hashtag when closing chocolate.

--- src/plugins/history.coffee
+++ src/plugins/history.coffee
@@ -24,13 +24,13 @@
       getImageFromUri()
       return

-  addEvent window, 'load', ->
+  addEvent document, 'DOMContentLoaded', ->
     getImageFromUri()
     return

   pushState = (title, hash) ->
     title = title or ''
-    return unless hash
+    hash = hash or ''

     window.history.pushState null, title, "##{hash}"
     return


Clean beautify.coffee

Clean beautify.coffee

--- src/beautify.coffee
+++ src/beautify.coffee
@@ -3,9 +3,9 @@
 expandEntity = (text, entity) ->
   text = text.replace entity.url, entity.expanded_url
   text = text.replace entity.url, entity.display_url
+  text

 getImage = (entity, standard) ->
-
   media =
     url: entity.media_url
     w: parseInt entity.sizes.medium.w, 10
@@ -22,45 +22,52 @@
   inline = ""
   inline += ""
   inline += ""
+  inline

 pad = (n) -> if n < 10 then "0#{n}" else n

 module.exports = (options, counter) ->
-  return (result, auto = true) ->
+  return (tweet, auto = true) ->
     if auto is true
-      text = twitter.autoLink result.text, target: '_blank'
+      text = twitter.autoLink tweet.text, target: '_blank'
     else
-      text = twitter.autoLinkUrlsCustom result.text, target: '_blank'
+      text = twitter.autoLinkUrlsCustom tweet.text, target: '_blank'

-    if result.entities.urls? and result.entities.urls.length > 0
-      for entity in result.entities.urls
+    if tweet.entities.urls? and tweet.entities.urls.length > 0
+      for entity in tweet.entities.urls
         text = expandEntity text, entity

     inline = false
-    if result.entities.media? and result.entities.media.length > 0
-      for entity in result.entities.media
+    if tweet.entities.media? and tweet.entities.media.length > 0
+      for entity in tweet.entities.media
         text = expandEntity text, entity
         inline = getImage entity, options.media

+    tweet.text = text
+    tweet.text += "\n#{inline}" if inline

-    result.text = text
-    result.text += inline if inline
+    image = tweet.user.profile_image_url
+    pos = image.lastIndexOf '_'
+    tweet.user.profile_image_url = "#{image.substring(0, pos)}_bigger#{image.substring(pos + 7)}"

-    image = result.user.profile_image_url
-    pos = image.lastIndexOf '_'
-    result.user.profile_image_url = "#{image.substring(0, pos)}_bigger#{image.substring(pos + 7)}"
+    created = new Date tweet.created_at

-    date = new Date result.created_at
-    created = "#{date.getFullYear()}-#{pad(date.getMonth() + 1)}-#{pad(date.getDate())} #{pad(date.getHours())}:#{pad(date.getMinutes())}"
+    date = [
+      created.getFullYear()
+      pad created.getMonth() + 1
+      pad created.getDate()
+    ]

-    data =
-      id: result.id_str
-      link: "http://twitter.com/#{result.user.screen_name}"
-      avatar: result.user.profile_image_url
-      login: result.user.screen_name
-      name: result.user.name or result.user.screen_name
-      text: result.text
-      date: created
-      iso: date.toISOString()
+    time = [
+      pad created.getHours()
+      pad created.getMinutes()
+    ]

-    data
+    id: tweet.id_str
+    link: "http://twitter.com/#{tweet.user.screen_name}"
+    avatar: tweet.user.profile_image_url
+    login: tweet.user.screen_name
+    name: tweet.user.name or tweet.user.screen_name
+    text: tweet.text
+    date: date.join('-') + ' ' + time.join(':')
+    iso: date.toISOString()


Fix weird editor issues (#120, #121)

Fix weird editor issues (#120, #121)

- Why do I have to create editor instance each time? medium-editor sucks

--- client/views/admin/edit.coffee
+++ client/views/admin/edit.coffee
@@ -30,6 +30,7 @@
 save = (tpl, cb) ->
   $form = tpl.$('form')
   $editable = $('.editable', $form)
+  editor = BlogEditor.make tpl

   # Make paragraphs commentable
   i = $editable.find('p[data-section-id]').length + 1
@@ -38,10 +39,10 @@
     i++

   # Highlight code blocks
-  tpl.editor.highlightSyntax()
+  editor.highlightSyntax()

   if $editable.is(':visible')
-    body = tpl.editor.contents()
+    body = editor.contents()
   else
     body = $('.html-editor', $form).val().trim()

@@ -112,7 +113,7 @@
     $tags.tagsinput('input').typeahead 'val', ''

   # Medium editor
-  @editor = BlogEditor.make @
+  BlogEditor.make @

 Template.blogAdminEdit.helpers
   post: ->
@@ -124,7 +125,7 @@
     if tpl.$('.editable').is(':visible')
       return

-    tpl.editor.highlightSyntax()
+    BlogEditor.make(tpl).highlightSyntax()
     setEditMode tpl, 'visual'

   'click .html-toggle': (e, tpl) ->
@@ -133,7 +134,7 @@
     if $html.is(':visible')
       return

-    $html.val tpl.editor.pretty()
+    $html.val BlogEditor.make(tpl).pretty()
     setEditMode tpl, 'html'
     $html.height($editable.height())


Transitions & colors tested. Going to refactor some of the code to simplify the logic

--- src/progress_circle.coffee
+++ src/progress_circle.coffee
@@ -1,15 +1,31 @@
+# Shape size defaults
 svgHeight = 500
 svgWidth = 500
 innerRadius = 75
 outerRadius = 100
-circleRadius = 50
+cornerRadius = 20
+circleRadius = 60
 innerPercent = .15
 outerPercent = .50
 innerThickness = 10
 outerThickness = 20
-defaultArcColor = 'chartreuse'
+
+# Color defaults
+defaultActualArcColor = '#78C000'
+defaultExpectedArcColor = '#C7E596'
+default50DeltaColor = 'red'
+default25DeltaColor = 'orange'
+defaultCircleColor = '#F4F4F4'
+defaultTextColor = '#777777'
+defaultTextPercentColor = '#444444'
+
+# Font defaults
+defaultTextFontSize = "15px"
+defaultTextPercentFontSize = "40px"
+
+# Timing defaults
 defaultDuration = 750
-defaultDelay = 500
+defaultDelay = 100

 actualArc = {}
 expectedArc = {}
@@ -28,17 +44,29 @@
                 # .attr("cx", 250)
                 # .attr("cy", 250)
                 .attr("r", radius)
-                .style("fill", "purple")
+                .style("fill", defaultCircleColor)

         # Draw the percentage text on top of the circle
         drawText = (percent) ->
             console.log("drawText: #{percent}")
-            # svg.append("text")
-            #     .text(percent)

-        transitionText = (text) ->
-            console.log("transitionText")
+            # Progress label
+            svg.append("text")
+                .text "Progress"
+                .attr "font-size", defaultTextFontSize
+                .attr "font-family", "sans-serif"
+                .attr "fill", defaultTextColor
+                .style "text-anchor", "middle"
+                .attr("transform", "translate(0, 15)")

+            # Percent value
+            svg.append("text")
+                .text percent*100+"%"
+                .attr "font-size", defaultTextPercentFontSize
+                .attr "font-family", "sans-serif"
+                .attr "fill", defaultTextColor
+                .style "text-anchor", "middle"
+                .attr("transform", "translate(0, -5)")

         # Create and return SVG arc
         createArc = (percent, radius, thickness) ->
@@ -47,35 +75,32 @@
             arc = d3.svg.arc()
                 .innerRadius radius - thickness
                 .outerRadius radius
+                .cornerRadius cornerRadius
                 .startAngle 0
                 #.endAngle 2*Math.PI*percent

-        # Append
-        drawArc = (arc, colorIsRed) ->
-            color = defaultArcColor
-            if colorIsRed
-                color = 'red'
+        drawArc = (arc, color) ->
+            console.log("drawArc")

             arcValue = svg.append 'path'
                 .datum
                     endAngle: 0
                 .style 'fill', color
                 .attr 'd', arc
-

         drawExpectedArc = ->
             console.log("drawExpectedArc")

             @expectedArc = createArc(attrs.expected, innerRadius, innerThickness)
-            @expectedArcValue = drawArc(@expectedArc, false)
+            @expectedArcValue = drawArc(@expectedArc, defaultExpectedArcColor, false)

         drawActualArc = ->
             console.log("drawActualArc")

             @actualArc = createArc(attrs.actual, outerRadius, outerThickness)
-            colorIsRed = arcColorIsRed(attrs.actual, attrs.expected)
-            @actualArcValue = drawArc(@actualArc, colorIsRed)
-            @text = drawText(attrs.actual)
+            color = getActualArcColor(attrs.actual, attrs.expected)
+            @actualArcValue = drawArc(@actualArc, color)
+            @textPercent = drawText(attrs.actual)

         drawBothArcs = ->
             console.log("drawBothArcs")
@@ -85,26 +110,26 @@

         transitionBothArcs = ->
             console.log("transitionBothArcs")
-
+            sanitizeInputs()
             transitionActualArc()
             transitionExpectedArc()

         transitionExpectedArc = ->
             console.log("transitionExpectedArc")
-
-            transitionArc(@expectedArc, @expectedArcValue, attrs.expected, defaultDuration, 0)
+            color = getActualArcColor(attrs.actual, attrs.expected)
+            if (color != defaultActualArcColor)
+                transitionArc(@actualArc, @actualArcValue, attrs.actual, defaultDuration, color)
+            transitionArc(@expectedArc, @expectedArcValue, attrs.expected, defaultDuration, defaultExpectedArcColor)

         transitionActualArc = ->
             console.log("transitionActualArc")
-            colorIsRed = arcColorIsRed(attrs.actual, attrs.expected)
-            transitionArc(@actualArc, @actualArcValue, attrs.actual, defaultDuration, colorIsRed)
-            transitionText(attrs.actual)
+            color = getActualArcColor(attrs.actual, attrs.expected)
+            transitionArc(@actualArc, @actualArcValue, attrs.actual, defaultDuration, color)
+            @textPercent.transition()
+                .text attrs.actual*100+"%"

-        transitionArc = (arc, arcValue, percent, duration, colorIsRed) ->
-            console.log("transitionArc")
-            color = defaultArcColor
-            if (colorIsRed)
-                color = 'red'
+        transitionArc = (arc, arcValue, percent, duration, color) ->
+            console.log("transitionArc. percent: #{percent}")

             arcValue.transition()
                 .delay defaultDelay
@@ -113,14 +138,6 @@
                 #.style 'color' color
                 .call arcTween, arc, 2*Math.PI*percent
             arcValue.style('fill', color)
-
-
-            # if (colorIsRed)
-            #     arcValue.transition()
-            #         .style 'fill', 'red'
-            # else
-            #     arcValue.transition()
-            #         .style 'fill', 'chartreuse'

         arcTween = (transition, arc, newAngle) ->
             console.log("arcTween. newAngle: #{newAngle}")
@@ -132,30 +149,48 @@
                     arc d

         # return color based on values of actual and expected percents
-        arcColorIsRed = (actualPercent, expectedPercent) ->
-            console.log("arcColor: #{actualPercent}, #{expectedPercent}")
-            returnVal = 0
+        getActualArcColor = (actualPercent, expectedPercent) ->
+            console.log("getActualArcColor: #{actualPercent}, #{expectedPercent}")
+
             console.log("checking color: actual: #{actualPercent}, #{expectedPercent}")
-            if expectedPercent - actualPercent >= .5
-                console.log("expected 50 > actual")
-                returnVal = 1
-            if actualPercent > .25
-                console.log("actual > 25")
-                returnVal = 1
+            if expectedPercent - actualPercent > .5
+                console.log("50% delta")
+                return default50DeltaColor
+            if expectedPercent - actualPercent > .25
+                console.log("25% delta")
+                return default25DeltaColor

-            returnVal
+            defaultActualArcColor
+
+        sanitizeInputs = ->
+            console.log("sanitizeInputs before: #{attrs.actual}, #{attrs.expected}")
+            if (isNaN attrs.actual)
+                attrs.actual = 0
+            if (isNaN attrs.expected)
+                attrs.expected = 0
+
+            if (attrs.actual > 1)
+                attrs.actual = 1
+            if (attrs.actual < 0)
+                attrs.actual = 0
+
+            if (attrs.expected > 1)
+                attrs.expected = 1
+            if (attrs.expected < 0)
+                attrs.expected = 0
+
+            console.log("sanitizeInputs after: #{attrs.actual}, #{attrs.expected}")

         # Create circle, innerArc, & outerArc
         svg = d3.select('svg')
             .append('g')
                 .attr("transform", "translate(#{svgWidth/2}, #{svgHeight/2})")
-        # console.log("SVG: "+svg)
-        # console.log("attrs "+attrs)
-        # console.log("scope #{scope.actual}")
+
         console.log("Actual: #{attrs.actual}")
         console.log("Expected: #{attrs.expected}")
+        sanitizeInputs()
+        drawCircle(innerPercent, circleRadius)
         drawBothArcs()
-        drawCircle(innerPercent, circleRadius)
         scope.$watch 'expected', transitionBothArcs
         scope.$watch 'actual', transitionBothArcs


remove obsolete test

remove obsolete test

--- src/protomixin.spec.coffee
+++ src/protomixin.spec.coffee
@@ -192,21 +192,6 @@
         Example.mixinto_proto @mixin, omits: ['bar', 'baz']
       ).toThrow new errors.ArgumentError "Found nothing to mix in!"

-    xit 'should throw an error when supplying a non object-literal hooks option', ->
-      non_obj_literals = [
-        []
-        1
-        'String'
-        false
-        null
-      ]
-
-      for non_obj_literal in non_obj_literals
-        expect(=>
-          class Example
-          Example.mixinto_proto @mixin, pre_mixinmethod_hooks: non_obj_literal
-        ).toThrow new errors.ArgumentError "Expected object literal for hooks option"
-
     it 'should throw an error when the hook configuration is not an Array of Strings', ->
       bad_hook_values = [
         'String'


Punycode builtin changed.

Punycode builtin changed.

--- src/builtins.coffee
+++ src/builtins.coffee
@@ -25,7 +25,7 @@
   net:                  null
   os:                   'os-browserify/browser.js'
   path:                 'path-browserify/index.js'
-  punycode:             'punycode/index.js'
+  punycode:             'punycode/punycode.js'
   querystring:          'querystring-es3/index.js'
   readline:             null
   repl:                 null


rename private method name.

rename private method name.

--- coffee/services/redmine.coffee
+++ coffee/services/redmine.coffee
@@ -86,7 +86,7 @@
   ###
    set basic configs for $http.
   ###
-  setBasicConfig: (config, auth) ->
+  _setBasicConfig: (config, auth) ->
     config.headers = "Content-Type": Redmine.CONTENT_TYPE
     config.timeout = config.timeout or Redmine.AJAX_TIME_OUT
     if auth.apiKey? and auth.apiKey.length > 0
@@ -97,7 +97,7 @@

   ###
-   load issues following selected project
+   load issues.
   ###
   getIssues: (success, error, params) ->
     params.limit = params.limit or 100
@@ -107,7 +107,7 @@
       url: @auth.url + "/issues.json"
       params: params
       timeout: cancelDefer.promise
-    config = @setBasicConfig config, @auth
+    config = @_setBasicConfig config, @auth
     @$http(config)
       .success((data, status, headers, config) =>
         data.url = @auth.url
@@ -150,7 +150,7 @@
     config =
       method: "GET"
       url: @auth.url + "/issues/#{issueId}.json"
-    config = @setBasicConfig config, @auth
+    config = @_setBasicConfig config, @auth
     @$http(config)
       .success((data, status, headers, config) =>
         if data?.issue?
@@ -182,7 +182,7 @@
       method: "POST"
       url: @auth.url + "/issues/#{@_timeEntryData.time_entry.issue_id}/time_entries.json"
       data: Redmine.JSONtoXML @_timeEntryData
-    config = @setBasicConfig config, @auth
+    config = @_setBasicConfig config, @auth
     config.headers = "Content-Type": "application/xml"
     @$http(config)
       .success((data) =>
@@ -206,7 +206,7 @@
       method: "GET"
       url: @auth.url + "/time_entries.json"
       params: params
-    config = @setBasicConfig config, @auth
+    config = @_setBasicConfig config, @auth
     @$http(config)
       .success((args...) =>
         args[0].url = @auth.url
@@ -230,7 +230,7 @@
       method: "GET"
       url: @auth.url + "/projects.json"
       params: params
-    config = @setBasicConfig config, @auth
+    config = @_setBasicConfig config, @auth
     @$http(config)
       .success( (data, status, headers, config) =>
         data.url = @auth.url
@@ -259,7 +259,7 @@
     config =
       method: "GET"
       url: @auth.url + "/users/current.json?include=memberships"
-    config = @setBasicConfig config, @auth
+    config = @_setBasicConfig config, @auth
     @$http(config)
       .success( (data, status, headers, config) =>
         if not data.user or not data.user.id
@@ -297,7 +297,7 @@
     config =
       method: "GET"
       url: @auth.url + "/enumerations/time_entry_activities.json"
-    config = @setBasicConfig config, @auth
+    config = @_setBasicConfig config, @auth
     @$http(config)
       .success( (data, status, headers, config) =>
         data.url = @auth.url
@@ -317,7 +317,7 @@
       method: "GET"
       url: @auth.url + "/queries.json"
       params: params
-    config = @setBasicConfig config, @auth
+    config = @_setBasicConfig config, @auth
     @$http(config)
       .success((args...) =>
         args[0].url = @auth.url


Don't keep comments.

Don't keep comments.

--- src/nodes.coffee
+++ src/nodes.coffee
@@ -458,7 +458,8 @@
   compileNode: (o, level) ->
     code = '/*' + multident(@comment, @tab) + "\n#{@tab}*/"
     code = o.indent + code if (level or o.level) is LEVEL_TOP
-    code
+    #code
+    ""

 #### Call


cooldown finilazation

cooldown finilazation

--- src/cooldown.coffee
+++ src/cooldown.coffee
@@ -14,7 +14,7 @@
     target ||= 0
     approachBy = 1 unless approachBy?

-
+    # Set
     I.cooldowns[name] = {
       options.target
       options.approachBy


nothing to see here.

nothing to see here.

--- web/app/source/index.cjsx
+++ web/app/source/index.cjsx
@@ -43,13 +43,11 @@
   render: ->
     <div className="container">
       <ChannelGraph channel={35686} chartType="egg"/>
-
       <ChannelGraph channel={35686} chartType="roofBarChart"/>
       <ChannelGraph channel={35686} chartType="roofLineChart"/>
       <ChannelGraph channel={35687} chartType="roofBarChart"/>
       <ChannelGraph channel={35687} chartType="roofLineChart"/>
       <ChannelGraph channel={35688} chartType="lineChart"/>
-
     </div>


Show the last line unless its empty (Resolves #113)

--- SingularityUI/app/collections/LogLines.coffee
+++ SingularityUI/app/collections/LogLines.coffee
@@ -43,8 +43,9 @@
         # split on newlines
         lines = result.data.split @delimiter

-        # omit the last element, since it'll either be a blank or incomplete line
-        lines = _.initial(lines)
+        # omit the last element only if it's blank
+        if lines[lines.length - 1] is ''
+            lines = _.initial(lines)

         # omit the first (incomplete) element unless we're at the beginning of the file
         if offset > 0 and lines.length > 0


cayw cleanup

cayw cleanup

--- chrome/content/zotero-better-bibtex/web-endpoints.coffee
+++ chrome/content/zotero-better-bibtex/web-endpoints.coffee
@@ -248,25 +248,28 @@
     citation = []

     # {"citationItems":[{"id":"5","locator":"page","prefix":"prefix","suffix":"suffix","suppress-author":true}],"properties":{}}
+    items = []
+    for item in @citation.citationItems
+      citekey = Zotero.BetterBibTeX.keymanager.get({itemID: item.id}, 'on-export')
+      continue unless citekey
+      item.citekey = citekey.citekey
+      items.push(item)
+
     switch @config.format
       when 'mmd'
-        for item in @citation.citationItems
-          citekey = Zotero.BetterBibTeX.keymanager.get({itemID: item.id}, 'on-export')
-          continue unless citekey
+        for item in items
           if item.prefix
-            citation.push("[#{item.prefix}][##{citekey.citekey}]")
+            citation.push("[#{item.prefix}][##{item.citekey}]")
           else
-            citation.push("[##{citekey.citekey}][]")
+            citation.push("[##{item.citekey}][]")
         citation = citation.join('')

       when 'pandoc'
-        for item in @citation.citationItems
-          citekey = Zotero.BetterBibTeX.keymanager.get({itemID: item.id}, 'on-export')
-          continue unless citekey
+        for item in items
           cite = ''
           cite += "#{item.prefix} " if item.prefix
           cite += '-' if item['suppress-author']
-          cite += "@#{citekey.citekey}"
+          cite += "@#{item.citekey}"
           cite += ", #{item.locator}" if item.locator
           cite += " #{item.suffix}" if item.suffix
           citation.push(cite)
@@ -276,9 +279,8 @@
           citation = '[' + citation.join(';') + ']'

       else
-        for item in @citation.citationItems
-          cite = Zotero.BetterBibTeX.keymanager.get({itemID: item.id}, 'on-export').citekey
-          citation.push(@config.keyprefix + cite + @config.keypostfix)
+        for item in items
+          citation.push(@config.keyprefix + item.citekey + @config.keypostfix)
         if citation.length == 0
           citation = ''
         else


Added two new endpoints: 'search' and 'item'

--- chrome/content/zotero-better-bibtex/web-endpoints.coffee
+++ chrome/content/zotero-better-bibtex/web-endpoints.coffee
@@ -127,3 +127,93 @@

   response = response[0] unless batchRequest
   return sendResponseCallback(200, 'application/json', JSON.stringify(response))
+
+Zotero.BetterBibTeX.endpoints.search = { supportedMethods: ['GET'] }
+Zotero.BetterBibTeX.endpoints.search.init = (url, data, sendResponseCallback) ->
+  try
+    query = url.query.query
+  catch err
+    query = null
+
+  if not query
+    sendResponseCallback(501, 'text/plain', 'No query specified')
+    return
+
+  try
+    Zotero = Components.classes["@zotero.org/Zotero;1"].getService(Components.interfaces.nsISupports).wrappedJSObject
+    search = new Zotero.Search()
+
+    # default search conditions - addCondition(condition, operator, value, required)
+    search.addCondition('quicksearch-titleCreatorYear', 'contains', query, false)
+#    search.addCondition('quicksearch-everything', 'contains', query, false)
+
+    # fetching result IDs
+    results = search.search()
+
+    if not results
+      Zotero.BetterBibTeX.log("No results found")
+      sendResponseCallback(200, 'text/plain', JSON.stringify([]))
+      return
+
+    # fetching items using IDs
+    items = Zotero.Items.get(results)
+
+    selected = []
+    for item in items
+      response = {
+        id: item.id
+        key: item.key
+        libraryKey: item.libraryKey
+        title: item.getField('title')
+        date: item.getField('date')
+        extra: item.getField('extra')
+        creators: []
+      }
+
+      for creator in item.getCreators()
+        author = {}
+        author.lastName = creator.ref.lastName
+        author.firstName = creator.ref.firstName
+        response.creators.push(author)
+      selected.push(response)
+
+    Zotero.BetterBibTeX.log("Search results for query #{query}")
+    sendResponseCallback(200, 'text/plain', JSON.stringify(selected))
+
+  catch err
+    Zotero.BetterBibTeX.log("Could not perform search: #{err}", err)
+    sendResponseCallback(404, 'text/plain', "Could not perform search: #{err}")
+
+Zotero.BetterBibTeX.endpoints.item = { supportedMethods: ['POST'] }
+Zotero.BetterBibTeX.endpoints.item.init = (url, data, sendResponseCallback) ->
+  data = JSON.parse(data)
+  response = []
+
+  if data
+    translator = if data.translator then data.translator else "betterbiblatex"
+    format = if data.format then data.format else "json"
+    items = switch
+      when data.ids then Zotero.Items.get(data.ids)
+      when data.bibtexKeys then Zotero.Items.get(Zotero.BetterBibTeX.schomd.item(data.bibtexKeys))
+
+  try
+    if format is "json"
+      for item in items
+        bibtex = Zotero.BetterBibTeX.translate(Zotero.BetterBibTeX.getTranslator(translator), {items: [item]}, Zotero.BetterBibTeX.displayOptions(url))
+        obj = {
+          id: item.id
+          key: item.key
+          bibtex: bibtex
+        }
+        response.push(obj)
+    else
+      bibtex = Zotero.BetterBibTeX.translate(Zotero.BetterBibTeX.getTranslator(translator), {items: items}, Zotero.BetterBibTeX.displayOptions(url))
+
+    Zotero.BetterBibTeX.log("Exporting items #{items}")
+    switch format
+      when "json" then sendResponseCallback(200, 'application/json', JSON.stringify(response))
+      when "text" then sendResponseCallback(200, 'plain/text', bibtex)
+
+  catch err
+    Zotero.BetterBibTeX.log("Could not export items '#{items}", err)
+    sendResponseCallback(404, 'text/plain', "Could not export items '#{items}': #{err}")


Fix Ace preview button issue

Fix Ace preview button issue

--- client/app/Applications/Ace.kdapplication/AppView.coffee
+++ client/app/Applications/Ace.kdapplication/AppView.coffee
@@ -42,7 +42,7 @@
       menu          : [@getAdvancedSettingsMenuItems()]

-    publicUrlCheck = /.*\/(.*\.(beta\.)*koding\.com)\/website\/(.*)/
+    publicUrlCheck = /.*\/(.*\.koding.com)\/website\/(.*)/
     @previewButton = new KDButtonView
       style     : "editor-button"
       icon      : yes


Sidebar: Add unread indicator to machine box

--- client/app/lib/activity/sidebar/sidebarmachinebox.coffee
+++ client/app/lib/activity/sidebar/sidebarmachinebox.coffee
@@ -32,6 +32,10 @@

     @addSubView @machineItem = new NavigationMachineItem {}, machineData

+    @addSubView @unreadIndicator = new KDCustomHTMLView
+      tagName  : 'cite'
+      cssClass : 'count hidden'
+
     @createWorkspacesLabel()
     @createWorkspacesList()
     @watchMachineState()
@@ -154,6 +158,7 @@

     @listWrapper.hide()
     @workspacesLabel.hide()
+    @unreadIndicator.show()  if @unreadCount > 0
     @isListCollapsed = yes

@@ -161,6 +166,7 @@

     @listWrapper.show()
     @workspacesLabel.show()
+    @unreadIndicator.hide()
     @isListCollapsed = no

@@ -226,3 +232,28 @@
       switch state
         when Stopping, Terminating then @deselect()
         when Terminated then @destroy()
+
+
+  setUnreadCount: (channelId, count) ->
+
+    return  unless workspaceItem = @getWorkspaceItemByChannelId channelId
+
+    workspaceItem.setUnreadCount count
+
+    @updateUnreadCount()
+
+    return  unless count is 0
+
+    kd.singletons.socialapi.channel.updateLastSeenTime {channelId}, kd.noop
+
+
+  updateUnreadCount: ->
+
+    @unreadCount = 0
+
+    for own _, workspaceItem of @workspaceListItemsById
+      @unreadCount += workspaceItem.unreadCount or 0
+
+    @unreadIndicator.updatePartial @unreadCount
+    if @isListCollapsed and @unreadCount > 0
+      @unreadIndicator.show()


Home: made section updatable from other code parts

--- client/landing/site.hackathon/coffee/home/AppView.coffee
+++ client/landing/site.hackathon/coffee/home/AppView.coffee
@@ -83,7 +83,7 @@
     {firstName, lastName, nickname, hash} = KD.whoami().profile
     {isApplicant, isApproved, isWinner} = getStats()

-    @addSubView (section = new KDCustomHTMLView
+    @addSubView (@section = new KDCustomHTMLView
       tagName  : 'section'
       cssClass : 'logged-in'
     ), '.form-wrapper'
@@ -91,7 +91,7 @@
     size     = 80
     fallback = "https://koding-cdn.s3.amazonaws.com/square-avatars/default.avatar.#{size}.png"

-    section.addSubView avatarBg = new KDCustomHTMLView
+    @section.addSubView avatarBg = new KDCustomHTMLView
       cssClass   : 'avatar-background'

     avatarBg.addSubView avatar = new KDCustomHTMLView
@@ -100,7 +100,7 @@
       attributes :
         src      : "//gravatar.com/avatar/#{hash}?size=#{size}&d=#{fallback}&r=g"

-    section.addSubView label = new KDLabelView
+    @section.addSubView label = new KDLabelView
       title : "Hey, #{firstName or nickname}!"

     label.addSubView notYou = new CustomLinkView
@@ -113,7 +113,7 @@

     return if isApplicant

-    section.addSubView button = new KDButtonView
+    @section.addSubView button = new KDButtonView
       cssClass : 'apply-button solid green medium'
       title    : 'APPLY NOW'
       loader   : yes
@@ -125,14 +125,19 @@
           success     : (stats) =>
             button.hideLoader()
             KD.campaignStats = stats
-            section.destroy()
-            @updateGreeting()
-            @updateStats()
-            @createApplyWidget()
+            @updateWidget()
           error       : (xhr) ->
             {responseText} = xhr
             button.hideLoader()
             new KDNotificationView title : responseText
+
+
+  updateWidget : ->
+
+    @section.destroy()
+    @updateGreeting()
+    @updateStats()
+    @createApplyWidget()

   createJudges : ->


Remove old sprockets

Remove old sprockets

--- src/set/set_mapping.coffee
+++ src/set/set_mapping.coffee
@@ -1,6 +1,5 @@
 #= require ./set
 #= require ./set_observer
-#= require ./set_proxy

 class Batman.SetMapping extends Batman.Set
   constructor: (@base, @key) ->
@@ -11,8 +10,8 @@
     @_setObserver = new Batman.SetObserver(@base)
     @_setObserver.observedItemKeys = [@key]
     @_setObserver.observerForItemAndKey = (item) => (newValue, oldValue) => @_handleItemModified(item, newValue, oldValue)
-    @_setObserver.on 'itemsWereAdded', @_handleItemsAdded.bind(this)
-    @_setObserver.on 'itemsWereRemoved', @_handleItemsRemoved.bind(this)
+    @_setObserver.on 'itemsWereAdded', @_handleItemsAdded.bind(@)
+    @_setObserver.on 'itemsWereRemoved', @_handleItemsRemoved.bind(@)
     @_setObserver.startObserving()

   _handleItemsAdded: (items, indexes) ->


raise concurrent error limit

raise concurrent error limit

--- src/smc-hub/rethink.coffee
+++ src/smc-hub/rethink.coffee
@@ -113,7 +113,7 @@
             warning  : 30          # display warning and stop using connection if run takes this many seconds or more
             error    : 10*60       # kill any query that takes this long (and corresponding connection)
             concurrent_warn  : 500  # if number of concurrent outstanding db queries exceeds this number, put a concurrent_warn message in the log.
-            concurrent_error : 600  # if nonzero, and this many queries at once, any query instantly fails!
+            concurrent_error : 1000  # if nonzero, and this many queries at once, any query instantly fails!
             mod_warn : 2           # display MOD_WARN warning in log if any query modifies at least this many docs
             cache_expiry  : 15000  # expire cached queries after this many milliseconds (default: 15s)
             cache_size    : 250    # cache this many queries; use @...query.run({cache:true}, cb) to cache result for a few seconds


Fix intercom again (#3593)

Fix intercom again (#3593)

--- angular/core/services/intercom_service.coffee
+++ angular/core/services/intercom_service.coffee
@@ -23,7 +23,6 @@
     closed_motions_count: group.closedMotionsCount
     has_custom_cover: group.hasCustomCover
     invitations_count: group.invitationsCount
-

   service = new class IntercomService
     available: ->
@@ -57,13 +56,14 @@

     updateWithGroup: (group) ->
       return unless @available()
+      user = Session.user()
       return if currentGroup == group
       return if group.isSubgroup()
       return if !user.isMemberOf(group)
       currentGroup = group
       $window.Intercom 'update',
-        email: Session.user().email
-        user_id: Session.user().id
+        email: user.email
+        user_id: user.id
         company: mapGroup(group)

     contactUs: ->


stupid fix. Gees.

stupid fix. Gees.

--- src/smc-util/schema.coffee
+++ src/smc-util/schema.coffee
@@ -456,7 +456,7 @@
                 # the file, which is confusing and wastes a lot of resources.
                 x = obj.users?[account_id]
                 recent = misc.minutes_ago(3)
-                if x? and x.edit >= recent or x.chat >= recent
+                if x? and (x.edit >= recent or x.chat >= recent)
                     db.touch(project_id:obj.project_id, account_id:account_id)
                 cb?()


Display icon and commit message on network graph.

--- app/assets/javascripts/branch-graph.js.coffee
+++ app/assets/javascripts/branch-graph.js.coffee
@@ -7,7 +7,7 @@
     @colors = ["#000"]
     @offsetX = 120
     @offsetY = 20
-    @unitTime = 20
+    @unitTime = 30
     @unitSpace = 10
     @load()

@@ -178,6 +178,15 @@
     r.circle(x, y, 3).attr(
       fill: @colors[commit.space]
       stroke: "none"
+    )
+    r.rect(@offsetX + @unitSpace * @mspace + 10, y - 10, 20, 20).attr(
+      fill: "url(#{commit.author.icon})"
+      stroke: @colors[commit.space]
+      "stroke-width": 2
+    )
+    r.text(@offsetX + @unitSpace * @mspace + 35, y, commit.message.split("\n")[0]).attr(
+      "text-anchor": "start"
+      font: "14px Monaco, monospace"
     )

   drawLines: (x, y, commit) ->


It now resolves the class names printed.

--- console/runner.coffee
+++ console/runner.coffee
@@ -156,4 +156,13 @@

   if argv['list-class-cache']
     for k in Object.keys rs.loaded_classes
-      console.log k
+      # Find where it was loaded from.
+      file = k + ".class"
+      for cpath in jvm.classpath
+        fpath = cpath + '/' + file
+        try
+          if fs.statSync(fpath).isFile()
+            console.log(path.resolve(fpath))
+            break
+        catch e
+          # Do nothing; iterate.


Redirect to /app/cc from / when user is already logged in

--- routes/index.coffee
+++ routes/index.coffee
@@ -2,13 +2,13 @@
 router = express.Router()

 router.get '/', (req, res) ->
-  res.render 'login', error: req.flash 'error'
+  if req.user
+    res.redirect '/app/cc'
+  else
+    res.render 'login', error: req.flash 'error'

 router.get '/app/:page?', (req, res) ->
   res.render 'index', title: req.page
-
-router.get "/api/name", (req, res) ->
-  res.json name: 'YEP'

 router.get "/partials/view1", (req, res) ->
   res.render 'partials/partial1', pretty: true


log firebase errors

log firebase errors

--- routes/index.coffee
+++ routes/index.coffee
@@ -1,9 +1,11 @@
 debug = require 'debug'
 log = debug 'app:log'
 errorLog = debug 'app:error'
-fail = (msg) ->
-  errorLog msg
-  throw new Error msg
+fail = (err) ->
+  if typeof err is 'string'
+    err = new Error err
+  errorLog err
+  throw err

 express = require 'express'
 firebase = require '../lib/pegg-firebase-client'
@@ -40,19 +42,20 @@
   validateClient req.body.secret
   req.body.secret = undefined

+  msg = "submitting new user notification to firebase: "+ JSON.stringify req.body
+  log msg
+
   for friendId in req.body.friends
-    firebase.child('inbound').child(friendId).push
+    firebase.child('inbound').child(friendId).push {
       dts: req.body.dts
       type: 'friendsUpdate'
       friendId: req.body.userId
-  firebase.child('inbound').child(req.body.userId).push
+    }, (error) => fail error if error?
+  firebase.child('inbound').child(req.body.userId).push {
     dts: req.body.dts
     type: 'friendsUpdate'
+  }, (error) => fail error if error?

-
-  msg = "submitting new user notification to firebase: "+ JSON.stringify req.body
-  log msg
   res.send msg

-
 module.exports = router


adds upsert to optarg white list in js driver

--- drivers/javascript2/src/ast.coffee
+++ drivers/javascript2/src/ast.coffee
@@ -133,6 +133,7 @@
             when 'datacenter' then 'datacenter'
             when 'useOutdated' then 'use_outdated'
             when 'cacheSize' then 'cache_size'
+            when 'upsert' then 'upsert'
             when 'left' then 'left'
             when 'right' then 'right'
             when 'base' then 'base'


ui changes

ui changes

--- src/documents/index.html.coffee
+++ src/documents/index.html.coffee
@@ -6,10 +6,11 @@
 ---

 div class: "hero-unit text-center", ->
-	p "I'm a developer advocate and program manager for Microsoft DPE based in Illinois. A Chicago native who can’t spell, Clark as a kid actually made his money building cars, getting grease under his nails. That art of building would later lead him to software development where he drinks the Web and Mobile Development Kool-Aid. At Microsoft, Clark spends his days working with Global ISVs helping them deliver their top products leveraging Windows 8, Windows Phone and Windows Azure. As of that wasn’t enough, Clark is the co-founder of a polyglot software development conference called That Conference http://ThatConference.com. You can check out his blog at http://csell.net or find his family cruising around in their 1968 Camaro SS."
+	p "I'm a husband, a father, a geek. Love to sling code, and even love design and typography. I'm currently playing Program Manger at Microsoft. I founded an awesome conference, That Conference. I wrench on cars and live in the midwest."
+	p "Regardless, I'm just a guy."

 div class: "row", ->
-	div class: "col-md-6", ->
+	div class: "col-md-7", ->
 		div class: "row", ->
 			header class: "col-md-12", ->
 				h1 ->
@@ -18,14 +19,16 @@
 						" Articles"

 			for document in @getCollection('posts').toJSON().slice(0,5)
-				article class: "col-md-12", ->
+				article class: "col-md-12 articleList", ->
 					h3 ->
 						a href: document.url, document.title

 					p document.subTitle
-					p class: "text-right text-muted", document.date.toDateString()
+					h6 class: "text-right text-muted", ->
+						em document.date.toDateString()
+

-	div class: "col-md-5 col-md-offset-1", ->
+	div class: "col-md-3 col-md-offset-2", ->
 		## Social Info
 		div class: "row", ->
 			header class: "col-md-12", ->


notes on opendatakit formdef compatibility

--- webapp/models/Form.coffee
+++ webapp/models/Form.coffee
@@ -1,13 +1,31 @@
 # A Form
 Thing = require 'models/Thing'
+# loose compatibility with OpenDataKit xlsform, http://opendatakit.org/help/form-design/xlsform
+# http://opendatakit.org/use/2_0_tools/older-versions/xlsxconverter-beta-2-0-v2/
+# with a dash of compatibility with formDef.json, https://code.google.com/p/opendatakit/wiki/SurveyJSON
+
+# model: <OMITTED FOR NOW>
+# survey: [{ type:TYPE, values_list:LISTNAME?, name, display: { text, hint? },
+#            required:BOOL? }, ...]
+#   (see also condition, constraint, default, ...)
+#   TYPE: text, integer, decimal, select_one [options], select_multiple [options], note (no input),
+#        geopoint, image, barcode, date, datetime, audio, video, calculate
+# (auto) start (datetime), end, today (day of survey), deviceid, subscriberid, sim_serial, phone_number
+# choices: { LISTNAME: [{ choice_list_name?, data_value, display: { text?, image? }, ...], ...}
+# settings: <HANDLED AT TOP LEVEL FOR NOW>
+#           [{setting_name, value, display: { title? } },...]
+#   SETTING_NAMEs: survey <with display:title> => title, form_id => id,
+#                  form_version (int, YYYYMMDD) => version

 module.exports = class Form extends Thing
   defaults:
     title: ''
     description: ''
     type: 'form'
-    # TODO
+    survey: []
+    choices: []
+    # id
+    # version

   idAttribute: '_id'

-


added TMM calculation

added TMM calculation

--- app/assets/javascripts/card.coffee
+++ app/assets/javascripts/card.coffee
@@ -161,9 +161,27 @@
 			'subtotal', subTotal
 			'speedAndRangeFactor', speedAndRangeFactor
 			'forceBonus', forceBonus
-
+
 		Math.max(1, Math.round(speedAndRangeFactor * subTotal + forceBonus))
-
+
+	calculateTMM = ->
+		tmmString = for speed, i in movement.values
+			if speed <= 2 * (if movement.units is 'hexes' then 1 else 2)
+				tmmValue = 0
+			else if speed <= 4 * (if movement.units is 'hexes' then 1 else 2)
+				tmmValue = 1
+			else if speed <= 6 * (if movement.units is 'hexes' then 1 else 2)
+				tmmValue = 2
+			else if speed <= 9 * (if movement.units is 'hexes' then 1 else 2)
+				tmmValue = 3
+			else if speed <= 17 * (if movement.units is 'hexes' then 1 else 2)
+				tmmValue = 4
+			else if speed > 17 * (if movement.units is 'hexes' then 1 else 2)
+				tmmValue = 5
+			if movement['j']
+				tmmValue += 1
+			tmmValue + if movement.modes[i] then movement.modes[i] else ''
+		tmmString.join('/')

 	parseMovement = ->
 		movement = {}
@@ -173,6 +191,7 @@
 			mode: (moves[i].match(/[a-z]$/i)?[0] || '').toLowerCase()
 		} for move, i in moves
 		movement.values = (move.value for move in moves)
+		movement.modes = (move.mode for move in moves)
 		(movement[move.mode] = move.value) for move in moves
 		movement.base = movement.values[0]
 		movement.units = if ~mv.value.indexOf '"' then 'inches' else 'hexes'
@@ -205,7 +224,11 @@
 		finalPV = if isNaN(finalPV) then '?' else finalPV
 		pv.value = finalPV

+		tmm.value = calculateTMM()
+
+
 	offensiveSpecialAbilityFactors = {
+		# using advanced rules artillery damage values
 		ARTAIS: (rating) -> 3 * 4 * rating
 		ARTAC: (rating) -> 3 * 4 * rating
 		ARTT: (rating) -> 2 * 4 * rating
@@ -271,7 +294,6 @@

-	# TODO: calculate TMM
 	# TODO: calculate possible roles
 	# TODO: allow manual override, no PV calculations


"Temporary" patch to work with HTMLBars

"Temporary" patch to work with HTMLBars

--- packages/emberella/lib/core.coffee
+++ packages/emberella/lib/core.coffee
@@ -1,5 +1,16 @@
 root = window
 Emberella = root.Emberella = Ember.Namespace.create()
-Emberella.VERSION = '0.0.1'
+Emberella.VERSION = '0.0.2'
+
+
+# Use HTMLBars compiler if available
+# Eventually, everything will probably go all component-y and templates will
+# be pre-compiled by Tomsters with fairy wings and pointy hats. When we get to
+# that point, the entire Emberella premise will change and these 2 lines of
+# code won't matter.
+
+if Ember.typeOf(Ember.HTMLBars.compile) is 'function' and Ember.typeOf(Ember.Handlebars.compile) isnt 'function'
+  Ember.Handlebars.compile = Ember.HTMLBars.compile
+

 Ember.libraries?.register('Emberella', Emberella.VERSION)


Use business name returned from Google if possible.

--- app/assets/javascripts/main.coffee
+++ app/assets/javascripts/main.coffee
@@ -52,7 +52,7 @@
                 if not found
                     # TODO: ick. We should be using SimpleMVC views to render the popup.
                     contentString = '
' +
-                        '
<input type="text" id="businessName" placeholder="Name of the business" />
' +
+                        '
<input type="text" id="businessName" text="' + self._place.name + '" placeholder="Name of the business" />
' +
                         '
<address id="businessAddress">' + self._place.formatted_address + '</address>
' +
                         '
<input type="checkbox" id="pinEnabled" value="true"/>business has PIN pad
' +
                         '
add business

'


shareddoc

shareddoc

--- core/KDWindowController.coffee
+++ core/KDWindowController.coffee
@@ -84,7 +84,7 @@

       event.returnValue = msg if event # For IE and Firefox prior to version 4

-      return msg
+      return msg unless window.hash.location isnt 'localhost'

   setDragInAction:(action = no)->
     $('body')[if action then "addClass" else "removeClass"]("dragInAction")


Hrm. HighWaterMark isn't doing what we need it to. This is going to be tough.

--- test/files/stream.iced
+++ test/files/stream.iced
@@ -8,6 +8,13 @@

 loop_limit = 5000
 loop_skip = 29
+
+first_different_byte = (buf1, buf2) ->
+  limit = if buf1.length < buf2.length then buf1.length else buf2.length
+  for i in [0...limit]
+    if buf1[i] != buf2[i]
+      return i
+  return -1

 stream_random_data = (strm, len) ->
   data = prng(len)
@@ -41,7 +48,7 @@

   T.equal(stock, encoded_data, "bad output found: base=#{base} len=#{len}")

-test_bx_streaming_correctness = (T, base, len, stock_func) ->
+test_bx_streaming_correctness = (T, base, len) ->
   encoding = encoding_for_base(base)
   encoder = new stream.StreamEncoder(encoding)
   block_encoder = new stream.StreamEncoder(encoding)
@@ -50,8 +57,9 @@
   block_encoder.write(data)
   encoded_data = encoder.read()
   block_encoded_data = block_encoder.read()
+  fdiff = first_different_byte(encoded_data, block_encoded_data)

-  T.equal(encoded_data, block_encoded_data, "max was right: base=#{base} len=#{len}")
+  T.equal(encoded_data, block_encoded_data, "max was right: base=#{base} len=#{len} fdiff=#{fdiff}")

 encoding_for_base = (base) ->
   switch base
@@ -95,3 +103,8 @@
   for i in [1...loop_limit] by loop_skip
     test_bx_output(T, 64, i, b64stock.encode)
   cb()
+
+exports.test_b62_streaming_correctness = (T, cb) ->
+  for i in [1...loop_limit] by loop_skip
+    test_bx_streaming_correctness(T, 62, i)
+  cb()


Remove video controls from subject viewer

--- app/components/subject-viewer.cjsx
+++ app/components/subject-viewer.cjsx
@@ -106,31 +106,6 @@
                   </button>}
               }

-      when 'video'
-
-
-            {if @state.playing
-              <button type="button" className="secret-button" aria-label="Pause" onClick={@playVideo.bind this, false}>
-
-              </button>
-            else
-              <button type="button" className="secret-button" aria-label="Play" onClick={@playVideo.bind this, true}>
-
-              </button>}
-
-          <input type="range" className="video-scrubber" ref="videoScrubber" min="0" step="any" onChange={@seekVideo} />
-
-          Speed:
-            {for rate, i in [0.25, 0.5, 1]
-
-                <input type="radio" name="playbackRate" value={rate} checked={rate == @state.playbackRate} onChange={@setPlayRate} />
-
-                  {rate}×
-
-
-            }
-
-

       {if type is 'image'


Fix: typo

Fix: typo

--- test/gobgp-node.coffee
+++ test/gobgp-node.coffee
@@ -66,7 +66,7 @@

       expect(communities(PREFIX)).to.eql [4294967042]

-    it 'shows the RIB', ->  # TODO: This does actually nothing. Usechai-as-promised
+    it 'shows the RIB', ->  # TODO: This does actually nothing. Use chai-as-promised
       exec "gobgp global rib add #{PREFIX}"

       gobgp.getRib family: 'ipv4-unicast', (err, table) ->
@@ -96,7 +96,7 @@
       expect(table.length).to.equal 1
       expect(table[0].prefix).to.equal "[source:#{PREFIX}]"

-    it 'shows the RIB', ->  # TODO: This does actually nothing. Usechai-as-promised
+    it 'shows the RIB', ->  # TODO: This does actually nothing. Use chai-as-promised
       exec "gobgp global rib -a ipv4-flowspec add #{FLOWSPEC_PREFIX}"

       gobgp.getRib family: 'ipv4-flowspec', (err, table) ->


added test for #19

added test for #19

--- test/index_test.coffee
+++ test/index_test.coffee
@@ -131,6 +131,17 @@
         )
       done
     )
+
+
+  it "should keep the relative paths", (done) ->
+
+    checkExpectedFiles(
+      ["fuz/ahah.js", "duu.js"]
+      vinylfs.src("#{dir}/fixtures/core/**/*.js")
+        .pipe(amdOptimize("duu"))
+      done
+    )
+

   it "should remove the ast property when done", (done) ->


Added fields to SkyServer model

Added fields to SkyServer model

--- app/models/SkyServerSubject.coffee
+++ app/models/SkyServerSubject.coffee
@@ -1,6 +1,6 @@

 class SkyServerSubject extends Spine.Model
-  @configure 'SkyServerSubject', 'ra', 'dec', 'zooniverse_id'
+  @configure 'SkyServerSubject', 'objID', 'specObjID', 'ra', 'dec', 'raErr', 'decErr', 'b', 'l', 'mjd', 'type', 'u', 'g', 'r', 'i', 'z', 'err_u', 'err_g', 'err_r', 'err_i', 'err_z', 'petroR90_u', 'petroR90_g', 'petroR90_r', 'petroR90_i', 'petroR90_z', 'extinction_u', 'extinction_g', 'extinction_r', 'extinction_i', 'extinction_z', 'fracDeV_u', 'fracDeV_g', 'fracDeV_r', 'fracDeV_i', 'fracDeV_z', 'zooniverse_id'

   @fetch: (count = 1) ->
     params =
@@ -17,9 +17,42 @@
     @lastFetch = new Array
     for result in json
       item = @create
+        objID: result.objID
+        specObjID: result.specObjID
         ra: result.ra
         dec: result.dec
-        zooniverse_id: result.objID
+        raErr: result.raErr
+        decErr: result.decErr
+        b: result.b
+        l: result.l
+        mjd: result.mjd
+        type: result.type
+        u: result.u
+        g: result.g
+        r: result.r
+        i: result.i
+        z: result.z
+        err_u: result.err_u
+        err_g: result.err_g
+        err_r: result.err_r
+        err_i: result.err_i
+        err_z: result.err_z
+        petroR90_u: result.petroR90_u
+        petroR90_g: result.petroR90_g
+        petroR90_r: result.petroR90_r
+        petroR90_i: result.petroR90_i
+        petroR90_z: result.petroR90_z
+        extinction_u: result.extinction_u
+        extinction_g: result.extinction_g
+        extinction_r: result.extinction_r
+        extinction_i: result.extinction_i
+        extinction_z: result.extinction_z
+        fracDeV_u: result.fracDeV_u
+        fracDeV_g: result.fracDeV_g
+        fracDeV_r: result.fracDeV_r
+        fracDeV_i: result.fracDeV_i
+        fracDeV_z: result.fracDeV_z
+        zooniverse_id: null
       @lastFetch.push item


add \seven

add \seven

without serif

--- src/glyphs/figures/seven.coffee
+++ src/glyphs/figures/seven.coffee
@@ -1,7 +1,7 @@
 exports.glyphs['seven'] =
 	unicode: '7'
 	ot:
-		advanceWidth: 0
+		advanceWidth: contours[0].nodes[1].expandedTo[1].x + 5 * spacing
 	tags: [
 		'all',
 		'latin',
@@ -10,8 +10,50 @@
 	contours:
 		0:
 			skeleton: true
-			closed: true
+			closed: false
 			nodes:
 				0:
-					x: 0
-					y: 0
+					x: 109 + (16)
+					y: ( 220 / 250 ) * descender
+					typeOut: 'line'
+					expand: Object({
+						width: thickness * ( 65 / 85 )
+						angle: 0 + 'deg'
+						distr: 0.25
+					})
+				1:
+					x: 428 + (6)
+					y: ( 485 / 500 ) * xHeight
+					typeOut: 'line'
+					expand: Object({
+						width: thickness * ( 25 / 85 ) * contrast
+						angle: 0 + 'deg'
+						distr: 0.25
+					})
+				2:
+					x: contours[0].nodes[1].expandedTo[1].x
+					y: contours[0].nodes[1].expandedTo[1].y
+					typeOut: 'line'
+					expand: Object({
+						width: ( 15 / 500 ) * xHeight
+						angle: 90 + 'deg'
+						distr: 0
+					})
+				3:
+					x: contours[0].nodes[1].expandedTo[0].x + ( contours[0].nodes[1].expandedTo[1].x - contours[0].nodes[1].expandedTo[0].x ) * 0.5
+					y: xHeight
+					typeOut: 'line'
+					expand: Object({
+						width: thickness * ( 75 / 85 )
+						angle: Utils.lineAngle( contours[0].nodes[0].point, contours[0].nodes[1].point )
+						distr: 1
+					})
+				4:
+					x: 80
+					y: xHeight
+					typeOut: 'line'
+					expand: Object({
+						width: thickness * ( 75 / 85 )
+						angle: Utils.lineAngle( contours[0].nodes[0].point, contours[0].nodes[1].point )
+						distr: 1
+					})


Test every li vs. lm case. I think.

Test every li vs. lm case. I think.

--- test/types/json.coffee
+++ test/types/json.coffee
@@ -424,6 +424,51 @@
 		test.deepEqual [{p:[1],ld:3,li:4}], type.transform [{p:[0],ld:3,li:4}], [{p:[1],lm:0}], 'client'
 		test.done()

+	'li vs lm': (test) ->
+		li = (p) -> [{p:[p],li:[]}]
+		lm = (f,t) -> [{p:[f],lm:t}]
+		xf = type.transform
+
+		test.deepEqual (li 0), xf (li 0), (lm 1, 3), 'client'
+		test.deepEqual (li 1), xf (li 1), (lm 1, 3), 'client'
+		test.deepEqual (li 1), xf (li 2), (lm 1, 3), 'client'
+		test.deepEqual (li 2), xf (li 3), (lm 1, 3), 'client'
+		test.deepEqual (li 3), xf (li 4), (lm 1, 3), 'client'
+		test.deepEqual (li 5), xf (li 5), (lm 1, 3), 'client'
+
+		test.deepEqual (lm 2, 4), xf (lm 1, 3), (li 0), 'server'
+		test.deepEqual (lm 2, 4), xf (lm 1, 3), (li 1), 'server'
+		test.deepEqual (lm 1, 4), xf (lm 1, 3), (li 2), 'server'
+		test.deepEqual (lm 1, 4), xf (lm 1, 3), (li 3), 'server'
+		test.deepEqual (lm 1, 4), xf (lm 1, 3), (li 4), 'server'
+		test.deepEqual (lm 1, 3), xf (lm 1, 3), (li 5), 'server'
+
+		test.deepEqual (li 0), xf (li 0), (lm 1, 2), 'client'
+		test.deepEqual (li 1), xf (li 1), (lm 1, 2), 'client'
+		test.deepEqual (li 1), xf (li 2), (lm 1, 2), 'client'
+		test.deepEqual (li 2), xf (li 3), (lm 1, 2), 'client'
+		test.deepEqual (li 4), xf (li 4), (lm 1, 2), 'client'
+
+		test.deepEqual (li 0), xf (li 0), (lm 3, 1), 'client'
+		test.deepEqual (li 1), xf (li 1), (lm 3, 1), 'client'
+		test.deepEqual (li 3), xf (li 2), (lm 3, 1), 'client'
+		test.deepEqual (li 4), xf (li 3), (lm 3, 1), 'client'
+		test.deepEqual (li 4), xf (li 4), (lm 3, 1), 'client'
+
+		test.deepEqual (lm 4, 2), xf (lm 3, 1), (li 0), 'server'
+		test.deepEqual (lm 4, 2), xf (lm 3, 1), (li 1), 'server'
+		test.deepEqual (lm 4, 1), xf (lm 3, 1), (li 2), 'server'
+		test.deepEqual (lm 4, 1), xf (lm 3, 1), (li 3), 'server'
+		test.deepEqual (lm 3, 1), xf (lm 3, 1), (li 4), 'server'
+
+		test.deepEqual (li 0), xf (li 0), (lm 2, 1), 'client'
+		test.deepEqual (li 1), xf (li 1), (lm 2, 1), 'client'
+		test.deepEqual (li 3), xf (li 2), (lm 2, 1), 'client'
+		test.deepEqual (li 3), xf (li 3), (lm 2, 1), 'client'
+		test.deepEqual (li 4), xf (li 4), (lm 2, 1), 'client'
+
+		test.done()
+
 exports.object =
 	'Apply sanity checks': (test) ->
 		test.deepEqual {x:'a', y:'b'}, type.apply {x:'a'}, [{p:['y'], oi:'b'}]


Adds the spec on adding multiple functions at once

--- spec/index.coffee
+++ spec/index.coffee
@@ -104,7 +104,22 @@
       fnWithAspects()
       beforeFn.should.not.have.been.calledOn scope
       afterFn.should.not.have.been.calledOn scope
-  xdescribe "Adding multiple aspects at once", ->
+  describe "Adding multiple aspects at once", ->
+    it 'should add all the functions passed in parameter to the aspect', ->
+      fn = sinon.spy ->
+      fnWithAspects = maypop fn
+      beforeFn1 = sinon.spy ->
+      beforeFn2 = sinon.spy ->
+      afterFn1 = sinon.spy ->
+      afterFn2 = sinon.spy ->
+      fnWithAspects.before beforeFn1, beforeFn2
+      fnWithAspects.after afterFn1, afterFn2
+      fnWithAspects()
+      beforeFn1.should.have.been.calledBefore beforeFn2
+      beforeFn2.should.have.been.calledBefore fn
+      fn.should.have.been.calledBefore afterFn1
+      afterFn1.should.have.been.calledBefore afterFn2
+
   xdescribe "exceptions", ->


clone isn't needed

clone isn't needed

--- src/stores/exercise.coffee
+++ src/stores/exercise.coffee
@@ -172,7 +172,7 @@
     getTemplate: (id) ->
       questionId = QuestionStore.freshLocalId()

-      tags: _.clone []
+      tags: []
       stimulus_html:"",
       questions:[_.extend({}, QuestionStore.getTemplate(), {id: questionId})]


note error for fullscreen mode

note error for fullscreen mode

--- src/textorum/helper.coffee
+++ src/textorum/helper.coffee
@@ -405,6 +405,8 @@
           node = node.firstChild
         else
           while node.nextSibling is null
+            # FIXME: On fullscreen, I believe at some point parentNode is null,
+            # resulting call to node.nextSibling in the while above error.
             node = node.parentNode
             depth -= 1
             if handler.endTag


Clean up helper.

Clean up helper.

--- src/Helper.coffee
+++ src/Helper.coffee
@@ -1,115 +1,28 @@
 _ = require 'lodash'
-path = require 'path'

-# Calculate parent module (invoker) root path
-# We assume, that we are in the node_modules/node-wire-context-helper
-root = path.resolve "#{__dirname}/../../.."
+H =
+  ########################################
+  # Use references easily
+  ref: (name) ->
+    $ref: name

-instances = {}
+  refs: (names) ->
+    _.map names, H.ref

-module.exports = (prefix = "lib") ->
-  return instances[prefix] if _.has instances, prefix
+  environment: ->
+    process.env['NODE_ENV'] ? 'production'

-  instances[prefix] = H =
-    path: (tail) ->
-      path.resolve "#{root}/#{tail}"
+  env: (cases) ->
+    if _.has cases, H.environment()
+      _.result cases, H.environment()
+    else if _.has cases, 'default'
+      _.result cases, 'default'

-    lib: (tail) ->
-      path.resolve "#{root}/#{prefix}/#{tail}"
-
-    domain: (tail) ->
-      H.lib "domain/#{tail}"
-
-    model: (name) ->
-      H.domain "models/#{name}"
-
-    Model: (name) ->
-      H.model "#{name}Model"
-
-    repository: (name) ->
-      H.domain "repositories/#{name}"
-
-    Repository: (name = '') ->
-      H.repository "#{name}Repository"
-
-    service: (name) ->
-      H.domain "services/#{name}"
-    Service: (name) ->
-      H.service "#{name}Service"
-    infrastructure: (tail) ->
-      H.lib "infrastructure/#{tail}"
-    web: (tail) ->
-      H.infrastructure "web/#{tail}"
-    application: (tail) ->
-      H.web tail
-    applicationMiddleware: (tail) ->
-      H.application "middlewares/#{tail}"
-    ApplicationMiddleware: (tail) ->
-      H.applicationMiddleware "#{tail}Middleware"
-    applicationConfigurator: (tail) ->
-      H.application "configurators/#{tail}"
-    ApplicationConfigurator: (tail) ->
-      H.applicationConfigurator "#{tail}Configurator"
-    applicationFactory: (tail) ->
-      H.application "factories/#{tail}"
-    ApplicationFactory: (tail) ->
-      H.applicationFactory "#{tail}Factory"
-    controller: (tail) ->
-      H.web "controllers/#{tail}"
-    Controller: (tail) ->
-      H.controller "#{tail}Controller"
-    persistence: (name) ->
-      H.infrastructure "persistence/#{name}"
-    messaging: (name) ->
-      H.infrastructure "messaging/#{name}"
-    log: (tail) ->
-      H.infrastructure "log/#{tail}"
-    i18n: (tail) ->
-      H.infrastructure "i18n/#{tail}"
-    cli: (tail) ->
-      H.infrastructure "cli/#{tail}"
-    command: (tail) ->
-      H.cli "commands/#{tail}"
-    Command: (tail) ->
-      H.command "#{tail}Command"
-    view: (tail) ->
-      path.resolve "#{root}/views/#{tail}/"
-    View: (controller, action, ext = ".html") ->
-      H.view "#{controller}/#{action}#{ext}"
-    util: (tail) ->
-      H.lib "utils/#{tail}"
-    context: (tail) ->
-      H.lib "contexts/#{tail}"
-
-    ########################################
-    # Use references easily
-    ref: (name) ->
-      $ref: name
-
-    refs: (names) ->
-      _.map names, H.ref
-
-    ########################################
-    # Bean creation utilities
-    bean: (mod, args, properties) ->
-      create:
-        module: mod
-        args: args
-      properties: properties
-
-    environment: ->
-      process.env['NODE_ENV'] ? 'production'
-
-    env: (cases, def) ->
-      if _.has cases, H.environment()
-        _.result cases, H.environment()
-      else
-        def
-
-    envVar: (name, def) ->
-      if process.env[name]
-        console.log "Using #{name} environment variable"
-        process.env[name]
-      else
-        console.log "Using default value for #{name} environment variable: #{def}"
-        def+  envVar: (name, def) ->
+    if process.env[name]
+      value = process.env[name]
+      console.log "Using #{name} environment variable: #{value}"
+      value
+    else
+      console.log "Using default value for #{name} environment variable: #{def}"
+      def


Added getCSSAnimation

Added getCSSAnimation

--- src/Sprite.coffee
+++ src/Sprite.coffee
@@ -14,6 +14,7 @@
     @setOpacity 1
     @setImage null
     @setImageClip 0, 0, 1, 1
+    @stopCSSAnimation()

   getWidth: -> @_width
   getHeight: -> @_height
@@ -77,6 +78,24 @@
   getImageClipV2: DivSugar._Scene.getImageClipV2
   setImageClip: DivSugar._Scene.setImageClip

+  getCSSAnimation: -> @_cssAnimation
+
+  playCSSAnimation: (name, duration, timing = 'ease', delay = 0, count = 1, direction = 'normal', fill = 'both') ->
+    @_cssAnimation = name
+    @style[DivSugar._animationName] = name
+    @style[DivSugar._animationDuration] = "#{duration.toFixed(DivSugar.NUM_OF_DIGITS)}s"
+    @style[DivSugar._animationTimingFunction] = timing
+    @style[DivSugar._animationDirection] = "#{delay.toFixed(DivSugar.NUM_OF_DIGITS)}s"
+    @style[DivSugar._animationIterationCount] = count
+    @style[DivSugar._animationDirection] = direction
+    @style[DivSugar._animationFillMode] = fill
+    return @
+
+  stopCSSAnimation: (name) ->
+    @_cssAnimation = null
+    @style[DivSugar._animationName] = 'none'
+    return @
+
   translate: (offsetX, offsetY, offsetZ) ->
     @_transform.translate offsetX, offsetY, offsetZ
     @style[DivSugar._transform] = @_transform.toCSSTransform()
@@ -91,15 +110,3 @@
     @_transform.scale scaleX, scaleY, scaleZ
     @style[DivSugar._transform] = @_transform.toCSSTransform()
     return @
-
-  playCSSAnimation: (name, duration, timing = 'ease', delay = 0, count = 1, direction = 'normal', fill = 'both') ->
-    @style[DivSugar._animationName] = name
-    @style[DivSugar._animationDuration] = "#{duration.toFixed(DivSugar.NUM_OF_DIGITS)}s"
-    @style[DivSugar._animationTimingFunction] = timing
-    @style[DivSugar._animationDirection] = "#{delay.toFixed(DivSugar.NUM_OF_DIGITS)}s"
-    @style[DivSugar._animationIterationCount] = count
-    @style[DivSugar._animationDirection] = direction
-    @style[DivSugar._animationFillMode] = fill
-
-  stopCSSAnimation: (name) ->
-    @style[DivSugar._animationName] = 'none'


whitespace for better readability

whitespace for better readability

--- src/airbud.coffee
+++ src/airbud.coffee
@@ -6,15 +6,20 @@
   @_defaults:
     # The URL to fetch
     url: null
+
     # How many times to try
     retries: 0
+
     # Timeout in milliseconds per try
     timeout: null
+
     # Automatically parse json
     parseJson: true
+
     # A key to find in the rootlevel of the parsed json.
     # If not found, Airbud will error out
     expectedKey: null
+
     # Only used by Airbud's own testsuite to test the timeout mechanism
     testDelay: 0


fix(vm.delete): import mapFilter (#497)

fix(vm.delete): import mapFilter (#497)

--- src/api/vm.coffee
+++ src/api/vm.coffee
@@ -23,6 +23,7 @@
   formatXml: $js2xml,
   isArray: $isArray,
   map,
+  mapFilter,
   mapToArray,
   noop,
   parseSize,


Move App.require methods into developer.do

--- src/batman.coffee
+++ src/batman.coffee
@@ -1376,15 +1376,15 @@
             @run() if @wantsToRun
       @

-  @controller: (names...) ->
-    names = names.map (n) -> n + '_controller'
-    @require 'controllers', names...
-
-  @model: ->
-    @require 'models', arguments...
-
-  @view: ->
-    @require 'views', arguments...
+    @controller: (names...) ->
+      names = names.map (n) -> n + '_controller'
+      @require 'controllers', names...
+
+    @model: ->
+      @require 'models', arguments...
+
+    @view: ->
+      @require 'views', arguments...

   # Layout is the base view that other views can be yielded into. The
   # default behavior is that when `app.run()` is called, a new view will
@@ -4003,14 +4003,14 @@
       begin = offset-@offset
       end = begin + limit
       @items.slice(begin, end)
-
+
   offset: 0
   limit: 10
   totalCount: 0
-
+
   offsetFromPageAndLimit: (page, limit) -> Math.round((+page - 1) * limit)
   pageFromOffsetAndLimit: (offset, limit) -> offset / limit + 1
-
+
   toArray: ->
     cache = @get('cache')
     offset = @get('offset')
@@ -4022,10 +4022,10 @@
     @pageFromOffsetAndLimit(@get('offset'), @get('limit'))
   pageCount: ->
     Math.ceil(@get('totalCount') / @get('limit'))
-
+
   previousPage: -> @set('page', @get('page')-1)
   nextPage: -> @set('page', @get('page')+1)
-
+
   loadItemsForOffsetAndLimit: (offset, limit) -> # override on subclasses or instances
   updateCache: (offset, limit, items) ->
     @set('cache', new Batman.Paginator.Cache(offset, limit, items))


:open_hands::bike: Updated in browser at strd6.github.io/editor

--- pixie.cson
+++ pixie.cson
@@ -1,4 +1,4 @@
-version: "0.4.0-pre.1"
+version: "0.4.0-pre.2"
 entryPoint: "main"
 remoteDependencies: [
   "https://code.jquery.com/jquery-1.11.0.min.js"


open game-dev-2-final-project for Tarena national final classroom (#5485)

--- app/views/play/CampaignView.coffee
+++ app/views/play/CampaignView.coffee
@@ -449,6 +449,9 @@
           unless @levelStatusMap[session.get('levelID')] is 'complete'  # Don't overwrite a complete session with an incomplete one
             @levelStatusMap[session.get('levelID')] = if session.get('state')?.complete then 'complete' else 'started'
           @levelDifficultyMap[session.get('levelID')] = session.get('state').difficulty if session.get('state')?.difficulty
+        if @courseInstance.get('classroomID') == "5d12e7e36eea5a00ac71dc8f"  # Tarena national final classroom
+          unless @levelStatusMap['game-dev-2-final-project']  #make sure all players could access GD2 final on competition day
+            @levelStatusMap['game-dev-2-final-project'] = 'started'

   buildLevelScoreMap: ->
     for session in @sessions.models


Re-enable challenge level logic in CampaignView

--- app/views/play/CampaignView.coffee
+++ app/views/play/CampaignView.coffee
@@ -405,12 +405,11 @@
         continue if practiceOnly and not @campaign.levelIsPractice(nextLevel)

         # If it's a challenge level, we efficiently determine whether we actually do want to point it out.
-        # TODO: Re-enable after HoC
-#        if nextLevel.slug is 'kithgard-mastery' and not @levelStatusMap[nextLevel.slug] and @calculateExperienceScore() >= 3
-#          unless (timesPointedOut = storage.load("pointed-out-#{nextLevel.slug}") or 0) > 3
-#            # We may determineNextLevel more than once per render, so we can't just do this once. But we do give up after a couple highlights.
-#            dontPointTo = _.without dontPointTo, nextLevel.slug
-#            storage.save "pointed-out-#{nextLevel.slug}", timesPointedOut + 1
+        if nextLevel.slug is 'kithgard-mastery' and not @levelStatusMap[nextLevel.slug] and @calculateExperienceScore() >= 3
+          unless (timesPointedOut = storage.load("pointed-out-#{nextLevel.slug}") or 0) > 3
+            # We may determineNextLevel more than once per render, so we can't just do this once. But we do give up after a couple highlights.
+            dontPointTo = _.without dontPointTo, nextLevel.slug
+            storage.save "pointed-out-#{nextLevel.slug}", timesPointedOut + 1

         # Should we point this level out?
         if not nextLevel.disabled and @levelStatusMap[nextLevel.slug] isnt 'complete' and nextLevel.slug not in dontPointTo and


sidebar: update machine tree when there is a message

--- client/Social/Activity/sidebar/activitysidebar.coffee
+++ client/Social/Activity/sidebar/activitysidebar.coffee
@@ -205,9 +205,16 @@
       channel.participantCount = participantCount
       channel.emit 'update'

-      index = 0  if typeConstant is 'privatemessage'
-
-      item = @addItem channel, index
+      isPrivateMessage = typeConstant is 'privatemessage'
+
+      index = 0  if isPrivateMessage
+      item  = @addItem channel, index
+
+      # this method will update the vm tree
+      # `isParticipant` check may be reduntant,
+      # but it's left there just to be more cautious. ~Umut
+      @fetchWorkspaces()  if update.isParticipant and isPrivateMessage
+
       @setUnreadCount item, channel, unreadCount


Removing unneeded require.

Removing unneeded require.

--- src/canvas.coffee
+++ src/canvas.coffee
@@ -2,8 +2,8 @@
 # An interface to an HTML5 Canvas element.
 #

+
 #= require wolf
-#= require logger

 class wolf.Canvas


Starting to add toolbar items.

Starting to add toolbar items.

--- common/lib/xmodule/xmodule/js/src/problem/edit.coffee
+++ common/lib/xmodule/xmodule/js/src/problem/edit.coffee
@@ -1,6 +1,7 @@
 class @MarkdownEditingDescriptor extends XModule.Descriptor
   constructor: (element) ->
     $body.on('click', '.editor-tabs .tab', @changeEditor)
+    $body.on('click', '.editor-bar a', @onToolbarButton);

     @xml_editor = CodeMirror.fromTextArea($(".xml-box", element)[0], {
     mode: "xml"
@@ -27,6 +28,16 @@
       @setCurrentEditor(@xml_editor)
       @xml_editor.setValue(MarkdownEditingDescriptor.markdownToXml(@markdown_editor.getValue()))

+  onToolbarButton: (e) =>
+    e.preventDefault();
+    switch $(e.currentTarget).attr('class')
+      when "multiple-choice-button" then console.log("multiple choice")
+      when "string-button" then console.log("string-button")
+      when "number-button" then console.log("number-button")
+      when "checks-button" then console.log("checks-button")
+      when "dropdown-button" then console.log("dropdown-button")
+      else console.log("unknown option")
+
   setCurrentEditor: (editor) ->
     $(@current_editor.getWrapperElement()).hide()
     @current_editor = editor
@@ -35,6 +46,7 @@

   save: ->
     $body.off('click', '.editor-tabs .tab', @changeEditor)
+    $body.off('click', '.editor-bar a', @onToolbarButton);
     data: @xml_editor.getValue()

   @markdownToXml: (markdown)->


edit mode

edit mode

--- app/assets/javascripts/hosts.js.coffee
+++ app/assets/javascripts/hosts.js.coffee
@@ -1,3 +1,5 @@
 # Place all the behaviors and hooks related to the matching controller here.
 # All this logic will automatically be available in application.js.
-# You can use CoffeeScript in this file: http://coffeescript.org/+# You can use CoffeeScript in this file: http://coffeescript.org/
+$('a.edit').on 'click', =>
+	@editMode = !@editMode


Make submit hotkey available to all notes forms.

Addresses #2221.

--- app/assets/javascripts/notes.js.coffee
+++ app/assets/javascripts/notes.js.coffee
@@ -63,13 +63,11 @@
     # fetch notes when tab becomes visible
     $(document).on "visibilitychange", @visibilityChange

-    @notes_forms = '.js-main-target-form textarea, .js-discussion-note-form textarea'
     # Chrome doesn't fire keypress or keyup for Command+Enter, so we need keydown.
-    $(document).on('keydown', @notes_forms, (e) ->
+    $(document).on 'keydown', '.js-note-text', (e) ->
       return if e.originalEvent.repeat
       if e.keyCode == 10 || ((e.metaKey || e.ctrlKey) && e.keyCode == 13)
-        $(@).parents('form').submit()
-    )
+        $(@).closest('form').submit()

   cleanBinding: ->
     $(document).off "ajax:success", ".js-main-target-form"
@@ -84,7 +82,7 @@
     $(document).off "click", ".js-discussion-reply-button"
     $(document).off "click", ".js-add-diff-note-button"
     $(document).off "visibilitychange"
-    $(document).off "keydown", @notes_forms
+    $(document).off "keydown", ".js-note-text"
     $(document).off "keyup", ".js-note-text"
     $(document).off "click", ".js-note-target-reopen"
     $(document).off "click", ".js-note-target-close"


Forbid using dialog module before app is ready

--- atom/browser/api/lib/dialog.coffee
+++ atom/browser/api/lib/dialog.coffee
@@ -1,5 +1,6 @@
 binding = process.atomBinding 'dialog'
 v8Util = process.atomBinding 'v8_util'
+app = require 'app'
 BrowserWindow = require 'browser-window'

 fileDialogProperties =
@@ -22,8 +23,12 @@
     options = null
   [window, options, callback]

+checkAppInitialized = ->
+  throw new Error('dialog module can only be used after app is ready') unless app.isReady()
+
 module.exports =
   showOpenDialog: (args...) ->
+    checkAppInitialized()
     [window, options, callback] = parseArgs args...

     options ?= title: 'Open', properties: ['openFile']
@@ -52,6 +57,7 @@
                            wrappedCallback

   showSaveDialog: (args...) ->
+    checkAppInitialized()
     [window, options, callback] = parseArgs args...

     options ?= title: 'Save'
@@ -72,6 +78,7 @@
                            wrappedCallback

   showMessageBox: (args...) ->
+    checkAppInitialized()
     [window, options, callback] = parseArgs args...

     options ?= type: 'none'


Don't do stats if signed out

Don't do stats if signed out

--- app/assets/javascripts/users.js.coffee
+++ app/assets/javascripts/users.js.coffee
@@ -1,14 +1,15 @@
 $(document).ready ->
   miniStats = $("#mini-user-stats")

-  viewsEl = miniStats.find(".views .number")
-  recommendationsEl = miniStats.find(".recommendations .number")
-  favouritesEl = miniStats.find(".favourites .number")
+  if miniStats.length > 0
+    viewsEl = miniStats.find(".views .number")
+    recommendationsEl = miniStats.find(".recommendations .number")
+    favouritesEl = miniStats.find(".favourites .number")

-  userChannel.bind "stats_update", (data) ->
-    viewsEl.changeStat(data['views'])
-    recommendationsEl.changeStat(data['recommendations'])
-    favouritesEl.changeStat(data['favourites'])
+    userChannel.bind "stats_update", (data) ->
+      viewsEl.changeStat(data['views'])
+      recommendationsEl.changeStat(data['recommendations'])
+      favouritesEl.changeStat(data['favourites'])

 $.fn.changeStat = (newValue) ->
   val = newValue.toString()


front(backlog): Change graph colors

front(backlog): Change graph colors

--- app/coffee/modules/backlog/main.coffee
+++ app/coffee/modules/backlog/main.coffee
@@ -1130,20 +1130,20 @@
         data.push({
             data: _.zip(milestonesRange, optimal_line)
             lines:
-                fillColor : "rgba(120,120,120,0.2)"
+                fillColor : "rgba(194,194,194,0.2)"
         })
         evolution_line = _.filter(_.map(dataToDraw.milestones, (ml) -> ml.evolution), (evolution) -> evolution?)
         data.push({
             data: _.zip(milestonesRange, evolution_line)
             lines:
-                fillColor : "rgba(102,153,51,0.3)"
+                fillColor : "rgba(147,169,93,0.15)"
         })
         client_increment_line = _.map dataToDraw.milestones, (ml) ->
             -ml["team-increment"] - ml["client-increment"]
         data.push({
             data: _.zip(milestonesRange, client_increment_line)
             lines:
-                fillColor : "rgba(255,51,51,0.3)"
+                fillColor : "rgba(147,169,93,0.15)"
         })
         team_increment_line = _.map(dataToDraw.milestones, (ml) -> -ml["team-increment"])
         data.push({
@@ -1154,7 +1154,7 @@
         colors = [
             "rgba(0,0,0,1)"
             "rgba(120,120,120,0.2)"
-            "rgba(102,153,51,1)"
+            "rgba(147,169,93,1)"
             "rgba(153,51,51,1)"
             "rgba(255,51,51,1)"
         ]


Add ability to set titlebar active state using Node/Selection

--- modules/titlebar/main/index.coffee
+++ modules/titlebar/main/index.coffee
@@ -22,6 +22,10 @@
   animateTitlebar(hx.select(@selector).selectAll('.hx-titlebar-menu-icons'))
   animateTitlebar(hx.select(@selector).selectAll('.hx-titlebar-linkbar'))

+
+isElement = (obj) -> !!(obj and obj.nodeType is 1)
+
+isSelection = (obj) -> obj instanceof hx.Selection

 class TitleBar
   constructor: (@selector) ->
@@ -66,8 +70,10 @@
       if id?
         @_.active = if hx.isString(id)
           hx.select(id).classed('hx-selected', true)
-        else
+        else if hx.isNumber(id)
           hx.select(selection.node(id)).classed('hx-selected', true)
+        else if isElement(id) or isSelection(id)
+          hx.select(id).classed('hx-selected', true)
         this
     else
       @_.active


Strip null table columns

Strip null table columns

Fixes #10

--- src/quest.coffee
+++ src/quest.coffee
@@ -184,7 +184,7 @@
           if trimWhitespace and typeof(value) == 'string'
             value = value.trim()
           else
-            value
+            value ?= ''

       table.push.apply table, values
       console.log table.toString()