- [grunt] any changes applied to nunjucks partials, which begins with `_*.nj`, will now kick-off renedring of all layouts during watch

--- tasks/grunt-contrib-watch.coffee
+++ tasks/grunt-contrib-watch.coffee
@@ -23,8 +23,11 @@
       # @todo Add `newer:` when relative `grunt-newer` bug will be fixed
       tasks: ['sprite']
     nunjucks:
-      files: ['<%= path.source.layouts %>/{,**/}*.nj']
+      files: ['<%= path.source.layouts %>/{,**/}*.nj', '!<%= path.source.layouts %>{,**/}_*.nj']
       tasks: ['newer:nunjucks']
+    nunjucksPartials:
+      files: ['<%= path.source.layouts %>{,**/}_*.nj']
+      tasks: ['nunjucks']
     images:
       files: ['<%= path.source.images %>/{,**/}*.{jpg,jpeg,gif,png}']
       tasks: ['newer:responsive_images:thumbnails']


FIX: Fix filters to use new staff filter value

--- src/app/common/filters/filters.coffee
+++ src/app/common/filters/filters.coffee
@@ -11,7 +11,7 @@
 .filter('showStudents', ->
   (input, kind, tutorName) ->
     if input
-      if kind == "myStudents"
+      if kind == "mine"
         _.filter  input, { tutorial: {tutor_name: tutorName} }
       else
         input
@@ -22,7 +22,7 @@
 .filter('showTasks', ->
   (input, kind, tutorName) ->
     if input
-      if kind == "myStudents"
+      if kind == "mine"
         _.filter  input, (t) -> (t?) && t.project().tutorName() == tutorName
       else
         input


Input fixes

Input fixes

--- src/game.coffee
+++ src/game.coffee
@@ -56,12 +56,6 @@
     _create_registry: (data) ->
         cache = engine.utils.create_from_constructor_string @config.registry_cache_class
         return engine.utils.create_from_constructor_string @config.thing_registry_class, cache, data
-
-    _setup_keyboard_manager: ->
-        @keyboard_manager = new engine.input.KeyboardManager()
-        # TODO: Send actual bindings
-        @player_keyboard_input = @keyboard_manager.bind_input_layer @player_action_mappings, false
-        #@global_ui_keyboard_input = @keyboard_manager.bind_input_layer undefined, false

     _define_player_action_mappings: (player) ->
         bindings = [
@@ -110,20 +104,30 @@
                         @player_action_failure_callback error_message
             )(binding)

+    _setup_keyboard_manager: ->
+        @keyboard_manager = new engine.input.KeyboardManager()
+        # TODO: Send actual bindings
+        @player_keyboard_input = @keyboard_manager.bind_input_layer @player_action_mappings, false
+        #@global_ui_keyboard_input = @keyboard_manager.bind_input_layer undefined, false
+
+    _player_input_on: ->
+        @player_keyboard_input.on()
+
+    _player_input_off: ->
+        @player_keyboard_input.off()
+
     _protagonist_ready: (player_character) ->
         player_character.listen_for_actions_handler = (success_callback, failure_callback) =>
             @player_action_success_callback = success_callback
             @player_action_failure_callback = failure_callback
-            # Start listening for input
             @_player_input_on()

         player_character.offerred_turn_handler = =>
             # Every time the player is offered a turn we will auto-save
             @quicksave()
-            # And allow them input
-            @_player_input_on()
+
+        @_define_player_action_mappings player_character
         @_setup_keyboard_manager()
-        @_define_player_action_mappings player_character
         @world.protagonist_ready player_character

     _get_save_data: (is_quicksave=false) ->
@@ -155,12 +159,6 @@
         ###
         return @_get_save_data false

-    _player_input_on: ->
-        @player_keyboard_input.on()
-
-    _player_input_off: ->
-        @player_keyboard_input.off()
-
     set_protagonist: (creature) ->
         @registry.register_thing creature
         player_character = engine.utils.create_from_constructor_string @config.player_character_class


no more refresh route

no more refresh route

--- apps/auth/index.coffee
+++ apps/auth/index.coffee
@@ -10,5 +10,4 @@
 app.set "view engine", "jade"

 # Log out
-app.get '/me/refresh', routes.refresh
 app.get '/me/sign_out', routes.logout, routes.redirectBack


Minor Changes and Bug Fixes to Equations.coffee

--- Coffeescript/Equations.coffee
+++ Coffeescript/Equations.coffee
@@ -12,40 +12,51 @@
   isOne: () -> false
   simplify: () -> this

-class Number extends Item
+class Num extends Item
   constructor: (@value) ->
-  evaluate: (vars) -> value
-  differential: (d) -> new Number(0)
-  isZero: () -> value == 0
-  isOne: () -> value == 1
+  evaluate: (vars) -> @value
+  differential: (d) -> new Num(0)
+  isZero: () -> @value == 0
+  isOne: () -> @value == 1
   simplify: () -> this

 class Variable extends Item
   constructor: (@name) ->
-  evaluate: (vars) -> vars[name] | 0
-  differential: (d) -> if d == name then new Number(1) else new Number(0)
+  evaluate: (vars) -> vars[@name] | 0
+  differential: (d) -> if d == @name then new Num(1) else new Num(0)
   isZero: () -> false
   isOne: () -> false
   simplify: () -> this

 class Sum extends Item
   constructor: (@items) ->
-  evaluate: (vars) -> items.reduce((r,i) -> r + i.evaluate(vars))
-  differential: (d) -> (new Sum(items.map((x) -> x.differential(d)))).simplify()
-  isZero: () -> items.reduce(true, (a,x) -> a and x.isZero())
+  evaluate: (vars) ->
+    @items.reduce (r,i) ->
+      r + i.evaluate(vars)
+    , 0
+  differential: (d) -> (new Sum(@items.map((x) -> x.differential(d)))).simplify()
+  isZero: () ->
+    @items.reduce (a,x) ->
+      a and x.isZero()
+    , true
   isOne: () -> false
   simplify: () ->
-    newItems = items.map((x) -> x.simplify()).filter((x) -> x != null and !x.isZero())
-    numbers = newItems.filter((x) -> x.constructor == Number)
+    newItems = @items.map((x) -> x.simplify()).filter((x) -> !x.isZero())
+    nums = newItems.filter((x) -> x.constructor == Num)
     otherSums = newItems.filter((x) -> x.constructor == Sum)
-    rest = newItems.filter((x) -> x.constructor != Number and x.constructor != Sum)
-    newItems = otherSums.reduce([], (r,x) -> r.concat(x))
-    number = numbers.reduce((a,b) -> a + b)
-    newItems.push(number)
+    rest = newItems.filter((x) -> x.constructor != Num and x.constructor != Sum)
+    newItems = []
+    if otherSums.length != 0
+      newItems = otherSums.reduce (r,x) ->
+        r.concat(x)
+      , []
+    if nums.length != 0
+      num = nums.reduce((a,b) -> a + b)
+      newItems.push(num)
     for item in rest
       newItems.push(item)
     if newItems.length == 0
-      new Number(0)
+      new Num(0)
     else if newItems.length == 1
       newItems[0]
     else
@@ -53,66 +64,102 @@

 class Multiplication extends Item
   constructor: (@firstInput, @secondInput) ->
-  evaluate: (vars) -> firstInput.evaluate(vars) * secondInput.evaluate(vars)
-  differential: (d) -> (new Sum([new Multiplication(firstInput.differential(d), secondInput), new Multiplication(firstInput, secondInput.differential(d))])).simplify()
-  isZero: () -> firstInput.isZero() or secondInput.isZero()
+  evaluate: (vars) -> @firstInput.evaluate(vars) * @secondInput.evaluate(vars)
+  differential: (d) -> (new Sum([new Multiplication(@firstInput.differential(d), @secondInput), new Multiplication(@firstInput, @secondInput.differential(d))])).simplify()
+  isZero: () -> @firstInput.isZero() or @secondInput.isZero()
   isOne: () -> false
   simplify: () ->
-    first = firstInput.simplify()
-    second = secondInput.simplify()
+    first = @firstInput.simplify()
+    second = @secondInput.simplify()
     if first.isZero() or second.isZero()
-      new Number(0)
+      new Num(0)
     else if first.isOne()
       second
     else if second.isOne()
       first
+    else if first.constructor == Num and second.constructor == Num
+      new Num(first.value * second.value)
     else
       new Multiplication(first,second)

 class PolynomialOp extends Item
   constructor: (@var, @pow) ->
-  evaluation: (vars) -> this.var.evaluate(vars) ** pow
-  isZero: () -> this.var.isZero()
-  isOne: () -> pow == 0
-  differential: (d) -> (new Multiplication(new Multiplication(this.var.differential(d), pow),new PolynomialOp(this.var, pow - 1))).simplify()
+  evaluate: (vars) -> @var.evaluate(vars) ** pow
+  isZero: () -> @var.isZero()
+  isOne: () -> @pow == 0
+  differential: (d) ->
+    if @pow == 2
+      (new Multiplication(new Multiplication(@var.differential(d), new Num(@pow)),@var)).simplify()
+    else
+      (new Multiplication(new Multiplication(@var.differential(d), new Num(@pow)),new PolynomialOp(@var, @pow - 1))).simplify()
   simplify: () ->
-    if pow == 0
-      new Number(1)
+    if @pow == 1
+      @var
     else
       this

-class VarUnderOne extends item
+class VarUnderOne extends Item
   constructor: (@var) ->
-  evaluation: (vars) -> 1 / this.var.evaluate(vars)
-  differential: (d) -> (new Multiplication(new Multiplication(new Item(-1), this.var.differential(d)),new PolynomialOp(this.var, new Number(-2)))).simplify()
-  isOne() -> this.var.isOne()
-  isZero() -> false
+  evaluate: (vars) -> 1 / @var.evaluate(vars)
+  differential: (d) ->
+    (new Multiplication(new Multiplication(new Num(-1), @var.differential(d)), new PolynomialOp(@var, new Num(-2)))).simplify()
+  isOne: () -> @var.isOne()
+  isZero: () -> false
   simplify: () ->
-    if this.var.constructor == Number
-      new Number(1/this.var.value)
+    if @var.constructor == Num
+      new Num(1/@var.value)
     else
       this

 class Log extends Item
   constructor: (@var) ->
-  evaluation: (vars) -> Math.log(this.var.evaluate(vars))
-  isOne: () -> this.var.constructor == Number and this.var.value == Math.E
-  isZero: () -> this.var.isOne()
-  differential: (d) -> (new Multiplication(new VarUnderOne(this.var), this.var.differential(d))).simplify()
-  simplify: () -> this
+  evaluate: (vars) -> Math.log(@var.evaluate(vars))
+  isOne: () -> @var.constructor == Num and @var.value == Math.E
+  isZero: () -> @var.isOne()
+  differential: (d) -> (new Multiplication(new VarUnderOne(@var), @var.differential(d))).simplify()
+  simplify: () ->
+    newVar = @var.simplify()
+    if newVar.constructor == Num
+      new Num(Math.log(newVar.value))
+    else
+      new Log(newVar)

-class ExponentialOp extends Item ->
+class ExponentialOp extends Item
   constructor: (@var, @exp) ->
-  evaluation: (vars) -> (exp ** this.var.evaluate(vars))
-  isZero: () -> exp.isZero()
-  isOne: () -> exp.isOne() or this.var.isZero()
+  evaluate: (vars) -> @exp.evaluate(vars) ** @var.evaluate(vars)
+  isZero: () -> @exp.isZero()
+  isOne: () -> @exp.isOne() or @var.isZero()
+  differential: (d) -> (new Multiplication(new Multiplication(new Log(@exp), @var.differential(d)),new ExponentialOp(@var,@exp))).simplify()
+  simplify: () ->
+    newVar = @var.simplify()
+    newExp = @exp.simplify()
+    if newVar.constructor == Num and newExp.constructor == Num
+      new Num(newExp.value ** newVar)
+    else
+      new ExponentialOp(newVar, newExp)

 class Function extends Item
   constructor: (@name, @vars, @item) ->
-  evaluate: (vars) -> item.evaluate(vars)
-  differential: (d) -> new Function(name + "'",vars,item.differential(d))
+  evaluate: (vars) -> @item.evaluate(vars)
+  differential: (d) -> new Function(@name + "'",@vars,@item.differential(d))
+  isZero: () -> @item.isZero()
+  isOne: () -> @item.isOne()
+  simplify: () -> new Function(@name,@vars,@item.simplify())

 class Graph
   constructor: (@name, @f) ->
-  dimensions: () -> f.vars.length + 1
-  axis: (i) -> if i == 0 then f.name else f.vars[i-1]
+  dimensions: () -> @f.vars.length + 1
+  axis: (i) -> if i == 0 then @f.name else @f.vars[i-1]
+
+
+x = new Variable("x")
+
+g = new Function("f", ["x"], new PolynomialOp(x,2))
+
+f = new Function("f", ["x"], new Multiplication(new ExponentialOp(x,new Num(Math.E)), x))
+
+y = new Multiplication(x,new Log(x))
+
+h = y.differential("x")
+
+fd = f.differential("d")


Reset view on load

Reset view on load

--- conda_ui/static/conda_ui/tab_view.coffee
+++ conda_ui/static/conda_ui/tab_view.coffee
@@ -22,10 +22,12 @@
             @listenTo(@pkgs, 'sync', () => @render())
             @listenTo(@envs, 'activate', () => @render())
             @listenTo(@pkgs, 'filter', () => @render())
-            @listenTo(@envs, 'request', () => @loading.fadeIn(400))
+            @listenTo(@envs, 'request', () => @showLoading())

         showLoading: ->
             @loading.slideDown()
+            if @ractive?
+                @ractive.reset({})

         hideLoading: ->
             @loading.slideUp()


Moves RSS to /magazine articles

Moves RSS to /magazine articles

--- apps/rss/routes.coffee
+++ apps/rss/routes.coffee
@@ -21,10 +21,9 @@
 @instantArticles = (req, res, next) ->
   new Articles().fetch
     data:
-      author_id: sd.ARTSY_EDITORIAL_ID
+      featured: true
       published: true
-      sort: '-updated_at'
-      instant_article: true
+      sort: '-published_at'
       limit: PAGE_SIZE_FACEBOOK
     error: res.backboneError
     success: (articles) ->


Tooltips

Tooltips


Display image upload error as alert

Display image upload error as alert

Not the nicest UX but better than nothing.

--- app/assets/javascripts/admin/products/services/product_image_service.js.coffee
+++ app/assets/javascripts/admin/products/services/product_image_service.js.coffee
@@ -13,3 +13,5 @@
       @imageUploader.onSuccessItem = (image, response) =>
         product.thumb_url = response.thumb_url
         product.image_url = response.image_url
+      @imageUploader.onErrorItem = (image, response) =>
+        alert(response.errors.join("\n"))


'Commit one'

'Commit one'

GIT_COMMITTER_DATE="Wed Feb 16 14:00 2011 +0100"

--- bin/home.coffee
+++ bin/home.coffee
@@ -1 +1,4 @@
-alert 'I am here'+alert 'I am here'
+
+
+alert 'asdad'


Don't use events on atom.grammars

Don't use events on atom.grammars

--- lib/package-grammars-view.coffee
+++ lib/package-grammars-view.coffee
@@ -1,6 +1,7 @@
 path = require 'path'
 _ = require 'underscore-plus'
-{$$$, View} = require 'atom'
+{CompositeDisposable} = require 'atom'
+{$$$, View} = require 'atom-space-pen-views'
 SettingsPanel = require './settings-panel'

 # View to display the grammars that a package has registered.
@@ -11,9 +12,15 @@
       @div outlet: 'grammarSettings'

   initialize: (packagePath) ->
+    @disposables = new CompositeDisposable()
     @packagePath = path.join(packagePath, path.sep)
     @addGrammars()
-    @subscribe atom.grammars, 'grammar-added grammar-updated', => @addGrammars()
+
+    @disposables.add atom.grammars.onDidAddGrammar => @addGrammars()
+    @disposables.add atom.grammars.onDidUpdateGrammar => @addGrammars()
+
+  beforeRemove: ->
+    @disposables.dispose()

   getPackageGrammars: ->
     packageGrammars = []


Update ranges tracker.

Update ranges tracker.


Added: rule to email subject/content options

--- controller.coffee
+++ controller.coffee
@@ -29,18 +29,19 @@

             url += '?key=' + rule.key

+            emailOptions = {doc, url, rule}
             async.parallel [
-                (cb) -> mailSubject {doc, url}, cb
-                (cb) -> mailTemplate {doc, url}, cb
+                (cb) -> mailSubject emailOptions, cb
+                (cb) -> mailTemplate emailOptions, cb
             ], (err, results) ->
                 [subject, htmlContent] = results
-                mailOptions =
+                emailInfo =
                     to: rule.email
                     subject: subject
                     content: url
                     html: htmlContent

-                CozyAdapter.sendMailFromUser mailOptions, cb
+                CozyAdapter.sendMailFromUser emailInfo, cb

     # change the whole clearance object
     out.change = (req, res, next) ->


change order of prefab choices

change order of prefab choices

--- generators/prefab/index.coffee
+++ generators/prefab/index.coffee
@@ -22,7 +22,7 @@
         type    : 'list'
         name    : 'prefabType'
         message : 'What type of prefab would you like to create?'
-        choices : ['Sprite', 'TileSprite', 'Group', 'Text', 'Button', 'Emitter', 'BitmapData']
+        choices : ['Text', 'Group', 'Sprite', 'Button', 'Emitter', 'TileSprite', 'BitmapData']
         default : 0
       }
       {


update comments

update comments

--- dist/backbone-mixin.coffee
+++ dist/backbone-mixin.coffee
@@ -61,7 +61,7 @@
       # @lang=en Helper helps listen obj for name change
       # and callback for current value
       #
-      # @lang=ru Хелпер позволяет повесить слушателя на obj
+      # @lang=ru Хелпер позволяет повесить слушателя на obj
       # на измение значения name и сразу же вызвать callback для текущего значения
       #
       listenToValue:(obj, _name, callback) ->
@@ -142,6 +142,13 @@
         view

       showCurrent:(callback)->
+      #
+      # @lang=en show current view
+      #   callback - function call, invokes when current view is shown
+      #
+      # @lang=ru Показать текущую view
+      #   callback - вызов функции, когда показ текущей view завершился успехом
+      #
         this.trigger "onBeforeShow"
         @onBeforeShow?()
         unless @_$_oneShow?
@@ -173,6 +180,13 @@
           finish()                                      #->finish 3

       closeCurrent:(callback)->
+      #
+      # @lang=en close current view
+      #   callback - function call, invokes when current view is closed
+      #
+      # @lang=ru Показать текущую view
+      #   callback - вызов функции, когда закрытие текущей view завершилося успехом
+      #
         this.trigger "onBeforeClose"
         @onBeforeClose?()
         finish = _.after 3, =>  callback?()             #--finish 3 times
@@ -196,7 +210,11 @@
           @trigger "onClose"
           @onClose?()
           finish()                                      #->finish 3
-
+      #
+      # @lang=en Close view
+      #
+      # @lang=ru Закрытие view
+      #
       close:(_view, callback)->
         unless _view?
           callback?()
@@ -272,7 +290,11 @@
               remove.apply view, arguments
               view._$_p.removeFlag = true
         @_$_p.diViews[key]
-
+      #
+      # @lang=en Reload html template in root dom-element
+      #
+      # @lang=ru Перезаливка html шаблона в корневой dom элемент
+      #
       reloadTemplate:->
         template = null
         data = null
@@ -290,7 +312,11 @@
           @$el.html @templateFunc(template, data)
         else if template?
           @$el.html template
-
+      #
+      # @lang=en unbingind regions for current view
+      #
+      # @lang=ru деинициализация регионов для текущей view
+      #
       unbindRegions:->
         return unless @regions
         for k,v of @regions
@@ -299,7 +325,11 @@
           delete this.r[k]
           if @regions.__oldmode__
             delete this[k]
-
+      #
+      # @lang=en binding regions for current view
+      #
+      # @lang=ru инициализация регионов для текущей view
+      #
       bindRegions:->
         @r ?= {}
         return unless @regions
@@ -331,7 +361,11 @@
               _.extend options, v.scope
             this.r[k] = new View options
           this[k] = this.r[k] if isOldMode and not this[k]?
-
+      #
+      # @lang=en binding ui elements for current view
+      #
+      # @lang=ru инициализация ui элементов для текущей view
+      #
       bindUIElements:->
         return unless @ui?
         @unbindUIElements()
@@ -339,12 +373,20 @@
         @ui = {}
         for k, v of @_$_p.varbindUIElements
           @ui[k] = @$el.find v
-
+      #
+      # @lang=en unbinding ui elements for current view
+      #
+      # @lang=ru деинициализация ui элементов для текущей view
+      #
       unbindUIElements:->
         return unless @_$_p.varbindUIElements?
         @ui = @_$_p.varbindUIElements
         @_$_p.varbindUIElements = null
-
+      #
+      # @lang=en binding ui elements for current view with epoxy support
+      #
+      # @lang=ru инициализация ui элементов для текущей view с поддержкой epoxy
+      #
       bindUIEpoxy:->
         return unless @bindings
         @unbindUIEpoxy()
@@ -359,7 +401,11 @@
             memo[k] = v
           memo
         ),{}
-
+      #
+      # @lang=en binding ui elements for current view with epoxy support
+      #
+      # @lang=ru инициализация ui элементов для текущей view c поддержкой epoxy
+      #
       unbindUIEpoxy:->
         return unless @_$_p.var_bindings?
         @bindings = @_$_p.var_bindings


perf: stop emitting updates on HTTP 304s

When an HTTP source returns 304, we emit an identical object, triggering
potentially expensive (for large objects) downstream operations - this
change uses `.distinctUntilChanged()` with a strict equality comparison
to reduce events

--- src/http.coffee
+++ src/http.coffee
@@ -72,6 +72,6 @@
       'If-Modified-Since': lastModified
     }).then(checkModified, returnLastKnown)

