add search ability to blacklist list

add search ability to blacklist list

--- src/uapanfw.coffee
+++ src/uapanfw.coffee
@@ -87,16 +87,33 @@
       "displayed #{modulename} help"
     robot.logger.info logmsg

-  robot.respond /fw (?:blacklist|b)$/i, (msg) ->
+  robot.respond /fw (?:blacklist|b)(?: (cidr|url)(?: ([^ ]+)|)|)$/i, (msg) ->
     logmsg = "#{modulename}: #{msg.envelope.user.name} requested: blacklist"
     robot.logger.info logmsg

-    arr = ['Blacklist items and expirations:']
+    bl_type = false
+    bl_type = msg.match[1] if msg.match[1]
+
+    bl_search = false
+    bl_search = msg.match[2] if msg.match[2]
+
+    arr = ['Blacklist items and expirations']
     for obj in data
       expires = moment(obj.expires)
-      if expires.isAfter()
-        arr.push "`#{obj.val}` #{expires.fromNow()}"
-    robot.send {room: msg.message?.user?.name}, arr.join "\n"
+      if bl_type and bl_type != obj.type
+        #console.log 'skipping type: '+ obj.type
+        continue
+      if bl_search and obj.val.indexOf(bl_search) == -1
+        #console.log 'skipping search: '+ obj.val
+        continue
+      if expires.isBefore() # now
+        #console.log 'skipping expires: '+ obj.expires
+        continue
+      #console.log 'adding to array: '
+      #console.log obj
+      arr.push "#{obj.type} `#{obj.val}` #{expires.fromNow()}"
+
+    robot.reply arr.join "\n"

     logmsg = "#{modulename}: robot responded to #{msg.envelope.user.name}: " +
       "displayed blacklist items and expirations"


Formatting the ElasticSearch command docs

--- src/scripts/elasticsearch.coffee
+++ src/scripts/elasticsearch.coffee
@@ -2,16 +2,16 @@
 #   Get ElasticSearch Cluster Information
 #
 # Commands:
-#   hubot: elasticsearch cluster health [cluster] - Gets the cluster health for the given server or alias
-#   hubot: elasticsearch cat nodes [cluster] - Gets the information from the cat nodes endpoint for the given server or alias
-#   hubot: elasticsearch cat indexes [cluster] - Gets the information from the cat indexes endpoint for the given server or alias
-#   hubot: elasticsearch clear cache [cluster] - Clears the cache for the specified cluster
-#   hubot: elasticsearch cluster settings [cluster] - Gets a list of all of the settings stored for the cluster
+#   hubot: elasticsearch cluster health [cluster]     - Gets the cluster health for the given server or alias
+#   hubot: elasticsearch cat nodes [cluster]          - Gets the information from the cat nodes endpoint for the given server or alias
+#   hubot: elasticsearch cat indexes [cluster]        - Gets the information from the cat indexes endpoint for the given server or alias
+#   hubot: elasticsearch clear cache [cluster]        - Clears the cache for the specified cluster
+#   hubot: elasticsearch cluster settings [cluster]   - Gets a list of all of the settings stored for the cluster
 #   hubot: elasticsearch disable allocation [cluster] - disables shard allocation to allow nodes to be taken offline
-#   hubot: elasticsearch enable allocation [cluster] - renables shard allocation
-#   hubot: elasticsearch show aliases - shows the aliases for the list of ElasticSearch instances
+#   hubot: elasticsearch enable allocation [cluster]  - renables shard allocation
+#   hubot: elasticsearch show aliases                 - shows the aliases for the list of ElasticSearch instances
 #   hubot: elasticsearch add alias [alias name] [url] - sets the alias for a given url
-#   hubot: elasticsearch clear alias [alias name] - please note that this needs to include any port numbers as appropriate
+#   hubot: elasticsearch clear alias [alias name]     - please note that this needs to include any port numbers as appropriate
 #
 # Notes:
 #   The server must be a fqdn (with the port!) to get to the elasticsearch cluster


Fixes udefine reference

Fixes udefine reference

--- udefine/config.coffee
+++ udefine/config.coffee
@@ -1,7 +1,7 @@
 do (names = ['requestAnimationFrame', 'cancelAnimationFrame']) ->
   udefine.configure (root) ->
     for n in names
-      root.udefine.inject.add [n.toLowerCase()],
+      udefine.inject.add [n.toLowerCase()],
         root: root
         name: n


separating concerns

separating concerns

--- console/runner.coffee
+++ console/runner.coffee
@@ -61,13 +61,16 @@
   fs.mkdirSync tmpdir unless fs.existsSync tmpdir
   new AdmZip(jar_path).extractAllTo tmpdir, true
   jvm.classpath.unshift tmpdir
-  return main_class_name if main_class_name?
+  return tmpdir
+
+find_main_class = (extracted_jar_dir) ->
   # find the main class in the manifest
-  manifest = fs.readFileSync "#{tmpdir}/META-INF/MANIFEST.MF", 'utf8'
+  manifest_path = "#{extracted_jar_dir}/META-INF/MANIFEST.MF"
+  manifest = fs.readFileSync manifest_path, 'utf8'
   for line in manifest.split '\n'
     match = line.match /Main-Class: (\S+)/
     return util.int_classname(match[1]) if match?
-  console.error "No main class provided and no Main-Class found in #{jar_path}"
+  return

 if require.main == module
@@ -119,8 +122,10 @@
   java_cmd_args = (argv.java?.toString().split /\s+/) or []

   if argv.jar?
-    cname = extract_jar argv.jar, cname
-    return unless cname?  # couldn't find the main class in the manifest
+    jar_dir = extract_jar argv.jar
+    cname = find_main_class(jar_dir) unless cname?
+    unless cname?
+      console.error "No main class provided and no Main-Class found in #{argv.jar}"

   if argv.profile?
     run_profiled rs, cname, java_cmd_args, argv.hot


Set up a `creditCards` relation on the User

--- models/mixins/relations/user.coffee
+++ models/mixins/relations/user.coffee
@@ -22,8 +22,12 @@
     authentications = new Backbone.Collection
     authentications.url = "#{API_URL}/api/v1/me/authentications"

+    creditCards = new Backbone.Collection
+    creditCards.url = "#{API_URL}/api/v1/me/credit_cards"
+
     @__related__ =
       collectorProfile: collectorProfile
       account: account
       location: location
       authentications: authentications
+      creditCards: creditCards


webapp/assistant: instantiate the assistant dialog subcomponent only once

--- src/smc-webapp/jupyter/main.cjsx
+++ src/smc-webapp/jupyter/main.cjsx
@@ -76,6 +76,17 @@
             metadata            : rtypes.immutable.Map
             trust               : rtypes.bool

+    componentWillMount: ->
+        # instantiate assistant dialog subcomponent → returned in @render_assistant_dialog
+        {instantiate_component} = require('../assistant/main')
+        store             = @props.actions.store
+        return null if not store?
+        project_id        = store.get('project_id')
+        path              = store.get('path')
+        # assistant actions are already initialized in JupyterActions::_init
+        assistant_actions = @props.actions.assistant_actions
+        @assistant_dialog = instantiate_component(project_id, path, assistant_actions)
+
     render_error: ->
         if @props.error
             <ErrorDisplay
@@ -222,14 +233,7 @@
         />

     render_assistant_dialog: ->
-        {instantiate_component} = require('../assistant/main')
-        store             = @props.actions.store
-        return null if not store?
-        project_id        = store.get('project_id')
-        path              = store.get('path')
-        # assistant is initialized in JupyterActions::_init
-        assistant_actions = @props.actions.assistant_actions
-        return instantiate_component(project_id, path, assistant_actions)
+        @assistant_dialog

     render_json_viewer: ->
         <JSONView


Increase test timeout.

Increase test timeout.

--- src/spec/integration.spec.coffee
+++ src/spec/integration.spec.coffee
@@ -4,7 +4,7 @@
 Q = require('q')

 # Increase timeout
-jasmine.getEnv().defaultTimeoutInterval = 3000
+jasmine.getEnv().defaultTimeoutInterval = 20000

 describe '#run', ->
   beforeEach ->


fixed lint errors

fixed lint errors

--- app/server/api.coffee
+++ app/server/api.coffee
@@ -123,7 +123,8 @@
 	try

 		FogBugzService.updateStatus user, item, newStatus
-		console.log 'item %s status was update on %s, assigned to %s', item.id, item.status, item.assignee.name
+		console.log 'item %s status was update on %s, assigned to %s',
+			item.id, item.status, item.assignee.name

 		# commit changes
 		WorkItems.update itemId, item
@@ -135,9 +136,10 @@

 Meteor.methods {

-	# TODO try to avoid need to call this from client, subscribe on some server event
+	# TODO try to avoid need to call onLogin from client
+	# subscribe on some server event
+	# called when user is logged in to load boards, etc

-	# called when user is logged in to load boards, etc
 	onLogin: (userId) ->
 		user = Meteor.users.findOne userId
 		throw new Error "Cant find user #{userId}" if not user
@@ -162,7 +164,8 @@
 		item = WorkItems.findOne itemId
 		throw new Error "Cant find work item #{itemId}" if not item

-		console.log 'updating item %s by %s from status %s on %s', item.id, user.username, oldStatus, newStatus
+		console.log 'updating item %s by %s from status %s on %s',
+			item.id, user.username, oldStatus, newStatus

 		# predictive change to quickly update clients
 		WorkItems.update itemId, {$set: {status: newStatus}}


tests for core

tests for core

--- src/test/services/dynamic.coffee
+++ src/test/services/dynamic.coffee
@@ -6,62 +6,62 @@
 ## file 'LICENSE.txt', which is part of this source code package.
 ##

-describe 'dynamic directive tests', ->
+# describe 'dynamic directive tests', ->

-  beforeEach(module('CoolFormServices'))
+#   beforeEach(module('CoolFormServices'))

-  form =
-    name: "form"
-    action: "/"
-    dependencies: [
-      type: 'directive'
-      field_type: 'text'
-      name: 'test-text'
-      ]
-    pages: [
-      lines:[
-        fields:[
-          {
-            name: "field"
-            type: "text"
-            label: "field"
-            validation: [
-              validator:"email"
-              options: [
-                message: "not an email"
-                ]
-              ]
-            }
-          ]
-        ]
-      ]
+#   form =
+#     name: "form"
+#     action: "/"
+#     dependencies: [
+#       type: 'directive'
+#       field_type: 'text'
+#       name: 'test-text'
+#       ]
+#     pages: [
+#       lines:[
+#         fields:[
+#           {
+#             name: "field"
+#             type: "text"
+#             label: "field"
+#             validation: [
+#               validator:"email"
+#               options: [
+#                 message: "not an email"
+#                 ]
+#               ]
+#             }
+#           ]
+#         ]
+#       ]

-  # directiveLoaded = false
-  # angular.module('TestModule', []).directive('testText', ->
-  #     l = (scope) ->
-  #       handlers =
-  #         change: (nVal) ->
-  #           if scope.value != nVal then scope.value = nVal
-  #       dump "hello"
-  #       directiveLoaded = true
-  #       setValue = scope.service.registerField(scope.field.name, handlers)
-  #       scope.value = if scope.field.value then value else ""
-  #       scope.$watch('value', (v, o) -> setValue(v))
+#   # directiveLoaded = false
+#   # angular.module('TestModule', []).directive('testText', ->
+#   #     l = (scope) ->
+#   #       handlers =
+#   #         change: (nVal) ->
+#   #           if scope.value != nVal then scope.value = nVal
+#   #       dump "hello"
+#   #       directiveLoaded = true
+#   #       setValue = scope.service.registerField(scope.field.name, handlers)
+#   #       scope.value = if scope.field.value then value else ""
+#   #       scope.$watch('value', (v, o) -> setValue(v))

-  #     return {
-  #       restrict: 'E'
-  #       scope:
-  #         field: '='
-  #         service: '='
-  #       template: """<input type="text" ng-model="value"/>"""
-  #       link: l
-  #     }
-  #   )
+#   #     return {
+#   #       restrict: 'E'
+#   #       scope:
+#   #         field: '='
+#   #         service: '='
+#   #       template: """<input type="text" ng-model="value"/>"""
+#   #       link: l
+#   #     }
+#   #   )

-  it 'load directive and override default text input', inject((registrationService) ->
-    service = registrationService(form)
-    expect(service.directives.get('text')).toEqual('test-text')
-  )
+#   it 'load directive and override default text input', inject((registrationService) ->
+#     service = registrationService(form)
+#     expect(service.directives.get('text')).toEqual('test-text')
+#   )


refactor(app generator): minification will produce the same artifact (name) as an as-is build

--- generators/app/templates/_Gruntfile.coffee
+++ generators/app/templates/_Gruntfile.coffee
@@ -269,10 +269,6 @@
                 ]

-            uglify:
-                src: [ 'dist/app/bundle.js' ]
-
-
         ##
         ##  Compile your sass to bundled css.
         ##
@@ -386,7 +382,7 @@
         uglify:
             dist:
                 files:
-                    'dist/app/bundle.min.js': [ 'dist/app/bundle.js' ]
+                    'dist/app/bundle.js': [ 'dist/app/bundle.js' ]

         mochaTest:
