Add specs for mixpanel tracking.

Add specs for mixpanel tracking.

--- apps/gallery_partnerships/test/client/view.coffee
+++ apps/gallery_partnerships/test/client/view.coffee
@@ -12,7 +12,6 @@
         $: benv.require 'jquery'
         crop: sinon.stub()
       Backbone.$ = $
-      @GalleryPartnershipsView = rewire '../../client/view'
       $.fn.waypoint = sinon.stub()
       data = _.extend require('../fixture/content.json'), sd: {}
       benv.render resolve(__dirname, '../../templates/index.jade'), data, =>
@@ -22,6 +21,8 @@
     benv.teardown()

   beforeEach ->
+    @GalleryPartnershipsView = rewire '../../client/view'
+    @GalleryPartnershipsView.__set__ 'analytics', track: click: (@track = sinon.stub())
     sinon.stub @GalleryPartnershipsView::, 'initialize'
     @view = new @GalleryPartnershipsView el: $('body')
     @clock = sinon.useFakeTimers()
@@ -29,6 +30,30 @@
   afterEach ->
     @clock.restore()
     @view.initialize.restore()
+
+  describe 'analytics', ->
+    it 'tracks nav link clicks', ->
+      (track = @track).notCalled.should.be.ok
+      callCount = 0
+      $('.gallery-partnerships-nav-link.internal').each ->
+        $(this).click()
+        track.callCount.should.equal ++callCount
+        _.last(track.args)[0].should.equal 'Clicked nav link on gallery partnerships'
+        _.last(track.args)[1].should.eql section: $(this).attr('data-section')
+
+    it 'tracks nav apply button clicks', ->
+      @track.notCalled.should.be.ok
+      $('.gallery-partnerships-nav-link.apply').click()
+      @track.calledOnce.should.be.ok
+      @track.args[0][0].should.equal 'Clicked nav apply on gallery partnerships'
+
+    it 'tracks bottom apply button clicks', ->
+      console.log @track.callCount
+      @track.notCalled.should.be.ok
+      $('.apply .apply-button').click()
+      console.log @track.callCount
+      @track.calledOnce.should.be.ok
+      @track.args[0][0].should.equal 'Clicked bottom apply on gallery partnerships'

   describe 'slideshow', ->
     before ->


Add path animations

Add path animations

--- framer/Animation.coffee
+++ framer/Animation.coffee
@@ -7,6 +7,7 @@
 {BaseClass} = require "./BaseClass"
 {Animator} = require "./Animators/Animator"
 {LinearAnimator} = require "./Animators/LinearAnimator"
+{SVG, SVGPath} = require "./SVG"
 Curves = require "./Animators/Curves"

 numberRE = /[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/
@@ -260,6 +261,7 @@
 	_start: =>
 		@_delayTimer = null
 		@emit(Events.AnimationStart)
+		@_previousValue = 0
 		Framer.Loop.on("update", @_update)

 	finish: =>
@@ -277,9 +279,22 @@

 	_prepareUpdateValues: =>
 		@_valueUpdaters = {}
-
 		for k, v of @_stateB
-			if Color.isColorObject(v) or Color.isColorObject(@_stateA[k])
+			if SVGPath.isPath(v)
+				path = new SVGPath(v)
+				direction = null
+				start = null
+				end = null
+				switch k
+					when "x", "minX", "midX", "maxX", "width"
+						direction = "horizontal"
+					when "y", "minY", "midY", "maxY", "height"
+						direction = "vertical"
+					when "rotation", "rotationZ", "rotationX", "rotationY"
+						direction = "angle"
+
+				@_valueUpdaters[k] = path.valueUpdater(direction, @_target, @_target[k])
+			else if Color.isColorObject(v) or Color.isColorObject(@_stateA[k])
 				@_valueUpdaters[k] = @_updateColorValue
 			else if Gradient.isGradient(v) or Gradient.isGradient(@_stateA[k])
 				@_valueUpdaters[k] = @_updateGradientValue
@@ -297,7 +312,9 @@
 				@_valueUpdaters[k] = @_updateNumberValue

 	_updateValues: (value) =>
-		@_valueUpdaters[k](k, value) for k, v of @_stateB
+		delta = value - @_previousValue
+		@_previousValue = value
+		@_valueUpdaters[k](k, value, delta) for k, v of @_stateB
 		return null

 	_updateNumberValue: (key, value) =>
@@ -400,7 +417,7 @@
 		return _.pick(@layer, _.keys(@properties))

 	@isAnimatable = (v) ->
-		_.isNumber(v) or _.isFunction(v) or isRelativeProperty(v) or Color.isColorObject(v) or Gradient.isGradientObject(v)
+		_.isNumber(v) or _.isFunction(v) or isRelativeProperty(v) or Color.isColorObject(v) or Gradient.isGradientObject(v) or SVGPath.isPath(v)

 	# Special cases that animate with different types of objects
 	@isAnimatableKey = (k) ->
@@ -418,7 +435,10 @@
 					when "size" then derivedKeys = ["width", "height"]
 					when "point" then derivedKeys = ["x", "y"]
 					else derivedKeys = []
-				if _.isObject(v)
+				if SVGPath.isPath(v)
+					for derivedKey in derivedKeys
+						animatableProperties[derivedKey] = v
+				else if _.isObject(v)
 					_.defaults(animatableProperties, _.pick(v, derivedKeys))
 				else if _.isNumber(v)
 					for derivedKey in derivedKeys


Add `trackchanges.continueOnError` as default

--- server-ce/settings.coffee
+++ server-ce/settings.coffee
@@ -100,6 +100,9 @@
 	# 		key: "AWS_KEY"
 	# 		secret: "AWS_SECRET"
 	#
+
+	trackchanges:
+		continueOnError: true

 	# Local disk caching
 	# ------------------


random password

random password

--- code/linode.coffee
+++ code/linode.coffee
@@ -122,7 +122,7 @@
       'DistributionID': id
       'Label': 'system'
       'Size': @config.disk_size * 1000
-      'rootPass': 'r00ter'
+      'rootPass': Math.random() + "" + Math.random()
       'rootSSHKey': LithiumConfig.sshkey_public()
       , callback


debugging on during beta

debugging on during beta

--- content/better-bibtex.coffee
+++ content/better-bibtex.coffee
@@ -6,6 +6,10 @@
 Zotero.BetterBibTeX.ErrorReport = require('./error-report/error-report.coffee')

 Prefs = require('./preferences.coffee') # needs to be here early, initializes the prefs observer
+
+# TODO: remove after beta
+Zotero.Prefs.get('debug.store', true)
+Zotero.Debug.setStore(true)

 Translators = require('./translators.coffee')
 KeyManager = require('./keymanager.coffee')


stray console log

stray console log

--- assets/artwork.coffee
+++ assets/artwork.coffee
@@ -1,4 +1,3 @@
 require '../components/main_layout/client.coffee'
 require('../lib/analytics.coffee').load ->
-  console.log 'loaded'
   $ require('../apps/artwork/client/index.coffee').init


updated

updated

--- lib/Bumped.messages.coffee
+++ lib/Bumped.messages.coffee
@@ -7,7 +7,7 @@
   ADD_FILE            : (file) -> "'#{file}' has been added."
   ADD_ALREADY_FILE    : (file) -> "'#{file}' is already added."
   REMOVE_FILE         : (file) -> "'#{file}' has been removed."
-  DETECTED_FILE       : (file) -> "'#{file}' has been detected in the directory."
+  DETECTED_FILE       : (file) -> "Detected '#{file}' in the directory."
   CREATED_VERSION     : (version) -> "Releases version '#{version}'."
   CURRENT_VERSION     : (version) -> "Current version is '#{version}'."


Don't call methods that no longer exist.

--- lib/conflict-marker.coffee
+++ lib/conflict-marker.coffee
@@ -24,7 +24,6 @@

       @subs.add c.onDidResolveConflict =>
         unresolved = (v for v in @coveringViews when not v.conflict().isResolved())
-        v.reposition() for v in unresolved
         resolvedCount = @conflicts.length - Math.floor(unresolved.length / 3)
         atom.emitter.emit 'merge-conflicts:resolved',
           file: @editor.getPath(),


add openSettings command

add openSettings command

--- lib/edit-in-new-tab.coffee
+++ lib/edit-in-new-tab.coffee
@@ -1,4 +1,5 @@
 {CompositeDisposable} = require 'atom'
+meta = require '../package.json'

 module.exports = EditInNewTab =
   config:
@@ -68,8 +69,9 @@
     @subscriptions = new CompositeDisposable

     # Register commands
-    @subscriptions.add atom.commands.add 'atom-workspace', 'selection:edit-in-new-tab': => @editInNewTab(false)
-    @subscriptions.add atom.commands.add 'atom-workspace', 'selection:move-to-new-tab': => @editInNewTab(true)
+    @subscriptions.add atom.commands.add 'atom-workspace', 'edit-in-new-tab:copy-selection': => @editInNewTab(false)
+    @subscriptions.add atom.commands.add 'atom-workspace', 'edit-in-new-tab:move-selection': => @editInNewTab(true)
+    @subscriptions.add atom.commands.add 'atom-workspace', 'edit-in-new-tab:open-package-settings': => @openSettings()

   deactivate: ->
     @subscriptions.dispose()
@@ -118,3 +120,6 @@

       .catch (error) ->
         atom.notifications.addError(error, dismissable: true)
+
+  openSettings: ->
+    atom.workspace.open("atom://config/packages/#{meta.name}")


setMessageTypes

setMessageTypes

--- lib/haskell-ghc-mod.coffee
+++ lib/haskell-ghc-mod.coffee
@@ -57,6 +57,11 @@
     console.log "consumeUPI"
     disposables = new CompositeDisposable
     disposables.add upi.disposables
+
+    upi.setMessageTypes
+      error: {}
+      warning: {}
+      lint: {}

     disposables.add atom.commands.add 'atom-workspace',
       'ide-haskell:shutdown-backend': =>


Update Csound message callback functions

--- lib/message-manager.coffee
+++ lib/message-manager.coffee
@@ -5,7 +5,7 @@
   constructor: (csound, Csound) ->
     @emitter = new Emitter

-    messageCallback = (Csound, attributes, string) =>
+    messageCallback = (attributes, string) =>
       @emitter.emit 'did-receive-message', {string: string, attributes: attributes}
     csound.SetDefaultMessageCallback messageCallback
     csound.SetMessageCallback Csound, messageCallback


#424 :fire: was not necessary

#424 :fire: was not necessary

--- lib/operator-insert.coffee
+++ lib/operator-insert.coffee
@@ -164,16 +164,6 @@
   @extend()
   which: 'end'

-class InsertAtStartOfInnerCurrentLine extends InsertByTarget
-  @extend()
-  which: 'start'
-  target: "InnerCurrentLine"
-
-class InsertAtEndOfInnerCurrentLine extends InsertByTarget
-  @extend()
-  which: 'end'
-  target: "InnerCurrentLine"
-
 class InsertAtStartOfInnerSmartWord extends InsertByTarget
   @extend()
   which: 'start'


Up cozy-indexer version, handle breaking changes

--- server/lib/indexer.coffee
+++ server/lib/indexer.coffee
@@ -50,6 +50,10 @@
 exports.initialize = (callback) ->

     async.waterfall [
+        (callback) ->
+            indexer.init (err, si) ->
+                callback err
+
         (callback) ->
             indexer.store.open callback

@@ -351,6 +355,7 @@

 exports.status = getStatus

+
 saveStatus = (docType, status, callback) ->
     status = JSON.stringify status
     indexer.store.set "indexingstatus/#{docType}", status, callback


Removed debugging print.

Removed debugging print.

--- server/lib/install.coffee
+++ server/lib/install.coffee
@@ -1,5 +1,3 @@
-console.log Meteor.settings
-
 if Meteor.users.findOne {}
   __meteor_runtime_config__.INSTALL = @INSTALL = false


reduced Table-field cell height

reduced Table-field cell height

--- coffee/core.coffee
+++ coffee/core.coffee
@@ -21,7 +21,7 @@
         }

-  Constants.DIAG_CELL_HEIGHT = 20
+  Constants.DIAG_CELL_HEIGHT = 18
   Constants.DIAG_UNIQUE_ID = "diag-table-"
   #Models
   window.TableField = Backbone.Model.extend


reticulating models and plinks

reticulating models and plinks

--- coffee/main.coffee
+++ coffee/main.coffee
@@ -378,11 +378,12 @@
       @props = {
         name: @props.name,
         sys: @props.sys,
-        design: @props.dsg,
+        design: @props.design,
         endpoints: [
-          {name: "link0", sys: "root", design: dsg, ifname: ""},
-          {name: "link0", sys: "root", design: dsg, ifname: ""}
+          {name: "link0", sys: "root", design: dsg},
+          {name: "link0", sys: "root", design: dsg}
         ]
+        bindings: ""
       }
       @props[@endpoint[0].props.name] = "" if @endpoint[0] instanceof Model
       @props[@endpoint[1].props.name] = "" if @endpoint[1] instanceof Model
@@ -390,11 +391,13 @@
     setEndpointData: ->
       @props.endpoints[0].name = @endpoint[0].props.name
       @props.endpoints[0].sys = @endpoint[0].props.sys
-      @props.endpoints[0].ifname = @ep_ifx[0]
+      if !@isPhysical()
+        @props.endpoints[0].ifname = @ep_ifx[0]

       @props.endpoints[1].name = @endpoint[1].props.name
       @props.endpoints[1].sys = @endpoint[1].props.sys
-      @props.endpoints[1].ifname = @ep_ifx[1]
+      if !@isPhysical()
+        @props.endpoints[1].ifname = @ep_ifx[1]

     ifInternetToWanLink:  ->
       @applyWanProps() if @isInternet()
@@ -811,8 +814,10 @@
       true

     for _, l of link_updates
+      type = "Link"
+      type = "Plink" if l.isPhysical()
       link_msg.Elements.push(
-        { OID: l.id, Type: l.constructor.name, Element: l.props }
+        { OID: l.id, Type: type, Element: l.props }
       )
       true


also emit task id

also emit task id

Needed for the task panels store

--- tutor/src/flux/task.coffee
+++ tutor/src/flux/task.coffee
@@ -101,7 +101,7 @@

   stepCompleted: (obj, taskStepId) ->
     this._loaded(obj, obj.id)
-    @emit('step.completed', taskStepId)
+    @emit('step.completed', taskStepId, obj.id)

   exports:
     getSteps: (id) ->


fixed tests

fixed tests

--- test/cli_test.coffee
+++ test/cli_test.coffee
@@ -96,7 +96,7 @@
   )

   tests =
