Minor changes to the compiler

Minor changes to the compiler

--- library/membrane/visual.coffee
+++ library/membrane/visual.coffee
@@ -107,15 +107,15 @@
     # implementation for greater understanding of what it does exactly.
     compileContext: (context) ->
         [x, c, j] = ["text/css", "stylesheet", "text/javascript"]
+        script = (s) -> ""
+        style = (s) -> ""
         sheet = (s) -> "<link rel=\x22#{c}\x22 href=\x22#{s}\x22>"
-        style = (s) -> ""
-        script = (s) -> ""
         source = (s) -> ""
         template = "%s%s"
-        sheets = _.map(context.sheets, sheet).join new String
-        styles = _.map(context.styles, style).join new String
-        scripts = _.map(context.scripts, script).join new String
-        sources = _.map(context.sources, source).join new String
+        scripts = _.map(context.scripts, script).join String()
+        sources = _.map(context.sources, source).join String()
+        sheets = _.map(context.sheets, sheet).join String()
+        styles = _.map(context.styles, style).join String()
         joined = sheets + styles + scripts + sources
         format template, context.doctype, joined


useful corrections to the streamers

useful corrections to the streamers

--- library/nucleus/compose.coffee
+++ library/nucleus/compose.coffee
@@ -69,18 +69,19 @@
     # each defined in the peer of the inheritance tree. Basically this
     # is a utility to asynchronously call super methods down the stack.
     # Each method must call its last parameter `next` for proceeding.
+    # Use `async` error propagation mechanism to break out of stream.
     Object.defineProperty Object::, "downstream",
-        enumerable: no, value: (definition) -> =>
+        enumerable: no, value: (def) -> (args...) =>
             fx = (f) => (a...) => f.apply @, cc(a)
             cc = (a) -> _.toArray(args).concat(a)
-            assert _.isPlainObject definition
-            targeted = _.head _.keys definition
-            callback = _.head _.values definition
+            malformed = "no POJO style definition"
+            assert _.isPlainObject def, malformed
+            assert targeted = _.head _.keys def
+            assert callback = _.head _.values def
             hierarchy = @constructor.hierarchy()
             assert hierarchy.unshift @constructor
-            assert _.isArguments args = arguments
             resolve = (c) -> c.prototype?[targeted]
-            threads = _.map hierarchy, resolve
+            assert threads = _.map hierarchy, resolve
             methods = _.filter threads, _.isFunction
             prepped = _.unique methods.reverse()
             applied = _.map prepped, (fn) -> fx fn
@@ -92,18 +93,19 @@
     # each defined in the peer of the inheritance tree. Basically this
     # is a utility to asynchronously call super methods up the stack.
     # Each method must call its last parameter `next` for proceeding.
+    # Use `async` error propagation mechanism to break out of stream.
     Object.defineProperty Object::, "upstream",
-        enumerable: no, value: (definition) -> =>
+        enumerable: no, value: (def) -> (args...) =>
             fx = (f) => (a...) => f.apply @, cc(a)
             cc = (a) -> _.toArray(args).concat(a)
-            assert _.isPlainObject definition
-            targeted = _.head _.keys definition
-            callback = _.head _.values definition
+            malformed = "no POJO style definition"
+            assert _.isPlainObject def, malformed
+            assert targeted = _.head _.keys def
+            assert callback = _.head _.values def
             hierarchy = @constructor.hierarchy()
             assert hierarchy.unshift @constructor
-            assert _.isArguments args = arguments
             resolve = (c) -> c.prototype?[targeted]
-            threads = _.map hierarchy, resolve
+            assert threads = _.map hierarchy, resolve
             methods = _.filter threads, _.isFunction
             prepped = _.toArray _.unique methods
             applied = _.map prepped, (fn) -> fx fn


add metric for project history queue

add metric for project history queue

--- services/document-updater/app/coffee/ProjectHistoryRedisManager.coffee
+++ services/document-updater/app/coffee/ProjectHistoryRedisManager.coffee
@@ -2,9 +2,13 @@
 projectHistoryKeys = Settings.redis?.project_history?.key_schema
 rclient = require("redis-sharelatex").createClient(Settings.redis.documentupdater)
 logger = require('logger-sharelatex')
+metrics = require('./Metrics')

 module.exports = ProjectHistoryRedisManager =
 	queueOps: (project_id, ops..., callback = (error, projectUpdateCount) ->) ->
+		# Record metric for ops pushed onto queue
+		for op in ops
+			metrics.summary "redis.projectHistoryOps", op.length, {status: "push"}
 		multi = rclient.multi()
 		# Push the ops onto the project history queue
 		multi.rpush projectHistoryKeys.projectHistoryOps({project_id}), ops...


Don't make assumptions about the character width in specs

--- spec/atom/editor-spec.coffee
+++ spec/atom/editor-spec.coffee
@@ -52,13 +52,13 @@

     describe "when soft-wrap is enabled", ->
       beforeEach ->
-        editor.width(11 * 50)
+        otherEditor = new Editor
+        otherEditor.attachToDom()
+        charWidth = otherEditor.charWidth
+        otherEditor.remove()
+        editor.width(charWidth * 50)
         editor.setSoftWrap(true)
         editor.attachToDom()
-        # this verifies the assumption made above in setting the editor's width
-        # charWidth isn't be calculated until it's the editor is on the DOM, but
-        # we need to ensure that the maxLineLength is recalculated when we attach.
-        expect(editor.charWidth).toBe 11

       it "wraps lines that are too long to fit within the editor's width, adjusting cursor positioning accordingly", ->
         expect(editor.lines.find('.line').length).toBe 16


fix cache urls

fix cache urls

--- lib/assets/javascripts/_request_manager.js.coffee
+++ lib/assets/javascripts/_request_manager.js.coffee
@@ -9,6 +9,9 @@
       urlsCached:[]
     }
   cleanCache: (url)->
+    exists = @cache.urlsCached.indexOf(url)
+    if exists!=-1
+      @cache.urlsCached.splice(exists,1)
     @cache.urlsCached.push(url)
     if @cache.urlsCached.length > @cache.cacheNumber
       dUrl = @cache.urlsCached.shift()


utils: check for existence of tag.lazyNode to filter out non-existent nodes

--- client/Main/utils.coffee
+++ client/Main/utils.coffee
@@ -225,7 +225,9 @@
     return  str unless tokenMatches = str.match /\|.+?\|/g

     tagMap = {}
-    data.tags?.forEach (tag) -> tagMap[tag.getId()] = tag
+    data.tags?.forEach (tag) ->
+      unless tag.lazyNode?
+        tagMap[tag.getId()] = tag

     viewParams = []
     for tokenString in tokenMatches


Added another test for sorted sequences.

--- spec/finger_tree_spec.coffee
+++ spec/finger_tree_spec.coffee
@@ -169,3 +169,10 @@

   it "should merge correctly", ->
     expect(asArray t1.merge(t2)).toEqual [0, 1, 2, 3, 4, 5, 6, 7, 7, 8, 9]
+
+describe "A pair of single-element sorted sequences", ->
+  t1 = SortedSeq.build 8
+  t2 = SortedSeq.build 4
+
+  it "should merge correctly", ->
+    expect(asArray t1.merge(t2)).toEqual [4, 8]


allow scrolling in modal for long shopping carts

--- public/app.coffee
+++ public/app.coffee
@@ -22,11 +22,13 @@
     @_modal = picoModal
       content: view.el
       modalStyles:
+        "background-color": "white"
+        "max-height": "80%"
+        "max-width": "1000px"
         "min-width": "500px"
-        "max-width": "1000px"
+        "overflow": "auto"
+        "padding": "20px"
         "width": "95%"
-        "background-color": "white"
-        "padding": "20px"
     @_modal.show()

   @closeModal: ->


mark twins

mark twins

--- server/models/message.coffee
+++ server/models/message.coffee
@@ -10,6 +10,7 @@
     normSubject: String      # normalized subject (no Re: ...)
     conversationID: String   # all message in thread have same conversationID
     mailboxIDs: (x) -> x     # mailboxes as an hash {boxID:uid, boxID2:uid2}
+    hasTwin: (x) -> x        # [String] mailboxIDs where this message has twin
     flags: (x) -> x          # [String] flags of the message
     headers: (x) -> x        # hash of the message headers
     from: (x) -> x           # array of {name, address}
@@ -110,17 +111,22 @@
             log.debug "        add"
             existing.addToMailbox box, uid, callback
         else if existing
-            log.debug "        evil twin, ignore"
             # this is the weird case when a message is in the box
             # under two different UIDs
-            # @TODO : maybe mark the message to handle modification of
-            # such messages
-            return callback null
+            log.debug "        twin"
+            existing.markTwin box, uid, callback
         else
             log.debug "        fetch"
             setTimeout ->
                 box.imap_fetchOneMail uid, callback
             , 50
+
+Message::markTwin = (box, uid, callback) ->
+    hasTwin = @hasTwin or []
+    return callback null unless box.id in hasTwin
+    hasTwin.push box.id
+    @updateAttributes changes: {hasTwin}, callback
+

 # Public: get the uids present in a box in cozy
 #


Added lots of logging.

Added lots of logging.

--- src/kato-http-api.coffee
+++ src/kato-http-api.coffee
@@ -23,6 +23,8 @@
     katoRoomURL: process.env.HUBOT_KATO_ALL_ROOM_HTTP_POST_URL

   robot.on 'kato-http-post', (katoHttpPost) ->
+    console.log("Event detected:")
+    console.log(katoHttpPost)
     sendMessage(katoHttpPost)

   sendMessage = (katoHttpPost) ->
@@ -34,10 +36,15 @@
       renderer: 'markdown'
     })

+    console.log("Data object inside function:")
+    console.log(data)
+
     robot.http(config.secrets.katoRoomURL)
       .header('Content-Type', 'application/json')
       .post(data) (err, res, body) ->
         if err
-          robot.emit 'error', err, res, body
+#          robot.emit 'error', err, res, body
+          console.log("HTTP error:")
+          console.log(err, res, body)
           return
         robot.emit res, body


Collaboration: Improve optional parameter handling

--- client/ide/routes.coffee
+++ client/ide/routes.coffee
@@ -96,7 +96,10 @@
     KD.getSingleton('router').handleRoute "/IDE/#{machineLabel}/#{workspaceSlug}"

-  routeToLatestWorkspace = ({ params : { machineLabel } }) ->
+  routeToLatestWorkspace = (options = {}) ->
+
+    {params}       = options
+    {machineLabel} = params  if params

     # we assume that if machineLabel is all numbers it is the channelId - SY
     return loadCollaborativeIDE machineLabel  if machineLabel and /^[0-9]+$/.test machineLabel
@@ -147,4 +150,4 @@

       { machineLabel, workspaceSlug, username, channelId } = data.params

-      loadWorkspace machineLabel, workspaceSlug, username, channelId+      loadWorkspace machineLabel, workspaceSlug, username, channelId


Collaboration: Remove reference to obsolete `owner` field

--- client/ide/routes.coffee
+++ client/ide/routes.coffee
@@ -125,7 +125,7 @@
       try
         for workspace in KD.userWorkspaces when workspace.channelId is channel.id
           machine   = (KD.userMachines.filter (m) -> m.uid is workspace.machineUId)[0]
-          username  = if workspace.owner then workspace.owner else KD.nick()
+          username  = KD.nick()
           channelId = channel.id
           return loadIDE { machine, workspace, username, channelId }


Remove extra slash

Remove extra slash

--- client/lib/avatar.coffee
+++ client/lib/avatar.coffee
@@ -4,7 +4,7 @@
 	if not username?
 		return

-	return "#{Meteor.absoluteUrl()}/avatar/#{username}.jpg?_dc=#{random}"
+	return "#{Meteor.absoluteUrl()}avatar/#{username}.jpg?_dc=#{random}"

 @updateAvatarOfUsername = (username) ->
 	key = "avatar_random_#{username}"


Fix Specs On Windows

Fix Specs On Windows

--- spec/text-buffer-spec.coffee
+++ spec/text-buffer-spec.coffee
@@ -12,11 +12,13 @@
   buffer = null

   beforeEach ->
+    temp.track()
     jasmine.addCustomEqualityTester(require("underscore-plus").isEqual)
     # When running specs in Atom, setTimeout is spied on by default.
     jasmine.useRealClock?()

   afterEach ->