-  onInterval interval, load
+  onInterval(interval, load).distinctUntilChanged(null, (a, b) -> a == b)

 module.exports = httpResource


including GravatarImagenOnForm on FacturasNewView


Neo4jTransaction in-code docs

Neo4jTransaction in-code docs

New Methods
 - `current ` - Get current data in Neo4j Transaction
 - `last ` - Get last received data in Neo4j Transaction

--- transaction.coffee
+++ transaction.coffee
@@ -1,95 +1,83 @@
+###
+@locus Server
+@summary Implementation of Transactional Cypher HTTP endpoint
+         Event-driven and chainable
+         Have to be finished by calling `.commit()` method
+@class Neo4jTransaction
+@url http://neo4j.com/docs/2.2.5/rest-api-transactional.html
+###
 class Neo4jTransaction
   __proto__: events.EventEmitter.prototype
   constructor: (@_db, settings, opts = {}) ->
     events.EventEmitter.call @
-    @ready = false
+    @_ready = false
     @_results = []

     @on 'commit', (statement, callback) =>
-      if @ready then @__commit statement, callback else @once 'ready', => @__commit statement, callback
+      if @_ready then @__commit statement, callback else @once 'ready', => @__commit statement, callback
       return

     @on 'execute', (statement, fut) =>
-      if @ready then @__execute statement, fut else @once 'ready', => @__execute statement, fut
+      if @_ready then @__execute statement, fut else @once 'ready', => @__execute statement, fut
       return

     @on 'resetTimeout', (fut) =>
-      if @ready then @__resetTimeout fut else @once 'ready', => @__resetTimeout fut
+      if @_ready then @__resetTimeout fut else @once 'ready', => @__resetTimeout fut
       return

     @on 'rollback', (fut) =>
-      if @ready then @__rollback fut else @once 'ready', => @__rollback fut
+      if @_ready then @__rollback fut else @once 'ready', => @__rollback fut
       return

     @on 'ready', (cb) => cb null, true

-    statement = []
-    statement.push @__prepare(settings, opts).request if settings
+    statement = @__prepare(settings, opts).request if settings
+    statement = [] unless statement

     Meteor.wrapAsync((cb) =>
       @_db.__call @_db.__service.transaction.endpoint, data: statements: statement, 'POST', (error, response) =>
-        @__proceedResults error, response
+        @__proceedResults error, response,
         @_commitURL = response.data.commit
         @_execURL = response.data.commit.replace '/commit', ''
         @_expiresAt = response.data.transaction.expires
-        @ready = true
+        @_ready = true
         @emit 'ready', cb
     )()

   __prepare: (settings, opts = {}) ->
     {opts, cypher, resultDataContents, reactive} = @_db.__parseSettings settings, opts
+    console.log {opts, cypher, resultDataContents, reactive}
+
+    fill = (cs) ->
+      statement: cs
+      parameters: opts
+      resultDataContents: resultDataContents