-    tearDown:(callback) ->
+    tearDown: (callback) ->
       return do callback unless fs.existsSync OUTPUT_DIR

       fs.readdirSync(OUTPUT_DIR).forEach (file) ->
@@ -120,43 +120,43 @@

         test.done()

-  relativeLink: testAbsolute "#{COMMAND} -ep \"anchor\"", """
+  relativeLink: testAbsolute("#{COMMAND} -ep \"anchor\"", """
     [anchor][0]

     [0]: mock

-  """, 'Link should be relative', '-ep'
+  """, 'Link should be relative', '-ep')

-  relativeRootLink: testAbsolute "#{COMMAND} -ep \"anchor\"", """
+  relativeRootLink: testAbsolute("#{COMMAND} -ep \"anchor\"", """
     [anchor][0]

     [0]: /mock

-  """, 'Root link should be relative', '-ep'
+  """, 'Root link should be relative', '-ep')

-  absoluteLink: testAbsolute "#{COMMAND} -epa \"anchor\"", """
+  absoluteLink: testAbsolute("#{COMMAND} -epa \"anchor\"", """
     [anchor][0]

     [0]: #{toFileUrl 'mock'}

-  """, 'Link should be absolute', '-epa'
+  """, 'Link should be absolute', '-epa')

-  absoluteRootLink: testAbsolute "#{COMMAND} -epa \"anchor\"", """
+  absoluteRootLink: testAbsolute("#{COMMAND} -epa \"anchor\"", """
     [anchor][0]

     [0]: #{toFileUrl '/mock'}

-  """, 'Root link should be absolute', '-epa'
+  """, 'Root link should be absolute', '-epa')

-  relativeImage: testAbsolute "#{COMMAND} -ep \"\"", """
+  relativeImage: testAbsolute("#{COMMAND} -ep \"\"", """
     ![](mock)

-  """, 'Image should be relative', '-ep'
+  """, 'Image should be relative', '-ep')

-  absoluteImage: testAbsolute "#{COMMAND} -epa \"\"", """
+  absoluteImage: testAbsolute("#{COMMAND} -epa \"\"", """
     ![](#{toFileUrl 'mock'})

-  """, 'Image should be absolute', '-epa'
+  """, 'Image should be absolute', '-epa')

 exports.stdio = (test) ->
   test.expect 2


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/distance.coffee
+++ test/distance.coffee
@@ -22,6 +22,6 @@
     width: 7
     height: 20

-
+  equals,

 module()


add required rect() method to Drawable model

--- src/drawable/Drawable.coffee
+++ src/drawable/Drawable.coffee
@@ -35,3 +35,11 @@
     ###
     draw: () ->
         throw new Error "[Rotten.Drawable] Your drawable should override draw() method."
+
+
+    ###*
+     # Returns an actual rect of your drawable
+     # @method rect
+    ###
+    rect: ->
+        throw new Error "[Rotten.Drawable] Your drawable should override rect() method."


fix typeo

fix typeo

--- assets/javascripts/views/post/edit_post.js.coffee
+++ assets/javascripts/views/post/edit_post.js.coffee
@@ -15,7 +15,7 @@
     setImmediate =>
       @constructor.instances.all[@_child_views.EditPostMentionsAutoCompleteTextarea?[0]]?.textarea_view?.focus()

-    # inherit specific method from NewPostForm
+    # inherit specific methods from NewPostForm
     for method in ['submitWithValidation', 'initCharCounter', 'updateCharCounter', 'initValidation', 'validate', 'showErrors', 'clearErrors', 'buildPostAttributes', 'buildPostMentionsAttributes', 'buildPostPermissionsAttributes']
       do (method) =>
         @[method] = => TentStatus.Views.NewPostForm::[method].apply(@, arguments)


Refactored out the side effect to reduce ugliness

Great example of when "last line in function must be the returned thing"
and why tap is great for computing a value you want to return and then
having some other trailing mutation after-the-fact. Eliminated a local
and two ugly "-1"'s to keep the index in the right place when finding
the sequenced stub value

--- lib/store/stubbings.coffee
+++ lib/store/stubbings.coffee
@@ -9,12 +9,8 @@

   invoke: (testDouble, args) ->
     return unless stubbing = stubbingFor(testDouble, args)
-    stubbing.callCount += 1
-    callIndex = stubbing.callCount - 1
-    if callIndex < stubbing.stubbedValues.length
-      stubbing.stubbedValues[callIndex]
-    else
-      _.last(stubbing.stubbedValues)
+    _.tap stubbedValueFor(stubbing), ->
+      stubbing.callCount += 1

   for: (testDouble) ->
     store.for(testDouble).stubbings
@@ -22,6 +18,12 @@
 stubbingFor = (testDouble, actualArgs) ->
   _(store.for(testDouble).stubbings).findLast (stubbing) ->
     isSatisfied(stubbing, actualArgs)
+
+stubbedValueFor = (stubbing) ->
+  if stubbing.callCount < stubbing.stubbedValues.length
+    stubbing.stubbedValues[stubbing.callCount]
+  else
+    _.last(stubbing.stubbedValues)

 isSatisfied = (stubbing, actualArgs) ->
   argsMatch(stubbing.args, actualArgs, stubbing.config) &&


:green_heart: Fix variable markers id still as numbers

--- lib/variable-marker.coffee
+++ lib/variable-marker.coffee
@@ -31,7 +31,7 @@
   serialize: ->
     return if @wasDestroyed
     {
-      markerId: @marker.id
+      markerId: String(@marker.id)
       bufferRange: @marker.getBufferRange().serialize()
       variable: @variable.name
     }


Add js-hover to body irrespective of browser support so that other hovers can function as normal.

--- app/assets/javascripts/lib/core/base.coffee
+++ app/assets/javascripts/lib/core/base.coffee
@@ -79,11 +79,12 @@
         window.s.linkstacker("footer")

     scrollPerf: ->
+      # Add this irrespective of browser support so that other hovers can function as normal.
+      $('body').addClass('js-hover')
+
       if ($('html.ie7, html.ie8, body.browserIE7, body.browserIE8').length is 0)
         # Used to track the enabling of hover effects
         enableTimer = false
-
-        $('body').addClass('js-hover')

         # Listen for a scroll and use that to remove the possibility of hover effects
         window.addEventListener 'scroll', ->


Cleanup new App created event so we can call it anywhere

--- client/landing/app/Applications/StartTab.kdapplication/AppView.coffee
+++ client/landing/app/Applications/StartTab.kdapplication/AppView.coffee
@@ -14,6 +14,9 @@
     appsController.on "AppsRefreshed", (apps)=>
       @getSingleton("kodingAppsController").appStorage.fetchStorage (storage)=>
         @decorateApps apps
+
+    appsController.on "aNewAppCreated", =>
+      @aNewAppCreated()

     # mainView.sidebar.finderResizeHandle.on "DragInAction", =>
     #   log "DragInAction", mainView.contentPanel.getWidth()
@@ -44,17 +47,7 @@
       # iconClass   : "make-an-app"
       title       : "Make a new App"
       callback    : =>
-        appsController.makeNewApp (err)=>
-          if not err
-            new KDNotificationView
-              type     : "mini"
-              cssClass : "success"
-              title    : "App is created! Check your Applications folder!"
-
-            @removeAppIcons()
-            @showLoader()
-            appsController.refreshApps =>
-              @hideLoader()
+        appsController.makeNewApp()

     # appManager.fetchStorage "KodingApps", "1.0", (err, storage)=>
     #   storage.update $set : { "bucket.apps" : {} }, =>
@@ -96,6 +89,17 @@
       @appItemContainer.addSubView new StartTabOldAppThumbView
         tab : @
       , app
+
+  aNewAppCreated:->
+    new KDNotificationView
+      type     : "mini"
+      cssClass : "success"
+      title    : "App is created! Check your Applications folder!"
+
+    @removeAppIcons()
+    @showLoader()
+    @getSingleton("kodingAppsController").refreshApps =>
+      @hideLoader()

   pistachio:->
     """


Disable whitespace plugin

Disable whitespace plugin

It was messing with the editorconfig plugin.

--- atom/atom/config.cson
+++ atom/atom/config.cson
@@ -52,6 +52,7 @@
       "file-type-icons"
       "autocomplete-bibtex"
       "wrap-guide"
+      "whitespace"
     ]
     ignoredNames: [
       ".git"


Fix pusher auth on pro

Fix pusher auth on pro

--- app/utils/pusher.coffee
+++ app/utils/pusher.coffee
@@ -1,4 +1,5 @@
 `import ENV from 'travis/config/environment'`
+`import Ajax from 'travis/utils/ajax'`

 TravisPusher = (config) ->
   @init(config)
@@ -117,7 +118,7 @@

     unless channels.fetching
       channels.fetching = true
-      Travis.ajax.post Pusher.channel_auth_endpoint, { socket_id: socketId, channels: names }, (data) ->
+      Ajax.post Pusher.channel_auth_endpoint, { socket_id: socketId, channels: names }, (data) ->
         channels.fetching = false
         callback(data.channels) for callback in channels.callbacks


Support IFERROR

Support IFERROR

--- src/formula/evaluator.coffee
+++ src/formula/evaluator.coffee
@@ -485,6 +485,13 @@
       true_expr
     else
       false_expr
+
+  IFERROR: (args)->
+    @checkArgumentSize(args, 2)
+    x = @getValue(args[0])
+    if x instanceof FormulaError
+      return @getValue(args[1])
+    x

   INT: (args)->
     @checkArgumentSize(args, 1)


Implement Categories, clean up old code

Implement Categories, clean up old code

--- model/Checkmark.coffee
+++ model/Checkmark.coffee
@@ -12,7 +12,7 @@
 Checkmarks.before.insert (userId, Checkmark) ->
   Checkmark._id ||= Random.id()
   now = new Date()
-  _.defaults(Checkmark,
+  _.autovalues(Checkmark,
     userId: userId
     updatedAt: now
     createdAt: now


ace: added Delphi highlighting to code snips and stuff

--- client/app/Applications/Ace.kdapplication/acesettings.coffee
+++ client/app/Applications/Ace.kdapplication/acesettings.coffee
@@ -109,6 +109,7 @@
     html        : ["HTML"         , "htm|html|xhtml"]
     c_cpp       : ["C/C++"        , "c|cc|cpp|cxx|h|hh|hpp"]
     clojure     : ["Clojure"      , "clj"]
+    delphi      : ["Delphi"       , "delphi"]
     jade        : ["Jade"         , "jade"]
     java        : ["Java"         , "java"]
     javascript  : ["JavaScript"   , "js"]


hackathon: added PubNub

hackathon: added PubNub

--- client/landing/site.hackathon/coffee/home/AppViewFull.coffee
+++ client/landing/site.hackathon/coffee/home/AppViewFull.coffee
@@ -443,6 +443,12 @@

The top-performing team that has an oDesk.com or Elance.com account will receive an extra $2,500.
               Be sure to add your oDesk or Elance userID to the questionnaire that we send you.

+

+

+

+

+
+
PubNub will award an additional $1,000 to the team that makes the best use of their API.


Client: Activity: Updated socialApiController mapChannel method to set correct properties of item by operation type (create or edit)

--- client/app/lib/socialapicontroller.coffee
+++ client/app/lib/socialapicontroller.coffee
@@ -304,11 +304,19 @@
     item.payload             = data.payload

     if isFeedEnabled() and item.typeConstant in ['privatemessage', 'bot']
-      item._originalName = item._originalName or item.name
-      item.name = data.purpose
-      item.purpose = item.payload?.description or ''
+      # if cache founded this is an update operation so we have to change purpose
+      # with data payload description value. Otherwise this is a create private message
+      # operation and we have to set item name with purpose value of data and
+      # item purpose with payload description
+      if cacheFound
+        item.payload.description = data.payload?.description or ''
+        item.purpose             = data.payload?.description or ''
+      else
+        item._originalName = item._originalName or item.name
+        item.name          = data.purpose
+        item.purpose       = item.payload?.description or ''
     else
-      item.name = data.name
+      item.name    = data.name
       item.purpose = data.purpose

     item.participantCount    = channel.participantCount


Client: Activity: Added mapUpdatedChannel to update  purpose and payload data of cacheItem

--- client/app/lib/socialapicontroller.coffee
+++ client/app/lib/socialapicontroller.coffee
@@ -263,6 +263,21 @@
     }

+  mapUpdatedChannel = (options) ->
+
+    { socialapi } = kd.singletons
+
+    channel = options.channel
+
+    # if we find the channel in cache, replace item with it
+    cacheItem = socialapi.retrieveCachedItem channel.typeConstant, channel.id
+
+    cacheItem.purpose = channel.purpose
+    cacheItem.payload = channel.payload
+
+    return cacheItem
+
+
   mapChannel = (channel) ->
     { socialapi } = kd.singletons

@@ -609,6 +624,7 @@
       {event: "AddedToChannel",     mapperFn: mapParticipant}
       {event: "RemovedFromChannel", mapperFn: mapParticipant}
       {event: "ChannelDeleted",     mapperFn: mapChannel}
+      {event: "ChannelUpdated",     mapperFn: mapUpdatedChannel}
     ]

   serialize = (obj) ->


Collab: settings pane do not bind channel events temp

--- client/ide/views/chat/settingspane.coffee
+++ client/ide/views/chat/settingspane.coffee
@@ -30,6 +30,8 @@

   bindChannelEvents: ->
+
+    return

     channel = @getData()


resurrection.embedly: embed type getter

resurrection.embedly: embed type getter

--- client/Main/utils.coffee
+++ client/Main/utils.coffee
@@ -467,3 +467,30 @@
     form.addSubView finderWrapper = new KDView cssClass : "save-as-dialog save-file-container", null
     finderWrapper.addSubView finder
     finderWrapper.setHeight 200
+
+  # TODO: Not totally sure what this is supposed to do, but I put it here
+  #       to bypass awful hacks by Arvid Kahl:
+  getEmbedType: (type) ->
+    switch type
+      when 'audio', 'xml', 'json', 'ppt', 'rss', 'atom'
+        return 'object'
+
+      # this is usually just a single image
+      when 'photo','image'
+        return 'image'
+
+      # rich is a html object for things like twitter posts
+      # link is fallback for things that may or may not have any kind of preview
+      # or are links explicitly
+      # also captures 'rich content' and makes regular links from that data
+      when 'link', 'html'
+        return 'link'
+
+      # embedly supports many error types. we could display those to the user
+      when 'error'
+        log 'Embedding error ', data.error_type, data.error_message
+        return 'error'
+
+      else
+        log "Unhandled content type '#{type}'"
+        return 'error'


shortened transformOrigin to origin

shortened transformOrigin to origin

--- oriDomi.coffee
+++ oriDomi.coffee
@@ -60,7 +60,7 @@

 css =
   transform: 'transform'
-  transformOrigin: 'transformOrigin'
+  origin: 'transformOrigin'
   transformStyle: 'transformStyle'
   transitionProp: 'transitionProperty'
   transitionDuration: 'transitionDuration'
@@ -195,7 +195,7 @@
     hPanel.style[css.transitionProp] = css.transformProp
     hPanel.style[css.transitionDuration] = @settings.speed + 's'
     hPanel.style[css.transitionEasing] = @settings.easingMethod
-    hPanel.style[css.transformOrigin] = 'top'
+    hPanel.style[css.origin] = 'top'
     hPanel.style[css.transformStyle] = 'preserve-3d'
     if @settings.forceAntialiasing
       hPanel.style.outline = '1px solid transparent'
@@ -245,7 +245,7 @@
     vPanel.style.width = @panelWidth + 'px'
     vPanel.style.height = '100%'
     vPanel.style[css.transform] = @_transform [@panelWidth, 0]
-    vPanel.style[css.transformOrigin] = 'left'
+    vPanel.style[css.origin] = 'left'
     vPanel.appendChild vMask

     for anchor in ['left', 'right']
@@ -257,7 +257,7 @@
         if anchor is 'left'
           xOffset = -(i * @panelWidth)
         else
-          panel.style[css.transformOrigin] = 'right'
+          panel.style[css.origin] = 'right'
           xOffset = -((@vPanels * @panelWidth) - (@panelWidth * (i + 1)))

         content.style[css.transform] = @_transform [xOffset, 0]


killed off getRight/getBottom coord functions

--- oriDomi.coffee
+++ oriDomi.coffee
@@ -431,14 +431,6 @@
         'left'

-  _getRightAnchorCoord: ->
-    @panelWidth * (@vPanels - 1) - @vPanels + 1
-
-
-  _getBottomAnchorCoord: ->
-    @panelHeight * (@hPanels - 1) - @hPanels + 1
-
-
   _methodDefaults:
     accordion:
       sticky: false


deprecated displayStyle property

deprecated displayStyle property

--- oridomi.coffee
+++ oridomi.coffee
@@ -225,10 +225,6 @@
     # Record the current global styling of the target element.
     @_elStyle = root.getComputedStyle @el

-    # Save the original CSS display of the target. If `none`, assume `block`.
-    @displayStyle = @_elStyle.display
-    @displayStyle = 'block' if @displayStyle is 'none'
-
     # To calculate the full dimensions of the element, create arrays of relevant metric keys.
     xMetrics = ['width', 'paddingLeft', 'paddingRight', 'borderLeftWidth', 'borderRightWidth']
     yMetrics = ['height', 'paddingTop', 'paddingBottom', 'borderTopWidth', 'borderBottomWidth']


DRYer shader line

DRYer shader line

--- oridomi.coffee
+++ oridomi.coffee
@@ -749,8 +749,8 @@
     for panel, i in @panels[@lastAnchor]
       panel.style[css.transitionDuration] = speed
       if @shading
-        @shaders[@lastAnchor][shaderPair[0]][i].style[css.transitionDuration] = speed
-        @shaders[@lastAnchor][shaderPair[1]][i].style[css.transitionDuration] = speed
+        for side in shaderPair
+          @_shaders[@lastAnchor][side][i].style[css.transitionDuration] = speed

     # Return null and not the loop's result.
     null


Fixed indentation for headings and sameline tags

--- settings/language-ss.cson
+++ settings/language-ss.cson
@@ -2,5 +2,5 @@
 	editor:
 		commentStart: '<%-- '
 		commentEnd: ' --%>'
-		increaseIndentPattern: '<%\\s+(?:if|else|loop|with|control|cache|uncache)|<%t|<(?!area|base|br|col|frame|hr|html|img|input|link|meta|param)[a-zA-Z]+|^\\/>'
+		increaseIndentPattern: '<%\\s+(if|else|loop|with|control|cache|uncache)(?!.*<% end_\\1)|<%t|<(?!area|base|br|col|iframe|hr|html|img|input|link|meta|param)([A-Za-z0-9]+)(?!.*<\/\\2)|^\\/>'
 		decreaseIndentPattern: '<%\\s+end_[a-zA-Z]+|else]|<\\/[a-zA-Z]+>'


Changed quotes

Changed quotes

--- client/test/lib/helpers/idehelpers.coffee
+++ client/test/lib/helpers/idehelpers.coffee
@@ -170,7 +170,7 @@

     webPath         = '/home/' + user.username + '/Web'
     fileSelector    = "span[title='" + webPath + '/' + file + "']"
-    submenuSelector = 'li.as-#{type}'
+    submenuSelector = "li.as-#{type}"
     extension = '.zip'

     if type is 'targz'


remove hardcoded admin id cause we have current user

--- app/scripts/common/resources/account.coffee
+++ app/scripts/common/resources/account.coffee
@@ -13,7 +13,6 @@
   grantAllocationRights: (account)->
     #TODO Admin stuff
     params =
-      admin_id: 1
       amount: account._allocation_rights
     Restangular.one('users', account.user_id).get().then (success)->
       success.post("grant_allocation_rights/#{account.budget_id}", params)


[Server][AutoRefresh][Reloader] fix restoring scroll offset after full reload

--- lib/Epuber/server/auto_refresh/reloader.coffee
+++ lib/Epuber/server/auto_refresh/reloader.coffee
@@ -49,7 +49,7 @@

     constructor: (@window, @console) ->
         @document = @window.document
-        @window.addEventListener('load', (=> @_restoreScrollPosition), false)
+        $(@window).load => @_restoreScrollPosition()

     _head: ->


Also redundant, as the CollectionUtils already triggers a reset any time anything changes

--- core/app/backbone/collections/ndp_evidence_collection.coffee
+++ core/app/backbone/collections/ndp_evidence_collection.coffee
@@ -1,7 +1,6 @@
 class window.NDPEvidenceCollection extends Backbone.Factlink.Collection

   initialize: (models, options) ->
-    @on 'change sync', @sort, @
     @fact = options.fact

     @_containedCollections = [


Shorter wake up hint delay.

Shorter wake up hint delay.

--- packages/pixelartacademy-season1-episode1/start/scenes/wakeup.coffee
+++ packages/pixelartacademy-season1-episode1/start/scenes/wakeup.coffee
@@ -48,8 +48,8 @@
       @_hintIndex = Math.min (@_hintIndex or 0) + 1, 3
       listener.startScript label: "Try#{@_hintIndex}"

-      # If we haven't displayed all three hints, give another hint after 30 seconds.
-      @giveHint 30000 if @_hintIndex < 3
+      # If we haven't displayed all three hints, give another hint after 10 seconds.
+      @giveHint 10000 if @_hintIndex < 3
     ,
       delay

@@ -90,8 +90,8 @@
   onEnter: (enterResponse) ->
     scene = @options.parent

-    # Show the first hint after 30 seconds.
-    scene.giveHint 30000
+    # Show the first hint after 10 seconds.
+    scene.giveHint 10000

   onCommand: (commandResponse) ->
     scene = @options.parent


ws for logger

ws for logger

--- packages/deft/test/coffee/log/Logger.coffee
+++ packages/deft/test/coffee/log/Logger.coffee
@@ -4,277 +4,277 @@
 ###

 describe( 'Deft.log.Logger', ->
-
+
 	describe( 'log()', ->
-
+
 		if Ext.getVersion( 'extjs' )?
 			# Ext JS
-
+
 			describe( 'logs a message with the specified priority', ->
 				logFunction = null
-
+
 				beforeEach( ->
 					logFunction = sinon.stub( Ext, 'log' )
 					return
 				)
-
+
 				afterEach( ->
 					logFunction.restore()
 					return
 				)
-
+
 				specify( 'no priority specified', ->
 					Deft.Logger.log( 'message', 'info' )
-
+
 					expect( logFunction ).to.be.calledOnce.and.calledWith( { level: 'info', msg: 'message' } )
 					return
 				)
-
+
 				specify( 'verbose', ->
 					Deft.Logger.log( 'message', 'verbose' )
-
+
 					expect( logFunction ).to.be.calledOnce.and.calledWith( { level: 'info', msg: 'message' } )
 					return
 				)
-
+
 				specify( 'deprecate', ->
 					Deft.Logger.log( 'message', 'deprecate' )
-
+
 					expect( logFunction ).to.be.calledOnce.and.calledWith( { level: 'warn', msg: 'message' } )
 					return
 				)
-
+
 				specify( 'warn', ->
 					Deft.Logger.log( 'message', 'warn' )
-
+
 					expect( logFunction ).to.be.calledOnce.and.calledWith( { level: 'warn', msg: 'message' } )
 					return
 				)
-
+
 				specify( 'error', ->
 					Deft.Logger.log( 'message', 'error' )
-
+
 					expect( logFunction ).to.be.calledOnce.and.calledWith( { level: 'error', msg: 'message' } )
 					return
 				)
-
+
 				return
 			)
-
+
 		else
 			# Sencha Touch
-
+
 			describe( 'logs a message with the specified priority, when Ext.Logger is available', ->
 				logFunction = null
-
+
 				beforeEach( ->
 					if not Ext.Logger?
-						Ext.define( 'Ext.Logger',
+						Ext.define( 'Ext.Logger',
 							singleton: true
 							log: Ext.emptyFn
 							isMock: true
 						)
 					logFunction = sinon.stub( Ext.Logger, 'log' )
 				)
-
+
 				afterEach( ->
 					logFunction.restore()
 					if Ext.Logger.isMock
 						Ext.Logger = null
 				)
-
+
 				specify( 'no priority specified', ->
 					Deft.Logger.log( 'message', 'info' )
-
+
 					expect( logFunction ).to.be.calledOnce.and.calledWith( 'message', 'info' )
 					return
 				)
-
+
 				specify( 'verbose', ->
 					Deft.Logger.log( 'message', 'verbose' )
-
+
 					expect( logFunction ).to.be.calledOnce.and.calledWith( 'message', 'verbose' )
 					return
 				)
-
+
 				specify( 'info', ->
 					Deft.Logger.log( 'message', 'info' )
-
+
 					expect( logFunction ).to.be.calledOnce.and.calledWith( 'message', 'info' )
 					return
 				)
-
+
 				specify( 'deprecate', ->
 					Deft.Logger.log( 'message', 'deprecate' )
-
+
 					expect( logFunction ).to.be.calledOnce.and.calledWith( 'message', 'deprecate' )
 					return
 				)
-
+
 				specify( 'warn', ->
 					Deft.Logger.log( 'message', 'warn' )
-
+
 					expect( logFunction ).to.be.calledOnce.and.calledWith( 'message', 'warn' )
 					return
 				)
-
+
 				specify( 'error', ->
 					Deft.Logger.log( 'message', 'error' )
-
+
 					expect( logFunction ).to.be.calledOnce.and.calledWith( 'message', 'error' )
 					return
 				)
-
+
 				return
 			)
-
+
 			describe( 'silently ignores messages when Ext.Logger is unavailable', ->
 				logger = null
-
+
 				beforeEach( ->
 					logger = Ext.Logger
 					Ext.Logger = null
 					return
 				)
-
+
 				afterEach( ->
 					Ext.Logger = logger
 					return
 				)
-
+
 				specify( 'no priority specified', ->
 					expect( -> Deft.Logger.log( 'message', 'info' ) ).to.not.throw( Error )
 					return
 				)
-
+
 				specify( 'verbose', ->
 					expect( -> Deft.Logger.log( 'message', 'verbose' ) ).to.not.throw( Error )
 					return
 				)
-
+
 				specify( 'deprecate', ->
 					expect( -> Deft.Logger.log( 'message', 'deprecate' ) ).to.not.throw( Error )
 					return
 				)
-
+
 				specify( 'warn', ->
 					expect( -> Deft.Logger.log( 'message', 'warn' ) ).to.not.throw( Error )
 					return
 				)
-
+
 				specify( 'error', ->
 					expect( -> Deft.Logger.log( 'message', 'error' ) ).to.not.throw( Error )
 					return
 				)
-
+
 				return
 			)
 	)
-
+
 	describe( 'verbose()', ->
 		logFunction = null
-
-		beforeEach( ->
-			logFunction = sinon.stub( Deft.Logger, 'log' )
-		)
-
-		afterEach( ->
-			logFunction.restore()
-		)
-
+
+		beforeEach( ->
+			logFunction = sinon.stub( Deft.Logger, 'log' )
+		)
+
+		afterEach( ->
+			logFunction.restore()
+		)
+
 		specify( 'calls log() with specified message with verbose priority', ->
 			Deft.Logger.verbose( 'message' )
-
+
 			expect( logFunction ).to.be.calledOnce.and.calledWith( 'message', 'verbose' )
 			return
 		)
-
-		return
-	)
-
+
+		return
+	)
+
 	describe( 'info()', ->
 		logFunction = null
-
-		beforeEach( ->
-			logFunction = sinon.stub( Deft.Logger, 'log' )
-		)
-
-		afterEach( ->
-			logFunction.restore()
-		)
-
+
+		beforeEach( ->
+			logFunction = sinon.stub( Deft.Logger, 'log' )
+		)
+
+		afterEach( ->
+			logFunction.restore()
+		)
+
 		specify( 'calls log() with specified message with info priority', ->
 			Deft.Logger.info( 'message' )
-
+
 			expect( logFunction ).to.be.calledOnce.and.calledWith( 'message', 'info' )
 			return
 		)
-
-		return
-	)
-
+
+		return
+	)
+
 	describe( 'deprecate()', ->
 		logFunction = null
-
-		beforeEach( ->
-			logFunction = sinon.stub( Deft.Logger, 'log' )
-		)
-
-		afterEach( ->
-			logFunction.restore()
-		)
-
+
+		beforeEach( ->
+			logFunction = sinon.stub( Deft.Logger, 'log' )
+		)
+
+		afterEach( ->
+			logFunction.restore()
+		)
+
 		specify( 'calls log() with specified message with deprecate priority', ->
 			Deft.Logger.deprecate( 'message' )
-
+
 			expect( logFunction ).to.be.calledOnce.and.calledWith( 'message', 'deprecate' )
 			return
 		)
-
-		return
-	)
-
+
+		return
+	)
+
 	describe( 'warn()', ->
 		logFunction = null
-
-		beforeEach( ->
-			logFunction = sinon.stub( Deft.Logger, 'log' )
-		)
-
-		afterEach( ->
-			logFunction.restore()
-		)
-
+
+		beforeEach( ->
+			logFunction = sinon.stub( Deft.Logger, 'log' )
+		)
+
+		afterEach( ->
+			logFunction.restore()
+		)
+
 		specify( 'calls log() with specified message with warn priority', ->
 			Deft.Logger.warn( 'message' )
-
+
 			expect( logFunction ).to.be.calledOnce.and.calledWith( 'message', 'warn' )
 			return
 		)
-
-		return
-	)
-
+
+		return
+	)
+
 	describe( 'error()', ->
 		logFunction = null
-
-		beforeEach( ->
-			logFunction = sinon.stub( Deft.Logger, 'log' )
-		)
-
-		afterEach( ->
-			logFunction.restore()
-		)
-
+
+		beforeEach( ->
+			logFunction = sinon.stub( Deft.Logger, 'log' )
+		)
+
+		afterEach( ->
+			logFunction.restore()
+		)
+
 		specify( 'calls log() with specified message with error priority', ->
 			Deft.Logger.error( 'message' )
-
+
 			expect( logFunction ).to.be.calledOnce.and.calledWith( 'message', 'error' )
 			return
 		)
-
-		return
-	)
-
+
+		return
+	)
+
 	return
-)+)


Add logs about GM detection

Add logs about GM detection

--- packages/rocketchat-file/file.server.coffee
+++ packages/rocketchat-file/file.server.coffee
@@ -20,19 +20,24 @@
 		RocketChat.settings.updateOptionsById 'Accounts_AvatarResize', {alert: 'The_image_resize_will_not_work_because_we_can_not_detect_ImageMagick_or_GraphicsMagick_installed_in_your_server'}

+console.log 'GM: Getting GraphicsMagick version'
 exec 'gm version', Meteor.bindEnvironment (error, stdout, stderr) ->
 	if not error? and stdout.indexOf('GraphicsMagick') > -1
+		console.log 'GM: GraphicsMagick installed'
 		RocketChatFile.enable()

 		RocketChat.Info.GraphicsMagick =
 			enabled: true
 			version: stdout
 	else
+		console.log 'GM: GraphicsMagick not installed'
 		RocketChat.Info.GraphicsMagick =
 			enabled: false

+	console.log 'GM: Getting ImageMagick version'
 	exec 'convert -version', Meteor.bindEnvironment (error, stdout, stderr) ->
 		if not error? and stdout.indexOf('ImageMagick') > -1
+			console.log 'GM: ImageMagick installed'
 			if RocketChatFile.enabled isnt true
 				# Enable GM to work with ImageMagick if no GraphicsMagick
 				RocketChatFile.gm = RocketChatFile.gm.subClass({imageMagick: true})
@@ -42,6 +47,7 @@
 				enabled: true
 				version: stdout
 		else
+			console.log 'GM: ImageMagick installed'
 			if RocketChatFile.enabled isnt true
 				RocketChatFile.disable()


some data changes

some data changes

--- cnab/test/Example.coffee
+++ cnab/test/Example.coffee
@@ -42,7 +42,7 @@
 require('expect.js')(file).to.have.length 1204

 moment = require 'moment'
-fileName = "PAG399_0006930_#{moment().format 'DDMMYY'}01.REM"
+fileName = "example_file.REM"
 path = process.env.EFS or './gen_files'

 fs = require 'fs'


codesearch.coffee can extract the relevant information from the links

--- coffee/codesearch.coffee
+++ coffee/codesearch.coffee
@@ -21,12 +21,13 @@

 app.directive "popup", ->

-    console.log "Binding popup"
-
     (scope, element) ->

         element.bind "click", (e) ->
             e.preventDefault()
+            console.log e.target.getAttribute "href"
+            console.log e.target.innerText
+            console.log e.target.dataset.ajaxUrl
         return


Remove testing .coffee file.

Remove testing .coffee file.

--- coffee/someScript.coffee
+++ coffee/someScript.coffee
@@ -1,5 +0,0 @@
-number = 42
-
-opposite = true
-
-number = -42 if opposite


fixed disqus

fixed disqus

--- docpad.coffee
+++ docpad.coffee
@@ -63,7 +63,7 @@
 			]

 		# Discus.com settings
-		disqusShortName: 'csell5'
+		disqusShortName: 'csell'

 		# -----------------------------
 		# Helper Functions


fix old urls

fix old urls


Regenerate on raw change

Regenerate on raw change

--- docpad.coffee
+++ docpad.coffee
@@ -5,6 +5,8 @@
   v for k, v of map

 #@poweredByDocPad = false
+
+@regeneratePaths = ['raw']

 @templateData =
   site:


edison.coffe: Change config.json location from resin-conf partition to resin-boot partition

Signed-off-by: Theodor Gherzan <be3f40ffe21d83aefcbc279c996fd6bca5445244@resin.io>

--- edison.coffee
+++ edison.coffee
@@ -82,7 +82,7 @@

 	configuration:
 		config:
-			image: 'config.img'
+			image: 'resin-image-edison.hddimg'
 			path: '/config.json'

 	initialization:


Fix indention and refactor of react_fact

--- core/app/backbone/views/react_fact.coffee
+++ core/app/backbone/views/react_fact.coffee
@@ -1,17 +1,11 @@
-ReactFactBody = React.createBackboneClass
+window.ReactFact = React.createBackboneClass
   render: ->
     fact_url_host = @model().factUrlHost()
-    _a ["annotation-favicon", href: @model().get('proxy_open_url'), target: '_blank'],
-    _img ["proxy-link-favicon", src:"https://www.google.com/s2/u/0/favicons?domain=#{fact_url_host}"],
+    _div ['feed-activity-annotation'],
+      _span ["annotation-site"],
+        _a ['annotation-site-avatar', href: @model().get('proxy_open_url'), target: '_blank'],
+          _img ['proxy-link-favicon', src: "https://www.google.com/s2/u/0/favicons?domain=#{fact_url_host}"],

-    _a ["annotation-body",
-        href: @model().get('proxy_open_url'),
-        target: '_blank'],
-      @model().get('displaystring')
-
-
-window.ReactFact = React.createBackboneClass
-  render: ->
-    _div ['feed-activity-annotation'],
-      ReactFactBody(model: @model())
-
+      _span ["annotation-body"],
+        _a ['annotation-body-content', href: @model().get('proxy_open_url'), target: '_blank'],
+          @model().get('displaystring')


ComputeEventListener: make sure there is only one listener in the queue with same type and same id

--- client/Main/providers/computeeventlistener.coffee
+++ client/Main/providers/computeeventlistener.coffee
@@ -33,8 +33,13 @@

   addListener:(type, eventId)->

-    @listeners.push { type, eventId }
-    @start()  unless @running
+    inList = no
+    @listeners.forEach (_)->
+      inList |= _.type is type and _.eventId is eventId
+
+    unless inList
+      @listeners.push { type, eventId }
+      @start()  unless @running

   tick:->


activity: decorate possible tokens [#79542902]

--- client/Social/Activity/views/inputview.coffee
+++ client/Social/Activity/views/inputview.coffee
@@ -119,6 +119,14 @@
     then @insertNewline()
     else @emit 'Enter', value

+  cancel: ->
+
+    if @activeRule
+      {prefix} = @activeRule
+      value = @tokenInput.textContent.substring(prefix.length).toLowerCase()
+      @addToken name: value
+
+    super

   insertNewline: ->
     document.execCommand 'insertText', no, "\n"


Client: remove references to the JTag

Client: remove references to the JTag

--- client/Social/Activity/views/inputview.coffee
+++ client/Social/Activity/views/inputview.coffee
@@ -168,10 +168,7 @@
       tokenView.emit "viewAppended"
       return tokenView.getElement().outerHTML

-  getTokenFilter: ->
-    switch @activeRule.prefix
-      when "#" then (token) -> token instanceof KD.remote.api.JTag
-      else noop
+  getTokenFilter: -> noop

   fillTokenMap = (tokens, map) ->
     tokens.forEach (token) ->


Minor cleanups

Minor cleanups

--- emails.coffee
+++ emails.coffee
@@ -6,6 +6,11 @@
 # Made by Johan Brook for [Lookback](https://github.com/lookback).

 TAG = 'mailer'
+
+CONTENT_TYPES = {
+  html: 'text/html',
+  text: 'text/plain'
+}

 # ## Setup

@@ -269,13 +274,12 @@
   # This function adds the `preview` route from a `template` object.
   # It will apply the returned data from a `data` function on the
   # provided `route` prop from the template.
-  previewAction = (opts, template, params) ->
-    check opts,
-      type: Match.OneOf('html', 'text')
-
-    return (template, params) ->
+  previewAction = (type) ->
+    check type, Match.OneOf('html', 'text')
+
+    return (req, res, params, template) ->
       try
-        data = template.route.data and template.route.data.call(this, params)
+        data = template.route.data and template.route.data.call(res, params)
       catch ex
         msg = ''
         exception = 'Exception in '+template.name+' data function: '+ex.message
@@ -291,29 +295,29 @@
           msg = exception

         Utils.Logger.error(msg, TAG)
-        this.writeHead(500)
-        return this.end(msg)
+        res.writeHead(500)
+        res.end(msg)

       # Compile, since we wanna refresh markup and CSS inlining.
       compile template

-      Utils.Logger.info "Rendering #{template.name} as #{opts.type} ...", TAG
+      Utils.Logger.info "Rendering #{template.name} as #{type} ...", TAG

       try
         html = render template.name, data
-        content = if opts.type is 'html' then html else toText(html)
+        content = if type is 'html' then html else toText(html)
         Utils.Logger.info "Rendering successful!", TAG
       catch ex
         msg = 'Could not preview email: ' + ex.message
         Utils.Logger.error msg, TAG
         content = msg

-      @writeHead 200, 'Content-Type': if opts.type is 'html' then 'text/html' else 'text/plain'
-      @end(content, 'utf8')
+      res.writeHead 200, 'Content-Type': CONTENT_TYPES[type]
+      res.end(content, 'utf8')

   # This function adds the `send` route, for easy sending emails from
   # the browser.
-  sendAction = (template, params) ->
+  sendAction = (req, res, params, template) ->
     # Who to send to? It depends: it primarly reads from the `?to`
     # query param, and secondly from the `testEmail` prop in settings.
     to = params.query.to or settings.testEmail
@@ -322,33 +326,33 @@

     if to?
       try
-        data = template.route.data and template.route.data.call(this, params)
+        data = template.route.data and template.route.data.call(res, params)
       catch ex
         Utils.Logger.error 'Exception in '+template.name+' data function: '+ex.message, TAG
         return

-      res = sendEmail(
+      result = sendEmail(
         to: to
         data: data
         template: template.name
         subject: '[TEST] ' + template.name
       )

-      if res is false
-        @response.writeHead 500
+      if result is false
+        res.writeHead 500
         msg = 'Did not send test email, something went wrong. Check the logs.'
       else
-        @writeHead 200
+        res.writeHead 200
         # If there's no `MAIL_URL` environment variable, Meteor cannot send
         # the email and echoes it out to `STDOUT` instead.
         reallySentEmail = !!process.env.MAIL_URL
         msg = if reallySentEmail then "Sent test email to #{to}" else "Sent email to STDOUT"

-      @end(msg)
+      res.end(msg)

     else
-      @writeHead 400
-      @end("No testEmail provided.")
+      res.writeHead 400
+      res.end('No testEmail provided.')

   # Adds all the routes from a template.
   addRoutes = (template) ->
@@ -356,8 +360,8 @@
     check template.route.path, String

     types =
-      preview: previewAction(type: 'html')
-      text: previewAction(type: 'text')
+      preview: previewAction('html')
+      text: previewAction('text')
       send: sendAction

     _.each types, (action, type) ->
@@ -378,7 +382,7 @@

       # we use Picker for server side routes
       Picker.route(path, (params, req, res) ->
-        action.call res, template, params
+        action(req, res, params, template)
       )


Rename /global to /site-feed

Rename /global to /site-feed

--- lib/assets/javascripts/routers/posts.js.coffee
+++ lib/assets/javascripts/routers/posts.js.coffee
@@ -2,7 +2,7 @@
   routes: {
     ""                   : "root"
     "posts"              : "index"
-    "global"             : "siteFeed"
+    "site-feed"          : "siteFeed"
     "replies"            : "replies"
     "reposts"            : "reposts"
     "posts/:id"          : "post"


UserEnvironmentDataProvider: Fix machine instance.

--- client/app/lib/userenvironmentdataprovider.coffee
+++ client/app/lib/userenvironmentdataprovider.coffee
@@ -82,7 +82,9 @@
       return  callback new Machine { machine }  if machine

       @fetchMachineByUId labelOrUId, (machine) =>
-        callback new Machine { machine }
+        machine = if machine then new Machine { machine } else null
+
+        callback machine

   fetchWorkspaceByMachineUId: (options, callback) ->


sidebar animation fixed

sidebar animation fixed

--- client/app/MainApp/sidebar/sidebarview.coffee
+++ client/app/MainApp/sidebar/sidebarview.coffee
@@ -13,9 +13,6 @@
     @avatar = new AvatarView
       tagName    : "div"
       cssClass   : "avatar-image-wrapper"
-      bind       : "mouseenter mouseleave"
-      mouseenter : => @animateLeftNavIn()
-      mouseleave : => @animateLeftNavOut()
       size       :
         width    : 160
         height   : 76
@@ -23,9 +20,6 @@

     @avatarAreaIconMenu = new AvatarAreaIconMenu
       delegate     : @
-      bind         : "mouseenter mouseleave"
-      mouseenter   : => @animateLeftNavIn()
-      mouseleave   : => @animateLeftNavOut()

     # currentGroupData = @getSingleton('groupsController').getCurrentGroupData()

@@ -38,9 +32,6 @@
       view           : new NavigationList
         type         : "navigation"
         itemClass    : NavigationLink
-        bind         : "mouseenter mouseleave"
-        mouseenter   : => @animateLeftNavIn()
-        mouseleave   : => @animateLeftNavOut()
       wrapper        : no
       scrollView     : no
     , navItems
@@ -52,9 +43,6 @@
         type         : "navigation"
         cssClass     : "account"
         itemClass    : NavigationLink
-        bind         : "mouseenter mouseleave"
-        mouseenter   : => @animateLeftNavIn()
-        mouseleave   : => @animateLeftNavOut()
       wrapper        : no
       scrollView     : no
     , accNavItems
@@ -66,9 +54,6 @@
         type         : "navigation"
         cssClass     : "account admin"
         itemClass    : AdminNavigationLink
-        bind         : "mouseenter mouseleave"
-        mouseenter   : => @animateLeftNavIn()
-        mouseleave   : => @animateLeftNavOut()
       wrapper        : no
       scrollView     : no

@@ -78,9 +63,6 @@
       view           : new NavigationList
         type         : "footer-menu"
         itemClass    : FooterMenuItem
-        bind         : "mouseenter mouseleave"
-        mouseenter   : => @animateLeftNavIn()
-        mouseleave   : => @animateLeftNavOut()
       wrapper        : no
       scrollView     : no
     , footerMenuItems
@@ -174,6 +156,11 @@
       return if newFpWidth < 13
       cp.$().css left : cp._left - x, width : cp._width + x
       $fp.css "width", newFpWidth
+
+    # exception - Sinan, Jan 2013
+    # we bind this with jquery directly bc #main-nav is no KDView but just HTML
+    @$('#main-nav').on "mouseenter", @animateLeftNavIn.bind @
+    @$('#main-nav').on "mouseleave", @animateLeftNavOut.bind @

   viewAppended:->


increase page size for activity card

increase page size for activity card

--- lineman/app/components/thread_page/activity_card/activity_card.coffee
+++ lineman/app/components/thread_page/activity_card/activity_card.coffee
@@ -8,7 +8,7 @@
     $scope.firstLoadedSequenceId = 0
     $scope.loadingForward = false
     $scope.loadingBackward = false
-    $scope.pageSize = 25
+    $scope.pageSize = 50

     $scope.init = ->
       $scope.discussion.markAsRead(0)


Changed the Port

Changed the Port

--- gulp/development/webserver.coffee
+++ gulp/development/webserver.coffee
@@ -11,7 +11,7 @@

     app.use( express.static(__projectdir) )

-    http.listen 61337, ->
+    http.listen 63647, ->
       console.log "yea listengin!"


WIP: register from a guest account

WIP: register from a guest account

--- client/landing/app/MainApp/login/loginview.coffee
+++ client/landing/app/MainApp/login/loginview.coffee
@@ -173,33 +173,30 @@
     @registerForm.notificationsDisabled = yes
     @registerForm.notification?.destroy()

-    console.trace()
-
-    KD.remote.api.JUser.convert formData, (err, account, replacementToken)=>
-      console.log {arguments}
-      # @registerForm.button.hideLoader()
-      # if err
-      #   {message} = err
-      #   warn "An error occured while registering:", err
-      #   @registerForm.notificationsDisabled = no
-      #   @registerForm.emit "SubmitFailed", message
-      # else
-      #   $.cookie 'clientId', replacementToken
-      #   KD.getSingleton('mainController').accountChanged account
-      #   new KDNotificationView
-      #     cssClass  : "login"
-      #     title     : if kodingenUser then 'Nice to see an old friend here!' else 'Good to go, Enjoy!'
-      #     # content   : 'Successfully registered!'
-      #     duration  : 2000
-      #   KD.getSingleton('router').clear()
-      #   setTimeout =>
-      #     @hide()
-      #     @registerForm.reset()
-      #     @registerForm.button.hideLoader()
-      #     # setTimeout =>
-      #     #   KD.getSingleton('mainController').emit "ShowInstructionsBook"
-      #     # , 1000
-      #   , 1000
+    KD.remote.api.JUser.convert formData, (err, replacementToken)=>
+      account = KD.whoami()
+      @registerForm.button.hideLoader()
+      if err
+        {message} = err
+        warn "An error occured while registering:", err
+        @registerForm.notificationsDisabled = no
+        @registerForm.emit "SubmitFailed", message
+      else
+
+
+        $.cookie 'clientId', replacementToken
+        KD.getSingleton('mainController').accountChanged account
+        new KDNotificationView
+          cssClass  : "login"
+          title     : if kodingenUser then 'Nice to see an old friend here!' else 'Good to go, Enjoy!'
+          # content   : 'Successfully registered!'
+          duration  : 2000
+        KD.getSingleton('router').clear()
+        setTimeout =>
+          @hide()
+          @registerForm.reset()
+          @registerForm.button.hideLoader()
+        , 1000

   doLogin:(credentials)->
     credentials.username = credentials.username.toLowerCase()


Previous error is hidden after new ROM is loaded.

--- client/controllers/emulator-controller.coffee
+++ client/controllers/emulator-controller.coffee
@@ -36,6 +36,7 @@
     $scope.$on "cartridgeLoadSuccess", (gameId) ->
         globalParams.gameId = gameId
         $scope.loading = false
+        $scope.error = null
         emulator.start()

     $scope.$on "cartridgeLoadError", (event, error) ->


Correctly set parent folder metadata

Correctly set parent folder metadata

If a user named their doc using the title input field, then clicked
Save and selected a folder, the folder selection would not be used.

If the user types in a filename in the Save dialog, the `confirm`
function correctly sets the appropriate folder, but this does not happen
unless the filename has been changed.

The `getStateForFolder` function sets the folder in the state, but this
is never passed into the metadata. This commit fixes that.

[#120377399]

--- src/code/views/file-dialog-tab-view.coffee
+++ src/code/views/file-dialog-tab-view.coffee
@@ -93,6 +93,8 @@

   getStateForFolder: (folder) ->
     metadata = if @isOpen() then @state?.metadata or null else @props.client.state.metadata
+    metadata.parent = folder
+
     folder: folder
     metadata: metadata
     list: []


Adjusted the way datatypes are extracted from patient records to account for slashes in the names (#33)

--- app/assets/javascripts/cqlpatient.js.coffee
+++ app/assets/javascripts/cqlpatient.js.coffee
@@ -184,7 +184,7 @@
           # e.g. "Encounter, Performed: Face-to-Face Interaction" becomes
           # EncounterPerformed
           classname = dc.description.substr(0, dc.description.indexOf(':'))
-          classname = classname.replace(/,/g, '')
+          classname = classname.replace(/,/g, '').replace(/\//g, '')
           classname = classname.replace(/ /g, '')
           unless data_types[classname]?
             data_types[classname] = []


Add pattern-matching for structured-control keywords

This is a non-standard APL feature supported by APLX and Dyalog. Despite
being similar in appearance to labels, the two constructs are unrelated.

Sources:
  * http://microapl.com/apl/apl_concepts_chapter6.html
  * http://docs.dyalog.com/14.1/Dyalog%20APL%20Language%20Reference.pdf

--- grammars/apl.cson
+++ grammars/apl.cson
@@ -79,6 +79,7 @@
 			{ include: "#definition" }
 			{ include: "#comment"    }
 			{ include: "#label"      }
+			{ include: "#sck"        }
 			{ include: "#strings"    }
 			{ include: "#float"      }
 			{ include: "#int"        }
@@ -358,6 +359,17 @@
 		}]

+	# Structured-control keywords (APLX/Dyalog feature)
+	sck:
+		patterns: [{
+			name: "keyword.control.sck.apl"
+			match: "(?<=\\s|^)(:)([A-Za-z]+)"
+			captures:
+				1: {name: "punctuation.definition.sck.begin.apl"}
+				2: {name: "entity.sck.name.apl"}
+		}]
+
+
 	# Heredocs (GNU APL)
 	heredocs:
 		patterns: [{


修复rss页面不能添加feed的bug

修复rss页面不能添加feed的bug

--- app/assets/javascripts/dispatcher.js.coffee
+++ app/assets/javascripts/dispatcher.js.coffee
@@ -20,7 +20,7 @@
       when 'dashboard:show', 'dashboard:archive'
         new Dashboard()
         new Activities()
-      when 'rssfeeds:add_rss_feed'
+      when 'rssfeeds:add_rss_feed', 'rssfeeds:rss'
         new Feeds()


Collapse some redundancy and prep the next spec.

--- spec/conflict-spec.coffee
+++ spec/conflict-spec.coffee
@@ -14,11 +14,8 @@
     editor = editorView.getEditor()

   it "parses itself from a two-way diff marking", ->
-    loadPath('single-2way-diff.txt')
-
-    cs = Conflict.all editor
-    c = cs[0]
-    console.log c.ours.marker
+    loadPath('single-2way-diff.txt')s
+    c = Conflict.all(editor)[0]

     expect(c.ours.marker.getTailBufferPosition().toArray()).toEqual([1, 0])
     expect(c.ours.marker.getHeadBufferPosition().toArray()).toEqual([2, 0])
@@ -27,31 +24,9 @@
     expect(c.theirs.marker.getHeadBufferPosition().toArray()).toEqual([4, 0])

   it "finds conflict markings from a file", ->
-    content = """
-              This is some text before the marking.
+    loadPath('multi-2way-diff.txt')
+    cs = Conflict.all(lines)

-              More text.
-
-              <<<<<<< HEAD
-              My changes
-              Multi-line even
-              =======
-              Your changes
-              >>>>>>> other-branch
-
-              Text in between.
-
-              <<<<<<< HEAD
-              More of my changes
-              =======
-              More of your changes
-              >>>>>>> other-branch
-
-              Stuff at the end.
-              """
-    lines = asLines content
-
-    cs = Conflict.all(lines)
     expect(cs.length).toBe(2)
     expect(cs[0].ours.lines.eq(1).text()).toBe("Multi-line even")
     expect(cs[1].theirs.lines.eq(0).text()).toBe("More of your changes")


Another "on" subscription in the specs.

Another "on" subscription in the specs.

--- spec/conflict-spec.coffee
+++ spec/conflict-spec.coffee
@@ -98,7 +98,7 @@

     it 'broadcasts an event on resolution', ->
       resolved = false
-      conflict.on "conflict:resolved", -> resolved = true
+      conflict.onDidResolveConflict -> resolved = true
       conflict.ours.resolve()
       expect(resolved).toBe(true)


Fix memory leak in emitFile.

Fix memory leak in emitFile.

--- compiler/emitFile.coffee
+++ compiler/emitFile.coffee
@@ -155,16 +155,12 @@
                 output_type = mime.lookup(output_path)

             unless defer_emits or not output_content
-                _doWrite()
+                SDKError.log("Saving #{ colors.green(output_path) } #{ colors.grey('(' + output_content.length + ' bytes, ' + output_type + ')') }")
+                writeFile
+                    path        : output_path
+                    content     : output_content
+                    type        : output_type
             return [output_type, output_content]
-
-        _doWrite = ->
-            SDKError.log("Saving #{ colors.green(output_path) } #{ colors.grey('(' + output_content.length + ' bytes, ' + output_type + ')') }")
-
-            writeFile
-                path        : output_path
-                content     : output_content
-                type        : output_type

         unless defer_emits
             _doProcess()
@@ -176,9 +172,12 @@
             else
                 files_emitted_indexed[output_path] = {
                     path: output_path,
-                    render: _doProcess,
+                    # Avoid hanging on to the _doProcess function to prevent
+                    # a memory leak.
+                    render: if defer_emits then _doProcess else null,
                     type: output_type,
                 }
+            output_content = null

             exportMetadata(file_path, options.metadata) if options.metadata


don't alter banner since framerate works now :P

--- app/assets/jax/controllers/geodes.js.coffee
+++ app/assets/jax/controllers/geodes.js.coffee
@@ -125,6 +125,3 @@

     # sun's orbit
     @sun.camera.position = [ Math.cos(@theta)*20, Math.sin(@theta)*20, Math.sin(@theta)*10 ]
-
-    fps = @stats.fps
-    $('#jax-banner').html("FPS : #{fps}")


KDTokenizedInputView: sanitize input value prior to token matching

--- components/inputs/KDTokenizedInputView.coffee
+++ components/inputs/KDTokenizedInputView.coffee
@@ -83,10 +83,13 @@

   matchToken: ->
     return  @cancel() unless @tokenInput.parentNode
+    @sanitizeInput()
     token = @tokenInput.textContent.substring @activeRule.prefix.length
     if token
       {dataSource} = @activeRule
       dataSource token, @bound "showMenu"
+
+  sanitizeInput: ->

   showMenu:(options, data)->
     @menu?.destroy()


Refactor router

Refactor router

--- components/Router.coffee
+++ components/Router.coffee
@@ -14,41 +14,61 @@
       missed: new noflo.Port 'string'

     @inPorts.url.on 'data', (url) =>
+      matched = @matchUrl url
       if @outPorts.route.isAttached()
-        @outPorts.route.send true
+        @outPorts.route.send matched
         @outPorts.route.disconnect()
+      unless matched
+        if @outPorts.missed.isAttached()
+          @outPorts.missed.send url
+          @outPorts.missed.disconnect()
+          return

-      if url is ''
-        @outPorts.main.send url
-        @outPorts.main.disconnect()
-        return
+      route = matched.shift()
+      switch route
+        when 'main'
+          return unless @outPorts.main.isAttached()
+          @outPorts.main.send true
+          @outPorts.main.disconnect()
+          return
+        when 'sketch'
+          return unless @outPorts.graph.isAttached()
+          @outPorts.graph.send matched[0]
+          @outPorts.graph.disconnect()
+          return
+        when 'graph', 'component'
+          project = matched.shift()
+          if @outPorts.project.isAttached()
+            @outPorts.project.send project
+            @outPorts.project.disconnect()
+          if route is 'graph' and @outPorts.graph.isAttached()
+            for graph in matched
+              @outPorts.graph.send graph
+            @outPorts.graph.disconnect()
+          if route is 'component' and @outPorts.component.isAttached()
+            @outPorts.component.send matched[0]
+          return
+        when 'example'
+          return unless @outPorts.example.isAttached()
+          @outPorts.example.send matched[0]
+          @outPorts.example.disconnect()

-      if url.substr(0, 8) is 'project/'
-        remainder = url.substr 8
-        parts = remainder.split '/'
-        @outPorts.project.send parts.shift()
-        @outPorts.project.disconnect()
-        if parts[0] is 'component' and parts.length is 2
-          @outPorts.component.send parts[1]
-          @outPorts.component.disconnect()
-          return
-        for part in parts
-          @outPorts.graph.send part
-        @outPorts.graph.disconnect()
-        return
-
-      if url.substr(0, 6) is 'graph/'
-        @outPorts.graph.send url.substr 6
-        @outPorts.graph.disconnect()
-        return
-
-      if url.substr(0, 8) is 'example/'
-        @outPorts.example.send url.substr 8
-        @outPorts.example.disconnect()
-        return
-
-      if @outPorts.missed.isAttached()
-        @outPorts.missed.send url
-        @outPorts.missed.disconnect()
+  matchUrl: (url) ->
+    if url is ''
+      return ['main']
+    if url.substr(0, 8) is 'project/'
+      remainder = url.substr 8
+      parts = remainder.split '/'
+      project = parts.shift()
+      if parts[0] is 'component' and parts.length is 2
+        return ['component', project, parts[1]]
+      graph = ['graph', project]
+      graph.push part for part in parts
+      return graph
+    if url.substr(0, 6) is 'graph/'
+      return ['sketch', url.substr(6)]
+    if url.substr(0, 8) is 'example/'
+      return ['example', url.substr(8)]
+    return null

 exports.getComponent = -> new Router


add test for ctrl-c entering command mode

--- spec/vim-mode-spec.coffee
+++ spec/vim-mode-spec.coffee
@@ -852,3 +852,11 @@

       expect(editor).toHaveClass 'command-mode'
       expect(editor).not.toHaveClass 'insert-mode'
+
+    it "puts the editor into command mode when <ctrl-c> is pressed", ->
+      expect(editor).not.toHaveClass 'command-mode'
+
+      editor.trigger keydownEvent('ctrl-c')
+
+      expect(editor).toHaveClass 'command-mode'
+      expect(editor).not.toHaveClass 'insert-mode'


added biweekly events

added biweekly events

--- app/coffee/drm-calendar/drm-calendar.coffee
+++ app/coffee/drm-calendar/drm-calendar.coffee
@@ -63,6 +63,7 @@
                     dayNum: null
                     type: "holiday"
                     recurrance: "yearly"
+                    endRecurrance: "never"
                     notes: null
                 mlkBirthday:
                     name: "Martin Luther King's Birthday"
@@ -73,6 +74,7 @@
                     dayNum: 3
                     type: "holiday"
                     recurrance: "yearly"
+                    endRecurrance: "never"
                     notes: null
                 groundhogDay:
                     name: "Groundhog Day"
@@ -83,6 +85,7 @@
                     dayNum: null
                     type: "holiday"
                     recurrance: "yearly"
+                    endRecurrance: "never"
                     notes: null
                 valentinesDay:
                     name: "Valentine's Day"
@@ -93,6 +96,7 @@
                     dayNum: null
                     type: "holiday"
                     recurrance: "yearly"
+                    endRecurrance: "never"
                     notes: null
                 presidentsDay:
                     name: "President's Day"
@@ -103,6 +107,7 @@
                     dayNum: 3
                     type: "holiday"
                     recurrance: "yearly"
+                    endRecurrance: "never"
                     notes: null
                 stPatricksDay:
                     name: "St. Patrick's Day"
@@ -113,6 +118,7 @@
                     dayNum: null
                     type: "holiday"
                     recurrance: "yearly"
+                    endRecurrance: "never"
                     notes: null
                 aprilFools:
                     name: "April Fool's Day"
@@ -123,6 +129,7 @@
                     dayNum: null
                     type: "holiday"
                     recurrance: "yearly"
+                    endRecurrance: "never"
                     notes: null
                 earthDay:
                     name: "Earth Day"
@@ -133,6 +140,7 @@
                     dayNum: null
                     type: "holiday"
                     recurrance: "yearly"
+                    endRecurrance: "never"
                     notes: null
                 arborDay:
                     name: "Arbor Day"
@@ -143,6 +151,7 @@
                     dayNum: "last"
                     type: "holiday"
                     recurrance: "yearly"
+                    endRecurrance: "never"
                     notes: null
                 mayDay:
                     name: "May Day"
@@ -153,6 +162,7 @@
                     dayNum: null
                     type: "holiday"
                     recurrance: "yearly"
+                    endRecurrance: "never"
                     notes: null
                 cincoDeMayo:
                     name: "Cinco De Mayo"
@@ -163,6 +173,7 @@
                     dayNum: null
                     type: "holiday"
                     recurrance: "yearly"
+                    endRecurrance: "never"
                     notes: null
                 mothersDay:
                     name: "Mother's Day"
@@ -173,6 +184,7 @@
                     dayNum: 2
                     type: "holiday"
                     recurrance: "yearly"
+                    endRecurrance: "never"
                     notes: null
                 memorialDay:
                     name: "Memorial Day"
@@ -183,6 +195,7 @@
                     dayNum: "last"
                     type: "holiday"
                     recurrance: "yearly"
+                    endRecurrance: "never"
                     notes: null
                 flagDay:
                     name: "Flag Day"
@@ -193,6 +206,7 @@
                     dayNum: null
                     type: "holiday"
                     recurrance: "yearly"
+                    endRecurrance: "never"
                     notes: null
                 fathersDay:
                     name: "Father's Day"
@@ -202,6 +216,7 @@
                     day: "Sunday"
                     type: "holiday"
                     recurrance: "yearly"
+                    endRecurrance: "never"
                     notes: null
                     dayNum: 3
                 independenceDay:
@@ -213,6 +228,7 @@
                     dayNum: null
                     type: "holiday"
                     recurrance: "yearly"
+                    endRecurrance: "never"
                     notes: null
                 laborDay:
                     name: "Labor Day"
@@ -223,6 +239,7 @@
                     dayNum: 1
                     type: "holiday"
                     recurrance: "yearly"
+                    endRecurrance: "never"
                     notes: null
                 patroitDay:
                     name: "Patroit Day"
@@ -233,6 +250,7 @@
                     dayNum: null
                     type: "holiday"
                     recurrance: "yearly"
+                    endRecurrance: "never"
                     notes: null
                 columbusDay:
                     name: "Columbus Day"
@@ -243,6 +261,7 @@
                     dayNum: 2
                     type: "holiday"
                     recurrance: "yearly"
+                    endRecurrance: "never"
                     notes: null
                 halloween:
                     name: "Halloween"
@@ -253,6 +272,7 @@
                     dayNum: null
                     type: "holiday"
                     recurrance: "yearly"
+                    endRecurrance: "never"
                     notes: null
                 veteransDay:
                     name: "Veteran's Day"
@@ -263,6 +283,7 @@
                     dayNum: null
                     type: "holiday"
                     recurrance: "yearly"
+                    endRecurrance: "never"
                     notes: null
                 thanksgiving:
                     name: "Thanksgiving"
@@ -273,6 +294,7 @@
                     dayNum: 4
                     type: "holiday"
                     recurrance: "yearly"
+                    endRecurrance: "never"
                     notes: null
                 pearlHarborDay:
                     name: "Pearl Harbor Day"
@@ -283,6 +305,7 @@
                     dayNum: null
                     type: "holiday"
                     recurrance: "yearly"
+                    endRecurrance: "never"
                     notes: null
                 festivus:
                     name: "Festivus"
@@ -293,6 +316,7 @@
                     dayNum: null
                     type: "holiday"
                     recurrance: "yearly"
+                    endRecurrance: "never"
                     notes: null
                 christmasEve:
                     name: "Christmas Eve"
@@ -303,6 +327,7 @@
                     dayNum: null
                     type: "holiday"
                     recurrance: "yearly"
+                    endRecurrance: "never"
                     notes: null
                 christmas:
                     name: "Christmas"
@@ -313,6 +338,7 @@
                     dayNum: null
                     type: "holiday"
                     recurrance: "yearly"
+                    endRecurrance: "never"
                     notes: null
                 boxingDay:
                     name: "Boxing Day"
@@ -323,6 +349,7 @@
                     dayNum: null
                     type: "holiday"
                     recurrance: "yearly"
+                    endRecurrance: "never"
                     notes: null
                 newYearsEve:
                     name: "New Year's Eve"
@@ -333,6 +360,7 @@
                     dayNum: null
                     type: "holiday"
                     recurrance: "yearly"
+                    endRecurrance: "never"
                     notes: null
                 mybirthday:
                     name: "My Birthday"
@@ -343,6 +371,7 @@
                     dayNum: null
                     type: "birthday"
                     recurrance: "yearly"
+                    endRecurrance: "never"
                     notes: "Here are some notes"
                 rabbitRabbitDay:
                     name: "Rabbit Rabbit Day"
@@ -353,6 +382,7 @@
                     dayNum: null
                     type: "fun day"
                     recurrance: "monthly"
+                    endRecurrance: "never"
                     notes: "Say Rabbit Rabbit for good luck this month"
                 firstMonday:
                     name: "First Monday"
@@ -363,17 +393,30 @@
                     dayNum : 1
                     type: "test"
                     recurrance: "monthly"
+                    endRecurrance: "never"
                     notes: "This is the first Monday of the month"
-                # lawnDay:
-                #     name: "Lawn Day"
-                #     month: "April"
-                #     year: null
-                #     eventDate: 17
-                #     day: "Thursday"
-                #     dayNum: null
-                #     type: "test"
-                #     recurrance: "biweekly"
-                #     notes: "Every other Thursday"
+                lawnDay:
+                    name: "Lawn Day"
+                    month: "April"
+                    year: null
+                    eventDate: 24
+                    day: "Thursday"
+                    dayNum: null
+                    type: "test"
+                    recurrance: "biweekly"
+                    endRecurrance: "never"
+                    notes: "Every other Thursday"
+                notLawnDay:
+                    name: "Not Lawn Day"
+                    month: "April"
+                    year: null
+                    eventDate: 17
+                    day: "Thursday"
+                    dayNum: null
+                    type: "test"
+                    recurrance: "biweekly"
+                    endRecurrance: "never"
+                    notes: "Every other Thursday"
                 wednesdays:
                     name: "Wednesdays"
                     month: null
@@ -383,6 +426,7 @@
                     dayNum: null
                     type: "test"
                     recurrance: "weekly"
+                    endRecurrance: "never"
                     notes: "Every single Wednesday"
                 wakeup:
                     name: "Wake Up"
@@ -393,6 +437,7 @@
                     dayNum: null
                     type: "test"
                     recurrance: "daily"
+                    endRecurrance: "never"
                     note: "Wake Up Every Day"
                 oneTimeEvent:
                     name: "One Time Event"
@@ -403,6 +448,7 @@
                     dayNum: null
                     type: "test"
                     recurrance: "none"
+                    endRecurrance: null
                     note: "do this once"

             self.createCalendar self.currentMonth, self.currentYear
@@ -538,8 +584,8 @@
             currentYear = calendarInner.data 'year'
             weeks = calendarInner.find('tbody').find 'tr'

-            getWeekNum = (dayNum, day, numberDays, dayShift) ->
-                numberWeeks = self.getWeeksInMonth(numberDays, dayShift)
+            getEventWeekNum = (dayNum, day, numberDays, dayShift) ->
+                numberWeeks = self.getWeeksInMonth numberDays, dayShift
                 lastWeekLength = weeks.eq(numberWeeks).length

                 if dayNum is 'last' and dayShift <= day
@@ -551,38 +597,54 @@

             $.each self.events, (key, value) ->
                 eventDays = []
-                # add yearly events
-                if value.recurrance is 'yearly'
-                    month = $.inArray value.month, self.months
+                month = $.inArray value.month, self.months
+                if value.recurrance is 'yearly' and currentMonth is month
+                    # add yearly events

                     if value.day
                         day = $.inArray value.day, self.days
-                        weekNum = getWeekNum value.dayNum, day, numberDays, dayShift
+                        eventWeekNum = getEventWeekNum value.dayNum, day, numberDays, dayShift

                         if currentMonth is month
-                            eventWeek = if dayShift <= day then eventWeek = weeks.eq weekNum else eventWeek = weeks.eq weekNum + 1
+                            eventWeek = if dayShift <= day then eventWeek = weeks.eq eventWeekNum else eventWeek = weeks.eq eventWeekNum + 1
                             eventDate = eventWeek.find('td').eq(day).data 'date'
                     else
                         eventDate = value.eventDate
-
-                    if currentMonth is month
-                        eventDays.push calendarInner.find "[data-date=#{eventDate}]"
-                # add monthly events
+
+                    eventDays.push calendarInner.find "[data-date=#{eventDate}]"
+
                 else if value.recurrance is 'monthly'
-
+                    # add monthly events
                     if value.day
                         day = $.inArray value.day, self.days
-                        weekNum = getWeekNum value.dayNum, day, numberDays, dayShift
-                        eventWeek = if dayShift <= day then eventWeek = weeks.eq weekNum else eventWeek = weeks.eq weekNum + 1
+                        eventWeekNum = getEventWeekNum value.dayNum, day, numberDays, dayShift
+                        eventWeek = if dayShift <= day then eventWeek = weeks.eq eventWeekNum else eventWeek = weeks.eq eventWeekNum + 1
                         eventDate = eventWeek.find('td').eq(day).data 'date'
                     else
                         eventDate = value.eventDate

                     eventDays.push calendarInner.find "[data-date=#{eventDate}]"
-                # else if value.recurrance is 'biweekly'
-                #     # events that occur every 2 weeks
+
+                else if value.recurrance is 'biweekly'
+                    # events that occur every 2 weeks
+                    if value.day
+                        day = $.inArray value.day, self.days
+                        eventDate = []
+                        length = weeks.length
+                        startWeekNum = weeks.has("td[data-date=#{value.eventDate}]").data 'week'
+                        weekPattern = if startWeekNum % 2 is 0 then 'even' else 'odd'
+                        eventWeeks = calendarInner.find('tbody').find "tr.#{weekPattern}-week"
+
+                        $.each eventWeeks, (key, value) ->
+                            that = $ value
+                            weekLength = that.find("td:not(.#{self.classes.muted})").length
+                            eventDate.push that.find('td').eq(day).data('date')
+
+                    $.each eventDate, (key, value) ->
+                        eventDays.push calendarInner.find "[data-date=#{value}]"
+
                 else if value.recurrance is 'weekly'
-                    # weekly events
+                    # weekly events
                     if value.day
                         day = $.inArray value.day, self.days
                         eventDate = []
@@ -605,13 +667,11 @@
                     days.each ->
                         eventDays.push $(@)

-                else if value.recurrance is 'none'
-                    month = $.inArray value.month, self.months
-
-                    if currentMonth is month and currentYear is value.year
-                        eventDate = value.eventDate
-
-                        eventDays.push calendarInner.find "[data-date=#{eventDate}]"
+                else if value.recurrance is 'none' and currentMonth is month and currentYear is value.year
+
+                    eventDate = value.eventDate
+
+                    eventDays.push calendarInner.find "[data-date=#{eventDate}]"

                 if eventDays.length > 0
                     $.each eventDays, (key, day) ->
@@ -621,12 +681,44 @@

         highlightWeekends: =>
             self = @
-            weeks = self.calendar.find("div.#{@calendarInnerClass}").find 'tr'
+            weeks = self.calendar.find("div.#{@calendarInnerClass} tbody").find 'tr'

             $.each weeks, ->
                 dates = $(@).find "td"
                 $.each self.weekend, (key, value) ->
                     dates.eq(value).not(".#{self.classes.muted}, .#{self.classes.today}, .#{self.classes.holiday}").addClass self.classes.weekend
+
+        addWeekNumbers: =>
+            self = @
+            calendarInner = self.calendar.find "div.#{@calendarInnerClass}"
+            month = calendarInner.data 'month'
+            year = calendarInner.data 'year'
+            weekNum = 1
+            weeks = self.calendar.find("div.#{@calendarInnerClass} tbody").find 'tr'
+            weekNums = []
+
+            $.each self.months, (key, value) ->
+                numberDays = self.getDaysInMonth (key + 1), year
+                firstDay = self.getDayOfWeek key, year, 1
+                dayShift = if firstDay is self.daysPerWeek then 0 else firstDay
+                numberWeeks = self.getWeeksInMonth numberDays, dayShift
+                week = 1
+
+                until week > numberWeeks
+                    if week is 1 and firstDay isnt 0
+                        weekNum = weekNum
+                    else
+                        weekNum = weekNum + 1
+
+                    if month is key
+                        weekNums.push weekNum
+                    week = week + 1
+                weekNums
+
+            $.each weekNums, (key, value) ->
+                week = weeks.eq key
+                if value % 2 is 0 then week.addClass('even-week') else week.addClass('odd-week')
+                week.attr 'data-week', value

         advanceMonth: (direction) =>
             calendarInner = @calendar.find "div.#{@calendarInnerClass}"
@@ -735,6 +827,7 @@

             self.highlightCurrentDay()
             self.highlightWeekends()
+            self.addWeekNumbers()
             self.createEvent "Dad's Birthday", "April", null, 9, null, null, "birthday", "some notes"
             self.createEvent "Foby's Birthday", "November", null, 23, null, null, "birthday", "some notes"
             self.addEventsToCalendar numberDays, dayShift


use localized dates in handlebars dateString helper

test plan:
 - start and conclude a conference in a course
 - the conference's conclude date should appear in a locale-
   appropriate format

fixes CNVS-34754

Change-Id: I25104705409f123b629ac194aafe29799de6b186
Reviewed-on: https://gerrit.instructure.com/101140
Reviewed-by: Felix Milea-Ciobanu <9384aa226ac9d3237ed0bc52e18ffcb251ee0bf7@instructure.com>
QA-Review: David Tan <6283e310a2278fc51bdc4fb5974f70dd9c3366b9@instructure.com>
Tested-by: Jenkins
Product-Review: Jeremy Stanley <b3f594e10a9edcf5413cf1190121d45078c62290@instructure.com>

--- app/coffeescripts/handlebars_helpers.coffee
+++ app/coffeescripts/handlebars_helpers.coffee
@@ -116,7 +116,7 @@
     # timezone preference. expects: anything tz() can handle
     dateString : (datetime) ->
       return '' unless datetime
-      tz.format(datetime, '%m/%d/%Y')
+      I18n.l "date.formats.medium", datetime

     # Convert the total amount of minutes into a Hours:Minutes format.
     minutesToHM : (minutes) ->


removed dasharray style

removed dasharray style

--- app/assets/javascripts/d3/topology.coffee
+++ app/assets/javascripts/d3/topology.coffee
@@ -35,14 +35,6 @@
       {x: right_x - distance / 4, y: right_y}
       {x: right_x, y: right_y}
     ]
-
-  # returns the parameter for dasharray property
-  style: =>
-    switch @type
-      when 'flexible' then '1, 1'
-      when 'share' then '3, 1'
-      when 'constant' then ''
-      when 'dependent' then '2, 1, 2'

 class Topology
   constructor: ->
@@ -130,7 +122,6 @@
       .append('path')
       .attr('class', 'link')
       .style('stroke', (d) -> d.color)
-      .style('stroke-dasharray', (d) -> d.style())
       .attr('d', (link) => @make_line link.path_points())

     @rendered = true


better handling of failed preview request (e.g. 404) in js plugin

--- app/assets/javascripts/url-preview.coffee
+++ app/assets/javascripts/url-preview.coffee
@@ -1,17 +1,19 @@
 class @UrlPreview
   URL_PATTERN = /((https?:\/\/)|([-a-zA-Z0-9@:%._\+~#=]+\.))?[-a-zA-Z0-9@%._\+~#=]{2,256}\.[a-z]{2,10}\b([-a-zA-Z0-9@:%_\+.~#?&//=]*)/g
-
+
   constructor: (options) ->
     @reset()
     @options = options
     @serviceUrl = '/url-preview'

   process: (str) ->
+    # If the string is not an URL, return as if it wasn't found
     if !(url = @parseStr(str))
       @lastRequest.url = null
       @previewNotFound()
       return
-    else if @lastRequest.url == url
+    # Avoid fetching back the same URL
+    else if @lastRequest.url is url
       return

     @options.beforeSend?()
@@ -25,12 +27,13 @@
     urls = str.match(URL_PATTERN)
     if urls
       "http://".concat(urls[0]) unless urls[0].match(/https?:\/\//)
-      return urls[0]
+      return urls[0]

   # Send request to remote service
   requestPreview: (url, callback) ->
     $.getJSON(@serviceUrl, url: url)
       .done((resp) => @processResponse(resp))
+      .fail(=> @previewNotFound())

   # Response handling
   processResponse: (resp) ->


Fix path to icon in notifications

Fix path to icon in notifications

--- src/coffee/background.coffee
+++ src/coffee/background.coffee
@@ -60,7 +60,7 @@
       this.notify "New Promo: #{ship}!", "As of #{date_string} there is a #{ship} promo active!", "#{@blink_url}/?#blink_#{id}"

   notify: (title, body, click_url = false) ->
-    n = webkitNotifications.createNotification '/icon48.png', title, body
+    n = webkitNotifications.createNotification '/resources/img/icon48.png', title, body

     if click_url
       n.onclick = ->


auto scale legend

auto scale legend

--- src/coffee/choropleth.coffee
+++ src/coffee/choropleth.coffee
@@ -61,9 +61,12 @@

     drawLegend: (min_val, max_val)->
         geomap = this
-        rw = 10
-        lh = 200
+        rw = 15
+        lh = 120
         offset_y = geomap.properties.height - lh - 150
+        # reverse a copy to not alter colors array
+        cdata = colors.slice().reverse()
+        rh = lh / cdata.length

         geomap.private.svg.select('g#legend').remove()

@@ -90,17 +93,14 @@
             .attr('x', 0)
             .attr('y', lh + 2 * rw)

-        # reverse a copy to not alter colors array
-        cdata = colors.slice().reverse()
-
         # draw color scale
         sg.selectAll('rect')
             .data(cdata)
             .enter().append('rect')
-            .attr('y', (d, i)-> i * rw)
+            .attr('y', (d, i)-> i * rh)
             .attr('fill', (d, i)-> cdata[i])
             .attr('width', rw)
-            .attr('height', rw)
+            .attr('height', rh)

 (exports? or this).d3.geomap.choropleth = ()->


Removed unused orderedByAuthority

Removed unused orderedByAuthority

--- core/app/backbone/collections/channels.coffee
+++ core/app/backbone/collections/channels.coffee
@@ -54,12 +54,6 @@
       -parseFloat(channel.get("created_by_authority"))
     @sort()

-  orderedByAuthority: ->
-    topchannels = new ChannelList(@models)
-    topchannels.orderByAuthority()
-
-    topchannels
-
   channelArrayForIds: (ids) ->
     array = []
     @each (ch) ->


Take new high contrast map background tiles into use.

--- src/map.coffee
+++ src/map.coffee
@@ -20,7 +20,10 @@
     wmtsPath = (style, language) ->
         stylePath =
             if style == 'accessible_map'
-                "osm-toner/etrs_tm35fin"
+                if language == 'sv'
+                    "osm-sm-visual-sv/etrs_tm35fin"
+                else
+                    "osm-sm-visual/etrs_tm35fin"
             else if RETINA_MODE
                 if language == 'sv'
                     "osm-sm-sv-hq/etrs_tm35fin_hq"


Ah, this doesn't really work here

Ah, this doesn't really work here

--- widgets/graph/graph.coffee
+++ widgets/graph/graph.coffee
@@ -9,8 +9,8 @@

   ready: ->
     color = '#138052'
-    if @get('current') > 8
-      color = '#f00'
+#    if @get('current') > 8
+#      color = '#f00'
     container = $(@node).parent()
     # Gross hacks. Let's fix this.
     width = (Dashing.widget_base_dimensions[0] * container.data("sizex")) + Dashing.widget_margins[0] * 2 * (container.data("sizex") - 1)


movb operation

movb operation

--- grammars/intuicio4.cson
+++ grammars/intuicio4.cson
@@ -179,7 +179,7 @@
 	}
 	{
 		# operation
-		'match': '\\b(call|async|join|ret|mov|add|sub|mul|div|and|or|xor|neg|lsh|rsh|eq|ls|gr|push|pop|drop|cpush|cpop|cdrop|ctxc|ctxd|ctxs|jif|goto|jump|address|new|del|ptr|val|cast|nil|vargs|vargr|mtxc|mtxd|lock|unlock)\\b'
+		'match': '\\b(call|async|join|ret|mov|movb|add|sub|mul|div|and|or|xor|neg|lsh|rsh|eq|ls|gr|push|pop|drop|cpush|cpop|cdrop|ctxc|ctxd|ctxs|jif|goto|jump|address|new|del|ptr|val|cast|nil|vargs|vargr|mtxc|mtxd|lock|unlock)\\b'
 		'name': 'keyword.control.operation.intuicio4'
 	}
 	{


Edit Impala text

Edit Impala text

Removed parentheses from scientific name. Minor text edits.

--- app/lib/field-guide-content/impala.coffee
+++ app/lib/field-guide-content/impala.coffee
@@ -3,7 +3,7 @@
     Impala are medium-sized antelopes, with long necks and long, evenly developed limbs. Their coat is short and glossy reddish-brown in color with white hair inside the ears, over each eye, on the chin, upper throat, under parts, and buttocks. Black markings include ear tips, forehead patch, and three vertical lines on rump.
   '''

-  scientificName: '(Aepyceros melampus)'
+  scientificName: 'Aepyceros melampus'
   mainImage: 'assets/fieldguide-content/mammals/impala/impala-feature.jpg'

   conservationStatus: 'Least Concern' # Options are Least Concern, Near Threatened, Vulnerable, and Endangered; all have their own little icons as well.
@@ -25,7 +25,7 @@
     value: '6.5 months'
   }, {
     label: 'Avg. number of offspring'
-    value: '1 calf'
+    value: '1'
   }]

   sections: [{
@@ -40,7 +40,7 @@
   }, {
     title: 'Behavior'
     content: '''
-
Impala are active night and day, alternating resting and grazing, and drinking at least once a day. Females and young form herds of 50 to 100 individuals that have a home range of about 2 to 6 [square?] kilometers. Sexually mature males form territories that vary in size from 0.2 to 0.9 square kilometers. These territories are marked with urine and feces. Males will attempt to keep female herds within their territory and defend them against intruding males. They use their long, spiral horns to challenge each other.

+
Impala are active night and day, alternating resting and grazing, and drinking at least once a day. Females and young form herds of 50 to 100 individuals that have a home range of about 2 to 6 square kilometers. Sexually mature males form territories that vary in size from 0.2 to 0.9 square kilometers. These territories are marked with urine and feces. Males will attempt to keep female herds within their territory and defend them against intruding males. They use their long, spiral horns to challenge each other.

     '''
   }, {
     title: 'Breeding'


Minimize stutter effect

Minimize stutter effect

--- app/scripts/components/generic/HUD.coffee
+++ app/scripts/components/generic/HUD.coffee
@@ -8,15 +8,15 @@
       viewportX: x
       viewportY: y
       viewportZ: z
-      x: x - Crafty.viewport._x
-      y: y - Crafty.viewport._y
+      x: x - Crafty.viewport.x
+      y: y - Crafty.viewport.y
       z: z
-    @hudFloat = Crafty.bind 'ViewportScroll', =>
+    @hudFloat = Crafty.bind 'EnterFrame', =>
       @attr
-        x: @viewportX - Crafty.viewport._x
-        y: @viewportY - Crafty.viewport._y
+        x: @viewportX - Crafty.viewport.x
+        y: @viewportY - Crafty.viewport.y
     this

   remove: ->
-    Crafty.unbind 'ViewportScroll', @hudFloat
+    Crafty.unbind 'EnterFrame', @hudFloat


use ' ' for disabled prefix mappings

use ' ' for disabled prefix mappings

--- client/keymap.coffee
+++ client/keymap.coffee
@@ -81,7 +81,7 @@
 bq = $.extend {}, BQ; do -> s = prefs.prefixMap(); for i in [0...s.length] by 2 then bq[s[i]] = s[i + 1] # current ` map
 @getBQMap = -> $.extend {}, bq
 @getBQKeyFor = getBQKeyFor = (v) -> (for k in KS when bq[k] == v then return k); ''
-@setBQMap = (bq1) -> $.extend bq, bq1; prefs.prefixMap join(for k, v of bq when v != BQ[k] then k + v); return
+@setBQMap = (bq1) -> $.extend bq, bq1; prefs.prefixMap join(for k, v of bq when v != (BQ[k] || ' ') then k + v); return

 bqChangeHandler = (cm, o) -> # o: changeObj
   l = o.from.line; i = o.from.ch


Corrected the rejection output from the pubsub subscribe method.

--- src/pubsub.coffee
+++ src/pubsub.coffee
@@ -136,6 +136,7 @@
     .catch (error) =>
       logger.error "Error subscribing to channel '#{channel}'", error
       delete @handlers[channel]
+      throw error

   # Unsubscribe from a channel.
   unsubscribe: (channel) ->
@@ -346,7 +347,7 @@
               message = 'Unknown action or bad record format from server'
               setImmediate => @emit 'error', new errors.PubSubError("#{message}: #{rec}", error)

-              logger.error "#{message}: #{error}\n#{error.stack}"
+              logger.error "#{message}: #{rec}: #{error}\n#{error.stack}"
             else
               record = validation.value


fix timepicker on firefox #2711

fix timepicker on firefox #2711

--- lineman/app/components/proposal_form/closing_at_field.coffee
+++ lineman/app/components/proposal_form/closing_at_field.coffee
@@ -18,6 +18,7 @@
     $scope.hours = _.times 24, (i) -> i

     $scope.times = _.times 24, (i) ->
+      i = "0#{i}" if i < 10
       moment("2015-01-01 #{i}:00").format('h a')

     $scope.dateToday = moment().format('YYYY-MM-DD')


properly check for split test outcome

properly check for split test outcome

--- apps/artwork/client/layered_search.coffee
+++ apps/artwork/client/layered_search.coffee
@@ -77,7 +77,7 @@

   initialize: (options = {}) ->
     { @artwork, @fair } = options
-    @relatedV2 = splitTest('merchandized_rails').outcome()
+    @relatedV2 = splitTest('merchandized_rails').outcome() is 'true'
     @setupLayers()

   setupLayers: ->


:mens::boot: Updated at https://danielx.net/editor/

--- fs.coffee
+++ fs.coffee
@@ -10,6 +10,9 @@

 json = (response) ->
   response.json()
+
+blob = (response) ->
+  response.blob()

 uploadToS3 = (bucket, key, file, options={}) ->
   {cacheControl} = options
@@ -25,15 +28,7 @@
 getFromS3 = (bucket, key) ->
   fetch("https://#{bucket.config.params.Bucket}.s3.amazonaws.com/#{key}")
   .then status
-  .then (response) ->
-    contentType = response.headers.get('Content-Type')
-
-    if contentType.match /^text/
-      response.text()
-    else if contentType.match /json$/
-      response.json()
-    else
-      response.blob()
+  .then blob

 list = (bucket, id, dir) ->
   unless startsWith dir, delimiter