+    buffer?.destroy()
     buffer = null

   describe "construction", ->
@@ -1055,7 +1057,8 @@
     [filePath, newPath, bufferToChange, eventHandler] = []

     beforeEach (done) ->
-      filePath = join(__dirname, "fixtures", "manipulate-me")
+      tempDir = temp.mkdirSync('text-buffer')
+      filePath = join(tempDir, "manipulate-me")
       newPath = "#{filePath}-i-moved"
       fs.writeFileSync(filePath, "")
       bufferToChange = new TextBuffer({filePath, load: false})
@@ -1090,7 +1093,8 @@
     [filePath, bufferToChange, eventHandler] = []

     beforeEach (done) ->
-      filePath = join(__dirname, "fixtures", "manipulate-me")
+      tempDir = temp.mkdirSync('text-buffer')
+      filePath = join(tempDir , "manipulate-me")
       fs.writeFileSync(filePath, "")
       bufferToChange = new TextBuffer({filePath, load: false})
       eventHandler = jasmine.createSpy('eventHandler')
@@ -1117,7 +1121,7 @@
         done()

     afterEach ->
-      buffer.destroy()
+      buffer?.destroy()

     it "does not notify ::onDidChange observers when the file is written via TextBuffer::save", (done) ->
       buffer.insert([0,0], "HELLO!")
@@ -1130,28 +1134,27 @@
       , 30)

     describe "when the buffer is in an unmodified state before the file is modified on disk", ->
-      [changeHandler] = []
+      [event1, event2] = []
       beforeEach (done) ->
         calls = 0
-        changeHandler = jasmine.createSpy('changeHandler').and.callFake ->
+        buffer.onDidChange (args) ->
           calls = calls + 1
-          done() if calls >= 2
-        buffer.onDidChange changeHandler
+          event1 = args if calls is 1
+          event2 = args if calls is 2
+          done() if calls > 1
         fs.writeFileSync(filePath, "second")
-        expect(changeHandler.calls.count()).toBe 0
+        expect(calls).toBe(0)

       it "changes the in-memory contents of the buffer to match the new disk contents and notifies ::onDidChange observers", ->
-        [event] = changeHandler.calls.allArgs()[0]
-        expect(event.oldRange).toEqual [[0, 0], [0, 0]]
-        expect(event.newRange).toEqual [[0, 0], [0, 6]]
-        expect(event.oldText).toBe ""
-        expect(event.newText).toBe "second"
-
-        [event] = changeHandler.calls.allArgs()[1]
-        expect(event.oldRange).toEqual [[0, 6], [0, 11]]
-        expect(event.newRange).toEqual [[0, 6], [0, 6]]
-        expect(event.oldText).toBe "first"
-        expect(event.newText).toBe ""
+        expect(event1.oldRange).toEqual [[0, 0], [0, 0]]
+        expect(event1.newRange).toEqual [[0, 0], [0, 6]]
+        expect(event1.oldText).toBe ""
+        expect(event1.newText).toBe "second"
+
+        expect(event2.oldRange).toEqual [[0, 6], [0, 11]]
+        expect(event2.newRange).toEqual [[0, 6], [0, 6]]
+        expect(event2.oldText).toBe "first"
+        expect(event2.newText).toBe ""

         expect(buffer.isModified()).toBeFalsy()

@@ -1183,7 +1186,8 @@
     [filePath, bufferToDelete] = []

     beforeEach (done) ->
-      filePath = join(temp.dir, 'atom-file-to-delete.txt')
+      tempDir = temp.mkdirSync()
+      filePath = join(tempDir, 'atom-file-to-delete.txt')
       fs.writeFileSync(filePath, 'delete me')
       bufferToDelete = new TextBuffer({filePath, load: false})
       filePath = bufferToDelete.getPath() # symlinks may have been converted
@@ -1192,7 +1196,7 @@
         done()

     afterEach ->
-      bufferToDelete.destroy()
+      bufferToDelete?.destroy()

     describe "when the file is modified", ->
       beforeEach (done) ->


Update spec based on new interface for Grammar::tokenizeLine

--- spec/text-editor-spec.coffee
+++ spec/text-editor-spec.coffee
@@ -4112,8 +4112,9 @@

       runs ->
         grammar = atom.grammars.selectGrammar("text.js")
-        {tokens} = grammar.tokenizeLine("var i; // http://github.com")
-
+        {line, tags} = grammar.tokenizeLine("var i; // http://github.com")
+
+        tokens = atom.grammars.decodeContent(line, tags)
         expect(tokens[0].value).toBe "var"
         expect(tokens[0].scopes).toEqual ["source.js", "storage.modifier.js"]


#542 wrote spec based on current behavior with FIXME memo

--- spec/text-object-spec.coffee
+++ spec/text-object-spec.coffee
@@ -504,6 +504,51 @@
         it "case-2 normal", -> check close, 'd', text: textFinal, cursor: [0, 2]
         it "case-3 visual", -> check open, 'v', {selectedText}
         it "case-4 visual", -> check close, 'v', {selectedText}
+
+      describe "change mode to characterwise", ->
+        # FIXME last "\n" should not be selected
+        textSelected = """
+        __1,
+        __2,
+        __3\n
+        """.replace(/_/g, ' ')
+
+
+        beforeEach ->
+          set
+            textC: """
+            {
+              |1,
+              2,
+              3
+            }
+            """
+          ensure mode: 'normal'
+
+        it "from vC, final-mode is 'characterwise'", ->
+          ensure 'v',
+            selectedText: ['1']
+            mode: ['visual', 'characterwise']
+          ensure 'i B',
+            selectedText: textSelected
+            mode: ['visual', 'characterwise']
+
+        it "from vL, final-mode is 'characterwise'", ->
+          ensure 'V',
+            selectedText: ["  1,\n"]
+            mode: ['visual', 'linewise']
+          ensure 'i B',
+            selectedText: textSelected
+            mode: ['visual', 'linewise'] # FIXME to 'characterwise'
+
+        it "from vB, final-mode is 'characterwise'", ->
+          ensure 'ctrl-v',
+            selectedText: ["1"]
+            mode: ['visual', 'blockwise']
+          ensure 'i B',
+            selectedText: textSelected
+            mode: ['visual', 'blockwise'] # FIXME to 'characterwise'
+
     describe "a-curly-bracket", ->
       beforeEach ->
         set
@@ -535,6 +580,51 @@
         it "case-2 normal", -> check close, 'd', text: textFinal, cursor: [0, 1]
         it "case-3 visual", -> check open, 'v', {selectedText}
         it "case-4 visual", -> check close, 'v', {selectedText}
+
+      describe "change mode to characterwise", ->
+        textSelected = """
+          {
+            1,
+            2,
+            3
+          }
+          """
+        beforeEach ->
+          set
+            textC: """
+            {
+              |1,
+              2,
+              3
+            }
+
+            hello
+            """
+          ensure mode: 'normal'
+
+        it "from vC, final-mode is 'characterwise'", ->
+          ensure 'v',
+            selectedText: ['1']
+            mode: ['visual', 'characterwise']
+          ensure 'a B',
+            selectedText: textSelected
+            mode: ['visual', 'characterwise']
+        it "from vL, final-mode is 'characterwise'", ->
+          ensure 'V',
+            selectedText: ["  1,\n"]
+            mode: ['visual', 'linewise']
+          ensure 'a B',
+            selectedText: textSelected
+            mode: ['visual', 'linewise'] # FIXME to 'characterwise'
+
+        it "from vB, final-mode is 'characterwise'", ->
+          ensure 'ctrl-v',
+            selectedText: ["1"]
+            mode: ['visual', 'blockwise']
+          ensure 'a B',
+            selectedText: textSelected
+            mode: ['visual', 'blockwise'] # FIXME to 'characterwise'
+
   describe "AngleBracket", ->
     describe "inner-angle-bracket", ->
       beforeEach ->


better abstract method error

better abstract method error

--- src/loader/loader.coffee
+++ src/loader/loader.coffee
@@ -1,7 +1,7 @@
 class AbstractMethodError extends Error
-    constructor: ->
+    constructor: (name) ->
         @name = "AbstractMethodError"
-        @message = "Calling abstract method detected."
+        @message = "Calling abstract method `#{name}` detected."

 class UndeclaredRequireError extends Error
     constructor: (@self_name, @require_name) ->
@@ -103,7 +103,7 @@
         @this = {}
         @window = @get_window()
         @errors = []
-    render: ->  throw new AbstractMethodError()
+    render: ->  throw new AbstractMethodError("render")
     run: ->
         code = @render()
         func = new Function(code)
@@ -112,12 +112,12 @@
         return null if @errors.length > 0
         return @_make()
     get_window: -> return __proto__: window
-    get_require: -> throw new AbstractMethodError()
+    get_require: -> throw new AbstractMethodError("get_require")
     _fail: (reason) ->
         @errors.push(reason)
         throw reason
-    _check: (result) -> throw new AbstractMethodError()
-    _make: -> throw new AbstractMethodError()
+    _check: (result) -> throw new AbstractMethodError("_check")
+    _make: -> throw new AbstractMethodError("_make")

 class CJSEvaluator extends BasicEvaluator
     constructor: (options) ->


Fix: `Vim::enterNormalMode` should reset `@keys`

Otherwise keys accumulated in other modes might be interpreted when going
back to normal mode. For example, following a link with a hint ending with
"a" and then directly trying to follow another link, would do so in a new
tab. That last "a" was still in `@keys` when returning to normal mode, so
when trying to use the "f" command, it was interpreted as "af" instead.

--- extension/packages/vim.coffee
+++ extension/packages/vim.coffee
@@ -25,6 +25,7 @@
     return if @mode == MODE_NORMAL
     @modes[@mode].onEnterNormalMode?(this, @storage.modes[@mode])
     @mode = MODE_NORMAL
+    @keys.length = 0

   onInput: (keyStr, event) ->
     @keys.push(keyStr)


Fixed rendering, now nodes are shown on top of edges

--- public/coffeescripts/renderer.coffee
+++ public/coffeescripts/renderer.coffee
@@ -52,11 +52,12 @@
     ctx.fillStyle = 'white'
     ctx.fillRect 0, 0, canvas.width, canvas.height

+    particleSystem.eachEdge (edge, fromPoint, toPoint) =>
+      @drawEdge(edge, fromPoint, toPoint)
+
     particleSystem.eachNode (node, point) =>
       @drawNode(node, point)

-    particleSystem.eachEdge (edge, fromPoint, toPoint) =>
-      @drawEdge(edge, fromPoint, toPoint)

   drawNode: (node, point) ->
       MARGIN = 10


fetchRelativeComments

fetchRelativeComments

--- server/app/bongo/models/messages/post.coffee
+++ server/app/bongo/models/messages/post.coffee
@@ -32,7 +32,7 @@
       instance        : [
         'on','reply','restComments','commentsByRange'
         'like','fetchLikedByes','mark','unmark','fetchTags'
-        'delete','modify'
+        'delete','modify','fetchRelativeComments'
       ]
     schema            : schema
     relationships     :
@@ -360,6 +360,16 @@
     .endGraphlet()
     .fetchRoot callback

+  fetchRelativeComments:({limit, before, after}, callback)->
+    limit ?= 10
+    if before? and after?
+      callback new KodingError "Don't use before and after together."
+    selector = timestamp:
+      if before? then  $lt: before
+      else if after? then $gt: after
+    options = {limit, sort: timestamp: 1}
+    @fetchComments selector, options, callback
+
   commentsByRange:(options, callback)->
     [callback, options] = [options, callback] unless callback
     {from, to} = options
@@ -375,7 +385,7 @@
       queryOptions = skip: from
       if to
         queryOptions.limit = to - from
-    queryOptions.sort = timestamp: -1
+    queryOptions.sort = timestamp: 1
     @fetchComments selector, queryOptions, callback

   restComments:(skipCount, callback)->


Better bahaviour for when FILE in `zappa FILE` does not exist. No longer simply dies with an exception but prints a more friendly error before a process.exit(-1). Untested, but all examples appear to function correctly against the change.

--- bin/zappa.coffee
+++ bin/zappa.coffee
@@ -69,11 +69,18 @@

 if args.length > 0
   file = args[0]
-  if options.compile then compile file
-  else
-    if options.watch
-      remove_watch_option()
-      spawn_child()
-      watch file
+
+  path.exists file, (exists) ->
+    if !exists
+      console.log "#{file} not found."
+      process.exit -1
+
+    if options.compile
+      compile file
     else
-      zappa.run_file file, options
+      if options.watch
+        remove_watch_option()
+        spawn_child()
+        watch file
+      else
+        zappa.run_file file, options


Implementing new routes model.

Implementing new routes model.

--- www/src/theoricus/core/router.coffee
+++ www/src/theoricus/core/router.coffee
@@ -3,7 +3,9 @@

 StringUril = require 'theoricus/utils/string_util'
 Route = require 'theoricus/core/route'
-History = require ':history'
+Routes = require 'app/config/routes'
+
+require ':history'

 Factory = null

@@ -24,7 +26,7 @@
   constructor:( @the, @on_change )->
     Factory = @the.factory

-    for route, opts of app.routes
+    for route, opts of Routes.routes
       @map route, opts.to, opts.at, opts.el, @

     History.Adapter.bind window, 'statechange', =>
@@ -32,7 +34,7 @@

     setTimeout =>
       url = window.location.pathname
-      url = app.root if url == "/"
+      url = Routes.root if url == "/"
       @run url
     , 1

@@ -67,7 +69,7 @@
       url = "/#{url}" if (url.slice 0, 1) isnt '/'

       # fallback to root url in case user enter the '/'
-      url = app.root if url == "/"
+      url = Routes.root if url == "/"

       for route in @routes
         if route.matcher.test url


Allow { focus } option for changes

Allow { focus } option for changes

--- lib/assets/javascripts/unpoly/classes/change/update_layer.coffee
+++ lib/assets/javascripts/unpoly/classes/change/update_layer.coffee
@@ -224,6 +224,7 @@
         unless @layer.isRoot() && up.fragment.targetsBody(selector)
           oldElement = @layer.element.children[0]

+      # Each step inherits all options of this change.
       return u.merge(@options, { selector, placement, reveal, oldElement })

   findOld: ->
@@ -258,7 +259,7 @@
     if @focus == 'keep'
       for step in @steps
         if step.placement == 'swap'
-          step.focus = up.Focus.preserveWithin(step.oldElement)
+          step.focus ?= up.Focus.preserveWithin(step.oldElement)

   containedByRivalStep: (steps, candidateStep) ->
     return u.some steps, (rivalStep) ->


Added method to revoke authorization

Added method to revoke authorization

--- src/octonode/auth.coffee
+++ src/octonode/auth.coffee
@@ -26,13 +26,26 @@
     @options = options
     return this

+  revoke: (id, callback) ->
+    if @mode == @modes.cli
+      request
+        url: "https://#{@options.username}:#{@options.password}@api.github.com/authorizations/#{id}"
+        method: 'DELETE'
+      , (err, res, body) ->
+        if err? or res.statusCode isnt 204 then callback(err or new Error(JSON.parse(body).message)) else callback(null)
+    else
+      callback new Error('Cannot revoke authorization in web mode')
+
   login: (scopes, callback) ->
     if @mode == @modes.cli
+      if scopes instanceof Array
+        scopes = JSON.stringify scopes: scopes
+      else
+        scopes = JSON.stringify scopes
       request
         url: "https://#{@options.username}:#{@options.password}@api.github.com/authorizations"
         method: 'POST'
-        body: JSON.stringify
-          "scopes": scopes
+        body: scopes
         headers:
           'Content-Type': 'application/json'
       , (err, res, body) ->


Simply accept an ID on initialization

Simply accept an ID on initialization

--- models/channel.coffee
+++ models/channel.coffee
@@ -29,12 +29,8 @@
         options.url = "#{API_URL}/channels/#{@id}"
     super

-  initialize: ({ channel_slug, username }) ->
-    @slug = channel_slug
-    @username = username
-
   slugOrId: ->
-    @slug or @id
+    @id or @get('slug')

   shareHref: ->
     "#{APP_URL}/share/#{@get('share_link')}"


Re-connect on error to avoid the error to persist

--- components/Client.coffee
+++ components/Client.coffee
@@ -3,7 +3,7 @@
 noflo = require("noflo")

 # Pool all queries through a single connection for each database
-client = {}
+clients = {}

 class Client extends noflo.Component

@@ -42,17 +42,18 @@
       @sqls.push data

     @inPorts.in.on "disconnect", =>
+      client = clients[@url]
       token = @token
       query = _.flatten(@sqls).join(";\n")

-      unless client[@url]?
+      unless client?
         throw new Error "Server connection has not yet been established"
       unless token?
         throw new Error "Missing token for return connection"
       unless query?
         throw new Error "Missing query to execute"

-      result = client[@url].query query
+      result = client.query query

       # Send row forward
       result.on "row", (row, result) =>
@@ -60,14 +61,18 @@

       # Send to error port
       result.on "error", (e) =>
-        e.query = query
         unless @outPorts.error.isAttached()
           throw new Error "No error port attached"

+        e.query = query
         @outPorts.error.send e
         @outPorts.error.disconnect()

+        # Re-connect to avoid persisting errors
+        @startServer @url
+
       result.on "end", (result) =>
+        rows = result?.rows or []
         @outPorts.out.beginGroup token
         if _.isEmpty rows
           @outPorts.out.send null
@@ -78,13 +83,13 @@

   startServer: (url) ->
     @endServer url
-    client[url] = new pg.Client url
-    client[url].connect()
+    clients[url] = new pg.Client url
+    clients[url].connect()

   endServer: (url) ->
     if url?
-      client[url]?.end()
+      clients[url]?.end()
     else
-      client[url].end() for url in _.keys client
+      clients[url].end() for url in _.keys clients

 exports.getComponent = -> new Client


create id before upsert

create id before upsert

--- api/apps/articles/model.coffee
+++ api/apps/articles/model.coffee
@@ -96,7 +96,7 @@
 # Persistence

 @save = (data, callback) ->
-  id = ObjectId data._id
+  id = ObjectId(data._id) or new ObjectId()
   whitelisted = _.pick data, _.keys schema
   whitelisted.author_id = whitelisted.author_id?.toString()
   Joi.validate whitelisted, schema, (err, data) ->
@@ -110,7 +110,7 @@
       data.slugs = data.slugs.concat [slug] unless slug in data.slugs
       db.articles.update { _id: id }, data, { upsert: true }, (err, res) ->
         return callback err if err
-        callback null, _.extend data, _id: res.upserted?[0]?._id or id
+        callback null, _.extend data, _id: id

 @syncToPost = (article, accessToken, callback) ->


Added topic changing to Campfire adapter.

--- src/adapters/campfire.coffee
+++ src/adapters/campfire.coffee
@@ -14,6 +14,10 @@

   reply: (user, strings...) ->
     @send user, strings.map((str) -> "#{user.name}: #{str}")...
+
+  topic: (user, strings...) ->
+    @bot.Room(user.room).speak "Changing topic: #{strings.join}"
+    @bot.topic(strings.join("\n"))

   run: ->
     self = @
@@ -108,6 +112,9 @@
     paste: (text, callback) ->
       @message text, "PasteMessage", callback

+    topic: (text, callback) ->
+      self.put "/room/#{id}", text, callback
+
     sound: (text, callback) ->
       @message text, "SoundMessage", callback

@@ -184,6 +191,9 @@
   post: (path, body, callback) ->
     @request "POST", path, body, callback

+  put: (path, body, callback) ->
+    @request "PUT", path, body, callback
+
   request: (method, path, body, callback) ->
     headers =
       "Authorization" : @authorization


Update responsive_table.coffee

Update responsive_table.coffee

Removed the extra `.` in selector

--- vendor/assets/javascripts/responsive_table.coffee
+++ vendor/assets/javascripts/responsive_table.coffee
@@ -6,6 +6,6 @@
       $("tr td:nth-child(#{index + 1})", $(this).closest('thead').next('tbody')).attr('data-label', label)
   )

-  $('table.table-responsive-xs, table..table-responsive-sm').each ->
+  $('table.table-responsive-xs, table.table-responsive-sm').each ->
     $(this).trigger('set-label')
-) jQuery+) jQuery


Fix tests

Fix tests

--- spec/spell-check-spec.coffee
+++ spec/spell-check-spec.coffee
@@ -181,7 +181,7 @@
           atom.commands.dispatch editorElement, 'spell-check:correct-misspelling'
           expect(editorElement.querySelectorAll('.corrections').length).toBe 1
           expect(editorElement.querySelectorAll('.corrections li').length).toBe 0
-          expect(editorElement.querySelector('.corrections').textContent).toBe "No corrections"
+          expect(editorElement.querySelector('.corrections').textContent).toMatch /No corrections/

   describe "when the editor is destroyed", ->
     it "destroys all misspelling markers", ->


last lint

last lint

--- lib/eslogger.coffee
+++ lib/eslogger.coffee
@@ -44,7 +44,7 @@
               @robot.logger.warning res.statusCode
               @robot.logger.warning body

