Implemented card flip and stand actions.

--- client/script.coffee
+++ client/script.coffee
@@ -5,9 +5,9 @@
 @camera = new CoordinateSystem parent: null
 @table = new CoordinateSystem parent: @camera
 @cards = [
-    (new CoordinateSystem parent: @table).do translate:{ x: 0  , y: 20, z:15}
-    (new CoordinateSystem parent: @table).do translate:{ x: 150, y: 20, z:15}
-    (new CoordinateSystem parent: @table).do translate:{ x: 300, y: 20, z:15}
+    (new CoordinateSystem parent: @table).do translate:{ x: 0  , y: 20, z:1}, duration: 0
+    (new CoordinateSystem parent: @table).do translate:{ x: 150, y: 20, z:1}, duration: 0
+    (new CoordinateSystem parent: @table).do translate:{ x: 300, y: 20, z:1}, duration: 0
 ]

 camera.do translate: { z: -800}
@@ -17,8 +17,7 @@
     @table.do rotate:{ x:45 }, around:{ y:height/2 }

     window.viewport = d3.select ".viewport"
-    draw()
-    @changePlayer()
+    setInterval (()-> draw()), 16

  # draw function
@@ -30,9 +29,6 @@
         .append("div")
         .attr("class", "table")
     d3table
-        .transition()
-        .duration(100)
-        .ease("linear")
         .style("-webkit-transform", (d) -> d.absoluteMatrix())

     d3cards = viewport.selectAll(".card").data(@cards)
@@ -40,18 +36,40 @@
         .append("div")
         .attr("class", "card")
         .on("mousedown", (d) ->
+            d.clicked ?= -1
+            d.standing ?= 1
+
             if d3.event.shiftKey
-                d.showing = !d.showing
-                d.parent = if d.showing then table else new CoordinateSystem parent: null
+                d.parent = switch d.parent
+                    when table then null
+                    when null then table
+                    # when camera then table
+
+            else if d3.event.altKey
+                d.standing *= -1
+                d.do(
+                    rotate   : { x:  90 * d.standing * d.clicked}
+                    around   : { y: 0 }
+                    duration : 1000
+                )
             else
-                d.clicked = !d.clicked
-                # d.do rotate:{ x:  (if d.clicked then 90 else -90 ) }
+                d.clicked *= -1
                 d.do(
-                    rotate     :{ x:  (if d.clicked then 179 else 181 ) }
-                    around     :{ y: 0 }
-                    translate  :{ y: -153 }
+                    translate: { z: 153/2 *d.clicked }
+                    duration : 500
+                    ease : d3.ease("cubic-in")
                 )
-            draw()
+                .do(
+                    translate:{ z: -153/2 * d.clicked }
+                    duration : 500
+                    delay    : 500
+                    ease : d3.ease("cubic-out")
+                )
+                d.do(
+                    rotate   : { x:  180 }
+                    around   : { y: 153/2 }
+                    duration : 1000
+                )
         );
     d3cardsEnter
         .append("div")
@@ -62,30 +80,38 @@

     d3cards
-        .transition()
-        .duration(100)
-        .ease("linear")
         .style("-webkit-transform", (d) -> d.absoluteMatrix())
+        .style("z-index", (d) ->
+            #todo compute distance and set z-index to fix chrome aparently missing z-buffer
+        )

     d3cards.exit()
-        .transition()
-        .duration(100)
-        .ease("linear")
         .style("opacity", 0)
         .remove()

 $(document).keydown (e) ->
-    if !e.shiftKey
+
+    if not e.shiftKey and not e.altKey
         switch e.keyCode
             when 37
-                table.do({rotate:{z:10}, around:{x:width/2 , y:height/2} })
+                table.do({rotate:{z:180}, around:{x:width/2 , y:height/2} })
             when 38
                 table.do({rotate:{x:10}, around:{x:width/2 , y:height/2} })
             when 39
-                table.do({rotate:{z:-10}, around:{x:width/2 , y:height/2} })
+                table.do({rotate:{z:-180}, around:{x:width/2 , y:height/2} })
             when 40
                 table.do({rotate:{x:-10}, around:{x:width/2 , y:height/2} })
-    else
+    if not e.shiftKey and e.altKey
+        switch e.keyCode
+            when 37
+                camera.do({rotate:{z:180}, around:{x:width/2 , y:height/2} })
+            when 38
+                camera.do({rotate:{x:10}, around:{x:width/2 , y:height/2} })
+            when 39
+                camera.do({rotate:{z:-180}, around:{x:width/2 , y:height/2} })
+            when 40
+                camera.do({rotate:{x:-10}, around:{x:width/2 , y:height/2} })
+    if e.shiftKey and not e.altKey
         switch e.keyCode
             when 37
                 camera.do translate:{x:10}
@@ -95,31 +121,3 @@
                 camera.do translate:{x:-10}
             when 40
                 camera.do translate:{z:-10}
-    draw()
-    e.preventDefault()
-
-
-
-showCardActive = no
-@showCard = ->
-    cards[0].parent = if showCardActive then table else new CoordinateSystem parent: null
-    draw()
-    showCardActive = not showCardActive
-
-player = 0
-@changePlayer1 = ->
-    player = (player+1) % 2
-    table.do({rotate:{z:179.999}, around:{x:width/2 , y:height/2} })
-    draw()
-
-@changePlayer = ->
-    # todo: think how to use d3 to interpolate transformations in a sound way
-    player = (player+1) % 2
-    steps = 50;
-    i=0
-    while i++ < steps
-        setTimeout ->
-                table.do({rotate:{z:180/steps}, around:{x:width/2 , y:height/2} })
-                draw()
-            , 100*i
-# yo.@cards[0].parent.matrix3d=yo.@cards[0].parent.matrix3d.rotate(-75,0,0);yo.draw()


Adding isStatic, setVelocity.

Adding isStatic, setVelocity.

--- src/element.coffee
+++ src/element.coffee
@@ -32,6 +32,7 @@
             dragCoefficient: 0.7
             visible: true
             id: wolf.getUniqueId()
+            static: false
         ((@[k] = v) for k, v of wolf.defaults(opts, defaults))

     # Return the element's position.
@@ -53,13 +54,19 @@

     # Apply an impulse force to the element.
     applyImpulse : (impulse) ->
+        return this if @isStatic()
         velocity = @getVelocity().add(impulse)
+        @setVelocity(velocity)
+
+    setVelocity : (velocity) ->
         @direction = velocity.normalize()
         @speed = velocity.getLength()

     # Apply the given force to the element for the given number of
     # milliseconds.
     applyForce : (force, milliseconds) ->
+        return this if @isStatic()
+
         # Calculate the effects of the force.
         acceleration = force.scale(@getInverseMass())
         velocity = @getVelocity().add(acceleration.scale(milliseconds))
@@ -100,6 +107,10 @@
         @unbind()
         return this

+    # Return true if the element is static.
+    isStatic : () ->
+        return @static
+
     # Render the element on the given canvas context.
     render : (context) ->
         throw new Error("Not Implemented error")
@@ -118,7 +129,6 @@
     rotate : (degrees) ->
         throw new Error("Not Implemented error")

-
     # Tell the element that one step in the simulation has elapsed.
     elapse : (milliseconds, iteration) ->
         this


Support ~/.atom/user.less

Support ~/.atom/user.less

--- src/app/atom.coffee
+++ src/app/atom.coffee
@@ -109,9 +109,10 @@
     @loadedThemes.push Theme.load(name)

   loadUserStylesheet: ->
-    userStylesheetPath = fs.join(config.configDirPath, 'user.css')
+    userStylesheetPath = fs.resolve(fs.join(config.configDirPath, 'user'), ['css', 'less'])
     if fs.isFile(userStylesheetPath)
-      applyStylesheet(userStylesheetPath, fs.read(userStylesheetPath), 'userTheme')
+      userStyleesheetContents = loadStylesheet(userStylesheetPath)
+      applyStylesheet(userStylesheetPath, userStyleesheetContents, 'userTheme')

   getAtomThemeStylesheets: ->
     themeNames = config.get("core.themes") ? ['atom-dark-ui', 'atom-dark-syntax']


Allow the scope of a region to be specified via the 'data-scope' attribute.

Within the context of a site, a content region within a page could belong specifically to the page or globally to the site. A global/site content region would be shared with multiple pages within the site.

For example, given the following content regions on a page:

Editing the content in region_1 will affect all pages that include region_1, since it has been denoted as a 'site' region via the 'data-scope' attribute. Editing the content in region_2 will only affect the page that is being edited. The 'site' and 'page' scopes used here are just examples. The host application would define and handle the scopes it supports.

--- vendor/assets/javascripts/mercury/region.js.coffee
+++ vendor/assets/javascripts/mercury/region.js.coffee
@@ -7,6 +7,7 @@

     @document = @window.document
     @name = @element.attr('id')
+    @scope = @element.attr('data-scope')
     @history = new Mercury.HistoryBuffer()
     @build()
     @bindEvents()
@@ -95,6 +96,7 @@
   serialize: ->
     return {
       type: @type
+      scope: @scope
       value: @content(null, true)
       snippets: @snippets()
     }


[fix] Looked for templates in the configured place (2m)

--- vendor/assets/javascripts/neat/model_editor.coffee
+++ vendor/assets/javascripts/neat/model_editor.coffee
@@ -11,8 +11,8 @@

     # Renders the 'show' template normally,
     # renders 'edit' when in edit mode.
-    @showTemplate = JST["#{@viewPath}/show"]
-    @editTemplate = JST["#{@viewPath}/edit"]
+    @showTemplate = Neat.template["#{@viewPath}/show"]
+    @editTemplate = Neat.template["#{@viewPath}/edit"]

     # Wire up events.
     # Don't use Backbone's events hash because if subclasses


header - mobile search - expand/collapse

--- _assets/javascripts/shared/shared.coffee
+++ _assets/javascripts/shared/shared.coffee
@@ -49,8 +49,13 @@
     $(".search_holder .trigger .search").click (e) ->
       e.preventDefault()

-      $(".search_holder").toggleClass("expanded")
-
+      $search_form = $(".search_holder")
+      $search_box = $search_form.find("input")
+      if $search_form.hasClass("expanded")
+        $search_form.removeClass("expanded")
+      else
+        $search_form.addClass("expanded")
+        $search_box.focus()

   getURLParameter: (parameter) ->
     page_url = window.location.search.substring(1)


Create bundle right

Create bundle right

--- workers/social/lib/social/models/group/index.coffee
+++ workers/social/lib/social/models/group/index.coffee
@@ -1312,7 +1312,7 @@
         return callback err  if err?

         @addBundle bundle, ->
-          callback bundle
+          callback no, bundle

   fetchBundle$: permit 'commission resources',
     success: (client, rest...) -> @fetchBundle rest...


Costmetic changes

Costmetic changes

--- workers/social/lib/social/traits/slugifiable.coffee
+++ workers/social/lib/social/traits/slugifiable.coffee
@@ -42,7 +42,7 @@
           if name
             @suggestUniqueSlug client, slug, i+1, callback
           else
-              callback null, suggestedSlug
+            callback null, suggestedSlug

   claimUniqueSlug =(ctx, konstructor, slug, callback)->
     JName = require '../models/name'
@@ -56,8 +56,7 @@
     }
     nextNameFull = nextName.slug
     JName.claim nextNameFull, [nextName], konstructor, 'slug', (err, nameDoc)->
-      if err
-        callback err
+      if err then callback err
       else
         callback null, nextName


simplify syntax

simplify syntax

--- vpn.coffee
+++ vpn.coffee
@@ -5,43 +5,46 @@
 Router.setTemplateNameConverter (str) ->
   str

-Router.route "/", (->
-  @render "Home"
-  return
-),
+Router.route "/",
   name: "home"
+  action: ->
+    @render "Home"

 Router.route "/driver_lines"
-Router.route "/driver_lines/:_id/:name?", (->
-  @render "driver_line_show",
-    data: ->
-      DriverLines.findOne _id: @params._id
-),
+
+Router.route "/driver_lines/:_id/:name?",
   name: "driver_line_show"
+  action: ->
+    @render "driver_line_show"
+  data: ->
+    DriverLines.findOne _id: @params._id

 Router.route "/neuron_types"
-Router.route "/neuron_types/:_id/:name?", (->
-  @render "neuron_type_show",
-    data: ->
-      NeuronTypes.findOne _id: @params._id
-),
+
+Router.route "/neuron_types/:_id/:name?",
   name: "neuron_type_show"
+  action: ->
+    @render "neuron_type_show"
+  data: ->
+    NeuronTypes.findOne _id: @params._id

 Router.route "/neuropils"
-Router.route "/neuropils/:_id/:name?", (->
-  @render "neuropil_show",
-    data: ->
-      Neuropils.findOne _id: @params._id
-),
+
+Router.route "/neuropils/:_id/:name?",
   name: "neuropil_show"
+  action: ->
+    @render "neuropil_show"
+  data: ->
+    Neuropils.findOne _id: @params._id

 Router.route "/binary_data"
-Router.route "/binary_data/:_id/:name?", (->
-  @render "binary_data_show",
-    data: ->
-      BinaryData.findOne _id: @params._id
-),
+
+Router.route "/binary_data/:_id/:name?",
   name: "binary_data_show"
