remove redundant checks for stack length

--- src/backbone.viewstack.coffee
+++ src/backbone.viewstack.coffee
@@ -187,7 +187,7 @@

       @hasSlid = false

-      if _e.pageX - offset.left < 40 and @stack.length > 1
+      if _e.pageX - offset.left < 40
         nextView = @stack[@stack.length - 2]

@@ -205,7 +205,7 @@
     # don't slide outside of the bounds. Don't listen anymore when we've gone
     # over 20 pixels vertically.
     onMove: (e) ->
-      return if not @slide? or @stack.length < 2
+      return if not @slide?

       e.preventDefault() if e.type is "touchmove"

@@ -242,7 +242,7 @@
     # hidden view returns to its transition position and then clear it's
     # position after the transition duration has passed.
     onEnd: (e) ->
-      return if not @slide? or @stack.length < 2
+      return if not @slide?

       @transitionView @slide.prev, true
       @transitionView @slide.next, true


indexed.coffee: extracted some common wrapper code into a superclass


fixed first time open issue

fixed first time open issue

--- lib/MainView.coffee
+++ lib/MainView.coffee
@@ -35,4 +35,4 @@

     # http://superuser.com/questions/206229/how-to-make-a-blank-page-in-google-chrome-at-start-up
     @setURL('chrome://newtab')
-    setTimeout (=>@setURL(url)), 100
+    setTimeout (=>@setURL(url)), 350


allow HumanReadableLogs to accept an already parsed log


Auto fill the ~/.gdbinit file, fix #1

Auto fill the ~/.gdbinit file, fix #1

--- lib/atom-gdb.coffee
+++ lib/atom-gdb.coffee
@@ -32,6 +32,7 @@
     @subscriptions.add atom.commands.add 'atom-text-editor', 'atom-gdb:toggle-breakpoint': => @toggleBreakpoint()

     @handleSettingsFile()
+    @checkGlobalGdbInit()

     atom.workspace.onDidOpen (event)->
       if event.uri != undefined
@@ -209,4 +210,14 @@
   saveAll: ->
     editors = atom.workspace.getTextEditors()
     for i of editors
-        editors[i].save()
+      editors[i].save()
+
+  checkGlobalGdbInit: ->
+    globalGdbInitFile = new File(process.env['HOME'] + "/.gdbinit", false)
+    globalGdbInitFile.read()
+      .then (content) ->
+        if content == null or not content.match /^set auto-load safe-path/m
+          globalGdbInitFile.write (content or "") + "\n# Added by atom-gdb\nset auto-load safe-path /"
+          console.log "~/.gdbinit has been updated for atom-gdb"
+        else
+          console.log "~/.gdbinit is fine for atom-gdb"


SaveOnStart implemented, fix #3

SaveOnStart implemented, fix #3

--- lib/atom-gdb.coffee
+++ lib/atom-gdb.coffee
@@ -18,6 +18,9 @@
     debuggerCommand:
       type: 'string'
       default: 'qtcreator -client -debug'
+    saveOnStart:
+      type:'boolean'
+      default:true

   activate: (state) ->
     @subscriptions = new CompositeDisposable
@@ -84,6 +87,7 @@
     return value != undefined

   start: ->
+    @saveAll() if atom.config.get('atom-gdb.saveOnStart')
     commandWords = atom.config.get('atom-gdb.debuggerCommand').split " "
     command = commandWords[0]
     args = commandWords[1..commandWords.length]
@@ -102,6 +106,7 @@
     @runProcess(command, args, cwd)

   startNoDebug: ->
+    @saveAll() if atom.config.get('atom-gdb.saveOnStart')
     exe = @getSetting('executablePath')
     if exe == ""
       @start() if @selectExecutable()
@@ -200,3 +205,8 @@
         return i
       ++i
     return -1
+
+  saveAll: ->
+    editors = atom.workspace.getTextEditors()
+    for i of editors
+        editors[i].save()


if, unless, enable, options

if, unless, enable, options

--- lib/bindings.coffee
+++ lib/bindings.coffee
@@ -14,15 +14,30 @@
       value = bindArg.getVmValue()
       bindArg.setVmValue(!value)

-
-addBinding
-  name: 'if'
-  autorun: (c, bindArg) ->
-    if bindArg.getVmValue()
+showHide = (reverse) ->
+  (c, bindArg) ->
+    show = bindArg.getVmValue()
+    show = !show if reverse
+    if show
       bindArg.element.show()
     else
       bindArg.element.hide()

+addBinding
+  name: 'if'
+  autorun: showHide(false)
+
+addBinding
+  name: 'visible'
+  autorun: showHide(false)
+
+addBinding
+  name: 'unless'
+  autorun: showHide(true)
+
+addBinding
+  name: 'hide'
+  autorun: showHide(true)

 addBinding
   name: 'value'
@@ -211,4 +226,49 @@
         bindArg.element.focus()
       else
         bindArg.element.blur()
-    return+    return
+
+enable = (elem) ->
+  if elem.is('button') or elem.is('input') or elem.is('textarea')
+    elem.removeAttr('disabled')
+  else
+    elem.removeClass('disabled')
+
+disable = (elem) ->
+  if elem.is('button') or elem.is('input') or elem.is('textarea')
+    elem.attr('disabled', 'disabled')
+  else
+    elem.addClass('disabled')
+
+enableDisable = (reverse) ->
+  (c, bindArg) ->
+    isEnable = bindArg.getVmValue()
+    isEnable = !isEnable if reverse
+    if isEnable
+      enable bindArg.element
+    else
+      disable bindArg.element
+
+addBinding
+  name: 'enable'
+  autorun: enableDisable(false)
+
+addBinding
+  name: 'disable'
+  autorun: enableDisable(true)
+
+addBinding
+  name: 'options'
+  selector: 'select'
+  bindIf: (bindArg) -> not bindArg.element.prop('multiple')
+  autorun: (c, bindArg) ->
+    source = bindArg.getVmValue()
+    optionsText = bindArg.elementBind.optionsText
+    optionsValue = bindArg.elementBind.optionsValue
+    selection = bindArg.getVmValue(bindArg.elementBind.value)
+    bindArg.element.find('option').remove()
+    for item in source
+      itemText = _.escape(if optionsText then item[optionsText] else item)
+      itemValue = _.escape(if optionsValue then item[optionsValue] else item)
+      selected = if selection is itemValue then "selected='selected'" else ""
+      bindArg.element.append("<option #{selected} value=\"#{itemValue}\">#{itemText}</option>")


Reset file.removed upon compile. #782

Reset file.removed upon compile. #782

--- src/fs_utils/file_list.coffee
+++ src/fs_utils/file_list.coffee
@@ -93,6 +93,7 @@
       .forEach(@compile)

   compile: (file) ->
+    file.removed = false
     path = file.path
     if @compiling[path]
       @resetTimer()


Skinny arrows for class methods

Skinny arrows for class methods

--- src/fs_utils/file_list.coffee
+++ src/fs_utils/file_list.coffee
@@ -92,7 +92,7 @@
         not @compiled[dependent.path]
       .forEach(@compile)

-  compile: (file) =>
+  compile: (file) ->
     path = file.path
     if @compiling[path]
       @resetTimer()
@@ -106,7 +106,7 @@
         @compiled[path] = true
         @emit 'compiled', path

-  copy: (asset) =>
+  copy: (asset) ->
     path = asset.path
     @copying[path] = true
     asset.copy (error) =>
@@ -127,7 +127,7 @@
     @assets.push file
     file

-  _change: (path, compiler, linters, isHelper) =>
+  _change: (path, compiler, linters, isHelper) ->
     ignored = @isIgnored path
     if @is 'assets', path
       unless ignored
@@ -138,7 +138,7 @@
       else
         @compile (@find(path) ? @_add path, compiler, linters, isHelper)

-  _unlink: (path) =>
+  _unlink: (path) ->
     ignored = @isIgnored path
     if @is 'assets', path
       unless ignored


result refactor

result refactor

--- lib/eval.coffee
+++ lib/eval.coffee
@@ -28,11 +28,11 @@
       @client.eval(@evalData(editor, sel)).then ({start, end, result, plainresult}) =>
         error = result.type == 'error'
         view = if error then result.view else result
-        r = @ink?.results.showForLines editor, start-1, end-1,
+        fade = not @ink.Result.removeLines editor, start-1, end-1
+        r = new @ink.Result editor, [start-1, end-1],
           content: views.render view
-          clas: 'julia'
           error: error
-          plainresult: plainresult
+          fade: fade
         if error and result.highlights?
           @showError r, result.highlights
         notifications.show "Evaluation Finished"
@@ -47,7 +47,7 @@
       view = if result.type then result.view else result
       view = @ink.tree.fromJson(view)[0]
       @ink.links.linkify view
-      r = @ink?.results.toggleUnderline editor, range,
+      r = @ink.results.toggleUnderline editor, range,
         content: view
         clas: 'julia'


more linter warnings

more linter warnings

--- lib/eval.coffee
+++ lib/eval.coffee
@@ -56,9 +56,9 @@
   getWord: (editor) ->
     cursor = editor.getLastCursor()
     # The following line is kinda iffy: The regex may or may not be well chosen
-    # and it duplicates the efforts from atom-language-julia. It might be
-    # better to select the current word via finding the smallest <span> containing
-    # the cursor which also has `function` or `macro` as its class.
+    # and it duplicates the efforts from atom-language-julia. It might be better
+    # to select the current word via finding the smallest <span> containing the
+    # cursor which also has `function` or `macro` as its class.
     range = cursor.getCurrentWordBufferRange({wordRegex: /[\u00A0-\uFFFF\w_!´]*\.?@?[\u00A0-\uFFFF\w_!´]+/})
     word = editor.getTextInBufferRange range
     [word, range]
@@ -69,7 +69,7 @@
                       path: editor.getPath()
                       module: editor.juliaModule
                       code: editor.getText()
-                    }).then (result) =>
+                    }).then (result) ->
         notifications.show "Evaluation Finished"

   showError: (r, lines) ->


GameObject.Effect

GameObject.Effect

--- src/game_object_effect.coffee
+++ src/game_object_effect.coffee
@@ -2,7 +2,8 @@
 The Fadeable module provides a method to fade a sprite to transparent.
 You may also provide a callback function that is executed when the sprite has finished fading out.

-@name Ef
+@name Effect
+@field
 @module
 @constructor
 @param {Object} I Instance variables


Skip database phase if there is no DB configured

--- lib/database.coffee
+++ lib/database.coffee
@@ -1,6 +1,7 @@
 {Schema} = require 'jugglingdb'

 exports.getSchema = (config) ->
+  return null unless config.database
   schema = new Schema config.database.provider, config.database.configuration

   unless config.database.logQueries is undefined


making mongoose use kriskowal's Q Promises

--- lib/endpoint.coffee
+++ lib/endpoint.coffee
@@ -1,6 +1,7 @@
+Q = require('q')
 mongoose = require('mongoose')
+mongoose.Promise = Q.Promise;
 _ = require('underscore')
-Q = require('q')
 log = require('./log')
 request = require('./request')
 minimatch = require('minimatch')


Make AFMFont.open different from constructor

--- lib/font/afm.coffee
+++ lib/font/afm.coffee
@@ -2,11 +2,9 @@

 class AFMFont
   @open: (filename) ->
-    new AFMFont(filename)
+    new AFMFont fs.readFileSync filename, 'utf8'

-  constructor: (filename) ->
-    @contents = fs.readFileSync filename, 'utf8'
-
+  constructor: (@contents) ->
     @attributes = {}
     @glyphWidths = {}
     @boundingBoxes = {}


Fix typo so that description for lintOnFly is displayed.

--- lib/init.coffee
+++ lib/init.coffee
@@ -8,7 +8,7 @@
     lintOnFly:
       type: 'boolean'
       default: true
-      descrition: "Enable linting on the fly. Need to restart Atom."
+      description: "Enable linting on the fly. Need to restart Atom."
     ignoreErrorsAndWarnings:
       type: 'string'
       default: ''


change activate msg

change activate msg

--- lib/init.coffee
+++ lib/init.coffee
@@ -5,4 +5,4 @@
     jsStandardExecutablePath: path.join __dirname, '..', 'node_modules', 'standard', 'bin'

   activate: ->
-    console.log 'activate linter-js-standard'
+    console.log 'linter-js-standard activated'


Use skinny arrow

Use skinny arrow

--- lib/git-view.coffee
+++ lib/git-view.coffee
@@ -79,7 +79,7 @@
   getActiveItem: ->
     atom.workspace.getActivePaneItem()

-  update: =>
+  update: ->
     @updateBranchText()
     @updateAheadBehindCount()
     @updateStatusText()


cleaning

cleaning

--- lib/main.coffee
+++ lib/main.coffee
@@ -18,8 +18,8 @@
   getRemote: (host, username, cb) ->
     remote = {}
     host = "#{username}@#{host}" if username?
-    remote.sexec = (cmd, cb) -> exec 'ssh', [host, cmd], false, cb
-    remote.exec = (cmd, cb) -> exec 'ssh', [host, cmd], true, cb
+    remote.sexec = (cmd, cb) -> exec 'ssh', [host, cmd], true, cb
+    remote.exec = (cmd, cb) -> exec 'ssh', [host, cmd], false, cb
     remote.run = (cmds..., cb) -> async.mapSeries cmds, remote.exec, cb
     return remote

@@ -27,7 +27,7 @@
   getLocal: (cwd) ->
     opt = cwd: cwd
     local = {}
-    local.sexec = (cmd, cb) -> exec 'sh', ['-c', cmd], false, cb
-    local.exec = (cmd, cb) -> exec 'sh', ['-c', cmd], true, cb
+    local.sexec = (cmd, cb) -> exec 'sh', ['-c', cmd], true, cb
+    local.exec = (cmd, cb) -> exec 'sh', ['-c', cmd], false, cb
     local.run = (cmds..., cb) -> async.mapSeries cmds, local.exec, cb
     return local


Show notification for invalid file names

--- lib/main.coffee
+++ lib/main.coffee
@@ -731,17 +731,9 @@
     invalidFiles = files.filter (file) ->
       path.basename(file).indexOf(' ') > -1
     if invalidFiles.length
-      errors = []
-      for file in invalidFiles
-        errors.push
-          file: file,
-          message: 'File contains space in its name'
-          row: 0,
-          col: 0
-
-      @CompileErrorsView ?= require './views/compile-errors-view'
-      @SettingsHelper.setLocal 'compile-status', {errors: errors}
-      atom.commands.dispatch @workspaceElement, "#{@packageName()}:show-compile-errors"
+      invalidFiles = invalidFiles.reduce (acc, value) -> "#{acc}\n#{value}"
+      atom.notifications.addError("Following files have spaces in their names:\n\n#{invalidFiles}\n\nPlease rename them")
+      @SettingsHelper.setLocal 'compile-status', null
       atom.commands.dispatch @workspaceElement, "#{@packageName()}:update-compile-status"
       return


:art: Reclassify messages on pane item change

--- lib/messages.coffee
+++ lib/messages.coffee
@@ -1,15 +1,20 @@
 Helpers = require('./helpers')
-{Emitter} = require 'atom'
+{CompositeDisposable, Emitter} = require 'atom'

 class MessageRegistry
   constructor: (@linter)->
     @messages = new Map()
     @emitter = new Emitter
+    @subscriptions = new CompositeDisposable
+    @subscriptions.add atom.workspace.onDidChangeActivePaneItem =>
+      @messages.forEach (messages) => @classifyMessages(messages)
+      @emitter.emit 'did-classify'

   set: (linter, messages) ->
     Helpers.validateMessages(messages)
     @classifyMessages(messages)
     @messages.set(linter, messages)
+    @emitter.emit 'did-classify'
     @emitter.emit 'did-change', @messages

   delete: (linter) ->
@@ -19,8 +24,11 @@
   get: ->
     return @messages

-  onDidChange: (callback)->
+  onDidChange: (callback) ->
     return @emitter.on 'did-change', callback
+
+  onDidClassify: (callback) ->
+    return @emitter.on 'did-classify', callback

   classifyMessages: (messages)->
     isProject = @linter.state.scope is 'Project'
@@ -31,5 +39,10 @@
       else
         message.currentFile = false

+  destroy: ->
+    @messages.clear()
+    @subscriptions.dispose()
+    @emitter.dispose()
+

 module.exports = MessageRegistry


fix bug, garbage

fix bug, garbage

--- lib/operator.coffee
+++ lib/operator.coffee
@@ -63,8 +63,13 @@
         ranges = @mutationManager.getMarkerBufferRanges().filter (range) -> not range.isEmpty()
       else
         ranges = @mutationManager.getBufferRangesForCheckpoint(@flashCheckpoint)
-      @flashType += '-long' if @isOccurrence()
-      @vimState.flash(ranges, type: @flashType)
+      @vimState.flash(ranges, type: @getFlashType())
+
+  getFlashType: ->
+    if @isOccurrence()
+      @flashType + '-long'
+    else
+      @flashType

   trackChangeIfNecessary: ->
     return unless @trackChange
@@ -316,11 +321,11 @@
 # ================================
 class Delete extends Operator
   @extend()
-  hover: con: ':delete:', emoj: ':scssors:'
+  hover: icon: ':delete:', emoji: ':scissors:'
   trackChange: true
   flashType: 'operator-remove'
   flashCheckpoint: 'did-select-occurrence'
-  stayOptonName: 'stayOnDelete'
+  stayOptionName: 'stayOnDelete'

   execute: ->
     @onDidSelectTarget =>


Don't link to a 404 page

Don't link to a 404 page

--- lib/provider.coffee
+++ lib/provider.coffee
@@ -96,7 +96,7 @@
     text: tag
     type: 'tag'
     description: description ? "HTML <#{tag}> tag"
-    descriptionMoreURL: @getTagDocsURL(tag)
+    descriptionMoreURL: if description then @getTagDocsURL(tag) else null

   getAttributeNameCompletions: ({editor, bufferPosition}, prefix) ->
     completions = []
@@ -124,7 +124,7 @@
     displayText: attribute
     type: 'attribute'
     description: description ? "Global #{attribute} attribute"
-    descriptionMoreURL: @getGlobalAttributeDocsURL(attribute)
+    descriptionMoreURL: if description then @getGlobalAttributeDocsURL(attribute) else null

   getAttributeValueCompletions: ({editor, bufferPosition}, prefix) ->
     tag = @getPreviousTag(editor, bufferPosition)


Recast @tpoisot's solution in a slightly different idiom. Thanks, Timothée!

--- lib/provider.coffee
+++ lib/provider.coffee
@@ -87,11 +87,10 @@
     @possibleWords = possibleWords

   readBibtexFiles: (bibtexFiles) =>
-
-    # CHANGED make sure that it works even with as single file
-    if typeof bibtexFiles is 'string'
+    # Make sure our list of BibTeX files is an array, even if it's only one file.
+    if not Array.isArray(bibtexFiles)
       bibtexFiles = [bibtexFiles]
-
+
     try
       bibtex = []


:abc: Convert provider.coffee From JSDoc To TomDoc

--- lib/provider.coffee
+++ lib/provider.coffee
@@ -1,57 +1,45 @@
-###
- * A provider provides an interface to the autocomplete+ package. Third-party
- * packages can register providers which will then be used to generate the
- * suggestions list.
-###
-
+# Public: A provider provides an interface to the autocomplete package. Third-party
+# packages can register providers which will then be used to generate the
+# suggestions list.
 module.exports =
 class Provider
   wordRegex: /\b\w*[a-zA-Z_]\w*\b/g
+
   constructor: (@editorView) ->
     {@editor} = editorView
     @initialize.apply this, arguments

-  ###
-   * An an initializer for subclasses
-   * @private
-  ###
+  # Public: An initializer for subclasses
   initialize: ->
     return

-  ###
-   * Defines whether the words returned at #buildWordList() should be added to
-   * the default suggestions or should be displayed exclusively
-   * @type {Boolean}
-  ###
+  # Public: Defines whether the words returned at {::buildWordList} should be added to
+  # the default suggestions or should be displayed exclusively
   exclusive: false

-  ###
-   * Gets called when the document has been changed. Returns an array with
-   * suggestions. If `exclusive` is set to true and this method returns suggestions,
-   * the suggestions will be the only ones that are displayed.
-   * @return {Array}
-   * @public
-  ###
+  # Public: Gets called when the document has been changed. Returns an array with
+  # suggestions. If `exclusive` is set to true and this method returns suggestions,
+  # the suggestions will be the only ones that are displayed.
+  #
+  # Returns An {Array} of suggestions.
   buildSuggestions: ->
     throw new Error "Subclass must implement a buildWordList(prefix) method"

-  ###
-   * Gets called when a suggestion has been confirmed by the user. Return true
-   * to replace the word with the suggestion. Return false if you want to handle
-   * the behavior yourself.
-   * @param  {Suggestion} suggestion
-   * @return {Boolean}
-   * @public
-  ###
+  # Public: Gets called when a suggestion has been confirmed by the user. Return true
+  # to replace the word with the suggestion. Return false if you want to handle
+  # the behavior yourself.
+  #
+  # suggestion - The {Suggestion} to confirm
+  #
+  # Returns {Boolean} indicating whether the suggestion should be automatically replaced.
   confirm: (suggestion) ->
     return true

-  ###
-   * Finds and returns the content before the current cursor position
-   * @param {Selection} selection
-   * @return {String}
-   * @private
-  ###
+  # Public: Finds and returns the content before the current cursor position
+  #
+  # selection - The {Selection} for the current cursor position
+  #
+  # Returns {String} with the prefix of the {Selection}
   prefixOfSelection: (selection) ->
     selectionRange = selection.getBufferRange()
     lineRange = [[selectionRange.start.row, 0], [selectionRange.end.row, @editor.lineLengthForBufferRow(selectionRange.end.row)]]


Store data in context instead of dimensions

--- lib/reporter.coffee
+++ lib/reporter.coffee
@@ -8,7 +8,7 @@
     send: (eventType, data) ->
       params =
         timestamp: new Date().getTime() / 1000
-        dimensions: data
+        context: data

       requestOptions =
         method: 'POST'


Add safe disabling for non webkit browsers. How test this? Don't know... Need some ideas for this.

--- lib/reporter.coffee
+++ lib/reporter.coffee
@@ -75,15 +75,20 @@
     @param {Object|null} options - options for reporter
   ###
   constructor: (options = {}) ->
-    if options
-      @title = options.title              if options.title
-      @replaceId = options.replaceId      if options.replaceId
-      @timeout = options.timeout          if options.timeout
-      @passedIcon = options.passedIcon    if options.passedIcon
-      @failedIcon = options.failedIcon    if options.failedIcon
-      @runningIcon = options.runningIcon  if options.runningIcon
-    @startedAt =  null
-    @finishedAt = null
+    if jasmine.getGlobal().webkitNotifications
+      if options
+        @title = options.title              if options.title
+        @replaceId = options.replaceId      if options.replaceId
+        @timeout = options.timeout          if options.timeout
+        @passedIcon = options.passedIcon    if options.passedIcon
+        @failedIcon = options.failedIcon    if options.failedIcon
+        @runningIcon = options.runningIcon  if options.runningIcon
+      @startedAt =  null
+      @finishedAt = null
+    else
+      # safe stubing functions for reporting by empty methods
+      @reportRunnerStarting = () ->
+      @reportRunnerResults = () ->
     return

   ###


Add default value for regexFindMode setting.

Because we haven't had a default value for this setting, we never sync
it, which means -- when its not at its default value -- it isn't picked
up in content scripts by the new settings system.

Fixes #1731.

--- lib/settings.coffee
+++ lib/settings.coffee
@@ -146,6 +146,7 @@
       """
     newTabUrl: "chrome://newtab"
     grabBackFocus: false
+    regexFindMode: false

     settingsVersion: Utils.getCurrentVersion()
     helpDialog_showAdvancedCommands: false


Dispose subscriptions to config changes

Dispose subscriptions to config changes

--- lib/tab-view.coffee
+++ lib/tab-view.coffee
@@ -117,6 +117,7 @@
     @iconSubscription?.dispose()
     @mouseEnterSubscription?.dispose()
     @configSubscription?.dispose()
+    @vcsConfigSubscription?.dispose()
     @savedSubscription?.dispose()
     @subscriptions?.dispose()
     @destroyTooltip()


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/array_extensions.coffee
+++ src/array_extensions.coffee
@@ -1,5 +1,5 @@
 ###*
-Calcu
+Calculate the aerage

 [null, undefined, 3, 3, undefined, 5].compact()


use short variable names

use short variable names

--- layout.coffee
+++ layout.coffee
@@ -22,8 +22,8 @@
         walk topState, (state, parent) ->
             node = {
                 name: state.name
-                width: 40
-                height: 40
+                w: 40
+                h: 40
                 children: []
             }
             nodes.push(node)
@@ -50,10 +50,10 @@

     cell.append('rect')
         .attr('class', 'border')
-        .attr('x', (node) -> - node.width / 2)
-        .attr('y', (node) -> - node.height / 2)
-        .attr('width', (node) -> node.width)
-        .attr('height', (node) -> node.height)
+        .attr('x', (node) -> - node.w / 2)
+        .attr('y', (node) -> - node.h / 2)
+        .attr('width', (node) -> node.w)
+        .attr('height', (node) -> node.h)
         .attr('rx', 5)
         .attr('ry', 5)

@@ -77,14 +77,14 @@
     force.on 'tick', ->
         for node in nodes
             if node.children.length > 0
-                xMin = d3.min(node.children, (d) -> d.x - d.width / 2) - pad.left
-                xMax = d3.max(node.children, (d) -> d.x + d.width / 2) + pad.right
-                yMin = d3.min(node.children, (d) -> d.y - d.height / 2) - pad.top
-                yMax = d3.max(node.children, (d) -> d.y + d.height / 2) + pad.bottom
-                node.width = xMax - xMin
-                node.height = yMax - yMin
-                dx = xMin + node.width / 2 - node.x
-                dy = yMin + node.height / 2 - node.y
+                xMin = d3.min(node.children, (d) -> d.x - d.w / 2) - pad.left
+                xMax = d3.max(node.children, (d) -> d.x + d.w / 2) + pad.right
+                yMin = d3.min(node.children, (d) -> d.y - d.h / 2) - pad.top
+                yMax = d3.max(node.children, (d) -> d.y + d.h / 2) + pad.bottom
+                node.w = xMax - xMin
+                node.h = yMax - yMin
+                dx = xMin + node.w / 2 - node.x
+                dy = yMin + node.h / 2 - node.y
                 node.x += dx
                 node.y += dy
                 if node.fixed
@@ -99,13 +99,13 @@
             .attr('transform', (node) -> "translate(#{node.x},#{node.y})")

         svg.selectAll('.cell rect')
-            .attr('x', (node) -> - node.width / 2)
-            .attr('y', (node) -> - node.height / 2)
-            .attr('width', (node) -> node.width)
-            .attr('height', (node) -> node.height)
+            .attr('x', (node) -> - node.w / 2)
+            .attr('y', (node) -> - node.h / 2)
+            .attr('width', (node) -> node.w)
+            .attr('height', (node) -> node.h)

         svg.selectAll('.cell text')
-            .attr('y', (node) -> pad.top - node.height / 2 - 5)
+            .attr('y', (node) -> pad.top - node.h / 2 - 5)

         link.attr('x1', (d) -> d.source.x)
             .attr('y1', (d) -> d.source.y)
@@ -140,7 +140,7 @@
             dx = node.x - other.x
             dy = node.y - other.y
             l = Math.sqrt(dx * dx + dy * dy)
-            r = d3.max([node.width + other.width, node.height + other.height]) * .7
+            r = d3.max([node.w + other.w, node.h + other.h]) * .7
             if l < r
                 l = (l - r) / l * .5
                 move(node, - dx * l, - dy * l)


Just destroy the view of the post if data is fake.

--- client/app/Applications/Activity.kdapplication/views/activityitemchild.coffee
+++ client/app/Applications/Activity.kdapplication/views/activityitemchild.coffee
@@ -142,6 +142,13 @@
             color    : "#ffffff"
             diameter : 16
           callback   : =>
+
+            if data.fake
+              @emit 'ActivityIsDeleted'
+              modal.buttons.Delete.hideLoader()
+              modal.destroy()
+              return
+
             data.delete (err)=>
               modal.buttons.Delete.hideLoader()
               modal.destroy()
@@ -149,7 +156,7 @@
               else new KDNotificationView
                 type     : "mini"
                 cssClass : "error editor"
-                title     : "Error, please try again later!"
+                title    : "Error, please try again later!"

   click: KD.utils.showMoreClickHandler


ネストについて

ネストについて

ちょっと検討

--- src/coffee/kazitori.coffee
+++ src/coffee/kazitori.coffee
@@ -359,6 +359,11 @@
     if not callback
       if isBefore
         callback = @_bindFunctions(name)
+      else if name isinstanceof Kazitori
+        console.log "nest"
+        #@.handlers.concat name.handlers
+        #@.beforeHandlers.concat name.beforeHandlers
+        #return @
       else if typeof name is "function"
         callback = name
       else
@@ -370,6 +375,15 @@
       callback && callback.apply(@.router, args)
     ,@)
     return @
+
+  #ルールを後から追加する
+  # add:(rule)->
+    # return @
+
+  #ルールを削除する
+  # remove:(rule)->
+    # return @
+

   #URL を読み込む
   loadURL:(fragmentOverride, options)->
@@ -499,7 +513,7 @@
       return
     routes = @_keys(@.routes)
     for rule in routes
-      @registerHandler(rule, @.routes[rule],false)
+      @registerHandler(rule, @.routes[rule], false)
     return

   # befores から指定された事前に処理したいメソッドをバインド


Fix upstream/#306

Fix upstream/#306

--- src/core/normalizer.coffee
+++ src/core/normalizer.coffee
@@ -57,9 +57,10 @@
         node.removeAttribute(attribute)
     )
     # Chrome turns <b> into style in some cases
-    if (node.style.fontWeight == 'bold')
+    if (node.style.fontWeight == 'bold' or node.style.fontWeight > 500)
       node.style.fontWeight = ''
       dom(node).wrap(document.createElement('b'))
+      node = node.parentNode
     this.whitelistStyles(node)
     return this.whitelistTags(node)


Don’t need this

Don’t need this

--- src/deserialize-svg.coffee
+++ src/deserialize-svg.coffee
@@ -36,7 +36,6 @@
     null

   parentNode = null
-  window.objs = objects
   objects

 # Convert nodes to svg.js elements


refactor draw()

refactor draw()

--- src/github-calendar.coffee
+++ src/github-calendar.coffee
@@ -5,6 +5,7 @@
 page = 1
 model = []
 eventMap = {}
+processedUrls = 0
 urls = [1..10].map (i)->
 	GITHUB_USER_URL + '/' + user + '/events?page=' + i

@@ -41,6 +42,14 @@
 		$('#github-calendar > .description').append ' commits:' + model.commitsLength
 	)

+draw = ->
+	for grid, i in model
+		if eventMap.hasOwnProperty grid.created_at.format()
+			e = eventMap[ grid.created_at.format() ]
+			commitsLength = if e.payload.commits then e.payload.commits.length else 0
+			grid.commitsLength = commitsLength
+	paintGrids i % 7, Math.floor(i / 7), grid
+
 eventsHandler = (events)->
 	events.forEach (e)->
 		# console.log e
@@ -60,13 +69,10 @@
 			else
 				mergedResult.push current
 				return current
+	processedUrls += 1
+	if (processedUrls > 0)
+		draw()

-	for grid, i in model
-		if eventMap.hasOwnProperty grid.created_at.format()
-			e = eventMap[ grid.created_at.format() ]
-			commitsLength = if e.payload.commits then e.payload.commits.length else 0
-			grid.commitsLength = commitsLength
-		paintGrids i % 7, Math.floor(i / 7), grid

 # MAIN
 $('#github-calendar').append $('<div class="content">')
@@ -83,5 +89,4 @@
 $.get( GITHUB_USER_URL + '/' + user + '/events?page=' + page)
 	.done eventsHandler

-
 paper = Raphael(10, 10, 600, 100)


check for truth, not existence when identifying navigation flow

--- app/assets/javascripts/components/mark/index.cjsx
+++ app/assets/javascripts/components/mark/index.cjsx
@@ -262,7 +262,7 @@
                     { if false
                       <button type="button" className="back minor-button" disabled={onFirstAnnotation} onClick={@destroyCurrentAnnotation}>Back</button>
                     }
-                    { if @getNextTask() and not @state.badSubject?
+                    { if @getNextTask() and @state.badSubject != true?
                         <button type="button" className="continue major-button" disabled={waitingForAnswer} onClick={@advanceToNextTask}>Next</button>
                       else
                         if @state.taskKey == "completion_assessment_task"


Fix bug when an ad card starts out hidden.

--- app/assets/javascripts/lib/core/ad_manager.coffee
+++ app/assets/javascripts/lib/core/ad_manager.coffee
@@ -93,7 +93,8 @@
       adManager.afterLoaded adEl, (adEl, iframe) ->
         if iframe.height() > $(adEl).height()
           thisCard = $(adEl).closest('.card').addClass 'ad-doubleMpu'
-          cardsPerRow = Math.floor $(adEl).closest('.grid-view').width() / (thisCard.width() / 2)
+          grid = $(adEl).closest('.grid-view')
+          cardsPerRow = Math.floor grid.width() / (grid.find('.card--single').width())
           cards = $('.results .card')
           thisCardIndex = cards.index(thisCard)

@@ -104,17 +105,17 @@

           # Eliminate all cards preceding our ad element so we can place a dummy el at the nth position *after* the current one using .eq()
           cards = $(cards.splice(thisCardIndex))
-          dummyEl = '
'
+          dummyCard = '
'

           thisCard.css(
             left: thisCard.position().left
             position: 'absolute'
             top: thisCard.position().top
           )
-
+
           # cardsPerRow - 2 because the mpu takes the width of 2 cards.
-          cards.eq(cardsPerRow - 2).after(dummyEl)
-          thisCard.before(dummyEl)
+          cards.eq(cardsPerRow - 2).after(dummyCard)
+          thisCard.before(dummyCard)

     hideEmpty : (adEl, type) -> # TODO: remove type /spike
       adManager.afterLoaded adEl, (adEl, iframe) ->


Make leaderboard always show and ad cards hide first then show if there's an ad.

--- app/assets/javascripts/lib/core/ad_manager.coffee
+++ app/assets/javascripts/lib/core/ad_manager.coffee
@@ -54,15 +54,17 @@
           # Exploit the fact that getElementById only returns the FIRST element with that ID and make this one unique.
           adEl.id = newId

-          if /leaderboard/i.test(type)
-            toPoll[newId] = adManager.showLeaderboard
-          else if /mpu/i.test(type)
-            toPoll[newId] = adManager.checkMpu
+          if /mpu/i.test(type)
+            toPoll[newId] = ->
+              adManager.showAd.apply this, arguments
+              adManager.checkMpu.apply this, arguments
           else if /adsense/i.test(type)
             # Note: we're using the old, non DFP way of calling adsense ads. Ideally once we figure out how to serve these through DFP, this can be ditched in favour of the below commented out code.
             continue
           else if /oneByOne/.test(type)
             toPoll[newId] = adManager.checkOneByOne
+          else if /trafficDriver/.test(type)
+            toPoll[newId] = adManager.showAd

           adUnit = googletag.defineSlot("/"+unit.join("/"), adSize, newId).addService(pubAds)
           adManager.adUnits[newId] = adUnit
@@ -146,9 +148,9 @@
       , 0
       # Note: the wallpaper does all the work from the code that's returned by the ad server.

-    showLeaderboard : (adEl, iframe) ->
+    showAd : (adEl, iframe) ->
       if adEl.style.display isnt 'none'
-        $(adEl).closest('.row--leaderboard').removeClass('is-closed')
+        $(adEl).closest('.js-card').removeClass('is-closed')

     setupInterstitial : (adEl, iframe) ->
       adLink = $(iframe).contents().find('#ad-link')


improved speed logging

improved speed logging

--- app/assets/javascripts/oxalis/model/binary.coffee
+++ app/assets/javascripts/oxalis/model/binary.coffee
@@ -86,7 +86,12 @@
       plane.updateContrastCurves(@contrastCurves)

-  printConnectionInfo : ->
+  printConnectionInfo : _.once ->
+    @printConnectionInfo = _.throttle(@printConnectionInfoImpl, @PING_THROTTLE_TIME)
+    @printConnectionInfo()
+
+
+  printConnectionInfoImpl : ->

     currentDate = new Date()
     interval = currentDate - @lastPingTime
@@ -114,15 +119,12 @@
     @queue.clear()

-  ping : _.once (position, options) ->
-
-    @ping = _.throttle(@pingImpl, @PING_THROTTLE_TIME)
-    @arbitraryPing(position, options)
+  ping = @pingImpl

   pingImpl : (position, {zoomStep, area, activePlane}) ->

-    @printConnectionInfo()
+    @printConnectionInfoImpl()

     if @lastPosition?


correct starting route

correct starting route

--- app/assets/javascripts/pinkman_base/router.coffee
+++ app/assets/javascripts/pinkman_base/router.coffee
@@ -318,7 +318,7 @@
       Pinkman.router = this
       App.router = this
       window.$r = App.router
-      @activate(window.location.pathname)
+      @activate(window.location.href)
       $('body').on 'click', 'a:not([data-pinkman="false"],[target="blank"],[target="_blank"])', (ev) =>
         ev.preventDefault()
         path = ev.currentTarget.href


Eugenia notation - serialise containment references for links.

--- app/controllers/notations/eugenia_notation.coffee
+++ app/controllers/notations/eugenia_notation.coffee
@@ -8,14 +8,15 @@
     @serialisePaletteHeader(palette) + @serialisePaletteBody(palette)

   serialisePaletteHeader: (palette) ->
-    nodeShapeContainmentReferences = ("  val #{ns.name}[*] #{ns.name}s;" for ns in palette.nodeShapes().all()).join('\n')
+    shapes = palette.nodeShapes().all().concat palette.linkShapes().all()
+    containmentReferences = ("  val #{s.name}[*] #{s.name.toLowerCase()}s;" for s in shapes).join('\n')

     """@namespace(uri="yourlanguage", prefix="yourlanguage")
     package yourlanguage;

     @gmf.diagram(foo="bar")
     class Root {
-    #{nodeShapeContainmentReferences}
+    #{containmentReferences}
     }

     abstract class Element {}\n\n"""


Tests: More failing </script> fun

Tests: More failing </script> fun

--- spec/runner.coffee
+++ spec/runner.coffee
@@ -366,10 +366,30 @@
         chai.expect(err.message).to.contain 'TIMEOUT'
         done()

-  describe 'input data containing </script>', ->
+  describe 'input data containing <script>...</script>', ->
     it 'should succeed', (done) ->
       filter = local 'return-input'
       input = { 'foo': 'barbaz', 'htmlscript': '<script>alert("Works!")</script>' }
+      options = {}
+      solver.runJob filter, input, options, (err, solution, details) ->
+        chai.expect(err).to.not.exist
+        chai.expect(solution).to.eql input
+        done()
+
+  describe 'input data containing </script>...<script>', ->
+    it 'should succeed', (done) ->
+      filter = local 'return-input'
+      input = { 'foo': 'barbaz', 'htmlbogusscript': '</script> <script>alert("Works!")</script> <script>' }
+      options = {}
+      solver.runJob filter, input, options, (err, solution, details) ->
+        chai.expect(err).to.not.exist
+        chai.expect(solution).to.eql input
+        done()
+
+  describe 'input data containing HTML comment', ->
+    it 'should succeed', (done) ->
+      filter = local 'return-input'
+      input = { 'foo': 'barbaz', 'htmlcomment': '<!-- FFOO -->' }
       options = {}
       solver.runJob filter, input, options, (err, solution, details) ->
         chai.expect(err).to.not.exist


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com


removed debug statements

removed debug statements


allow dash in domain names

allow dash in domain names


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/function_extensions.coffee
+++ test/function_extensions.coffee
@@ -25,7 +25,7 @@
   score = 0

   addScore = ->
-
+    score +=

   onceScore = ->
     ( ->


tests(productImport): use random string function to generate skus

--- test/product-import.spec.coffee
+++ test/product-import.spec.coffee
@@ -6,6 +6,7 @@
 path = require 'path'
 {ExtendedLogger} = require 'sphere-node-utils'
 package_json = require '../package.json'
+randomString = require 'randomstring'

 frozenTimeStamp = new Date().getTime()

@@ -526,14 +527,13 @@

   describe '::_getExistingProductsForSkus', ->

-    it 'should split into multiple querys', (done) ->
+    it 'should split into multiple queries', (done) ->
       spyOn(@import.client.productProjections, 'fetch').andReturn({
         then: (fn) -> fn({ body: { results: [] } })
       })
-      sku = 'SK/U'
       skus = []
       for i in [1..10000]
-        skus.push(sku)
+        skus.push(randomString.generate(Math.round(Math.random()*100)))
       chunks = @import.commonUtils._separateSkusChunksIntoSmallerChunks(
         skus,
         @import._getWhereQueryLimit()
@@ -552,10 +552,9 @@
       spyOn(@import.client.productProjections, 'fetch').andReturn({
         then: (fn) -> fn({ body: { results: ['result1', 'result2'] } })
       })
-      sku = 'SK/U'
       skus = []
       for i in [1..10000]
-        skus.push(sku)
+        skus.push(randomString.generate(Math.round(Math.random()*100)))
       chunks = @import.commonUtils._separateSkusChunksIntoSmallerChunks(
         skus,
         @import._getWhereQueryLimit()


Simple time entry creation test failure

Simple time entry creation test failure

--- test/routes/time_entries.coffee
+++ test/routes/time_entries.coffee
@@ -7,6 +7,8 @@
 startApp = require './../start_app'
 base = require './../base'
 options = require './../options'
+
+TimeEntry = require "#{__dirname}/../../models/time_entry"

 testUser = undefined
 testProject = undefined
@@ -50,4 +52,15 @@
     deleteUserAndProject done

   it 'should make a time entry', (done)->
-    setTimeout done, 100
+    ops = _.clone options
+    ops.body =
+      start: new Date('May 4 1987')
+      end: new Date()
+      message: 'I am a wonderful time entry :)'
+      projectId: testProject.id
+      userId: testUser.id
+    request.post (base '/time_entries'), (e,r,b)->
+      r.statusCode.should.be.equal 200
+      te = new TimeEntry b
+      te.validate().should.be.true
+      done()


describe.only deleted for GenericParser

describe.only deleted for GenericParser

--- test/test-generic-parser.coffee
+++ test/test-generic-parser.coffee
@@ -3,7 +3,7 @@

 { GenericParser } = require './../src/generic-parser'

-describe.only 'GenericParser', ->
+describe 'GenericParser', ->

   describe 'constuctor', ->


Import figures from LaTeX zipfile!

Import figures from LaTeX zipfile!

--- client/import.iced
+++ client/import.iced
@@ -189,9 +189,23 @@
   else
     x

+basename = (path) ->
+  if '/' in path
+    path[path.lastIndexOf('/')+1..]
+  else
+    path
+
+path2ext = (path) ->
+  if '.' in path
+    path[path.lastIndexOf('.')..]
+  else
+    ''
+
 importLaTeX = (group, zip) ->
   me = Meteor.user().username
   zip = new JSZip zip
+
+  ## Extract bib entries.
   bibs = {}
   for filename, content of zip.files
     if filename[-4..] == '.bib'
@@ -219,16 +233,48 @@
           year: year
           abbrev: abbrev
           url: match[2][1...-1]
+
+  ## Extract figures.
+  figures = {}
+  for filename, content of zip.files
+    if filename[-4..] == '.tex'
+      tex = content.asText()
+      tex = tex.replace /%.*$\n?/mg, ''
+      .replace /\\begin\s*{(wrap)?figure}([^\0]*?)\\end\s*{(wrap)?figure}\s*/g,
+        (match, p1, p2, p3) ->
+          graphics = []
+          gr = /\\includegraphics\s*(\[[^\[\]]*\]\s*)?{((?:[^{}]|{[^{}]*})*)}/g
+          while (match = gr.exec p2)?
+            filename = match[2]
+            for extension in ['', '.png', '.jpg', '.pdf']
+              if filename + extension of zip.files
+                filename += extension
+                break
+            if filename not of zip.files
+              console.warn "Missing file for \\includegraphics{#{graphics}}"
+            graphics.push filename
+          caption = /\\caption\s*{((?:[^{}]|{[^{}]*})*)}/.exec p2
+          caption = caption[1]
+          labels = []
+          lr = /\\label\s*{((?:[^{}]|{[^{}]*})*)}/g
+          while (match = lr.exec p2)?
+            labels.push match[1]
+          console.log "Figure #{labels.join ' / '} = #{graphics.join ' & '} = #{caption}"
+          figure =
+            graphics: graphics
+            caption: caption
+            labels: labels
+          for label in labels
+            figures[label] = figure
+
+  ## Extract sections.
   for filename, content of zip.files
     if filename[-4..] == '.tex'
       tex = content.asText()
       ## Remove comments (to ignore \sections inside comments).
-      ## Also remove figures, as we're not handling that yet...
       tex = tex
       .replace /%.*$\n?/mg, ''
-      .replace /\\begin\s*{figure}[^\0]*?\\end\s*{figure}\s*/g, ''
-      .replace /\\begin\s*{wrapfigure}[^\0]*?\\end\s*{wrapfigure}\s*/g, ''
-      .replace /\\begin\s*{wrapfigure}[^\0]*?\\end\s*{wrapfigure}\s*/g, ''
+      .replace /\\begin\s*{(wrap)?figure}[^\0]*?\\end\s*{(wrap)?figure}\s*/g, ''
       .replace /\\cite\s*(?:\[([^\[\]]*)\]\s*)?{([^{}]*)}/g, (match, p1, p2) ->
         '[' + (
           for cite in p2.split ','
@@ -244,6 +290,7 @@
       start = null
       labels = {}
       messages = []
+      figurecount = 0
       r = /\\(sub)*section\s*{((?:[^{}]|{[^{}]*})*)}|\\bibliography/g
       while (match = r.exec tex)?
         if start?
@@ -273,18 +320,74 @@
         start = match.index + match[0].length

       for message in messages
+        attach = []
         message.body = message.body
-        .replace /\\ref{([^{}]*)}/, (match, p1) ->
+        .replace /\\ref\s*{([^{}]*)}/, (match, p1) ->
           if p1 of labels
             labels[p1]
+          else if p1 of figures
+            attach.push figures[p1]
+            "``#{figures[p1].labels[figures[p1].labels.length-1]}''"
           else
+            console.warn "Unresolved #{match}"
             match
         revision = _.clone message
         revision.updated = revision.created
         delete revision.created
         revision.updators = [revision.creator]
         delete revision.creator
-        Meteor.call 'messageImport', group, null, message, [revision]
+        await Meteor.call 'messageImport', group, null, message, [revision], defer error, message._id
+
+        attach = _.unique attach  ## upload \ref'd figures only once each
+        for figure in attach
+          figure.message =
+            title: "Figure ``#{figure.labels[figure.labels.length-1]}''"
+            body: figure.caption
+            published: now
+            format: 'latex'
+          frevision = _.clone figure.message
+          figure.message.creator = me
+          figure.message.created = now
+          frevision.updators = [me]
+          frevision.updated = now
+          await Meteor.call 'messageImport', group, message._id, figure.message, [frevision], defer error, figure.message._id
+
+        await
+          for figure in attach
+            figuremsg =
+              title: "Figure #{figure.labels[figure.labels.length-1]}"
+              body: body
+
+            figure.files = []
+            for filename in figure.graphics
+              base = basename filename
+              file = new File [zip.files[filename].asArrayBuffer()],
+                       base,
+                       type: ext2type[path2ext base]
+                       lastModified: now
+              file.creator = me
+              file.created = now
+              file.group = group
+              figure.files.push file
+              do (d = defer file.file2id) ->
+                file.callback = (file2) ->
+                  d file2.uniqueIdentifier
+              Files.resumable.addFile file
+        await
+          for figure in attach
+            for file in figure.files
+              filerev =
+                format: 'file'
+                title: file.name
+                body: file.file2id
+                published: now
+              filemsg = _.clone filerev
+              filemsg.creator = me
+              filemsg.created = now
+              filerev.updators = [me]
+              filerev.updated = now
+              Meteor.call 'messageImport', group, figure.message._id, filemsg, [filerev],
+                defer()  ## ignoring error

 #importLaTeX.readAs = 'Text'
 importLaTeX.readAs = 'ArrayBuffer'


Improved descriptions (English)

Improved descriptions (English)

--- test/unit/add-tests-test.coffee
+++ test/unit/add-tests-test.coffee
@@ -16,7 +16,7 @@

   describe '#run', ->

-    describe 'when raml contains single GET', ->
+    describe 'when RAML contains single GET', ->

       tests = []
       testFactory = new TestFactory()
@@ -63,7 +63,7 @@
         assert.isNull res.headers
         assert.isNull res.body

-    describe 'when raml contains one GET and one POST', ->
+    describe 'when RAML contains one GET and one POST', ->

       tests = []
       testFactory = new TestFactory()
@@ -110,7 +110,7 @@
         assert.isNull res.headers
         assert.isNull res.body

-    describe 'when raml includes multiple referencing schemas', ->
+    describe 'when RAML includes multiple referencing schemas', ->

       tests = []
       testFactory = new TestFactory
@@ -154,7 +154,7 @@
         assert.isNull res.headers
         assert.isNull res.body

-    describe 'when raml has inline and included schemas', ->
+    describe 'when RAML has inline and included schemas', ->

       tests = []
       testFactory = new TestFactory
@@ -198,7 +198,7 @@
         assert.isNull res.headers
         assert.isNull res.body

-    describe 'when raml contains three-levels endpoints', ->
+    describe 'when RAML contains three-levels endpoints', ->

       tests = []
       testFactory = new TestFactory()
@@ -238,7 +238,7 @@
         assert.deepEqual test.request.params,
           machine_id: '1'

-    describe 'when raml has resource not defined method', ->
+    describe 'when RAML has resource not defined method', ->

       tests = []
       testFactory = new TestFactory()
@@ -266,7 +266,7 @@
       it 'should set test.name', ->
         assert.equal tests[0].name, 'GET /root/machines -> 200'

-    describe 'when raml has invalid request body example', ->
+    describe 'when RAML has invalid request body example', ->

       tests = []
       testFactory = new TestFactory()
@@ -308,7 +308,7 @@
       it 'should give a warning', ->
         assert.ok console.warn.called

-      it 'should added 1 test', ->
+      it 'should add 1 test', ->
         assert.lengthOf tests, 1
         assert.equal tests[0].name, 'POST /machines -> 204'


Change how thumbnails for lists are marked up We now need to place the image tag for a thumbnail as the first child of the anchor tag rather than as the first child of the list tag.

--- src/opac/search_result.coffee
+++ src/opac/search_result.coffee
@@ -30,8 +30,8 @@
 	#
 	tpl_summary_info = _.template '''
 	<li id="title_id_<%= title_id %>">
-

+


Fix touch events on iOS

Fix touch events on iOS

--- layout.coffee
+++ layout.coffee
@@ -440,10 +440,14 @@

   class Sprite
 #    guid = 0
+    noop = () ->
     stylemap= {x: 'left', y: 'top', bgcolor: 'backgroundColor'}
     fcamelCase = ( all, letter ) ->
       letter.toUpperCase()
     rdashAlpha = /-([\da-z])/gi
+    capabilities =
+      # detect touchhttp://stackoverflow.com/questions/4817029/whats-the-best-way-to-detect-a-touch-screen-device-using-javascript
+      touch: 'ontouchstart' of window || 'onmsgesturechange' of window # deal with ie10

     constructor: (jqel, view) ->
       # console.log 'new sprite', jqel, view
@@ -496,6 +500,10 @@

     set_clickable: (clickable) ->
       @setStyle('pointer-events', if clickable then 'auto' else 'none')
+
+      if capabilities.touch
+        # ugly hack to make touch events emulate clicks, see http://sitr.us/2011/07/28/how-mobile-safari-emulates-mouse-events.html
+        @el.onclick = noop

       # TODO: retrigger the event for the element below for IE and Opera? See http://stackoverflow.com/questions/3680429/click-through-a-div-to-underlying-elements
       # el = $(event.target)


First cut at a layout

First cut at a layout

--- layout.coffee
+++ layout.coffee
@@ -360,10 +360,46 @@
 					initFromElement(child, parent)

+	class Layout extends Node
+		attribute = 'x'
+		otherattribute = 'width'
+		spacing = 10
+		locked = true
+		constructor: (el, options = {}) ->
+			super(el, options)
+			@parent.bind('subviews', @added)
+			for subview in @parent.subviews
+				@added(subview)
+			locked = false
+			@update()
+			#console.log('layout', @parent, options)
+
+		added: (child) =>
+#			console.log 'added', child
+			child.bind(otherattribute, @update)
+			@update(child)
+
+		update: (sender) =>
+			return if locked
+#			console.log 'update', sender
+			subviews = @parent.subviews
+			pos = 0
+			skip = true if sender
+			for subview in @parent.subviews
+				if (skip and subview != sender)
+#					console.log 'skipping', subview
+				else
+					subview.setAttribute(attribute, pos)
+					skip = false
+
+				pos += spacing + subview[otherattribute]
+
+
 	exports = {
 		view: View,
 		class: Class,
 		node: Node,
+		layout: Layout,
 		init: init
 	}


Don't store undefined values as IIPs

Don't store undefined values as IIPs

--- src/plugins/nofloGraph.coffee
+++ src/plugins/nofloGraph.coffee
@@ -131,6 +131,7 @@
           return if iip.from.data is value
           metadata = iip.metadata
           graph.nofloGraph.removeInitial node.nofloNode.id, port
+      return if value is undefined
       graph.nofloGraph.addInitial value, node.nofloNode.id, port, metadata
     node.on 'bang', (port) ->
       for iip in graph.nofloGraph.initializers


payment: better currency formatting

payment: better currency formatting

--- client/landing/app/Applications/Dashboard.kdapplication/views/productview.coffee
+++ client/landing/app/Applications/Dashboard.kdapplication/views/productview.coffee
@@ -8,11 +8,11 @@
     product = @getData()

     title     = product.title
-    price     = (product.feeAmount / 100).toFixed 2
+    price     = @utils.formatMoney product.feeAmount / 100
     fmtPrice  =
       if product.priceIsVolatile
       then '(price is volatile)'
-      else "$#{ price }"
+      else "#{ price }"

     subscriptionType =
       if product.subscriptionType is 'single'


(feature) Don't require ace to minimize payload.

--- src/markdown_editor.coffee
+++ src/markdown_editor.coffee
@@ -1,8 +1,7 @@
-Ace = require 'brace'
-Range = Ace.acequire 'ace/range'
-require 'brace/mode/markdown'
-require 'brace/theme/chrome'
-require 'brace/ext/language_tools'
+Range = (ace.require 'ace/range')
+ace.require 'ace/mode/markdown'
+ace.require 'ace/theme/chrome'
+ace.require 'ace/ext/language_tools'

 _ = require 'lodash'


Rename event argument to e, this is our default event var name

--- src/mentions-kinder.coffee
+++ src/mentions-kinder.coffee
@@ -147,10 +147,10 @@
   handleReset: =>
     @$editable.empty()

-  handlePlaceholder: (event)=>
-    if event.type == 'focus'
+  handlePlaceholder: (e)=>
+    if e.type == 'focus'
       @$placeholder?.detach()
-    else if event.type == 'blur' || event.type == 'reset'
+    else if e.type == 'blur' || e.type == 'reset'
       if @_strip(@serializeEditable()) == ''
         @$editable.empty().append(@$placeholder)


Export our UUID class and allow {uuid} = require 'ofx/UUID'.

--- application/chrome/content/wesabe/ofx/UUID.coffee
+++ application/chrome/content/wesabe/ofx/UUID.coffee
@@ -38,6 +38,9 @@

   @string: ->
     (new UUID).toString()
+
+  @uuid: ->
+    UUID.string()

 createUUID = ->
   # JavaScript Version of UUID implementation.
@@ -139,3 +142,6 @@
   num = Math.round(Math.random() * max)
   num = if num < min then min else max
   return num
+
+
+module.exports = UUID


fix failure when brain is loaded from serialization

--- src/scripts/days_since.coffee
+++ src/scripts/days_since.coffee
@@ -23,7 +23,7 @@
   robot.respond /how long since\s+(.*?)\??$/i, (msg) ->
     if robot.brain.data.days_since && robot.brain.data.days_since[msg.match[1]]
       date = robot.brain.data.days_since[msg.match[1]]
-      days_since = Math.floor((new Date - date.getTime()) / (1000*24*60*60))
+      days_since = Math.floor((new Date - new Date(date).getTime()) / (1000*24*60*60))
       msg.send "it's been " + days_since + " days since " + msg.match[1]
     else
       msg.send "I don't recall that event"


Avoid furnishing box

Avoid furnishing box

--- code/serv.coffee
+++ code/serv.coffee
@@ -270,8 +270,7 @@
         cd /opt/cobalt &&
         . ./code/box_lib.sh &&
         create_user #{user_name} &&
-        create_user_directories #{user_name} &&
-        furnish_box #{user_name}
+        create_user_directories #{user_name}
         """
   # insecure - sanitise user_name
   exec cmd, callback


Model.updateAll is now available, uses roughly the mongo syntax

--- models/mongo.coffee
+++ models/mongo.coffee
@@ -108,6 +108,11 @@
                 items.push unpacked
                 fn null, items if items.length == array.length

+    updateAll: (query, cmd, fn) ->
+      @prototype.collection (error, collection) ->
+        return fn error if error?
+        collection.update Mongo.queryify(query), cmd, (upsert: false, multi: true), fn
+
   InstanceMethods:
     collection: (fn) ->
       Mongo.db.collection @serializer.name, fn


updateMetadata calls nback so that Horace this is working.

--- adapter.coffee
+++ adapter.coffee
@@ -80,13 +80,8 @@
 # Update the metadata contained within the pdf file with
 # the info in the supplied horace-metadata instance
 updateMetadata = (metadata, nback)->
-
-
-_.extend module.exports,
-  id             : _Adapter_Id
-  getBook        : getBook
-  getMetadata    : getMetadata
-  updateMetadata : updateMetadata
+  #TODO : Implement Me!!!
+  nback null, metadata

 _.extend module.exports,
   id               : _Adapter_Id


JS: Add tests for translation file retrieval

--- tests_javascript/test_ui.coffee
+++ tests_javascript/test_ui.coffee
@@ -1,6 +1,8 @@
 assert = require 'assert'
 sinon = require 'sinon'
 ui = require 'evesrp/common-ui'