-  getLogs = (room, start, stop, cb) ->
+  getLogs: (room, start, stop, cb) ->
     # would be good to replace this with a filter, we don't need relevance
     query = {
       query: {


Added Lua

Added Lua

--- lib/grammars.coffee
+++ lib/grammars.coffee
@@ -80,9 +80,8 @@
     command: "scala"
     "Selection Based": (code) -> ['-e', code]
     "File Based": (filename) -> [filename]
-
+
   Lua:
     command: "lua"
     "Selection Based": (code) -> ['-e', code]
     "File Based": (filename) -> [filename]
-


Even better deletion method.

Even better deletion method.

--- lib/handlers.coffee
+++ lib/handlers.coffee
@@ -4,13 +4,16 @@
     if rawdata[0] is 127
       if (share.typed.length - share.right) isnt 0
         ui.save 'cur'
-        burst = share.typed.split('')
-        burst.splice share.typed.length - share.right - 1, 1
-        share.typed = burst.join ''
-        ui
-          .prompt share.typed
+
+        share.typed = ((typed)->
+          index = share.typed.length - share.right
+          return typed.slice(0, index - 1) + typed.slice(index)
+        )(share.typed)
+
+        ui.prompt share.typed
           .restore 'cur'
           .left 1
+
       return true

     # Arrow


TTL cache function.

TTL cache function.

--- lib/memoized.coffee
+++ lib/memoized.coffee
@@ -11,3 +11,18 @@
       else
         memos[key] = base.apply(this, arguments)

+# TTL cached
+this.ttlCache = (ttl) ->
+    (base) ->
+        t = null
+        cachedVal = null
+        ->
+          if not cachedVal?
+            t = new Date().getTime
+            cachedVal = base.apply(this, arguments)
+          else
+            _temp = new Date().getTime
+            if t + ttl < _temp
+                t = _temp
+                cachedVal = base.apply(this, arguments)
+          cachedVal


Fix descendant roots during superdelete

Fix descendant roots during superdelete

--- lib/messages.coffee
+++ lib/messages.coffee
@@ -1106,6 +1106,20 @@
         Messages.update child,
           $unset: root: ''
         _submessagesChanged child
+        descendants = descendantMessageIds child
+        if descendants.length > 0
+          Messages.update
+            _id: $in: descendants
+          ,
+            $set: root: child
+          ,
+            multi: true
+          EmojiMessages.update
+            message: $in: descendants
+          ,
+            $set: root: child
+          ,
+            multi: true

     ## Delete all associated files.
     MessagesDiff.find


Fix bug in canSee where authors couldn't see their own deleted posts.

--- lib/messages.coffee
+++ lib/messages.coffee
@@ -61,7 +61,8 @@
   else if groupRoleCheck group, 'read', user
     ## Regular users can see all messages they authored, plus
     ## published undeleted messages by others.
-    message.published and not message.deleted
+    (message.published and not message.deleted) or
+    user.username of (message.authors ? {})
   else
     false


:bug: guard for range is null

:bug: guard for range is null

--- lib/operator.coffee
+++ lib/operator.coffee
@@ -105,7 +105,7 @@
         unless @patternForOccurence
           {pattern, bufferRange} = @getPatternAndBufferRangeForOccurrence()
           @patternForOccurence = pattern
-          if scanRanges?.length and not @isMode('visual', 'blockwise')
+          if scanRanges?.length and bufferRange? and not @isMode('visual', 'blockwise')
             lastRangeIndex = scanRanges.length - 1
             scanRanges[lastRangeIndex] = scanRanges[lastRangeIndex].union(bufferRange)


Use GET to get Campfire rooms, not POST

Use GET to get Campfire rooms, not POST

There's no need to use POST when fetching a room from Campfire, and
doing so risks running up against Campfire's POST rate limit (50 POSTs
in 5 seconds).

--- src/adapters/campfire.coffee
+++ src/adapters/campfire.coffee
@@ -96,7 +96,7 @@
     logger = @robot.logger

     show: (callback) ->
-      self.post "/room/#{id}", "", callback
+      self.get "/room/#{id}", "", callback

     join: (callback) ->
       self.post "/room/#{id}/join", "", callback


Add run command to start execution

Add run command to start execution

--- lib/terminal.coffee
+++ lib/terminal.coffee
@@ -40,6 +40,7 @@
         unset: @unsetCommand
         clear: @clearCommand
         topkek: @topkekCommand
+        run: @runCommand

     @focused = false
     @refCount = 0
@@ -378,6 +379,10 @@
   topkekCommand: =>
     @writeLn(terminalUtils.TOPKEK)

+  runCommand: =>
+    workspaceView = atom.views.getView(atom.workspace)
+    atom.commands.dispatch(workspaceView,'levels:start-execution')
+
   # ## Locking and unlocking the terminal ----------------------------------------
   #
   # isBusy: ->


load() that is what I was looking for! makes it a lot more robust

--- meteor/client/mtrPlayer.coffee
+++ meteor/client/mtrPlayer.coffee
@@ -44,10 +44,13 @@

   setChannel: (channel) ->
     @readyState = @networkState = null
-    Session.set 'channel', ''
+    Session.set 'channel', channel

-    # Attempt at making things more robust
-    Meteor.setTimeout (-> Session.set 'channel', channel), 20
+    @audioTag()?.pause()
+    Meteor.setTimeout (=>
+      @audioTag().load()
+      @audioTag().play()
+    ), 10

     location.href = '#' + channel
     document.title = channel + ' | radio.meteor.com'
@@ -57,7 +60,7 @@
     newNetworkState = @audioTag().networkState
     newReadyState = @audioTag().readyState

-    @audioTag().volume = 1
+    # @audioTag().volume = 1
     @play()

     myCL ['checkStatus net', newNetworkState, @networkState,


Client: Activity: Updated PostPane to pass onScrollThresholdReached and thread props for infinite scroll event handling

--- client/activity/lib/components/postpane/index.coffee
+++ client/activity/lib/components/postpane/index.coffee
@@ -25,13 +25,17 @@

     ActivityFlux.actions.message.createComment @message('id'), event.value

+  onScrollThresholdReached: (event) ->
+    console.log 'loadComments '

   render: ->
     <ChatPane
+      thread={@props.thread}
       className="PostPane"
       title={"#{@channel 'name'}/#{@message 'slug'}"}
       messages={@props.messages}
       onSubmit={@bound 'onSubmit'}
+      onScrollThresholdReached={@bound 'onScrollThresholdReached'}
     />


client: align equals

client: align equals

--- client/app/lib/desktopnotificationscontroller.coffee
+++ client/app/lib/desktopnotificationscontroller.coffee
@@ -1,6 +1,6 @@
-_ = require 'lodash'
-kd = require 'kd'
-Promise = require 'bluebird'
+_                    = require 'lodash'
+kd                   = require 'kd'
+Promise              = require 'bluebird'
 browserNotifications = require('browser-notifications')(Promise)

 ###*
@@ -59,7 +59,7 @@

 defaultNotificationOptions =
-  iconUrl: '/a/images/logos/share_logo.png'
-  title: ''
-  message: ''
+  iconUrl : '/a/images/logos/share_logo.png'
+  title   : ''
+  message : ''


comment

comment

--- client/landing/app/MainApp/ApplicationManager.coffee
+++ client/landing/app/MainApp/ApplicationManager.coffee
@@ -1,3 +1,4 @@
+# uncomplicate this - Sinan 7/2012
 class ApplicationManager extends KDObject
   constructor: ->
     @controllers            = {}


add artists field to artworks

add artists field to artworks

--- node/fabricate.coffee
+++ node/fabricate.coffee
@@ -46,7 +46,11 @@
       title: 'Skull'
       artist:
         id: 'andy-warhol'
-        name: "Andy Warhol"
+        name: 'Andy Warhol'
+      artists: [
+        id: 'andy-warhol'
+        name: 'Andy Warhol'
+      ]
       images: [
         fabricate 'artwork_image'
         fabricate 'artwork_image', is_default: true


do not invoke setAlwaysOnTop(...) in vain

When we call setAlwaysOnTop(x) and the current on-top state is already x, this has the side effect of bringing the
window to front.  Avoid that by remembering the on-top state and comparing x with it.

This fixes http://mantis.dyalog.com/view.php?id=12452

--- init-nw.coffee
+++ init-nw.coffee
@@ -73,8 +73,9 @@
         height: +urlParams.height
     else
       D.floatingWindows = []; D.floatOnTop = 0
-      nww.on 'focus', -> (for x in D.floatingWindows then x.setAlwaysOnTop !!D.floatOnTop); return
-      nww.on 'blur',  -> (for x in D.floatingWindows then x.setAlwaysOnTop false         ); return
+      aot = (x) -> (for w in D.floatingWindows when w.aot != x then w.aot = x; w.setAlwaysOnTop x); return
+      nww.on 'focus', -> aot !!D.floatOnTop; return
+      nww.on 'blur',  -> aot false; return
       if localStorage.pos then try
         pos = JSON.parse localStorage.pos
         restoreWindow nww, x: pos[0], y: pos[1], width: pos[2], height: pos[3]


check raw socket instead of sails socket

--- www/js/controllers/msg.coffee
+++ www/js/controllers/msg.coffee
@@ -76,13 +76,14 @@
 						$scope.$apply 'collection.models'
 			addMsg: ->
 				msg = new resource.Msg type: type, to: chat.jid, body: ''
-				connected = new Promise (resolve, reject) ->
-					if io.socket?.connected
-						resolve()
-					else
-						io.socket?.once 'connect', resolve
-						io.socket?.once 'error', reject
-				connected
+				Promise
+					.try ->
+						socket = io.socket?._raw
+						if socket?.connected
+							Promise.resolve()
+						else
+							socket?.once 'connect', Promise.resolve
+							socket?.once 'error', Promise.reject
 					.then ->
 						collection.add msg
 						$ionicScrollDelegate.scrollTop true


Remove unnecessary fat arrow

Remove unnecessary fat arrow

--- src/chaplin/lib/route.coffee
+++ src/chaplin/lib/route.coffee
@@ -200,7 +200,7 @@
     # Replace the optional portion with a non-capturing and optional group.
     "(?:#{portion})?"

-  replaceParams: (s, callback) =>
+  replaceParams: (s, callback) ->
     # Parse :foo and *bar, replacing via callback.
     s.replace paramRegExp, callback


Создание массивов

Создание массивов


Client: Activity: Deleted onInputKeydown event handler to prevent creating double channel when keypress enter

--- client/activity/lib/components/createprivatechannelmodal/index.coffee
+++ client/activity/lib/components/createprivatechannelmodal/index.coffee
@@ -297,14 +297,6 @@
     element.focus()

-  onInputKeydown: (event) ->
-
-    { ENTER } = KeyboardKeys
-
-    if event.which == ENTER
-      @createChannel()
-
-
   renderNickname: (participant, isNicknameVisible)->

     nickname = participant.getIn ['profile', 'nickname']
@@ -397,7 +389,7 @@
             Name
              (optional)

-          <input className='Reactivity-input' value={@state.name} maxlength='20' onChange={@bound 'setName'} onKeyDown={@bound 'onInputKeydown'}/>
+          <input className='Reactivity-input' value={@state.name} maxlength='20' onChange={@bound 'setName'}/>

             This is how this conversation is going to appear on your sidebar.

@@ -407,7 +399,7 @@
             Purpose
              (optional)

-          <input className='Reactivity-input' value={@state.purpose} maxlength='200' onChange={@bound 'setPurpose'} onKeyDown={@bound 'onInputKeydown'}/>
+          <input className='Reactivity-input' value={@state.purpose} maxlength='200' onChange={@bound 'setPurpose'}/>

             Set a purpose to your conversation that describes what it will be used for.


Home: Logo to use router while loggedout

--- client/Main/maincontroller/mainviews/mainview.coffee
+++ client/Main/maincontroller/mainviews/mainview.coffee
@@ -46,9 +46,10 @@
       partial   : '<cite></cite>'
       click     : (event)=>
         KD.utils.stopDOMEvent event
+        {router} = KD.getSingletons
         if KD.isLoggedIn()
-        then KD.getSingleton('router').handleRoute "/Activity", {entryPoint}
-        else location.replace '/'
+        then router.handleRoute '/Activity', {entryPoint}
+        else router.handleRoute '/'

     @headerContainer.addSubView @logo


DefineStackView: add warnings for userInput types

--- client/admin/lib/views/stacks/definestackview.coffee
+++ client/admin/lib/views/stacks/definestackview.coffee
@@ -294,30 +294,48 @@

     requiredData  = requirementsParser template
     errors        = []
+    warnings      = []

     fetchUserData = (callback) ->
       account.fetchFromUser requiredData.user, (err, data) ->
         kd.warn err  if err
         callback data ? {}

+    process = (data, type) ->
+
+      if data.length > 0
+        console.warn "#{type.capitalize()} for preview requirements: ", data
+
+        issueList = ''
+        for issue in data
+          issueList += " - #{issue}\n"
+
+        issues = "> Following #{type} found while generating
+                  preview for this template: \n#{issueList}"
+      else
+        issues = ''
+
+      return issues
+
+
     generatePreview = =>

       for type, data of requiredData
+
         for field in data
+
+          if type is 'userInput'
+            warnings.push "Variable `#{field}` will be requested from user."
+            continue
+
           if content = jspath.getAt availableData[type], field
             search   = ///\${var.koding_#{type}_#{field}}///g
             template = template.replace search, content
           else
             errors.push "Variable `#{field}` not found in `#{type}` data."

-      if errors.length > 0
-        console.warn "Errors for preview requirements: ", errors
-
-        errors = " - #{error}\n" for error in errors
-        errors = "> Following errors found while generating
-                  preview for this template: \n#{errors}"
-      else
-        errors = ''
+      errors   = process errors, 'errors'
+      warnings = process warnings, 'warnings'

       new kd.ModalView
         title          : 'Template Preview'
@@ -328,6 +346,8 @@
         overlayOptions : cssClass : 'second-overlay'
         content        : applyMarkdown """
           #{errors}
+
+          #{warnings}
           ```coffee
           #{template}
           ```


update related discussion when add new embed

--- resources/assets/coffee/react/beatmap-discussions-history/main.coffee
+++ resources/assets/coffee/react/beatmap-discussions-history/main.coffee
@@ -42,6 +42,7 @@

     discussions = @state.discussions
     users = @state.users
+    relatedDiscussions = @state.relatedDiscussions

     discussionIds = _.map discussions, 'id'
     userIds = _.map users, 'id'
@@ -56,6 +57,8 @@
         # The discussion list shows discussions started by the current user, so it can be assumed that the first post is theirs
         newDiscussion.starting_post = newDiscussion.posts[0]
         discussions.push(newDiscussion)
+      else
+        relatedDiscussions.push(newDiscussion)

     _.each beatmapset.related_users, (newUser) ->
       if userIds.includes(newUser.id)
@@ -67,6 +70,7 @@
     @setState
       discussions: _.reverse(_.sortBy(discussions, (d) -> Date.parse(d.starting_post.created_at)))
       users: users
+      relatedDiscussions: relatedDiscussions

   discussions: =>


Edit Spec T004

Edit Spec T004

--- spec/src/coffee/label_category/models/t004_label_category_spec.coffee
+++ spec/src/coffee/label_category/models/t004_label_category_spec.coffee
@@ -199,10 +199,15 @@
       it "C001: check", ->
         destroy_event_spy = sinon.spy()
         @root.on "destroy", destroy_event_spy
+        destroy_event_spy.called.should.equal false
         @root.children("1").children("1").children("1").destroy()
+        destroy_event_spy.called.should.equal false
         @root.children("1").children("1").children("2").destroy()
+        destroy_event_spy.called.should.equal false
         @root.children("1").children("1").children("3").destroy()
+        destroy_event_spy.called.should.equal false
         @root.children("1").children("1").children("4").destroy()
+        destroy_event_spy.called.should.equal false
         @root.children("1").children("1").children("5").destroy()
         destroy_event_spy.called.should.equal true


JWorkspace: Added machineLabel to model.

--- workers/social/lib/social/models/workspace.coffee
+++ workers/social/lib/social/models/workspace.coffee
@@ -11,6 +11,7 @@
       name         : String
       slug         : String
       machineUId   : String
+      machineLabel : String
       rootPath     : String
       owner        : String
       layout       : Object
@@ -27,7 +28,7 @@
     data.owner = client.connection.delegate._id
     data.slug  = Inflector.slugify data.name?.toLowerCase()

-    {name, slug, machineUId, rootPath, owner, layout} = data
+    {name, slug, machineUId, rootPath, owner, layout, machineLabel} = data

     JWorkspace.one { slug }, (err, workspace) ->
       return callback err, null  if err
@@ -57,9 +58,9 @@
           name  = "#{name} #{seed}"
           slug  = "#{slug}-#{seed}"

-          create_ { name, slug, machineUId, rootPath, owner, layout }, callback
+          create_ { name, slug, machineUId, machineLabel, rootPath, owner, layout }, callback
       else
-        create_ { name, slug, machineUId, rootPath, owner, layout }, callback
+        create_ { name, slug, machineUId, machineLabel, rootPath, owner, layout }, callback

   create_ = (data, callback) ->
     workspace  = new JWorkspace data


Add “len” check

Add “len” check

--- src/formula_evaluator.coffee
+++ src/formula_evaluator.coffee
@@ -563,6 +563,11 @@
     --start
     if start < 0 || len < 0
       @error(ERROR_VALUE)
+
+    #start + len が文字列の長さを超える場合は補正する
+    if start + len > multibyteLength(str)
+      len = multibyteLength(str) - start
+
     prefix_space = ""
     #2バイト文字の途中から始まっていた場合
     if multibyteLength(mbsubstr(str, 0, start)) < start


Fix source maps.

Fix source maps.

--- src/fs_utils/generate.coffee
+++ src/fs_utils/generate.coffee
@@ -154,7 +154,7 @@
         data += "\n/*@ sourceMappingURL=#{base}*/"

     common.writeFile path, data, ->
-      if map and config.modules.sourceMaps
+      if map and config.sourceMaps
         common.writeFile "#{path}.map", map.toString(), callback
       else
         callback()


Remove logging

Remove logging

--- framer/VekterTextLayer.coffee
+++ framer/VekterTextLayer.coffee
@@ -73,7 +73,6 @@
 		size =
 			width: rect.right - rect.left
 			height: rect.bottom - rect.top
-		console.log @element
 		return size

 class StyledTextBlock


Just call it, no need to pass null.

Just call it, no need to pass null.

--- app/client/js/react/app.coffee
+++ app/client/js/react/app.coffee
@@ -22,10 +22,10 @@

       render: ->
         div className: 'app',
-          header.create null
+          header.create()
           switch routes.active
-            when routes.home then home.create null
-            when routes.newSong then editSong.create null
-            when routes.song then song.create null
-            when routes.notFound then notFound.create null
-          footer.create null+            when routes.home then home.create()
+            when routes.newSong then editSong.create()
+            when routes.song then song.create()
+            when routes.notFound then notFound.create()
+          footer.create()


added index definitions

added index definitions

--- app/coffee/MongoManager.coffee
+++ app/coffee/MongoManager.coffee
@@ -73,3 +73,7 @@
 		db.projectHistoryMetaData.ensureIndex { project_id: 1 }, { background: true }
 		# TTL index for auto deleting week old temporary ops
 		db.docHistory.ensureIndex { expiresAt: 1 }, { expireAfterSeconds: 0, background: true }
+		# For finding packs to be checked for archiving
+		db.docHistory.ensureIndex { last_checked: 1 }, { background: true }
+		# For finding archived packs
+		db.docHistoryIndex.ensureIndex { project_id: 1 }, { background: true }


fix shopping cart position

fix shopping cart position

--- public/views/widget_view.coffee
+++ public/views/widget_view.coffee
@@ -9,7 +9,6 @@

-

Loading ...


improved status command

improved status command

--- example/commands/status.command.coffee
+++ example/commands/status.command.coffee
@@ -1,24 +1,24 @@
 # load the config file
 config = require '../config.coffee'

-# setTimeout hack to wait for fchat client module to initialize before we require it
-setTimeout ->
-  # get the fchat client
-  fchat = require '../lib/fchat.coffee'
-  # wait for fchat client to connect to fchat server and for f-chat to send identity
-  fchat.on 'IDN', ->
-    # get the status from the config file
-    status = config 'status'
-    # if no status valid saved, default to
-    unless /^(online|looking|busy|dnd|idle|away|crown)$/.test status then status = 'online'
-    # get the status message from the config file
-    statusmsg = config 'statusmsg'
-    # if no status saved, default to
-    statusmsg or= "#{config 'character'} is ONLINE!"
-    # send the saved status message to the server
-    fchat.send 'STA', { status, statusmsg }
+# get the fchat client
+fchat = require '../lib/fchat.coffee'

-    console.log "#{status}, #{statusmsg}"
+# wait for fchat client to connect to fchat server and for f-chat to send identity
+fchat.on 'IDN', ->
+  # get the status from the config file
+  status = config 'status'
+  # if no status valid saved, default to
+  unless /^(online|looking|busy|dnd|idle|away|crown)$/.test status then status = 'online'
+  # get the status message from the config file
+  statusmsg = config 'statusmsg'
+  # if no status saved, default to
+  statusmsg or= "#{config 'character'} is ONLINE!"
+
+  # send the saved status message to the server
+  fchat.send 'STA', { status, statusmsg }
+
+  console.log "STATUS: #{status}, #{statusmsg}"

 module.exports =
   '!status': ->
@@ -36,7 +36,7 @@
       (match, statusMatch) ->
         # save the real status to status veriable
         status = statusMatch.toLowerCase()
-        # replaces the status with nothing to return the statusmsg
+        # replaces the status with nothing so that it does show up in statusmsg
         ''

     # Send status to chat server using https://wiki.f-list.net/FChat_client_commands#STA


Add collection generators.

Add collection generators.

--- src/commands/scaffold.coffee
+++ src/commands/scaffold.coffee
@@ -15,6 +15,8 @@
   backbone:
     modelTest: 'javascripts'
     model: 'javascripts'
+    collectionTest: 'javascripts'
+    collection: 'javascripts'
     template: 'templates'
     style: 'stylesheets'
     viewTest: 'javascripts'
@@ -26,6 +28,8 @@
     controller: 'javascripts'
     modelTest: 'javascripts'
     model: 'javascripts'
+    collectionTest: 'javascripts'
+    collection: 'javascripts'
     template: 'templates'
     style: 'stylesheets'
     viewTest: 'javascripts'
@@ -48,6 +52,14 @@
         generator('modelTest', name)
       )

+    collectionTest: (name) ->
+      [sysPath.join(config.paths.test, 'models', "#{helpers.pluralize name}_test")]
+
+    collection: (name) ->
+      [sysPath.join(config.paths.app, 'models', "#{helpers.pluralize name}")].concat(
+        generator('collectionTest', name)
+      )
+
     template: (name) ->
       [sysPath.join(config.paths.app, 'views', 'templates', "#{name}")]

@@ -66,6 +78,7 @@

     scaffold: (name) ->
       generator('model', name).concat(
+        generator('collection', name),
         generator('view', name)
       )

@@ -93,6 +106,14 @@
         generator('modelTest', name)
       )

+    collectionTest: (name) ->
+      [sysPath.join(config.paths.test, 'models', "#{helpers.pluralize name}_test")]
+
+    collection: (name) ->
+      [sysPath.join(config.paths.app, 'models', "#{helpers.pluralize name}")].concat(
+        generator('collectionTest', name)
+      )
+
     template: (name) ->
       [sysPath.join(config.paths.app, 'views', 'templates', "#{name}")]

@@ -111,7 +132,9 @@

     scaffold: (name) ->
       generator('controller', name).concat(
-        generator('model', name), generator('view', name)
+        generator('model', name),
+        generator('collection', name),
+        generator('view', name)
       )

 getGenerator = (config, plugins) ->


Fix multiple postfix conditionals

Fix multiple postfix conditionals

--- grammar.coffee
+++ grammar.coffee
@@ -592,7 +592,7 @@
   ['right',     '=', ':', 'COMPOUND_ASSIGN', 'RETURN', 'THROW', 'EXTENDS']
   ['right',     'FORIN', 'FOROF', 'BY', 'WHEN']
   ['right',     'IF', 'ELSE', 'FOR', 'WHILE', 'UNTIL', 'LOOP', 'SUPER', 'CLASS']
-  ['right',     'POST_IF']
+  ['left',      'POST_IF']
 ]

 # Wrapping Up


simplifying the grammar by replacing all of our trailing comma rules with an OptComma nonterminal.

--- grammar.coffee
+++ grammar.coffee
@@ -185,6 +185,12 @@
     o "=>",                                     -> 'boundfunc'
   ]

+  # An optional, trailing comma.
+  OptComma: [
+    o ''
+    o ','
+  ]
+
   # The list of parameters that a function accepts can be of any length.
   ParamList: [
     o "",                                       -> []
@@ -249,10 +255,8 @@

   # In CoffeeScript, an object literal is simply a list of assignments.
   Object: [
-    o "{ AssignList }",                         -> new ObjectNode $2
-    o "{ IndentedAssignList }",                 -> new ObjectNode $2
-    o "{ AssignList , }",                       -> new ObjectNode $2
-    o "{ IndentedAssignList , }",               -> new ObjectNode $2
+    o "{ AssignList OptComma }",                -> new ObjectNode $2
+    o "{ IndentedAssignList OptComma }",        -> new ObjectNode $2
   ]

   # Assignment of properties within an object literal can be separated by
@@ -267,8 +271,7 @@

   # An **AssignList** within a block indentation.
   IndentedAssignList: [
-    o "INDENT AssignList OUTDENT",              -> $2
-    o "INDENT AssignList , OUTDENT",            -> $2
+    o "INDENT AssignList OptComma OUTDENT",     -> $2
   ]

   # Class definitions have optional bodies of prototype property assignments,
@@ -320,14 +323,12 @@

   # The list of arguments to a function call.
   Arguments: [
-    o "CALL_START ArgList CALL_END",            -> $2
-    o "CALL_START ArgList , CALL_END",          -> $2
+    o "CALL_START ArgList OptComma CALL_END",   -> $2
   ]

   # Calling super.
   Super: [
-    o "SUPER CALL_START ArgList CALL_END",      -> new CallNode 'super', $3
-    o "SUPER CALL_START ArgList , CALL_END",    -> new CallNode 'super', $3
+    o "SUPER CALL_START ArgList OptComma CALL_END", -> new CallNode 'super', $3
   ]

   # A reference to the *this* current object.
@@ -355,8 +356,7 @@

   # The array literal.
   Array: [
-    o "[ ArgList ]",                            -> new ArrayNode $2
-    o "[ ArgList , ]",                          -> new ArrayNode $2
+    o "[ ArgList OptComma ]",                   -> new ArrayNode $2
   ]

   # The **ArgList** is both the list of objects passed into a function call,
@@ -370,8 +370,7 @@
     o "ArgList TERMINATOR Expression",          -> $1.concat [$3]
     o "ArgList , TERMINATOR Expression",        -> $1.concat [$4]
     o "ArgList , INDENT Expression",            -> $1.concat [$4]
-    o "ArgList OUTDENT"
-    o "ArgList , OUTDENT"
+    o "ArgList OptComma OUTDENT"
   ]

   # Just simple, comma-separated, required arguments (no fancy syntax). We need


Maintenance: Local history log consistency bugfix

--- app/assets/javascripts/app/lib/app_post/interface_handle.coffee
+++ app/assets/javascripts/app/lib/app_post/interface_handle.coffee
@@ -84,7 +84,8 @@
           history = App.Config.get('History')
           if history[10]
             history.shift()
-          history.push window.location.hash
+
+          history.push '#' + params.match.input

           # execute controller
           controller = (params) =>


Add support for janky show/delete

Add support for janky show/delete

--- src/scripts/janky.coffee
+++ src/scripts/janky.coffee
@@ -83,6 +83,9 @@
     cb e, 500, "Client Error"
   req.end(bodyParams)

+del = (path, params, cb) ->
+  post path, params, cb, 'DELETE'
+
 module.exports = (robot) ->
   robot.respond /ci\??$/i, (msg) ->
     get "help", { }, (err, statusCode, body) ->
@@ -180,3 +183,23 @@
   robot.router.post "/janky", (req, res) ->
     robot.messageRoom req.body.room, req.body.message
     res.end "ok"
+
+  robot.respond /ci show ([-_\.0-9a-zA-Z]+)/i, (msg) ->
+    app = msg.match[1]
+    get "show/#{app}", { }, (err, statusCode, body) ->
+      if statusCode == 200
+        repo = JSON.parse(body)
+        lines = for key, val of repo
+          "#{key}: #{val}"
+       response = lines.join("\n")
+        msg.send response
+      else
+        replyMsg = "Error F7U12: Can't show: #{statusCode}: #{body}"
+        msg.reply replyMsg
+
+  robot.respond /ci delete ([-_\.0-9a-zA-Z]+)/i, (msg) ->
+    app = msg.match[1]
+    del "#{app}", {}, (err, statusCode, body) ->
+      if statusCode != 200
+        msg.reply "I got an error removing #{app}; sometimes deleting all the old commits/branches times out the unicorn. Maybe
+      msg.send body


Added threshold configuration comment

Added threshold configuration comment

--- app/assets/javascripts/lib/mobile/autocomplete_mobile.js.coffee
+++ app/assets/javascripts/lib/mobile/autocomplete_mobile.js.coffee
@@ -9,6 +9,7 @@
 #     id                      : [string] The target form element
 #     uri                     : [string] The search endpoint
 #     scope                   : [string] (Optional) Value to specify as the scope of the search
+#     threshold               : [number] (Optional) The number of characters required before searching
 #     throttle                : [number] (Optional) Time in ms to throttle requests to search endpoint
 #     resultsClass            : [string] (Optional) Class name for results list element
 #     resultItemClass         : [string] (Optional) Class name for result list item element


Use default attributes that make it clear that it is a new record.

--- app/assets/javascripts/modules/timelet/models/timelet.js.coffee
+++ app/assets/javascripts/modules/timelet/models/timelet.js.coffee
@@ -4,5 +4,5 @@
   localStorage: new Backbone.LocalStorage 'Timelets'
   defaults:
     running: false
-    duration: 30
-    name: 'My Timelet'
+    duration: '--'
+    name: 'New timelet'


Change copy function name.

Change copy function name.

--- app/javascripts/helpers.coffee
+++ app/javascripts/helpers.coffee
@@ -2,24 +2,33 @@
   helpers = ng.module 'qiprofile.helpers', []

   helpers.factory 'Helpers', ->
-    # Copies the given source detail object properties into the
-    # destination object, with the following exception:
-    # * fields which begin with an underscore are not copied
+    # Copies the given source object properties into the
+    # destination object, with the following exceptions:
+    # * properties which begin with an underscore are not copied
     #   (including the _id field)
-    copyDetail: (source, dest) ->
+    # * null source property values are not copied
+    #
+    # @param source the copy source object
+    # @param dest the copy destination object
+    copyNonNullPublicProperties: (source, dest) ->
+      # @param obj the object to check
+      # @returns the properties which do not begin with an
+      #   underscore and have a non-null value
+      nonNullPublicProperties = (obj) ->
+        Object.keys(obj).filter (prop) -> obj[prop]? and prop[0] != '_'
+
       # Copy the detail properties into the parent object.
-      srcProps = Object.getOwnPropertyNames(source)
-      destProps = Object.getOwnPropertyNames(dest)
-      fields = _.difference(srcProps, destProps)
-      for field in fields
-        if field[0] != '_'
-          dest[field] = source[field]
+      srcProps = nonNullPublicProperties(source)
+      destProps = nonNullPublicProperties(dest)
+      copyProps = _.difference(srcProps, destProps)
+      for prop in copyProps
+        dest[prop] = source[prop]

-    # If the given attribute value is a string, then this function
-    # resets it to the parsed date.
-    fixDate: (obj, attr) ->
-      date = obj[attr]
-      # Silly Javascript idiom for string type testing.
-      if typeof date is 'string' or date instanceof String
-        # Reset the attribute to a date.
-        obj[attr] = moment(date)
+    # If the value of the given property is a string, then this
+    # function resets it to the parsed date.
+    #
+    # @param obj the object containing the date property
+    # @param property the date property
+    fixDate: (obj, property) ->
+      date = obj[property]
+      obj[property] = moment(date) if _.isString(date)


Add missing page classes.

Add missing page classes.

--- app/client/js/react/app.coffee
+++ app/client/js/react/app.coffee
@@ -82,6 +82,7 @@
           when mySongs then 'home'
           when notFoundPage then 'notfound'
           when songPage then 'song'
+          when songsPage, recentlyUpdatedSongsPage then 'songs'
           when trashPage then 'trash'

       componentDidMount: ->


Sleppa óstafnæmis-flag í dataset eigindum

--- app/scripts/extended.coffee
+++ app/scripts/extended.coffee
@@ -38,7 +38,7 @@
             localizer = localize($scope)
             # console.log("getStructValues data", data)

-            dataset = _.map (_.uniq data), (item) ->
+            dataset = _.map (_.uniq data), (item) ->
                 if item is ""
                     return [item, util.getLocaleString "empty"]
                 return [item, localizer item]
@@ -67,6 +67,7 @@
         template: selectTemplate
         controller: ["$scope", ($scope) ->
             localizer = localize($scope)
+            delete $scope.orObj.flags?["c"]
             if _.isArray $scope.dataset
                 dataset = _.map $scope.dataset, (item) -> return [item, localizer item]
             else


ssl change port

ssl change port

--- app/scripts/controllers.coffee
+++ app/scripts/controllers.coffee
@@ -191,8 +191,8 @@
           if archive.flvpath

             $scope.ame_artwork_gallery.media.push({
-              picture : archive.flvthumb.replace("http://", "https://")
-              medium_url : $sce.trustAsResourceUrl(archive.flvpath.replace("http://", "https://"))
+              picture : archive.flvthumb.replace("http://", "https://").replace(":80", ":443")
+              medium_url : $sce.trustAsResourceUrl(archive.flvpath.replace("http://", "https://").replace(":80", ":443"))
               description : archive.field8 #media ame title
            })


Fix ShareEntry propTypes.

Fix ShareEntry propTypes.

--- entry/ShareEntry.cjsx
+++ entry/ShareEntry.cjsx
@@ -34,9 +34,9 @@
         when 'email'
             body = """
                 #{ entry.title }
-                by #{ entry.byline }
+                #{ if entry.byline then "by #{ entry.byline }" else '' }

-                #{ entry.summary }
+                #{ entry.summary or '' }

                 #{ entry.full_link }
             """
@@ -49,23 +49,26 @@
     displayName: 'ShareEntry'

     propTypes:
-        services: React.PropTypes.oneOf([
-                'linkedin'
-                'facebook'
-                'twitter'
-                'googleplus'
-                'pinterest'
-                'appdotnet'
-                'email'
-            ])
+        services: (props, prop_name, component_name) ->
+            for service in props[prop_name]
+                unless service in [
+                    'linkedin'
+                    'facebook'
+                    'twitter'
+                    'googleplus'
+                    'pinterest'
+                    'appdotnet'
+                    'email'
+                ]
+                    return new Error('')
         entry: React.PropTypes.shape
             full_link   : React.PropTypes.string.isRequired
             cover_image : React.PropTypes.oneOfType([
                     React.PropTypes.string
                     React.PropTypes.object
-                ]).isRequired
+                ])
             title       : React.PropTypes.string.isRequired
-            summary     : React.PropTypes.string.isRequired
+            summary     : React.PropTypes.string

     render: ->
         <div className='ShareEntry'>


Simpler page-load code.

Simpler page-load code.

--- src/slide-pack-ui.coffee
+++ src/slide-pack-ui.coffee
@@ -32,6 +32,17 @@
     current = 1 if current > max
     navigate()

+  move = (event) ->
+    #Handle touch-device moves
+    #If there's exactly one finger inside this element
+    if event.targetTouches.length == 1
+      touch = event.targetTouches[0];
+      # Place element where the finger is
+      obj.style.left = touch.pageX + 'px';
+      obj.style.top = touch.pageY + 'px';
+  }
+}, false);
+
   show = (to) ->
     $slidePack.find('section.active').removeClass 'active'
     $slidePack.find('section').eq(to - 1).addClass 'active'
@@ -45,14 +56,13 @@
       # pushState/replaceState
       show event.state.current
     else
-      # page load. TODO: Decide if act different on links (i.e: save history)
-      page = (Number) window.location.hash.replace(slide_prefix,'')
-      if(current = page)
-        show current
-
+      # page load
+      current = (Number) window.location.hash.replace(slide_prefix,'')
+      show(current) if current

   installNavigationHandler = ->
     window.onpopstate = handleNavigation
+    window.onclick    = checkIf

   init : init
   prev : prev


auto pin mode

auto pin mode

--- grovepi.coffee
+++ grovepi.coffee
@@ -40,7 +40,7 @@
 			modes[port] = mode
 			send CMD.ranger, port, mode

-	send = ( cmd, port, args... ) ->
+	send = ( cmd, port, args... ) =>
 		unless cmd is CMD.mode
 			@mode port, MODES.output
 		writeArgs = [cmd]


more consitant coding style on Gruntfile

--- app/templates/Gruntfile.coffee
+++ app/templates/Gruntfile.coffee
@@ -19,28 +19,28 @@

     watch:
       gruntfile:
-        files: [ 'Gruntfile.coffee' ]<% if ( stylesLang === 'sass') { %>
+        files: [ 'Gruntfile.coffee' ]<% if ( stylesLang === 'sass' ) { %>

       styles:
         files: '<%%= config.app %>/styles/{,*/}*.{scss,sass}'
         tasks: [
           'sass'
           'autoprefixer'
-        ]<% } %><% if ( stylesLang === 'less') { %>
+        ]<% } %><% if ( stylesLang === 'less' ) { %>

       styles:
         files: '<%%= config.app %>/styles/{,*/}*.less'
         tasks: [
           'less'
           'autoprefixer'
-        ]<% } %><% if ( stylesLang === 'vanilla') { %>
+        ]<% } %><% if ( stylesLang === 'vanilla' ) { %>

       styles:
         files: [ '<%%= config.app %>/styles/{,*/}*.css' ]
         tasks: [
           'copy:styles'
           'autoprefixer'
-        ]<% } %><% if ( scriptsLang === 'coffeescript') { %>
+        ]<% } %><% if ( scriptsLang === 'coffeescript' ) { %>

       scripts:
         files: [ '<%%= config.app %>/scripts/{,*/}*.coffee' ]
@@ -48,7 +48,7 @@
           'coffeelint'
           'coffee'
           'replace:scripts'
-        ]<% } %><% if ( scriptsLang === 'javascript') { %>
+        ]<% } %><% if ( scriptsLang === 'javascript' ) { %>

       scripts:
         files: [ '<%%= config.app %>/scripts/{,*/}*.js' ]
@@ -78,7 +78,7 @@
         src: '<%%= config.dist %>'
         dot: true
       server:
-        src: '<%%= config.temp %>'<% if ( scriptsLang === 'coffeescript') { %>
+        src: '<%%= config.temp %>'<% if ( scriptsLang === 'coffeescript' ) { %>

     coffeelint:
       options:
@@ -86,12 +86,12 @@
           'level': 'ignore'
         'no_empty_param_list':
           'level': 'error'
-      files: [ '<%%= config.app %>/scripts/*.coffee' ]<% } %><% if ( scriptsLang === 'javascript') { %>
+      files: [ '<%%= config.app %>/scripts/*.coffee' ]<% } %><% if ( scriptsLang === 'javascript' ) { %>

     jshint:
       files: [ '<%%= config.app %>/scripts/*.js' ]
       options:
-        jshintrc: '.jshintrc'<% } %><% if ( stylesLang === 'sass') { %>
+        jshintrc: '.jshintrc'<% } %><% if ( stylesLang === 'sass' ) { %>

     sass:
       options:
@@ -99,14 +99,14 @@
         loadPath: '<%%= config.app %>/bower_components'
       server:
         files:
-          '<%%= config.temp %>/styles/screen.css': [ '<%%= config.app %>/styles/screen.scss' ]<% } %><% if ( stylesLang === 'less') { %>
+          '<%%= config.temp %>/styles/screen.css': [ '<%%= config.app %>/styles/screen.scss' ]<% } %><% if ( stylesLang === 'less' ) { %>

     less:
       compile:
         options:
           paths: [ '<%%= config.app %>/styles' ]
         files:
-          '<%%= config.temp %>/styles/screen.css': '<%%= config.app %>/styles/screen.less'<% } %><% if ( scriptsLang === 'coffeescript') { %>
+          '<%%= config.temp %>/styles/screen.css': '<%%= config.app %>/styles/screen.less'<% } %><% if ( scriptsLang === 'coffeescript' ) { %>

     coffee:
       jitter:
@@ -142,13 +142,13 @@
         dot: true
         cwd: '<%%= config.app %>/images'
         dest: '<%%= config.dist %>/images'
-        src: '*.{ico,png,txt,jpg}'<% if ( stylesLang === 'vanilla') { %>
+        src: '*.{ico,png,txt,jpg}'<% if ( stylesLang === 'vanilla' ) { %>
       styles:
         expand: true
         dot: true
         cwd: '<%%= config.app %>/styles'
         dest: '<%%= config.temp %>/styles'
-        src: '*.css'<% } %><% if ( scriptsLang === 'javascript') { %>
+        src: '*.css'<% } %><% if ( scriptsLang === 'javascript' ) { %>
       scripts:
         expand: true
         dot: true
@@ -198,7 +198,7 @@
           keepSpecialComments: 0
           banner: '/*! <%%= pkg.name %> - v<%%= pkg.version %> - <%%= grunt.template.today("yyyy-mm-dd") %> */'
         files:
-          '<%%= config.dist %>/styles/screen.css': [ '<%%= config.temp %>/styles/screen.css' ]<% if ( stylesLang === 'vanilla') { %>
+          '<%%= config.dist %>/styles/screen.css': [ '<%%= config.temp %>/styles/screen.css' ]<% if ( stylesLang === 'vanilla' ) { %>
           '<%%= config.dist %>/styles/inuit.css': [ '<%%= config.temp %>/styles/inuit.css' ]<% } %>

     uglify:
@@ -258,15 +258,15 @@
           livereload: false

     concurrent:
-      lint: [ <% if ( scriptsLang === 'coffeescript') { %>
-        'coffeelint'<% } %><% if ( scriptsLang === 'javascript') { %>
+      lint: [ <% if ( scriptsLang === 'coffeescript' ) { %>
+        'coffeelint'<% } %><% if ( scriptsLang === 'javascript' ) { %>
         'jshint'<% } %>
       ]
-      compile: [ <% if ( stylesLang === 'sass') { %>
-        'sass'<% } %><% if ( stylesLang === 'less') { %>
-        'less'<% } %><% if ( stylesLang === 'vanilla') { %>
-        'copy:styles'<% } %><% if ( scriptsLang === 'coffeescript') { %>
-        'coffee'<% } %><% if ( scriptsLang === 'javascript') { %>
+      compile: [ <% if ( stylesLang === 'sass' ) { %>
+        'sass'<% } %><% if ( stylesLang === 'less' ) { %>
+        'less'<% } %><% if ( stylesLang === 'vanilla' ) { %>
+        'copy:styles'<% } %><% if ( scriptsLang === 'coffeescript' ) { %>
+        'coffee'<% } %><% if ( scriptsLang === 'javascript' ) { %>
         'copy:scripts'<% } %>
         'concat'
       ]


remove if

remove if

--- app/utils/connector-rfb.coffee
+++ app/utils/connector-rfb.coffee
@@ -27,10 +27,9 @@
     @rfb.connect ENV.deviceFarmHost, ENV.deviceFarmPort, '1234', "#{ENV.deviceFarmPath}?token=#{@deviceToken}"

   resize: ->
-    if @rfb.get_display
-      display = @rfb.get_display
-      scaleRatio = display.autoscale 100, 100, true
-      @rfb.get_mouse().set_scale scaleRatio
+    display = @rfb.get_display
+    scaleRatio = display.autoscale 100, 100, true
+    @rfb.get_mouse().set_scale scaleRatio

   disconnect: ->
     @rfb.disconnect()


Added external FACTLINK_JQUERY for debugging purposes

--- app/js/jail_iframe/core.coffee
+++ app/js/jail_iframe/core.coffee
@@ -5,6 +5,8 @@
 window.$ = (selector, context) ->
   new jQuery.fn.init(selector, context || window.parent.document)

+window.parent.FACTLINK_JQUERY = window.$
+
 $.fn = $.prototype = jQuery.fn
 jQuery.extend $, jQuery


Let's try 30 FPS for a while now.

Let's try 30 FPS for a while now.

--- app/lib/surface/Surface.coffee
+++ app/lib/surface/Surface.coffee
@@ -48,7 +48,7 @@
     coords: true
     playJingle: false
     showInvisible: false
-    frameRate: 60  # Best as a divisor of 60, like 15, 30, 60, with RAF_SYNCHED timing.
+    frameRate: 30  # Best as a divisor of 60, like 15, 30, 60, with RAF_SYNCHED timing.

   subscriptions:
     'level-disable-controls': 'onDisableControls'


Add missing ngRoute

Add missing ngRoute

Add missing ngRoute

--- app/plugins/terms/terms.coffee
+++ app/plugins/terms/terms.coffee
@@ -21,7 +21,7 @@

 taiga = @.taiga

-module = angular.module("taigaPlugins")
+module = angular.module("taigaPlugins", ["ngRoute"])

 template = _.template("""
 <p class="register-text">


candidatures wording

candidatures wording

--- app/scripts/controllers.coffee
+++ app/scripts/controllers.coffee
@@ -255,9 +255,9 @@
     {title: 'Courrier', sortby: {"physical_content": 2, "physical_content_received": 3}, count:0},
     {title: 'En attente de validation', sortby: {"application_completed": 2, "application_complete": 3}, count:0},
     {title: 'Visées', sortby: {"application_complete": 2}, count:0},
-    {title: 'Admies à l\'entretien', sortby: {"selected_for_interview": 2}, count:0},
-    {title: 'Selectionnées', sortby: {"selected": 2}, count:0},
-    {title: 'Sur liste d\'attente', sortby: {"wait_listed": 2}, count:0},
+    {title: 'Selectionnés pour l\'entretien', sortby: {"selected_for_interview": 2}, count:0},
+    {title: 'Admis', sortby: {"selected": 2}, count:0},
+    {title: 'Admis sur liste d\'attente', sortby: {"wait_listed": 2}, count:0},
   ]
   $scope.select_orders = [
       {title: "Numéro d'inscription", value: {ordering: "id"}}


log params

log params

--- app/scripts/controllers.coffee
+++ app/scripts/controllers.coffee
@@ -248,6 +248,9 @@
   # init
   $scope.candidatures = []
   $scope.candidat_id = $stateParams.id
+  console.log ("Candidatures : ")
+  console.log($scope.candidat_id)
+  console.log(stateParams)
   # order
   # none = 1 | true = 2 | false = 3
   $scope.select_criteres = [


clarify gulp shell command intent by adding small amount of code

--- app/templates/_Gulpfile.coffee
+++ app/templates/_Gulpfile.coffee
@@ -78,8 +78,10 @@
 #   $SHELL
 #*------------------------------------*/
 gulp.task 'shell', shell.task [
-	"cd styleguide/public",
-	'ln -s ../../' + conf.path.assets + ' assets'
+	[
+		"cd styleguide/public",
+		"ln -s ../../#{conf.path.assets} assets"
+	].join(' && ')
 ]


noch ein trauriger versuch, bin jetzt wieder komplett bei der version, die ich vom raspberry gezogen hatte

--- hdc1008.coffee
+++ hdc1008.coffee
@@ -28,6 +28,10 @@
         description: "The measured temperature"
         type: t.number
         unit: '°C'
+      humidity:
+        description: "The actual degree of Humidity"
+        type: "number"
+        unit: '%'

     template: "temperature"

@@ -38,12 +42,16 @@
       @id = @config.id
       @name = @config.name
       @_temperature = lastState?.temperature?.value
+      @_humidity = lastState?.humidity?.value
       HDC1008 = require 'hdc1008'
       @sensor = new HDC1008({
         address: parseInt @config.address,
         device: @config.device,
-        commandTemp: 0x00, lengthTemp:2, commandHumi: 0x01, lengthHumi:2
-	 		});
+        commandTemp: 0x00,
+        lengthTemp: 2,
+        commandHumi: 0x01,
+        lengthHumi: 2
+      });
       Promise.promisifyAll(@sensor)

       super()
@@ -57,20 +65,38 @@

     requestValue: ->
       @sensor.readTemperature( (value) =>
-        #if value isnt @_temperature
+      # if value isnt @_temperature
           @_temperature = value
           @emit 'temperature', value
-        #else
-        #  env.logger.debug("Sensor value (#{value}) did not change.")
-      #).catch( (error) =>
-      #  env.logger.error(
+      # else
+       #  env.logger.debug("Sensor value (#{value}) did not change.")
+    # ).catch( (error) =>
+     #  env.logger.error(
       #    "Error reading HDC1008Sensor with address #{@config.address}: #{error.message}"
-      #  )
+      # )
       #  env.logger.debug(error.stack)
       )

+      @sensor.readHumidity( (value) =>
+     # if value isnt @_humidity
+          @_humidity = value
+          @emit 'humidity', value
+     # else
+      #  env.logger.debug("Sensor value (#{value}) did not change.")
+     # ).catch( (error) =>
+      #  env.logger.error(
+       #   "Error reading HDC1008Sensor with address #{@config.address}: #{error.message}"
+      #  )
+       # env.logger.debug(error.stack)
+
+
+      )
+
+
     getTemperature: -> Promise.resolve(@_temperature)
+    getHumidity: -> Promise.resolve(@_humidity)

   # Create a instance and return it to the framework
   myPlugin = new HDC1008Plugin
-  return myPlugin														+  return myPlugin
+


fix(web): Fixing actor library path

fix(web): Fixing actor library path

--- apps/actor-web/gulpfile.coffee
+++ apps/actor-web/gulpfile.coffee
@@ -48,7 +48,7 @@
   gulp.src ['./assets/**/*']
     .pipe gulp.dest './dist/assets/'
   gulp.src ['./bower_components/actor/**/*.js']
-    .pipe gulp.dest './dist/assets/actor'
+    .pipe gulp.dest './dist/assets/js/actor'

 gulp.task 'usemin', ->
   gulp.src ['./index.html']


Remove debug statement.

Remove debug statement.

--- src/tags/checkout.coffee
+++ src/tags/checkout.coffee
@@ -175,8 +175,6 @@
         @update()

   discount: ->
-    console.log 'discount'
-
     switch @ctx.coupon.type
       when 'flat'
         if !@ctx.coupon.productId? || @ctx.coupon.productId == ''


Send unset type as "Other"

Send unset type as "Other"

--- apps/apply/client/modes.coffee
+++ apps/apply/client/modes.coffee
@@ -3,7 +3,7 @@
     value: 'initial'
     slug: ''
     copy: 'Select Organization Type'
-    type: null
+    type: 'Other'
   gallery:
     value: 'gallery'
     slug: 'gallery'


:lipstick:

:lipstick:

--- spec/app/project-spec.coffee
+++ spec/app/project-spec.coffee
@@ -6,7 +6,7 @@
   beforeEach ->
     project.setPath(project.resolve('dir'))

-  describe "when editSession is destroyed", ->
+  describe "when an edit session is destroyed", ->
     it "removes edit session and calls destroy on buffer (if buffer is not referenced by other edit sessions)", ->
       editSession = project.buildEditSession("a")
       anotherEditSession = project.buildEditSession("a")


refactor(build option): compile.htmlImports.client.enable to keep track of the html imports per js file

--- src/plugins/gulp-compile-html-imports.coffee
+++ src/plugins/gulp-compile-html-imports.coffee
@@ -10,47 +10,71 @@
 PLUGIN_NAME = 'gulp-compile-html-imports'
 PluginError = gutil.PluginError

+# Regular Expressions
+# ===================
+Regx =
+	htmlImports:
+		/\bimport\s+(?:(.+)\s+from\s+)?[\'"`]([^`"\']+\.html)[`"\'](?=\n|\s?;|\s(?!\S))(\s*;)?/g
+
+	htmlImport: (statement) ->
+		# /import template from '..\/views\/rb-nav.html';/g
+		new RegExp statement, 'g'
+
+	templateVar: (variable) ->
+		# /\btemplate(?=\n|\s?;|\s(?!\S))/g
+		new RegExp "\\b#{variable}(?=\\n|\\s?;|\\s(?!\\S))", 'g'
+
+# Html Imports
+# { jsFilePath: { htmlFilePath: { statement:'', variable:'', html:'' }}}
+# paths     = absolute
+# statement = import template from '../views/rb-nav.html';
+# variable  = template
+# ======================================================================
+HtmlImports = {}
+
 # Compiler
 # ========
-compileImports =
-	opts: {}
-
-	htmlImports: [] # :[{}] -> { statement: '', variable: '', path: '', html: '' }
-
-	get: (file, opts) -> # :string
-		@_setOpts file, opts
+CompileImports =
+	get: (file, opts={}) -> # :string
 		js = file.contents.toString()
 		@_setHtmlImportVars file, js
-		js = @_htmlImportReplace js
-		js = @_templateVarReplace js
+		js = @_htmlImportReplace file, js
+		js = @_templateVarReplace file, js
 		js

-	_setOpts: (file, opts={}) -> # :void
-		Object.assign @opts, opts
+	_setHtmlImportVars: (file, js) -> # :void
+		jsPath      = file.path
+		htmlImports = {}

-	_setHtmlImportVars: (file, js) -> # :void
-		regx = /\bimport\s+(?:(.+)\s+from\s+)?[\'"`]([^`"\']+\.html)[`"\'](?=\n|\s?;|\s(?!\S))(\s*;)?/g
-		while (match = regx.exec(js)) != null
-			htmlImport =
-				statement: match[0]
-				variable:  match[1]
-				path:      path.join file.dirname, match[2]
-			htmlImport.html = fse.readFileSync htmlImport.path, 'utf8'
-			@htmlImports.push htmlImport
+		while (match = Regx.htmlImports.exec(js)) != null
+			statement     = match[0]
+			variable      = match[1]
+			importPath    = match[2]
+			importPathAbs = path.join file.dirname, importPath
+			try
+				html = fse.readFileSync importPathAbs, 'utf8'
+			catch e
+				e.message = "html import file in #{file.relative} doesn't exist: #{importPath}"
+				console.error e.message.error
+				continue
+			htmlImport = { statement, variable, html }
+			htmlImports[importPathAbs] = htmlImport

-	_htmlImportReplace: (js) -> # :string
-		return js unless @htmlImports.length
-		for htmlImport, i in @htmlImports
-			regx = new RegExp "#{htmlImport.statement}", 'g'
-			js = js.replace regx, ''
+		hasImports = !!Object.getOwnPropertyNames(htmlImports).length
+		return delete HtmlImports[jsPath] unless hasImports
+		HtmlImports[jsPath] = {} unless HtmlImports[jsPath]
+		HtmlImports[jsPath] = htmlImports
+
+	_htmlImportReplace: (file, js) -> # :string
+		return js unless HtmlImports[file.path]
+		for importPath, htmlImport of HtmlImports[file.path]
+			js = js.replace Regx.htmlImport(htmlImport.statement), ''
 		js

-	_templateVarReplace: (js) -> # :string
-		return js unless @htmlImports.length
-		for htmlImport, i in @htmlImports
-			# \btemplate(?=\n|\s?;|\s(?!\S))
-			regx = new RegExp "\\b#{htmlImport.variable}(?=\\n|\\s?;|\\s(?!\\S))", 'g'
-			js = js.replace regx, "`#{htmlImport.html}`"
+	_templateVarReplace: (file, js) -> # :string
+		return js unless HtmlImports[file.path]
+		for importPath, htmlImport of HtmlImports[file.path]
+			js = js.replace Regx.templateVar(htmlImport.variable), "`#{htmlImport.html}`"
 			js
 		js

@@ -63,7 +87,7 @@
 		return cb new PluginError PLUGIN_NAME, 'streaming not supported' if file.isStream()
 		return cb null, file unless file.isBuffer()
 		try
-			compiledJS    = compileImports.get file, opts
+			compiledJS    = CompileImports.get file, opts
 			file.contents = new Buffer compiledJS
 		catch e
 			return cb new PluginError PLUGIN_NAME, e


added button.close to auto-close handler

--- modules/gui/lib/widgets/Popup.coffee
+++ modules/gui/lib/widgets/Popup.coffee
@@ -97,7 +97,7 @@
                 done(err) if err

                 if floyd.system.platform is 'remote'
-                    @find('a.close').click ()=>
+                    @find('button.close, a.close').click ()=>
                         @close()

                     if @data.fade


Create valid DOM ids for stylesheets

Create valid DOM ids for stylesheets

--- src/theme-manager.coffee
+++ src/theme-manager.coffee
@@ -160,18 +160,22 @@
       """

   # Internal-only:
+  stringToId: (string) ->
+    string.replace(/\\/g, '')
+
+  # Internal-only:
   removeStylesheet: (stylesheetPath) ->
     unless fullPath = @resolveStylesheet(stylesheetPath)
       throw new Error("Could not find a file at path '#{stylesheetPath}'")
-    @stylesheetElementForId(fullPath).remove()
+    @stylesheetElementForId(@stringToId(fullPath)).remove()

   # Internal-only:
-  applyStylesheet: (id, text, ttype = 'bundled') ->
-    styleElement = @stylesheetElementForId(id)
+  applyStylesheet: (path, text, ttype = 'bundled') ->
+    styleElement = @stylesheetElementForId(@stringToId(path))
     if styleElement.length
       styleElement.text(text)
     else
       if $("head style.#{ttype}").length
-        $("head style.#{ttype}:last").after "<style class='#{ttype}' id='#{id}'>#{text}</style>"
+        $("head style.#{ttype}:last").after "<style class='#{ttype}' id='#{@stringToId(path)}'>#{text}</style>"
       else
-        $("head").append "<style class='#{ttype}' id='#{id}'>#{text}</style>"
+        $("head").append "<style class='#{ttype}' id='#{@stringToId(path)}'>#{text}</style>"


Skip deprecated selectors upgrade for base style sheets too

--- src/theme-manager.coffee
+++ src/theme-manager.coffee
@@ -132,10 +132,10 @@
   #
   # Returns a {Disposable} on which `.dispose()` can be called to remove the
   # required stylesheet.
-  requireStylesheet: (stylesheetPath, priority) ->
+  requireStylesheet: (stylesheetPath, priority, skipDeprecatedSelectorsTransformation) ->
     if fullPath = @resolveStylesheet(stylesheetPath)
       content = @loadStylesheet(fullPath)
-      @applyStylesheet(fullPath, content, priority)
+      @applyStylesheet(fullPath, content, priority, skipDeprecatedSelectorsTransformation)
     else
       throw new Error("Could not find a file at path '#{stylesheetPath}'")

@@ -181,7 +181,7 @@
     @reloadBaseStylesheets()

   reloadBaseStylesheets: ->
-    @requireStylesheet('../static/atom', -2)
+    @requireStylesheet('../static/atom', -2, true)

   stylesheetElementForId: (id) ->
     escapedId = id.replace(/\\/g, '\\\\')
@@ -236,8 +236,15 @@
   removeStylesheet: (stylesheetPath) ->
     @styleSheetDisposablesBySourcePath[stylesheetPath]?.dispose()

-  applyStylesheet: (path, text, priority) ->
-    @styleSheetDisposablesBySourcePath[path] = @styleManager.addStyleSheet(text, {priority, sourcePath: path})
+  applyStylesheet: (path, text, priority, skipDeprecatedSelectorsTransformation) ->
+    @styleSheetDisposablesBySourcePath[path] = @styleManager.addStyleSheet(
+      text,
+      {
+        priority,
+        skipDeprecatedSelectorsTransformation,
+        sourcePath: path
+      }
+    )

   activateThemes: ->
     new Promise (resolve) =>


Better logs on unknown anchors

Better logs on unknown anchors

--- src/renderer/impl/base/level1/anchors.coffee
+++ src/renderer/impl/base/level1/anchors.coffee
@@ -2,6 +2,7 @@

 assert = require 'src/assert'
 log = require 'src/log'
+utils = require 'src/utils'

 log = log.scope 'Renderer', 'Anchors'

@@ -288,10 +289,13 @@
             @prop = getItemProp[source]
             @getSourceValue = getSourceValue[source]
             @getTargetValue = getTargetValue[line][@type]
+            @targetItem = null
+
+            Object.seal @

             if typeof @getTargetValue isnt 'function'
                 @getTargetValue = GET_ZERO
-                log.error "Anchor '#{@source}: #{def.join('.')}' is not supported"
+                log.error "Unknown anchor `#{@}` given"

             switch target
                 when 'parent'
@@ -314,12 +318,13 @@
                     item.onPreviousSiblingChange onPreviousSiblingChange, @
                     onPreviousSiblingChange.call @, null
                 else
+                    if not utils.isObject(target) or handler not of target
+                        log.error "Unknown anchor `#{@}` given"
+                        return
                     if @targetItem = target
                         for handler in getTargetWatchProps[line][@type]
                             @targetItem[handler] update, @
                     update.call @
-
-            Object.preventExtensions @

         update: ->
             # sometimes it can be already destroyed


expose leaflet object to controller

expose leaflet object to controller

--- src/scripts/controllers/mapController.coffee
+++ src/scripts/controllers/mapController.coffee
@@ -70,8 +70,9 @@
     dirty

 class LiveMapController extends MapController
-  @$inject: ['$log', '$scope', '$http', 'missionService']
-  constructor: (@log, scope, http, @missionService) ->
+  @$inject: ['$scope', '$log', 'leafletData', '$http', 'missionService']
+  constructor: (scope, @log, leafletData, http, @missionService) ->
+    scope.leafletData = leafletData
     @boundsFactory = new BoundsFactory

     @count = 0
@@ -79,9 +80,6 @@
     scope.vehiclePaths = {}
     scope.bounds = {} # Angular wants to see at least an empty object (not undefined)
     scope.center = {}
-    #  lat: 19.4284700
-    #  lng: -99.1276600
-    #  zoom: 3

     # TODO: fake markers arn't workng
     #scope.vehicleMarkers['missionId_fake'] = {lat: scope.hawaii.lat, lng: scope.hawwai.lng, focus: false, draggable: false}


Warn on invalid JSON specs; fix

Warn on invalid JSON specs; fix

--- spec/schema/main-spec.coffee
+++ spec/schema/main-spec.coffee
@@ -48,7 +48,13 @@
       expect(sample).toEqual test.equal

 glob.sync("#{__dirname}/**/*.json").forEach (file) ->
-  suite = JSON.parse(fs.readFileSync(file))
+  suite = try
+    JSON.parse(fs.readFileSync(file))
+  catch e
+    console.log "Invalid JSON: #{file}"
+    console.log e.message
+    process.exit 1
+    null

   (if Array.isArray(suite) then suite else [suite]).forEach (suite) ->
     return if suite.xdescription


Fix spec

Fix spec

--- spec/text-editor-spec.coffee
+++ spec/text-editor-spec.coffee
@@ -113,7 +113,7 @@
         expect(editor1.isSoftWrapped()).toBe true
         expect(editor1.getSoftTabs()).toBe false

-        atom.config.set('editor.tabLength', 100)
+        atom.config.set('editor.tabLength', 8)
         atom.config.set('editor.softWrap', false)
         atom.config.set('editor.softTabs', true)

@@ -121,7 +121,7 @@
         atom.workspace.open('b').then (o) -> editor2 = o

       runs ->
-        expect(editor2.getTabLength()).toBe 100
+        expect(editor2.getTabLength()).toBe 8
         expect(editor2.isSoftWrapped()).toBe false
         expect(editor2.getSoftTabs()).toBe true