+  action: ->
+    @render "binary_data_show"
+  data: ->
+    BinaryData.findOne _id: @params._id

 Router.route "/RecentChanges"

@@ -49,6 +52,7 @@
   @render "Search",
     data: ->
       @params
+  return

 @remove_driver_line = (my_id) ->
   rdl = (doc) ->


Index directly into array

Index directly into array

--- src/grammar.coffee
+++ src/grammar.coffee
@@ -226,7 +226,7 @@

     # Pop last content name scope if an end pattern is matched since only
     # text between the begin/end patterns should have the content name scope
-    if endPatternMatch and rule?.contentScopeName and rule is _.last(stack)
+    if endPatternMatch and rule?.contentScopeName and rule is stack[stack.length - 1]
       scopes.pop()

     scopes


four photons with physics

four photons with physics

--- src/Arena.coffee
+++ src/Arena.coffee
@@ -14,11 +14,7 @@
     @screen.anchor.x = 0.5
     @screen.anchor.y = 0.5

-    @circle = @game.add.sprite(400, 225, 'circle')
-    @circle.width = 24
-    @circle.height = 24
-    @circle.anchor.x = 0.5
-    @circle.anchor.y = 0.5
+    @photons = @game.add.group()

     @square = @game.add.sprite(400, 225, 'square')
     @square.width = 800
@@ -27,12 +23,46 @@
     @square.anchor.y = 0.5
     @square.alpha = 0.05

+    circle = @game.add.sprite(400, 225, 'circle')
+    @game.physics.enable(circle, Phaser.Physics.ARCADE)
+    circle.width = 24
+    circle.height = 24
+    circle.anchor.x = 0.5
+    circle.anchor.y = 0.5
+    circle.body.velocity.x = 200
+    @photons.add(circle)
+
+    circle = @game.add.sprite(400, 225, 'circle')
+    @game.physics.enable(circle, Phaser.Physics.ARCADE)
+    circle.width = 24
+    circle.height = 24
+    circle.anchor.x = 0.5
+    circle.anchor.y = 0.5
+    circle.body.velocity.x = -200
+    @photons.add(circle)
+
+    circle = @game.add.sprite(400, 225, 'circle')
+    @game.physics.enable(circle, Phaser.Physics.ARCADE)
+    circle.width = 24
+    circle.height = 24
+    circle.anchor.x = 0.5
+    circle.anchor.y = 0.5
+    circle.body.velocity.y = 200
+    @photons.add(circle)
+
+    circle = @game.add.sprite(400, 225, 'circle')
+    @game.physics.enable(circle, Phaser.Physics.ARCADE)
+    circle.width = 24
+    circle.height = 24
+    circle.anchor.x = 0.5
+    circle.anchor.y = 0.5
+    circle.body.velocity.y = -200
+    @photons.add(circle)
+
   render:->
     @buffer.renderXY(@game.stage, 0, 0, false)

   update:->
-    @circle.x += 6
-    @circle.x %= 800

   destroy:->


Remove unused variables

Remove unused variables

--- src/Bacon.coffee
+++ src/Bacon.coffee
@@ -173,18 +173,18 @@
   current = (ctxStack) -> ctxStack[ctxStack.length - 1]
   setValue = (ctxStack, key, value) -> current(ctxStack)[key] = value
   applyStreamValue = (key, index) -> (ctxStack, values) -> setValue(ctxStack, key, values[index])
-  constantValue = (key, value) -> (ctxStack, values) -> setValue(ctxStack, key, value)
+  constantValue = (key, value) -> (ctxStack) -> setValue(ctxStack, key, value)
   mkContext = (template) -> if template instanceof Array then [] else {}
   compile = (key, value) ->
     if (value instanceof Observable)
       streams.push(value)
       funcs.push(applyStreamValue(key, streams.length - 1))
     else if (value == Object(value) and typeof value != "function")
-      pushContext = (key) -> (ctxStack, values) ->
+      pushContext = (key) -> (ctxStack) ->
         newContext = mkContext(value)
         setValue(ctxStack, key, newContext)
         ctxStack.push(newContext)
-      popContext = (ctxStack, values) -> ctxStack.pop()
+      popContext = (ctxStack) -> ctxStack.pop()
       funcs.push(pushContext(key))
       compileTemplate(value)
       funcs.push(popContext)
@@ -207,7 +207,7 @@
   isNext: -> false
   isError: -> false
   hasValue: -> false
-  filter: (f) -> true
+  filter: -> true

 class Next extends Event
   constructor: (valueF) ->
@@ -721,7 +721,7 @@
     removeSub = (subscription) ->
       subscriptions = _.without(subscription, subscriptions)
     waiters = null
-    done = (event) ->
+    done = ->
       if waiters?
         ws = waiters
         waiters = null
@@ -853,7 +853,6 @@
           return
     subscribeAll = (newSink) =>
       sink = newSink
-      unsubFuncs = []
       for subscription in cloneArray(subscriptions)
         subscribeInput(subscription)
       unsubAll
@@ -888,7 +887,7 @@
       @consume = () -> invoke(queue[0])
       @push  = (x) -> queue = [x]
       @mayHave = -> true
-      @hasAtLeast = (c) -> queue.length
+      @hasAtLeast = -> queue.length

 Source.fromObservable = (s) ->
   if s instanceof Source
@@ -933,7 +932,7 @@
         !source.sync or source.ended
       cannotMatch = (p) ->
         _.any(p.ixs, (i) -> !sources[i.index].mayHave(i.count))
-      part = (source, sourceIndex) -> (unsubAll) ->
+      part = (source) -> (unsubAll) ->
         source.subscribe (e) ->
           if e.isEnd()
             source.markEnded()


`x = {} if x` -> `x and= {}`, per Michael's suggestion

https://github.com/jashkenas/coffee-script/pull/1324/files#r25463

--- coffee-script.coffee
+++ coffee-script.coffee
@@ -61,7 +61,7 @@
       if options.filename then fs.realpathSync(options.filename) else '.'

   # Clear the module cache.
-  mainModule.moduleCache = {} if mainModule.moduleCache
+  mainModule.moduleCache and= {}

   # Assign paths for node_modules loading
   if process.binding('natives').module


Replace Python 'raise' with Powershell 'throw' keyword.

--- grammars/powershell.cson
+++ grammars/powershell.cson
@@ -93,7 +93,7 @@
   }
   {
     'comment': 'keywords that alter flow from within a block'
-    'match': '\\b(?i:(break|continue|pass|raise|return|exit|yield))\\b'
+    'match': '\\b(?i:(break|continue|pass|throw|return|exit|yield))\\b'
     'name': 'keyword.control.flow.powershell'
   }
   {


optimize

optimize

--- src/Bacon.coffee
+++ src/Bacon.coffee
@@ -1084,7 +1084,7 @@
             for p in pats
                if match(p)
                  #console.log "match", p
-                 functions = _.map ((i) -> sources[i.index].consume()), p.ixs
+                 functions = (sources[i.index].consume() for i in p.ixs)
                  reply = sink trigger.e.apply ->
                    values = (fun() for fun in functions)
                    p.f(values ...)


gitteh.clone() now writes out local ref, which pretty much wraps up the functionality of a basic clone.

--- coffee/gitteh.coffee
+++ coffee/gitteh.coffee
@@ -363,7 +363,13 @@
 			repo.ref remote.HEAD, true, wrapCallback cb, (ref) ->
 				cb null, repo, remote, ref

-		# We now have fully resolved OID head ref. Fetch the commit.
+		# We now have fully resolved OID head ref. Create a local branch.
+		(repo, remote, headRef, cb) ->
+			refName = remote.fetchSpec.transformFrom remote.HEAD
+			repo.createReference refName, headRef.target, wrapCallback cb, ->
+				cb null, repo, remote, headRef
+
+		# And now fetch the commit.
 		(repo, remote, headRef, cb) ->
 			repo.commit headRef.target, wrapCallback cb, (commit) ->
 				cb null, repo, remote, commit
@@ -397,18 +403,17 @@
 			checkoutTree = (tree, dest, cb) ->
 				async.forEach tree.entries, handleEntry.bind(null, dest), cb
 			checkoutTree headTree, repo.workingDirectory, wrapCallback cb, ->
-				cb null, repo, headTree
+				cb null, repo, remote, headTree

 		# Update the git index with the tree we just checked out.
-		(repo, headTree, cb) ->
+		(repo, remote, headTree, cb) ->
 			repo.index.readTree headTree.id, wrapCallback cb, ->
-				cb null, repo
+				cb null, repo, remote

 		# Now write the index back to disk.
-		(repo, cb) ->
+		(repo, remote, cb) ->
 			repo.index.write wrapCallback cb, ->
-				cb null, repo
-
+				cb null, repo, remote
 	], (err, repo) ->
 		return emitter.emit "error", err if err?


Backspace event naming

Backspace event naming

--- framer/InputLayer.coffee
+++ framer/InputLayer.coffee
@@ -5,7 +5,7 @@
 {Events} = require "./Events"

 Events.EnterKey  = "EnterKey"
-Events.BackSpaceKey  = "BackSpaceKey"
+Events.BackspaceKey  = "BackspaceKey"
 Events.InputValueChange = "InputValueChange"
 Events.InputFocus = "InputFocus"
 Events.InputBlur = "InputBlur"
@@ -115,7 +115,7 @@

 			# If backspace key
 			if e.which is 8
-				@emit(Events.BackSpaceKey, event)
+				@emit(Events.BackspaceKey, event)

 			# Revert to placeholder
 			if @value is ""
@@ -145,7 +145,7 @@
 	@define "multiLine", @simpleProperty("multiLine", false)

 	onEnterKey: (cb) -> @on(Events.EnterKey, cb)
-	onBackSpaceKey: (cb) -> @on(Events.BackSpaceKey, cb)
+	onBackspaceKey: (cb) -> @on(Events.BackspaceKey, cb)
 	onInputChange: (cb) -> @on(Events.InputValueChange, cb)
 	onInputFocus: (cb) -> @on(Events.InputFocus, cb)
 	onInputBlur: (cb) -> @on(Events.InputBlur, cb)


Filter dotfiles / emacs files.

Filter dotfiles / emacs files.

--- src/helpers.coffee
+++ src/helpers.coffee
@@ -64,6 +64,9 @@

 class exports.Watcher extends EventEmitter