-    return {
-      request:
-        statement: cypher
-        parameters: opts
-        resultDataContents: resultDataContents
-      reactive: reactive
-    }
-
-  execute: (settings, opts = {}) ->
-    fut = new Future()
-    @emit 'execute', @__prepare(settings, opts), fut
-    return fut.wait()
+    statements = request: [], reactive: reactive
+    if _.isArray cypher
+      statements.request.push fill cypherString for cypherString in cypher
+    else if _.isString cypher
+      statements.request.push fill cypher
+    return statements

   __execute: (statement, fut) ->
-    @_db.__call @_execURL, data: statements: [statement.request], 'POST', (error, response) =>
+    @_db.__call @_execURL, data: statements: statement.request, 'POST', (error, response) =>
       @__proceedResults error, response, statement.reactive
       fut.return @
     return

-  commit: (settings, opts = {}, callback) ->
-    statement = @__prepare(settings, opts) if settings
-
-    unless callback
-      return Meteor.wrapAsync((cb) =>
-        @emit 'commit', statement, cb
-      )()
-    else
-      @emit 'commit', statement, callback
-      return
-
   __commit: (statement, callback) ->
-    @_db.__call @_commitURL, data: statements: [statement.request], 'POST', (error, response) =>
-      @__proceedResults error, response, statement.reactive
+    data = data: statements: statement.request if statement
+    @_db.__call @_commitURL, data, 'POST', (error, response) =>
+      @__proceedResults error, response, statement.reactive if statement
       callback null, @_results
       return
-
-  resetTimeout: ->
-    fut = new Future()
-    @emit 'resetTimeout', fut
-    return fut.wait()

   __resetTimeout: (fut) ->
     @_db.__call @_execURL, data: statements: [], 'POST', (error, response) =>
       @_expiresAt = response.data.transaction.expires
       fut.return @
     return
-
-  rollback: ->
-    fut = new Future()
-    @emit 'rollback', fut
-    return fut.wait()

   __rollback: (fut) ->
     @_db.__call @_execURL, null, 'DELETE', =>
@@ -102,4 +90,95 @@
       @_db.__cleanUpResponse response, (result) =>
         @_results.push new Neo4jCursor @_db.__transformData result, reactive
     else
-      console.error error+      console.error error
+      console.trace()
+
+  ###
+  @locus Server
+  @summary Reset transaction timeout of an open Neo4j Transaction
+  @name rollback
+  @class Neo4jTransaction
+  @url http://neo4j.com/docs/2.2.5/rest-api-transactional.html#rest-api-rollback-an-open-transaction
+  @returns {undefined}
+  ###
+  rollback: ->
+    fut = new Future()
+    @emit 'rollback', fut
+    return fut.wait()
+
+  ###
+  @locus Server
+  @summary Reset transaction timeout of an open Neo4j Transaction
+  @name resetTimeout
+  @class Neo4jTransaction
+  @url http://neo4j.com/docs/2.2.5/rest-api-transactional.html#rest-api-reset-transaction-timeout-of-an-open-transaction
+  @returns Neo4jTransaction
+  ###
+  resetTimeout: ->
+    fut = new Future()
+    @emit 'resetTimeout', fut
+    return fut.wait()
+
+  ###
+  @locus Server
+  @summary Execute statement in open Neo4j Transaction
+  @name execute
+  @class Neo4jTransaction
+  @url http://neo4j.com/docs/2.2.5/rest-api-transactional.html#rest-api-execute-statements-in-an-open-transaction
+  @param {Object | String | [String]} settings - Cypher query as String or Array of Cypher queries or object of settings
+  @param {String | [String]} settings.cypher - Cypher query(ies), alias: `settings.query`
+  @param {Object} settings.opts - Map of cypher query(ies) parameters, aliases: `settings.parameters`, `settings.params`
+  @param {[String]} settings.resultDataContents - Array of contents to return from Neo4j, like: 'REST', 'row', 'graph'. Default: `['REST']`
+  @param {Boolean} settings.reactive - Reactive nodes updates on Neo4jCursor.fetch(). Default: `false`. Alias: `reactiveNodes`
+  @param {Object} opts - Map of cypher query(ies) parameters
+  @returns Neo4jTransaction
+  ###
+  execute: (settings, opts = {}) ->
+    fut = new Future()
+    @emit 'execute', @__prepare(settings, opts), fut
+    return fut.wait()
+
+  ###
+  @locus Server
+  @summary Commit Neo4j Transaction
+  @name commit
+  @class Neo4jTransaction
+  @url http://neo4j.com/docs/2.2.5/rest-api-transactional.html#rest-api-commit-an-open-transaction
+  @param {Object | String | [String]} settings - Cypher query as String or Array of Cypher queries or object of settings
+  @param {String | [String]} settings.cypher - Cypher query(ies), alias: `settings.query`
+  @param {Object} settings.opts - Map of cypher query(ies) parameters, aliases: `settings.parameters`, `settings.params`
+  @param {[String]} settings.resultDataContents - Array of contents to return from Neo4j, like: 'REST', 'row', 'graph'. Default: `['REST']`
+  @param {Boolean} settings.reactive - Reactive nodes updates on Neo4jCursor.fetch(). Default: `false`. Alias: `reactiveNodes`
+  @param {Function} settings.callback - Callback function. If passed, the method runs asynchronously, instead of synchronously, and calls asyncCallback. Alias: `settings.cb`
+  @param {Object} opts - Map of cypher query(ies) parameters
+  @param {Function} callback - Callback function. If passed, the method runs asynchronously, instead of synchronously, and calls asyncCallback.
+  @returns {[Object]} - Array of Neo4jCursor(s)
+  ###
+  commit: (settings, opts = {}, callback) ->
+    statement = @__prepare(settings, opts) if settings
+
+    unless callback
+      return Meteor.wrapAsync((cb) =>
+        @emit 'commit', statement, cb
+      )()
+    else
+      @emit 'commit', statement, callback
+      return
+
+  ###
+  @locus Server
+  @summary Get current data in Neo4j Transaction
+  @name current
+  @class Neo4jTransaction
+  @returns {[Object]} - Array of Neo4jCursor(s)
+  ###
+  current: () -> @_results
+
+  ###
+  @locus Server
+  @summary Get last received data in Neo4j Transaction
+  @name last
+  @class Neo4jTransaction
+  @returns {Object | null} - Neo4jCursor(s)
+  ###
+  last: () -> if @_results.length > 0 then @_results[@_results.length - 1] else null


pricing: plans view carries the state to children views

--- client/Pricing/views/plans.coffee
+++ client/Pricing/views/plans.coffee
@@ -1,4 +1,6 @@
 class PricingPlansView extends KDView
+
+  initialState: {}

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

@@ -7,13 +9,14 @@

     super options, data

-    @state = {}
+    @state = @utils.extend @initialState, options.state

     @planViews = {}

     for plan in @plans
       plan.delegate = this
       planTitle = plan.title.toLowerCase()
+      plan.state = @state
       @addSubView view = new SinglePlanView plan
       @forwardEvent view, "PlanSelected"
       @planViews[planTitle] = view


display planned import date + hiding datepicker on hourly importaton

--- client/app/views/konnector.coffee
+++ client/app/views/konnector.coffee
@@ -11,6 +11,7 @@
         slug = @model.get 'slug'
         lastImport = @model.get 'lastImport'
         isImporting  = @model.get 'isImporting'
+        lastAutoImport = @model.get 'lastAutoImport'

         @$el.addClass "konnector-#{slug}"

@@ -65,25 +66,39 @@

 Select a starting date
