Fixes #1813

Fixes #1813

--- rewriter.coffee
+++ rewriter.coffee
@@ -155,13 +155,13 @@
     condition = (token, i) ->
       [tag] = token
       return yes if not seenSingle and token.fromThen
-      seenSingle  = yes if tag in ['IF', 'ELSE', 'CATCH', '->', '=>']
+      seenSingle  = yes if tag in ['IF', 'ELSE', 'CATCH', '->', '=>', 'CLASS']
       seenControl = yes if tag in ['IF', 'ELSE', 'SWITCH', 'TRY', '=']
       return yes if tag in ['.', '?.', '::'] and @tag(i - 1) is 'OUTDENT'
       not token.generated and @tag(i - 1) isnt ',' and (tag in IMPLICIT_END or
         (tag is 'INDENT' and not seenControl)) and
         (tag isnt 'INDENT' or
-          (@tag(i - 2) isnt 'CLASS' and @tag(i - 1) not in IMPLICIT_BLOCK and
+          (@tag(i - 2) not in ['CLASS', 'EXTENDS'] and @tag(i - 1) not in IMPLICIT_BLOCK and
           not ((post = @tokens[i + 1]) and post.generated and post[0] is '{')))

     action = (token, i) ->


don't modify url when using back/forward buttons

--- manager/app/assets/javascripts/stark/stark.js.coffee
+++ manager/app/assets/javascripts/stark/stark.js.coffee
@@ -81,6 +81,7 @@
     state = new @states[state_name]()
     state.app = @
     state.bind_app_events()
+    state.params = state_data.params if state_data.params?

     @log "got state_data", state_data

@@ -131,10 +132,9 @@
       state._views.push view
     , @ # context for _.each

-
-    # TODO update URL from state
-    if @current_state? && state.url?
+    if @current_state? && state.url? && (!state.params? || state.params.changeURL == true)
       # there was a previous state, update browser url
+      # does not fire when using back/forward buttons as params.changeURL will be false
       @router.changeURL state.create_url()

     state.activate()


There were four pairs of consecutive linespaces; make each instance a single linespace instead for stylistic consistency.

--- src/Bacon.coffee
+++ src/Bacon.coffee
@@ -70,7 +70,6 @@
     promise.then(handler, (e) -> handler(new Error(e)))
     -> promise.abort?() if abort
   , ((value) -> [value, end()]))
-

 Bacon.noMore = ["<no-more>"]

@@ -261,7 +260,6 @@
       f(ctxStack, values)
     rootContext
   withDescription(Bacon, "combineTemplate", template, Bacon.combineAsArray(streams).map(combinator))
-

 Bacon.retry = (options) ->
   throw new Exception("'source' option has to be a function") unless isFunction(options.source)
@@ -674,7 +672,6 @@
       else if (delay != undefined)
         buffer.schedule()
     withDescription(this, "bufferWithTimeOrCount", delay, count, @buffer(delay, flushOrSchedule, flushOrSchedule))
-

   buffer: (delay, onInput = (->), onFlush = (->)) ->
     buffer = {
@@ -1402,7 +1399,6 @@
     unsub

   hasWaiters = -> waiters.length > 0
-

   { whenDoneWith, hasWaiters, inTransaction, currentEventId, wrappedSubscribe }
 )()


refactor: use withMethodCallSupport in makeFunction, reduce code size

--- src/Bacon.coffee
+++ src/Bacon.coffee
@@ -1078,25 +1078,23 @@
 assertArray = (xs) -> assert "not an array : " + xs, xs instanceof Array
 assertNoArguments = (args) -> assert "no arguments supported", args.length == 0
 assertString = (x) -> assert "not a string : " + x, typeof x == "string"
-methodCall = (obj, method, args) ->
-  assertString(method)
-  if args == undefined then args = []
-  (value) -> obj[method]((args.concat([value]))...)
 partiallyApplied = (f, applied) ->
   (args...) -> f((applied.concat(args))...)
 makeSpawner = (f) ->
     f = _.always(f) if f instanceof Observable
     assertFunction(f)
     f
-makeFunction = (f, args) ->
+makeFunction_ = withMethodCallSupport (f, args...) ->
   if isFunction f
     if args.length then partiallyApplied(f, args) else f
   else if isFieldKey(f)
     toFieldExtractor(f, args)
-  else if typeof f == "object" and args.length
-    methodCall(f, _.head(args), _.tail(args))
   else
     _.always f
+
+makeFunction = (f, args) ->
+  makeFunction_(f, args...)
+
 isFieldKey = (f) ->
   (typeof f == "string") and f.length > 1 and f.charAt(0) == "."
 Bacon.isFieldKey = isFieldKey


Updated many many snippet comment

Updated many many snippet comment

--- completions.cson
+++ completions.cson
@@ -705,7 +705,7 @@
   'definitions': {
     'comment': """
     /**
-     * \{@inheritdoc \}
+     * Defines Database fields for the Many_many bridging table
      * @var array
      */
     """


rolled back server.js-related component changes

--- component.coffee
+++ component.coffee
@@ -256,12 +256,12 @@
       commands.install.action()
       tests = discoverTests argv.staticpath
       extraHtml = getTestTemplate() + "\n" + stringBundlesAsRequirejsModule()
-      testServer.createServer(argv.staticpath,
-        config: getConfig()
-        extra: extraHtml
-        middlewares: getTestMiddleware(),
-        optimizations: argv.testoptimizations
-      ).listen(argv.port, argv.host)
+      testServer.createServer(
+          argv.staticpath,
+          getConfig(),
+          extraHtml,
+          getTestMiddleware())
+        .listen(argv.port, argv.host)
       log "serving at http://#{argv.host}:#{argv.port}"
       log "available tests:"
       listTests tests, argv.host, argv.port
@@ -383,10 +383,6 @@
       boolean: true
       describe: "just list tests\n(cmd: test)"

-    .option "testoptimizations",
-      alias: "O"
-      describe: "level of optimizations to pursue\n(cmd: test)"
-
     .option "templatepath",
       string: true
       alias: "t"


new window honors devMode and safeMode

new window honors devMode and safeMode

--- lib/tab-bar-view.coffee
+++ lib/tab-bar-view.coffee
@@ -148,7 +148,7 @@
     else if typeof item.getUri is 'function'
       itemURI = item.getUri()
     return unless itemURI?
-    atom.open({pathsToOpen: [itemURI], newWindow: true})
+    atom.open({pathsToOpen: [itemURI], newWindow: true, devMode: atom.devMode, safeMode: atom.safeMode})
     @closeTab(tab)

   splitTab: (fn) ->


reorder by affinity

reorder by affinity

--- lib/text-objects.coffee
+++ lib/text-objects.coffee
@@ -1,10 +1,9 @@
 {Range} = require 'atom'
+_    = require 'underscore-plus'
+Base = require './base'

 AllWhitespace = /^\s$/
 WholeWordRegex = /\S+/
-_ = require 'underscore-plus'
-Base = require './base'
-

 class TextObject extends Base
   constructor: (@editor, @state) ->
@@ -24,6 +23,8 @@
     @editor.setSelectedBufferRanges(@selection)
     _.times(count, -> true)

+# Word
+# -------------------------
 class SelectInsideWord extends TextObject
   select: ->
     @editor.selectWordsContainingCursors()
@@ -40,17 +41,30 @@
         selection.selectRight()
       true

+# WholeWord
+# -------------------------
 class SelectInsideWholeWord extends TextObject
   select: ->
     for selection in @editor.getSelections()
       range = selection.cursor.getCurrentWordBufferRange({wordRegex: WholeWordRegex})
       selection.setBufferRange(range)
+      true
+
+class SelectAWholeWord extends TextObject
+  select: ->
+    for selection in @editor.getSelections()
+      range = selection.cursor.getCurrentWordBufferRange({wordRegex: WholeWordRegex})
+      selection.setBufferRange(range)
+      loop
+        endPoint = selection.getBufferRange().end
+        char = @editor.getTextInRange(Range.fromPointWithDelta(endPoint, 0, 1))
+        break unless AllWhitespace.test(char)
+        selection.selectRight()
       true

 # SelectInsideQuotes and the next class defined (SelectInsideBrackets) are
 # almost-but-not-quite-repeated code. They are different because of the depth
 # checks in the bracket matcher.
-
 class SelectInsideQuotes extends TextObject
   constructor: (@editor, @char, @includeQuotes) ->

@@ -164,18 +178,8 @@
           selection.setBufferRange([start, end])
       not selection.isEmpty()

-class SelectAWholeWord extends TextObject
-  select: ->
-    for selection in @editor.getSelections()
-      range = selection.cursor.getCurrentWordBufferRange({wordRegex: WholeWordRegex})
-      selection.setBufferRange(range)
-      loop
-        endPoint = selection.getBufferRange().end
-        char = @editor.getTextInRange(Range.fromPointWithDelta(endPoint, 0, 1))
-        break unless AllWhitespace.test(char)
-        selection.selectRight()
-      true
-
+# Paragraph
+# -------------------------
 class SelectInsideParagraph extends TextObject
   constructor: (@editor, @inclusive) ->
   select: ->
@@ -197,5 +201,11 @@
         selection.selectDown()
       true

-module.exports = {TextObject, SelectInsideWord, SelectInsideWholeWord, SelectInsideQuotes,
-  SelectInsideBrackets, CurrentSelection, SelectAWord, SelectAWholeWord, SelectInsideParagraph, SelectAParagraph}
+module.exports = {
+  TextObject,
+  CurrentSelection,
+  SelectInsideWord, SelectAWord,
+  SelectInsideWholeWord, SelectAWholeWord,
+  SelectInsideQuotes, SelectInsideBrackets,
+  SelectInsideParagraph, SelectAParagraph
+}


terminal now redraws when resized

terminal now redraws when resized

--- src/Board.coffee
+++ src/Board.coffee
@@ -7,6 +7,9 @@
 	constructor: (@boardManager) ->
 		if config.border.length > 1
 			throw new Error 'The border configuration option only supports 1 character :('
+
+		process.on 'SIGWINCH', =>
+			@draw()

 	set: (parameters) ->
 		unless @data?


oops forgot nib

oops forgot nib

--- gulp/build.coffee
+++ gulp/build.coffee
@@ -4,6 +4,7 @@
 run    = require 'run-sequence'
 jade   = require 'gulp-jade'
 stylus = require 'gulp-stylus'
+nib = require 'nib'

 webpack     = require "webpack"
 browserSync = require("browser-sync").get("A")


js state machine

js state machine

--- src/State.coffee
+++ src/State.coffee
@@ -198,6 +198,7 @@
       if block then block(event)

       return event
+
     before_transition: (options, block) ->
       callback = callbacks.add('before', options, this, block)


Some comments

Some comments

--- src/stdlib/settings.coffee
+++ src/stdlib/settings.coffee
@@ -1,6 +1,18 @@
 fs = require 'fs'
 {CoffeeScript} = require 'coffee-script'

+# Settings file looks like:
+#     editor: # name of class
+#       theme: "twilight"
+#       tabSize: 2
+#       softTabs: true
+#       showInvisibles: false
+#
+#     project:
+#       ignorePattern: /x|y|z/
+#
+# Settings are applied to object x's settings variable by calling applyTo(x) on
+# instance of Settings.
 module.exports =
 class Settings
   settings: {}


make it through the messaging protocol, in writing to the thread server

--- handlers/msg.iced
+++ handlers/msg.iced
@@ -11,6 +11,7 @@
 {bufeq_secure,dict_merge,unix_time} = require('iced-utils').util
 kbmc                      = require('keybase-messenger-core')
 idcheckers                = kbmc.id.checkers
+pgpchecker                = kbmc.pgp.checker
 {Cipher}                  = kbmc

 #=============================================================================
@@ -101,14 +102,31 @@
   input_template : -> dict_merge super(), {
     chunk_zid : checkers.nnint
     msg_zid : checkers.nnint
-    data : Cipher.checker
+    ctext : Cipher.checker
   }

   #-----------------

   _handle : (cb) ->
     q = "INSERT INTO chunks (thread_id, msg_zid, chunk_zid, data) VALUES(?,?,?,FROM_BASE64(?))"
-    args = [ H(@input.i), @input.msg_zid, @input.chunk_zid, Cipher.encode_to_db(@input.data) ]
+    args = [ H(@input.i), @input.msg_zid, @input.chunk_zid, Cipher.encode_to_db(@input.ctextechunk) ]
+    await mm.db.update1 q, args, defer err
+    cb err
+
+#=============================================================================
+
+class PostSigHandler extends Base
+
+  input_template : -> dict_merge super(), {
+    sig : pgpchecker()
+    msg_zid : checkers.nnint
+  }
+
+  #-----
+
+  _handle : (cb) ->
+    q = "UPDATE messages SET sig=? WHERE thread_id=? and msg_zid=? and sender_zid=?"
+    args = [ @input.sig, H(@input.i), @input.msg_zid, @input.sender_zid ]
     await mm.db.update1 q, args, defer err
     cb err

@@ -117,6 +135,7 @@
 exports.bind_to_app = (app) ->
   PostHeaderHandler.bind app, api_route("msg/header"), POST
   PostChunkHandler.bind app, api_route("msg/chunk"), POST
+  PostSigHandler.bind app, api_route("msg/sig"), POST

 #=============================================================================


Use __dirname in path.resolve for specs

Use __dirname in path.resolve for specs

--- spec/native-compile-cache-spec.coffee
+++ spec/native-compile-cache-spec.coffee
@@ -68,12 +68,12 @@

   describe "when a previously required and cached file changes", ->
     beforeEach ->
-      fs.writeFileSync path.resolve('./spec/fixtures/native-cache/file-5'), """
+      fs.writeFileSync path.resolve(__dirname + '/fixtures/native-cache/file-5'), """
       module.exports = function () { return "file-5" }
       """

     afterEach ->
-      fs.unlinkSync path.resolve('./spec/fixtures/native-cache/file-5')
+      fs.unlinkSync path.resolve(__dirname + '/fixtures/native-cache/file-5')

     it "removes it from the store and re-inserts it with the new cache", ->
       fn5 = require('./fixtures/native-cache/file-5')


Fix tests

Fix tests

--- spec/results-model-spec.coffee
+++ spec/results-model-spec.coffee
@@ -31,7 +31,7 @@
       runs ->
         resultsModel.on 'result-added', resultAddedSpy
         resultsModel.on 'result-removed', resultRemovedSpy
-        searchPromise = resultsModel.search('items', '', ['*.js'])
+        searchPromise = resultsModel.search('items', ['*.js'], '')

       waitsForPromise ->
         searchPromise


Safer type check in array.slice

Safer type check in array.slice

--- src/array.coffee
+++ src/array.coffee
@@ -61,7 +61,8 @@
   do array.slice(0, xs).reverse

 array.slice = uncurry (start) -> (end) ->
-  if end instanceof Array
+  if typeChecks.isArray end
+    xs = end
     end.slice start
   else
     (xs) -> xs.slice start, end


Add failing spec for unquoted invalid

Add failing spec for unquoted invalid

--- spec/toggle-quotes-spec.coffee
+++ spec/toggle-quotes-spec.coffee
@@ -80,8 +80,15 @@
       describe "when it is quoted", ->
         it "toggles the quotes", ->
           editor.setGrammar(atom.syntax.selectGrammar('test.json'))
-          console.log editor.getGrammar()
           editor.setText("{'invalid': true}")
           editor.setCursorBufferPosition([0, 4])
           toggleQuotes(editor)
           expect(editor.getText()).toBe '{"invalid": true}'
+
+      describe "when it is not quoted", ->
+        it "does not toggle the quotes", ->
+          editor.setGrammar(atom.syntax.selectGrammar('test.json'))
+          editor.setText("{invalid: true}")
+          editor.setCursorBufferPosition([0, 4])
+          toggleQuotes(editor)
+          expect(editor.getText()).toBe '{invalid: true}'


git verbose

git verbose

--- src/clone.coffee
+++ src/clone.coffee
@@ -1,5 +1,5 @@
 # clone command
-git = require("git.js")(process.cwd())
+git = require("git.js")(process.cwd(), {verbose: true})

 clone = (args) ->
   # TODO support other hosts like bitbucket.org and VCS systems like mecrurial


adding hsl constructor

adding hsl constructor

--- src/color.coffee
+++ src/color.coffee
@@ -1,5 +1,6 @@
 (->
   rgbParser = /^rgba?\((\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3}),?\s*(\d?\.?\d*)?\)$/
+  hslParser = /^hsla?\((\d{1,3}),\s*(\d?\.?\d*),\s*(\d?\.?\d*),?\s*(\d?\.?\d*)?\)$/

   parseHex = (hexString) ->
     hexString = hexString.replace(/#/, '')
@@ -20,7 +21,7 @@
           if hexString.substr(6, 2).length then parseInt(hexString.substr(6, 2), 16) / 255.0 else 1.0
         ]
       else
-        undefined
+        return undefined

   parseRGB = (colorString) ->
     return undefined unless bits = rgbParser.exec(colorString)
@@ -31,6 +32,39 @@
       parseInt(bits[3])
       if bits[4]? then parseFloat(bits[4]) else 1.0
     ]
+
+  parseHSL = (colorString) ->
+    return undefined unless bits = hslParser.exec(colorString)
+
+    return hslToRgb(bits)
+
+  hslToRgb = (hsl) ->
+    h = hsl[0] / 360.0
+    s = hsl[1]
+    l = hsl[2]
+    a = (if hsl[3] then hsl[3] else 1.0)
+
+    r = g = b = null
+
+    hueToRgb = (p, q, t) ->
+      t += 1 if t < 0
+      t -= 1 if t > 1
+
+      return p + (q - p) * 6 * t if t < 1/6
+      return q if t < 1/2
+      return p + (q - p) * (2/3 - t) * 6 if t < 2/3
+      return p
+
+    if s == 0
+      r = g = b = l
+    else
+      q = (if l < 0.5 then l * (1 + s) else l + s - l * s)
+      p = 2 * l - q
+      r = hueToRgb(p, q, h + 1/3)
+      g = hueToRgb(p, q, h)
+      b = hueToRgb(p, q, h - 1/3)
+
+    return Color([(r * 0xFF).round(), (g * 0xFF).round(), (b * 0xFF).round(), a])

   normalizeKey = (key) ->
     key.toString().toLowerCase().split(' ').join('')
@@ -116,46 +150,19 @@
           other.b() == self.b() &&
           other.a() == self.a()

-      hslToRgb: (hsl) ->
-        h = hsl[0] / 360.0
-        s = hsl[1]
-        l = hsl[2]
-
-        r = g = b = null
-
-        hueToRgb = (p, q, t) ->
-          t += 1 if t < 0
-          t -= 1 if t > 1
-
-          return p + (q - p) * 6 * t if t < 1/6
-          return q if t < 1/2
-          return p + (q - p) * (2/3 - t) * 6 if t < 2/3
-          return p
-
-        if s == 0
-          r = g = b = l
-        else
-          q = (if l < 0.5 then l * (1 + s) else l + s - l * s)
-          p = 2 * l - q
-          r = hueToRgb(p, q, h + 1/3)
-          g = hueToRgb(p, q, h)
-          b = hueToRgb(p, q, h - 1/3)
-
-        return Color([(r * 0xFF).round(), (g * 0xFF).round(), (b * 0xFF).round()])
-
       lighten: (amount) ->
         hsl = self.toHsl()
         hsl[0] = hsl[0].round()
         hsl[2] = hsl[2] + amount

-        return Color(self.hslToRgb(hsl))
+        return Color(hslToRgb(hsl))

       darken: (amount) ->
         hsl = self.toHsl()
         hsl[0] = hsl[0].round()
         hsl[2] = hsl[2] - amount

-        return Color(self.hslToRgb(hsl))
+        return Color(hslToRgb(hsl))

       rgba: -> "rgba(#{self.r()}, #{self.g()}, #{self.b()}, #{self.a()})"


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/color.coffee
+++ src/color.coffee
@@ -41,17 +41,17 @@
     parsedColor = null

     if arguments.length == 2
-      if arguments[0].length == 3 && Object.prototype.toString.call(color) == '[object Array]'
+      if arguments[0].length == 3 && Object.prototype.toString.call(arguments[0]) == '[object Array]'
         parsedColor = [parseInt(arguments[0][0]), parseInt(arguments[0][1]), parseInt(arguments[0][2]), parseFloat(arguments[1])]
     else if arguments.length > 2
       alpha = if arguments[3]? then arguments[3] else 1
       parsedColor = [parseInt(arguments[0]), parseInt(arguments[1]), parseInt(arguments[2]), parseFloat(alpha)]
-    else if color.length > 2 && Object.prototype.toString.call(color) == '[object Array]'
-      alpha = if color[3]? then color[3] else 1
+    else if arguments.length > 2 && Object.prototype.toString.call(arguments[0]) == '[object Array]'
+      alpha = if arguments[3]? then arguments[3] else 1
       parsedColor = [
-        parseInt(color[0]),
-        parseInt(color[1]),
-        parseInt(color[2]),
+        parseInt(arguments[0]),
+        parseInt(arguments[1]),
+        parseInt(arguments[2]),
         parseFloat(alpha)
       ]
     else


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/color.coffee
+++ src/color.coffee
@@ -1570,8 +1570,7 @@
   ["FFFFFF", "White"]
 ]

-names.each (i, element) ->
-  debugger
+names.each (element) ->
   lookup[element[1].toLowerCase().replace(/ /g, "")] = Color(element[0])

 ###


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/color.coffee
+++ src/color.coffee
@@ -944,6 +944,27 @@
     opacify: (amount) ->
       @copy().opacify$(amount)

+    ###*
+    The calling color with its alpha increased by `amount`.
+
+

+    color = Color(0, 0, 0, 0)
+
+    color.a
+    # => 1
+
+    # We modify color in place
+    color.opacify$(0.25)
+
+    color.a
+    # => 0.25
+

+
+    @name opacify$
+    @methodOf Color#
+
+    @returns {Color} The calling color with its alpha increased by `amount`
+    ###
     opacify$: (amount) ->
       @a += amount


Code and commentary diverged in that short time.  Fix.

--- scope.coffee
+++ scope.coffee
@@ -35,7 +35,8 @@
   # 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
+  # in.  It will return the topmost method object otherwise, which may
+  # be null if this is being called from outside a function.
   getMethodRecurse: ->
     if @method?.name? then @method
     else if @parent then @parent.getMethodRecurse()


Nodejs Web Server Tests: test csrf token for reset handler

--- servers/lib/server/handlers/reset.test.coffee
+++ servers/lib/server/handlers/reset.test.coffee
@@ -1,31 +1,30 @@
-Bongo                             = require 'bongo'
-koding                            = require './../bongo'
-request                           = require 'request'
-querystring                       = require 'querystring'
-
-{ daisy }                         = Bongo
-{ expect }                        = require 'chai'
-{ generateRandomEmail
+{ daisy
+  expect
+  request
+  generateRandomEmail
   generateRandomString
-  generateRandomUsername }        = require '../../../testhelper'
+  generateRandomUsername
+  checkBongoConnectivity }        = require '../../../testhelper'
+{ testCsrfToken }                 = require '../../../testhelper/handler'
 { defaultExpiryPeriod
   generateResetRequestParams }    = require '../../../testhelper/handler/resethelper'
 { generateRegisterRequestParams } = require '../../../testhelper/handler/registerhelper'

-JUser                             = null
-JPasswordRecovery                 = null
+JUser                             = require '../../../models/user'
+JPasswordRecovery                 = require '../../../models/passwordrecovery'
+
+
+beforeTests = before (done) ->
+
+  checkBongoConnectivity done
+

 # here we have actual tests
 runTests = -> describe 'server.handlers.reset', ->

-  beforeEach (done) ->
-
-    # including models before each test case, requiring them outside of
-    # tests suite is causing undefined errors
-    { JUser
-      JPasswordRecovery } = koding.models
-
-    done()
+  it 'should fail when csrf token is invalid', (done) ->
+
+    testCsrfToken generateResetRequestParams, 'post', done

   it 'should send HTTP 404 if request method is not POST', (done) ->


#262: Fixing the :host declaration for TextMate theme conversion

--- src/text-mate-theme.coffee
+++ src/text-mate-theme.coffee
@@ -118,21 +118,21 @@

     @rulesets.push
       selector: 'atom-text-editor.is-focused .cursor,
-                 :host.is-focused .cursor'
+                 :host(.is-focused) .cursor'
       properties:
         'border-color': '@syntax-cursor-color'

     @rulesets.push
       selector: 'atom-text-editor.is-focused .selection .region,
-                 :host.is-focused .selection .region'
+                 :host(.is-focused) .selection .region'
       properties:
         'background-color': '@syntax-selection-color'

     @rulesets.push
       selector: 'atom-text-editor.is-focused .line-number.cursor-line-no-selection,
                  atom-text-editor.is-focused .line.cursor-line,
-                 :host.is-focused .line-number.cursor-line-no-selection,
-                 :host.is-focused .line.cursor-line'
+                 :host(.is-focused) .line-number.cursor-line-no-selection,
+                 :host(.is-focused) .line.cursor-line'
       properties:
         'background-color': @translateColor(settings.lineHighlight)


:pencil: returned disposable

:pencil: returned disposable

--- src/tooltip-manager.coffee
+++ src/tooltip-manager.coffee
@@ -25,6 +25,9 @@
   #   * `keyBindingTarget` An `HTMLElement` on which to look up the key binding.
   #     If this option is not supplied, the first of all matching key bindings
   #     for the given command will be rendered.
+  #
+  # Returns a {Disposable} on which `.dispose()` can be called to remove the
+  # tooltip.
   add: (target, options) ->
     requireBootstrapTooltip()


Fixes the create new btn bug

Fixes the create new btn bug

users with only the create-p permission were not able to click the "create new" button in "all channels" tab

--- packages/rocketchat-ui-sidenav/side-nav/listCombinedFlex.coffee
+++ packages/rocketchat-ui-sidenav/side-nav/listCombinedFlex.coffee
@@ -30,7 +30,7 @@
 		SideNav.closeFlex()

 	'click footer .create': ->
-		if RocketChat.authz.hasAtLeastOnePermission( 'create-c')
+		if RocketChat.authz.hasAtLeastOnePermission ['create-c', 'create-p']
 			SideNav.setFlex "createCombinedFlex"

 	'mouseenter header': ->


Give up on double email notification fix.

--- packages/sanfrancisco-apartment/studio/emailnotification.coffee
+++ packages/sanfrancisco-apartment/studio/emailnotification.coffee
@@ -33,22 +33,14 @@
         location: @inboxLocation
         timelineId: LOI.adventure.currentTimelineId()

-      # TODO: Remove debug output after fixing double notifications.
-      console.log "Checking inbox"
-      console.log "not?", email.wasNotified(), email for email in inbox.things()
+      # TODO: To prevent double notifications, implement game state merging since notified values get overridden from the server.

       unless _.every (email.wasNotified() for email in inbox.things())
         Tracker.nonreactive =>
           @listeners[0].startScript()

-          console.log "email ping"
-          console.log "not?", email.wasNotified(), email for email in inbox.things()
-
           for email in inbox.things() when not email.wasNotified()
-            console.log "marking email", email
             email.markAsNotified()
-
-        console.log "done notifs"

   destroy: ->
     super arguments...


Fix body encoding for email content

Fix body encoding for email content

--- app/assets/javascripts/admin/suppliers_code.js.coffee
+++ app/assets/javascripts/admin/suppliers_code.js.coffee
@@ -69,7 +69,7 @@

   $('[data-hook="send_email_button"]').on "click", ->
     content = $(this).siblings('textarea').val()
-    encoded_content = encodeURI($('<div/>').text(content).html())
+    encoded_content = encodeURIComponent(content)

     email = $(this).siblings('[data-email]').data('email')
     composeGmail(email, 'Naročilo', encoded_content)


fix nested many initial orderable positions

--- app/assets/javascripts/para/inputs/nested_many.coffee
+++ app/assets/javascripts/para/inputs/nested_many.coffee
@@ -17,7 +17,7 @@

     @$fieldsList.on('sortupdate', $.proxy(@sortUpdate, this))

-  sortUpdate: (e, ui) ->
+  sortUpdate: ->
     @$fieldsList.find('.form-fields').each (i, el) ->
       $(el).find('.resource-position-field').val(i)

@@ -31,6 +31,7 @@
     if @orderable
       @$fieldsList.sortable('destroy')
       @initializeOrderable()
+      @sortUpdate()

     if ($redactor = $element.find('[data-redactor]')).length
       $redactor.simpleFormRedactor()


Fixed broken pause function. Time for more tests!

--- app/assets/javascripts/roofpig/AlgAnimation.js.coffee
+++ app/assets/javascripts/roofpig/AlgAnimation.js.coffee
@@ -11,7 +11,7 @@
     @move_animation.animate()

   _next_alg_move: ->
-    if @alg.at_end()
+    if @alg.at_end() || not @alg.playing
       @finished = true
     else
       @move_animation = @alg.next_move()


Require onCompile hook relative to working directory

--- src/beans.coffee
+++ src/beans.coffee
@@ -34,7 +34,7 @@

   # Load compilation event handler, if any.
   if info.onCompile?
-    info.onCompile = require info.onCompile
+    info.onCompile = require path.resolve(info.onCompile)

   # Expands source and target paths.
   info.sourcePath = path.resolve info.sourcePath


fix(userpilot): load before is ready

fix(userpilot): load before is ready

--- app/modules/navigation-bar/navigation-bar.directive.coffee
+++ app/modules/navigation-bar/navigation-bar.directive.coffee
@@ -33,11 +33,13 @@
             userPilotIframe = document.querySelector('#userpilot-resource-centre-frame')

             if userPilotIframe
-                clearInterval(userPilotInterval)
-
                 scope.$applyAsync () =>
                     userPilotIframeDocument = userPilotIframe.contentWindow.document.body
-                    scope.vm.userPilotTitle = userPilotIframeDocument.querySelector('#widget-title').innerText
+                    widget = userPilotIframeDocument.querySelector('#widget-title')
+
+                    if widget
+                        scope.vm.userPilotTitle = widget.innerText
+                        clearInterval(userPilotInterval)

         attempts = 10


Fixup build/new and add run-buildlife-in-environment

--- src/build.coffee
+++ src/build.coffee
@@ -8,8 +8,9 @@
 #   HUBOT_HILLTOP_PATH
 #
 # Commands:
-#   hubot build me <project> <workflow> - Build an originating Anthill workflow.
+#   hubot build <project> <workflow> - Build an originating Anthill workflow.
 #   hubot show build <buildlife> - Show details of an Anthill buildlife.
+#   hubot <workflow> <buildlife> to|in <environment> - Run a workflow against a buildlife in an environment.

 exec = require('child_process').exec
 hilltop = (commands, cb) ->
@@ -19,10 +20,14 @@
     cb return_text

 module.exports = (robot) ->
-  robot.respond /build me (.+) (.+)/i, (msg) ->
-    hilltop "build start \"#{msg.match[1]}\" \"#{msg.match[2]}\"", (output) ->
-      msg.send output.replace /\s+$/g, ""
-
   robot.respond /show build (\d+)/i, (msg) ->
     hilltop "build show #{msg.match[1]}", (output) ->
       msg.send output.replace /\s+$/g, ""
+
+  robot.respond /build (.+) (.+)/i, (msg) ->
+    hilltop "build new \"#{msg.match[1]}\" \"#{msg.match[2]}\"", (output) ->
+      msg.send output.replace /\s+$/g, ""
+
+  robot.respond /(.+) (\d+) (?:to|in) (.+)/i, (msg) ->
+    hilltop "build run \"#{msg.match[2]}\" \"#{msg.match[1]}\" \"#{msg.match[3]}\"", (output) ->
+      msg.send output.replace /\s+$/g, ""


+ 'utf8', 'utf-8' aliases to TextCodec

+ 'utf8', 'utf-8' aliases to TextCodec

--- src/codec.coffee
+++ src/codec.coffee
@@ -3,10 +3,12 @@
 isString    = util.isString
 Codec       = module.exports = require './abstract-codec'
 register    = Codec.register
+aliases     = Codec.aliases
 isBuffer    = Buffer.isBuffer

 class TextCodec
   register TextCodec, Codec
+  aliases  TextCodec, 'utf8', 'utf-8'

   _encodeString: (data)->
     if not data? or isBuffer data


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/color.coffee
+++ src/color.coffee
@@ -405,9 +405,29 @@

       return this

+    ###*
+    A new color. The calling color mixed with `other` using `amount` as the mixing ratio. If amount is not passed, then the colors are mixed evenly.
+
+    @name mixWith
+    @methodOf Color#
+    @param {Color} other the other color to mix
+    @param {Number} [amount] the mixing ratio of the calling color to `other`
+
+    @returns {Color} A new color that is a mix of the calling color and `other`
+    ###
     mixWith: (other, amount) ->
       @copy().mixWith$(other, amount)

+    ###*
+    A new color. The calling color mixed with `other` using `amount` as the mixing ratio. If amount is not passed, then the colors are mixed evenly.
+
+    @name mixWith$
+    @methodOf Color#
+    @param {Color} other the other color to mix
+    @param {Number} [amount] the mixing ratio of the calling color to `other`
+
+    @returns {Color} The modified calling color after mixing it with `other`
+    ###
     mixWith$: (other, amount) ->
       amount ||= 0.5


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/color.coffee
+++ src/color.coffee
@@ -968,7 +968,7 @@
     @returns {Color} The calling color with its alpha increased by `amount`
     ###
     opacify$: (amount) ->
-      @a += amount
+      @a = (@a + amount).clamp(0, 1)

       return this


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

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

     parsedColor = null

-    parsedColor = parseRGB(color)
+    parsedColor = parseRGB(color) || parseHex(color)

     alpha = parsedColor[3]


Reset rotation angle on file replace

Reset rotation angle on file replace

--- client/message.coffee
+++ client/message.coffee
@@ -1166,9 +1166,13 @@
   else
     file = files[0]
     file.callback = (file2, done) ->
-      Meteor.call 'messageUpdate', message,
+      diff =
         file: file2.uniqueIdentifier
-      , done
+      ## Reset rotation angle on replace
+      data = findMessage message
+      if data.rotate
+        diff.rotate = 0
+      Meteor.call 'messageUpdate', message, diff, done
     file.group = group
     Files.resumable.addFile file, e


Avoid autofolding image with children. Fix #363

--- client/message.coffee
+++ client/message.coffee
@@ -365,11 +365,14 @@

 checkImage = (id) ->
   return unless id of images
-  ## Image gets unnaturally folded if it's referenced at least once.
+  ## Image gets unnaturally folded if it's referenced at least once
+  ## and doesn't have any children (don't want to hide children, and this
+  ## can also lead to infinite loop if children has the image reference).
   messageFolded.set id, images[id].naturallyFolded or
-    images[id].count > 0 or
-    (imagesInternal[images[id].file]? and
-     imagesInternal[images[id].file].count > 0)
+    (not images[id].children and
+     (images[id].count > 0 or
+      (imagesInternal[images[id].file]? and
+       imagesInternal[images[id].file].count > 0)))
   ## No longer care about this image if it's not referenced and doesn't have
   ## a rendered template.
   if images[id].count == 0 and id not of id2template
@@ -444,6 +447,7 @@
     id2template[data._id] = @
     ## If message is naturally folded, don't count images it references.
     images[data._id].naturallyFolded = naturallyFolded data
+    images[data._id].children = data.children?.length
     if images[data._id].naturallyFolded
       for id of @images
         images[id].count -= 1


Added TODO.

Added TODO.

--- client/results.coffee
+++ client/results.coffee
@@ -50,6 +50,7 @@
   subscribeToNext = (numResults) ->
     next = Session.get('lastResultSubscribed') + numResults
     Session.set 'lastResultSubscribed', next
+    # TODO: Remove debug
     console.log next
     Meteor.subscribe 'publications-by-ids', Session.get('resultIds').slice 0, next


Allow empty results for compile error

Allow empty results for compile error

--- server/testSystems/informatics/InformaticsSubmitDownloader.coffee
+++ server/testSystems/informatics/InformaticsSubmitDownloader.coffee
@@ -66,7 +66,7 @@
             data = await @adminUser.download(href)
             logger.info "results data=", data
             result = JSON.parse(data)
-            if not result.tests?[1] and not result.message.includes('status="SV"')
+            if not result.tests?[1] and not result.message.includes('status="SV"') and not result.protocol.includes('compile-error="yes"')
                 throw "No results found"
             return result
         catch e


Change arrow colors [#143266915]

Change arrow colors [#143266915]

Change arrow colors to red (#E85D64/rgba(232,93,100,1)) and blue (#8EA2E1/rgba(142,162,225,1))

--- src/code/utils/link-colors.coffee
+++ src/code/utils/link-colors.coffee
@@ -1,12 +1,11 @@
 module.exports =
   default             :'#777'
   defaultFaded        : "rgba(120,120,120,0)"
-  increase            : "rgba(230,40,20,1)"
-  decrease            : "rgba(0,110,200,1)"
-  increaseFaded       : "rgba(230,40,20,0.2)"
-  decreaseFaded       : "rgba(0,110,200,0.2)"
+  increase            : "rgba(232,93,100,1)"
+  decrease            : "rgba(142,162,225,1)"
+  increaseFaded       : "rgba(232,93,100,0.2)"
+  decreaseFaded       : "rgba(142,162,225,0.2)"
   dashed              : "#aaa"
   selectedOutline     : "rgba(250,200,60,0.7)"
   customRelationship  : "#778"
   defaultLight        : '#233'
-


removed sleep, added check for element

removed sleep, added check for element

--- src/components/scores/export.cjsx
+++ src/components/scores/export.cjsx
@@ -137,6 +137,8 @@
         Export
       </AsyncButton>

+    if finalDownloadUrl then className += ' generated'
+

         {actionButton}


Button: For 'click' event, stop 'dblclick' Event.

--- src/elements/Button/Button.coffee
+++ src/elements/Button/Button.coffee
@@ -258,10 +258,9 @@
 				return

 		Events.listen
-			type: ["mousedown"] # , "touchstart"]
+			type: Button.clickTypesPrevent[@_click_type]
 			node: @DOM
 			call: (ev) =>
-				# don't focus element
 				ev.preventDefault()
 				ev.stopPropagation()

@@ -929,9 +928,14 @@
 			node: @DOM

 	@clickTypes:
-		click: ["click"]
-		mouseup: ["mouseup"]
-		dblclick: ["dblclick"]
+		click: ['click']
+		mouseup: ['mouseup']
+		dblclick: ['dblclick']
+
+	@clickTypesPrevent:
+		click: ['dblclick', 'mousedown']
+		mouseup: ['mouseup', 'mousedown']
+		dblclick: ['click', 'mousedown']

 CUI.defaults.class.Button = CUI.Button


Update graph.coffee

Update graph.coffee

--- src/graph.coffee
+++ src/graph.coffee
@@ -1,3 +1,5 @@
+# Copyright 2016 ASAP.
+#
 # Licensed to the Apache Software Foundation (ASF) under one or more
 # contributor license agreements.  See the NOTICE file distributed with
 # this work for additional information regarding copyright ownership.


Add option to get count of listed files. Closes #20

--- src/coffee/commands/files-list.coffee
+++ src/coffee/commands/files-list.coffee
@@ -1,4 +1,5 @@
 debug = require('debug')('s3utils-files-list')
+_ = require 'underscore'
 program = require 'commander'
 Helpers = require '../helpers'
 S3Client = require '../services/s3client'
@@ -8,6 +9,7 @@
 .option '-c, --credentials <path>', 'set s3 credentials file path'
 .option '-p, --prefix <name>', 'all files matching the prefix will be loaded'
 .option '-r, --regex [name]', 'an optional RegExp used for filtering listed files (e.g.: /(.*)\.jpg/)', ''
+.option '--count', 'whether to get the total count of the files or the printed list of them', false
 .option '-l, --logFile <path>', 'optionally log to a file instead of printing to console (errors will still be printed to stderr)'
 .parse process.argv

@@ -22,9 +24,13 @@

     s3client = new S3Client loadedCredentials

-    Logger.info 'Fetching files...'
+    Logger.info 'Fetching files for prefix %s (with regex %s)...', program.prefix, program.regex
     s3client.filteredList {prefix: program.prefix}, program.regex
-    .then (files) -> Logger.data 'Matched files', files: files
+    .then (files) ->
+      if program.count
+        Logger.info 'Matched files count: %s', _.size(files)
+      else
+        Logger.data 'Matched files', files: files
   else
     Logger.error 'Missing required arguments'
     program.help()


update extend to Object.assign

update extend to Object.assign

--- src/index.coffee
+++ src/index.coffee
@@ -182,7 +182,7 @@
         data[k] = opts[k]
       else
         for k2, v2 of data[k]
-          extend data[k][k2], opts[k][k2]
+          objectAssign data[k][k2], opts[k][k2]

   @data.set data


Remove excluded fields in instance2json

Remove excluded fields in instance2json

--- src/index.coffee
+++ src/index.coffee
@@ -133,6 +133,9 @@
     obj = item.toObject(getters: @options.getters)
     if @options.addLinks
       obj['href'] = @_mount_info.url_prefix + "/#{item.id}"
+    for field in @options.exclude
+      if field of obj
+        delete obj[field]
     obj

   action_index: (req, res) ->


Dont clear cache object

Dont clear cache object

--- src/index.coffee
+++ src/index.coffee
@@ -112,13 +112,6 @@
                         cb null, re

-# Clear cache every 24 hours
-setInterval ->
-    console.log "Clearing cache"
-    cache = {}
-, 1000 * 60 * 24
-
-
 ser.listen port

 # for testing


Added types to the default exported symbols for sharejs

--- src/index.coffee
+++ src/index.coffee
@@ -1,2 +1,3 @@
 exports.server = require './server'
 exports.client = require './client'
+exports.types = require './types'


liveReload fixes for #74

liveReload fixes for #74

--- src/index.coffee
+++ src/index.coffee
@@ -82,7 +82,7 @@
     if opt.livereload
       opt.livereload = {}  if typeof opt.livereload is "boolean"
       opt.livereload.port = 35729  unless opt.livereload.port
-      middleware.push liveReload(port: opt.livereload.port)
+      middleware.push liveReload(opt.livereload)
     if typeof opt.root == "object"
       opt.root.forEach (path) ->
         middleware.push connect.static(path)


routes separated

routes separated

--- src/index.coffee
+++ src/index.coffee
@@ -1,4 +1,3 @@
-posts = require './controllers/posts'

 express = require 'express'
 stylus = require 'stylus'
@@ -14,13 +13,11 @@
 # Set View Engine
 app.set 'view engine', 'jade'

-app.get '/', (req, resp) ->
-  resp.render 'index'
+# Initialize routes
+routes = require './routes'
+routes(app)

-# Get root_path return index view
-app.get "/posts/", posts.index
-app.get "/posts/new", posts.new
-app.post "/posts/create", posts.create
+

 # Define Port
 port = process.env.PORT or process.env.VMC_APP_PORT or 3000


Fixed typos

Fixed typos

--- src/index.coffee
+++ src/index.coffee
@@ -130,7 +130,7 @@
       ,
         (err, ws) ->
           return cb err if err
-          rs = fs.createReadStream obj
+          rs = fs.createReadStream filename
           rs.pipe(ws)
           ws.on 'error', (err) ->
             cb err
@@ -157,7 +157,7 @@
       refs.push '.git/HEAD'
       console.log "Here are the refs!"
       console.dir refs
-      async.eachLimit refs, 1, writeObj, cb
+      async.eachLimit refs, 1, copyFile, cb
     ], (err) ->
       throw err if err
       db.close (err) ->


changed port of http-server to 1823

changed port of http-server to 1823

--- src/index.coffee
+++ src/index.coffee
@@ -7,7 +7,7 @@
 	brunchPlugin: yes

 	constructor: (@config) ->
-		@enabled = @config.optimize
+		@enabled = @config.optimize and @config.staticPhantomRenderer
 		@paths = @config.staticPhantomRenderer.paths
 		@host = @config.staticPhantomRenderer.host
 		@public = @config.paths.public
@@ -30,14 +30,14 @@

 			# starting server, unless host given
 			unless @host
-				@host = 'http://localhost:8080/'
-				@server = exec "http-server #{@public}", (error, stdout, stderr) =>
+				@host = 'http://localhost:1823/'
+				@server = exec "http-server -p 1823 #{@public}", (error, stdout, stderr) =>
 					if error
 						@server.kill()
 						return error
 					console.log '[static-renderer] ' + stdout if stdout
 					console.log '[static-renderer] err: ' + stderr if stderr
-				console.log '[static-renderer] starting webserver and serving ' + @public
+				console.log '[static-renderer] starting webserver and serving ' + @public + ' on port 1823'
 			else
 				@host += '/' unless @host.match /\/$/
 				console.log '[static-renderer] loading paths form ' + @host


Changed defaults

Changed defaults

--- src/index.coffee
+++ src/index.coffee
@@ -1,21 +1,11 @@
-
 filter = require './filter'
 search = require './search'

-def =
+defaults =
   version: "1.1"
   lang: ["en"]
-  words: [
-      '#js'
-    , '#nodejs'
-    , 'nodejs'
-    , 'node.js'
-  ]
   mute: []
-  spam: [
-      '"text/javascript"'
-    , 'javascript:void(0)'
-  ]
+  spam: []
   count: "count.txt"

@@ -27,25 +17,21 @@
   token_secret: ''\n
 }\n
 "
+
 parse = (options) ->
-  if options.oauth
-    if options.oauth.consumer_key and options.oauth.consumer_secret
-      if options.oauth.token and options.oauth.token_secret
-        if not options.version
-          options.version = def.version
-        if not options.words
-          options.words = def.words
-        if not options.spam
-          options.spam = def.span
-        if not options.countfile
-          options.count = def.count
-        return options
-  throw oauthErr
+  if not options.words or options.words.length < 1
+    throw "Set words for search"
+  if not (options.oauth and options.oauth.consumer_key and options.oauth.consumer_secret and options.oauth.token and options.oauth.token_secret)
+    throw oauthErr
+  for own key, value of defaults
+    if not options.hasOwnProperty key
+      options[key] = value
+  return options

 module.exports = (options) ->
   options = parse options
-  twtcst =
+  return {
     filter: filter options
     search: search options
-  return twtcst
+  }


add how to download to github download page

--- tasks/release.coffee
+++ tasks/release.coffee
@@ -82,6 +82,10 @@
             body += link({name: 'update.asar', link: GLOBAL.vtReports[item]})

+        body += '\n \
+        ### How to Download: \n
+        Below you\'ll find the download section. If you\'re on Windows, your best bet it to select the "Windows Setup" to get yourself started with Championify. If you have trouble installing you can always try the ".zip" version. For Mac, download the file labeled "OSX", extract the .zip, and you\'ll be good to go!'
+
         create_release = {
           owner: 'dustinblackman'
           repo: 'Championify'


Better docs

Better docs

--- transaction.coffee
+++ transaction.coffee
@@ -2,7 +2,7 @@
 @locus Server
 @summary Implementation of Transactional Cypher HTTP endpoint
          Event-driven and chainable
-         Have to be finished by calling `.commit()` method
+         Have to be finished by calling `.commit()` or `.rollback()` method
 @class Neo4jTransaction
 @url http://neo4j.com/docs/2.2.5/rest-api-transactional.html
 ###


added time display under mouse on progress bar

--- coffee/_player.coffee
+++ coffee/_player.coffee
@@ -208,4 +208,22 @@
       videojs('video_player').volume(0)

 videoContainer.find(".ExpandButton").on "click", (e) ->
-      $("#video-container").toggleClass "expanded"+      $("#video-container").toggleClass "expanded"
+
+$('#PlayerContainer .progress-bg').on 'mousemove', (e) ->
+    if videojs('video_player').currentTime() != 0
+        percentage = ((e.pageX - $(this).offset().left) / $(this).width())
+        time = percentage*videojs('video_player').duration()
+        minutes = parseInt(time / 60) % 60
+        seconds = parseInt(time % 60)
+        if seconds < 10
+            time = minutes + ":0" + seconds
+        else
+            time = minutes + ":" + seconds
+        $('#PlayerContainer .mouse-time').show()
+        $('#PlayerContainer .mouse-time').text(time)
+        margin = e.pageX - 135
+        $('#PlayerContainer .mouse-time').css({'margin-left': (margin) + 'px'})
+
+$('#PlayerContainer .progress-bg').on 'mouseout', (e) ->
+    $('#PlayerContainer .mouse-time').hide()


add toBeApproximately

add toBeApproximately

--- test/helpers/matchers.coffee
+++ test/helpers/matchers.coffee
@@ -60,5 +60,15 @@
           return { message: message, pass: pass }
       }

+    toBeApproximately: ->
+      return {
+        compare: (actual, expected, tolerance) ->
+          pass = Math.abs(actual - expected) <= tolerance
+          return {
+            pass: pass
+            message: "#{actual} is #{if pass then '' else 'not'} approximately #{expected}"
+          }
+      }
+
   jasmine.addMatchers(matchers)
 )


Update ensureProperty -> ensureResource.

--- test/integration.spec.coffee
+++ test/integration.spec.coffee
@@ -23,7 +23,7 @@
     debug "#{_.size results} deleted."
     Promise.resolve()

-ensureProperty = (service, predicate, sampleData) ->
+ensureResource = (service, predicate, sampleData) ->
   debug "Ensuring existence of #{service}..."
   service.where(predicate).fetch()
   .then (result) ->
@@ -53,9 +53,9 @@

     @logger.info 'About to setup...'
     cleanup(@logger, @client)
-    .then => ensureProperty(@client.productTypes, 'name="Sample Product Type"', sampleProductType)
-    .then => ensureProperty(@client.categories, 'name(en="Snowboard equipment")', sampleCategory)
-    .then => ensureProperty(@client.taxCategories, 'name="Standard tax category"', sampleTaxCategory)
+    .then => ensureResource(@client.productTypes, 'name="Sample Product Type"', sampleProductType)
+    .then => ensureResource(@client.categories, 'name(en="Snowboard equipment")', sampleCategory)
+    .then => ensureResource(@client.taxCategories, 'name="Standard tax category"', sampleTaxCategory)
     .then ->
       done()
     .catch (err) -> done(_.prettify err)


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/heavy.coffee
+++ src/heavy.coffee
@@ -1,11 +0,0 @@
-Heavy = (I) ->
-  I ||= {}
-
-  $.reverseMerge I,
-    gravity: 0.2
-    maxSpeed: 5
-
-  before:
-    update: ->
-      I.velocity = I.velocity.add(Point(0, I.gravity))
-


Add MIT header to source files

Add MIT header to source files

--- src/index.coffee
+++ src/index.coffee
@@ -1,3 +1,26 @@
+# The MIT License
+#
+# Copyright (c) 2011 Tim Smart
+#
+# Permission is hereby granted, free of charge, to any person obtaining a
+# copy of this software and associated documentation files
+# (the "Software"), to deal in the Software without restriction,
+# including without limitation the rights to use, copy, modify, merge,
+# publish, distribute, sublicense, and/or sell copies of the Software, and
+# to permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
 spawn = require('child_process').spawn
 path  = require 'path'


Fix typo

Fix typo

--- src/index.coffee
+++ src/index.coffee
@@ -10,7 +10,7 @@
     for pattern, index in patterns
       return index if minimatch s, pattern

-    return globs.length
+    return patterns.length

   onEnd = ->
     files.sort (a, b) ->


Call functions less

Call functions less

--- src/index.coffee
+++ src/index.coffee
@@ -8,9 +8,9 @@
 once = require 'once'

-factory = (defaults, plugins) ->
+factory = (defaults = {}, plugins = []) ->
   request = (req, cb) ->
-    req = new Request extend request.defaults(), req
+    req = new Request extend defaults, req

     # Give the plugins a chance to create the XHR object
     for plugin in request.plugins
@@ -71,10 +71,10 @@
   request.plugins = plugins or []

   request.defaults = (newValues) ->
-    if newValues then factory extend(@defaults(), newValues), @plugins
+    if newValues then factory extend(defaults, newValues), @plugins
     else defaults

-  request.use = (plugins...) -> factory @defaults(), @plugins.concat plugins
+  request.use = (plugins...) -> factory defaults, @plugins.concat plugins

   request.bare = -> factory()


Remove errant console.log

Remove errant console.log

--- src/index.coffee
+++ src/index.coffee
@@ -53,8 +53,6 @@
         path.resolve(process.cwd(), pathName)
       else
         path.resolve(@opts.path, migrationName)
-
-    console.log {cwd: path.join(process.cwd(), pathName), cwdExists: fse.existsSync(path.join(process.cwd(), pathName)), migrationName, pathName}

     migration = require pathName
     migration.name = migrationName


Check 'res.headersSent' instead of 'res._headerSent'

--- src/index.coffee
+++ src/index.coffee
@@ -98,7 +98,7 @@
     resolve = (result) ->
       log.it req.method + " " + req.path + " " + (now() - startTime).toFixed(3) + "ms"

-      return if res._headerSent
+      return if res.headersSent
       return next() unless result

       if result.constructor is Object


* remove debug statement

* remove debug statement

--- src/index.coffee
+++ src/index.coffee
@@ -90,13 +90,10 @@
         continue unless user.id?
         id = ixEmails[user.email]
         if not id?
-          console.log 'user.id', user.id
           u = usersCache[user.id] = {}
         else
           u = usersCache[id]
-        console.log 'user=', user
         for k,v of user
-          console.log k,'=',v
           if v? and not (k in ['id', 'commits', 'percent'])
             if k is 'email' and _.isArray(u[k])
               u[k].push v unless v in u[k]


Tweaks

Tweaks

--- src/index.coffee
+++ src/index.coffee
@@ -15,7 +15,7 @@

   _: _
   Backbone: Backbone
-publish._.extend(BackboneHTTP, publish)
+_.extend(BackboneHTTP, publish)

 # re-expose modules
 BackboneHTTP.modules = {'backbone-orm': BackboneORM}


New robot events and stack events

New robot events and stack events

--- src/index.coffee
+++ src/index.coffee
@@ -14,7 +14,7 @@
       @fn(args); @

   Stack : class Stack extends EventEmitter
-    constructor: (options) ->
+    constructor: (options = {}) ->
       {@name} = options
       @name ?= uuid.v4()
       @actions = []; @
@@ -25,13 +25,14 @@
           @actions.push(action)
       else
         @actions.push(actions)
+      @emit 'stack-push', actions; @

     execute: (args) ->
       ret = null
       for action in @actions
         ret ?= args
         ret = action.fn(ret);
-      return @
+      @emit 'stack-execute', @actions; @

   Sensor : class Sensor extends EventEmitter
     constructor: (options) ->
@@ -74,10 +75,12 @@
     after: (timeInterval, fn) ->
       setTimeout =>
         fn(@)
-      , timeInterval; @
+      , timeInterval;
+      @emit 'robot-after', @; @

     do: (fn) ->
-      fn(); @
+      fn(@);
+      @emit 'robot-do', @; @

     watchSensor: (eventName, fn) ->
       for name, sensor of @sensors
@@ -85,9 +88,9 @@
           fn(data)
       return @

-    watchAction: (actionName, fn) ->
+    watchAction: (eventName, fn) ->
       for name, action of @actions
-        action.on actionName, (data) ->
+        action.on eventName, (data) ->
           fn(data)
       return @


Removed unnecessary comments

Removed unnecessary comments

--- src/index.coffee
+++ src/index.coffee
@@ -12,12 +12,10 @@
       else
         # trim the space from the right of the text
         sText = sText.replace(/\s+$/, '')
-        # sText = sText.substr(0, sText.length - 1)  while sText.charAt(sText.length - 1) is " "
       return sText + "#{sAppend}"
     sText
 ).filter "words", ->
   (sText, iWordsLength, sAppend = '...') ->
-    # return sText  if isNaN(iWordsLength)
     iWordsLength = 10 if isNaN(iWordsLength)
     return ""  if iWordsLength <= 0
     if sText


Add relax option

Add relax option

--- src/index.coffee
+++ src/index.coffee
@@ -113,7 +113,7 @@
         # it isnt an column delimiter and
         # it isnt the begining of a comment
         areNextCharsRowDelimiters = @options.rowDelimiter and chars.substr(i+1, @options.rowDelimiter.length) is @options.rowDelimiter
-        if @nextChar and not areNextCharsRowDelimiters and @nextChar isnt @options.delimiter and @nextChar isnt @options.comment
+        if not @options.relax and @nextChar and not areNextCharsRowDelimiters and @nextChar isnt @options.delimiter and @nextChar isnt @options.comment
           return @error new Error "Invalid closing quote at line #{@lines+1}; found #{JSON.stringify(@nextChar)} instead of delimiter #{JSON.stringify(@options.delimiter)}"
         @quoting = false
         @closingQuote = i


Remove log statement

Remove log statement

--- src/index.coffee
+++ src/index.coffee
@@ -87,7 +87,6 @@
     @setState isPressed: false

   handleDocumentMouseUp: ->
-    # console.log 'document mouse up', @state.isInside
     # Remove the listener
     eventlistener.remove document, 'mouseup', @handleDocumentMouseUp


Rename tabs.

Rename tabs.

--- src/index.coffee
+++ src/index.coffee
@@ -232,7 +232,7 @@
     spy.panel = panel

     coffeeconsole = new CoffeeConsoleWidget()
-    coffeeconsole.title.text = 'CoffeeScript REPL'
+    coffeeconsole.title.text = 'Console'
     coffeeconsole.title.closable = true

     linkViewWidget = new LinkViewWidget(syncMaster)
@@ -245,7 +245,7 @@
     spy.linkView = linkView

-    cmTaskpaper = new CodeMirrorWidget({
+    textView = new CodeMirrorWidget({
         mode: 'text/plain'
         lineNumbers: true
         foldGutter:
@@ -253,11 +253,11 @@
         gutters: ['CodeMirror-linenumbers', 'CodeMirror-foldgutter']
         tabSize: 4
     })
-    cmTaskpaper.title.text = 'CodeMirror View'
-    cmTaskpaper.title.closable = true
+    textView.title.text = 'Text View'
+    textView.title.closable = true

     # Initialize CodeMirror document
-    doc = cmTaskpaper.editor.doc
+    doc = textView.editor.doc
     docView = new DocView(syncMaster, doc)

     # Initialize outline
@@ -269,13 +269,13 @@
     spy.docView = docView
     spy.outlineView = outlineView

-    panel.insertRight(cmTaskpaper)
-    panel.insertBottom(coffeeconsole, cmTaskpaper)
+    panel.insertRight(textView)
+    panel.insertBottom(coffeeconsole, textView)
     panel.insertLeft(linkViewWidget)

-    spy.ctp = spy.cmTaskpaper = cmTaskpaper
+    spy.ctp = spy.textView = textView
     spy.cc = spy.coffeeconsole = coffeeconsole
-    spy.lvw = spy.cmTaskpaper = linkViewWidget
+    spy.lvw = spy.textView = linkViewWidget

     panel.attach(document.body)


:penguin: Run tests also on Linux

:penguin: Run tests also on Linux

--- build/Gruntfile.coffee
+++ build/Gruntfile.coffee
@@ -226,7 +226,7 @@
   ciTasks.push('set-version', 'check-licenses', 'lint')
   ciTasks.push('mkdeb') if process.platform is 'linux'
   ciTasks.push('create-windows-installer') if process.platform is 'win32'
-  ciTasks.push('test') if process.platform is 'darwin'
+  ciTasks.push('test') if process.platform in ['darwin', 'linux']
   ciTasks.push('codesign')
   ciTasks.push('publish-build')
   grunt.registerTask('ci', ciTasks)


font size selector tweak (carl) + id selector fix

--- public/dreamcatcher/controllers/settings_controller.coffee
+++ public/dreamcatcher/controllers/settings_controller.coffee
@@ -97,7 +97,7 @@
     @updateSettingsModel {'user[default_menu_style]': defaultMenuStyle}

   '#fontLarge, #fontMedium, #fontSmall click': (element) ->
-    defaultFontSize = @.id
+    defaultFontSize = element.attr("id")
     log('defaultFontSize: ' + defaultFontSize)
     @displayDefaultFontSize defaultFontSize
     @updateSettingsModel {'user[default_font_size]': defaultFontSize}


List change

List change

--- site/app.coffee
+++ site/app.coffee
@@ -455,16 +455,27 @@
       $('#map_list_element_'+loc).hover(
         ((ev) ->
           loc = this.id.substring(17)
-          if loc in REGIONS
-            text = ""
-            for name in REGION2STATE[loc]
-              text = text + name + ","
-            $('#map_list_element_text_'+loc).html(text.substring(0,text.length-1)).css("font-size","12px")
+          current = saveThis.mapData[loc]
+          ili = '' + (Math.round(current.value * 100) / 100)
+          if '.' in ili
+            ili += '00'
+            idx = ili.indexOf('.')
+            ili = ili.slice(0, idx + 3)
+          else
+            ili += '.00'
+          std = '' + (Math.round(current.std * 100) / 100)
+          if '.' in std
+            std += '00'
+            idx = std.indexOf('.')
+            std = std.slice(0, idx + 3)
+          else
+            std += '.00'
+          ili = '(' + ili + '±' + std + ')%'
+          $('#map_list_element_text_'+loc).text(ili)
           saveThis.renderMap(loc))
         ((ev) ->
           loc = this.id.substring(17)
-          if loc in REGIONS
-            $('#map_list_element_text_'+loc).html(NAMES[loc]).css("font-size","16px")
+          $('#map_list_element_text_'+loc).text(NAMES[loc])
           saveThis.renderMap()))

   renderMap: (highlight=null) ->


add return to silence warnings

add return to silence warnings

--- src/concept-coach/launcher/index.cjsx
+++ src/concept-coach/launcher/index.cjsx
@@ -28,6 +28,7 @@

   launch: ->
     channel.emit('launcher.clicked')
+    undefined # stop react from complaining about returning false from a handler

   render: ->
     {isLaunching, defaultHeight} = @props
@@ -46,4 +47,4 @@
       </div>
     </div>

-module.exports = {Launcher}+module.exports = {Launcher}


Fix junks folders created for patch file

--- lib/models/diffs/diff-chunk.coffee
+++ lib/models/diffs/diff-chunk.coffee
@@ -1,3 +1,5 @@
+path = require 'path'
+
 DiffLine = require './diff-line'
 ListItem = require '../list-item'
 {git} = require '../../git'
@@ -46,4 +48,4 @@
     git.git "apply --cached --reverse #{@patchPath()}"

   patchPath: ->
-    atom.project.getRepo().getWorkingDirectory() + ".git/atomatigit_diff_patch"
+    path.join git.getPath(), ".git/atomatigit_diff_patch"


Test pgp_full_hash

Test pgp_full_hash

--- test/files/keymanager.iced
+++ test/files/keymanager.iced
@@ -5,6 +5,7 @@
 {box} = require '../../lib/keybase/encode'
 {Encryptor} = require 'triplesec'
 {base91} = require '../../lib/basex'
+example_keys = (require '../data/keys').keys

 asp = new ASP {}
 bundle = null
@@ -99,3 +100,11 @@
   compare_bundles T, bundle, b3
   T.no_error err
   cb()
+
+exports.pgp_full_hash = (T,cb) ->
+  await KeyManager.import_from_armored_pgp { armored : example_keys.portwood }, defer err, km
+  T.no_error err
+  await km.pgp_full_hash {}, defer err, hash
+  T.no_error err
+  T.assert hash == "5c31f2642b01d6beaf836999dafb54db59295a27a1e6e75665edc8db22691d90", "full hash doesn't match"
+  cb()


Remove redundant tooltip

Remove redundant tooltip

--- resources/assets/coffee/react/_components/user-card.coffee
+++ resources/assets/coffee/react/_components/user-card.coffee
@@ -137,7 +137,6 @@
                 el 'i', className: 'fa fa-cog user-dropdown-modal-menu__icon'
             el 'a',
               href: window.logoutUrl
-              title: Lang.get('layout.menu.user.logout')
               className: 'user-dropdown-modal-menu__item js-logout-link'
               'data-method': 'delete'
               'data-confirm': Lang.get 'users.logout_confirm'
@@ -146,7 +145,6 @@
               el 'i', className: 'fa fa-sign-out user-dropdown-modal-menu__icon'
             el 'a',
               href: window.helpUrl
-              title: Lang.get('layout.menu.user.help')
               className: 'user-dropdown-modal-menu__item'
               Lang.get 'layout.menu.user.help'
               el 'i', className: 'fa fa-question-circle user-dropdown-modal-menu__icon'


fix a mistake in the doc string

fix a mistake in the doc string

--- library/gearbox/application.coffee
+++ library/gearbox/application.coffee
@@ -70,7 +70,7 @@
     # kernel that gets fired once some component has been booted.
     # Method monitors all the attachment events, and every time it
     # checks if all the components has been booted. Once it so, it
-    # broadcasts `completed` event to the kernel. The event will be
+    # broadcasts `completed` event via the root. The event will be
     # repeated after connection restoring, however a special event
     # `completed-once` is guaranteed to be fired one time only.
     waitCompletion: @synchronize "attached", (service) ->


Fix safebooru links

Fix safebooru links

--- modules/danbooru.coffee
+++ modules/danbooru.coffee
@@ -34,7 +34,7 @@
         return
       url =
         if r.file_url.match(/^http/) then r.file_url
-        else "https://danbooru.donmai.us#{r.file_url}"
+        else "https://danbooru.donmai.us#{r.file_url.replace(/\/\//g, '/')}"
       # Send the picture
       msg.reply '', embed: {
         title: l.generic.sauceBtn
@@ -54,7 +54,7 @@
         return
       url =
         if r.file_url.match(/^http/) then r.file_url
-        else "https://danbooru.donmai.us#{r.file_url}"
+        else "https://danbooru.donmai.us#{r.file_url.replace(/\/\//g, '/')}"
       # Send the picture
       msg.reply '', embed: {
         title: l.generic.sauceBtn


Move router to Root component

Move router to Root component

--- static/scripts/main.coffee
+++ static/scripts/main.coffee
@@ -317,9 +317,6 @@
     ["/settings", "Settings"]
 ]

-# TODO: Move this to `Root` component.
-window.router = Router()
-
 Root = React.createClass
     displayName: "Root"

@@ -327,13 +324,16 @@
         currentComponent: Noop()
         alert: Noop()

+    componentWillMount: ->
+        @router = Router()
+
     componentDidMount: ->
         _.forEach @props.routes, (route) =>
             [url, component] = route

-            router.on url, =>
-                @setState currentComponent: component(addAlert: @addAlert)
-        router.init @props.defaultRoute
+            @router.on url, =>
+                @setState currentComponent: component(router: @router, addAlert: @addAlert)
+        @router.init @props.defaultRoute

     render: ->
         (Dom.div className: "container",


Add memes: Ackbar, Confession Bear, Overly Attached Girlfriend, Foul Bachelor Frog

--- src/memes.coffee
+++ src/memes.coffee
@@ -20,6 +20,7 @@

 memes =
   "aag": "Ancient Aliens Guy",
+  "ackbar":"Ackbar (It's a trap)",
   "afraid": "'Afraid to Ask'",
   "alwaysonbeat": "Always On Beat",
   "ants": "Do You Want Ants? (archer)",
@@ -31,12 +32,14 @@
   "both": "Why Not Both?",
   "bs": "This is Bull, Shark",
   "buzz": "X, X Everywhere",
+  "cb": "Confession Bear",
   "chosen": "You Were the Chosen One!",
   "crazypills": "I Feel Like I'm Taking Crazy Pills",
   "doge": "Doge",
   "elf": "You Sit on a Throne of Lies",
   "ermg": "Ermahgerd",
   "fa": "Forever Alone",
+  "fbf": "Foul Bachelor Frog",
   "fetch": "Stop Trying to Make Fetch Happen",
   "fry": "Futurama Fry",
   "fwp": "First World Problems",
@@ -54,6 +57,7 @@
   "mordor": "One Does Not Simply Walk into Mordor",
   "morpheus": "Matrix Morpheus",
   "officespace": "That Would be Great",
+  "oag": "Overly Attached Girlfriend",
   "oprah": "Oprah You Get a Car",
   "philosoraptor": "Philosoraptor",
   "regret": "I Immediately Regret This Decision (anchorman)",


mocha: Also allow specifying timeout for fixture setup

NoFlo is insanely slow, using 10 seconds on a modern laptop...

--- src/mocha.coffee
+++ src/mocha.coffee
@@ -38,6 +38,7 @@
 exports.run = (rt, tests, options) ->
   # default pretty high to give time for runtime to start
   options.starttimeout = 5000 if not options.starttimeout?
+  options.fixturetimeout = 2000 if not options.fixturetimeout?

   runner = new Runner rt
   try
@@ -69,5 +70,6 @@
       runner.disconnect done

   for suite in suites
+    suite.timeout = options.fixturetimeout if not suite.timeout?
     runSuite runner, suite


max buffer for exec increased to 200MB

max buffer for exec increased to 200MB

--- terminal/core.coffee
+++ terminal/core.coffee
@@ -3,7 +3,7 @@
 getStructured = require './structure'
 displayFormatted = require './format'
 run = (command,cb)->
-  cp = exec(command, (e,r,b)->
+  cp = exec(command, maxBuffer:200*200*1024, (e,r,b)->
     if e
       awkward.log chalk.red e
       return


Adds file exec method with progress

Adds file exec method with progress

--- src/model.coffee
+++ src/model.coffee
@@ -88,6 +88,9 @@
   exec: (commandName, params, callback) ->
     @commands.exec @_request, commandName, params, callback

+  # Execute a file command
+  file: (commandName, params, progress, callback) ->
+    @commands.fileExec @_request, commandName, params, progress, callback

 # A representation of a Collection+JSON item
@@ -161,7 +164,7 @@
     if typeof params is 'function'
       callback = params
       params = null
-
+
     if params
       fields = []
       for own key, value of params
@@ -258,6 +261,21 @@
       params = undefined
     @_request(request, 'post', rel, params, 'items').callback callback

+  # Execute a file command with the given parameters
+  fileExec: (request, rel, params, progress, callback) ->
+    if typeof params is 'function'
+      callback = progressEvent
+      progressEvent = params
+      params = undefined
+    progressHook = (xhr, data) ->
+      if data instanceof FormData
+        xhr.upload.addEventListener 'progress', (e) ->
+          if e.lengthComputable
+            progress {loaded: e.loaded, total: e.total}
+    request.hook progressHook
+    @_request(request, 'post', rel, params, 'items')
+    .callback callback
+
   cloneEmpty: ->
     clone = new MetaList()
     for own rel, entry of this


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/animated.coffee
+++ test/animated.coffee
@@ -431,13 +431,12 @@

   animation = GameObject
     data: animationData
+    includedModules: ["Animated"]

   animation.bind "Complete", ->
     window.completeFired = true
-
-  animation.include(Animated)
-
-  (animation.I.activeAnimation.frames.length).times ->
+
+  animation.I.activeAnimation.frames.length.times ->
     animation.update()

   ok window.completeFired, "Complete event fired"


removing erroneous tabs from nodes.coffee.

--- src/nodes.coffee
+++ src/nodes.coffee
@@ -542,11 +542,11 @@
     if @isNew
       idt = @tab + TAB
       return """
-		(function(func, args, ctor) {
-		#{idt}ctor.prototype = func.prototype;
-		#{idt}var child = new ctor, result = func.apply(child, args);
-		#{idt}return typeof result === "object" ? result : child;
-		#{@tab}})(#{ @variable.compile o, LEVEL_LIST }, #{splatArgs}, function() {})
+        (function(func, args, ctor) {
+        #{idt}ctor.prototype = func.prototype;
+        #{idt}var child = new ctor, result = func.apply(child, args);
+        #{idt}return typeof result === "object" ? result : child;
+        #{@tab}})(#{ @variable.compile o, LEVEL_LIST }, #{splatArgs}, function() {})
       """
     base = new Value @variable
     if (name = base.properties.pop()) and base.isComplex()


CustomPartials: Add preview related fields to data when save.

--- client/Dashboard/views/customviews/addnewcustomviewform.coffee
+++ client/Dashboard/views/customviews/addnewcustomviewform.coffee
@@ -36,13 +36,15 @@
         @editor.ace.setSyntax "html"

   addNew: ->
-    isUpdate       = @getData()
-    data           =
-      name         : @input.getValue()
-      partial      : @editor.getValue()
-      partialType  : @getOption "viewType"
-      isActive     : no
-      viewInstance : ""
+    isUpdate          = @getData()
+    data              =
+      name            : @input.getValue()
+      partial         : @editor.getValue()
+      partialType     : @getOption "viewType"
+      isActive        : no
+      viewInstance    : ""
+      isPreview       : no
+      previewInstance : no

     # TODO: fatihacet - DRY callbacks
     if isUpdate


Fix contextmenu positioning when altkey performed

We have to create a fakeEvent object here since event.pageY/X is read-only so
updating offsets was not working which was causing to create context menu in a
wrong position when altkey shortcut used

--- client/finder/filetree/controllers/findertreecontroller.coffee
+++ client/finder/filetree/controllers/findertreecontroller.coffee
@@ -627,10 +627,20 @@
   performDownKey:(nodeView, event)->

     if event.altKey
-      offset = nodeView.$('.chevron').offset()
-      event.pageY = offset.top
-      event.pageX = offset.left
-      @contextMenu nodeView, event
+
+      # We have to create a fakeEvent object here
+      # since event.pageY/X is read-only so updating
+      # offsets was not working which was causing to
+      # create context menu in a wrong position when
+      # altkey shortcut used
+      offset    = nodeView.$('.chevron').offset()
+      fakeEvent =
+        pageY   : offset.top
+        pageX   : offset.left
+        stopPropagation : ->
+        preventDefault  : ->
+
+      @contextMenu nodeView, fakeEvent
     else
       super


Age in seconds

Age in seconds

--- test/cooldown.coffee
+++ test/cooldown.coffee
@@ -85,6 +85,7 @@
   obj = GameObject()

   obj.update(1)
+  obj.t

   equals obj.I.age, 1, "should successfully update"


Missing period

Missing period

--- test/fixtures.coffee
+++ test/fixtures.coffee
@@ -54,5 +54,5 @@
     { id: 1, creator_id: 1, commentable_type: "Todo", commentable_id: 2 }
   ]

-exports App = App
+exports.App = App
 exports.Backbone = Backbone


Fixes [#3738298] , clears display after certain types of operations.

--- calculator_spec.coffee
+++ calculator_spec.coffee
@@ -16,10 +16,14 @@
     expect(@handle.output_content()).toEqual("8")

-  it "automatically clears when another number starts getting entered after a calculation", ->
-    @handle.press_buttons("2 ^ 2 = 1")
-    expect(@handle.output_content()).toEqual("1")
+  describe "clearing after calculation", ->
+    it "automatically clears when another number starts getting entered after a calculation", ->
+      @handle.press_buttons("2 ^ 2 = 1")
+      expect(@handle.output_content()).toEqual("1")

+    it "preserves numbers for future calculations", ->
+      @handle.press_buttons("2 ^ 2 = + 1 =")
+      expect(@handle.output_content()).toEqual("5")

   it "will display decimal numbers correctly", ->
     @handle.press_buttons("1 . 0 1")


AccountMenu: your groups submenu handling triggered now also on click

--- client/Main/avatararea/avatarareagroupswitcherpopup.coffee
+++ client/Main/avatararea/avatarareagroupswitcherpopup.coffee
@@ -102,6 +102,11 @@
       return  if $(event.target).closest().is '.submenu'
       @groupSubMenuWrapper.unsetClass 'active'

+    handleSubMenu = (event)=>
+      KD.utils.stopDOMEvent event
+      submenuShown = yes
+      @groupSubMenuWrapper.setClass 'active'
+      @populateGroups()

     @avatarPopupContent.addSubView new KDCustomHTMLView
       tagName    : 'a'
@@ -109,12 +114,9 @@
       cssClass   : 'bottom'
       partial    : 'Your groups'
       bind       : 'mouseenter mousemove'
-      mouseenter : (event)=>
-        KD.utils.stopDOMEvent event
-        submenuShown = yes
-        @groupSubMenuWrapper.setClass 'active'
-        @populateGroups()
-      mousemove : KD.utils.stopDOMEvent
+      mouseenter : handleSubMenu
+      click      : handleSubMenu
+      mousemove  : KD.utils.stopDOMEvent

     @avatarPopupContent.addSubView new KDCustomHTMLView
       tagName    : 'a'


reactivity: do not unmount chat input for non participant since it can break binding execution of other components

--- client/activity/lib/components/publicchatpane/index.coffee
+++ client/activity/lib/components/publicchatpane/index.coffee
@@ -42,17 +42,20 @@

     return null  unless @props.thread?.get 'messages'

-    footerInnerComponent = if @channel 'isParticipant'
-    then <ChatInputWidget
+    isParticipant = @channel 'isParticipant'
+
+
+

+        <ChatInputWidget
            ref       = 'chatInputWidget'
            onSubmit  = { @bound 'onSubmit' }
            onCommand = { @bound 'onCommand' }
            channelId = { @channel 'id' }
            onResize  = { @bound 'onResize' }/>
-    else @renderFollowChannel()
-
-
-      {footerInnerComponent}
+

+

+        { @renderFollowChannel() }
+


Use vm.shutdown instead vm.stop

Use vm.shutdown instead vm.stop

--- client/landing/app/MainApp/VirtualizationController.coffee
+++ client/landing/app/MainApp/VirtualizationController.coffee
@@ -35,6 +35,9 @@
     @_runWraper 'vm.start', vm, callback

   stop:(vm, callback)->
+    @_runWraper 'vm.shutdown', vm, callback
+
+  halt:(vm, callback)->
     @_runWraper 'vm.stop', vm, callback

   reinitialize:(vm, callback)->
@@ -147,7 +150,7 @@
   askForApprove:(command, callback)->

     switch command
-      when 'vm.stop'
+      when 'vm.stop', 'vm.shutdown'
         content = """Turning off your VM will <b>stop</b> running Terminal
                      instances and all running proccesess that you have on
                      your VM. Do you want to continue?"""


Fixing test client bug

Fixing test client bug

--- gameserver/generatetestclients.coffee
+++ gameserver/generatetestclients.coffee
@@ -7,7 +7,7 @@
 db = new sqlite3.Database('profiles.db')
 for num in [0..NUM_CLIENTS-1]
     league = LEAGUES[Math.floor(Math.random()*LEAGUES.length)]
-    @db.run(
+    db.run(
         'INSERT INTO profiles (url, region, name, league) VALUES ($1, $2, $3, $4)',
         [num, 'AM', 'client-' + num, league],
         (err) =>


Client: Activity: Feed: formatting

Client: Activity: Feed: formatting

--- client/activity/lib/routes/singlepublicchannel.coffee
+++ client/activity/lib/routes/singlepublicchannel.coffee
@@ -1,15 +1,14 @@
-kd                        = require 'kd'
-ChannelThreadPane         = require 'activity/components/channelthreadpane'
-ActivityFlux              = require 'activity/flux'
-transitionToChannel       = require 'activity/util/transitionToChannel'
-SingleMessageRoute        = require './singlemessage'
+kd                  = require 'kd'
+ChannelThreadPane   = require 'activity/components/channelthreadpane'
+ActivityFlux        = require 'activity/flux'
+transitionToChannel = require 'activity/util/transitionToChannel'
+SingleMessageRoute  = require './singlemessage'

 {
   thread  : threadActions,
   message : messageActions } = ActivityFlux.actions

 { selectedChannelThread, channelByName } = ActivityFlux.getters
-

 module.exports = class SingleChannelRoute


payment: remove spaces and dashes from cc input on blur

restore them on focus

--- client/landing/app/MainApp/payment/billingform.coffee
+++ client/landing/app/MainApp/payment/billingform.coffee
@@ -30,6 +30,11 @@
       cardNumber          :
         label             : 'Credit Card'
         placeholder       : 'Credit Card Number'
+        blur              : ->
+          @oldValue = @getValue()
+          @setValue @oldValue.replace /\s|-/g, ''
+        focus             : ->
+          @setValue @oldValue  if @oldValue
         validate          :
           event           : 'blur'
           rules           :


use coffeescript idioms

use coffeescript idioms

--- test/src/love.coffee
+++ test/src/love.coffee
@@ -86,4 +86,4 @@
       expect(draw).to.have.been.called
       expect(nextFrame).to.have.been.called

-      this.clock.restore()
+      @clock.restore()


Remove unused code and include problem ID of data we send

--- common/lib/xmodule/xmodule/js/src/capa/display.coffee
+++ common/lib/xmodule/xmodule/js/src/capa/display.coffee
@@ -22,7 +22,6 @@

     @$('section.action input:button').click @refreshAnswers
     @$('section.action input.check').click @check_fd
-    #@$('section.action input.check').click @check
     @$('section.action input.reset').click @reset
     @$('section.action button.show').click @show
     @$('section.action input.save').click @save
@@ -162,10 +161,6 @@
   #       maybe preferable to consolidate all dispatches to use FormData
   ###
   check_fd: =>
-    # Calling check from check_fd will result in firing the 'problem_check' event twice,
-    # since it is also called in the check function.
-    # Logger.log 'problem_check', @answers
-
     # If there are no file inputs in the problem, we can fall back on @check
     if $('input:file').length == 0
       @check()
@@ -243,6 +238,7 @@

     # Segment.io
     analytics.track "Problem Checked",
+      problem_id: @id
       answers: @answers

     $.postWithPrefix "#{@url}/problem_check", @answers, (response) =>


added comment to explain sorting

added comment to explain sorting

--- common/static/coffee/src/discussion/discussion.coffee
+++ common/static/coffee/src/discussion/discussion.coffee
@@ -62,8 +62,7 @@
         #The comment client asks each thread for a value by which to sort the collection
         #and calls this sort routine regardless of the order returned from the LMS/comments service
         #so, this takes advantage of this per-thread value and returns tomorrow's date
-        #for pinned threads, ensuring that they appear first, which is the intent of pinned threads
-        #the negative is to display most recent first
+        #for pinned threads, ensuring that they appear first, (which is the intent of pinned threads)
         #
         if thread.get('pinned')
           #use tomorrow's date
@@ -76,7 +75,7 @@
     sortByDateRecentFirst: (thread) ->
         #
         #Same as above
-        #the negative is to display most recent first (basically to flip the order)
+        #but negative to flip the order (newest first)
         #
         if thread.get('pinned')
           #use tomorrow's date


Closes #43; Pulled color computation out of _paintEntry.

--- coffee/time/heatmap.coffee
+++ coffee/time/heatmap.coffee
@@ -127,6 +127,13 @@
     while (--entryIndex >= 0) and (--drawColumn >= 0)
       @_paintEntry(entryIndex, drawColumn)

+  # Computes the correct color for a given bucket.
+  # @param [Integer] value Normalized value at the bucket.
+  # @param [Integer] max Normalized maximum for the column.
+  # @param [String] color Computed base color for the bucket.
+  _computeColor: (value, max, color) ->
+    Epoch.toRGBA(color, @_colorFn(value, max))
+
   # Paints a single entry column on the paint canvas at the given column.
   # @param [Integer] entryIndex Index of the entry to paint.
   # @param [Integer] drawColumn Column on the paint canvas to place the visualized entry.
@@ -161,7 +168,7 @@
       for entry in entries
         max += (entry.buckets[bucket] / sum) * maxTotal
       if sum > 0 or @options.paintZeroValues
-        @p.fillStyle = Epoch.toRGBA(color, @_colorFn(sum, max))
+        @p.fillStyle = @_computeColor(sum, max, color)
         @p.fillRect xPos, (j-1) * h, w-@options.bucketPadding, h-@options.bucketPadding
       j--


Make stdin JS snippet less context-specific

--- user/snippets.cson
+++ user/snippets.cson
@@ -90,13 +90,15 @@
 	"Read Stdin":
 		prefix: "stdin"
 		body: """
-			return new Promise(resolve => {
+			new Promise(resolve => {
 				let input = "";
 				process.stdin.setEncoding("UTF8");
 				process.stdin.on("readable", () => {
 					const chunk = process.stdin.read();
 					null !== chunk ? input += chunk : resolve(input);
 				})
+			}).then(data => {
+				$1
 			});
 		"""
 	Print:


font fixes

font fixes

--- web/scripts/gameManager.coffee
+++ web/scripts/gameManager.coffee
@@ -516,7 +516,7 @@
         gn = gn.concat(num)
         ma = []
         ma[0] = "Congratulations!"
-        window.objCloud(400,ma,"body","30%","30%",3,gn,@gameManager)
+        window.objCloud(400,ma,"body","30%","30%",1.5,gn,@gameManager)
         return

     gameLost: =>


Code cleanup

Code cleanup

--- src/Pump.coffee
+++ src/Pump.coffee
@@ -1,6 +1,6 @@
 EventEmitter = require('events').EventEmitter
-Promise = require('bluebird')
-Buffer = require('./Buffer')
+Promise = require 'bluebird'
+Buffer = require './Buffer'
 BufferDebugMixin = require './mixin/BufferDebugMixin'

 module.exports = class Pump extends EventEmitter


Move to variable

Move to variable

--- resources/assets/coffee/_classes/textarea-key-down.coffee
+++ resources/assets/coffee/_classes/textarea-key-down.coffee
@@ -21,11 +21,14 @@
   @CANCEL = 'cancel'
   @SUBMIT = 'submit'

+  @KEY_ENTER = 13
+  @KEY_ESC = 27
+
   @createHandler: (callback) =>
     (event) =>
-      if event.keyCode == 27
+      if event.keyCode == @KEY_ESC
         type = @CANCEL
-      else if event.keyCode == 13 && !event.shiftKey
+      else if event.keyCode == @KEY_ENTER && !event.shiftKey
         event.preventDefault()
         type = @SUBMIT


Don't attempt to render points of failure graph if there have been zero plays

--- resources/assets/coffee/react/beatmapset-page/info.coffee
+++ resources/assets/coffee/react/beatmapset-page/info.coffee
@@ -87,7 +87,7 @@

   renderChart: ->
-    return if !@props.beatmapset.has_scores
+    return if !@props.beatmapset.has_scores || @props.beatmap.playcount < 1

     unless @_failurePointsChart?
       options =


Store the server created by connect

Store the server created by connect

--- src/robot.coffee
+++ src/robot.coffee
@@ -200,7 +200,7 @@
           @logger.debug "Registered route: DELETE #{route}"
           app.delete route, callback

-    @connect.listen process.env.PORT || 8080
+    @server = @connect.listen process.env.PORT || 8080

     herokuUrl = process.env.HEROKU_URL


disable logging signature verification

disable logging signature verification

Signed-off-by: Joshua Rubin <5af9dd6785dff068189d7fd731f38e1d0699263c@zvelo.com>

--- src/robot.coffee
+++ src/robot.coffee
@@ -261,7 +261,6 @@

   requireHubSignature: (req, res, next) ->
     return next new Error "Invalid Hub Signature" unless req.hubVerified
-    @logger.debug "x-hub-signature verified"
     next()

   # Setup the Express server's defaults.


allow configurable save interval for the brain

--- src/robot.coffee
+++ src/robot.coffee
@@ -190,9 +190,7 @@
     @data =
       users: { }

-    @saveInterval = setInterval =>
-      @save()
-    , 5000
+    @resetSaveInterval 5

   save: ->
     @emit 'save', @data
@@ -201,6 +199,12 @@
     clearInterval @saveInterval
     @save()
     @emit 'close'
+
+  resetSaveInterval: (seconds) ->
+    clearInterval @saveInterval if @saveInterval
+    @saveInterval = setInterval =>
+      @save()
+    , seconds * 1000

   # Merge keys loaded from a DB against the in memory representation
   #


Also clear on error

Also clear on error

--- core/app/backbone/views/users/react_change_password.coffee
+++ core/app/backbone/views/users/react_change_password.coffee
@@ -43,8 +43,9 @@
       success: =>
         window.parent.FactlinkApp.NotificationCenter.success 'Your password has been changed!'
         @props.model.clear()
-      error: ->
+      error: =>
         window.parent.FactlinkApp.NotificationCenter.error 'Could not change your password, please try again.'
+        @props.model.clear()

   render: ->
     return _span([], 'Please sign in.') unless FactlinkApp.signedIn()


Updated the help generator to allow for .js scripts to include help lines as well.

--- src/robot.coffee
+++ src/robot.coffee
@@ -124,7 +124,7 @@
     Fs.readFile path, "utf-8", (err, body) =>
       throw err if err
       for i, line of body.split("\n")
-        break    if line[0] != '#'
+        break    if !(line[0] == '#' or line.substr(0, 2) == '//')
         continue if !line.match('-')
         @commands.push line[2..line.length]


Also emit the @data

Also emit the @data

--- src/robot.coffee
+++ src/robot.coffee
@@ -214,7 +214,7 @@
     for k of (data or { })
       @data[k] = data[k]

-    @emit 'loaded'
+    @emit 'loaded', @data

 class Robot.Message
   # Represents an incoming message from the chat.


Add `route` function on Robot

Add `route` function on Robot

This allows users to specify routes and callbacks. Those callbacks will
be called when the matched route is requested.

--- src/robot.coffee
+++ src/robot.coffee
@@ -23,10 +23,21 @@
     @listeners   = []
     @loadPaths   = []
     @enableSlash = false
+    connect      = require 'connect'
+    @connect     = connect()

     @logger      = new Log process.env.HUBOT_LOG_LEVEL or "info"

     @loadAdapter adapterPath, adapter if adapter?
+
+  # Public: Specify a router and callback to register as Connect middleware.
+  #
+  # route    - A String of the route to match.
+  # callback - A Function that is called when the route is requested
+  #
+  # Returns nothing.
+  route: (route, callback) ->
+    @connect.use route, callback

   # Public: Adds a Listener that attempts to match incoming messages based on
   # a Regex.
@@ -241,6 +252,7 @@
     matchedUsers

   run: ->
+    @connect.listen process.env.PORT
     @adapter.run()

   shutdown: ->


spec: replace this.sandbox with sinon

spec: replace this.sandbox with sinon

Change-Id: Ic60c3f3661bcaf752f6db2c41357b9f09736d5f2
Reviewed-on: https://gerrit.instructure.com/156965
Tested-by: Jenkins
Reviewed-by: Ryan Shaw <ea3cd978650417470535f3a4725b6b5042a6ab59@instructure.com>
Product-Review: Jeremy Neander <20252aad5efdc666f083d82ab63ed0f9f72a043e@instructure.com>
QA-Review: Jeremy Neander <20252aad5efdc666f083d82ab63ed0f9f72a043e@instructure.com>

--- spec_canvas/coffeescripts/graphs/AssignmentTardinessSpec.coffee
+++ spec_canvas/coffeescripts/graphs/AssignmentTardinessSpec.coffee
@@ -169,7 +169,7 @@
       now:    new Date(2000, 1, 14)
       future: new Date(2000, 2, 1)
       end:    new Date(2000, 3, 1)
-    clock = @sandbox.useFakeTimers +dates.now
+    clock = sinon.useFakeTimers +dates.now

     # setup color map
     colors =
@@ -248,3 +248,5 @@
       shape: 'circle'
       color: colors.onTime
       fill: colors.onTime
+
+    clock.restore()


fix invalid role error msg when removing user from room (#3878)

--- packages/rocketchat-authorization/server/functions/removeUserFromRoles.coffee
+++ packages/rocketchat-authorization/server/functions/removeUserFromRoles.coffee
@@ -8,7 +8,7 @@

 	roleNames = [].concat roleNames

-	existingRoleNames = _.pluck(RocketChat.authz.getRoles(), 'name')
+	existingRoleNames = _.pluck(RocketChat.authz.getRoles(), '_id')
 	invalidRoleNames = _.difference(roleNames, existingRoleNames)
 	unless _.isEmpty(invalidRoleNames)
 		throw new Meteor.Error 'error-invalid-role', 'Invalid role', { function: 'RocketChat.authz.removeUserFromRoles' }


Fix border/shadow setter regexp

Fix border/shadow setter regexp

--- src/shape.coffee
+++ src/shape.coffee
@@ -54,7 +54,7 @@
   @property 'border',
     get: -> "#{@_border.width}px #{@_border.color}"
     set: (border) ->
-      values = border.match(/^(\d+px) (.+)$/)
+      values = border.match(/^(\d+)(?:px)? (.+)$/)

       if values?.length == 3
         @_border.width = parseInt values[1], 10
@@ -69,13 +69,13 @@
   @property 'shadow',
     get: -> "#{@_shadow.x}px #{@_shadow.y}px #{@_shadow.blur}px #{@_shadow.color}"
     set: (shadow) ->
-      values = shadow.match(/^(.+) (.+) (.+) (.+)$/)
+      values = shadow.match(/^(\d+)(?:px)? (\d+)(?:px)? (\d+)(?:px)? (.+)$/)

       if values?.length == 5
         @_shadow.x = parseInt values[1], 10
         @_shadow.y = parseInt values[2], 10
         @_shadow.blur = parseInt values[3], 10
-        @_shadow.color = values[4] # TODO: rgba(x, x, x, x) doesn't work
+        @_shadow.color = values[4]
         @dirty = true
       else
         console.warn('Use format "(x)px (y)px (blur)px (color)" when setting shadows')


Adds tests for contributingByline function

--- test/models/article.coffee
+++ test/models/article.coffee
@@ -178,3 +178,21 @@
     it 'returns multiple contributing authors', ->
       @article.set 'contributing_authors', [{name: 'Molly'}, {name: 'Kana'}, {name: 'Christina'}]
       @article.byline().should.equal 'Molly, Kana and Christina'
+
+  describe 'contributingByline', ->
+
+    it 'returns an empty string when there are no contributing authors', ->
+      @article.set 'contributing_authors', []
+      @article.contributingByline().should.equal ''
+
+    it 'returns the contributing author name if there is one', ->
+      @article.set 'contributing_authors', [{name: 'Molly'}]
+      @article.contributingByline().should.equal 'Molly'
+
+    it 'returns "and" with two contributing authors', ->
+      @article.set 'contributing_authors', [{name: 'Molly'}, {name: 'Kana'}]
+      @article.contributingByline().should.equal 'Molly and Kana'
+
+    it 'returns multiple contributing authors', ->
+      @article.set 'contributing_authors', [{name: 'Molly'}, {name: 'Kana'}, {name: 'Christina'}]
+      @article.contributingByline().should.equal 'Molly, Kana and Christina'


Removed unnecessary job data

Removed unnecessary job data

--- sample.coffee
+++ sample.coffee
@@ -469,8 +469,7 @@
                      metadata: file.metadata
                   job = new Job myJobs, 'makeThumb',
                      owner: file.metadata._auth.owner
-                     inputFileURL: Meteor.absoluteUrl("#{myData.baseURL[1..]}/#{file._id}")
-                     outputFileURL: Meteor.absoluteUrl("#{myData.baseURL[1..]}/put/#{outputFileId}")
+                     # These Id values are used by the worker to read and write the correct files for this job.
                      inputFileId: file._id
                      outputFileId: outputFileId
                   if jobId = job.delay(5000).retry({ wait: 20000, retries: 5 }).save()
@@ -525,7 +524,6 @@

             inStream = myData.findOneStream { _id: job.data.inputFileId }
             unless inStream
-               outStream.releaseLock()
                job.fail 'Input file not found'
                return cb()


.rendered is not a template helper!

.rendered is not a template helper!

--- sample.coffee
+++ sample.coffee
@@ -13,7 +13,6 @@
       { method: 'get', path: '/:_id', lookup: (params, query) -> return { _id: params._id }},
       { method: 'put', path: '/put/:_id', lookup: (params, query) -> return { _id: params._id }}
    ]}
-   # Define a GET API that uses the md5 sum id files
 )

 myJobs = JobCollection 'queue', { idGeneration: 'MONGO' }
@@ -126,6 +125,12 @@
       else
          name

+   shortFilename = (w = 16) ->
+      shorten this.filename, w
+
+   isImage = () ->
+      imageTypes[this.contentType]?
+
    Template.top.helpers
       loginToken: () ->
          Meteor.userId()
@@ -138,14 +143,17 @@
          return "active" if pill is "#{this}"

    fileTableHelpers =
+      dataEntries: () ->
+         # Reactively populate the table
+         this.find({}, {sort:{filename: 1}})
+
       owner: () ->
          this.metadata?._auth?.owner

       id: () ->
          "#{this._id}"

-      shortFilename: (w = 16) ->
-         shorten this.filename, w
+      shortFilename: shortFilename

       uploadStatus: () ->
          percent = Session.get "#{this._id}"
@@ -160,21 +168,22 @@
       uploadProgress: () ->
          percent = Session.get "#{this._id}"

-      isImage: () ->
-         imageTypes[this.contentType]?
+      isImage: isImage

       altMessage: () ->
          if this.length isnt 0
             "Processing thumbnail of #{shorten this.filename, 20}..."
          else
             "Uploading #{shorten this.filename, 20}..."
+
+   Template.fileTable.helpers fileTableHelpers

    fileTableEvents =
       # Wire up the event to remove a file by clicking the `X`
       'click .del-file': (e, t) ->
          t.data.remove this._id

-   Template.gallery.helpers fileTableHelpers
+   Template.fileTable.events fileTableEvents

    Template.gallery.helpers
       dataEntries: () ->
@@ -184,23 +193,18 @@
       thumb: () ->
          "#{this.metadata.thumb}"

-      rendered: () ->
-         # This assigns a file drop zone to the "file table"
-         this.data.resumable.assignDrop $(".#{myData.root}DropZone")
+      isImage: isImage
+
+      shortFilename: shortFilename
+
+   Template.gallery.rendered = () ->
+      # This assigns a file drop zone to the "file table"
+      this.data.resumable.assignDrop $(".#{myData.root}DropZone")

    Template.fileControls.events
       'click .remove-files': (e, t) ->
          console.log "Removing all files"
          this.find({}).forEach ((d) -> this.remove(d._id)), this
-
-   Template.fileTable.helpers fileTableHelpers
-
-   Template.fileTable.helpers
-      dataEntries: () ->
-         # Reactively populate the table
-         this.find({}, {sort:{filename: 1}})
-
-   Template.fileTable.events fileTableEvents

    jobTableEvents =
       'click .cancel-job': (e, t) ->


Don't give Awebot points.

Don't give Awebot points.

--- scripts/points.coffee
+++ scripts/points.coffee
@@ -104,8 +104,8 @@
             people = flatten([chat.chatters.moderators, chat.chatters.staff, chat.chatters.admins, chat.chatters.global_mods, chat.chatters.viewers])

             for username in people
-                points[username] ?= 0
-                points[username] += 5
+                points[username] ?= 0 unless username = "awebot"
+                points[username] += 5 unless username = "awebot"
                 save(robot)

                 # Winners (and then recall) is equal to all users


New 'onAttrsChange' parameters support added

--- style.coffee
+++ style.coffee
@@ -36,25 +36,25 @@
 		if @file.isRendered
 			@updateText()

-	attrsChangeListener = (e) ->
-		if e.name is 'neft:style'
+	attrsChangeListener = (name, oldValue) ->
+		if name is 'neft:style'
 			@reloadItem()
 			if @file.isRendered
 				@render()
 				@findItemParent()
-		else if e.name is 'href' and @isLink()
+		else if name is 'href' and @isLink()
 			@item?.linkUri = @getLinkUri()

 		if @file.isRendered
-			if e.name is 'class'
-				@syncClassAttr e.oldValue
-
-			return unless @attrs?.hasOwnProperty(e.name)
-			value = @node.attrs.get e.name
+			if name is 'class'
+				@syncClassAttr oldValue
+
+			return unless @attrs?.hasOwnProperty(name)
+			value = @node.attrs.get name
 			if @file.funcs?.hasOwnProperty value
 				log.warn "Dynamic listening on Renderer events is not supported"
 				return
-			@setAttr e.name, value
+			@setAttr name, value

 	reloadItemsRecursively = (style) ->
 		style.reloadItem()


add duplicate name check on local config loading

--- src/module/config/local_config.coffee
+++ src/module/config/local_config.coffee
@@ -40,6 +40,9 @@
 			if _fStat.isDirectory()
 				@_loadDir(_fName)
 			else if path.extname(_fName) == ".json"
+				_key = path.basename(_fName, ".json")
+				if ConfigMap[_key]
+					Log.error("local file name : #{_key} is duplicate!!!! please check!!")
 				ConfigMap[path.basename(_fName, ".json")] = require(_fName)
 		return