+  # RegExp that would filter invalid files (dotfiles, emacs caches etc).
+  invalid: /^(\.|#)/
+
   constructor: ->
     #console.log "Created"
     @watched = {}
@@ -102,6 +105,7 @@
     @_watch directory, read

   _handle: (file) ->
+    return if @invalid.test path.basename file
     fs.realpath file, (error, filePath) =>
       return exports.logError error if error?
       fs.stat file, (error, stats) =>
@@ -132,9 +136,8 @@

 # Filter out dotfiles, emacs swap files and directories.
 exports.filterFiles = (files, sourcePath) ->
-  re = /^(\.|#)/
   files.filter (filename) ->
-    return no if re.test filename
+    return no if (exports.Watcher::invalid).test filename
     stats = fs.statSync path.join sourcePath, filename
     return no if stats?.isDirectory()
     yes


cleaner without introducing function

cleaner without introducing function

--- lib/atom-sharper/atom-sharper.coffee
+++ lib/atom-sharper/atom-sharper.coffee
@@ -14,7 +14,7 @@
     atom.workspaceView.command "atom-sharper:request", _.debounce(Omni.syntaxErrors, 200)
     atom.workspaceView.command "editor:display-updated", _.debounce(Omni.syntaxErrors, 200)
     atom.workspaceView.command "atom-sharper:go-to-definition", =>
-      @navigateToWord = @wordUnderCursor()
+      @navigateToWord = atom.workspace.getActiveEditor()?.getWordUnderCursor()
       Omni.goToDefinition()

     atom.on "omni:navigate-to", (position) =>
@@ -41,10 +41,6 @@
     else
       atom.packages.once 'activated', createStatusEntry

-  wordUnderCursor: ->
-    editor = atom.workspace.getActiveEditor()
-    editor.getWordUnderCursor()
-
   toggle: ->
     OmniSharpServer.get().toggle()


Faster IE filter check

Faster IE filter check

--- lib/autoprefixer/hacks/filter.coffee
+++ lib/autoprefixer/hacks/filter.coffee
@@ -22,7 +22,7 @@
   # Check is it Inetrenet Explorer filter
   constructor: ->
     super
-    if @value.match(/DXImageTransform\.Microsoft/)
+    if @value.indexOf('DXImageTransform.Microsoft') != -1
       @unprefixed = @prop = '-ms-filter'

 module.exports = Filter


Dashboard updated to handle API's update #693

--- admin/static/coffee/models.coffee
+++ admin/static/coffee/models.coffee
@@ -278,7 +278,7 @@
         # CPU, mem, disk
         for m in machines.models
             mstats = m.get_stats().proc
-            if mstats? and mstats.global_cpu_util?
+            if mstats? and mstats.global_cpu_util? #if global_cpu_util is present, the other attributes should too.
                 __s.global_cpu_util_avg += parseFloat(mstats.global_cpu_util.avg)
                 __s.global_mem_total += parseInt(mstats.global_mem_total)
                 __s.global_mem_used += parseInt(mstats.global_mem_used)


:bug: Fix raise error while checking editor.displayBuffer, because `TextEditor.prototype.displayBuffer` has always been private, #5

--- lib/minimap-bookmarks-binding.coffee
+++ lib/minimap-bookmarks-binding.coffee
@@ -9,20 +9,13 @@
     @decorationSubscriptionsByMarkerId = {}

     # https://github.com/atom/bookmarks/blob/master/lib/main.coffee#L38
-    markerLayerId = atom.packages.packageStates?.bookmarks?[@editor.id]?.markerLayerId
-    if markerLayerId?
-      @subscriptions.add @editor.getMarkerLayer(markerLayerId).onDidCreateMarker (marker) =>
+    id = atom.packages.packageStates?.bookmarks?[@editor.id]?.markerLayerId
+    markerLayer = markerLayerId && @editor.getMarkerLayer(id)
+    if markerLayer?
+      @subscriptions.add markerLayer.onDidCreateMarker (marker) =>
         @handleMarker(marker)

-      @editor.getMarkerLayer(markerLayerId).findMarkers().forEach (marker) =>
-        @handleMarker(marker)
-
-    else if @editor.displayBuffer.onDidCreateMarker?
-      @subscriptions.add @editor.displayBuffer.onDidCreateMarker (marker) =>
-        if marker.matchesProperties(class: 'bookmark')
-          @handleMarker(marker)
-
-      @editor.displayBuffer.findMarkers(class: 'bookmark').forEach (marker) =>
+      markerLayer.findMarkers().forEach (marker) =>
         @handleMarker(marker)

   handleMarker: (marker) ->


Fix .dropdown__trigger class not being added to trigger element

--- dropdown.coffee
+++ dropdown.coffee
@@ -172,7 +172,7 @@
       return attrs

 Template.dropdownTrigger.rendered = ->
-  this.$(':first').addClass(DROPDOWN_TRIGGER.slice(1))
+  this.$('*').first().addClass(DROPDOWN_TRIGGER.slice(1))

 Template.dropdownTrigger.helpers(
   isActive: ->
@@ -217,7 +217,7 @@
     Dropdowns.hideAllBut(name)
     Dropdowns.toggle(name)

-    Tracker.afterFlush positionDropdown(name, tmpl.find(':first'))
+    Tracker.afterFlush positionDropdown(name, tmpl.find(DROPDOWN_TRIGGER))
 )

 Template.registerHelper 'dropdownIsActive', isActive


upd(bindings): removed comments

upd(bindings): removed comments

--- src/bindings.coffee
+++ src/bindings.coffee
@@ -402,6 +402,8 @@

       @updateBinders(scope)

+  # Async: Intercepts `Rivets.Binding::bind` to build `@componentView` with a localized
+  # map of models from the root view. Bind `@componentView` on subsequent calls.
   bindAsync: =>
     if @componentView?
       @componentView.bindAsync().then () =>
@@ -466,77 +468,6 @@
             scope.ready? if @templateView then @templateView else {}
             @updateBinders(scope)

-
-  # Async: Intercepts `Rivets.Binding::bind` to build `@componentView` with a localized
-  # map of models from the root view. Bind `@componentView` on subsequent calls.
-#  bindAsync: =>
-#    if @componentView?
-#      @componentView.bindAsync().then () =>
-#        if @templateView?
-#          @templateView.bindAsync()
-#    else
-#      @el._bound = true
-#
-#      options = {}
-#
-#      for option in Rivets.extensions
-#        options[option] = {}
-#        options[option][k] = v for k, v of @component[option] if @component[option]
-#        options[option][k] ?= v for k, v of @view[option]
-#
-#      for option in Rivets.options
-#        options[option] = @component[option] ? @view[option]
-#
-#      bindingRegExp = @view.bindingRegExp()
-#
-#      for attribute in @el.attributes or []
-#        if (!bindingRegExp.test(attribute.name) && attribute.value)
-#          propertyName = @camelCase attribute.name
-#
-#          if propertyName in (@component.static ? [])
-#            @static[propertyName] = attribute.value
-#          else
-#            @binders[propertyName] = attribute.value
-#
-#      unless @bound
-#        Object.keys(@binders).forEach (key) =>
-#          binder =
-#            routine: (el, value) => scope?[key] = value
-#            getValue: () => scope?[key]
-#
-#          @binders[key] = @view.addBinding null, binder, @binders[key]
-#
-#        @bound = true
-#
-#      if isProdEnv and @isRenderedComponent()
-#        scope = @buildLocalScope()
-#        componentViewPromise = @buildComponentViewAsync Array.prototype.slice.call(@el.childNodes), scope, options, @view
-#        componentViewPromise.then (componentView) =>
-#          @componentView = componentView
-#          scope.ready? @componentView
-#          @updateBinders scope
-#      else
-#        if isProdEnv
-#          componentTemplatePromise = @buildRuntimeComponentTemplateAsync()
-#        else
-#          componentTemplatePromise = @buildComponentTemplateAsync()
-#
-#        componentTemplatePromise.then (componentTemplate) =>
-#          componentContent = @buildComponentContent()
-#          componentViewPromise = @buildComponentViewAsync componentContent, @view.models, options
-#
-#          componentViewPromise.then (componentView) =>
-#            @componentView = componentView
-#            @el.appendChild componentTemplate
-#            scope = @buildLocalScope()
-#            templateViewPromise = @buildViewInstanceAsync componentTemplate, scope, options
-#            templateViewPromise.then (templateView) =>
-#              @templateView = templateView
-#              @insertContent componentTemplate, componentContent
-#              scope.ready? if @templateView then @templateView else {}
-#              @updateBinders scope
-
-
   updateBinders: (scope) =>
     for key, binder of @binders
       @upstreamObservers[key] = @observe scope, key, ((key, binder) => =>


basic open file feature (new window)

basic open file feature (new window)

--- src/FileManager/FileManagerCtrl.coffee
+++ src/FileManager/FileManagerCtrl.coffee
@@ -1,5 +1,5 @@
 angular.module('fileManager').
-controller('FileManagerCtrl', ($scope, $stateParams, session, fileManager, $document) ->
+controller('FileManagerCtrl', ($scope, $stateParams, session, fileManager, $document, $window) ->
   $scope.selected = {
     files: {}
     clipboard: {}
@@ -54,6 +54,11 @@

   # ---------Context-Menu------------
   $scope.openFile = ->
+    for key, file of $scope.selected.files
+      file.getContent().then (content) =>
+        blob = new Blob([content])
+        url = URL.createObjectURL(blob)
+        $window.open(url, file.metadata.name)

   $scope.shareFiles = ->
     for key, file of $scope.selected.files


app: Add TwoPoint service and rearrange things to better support two point definitions.

--- Oghma/app.coffee
+++ Oghma/app.coffee
@@ -112,6 +112,9 @@
   # Grid services.  See Oghma.Grid.
   grid: null

+  # TwoPoint services.  See Oghma.TwoPoint.
+  twopoint: null
+
   # Constructor
   #
   # Sets up server connections for {Heron.Comet} and {Heron.Dictionary} and
@@ -159,6 +162,28 @@
         @verbose( 'Connected to server.' )
         @login_phase()
     )
+
+    # Viewport and Kinetic Panel
+    @viewport = Ext.create(
+      'Ext.container.Viewport',
+      layout: 'border'
+    )
+    @table = Ext.create( 'Oghma.Ext.Table',
+      region: 'center'
+    )
+    @table.onZoom.add( ( zoom ) => @me()?.set( zoom: zoom ) )
+
+    @viewport.add( @table )
+    @stage = @table.stage
+
+    for layer_name in @layer_order
+      @layer[ layer_name ] = new Kinetic.Layer()
+      @zindex[ layer_name ] = new Oghma.ZIndex()
+      @stage.add( @layer[ layer_name ] )
+
+    # Services
+    @grid = new Oghma.Grid( this )
+    @twopoint = new Oghma.TwoPoint( this, @layer.controls )

     # Thingyverses
     @allverse = new Heron.Thingyverse()
@@ -184,9 +209,6 @@
       @join_table( initial_table )
     )

-    # Services
-    @grid = new Oghma.Grid( this )
-
     # Allow right clicks to hit nodes.
     window.oncontextmenu = ( event ) ->
         event.preventDefault()
@@ -204,24 +226,6 @@

     # Action
     @action = new Oghma.Action( this )
-
-    # Viewport and Kinetic Panel
-    @viewport = Ext.create(
-      'Ext.container.Viewport',
-      layout: 'border'
-    )
-    @table = Ext.create( 'Oghma.Ext.Table',
-      region: 'center'
-    )
-    @table.onZoom.add( ( zoom ) => @me()?.set( zoom: zoom ) )
-
-    @viewport.add( @table )
-    @stage = @table.stage
-
-    for layer_name in @layer_order
-      @layer[ layer_name ] = new Kinetic.Layer()
-      @zindex[ layer_name ] = new Oghma.ZIndex()
-      @stage.add( @layer[ layer_name ] )

     # Set up keymap
     @keymap = Ext.create( 'Ext.util.KeyMap', target: Ext.getDoc() )


Fix minor cat issues

Fix minor cat issues

- Fix logic to check cats input
- Missing catsafe toString call

--- scripts/cats.coffee
+++ scripts/cats.coffee
@@ -30,19 +30,19 @@
     catsafe = res.match[1]
     greyhound = res.match[2]

-    if catsafe isnt 'yes' or isnt 'no'
+    if catsafe not in ['yes', 'no']
       res.reply "I'm not sure what 'cats #{catsafe} #{greyhound}' means 😕\n" +
                 "Please use 'cats yes #{greyhound}' or 'cats no #{greyhound}'"
       return
+    catsafe = catsafe is 'yes'

-    catsafe = catsafe is 'yes'
     message = "#{capitalize(greyhound)} is #{toString(catsafe)}"
     branch = "cats-#{greyhound}"
     user =
       name: res.message.user?.real_name?,
       email: res.message.user?.profile?.email?

-    res.reply "Labeling #{capitalize(greyhound)} as #{catmsg}\n" +
+    res.reply "Labeling #{capitalize(greyhound)} as #{toString(catsafe)}\n" +
               "Hang on a sec..."
     git.pull (repo) ->
       git.branch repo, branch, (ref) ->


Better cat safe messages

Better cat safe messages

--- scripts/cats.coffee
+++ scripts/cats.coffee
@@ -16,10 +16,16 @@

 git = require './git'

+toString = (catsafe) ->
+  return if catsafe then "cat safe! 😸" else "not cat safe 😿"
+
 cats = (greyhound, catsafe, callback) ->
   git.loadGreyhounds (greyhounds) ->
     if greyhound not of greyhounds
       return callback "Sorry, couldn't find #{greyhound} 😕"
+
+    if greyhounds[greyhound].cats is catsafe
+      return callback "#{capitalize(greyhound)} is already #{toString(catsafe)}"

     greyhounds[greyhound].cats = catsafe
     git.dumpGreyhounds greyhounds, callback
@@ -29,17 +35,13 @@
     catsafe = res.match[1]
     greyhound = res.match[2]

-    if catsafe is 'yes'
-      catsafe = yes
-    else if catsafe is 'no'
-      catsafe = no
-    else
+    if catsafe isnt 'yes' or isnt 'no'
       res.reply "I'm not sure what 'cats #{catsafe} #{greyhound}' means 😕\n" +
                 "Please use 'cats yes #{greyhound}' or 'cats no #{greyhound}'"
       return

-    catmsg = if catsafe then "cat safe! 😸" else "not cat safe 😿"
-    message = "#{capitalize(greyhound)} is #{catmsg}"
+    catsafe = catsafe is 'yes'
+    message = "#{capitalize(greyhound)} is #{toString(catsafe)}"
     branch = "cats-#{greyhound}"
     user =
       name: res.message.user.real_name,


Docs - updates

Docs - updates

Added docs/api version 5 support
Changed documentation source site for version 3

--- scripts/docs.coffee
+++ scripts/docs.coffee
@@ -21,6 +21,7 @@
 module.exports = (robot) ->
   getQueryUrl = (doctype, version, query) ->
     version = 3 if version?.match /^3(.*)/i
+    version = 'master' if version == '5'

     if doctype == 'api'
       if version == 3
@@ -33,7 +34,9 @@
       "site:php.net/manual/en #{query}"
     else
       if version == 3
-        "site:three.laravel.com/docs #{query}"
+        "site:laravel3.veliovgroup.com/docs #{query}"
+      else if version == 'master'
+        "site:laravel.com/docs/#{version} #{query}"
       else
         "site:laravel.com/docs #{query}"


Wording

Wording

--- files.coffee
+++ files.coffee
@@ -643,7 +643,7 @@
       check @responseHeaders, Match.OneOf Object, Function

       @_preCollection = new Mongo.Collection '__pre_' + @collectionName
-      @_preCollection._ensureIndex {'createdAt': 1}, {expireAfterSeconds: @continueUploadTTL, background: true}
+      @_preCollection._ensureIndex {createdAt: 1}, {expireAfterSeconds: @continueUploadTTL, background: true}
       _preCollectionCursor = @_preCollection.find {}
       _preCollectionCursor.observe removed: (doc) ->
         # Free memory after upload is done
@@ -1059,7 +1059,7 @@
   @locus Server
   @memberOf FilesCollection
   @name _finishUpload
-  @summary Internal method. Finish upload, close Writable stream, add recored to MongoDB and flush used memory
+  @summary Internal method. Finish upload, close Writable stream, add record to MongoDB and flush used memory
   @returns {undefined}
   ###
   _finishUpload: if Meteor.isServer then (result, opts, cb) ->


Add missing indentation mode 'Cramer' to floskell.coffee

--- contrib/floskell.coffee
+++ contrib/floskell.coffee
@@ -57,6 +57,8 @@
           prettifyFile 'johan-tibell', target.getModel()
         'floskell:prettify-gibiansky': ({target}) =>
           prettifyFile 'gibiansky', target.getModel()
+        'floskell:prettify-cramer': ({target}) =>
+          prettifyFile 'cramer', target.getModel()

     @menu.add atom.menu.add [
       label: 'floskell'
@@ -65,6 +67,7 @@
         {label: 'Chris Done', command: 'floskell:prettify-chris-done'}
         {label: 'Johan Tibell', command: 'floskell:prettify-johan-tibell'}
         {label: 'Gibiansky', command: 'floskell:prettify-gibiansky'}
+        {label: 'Cramer', command: 'floskell:prettify-cramer'}
       ]
     ]


feat: (#866) Add status code to test id

feat: (#866) Add status code to test id

--- src/transaction-runner.coffee
+++ src/transaction-runner.coffee
@@ -324,7 +324,7 @@

     configuredTransaction =
       name: transaction.name
-      id: request.method + ' ' + request.uri
+      id: request.method + ' (' + expected.statusCode + ') ' + request.uri
       host: @parsedUrl.hostname
       port: @parsedUrl.port
       request: request


js state machine

js state machine

--- src/State.coffee
+++ src/State.coffee
@@ -217,6 +217,7 @@
   return I
 ###

+###
 StateCollection = ->
   states = []

@@ -231,6 +232,7 @@
         }`

     all: -> states
+###

 Transition = (machine, event, from, to, params) ->
   self =


create tmp files within the db directory

--- src/Store.coffee
+++ src/Store.coffee
@@ -37,7 +37,7 @@
     json = JSON.stringify o, null, indent
   catch e
     return if cb? then cb e else e
-  tmpFileName = "#{uuid.v4()}.tmp"
+  tmpFileName = "#{@_getFileName uuid.v4()}.tmp"
   if cb?
     fs.writeFile tmpFileName, json, 'utf8', (err) ->
       return cb err if err
@@ -110,9 +110,9 @@
     if @_memory then done()
     else
       if cb?
-        saveObjectToFile @_cache, file, done
+        saveObjectToFile.call @, @_cache, file, done
       else
-        err = (o = saveObjectToFile @_cache, file) instanceof Error
+        err = (o = saveObjectToFile.call @, @_cache, file) instanceof Error
         done err, o
   else
     done = (err) =>
@@ -156,7 +156,7 @@
       @_cache = @allSync()

   _getFileName: (id) ->
-    if @_single then "#{@_dir}/#{path.basename @name}.json"
+    if @_single then path.join @_dir, (path.basename @name) + ".json"
     else id2fileName id, @_dir

   save: (id, o, cb=->) -> save.call @, id, o, cb


Add explaining comment

Add explaining comment

--- src/views/search-input.coffee
+++ src/views/search-input.coffee
@@ -19,6 +19,8 @@
         initialize: (@model, @searchResults) ->
             @listenTo @searchResults, 'ready', @adaptToQuery
             @listenTo @searchResults, 'reset', =>
+                # This really clears the search query eg. when search is closed (but also causes
+                # search input to clear when there's no search results)
                 if @searchResults.isEmpty()
                     @setInputText ''
         adaptToQuery: (model, value, opts) ->


WIP refactoring to allow for dataflow analysis.

--- src/compiler.coffee
+++ src/compiler.coffee
@@ -52,7 +52,7 @@
     @stack = []
     @locals = []
     @next = []
-    @body = ""
+    @stmts = []
     @visited = false

   push: (values...) -> @stack.push.apply @stack, values
@@ -60,35 +60,51 @@
   pop: -> @stack.pop()
   pop2: -> @stack.pop(); @stack.pop()
   put_cl: (idx, v) ->
-    if v?.match? /l\d+/
-      @add_line "l#{idx} = #{v}"
-      v = "l#{idx}"
     @locals[idx] = v
   put_cl2: (idx, v) ->
-    if v?.match? /l\d+/
-      @add_line "l#{idx} = #{v}"
-      v = "l#{idx}"
     @locals[idx] = v
     @locals[idx+1] = null
   cl: (idx) -> @locals[idx]
-  add_line: (line) -> @body += line + ";\n"
+  add_stmt: (stmt) -> @stmts.push stmt
   new_temp: -> @block_chain.new_temp()

   compile_epilogue: ->
     # copy our stack / local values into appropriately-named vars so that they
     # can be accessed from other blocks
-    for s, i in @stack when s?
-      continue if s == "s#{i}"
-      @add_line "s#{i} = #{s}"
-      @stack[i] = "s#{i}"
-
-    for l, i in @locals when l?
-      continue if l == "l#{i}"
-      @add_line "l#{i} = #{l}"
-      @locals[i] = "l#{i}"
-
-  compile: (@stack = @stack, @locals = @locals) ->
+    rv = []
+    for blk_id in @next
+      block = @block_chain.get_block_from_instr blk_id
+      for s, i in @stack when s?
+        unless /^\$\d+$/.test s
+          temp = @new_temp()
+          rv.push new Move temp, s
+          @stack[i] = temp
+        rv.push new Move block.in_stack[i], @stack[i] if block.in_stack[i]?
+
+      for l, i in @locals when l?
+        unless /^\$\d+$/.test l
+          temp = @new_temp()
+          rv.push new Move temp, l
+          @locals[i] = temp
+        rv.push new Move block.in_locals[i], @locals[i] if block.in_locals[i]?
+    rv
+
+  compile: (prev_stack, prev_locals) ->
+    return if @visited
+
     @visited = true
+
+    # the first block has its local vars set from the function params
+    unless @start_idx == 0
+      @stack =
+        for s in prev_stack
+          if s? then @new_temp() else null
+      @locals =
+        for l in prev_locals
+          if l? then @new_temp() else null
+      # dup the stack / locals so the next pass can retrieve our input variables
+      @in_stack = @stack[..]
+      @in_locals = @locals[..]

     instr_idx = @start_idx
     for op in @opcodes
@@ -101,32 +117,41 @@
     # branching instructions will print the epilogue before they branch; return
     # instructions obviate the need for one
     unless op.offset? or (op.name.indexOf 'return') != -1
-      @compile_epilogue()
+      @add_stmt => @compile_epilogue()
+
+    for idx in @next
+      next_block = @block_chain.get_block_from_instr idx
+      # java bytecode verification ensures that the stack height and stack /
+      # local table types match up across blocks
+      next_block.compile @stack, @locals
+
+    linearized_stmts = ""
+    linearize = (arr) ->
+      for s in arr
+        if _.isFunction s
+          linearize s()
+        else
+          linearized_stmts += s + ";\n"
+    linearize @stmts

     @compiled_str =
       """
       case #{@start_idx}:
       // #{op.name for op in @opcodes}
-      #{@body}
+      #{linearized_stmts}
       """
-    for idx in @next
-      next_block = @block_chain.get_block_from_instr idx
-      unless next_block.visited
-        # java bytecode verification ensures that the stack height and stack /
-        # local table types match up across blocks
-        next_block.compile @stack[..], @locals[..]

 class Expr

   constructor: (str, subexps...) ->
-    @fragments = str.split /($\d+)/
+    @fragments = str.split /(\$\d+)/
     for frag, i in @fragments
-      if /$\d+/.test frag
+      if /\$\d+/.test frag
         @fragments[i] = subexps[parseInt frag[1..], 10]

   eval: (b) ->
     temp = b.new_temp()
-    b.add_line "$0 = #{@}", temp
+    b.add_stmt "$0 = #{@}", temp
     new Primitive b

   toString: -> @fragments.join ''
@@ -138,6 +163,12 @@
   eval: -> @

   toString: -> @str
+
+class Move
+
+  constructor: (@dest, @src) ->
+
+  toString: -> "#{@dest} = #{@src}"

 cmpMap =
   eq: '=='
@@ -173,7 +204,7 @@
       b.push val
   ArrayLoadOpcode: (b) ->
     temp = b.new_temp()
-    b.add_line """
+    b.add_stmt """
     var idx = #{b.pop()};
     var obj = rs.check_null(#{b.pop()});
     var array = obj.array;
@@ -195,15 +226,15 @@
           "#{cmpMap[cmpCode]} 0"
     b.next.push @offset + idx
     v = b.pop()
-    b.compile_epilogue()
-    b.add_line "if (#{v} #{cond}) { label = #{@offset + idx}; continue }"
+    b.add_stmt -> b.compile_epilogue()
+    b.add_stmt "if (#{v} #{cond}) { label = #{@offset + idx}; continue }"
   BinaryBranchOpcode: (b, idx) ->
     cmpCode = @name[7..]
     b.next.push @offset + idx
     v2 = b.pop()
     v1 = b.pop()
-    b.compile_epilogue()
-    b.add_line "if (#{v1} #{cmpMap[cmpCode]} #{v2}) { label = #{@offset + idx}; continue }"
+    b.add_stmt -> b.compile_epilogue()
+    b.add_stmt "if (#{v1} #{cmpMap[cmpCode]} #{v2}) { label = #{@offset + idx}; continue }"
   InvokeOpcode: (b, idx) ->
     method = new Method # kludge
     method.access_flags = { static: @name == 'invokestatic' }
@@ -226,17 +257,17 @@
     b.stack.length -= method.param_bytes

     virtual = @name in ['invokevirtual', 'invokeinterface']
-    b.add_line "rs.push(#{params.join ','})"
-    b.add_line "rs.method_lookup(#{JSON.stringify @method_spec}).run(rs, #{virtual})"
+    b.add_stmt "rs.push(#{params.join ','})"
+    b.add_stmt "rs.method_lookup(#{JSON.stringify @method_spec}).run(rs, #{virtual})"

     unless method.return_type.toString() is 'V'
       temp = b.new_temp()

       if method.return_type.toString() in ['D', 'J']
-        b.add_line "#{temp} = rs.pop2()"
+        b.add_stmt new Move "#{temp} = rs.pop2()"
         b.push2 temp
       else
-        b.add_line "#{temp} = rs.pop()"
+        b.add_stmt "#{temp} = rs.pop()"
         b.push temp

 compile_obj_handlers = {
@@ -256,14 +287,14 @@
   dconst_0: { compile: (b) -> b.push2 new Primitive "0"; }
   dconst_1: { compile: (b) -> b.push2 new Primitive "1"; }
   # the *astore commands don't work yet...
-  iastore: {compile: (b) -> b.add_line "b.check_null($2).array[$1]=$0",b.pop(),b.pop(),b.pop()}
-  lastore: {compile: (b) -> b.add_line "b.check_null($2).array[$1]=$0",b.pop(),b.pop(),b.pop2()}
-  fastore: {compile: (b) -> b.add_line "b.check_null($2).array[$1]=$0",b.pop(),b.pop(),b.pop()}
-  dastore: {compile: (b) -> b.add_line "b.check_null($2).array[$1]=$0",b.pop(),b.pop(),b.pop2()}
-  aastore: {compile: (b) -> b.add_line "b.check_null($2).array[$1]=$0",b.pop(),b.pop(),b.pop()}
-  bastore: {compile: (b) -> b.add_line "b.check_null($2).array[$1]=$0",b.pop(),b.pop(),b.pop()}
-  castore: {compile: (b) -> b.add_line "b.check_null($2).array[$1]=$0",b.pop(),b.pop(),b.pop()}
-  sastore: {compile: (b) -> b.add_line "b.check_null($2).array[$1]=$0",b.pop(),b.pop(),b.pop()}
+  iastore: {compile: (b) -> b.add_stmt "b.check_null($2).array[$1]=$0",b.pop(),b.pop(),b.pop()}
+  lastore: {compile: (b) -> b.add_stmt "b.check_null($2).array[$1]=$0",b.pop(),b.pop(),b.pop2()}
+  fastore: {compile: (b) -> b.add_stmt "b.check_null($2).array[$1]=$0",b.pop(),b.pop(),b.pop()}
+  dastore: {compile: (b) -> b.add_stmt "b.check_null($2).array[$1]=$0",b.pop(),b.pop(),b.pop2()}
+  aastore: {compile: (b) -> b.add_stmt "b.check_null($2).array[$1]=$0",b.pop(),b.pop(),b.pop()}
+  bastore: {compile: (b) -> b.add_stmt "b.check_null($2).array[$1]=$0",b.pop(),b.pop(),b.pop()}
+  castore: {compile: (b) -> b.add_stmt "b.check_null($2).array[$1]=$0",b.pop(),b.pop(),b.pop()}
+  sastore: {compile: (b) -> b.add_stmt "b.check_null($2).array[$1]=$0",b.pop(),b.pop(),b.pop()}
   pop: {compile: (b) -> b.pop()}
   pop2: {compile: (b) -> b.pop2()}
   # TODO: avoid duplicating non-primitive expressions so as to save on computation
@@ -316,39 +347,39 @@
   i2c: { compile: (b) -> b.push "#{b.pop()}&0xFFFF" }
   i2s: { compile: (b) -> b.push "util.truncate(#{b.pop()}, 16)" }

-  ireturn: { compile: (b) -> b.add_line "return #{b.pop()}" }
-  lreturn: { compile: (b) -> b.add_line "return #{b.pop2()}" }
-  freturn: { compile: (b) -> b.add_line "return #{b.pop()}" }
-  dreturn: { compile: (b) -> b.add_line "return #{b.pop2()}" }
-  areturn: { compile: (b) -> b.add_line "return #{b.pop()}" }
-  'return': { compile: (b) -> b.add_line "return" }
+  ireturn: { compile: (b) -> b.add_stmt "return #{b.pop()}" }
+  lreturn: { compile: (b) -> b.add_stmt "return #{b.pop2()}" }
+  freturn: { compile: (b) -> b.add_stmt "return #{b.pop()}" }
+  dreturn: { compile: (b) -> b.add_stmt "return #{b.pop2()}" }
+  areturn: { compile: (b) -> b.add_stmt "return #{b.pop()}" }
+  'return': { compile: (b) -> b.add_stmt "return" }

   arraylength: { compile: (b) ->
     t = b.new_temp()
-    b.add_line "#{t} = rs.check_null(#{b.pop()}).array.length"
+    b.add_stmt "#{t} = rs.check_null(#{b.pop()}).array.length"
     b.push t
   }

   getstatic: { compile: (b) ->
     temp = b.new_temp()
-    b.add_line "#{temp} = rs.static_get(#{JSON.stringify @field_spec})"
+    b.add_stmt "#{temp} = rs.static_get(#{JSON.stringify @field_spec})"
     if @field_spec.type in ['J','D'] then b.push2 temp else b.push temp }

   'new': { compile: (b) ->
     temp = b.new_temp()
-    b.add_line "#{temp} = rs.init_object(#{JSON.stringify @class})"
+    b.add_stmt "#{temp} = rs.init_object(#{JSON.stringify @class})"
     b.push temp }

   goto: { compile: (b, idx) ->
     b.next = [@offset + idx]
-    b.compile_epilogue()
-    b.add_line "label = #{@offset + idx}; continue"
+    b.add_stmt -> b.compile_epilogue()
+    b.add_stmt "label = #{@offset + idx}; continue"
   }

   goto_w: { compile: (b, idx) ->
     b.next = [@offset + idx]
-    b.compile_epilogue()
-    b.add_line "label = #{@offset + idx}; continue"
+    b.add_stmt -> b.compile_epilogue()
+    b.add_stmt "label = #{@offset + idx}; continue"
   }
 }

@@ -388,14 +419,6 @@
         #{name}: function(#{param_names.join ", "}) {
           var label = 0;
           #{if temps.length > 0 then "var #{temps.join ", "};" else ""}
-          #{if m.code.max_locals > 0
-              "var " + (("l#{i}" for i in [0...m.code.max_locals]).join ", ") + ";"
-            else
-              ""}
-          #{if m.code.max_stack > 0
-              "var " + (("s#{i}" for i in [0...m.code.max_stack]).join ", ") + ";"
-            else
-              ""}
           while (true) {
             switch (label) {
 #{(b.compiled_str for b in block_chain.blocks).join ""}


Votes are freedom

Votes are freedom

--- scripts/vote.coffee
+++ scripts/vote.coffee
@@ -127,7 +127,7 @@
     msg.reply if Vote.current then sure_am else nope

   # Election driver
-  robot.respond /vote ([^\s]+)\s?(.*)?$/, (msg) ->
+  robot.respond /(vote|freedom) ([^\s]+)\s?(.*)?$/, (msg) ->
     action = msg.match[1].trim().toLowerCase()
     arg = msg.match[2]?.trim()


consoleのデバッグログを消した

consoleのデバッグログを消した

--- send_message.coffee
+++ send_message.coffee
@@ -275,7 +275,7 @@
         when "chatwork"
           messages = encodeURIComponent "[info]#{messages}[/info]"
           uri      = "#{tg}?#{@msgLabel}=#{messages}"
-          console.log "URI: #{uri}"
+          # console.log "URI: #{uri}"
           request.post
             url: uri
             headers: @headers


litle refactoring

litle refactoring

--- ether.js.coffee
+++ ether.js.coffee
@@ -17,14 +17,21 @@
     founded

   @add_member: (klasses, functions) ->
+    @bash @add_to_instance, klasses, functions
+
+  @add_class_methods: (klasses, functions) ->
+    @bash @add_to_class, klasses, functions
+
+  @bash: (add_to_x, klasses, functions) ->
     for klass, def of klasses
       for fun, body of functions
-        window.A[klass].prototype[fun] = body
+        add_to_x klass, fun, body

-  @add_class_methods: (klasses, functions) ->
-    for klass, def of klasses
-      for fun, body of functions
-        window.A[klass].constructor.prototype[fun] = body
+  @add_to_instance: (klass, fun, body) ->
+    window.A[klass].prototype[fun] = body
+
+  @add_to_class: (klass, fun, body) ->
+    window.A[klass].constructor.prototype[fun] = body

 class @A.Ether
 @A.Ether = @Ether


Always assign to autoUpdater

Always assign to autoUpdater

--- src/browser/auto-update-manager.coffee
+++ src/browser/auto-update-manager.coffee
@@ -20,7 +20,7 @@
     process.nextTick => @setupAutoUpdater()

   setupAutoUpdater: ->
-    autoUpdater ?= require 'auto-updater'
+    autoUpdater = require 'auto-updater'

     if process.platform is 'win32'
       autoUpdater.checkForUpdates = => @checkForUpdatesShim()


fix(auto-updates): Point at nylas, don't think it's following redirects

--- src/browser/auto-update-manager.coffee
+++ src/browser/auto-update-manager.coffee
@@ -21,9 +21,9 @@
     if process.platform is 'win32'
       # Squirrel for Windows can't handle query params
       # https://github.com/Squirrel/Squirrel.Windows/issues/132
-      @feedUrl = 'https://edgehill.nilas.com/update-check'
+      @feedUrl = 'https://edgehill.nylas.com/update-check'
     else
-      @feedUrl = "https://edgehill.nilas.com/update-check?version=#{@version}"
+      @feedUrl = "https://edgehill.nylas.com/update-check?version=#{@version}"

     process.nextTick => @setupAutoUpdater()


Flip conditional around

Flip conditional around

--- src/library.coffee
+++ src/library.coffee
@@ -158,9 +158,9 @@
         names.push name if names.indexOf(name) is -1
         continue

-    # Skip sending components-changed if nothing changed
-    return unless names.length
-    @emit 'components-changed', names, @components
+    # Send components-changed only if something changed
+    if names.length
+      @emit 'components-changed', names, @components

   load: (callback) ->
     componentsFromDirectory @options.componentdir, @options.config, (err, components) =>


alters for embed styling

alters for embed styling

--- static/embed/oab_embed.coffee
+++ static/embed/oab_embed.coffee
@@ -36,13 +36,13 @@
   _L.each els, (el) ->
     if typeof html is 'string'
       el.innerHTML = (if append then el.innerHTML else '') + html
-    was = _L.get el, '_L_display'
-    was = 'block' if typeof was isnt 'string' or was is 'none' # TODO should be inline in which cases...
+    was = _L.get el, '_l_display'
+    was = (if el.tagName is 'DIV' then 'block' else 'inline') if typeof was isnt 'string' or was is 'none' # TODO should be inline in which cases...
     el.style.display = was
 _L.hide = (els) ->
   _L.each els, (el) ->
     if el.style.display isnt 'none'
-      _L.set el, '_L_display', el.style.display
+      _L.set el, '_l_display', el.style.display
     el.style.display = 'none'
 _L.get = (els, attr) ->
   res = undefined
@@ -198,7 +198,6 @@
   this.f ?= {} # the result of the find/ill/permission request to the backend
   this.template ?= _oab[this.plugin + '_template'] # template or css can be passed in or are as defined below

-  this.bootstrap ?= true
   this.css ?= _oab.css
   this._loading = false # tracks when loads are occurring
   this.submit_after_metadata = false # used by instantill to track if metadata has been provided by user
@@ -224,7 +223,6 @@

   if not this.config.autorun # stupidly, true means don't run it...
     ap = if typeof this.config.autorunparams is 'string' then this.config.autorunparams.split(',') else if typeof this.config.autorunparams is 'object' then this.config.autorunparams else ['doi','title','url','atitle','rft_id','journal','issn','year','author']
-    ap.push('confirmed') if 'confirmed' not in ap # confirmed here is extra to the ill ones, used to confirm the file has been confirmed
     ap = ap.replace(/"/g,'').replace(/'/g,'').split(',') if typeof ap is 'string'
     for o in ap
       o = o.split('=')[0].trim()
@@ -233,6 +231,8 @@
   if window.location.search.indexOf('email=') isnt -1
     this.data.email = window.location.search.split('email=')[1].split('&')[0].split('#')[0]
     _L.remove '#_oab_collect_email'
+  if window.location.search.indexOf('confirmed=') isnt -1
+    this.data.confirmed = window.location.search.split('confirmed=')[1].split('&')[0].split('#')[0]
   if window.location.search.indexOf('panel=') isnt -1
     this.panel window.location.search.split('panel=')[1].split('&')[0].split('#')[0]
   if window.location.search.indexOf('section=') isnt -1
@@ -297,7 +297,8 @@
         this.restart()

 _oab.prototype.restart = (e, val) ->
-  try e.preventDefault()
+  if e.target.id isnt'_oab_permissionemail'
+    try e.preventDefault()
   this.data = {}
   this.f = {}
   this.loading false
@@ -489,7 +490,7 @@
     if fl? and fl.files? and fl.files.length
       this.file = new FormData()
       this.file.append 'file', fl.files[0]
-    else if this.f?.url? or this.f?.permissions?.permissions?.archiving_allowed
+    else if this.file isnt true # can be set to true when dark deposit is being followed - no file required. Or a demo may set it to true
       _L.show '#_oab_error', '
Whoops, you need to give us a file! Check it\'s uploaded.
'
       _L.css '#_oab_file', 'border-color','#f04717'
       return
@@ -503,7 +504,7 @@
     data.redeposit = this.f.url if typeof this.f?.url is 'string'
     data.pilot = this.config.pilot if this.config.pilot
     data.live = this.config.live if this.config.live
-    if this.file
+    if typeof this.file isnt 'boolean'
       for d of data
         if d is 'metadata'
           for md of data[d]
@@ -517,7 +518,7 @@
       data
       (res) =>
         this.loading false
-        if this.file
+        if typeof this.file isnt 'boolean'
           if res.zenodo?.already or (this.data.confirmed and not res.zenodo?.url) #or not this.f?.permissions?.file?.archivable
             this.done 'check'
           else if res.error
@@ -606,6 +607,7 @@
         _L.hide '._oab_get_email'
         _L.show '._oab_oa'
       else
+        this.file = true # no file required for oa deposit...
         _L.show '._oab_oa_deposit'
     else if this.f?.permissions?.permissions?.archiving_allowed
       # can be shared, depending on permissions info
@@ -637,6 +639,7 @@
       _L.show '._oab_permission_required'
     else
       # can't be directly shared but can be passed to library for dark deposit
+      this.file = true
       _L.hide '#_oab_file'
       _L.show '._oab_dark_deposit'

@@ -1017,7 +1020,7 @@

All that\'s left to do is wait. Once the journal gives you permission to share, come back and we\'ll help you finish the job.

-

Do another

+
Do another

'
@@ -1026,7 +1029,6 @@
 # or key can be a user ID string and val must be empty, or key and val can both be empty and config will attempt
 # to be retrieved from setup, or localstorage and/or from the API if a user ID is available from setup
 _oab.prototype.configure = (key, val, build, preview) ->
-  console.log preview
   if typeof key is 'string' and not val? and key.startsWith '{'
     try key = JSON.parse key
   if typeof key is 'string' and not val? and (not this.uid? or this.uid is 'anonymous')
@@ -1074,6 +1076,7 @@
       # if bootstrap css is already present on the page, and bootstrap value is not set,
       # and css has not been set either, then use bootstrap
       _L.append this.element, '

'
+      console.log _L.gebi('#_oab_bootstrap_test').offsetHeight
       this.bootstrap = _L.gebi('#_oab_bootstrap_test').offsetHeight isnt 0
       _L.remove '_#oab_bootstrap_test'
     if this.bootstrap is true


Removed trialStarted field

Removed trialStarted field

It isn't used anywhere else, and it has the same value and meaning as
"created" which has a much clearer name. It's just confusing, especially
now we have "planExpires".

--- server/code/model/user.coffee
+++ server/code/model/user.coffee
@@ -27,7 +27,6 @@
   accountLevel: String
   planExpires: Date
   recurlyAccount: {type: String, unique: true}
-  trialStarted: {type: Date, default: Date.now}
   acceptedTerms: Number # a value of 0 or null means you need to accept terms on next login
   created: {type: Date, default: Date.now}
   logoUrl: String


Fix watchlist privacy dropdown sync bug.

--- app/assets/javascripts/library.js.coffee
+++ app/assets/javascripts/library.js.coffee
@@ -278,6 +278,7 @@
               model: @model
             @dropdown.find(".rating").replaceWith ratingView.render().el
             @dropdown.find(".status-change-widget").append statusChangeWidget.render().el
+            @dropdown.find("option[value=" + (if @model.get("private") then "private" else "public") + "]").prop("selected", true)
             # Submit updates when needed.
             @dropdown.find("form.custom").submit -> that.submitDropdownForm()
             @dropdown.find("form.custom").change -> that.submitDropdownForm()


Ensure view json is defined

Ensure view json is defined

Signed-off-by: Byron Ruth <e9d71f5ee7c92d6dc9e92ffdad17b8bd49418f98@devel.io>

--- src/coffee/cilantro/models/view.coffee
+++ src/coffee/cilantro/models/view.coffee
@@ -59,7 +59,7 @@
             @jsonToFacets(attrs.json)
             return attrs

-        jsonToFacets: (json) ->
+        jsonToFacets: (json={}) ->
             # Implies this is an array of object, set directly. This is for
             # forwards compatibility.
             if _.isArray(json)


Add categories handling

Add categories handling

--- src/coffee/import/productimport.coffee
+++ src/coffee/import/productimport.coffee
@@ -24,15 +24,37 @@
     @failCounter = 0
     @success = false

-
   ###
-  Reads given products XML file and creates new products in Sphere.
+  Reads given import XML files and creates/updates product types, categories and products in Sphere.
+  Detailed steps:
+  1) Fetch product type, categories and load import XML files
+
+  2) Build product type updates (creates missing manufacturers)
+  2.1) Send product type updates
+
+  3) Build category creates (new categories only)
+  3.1) send category creates
+
+  4) Fetch categories (get fresh list after category creates)
+
+  5) Build category updates (updates for existing/old categories plus sets parent child relations on all categories)
+  5.1) Send category updates
+
+  6) Build product creates
+  6.1) Send product creates
+
+  7) TODO: Build product updates
+  7.1) TODO: Send product updates
+
+  8) TODO: Build category deletes
+  8.1) TODO: Send category deletes
+
   @param {function} callback The callback function to be invoked when the method finished its work.
   @return Result of the given callback
   ###
   execute: (callback) ->
     @startTime = new Date().getTime()
-    @logger.info 'ProductImport started...'
+    @logger.info 'ProductImport execution started.'

     Q.spread [
       @_loadMapping @_options.mapping
@@ -40,27 +62,42 @@
       @_loadManufacturersXML @_options.manufacturers
       @_loadCategoriesXML @_options.categories
       @_fetchProductTypes()
+      @_fetchCategories()
       ],
-      (mappingData, productsData, manufacturersData, categoriesData, productTypesData) =>
-        @logger.debug "mappingData:\n#{mappingData}"
-        @logger.debug "productsData:\n#{utils.pretty productsData}"
-        @logger.debug "productTypesData:\n#{utils.pretty productTypesData}"
-        @mapping = JSON.parse mappingData
-        @toBeImported = _.size(productsData.Products?.Product)
-        productType = @_getProductType productTypesData
-        manufacturers = @_processManufacturers(manufacturersData, productType) if manufacturersData
-        @_updateProductType(productType, manufacturers) if manufacturers
-        @_updateCategories categoriesData if categoriesData
-        products = @_buildProductsData(productsData, productType)
-        @logger.info "[Products] About to create '#{_.size products}' products."
-        @_batch _.map(products, (p) => @_createProduct p), 100
+      (mapping, productsXML, manufacturersXML, categoriesXML, fetchedProductTypes, fetchedCategories) =>
+        @logger.debug "mapping:\n#{mapping}"
+        @logger.debug "productsXML:\n#{utils.pretty productsXML}"
+        @logger.debug "fetchedProductTypes:\n#{utils.pretty fetchedProductTypes}"
+        @mapping = JSON.parse mapping
+        @productsXML = productsXML
+        @categoriesXML = categoriesXML
+        @fetchedCategories = @_transformByCategoryExternalId fetchedCategories.results
+        @toBeImported = _.size(productsXML.Products?.Product)
+        @productType = @_getProductType fetchedProductTypes
+        manufacturers = @_buildManufacturers(manufacturersXML, @productType) if manufacturersXML
+        @_updateProductType(@productType, manufacturers) if manufacturers
+    .then (productTypeUpdateResult) =>
+      @categories = @_buildCategories(@categoriesXML) if @categoriesXML
+      creates = @_buildCategoryCreates(@categories, @fetchedCategories) if @categories
+      @_batch(_.map(creates, (c) => @_createCategory c), 100) if creates
+    .then (createCategoriesResult) =>
+      # fetch created categories to get id's used for parent reference creation
+      @_fetchCategories()
+    .then (fetchedCategories) =>
+      @fetchedCategories = @_transformByCategoryExternalId fetchedCategories.result
+      updates = @_buildCategoryUpdates(@categories, @fetchedCategories) if @categories
+      @_batch(_.map(updates, (c) => @_updateCategory c), 100) if updates
+    .then (updateCategoriesResult) =>
+      @logger.info '[Products] Products import started...'
+      products = @_buildProducts(@productsXML, @productType, @fetchedCategories)
+      @logger.info "[Products] About to create '#{_.size products}' products."
+      @_batch(_.map(products, (p) => @_createProduct p), 100) if products
     .fail (error) =>
       @logger.error "Error on execute method; #{error}"
       @logger.error "Error stack: #{error.stack}" if error.stack
       @_processResult(callback)
     .done (result) =>
       @_processResult(callback)
-

   _processResult: (callback) ->
     endTime = new Date().getTime()
@@ -82,15 +119,15 @@
   _loadCategoriesXML: (path) ->
     utils.xmlToJson(path) if path

-  _processManufacturers: (manufacturersData, productType) ->
-    @logger.info '[Manufacturers] Update manufacturers started...'
-    @logger.debug "[Manufacturers] manufacturersData:\n#{utils.pretty manufacturersData}"
+  _buildManufacturers: (data, productType) ->
+    @logger.info '[Manufacturers] Manufacturers import started...'
+    @logger.debug "[Manufacturers] data:\n#{utils.pretty data}"

     if not @mapping.productMapping.ManufacturerId
-      @logger.info '[Manufacturers] Mmapping for ManufacturerId is not defined. No manufacturers will be created.'
+      @logger.info '[Manufacturers] Mapping for ManufacturerId is not defined. No manufacturers will be created.'
       return

-    count = manufacturersData.Manufacturers?['$'].count
+    count = _.size(data.Manufacturers?.Manufacturer)
     if count
       @logger.info "[Manufacturers] Manufacturers found: '#{count}'"
     else
@@ -98,17 +135,19 @@
       return

     attributeName = @mapping.productMapping.ManufacturerId.to
+    # find attribute on product type
     matchedAttr = _.find productType.attributes, (value) -> value.name is attributeName
     if not matchedAttr
       throw new Error "Error on manufacturers import. ManufacturerId mapping attributeName: '#{attributeName}' could not be found on product type with id: '#{productType.id}'"

     actions = []
+    # extract only keys from product type attribute values
     keys = _.pluck(matchedAttr.type.values, 'key')
-    _.each manufacturersData.Manufacturers.Manufacturer, (m) =>
+    _.each data.Manufacturers.Manufacturer, (m) =>
       key = m.ManufacturerId[0]
       exists = _.contains(keys,  key)
       if not exists
-        # create new addLocalizedEnumValue action
+        # attribute value does not exist yet -> create new addLocalizedEnumValue action
         value = @_getLocalizedValue(m.Translations[0], 'Name')
         action =
           action: 'addLocalizedEnumValue'
@@ -124,11 +163,171 @@
         version: productType.version
         actions: actions
     else
-      @logger.info "[Manufacturers] No update action for attribute '#{attributeName}' required.'#{_.size actions}'"
-
-  _updateCategories: (categoriesData) ->
-    @logger.info '[Categories] Update categories started...'
-    @logger.debug "[Categories] categoriesData:\n#{utils.pretty categoriesData}"
+      @logger.info "[Manufacturers] No update action for manufacturers attribute '#{attributeName}' required."
+
+  _buildCategories: (data) ->
+    @logger.info '[Categories] Categories import started...'
+    @logger.debug "[Categories] data:\n#{utils.pretty data}"
+    count = _.size(data.Categories?.Category)
+    if count
+      @logger.info "[Categories] Categories found: '#{count}'"
+    else
+      @logger.info '[Categories] No categories found or undefined. Please check manufacturers input XML.'
+      return
+    categoriesData = data.Categories.Category
+    # get root categories only
+    rootsData = _.filter categoriesData, (c) -> c.ParentId is undefined
+    # get subcategories only
+    subcategoriesData = _.reject categoriesData, (c) -> c.ParentId is undefined
+    # create a list of categories with data prepared for category creation
+    roots = @_getCategoryKeyValuePairs(rootsData)
+    list = [roots]
+    # recursively build categories data
+    @_createSubcategories(roots, subcategoriesData, list)
+    categories = _.flatten list
+    @logger.debug "[Categories] Roots count: '#{_.size roots}'"
+    @logger.info "[Categories] Import candidates count: '#{_.size categories}'"
+    @logger.debug "[Categories] Import candidates data: '#{utils.pretty categories}'"
+
+    categories
+
+  # TODO: do not use slug as external category id (required Sphere support of custom attributes on category)
+  _buildCategoryCreates: (data, fetchedCategories) =>
+    creates = []
+    _.each data, (c) ->
+      exists = _.has(fetchedCategories, c.id)
+      # we are interested in new categories only
+      if not exists
+        name = c.name
+        slug = c.slug
+        # set category external id (over slug as workaround)
+        slug.nl = c.id
+        create =
+          name: name
+          slug: slug
+        creates.push create
+
+    count = _.size(creates)
+    if count > 0
+      @logger.info "[Categories] Create count: '#{count}'"
+      creates
+    else
+      @logger.info "[Categories] No category create required."
+      null
+
+  _buildCategoryUpdates: (data, fetchedCategories) ->
+    updates = []
+    _.each data, (c) =>
+      actions = []
+      newName = c.name
+      oldCategory = @_getCategoryByExternalId(c.id, fetchedCategories)
+      oldName = oldCategory.name
+
+      # check if category name changed
+      if not _.isEqual(newName, oldName)
+        action =
+          action: "changeName"
+          name: newName
+        actions.push action
+
+      parentId = c.parentId
+      parentCategory = @_getCategoryByExternalId(parentId, fetchedCategories) if parentId
+      # check if category need to be assigned to parent
+      if parentCategory
+        action =
+          action: "changeParent"
+          parent:
+            typeId: "category"
+            id: parentCategory.id
+        actions.push action
+
+      if _.size(actions) > 0
+        wrapper =
+          id: oldCategory.id
+          actions:
+            version: oldCategory.version
+            actions: actions
+        updates.push wrapper
+
+    count = _.size(updates)
+    if count > 0
+      @logger.info "[Categories] Update count: '#{count}'"
+      updates
+    else
+      @logger.info "[Categories] No category update required."
+      null
+
+  _transformByCategoryExternalId: (fetchedCategories) =>
+    fetchedCats = {}
+    _.each fetchedCategories, (el) ->
+      externalId = el.slug.nl
+      throw new Error "[Categories] Slug for language 'nl' (workaround: used as 'externalId') in MC is empty; Category id: '#{el.id}'" if not externalId
+      fetchedCats[externalId] = el
+    fetchedCats
+
+  _getCategoryByExternalId: (id, fetchedCats) ->
+    category = fetchedCats[id]
+    throw new Error "[Categories] Unexpected error. Parent category with externalId: '#{id}' not found." if not category
+    category
+
+  _createSubcategories: (categories, subcategoriesData, list) ->
+    _.each categories, (category) =>
+      categoryId = category.id
+      subcategories = _.filter subcategoriesData, (c) -> c.ParentId[0] is categoryId
+      if _.size(subcategories) > 0
+        subs = @_getCategoryKeyValuePairs(subcategories, categoryId)
+        list.push subs
+        @_createSubcategories(subs, subcategoriesData, list)
+
+  _getCategoryKeyValuePairs: (categories, parentId) ->
+    container = []
+    _.each categories, (c) =>
+      names = @_getLocalizedValue(c.Translations[0], 'Name')
+      slugs = @_getLocalizedSlugs(names)
+      category =
+        id: c.CategoryId[0]
+        name: names
+        slug: slugs
+      category.parentId = parentId if parentId
+      container.push category
+    container
+
+  _fetchCategories: ->
+    deferred = Q.defer()
+    @rest.GET '/categories', (error, response, body) ->
+      if error
+        deferred.reject error
+      else
+        deferred.resolve body
+    deferred.promise
+
+  _createCategory: (payload) ->
+    deferred = Q.defer()
+    @rest.POST '/categories', payload, (error, response, body) ->
+      if error
+        deferred.reject "HTTP error on new category creation; Error: #{error}; Request body: \n #{utils.pretty payload} \n\n Response body: '#{utils.pretty body}'"
+      else
+        if response.statusCode isnt 201
+          message = "Error on new category creation; Request body: \n #{utils.pretty payload} \n\n Response body: '#{utils.pretty body}'"
+          deferred.reject message
+        else
+          message = 'New category created.'
+          deferred.resolve message
+    deferred.promise
+
+  _updateCategory: (payload) ->
+    deferred = Q.defer()
+    @rest.POST "/categories/#{payload.id}", payload.actions, (error, response, body) ->
+      if error
+        deferred.reject "HTTP error on category update; Error: #{error}; Request body: \n #{utils.pretty payload} \n\n Response body: '#{utils.pretty body}'"
+      else
+        if response.statusCode isnt 200
+          message = "Error on category type update; Request body: \n #{utils.pretty payload} \n\n Response body: '#{utils.pretty body}'"
+          deferred.reject message
+        else
+          message = 'Category type updated.'
+          deferred.resolve message
+    deferred.promise

   _fetchProductTypes: ->
     deferred = Q.defer()
@@ -145,7 +344,7 @@
       if error
         deferred.reject "HTTP error on product type update; Error: #{error}; Request body: \n #{utils.pretty payload} \n\n Response body: '#{utils.pretty body}'"
       else
-        if response.statusCode isnt 201
+        if response.statusCode isnt 200
           message = "Error on product type update; Request body: \n #{utils.pretty payload} \n\n Response body: '#{utils.pretty body}'"
           deferred.reject message
         else
@@ -170,20 +369,26 @@
           deferred.resolve message
     deferred.promise

-  _batch: (list, numberOfParallelRequest) =>
-    current = _.take list, numberOfParallelRequest
-    Q.all(current)
-    .then (result) =>
-      if _.size(current) < numberOfParallelRequest
-        @success = true
-      else
-        @_batch _.tail(list, numberOfParallelRequest), numberOfParallelRequest
-    .fail (error) =>
-      @logger.error "Error batch processing; #{error}"
-      @logger.error "Error stack: #{error.stack}" if error.stack
-      @success = false
-
-  _buildProductsData: (data, productType) =>
+  _batch: (list, numberOfParallelRequest) ->
+    deferred = Q.defer()
+    doBatch = (list, numberOfParallelRequest) ->
+      current = _.take list, numberOfParallelRequest
+      Q.all(current)
+      .then (result) ->
+        if _.size(current) < numberOfParallelRequest
+          # @success = true
+          deferred.resolve true
+        else
+          doBatch _.tail(list, numberOfParallelRequest), numberOfParallelRequest
+      .fail (error) ->
+        # @logger.error "Error batch processing; #{error}"
+        # @logger.error "Error stack: #{error.stack}" if error.stack
+        # @success = false
+        deferred.reject error
+     doBatch(list, numberOfParallelRequest)
+     deferred.promise
+
+  _buildProducts: (data, productType, fetchedCategories) =>
     products = []
     _.each data.Products?.Product, (p) =>
       names = @_getLocalizedValue(p.Descriptions[0], 'Title')
@@ -200,6 +405,9 @@
         description: descriptions
         variants: []

+      # assign categories
+      @_processCategories(product, p)
+
       variantBase =
         attributes: []
       # exclude product attributes which has to be handled differently
@@ -250,6 +458,20 @@
       @_processImages(item, product, variant)

     variant
+
+  _processCategories: (product, data) ->
+    catReferences = []
+    _.each data.Categories?.Category, (c) =>
+      categoryId = c.CategoryId[0]
+      existingCategory = @_getCategoryByExternalId(categoryId, fetchedCategories)
+      id = existingCategory.id
+      reference =
+        typeId: "category"
+        id: id
+      catReferences.push reference
+
+    if _.size(catReferences) > 0
+      product.categories = catReferences

   _processImages: (item, product, variant) ->
     _.each item, (value, key) =>
@@ -337,8 +559,8 @@
       slugs[key] = slug
     slugs

-  _getProductType: (productTypesObj) ->
-    productTypes = productTypesObj.results
+  _getProductType: (fetchedProductTypes) ->
+    productTypes = fetchedProductTypes.results
     if _.size(productTypes) == 0
       throw new Error "No product type defined for SPHERE project '#{@_options.config.project_key}'. Please create one before running product import."


Better defaults for CoffeeConsole settings.

--- src/coffeeconsole/coffeeconsole.coffee
+++ src/coffeeconsole/coffeeconsole.coffee
@@ -22,8 +22,8 @@

 class CoffeeREPL
     DEFAULT_SETTINGS =
-        lastVariable: '$_'
-        maxLines: 500
+        lastVariable: '_'
+        maxLines: 10000
         maxDepth: 2
         showHidden: false
         colorize: true


Add pjax to schemes.js.coffee

Add pjax to schemes.js.coffee

--- app/assets/javascripts/schemes.js.coffee
+++ app/assets/javascripts/schemes.js.coffee
@@ -17,26 +17,41 @@

 selectedClass = null

-jQuery ->
-
-  for klass, section of SECTIONS
-    do (klass, section) ->
-      $(klass).click (event) ->
-        event.stopPropagation()
-        selectedClass = klass
-        $('#section_name').text(section)
-
+mainFunction = ->
   Raphael ->
-
     cp = $('#colorpicker')
     offset = cp.offset()

     cw = Raphael.colorwheel(offset.left, offset.top, 320, '#ccc', cp.get(0))

+    for klass, section of SECTIONS
+      do (klass, section) ->
+        $(klass).click (event) ->
+          event.stopPropagation()
+
+          selectedClass = klass
+          $('#section_name').text(section)
+
+          if klass == '#code' or klass == '.cl'
+            classColor = $(klass).css('background-color')
+          else
+            classColor = $(klass).css('color')
+
+          $('#section_name').css('color', classColor)
+          cw.color(classColor)
+
     onchange = ->
-      (clr) ->
-        clr = Raphael.color(clr)
-        $(selectedClass).css('color', clr)
-        $('#section_name').css('color', clr)
+      (color) ->
+        color = Raphael.color(color)
+        if selectedClass == '#code' or selectedClass == '.cl'
+          $(selectedClass).css('background-color', color)
+        else
+          $(selectedClass).css('color', color)
+        $('#section_name').css('color', color)

     cw.onchange = onchange()
+
+jQuery ->
+  mainFunction()
+  $('#main a').pjax('[data-pjax-container]')
+  $('body').bind 'pjax:end', mainFunction


Time distance: cast dates to Date object

--- app/assets/javascripts/sx.i18n.js.coffee
+++ app/assets/javascripts/sx.i18n.js.coffee
@@ -3,6 +3,9 @@
 Sx.I18n =
   # ported from rails's date helper distance_of_time_in_words
   timeDistanceInWords: (fromTime, toTime = new Date()) ->
+    fromTime = new Date(fromTime)
+    toTime   = new Date(toTime)
+
     distanceInMinutes = Math.round(Math.abs((toTime - fromTime) / 60 / 1000))

     if (distanceInMinutes >= 0 && distanceInMinutes <= 1)


simplify range

simplify range

--- src/diabetes.coffee
+++ src/diabetes.coffee
@@ -35,9 +35,8 @@
   range = ///
     ^              # anchor to the beginning of the string
     (              # begin a capture group
-    \d{2,3}        # numbers that fit mg/dL
-    |              # or
-    \d{1,2}(\.\d)? # numbers that fit mmol/L
+    \d{1,3}        # one to three digit numbers
+    (\.\d)?        # optional tenths place
     )              # end a capture group
     $              # anchor to the end of the string
     ///


fix submit/save buttons on desktop

fix submit/save buttons on desktop

--- app/assets/javascripts/components/questionnaire.cjsx
+++ app/assets/javascripts/components/questionnaire.cjsx
@@ -35,12 +35,14 @@

   renderButtons: =>
     unless @state.mode == "show"
-

-        <SaveButton/> {@renderSubmitButton()}
-

-

-        <SaveButton/> {@renderSubmitButton()}
-

+      [
+

+          <SaveButton/> {@renderSubmitButton()}
+
,
+

+          <SaveButton/> {@renderSubmitButton()}
+

+      ]

   renderSubmitButton: ->
     <SubmitButton enabled={QuestionnaireStore.requiredQuestionsAnswered()}/>


Added contact us controller (index is the default action)

--- config/routes.coffee
+++ config/routes.coffee
@@ -1,26 +1,30 @@
-module.exports =
-	"who-we-are":
-		title: "Who We Are"
-		subpages:
-			"what-is-enactus":
-				title: "What is Enactus?"
-			"enactus-lse":
-				title: "Enactus @ LSE"
-			"business-advisory-board":
-				title: "Business Advisory Board"
-	"what-we-do":
-		title: "What We Do"
-		subpages:
-			"social-projects":
-				title: "Social Projects"
-			"commercial-projects":
-				title: "Commercial Projects"
-			"start-a-new-project":
-				title: "Start a New Project"
-	"what-is-new":
-		title: "What Is New"
-		subpages:
+routes =
+	'who-we-are':
+		title: 'Who We Are'
+		actions:
+			'what-is-enactus':
+				title: 'What is Enactus?'
+			'enactus-lse':
+				title: 'Enactus @ LSE'
+			'business-advisory-board':
+				title: 'Business Advisory Board'
+	'what-we-do':
+		title: 'What We Do'
+		actions:
+			'social-projects':
+				title: 'Social Projects'
+			'commercial-projects':
+				title: 'Commercial Projects'
+			'start-a-new-project':
+				title: 'Start a New Project'
+	'what-is-new':
+		title: 'What Is New'
+		actions:
 			blog:
-				title: "Blog"
+				title: 'Blog'
 			events:
-				title: "Events"+				title: 'Events'
+	'contact-us':
+		title: 'Contact Us'
+
+module.exports = routes


a few more debugging improvements

a few more debugging improvements

--- evolution.coffee
+++ evolution.coffee
@@ -1327,22 +1327,22 @@
           -20
       else 0
   # some debugging methods
-  showSeen: ->
+  showSeen: (color='black') ->
     @universe.draw()
     vr = @visualRange()
     candidates = @cell.near @, vr
     seen = @universe.near @, vr, @visualAngle(), null, candidates
     for t in seen
       t.outline()
-      t.draw()
-  showHeard: ->
+      t.draw('black')
+  showHeard: (color='purple') ->
     @universe.draw()
     ar = @auditoryRange()
     candidates = @cell.near @, ar
     heard = @universe.near @, ar, 1, null, candidates
     for t in heard
       t.outline()
-      t.draw()
+      t.draw(color)
   relativeGeometry: (t) ->
     data = super(t)
     [ t1, t2 ] = @margins()


Update default sizes and animation, fix center position, add new positions

--- app/assets/javascripts/js_tools/ajax_popup.js.coffee
+++ app/assets/javascripts/js_tools/ajax_popup.js.coffee
@@ -3,13 +3,13 @@
   class AjaxPopup
     defaults:
       style: "center"
-      width: 200
-      height: 200
+      width: "200px"
+      height: "200px"

       animation_popup: "opacity"
       animation_overlay: "opacity"
       animation_duration: 400
-      animation_easing: "easeOutCubic"
+      animation_easing: "linear"

       disable_page_scroll: true
       hide_on_click_overlay: true
@@ -36,12 +36,21 @@

       if @config.width && !(@config.style in ["top", "bottom"])
         @$popup.css({ width: @config.width })
-        @$popup.css({ "margin-left": -@config.width/2 }) if @config.style == "center"
+
       if @config.height && !(@config.style in ["left", "right"])
         @$popup.css({ height: @config.height })
-        @$popup.css({ "margin-top": -@config.height/2 }) if @config.style == "center"
+
+      if @config.style == "center" && @config.width && @config.height
+        @$popup.css({ "margin-left": @_calc_center_margin(@config.width) })
+        @$popup.css({ "margin-top": @_calc_center_margin(@config.height) })

       $("body").append(@$overlay, @$popup)
+
+    _calc_center_margin: (size) ->
+      reg = /(\d+)(px|%|em)/ig
+      res = reg.exec(size)
+      num = parseInt(res[1], 10)
+      "-#{num / 2}#{res[2] || 'px'}"

     _set_callbacks: ->
       if @config.hide_on_click_overlay
@@ -56,7 +65,7 @@

     _hide_scroll: (elem = "body") ->
       if @config.disable_page_scroll
-        $(elem).css({ overflow: "hidden", "padding-right": @_get_scrollbar_width })
+        $(elem).css({ overflow: "hidden", "padding-right": @_get_scrollbar_width(elem) })

     _show_scroll: (elem = "body") ->
       if @config.disable_page_scroll
@@ -88,10 +97,10 @@
     _toggle_popup: ->
       animations = {
         opacity: [ { opacity: 0 }, { opacity: 1 } ],
-        left: [ { left: -@config.width }, { left: 0 } ],
-        right: [ { right: -@config.width }, { right: 0 } ],
-        top: [{ top: -@config.height }, { top: 0 } ],
-        bottom: [{ bottom: -@config.height }, { bottom: 0 } ]
+        left: [ { left: "-#{@config.width}" }, { left: 0 } ],
+        right: [ { right: "-#{@config.width}" }, { right: 0 } ],
+        top: [{ top: "-#{@config.height}" }, { top: 0 } ],
+        bottom: [{ bottom: "-#{@config.height}" }, { bottom: 0 } ]
       }
       animation_popup = animations[@config.animation_popup] || @config.animation_popup
       animation_overlay = animations[@config.animation_overlay] || @config.animation_overlay


Added class to join/sign-in elements

Added class to join/sign-in elements

--- app/assets/javascripts/lib/core/authenticator.coffee
+++ app/assets/javascripts/lib/core/authenticator.coffee
@@ -32,8 +32,8 @@

     showLoginAndRegister: ()->
       @emptyUserNav()
-      joinElement = "Join"
-      signinElement = "Sign-In"
+      joinElement = "Join"
+      signinElement = "Sign-In"
       $('nav.js-user-nav').prepend(signinElement + joinElement)

     showUserBox: ->


优化 linkParser

优化 linkParser


Accurate bypass for Android Mobile, non chrome

--- app/assets/javascripts/mixingpanel/by-pass.js.coffee
+++ app/assets/javascripts/mixingpanel/by-pass.js.coffee
@@ -6,8 +6,8 @@
     @ua = @parser.getResult()

   parse: (event, properties = {}) ->
-    if @ua.os.name is "Android" and @ua.browser.version is "4.0"
-      [ "#{event} [Android Mobile v4.0]",
+    if @ua.os.name is "Android" and @ua.browser.name isnt "Chrome"
+      [ "#{event} [Android Mobile]",
         $.extend(properties, {user_agent: @ua.ua}) ]
     else
       [ event, properties ]


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/metered.coffee
+++ src/metered.coffee
@@ -18,7 +18,7 @@
         width
       } = meterData

-      if I[]
+      if notI[]

       ratio = I[name] / I["max#{name.capitalize()}"]


Allow payment_sources and completed_orders to be empty

--- app/assets/javascripts/sprangular/models/user.coffee
+++ app/assets/javascripts/sprangular/models/user.coffee
@@ -4,8 +4,8 @@
     @billingAddress  = @findAddress(@bill_address_id) if @bill_address_id
     @shippingAddress = @findAddress(@ship_address_id) if @ship_address_id

-    @orders      = Sprangular.extend(@completed_orders, Sprangular.Order)
-    @creditCards = Sprangular.extend(@payment_sources, Sprangular.CreditCard)
+    @orders      = Sprangular.extend(@completed_orders || [], Sprangular.Order)
+    @creditCards = Sprangular.extend(@payment_sources || [], Sprangular.CreditCard)

   serialize: ->
     _.omit this, (value) ->


Dont cache the package paths by path name

Closes #24

--- lib/notification-issue.coffee
+++ lib/notification-issue.coffee
@@ -141,8 +141,7 @@
     return

   getPackagePathsByPackageName: ->
-    return @packagePathsByPackageName if @packagePathsByPackageName?
-    @packagePathsByPackageName = {}
+    packagePathsByPackageName = {}
     for pack in atom.packages.getLoadedPackages()
-      @packagePathsByPackageName[pack.name] = pack.path
-    @packagePathsByPackageName
+      packagePathsByPackageName[pack.name] = pack.path
+    packagePathsByPackageName


Don't watch files as grunt default

Don't watch files as grunt default

--- services/web/Gruntfile.coffee
+++ services/web/Gruntfile.coffee
@@ -380,8 +380,8 @@

 	grunt.registerTask 'test:modules:unit', 'Run the unit tests for the modules', ['compile:modules:server', 'compile:modules:unit_tests'].concat(moduleUnitTestTasks)

-	grunt.registerTask 'run', "Compile and run the web-sharelatex server", ['compile', 'env:run', 'parallel']
-	grunt.registerTask 'runq', "Compile and run the web-sharelatex server", ['compile', 'env:run', 'exec']
+	grunt.registerTask 'run:watch', "Compile and run the web-sharelatex server", ['compile', 'env:run', 'parallel']
+	grunt.registerTask 'run', "Compile and run the web-sharelatex server", ['compile', 'env:run', 'exec']

 	grunt.registerTask 'default', 'run'


activate some data

activate some data

this should be reverted as soon as the main group has data active again.

--- app/controllers/classifier.coffee
+++ app/controllers/classifier.coffee
@@ -25,7 +25,7 @@
 K2_0_SUBJECT_GROUP = "547d05ce415ac13139000001"
 K2_1_SUBJECT_GROUP = "54f4c5ab8f165b6e85000001"
 K2_2_SUBJECT_GROUP = "55db0eca05cd210084000001"
-MAIN_SUBJECT_GROUP = K2_2_SUBJECT_GROUP
+MAIN_SUBJECT_GROUP = K2_1_SUBJECT_GROUP
 SIMULATION_GROUP   = "5417014b3ae7400bda000002"

 USERS_OPT_IN = ['a', 'b', 'c', 'g', 'h', 'i']


error stopping talk posts from interface. Fix

--- app/controllers/classifier.coffee
+++ app/controllers/classifier.coffee
@@ -840,7 +840,7 @@

             by
             You
-            #{date.toDateString()}
+            #{new Date().toDateString()}

       """).animate({ scrollTop: container[0].scrollHeight}, 1000)


Start socket after handshake is completed

--- src/flowdock.coffee
+++ src/flowdock.coffee
@@ -104,7 +104,6 @@
         cookie.split(";")[0]
       )
       res.on "end", () =>
-        @start()
         @emit "login"
         callback()

@@ -164,9 +163,6 @@
     )
     return if @cookies.length == 0

-    if @flows.length == 0
-      @start()
-
     options =
       host: subdomain + host
       path: '/flows/' + flow
@@ -174,6 +170,7 @@
         'Cookie': @cookies.join("; ")

     handshake @cookies, subdomain, flow, =>
+      @start() unless @socket
       post_data = querystring.stringify(
         channel: '/meta'
         event: 'join'


More meaningful error messages in node-flowdock.

--- src/flowdock.coffee
+++ src/flowdock.coffee
@@ -63,8 +63,11 @@

     req = httpClient.get options, (res) =>
       parser = new BufferParser()
-      if res.statusCode > 500
-        @emit "error", res.statusCode, "Backend connection failed"
+      if res.statusCode >= 500
+        @emit "error", res.statusCode, "Streaming connection failed"
+        return
+      if res.statusCode >= 400
+        @emit "error", res.statusCode, "Flow not found"
         return

       res.on "data", (data) =>
@@ -109,8 +112,11 @@
         'Accept': 'application/json'

     req = httpClient.request options, (res) ->
+      if res.statusCode >= 500
+        @emit "error", res.statusCode, "Couldn't estabilish a connection to Flowdock Messages API"
+        return
       if res.statusCode >= 400
-        @emit 'error', res.statusCode, "Couldn't post your #{data.event} to Flowdock."
+        @emit 'error', res.statusCode, "Couldn't post your #{data.event} to Flowdock Messages API"
         return
     req.write(post_data)
     req.end()


Update variable name

Update variable name

--- src/fs-utils.coffee
+++ src/fs-utils.coffee
@@ -46,9 +46,9 @@
   # Returns true if the path is absolute, false otherwise.
   isAbsolute: (pathToCheck='') ->
     if process.platform is 'win32'
-      pathToResolve[1] is ':' # C:\ style
-    else
-      pathToResolve[0] is '/' # /usr style
+      pathToCheck[1] is ':' # C:\ style
+    else
+      pathToCheck[0] is '/' # /usr style

   # Public: Returns true if a file or folder at the specified path exists.
   exists: (pathToCheck) ->


Fix typo.

Fix typo.

--- application/chrome/content/wesabe/lang/number.coffee
+++ application/chrome/content/wesabe/lang/number.coffee
@@ -18,7 +18,7 @@
   ]

   parseOrdinalPhrase: (string) ->
-    for [patern, value] in @ORDINAL_PARSERS
+    for [pattern, value] in @ORDINAL_PARSERS
       if m = string.match(pattern)
         value = value(m) if wesabe.isFunction(value)
         return value


Confirm Sending

Confirm Sending

--- app/js/controllers/account.coffee
+++ app/js/controllers/account.coffee
@@ -53,7 +53,7 @@
             Growl.notice "The wallet was successfully imported."
             refresh_account()

-    $scope.send = ->
+    yesSend = ->
         WalletAPI.transfer($scope.amount, $scope.symbol, $scope.account.name, $scope.payto, $scope.memo).then (response) ->
             $scope.payto = ""
             $scope.amount = ""
@@ -61,6 +61,15 @@
             Growl.notice "", "Transaction broadcasted (#{angular.toJson(response.result)})"
             refresh_account()
             $scope.t_active=true
+
+    $scope.send = ->
+        $modal.open
+            templateUrl: "dialog-confirmation.html"
+            controller: "DialogConfirmationController"
+            resolve:
+                title: -> "Are you sure?"
+                message: -> "This will send " + $scope.amount + " " + $scope.symbol + " to " + $scope.payto
+                action: -> yesSend

     $scope.newContactModal = ->
       $modal.open


Remove unneeded curlies

Remove unneeded curlies

--- src/publish.coffee
+++ src/publish.coffee
@@ -258,7 +258,7 @@
   renamePackage: (pack, name, callback) ->
     if name?.length > 0 and pack.name isnt name
       message = "Renaming #{pack.name} to #{name}"
-      process.stdout.write("#{message}")
+      process.stdout.write(message)
       @setPackageName pack, name, (error) =>
         if error?
           @logFailure()


fix the logical condition

fix the logical condition

--- library/nucleus/kernel.coffee
+++ library/nucleus/kernel.coffee
@@ -192,7 +192,7 @@
         @domain.on "error", (err) => @emit "panic", err
         process.removeAllListeners "uncaughtException"
         process.on "uncaughtException", (error, arg) =>
-            return unless str(error) is "socket end"
+            return no if str(error) is "socket end"
             return @emit.call this, "panic", error

     # Shutdown the kernel instance. This includes shutting down both


Better use case for identica

Better use case for identica

--- library/nucleus/kernel.coffee
+++ library/nucleus/kernel.coffee
@@ -76,7 +76,7 @@
     # app name plus a verion after the at sign. If no arguments are
     # supplied, the method will get identica, otherwise - attempt to
     # set one. If there is no identica - it asks the configuration.
-    identica: (identica) ->
+    @identica: (identica) ->
         cns = "identica:compiled".toString()
         automatic = -> @$identica or nconf.get cns
         return automatic() if arguments.length is 0
@@ -126,7 +126,7 @@
         @broker = new content.JsonBroker this
         assert _.isObject(options), "no options"
         message = "Booted up the kernel instance"
-        identica = "Using %s as instance identica"
+        manifest = "Using %s as instance identica"
         sigint = "Received the SIGINT (interrupt signal)"
         sigterm = "Received the SIGTERM (terminate signal)"
         process.on "SIGINT", => @shutdownKernel sigint
@@ -137,8 +137,9 @@
         assert not _.isEmpty @setupSocketServers()
         assert not _.isEmpty @setupHotloadWatcher()
         @constructor.configure.apply @constructor
-        logger.info identica, @identica().bold
-        logger.info message.red; return this
+        assert identica = @constructor.identica()
+        logger.info manifest, identica.bold
+        logger.info message.red; return @

     # The public constructor of the kernel instrances. Generally
     # you should neither use it directly, not override. It serves


Fix the removal problems with scaled kernel

--- library/nucleus/scaled.coffee
+++ library/nucleus/scaled.coffee
@@ -86,8 +86,8 @@
         assert _.isObject options = @resolveSslDetails()
         assert _.isString host = nconf.get "master:host"
         assert _.isNumber port = nconf.get "master:https"
-        cmp = (e) -> (x) -> e.uuid is x.target.uuid or 0
-        remove = (srv) -> _.remove @queueOfHttps, cmp srv
+        cmp = (exm) -> (srv) -> exm.uuid is srv.uuid or 0
+        remove = (srv) => _.remove @queueOfHttps, cmp(srv)
         assert forward = @makeForward @queueOfHttps, "https"
         @secureProxy = https.createServer options, forward
         assert @secureProxy; @secureProxy.listen port, host
@@ -107,8 +107,8 @@
         assert _.isArray @queueOfHttp ?= new Array
         assert _.isString host = nconf.get "master:host"
         assert _.isNumber port = nconf.get "master:http"
-        cmp = (e) -> (x) -> e.uuid is x.target.uuid or 0
-        remove = (srv) -> _.remove @queueOfHttp, cmp srv
+        cmp = (exm) -> (srv) -> exm.uuid is srv.uuid or 0
+        remove = (srv) -> _.remove @queueOfHttp, cmp(srv)
         assert forward = @makeForward @queueOfHttp, "http"
         assert @serverProxy = http.createServer forward
         assert @serverProxy; @serverProxy.listen port, host
@@ -137,6 +137,7 @@
         merged.target.https = service.kind is "https"
         merged.target.rejectUnauthorized = false
         queue.push proxy = new HttpProxy merged
+        assert _.isString proxy.uuid = service.uuid
         proxy.on "proxyError", (err, req, res) =>
             msg = "got an error talking to backend: %s"
             res.writeHead 500, "a proxy backend error"


added share doc connection to editor

added share doc connection to editor

--- app/scripts/pages/editor/markdown.coffee
+++ app/scripts/pages/editor/markdown.coffee
@@ -87,13 +87,16 @@
   prev = createPreview "preview-" + task.id()
   markdownPreview = (editor) ->
     prev.render editor.getValue()
-
+
+  shareDocConnection = (aceRethink markdownEditor.Range, "GRP0", task.id())
+
   if document.getElementById 'editor-' + task.id()
     editor = markdownEditor.create 'editor-' + task.id(), task.solution(), plugins: [
       markdownPreview,
-      (aceRethink markdownEditor.Range, "GRP", task.id()),
       markdownEditor.clearResults,
       javascriptEditorErrors "js", prev
     ]

+    shareDocConnection.connect(editor);
+
   prev.render task.solution()