-Date
+From
 <input id="#{slug}-import-date" class="autoimport" maxlength="8" type="text"></input>

 """
         @$('.fields').append fieldHtml
-        if @$("##{slug}-autoimport-input").val() isnt 'none'
-            @$("##{slug}-first-import").show()
+
+        @$("##{slug}-first-import-date").hide()
+        @$("##{slug}-import-date").datepicker({minDate: 1, dateFormat: "dd-mm-yy" })
+
+        # if auto-importation is set to day, week, or month
+        if @$("##{slug}-autoimport-input").val() isnt 'none' \
+        and @$("##{slug}-autoimport-input").val() isnt 'hour'
+
+            # Only show the date if the first import didn't happened yet
+            if lastAutoImport? and moment(lastAutoImport).valueOf() > moment().valueOf()
+                @$("##{slug}-first-import-date").show()
+                @$("##{slug}-first-import-text").hide()
+                @$("##{slug}-import-date").val(moment(lastAutoImport).format 'DD-MM-YYYY')
+            else
+                @$("##{slug}-first-import").show()
         else
             @$("##{slug}-first-import").hide()
-        @$("##{slug}-first-import-date").hide()
-        @$("##{slug}-import-date").datepicker({minDate: 1, dateFormat: "dd-mm-yy" })
+
         @$("##{slug}-first-import-link").click (event) =>
             event.preventDefault()
             @$("##{slug}-first-import-date").show()
             @$("##{slug}-first-import-text").hide()
+
         @$("##{slug}-autoimport-input").change =>
-            if @$("##{slug}-autoimport-input").val() isnt 'none'
+            if @$("##{slug}-autoimport-input").val() isnt 'none' \
+            and @$("##{slug}-autoimport-input").val() isnt 'hour'
                 @$("##{slug}-first-import").show()
             else
                 @$("##{slug}-first-import").hide()
@@ -94,7 +109,6 @@

         importDate = $("##{slug}-import-date").val()
         fieldValues['date'] = importDate
-
         for name, val of @model.get 'fields'
             fieldValues[name] = $("##{slug}-#{name}-input").val()
         @model.set 'fieldValues', fieldValues


Add split methods

Add split methods

--- src/react-editor-view.coffee
+++ src/react-editor-view.coffee
@@ -82,6 +82,26 @@
     @attached = false
     @trigger 'editor:detached', this

+  # Public: Split the editor view left.
+  splitLeft: ->
+    pane = @getPane()
+    pane?.splitLeft(pane?.copyActiveItem()).activeView
+
+  # Public: Split the editor view right.
+  splitRight: ->
+    pane = @getPane()
+    pane?.splitRight(pane?.copyActiveItem()).activeView
+
+  # Public: Split the editor view up.
+  splitUp: ->
+    pane = @getPane()
+    pane?.splitUp(pane?.copyActiveItem()).activeView
+
+  # Public: Split the editor view down.
+  splitDown: ->
+    pane = @getPane()
+    pane?.splitDown(pane?.copyActiveItem()).activeView
+
   getPane: ->
     @closest('.pane').view()


Gallery component can have centerPadding style


code clean up, make variable names more clear


langFactory

langFactory

--- app/controllers/langFactory.coffee
+++ app/controllers/langFactory.coffee
@@ -6,3 +6,5 @@
       'ru' : translation_ru
     (lang) ->
       dictionary[lang]
+
+


further reorderings

further reorderings

--- library/membrane/document.coffee
+++ library/membrane/document.coffee
@@ -93,61 +93,6 @@
             reasoning: reasoning
             code: code

-    # Either get or set the version information of the method that
-    # is being described by this document. If you do no supply any
-    # arguments this method will return already described failures.
-    # The version should be a string with an arbitrary contents.
-    version: (version) ->
-        return @$version if arguments.length is 0
-        noVersion = "version should be a string"
-        assert _.isString(version), noVersion
-        @emit.call this, "version", arguments...
-        return @$version = vesrion.toString()
-
-    # Either get or set the remark to the method that is being
-    # described by this document. If you do not supply example
-    # this method will return you one, assuming it was set before.
-    # Notes are warning/beware messages about the implementation.
-    remark: (remark) ->
-        return @$remark if arguments.length is 0
-        noRemark = "the remark is not a string"
-        assert _.isString(remark), noRemark
-        @emit.call this, "remark", arguments...
-        return @$remark = remark.toString()
-
-    # Either get or set the inputs of the method that is being
-    # described by this document. If you do not supply inputs
-    # this method will return you one, assuming it was set before.
-    # Inputs is a description of the body that method expects.
-    inputs: (inputs) ->
-        return @$inputs if arguments.length is 0
-        noInputs = "the inputs is not a string"
-        assert _.isString(inputs), noInputs
-        @emit.call this, "inputs", arguments...
-        return @$inputs = inputs.toString()
-
-    # Either get or set the outputs of the method that is being
-    # described by this document. If you do not supply outputs
-    # this method will return you one, assuming it was set before.
-    # Outputs is a description of data returned by the method.
-    outputs: (outputs) ->
-        return @$outputs if arguments.length is 0
-        noOutputs = "the outputs is not a string"
-        assert _.isString(outputs), noOutputs
-        @emit.call this, "outputs", arguments...
-        return @$outputs = outputs.toString()
-
-    # Either get or set the description of the method that is being
-    # described by this document. If you do not supply description
-    # this method will return you one, assuming it was set before.
-    # Synopsis is a brief story of what this service method does.
-    synopsis: (synopsis) ->
-        return @$synopsis if arguments.length is 0
-        noSynopsis = "the synopsis is not a string"
-        assert _.isString(synopsis), noSynopsis
-        @emit.call this, "synopsis", arguments...
-        return @$synopsis = synopsis.toString()
-
     # Either get or set the relevant information of the method that
     # is being described by this document. If you do no supply any
     # arguments this method will return already described failures.
@@ -191,7 +136,7 @@
         return @$markings if arguments.length is 0
         noMarkings = "the markings should be a object"
         assert _.isObject(markings), noMarkings
-        _.extend (@$markings ?= {}), markings
+        assert _.extend (@$markings ?= {}), markings
         @emit.call @, "markings", arguments...
         assert @$markings; return @$markings

@@ -206,3 +151,58 @@
         assert _.extend (@$schemas ?= {}), schemas
         @emit.call this, "schemas", arguments...
         assert @$schemas; return @$schemas
+
+    # Either get or set the version information of the method that
+    # is being described by this document. If you do no supply any
+    # arguments this method will return already described failures.
+    # The version should be a string with an arbitrary contents.
+    version: (version) ->
+        return @$version if arguments.length is 0
+        noVersion = "version should be a string"
+        assert _.isString(version), noVersion
+        @emit.call this, "version", arguments...
+        return @$version = vesrion.toString()
+
+    # Either get or set the remark to the method that is being
+    # described by this document. If you do not supply example
+    # this method will return you one, assuming it was set before.
+    # Notes are warning/beware messages about the implementation.
+    remark: (remark) ->
+        return @$remark if arguments.length is 0
+        noRemark = "the remark is not a string"
+        assert _.isString(remark), noRemark
+        @emit.call this, "remark", arguments...
+        return @$remark = remark.toString()
+
+    # Either get or set the inputs of the method that is being
+    # described by this document. If you do not supply inputs
+    # this method will return you one, assuming it was set before.
+    # Inputs is a description of the body that method expects.
+    inputs: (inputs) ->
+        return @$inputs if arguments.length is 0
+        noInputs = "the inputs is not a string"
+        assert _.isString(inputs), noInputs
+        @emit.call this, "inputs", arguments...
+        return @$inputs = inputs.toString()
+
+    # Either get or set the outputs of the method that is being
+    # described by this document. If you do not supply outputs
+    # this method will return you one, assuming it was set before.
+    # Outputs is a description of data returned by the method.
+    outputs: (outputs) ->
+        return @$outputs if arguments.length is 0
+        noOutputs = "the outputs is not a string"
+        assert _.isString(outputs), noOutputs
+        @emit.call this, "outputs", arguments...
+        return @$outputs = outputs.toString()
+
+    # Either get or set the description of the method that is being
+    # described by this document. If you do not supply description
+    # this method will return you one, assuming it was set before.
+    # Synopsis is a brief story of what this service method does.
+    synopsis: (synopsis) ->
+        return @$synopsis if arguments.length is 0
+        noSynopsis = "the synopsis is not a string"
+        assert _.isString(synopsis), noSynopsis
+        @emit.call this, "synopsis", arguments...
+        return @$synopsis = synopsis.toString()


Upgrade the context to provide necessary values

--- library/visualize/context.coffee
+++ library/visualize/context.coffee
@@ -50,6 +50,31 @@
 # or may not be later implemented. Specifically, serialization logic.
 module.exports.Context = objectable -> class Context extends Object

+    # Defines the property that either fetches or sets the timestamp
+    # value for any context instance. Usually, the entity creating
+    # a new context instance will assign issue a creation timestamp
+    # expressed as some integral, non negative value. The typically
+    # used mechanism is using the UNIX timestamps, dervided from it.
+    Object.defineProperty @prototype, "stamps",
+        enumerable: no, value: (stamps) ->
+            correct = _.isNumber stamps
+            return @$stamps unless stamps?
+            invalid = "Stamps is not a number"
+            throw new Error invalid unless correct
+            @$stamps = stamps
+
+    # Defines the property that either fetches or sets the stamping
+    # value for any context instance. Usually, the entity creating
+    # a new context instance will mark it with a unique identifier.
+    # Typically this should be an issued v1 time based UUID tag.
+    Object.defineProperty @prototype, "imprint",
+        enumerable: no, value: (imprint) ->
+            correct = _.isString imprint
+            return @$imprint unless imprint?
+            invalid = "Imprint is not a string"
+            throw new Error invalid unless correct
+            @$imprint = imprint
+
     # Defines the property that either fetches or sets the foreign
     # flag for any context instance. If the foreign flag is in place
     # that means that the context originated from outside of the scope


Don't return values

Don't return values

--- app/js/loader/loader_common.coffee
+++ app/js/loader/loader_common.coffee
@@ -54,7 +54,8 @@
   proxy_method = (name) ->
     window.FACTLINK[name] = ->
       if iframe.contentWindow.Factlink?
-        return iframe.contentWindow.Factlink[name](arguments...)
+         # don't return the value, as we also don't do that when storing calls
+        iframe.contentWindow.Factlink[name](arguments...)
       else
         storedMethodCalls.push {name: name, arguments: arguments}
         return


removed quadratic test

removed quadratic test

--- test/specs/quadratic.spec.coffee
+++ test/specs/quadratic.spec.coffee
@@ -1,23 +0,0 @@
-{Smooth} = require '../../Smooth.coffee'
-
-{deriv} = require './util.coffee'
-
-
-describe 'Quadratic Interpolator', ->
-
-	arr = [1,3,-2,8,3]
-	s = Smooth arr, method:Smooth.METHOD_QUADRATIC, cubicTension: Smooth.CATMULL_ROM
-
-	it 'should match integer indexes', ->
-		expect(s 0).toEqual arr[0]
-		expect(s 1).toEqual arr[1]
-		expect(s 2).toEqual arr[2]
-		expect(s 3).toEqual arr[3]
-		expect(s 4).toEqual arr[4]
-
-	it 'should have little change in derivative near integers', ->
-		ds = deriv s
-		delta = 0.00001
-		expect(ds 1 + delta).toBeCloseTo ds 1 - delta
-		expect(ds 2 + delta).toBeCloseTo ds 2 - delta
-		expect(ds 3 + delta).toBeCloseTo ds 3 - delta


Fix sass binary name

Fix sass binary name

--- src/main.coffee
+++ src/main.coffee
@@ -67,7 +67,7 @@
   '.scss': new Language({ single: '//', multi: [ "/*", "*/" ] },
                         { cmd: 'scss', args: [ '-t', 'compressed' ] })
   '.sass': new Language({ single: '//', multi: [ "/*", "*/" ] },
-                        { cmd: 'scss', args: [ '-t', 'compressed' ] })
+                        { cmd: 'sass', args: [ '-t', 'compressed' ] })
   '.less': new Language({ single: '//', multi: [ "/*", "*/" ] },
                         { cmd: 'lessc', args: [ '-x' ] })
   '.styl': new Language({ single: '//', multi: [ "/*", "*/" ] },


fix main for tests

fix main for tests

--- src/main.coffee
+++ src/main.coffee
@@ -1,6 +1,7 @@
 requirejs.config
   paths:
     lib: '../lib'
+    spec: '../spec'
     cm: '../lib/codemirror-3.12'
   shim:
     'cm/codemirror':
@@ -21,5 +22,7 @@
 # exclude optional URI modules
 define("lib/#{m}", -> null) for m in ['IPv6', 'punycode', 'SecondLevelDomains']

-requirejs ['notebook'], (notebook) ->
-  $ notebook.init_editor
+# TODO how is this supposed to work?
+unless window.testing
+  requirejs ['notebook'], (notebook) ->
+    $ notebook.init_editor


fixed jshint warnings

fixed jshint warnings

--- src/scripts/directive.coffee
+++ src/scripts/directive.coffee
@@ -1,3 +1,17 @@
+###
+globals: angular, window
+
+  List of used element methods available in JQuery but not in JQuery Lite
+    in other words if you want to remove dependency on JQuery the following methods are to be implemented:
+
+    element.height()
+    element.outerHeight(true)
+    element.height(value) = only for Top/Bottom padding elements
+    element.scrollTop()
+    element.scrollTop(value)
+    element.offset()
+
+###
 angular.module('scroller', [])

   .directive( 'ngScrollViewport'
@@ -33,7 +47,7 @@

             match = $attr.ngScroll.match /^\s*(\w+)\s+in\s+(\w+)\s*$/
             if !match
-              throw Error "Expected ngScroll in form of '_item_ in _datasource_' but got '#{$attr.ngScroll}'"
+              throw new Error "Expected ngScroll in form of '_item_ in _datasource_' but got '#{$attr.ngScroll}'"

             itemName = match[1]
             datasourceName = match[2]
@@ -43,20 +57,6 @@

                 bufferSize = Math.max(3, +$attr.bufferSize || 10)
                 bufferPadding = -> viewport.height() * Math.max(.2, +$attr.padding || .5) # some extra space to initate preload in advance
-
-                ###
-
-                List of used element methods available in JQuery but not in JQuery Lite
-                in other words if you want to remove dependency on JQuery the following methods are to be implemented:
-
-                element.height()
-                element.outerHeight(true)
-                element.height(value) = only for Top/Bottom padding elements
-                element.scrollTop()
-                element.scrollTop(value)
-                element.offset()
-
-                ###

                 controller = null

@@ -70,10 +70,11 @@
                     switch template[0].localName
                       when 'li'
                         if canvas[0] == viewport[0]
-                          throw Error "element cannot be used as both viewport and canvas: #{canvas[0].outerHTML}"
+                          throw new Error "element cannot be used as both viewport and canvas: #{canvas[0].outerHTML}"
                         topPadding = angular.element('<li/>')
                         bottomPadding = angular.element('<li/>')
-                      when 'tr','dl' then throw Error "ng-scroll directive does not support <#{template[0].localName}> as a repeating tag: #{template[0].outerHTML}"
+                      when 'tr','dl'
+                        throw new Error "ng-scroll directive does not support <#{template[0].localName}> as a repeating tag: #{template[0].outerHTML}"
                       else
                         if canvas[0] == viewport[0]
                           # if canvas and the viewport are the same create a new div to service as canvas


remove scrolling flag (2)

remove scrolling flag (2)

--- src/scripts/ui-scroll.coffee
+++ src/scripts/ui-scroll.coffee
@@ -405,8 +405,8 @@
 							for item in buffer
 								item.scope.$destroy()
 								item.element.remove()
-							viewport.unbind 'resize', resizeHandler
-							viewport.unbind 'scroll', scrollHandler
+							viewport.unbind 'resize', resizeAndResizeHandler
+							viewport.unbind 'scroll', resizeAndResizeHandler
 							viewport.unbind 'mousewheel', wheelHandler

 						eventListener.$on "update.items", (event, locator, newItem)->


removeElement for angular 1.0

removeElement for angular 1.0

--- src/scripts/ui-scroll.coffee
+++ src/scripts/ui-scroll.coffee
@@ -26,8 +26,8 @@
 		])

 	.directive( 'uiScroll'
-		[ '$log', '$injector', '$rootScope', '$timeout',
-			(console, $injector, $rootScope, $timeout) ->
+		[ '$log', '$injector', '$rootScope', '$timeout', '$q',
+			(console, $injector, $rootScope, $timeout, $q) ->

 				$animate = $injector.get('$animate') if $injector.has && $injector.has('$animate')

@@ -410,11 +410,21 @@
 						adapter = {}
 						adapter.isLoading = false

-						removeElement = (index)->
-							promise = $animate.leave buffer[index].element,
-								-> buffer[index].scope.$destroy()
-							buffer.splice index, 1
-							promise
+						removeElement =
+							if $animate
+								(index)->
+									promise = $animate.leave buffer[index].element,
+										-> buffer[index].scope.$destroy()
+									buffer.splice index, 1
+									promise
+							else
+								(index)->
+									buffer[index].element.remove()
+									buffer[index].scope.$destroy()
+									buffer.splice index, 1
+									deferred = $q.defer()
+									deferred.resolve()
+									deferred.promise

 						applyUpdate = (wrapper, newItems) ->
 							inserted = []
@@ -435,13 +445,12 @@
 										inserted.push (insert ndx+i, newItem) for newItem,i in newItems
 										# now delete the old one
 										removeElement oldItemNdx
-										# re-index the buffer
-										item.scope.$index = first + i for item,i in buffer
 								else
 									# delete the item
 									promises.push removeElement wrapper.scope.$index-first
 									next--
-									item.scope.$index = first + i for item,i in buffer
+								# re-index the buffer
+								item.scope.$index = first + i for item,i in buffer
 							inserted

 						adapter.applyUpdates = (arg1, arg2) ->


Tests: `flushHeaders` isn't in node 0.10

--- test/util/stream_listener.coffee
+++ test/util/stream_listener.coffee
@@ -13,7 +13,10 @@
         setTimeout ->
             cb()
             res.writeHead 200, "Content-type": "text/plain"
-            res.flushHeaders()
+
+            # flushHeaders isn't in node 0.10. _send '' accomplishes the same thing
+            res.flushHeaders?()
+            res._send ''
         , delay

     server.listen port


Undisabled new adventurer levels.

Undisabled new adventurer levels.

--- app/views/play/WorldMapView.coffee
+++ app/views/play/WorldMapView.coffee
@@ -930,11 +930,9 @@
     description: 'Unlock the desert world, if you are strong enough to win this epic battle!'
     nextLevels:
       continue: 'the-dunes'
-    disabled: not me.isAdmin()
     x: 85.5
     y: 83.5
     adventurer: true
-    requiresSubscription: true
   }
   {
     name: 'Multiplayer Treasure Grove'
@@ -966,7 +964,6 @@
     description: 'Behold, the desert, full of glory, danger, and sand. Lots of sand.'
     nextLevels:
       continue: 'the-mighty-sand-yak'
-    disabled: not me.isAdmin()
     x: 8.47
     y: 21.93
     adventurer: true
@@ -980,7 +977,6 @@
     description: 'Test your nerves by dodging huge sand yaks on the open dunes!'
     nextLevels:
       continue: 'oasis'
-    disabled: not me.isAdmin()
     x: 16.56
     y: 27.77
     adventurer: true
@@ -994,7 +990,6 @@
     description: 'Run a gauntlet of sand yaks to reach oasis and quench your thirst!'
     #nextLevels:
     #  continue: ''
-    disabled: not me.isAdmin()
     x: 23.35
     y: 31.60
     adventurer: true


Moved Tactical Strike in from the edge of the map so it's a little more clickable on iPad.

--- app/views/play/WorldMapView.coffee
+++ app/views/play/WorldMapView.coffee
@@ -824,8 +824,8 @@
     id: 'tactical-strike'
     original: '5452cfa706a59e000067e4f5'
     description: 'They\'re, uh, coming right for us! Sneak up behind them.'
-    x: 95
-    y: 58
+    x: 88.65
+    y: 63.06
     nextLevels:
       continue: 'the-final-kithmaze'
     practice: true
@@ -837,8 +837,8 @@
     id: 'the-final-kithmaze'
     original: '541b434e1ccc8eaae19f3c33'
     description: 'To escape you must find your way through an Elder Kithman\'s maze.'
-    x: 89
-    y: 64
+    x: 81.93
+    y: 65.86
     nextLevels:
       more_practice: 'the-gauntlet'
       continue: 'kithgard-gates'
@@ -850,8 +850,8 @@
     id: 'the-gauntlet'
     original: '5452d8b906a59e000067e4fa'
     description: 'Rush for the stairs, battling foes at every turn.'
-    x: 82
-    y: 70
+    x: 84.89
+    y: 73.88
     nextLevels:
       continue: 'kithgard-gates'
     practice: true


Context: Add TODO about "dependency injection"

--- src/context/context.coffee
+++ src/context/context.coffee
@@ -9,6 +9,7 @@
   constructor: (@name) ->
     @_isInitialized = false
     @_isDestroyed = false
+    # TODO: Consider removing this "DI" since queries can be executed by simply accessing the context
     @_di =
       $query: => @query.apply @, arguments
     @_aggregateClasses = {}


Add hide password functionality to the command, query, and context error logging

--- src/context/context.coffee
+++ src/context/context.coffee
@@ -149,8 +149,10 @@

   command: (commandName, params) ->
     if @_isDestroyed
+      paramsWithHiddenPasswordValue = @_hidePasswordValue params
       return Promise.reject new Error """