@@ -685,7 +681,6 @@
             'app:dist'

             'uglify:dist'
-            'clean:uglify'

             'compress:app_dist'


One last fix to the search url.

One last fix to the search url.

--- app/treema-ext.coffee
+++ app/treema-ext.coffee
@@ -253,7 +253,7 @@
 class LatestVersionReferenceNode extends TreemaNode
   searchValueTemplate: '<input placeholder="Search" />

'
   valueClass: 'treema-latest-version'
-  url: '/db/article/search'
+  url: '/db/article'
   lastTerm: null

   constructor: ->
@@ -264,7 +264,7 @@
     link = (l for l in links when l.rel is 'db')[0]
     return unless link
     parts = (p for p in link.href.split('/') when p.length)
-    @url = "/db/#{parts[1]}/search"
+    @url = "/db/#{parts[1]}"
     @model = require('models/' + _.string.classify(parts[1]))

   buildValueForDisplay: (valEl) ->


Revert "Simplify default texPath due to regression in Atom"

This reverts commit 5513740fa23f1a4f0a66322c5c15b485c1c8c6f6.

--- lib/latex.coffee
+++ lib/latex.coffee
@@ -6,7 +6,7 @@

 module.exports =
   configDefaults:
-    texPath: "/usr/texbin"
+    texPath: "$PATH:/usr/texbin"
     outputDirectory: ""
     enableShellEscape: false


fix typo

fix typo

--- lib/minjs.coffee
+++ lib/minjs.coffee
@@ -23,7 +23,7 @@
 			)
 		return
 	activate: ->
-		atom.workspaceView.command "minjs:minify", => @compile()
+		atom.workspaceView.command "minjs:compile", => @compile()
 		atom.workspaceView.command "core:save", =>
 		if atom.config.get('minjs.minifyOnSave')
 			@compile()


Account for natural notes

Account for natural notes

--- lib/notes.coffee
+++ lib/notes.coffee
@@ -205,7 +205,7 @@
           noteName = "#{noteName}#"
         else if accidental == 'flat'
           noteName = "#{noteName}b"
-        else
+        else if accidental != '='
           throw "Unknown accidental: #{accidental}"
       pitch = new RelativePitch noteName, octave, REFERENCE_NOTE if noteName?
     duration = new Duration abcNote.duration, @_noteLength


alphabetize grammars

alphabetize grammars

--- lib/grammars.coffee
+++ lib/grammars.coffee
@@ -23,6 +23,19 @@
       command: 'osascript'
       args: (context) -> [context.filepath]

+  'Babel ES6 JavaScript':
+    "Selection Based":
+      command: "babel-node"
+      args: (context) -> ['-e', context.getCode()]
+    "File Based":
+      command: "babel-node"
+      args: (context) -> [context.filepath]
+
+  Batch:
+    "File Based":
+      command: ""
+      args: (context) -> [context.filepath]
+
   'Behat Feature':
     "File Based":
       command: "behat"
@@ -31,10 +44,6 @@
       command: "behat"
       args: (context) -> [context.fileColonLine()]

-  Batch:
-    "File Based":
-      command: ""
-      args: (context) -> [context.filepath]
   C:
     if GrammarUtils.OperatingSystem.isDarwin()
       "File Based":
@@ -45,6 +54,11 @@
         command: "bash"
         args: (context) -> ["-c", "cc -Wall -include stdio.h '" + context.filepath + "' -o /tmp/c.out && /tmp/c.out"]

+  'C# Script File':
+    "File Based":
+      command: "scriptcs"
+      args: (context) -> ['-script', context.filepath]
+
   'C++':
     if GrammarUtils.OperatingSystem.isDarwin()
       "File Based":
@@ -55,11 +69,6 @@
         command: "bash"
         args: (context) -> ["-c", "g++ -Wall -include stdio.h -include iostream '" + context.filepath + "' -o /tmp/cpp.out && /tmp/cpp.out"]

-  'C# Script File':
-    "File Based":
-      command: "scriptcs"
-      args: (context) -> ['-script', context.filepath]
-
   Clojure:
     "Selection Based":
       command: "lein"
@@ -95,6 +104,11 @@
   D:
     "File Based":
       command: "rdmd"
+      args: (context) -> [context.filepath]
+
+  Dart:
+    "File Based":
+      command: "dart"
       args: (context) -> [context.filepath]

   DOT:
@@ -199,14 +213,6 @@
       command: "node"
       args: (context) -> [context.filepath]

-  'Babel ES6 JavaScript':
-    "Selection Based":
-      command: "babel-node"
-      args: (context) -> ['-e', context.getCode()]
-    "File Based":
-      command: "babel-node"
-      args: (context) -> [context.filepath]
-
   "JavaScript for Automation (JXA)":
     "Selection Based":
       command: "osascript"
@@ -294,6 +300,14 @@
       command: "lua"
       args: (context) -> [context.filepath]

+  Makefile:
+    "Selection Based":
+      command: "bash"
+      args: (context) -> ['-c', context.getCode()]
+    "File Based":
+      command: "make"
+      args: (context) -> ['-f', context.filepath]
+
   MagicPython:
     "Selection Based":
       command: "python"
@@ -339,6 +353,14 @@
     "File Based":
       command: "newlisp"
       args: (context) -> [context.filepath]
+
+  Nim:
+    "File Based":
+      command: "bash"
+      args: (context) ->
+        file = GrammarUtils.Nim.findNimProjectFile(context.filepath)
+        path = GrammarUtils.Nim.projectDir(context.filepath)
+        ['-c', 'cd "' + path + '" && nim c --hints:off --parallelBuild:1 -r "' + file + '" 2>&1']

   NSIS:
     "Selection Based":
@@ -368,10 +390,45 @@
       command: "ocaml"
       args: (context) -> [context.filepath]

+  Octave:
+    "Selection Based":
+      command: "octave"
+      args: (context) -> ['-p', context.filepath.replace(/[^\/]*$/, ''), '--eval', context.getCode()]
+    "File Based":
+      command: "octave"
+      args: (context) -> ['-p', context.filepath.replace(/[^\/]*$/, ''), context.filepath]
+
   'Pandoc Markdown':
     "File Based":
       command: "panzer"
       args: (context) -> [context.filepath, "--output=" + context.filepath + ".pdf"]
+
+  Perl:
+    "Selection Based":
+      command: "perl"
+      args: (context) ->
+        code = context.getCode()
+        file = GrammarUtils.Perl.createTempFileWithCode(code)
+        [file]
+    "File Based":
+      command: "perl"
+      args: (context) -> [context.filepath]
+
+  "Perl 6":
+    "Selection Based":
+      command: "perl6"
+      args: (context)  -> ['-e', context.getCode()]
+    "File Based":
+      command: "perl6"
+      args: (context) -> [context.filepath]
+
+  "Perl 6 FE":
+    "Selection Based":
+      command: "perl6"
+      args: (context)  -> ['-e', context.getCode()]
+    "File Based":
+      command: "perl6"
+      args: (context) -> [context.filepath]

   PHP:
     "Selection Based":
@@ -384,37 +441,15 @@
       command: "php"
       args: (context) -> [context.filepath]

-  Perl:
-    "Selection Based":
-      command: "perl"
-      args: (context) ->
-        code = context.getCode()
-        file = GrammarUtils.Perl.createTempFileWithCode(code)
-        [file]
-    "File Based":
-      command: "perl"
-      args: (context) -> [context.filepath]
-
-  "Perl 6":
-    "Selection Based":
-      command: "perl6"
-      args: (context)  -> ['-e', context.getCode()]
-    "File Based":
-      command: "perl6"
-      args: (context) -> [context.filepath]
-
-  "Perl 6 FE":
-    "Selection Based":
-      command: "perl6"
-      args: (context)  -> ['-e', context.getCode()]
-    "File Based":
-      command: "perl6"
-      args: (context) -> [context.filepath]
-
   PowerShell:
     "File Based":
       command: "powershell"
       args: (context) -> [context.filepath.replace /\ /g, "` "]
+
+  Prolog:
+    "File Based":
+      command: "bash"
+      args: (context) -> ['-c', 'cd \"' + context.filepath.replace(/[^\/]*$/, '') + '\"; swipl -f \"' + context.filepath + '\" -t main --quiet']

   Python:
     "Selection Based":
@@ -486,14 +521,6 @@
       command: "bash"
       args: (context) -> ['-c', "rustc '#{context.filepath}' -o /tmp/rs.out && /tmp/rs.out"]

-  Makefile:
-    "Selection Based":
-      command: "bash"
-      args: (context) -> ['-c', context.getCode()]
-    "File Based":
-      command: "make"
-      args: (context) -> ['-f', context.filepath]
-
   Sage:
     "Selection Based":
       command: "sage"
@@ -556,14 +583,6 @@
     "File Based":
       command: "sml"
       args: (context) -> [context.filepath]
-
-  Nim:
-    "File Based":
-      command: "bash"
-      args: (context) ->
-        file = GrammarUtils.Nim.findNimProjectFile(context.filepath)
-        path = GrammarUtils.Nim.projectDir(context.filepath)
-        ['-c', 'cd "' + path + '" && nim c --hints:off --parallelBuild:1 -r "' + file + '" 2>&1']

   Swift:
     "File Based":
@@ -582,21 +601,3 @@
     "File Based":
       command: "bash"
       args: (context) -> ['-c', "tsc '#{context.filepath}' --out /tmp/js.out && node /tmp/js.out"]
-
-  Dart:
-    "File Based":
-      command: "dart"
-      args: (context) -> [context.filepath]
-
-  Octave:
-    "Selection Based":
-      command: "octave"
-      args: (context) -> ['-p', context.filepath.replace(/[^\/]*$/, ''), '--eval', context.getCode()]
-    "File Based":
-      command: "octave"
-      args: (context) -> ['-p', context.filepath.replace(/[^\/]*$/, ''), context.filepath]
-
-  Prolog:
-    "File Based":
-      command: "bash"
-      args: (context) -> ['-c', 'cd \"' + context.filepath.replace(/[^\/]*$/, '') + '\"; swipl -f \"' + context.filepath + '\" -t main --quiet']


Mark finished versions specially (#28)

Mark finished versions specially (#28)

--- lib/messages.coffee
+++ lib/messages.coffee
@@ -548,6 +548,14 @@
   else
     message

+@findLastDiff = (id) ->
+  MessagesDiff.find
+    id: id
+  ,
+    sort: updated: -1
+    limit: 1
+  .fetch()?[0]
+
 @messageEmpty = (message) ->
   message = findMessage message
   message.title.trim().length == 0 and
@@ -660,6 +668,7 @@

 export messageExtraFields = [
   'editing'
+  #'finished'
   'submessageCount'
   'submessageLastUpdated'
   #'children'
@@ -934,9 +943,15 @@
     Meteor.clearTimeout stopTimers[id]
     stopTimers[id] = Meteor.setTimeout ->
       ## This code is like an extreme form of `messageEditStop` below:
-      editor2messageUpdate id, Messages.findOne(id).editing ? []
+      editing = Messages.findOne(id).editing ? []
+      editor2messageUpdate id, editing
       Messages.update id,
         $set: editing: []
+      lastDiff = findLastDiff id
+      relevant = (editor for editor in editing when editor in (lastDiff?.updators ? []))
+      if relevant.length
+        MessagesDiff.update lastDiff._id,
+          $addToSet: finished: $each: relevant
       ShareJS.model.delete id
     , idleStop

@@ -1075,6 +1090,8 @@
         $addToSet: editing: Meteor.user().username

   messageEditStop: (id) ->
+    ## When updating this method, you might also want to update the "extreme"
+    ## form in `updateStopTimer` above.
     check Meteor.userId(), String
     if Meteor.isServer
       ## We used to do the following update in client too, to do
@@ -1085,8 +1102,12 @@
         Meteor.clearTimeout stopTimers[id]
         editor2messageUpdate id, [Meteor.user().username]
         ShareJS.model.delete id
-      ## xxx should add to last MessagesDiff (possibly just made) that
-      ## Meteor.user().username just committed this version.
+      ## If this user was involved in the last edit to this mesage,
+      ## mark it as "finished" version for the user.
+      lastDiff = findLastDiff id
+      if Meteor.user().username in (lastDiff?.updators ? [])
+        MessagesDiff.update lastDiff._id,
+          $addToSet: finished: Meteor.user().username

   messageImport: (group, parent, message, diffs) ->
     #check Meteor.userId(), String  ## should be done by 'canImport'


Add support for the collide event.

Add support for the collide event.

--- lib/observer.coffee
+++ lib/observer.coffee
@@ -31,19 +31,22 @@
           @drop("Invalid position " + element.getAttribute("position"))

       else if element.nodeName == "event"
-        if element.getAttribute('name') is "click"
-          if @document.getElementByUUID(element.getAttribute('uuid'))
-            @document.getElementByUUID(element.getAttribute("uuid")).dispatchEvent 'click', {
-              player : @player
-              point : Vector.fromString(element.getAttribute("point"))
-            }
-          else
-            console.log "Couldnt find element with uuid #{element.getAttribute('uuid')}"
+        el = element.getAttribute('uuid') && @document.getElementByUUID(element.getAttribute('uuid'))

+        if el && (element.getAttribute('name') is "click")
+          el.dispatchEvent 'click', {
+            player : @player
+            point : Vector.fromString(element.getAttribute("point"))
+          }
+        else if el && (element.getAttribute('name') is "collide")
+          el.dispatchEvent 'click', {
+            player : @player
+            normal : Vector.fromString(element.getAttribute("normal"))
+          }
         else if element.getAttribute('name') is 'chat'
           @reflector.chatChannel.sendMessage(@, element.getAttribute('message'))
         else
-          console.log "Unrecognized event element"
+          console.log "Unrecognized event element or element not found"
           console.log "  " + element.toString()
       else
         console.log "Unrecognized packet element"


operatorModifier logic cleanup

operatorModifier logic cleanup

--- lib/operator.coffee
+++ lib/operator.coffee
@@ -31,6 +31,7 @@
   finalMode: "normal"
   finalSubmode: null
   withOccurrence: false
+  forceWise: null
   patternForOccurence: null

   setMarkForChange: (range) ->
@@ -73,7 +74,7 @@
   restorePoint: (selection) ->
     which = if @wasNeedStay then 'head' else 'start'
     if swrap(selection).getProperties().head?
-      @onDidFinishOperation =>
+      @onDidFinishOperation ->
         # Deffer restoring cursor point.
         # restorePoint is processed one by one, so immediately updating cursorPosition result in intersecting range with other selection
         # when intersecting selection is destroyed, cursor is moved automatically.
@@ -102,8 +103,6 @@
           # @reportSelectionProperties('before')

     if @isWithOccurrence()
-      @withOccurrence = true
-
       scanRanges = null
       @onWillSelectTarget =>
         if @isMode('visual')
@@ -129,16 +128,25 @@
       if markerForTrackChange?
         @setMarkForChange(range) if (range = markerForTrackChange.getBufferRange())

+  # called by operationStack
+  setOperatorModifier: ({occurence, wise}) ->
+    if occurence? and @withOccurrence isnt occurence
+      @withOccurrence = occurence
+
+    if wise? and @forceWise isnt wise
+      @forceWise = wise
+
   # @target - TextObject or Motion to operate on.
   setTarget: (@target) ->
     unless _.isFunction(@target.select)
       @vimState.emitter.emit('did-fail-to-set-target')
       throw new OperatorError("#{@getName()} cannot set #{@target.getName()} as target")
     @target.setOperator(this)
+    @overrideTargetWise(@forceWise) if @forceWise?
     @emitDidSetTarget(this)
     this

-  forceWise: (wise) ->
+  overrideTargetWise: (wise) ->
     switch wise
       when 'characterwise'
         if @target.linewise
@@ -150,15 +158,16 @@
         @target.linewise = true

   isWithOccurrence: ->
-    @vimState.getOperatorModifier('occurence') or @withOccurrence
+    @withOccurrence
+
+  hasForceWise: ->
+    @forceWise?

   # Return true unless all selection is empty.
   # -------------------------
   selectTarget: ->
     @observeSelectAction()

-    if @isMode('operator-pending') and wise = @vimState.getOperatorModifier('wise')
-      @forceWise(wise)
     @emitWillSelectTarget()

     @target.select()
@@ -1131,6 +1140,7 @@
         swrap.resetProperties(@editor)
         @activateMode('visual', 'characterwise')

+# [FIXME] deperecate once spec is updated
 class SelectAllInRangeMarker extends AddSelection
   @extend()
   target: "RangeMarker"


Use Q() instead of Q.resolve()

Use Q() instead of Q.resolve()

--- lib/settings.coffee
+++ lib/settings.coffee
@@ -37,7 +37,7 @@

     @_clearContent() if @options.removeAfterParse == true

-    Q.resolve()
+    Q()

   get: (key) ->
     v = @settings[key]


:racehorse: Speed up table cached content generation through JSON serialization

--- lib/table.coffee
+++ lib/table.coffee
@@ -89,7 +89,7 @@
     @cachedContents = @getCacheContent()

   getCacheContent: ->
-    res = [@columns].concat(@rows).join('\n')
+    JSON.stringify([@columns].concat(@rows))

   initializeAfterSetup: ->
     @clearUndoStack()


utils.forEachFileDeep bug with deep folders fixed

--- lib/utils.coffee
+++ lib/utils.coffee
@@ -40,7 +40,7 @@
 				onFile path, stat
 				onReady()
 			else
-				exports.forEachFile path, onFile, onReady
+				exports.forEachFileDeep path, onFile, onReady

 	fs.readdir dir, (err, files) ->
 		if err or files.length is 0


Fixed indentation on permissioncheck

Fixed indentation on permissioncheck

--- snippets/php.cson
+++ snippets/php.cson
@@ -226,8 +226,8 @@
     \treturn ${1};
     }
     """
-    'permissionCheck':
-      'prefix': 'permissioncheck'
-      'body': """
-      permission::check(${1:string|array}, ${2:'any'}, ${3:$member})${0}
-      """
+  'permissionCheck':
+    'prefix': 'permissioncheck'
+    'body': """
+    permission::check(${1:string|array}, ${2:'any'}, ${3:$member})${0}
+    """


Don’t cascade computed values unless upstream values change

--- src/text-editor-presenter.coffee
+++ src/text-editor-presenter.coffee
@@ -361,14 +361,18 @@
     Math.max(contentHeight, @height)

   updateContentWidth: ->
-    @contentWidth = @pixelPositionForScreenPosition([@model.getLongestScreenRow(), Infinity]).left
-    @contentWidth += 1 unless @model.isSoftWrapped() # account for cursor width
-    @updateScrollbarDimensions()
+    contentWidth = @pixelPositionForScreenPosition([@model.getLongestScreenRow(), Infinity]).left
+    contentWidth += 1 unless @model.isSoftWrapped() # account for cursor width
+    unless @contentWidth is contentWidth
+      @contentWidth = contentWidth
+      @updateScrollbarDimensions()

   updateContentHeight: ->
-    @contentHeight = @lineHeight * @model.getScreenLineCount()
-    @updateHeight()
-    @updateScrollbarDimensions()
+    contentHeight = @lineHeight * @model.getScreenLineCount()
+    unless @contentHeight is contentHeight
+      @contentHeight = contentHeight
+      @updateHeight()
+      @updateScrollbarDimensions()

   computeClientHeight: ->
     @height - @horizontalScrollbarHeight
@@ -528,8 +532,10 @@
       @updateLineNumbersState()

   updateHeight: ->
-    @height = @explicitHeight ? @contentHeight
-    @updateScrollbarDimensions()
+    height = @explicitHeight ? @contentHeight
+    unless @height is height
+      @height = height
+      @updateScrollbarDimensions()

   setContentFrameWidth: (contentFrameWidth) ->
     unless @contentFrameWidth is contentFrameWidth


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/engine.coffee
+++ test/engine.coffee
@@ -185,6 +185,6 @@

   nextState = GameState()

-  engine.setState
+  engine.setState nex

 module()


Add default dimensions if running on Node.JS

--- game.coffee
+++ game.coffee
@@ -23,8 +23,8 @@
     @EAST = 2
     @WEST = 3

-    @WIDTH:  window.canvas.width
-    @HEIGHT:  window.canvas.height
+    @WIDTH:  if window? then window.canvas.width else 800
+    @HEIGHT:  if window? then window.canvas.height else 800

     @VELOCITY = 100


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/sprite.coffee
+++ test/sprite.coffee
@@ -6,7 +6,7 @@

 asyncTest "callback and cache", ->
   spriteUrl = "http://images.pixie.strd6.com/avatars/1/thumb.png"
-
+
   callback = ->
     sprite2 = Sprite.load spriteUrl, (s) ->
       ok s.width isnt null


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/sprite.coffee
+++ test/sprite.coffee
@@ -5,7 +5,7 @@
   ok Sprite.NONE

 asyncTest "callback and cache", ->
-  sprite = Sprite.load "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAIAAAACDbGyAAAAAXNSR0IArs4c6QAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9oMCRUiMrIBQVkAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAADElEQVQI12NgoC4AAABQAAEiE+h1AAAAAElFTkSuQmCC",
+  sprite = Sprite.load "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAIAAAACDbGyAAAAAXNSR0IArs4c6QAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9oMCRUiMrIBQVkAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAADElEQVQI12NgoC4AAABQAAEiE+h1AAAAAElFTkSuQmCC", ->

 module()


Fix jsonGet

Fix jsonGet

--- src/6.web.coffee
+++ src/6.web.coffee
@@ -64,11 +64,7 @@
         url: url
     )
 web.jsonGet = (url, options) ->
-    web.get(url, options)
-    .then (response) ->
-        new Promise((resolve, reject) ->
-            resolve(response)
-        )
     web.request
         method: "GET"
         url: url
+        responseBodyType: "json"


Stepped okSubmits date

Stepped okSubmits date

--- server/api/dashboard.coffee
+++ server/api/dashboard.coffee
@@ -56,7 +56,7 @@
 export default dashboard = () ->
     queries =
         # remember that months start from 0
-        ok: {ok: 1, lastSubmitTime: {$gt: new Date(2017, 9, 11)}},
+        ok: {ok: 1, lastSubmitTime: {$gt: new Date(2017, 9, 13)}},
         wa: {solved: 0, ok: 0, ignored: 0, attempts: {$gt: 0}},
         ig: {ignored: 1},
         ac: {solved: 1}


restore simpler processing of side-effects

--- src/Bacon.coffee
+++ src/Bacon.coffee
@@ -1417,7 +1417,6 @@
   waiterObs = []
   waiters = {}
   afters = []
-  processingAfters = false

   afterTransaction = (f) ->
     if rootEvent
@@ -1472,19 +1471,9 @@
       #console.log("done with tx")
       flush()
       rootEvent = undefined
-      if not processingAfters
-        try
-          processingAfters = true
-          while (afters.length)
-            theseAfters = afters
-            afters = []
-            for f, i in theseAfters
-              if (i == theseAfters.length - 1)
-                processingAfters = false
-              f()
-          result
-        finally
-          processingAfters = false
+      while (afters.length > 0)
+        afters.shift()()
+      result

   currentEventId = -> if rootEvent then rootEvent.id else undefined


Use composite unsubscribe in merge

Use composite unsubscribe in merge

--- src/Bacon.coffee
+++ src/Bacon.coffee
@@ -603,12 +603,9 @@
     assertEventStream(right)
     left = this
     new EventStream (sink) ->
-      unsubLeft = nop
-      unsubRight = nop
-      unsubscribed = false
-      unsubBoth = -> unsubLeft() ; unsubRight() ; unsubscribed = true
       ends = 0
-      smartSink = (event) ->
+      smartSink = (obs) -> (unsubBoth) -> obs.subscribe (event) ->
+
         if event.isEnd()
           ends++
           if ends == 2
@@ -619,10 +616,8 @@
           reply = sink event
           unsubBoth() if reply == Bacon.noMore
           reply
-      unsubLeft = left.subscribe(smartSink)
-      unsubRight = right.subscribe(smartSink) unless unsubscribed
-      unsubBoth
-
+      compositeUnsubscribe (smartSink left), (smartSink right)
+
   toProperty: (initValue) ->
     initValue = None if arguments.length == 0
     @scan(initValue, latter)


added some defaults to avoid failure

added some defaults to avoid failure

--- src/Bacon.coffee
+++ src/Bacon.coffee
@@ -2,7 +2,7 @@

 # eventTransformer - should return one value or one or many events
 Bacon.fromBinder = (binder, eventTransformer = _.id, desc) ->
-  new EventStream desc, (sink) ->
+  new EventStream describe((desc||"fromBinder"), binder), (sink) ->
     unbinder = binder (args...) ->
       value = eventTransformer(args...)
       unless isArray(value) and _.last(value) instanceof Event
@@ -262,6 +262,7 @@

 class Observable
   constructor: (desc) ->
+    desc = describe(desc)
     @assign = @onValue
     @deps = desc.deps
     @toString = desc.toString


update to catch only events that start with ‘pointer’

--- modules/selection/main/index.coffee
+++ modules/selection/main/index.coffee
@@ -381,7 +381,7 @@

       data.listenerNamesRegistered ?= new hx.Set

-      if name.indexOf('pointer') is -1 and not data.listenerNamesRegistered.has(name)
+      if name.indexOf('pointer') isnt 0 and not data.listenerNamesRegistered.has(name)
         handler = (e) -> eventEmitter.emit(name, e)
         data.listenerNamesRegistered.add(name)
         getMethod(node, 'addEventListener').call(node, name, handler)


Deselect markers on disable

Deselect markers on disable

--- scripts/controllers/CreaturePicker.coffee
+++ scripts/controllers/CreaturePicker.coffee
@@ -167,7 +167,7 @@
 			@resetStrays()

 	setDisabled: (@disabled) =>
-		if @disabled then marker.deactivate() for marker in @markers or [] when marker.active
+		if @disabled then marker.deselect() for marker in @markers or [] when marker.selected
 		if @disabled then @el.addClass 'disabled' else @el.removeClass 'disabled'

 	changeClassification: (@classification) =>


Prevented cloning the document when getting doc changes to improve performance.

--- src/Forms.coffee
+++ src/Forms.coffee
@@ -440,7 +440,7 @@
       template = getTemplate(template)
       formDoc = Form.getInputValues(template)
       origDoc = template.origDoc ? {}
-      origDoc = Objects.flattenProperties(Setter.clone(origDoc))
+      origDoc = Objects.flattenProperties(origDoc)
       delete origDoc._id
       # Form doc is the full set of fields which the doc supports. We must avoid creating modifiers
       # containing any other fields, since this form does not affect them.


remove unnecessary options parameter

remove unnecessary options parameter

--- lib/motions/general-motions.coffee
+++ lib/motions/general-motions.coffee
@@ -149,7 +149,7 @@
 class MoveRight extends Motion
   operatesInclusively: false

-  moveCursor: (cursor, count=1, options={}) ->
+  moveCursor: (cursor, count=1) ->
     _.times count, =>
       wrapToNextLine = settings.wrapLeftRightMotion()


Calc max height

Calc max height

--- lib/suggestion-list-element.coffee
+++ lib/suggestion-list-element.coffee
@@ -15,6 +15,7 @@
     @parentElement.classList.add('autocomplete-plus')
     @addActiveClassToEditor()
     @renderList() unless @ol
+    @calculateMaxListHeight()
     @itemsChanged()

   detachedCallback: ->
@@ -96,6 +97,16 @@
     @appendChild(@ol)
     @ol.className = 'list-group'

+  calculateMaxListHeight: ->
+    maxItems = atom.config.get('autocomplete-plus.maxVisibleSuggestions')
+    li = document.createElement('li')
+    li.textContent = 'test'
+    @ol.appendChild(li)
+    itemHeight = li.offsetHeight
+    console.log maxItems, itemHeight
+    @ol.style['max-height'] = "#{maxItems * itemHeight}px"
+    li.remove()
+
   renderItems: ->
     items = @visibleItems() or []
     items.forEach ({word, label, renderLabelAsHtml, className, prefix}, index) =>


Support overwrite image file

Support overwrite image file

Fixes #162

--- lib/views/insert-image-view.coffee
+++ lib/views/insert-image-view.coffee
@@ -134,7 +134,7 @@
       @copyImagePanel.removeClass("hidden")

   updateCopyImageDest: (file) ->
-    return if !file || @copyImagePanel.hasClass("hidden")
+    return unless file
     destFile = @copyImageDestPath(file, @titleEditor.getText())
     @copyImageMessage.text("Copy Image to #{destFile}")

@@ -187,19 +187,21 @@

     @editor.setTextInBufferRange(@range, text)

-  # TODO make this a service, so other packages can tap in and copy to other places
   copyImage: (file, callback) ->
     return callback() if utils.isUrl(file) || !fs.existsSync(file)

     try
       destFile = @copyImageDestPath(file, @titleEditor.getText())
+      performWrite = true

       if fs.existsSync(destFile)
-        atom.confirm
+        confirmation = atom.confirm
           message: "File already exists!"
-          detailedMessage: "Another file already exists at:\n#{destPath}"
-          buttons: ['OK']
-      else
+          detailedMessage: "Another file already exists at:\n#{destFile}\nDo you want to overwrite it?"
+          buttons: ["No", "Yes"]
+        performWrite = (confirmation == 1)
+
+      if performWrite
         fs.copy file, destFile, =>
           @imageEditor.setText(destFile)
           callback()


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/string_extensions.coffee
+++ test/string_extensions.coffee
@@ -13,7 +13,7 @@
   equals "String".constantize(), String, "look up a constant"
   equals "Math".constantize(), Math, "look up a constant"
   equals "Number".constantize(), Number, "look up a constant"
-  equals "Math.TAU".constantize(), Math.TAU, ""
+  equals "Math.TAU".constantize(), Math.TAU, "namespaced constants work too"

 test "#humanize", ->
   equals "employee_salary".humanize(), "Employee salary"


Fix bug in test of reorgLodData

Fix bug in test of reorgLodData


Added name caching

Added name caching

--- src/Roller.coffee
+++ src/Roller.coffee
@@ -93,11 +93,16 @@
 	}

-buildString = (n, prefix = '') ->
-	string = prefix
-	for i in [0...n]
-		string += '_'
-	string
+stringCache = ['']
+buildString = (n) ->
+	if n < stringCache.length
+		stringCache[n]
+	else
+		accumulator = stringCache[stringCache.length - 1]
+		for i in [stringCache.length..n]
+			accumulator += '_'
+			stringCache[i] = accumulator
+		accumulator

 buildProperty = (objectName, propertyName) ->
@@ -161,7 +166,7 @@
 		if properties.has name
 			(properties.get name).newProperty
 		else
-			newName = buildString properties.size, '_'
+			newName = buildString (properties.size + 1)
 			newProperty = buildProperty '_', newName
 			properties.set name, { newName, newProperty }
 			newProperty
@@ -172,7 +177,7 @@
 		if literals.has raw
 			(literals.get raw).newProperty
 		else
-			newName = buildString literals.size, '_'
+			newName = buildString (literals.size + 1)
 			newProperty = buildProperty '__', newName
 			literals.set raw, { newName, newProperty }
 			newProperty
@@ -183,7 +188,7 @@
 		if globals.has name
 			(globals.get name).newProperty
 		else
-			newName = buildString globals.size, '_'
+			newName = buildString (globals.size + 1)
 			newProperty = buildProperty '___', newName
 			globals.set name, { newName, newProperty }
 			newProperty


handle sr rescan

handle sr rescan

--- src/api/sr.coffee
+++ src/api/sr.coffee
@@ -24,3 +24,20 @@
     continue unless param of params

     xapi.call "SR.set_#{field}", SR.ref, params[param]
+
+exports.scan = ->
+  params = @getParams {
+    id: { type: 'string' }
+  }
+
+  # Current user must be an administrator.
+  @checkPermission 'admin'
+
+  try
+    SR = @getObject params.id
+  catch
+    @throw 'NO_SUCH_OBJECT'
+
+  xapi = @getXAPI SR
+
+  xapi.call "SR.scan", SR.ref


Fixes for restart/stop.

Fixes for restart/stop.

--- src/api/vm.coffee
+++ src/api/vm.coffee
@@ -380,8 +380,8 @@
   try
     # Attempts a clean reboot.
     xapi.call 'VM.clean_reboot', VM.ref
-  catch e
-    throw e unless
+  catch error
+    throw error unless error[0] is 'VM_MISSING_PV_DRIVERS'

     xapi.call 'VM.hard_reboot', VM.ref

@@ -425,8 +425,8 @@
   try
     # Attempts a clean shutdown.
     xapi.call 'VM.clean_shutdown', VM.ref
-  catch e
-    throw e unless
+  catch error
+    throw error unless error[0] is 'VM_MISSING_PV_DRIVERS'

     xapi.call 'VM.hard_shutdown', VM.ref


Revert "feat(vm.snapshot): copy ACLs from VM to snapshot (#495)"

This reverts commit dfee98b66bbff05ddc15b1533f4d9c7d492f3bc5.

Should not be necessary: snapshots inherit ACLs from their VM.

--- src/api/vm.coffee
+++ src/api/vm.coffee
@@ -1,5 +1,6 @@
 $assign = require 'lodash/assign'
 $debug = (require 'debug') 'xo:api:vm'
+$filter = require 'lodash/filter'
 $findIndex = require 'lodash/findIndex'
 $findWhere = require 'lodash/find'
 concat = require 'lodash/concat'
@@ -22,7 +23,6 @@
   formatXml: $js2xml,
   isArray: $isArray,
   map,
-  mapFilter,
   mapToArray,
   noop,
   parseSize,
@@ -301,16 +301,6 @@

   xapi = @getXapi(vm)

-  @getAllAcls().then((acls) =>
-    Promise.all(mapFilter(acls, (acl) =>
-      if (acl.object == vm.id)
-        return pCatch.call(
-          @removeAcl(acl.subject, acl.object, acl.action),
-          noop
-        )
-    ))
-  )
-
   # Update IP pools
   yield Promise.all(map(vm.VIFs, (vifId) =>
     vif = xapi.getObject(vifId)
@@ -652,18 +642,6 @@
   yield checkPermissionOnSrs.call(this, vm)

   snapshot = yield @getXapi(vm).snapshotVm(vm._xapiRef, name ? "#{vm.name_label}_#{new Date().toISOString()}")
-
-  # Copy VM's ACLs to the snapshot
-  @getAllAcls().then((acls) =>
-    Promise.all(mapFilter(acls, (acl) =>
-      if (acl.object == vm.id)
-        return pCatch.call(
-          @addAcl(acl.subject, snapshot.$id, acl.action),
-          noop
-        )
-    ))
-  )
-
   return snapshot.$id

 snapshot.params = {


Fix permission checking in `vm.{restart,stop}()`.

--- src/api/vm.coffee
+++ src/api/vm.coffee
@@ -370,6 +370,8 @@
     force: { type: 'boolean' }
   }

+  @checkPermission 'admin'
+
   try
     VM = @getObject id
   catch
@@ -415,6 +417,8 @@
     force: { type: 'boolean' }
   }

+  @checkPermission 'admin'
+
   try
     VM = @getObject id
   catch


Default to maxConnection of 1.

Default to maxConnection of 1.

--- src/broker.coffee
+++ src/broker.coffee
@@ -27,7 +27,7 @@
     @store = new Riak options.options
     @queue = async.queue (task, callback) =>
       @store.write task.id, task, callback
-    , options.maxConnections or 8
+    , options.maxConnections or 1

   _initSockets: ->
     @router = zmq.socket "router"


Implement customMessage

Implement customMessage

--- src/client.coffee
+++ src/client.coffee
@@ -203,6 +203,11 @@
     getChannelByID: (id) ->
         @channels[id]

+    customMessage: (postData, channelID) ->
+        @_apiCall 'POST', '/channels/' + channelID + '/create', postData, (data, header) =>
+            @logger.debug 'Posted custom message.'
+            return true
+
     postMessage: (msg, channelID) ->
         postData =
             message: msg


Moved message splitting logic to postMessage

--- src/client.coffee
+++ src/client.coffee
@@ -283,8 +283,27 @@
             user_id: @self.id
             channel_id: channelID

+        # break apart long messages (over 4096 bytes)
+        limit = 4096
+        post_data = JSON.stringify(postData)
+        message_data = ''
+        message_data = postData.message
+        content_length = new TextEncoder.TextEncoder('utf-8').encode(post_data).length
+        message_length = new TextEncoder.TextEncoder('utf-8').encode(message_data).length
+        message_limit = limit - (content_length - message_length)
+        chunks = []
+        chunks = message_data.match new RegExp("(.|[\r\n]){1,#{message_limit}}","g")
+        console.log "chunks length: #{chunks?.length}"
+        postData.message = chunks.shift()
+        console.log "message length: #{postData.message.length}"
+
         @_apiCall 'POST', @channelRoute(channelID) + '/posts/create', postData, (data, header) =>
             @logger.debug 'Posted message.'
+
+            if chunks?.length > 0
+              msg = chunks.join()
+              @postMessage msg, channelID
+
             return true

     setChannelHeader: (channelID, header) ->
@@ -312,7 +331,6 @@
     _apiCall: (method, path, params, callback) ->
         post_data = ''
         post_data = JSON.stringify(params) if params?
-        content_length = new TextEncoder.TextEncoder('utf-8').encode(post_data).length
         options =
             uri: (if useTLS then 'https://' else 'http://') + @host + (if @options.httpPort? then ':' + @options.httpPort else "") + apiPrefix + path
             method: method
@@ -320,20 +338,9 @@
             rejectUnauthorized: tlsverify
             headers:
                 'Content-Type': 'application/json'
-                'Content-Length': content_length
+                'Content-Length': new TextEncoder.TextEncoder('utf-8').encode(post_data).length
         options.headers['Authorization'] = 'BEARER ' + @token if @token
         @logger.debug "#{method} #{path}"
-
-        # break apart long messages
-        limit = 4096
-        message_limit = limit - (content_length - new TextEncoder.TextEncoder('utf-8').encode(params.message).length) if params?.message?
-        chunks = []
-        chunks = params.message.match new RegExp("[^]{1,#{message_limit}}","g") if params?.message?
-        console.log "chunks: #{chunks}"
-        if chunks.length > 1
-          params.message = chunks.shift()
-          @_apiCall method, path, params, callback
-
         request options, (error, res, value) ->
             if error
                 if callback? then callback({'id': null, 'error': error.errno}, {})


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/color2.coffee
+++ src/color2.coffee
@@ -77,7 +77,7 @@
     return rgbMap.concat(a)

   Color2 = (args...) ->
-    __proto__: Color::
+    __proto__: Color2::

     switch args.length
       when 0
@@ -109,7 +109,7 @@
     b: parsedColor[2]
     a: parsedColor[3]

-  Color:: =
+  Color2:: =
     equal: (other) ->
       other.r == @r &&
       other.g == @g &&


:non-potable_water: Include namespace when canceling subscription

Emissary does not clear our namespace listeners when calling off
with only the event name and the handler.

--- src/config.coffee
+++ src/config.coffee
@@ -211,11 +211,11 @@
         previousValue = _.clone(value)
         callback(value, {previous})

-    subscription = { cancel: => @off 'updated', updateCallback  }
-    @on "updated.#{keyPath.replace(/\./, '-')}", updateCallback
+    eventName = "updated.#{keyPath.replace(/\./, '-')}"
+    subscription = { cancel: => @off eventName, updateCallback  }
+    @on eventName, updateCallback
     callback(value) if options.callNow ? true
     subscription
-

   # Public: Unobserve all callbacks on a given key
   #


Use scale dymamic property

Use scale dymamic property

--- src/config.coffee
+++ src/config.coffee
@@ -5,8 +5,7 @@
     world.get('size') / world.get('split')
   grid_path: (world, args) ->
     size = world.get('size')
-    split = world.get('split')
-    scale = size / split
+    scale = world.get('scale ')
     path = ""
     for n in [scale..size-1] by scale
       path  += "M#{n},1 V#{size - 1} M1,#{n} H#{size - 1} "


Set also defaultSettings.core.projectHome when initializing Config

--- src/config.coffee
+++ src/config.coffee
@@ -407,6 +407,7 @@
       @configFilePath ?= path.join(@configDirPath, 'config.cson')

     @schema.properties.core.properties.projectHome = projectHomeSchema
+    @defaultSettings.core.projectHome = projectHomeSchema.default

   clear: ->
     @emitter = new Emitter


direct: Bind to broker on connect() instead of constructor

A bit more realistic wrt to real transports

--- src/direct.coffee
+++ src/direct.coffee
@@ -8,12 +8,14 @@
 class Client extends interfaces.MessagingClient
   constructor: (@address) ->
 #    console.log 'client', @address
-    @broker = brokers[@address]
+    @broker = null

   ## Broker connection management
   connect: (callback) ->
+    @broker = brokers[@address]
     return callback null
   disconnect: (callback) ->
+    @broker = null
     return callback null

   ## Manipulating queues


Return undefined to ? fallback works

Return undefined to ? fallback works

--- src/editor.coffee
+++ src/editor.coffee
@@ -836,7 +836,7 @@
       continue if @displayBuffer.tokenizedBuffer.lineForScreenRow(bufferRow).isComment()
       if match = @buffer.lineForRow(bufferRow).match(/^[ \t]/)
         return match[0][0] is ' '
-    false
+    undefined

   # Public: Returns a {Boolean} indicating whether softTabs are enabled for this
   # editor.
@@ -871,8 +871,6 @@
   normalizeTabsInBufferRange: (bufferRange) ->
     return unless @getSoftTabs()
     @scanInBufferRange /\t/g, bufferRange, ({replace}) => replace(@getTabText())
-
-

   ###
   Section: Soft Wrap Behavior


Remove metaprogrammed method delegators

Remove metaprogrammed method delegators

--- src/editor.coffee
+++ src/editor.coffee
@@ -151,13 +151,6 @@
     'autoDecreaseIndentForBufferRow', 'toggleLineCommentForBufferRow', 'toggleLineCommentsForBufferRows',
     toProperty: 'languageMode'

-  @delegatesMethods 'setLineHeight', 'getLineHeight', 'getDefaultCharWidth', 'setDefaultCharWidth',
-    'setHeight', 'getHeight', 'setWidth', 'getWidth', 'setScrollTop', 'getScrollTop',
-    'getScrollBottom', 'setScrollBottom', 'getScrollLeft', 'setScrollLeft', 'getScrollRight',
-    'setScrollRight', 'getScrollHeight', 'getScrollWidth', 'getVisibleRowRange',
-    'intersectsVisibleRowRange', 'selectionIntersectsVisibleRowRange', 'pixelPositionForScreenPosition',
-    'screenPositionForPixelPosition', 'pixelPositionForBufferPosition', toProperty: 'displayBuffer'
-
   @delegatesProperties '$lineHeight', '$defaultCharWidth', '$height', '$width',
     '$scrollTop', '$scrollLeft', 'manageScrollPosition', toProperty: 'displayBuffer'

@@ -1826,16 +1819,50 @@
     type: 'selection', editorId: @id, invalidate: 'never'

   getLineHeight: -> @displayBuffer.getLineHeight()
-
   setLineHeight: (lineHeight) -> @displayBuffer.setLineHeight(lineHeight)

   getScopedCharWidth: (args...) -> @displayBuffer.getScopedCharWidth(args...)
+  setScopedCharWidth: (args...) -> @displayBuffer.setScopedCharWidth(args...)

   getScopedCharWidths: (args...) -> @displayBuffer.getScopedCharWidths(args...)

-  setScopedCharWidth: (args...) -> @displayBuffer.setScopedCharWidth(args...)
-
   clearScopedCharWidths: -> @displayBuffer.clearScopedCharWidths()
+
+  getDefaultCharWidth: -> @displayBuffer.getDefaultCharWidth()
+  setDefaultCharWidth: (args...) -> @displayBuffer.setDefaultCharWidth(args...)
+
+  setHeight: (args...) -> @displayBuffer.setHeight(args...)
+  getHeight: -> @displayBuffer.getHeight()
+
+  setWidth: (args...) -> @displayBuffer.setWidth(args...)
+  getWidth: -> @displayBuffer.getWidth()
+
+  getScrollTop: -> @displayBuffer.getScrollTop()
+  setScrollTop: (args...) -> @displayBuffer.setScrollTop(args...)
+
+  getScrollBottom: -> @displayBuffer.getScrollBottom()
+  setScrollBottom: (args...) -> @displayBuffer.setScrollBottom(args...)
+
+  getScrollLeft: -> @displayBuffer.getScrollLeft()
+  setScrollLeft: (args...) -> @displayBuffer.setScrollLeft(args...)
+
+  getScrollRight: -> @displayBuffer.getScrollRight()
+  setScrollRight: (args...) -> @displayBuffer.setScrollRight(args...)
+
+  getScrollHeight: -> @displayBuffer.getScrollHeight()
+  getScrollWidth: (args...) -> @displayBuffer.getScrollWidth(args...)
+
+  getVisibleRowRange: -> @displayBuffer.getVisibleRowRange()
+
+  intersectsVisibleRowRange: (args...) -> @displayBuffer.intersectsVisibleRowRange(args...)
+
+  selectionIntersectsVisibleRowRange: (args...) -> @displayBuffer.selectionIntersectsVisibleRowRange(args...)
+
+  pixelPositionForScreenPosition: (args...) -> @displayBuffer.pixelPositionForScreenPosition(args...)
+
+  pixelPositionForBufferPosition: (args...) -> @displayBuffer.pixelPositionForBufferPosition(args...)
+
+  screenPositionForPixelPosition: (args...) -> @displayBuffer.screenPositionForPixelPosition(args...)

   # Deprecated: Call {::joinLines} instead.
   joinLine: ->


define editor api interface

define editor api interface

--- src/editor.coffee
+++ src/editor.coffee
@@ -1 +1,15 @@
-alert 'editor'
+
+class Editor
+  constructor: (@container) ->
+
+  insertAt: (startIndex, text, attributes = {}) ->
+
+  deleteAt: (startIndex, length) ->
+
+  getAt: (startIndex, length) ->
+
+  applyAttribute: (startIndex, length, attribute) ->
+
+  on: (event, callback) ->
+
+  off: (event, callback) ->


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/follow.coffee
+++ src/follow.coffee
@@ -46,7 +46,7 @@
 Follow = (I={}, self) ->
   Object.reverseMerge I,
     followSpeed: 1
-    velocity: Point(0, 0)
+    direction: Point(0, 0)

   follow: (obj) ->
     I.velocity = obj.position().subtract(self.position()).norm().scale(I.followSpeed)


add debugging again

add debugging again

--- src/hinoki.coffee
+++ src/hinoki.coffee
@@ -94,8 +94,20 @@
         if helfer.isThenable valueOrPromise
           # if the value is already being constructed
           # wait for that instead of starting a second construction.
+          hinoki.debug? {
+            event: 'lifetimeHasPromise'
+            path: path
+            promise: valueOrPromise
+            lifetime: lifetimes[valueIndex]
+          }
           valueOrPromise
         else
+          hinoki.debug? {
+            event: 'lifetimeHasValue'
+            path: path
+            value: valueOrPromise
+            lifetime: lifetimes[valueIndex]
+          }
           Promise.resolve valueOrPromise
       return new hinoki.PromiseAndCacheTarget promise, valueIndex

@@ -121,6 +133,12 @@
         Promise.reject(new hinoki.NotFoundError(path))
         cacheTarget
       )
+
+    hinoki.debug? {
+      event: 'sourceReturnedFactory'
+      path: path
+      factory: factory
+    }

     # we've got a factory.
     # lets make a value
@@ -207,8 +225,29 @@
     if helfer.isError(result)
       return Promise.reject new hinoki.ErrorInFactory path, factoryFunction, result
     if helfer.isThenable result
-      return result.catch (rejection) ->
-        Promise.reject new hinoki.PromiseRejectedError path, factoryFunction, rejection
+      hinoki.debug? {
+        event: 'factoryReturnedPromise'
+        path: path
+        promise: result
+        factory: factoryFunction
+      }
+      return result
+        .then (value) ->
+          hinoki.debug? {
+            event: 'promiseResolved'
+            path: path
+            value: value
+            factory: factoryFunction
+          }
+          return value
+        .catch (rejection) ->
+          Promise.reject new hinoki.PromiseRejectedError path, factoryFunction, rejection
+    hinoki.debug? {
+      event: 'factoryReturnedValue'
+      path: path
+      value: result
+      factory: factoryFunction
+    }
     return Promise.resolve result

   hinoki.callFactoryObjectArray = (path, factoryObject, dependenciesObject) ->


delete redundant promise wrapper

delete redundant promise wrapper

--- server/lib/gdoc_indicator_importer.coffee
+++ server/lib/gdoc_indicator_importer.coffee
@@ -17,30 +17,26 @@

 module.exports =
   import: (key) ->
-    new Promise((resolve, reject) ->

-      spreadsheet = null
-      definition = {}
+    spreadsheet = null
+    definition = {}

-      GDocWrapper.importByKey(key).then((spr)->
-        spreadsheet = spr
+    GDocWrapper.importByKey(key).then((spr)->
+      spreadsheet = spr

-        spreadsheet.getWorksheetData('Definition')
-      ).then((worksheet) ->
+      spreadsheet.getWorksheetData('Definition')
+    ).then((worksheet) ->

-        definition.name = worksheet['2']['1'].value
-        definition.theme = worksheet['2']['2'].value
-        definition.unit = worksheet['2']['3'].value
+      definition.name = worksheet['2']['1'].value
+      definition.theme = worksheet['2']['2'].value
+      definition.unit = worksheet['2']['3'].value

-        spreadsheet.getWorksheetData('Range')
-      ).then((worksheet) ->
+      spreadsheet.getWorksheetData('Range')
+    ).then((worksheet) ->

-        definition.ranges = extractRangesFromWorksheet(worksheet)
+      definition.ranges = extractRangesFromWorksheet(worksheet)

-        indicator = Indicator.buildWithDefaults(definition)
+      indicator = Indicator.buildWithDefaults(definition)

-        Promise.promisify(indicator.save, indicator)()
-      ).then(
-        resolve, reject
-      )
+      Promise.promisify(indicator.save, indicator)()
     )


code style fix

code style fix

--- resources/assets/coffee/react/beatmap-discussions/discussions.coffee
+++ resources/assets/coffee/react/beatmap-discussions/discussions.coffee
@@ -169,7 +169,7 @@
     className += ' u-hide-by-height' unless visible

     if discussion.parent_id?
-      parentDiscussion = _.find(@props.currentDiscussions.generalAll, {id: discussion.parent_id});
+      parentDiscussion = _.find(@props.currentDiscussions.generalAll, {id: discussion.parent_id})

     div
       key: discussion.id


always show nominators (if present)

always show nominators (if present)

--- resources/assets/coffee/react/beatmap-discussions/nominations.coffee
+++ resources/assets/coffee/react/beatmap-discussions/nominations.coffee
@@ -211,7 +211,7 @@
                 else
                   span null, osu.trans 'beatmaps.nominations.qualified-soon'

-            if (mapCanBeNominated || mapIsQualified) && nominators.length > 0
+            if nominators.length > 0
               div
                 className: "#{bn}__note #{bn}__note--nominators"
                 dangerouslySetInnerHTML:


Refactor: migrated storage plugin to classes

--- app/coffee/plugins/storage.coffee
+++ app/coffee/plugins/storage.coffee
@@ -12,30 +12,31 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.

-StorageProvider = ($rootScope) ->
-    service = {}
-    service.get = (key, _default) ->
+class StorageService extends TaigaBaseService
+    @.$inject = ["$rootScope"]
+    constructor: ($rootScope) ->
+        super()
+
+    get: (key, _default) ->
         serializedValue = localStorage.getItem(key)
         if serializedValue == null
             return _default or null

         return JSON.parse(serializedValue)

-    service.set = (key, val) ->
+    set: (key, val) ->
         if _.isObject(key)
-            _.each key, (val, key) ->
-                service.set(key, val)
+            _.each key, (val, key) =>
+                @set(key, val)
         else
             localStorage.setItem(key, JSON.stringify(val))

-    service.remove = (key) ->
+    remove: (key) ->
         localStorage.removeItem(key)

-    service.clear = ->
+    clear: ->
         localStorage.clear()
-
-    return service

 module = angular.module('gmStorage', [])
-module.factory('$gmStorage', ['$rootScope', StorageProvider])
+module.service('$gmStorage', StorageService)


fix the bug of set the payto with contact name

--- app/js/controllers/account.coffee
+++ app/js/controllers/account.coffee
@@ -87,7 +87,7 @@
                     payto.value
                 action: ->
                     (contact)->
-                        payto.value = contact
+                        $scope.payto = contact

     $scope.toggleVoteUp = ->


add more operation map

add more operation map

--- app/js/services/blockchain.coffee
+++ app/js/services/blockchain.coffee
@@ -249,6 +249,10 @@
         add_collateral_op_type : "Add Collateral Operation"
         remove_collateral_op_type : "Remove Collateral Operation"
         note_op_type: "Create Secret Note"
+        ad_op_type: "AD Operation"
+        game_op_type: "Game Operation"
+        buy_chips_type: "Buy Chip Operation"
+        create_game_operation_type: "Create Game Operation"

     # TODO
     populate_delegate: (record, active, rank) ->


Factor out Player#_visualizeTurn from #_endTurn.

--- src/player.coffee
+++ src/player.coffee
@@ -294,13 +294,15 @@
       @debug 'turn:start', @velocity.x

     _endTurn: ->
-      # Visualize the turn here, for now.
-      @sprite.scale.x = @nextDirection
-      @physics.offset = new Phaser.Point @_xOffset(@nextDirection), @_yOffset
+      @_visualizeTurn()

       @nextAction = 'start'
       @_isTurning = no
       @debug 'turn:end', @velocity.x
+
+    _visualizeTurn: (direction = @nextDirection) ->
+      @sprite.scale.x = direction
+      @physics.offset = new Phaser.Point @_xOffset(direction), @_yOffset

     # Update
     # ------


[neo4][cache] return formatted json for single activities

--- server/lib/server/graphdecorator.coffee
+++ server/lib/server/graphdecorator.coffee
@@ -1,36 +1,64 @@
 module.exports = class GraphDecorator

+  singleActivites =
+    'JStatusUpdate' : true
+    'JCodeSnip'     : true
+    'JDiscussion'   : true
+    'JTutorial'     : true
+
+  bucketActivities =
+    'CFollowerBucketActivity'  : true
+    'CNewMemberBucketActivity' : true
+    'CInstallerBucketActivity' : true
+
   @decorateToCacheObject:(data, callback)->
-    cacheObject = []
+
+    cacheObjects = []
+
     for datum in data
-      if datum.name == "JStatusUpdate"
-        localObject = {}
-        localObject.modifiedAt = datum.meta.modifiedAt
-        localObject["type"]       = "CStatusActivity"
-        localObject["_id"]        = datum.id
-        localObject["createdAt"]  = datum.meta.createdAt
-        localObject["originId"]   = datum.originId
-        localObject["originType"] = datum.originType
+      localObject = if singleActivites[datum.name]
+        @extractSingleActivity(datum)
+      else if bucketActivities[datum.name]
+        @extractBucketActivity(datum)
+      else
+        console.log "graphdecorator: unimplemented parsing of #{datum.name}"

-        console.log datum
-        snapshot = {}
-        snapshot.bongo_           = { constructorName : "JStatusUpdate" }
-        snapshot.slug             = datum.slug
-        snapshot.slug_            = datum.slug_
-        snapshot.originId         = datum.originId
-        snapshot.originType       = datum.originType
-        snapshot.meta             = datum.meta
-        snapshot.body             = datum.body
-        snapshot.attachments      = datum.attachments
-        snapshot._id              = datum._id
-        snapshot.repliesCount     = datum.relationData.reply?.length? or 0
-        snapshot.replies          = datum.relationData.reply? or null
-        snapshot.tags             = datum.relationData.tag? or null
-        snapshot.counts           = {}
-        snapshot.counts.following = 0
-        snapshot.counts.followers = datum.relationData.follower?.length or 0
+      cacheObjects.push localObject

-        localObject["snapshot"] = JSON.stringify( snapshot )
+    callback cacheObjects

-        cacheObject.push localObject
-    callback cacheObject+  @extractSingleActivity:(datum)->
+
+    localObject =
+      _id        : datum.id
+      type       : datum.name
+      originId   : datum.originId
+      originType : datum.originType
+      createdAt  : datum.meta.createdAt
+      modifiedAt : datum.meta.modifiedAt
+
+    snapshot =
+      _id               : datum._id
+      bongo_            :
+        constructorName : datum.name
+      slug              : datum.slug
+      slug_             : datum.slug_
+      originId          : datum.originId
+      originType        : datum.originType
+      meta              : datum.meta
+      body              : datum.body
+      attachments       : datum.attachments
+      repliesCount      : datum.relationData.reply?.length? or 0
+      replies           : datum.relationData.reply? or null
+      tags              : datum.relationData.tag? or null
+      counts            :
+        following       : 0
+        followers       : datum.relationData.follower?.length or 0
+
+    localObject["snapshot"] = JSON.stringify snapshot
+
+    return localObject
+
+  @extractBucketActivity:(datum)->
+
+    console.log "TODO: parse #{datum.name}"


Allow for herbarium subjects with null metadata (batch 2)

--- overrides.coffee
+++ overrides.coffee
@@ -89,17 +89,17 @@

       if page.workflow is 'herbarium'
         page.decisionTree.tasks.verify.setDefaults
-          species: metadata.scientific_name
-          date: metadata.collection_date_from
-          locality: metadata.locality_string
-          comments: metadata.label_comments
+          species: metadata.scientific_name ? ''
+          date: metadata.collection_date_from ? ''
+          locality: metadata.locality_string ? ''
+          comments: metadata.label_comments ? ''

         page.decisionTree.tasks.vc.setDefaults
-          vc: metadata.vc_number
-          registration: metadata.registration
+          vc: metadata.vc_number ? ''
+          registration: metadata.registration ? ''

         # disable the herbarium species if NHM already know it.
-        herbarium_species.disabled = !!metadata.scientific_name.length
+        herbarium_species.disabled = !!herbarium_species.value.length

       if page.workflow is 'field'
         page.decisionTree.tasks.species.clearFilters()


improve metrics of aspool

improve metrics of aspool

--- app/coffee/ASpellWorker.coffee
+++ app/coffee/ASpellWorker.coffee
@@ -11,11 +11,11 @@
 		@count = 0
 		@pipe = child_process.spawn("aspell", ["pipe", "-t", "--encoding=utf-8", "-d", language])
 		logger.info process: @pipe.pid, lang: @language, "starting new aspell worker"
-		metrics.inc "aspellWorker-start", 1, {method: @language}
+		metrics.inc "aspellWorker", 1, {status: "start", method: @language}
 		@pipe.on 'exit', () =>
 			@state = 'killed'
 			logger.info process: @pipe.pid, lang: @language, "aspell worker has exited"
-			metrics.inc "aspellWorker-exit" , 1, {method: @language}
+			metrics.inc "aspellWorker" , 1, {status: "exit", method: @language}
 		@pipe.on 'close', () =>
 			@state = 'closed' unless @state == 'killed'
 			if @callback?


fixing the stream to remove the youtube icon when clicked, shows up when video is minimized

--- app/coffee/publicstream.coffee
+++ app/coffee/publicstream.coffee
@@ -8,6 +8,8 @@
     link_id = $(el).data('id')
     $(el).find('a').click( (event) =>
       event.preventDefault()
+
+      $(event.currentTarget).parent().hide()

       $('.thumb-1d .video').hide()
       $('.thumb-1d').removeClass('videoExpand')


path to map tiles

path to map tiles

--- app/controllers/explore.coffee
+++ app/controllers/explore.coffee
@@ -92,7 +92,7 @@
     @map.el.appendTo @el.find('.map-container')

     # Create a custom layer
-    @terrainLayer = L.tileLayer("/tiles/{z}/{x}/{y}.png",
+    @terrainLayer = L.tileLayer("http://www.snapshotserengeti.org.s3.amazonaws.com/tiles/{z}/{x}/{y}.png",
       minZoom: 7
       maxZoom: 12
       attribution: 'Natural Earth (http://www.naturalearthdata.com/)'


Remove redundant output.

Remove redundant output.

--- src/pulsar.coffee
+++ src/pulsar.coffee
@@ -21,7 +21,6 @@
     pending = new PulsarJob(application, environment, 'deploy:pending', chat, true)
     pending.onfinish = ()->
       if(@.data.status != 'FINISHED')
-        chat.send @ + ' can\'t be executed due to previous errors'
         return
       deploy = new PulsarJob(application, environment, 'deploy', chat, isVerbose)
       deploy.onstart = () ->


Fix package menu items

Fix package menu items

Ampersands (`&`) don't show up in the actual menu (at least on macOS/X) so simply replace them with `+`.

--- menus/git-plus.cson
+++ menus/git-plus.cson
@@ -38,19 +38,19 @@
           'command': 'git-plus:diff-all'
         }
         {
-          'label': 'Add & Commit'
+          'label': 'Add + Commit'
           'command': 'git-plus:add-and-commit'
         }
         {
-          'label': 'Add & Commit & Push'
+          'label': 'Add + Commit + Push'
           'command': 'git-plus:add-and-commit-and-push'
         }
         {
-          'label': 'Add All & Commit'
+          'label': 'Add All + Commit'
           'command': 'git-plus:add-all-and-commit'
         }
         {
-          'label': 'Add All & Commit & Push'
+          'label': 'Add All + Commit + Push'
           'command': 'git-plus:add-all-commit-and-push'
         }
         {


Sidebar: code cleanup

Sidebar: code cleanup

--- client/app/lib/components/sidebarstacksection/sidebarstackheadersection.coffee
+++ client/app/lib/components/sidebarstacksection/sidebarstackheadersection.coffee
@@ -8,8 +8,7 @@

   openStackEditor: (e) ->

-    e.preventDefault()
-    e.stopPropagation()
+    kd.utils.stopDOMEvent e
     kd.singletons.router.handleRoute '/Stack-Editor'


Ace: update button colors in save&close modal

--- client/landing/app/Applications/Ace.kdapplication/aceapplicationtabview.coffee
+++ client/landing/app/Applications/Ace.kdapplication/aceapplicationtabview.coffee
@@ -15,7 +15,7 @@
       overlay       : yes
       buttons       :
         "SaveClose" :
-          cssClass  : "modal-clean-gray"
+          cssClass  : "modal-clean-green"
           title     : "Save and Close"
           callback  : =>
             if file.path.indexOf("localfile:") is 0
@@ -27,7 +27,7 @@
               ace.requestSave()
               @closePaneAndModal pane, modal
         "DontSave"  :
-          cssClass  : "modal-clean-gray"
+          cssClass  : "modal-clean-red"
           title     : "Don't Save"
           callback  : =>
             @closePaneAndModal pane, modal


Add ability to delete a list value

Add ability to delete a list value

--- app/assets/javascripts/admin/views/home/add_integration.js.coffee
+++ app/assets/javascripts/admin/views/home/add_integration.js.coffee
@@ -90,6 +90,11 @@
       $(e.currentTarget).parent().parent().parent().prepend(@listTemplate())
       false

+    @$el.on 'click', '.delete-value', (e) =>
+      window.Foo = $(e.currentTarget)
+      $(e.currentTarget).closest('.list-item').remove()
+      false
+
   parametersByConsumer: ->
     @ret = {}


fix task-overview, change aspect-ratio, avoid unnecessary semi-colons

--- app/assets/javascripts/admin/views/task/task_overview_view.coffee
+++ app/assets/javascripts/admin/views/task/task_overview_view.coffee
@@ -87,20 +87,23 @@

   fetchData : (start, end) ->

-    @minMaxHours = {}
     @fetchPromise = @model.fetch(
       data:
         start: start
         end: end
-    )
+    ).done( => @updateMinMaxHours() )

   getMinMaxHours : ->

-    # This function calculates the min/max working hours of the whole data set
+    # This function calculates the min/max working hours of the users of the selected team
     if _.isEmpty(@minMaxHours)
-      minTime = Math.min(_.pluck(@model.get("userInfos"), "workingTime")...)
-      maxTime = Math.max(_.pluck(@model.get("userInfos"), "workingTime")...)
+      selectedUsers = _.filter(@model.get("userInfos"), (userInfo) => @doDrawUser(userInfo.user))
+      workingTimes = _.pluck(selectedUsers, "workingTime")
+
+      if _.isEmpty(workingTimes) then workingTimes = [0]
+      minTime = Math.min(workingTimes...)
+      maxTime = Math.max(workingTimes...)

       # Convert ms to h
       @minMaxHours =
@@ -112,9 +115,17 @@
     return @minMaxHours

+  updateMinMaxHours : ->
+
+    @minMaxHours = {}
+    @getMinMaxHours()
+
+
   updateSelectedTeam : ->

     @team = @ui.team.find("select")[0].value
+    @updateMinMaxHours()
+    @renderRangeSlider()

   onRender : ->
@@ -280,7 +291,7 @@

     svgHead = """
               digraph G {
-                size="10,10";
+                size="16,9";
                 overlap=false;
                 graph [ bgcolor="transparent" ];
                 node  [ fontname="Helvetica, Arial, sans-serif",
@@ -296,7 +307,7 @@

   buildNodes : (taskTypes, projects) ->

-    svgUsers = @users.map( (user) =>
+    svgUsers = _.compact(@users.map( (user) =>
       if @doDrawUser(user)
         userName = user.firstName + " " + user.lastName
         workingHours = user.workingHours
@@ -304,7 +315,7 @@
         color = @colorJet(128 - (workingHours - minMaxHours.min) * (128 / minMaxHours.max - minMaxHours.min) + 128)

         @quoted(userName) + " [id="+ @quoted(user.id) + ", shape=box, fillcolor=" + @quoted(color) + "]"
-    ).join(";")
+    )).join(";")

     svgTaskTypes = ""
     svgProjects = ""
@@ -325,15 +336,17 @@
     svgFutureTaskTypesEdges = ""
     svgProjectEdges = ""

+    selectedUserInfos = _.filter(userInfos, (userInfo) => @doDrawUser(userInfo.user))
+
     if @doDrawTaskTypes()

-      svgTaskTypeEdges = userInfos.map( (userInfo) =>
+      svgTaskTypeEdges = selectedUserInfos.map( (userInfo) =>
         { user, taskTypes } = userInfo
         taskTypes.map( (taskType) => @edge(user.name, taskType.summary)) if @doDrawUser(user)
       ).join(";")

       svgFutureTaskTypesEdges  = "edge [ color=blue ];"
-      svgFutureTaskTypesEdges += userInfos.map( (userInfo) =>
+      svgFutureTaskTypesEdges += selectedUserInfos.map( (userInfo) =>
         { user, futureTaskType } = userInfo
         if(futureTaskType)
           @edge(user.name, futureTaskType.summary) if @doDrawUser(user)
@@ -341,7 +354,7 @@

     if @doDrawProjects()
-      svgProjectEdges = userInfos.map( (userInfo) =>
+      svgProjectEdges = selectedUserInfos.map( (userInfo) =>
         { user, projects } = userInfo
         projects.map( (project) => @edge(user.name, project.name)) if @doDrawUser(user)
       ).join(";")


FIXED current user reference in ViewProfileCtrl.

--- app/assets/javascripts/angular/controllers/ViewProfileCtrl.coffee
+++ app/assets/javascripts/angular/controllers/ViewProfileCtrl.coffee
@@ -11,6 +11,7 @@
         existing = _.find(result.data.apprenticeships, (apprenticeship) ->
           apprenticeship.links.master.id == user.id and apprenticeship.links.apprentice.id == currentUser.id
         )
+        console.log existing
         if existing
           $scope.bowLabel = "Connected"
           $('#bow-button').removeAttr("data-toggle")
@@ -24,7 +25,7 @@

   defer.promise
     .then User.loadCurrentUser().then (result) ->
-      $scope.currentUser = result
+      $scope.currentUser = result.users[0]
     .then User.loadOne(userId).then (result) ->
       $scope.user = result.users[0]
       showOrHideBow($scope.user, $scope.currentUser)
@@ -40,8 +41,6 @@
         $('#bow-modal').modal('hide')
         $scope.bowLabel = "Connected"
         $('#bow-button').removeAttr("data-toggle")
-    else
-      console.log "apprenticeship must be between an apprentice and master"

 ])


bugfix

bugfix

--- app/assets/javascripts/views/plugins/delete_button_view.js.coffee
+++ app/assets/javascripts/views/plugins/delete_button_view.js.coffee
@@ -26,7 +26,8 @@
     @showFirstPart()
     @$(".first a").button("loading")
     setTimeout =>
-      @$(".first a").button("reset")
+      if $btn = @$(".first a")
+        $btn.button("reset")
     , 2000
     null


Implement update functions for both if and unless binders. [#183]

--- src/rivets.coffee
+++ src/rivets.coffee
@@ -408,6 +408,9 @@
           @nested.unbind()
           delete @nested

+    update: (models) ->
+      @nested.update models
+
   unless:
     block: true

@@ -419,6 +422,9 @@

     routine: (el, value) ->
       Rivets.binders.if.routine.call @, el, not value
+
+    update: (models) ->
+      Rivets.binders.if.update.call @, models

   "on-*":
     function: true


blurg

blurg

--- src/asset.coffee
+++ src/asset.coffee
@@ -110,14 +110,14 @@
 Asset.registerFormatter
   to: "html"
   from:  "markdown"
-  async (asset) -> # md2html yield read asset.path
+  async (asset) ->
     if !asset.renderer?
       directory = dirname asset.path
       layout = "_layout"
       until layout.length > 13 || "_layout.jade" in (yield readdir directory)
         directory = resolve directory, ".."
         layout = join "..", layout
-      html = (md2html (yield read asset.path))
+      html = md2html yield read asset.path
         .replace /#\{/gm, "#{"
         .replace /\n/gm, "\n    "
       template = """


Update to v1 API.

Update to v1 API.

--- src/basic.coffee
+++ src/basic.coffee
@@ -64,7 +64,7 @@
   suggest: (success, error) ->
     @logger.log(LogLevel.DEBUG, "myna.suggest called")

-    data = { agent: @experiment }
+    data = { }

     # object string xhr -> void
     successWrapper = (data, msg, xhr) =>
@@ -91,7 +91,7 @@
         @logger.log(LogLevel.ERROR, "Myna.suggest did something unexpected")
         @logger.log(LogLevel.ERROR, data)
         if error
-          error(400, "The Myna client didn't handle this data: " + data)
+          error(400, "The Myna client didn't handle this data: #{data}")

       # xhr string string -> void
@@ -103,12 +103,12 @@
       @logger.log(LogLevel.ERROR, text)
       @logger.log(LogLevel.ERROR, error)
       @logger.log(LogLevel.ERROR, response)
-      @logger.log(LogLevel.ERROR, "myna.suggest failed: error " + response.code + " " + response.message)
+      @logger.log(LogLevel.ERROR, "myna.suggest failed: error #{response.code} #{response.message}")

       if error
         error(response.code, response.message)

-    this.doAjax("/suggest", data, successWrapper, errorWrapper)
+    this.doAjax("/v1/experiment/#{@experiment}/suggest", data, successWrapper, errorWrapper)

   # (Number, () -> Any, (Number, String) -> Any) -> Undefined
@@ -132,7 +132,6 @@
       return

     data =
-      agent: @experiment
       token: myna.token
       amount: amount || 1.0

@@ -156,7 +155,7 @@
       if error
         error(response.code, response.message)

-    myna.doAjax("/reward", data, successWrapper, errorWrapper)
+    myna.doAjax("/v1/experiment/#{@experiment}/reward", data, successWrapper, errorWrapper)

   saveToken: (token) ->


Fix race condition with synchronous networks where sometimes we wouldn't see all received packets, refs noflo/canadianness#9

--- src/runner.coffee
+++ src/runner.coffee
@@ -105,7 +105,9 @@
     return false unless s.payload.graph is currentGraph
     # We're only interested in data IPs, not brackets
     return false unless s.payload.event is 'data'
-    received = signalsToReceived observer.signals
+    # Get signals received until this message
+    receivedSignals = observer.signals.slice 0, observer.signals.indexOf(s) + 1
+    received = signalsToReceived receivedSignals
     result = (Object.keys(received).length == Object.keys(expectData).length)
     return result
   checkFailure = (s) ->


Fix test

Fix test

--- apps/artists/test/template.coffee
+++ apps/artists/test/template.coffee
@@ -32,7 +32,11 @@
         )

         @featuredLinksCollection = new Items(
-          _.times(3, -> new FeaturedLink(fabricate('featured_link'))), { id: 'foobar', item_type: 'FeaturedLink' }
+          _.times(3, ->
+            link = new FeaturedLink(fabricate('featured_link'))
+            link.set 'artist', new Artist(fabricate('artist'))
+            link
+          ), { id: 'foobar', item_type: 'FeaturedLink' }
         )

         @genes = new Items(_.times(2, =>


only redraw canvas is not empty

only redraw canvas is not empty

--- views/gorillas.coffee
+++ views/gorillas.coffee
@@ -19,6 +19,9 @@
     @height = @base_height + y
     @context.fillRect @position_at_x(), @position_at_y(), @width, @height
     @build_windows(@position_at_x(), @position_at_y())
+
+  redraw: ->
+    @draw(@x, @y)

   build_windows:(x, y) ->
     times = Math.round (@height)/31
@@ -102,6 +105,7 @@

 class Painter
   constructor: ->
+    @empty = true
     @canvas = document.getElementById "gorillas"
     @context = @canvas.getContext "2d"
     @color = '#00FFFF'
@@ -109,10 +113,20 @@
     @buildings = []

   draw_scene: ->
-    @clear()
-    @draw_the_sun()
-    @draw_buildings()
-    @draw_gorillas()
+    unless @empty
+      @clear()
+      @draw_the_sun()
+      @redraw_buildings()
+      @draw_gorillas()
+    else
+      @empty = false
+      @draw_the_sun()
+      @draw_buildings()
+      @draw_gorillas()
+
+  redraw_buildings:()->
+    for pos in [0...9]
+      @buildings[pos].redraw()

   draw_buildings:()->
     position = 0


ping ms

ping ms

--- modules/ping.coffee
+++ modules/ping.coffee
@@ -9,7 +9,7 @@
   pingCommandFunction: (msg, args)->
     sd = Date.now()
     @bot.sendMessage msg.channel, "Pong!"
-    .then (m)=> @bot.updateMessage m, "Pong! `#{Date.now() - sd}`"
+    .then (m)=> @bot.updateMessage m, "Pong! `#{Date.now() - sd}ms`"

   shutdown: =>
     @commands.unregisterCommand @pingCommand


fixes the problem with the _refError function.  it was checking against a non-existant argument called `fn'

--- bebopt.coffee
+++ bebopt.coffee
@@ -1,6 +1,10 @@
 'use strict'
-log = require './log'

+if 1
+  log = require './log'
+else
+  log = require './log'
+  log = log.fake()

 class Bebopt
   constructor: (@app) ->
@@ -10,20 +14,17 @@
     @_shortLong ?= {}
     @_parent = null

-  _debug: (y) ->
-    console.log
-
   _refError: (ref) =>
-    if typeof(fn) is 'string'
+    if typeof(ref) is 'string'
       if ref[0] isnt '#'
         err = "Bebopt: ref must begin with pound: `#{ref}'"
         throw new Error(err)
       _refns = ref.replace(/^#(.*)Beat\..*/, '$1')
       _refchild = ref.replace(/^#.*Beat\.(.*)/, '$1')
       _ref = @["_#{_refns}"][_refchild]
-      console.log(util.inspect(_ref, {colors: true, depth: null}))
-      console.log(_refns)
-      console.log(_refchild)
+      log.GOOFY(_ref)
+      log.GOOFY(_refns)
+      log.GOOFY(_refchild)
       if _ref is undefined
         err = "Bebopt: bad reference: #{_ref}: `#{ref}'"
         throw new Error(err)


Add 'for' tests.

Add 'for' tests.

--- spec/powershell-spec.coffee
+++ spec/powershell-spec.coffee
@@ -107,3 +107,12 @@
         expect(tokens[0]).toEqual value: "do", scopes: ["source.powershell","keyword.control.flow.powershell"]
       it "should highlight 'until'", ->
         expect(tokens[14]).toEqual value: "until", scopes: ["source.powershell","keyword.control.flow.powershell"]
+
+    describe "'For' statements", ->
+      tokens = null
+
+      beforeEach ->
+        {tokens} = grammar.tokenizeLine("for($i=0;i<10;$i++) { echo $i }")
+
+      it "should highlight 'for'", ->
+        expect(tokens[0]).toEqual value: "for", scopes: ["source.powershell","keyword.control.flow.powershell"]


Wait for package activation in specs

Wait for package activation in specs

--- spec/sort-lines-spec.coffee
+++ spec/sort-lines-spec.coffee
@@ -1,70 +1,81 @@
 {WorkspaceView} = require 'atom'

 describe "sorting lines", ->
-  [editorView] = []
+  [activationPromise, editor, editorView] = []
+
+  sortLines = (callback) ->
+    editorView.trigger "sort-lines:sort"
+    waitsForPromise -> activationPromise
+    runs(callback)

   beforeEach ->
     atom.workspaceView = new WorkspaceView
     atom.workspaceView.openSync()
-    atom.packages.activatePackage('sort-lines')
+
     editorView = atom.workspaceView.getActiveView()
+    editor = editorView.getEditor()
+
+    activationPromise = atom.packages.activatePackage('sort-lines')

   describe "when no lines are selected", ->
     it "sorts all lines", ->
-      editorView.setText """
+      editor.setText """
         Hydrogen
         Helium
         Lithium
       """
-      editorView.setCursorBufferPosition([0, 0])
-      editorView.trigger "sort-lines:sort"
-      expect(editorView.getText()).toBe """
-        Helium
-        Hydrogen
-        Lithium
-      """
+      editor.setCursorBufferPosition([0, 0])
+
+      sortLines ->
+        expect(editor.getText()).toBe """
+          Helium
+          Hydrogen
+          Lithium
+        """

   describe "when entire lines are selected", ->
     it "sorts the selected lines", ->
-      editorView.setText """
+      editor.setText """
         Hydrogen
         Helium
         Lithium
         Beryllium
         Boron
       """
-      editorView.setSelectedBufferRange([[1,0], [4,0]])
-      editorView.trigger "sort-lines:sort"
-      expect(editorView.getText()).toBe """
-        Hydrogen
-        Beryllium
-        Helium
-        Lithium
-        Boron
-      """
+      editor.setSelectedBufferRange([[1,0], [4,0]])
+
+      sortLines ->
+        expect(editor.getText()).toBe """
+          Hydrogen
+          Beryllium
+          Helium
+          Lithium
+          Boron
+        """

   describe "when partial lines are selected", ->
     it "sorts the selected lines", ->
-      editorView.setText """
+      editor.setText """
         Hydrogen
         Helium
         Lithium
         Beryllium
         Boron
       """
-      editorView.setSelectedBufferRange([[1,3], [3,2]])
-      editorView.trigger "sort-lines:sort"
-      expect(editorView.getText()).toBe """
-        Hydrogen
-        Beryllium
-        Helium
-        Lithium
-        Boron
-      """
+      editor.setSelectedBufferRange([[1,3], [3,2]])
+
+      sortLines ->
+        expect(editor.getText()).toBe """
+          Hydrogen
+          Beryllium
+          Helium
+          Lithium
+          Boron
+        """

   describe "when there are multiple selection ranges", ->
     it "sorts the lines in each selection range", ->
-      editorView.setText """
+      editor.setText """
         Hydrogen
         Helium    # selection 1
         Beryllium # selection 1
@@ -74,16 +85,17 @@
         Gallium
         Europium
       """
-      editorView.addSelectionForBufferRange([[1, 0], [3, 0]])
-      editorView.addSelectionForBufferRange([[4, 0], [6, 0]])
-      editorView.trigger "sort-lines:sort"
-      expect(editorView.getText()).toBe """
-        Hydrogen
-        Beryllium # selection 1
-        Helium    # selection 1
-        Carbon
-        Aluminum  # selection 2
-        Fluorine  # selection 2
-        Gallium
-        Europium
-      """
+      editor.addSelectionForBufferRange([[1, 0], [3, 0]])
+      editor.addSelectionForBufferRange([[4, 0], [6, 0]])
+
+      sortLines ->
+        expect(editor.getText()).toBe """
+          Hydrogen
+          Beryllium # selection 1
+          Helium    # selection 1
+          Carbon
+          Aluminum  # selection 2
+          Fluorine  # selection 2
+          Gallium
+          Europium
+        """


fix attrs

fix attrs

--- app/assets/javascripts/monster/list.coffee
+++ app/assets/javascripts/monster/list.coffee
@@ -4,6 +4,8 @@
   popups : false
   root   : false
   action : 'index'
+  attrs: =>
+    {}
   constructor:->
     @table_name = @_controller unless @table_name
     @_register()
@@ -15,10 +17,10 @@
     @index_success null, @data() if @data
     @$.destroy = @destroy
   reindex:=>
-    attrs = {}
+    attrs = @attrs()
     if @$.pagination && @$.pagination.page
       attrs.page = @$.pagination.page
-    @Api[@table_name][@action] attrs
+    @Api[@table_name][@action] @attrs()
   destroy:(model)=>
     name = _.singularize @table_name
     msg  = "Are you sure you wish to to destroy this #{name}"
@@ -56,7 +58,7 @@
 class window.PusherList
   scope: []
   pull: false
-  popups: true
+  popups: false
   root   : false
   action : 'index'
   constructor:->


Fix merge error

Fix merge error

--- app/assets/javascripts/oxalis/model.coffee
+++ app/assets/javascripts/oxalis/model.coffee
@@ -150,6 +150,9 @@
     @initSettersGetter()
     @trigger("sync")

+    # no error
+    return
+

   # For now, since we have no UI for this
   buildMappingsObject : (layers) ->


improves vertical sort

improves vertical sort

--- app/static/scripts/room.coffee
+++ app/static/scripts/room.coffee
@@ -242,10 +242,14 @@
                 $('.module').css 'clear', ''
             stop: (event, ui) ->
                 draggedItemId = '#' + ui.item.attr 'id'
-                prevHeight = do (do ui.item.prev).height
-                nextHeight = do (do ui.item.next).height
-                if ui.position.top >= prevHeight && ui.position.top >= nextHeight
-                    $(draggedItemId).css 'clear', 'left'
+                if ui.position.left < ui.originalPosition.left
+                    prevHeight = do (ui.item.prevAll '.module:first').height
+                    if prevHeight && ui.position.top >= prevHeight
+                        $(draggedItemId).css 'clear', 'left'
+                else if ui.position.left > ui.originalPosition.left
+                    nextHeight = do (ui.item.nextAll '.module:first').height
+                    if nextHeight && ui.position.top >= nextHeight
+                        $(draggedItemId).css 'clear', 'left'
                 else
                     $(draggedItemId).css 'clear', ''
                 if $('#mainCam').length


OHM-125: Hardend heartbeat uptime script

--- src/server.coffee
+++ src/server.coffee
@@ -616,11 +616,13 @@
           uptime =
             master: uptime.masterUptime

+          # remove eventListner
+          process.removeListener 'message', processEvent
+
           # send reponse back to API request
           return callback null, uptime

-          # remove eventListner
-          process.removeListener 'message', processEvent
+

       # listen for response from master


favorites popout bug fix

favorites popout bug fix

clicking close button would take you to home page. selector upgraded
for disambiguation.

--- app/assets/transit/lib/views/popout.coffee
+++ app/assets/transit/lib/views/popout.coffee
@@ -11,7 +11,7 @@
     'click .directions .btn-go': 'loadDirections'
     'click .directions .btn.here': 'geolocate'
     'click .options .btn-go': 'updateTrip'
-    'click .favorites a': 'navigateFavorite'
+    'click .favorites li a': 'navigateFavorite'

   render: () ->
     @options.parent.addClass('active')


Show conference date when conference is scheduled in the future

Test Plan:
  1.  Create a webex conference for a future date
  2.  Verify that the date shows up in the new conferences table

Change-Id: I6592c856c8ed0a3c7264d76f7fd4a4446ef790a2
closes: PS-1102
Reviewed-on: https://gerrit.instructure.com/31884
Tested-by: Jenkins <d95b56ce41a2e1ac4cecdd398defd7414407cc08@instructure.com>
Reviewed-by: Brandon Broschinsky <0c0b68ac974b5862d5ba281c3301c898f1fd97d1@instructure.com>
Product-Review: Adam Phillipps <0e18f44c1fec03ec4083422cb58ba6a09ac4fb2a@instructure.com>
QA-Review: Adam Phillipps <0e18f44c1fec03ec4083422cb58ba6a09ac4fb2a@instructure.com>

--- app/coffeescripts/models/Conference.coffee
+++ app/coffeescripts/models/Conference.coffee
@@ -25,7 +25,7 @@
       show_end: @get('permissions')['initiate'] && @get('started_at') && @get('long_running')

     schedule_data: ->
-      scheduled: 'scheduled_date' in @get('user_settings')
+      scheduled: 'scheduled_date' of @get('user_settings')
       scheduled_at: @get('user_settings').scheduled_date

     toJSON: ->


js - don't need anymore

js - don't need anymore

--- _assets/javascripts/shared/shared.coffee
+++ _assets/javascripts/shared/shared.coffee
@@ -8,7 +8,6 @@
     ElloWTFShared.watchSearchHeader()
     ElloWTFShared.watchURLSearchTerms()
     ElloWTFShared.watchSearchToggle()
-    ElloWTFShared.mobileDrawerCategoryWatch()
     ElloWTFShared.watchShareable()

   checkMobile: ->
@@ -75,7 +74,6 @@
         ElloWTFSearch.invokeSearch()
         return false

-
     $search_box.on "focusin", ->
       $(".search_holder").addClass("active")
     $search_box.on "focusout", ->
@@ -125,19 +123,6 @@
       # $search_form.removeClass("expanded") # might now need this anymore
       $search_form.find('span.form').removeAttr('style') # reset it in case previously fired

-  mobileDrawerCategoryWatch: ->
-    $(".category.main h2").click (e) ->
-      if $(window).width() < 720
-        e.preventDefault()
-
-      $category = $(this).parents(".category")
-      if $category.hasClass("expanded")
-        $category.removeClass("expanded")
-      else
-        $(".category.main").each ->
-          $(this).removeClass("expanded")
-        $(this).parents(".category").toggleClass("expanded")
-
   getURLParameter: (parameter) ->
     page_url = window.location.search.substring(1)
     url_variables = page_url.split("&")


fix scan benchmark

fix scan benchmark

--- test/perf/perf-specs/scan.coffee
+++ test/perf/perf-specs/scan.coffee
@@ -1,5 +1,5 @@
 require('../perf-helper.coffee').setupTest 'stream.scan(0, (a, x) -> a + x)', {
-  kefir: (stream) -> stream.scan(0, (a, x) -> a + x)
+  kefir: (stream) -> stream.scan(((a, x) -> a + x), 0)
   rx: (stream) -> stream.scan(0, (a, x) -> a + x)
   bacon: (stream) -> stream.scan(0, (a, x) -> a + x)
   getVal: -> 1


Added new account dropdown to navbar.

Added new account dropdown to navbar.

--- web/coffee/navbar.coffee
+++ web/coffee/navbar.coffee
@@ -11,7 +11,9 @@
     FAQ: "/faq"
     Sponsors: "/sponsors"
     News: "/news"
-  Logout: "/logout"
+  Account:
+    Manage: "/account"
+    Logout: "/logout"

 userLoggedIn =
   Game: "/game"
@@ -22,7 +24,9 @@
     FAQ: "/faq"
     Sponsors: "/sponsors"
     News: "/news"
-  Logout: "/logout"
+  Account:
+    Manage: "/account"
+    Logout: "/logout"

 userNotLoggedIn =
   Registration: "/registration"


Forward up.layer.isFront()

Forward up.layer.isFront()

--- lib/assets/javascripts/unpoly/layer.coffee
+++ lib/assets/javascripts/unpoly/layer.coffee
@@ -876,6 +876,7 @@
     'dismiss' # yes
     'isRoot' # yes
     'isOverlay' # yes
+    'isFront', # yes
     'on' # yes
     'off' # yes
     'emit' # yes


Fix docs for up.request

Fix docs for up.request

--- lib/assets/javascripts/unpoly/proxy.coffee
+++ lib/assets/javascripts/unpoly/proxy.coffee
@@ -188,7 +188,7 @@
     requests, if available. If set to `false` a network connection will always be attempted.
   @param {Object} [options.headers={}]
     An object of additional HTTP headers.
-  @param {Object} [options.data={}]
+  @param {Object|Array|FormData} [options.data={}]
     Parameters that should be sent as the request's payload.

     Parameters may be passed as one of the following forms:


don't mess with apostrophes

don't mess with apostrophes

--- resource/translators/translator.coffee
+++ resource/translators/translator.coffee
@@ -315,7 +315,7 @@
           if name.family.length > 1 && name.family[0] == '"' && name.family[name.family.length - 1] == '"'
             name.family = name.family.slice(1, -1)
           else
-            Zotero.BetterBibTeX.CSL.parseParticles(name, true)
+            Zotero.BetterBibTeX.CSL.parseParticles(name)

             if name['non-dropping-particle']
               name.family = '{' + LaTeX.text2latex((name['non-dropping-particle'] + ' ' + name.family).trim()) + '}'


refactored finders on vehicles controller

--- lib/controllers/vehicles_controller.coffee
+++ lib/controllers/vehicles_controller.coffee
@@ -1,11 +1,8 @@
 Sensor.VehiclesController = SC.ArrayProxy.extend
   content: []
-  findById: (id) ->
-    @get('content').find (v) -> v.get('id') == id
-  findByImei: (imei) ->
-    @get('content').find (v) -> v.get('imei') == imei
-  findByNickname: (nickname) ->
-    @findProperty('nickname',nickname)
+  findById: (id) -> @findProperty('id',id)
+  findByImei: (imei) -> @findProperty('imei',imei)
+  findByNickname: (nickname) -> @findProperty('nickname',nickname)
   moving: ( ->
     console.log "calculating moving"
     @filterProperty('state','moving')


small fix for logger usage

small fix for logger usage

--- src/sirius.coffee
+++ src/sirius.coffee
@@ -313,7 +313,7 @@
     if window.console
       console.log msg
     else
-      alert "Not supported `console`"
+      alert "Not supported `console`. You should define own `logger` function for Sirius.Application"
   #
   # @method #run(options)
   # @param options [Object] - base options for application
@@ -325,6 +325,7 @@
     @logger  = options["logger"]  || @logger
     @start    = options["start"]  || @start
     @logger("Logger enabled? #{@log}")
+
     n = @adapter.constructor.name
     @logger("Adapter: #{n}")