+en_US = require 'evesrp/translations/en-US.json'
+Jed = require 'jed'

 suite 'Common UI', () ->

@@ -97,15 +99,35 @@
 suite 'Localization', () ->

     setup () ->
+        global.scriptRoot = ''
         @sandbox = sinon.sandbox.create()
         @sandbox.useFakeServer()
+        @sandbox.useFakeXMLHttpRequest()

     teardown () ->
         @sandbox.restore()

-    test 'Should retrieve translation files for the current locale'
+    test 'Should retrieve translation files for the current locale', () ->
+        requested = false
+        @sandbox.server.respondWith '/static/translations/en-US.json',
+            (request) ->
+                requested = true
+                request.respond 200, {'Content-Type': 'application/json'},
+                    JSON.stringify en_US
+        ui.setupTranslations()
+        @sandbox.server.respond()
+        assert.ok requested

-    test 'Should return the existing promise for an in-progress setup'
+    test 'Should return the existing promise for an in-progress setup', () ->
+        @sandbox.server.respondWith '/static/translations/en-US.json', [
+            200
+            {'Content-Type': 'application/json'}
+            JSON.stringify en_US
+        ]
+        firstPromise = ui.setupTranslations()
+        @sandbox.server.respond()
+        secondPromise = ui.setupTranslations()
+        assert.strictEqual firstPromise, secondPromise

     test 'Should translate strings to the current locale'


cleanup

cleanup

--- lib/ui.coffee
+++ lib/ui.coffee
@@ -270,12 +270,12 @@
     @emitter = new Emitter
     @autoPreview = @provider.getConfig('autoPreview')
     @autoPreviewOnQueryChange = @provider.getConfig('autoPreviewOnQueryChange')
-    @showSearchOption = @provider.showSearchOption
     @highlighter = new Highlighter(this)
     @itemAreaStart = Object.freeze(new Point(1, 0))

     # Pull never changing info-only-properties from provider.
     {
+      @showSearchOption
       @showLineHeader
       @showColumnOnLineHeader
       @boundToSingleFile
@@ -641,9 +641,7 @@
     @refreshDisposables?.dispose()
     @refreshDisposables = new CompositeDisposable
     @filePathsForAllItems = []
-
     @emitWillRefresh()
-
     @highlighter.clearCurrentAndLineMarker()

     if @query?
@@ -727,7 +725,6 @@
     @requestItems({filePath})

     getItemPromise.then =>
-      # console.log 'fin', @items.getCount()
       @emitDidRefresh()
       @emitDidStopRefreshing()
       return null


change default terms text

change default terms text

--- src/smc-util/db-schema.coffee
+++ src/smc-util/db-schema.coffee
@@ -1110,7 +1110,7 @@
     terms_of_service:
         name    : "Terms of service"
         desc    : "The text displayed for the terms of service link (make empty to not require)."
-        default : 'By signing up you agree to our Terms of Service.'
+        default : 'Click to agree to our Terms of Service.'
     account_creation_email_instructions:
         name    : 'Account creation'
         desc    : "Instructions displayed next to the box where a user creates their account using their name and email address."


Classroom: Self loader class thumb. WIP.

--- client/app/Applications/Classroom.kdapplication/Views/classroomclassthumbview.coffee
+++ client/app/Applications/Classroom.kdapplication/Views/classroomclassthumbview.coffee
@@ -1,4 +1,4 @@
-class ClassroomClassThumbView extends JView
+class ClassroomClassThumbView extends KDView

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

@@ -6,35 +6,49 @@

     super options, data

-    @cdnRoot = "http://fatihacet.kd.io/cdn/classes/#{@getData().name}.kdclass/"
+    @addSubView @loader = new KDLoaderView
+      size    :
+        width : 40

-    devModeOptions = {}
-    if data.devMode
-      devModeOptions.cssClass = "top-badge gray"
-      devModeOptions.partial  = "Dev Mode"
+    @fetchManifest()

-    @devMode     = new KDCustomHTMLView devModeOptions
-    @icon        = new KDCustomHTMLView
-      tagName    : "img"
-      attributes :
-        src      : "#{@cdnRoot}/#{@getData().icns['128']}"
+  fetchManifest: ->
+    manifestURL = "#{@getOptions().cdnRoot}/#{@getData()}.kdclass/manifest.json"
+    KD.getSingleton("kiteController").run "curl -s #{manifestURL}", (err, res) =>
+      log err, res

-    @deleteIcon  = new KDCustomHTMLView
-      tagName    : "span"
-      cssClass   : "icon delete"
-      click      : ->
-        log "sadasdas"
+  createElements: ->
+  #   devModeOptions = {}
+  #   if data.devMode
+  #     devModeOptions.cssClass = "top-badge gray"
+  #     devModeOptions.partial  = "Dev Mode"

-  pistachio: ->
-    data   = @getData()
-    return """
-      {{> @devMode}}
-
{{> @icon}}

-

-        {{> @deleteIcon}}
-

-
-        #{data.name}
-        #{data.version}
-
-    """
+  #   @devMode     = new KDCustomHTMLView devModeOptions
+  #   @icon        = new KDCustomHTMLView
+  #     tagName    : "img"
+  #     attributes :
+  #       src      : "#{@cdnRoot}/#{@getData().icns['128']}"
+
+  #   @deleteIcon  = new KDCustomHTMLView
+  #     tagName    : "span"
+  #     cssClass   : "icon delete"
+  #     click      : ->
+  #       log "sadasdas"
+
+  viewAppended: ->
+    super
+    @loader.show()
+
+  # pistachio: ->
+  #   data   = @getData()
+  #   return """
+  #     {{> @devMode}}
+  #
{{> @icon}}

+  #

+  #       {{> @deleteIcon}}
+  #

+  #
+  #       #{data.name}
+  #       #{data.version}
+  #
+  #   """


add css class to outer mask

add css class to outer mask

--- maskew.coffee
+++ maskew.coffee
@@ -16,7 +16,6 @@

 testProp = (prop) ->
   return prop if testEl.style[prop]?
-
   capProp = prop.charAt(0).toUpperCase() + prop.slice 1
   for prefix in prefixList
     return key if testEl.style[(key = prefix + capProp)]?
@@ -46,6 +45,7 @@
     @_options.touch or= false
     @_options.anchor or= 'top'
     @_options.showElement or= false
+    @_options.className or= 'maskew'

     contents = @_el.cloneNode true
     elStyle = window.getComputedStyle @_el
@@ -81,6 +81,7 @@
     @_holder.appendChild @_el
     @_innerMask.appendChild @_holder
     @_outerMask.appendChild @_innerMask
+    @_outerMask.className = @_options.className

     @setTouch true if @_options.touch
     @skew @angle


webapp: show firefox warning on all platforms

--- src/smc-webapp/last.coffee
+++ src/smc-webapp/last.coffee
@@ -115,7 +115,8 @@
         MathJax.Hub?.Queue([mathjax_finish_startup])
     document.getElementsByTagName("head")[0].appendChild(mjscript)

-    if $.browser.firefox and window.navigator.platform != "MacIntel"
+    # hsy: showing firefox warning on all platforms (no idea why MacIntel was excluded)
+    if $.browser.firefox    # and window.navigator.platform != "MacIntel"
         # See https://github.com/sagemathinc/smc/issues/1314
         {alert_message} = require('./alerts')
         alert_message


conversation: avoid repeating the same username

[hacky way]

--- src/ui/views/applayout.coffee
+++ src/ui/views/applayout.coffee
@@ -51,24 +51,28 @@
   text = text.join " "
   return text

+lastuser = null
 messagesView = (model) ->
   messages = model.messagesByConversationId[model.conversationCurrent] || []
   messages.forEach (message) ->
     div class: 'message', ->
-      div class: 'user', ->
-        chat_id = (message.sender_id || message.user_id).chat_id
-        user = model.identitiesById[chat_id].name || 'Unknown'
-      div class: 'timestamp', ->
-        today = new Date()
-        date = new Date(message.timestamp / 1000)
-        if today.toLocaleDateString() == date.toLocaleDateString()
-          date = date.toLocaleTimeString()
-        else
-          date = date.toLocaleString()
-        return date
+      chat_id = (message.sender_id || message.user_id).chat_id
+      user = model.identitiesById[chat_id].name || 'Unknown'
+      if user != lastuser
+        div class: 'user', user
+        lastuser = user

       div class: 'body', ->
         messageBodyView model, message
+        div class: 'timestamp', ->
+          today = new Date()
+          date = new Date(message.timestamp / 1000)
+          if today.toLocaleDateString() == date.toLocaleDateString()
+            date = date.toLocaleTimeString()
+          else
+            date = date.toLocaleString()
+          return date
+

 messageInput = ->
   textarea rows:1,


Don't use deprecated APIs

Don't use deprecated APIs

--- spec/archive-editor-view-spec.coffee
+++ spec/archive-editor-view-spec.coffee
@@ -68,11 +68,11 @@
         archiveView.find('.file:eq(2)').trigger 'click'

       waitsFor ->
-        atom.workspaceView.getActivePane().getItems().length > 1
+        atom.workspace.getActivePane().getItems().length > 1

       runs ->
         expect(atom.workspaceView.getActiveView().getText()).toBe 'hey there\n'
-        expect(atom.workspaceView.getActivePaneItem().getTitle()).toBe 'fa.txt'
+        expect(atom.workspace.getActivePaneItem().getTitle()).toBe 'fa.txt'

   describe "when core:confirm is triggered", ->
     it "copies the contents to a temp file and opens it in a new editor", ->
@@ -83,11 +83,11 @@
         archiveView.find('.file:eq(0)').trigger 'core:confirm'

       waitsFor ->
-        atom.workspaceView.getActivePane().getItems().length > 1
+        atom.workspace.getActivePane().getItems().length > 1

       runs ->
         expect(atom.workspaceView.getActiveView().getText()).toBe ''
-        expect(atom.workspaceView.getActivePaneItem().getTitle()).toBe 'f1.txt'
+        expect(atom.workspace.getActivePaneItem().getTitle()).toBe 'f1.txt'

   describe "when the file is removed", ->
     it "destroys the view", ->
@@ -95,9 +95,9 @@
         archiveView.find('.entry').length > 0

       runs ->
-        expect(atom.workspaceView.getActivePane().getItems().length).toBe 1
-        atom.workspaceView.getActivePaneItem().file.emit('removed')
-        expect(atom.workspaceView.getActivePaneItem()).toBeUndefined()
+        expect(atom.workspace.getActivePane().getItems().length).toBe 1
+        atom.workspace.getActivePaneItem().file.emit('removed')
+        expect(atom.workspace.getActivePaneItem()).toBeUndefined()

   describe "when the file is modified", ->
     it "refreshes the view", ->
@@ -106,5 +106,5 @@

       runs ->
         spyOn(archiveView, 'refresh')
-        atom.workspaceView.getActivePaneItem().file.emit('contents-changed')
+        atom.workspace.getActivePaneItem().file.emit('contents-changed')
         expect(archiveView.refresh).toHaveBeenCalled()


Trigger refresh event handler when refreshing whole body

--- src/vtree_src/launcher.coffee
+++ src/vtree_src/launcher.coffee
@@ -32,7 +32,7 @@
     return if @isRefreshEventInitialized()
     @setRefreshEventAsInitialized()

-    $('body').on 'refresh', '*' , (e) =>
+    refreshHandler = (e) =>
       e.stopPropagation()

       # finding closest element with node (it can be actually e.currentTarget)
@@ -48,6 +48,9 @@

       node = @treeManager.nodesCache.getById(nodeId)
       @treeManager.refresh(node)
+
+    $('body').on 'refresh', refreshHandler
+    $('body').on 'refresh', '*' , refreshHandler

   @createViewsTree: ->
     @treeManager.createTree()


Fix conflicting colors for state segmentation

--- static/coffee/v2_polls.coffee
+++ static/coffee/v2_polls.coffee
@@ -115,11 +115,13 @@
   $.getJSON(url, (results) ->
     total = 0
     series = []
-
-    for segment, i in results
+
+    i = 0
+    for segment in results
       data = segment['data']
       if segmentType == "location" and not states[segment.osm_id]
         continue
+      i++
       cleanedData = []
       for key, value of data
         cleanedData.push([Date.parse(key), value])


Fixed old null breakpoint reference

Fixed old null breakpoint reference

--- src/scripts/views/data/SourceFile.coffee
+++ src/scripts/views/data/SourceFile.coffee
@@ -63,7 +63,7 @@
     # Existing breakpoint?
     if @breakpoints[cnt]?
       @breakpoints[cnt].remove window.hoocsd.messaging
-      @breakpoints[cnt] = null
+      delete @breakpoints[cnt]
     else
       window.hoocsd.messaging.sendMessage
         type: "js.setBreakpointByUrl"


Add missing require

Add missing require

--- src/command.coffee
+++ src/command.coffee
@@ -1,5 +1,6 @@
 child_process = require 'child_process'
 _ = require 'underscore-plus'
+config = require './apm'

 module.exports =
 class Command


Remove try/catch so we're notified of exceptions

--- src/trix/models/selection_manager.coffee
+++ src/trix/models/selection_manager.coffee
@@ -99,12 +99,8 @@
         @findContainerAndOffsetForLocation(locationRange.end)

     range = document.createRange()
-    try
-      range.setStart(rangeStart...)
-      range.setEnd(rangeEnd...)
-    catch err
-      range.setStart(@element, 0)
-      range.setEnd(@element, 0)
+    range.setStart(rangeStart...)
+    range.setEnd(rangeEnd...)

     selection = window.getSelection()
     selection.removeAllRanges()


admins can edit (and delete) teams

admins can edit (and delete) teams

--- nodeko.coffee
+++ nodeko.coffee
@@ -61,7 +61,8 @@
               fn()
           canEditTeam: (team) ->
             req.cookies.teamauthkey is team.authKey() or
-              team.hasMember(@currentPerson)
+              team.hasMember(@currentPerson) or
+              (@currentPerson? and @currentPerson.admin())
           ensurePermitted: (other, fn) ->
             permitted = (@currentPerson? and @currentPerson.admin() or
               other.hasMember? and @canEditTeam(other) or


gross: if you are trying to rsvp for joyent, and the competition is full, login so you can edit your team

--- nodeko.coffee
+++ nodeko.coffee
@@ -97,10 +97,15 @@
     @next()

 get '/register', ->
-  if @currentPerson?
-    @redirectToTeam @currentPerson, '/teams/new'
-  else
-    @redirect '/teams/new'
+  Team.all (error, teams) =>
+    altPath = if teams.length >= 222
+      "/login?return_to=#{@req.url}"
+    else
+      'teams/new'
+    if @currentPerson?
+      @redirectToTeam @currentPerson, '/teams/new'
+    else
+      @redirect altPath

 # list teams
 get '/teams', ->


Displayed post is now always in the chosen tag group.

--- client/coffeescript/templates/post_wrapper.coffee
+++ client/coffeescript/templates/post_wrapper.coffee
@@ -16,6 +16,11 @@
     for post in Posts.find( 'parent_id': @parent_id ).fetch()
       if selected_group == 'all' || selected_group of post.tags
         posts.push(post._id)
+
+    #If the currently showing post is not in this group, show one that is
+    if posts.length > 0 && not (Session.get("reply_#{@parent_id}") in posts)
+      Session.set("reply_#{@parent_id}", posts[0])
+
     return posts

   reply: ->


Another TODO comment

Another TODO comment

--- common/lib/xmodule/xmodule/js/src/jsloader.coffee
+++ common/lib/xmodule/xmodule/js/src/jsloader.coffee
@@ -18,6 +18,7 @@
       callback()
       return

+    # TODO: Verify the execution order of multiple placeholders
     completed      = (false for i in [1..placeholders.length])
     callbackCalled = false


move small method to one line

move small method to one line

--- core/app/backbone/views/fact_relation_view.coffee
+++ core/app/backbone/views/fact_relation_view.coffee
@@ -9,8 +9,7 @@
     userRegion: 'henk'
     popoverRegion: '.popover_region'

-  highlight: ->
-    @$el.css background: '#ffffe1'
+  highlight: -> @$el.css background: '#ffffe1'

   onRender: ->
     @factRelationRegion.show new FactRelationView model: @model


minor api change

minor api change

--- routes.coffee
+++ routes.coffee
@@ -53,7 +53,7 @@
         @response.end "#{@params.name} instance is scheduled for destruction."
     @route 'apiListInstances',
       where: 'server'
-      path: '/api/v1/:app/:version/instances'
+      path: '/api/v1/instances/:app/:version'
       action: ->
         check(@params.app, String)
         check(@params.version, String)


Aggregate: Rename plain object member variable from "root" to "instance" in order to not conflict with DDD terminology

--- src/aggregate/aggregate.coffee
+++ src/aggregate/aggregate.coffee
@@ -1,22 +1,15 @@
 class Aggregate

-  constructor: (@_context, @_eventric, @_name, Root) ->
+  constructor: (@_context, @_eventric, @_name, AggregateClass) ->
     @_domainEvents = []
-
-    if !Root
-      @root = {}
-    else
-      @root = new Root
-
-    @root.$emitDomainEvent = @emitDomainEvent
+    @instance = new AggregateClass
+    @instance.$emitDomainEvent = @emitDomainEvent

   emitDomainEvent: (domainEventName, domainEventPayload) =>
     DomainEventClass = @_context.getDomainEvent domainEventName
     if !DomainEventClass
-      err = "Tried to emitDomainEvent '#{domainEventName}' which is not defined"
-      @_eventric.log.error err
-      throw new Error err
+      throw new Error "Tried to emitDomainEvent '#{domainEventName}' which is not defined"

     aggregate =
       id: @id
@@ -26,13 +19,11 @@

     @_handleDomainEvent domainEventName, domainEvent
     @_eventric.log.debug "Created and Handled DomainEvent in Aggregate", domainEvent
-    # TODO: do a rollback if something goes wrong inside the handle function

   _handleDomainEvent: (domainEventName, domainEvent) ->
-    if @root["handle#{domainEventName}"]
-      @root["handle#{domainEventName}"] domainEvent, ->
-
+    if @instance["handle#{domainEventName}"]
+      @instance["handle#{domainEventName}"] domainEvent
     else
       @_eventric.log.debug "Tried to handle the DomainEvent '#{domainEventName}' without a matching handle method"


Projects: Created WorkspaceBundle with Ace, Terminal, Viewer and Workspace.

--- projects.coffee
+++ projects.coffee
@@ -157,8 +157,13 @@
     style         : "website/css/koding.#{KODING_VERSION}.css"
     script        : "website/js/koding.#{KODING_VERSION}.js"

+  WorkspaceBundle :
+    projects      : ['Ace', 'Terminal', 'Viewer', 'Workspace']
+    style         : "website/css/workspace.#{KODING_VERSION}.css"
+    script        : "website/js/workspace.#{KODING_VERSION}.js"
+
   TeamworkBundle  :
-    projects      : ['Workspace', 'CollaborativeWorkspace', 'Teamwork']
+    projects      : ['WorkspaceBundle', 'CollaborativeWorkspace', 'Teamwork']
     style         : "website/css/teamwork.#{KODING_VERSION}.css"
     script        : "website/js/teamwork.#{KODING_VERSION}.js"


Fix 404/notFound templates after IR upgrade. See #96.

- https://github.com/EventedMind/iron-router/issues/745

--- router.coffee
+++ router.coffee
@@ -1,7 +1,7 @@
 if Meteor.isClient
   Router.onBeforeAction 'loading'
   Router.onBeforeAction (pause) ->
-    if @_dataValue is null or typeof @_dataValue is 'undefined'
+    if not @_hasData()
       return

     Router.hooks.dataNotFound.call @, pause


Ship explodes when it bumps into something


Improve error message formatting

Improve error message formatting

--- src/assembler/error-formatter.coffee
+++ src/assembler/error-formatter.coffee
@@ -23,8 +23,14 @@

 formatters.set 'bad-instruction', ({ token }) ->
+	list = findSimilar instructionTypes, token.value
+	help = if list.length > 0
+		"similarly named instructions: #{list.join ', '}"
+	else
+		"no similarly named instructions found"
+
 	message: "Unrecognised instruction #{token.value}"
-	help: "similarly named instruction: #{findSimilar instructionTypes, token.value}"
+	help: help
 	coords: token.coords

@@ -52,8 +58,14 @@

 formatters.set 'undeclared-label', ({ token, labels }) ->
+	list = findSimilar labels, token.value
+	help = if list.length > 0
+		"similarly named labels: #{list.join ', '}"
+	else
+		"no similarly named labels found"
+
 	message: "Label #{token.value} has not been declared"
-	help: "similarly named labels: #{findSimilarLabels labels, token.value}"
+	help: help
 	coords: token.coords


Bare, and Closure

Bare, and Closure

Made the coffee compiles bare by default, although i think this is a
poor design. For now, we are doing this for compatibility with kdc.

In the long run it might be nice to add an app specific object into
the main closure for each app.. though with automatic ghosting from
coffee-script this may be challenging.

Either way, we're compatible for now, so there's that.

--- bin/kdc-plus.coffee
+++ bin/kdc-plus.coffee
@@ -84,7 +84,7 @@
     # Add our coffee transform
     if opts.coffee
       loader.transform (file, ext) ->
-        if ext is '.coffee' then return new CoffeeTransform()
+        if ext is '.coffee' then return new CoffeeTransform bare: true

     if opts.transform?
       # Currently we only support a single user transform, so until we figure
@@ -117,7 +117,15 @@

     # Now that we've declared everything, pipe our loader to our "outer",
     # which is the final destination for our stream.
-    loader.pipe outer
+
+    # First, write our closure start to the outer
+    outer.write "/* Compiled by kdc-plus v#{opts.version} */\n(function(){\n"
+
+    # On data, write it to our outer
+    loader.on 'data', (chunk) -> outer.write chunk
+
+    # And finally, when the data ends, close our app closure
+    loader.on 'end', -> outer.write "\n})()"


'since' order use created instead of updated (fix #429)

--- client/since.coffee
+++ client/since.coffee
@@ -10,7 +10,7 @@
     deleted: false
     private: $ne: true
   ,
-    sort: [['updated', 'asc']]
+    sort: [['created', 'asc']]

 topMessagesSince = (group, since) ->
   #console.log parseSince since


proxy: don't log browser address if unknown

--- proxy.coffee
+++ proxy.coffee
@@ -93,7 +93,7 @@
     return

   (socket) -> # this function is the result from calling Proxy()
-    log 'browser connected from ' + socket?.request?.connection?.remoteAddress
+    log 'browser connected' + if addr = socket?.request?.connection?.remoteAddress then ' from ' + addr else ''
     sockets.push socket

     {onevent} = socket # intercept all browser-to-proxy events and log them:


Auto add function names to global scope.

--- src/cakes.coffee
+++ src/cakes.coffee
@@ -29,3 +29,6 @@

 exports.But = (not_outcome)->
 	describe(" but: #{not_outcome}", callback)
+
+# Add function names to global scope.
+(global[name] = func for name, func of module.exports)


Extracting color names into its own file.


Make sure colors don't get accidentally matched for inherited properties

--- src/color.coffee
+++ src/color.coffee
@@ -1,3 +1,5 @@
+_ = require 'underscore'
+
 COLORS =
   black: 0
   red: 1
@@ -15,10 +17,10 @@
 exports = module.exports = (string, format) ->
   original = string

-  if format.foreground and COLORS[format.foreground]
+  if format.foreground and _.has COLORS, format.foreground
     string = ansi(COLORS[format.foreground] + 30) + string

-  if format.background and COLORS[format.background]
+  if format.background and _.has COLORS, format.background
     string = ansi(COLORS[format.background] + 40) + string

   string = string + ansi(0) unless string is original


the best javascript color library

the best javascript color library

--- src/color.coffee
+++ src/color.coffee
@@ -37,7 +37,7 @@

   window.Color = (color) ->
     # HAX: checking to see if we are passing in an instance of Color
-    return Color(arguments[0].channels()) if arguments[0].channels
+    return Color(arguments[0].channels()) if arguments[0]?.channels

     parsedColor = null


Teambuilder: Fixed bug where clicking a move further down wouldn't register

--- client/app/js/views/teambuilder_view.coffee
+++ client/app/js/views/teambuilder_view.coffee
@@ -304,8 +304,11 @@
   blurMoves: (e) =>
     $input = $(e.currentTarget)
     if @_preventBlur
+      $view = @getPokemonView()
+      previousScrollPosition = $view.scrollTop()
       $input.focus()
       e.preventDefault()
+      $view.scrollTop(previousScrollPosition) # prevent scroll from refocus
       return

     $selectedMove = @$selectedMove()


Distinguish between embedded and regular versions.

--- src/embed.coffee
+++ src/embed.coffee
@@ -170,6 +170,8 @@
         navigation: '#navigation-region'
         map: '#app-container'

+    window.isEmbedded = true
+
     # We wait for p13n/i18next to finish loading before firing up the UI
     $.when(p13n.deferred).done ->
         app.start()


Display ignored pool types with strikethrough

--- src/components/qa/exercise-card.cjsx
+++ src/components/qa/exercise-card.cjsx
@@ -1,6 +1,6 @@
 _ = require 'underscore'
 React = require 'react'
-
+classnames = require 'classnames'
 {ExerciseStore} = require '../../flux/exercise'
 String = require '../../helpers/string'
 ExerciseCard = require '../exercise-card'
@@ -9,11 +9,13 @@

   propTypes:
     exercise: React.PropTypes.object.isRequired
+    ignoredPoolTypes: React.PropTypes.object.isRequired

   renderHeader: ->

       {for pool in ExerciseStore.poolTypes(@props.exercise)
-        {String.titleize(pool)}}
+        className = classnames(pool, {'is-ignored': @props.ignoredPoolTypes[pool]})
+        {String.titleize(pool)}}

   render: ->


popover arrow offset fix

popover arrow offset fix

--- src/components/scores/late-work.cjsx
+++ src/components/scores/late-work.cjsx
@@ -91,6 +91,7 @@
     <BS.Popover
       {...@props}
       show={@state.isShown}
+      arrowOffsetTop={95}
       title={content.get('title')}
       id="late-work-info-popover-#{content.task.id}"
       className={content.className()}>


fix(contacts): Allow for contacts with no emails (Sentry #11535)

--- src/flux/stores/contact-store.coffee
+++ src/flux/stores/contact-store.coffee
@@ -167,6 +167,7 @@
     # remove query results that are duplicates, prefering ones that have names
     uniq = {}
     for contact in contacts
+      continue unless contact.email
       key = contact.email.toLowerCase()
       existing = uniq[key]
       if not existing or (not existing.name or existing.name is existing.email)


More specific errors for login

More specific errors for login

--- src/index.coffee
+++ src/index.coffee
@@ -56,7 +56,7 @@
     else if response.statusCode == 304
       callback null, entry.body
     else if response.statusCode != 200
-      callback new Error("Bad status code #{response.statusCode} getting posts: #{body}")
+      callback new Error("Bad status code #{response.statusCode} getting #{url}: #{body}")
     else
       key = "#{token}|#{url}"
       entry =
@@ -323,7 +323,7 @@
       if err
         callback err
       else if response.statusCode != 200
-        callback new Error("Bad status code #{response.statusCode}: #{body}")
+        callback new Error("Bad status code #{response.statusCode} getting #{url}: #{body}")
       else
         results = JSON.parse(body)
         page = _.pluck results.following, "id"
@@ -375,7 +375,7 @@
         if err
           callback err
         else if response.statusCode != 200
-          err = new Error("Bad status code #{response.statusCode}: #{body}")
+          err = new Error("Bad status code #{response.statusCode} posting to #{url}: #{body}")
           callback err
         else
           results = JSON.parse(body)
@@ -385,11 +385,12 @@
           last = now
           callback null
     (callback) ->
-      web.get 'https://api.producthunt.com/v1/me', {Authorization: "Bearer #{token}"}, (err, response, body) ->
+      url = 'https://api.producthunt.com/v1/me'
+      web.get url, {Authorization: "Bearer #{token}"}, (err, response, body) ->
         if err
           callback err
         else if response.statusCode != 200
-          callback new Error("Bad status code #{response.statusCode}: #{body}")
+          callback new Error("Bad status code #{response.statusCode} getting #{url}: #{body}")
         else
           results = JSON.parse(body)
           user = results.user


I'm really quite suspicuous of heroku right now

--- src/index.coffee
+++ src/index.coffee
@@ -51,9 +51,12 @@
   .on 'connection', (socket) ->
     online_data = onlineData() #don't need to recompute this for the next few emissions
     gdata.clients = online_data.clients
-    socket.emit 'online', online_data
-    socket.emit 'class', {cls_num:1}
-    # {class_num:1, class_text:"The fibonacci sequence adds the last two numbers in the sequence to find the next, e.g. 0,1,1,2,3,5. Find the 200th number"}
+    cls_data = onlineData()
+    cls_data.cls-num = 1
+    cls_data.cls-text = 'The Fibonacci sequence is formed by adding the preceding two terms to find the next,
+                         beginning with 0 and 1, e.g. 0,1,1,2,3,5,8... Find the 200th term of the sequence'
+    socket.emit 'online',
+
     socket.broadcast.emit 'online', online_data
     io.of('/teacher').emit 'render', online_data


testing

testing

--- src/index.coffee
+++ src/index.coffee
@@ -35,9 +35,9 @@
       console.log "curentMeeting found : ", currentMeeting
       meetingStartTime = _.get event, 'genisys.currentMeeting.startTime'
       noShowLimit = moment(meetingStartTime).utc().add(@limitMinutes, 'minute')
-      if moment().isBefore(noShowLimit)
+      if (moment().isBefore(noShowLimit) && moment(@validUntil).isBefore(@validUntil))
         @validUntil = noShowLimit
-        console.log "Initializing valid until for: #{@validUntil.toISOString()}"
+        console.log "Initializing valid until to no show limit as : #{@validUntil.toISOString()}"

       if (moment(@validUntil).isBefore(moment().utc()) && moment().utc().isAfter(noShowLimit))
         meetingId = _.get currentMeeting, 'meetingId'


testing

testing

--- src/index.coffee
+++ src/index.coffee
@@ -28,7 +28,8 @@

   checkMeeting:(event) =>
     currentMeeting = _.get event, 'genisys.currentMeeting'
-    meetingStartTime = moment(_.get event, 'genisys.currentMeeting.startTime').utc()
+    meetingStartTime = _.get event, 'genisys.currentMeeting.startTime'
+    meetingStartTime = moment(meetingStartTime).utc()
     if currentMeeting?
       console.log "====================================="
       console.log "curentMeeting found : ", currentMeeting
@@ -66,7 +67,7 @@

   handleReady: () =>
     @motion = new five.Motion 'P1-13'
-    @motion.on 'calibrated', () => debug 'Motion Sensor Calibrated'
+    @motion.on 'calibrated', () => console.log 'Motion Sensor Calibrated'
     @motion.on 'change', @updateValidUntil

   updateValidUntil: () =>


update input to use new context helper

update input to use new context helper

--- src/input.coffee
+++ src/input.coffee
@@ -35,10 +35,8 @@
     fn 'live', 'Updates when the property changes', (property, fn) ->
       $output = $ "<div class='live'/>"
       @nested_item $output, ->
-        context = @
         unless property.onValue?
           property = Bacon.combineTemplate property
-        property.onValue (v) ->
+        property.onValue @keeping_context (v) ->
           $output.empty()
-          context.in_context context, ->
-            fn v
+          fn v


added in structure from previous chat app for typing message. css pending

--- src/javascript/chat_room_view.coffee
+++ src/javascript/chat_room_view.coffee
@@ -66,7 +66,8 @@
     if convo_key is @convo.message_filter
       if bool
         if @$discussion.find('.incoming').length is 0
-          ##typing_notification = TYPING NOTIFICATION LAYOUT GOES HERE!!
+          typing_notification = "

#{typist.display_name} is typing...

"
+          that.$('.discussion').append(typingNotification);
           @$discussion.append(typing_notification)
           @scrollToLastMessage()
       else


limit

limit

--- query_runner.coffee
+++ query_runner.coffee
@@ -9,7 +9,9 @@

   run: ->

-    [txt, path, ignore, fields] = @query.match(/(([\w]+\/){0,2})(.+)/)
+    [txt, limit, path, ignore, fields] = @query.match(/([0-9]+ )?(([\w]+\/){0,2})(.+)/)
+
+    limit = +(limit.trim()) if limit

     pathParts = path.split("/").filter((p) -> p)

@@ -43,6 +45,7 @@
       fields: fields
       filters: filters
       sorts: sorts
+      limit: limit

     error = (response) =>
       if response.error


fix (Project) Case insensisteve

fix (Project) Case insensisteve


reverting Scope#temporary simplification.

--- scope.coffee
+++ scope.coffee
@@ -55,7 +55,10 @@

   # Generate a temporary variable name at the given index.
   temporary: (name, index) ->
-    '_' + name + if index > 1 then index else ''
+    if name.length > 1
+      '_' + name + if index > 1 then index else ''
+    else
+      '_' + (index + parseInt name, 36).toString(36).replace /\d/g, 'a'

   # Gets the type of a variable.
   type: (name) ->


Hack: switched to use of JS templates instead of simply HTML

--- lib/assets/javascripts/_request_manager.js.coffee
+++ lib/assets/javascripts/_request_manager.js.coffee
@@ -24,16 +24,7 @@
         'X-Wiselinks': state.data.render
         'X-Wiselinks-Referer': state.data.referer

-      dataType: "html"
-    ).done(
-      (data, status, xhr) ->
-        self._html_loaded($target, data, status, xhr)
-    ).fail(
-      (xhr, status, error) ->
-        self._fail($target, status, state, error, xhr.status, xhr.responseText)
-    ).always(
-      (data_or_xhr, status, xhr_or_error)->
-        self._always($target, status, state)
+      dataType: "js"
     )

   _normalize: (url) ->


Fixed individual file removal.

Fixed individual file removal.

--- sample.coffee
+++ sample.coffee
@@ -190,9 +190,9 @@
       'click .del-file': (e, t) ->
          # Management of thumbnails happens on the server!
          if this.metadata.thumbOf?
-            t.data.remove this.metadata.thumbOf
-         else
-            t.data.remove this._id
+            dataLookup[t.data.collection].remove this.metadata.thumbOf
+         else
+            dataLookup[t.data.collection].remove this._id

    Template.gallery.helpers
       dataEntries: () ->


Add some commentary.

Add some commentary.

--- scope.coffee
+++ scope.coffee
@@ -30,6 +30,12 @@
     else
       @positions[name] = @variables.push({name, type}) - 1

+  # When `super somarg` is called, we need to find the name of the current
+  # method we're in, so we know how to address the same method of the
+  # super class.  This can get complicated if super is being called
+  # from a closure.  getMethodRecurse() will walk up the scope
+  # tree until it finds the first method object that has a name filled
+  # in.  It will return an empty method object if none was found
   getMethodRecurse: ->
     if @method?.name? then @method
     else if @parent then @parent.getMethodRecurse()


count instead of log

count instead of log

--- scrap.coffee
+++ scrap.coffee
@@ -10,8 +10,17 @@
 done = (err) ->
 	console.log 'Done!'

-#sitemaps.parseRobot 'http://amazon.com/robots.txt', console.log, done
-sitemaps.parseRobot 'http://yahoo.com/robots.txt', console.log, done
+i = 0
+increaseI = (url) ->
+	i++
+
+printI = () ->
+	console.log i
+
+setInterval printI, 10000
+
+sitemaps.parseRobot 'http://amazon.com/robots.txt', increaseI, done
+#sitemaps.parseRobot 'http://yahoo.com/robots.txt', console.log, done

 #sitemaps.parseSitemap url, console.log, done


Point copy constructor

Point copy constructor

--- src/point.coffee
+++ src/point.coffee
@@ -29,7 +29,6 @@
   Point = (x, y) ->
     if typeof x is "object"

-    else

     __proto__: Point::


Populate @commands as we go, instead of when helpCommands are requested

--- src/robot.coffee
+++ src/robot.coffee
@@ -8,6 +8,8 @@
 Response                                                = require './response'
 {Listener,TextListener}                                 = require './listener'
 {TextMessage,EnterMessage,LeaveMessage,CatchAllMessage} = require './message'
+
+inspect = require('util').inspect

 HUBOT_DEFAULT_ADAPTERS = [ 'campfire', 'shell' ]

@@ -25,6 +27,7 @@
     @alias        = false
     @adapter      = null
     @Response     = Response
+    @commands     = []
     @listeners    = []
     @loadPaths    = []
     @enableSlash  = false
@@ -234,18 +237,7 @@
   #
   # Returns an Array of help commands for running scripts.
   helpCommands: ->
-    unless @commands
-      @logger.debug "helpCommands: haven't populated yet"
-      @commands = []
-
-      for script, scriptDocumentation of @documentation
-        @logger.debug "helpCommands: checking #{script} for commands"
-        if scriptDocumentation.commands
-          for command in scriptDocumentation.commands
-            @logger.debug "helpCommands: adding '#{command}' from #{script}"
-            @commands.push(command)
-    @commands
-
+    @commands.sort()

   # Private: load help info from a loaded script.
   #
@@ -281,13 +273,10 @@
           # lines in a section _do_ have leading whitespace
           else
             if currentSection
-              @logger.debug "parseHelp(#{scriptName}) adding #{cleanedLine.trim()} to #{currentSection}"
+              @logger.debug "parseHelp(#{scriptName}) adding '#{cleanedLine.trim()}' to #{currentSection}"
               scriptDocumentation[currentSection].push cleanedLine.trim()
-
-    if not scriptDocumentation.commands
-      scriptDocumentation.commands = []
-
-    scriptDocumentation.commands = scriptDocumentation.commands.sort()
+              if currentSection == 'commands'
+                @commands.push cleanedLine.trim()

   # Public: A helper send function which delegates to the adapter's send
   # function.


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/tween.coffee
+++ src/tween.coffee
@@ -23,4 +23,5 @@
         target: target
         start: I[property]
         easing: "linear"
-        startTime:
+        startTime: I.age
+        endTime: I.age +


Rewrite to use process.env config constants

--- routes/index.coffee
+++ routes/index.coffee
@@ -3,8 +3,6 @@
 express = require 'express'
 request = require 'request'
 qs = require 'querystring'
-
-secrets = require '../secrets'

 router = express.Router()

@@ -18,7 +16,7 @@
   uri = 'https://github.com/login/oauth/authorize'
   params =
     scope: 'repo'
-    client_id: secrets.GITHUB_CLIENT_ID
+    client_id: process.env.GITHUB_CLIENT_ID

   res.redirect "#{uri}?#{qs.stringify params}"

@@ -30,8 +28,8 @@
   uri = 'https://github.com/login/oauth/access_token'

   body =
-    client_id: secrets.GITHUB_CLIENT_ID
-    client_secret: secrets.GITHUB_CLIENT_SECRET
+    client_id: process.env.GITHUB_CLIENT_ID
+    client_secret: process.env.GITHUB_CLIENT_SECRET
     code: session_code
     accept: 'application/json'

@@ -47,7 +45,7 @@

 router.post '/postreceive', (req, res, next) ->
   repo = req.body.repository.full_name
-  q = { access_token: secrets.GITHUB_ACCESS_TOKEN }
+  q = { access_token: process.env.GITHUB_ACCESS_TOKEN }
   uri = "https://api.github.com/repos/#{repo}/issues?#{qs.stringify(q)}"

   requestsToHandle = 0


Closes #135: Warning when using money filters

--- src/utils.coffee
+++ src/utils.coffee
@@ -1,12 +1,18 @@
 # CartJS.Utils
 # Utility methods.
 # ----------------
+
+FORMAT_MONEY_WARNING = 'A money formatting filter was used, but Shopify.formatMoney is not available. See the note "Dependency when formatting monetary values" on this page: https://cartjs.org/pages/guide#getting-started-setup.'

 CartJS.Utils =

   # Log an informational message to the console iff debug mode is on and a console is available.
   log: () ->
     CartJS.Utils.console(console.log, arguments)
+
+  # Log a warning message to the console iff debug mode is on and a console is available.
+  warn: () ->
+    CartJS.Utils.console(console.warn, arguments)

   # Log an error message to the console iff debug mode is on and a console is available.
   error: () ->
@@ -90,7 +96,11 @@
         format = window.Currency.moneyFormats[currency][formatName]

     # Render the formatted amount using the Shopify formatter if available, else just the value.
-    if window.Shopify?.formatMoney? then Shopify.formatMoney(value, format) else value
+    if window.Shopify?.formatMoney?
+      Shopify.formatMoney(value, format)
+    else
+      CartJS.Utils.warn(FORMAT_MONEY_WARNING)
+      value

   # Return a resized image URL using Shopify's getSizedImageUrl if available.
   #


Fixed setStyle

Fixed setStyle

--- src/utils.coffee
+++ src/utils.coffee
@@ -53,10 +53,10 @@
     style.getPropertyValue property

 setStyle = (element, params) ->
-  style = window.getComputedStyle element
-  for own property, value of params
-    style.setProperty property, value
-  return
+  element.style[toCamelCase property] = value for own property, value of params
+
+toCamelCase = (str) ->
+  str.replace /-([a-z])/g, (str) -> str[1].toUpperCase()

 offsetWidth = (element) ->
   element.offsetWidth
@@ -118,4 +118,4 @@
       else
         shift = "translateX(#{shift}px)"

-      element.style[property] = shift+      element.style[property] = shift


Fixed slide shift size in IE. #27

Fixed slide shift size in IE. #27

--- src/utils.coffee
+++ src/utils.coffee
@@ -109,7 +109,7 @@
     needResize = false

     style = getStyle slide
-    shift = toInt style 'width'
+    shift = toInt slide.offsetWidth

     h = toInt(style 'height') -
         toInt(style 'padding-top') -


Explicitely send json error reports as JSON.

--- src/zappa.coffee
+++ src/zappa.coffee
@@ -649,7 +649,7 @@

         if @session.__socket[channel_name]?
           # Client (or hijacker) trying to re-key.
-          @send error:'Channel already assigned', channel_name: channel_name
+          @json error:'Channel already assigned', channel_name: channel_name
         else
           key = uuid.v4() # used for socket 'authorization'

@@ -666,9 +666,9 @@
           io_client.set socketio_key, io_data

           # Let the client know which key to use.
-          @send channel_name: channel_name, key: key
+          @json channel_name: channel_name, key: key
       else
-        @send error:'No session'
+        @json error:'No session'
       return

   context


Allow middleware when routing using key-value syntax

Added support for using arrays as values when using the object notation for routing.

Example:
    @get '/': [ authenticate, ->
      @render 'index'
    ]

--- src/zappa.coffee
+++ src/zappa.coffee
@@ -184,7 +184,16 @@
             handler: args[arity-1]
         else
           for k, v of arguments[0]
-            route verb: verb, path: k, handler: v
+            # Apply middleware if value is array
+            if v instanceof Array
+              route
+                verb: verb
+                path: k
+                middleware: flatten v[0...v.length-1]
+                handler: v[v.length-1]
+
+            else
+              route verb: verb, path: k, handler: v
         return

   context.client = (obj) ->


fixed redis host config

fixed redis host config

--- server.coffee
+++ server.coffee
@@ -22,7 +22,7 @@
   app.use express.methodOverride()
   app.use assets( src:'assets' )
   app.use express.session({
-    store:  new RedisStore(config.redis_opts),
+    store:  new RedisStore(config.redis),
     secret: config.session_key,
     cookie: {
       secure: false,