-        Context #{@name} was destroyed, cannot execute command #{commandName} with arguments #{JSON.stringify(params)}
+        Context #{@name} was destroyed, cannot execute command #{commandName} with arguments
+        #{JSON.stringify(paramsWithHiddenPasswordValue)}
       """

     executingCommand = new Promise (resolve, reject) =>
@@ -168,8 +170,10 @@
         @_logger.debug 'Completed Command', commandName
         resolve result
       .catch (error) =>
+        paramsWithHiddenPasswordValue = @_hidePasswordValue params
         commandErrorMessage = """
-          Command "#{commandName}" with arguments #{JSON.stringify(params)} of context "#{@name}" rejects with an error
+          Command "#{commandName}" with arguments #{JSON.stringify(paramsWithHiddenPasswordValue)} of context "#{@name}"
+          rejects with an error
         """

         if not error
@@ -183,6 +187,12 @@
     @_addPendingPromise executingCommand

     return executingCommand
+
+
+  _hidePasswordValue: (params) ->
+    if params.password
+      params.password = '******'
+    return params

   _getCommandServicesToInject: ->
@@ -239,8 +249,10 @@
         @_logger.debug "Completed Query #{queryName} with Result #{result}"
         resolve result
       .catch (error) =>
+        paramsWithHiddenPasswordValue = @_hidePasswordValue params
         queryErrorMessage = """
-          Query "#{queryName}" with arguments #{JSON.stringify(params)} of context "#{@name}" rejects with an error
+          Query "#{queryName}" with arguments #{JSON.stringify(paramsWithHiddenPasswordValue)} of context "#{@name}"
+          rejects with an error
         """

         if not error


Fixed drawing coordinates

Fixed drawing coordinates

--- src/core/bindEvents.coffee
+++ src/core/bindEvents.coffee
@@ -11,8 +11,8 @@
   else
     p = e.target.getBoundingClientRect()
     {
-      left: e.pageX - p.left,
-      top: e.pageY - p.top,
+      left: e.clientX - p.left,
+      top: e.clientY - p.top,
     }


#322 Keep comment in the middle of the list, but never show previous tree

--- app/assets/javascripts/oxalis/view/skeletontracing/skeletontracing_view.coffee
+++ app/assets/javascripts/oxalis/view/skeletontracing/skeletontracing_view.coffee
@@ -159,18 +159,39 @@
       oldIcon.toggleClass("fa-angle-right", false)

     activeHref = $("#comment-container a[data-nodeid=#{@model.skeletonTracing.getActiveNodeId()}]")
+    commentIcon = null
+
     if activeHref.length
-
-      newIcon = activeHref.parent("li").children("i")
-      newIcon.toggleClass("fa-angle-right", true)
-
-    # animate scrolling to the active tree
-    activeTree = $("#comment-container span[data-treeid=#{@model.skeletonTracing.getActiveTreeId()}]")
+      commentIcon = activeHref.parent("li").children("i")
+      commentIcon.toggleClass("fa-angle-right", true)
+
+    @scrollCommentsList(commentIcon)
+
+
+  scrollCommentsList : (commentIcon) ->
+
+    activeTreeId = @model.skeletonTracing.getActiveTreeId()
+    activeTree = $("#comment-container span[data-treeid=#{activeTreeId}]")
+    activeTreeTop = activeTree.offset().top
+    height = $('#comment-container').height()
+
     if activeTree.length
-      $("#comment-container").animate(
-        scrollTop: activeTree.offset().top - $("#comment-container").offset().top + $("#comment-container").scrollTop()
-        250
-      )
+      if commentIcon?
+        commentIconTop = commentIcon.offset().top
+        if commentIconTop - activeTreeTop > height / 2
+          @scrollCommentsListTo(commentIconTop - height / 2)
+          return
+      @scrollCommentsListTo(activeTreeTop)
+
+
+  scrollCommentsListTo : (top) ->
+
+    container = $("#comment-container")
+    offset = container.scrollTop() - container.offset().top
+    container.animate(
+      scrollTop: top + offset
+      250
+    )

   updateActiveTree : ->


make the "load all older projects" button impossible to miss

--- src/smc-webapp/projects.cjsx
+++ src/smc-webapp/projects.cjsx
@@ -1596,7 +1596,8 @@
         <Button
             onClick={@load}
             bsStyle='info'
-            bsSize='large'>
+            bsSize='large'
+            style={width:'100%', fontSize:'18pt'}>
             {@render_loading()}
             Load all older projects...
         </Button>
@@ -1604,7 +1605,7 @@
     render: ->
         if @props.done
             return
-

+

             {@render_button()}


removeEventListener

removeEventListener

--- assets/javascripts/audio.js.coffee
+++ assets/javascripts/audio.js.coffee
@@ -5,20 +5,22 @@
     @fadeoutTime = fadeoutTime
     @fadeinTime = fadeinTime
     @current = window.audio1
+    @current.addEventListener("MozAudioAvailable", @setFadeout, false)
     @next = window.audio2

   switchPlayer: ->
+    # http://stackoverflow.com/questions/4402287/javascript-remove-event-listener
+    @current.removeEventListener("MozAudioAvailable", @setFadeout)
     if @current == window.audio1
       @current = window.audio2
       @next = window.audio1
     else
       @current = window.audio1
       @next = window.audio2
+    @current.addEventListener("MozAudioAvailable", @setFadeout, false)

   play: ->
-    @current.volume = 1
     @current.play()
-    @current.addEventListener("MozAudioAvailable", @setFadeout, false)

   pause: ->
     @current.pause()
@@ -26,12 +28,11 @@
   setFadeout: =>
     #console.log("#{@fadeingOut} #{@current.duration - @current.currentTime} <= #{@fadeoutTime}")
     if @current.duration - @current.currentTime <= @fadeoutTime
-      @playNext()
+      @playNext({auto: true})

-  playNext: ->
-    if @fadeingOut
-      console.log("Fadeing out already, bro")
-      return
+  playNext: (obj = {}) ->
+    console.log("Fadeing out already, bro") unless obj.auto
+    return if @fadeingOut
     #@fadeoutStarted = new Date.getTime() / @second
     @fadeingOut = true
     @doFadeout()


user page:  loaders fixed

user page:  loaders fixed


Add helper to map error codes to friendly messages

--- src/pane.coffee
+++ src/pane.coffee
@@ -721,30 +721,27 @@
       message = "#{message} '#{itemPath}'" if itemPath
       @notificationManager.addWarning(message, options)

-    if error.code is 'EISDIR' or error.message?.endsWith?('is a directory')
+    customMessage = @getMessageForErrorCode(error.code)
+    if customMessage?
+      addWarningWithPath("Unable to save file: #{customMessage}")
+    else if error.code is 'EISDIR' or error.message?.endsWith?('is a directory')
       @notificationManager.addWarning("Unable to save file: #{error.message}")
-    else if error.code is 'EACCES'
-      addWarningWithPath('Unable to save file: Permission denied')
     else if error.code in ['EPERM', 'EBUSY', 'UNKNOWN', 'EEXIST', 'ELOOP', 'EAGAIN']
       addWarningWithPath('Unable to save file', detail: error.message)
-    else if error.code is 'EROFS'
-      addWarningWithPath('Unable to save file: Read-only file system')
-    else if error.code is 'ENOSPC'
-      addWarningWithPath('Unable to save file: No space left on device')
-    else if error.code is 'ENXIO'
-      addWarningWithPath('Unable to save file: No such device or address')
-    else if error.code is 'ENOTSUP'
-      addWarningWithPath('Unable to save file: Operation not supported on socket')
-    else if error.code is 'EIO'
-      addWarningWithPath('Unable to save file: I/O error writing file')
-    else if error.code is 'EINTR'
-      addWarningWithPath('Unable to save file: Interrupted system call')
-    else if error.code is 'ECONNRESET'
-      addWarningWithPath('Unable to save file: Connection reset')
-    else if error.code is 'ESPIPE'
-      addWarningWithPath('Unable to save file: Invalid seek')
     else if errorMatch = /ENOTDIR, not a directory '([^']+)'/.exec(error.message)
       fileName = errorMatch[1]
       @notificationManager.addWarning("Unable to save file: A directory in the path '#{fileName}' could not be written to")
     else
       throw error
+
+  getMessageForErrorCode: (errorCode) ->
+    switch errorCode
+      when 'EACCES' then 'Permission denied'
+      when 'ECONNRESET' then 'Connection reset'
+      when 'EINTR' then 'Interrupted system call'
+      when 'EIO' then 'I/O error writing file'
+      when 'ENOSPC' then 'No space left on device'
+      when 'ENOTSUP' then 'Operation not supported on socket'
+      when 'ENXIO' then 'No such device or address'
+      when 'EROFS' then 'Read-only file system'
+      when 'ESPIPE' then 'Invalid seek'


Added support for mobile browsers.

Added support for mobile browsers.

--- coffee/lib/abstract-chosen.coffee
+++ coffee/lib/abstract-chosen.coffee
@@ -260,10 +260,6 @@
   @browser_is_supported: ->
     if window.navigator.appName == "Microsoft Internet Explorer"
       return document.documentMode >= 8
-    if /iP(od|hone)/i.test(window.navigator.userAgent)
-      return false
-    if /Android/i.test(window.navigator.userAgent)
-      return false if /Mobile/i.test(window.navigator.userAgent)
     return true

   @default_multiple_text: "Select Some Options"


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/active_bounds.coffee
+++ test/active_bounds.coffee
@@ -7,7 +7,9 @@

 test "should not be destroyed when inside activeBounds", ->
   obj = GameObject
-    x
+    x: 50
+    y: 50
+

 test "should remove objects outside of activeBounds", 2, ->
   obj = GameObject


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/active_bounds.coffee
+++ test/active_bounds.coffee
@@ -17,6 +17,6 @@
   obj.I.x = 10000
   obj.I.y = 4000

-
+  obj.

 module()


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/active_bounds.coffee
+++ test/active_bounds.coffee
@@ -1,5 +1,5 @@
 module "ActiveBounds"

-test "should remove objects"
+test "should remove objects outside of active"

 module()


fix, update

fix, update

update - Support for portable Browsers
fixed - added regex string to remove all backward slashes in localhost url

--- lib/open-in-browsers-view.coffee
+++ lib/open-in-browsers-view.coffee
@@ -30,6 +30,9 @@
             browserClass = "fa #{browser}"
           if @curBrowser is browser
             browserClass  =+ " selected "
+
+          unless atom.config.get("open-in-browsers.#{browser}Path") != ''
+            browserClass += " hide "

           @span class:browserClass,'data-browser':"#{browser}", mousedown:'openBrowser'
       @sup class:localhost, "L"
@@ -84,7 +87,7 @@
       return false
     fpath = @getFilePath(target)
     cmd = "#{cmd} \"#{fpath}\""
-    cmd = cmd.replace '\\', '/'
+    cmd = cmd.replace "/\\/g", '/'
     exec  cmd if fpath
     @selectList?.cancel()


Adding a comment

Adding a comment

--- cs/options.coffee
+++ cs/options.coffee
@@ -226,6 +226,7 @@
     if typeof json is 'undefined' or typeof json is 'string'
       errorMessage = (typeof json is 'undefined' ? 'Oops, noe gikk galt' : json)
       $(cssSelector + ' .lines').html ''+errorMessage+''
+      # Show retry-button
       setTimeout ( ->
         $(cssSelector + ' .lines').html 'Prøve igjen?'
         $(cssSelector + ' .lines .retry').click ->


deleted file

deleted file


Fix a typo in legislation import for session.

--- jobs/import-legislation.coffee
+++ jobs/import-legislation.coffee
@@ -134,7 +134,7 @@
     job.data.session._id = new ObjectId(job.data.session._id)

     db.collection("legislation").find(sessionId: job.data.session._id).toArray (err, results) -> ifSuccessful err, callback, ->
-      results.forEach(legislation) ->
+      results.forEach (legislation) ->
         jobs.create('import legislation detail', legislation: legislation)

       callback()


shorter oncall message syntax to avoid collisions

--- scripts/pagerv2_commands.coffee
+++ scripts/pagerv2_commands.coffee
@@ -38,8 +38,7 @@
 #
 #   hubot pd [who is] oncall        - tells who is currently on call
 #   hubot pd [who is] next [oncall] - tells who is next on call
-#
-#   hubot pd [tell to |cc ] oncall <message> - cc oncall and send <message> to alerting channel
+#   hubot pd oncall <message>       - cc oncall and send <message> to alerting channel
 #
 #   hubot pd maintenances           - lists currently active maintenances
 #   hubot pd stfu|down [for] <duration> [because <reason>] - creates a maintenance
@@ -106,8 +105,8 @@
       res.send e
     res.finish()

-#   hubot pd [tell to |cc ] oncall <message> - cc oncall and send <message> to alerting channel
-  robot.respond /(?:pd )?(?:tell to |cc )?on ?call\s(.+)/, 'pd_msg_oncall', (res) ->
+#   hubot pd oncall <message> - cc oncall and send <message> to alerting channel
+  robot.respond /(?:pd )?on ?call\s(.+)/, 'pd_msg_oncall', (res) ->
     [ _, msg ] = res.match
     alertchan = process.env.PAGERV2_ANNOUNCE_ROOM
     pagerv2.getOncall()


Use a promise constructor so we can make the code a bit cleaner.

--- src/supervisor-update.coffee
+++ src/supervisor-update.coffee
@@ -21,18 +21,14 @@
 		console.log('Fetching updated supervisor:', updateImage)
 		docker.createImageAsync(fromImage: updateImage)
 	.then (stream) ->
-		deferred = Promise.defer()
+		return new Promise (resolve, reject) ->
+			if stream.headers['content-type'] is 'application/json'
+				stream.pipe(JSONStream.parse('error'))
+				.pipe(es.mapSync(reject))
+			else
+				stream.pipe(es.wait((error, text) -> reject(text)))

-		if stream.headers['content-type'] is 'application/json'
-			stream.pipe(JSONStream.parse('error'))
-			.pipe es.mapSync (error) ->
-				deferred.reject(error)
-		else
-			stream.pipe es.wait (error, text) -> deferred.reject(text)
-
-		stream.on 'end', -> deferred.resolve()
-
-		return deferred.promise
+			stream.on('end', resolve)
 	.then ->
 		console.log('Tagging updated supervisor:', updateImage)
 		docker.getImage(updateImage).tagAsync(


Switch UI tests to use simple dot reporter

--- ui/gulpfile.coffee
+++ ui/gulpfile.coffee
@@ -168,5 +168,5 @@
     'scripts:test:build'
 ], ->
     gulp.src(dist + 'test.html')
-        .pipe(plugins.mochaPhantomjs({reporter: 'spec'}))
+        .pipe(plugins.mochaPhantomjs({reporter: 'dot'}))
 )


minor addition to the widget html toolkit

--- library/semantic/abstract.coffee
+++ library/semantic/abstract.coffee
@@ -149,6 +149,7 @@
         assert @add = (fx) => @element.append deducts fx
         assert @classes = => return @element.attr "class"
         assert @siblings = => return $(".#{@reference}")
+        assert @remove = => return this.element.remove()
         assert @show = => this.element.show arguments...
         assert @hide = => this.element.hide arguments...
         assert @find = => this.element.find arguments...


Check for hidden private fields for non vendor doc

--- test/document-spec.coffee
+++ test/document-spec.coffee
@@ -51,6 +51,7 @@

   it 'should return document when status=Offentlig and tilbyder=TURAPP', (done) ->
     doc = data.getTrip('TURAPP', 'Offentlig')
+    delete doc.privat
     req.get(url(doc._id)).expect(200).end (err, res) ->
       assert.ifError(err)
       assert.deepEqual res.body, doc


Not great but workable method for checking whether there is any image at all

--- models/mixins/image_sizes.coffee
+++ models/mixins/image_sizes.coffee
@@ -15,6 +15,9 @@

   isCropped: (version) ->
     _.contains @croppedSizes, version
+
+  isWithImages: ->
+    @defaultImageVersion?()?

   publicVersions: ->
     _.without(@get('image_versions'), 'normalized')


Rotate track titles only if the ID's changed

--- assets/front.coffee
+++ assets/front.coffee
@@ -240,9 +240,10 @@
     console.log("InGetJSON")
     console.log(segments)
     for segment in segments
-      document.getElementById('artist').innerHTML = segment.tracks[0].metadata.id
-      # document.getElementById('title').innerHTML = segment.tracks[0].metadata.artist
+      document.getElementById('artist').innerHTML = segment.tracks[0].metadata.artist
+      # document.getElementById('title').innerHTML = segment.tracks[0].metadata.title
       window._next_artist = window._next_title = ""
+      window._track_id = segment.tracks[0].metadata.id
       console.log("Segment")
       console.log(segment)

@@ -261,15 +262,15 @@
     s.on 'message', (data) ->
       if typeof data is "string"
         data = JSON.parse(data)
-        # TODO Be safe against embedded HTML tags
-        nexttrack = data.segment.tracks[0].metadata.artist
-        # document.getElementById('artist').innerHTML = window._next_artist
-        document.getElementById('artist_next').innerHTML = "Up next: " + nexttrack
-        window._next_artist = nexttrack
+        if data.segment.tracks[0].metadata.id != window._track_id
+          window._track_id = data.segment.tracks[0].metadata.id
+          # TODO Be safe against embedded HTML tags
+          document.getElementById('artist').innerHTML = window._next_artist
+          window._next_artist = data.segment.tracks[0].metadata.artist
+          document.getElementById('artist_next').innerHTML = "Up next: " + window._next_artist
         console.log("GetTrackDets Data:")
         console.log(data.segment.tracks[0].metadata.id)
       if data.listener_count?
         window._listeners = data.listener_count
     window._socket = s
   setTimeout getTrackDetails, 1000
-


tests(macTime): Cleaned up e2e unit tests

--- test/e2e/time.spec.coffee
+++ test/e2e/time.spec.coffee
@@ -1,43 +1,43 @@
 describe "Mac Time Input", ->
+  input  = null
+  output = null
+
   beforeEach ->
     browser.get "/test/e2e/time.html"
     browser.waitForAngular()

+    input  = element(By.css(".mac-date-time"))
+    output = element(By.css(".output"))
+
+  afterEach ->
+    input  = null
+    output = null
+
   it "should return true", ->
-    input = element(By.css(".mac-date-time"))
     input.click()

-    output = element(By.css(".output"))
     expect(output.getText()).toEqual "10:55 PM"

   it "should change the meridian to AM after pressing the A button", ->
-    input = element(By.css(".mac-date-time"))
     input.click()
     input.sendKeys "A"

-    output = element(By.css(".output"))
     expect(output.getText()).toEqual "10:55 AM"

   it "should not change the meridian to AM after pressing the P button", ->
-    input = element(By.css(".mac-date-time"))
     input.click()
     input.sendKeys "P"

-    output = element(By.css(".output"))
     expect(output.getText()).toEqual "10:55 PM"

   it "should change the model after pressing down button", ->
-    input = element(By.css(".mac-date-time"))
     input.click()
     input.sendKeys protractor.Key.DOWN

-    output = element(By.css(".output"))
     expect(output.getText()).toEqual "10:55 AM"

   it "should change the model after pressing up button", ->
-    input = element(By.css(".mac-date-time"))
     input.click()
     input.sendKeys protractor.Key.UP

-    output = element(By.css(".output"))
     expect(output.getText()).toEqual "10:55 AM"


Implement admin and read-write roles

Implement admin and read-write roles

--- server/allow_and_startup.coffee
+++ server/allow_and_startup.coffee
@@ -23,24 +23,28 @@
       Meteor.roles.insert({name: role_name})

 # ----------------------------------------
+ReaderRoles = ['admin','read-write','read-only']
+WriterRoles = ['admin','read-write']
+
+# ----------------------------------------
 Meteor.publish "settings_to_client", ->
   SettingsToClient.find {}
 Meteor.publish "neuron_catalog_config", ->
   NeuronCatalogConfig.find {}

 Meteor.publish "driver_lines", ->
-  DriverLines.find {}  if @userId
+  DriverLines.find {}  if Roles.userIsInRole(@userId, ReaderRoles)
 Meteor.publish "neuron_types", ->
-  NeuronTypes.find {}  if @userId
+  NeuronTypes.find {}  if Roles.userIsInRole(@userId, ReaderRoles)
 Meteor.publish "brain_regions", ->
-  BrainRegions.find {}  if @userId
+  BrainRegions.find {}  if Roles.userIsInRole(@userId, ReaderRoles)
 Meteor.publish "binary_data", ->
-  BinaryData.find {}  if @userId
+  BinaryData.find {}  if Roles.userIsInRole(@userId, ReaderRoles)

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

 Meteor.publish "userData", ->
-  if @userId
+  if Roles.userIsInRole(@userId, ReaderRoles)
     Meteor.users.find {},
       fields:
         username: 1
@@ -49,17 +53,26 @@

 logged_in_allow =
   insert: (userId, doc) ->
-    !!userId
+    Roles.userIsInRole(userId, WriterRoles)

   update: (userId, doc, fields, modifier) ->
-    !!userId
+    Roles.userIsInRole(userId, WriterRoles)

   remove: (userId, doc) ->
-    !!userId
+    Roles.userIsInRole(userId, WriterRoles)

 DriverLines.allow logged_in_allow
 BinaryData.allow logged_in_allow
 NeuronTypes.allow logged_in_allow
 BrainRegions.allow logged_in_allow

-NeuronCatalogConfig.allow logged_in_allow
+NeuronCatalogConfig.allow(
+  insert: (userId, doc) ->
+    Roles.userIsInRole(userId, ['admin'])
+
+  update: (userId, doc, fields, modifier) ->
+    Roles.userIsInRole(userId, ['admin'])
+
+  remove: (userId, doc) ->
+    Roles.userIsInRole(userId, ['admin'])
+)


First pass on analogy panels

First pass on analogy panels

--- src/javascript/main.coffee
+++ src/javascript/main.coffee
@@ -1,4 +1,3 @@
-

 # Ideas
 # * Damage represented by ship icon (relative EHP)
@@ -10,17 +9,19 @@
 # * H/L/N/W colors
 # * More/better raw stat formatters (minutes to H:M, etc)
 # * tick marks on HP Chart
+# * Fix 1 column layout, test on phone/tablet

 React = require 'react'
 $ = require 'jquery'
+d3 = require 'd3'
+_ = require 'lodash'
+
 CharacterStats = require './character_stats'
 CharacterFacts = require './character_facts'
-d3 = require 'd3'
-_ = require 'lodash'

 dom = React.DOM

-source_json = './lowsec.json'
+source_json = './null_pvp.json'

 samples = [
   'dscan.json'
@@ -95,7 +96,16 @@
     label: 'Wormholes'
     color: '#0000df'

+# alias styles
+STYLES.HighSec = STYLES.high
+STYLES.LowSec = STYLES.low
+STYLES.NullSec = STYLES.null
+STYLES.Wormhole = STYLES.wormhole
+
 HP_BAR_ORDER = ['shield','armor','hull']
+
+eveIconUrl = (id, width = 32) ->
+  return "https://image.eveonline.com/Type/#{id}_#{width}.png"

 StatsUI = React.createClass(
   displayName: 'CharacterStatsUI'
@@ -111,19 +121,24 @@
   componentDidMount: ->
     $.get @state.source, (data) =>
       stats = new CharacterStats(data)
-      facts = new CharacterFacts(stats)
       @setState {stats: stats, year: data.aggregateYear}
   render: ->
+    if @state.stats
+      facts = new CharacterFacts(@state.stats).getFacts()

     title = React.createElement(Title, {character: @state.character, year: @state.year})

-    charInfoPanel = React.createElement(CharacterInfoPanel, {character: @state.character})
+    charInfoPanel = React.createElement(CharacterInfoPanel, {character: @state.character, facts: facts})

     travelJumpsPanel = React.createElement(TravelJumpsPanel, {stats: @state.stats})
     travelDistancePanel = React.createElement(TravelDistancePanel, {stats: @state.stats})

+    distanceAnalogy = React.createElement(DistanceAnalogyPanel, {distance: @state.stats?.total 'travelDistanceWarped'})
+
     weaponUsagePanel = React.createElement(WeaponUsagePanel, {stats: @state.stats})
     damageTakenPanel = React.createElement(DamageTakenPanel, {stats: @state.stats})
+
+    damageAnalogy = React.createElement(DamageAnalogyPanel, {damage: @state.stats?.totalDamageDealt})

     selfRepPanel = React.createElement(SelfRepPanel, {stats: @state.stats})
     repsReceivedPanel = React.createElement(RepsReceivedPanel, {stats: @state.stats})
@@ -137,15 +152,21 @@

       charInfoPanel

-      dom.div {className: 'container'},
+      dom.div {className: 'row'},
         dom.div {className: 'col-md-6'}, travelJumpsPanel
         dom.div {className: 'col-md-6'}, travelDistancePanel

-      dom.div {className: 'container'},
+      dom.div {className: 'row'},
+        distanceAnalogy
+
+      dom.div {className: 'row'},
         dom.div {className: 'col-md-6'}, weaponUsagePanel
         dom.div {className: 'col-md-6'}, damageTakenPanel

-      dom.div {className: 'container'},
+      dom.div {className: 'row'},
+        damageAnalogy
+
+      dom.div {className: 'row'},
         dom.div {className: 'col-md-6'}, repsGivenPanel
         dom.div {className: 'col-md-6'}, repsReceivedPanel

@@ -166,9 +187,10 @@
   render: ->
     dom.div {className: 'container'},
       dom.div {className: 'col-md-4'},
-        React.createElement(CharacterAvatar,{id: @props.character.id})
-      dom.div {className: 'col-md-8'}
-        dom.span 'bar'
+        React.createElement(CharacterAvatar, {id: @props.character.id})
+      dom.div {className: 'col-md-8'},
+        dom.ul null,
+          dom.li null, 'Prefers ' + STYLES[@props.facts?.preferredSec]?.label
 )

 CharacterAvatar = React.createClass(
@@ -239,6 +261,49 @@
       React.createElement(PieDataPanel,{data: @chartData(), chartType: 'pie'})
 )

+DistanceAnalogyPanel = React.createClass(
+  displayName: 'DistanceAnalogyPanel'
+  # in meters per second
+  vehicles:
+    light:
+      name: 'The speed of light'
+      speed: 299792458
+    voyagerOne:
+      name: 'Voyager 1'
+      speed: 17260.2
+    mph60:
+      name: 'A car travelling at 60 MPH'
+      speed: 26.8224
+    concord:
+      name: 'The Concorde'
+      speed: 605.292
+    walker:
+      name: 'Walking on foot'
+      speed: 1.4
+    cyclist:
+      name: 'Riding a bicycle'
+      speed: 6.5
+    ferrari:
+      name: 'A Ferrari F50 GT1'
+      speed: 105.5
+    iss:
+      name: 'The International Space Station orbiting the Earth'
+      speed: 7700
+
+  getInitialState: ->
+    return {
+      vehicle: _.sample @vehicles
+    }
+  render: ->
+    speedKmPerS = @state.vehicle.speed / 1000
+    speedOfLightAu = speedKmPerS / 149597871
+    totalAu = @props.distance
+    travelSeconds = totalAu / speedOfLightAu
+    lightSpeedYears = Intl.NumberFormat().format(Math.round(travelSeconds / 60 / 60 / 24/ 365))
+    distanceText = "#{@state.vehicle.name} would take #{lightSpeedYears} years to cover your total distance travelled."
+    dom.h4 {className: 'pull-right'}, dom.em(null,distanceText)
+)
+
 WeaponUsagePanel = React.createClass(
   displayName: 'WeaponUsagePanel'
   chartData: ->
@@ -281,6 +346,34 @@
     dom.div {className: 'container'},
       dom.h3 null, 'Damage Dealt'
       React.createElement(PieDataPanel,{data: @chartData()})
+)
+
+DamageAnalogyPanel = React.createClass(
+  displayName: 'DamageAnalogyPanel'
+  ships:
+    proteus:
+      pluralName: 'Proteii'
+      id: 29988
+      ehp: 150000
+      fit: 'https://o.smium.org/loadout/private/23324/6303219387142766592'
+    avatar:
+      pluralName: 'Avatars'
+      id: 11567
+      ehp: 22600000
+      fit: 'https://o.smium.org/loadout/private/23322/7459269642280763392'
+  render: ->
+    ship = _.sample @ships
+    numShips = d3.round(@props.damage / ship.ehp, 1)
+    stamps = []
+    for i in [0...Math.floor(numShips)]
+      stamps.push dom.img {src: eveIconUrl(ship.id), className: 'pull-right'}
+
+    dom.div null,
+      dom.h4 {className: 'pull-right'},
+        dom.em null,"You have dealt enough damage to kill #{numShips} "
+          dom.a {href: ship.fit}, ship.pluralName
+      dom.div {className: 'container'}, stamps
+
 )

 DamageTakenPanel = React.createClass(
@@ -438,7 +531,7 @@
     rows = _.map @props.data, (d) =>
       if STYLES[d.key]?.iconId
         iconCell = dom.td null,
-          dom.img {src: "https://image.eveonline.com/Type/#{STYLES[d.key].iconId}_32.png"}, null
+          dom.img {src: eveIconUrl(STYLES[d.key].iconId)}, null
       else
         iconCell = dom.td null, null
       return dom.tr {key: d.key},
@@ -451,7 +544,7 @@
       dom.td null, 'Total'
       dom.td null, Intl.NumberFormat().format(@props.total)
       dom.td null, ''
-    dom.table {className: 'table'},
+    dom.table {className: 'table table-condensed'},
       dom.tbody null,
         rows
 )


ADding client for spreadsheetsin.space

ADding client for spreadsheetsin.space

--- src/javascript/main.coffee
+++ src/javascript/main.coffee
@@ -12,6 +12,9 @@
       'disda.in':
         sso_client_id: 'be843963a3cf4ed3a5de411f1b4a84d4'
         callback_url: 'http://disda.in/eve/yir/'
+      'spreadsheetsin.space':
+        sso_client_id: 'ee5517f79a684842b0a5cf599d752411'
+        callback_url: 'http://spreadsheetsin.space/year-in-review/'
   live:
     sso_host: 'https://login.eveonline.com'
     sso_client_id: '67a0cc0f68d34e77b9751f8c75dd2e31'


allows caching of photos, remove flicking

--- server/controllers/photo.coffee
+++ server/controllers/photo.coffee
@@ -79,41 +79,42 @@
                         else
                             res.send photo, 201

+doPipe = (req, which, download, res) ->
+
+    if download
+        disposition = 'attachment; filename=' + req.photo.title
+        res.setHeader 'Content-disposition', disposition
+
+    request = req.photo.getFile which, (err) ->
+        if err then res.error 500, "File fetching failed.", err
+
+    # This is a temporary hack to allow caching
+    # ideally, we would do as follow :
+    # request.headers['If-None-Match'] = req.headers['if-none-match']
+    # but couchdb goes 500 (COUCHDB-1697 ?)
+    request.pipefilter = (couchres, myres) ->
+        if couchres.headers.etag is req.headers['if-none-match']
+            myres.send 304
+
+    request.pipe res
+
+
 module.exports.screen = (req, res) ->
-        res.setHeader 'Content-Type', 'image/jpg'
-        res.setHeader 'Cache-Control', 'public, max-age=31557600'
-        res.setHeader 'Content-disposition', 'attachment; filename=' + req.photo.title
-        which = if req.photo._attachments.screen then 'screen' else 'raw'
-        stream = req.photo.getFile which, (err) ->
-            if err then res.error 500, "File fetching failed.", err
-
-        stream.pipe res
+    which = if req.photo._attachments.screen then 'screen' else 'raw'
+    doPipe req, which, false, res

 module.exports.thumb = (req, res) ->
-        res.set 'Content-Type', 'image/jpeg'
-        res.setHeader 'Cache-Control', 'public, max-age=31557600'
-        stream = req.photo.getFile 'thumb', (err) ->
-            if err then res.error 500, "File fetching failed.", err
-
-        stream.pipe res
+    doPipe req, 'thumb', false, res

 module.exports.raw = (req, res) ->
-        res.set 'Content-Type', 'image/jpeg'
-        res.setHeader 'Cache-Control', 'public, max-age=31557600'
-        res.setHeader 'Content-disposition', 'attachment; filename=' + req.photo.title
-        stream = req.photo.getFile 'raw', (err) ->
-            if err then res.error 500, "File fetching failed.", err
-
-        stream.pipe res
+    doPipe req, 'raw', true, res

 module.exports.update = (req, res) ->
-        req.photo.updateAttributes req.body, (err) ->
-            return res.error 500, "Update failed." if err
-
-            res.send req.photo
+    req.photo.updateAttributes req.body, (err) ->
+        return res.error 500, "Update failed." if err
+        res.send req.photo

 module.exports.delete = (req, res) ->
-        req.photo.destroy (err) ->
-            return res.error 500, "Deletion failed." if err
-
-            res.success "Deletion succeded."
+    req.photo.destroy (err) ->
+        return res.error 500, "Deletion failed." if err
+        res.success "Deletion succeded."


Fixed security issue -- non-admins able to delete channels https://github.com/RocketChat/Rocket.Chat/issues/564

--- server/methods/eraseRoom.coffee
+++ server/methods/eraseRoom.coffee
@@ -1,15 +1,20 @@
 Meteor.methods
 	eraseRoom: (rid) ->
 		fromId = Meteor.userId()
-		# console.log '[methods] eraseRoom -> '.green, 'fromId:', fromId, 'rid:', rid

-		# ChatRoom.update({ _id: rid}, {'$pull': { userWatching: Meteor.userId(), userIn: Meteor.userId() }})
+		user = Meteor.users.findOne Meteor.userId()
+		if user.admin is true

-		userUnset = {'$unset': {}}
-		userUnset.$unset['readMessages.' + rid] = ''
-		Meteor.users.update({_id: Meteor.userId()}, userUnset)

-		ChatMessage.remove({rid: rid})
-		ChatSubscription.remove({rid: rid})
-		ChatRoom.remove(rid)
-		# @TODO remove das mensagens lidas do usuário
+			# console.log '[methods] eraseRoom -> '.green, 'fromId:', fromId, 'rid:', rid
+
+			# ChatRoom.update({ _id: rid}, {'$pull': { userWatching: Meteor.userId(), userIn: Meteor.userId() }})
+
+			userUnset = {'$unset': {}}
+			userUnset.$unset['readMessages.' + rid] = ''
+			Meteor.users.update({_id: Meteor.userId()}, userUnset)
+
+			ChatMessage.remove({rid: rid})
+			ChatSubscription.remove({rid: rid})
+			ChatRoom.remove(rid)
+			# @TODO remove das mensagens lidas do usuário


EB: fix healthcheck urls

EB: fix healthcheck urls

--- servers/lib/server/index.coffee
+++ servers/lib/server/index.coffee
@@ -271,8 +271,9 @@
   urls = [
     socialapi.proxyUrl
     newkontrol.url
-    socialApiUri
-    broker.uri
+    "#{KONFIG.publicHostname}#{socialApiUri}"
+    "#{KONFIG.publicHostname}#{broker.uri}/info"
+    "#{KONFIG.publicHostname}/kloud/kite/info"
   ]

   urlFns = urls.map (url)->->


geometry: added getBoundary function

geometry: added getBoundary function


Removed flavor text and illustrator from card data

--- data/fetch.coffee
+++ data/fetch.coffee
@@ -34,8 +34,6 @@
   "minimumdecksize",
   "faction",
   "factioncost", # influence
-  "flavor",
-  "illustrator",
   "number",
   "quantity",
   "setname",
@@ -68,9 +66,6 @@
 request.get baseurl + "cards", (error, response, body) ->
   if !error and response.statusCode is 200
     cards = selectFields(cardFields, JSON.parse(body))
-    #for card in cards
-      #fs.writeFile "cards/#{card.title}.json", JSON.stringify(card)
-
     # db.collection("cards").remove ->
     # db.collection("cards").insert cards, (err, result) ->
     fs.writeFile "cards.json", JSON.stringify(cards), ->


removed console log on clicking to clear textarea input

--- app/assets/javascripts/bit.js.coffee
+++ app/assets/javascripts/bit.js.coffee
@@ -13,5 +13,4 @@
       $("#title_input").removeClass("hidden")

   $('#clear_input').click ->
-    console.log "clicked"
     $( "textarea" ).val("")


removed an unused function

removed an unused function

--- source/javascripts/index/holidaySchedule.coffee
+++ source/javascripts/index/holidaySchedule.coffee
@@ -9,10 +9,6 @@

 time_format = (time) -> time.format('h:mm') if time?
 minutes_before_event = (event, minutes)-> NaharShalomScheduleHelpers.roundedToNearest5Minutes(moment(event).subtract(minutes, 'minutes'))
-
-show_mosaei_yom_tob = (date, zmanim) -> # This code is no longer called from anywhere
-  $('.mosaei-yom-tob').removeClass('hidden').find('.date').html(moment.weekdays()[date.weekday()])
-  $('.yom-tob-ends').html(set_hakochabim(zmanim))

 class Schedule
   constructor: (day_iterator) ->


Auto-load more on bottom of event page

Auto-load more on bottom of event page

--- app/assets/javascripts/events.coffee
+++ app/assets/javascripts/events.coffee
@@ -1,5 +1,4 @@
 $(document).ready ->
-
   $('.extend_button')
     .live 'ajax:complete', (event,data) ->
       e = $(event.target).closest('.event')
@@ -22,8 +21,12 @@
     $('#more_events').hide()
     $('#more_events_loading').show()
   $('#more_events').live 'ajax:complete', (event,data) ->
-    $('#events').append innerShiv data.responseText
-    d = $('#events').children('.event').last().find('time').attr('datetime')
-    url = $('#more_events').attr('href').replace(/&date=[^&]*/,"&date="+d)
-    $('#more_events').attr('href',url).show()
+    unless data.responseText.length == 1
+      $('#events').append innerShiv data.responseText
+      d = $('#events').children('.event').last().find('time').attr('datetime')
+      url = $('#more_events').attr('href').replace(/([&\?])date=[^&]*/,"$1date="+d)
+      $('#more_events').attr('href',url).show()
     $('#more_events_loading').hide()
+  $(window).scroll ->
+    if ($(window).scrollTop() == $(document).height() - $(window).height())
+      $('#more_events').click() unless $('#more_events').first().css('display') == "none"


Use jQuery delegate to handle blur events

--- app/assets/javascripts/events.coffee
+++ app/assets/javascripts/events.coffee
@@ -14,7 +14,7 @@
   minutes = ("0" + minutes).slice(-2)
   "#{hours}:#{minutes}#{am_or_pm}"
 $ ->
-  $(".times input").blur ->
+  $(".suggestions").delegate ".times input", "blur", ->
     if(!$(this).data("time-parsed") and $(this).val() != "")
       $(this).val(parseTime($(this).val()))
     $(this).data("time-parsed", true)


Add snippets to help work with Node

Add snippets to help work with Node

Redefining Atom's own "req" snippet to use double-quotes.

--- user/snippets.cson
+++ user/snippets.cson
@@ -76,6 +76,12 @@
 	Strict:
 		prefix: "js"
 		body: '"use strict";\n\n$1'
+	"Node-friendly log":
+		prefix: "nog"
+		body: 'console.log(JSON.stringify($1, null, "\\\\t"));'
+	Require:
+		prefix: "req"
+		body: 'const ${1:module} = require("${1:module}");'

 # CoffeeScript/CSON


Removed additional prettify reference

Removed additional prettify reference

--- app/assets/javascripts/global.coffee
+++ app/assets/javascripts/global.coffee
@@ -18,7 +18,6 @@
   Turbolinks.allowLinkExtensions('md')
   Turbolinks.enableProgressBar()
   window.$isDirty = false
-  prettyPrint()
   variablesReady() if (typeof variablesReady == 'function')
   datasetsReady()
   agreementsReady()


Post from party button

Post from party button

--- app/assets/javascripts/lights.coffee
+++ app/assets/javascripts/lights.coffee
@@ -17,7 +17,7 @@
             $this.prop 'checked', !$this.prop('checked')

     $('#party_canvas').on 'click', (e) ->
-        $.ajax(url: 'lights/party_on_off')
+        $.post('lights/party_on_off')
         togglePartyMode()

     $('.bulb').on 'click', (e) ->


#401 popup a modal to show full size map image

--- app/assets/javascripts/map.js.coffee
+++ app/assets/javascripts/map.js.coffee
@@ -1,4 +1,4 @@
-$ ->
+$(document).ready ->
   if $("#mapPreview").length or $("#baiduMap").length
     if $("#mapPreview").length
       map = new BMap.Map("mapPreview")
@@ -26,3 +26,13 @@

     updateMap $("#event_location").val() unless $("#event_location").val() is ""
     updateMap $("#baiduMap").data("location") if $("#baiduMap").length
+
+  mapImages = $("div.event-map img")
+  if mapImages.length > 0
+    mapImages.each (index) ->
+      mapModalId = "mapModal#{index}"
+      mapModal = $("

<button class='btn' data-dismiss='modal' aria-hidden='true'>Close</button>

")
+                    .appendTo('body')
+      $(this).clone().appendTo(mapModal.find('.modal-body'))
+      $(this).on 'click', ->
+        $("\##{mapModalId}").modal('show')


view.visible performance fix

view.visible performance fix

Replaced use of jQuery $el.is("visible") with a simpler
$el.style.display == "block" to significantly improve typing perfomance
in complex pages.

--- src/view.coffee
+++ src/view.coffee
@@ -44,7 +44,7 @@
   #
   # @return [Boolean]
   visible: ->
-    @$el.is(":visible")
+    @$el[0].style.display == "block"

   highlighted: ->
     @$el.find(".cur").length > 0


Fixed image outlet being set to true instead of value

--- src/view.coffee
+++ src/view.coffee
@@ -24,7 +24,7 @@
 				get: (outlet, view) ->
 					outlet.el.src
 				set: (outlet, view, value) ->
-					outlet.el.src = value? or ''
+					outlet.el.src = if value? then value or ''
 			html:
 				get: (outlet, view) ->
 					outlet.el.innerHTML


better json fn

better json fn

--- src/view.coffee
+++ src/view.coffee
@@ -26,11 +26,8 @@
 toffee.states = #{JSON.stringify states}

 toffee.__json = (locals, o) ->
-  try
-    json = JSON.stringify(o).replace(/</g,'\\\\u003C').replace(/>/g,'\\\\u003E').replace(/&/g,'\\\\u0026')
-  catch e
-    throw {stack:[], message: "JSONify error (\#{e.message}) on line \#{locals.__toffee.lineno}", toffee_line_base: locals.__toffee.lineno }
-  "" + json
+  if not o? then return "null"
+  else return "" + JSON.stringify(o).replace(/</g,'\\\\u003C').replace(/>/g,'\\\\u003E').replace(/&/g,'\\\\u0026')

 toffee.__raw = (locals, o) -> o


Router: Compilation and code styling fixes.

--- src/lib/core/Router.coffee
+++ src/lib/core/Router.coffee
@@ -29,10 +29,9 @@
     @historyManager.on 'Navigated', (e) =>
       path = e.data

-      return if @latestHandledRoute is path
-
-      @handleRoute_ path
-      @latestHandledRoute = path
+      unless @latestHandledRoute is path
+        @handleRoute_ path
+        @latestHandledRoute = path

   ###*
@@ -128,7 +127,7 @@
         token = token.replace /:/g, '' # replace the `:` for correct token name
         params[token] = values[index]  # match token with values

-      cb.call this, params, query if cb
+      cb?.call this, params, query

   ###*
@@ -137,6 +136,7 @@
     to match this route `/activity/post/1/comment/2`. Here is the regex101
     https://www.regex101.com/r/wX2kB6/1

+    @private
     @param {!string} route Actual page route.
     @return {RegExp|undefined} RegExp to match page URL.
   ###


fix #13

fix #13

--- client/app/models/todolist.coffee
+++ client/app/models/todolist.coffee
@@ -39,4 +39,4 @@
                 callback null, todolist

     @deleteTodoList = (id, callback) ->
-        requet.del "todolists/#{id}", callback
+        request.del "todolists/#{id}", callback


fix translation bug, can not reuse user.errors.invalid_code

--- app/coffeescripts/models/User.coffee
+++ app/coffeescripts/models/User.coffee
@@ -34,7 +34,7 @@
         too_long:     I18n.t("errors.too_long", "Can't exceed %{max} characters", {max: 255})
       self_enrollment_code:
         blank:        I18n.t("errors.required", "Required")
-        invalid:      I18n.t("errors.invalid_code", "Invalid code")
+        invalid:      I18n.t("errors.invalid_code", "Invalid Code")
         already_enrolled: I18n.t("errors.already_enrolled", "You are already enrolled in this course")
         full:         I18n.t("errors.course_full", "This course is full")
       terms_of_use: