dead code killing

dead code killing

--- coffee/main.coffee
+++ coffee/main.coffee
@@ -17,7 +17,6 @@
   g.ve.ebox = new StaticElementBox(g.ve, 120, g.ve.cheight/2 - 65)
   mh = g.ve.cheight - 167
   g.ve.mbox = new ModelBox(g.ve, mh, (g.ve.cheight - 140)/2 - mh/2.0 - 60)
-  #g.ve.surface = new Surface(g.ve)
   g.ve.datgui = null
   g.ve.render(g.ve)

@@ -32,8 +31,6 @@
     dsg = g.xp
     console.log("the xp is " + g.xp)
     initViz()
-    #loadXP()
-    #g.ve.addie.load()
     g.ve.addie.init()
     true
   ).fail () ->
@@ -43,9 +40,6 @@

 #Global event handlers
 root.vz_mousedown = (event, idx) ->
-  #g.ve.mouseh.ondown(event)
-  #g.ve.sview = g.ve.surfaceViews[idx]
-  #g.ve.surfaceViews[idx].mouseh.ondown(event)
   g.ve.sview.mouseh.ondown(event)

 root.hsplit_mdown = (event) =>
@@ -61,8 +55,6 @@
   g.ve.keyh.onup(event)

 root.vz_wheel = (event, idx) =>
-  #g.ve.mouseh.onwheel(event)
-  #g.ve.sview = g.ve.surfaceViews[idx]
   g.ve.sview.mouseh.onwheel(event)

 root.run = (event) =>
@@ -142,8 +134,6 @@
         {
          color: color,
          linewidth: 3
-         #transparent: false,
-         #opacity: 1
         }
       )
       @geom = new THREE.Geometry()
@@ -159,8 +149,6 @@

   Icon: class Icon
     constructor: (@texture, x, y, z) ->
-      #@texture = _texture.clone()
-      #@texture.needsUpdate = true
       @material = new THREE.SpriteMaterial({
         map: @texture,
         depthTest: false,
@@ -343,7 +331,6 @@
         @shp.obj3d.position.z)
       @shp = _shp
       @shp.obj3d.userData = this
-      #g.ve.iconCache[mdl] = tex
       @parent.obj3d.add(@shp.obj3d)
       g.ve.render()
       @tex = tex
@@ -490,8 +477,6 @@
       @shp = new Shapes.Circle(0x23549F, x, y, z, 15)
       @shp.obj3d.userData = this
       @parent.obj3d.add(@shp.obj3d)
-      #TODO you are here, all objects with changable props should have a props
-      #object
       @props = {
         name: "router0",
         sys: "root",
@@ -571,7 +556,6 @@
         design: dsg
       }

-      #TODO if ln itself is clicked on this messes up selection logic
       if isIcon
         @shp = new Shapes.Rectangle(@parent.material.color, x, y, z, 25, 25)
         @shp.obj3d.userData = this
@@ -590,9 +574,6 @@

     isIfxPhysical: (i) ->
       @endpoint[i] instanceof Phyo
-      #@endpoint[i] instanceof Sax
-
-

     applyWanProps: ->
       @props.capacity = 100
@@ -674,11 +655,8 @@
 class ElementBox
   #Constructs an ElementBox object given a @ve visual environment
   constructor: (@ve, @height, @y) ->
-    #@height = @ve.height - 10
     @width = 75
     @x = -@ve.cwidth/2  + @width / 2 + 5
-    #@x = 0
-    #@y =  0
     @z = 5
     @box = new Shapes.Rectangle(0x404040, @x, @y, @z, @width, @height)
     @box.obj3d.userData = this
@@ -727,15 +705,12 @@
     @ve.addie.update([m])

   addBaseElements: ->
-    #@addElement((box, x, y) -> new BaseElements.Model(box, x, y, 5))

 #The Surface holds visual representations of Systems and Elements
 #aka the majority of the screen
 class Surface
   #Constructs a Surface object given a @ve visual environment
   constructor: (@ve) ->
-    #@height = @ve.l
-    #@width = @ve.l
     @width = 5000
     @height = 5000
     @baseRect = new Shapes.Rectangle(0x262626, 0, 0, 0, @width, @height)
@@ -860,12 +835,6 @@
     if o.linep?
       o.linep.x += p.x
       o.linep.y += p.y
-
-    ###
-    if o.userData.glowBubble?
-      o.userData.glowBubble.position.x += p.x
-      o.userData.glowBubble.position.y += p.y
-    ###

     if o.lines?
       @updateLink(ln) for ln in o.lines
@@ -988,25 +957,15 @@
         gs.userData = obj
         obj.glowBubble = gs
         @selectGroup.add(gs)
-
-      ###
-      if obj.shp?
-        @selectGroup.add(obj.shp.obj3d)
-      if obj.ln?
-        @selectGroup.add(obj.ln.obj3d)
-      ###

       @ve.render()
     true

   clearSelection: (clearProps = true) ->

-    #delete gb.userData.glowBubble for gb in @selectGroup.children
-
     for x in @selectGroup.children
       if x.userData.glowBubble?
         delete x.userData.glowBubble
-        #@baseRect.obj3d.children.push(x)

     @selectGroup.children = []
     @clearPropsGUI() if clearProps
@@ -1048,7 +1007,6 @@
         @names[s]++
         n = s + @names[s]

-    #s + @names[s]
     n

 class SimSettings
@@ -1073,25 +1031,10 @@
     @sscene = new THREE.Scene()
     @pscene = new THREE.Scene()
     @surface = new Surface(this)
-    #@surfaceViews[1].renderer.setClearColor(0x363636, 1)
-    #@width = @container.offsetWidth
-    #@height = @container.offsetHeight
     @cwidth = @scontainer.offsetWidth #200
     @cheight = @scontainer.offsetHeight
     @iconCache = {}

-    #@l = Math.max(@width, @height)
-    #@l = 2000
-    ###
-    @camera = new THREE.OrthographicCamera(
-      @width / -2, @width / 2,
-      @height / 2, @height / -2,
-      1, 1000)
-    ###
-    #@camera = new THREE.OrthographicCamera(
-    #  @l/-2, @l/2,
-    #  @l/2, @l/-2,
-    #  1, 1000)
     @scamera = new THREE.OrthographicCamera(
       @cwidth / -2, @cwidth / 2,
       @cheight / 2, @cheight / -2,
@@ -1107,15 +1050,9 @@
     @alpha = 1
     @srenderer.setClearColor(@clear, @alpha)
     @scontainer.appendChild(@srenderer.domElement)
-    #@camera.position.z = 200
-    #@camera.zoom = 1
     @scamera.position.z = 200
     @scamera.zoom = 1
-    #@mouseh = new MouseHandler(this)
-    #@raycaster = new THREE.Raycaster()
-    #@raycaster.linePrecision = 10
     @namemanager = new NameManager(this)
-    #@xpcontrol = new ExperimentControl(this)
     @addie = new Addie(this)
     @propsEditor = new PropsEditor(this)
     @equationEditor = new EquationEditor(this)
@@ -1141,11 +1078,7 @@
     console.log("vsplit")

   render: ->
-    #sv.doRender() for sv in @surfaceViews
     @sview.doRender()
-    #@renderer.clear()
-    #@renderer.clearDepth()
-    #@renderer.render(@scene, @camera)

     @srenderer.clear()
     @srenderer.clearDepth()
@@ -1198,11 +1131,6 @@
     $("#diagnosticsPanel").css("top", (event.clientY+5)+"px")
     $("#hsplitter").css("top", event.clientY+"px")

-
-
-
-
-
   vdown: (event) =>
     $("#metasurface").mouseup(@vup)
     $("#metasurface").mousemove(@vmove)
@@ -1250,10 +1178,8 @@
     @renderer = new THREE.WebGLRenderer({antialias: true, alpha: true})
     @renderer.setSize(@cwidth, @cheight)
     @renderer.setClearColor(@clear, @alpha)
-    #@renderer.setPixelRatio(window.devicePixelRatio)

     @mouseh = new MouseHandler(this)
-    #@keyh = new KeyHandler(this.ve)
     @raycaster = new THREE.Raycaster()
     @raycaster.linePrecision = 10
     @container.appendChild(@renderer.domElement)
@@ -1294,21 +1220,6 @@
     for p in @panes
       p.camera.position.z = 200

-  ###
-  reInitCamera: () ->
-    @cwidth = @container.offsetWidth
-    @cheight = @container.offsetHeight
-    #@l = Math.max(@cwidth, @cheight)
-    @renderer.setSize(@l, @l)
-    @renderer.setClearColor(@clear, @alpha)
-    @camera.left = @l/-2
-    @camera.right = @l/2
-    @camera.top = @l/2
-    @camera.bottom = @l/-2
-    @camera.updateProjectionMatrix()
-    @doRender()
-  ###
-
   doRender: () ->
     for p in @panes
       vp = p.viewport
@@ -1590,7 +1501,6 @@
       if m.props.icon != ''
         do (m) => #capture m-ref before it gets clobbered by loop
           @ve.loadIcon(m.props.name, (tex) ->
-            #tex = @ve.iconCache[m.props.name]
             _tex = tex.clone()
             _tex.needsUpdate = true
             m.setIcon(_tex)
@@ -1609,8 +1519,6 @@

   doLoad: (m) =>
     @loadModels(m.models)
-    #while(@latchup > @latchdown)
-    #  console.log(@latchdown)
     @loadElements(m.elements)
     @loadSimSettings(m.simSettings)
     true
@@ -1725,8 +1633,6 @@

     l.endpoint[0] = a
     l.endpoint[1] = b
-    #l.ep_ifx[0] = x.endpoints[0].ifname
-    #l.ep_ifx[1] = x.endpoints[1].ifname

     l.applyPhysicalProps()
     @setProps(l, x)
@@ -1765,7 +1671,6 @@
     $.get "/addie/"+dsg+"/design/run", (data) =>
       console.log("run result: ")
       console.log(data)
-    #window.open(location.origin + "/results.html?xp=" + dsg)

   materialize: () =>
     if @mstate.up
@@ -1802,8 +1707,7 @@

   test: (ixs) ->
     ixs.length > 0 and
-    ixs[ixs.length - 1].object.userData instanceof StaticElementBox #and
-    #ixs[0].object.userData.cyjs?
+    ixs[ixs.length - 1].object.userData instanceof StaticElementBox

   handleDown: (ixs) ->
     @mh.sv.surface.clearSelection()
@@ -1815,6 +1719,7 @@
       #     this way many things may be linked without going back to the icon
       if e instanceof Link
         console.log "! linking objects"
+        @mh.sv.container.style.cursor = "crosshair"
         @mh.placingObject = null
         @mh.sv.container.onmousemove = (eve) => @mh.linkingH.handleMove0(eve)
         @mh.sv.container.onmousedown = (eve) => @mh.linkingH.handleDown0(eve)
@@ -1856,7 +1761,6 @@
     bix = @mh.sv.raycaster.intersectObject(@mh.sv.surface.baseRect.obj3d)

     if bix.length > 0
-      #ox = @mh.placingObject.shp.geom.boundingSphere.radius
       @mh.sv.surface.moveObject(@mh.placingObject.shp.obj3d, bix[0].point)
       @mh.sv.render()

@@ -1868,8 +1772,7 @@

   test: (ixs) ->
     ixs.length > 0 and
-    ixs[ixs.length - 1].object.userData instanceof ModelBox #and
-    #ixs[0].object.userData.cyjs?
+    ixs[ixs.length - 1].object.userData instanceof ModelBox

   handleDown: (ixs) ->
     @mh.sv.surface.clearSelection()
@@ -1924,16 +1827,13 @@
     @instance = null

   handleMove0: (event) ->
-    #console.log('mbox move0')
     @instance = @mh.makePlacingObject(@model.instantiate(null, 0, 0, 0, 25, 25))
-    #@instance.props.model = @model.props.name
     @instance.model = @model
     @instance.addArgs()
     @model.instances.push(@instance)
     @mh.sv.container.onmousemove = (eve) => @handleMove1(eve)

   handleMove1: (event) ->
-    #console.log('mbox move1')
     @mh.updateMouse(event)
     @mh.sv.ve.propsEditor.hide()
     @mh.sv.ve.equationEditor.hide()
@@ -1968,7 +1868,6 @@

     @mh.updateMouse(event)
     @start.copy(@mh.pos)
-    #@last.copy(@mh.pos)
     e = ixs[0].object.userData
     console.log "! surface select -- " + e.constructor.name
     console.log "current selection"
@@ -1994,13 +1893,10 @@

   applyGroupMove: () ->
     for x in @mh.sv.surface.selectGroup.children
-      #p = @mh.pos.sub(@last)
       p = new THREE.Vector3(@p1.x - @p0.x , @p1.y - @p0.y, @p0.z)
       if x.userData.shp?
         @mh.sv.surface.moveObjectRelative(x.userData.shp.obj3d, p)
       @mh.sv.surface.moveObjectRelative(x, p)
-      #x.position.x += @ve.surface.selectGroup.position.x
-      #x.position.y += @ve.surface.selectGroup.position.y

   updateGroupMove: () ->
     updates = []
@@ -2016,7 +1912,6 @@
     if @mh.placingObject.shp?
       @mh.placingObject.props.position = @mh.placingObject.shp.obj3d.position
       if @start.distanceTo(@end) > 0
-        #@mh.ve.addie.update([@mh.placingObject])
         @updateGroupMove()

     #@applyGroupMove()
@@ -2032,13 +1927,9 @@
     @p1.copy(bix[0].point)

     if bix.length > 0
-      #ox = @mh.placingObject.shp.geom.boundingSphere.radius
-      #@mh.ve.surface.moveObject(@mh.placingObject.shp.obj3d, bix[0].point)
-      #@mh.ve.surface.moveObject(@mh.ve.surface.selectGroup, bix[0].point)
       @applyGroupMove()
       @mh.sv.render()

-    #@last.copy(@mh.pos)
     @p0.copy(@p1)

 #TODO, me thinks that react.js orangular.js is meant to deal with precisely
@@ -2222,8 +2113,6 @@
     @mh.sv.container.onmousemove = null
     @mh.sv.container.onmousedown = (eve) => @mh.baseDown(eve)
     @mh.sv.container.onmouseup = null
-    #@mh.ve.surface.clearSelector()
-    #@mh.ve.surface.clearSelection()
     @mh.sv.render()

   handleMove: (event) ->
@@ -2313,12 +2202,12 @@

       @mh.sv.container.onmousemove = null
       @mh.sv.container.onmousedown = (eve) => @mh.baseDown(eve)
+      @mh.sv.container.style.cursor = "default"
     else
       console.log "! link1 miss"

   handleMove0: (event) ->
     @mh.updateMouse(event)
-    #console.log "! lm0"

   handleMove1: (event) ->
     #TODO replace me with baseRectIx when that is ready
@@ -2326,7 +2215,6 @@
     @mh.sv.raycaster.setFromCamera(@.mh.pos, @mh.icam)
     bix = @mh.sv.raycaster.intersectObject(@mh.sv.surface.baseRect.obj3d)
     if bix.length > 0
-      #console.log "! lm1"
       @mh.sv.scene.updateMatrixWorld()
       @mh.placingLink.ln.geom.vertices[1].x = bix[bix.length - 1].point.x
       @mh.placingLink.ln.geom.vertices[1].y = bix[bix.length - 1].point.y
@@ -2359,8 +2247,6 @@
     @apane = @sv.getPane(new THREE.Vector2(event.layerX, event.layerY))
     @icam = @apane.camera

-    #@pos.x =  (event.layerX / (@sv.cwidth/2) ) * 2 - 1
-    #@pos.y = -(event.layerY / @sv.cheight ) * 2 + 1
     if @apane.id == 1
       sr = @sv.ve.splitView.splitRatio()
     else
@@ -2371,7 +2257,6 @@

     @spos.x =  (event.layerX / @sv.ve.scontainer.offsetWidth ) * 2 - 1
     @spos.y = -(event.layerY / @sv.ve.scontainer.offsetHeight) * 2 + 1
-    #console.log(@pos.x + "," + @pos.y)

   baseRectIx: (event) ->
     @updateMouse(event)
@@ -2402,25 +2287,11 @@
     @sv.ve.propsEditor.hide()

     #get the list of objects the mouse click intersected
-    #@ve.scene.updateMatrixWorld()
     @updateMouse(event)
     console.log(@pos)

     #delegate the handling of the event to one of the handlers
     #check the model boxes first
-
-    ###
-    @ve.raycaster.setFromCamera(@spos, @ve.scamera)
-    sixs = @ve.raycaster.intersectObjects(@ve.sscene.children, true)
-    if      @eboxSH.test(sixs) then @eboxSH.handleDown(sixs)
-    else if @mboxSH.test(sixs) then @mboxSH.handleDown(sixs)
-    else
-      @ve.raycaster.setFromCamera(@pos, @ve.sview.camera)
-      ixs = @ve.raycaster.intersectObjects(@ve.scene.children, true)
-
-      if @surfaceESH.test(ixs) then @surfaceESH.handleDown(ixs)
-      else if @surfaceSSH.test(ixs) then @surfaceSSH.handleDown(ixs)
-    ###

     if event.which == 1
       @sv.raycaster.setFromCamera(@spos, @sv.ve.scamera)
@@ -2447,21 +2318,20 @@
   ondown: (event) =>
     keycode = window.event.keyCode || event.which

-    if(keycode == 74)
-      @ve.zoomin()
-    else if(keycode == 75)
-      @ve.zoomout()
-    else if(keycode == 72)
-      @ve.hsplit()
-    else if(keycode == 86)
-      @ve.vsplit()
-    else if(keycode == 8 || keycode == 46)
+    if(keycode == 8 || keycode == 46)
       @ve.surface.deleteSelection()
       event.preventDefault()
     else if(keycode == 16)
       console.log("multiselect start")
       @multiselect = true
       true
+    else if(keycode == 67)
+      @ve.sview.container.style.cursor = "crosshair"
+      @ve.sview.container.onmousemove = (eve) =>
+        @ve.sview.mouseh.linkingH.handleMove0(eve)
+      @ve.sview.container.onmousedown = (eve) =>
+        @ve.sview.mouseh.linkingH.handleDown0(eve)
+

   onup: (event) =>
     keycode = window.event.keyCode || event.which


handle aliases mentioned in README

handle aliases mentioned in README

--- lib/index.coffee
+++ lib/index.coffee
@@ -29,7 +29,13 @@
     # combine options and defaultOptions now...

     # 1. secure if some options related to a secure connection are specified
-    secure = options.rejectUnauthorized or options.key? or options.requestCert
+    if options.rejectUnauthorized or options.key? or options.private? or options.requestCert
+      secure = true
+      # convert aliases
+      if options.private? then options.key = options.private
+      if options.public? then options.cert = options.public
+      if options.root? then options.ca = options.root
+

     # 2. choose creator and use its function to build the socket
     socket = do (isServer, secure) ->


feat: add error callback

feat: add error callback

--- lib/index.coffee
+++ lib/index.coffee
@@ -19,6 +19,7 @@
       sandbox: answers
       key: answers.answers
       , (err, res) ->
+        return done err if err?
         console.log "complete"
         done()


fix comment with old name

fix comment with old name

--- lib/index.coffee
+++ lib/index.coffee
@@ -83,7 +83,7 @@

   on: (event, listener) ->
     if event is 'key'
-      unless @_chain?
+      unless @`_chain`?
         @_chain = buildChain array:[listener]
       else
         @_chain.add listener
@@ -110,7 +110,7 @@
       process.nextTick done

 # Use these ways:
-#  1. buildKevas = require 'kvstream'
+#  1. buildKevas = require 'kevas'
 #     stream = buildKevas options
 #
 #  2. stream = require('kevas') (options)


Pass options to io.listen()

Pass options to io.listen()

--- lib/index.coffee
+++ lib/index.coffee
@@ -1,4 +1,3 @@
-
 connect = require 'connect'
 express = require 'express'
 io = require 'socket.io'
@@ -29,7 +28,7 @@
     return this

 express.application.io = (options) ->
-    @io = io.listen @server
+    @io = io.listen @server, options
     @io.router = new Object
     @io.route = (route, next, options) ->
         return @router[route] next if options?.trigger is true


Removing a lot of dead code, and updating spec

--- dashboard_poller_spec.coffee
+++ dashboard_poller_spec.coffee
@@ -1,29 +0,0 @@
-#= require lib/dashboard_poller
-#= require mock-ajax
-
-describe "Dashboard Poller", ->
-  beforeEach ->
-    jasmine.Ajax.useMock()
-    @dashboard = ttm.lib.DashboardPoller.build()
-
-  describe "when the report is ready on the first iteration", ->
-    it "should return the dashboard data", ->
-      that = this
-
-      @dashboard.getData { type: 'lesson_leaders' }, (data) ->
-        that.data = data
-
-      request = mostRecentAjaxRequest()
-      expect(request.url).toBe('/dashboard/queries')
-      expect(request.method).toBe('POST')
-      request.response { status: 200, responseText: '{"id": "REPORT_ID" }' }
-
-      waits(1000)
-
-      runs ->
-        request = mostRecentAjaxRequest()
-        expect(request.url).toBe('/dashboard/queries/REPORT_ID/ready')
-        expect(request.method).toBe('GET')
-        request.response { status: 200, responseText: '{"is_ready": true, "data":"{ \\"key\\": \\"DATA\\" }" }' }
-
-        expect(that.data.key).toEqual 'DATA'


debugging bxSession

debugging bxSession

--- lib/services/bxSession/lib/bxSession.coffee
+++ lib/services/bxSession/lib/bxSession.coffee
@@ -5,7 +5,7 @@
   ($rootScope, $http, $q)->
     session = null
     authenticated = false
-    scope = null
+    scope = $rootScope

     # TODO: emit error object
     onError = () ->


clean Model

clean Model

--- lib/Model.coffee
+++ lib/Model.coffee
@@ -102,8 +102,11 @@
             query.run cb
           else query

+    @db: ->
+      db.r.db(db.config.db)
+
     @r: ->
-      db.r.db(db.config.db).table @tableName
+      @db().table @tableName

     @wrap: (data, cb) -> wrapAsync data, @, {isNew: no, wrapRelated: yes}, cb


Add Post and Channel endpoints

Add Post and Channel endpoints

--- compiler/ContentAPI.coffee
+++ compiler/ContentAPI.coffee
@@ -1,14 +1,16 @@
 ENTRY   = 'container'
 PACKAGE = 'package'
 POST    = 'post'
+CHANNEL = 'channel'
 IMAGE   = 'image'
 TEXT    = 'text'
 EMBED   = 'embed'

 ENDPOINTS =
-    container: 'content/'
-    package: 'content/'
-    # post: 'api/posts/'
+    container   : 'content/'
+    package     : 'content/'
+    post        : 'posts/'
+    channel     : 'channels/'

 fs      = require 'fs'
@@ -247,6 +249,7 @@
         , (result) ->
             SDKError.log("Got #{ result.length } entries from API.")
             cb(result)
+
     packages: (cb) ->
         @filter
             type: PACKAGE
@@ -255,13 +258,23 @@
         , (result) ->
             SDKError.log("Got #{ result.length } packages from API.")
             cb(result)
-    # posts: (cb) ->
-    #     @filter
-    #         type: POST
-    #         _sort: '-start_date'
-    #         is_public: true
-    #     , (result) ->
-    #         SDKError.log("Got #{ result.length } posts from API.")
-    #         cb(result)
+
+    posts: (cb) ->
+        @filter
+            type: POST
+            _sort: '-start_date'
+            is_public: true
+        , (result) ->
+            SDKError.log("Got #{ result.length } posts from API.")
+            cb(result)
+
+    channels: (cb) ->
+        @filter
+            type: CHANNEL
+            _sort: 'created_date'
+        , (result) ->
+            SDKError.log("Got #{ result.length } channels from API.")
+            cb(result)
+

 module.exports = ContentAPI


Fixed redundant compilation of templates (really fixes #9)

--- lib/index.coffee
+++ lib/index.coffee
@@ -52,6 +52,11 @@
     before_hook = (ctx) ->
       if @category == ctx.file.category && ctx.index == ctx.file.adapters.length
         ctx.file.original_content = ctx.file.content
+        # we client-compile the file by ourselves anyway,
+        # so letting roots compile it would be useless
+        # since we either replace ctx.content if not @concat
+        # or we don't write the file at all otherwise
+        ctx.content = ""

     after_hook = (ctx) ->
       if @category != ctx.category then return


remove unnecessary parentheticals

remove unnecessary parentheticals

--- src/test/index.coffee
+++ src/test/index.coffee
@@ -6,8 +6,8 @@
 app.use virtual
 app.get '/test', (req, res, next) ->
   res
-    .status(200)
-    .set('Content-Type', req.headers.accept)
+    .status 200
+    .set 'Content-Type', req.headers.accept
     .send()

 describe '#virtual', ->
@@ -19,60 +19,60 @@

   it 'should replace js ext in url with appropriate accept header', (done) ->
     unit
-      .httpAgent(app)
-      .get('/test.js')
-      .expect(200)
-      .expect('Content-Type', /^application\/javascript/i)
+      .httpAgent app
+      .get '/test.js'
+      .expect 200
+      .expect 'Content-Type', /^application\/javascript/i
       .end done

     return null

   it 'should replace json ext in url with appropriate accept header', (done) ->
     unit
-      .httpAgent(app)
-      .get('/test.json')
-      .expect(200)
-      .expect('Content-Type', /^application\/json/i)
+      .httpAgent app
+      .get '/test.json'
+      .expect 200
+      .expect 'Content-Type', /^application\/json/i
       .end done

     return null

   it 'should replace xml ext in url with appropriate accept header', (done) ->
     unit
-      .httpAgent(app)
-      .get('/test.xml')
-      .expect(200)
-      .expect('Content-Type', 'application/xml')
+      .httpAgent app
+      .get '/test.xml'
+      .expect 200
+      .expect 'Content-Type', 'application/xml'
       .end done

     return null

   it 'should replace html ext in url with appropriate accept header', (done) ->
     unit
-      .httpAgent(app)
-      .get('/test.html')
-      .expect(200)
-      .expect('Content-Type', /html/)
+      .httpAgent app
+      .get '/test.html'
+      .expect 200
+      .expect 'Content-Type', /html/
       .end done

     return null

   it 'should replace txt ext in url with appropriate accept header', (done) ->
     unit
-      .httpAgent(app)
-      .get('/test.txt')
-      .expect(200)
-      .expect('Content-Type', /plain/)
+      .httpAgent app
+      .get '/test.txt'
+      .expect 200
+      .expect 'Content-Type', /plain/
       .end done

     return null

   it 'should replace jpg ext in url with appropriate accept header', (done) ->
     unit
-      .httpAgent(app)
-      .get('/test.jpg')
-      .expect(200)
-      .expect('Content-Type', 'image/jpeg')
+      .httpAgent app
+      .get '/test.jpg'
+      .expect 200
+      .expect 'Content-Type', 'image/jpeg'
       .end done

     return null


Refactored code.

Refactored code.

--- lib/parse.coffee
+++ lib/parse.coffee
@@ -1,5 +1,28 @@
 require 'coffee-script'

+# ---
+
+to_value = (value) ->
+	return switch value
+		when 'none' then null
+		when 'on' then true
+		when 'off' then false
+		when 'true' then true
+		when 'false' then false
+		else value
+
+to_object = (previous, current) ->
+	previous[key] = to_value(val) for key, val of current if current
+
+	return previous
+
+to_array = (previous, current) ->
+	previous.push({}) if previous.length == 0 or current == null
+
+	previous[previous.length - 1][key] = to_value(val) for key, val of current if current
+
+	return previous
+
 # ---

 ###
@@ -13,20 +36,6 @@

 		return JSON.parse("{#{line}}")

-	to_value = (value) ->
-		return switch value
-			when 'none' then null
-			when 'true' then true
-			when 'false' then false
-			else value
-
-	to_array = (previous, current) ->
-		previous.push({}) if previous.length == 0 or current == null
-
-		previous[previous.length - 1][key] = to_value(val) for key, val of current if current
-
-		return previous
-
 	return input.split('\n').map(to_item).reduce(to_array, []).filter((item) -> Object.keys(item).length > 0)

 ###
@@ -38,11 +47,6 @@
 		line = line + '"' if line

 		return JSON.parse("{#{line}}")
-
-	to_object = (previous, current) ->
-		previous[key] = val for key, val of current if current
-
-		return previous

 	input.split('\n').map(to_item).reduce(to_object)

@@ -56,17 +60,5 @@

 		return JSON.parse("{#{line}}")

-	to_value = (value) ->
-		return switch value
-			when 'none' then null
-			when 'on' then true
-			when 'off' then false
-			else value
-
-	to_object = (previous, current) ->
-		previous[key] = to_value(val) for key, val of current if current
-
-		return previous
-
 	return input.split('\n').map(to_item).reduce(to_object)


Add method to iterate over a range

Add method to iterate over a range

--- lib/range.coffee
+++ lib/range.coffee
@@ -96,6 +96,13 @@
       Math.max(pointA.column, pointB.column)
     )

+  each: (block) ->
+    for row in [@start.row...@end.row]
+      for column in [@start.column...@end.column]
+        block(row, column)
+
+    return
+
   # Public: Returns a new range with the same start and end positions.
   copy: ->
     new @constructor(@start.copy(), @end.copy())


Avoid side effects

Avoid side effects

--- src/tools/argv.coffee
+++ src/tools/argv.coffee
@@ -1,4 +1,3 @@
-module.exports =
 argv = require '../sys/argv'

-do argv.shift if ':' == argv[0]
+module.exports = argv.slice Number ':' == argv[0]


preventDefault click events, closes #7

preventDefault click events, closes #7

--- src/touchclick.coffee
+++ src/touchclick.coffee
@@ -5,6 +5,7 @@
 $ = if typeof jQuery is 'function' then jQuery else require 'jquery'

 activeClass = 'touchactive'
+preventDefault = true

 # Store a timestamp of when the last touch event occurred
 lastTouched = 0
@@ -53,6 +54,10 @@
 events = (type) ->
   $el = $ this

+  if preventDefault
+    $el[type] 'click', (e) ->
+      e.preventDefault()
+
   if window.navigator.msPointerEnabled
     $el[type] 'MSPointerDown', touchstart
     $el[type] 'MSPointerUp', touchend


Fixed typo.

Fixed typo.

--- lib/saml2.coffee
+++ lib/saml2.coffee
@@ -115,8 +115,8 @@
         (cb_wf) => decrypt_assertion saml_response, @private_key, cb_wf
         (result, cb_wf) ->
           decrypted_assertion = (new xmldom.DOMParser()).parseFromString(result)
-          check_saml_signature decrypted_result, "adfs.crt", cb_wf
-        (cb_wf) -> parse_assertion_attributes (new xmldom.DOMParser()).parseFromString(decrypted_result), cb_wf
+          check_saml_signature result, "adfs.crt", cb_wf
+        (cb_wf) -> parse_assertion_attributes decrypted_assertion, cb_wf
         (assertion_attributes, cb_wf) -> cb_wf null, pretty_assertion_attributes assertion_attributes
       ], cb


Remove stray comment

Remove stray comment

--- lib/setup.coffee
+++ lib/setup.coffee
@@ -86,8 +86,6 @@
   # Inject sharify data before anything
   app.use sharify

-  #
-
   # Development / Test only middlewares that compile assets, mount antigravity, and
   # allow a back door to log in for tests.
   if "development" is NODE_ENV


Fix conflict

Fix conflict

--- lib/setup.coffee
+++ lib/setup.coffee
@@ -7,13 +7,8 @@
 {
   API_URL, APP_URL, NODE_ENV, ARTSY_ID, ARTSY_SECRET, SESSION_SECRET,
   SESSION_COOKIE_MAX_AGE, MICROGRAVITY_URL, DEFAULT_CACHE_TIME, COOKIE_DOMAIN, AUTO_GRAVITY_LOGIN,
-<<<<<<< HEAD
-  SESSION_COOKIE_KEY, SENTRY_DSN, API_REQUEST_TIMEOUT, FUSION_URL, IP_BLACKLIST, MOBILE_URL,
-  LOGGER_FORMAT
-=======
   SESSION_COOKIE_KEY, SENTRY_DSN, API_REQUEST_TIMEOUT, FUSION_URL, IP_BLACKLIST, REQUEST_LIMIT,
-  REQUEST_EXPIRE_MS, LOGGER_FORMAT
->>>>>>> master
+  REQUEST_EXPIRE_MS, MOBILE_URL, LOGGER_FORMAT
 } = config = require "../config"
 { parse, format } = require 'url'
 _ = require 'underscore'


Interval bugfix

Interval bugfix

--- lib/utils.coffee
+++ lib/utils.coffee
@@ -207,6 +207,7 @@
         iteration_info.start = start
         model_type.findOneNearestDate iteration_info.start, {key: key, reverse: true}, query, (err, model) ->
           return callback(err) if err
+          (no_models = true; return callback()) unless model
           iteration_info.first = model.get(key)
           callback()

@@ -231,6 +232,7 @@
         iteration_info.end = end
         model_type.findOneNearestDate iteration_info.end, {key: key}, query, (err, model) ->
           return callback(err) if err
+          (no_models = true; return callback()) unless model
           iteration_info.last = model.get(key)
           callback()


Every test works now with nodejs too! :8ball:

:

--- lib/y-xml.coffee
+++ lib/y-xml.coffee
@@ -124,10 +124,10 @@
 class YXml.Text extends YXml.Node
   constructor: (text = "")->
     super()
-    if text instanceof window.Text
+    if text.constructor is String
+      @_xml.text = text
+    else if text instanceof window.Text
       @_dom = text
-    else if text.constructor is String
-      @_xml.text = text
     else if text?
       throw new Error "The constructor of Y.Xml.Text expects either String or an Dom Text element!"

@@ -611,12 +611,3 @@

 if module?
   module.exports = YXml
-
-
-
-
-
-
-
-
-


Tighten up flow of jupyterDataDir

Tighten up flow of jupyterDataDir

--- lib/paths.coffee
+++ lib/paths.coffee
@@ -49,24 +49,19 @@
 jupyterDataDir = ->
   if process.env['JUPYTER_DATA_DIR']
     return process.env['JUPYTER_DATA_DIR']
-
-  # Windows first for easier testing
-  if process.platform == 'win32'
+  else if process.platform == 'win32'
     appData = process.env['APPDATA']
     if appData
       return path.join appData, 'jupyter'
     else
       return path.join jupyterConfigDir(), 'data'
-
-  home = userHome()
-
-  if process.platform == 'darwin'
-    return path.join(home, 'Library', 'Jupyter')
+  else if process.platform == 'darwin'
+    return path.join(userHome(), 'Library', 'Jupyter')
   else
     # Linux, non-OS X Unix, AIX, etc.
     xdg = process.env['XDG_DATA_HOME']
     if not xdg
-      xdg = path.join(home, '.local', 'share')
+      xdg = path.join(userHome(), '.local', 'share')
     return path.join(xdg, 'jupyter')


client: tidied up the controller syntax.

--- src/awraamba/coffee/app.coffee
+++ src/awraamba/coffee/app.coffee
@@ -82,8 +82,7 @@

     explore: (location) =>
       console.log "Controller.explore #{location}"
-      @_show 'explore_view', (view) =>
-        view.model.set 'value', location
+      @_show 'explore_view', (view) => view.model.set 'value', location

     watch: (theme) =>
       console.log "Controller.watch #{theme}"
@@ -92,8 +91,7 @@

     interact: (reaction) =>
       console.log "Controller.interact #{reaction}"
-      @_show 'interact_view', (view) =>
-        view.model.set 'value', reaction
+      @_show 'interact_view', (view) => view.model.set 'value', reaction

   # `main()` application entry point.  Call it to start the javascript.


Minor fixes.

Minor fixes.

--- lib/saml2.coffee
+++ lib/saml2.coffee
@@ -116,7 +116,7 @@
   sig = new xmlcrypto.SignedXml()
   sig.keyInfoProvider = getKey: -> format_pem(certificate, 'CERTIFICATE')
   sig.loadSignature signature[0].toString()
-  return sig.checkSignature(xml)
+  return sig.checkSignature xml

 # Takes in an xml @dom containing a SAML Status and returns true if at least one status is Success.
 check_status_success = (dom) ->
@@ -263,7 +263,7 @@
     (result, cb_wf) ->
       decrypted_assertion = (new xmldom.DOMParser()).parseFromString(result)
       unless _.some(idp_certificates, (cert) -> check_saml_signature result, cert)
-        return cb_wf new Error("SAML Assertion signature check failed!")
+        return cb_wf new Error("SAML Assertion signature check failed! (checked #{idp_certificates.length} certificate(s))")
       cb_wf null
     (cb_wf) -> async.lift(get_name_id) decrypted_assertion, cb_wf
     (name_id, cb_wf) ->


manual maybe

manual maybe

--- lib/setup.coffee
+++ lib/setup.coffee
@@ -35,8 +35,32 @@

   # Add up front middleware such as redirecting to Martsy and CORS support for login
   app.use redirectMobile
-  app.options '/users/sign_in', cors()
-  app.post '/users/sign_in', cors()
+
+  # Enable CORS with module
+  # corsOpts =
+  #   origin: [
+  #     'http://force.artsy.net'
+  #     'https://force.artsy.net'
+  #     'http://artsy.net'
+  #     'https://artsy.net'
+  #     'http://localhost:3003'
+  #     'https://localhost:3003'
+  #     'http://local.artsy.net:3003'
+  #     'https://local.artsy.net:3003'
+  #   ]
+  # app.options '/users/sign_in', cors(corsOpts)
+  # app.use '/users/sign_in', cors(corsOpts)
+
+  # Manual CORS (-__-)
+  enableCORS = (req, res, next) ->
+    res.set "Access-Control-Allow-Origin", "*"
+    res.set "Access-Control-Allow-Methods", "GET,PUT,POST,DELETE,OPTIONS"
+    res.set "Access-Control-Allow-Headers", "Content-Type, Authorization, Content-Length, X-Requested-With, X-XAPP-TOKEN"
+    if "OPTIONS" is req.method
+      res.send 200
+    else
+      next()
+  app.use enableCORS

   # Setup Artsy XAPP middleware
   app.use artsyXappMiddlware(


pass in cookie domain

pass in cookie domain

--- lib/setup.coffee
+++ lib/setup.coffee
@@ -69,8 +69,10 @@
     next()

   # Setup Artsy Passport
-  app.use express.cookieParser(SESSION_SECRET)
-  app.use express.cookieSession()
+  app.use express.cookieParser()
+  app.use express.cookieSession
+    secret: SESSION_SECRET
+    cookie: { domain: COOKIE_DOMAIN }
   app.use express.bodyParser()
   app.use artsyPassport _.extend(config,
     CurrentUser: CurrentUser


adds a route to get the milliseconds since the epoch

--- lib/setup.coffee
+++ lib/setup.coffee
@@ -228,6 +228,11 @@
   # Shortcuts are prioritized last
   app.use require "../apps/shortcuts"
   app.use require "../apps/clear_cache"
+
+  # route to ping for system time
+  app.get '/system/time', (req, res)->
+    res.send 200, {time: Date.now()}
+
   # Route to ping for system up
   app.get '/system/up', (req, res) ->
     res.send 200, { nodejs: true }


Minor fix.

Minor fix.

--- src/web-server.coffee
+++ src/web-server.coffee
@@ -64,6 +64,7 @@
     if socket?
       server.listen socket
     else
+      host ?= '0.0.0.0'
       server.listen port, host

     ++@_notYetListening


:lipstick:

:lipstick:

--- lib/available-package-view.coffee
+++ lib/available-package-view.coffee
@@ -50,7 +50,7 @@

     owner = @ownerFromRepository(@pack.repository)
     @filterText = "#{@pack.name} #{owner}"
-    @name = @pack.name
+    {@name} = @pack

     @handlePackageEvents()
     @updateEnablement()


refine log

refine log

--- lib/build/build-state-view.coffee
+++ lib/build/build-state-view.coffee
@@ -33,7 +33,7 @@
           @div id: 'toggle-out', =>
             @span class: 'glyphicon glyphicon-chevron-down'
             @span 'show output:'
-          @div id: 'out', style: 'height: 300px; overflow: scroll', =>
+          @div id: 'out', style: 'height: 300px; overflow: scroll; color: white;', =>

       @div class: 'actions', =>
         @div class: 'pull-left', =>


Move scrolling into function.

Move scrolling into function.

--- src/js/collection-editor.coffee
+++ src/js/collection-editor.coffee
@@ -118,6 +118,9 @@
     console.log "Latest URN: #{latest_urn}"
     callback(latest_urn)

+scroll_to_bottom = ->
+  $('html, body').animate({scrollTop: $(document).height()-$(window).height()},600,'linear')
+
 save_collection_form = ->
   collection = $('#collection_select').val()
   localStorage[collection] = true
@@ -125,7 +128,7 @@
     if $(child).attr('id') && !$(child).prop('disabled') && ($(child).attr('type') != 'hidden')
       localStorage["#{collection}:#{$(child).attr('id')}"] = get_value_for_form_input(child)
   $('#collection_form').after $('<div>').attr('class','alert alert-success').attr('id','save_success').append('Saved.')
-  $('html, body').animate({scrollTop: $(document).height()-$(window).height()},600,'linear')
+  scroll_to_bottom()
   $('#save_success').fadeOut 1800, ->
     $(this).remove()

@@ -148,7 +151,7 @@
     fusion_tables_query "INSERT INTO #{collection} (#{column_names.join(', ')}) VALUES (#{row_values.join(', ')})", (data) ->
       clear_collection_form()
       $('#collection_form').after $('<div>').attr('class','alert alert-success').attr('id','submit_success').append('Submitted.')
-      $('html, body').animate({scrollTop: $(document).height()-$(window).height()},600,'linear')
+      scroll_to_bottom()
       $('#submit_success').delay(1800).fadeOut 1800, ->
         $(this).remove()


added site verification

added site verification

--- src/layouts/default.html.coffee
+++ src/layouts/default.html.coffee
@@ -11,6 +11,7 @@
     meta "name": "author", "content": @site.author
     text @getBlock('meta').toHTML()
     meta "name": "viewport", "content": "width=device-width, initial-scale=1.0"
+    meta "name": "google-site-verification" "content": "7qBn2ADQE0AjoYPTvti8tvHvCjvmpn-G4A0Asm7jnMs"

     # Shortcut icons
     link rel: "shortcut icon",                href: "/ico/favicon.png"


Use full path to apm provided by package manager

Closes #9

--- lib/package-generator-view.coffee
+++ lib/package-generator-view.coffee
@@ -57,7 +57,7 @@
       true

   createPackageFiles: (callback) ->
-    @runCommand("apm", ['init', "--#{@mode}", "#{@getPackagePath()}"], callback)
+    @runCommand(atom.packages.getApmPath(), ['init', "--#{@mode}", "#{@getPackagePath()}"], callback)

   runCommand: (command, args, exit) ->
     new BufferedProcess({command, args, exit})


fix [skip ci]

fix
[skip ci]

--- libs/util.coffee
+++ libs/util.coffee
@@ -27,7 +27,7 @@
     okSign
     logSucc

-    getRegistry: (name) ->
+    getRegistry: (name = 'npm') ->
         host[name] or name

     readPackageFile: (name) ->


显示成功提示

显示成功提示

--- lib/project/create-project.coffee
+++ lib/project/create-project.coffee
@@ -69,7 +69,7 @@
             _.debounce(aft,300)
           Util.writeJson appConfigPath, Util.formatAppConfigToObj(info), writeCB
           @modalPanel.item.children(".loading-mask").remove()
-          # alert '项目创建成功'
+          alert '项目创建成功'
           atom.project.addPath(info.appPath)
           Util.rumAtomCommand 'tree-view:toggle' if $('.tree-view-resizer').length is 0
           @chameleonBox.closeView()


remove unused

remove unused

--- lib/provider/provider-base.coffee
+++ lib/provider/provider-base.coffee
@@ -1,5 +1,5 @@
 _ = require 'underscore-plus'
-{Point, CompositeDisposable, Emitter} = require 'atom'
+{Point, CompositeDisposable} = require 'atom'
 {saveEditorState} = require '../utils'
 UI = require '../ui'
 settings = require '../settings'
@@ -35,7 +35,6 @@
     @editorElement = @editor.element
     @pane = atom.workspace.paneForItem(@editor)
     @restoreEditorState = saveEditorState(@editor)
-    @emitter = new Emitter

     @ui = new UI(this, {input: @options.uiInput})

@@ -68,11 +67,13 @@
     {point, filePath} = item

     if filePath?
+      console.log 'case1'
       atom.workspace.open(filePath, pending: true).then (editor) ->
         editor.setCursorBufferPosition(point, autoscroll: false)
         editor.scrollToBufferPosition(point, center: true)
         return {editor, point}
     else
+      console.log 'case2'
       newPoint = @adjustPoint?(point)
       if newPoint?
         point = newPoint


Added enemy to gamescene.coffee

Added enemy to gamescene.coffee

--- ludumdare26/Game/GameScene.coffee
+++ ludumdare26/Game/GameScene.coffee
@@ -40,6 +40,7 @@

         game.worldObjects.push ground
         game.worldObjects.push player1
+        game.worldObjects.push enemy


pass new feature test

pass new feature test

--- spec/javascripts/atwho.spec.coffee
+++ spec/javascripts/atwho.spec.coffee
@@ -232,6 +232,16 @@
       trigger_atwho()
       expect(reposition_event).toHaveBeenTriggered()

+    it "trigger a special matched for @ with alias", ->
+      $inputor.atwho
+        at: "@"
+        alias: "at-memtions"
+
+      event = spyOnEvent($inputor, "matched-at-memtions.atwho")
+      trigger_atwho()
+      expect(event).toHaveBeenTriggered()
+
+
   describe "inner api", ->
     controller = null
     callbacks = null
@@ -265,3 +275,31 @@
       expect(controller.get_opt("data")).toBe "/atwho.json"
       expect(controller.model.fetch().length).toBe 3

+  describe "public api", ->
+    controller = null
+    data = []
+
+    beforeEach ->
+      controller = $inputor.data("atwho")
+      data = [
+        {one: 1}
+        {two: 2}
+        {three: 3}
+      ]
+
+    it "can load data for special flag", ->
+      $inputor.atwho "load", "@", data
+      expect(controller.model.fetch().length).toBe data.length
+
+    it "can load data with alias", ->
+      $inputor.atwho at: "@", alias: "at"
+      $inputor.atwho "load", "at", data
+      expect(controller.model.fetch().length).toBe data.length
+
+    it "can run it handly", ->
+      controller.set_context_for "@"
+      $inputor.caret('pos', 31)
+      $inputor.atwho "run"
+
+      expect(controller.view.$el).not.toBeHidden()
+


Add current room name to URL

Add current room name to URL

--- src/github-hook-manager.coffee
+++ src/github-hook-manager.coffee
@@ -35,8 +35,9 @@
   robot.respond /listen for events on ([^/]+)\/(.+)$/i, (response) ->
     user = response.match[1]
     repo = response.match[2]
+    room = response.message.room

     robot.logger.info "Request to add GitHub events hook to #{user}/#{repo} received"

     hookManager = new HookManager(robot, response)
-    hookManager.addHook(user, repo)
+    hookManager.addHook(user, repo, room: room)


callNow/Later -> run

callNow/Later -> run

--- common/action.coffee
+++ common/action.coffee
@@ -2,13 +2,9 @@
   constructor: (@fn) ->
     @timeoutId = null

-  callNow: (args...) ->
+  run: (delay, args...) ->
     @cancel()
-    @fn args...
-
-  callLater: (delay, args...) ->
-    @cancel()
-    @timeoutId = setTimeout (=> @callNow args...), delay
+    @timeoutId = setTimeout (=> @fn args...), delay

   cancel: ->
     clearTimeout @timeoutId


Remove test for getting an array of keys. This feature will be removed from the store shortly. Use Store::getIn instead of Store::_properties.getIn()

--- test/store.coffee
+++ test/store.coffee
@@ -180,25 +180,6 @@
 		expect instance.get 'a'
 		.to.equal 'test'

-	it 'should be able to get an array of values', () ->
-		class TestStore extends Store
-
-			initialize: () ->
-				@set {a: 'test', b: 'test2'}
-
-
-		instance = new TestStore
-
-		val = instance.get ['a', 'b']
-		expect val.toJS()
-		.to.have.keys ['a', 'b']
-
-		expect val.get 'a'
-		.to.equal 'test'
-
-		expect val.get 'b'
-		.to.equal 'test2'
-
 	it 'should dereference objects in get/set', () ->
 		nestedObject =
 			a:
@@ -213,7 +194,7 @@
 				@set nestedObject

 				nestedObject.a.b.c = "shouldntchangestoreprops"
-				expect @_properties.getIn(['a', 'b', 'c'])
+				expect @getIn(['a', 'b', 'c'])
 				.to.be.equal "test"


Use jquery#data

Use jquery#data

--- ui/javascripts/main.coffee
+++ ui/javascripts/main.coffee
@@ -38,8 +38,6 @@
   play_stone(move, board)
   set_move_on_ui_board(move)

-
-
 set_move_on_ui_board = (move)->
   $target_cell = $("table.go-board tr:nth-child(#{move.y + 1}) td:nth-child(#{move.x + 1})")
   stone = $(GO_STONE)
@@ -47,7 +45,9 @@
   $target_cell.append(stone)

 create_move_from_ui_move = ($td, color)->
-  create_stone($td.attr('data-x'), $td.attr('data-y'), color)
+  create_stone(($td.data('x')),
+               ($td.data('y')),
+               color)

 $ ->
   current_color = BLACK


create area if selection is in progress on mousedown

--- src/jquery-select-areas.coffee
+++ src/jquery-select-areas.coffee
@@ -110,6 +110,8 @@
       .on 'mousedown', (e) =>
         if @isDisabled() then return
         if event.which != 1 then return  # only left mouse btn
+        if @selection
+          @container.trigger 'mouseup'

         # relative to container
         click_x = e.pageX - @container.offset().left


Group data in normalize function instead of data load

--- resources/assets/coffee/_classes/changelog-chart.coffee
+++ resources/assets/coffee/_classes/changelog-chart.coffee
@@ -74,7 +74,7 @@
     @options.order = chartConfig.order
     @options.isBuild = chartConfig.isBuild

-    data = @normalizeData _.groupBy chartConfig.buildHistory, 'created_at'
+    data = @normalizeData chartConfig.buildHistory

     stack = d3.stack()
       .keys @options.order
@@ -189,7 +189,7 @@
     # normalize the user count values
     # and parse data into a form digestible by d3.stack()

-    for own timestamp, values of data
+    for own timestamp, values of _.groupBy data, 'created_at'
       sum = _.sumBy values, 'user_count'

       # parse date stored in strings to JS Date object for use by


CLI/repository fix for the new dynamic assets location support

--- cli/repository.coffee
+++ cli/repository.coffee
@@ -43,10 +43,12 @@
       fs = require('fs')

       fs.mkdirSync("#{location}/images", 0o0755)
-      build = build.replace /('|")(([^'"]+\.(gif|png|jpg|jpeg|svg|swf|eot|ttf|woff))|(\/assets\/[a-z0-9]+))\1/g,
+      build = build.replace /('|")(([^'"]+\.(gif|png|jpg|jpeg|svg|swf|eot|ttf|woff))|([^'"]*\/assets\/[a-z0-9]+))\1/g,
       (match, q, url)->
-        if url.indexOf('http://') > -1 or url.indexOf('/assets/') is 0
+        console.log(url)
+        if url.indexOf('http://') is 0 or url.indexOf('/assets/') is 0
           url = url.replace(/http:\/\/[^\/]+/, '')
+          console.log(url)

           do (url)->
             # downloading the image


fix syntax error

fix syntax error

--- mechanics/003-twilight-zone/luminosity.coffee
+++ mechanics/003-twilight-zone/luminosity.coffee
@@ -12,7 +12,7 @@
     canvas.width = 1052
     canvas.height = 80

-    ls = 0 for i in [0...10520]
+    ls = (0 for i in [0...10520])

     luminosity.paint = (data) ->
         ls.shift()


fixed typo

fixed typo

--- demo.coffee
+++ demo.coffee
@@ -211,7 +211,7 @@
     collector: statCollector title, (d) -> d.genes[gene]
     rows:      []
   }
-collectData = (u) ->
+collectData = ->
   evoData.generation += 1
   byId('ticks').innerHTML = intFormat evoData.generation
   byId('created').innerHTML = intFormat u.thingsCreated()
@@ -382,6 +382,6 @@
   firstClick.click()
   byId('about').style.width = byId('tabs').clientWidth - 20
   tryLoad(false)
-  u = makeUniverse()
+  makeUniverse()
   onEvent 'click', byId('universe'), (e) -> console.log u.imageFor('herbivore') if u
 )()


last shot 2

last shot 2

--- app/CNeditor/editor.coffee
+++ app/CNeditor/editor.coffee
@@ -5385,7 +5385,8 @@
     ###*
      * Called by the hotString controler when return is hit or when an item of
      * the auto complete is clicked.
-     * @param  {Object} autoItem {text:'value', type:'reminder'|'contact'|...}
+     * @param  {Object} autoItem
+     *                  {text:'value', type:'reminder'|'contact'|..., value:{}}
     ###
     doHotStringAction : (autoItem) ->


correct classes on pieces

correct classes on pieces

--- views/index.coffee
+++ views/index.coffee
@@ -17,51 +17,51 @@
           if num in [1,3,5,7] and letter in ['a','c','e','g']
             td class: "dark #{num+letter}", ->
               if num == 7
-                img src: 'images/chess_pieces/black_pawn.png', class: 'chess_piece'
+                img src: 'images/chess_pieces/black_pawn.png', class: "chess_piece #{num+letter}"
               if num == 1 and letter in ['a','h']
-                img src: 'images/chess_pieces/white_rook.png', class: 'chess_piece'
+                img src: 'images/chess_pieces/white_rook.png', class: "chess_piece #{num+letter}"
               if num == 1 and letter in ['b','g']
-                img src: 'images/chess_pieces/white_knight.png', class: 'chess_piece'
+                img src: 'images/chess_pieces/white_knight.png', class: "chess_piece #{num+letter}"
               if num == 1 and letter in ['c','f']
-                img src: 'images/chess_pieces/white_bishop.png', class: 'chess_piece'
+                img src: 'images/chess_pieces/white_bishop.png', class: "chess_piece #{num+letter}"
               if num == 1 and letter == 'e'
-                img src: 'images/chess_pieces/white_queen.png', class: 'chess_piece'
+                img src: 'images/chess_pieces/white_queen.png', class: "chess_piece #{num+letter}"

           else if num in [2,4,6,8] and letter in ['b','d','f','h']
             td class: "dark #{num+letter}", ->
               if num == 2
-                img src: 'images/chess_pieces/white_pawn.png', class: 'chess_piece'
+                img src: 'images/chess_pieces/white_pawn.png', class: "chess_piece #{num+letter}"
               if num == 8 and letter in ['a','h']
-                img src: 'images/chess_pieces/black_rook.png', class: 'chess_piece'
+                img src: 'images/chess_pieces/black_rook.png', class: "chess_piece #{num+letter}"
               if num == 8 and letter in ['b','g']
-                img src: 'images/chess_pieces/black_knight.png', class: 'chess_piece'
+                img src: 'images/chess_pieces/black_knight.png', class: "chess_piece #{num+letter}"
               if num == 8 and letter in ['c','f']
-                img src: 'images/chess_pieces/black_bishop.png', class: 'chess_piece'
+                img src: 'images/chess_pieces/black_bishop.png', class: "chess_piece #{num+letter}"
               if num == 8 and letter == 'd'
-                img src: 'images/chess_pieces/black_king.png', class: 'chess_piece'
+                img src: 'images/chess_pieces/black_king.png', class: "chess_piece #{num+letter}"

           else
             td class: "light #{num+letter}", ->
               if num == 2
-                img src: 'images/chess_pieces/white_pawn.png', class: 'chess_piece'
+                img src: 'images/chess_pieces/white_pawn.png', class: "chess_piece #{num+letter}"
               if num == 7
-                img src: 'images/chess_pieces/black_pawn.png', class: 'chess_piece'
+                img src: 'images/chess_pieces/black_pawn.png', class: "chess_piece #{num+letter}"
               if num == 1 and letter in ['a','h']
-                img src: 'images/chess_pieces/white_rook.png', class: 'chess_piece'
+                img src: 'images/chess_pieces/white_rook.png', class: "chess_piece #{num+letter}"
               if num == 1 and letter in ['b','g']
-                img src: 'images/chess_pieces/white_knight.png', class: 'chess_piece'
+                img src: 'images/chess_pieces/white_knight.png', class: "chess_piece #{num+letter}"
               if num == 1 and letter in ['c','f']
-                img src: 'images/chess_pieces/white_bishop.png', class: 'chess_piece'
+                img src: 'images/chess_pieces/white_bishop.png', class: "chess_piece #{num+letter}"
               if num == 1 and letter == 'd'
-                img src: 'images/chess_pieces/white_king.png', class: 'chess_piece'
+                img src: 'images/chess_pieces/white_king.png', class: "chess_piece #{num+letter}"
               if num == 8 and letter in ['a','h']
-                img src: 'images/chess_pieces/black_rook.png', class: 'chess_piece'
+                img src: 'images/chess_pieces/black_rook.png', class: "chess_piece #{num+letter}"
               if num == 8 and letter in ['b','g']
-                img src: 'images/chess_pieces/black_knight.png', class: 'chess_piece'
+                img src: 'images/chess_pieces/black_knight.png', class: "chess_piece #{num+letter}"
               if num == 8 and letter in ['c','f']
-                img src: 'images/chess_pieces/black_bishop.png', class: 'chess_piece'
+                img src: 'images/chess_pieces/black_bishop.png', class: "chess_piece #{num+letter}"
               if num == 8 and letter == 'e'
-                img src: 'images/chess_pieces/black_queen.png', class: 'chess_piece'
+                img src: 'images/chess_pieces/black_queen.png', class: "chess_piece #{num+letter}"
 coffeescript ->
   jQuery(document).ready ->
     now.ready ->
@@ -105,9 +105,7 @@
       if message
         $("#messages").append("<p>" + name + ": " + message + "</p>")
     now.receiveElement= (className, top, left) ->
-      console.log className
-      console.log top
-      console.log left
+

       $(".#{className.replace(" ",  ".").replace(" ui-draggable", "")}").css({"position":"relative","left":"#{left}px","top":"#{top}px"})


remove startup dependency on s3 settings

--- app/coffee/MongoAWS.coffee
+++ app/coffee/MongoAWS.coffee
@@ -9,12 +9,11 @@

 DAYS = 24 * 3600 * 1000 # one day in milliseconds

-AWS_CONFIG = {
-	accessKeyId: settings.trackchanges.s3.key
-	secretAccessKey: settings.trackchanges.s3.secret
-}
+createStream = (streamConstructor, project_id, doc_id, pack_id) ->
+	AWS_CONFIG =
+		accessKeyId: settings.trackchanges.s3.key
+		secretAccessKey: settings.trackchanges.s3.secret

-createStream = (streamConstructor, project_id, doc_id, pack_id) ->
 	return streamConstructor new AWS.S3(AWS_CONFIG), {
 		"Bucket": settings.trackchanges.stores.doc_history,
 		"Key": project_id+"/changes-"+doc_id+"/pack-"+pack_id


change the precondition to {autoDelete:false} for bongo

--- node_modules_koding/bongo/lib/bongo.coffee
+++ node_modules_koding/bongo/lib/bongo.coffee
@@ -176,7 +176,7 @@

   getBrokerExchangeOptions:->
     type        : 'topic'
-    autoDelete  : yes
+    autoDelete  : no
     durable     : no

   fetchBrokerExchange:(callback)->


Cherry-pick from Chris' branch to support $ suffix for prefered methods

--- node_modules_koding/bongo/lib/bongo.coffee
+++ node_modules_koding/bongo/lib/bongo.coffee
@@ -49,13 +49,13 @@
   @secure =(fn)->
     fn.isSecurityEnabled = yes
     fn
-
+
   {@asynchronizeOwnMethods} = require './util'
-
+
   {BongoError}    = require './errortypes'

   Client = require './client'
-
+
   @throwIt =(it)-> throw it if it?

   constructor:(options)->
@@ -156,7 +156,7 @@
         when 3 then 'service'
         when 4 then 'event'
         else        "additionalProperty#{i-4}"
-
+
     parseRoutingKey = (routingKey)->
       routingKey.split('.').reduce (acc, edge, i)->
         acc[getEdgeMeaning i] = edge
@@ -258,7 +258,7 @@

   handleAuth:(secretName, sessionToken)->
     @fetchBrokerExchange (brokerExchange)->
-      brokerExchange.publish
+      brokerExchange.publish
     # @emit 'auth', secretName, sessionToken

   handleRequest:(secretName, message)->
@@ -287,7 +287,11 @@
       if 'function' is typeof method
         fn = method
       else if ctx? and 'string' is typeof method
-        fn = ctx[method]
+        # prefer the method with the trailing dollar, since this should be, by
+        # convention, the version of the method which implements security for
+        # a given model, and provides a wrapper for it; otherwise, search for
+        # the method with the exact name.
+        fn = ctx["#{method}$"] ? ctx[method]
       return  unless fn?
       if fn.isSecurityEnabled
         bongo.fetchClient sessionToken, userArea, (client)->
@@ -382,5 +386,5 @@
     # implement dispatch method

   setClient:(rest...)-> Model.setClient rest...
-
+
   use:(fn...)-> @stack.push fn...


bongo: use broker amqp connection instead of creating new one

--- node_modules_koding/bongo/lib/bongo.coffee
+++ node_modules_koding/bongo/lib/bongo.coffee
@@ -85,11 +85,8 @@
     @stack = []
     @services = {}

-    @connection = amqp.createConnection @mqConfig
-    @connection.setMaxListeners(0)
-    @readyState = 0
-    @connection.once 'ready', => @readyState = 1
-
+    # TODO comment out this when we completely remove broker
+    #@initMQ()

     #@initBlacklist options.blacklist

@@ -111,7 +108,7 @@
     @updateInstancesMessageThrottleMs = 100

   initMQ: ->
-    @connection = amqp.createConnection @mqConfig, reconnect: yes
+    @connection = amqp.createConnection @mqConfig, reconnect: no
     @connection.setMaxListeners(0)
     @readyState = 0
     @connection.once 'ready', => @readyState = 1
@@ -223,7 +220,7 @@
     {exchangeName} = @mqConfig

     # TODO exchange version must be innjected here, when we have that support
-    @connection.exchange "#{exchangeName}:0", EXCHANGE_OPTIONS, (exchange) =>
+    @mq.connection?.exchange "#{exchangeName}:0", EXCHANGE_OPTIONS, (exchange) =>
       {eventName, body, account} = data
       message = {
         account
@@ -237,7 +234,6 @@
       exchange.publish "", message, type: "dispatcher_notify_user"
       exchange.close()

-    @connection.on 'error', @emit.bind this, 'error'

   parseRoutingKey: do ->
     getEdgeMeaning = (i)->


searches and icons

searches and icons

--- chrome/content/zotero-opds/zotero-opds.coffee
+++ chrome/content/zotero-opds/zotero-opds.coffee
@@ -9,17 +9,15 @@
   clients:
     "127.0.0.1": true

-  log: (msg, e) ->
-    msg = JSON.stringify(msg)  unless typeof msg == "string"
-    msg = "[opds] " + msg
-    if e
-      msg += "\nan error occurred: "
-      if e.name
-        msg += e.name + ": " + e.message + " \n(" + e.fileName + ", " + e.lineNumber + ")"
-      else
-        msg += e
-      msg += "\n" + e.stack  if e.stack
-    Zotero.debug(msg)
+  log: (msg...) ->
+    msg = for m in msg
+      switch
+        when (typeof m) in ['string', 'number'] then '' + m
+        when m instanceof Error and m.name then "#{m.name}: #{m.message} \n(#{m.fileName}, #{m.lineNumber})\n#{m.stack}"
+        when m instanceof Error then "#{e}\n#{e.stack}"
+        else JSON.stringify(m)
+
+    Zotero.debug("[better-bibtex] #{msg.join(' ')}")
     return

   url: ->
@@ -153,6 +151,19 @@
     collection: "with recursive collectiontree (collection) as (values (?) union all select c.collectionID from collections c join collectiontree ct on c.parentCollectionID = ct.collection) select max(dateModified) from collectiontree ct join collectionItems ci on ct.collection = ci.collectionID join items i on ci.itemID = i.itemID"

   endpoints:
+    'icon.png':
+      supportedMethods: ["GET"]
+      init: (url, data, sendResponseCallback) ->
+        # don't use Zotero.File.getContentsFromURL as it forces the output to text/plain
+        req = Components.classes["@mozilla.org/xmlextras/xmlhttprequest;1"].createInstance()
+        req.open('GET', "chrome://zotero/skin/#{url.query.name}", false)
+        req.responseType = 'arraybuffer'
+        req.send(null)
+
+        return sendResponseCallback(req.status, 'text/plain', "#{req.status}, #{if req.response then 'got' else 'no'} content") if req.status != 200 || !req.response
+
+        return sendResponseCallback(200, 'image/png', String.fromCharCode.apply(null, new Uint8Array(req.response)))
+
     index:
       supportedMethods: ["GET"]
       init: (url, data, sendResponseCallback) ->
@@ -162,10 +173,11 @@
           for collection in Zotero.getCollections()
             @collection(collection)

-          for group in Zotero.Groups.getAll()
-            @group(group)
-
-          # TODO: add saved searches
+          for search in Zotero.Searches.getAll()
+            @search(search)
+
+          @group()
+
           return)

         sendResponseCallback(200, "application/atom+xml", feed.serialize())
@@ -175,27 +187,35 @@
       supportedMethods: ["GET"]
       init: (url, data, sendResponseCallback) ->
         item = Zotero.Items.getByLibraryAndKey(url.query.library, url.query.key)
-        Zotero.OPDS.log('Getting binary')
         body = Zotero.File.getBinaryContents(item.getFile())
-        Zotero.OPDS.log('Got it')
         sendResponseCallback(200, item.attachmentMIMEType || 'application/pdf', body)
-        Zotero.OPDS.log('Sent it')
         return

     group:
       supportedMethods: ["GET"]
       init: (url, data, sendResponseCallback) ->
-        group = Zotero.Groups.getByLibraryID(url.query.id)
-        updated = Zotero.DB.valueQuery(Zotero.OPDS.sql.group, url.query.id)
-
-        feed = new Zotero.OPDS.Feed("/opds/group?id=#{url.query.id}", "Group: #{group.name}", updated, ->
-          for collection in group.getCollections() || []
-            @collection(collection)
-
-          for item in (new Zotero.ItemGroup('group', group)).getItems() || []
-            @item(item)
-
-          return)
+        if url.query.id == ''
+          updated = Zotero.DB.valueQuery(Zotero.OPDS.sql.index)
+          feed = new Zotero.OPDS.Feed("/opds/group?id=#{url.query.id}", 'Group Libraries', updated, ->
+            for group in Zotero.Groups.getAll()
+              @group(group)
+            return)
+
+        else
+          group = Zotero.Groups.getByLibraryID(url.query.id)
+          updated = Zotero.DB.valueQuery(Zotero.OPDS.sql.group, url.query.id)
+
+          feed = new Zotero.OPDS.Feed("/opds/group?id=#{url.query.id}", "Group: #{group.name}", updated, ->
+            for collection in group.getCollections() || []
+              @collection(collection)
+
+            for search in Zotero.Searches.getAll(url.query.id)
+              @search(search)
+
+            for item in (new Zotero.ItemGroup('group', group)).getItems() || []
+              @item(item)
+
+            return)

         sendResponseCallback(200, "application/atom+xml", feed.serialize())
         return
@@ -203,7 +223,7 @@
     collection:
       supportedMethods: ["GET"]
       init: (url, data, sendResponseCallback) ->
-        collection = Zotero.Collections.getByLibraryAndKey(url.query.library, url.query.key)
+        collection = Zotero.Collections.getByLibraryAndKey((if url.query.library == '0' then null else url.query.library), url.query.key)
         updated = Zotero.DB.valueQuery(Zotero.OPDS.sql.collection, collection.id) or collection.dateModified

         feed = new Zotero.OPDS.Feed("/opds/collection?library=#{url.query.library}&key=#{url.query.key}", "Collection: #{collection.name}", updated, ->
@@ -213,6 +233,23 @@
           for item in collection.getChildItems(false) || []
             @item(item)

+          return)
+
+        sendResponseCallback(200, "application/atom+xml", feed.serialize())
+        return
+
+    search:
+      supportedMethods: ["GET"]
+      init: (url, data, sendResponseCallback) ->
+        Zotero.OPDS.log("Search: #{JSON.stringify(url.query.library)}, #{JSON.stringify(url.query.key)}")
+        search = Zotero.Searches.getByLibraryAndKey((if url.query.library == '0' then null else url.query.library), url.query.key)
+        Zotero.OPDS.log('Search:', typeof search)
+
+        feed = new Zotero.OPDS.Feed("/opds/search?library=#{url.query.library}&key=#{url.query.key}", "Search: #{search.name}", null, ->
+          items = search.search() || []
+          if items.length > 0
+            for item in Zotero.Items.get(items)
+              @item(item)
           return)

         sendResponseCallback(200, "application/atom+xml", feed.serialize())
@@ -262,13 +299,23 @@
     @add(link: { rel: 'self', href: url, type: 'application/atom+xml;profile=opds-catalog;kind=navigation' })
     @add(link: { rel: 'start', href: '/opds', type: 'application/atom+xml;profile=opds-catalog;kind=navigation' })
     @add(title: title)
-    @add(updated: @date(updated))
+    @add(updated: @date(updated)) if updated

     @add(author: ->
       @add(name: 'Zotero OPDS')
       @add(uri: 'http://zotplus.github.io/opds')
       return)
     content.call(@)
+
+  search: (search) ->
+    @add(entry: ->
+      url = "/opds/search?library=#{search.libraryID || 0}&key=#{search.key}"
+      @add(title: search.name)
+      @add(link: { rel: 'subsection', href: url, type: 'application/atom+xml;profile=opds-catalog;kind=acquisition' })
+      @add(id: url)
+      @add(content: { type: 'text', '': search.name })
+      return)
+    return

   collection: (collection) ->
     updated = Zotero.DB.valueQuery(Zotero.OPDS.sql.collection, collection.id) or collection.dateModified
@@ -276,6 +323,7 @@
       url = "/opds/collection?library=#{collection.libraryID || 0}&key=#{collection.key}"
       @add(title: collection.name)
       @add(link: { rel: 'subsection', href: url, type: 'application/atom+xml;profile=opds-catalog;kind=acquisition' })
+      @add(link: { type: 'image/png', href: '/opds/icon.png?name=treesource-collection.png', rel: 'http://opds-spec.org/image' })
       @add(updated: @date(updated))
       @add(id: url)
       @add(content: { type: 'text', '': collection.name })
@@ -283,15 +331,21 @@
     return

   group: (group) ->
-    updated = Zotero.DB.valueQuery(Zotero.OPDS.sql.group, group.id)
-    libraryID = Zotero.Groups.getLibraryIDFromGroupID(group.id)
+    if group
+      updated = Zotero.DB.valueQuery(Zotero.OPDS.sql.group, group.id)
+      libraryID = Zotero.Groups.getLibraryIDFromGroupID(group.id)
+      name = group.name
+    else
+      updated = Zotero.DB.valueQuery(Zotero.OPDS.sql.index)
+      libraryID = ''
+      name = 'Group Libraries'
     @add(entry: ->
       url = "/opds/group?id=#{libraryID}"
-      @add(title: group.name)
+      @add(title: name)
       @add(link: { rel: 'subsection', href: url, type: 'application/atom+xml;profile=opds-catalog;kind=acquisition' })
       @add(updated: @date(updated))
       @add(id: url)
-      @add(content: { type: 'text', '': group.name })
+      @add(content: { type: 'text', '': name })
       return)
     return


Workspace: Used KDBlockingModalView when session disconnected.

--- client/app/CommonViews/workspace/collaborativeworkspace.coffee
+++ client/app/CommonViews/workspace/collaborativeworkspace.coffee
@@ -50,6 +50,9 @@
     @workspaceRef.on "child_added", (snapshot) =>
       log "everything is something happened", "child_added", snapshot.val(), snapshot.name()

+    @workspaceRef.on "child_changed", (snapshot) =>
+      log "everything is something happened", "child_changed", snapshot.val(), snapshot.name()
+
     @workspaceRef.on "child_removed", (snapshot) =>
       log "possible disconnection occured"

@@ -139,7 +142,7 @@
       title   = "Host disconnected"
       content = "It seems, host is disconnected from Firebase server. You cannot continue this session."

-    @disconnectedModal = new KDModalView
+    @disconnectedModal = new KDBlockingModalView
       title        : title
       content      : "<p>#{content}</p>"
       cssClass     : "host-disconnected-modal"


profile: removed loading bar references, restructured the code

--- client/landing/app/MainApp/lazy/staticprofilecontroller.coffee
+++ client/landing/app/MainApp/lazy/staticprofilecontroller.coffee
@@ -45,21 +45,6 @@
       unless err
         @reviveViewsOnUserLoad user

-  addLogic:(type,callback=->)->
-      unless @controllers[type]?
-        @addLogicForType type, =>
-          @controllers[type]?.hideLazyLoader()
-          callback()
-      else
-        @controllers[type]?.hideLazyLoader()
-        @showWrapper @wrappers[type]
-        callback()
-
-  displaySidebar:(show=yes,delay=250)->
-    @utils.wait delay, =>
-      @profileContentList["#{if show then '' else 'un'}setClass"] 'has-links'
-      @profileContentLinks["#{if show then 'un' else ''}setClass"] 'links-hidden'
-
   attachListeners:->

     @on 'CommentLinkReceivedClick', (view)=>
@@ -111,7 +96,7 @@

       @staticDefaultItem.show()
       @displaySidebar no
-      # @showLoadingBar()
+
       @emit 'StaticProfileNavLinkClicked', 'CBlogPostActivity', 'static', =>
         @showWrapper @wrappers['static']
         @staticDefaultItem.show()
@@ -122,7 +107,6 @@

     @on 'ActivityLinkClicked', (callback=->)=>
       @addLogic 'activity'
-      # @showLoadingBar()
       @emit 'StaticProfileNavLinkClicked', 'CBlogPostActivity', 'activity', =>
         @showWrapper @wrappers['activity']
         @displaySidebar yes
@@ -168,8 +152,6 @@
       unless @profileUser
         callback()
         return
-
-      @showLoadingBar() unless type in ['static']

       facets = [facets] if 'string' is typeof facets

@@ -598,27 +580,32 @@
         controller.showNoItemWidget() unless type in ['static']

-    @hideLoadingBar()
-
-  hideLoadingBar:->
-    # @profileLoadingBar.unsetClass 'active'
-    # @profileLoaderView.hide()
-
-  showLoadingBar:->
-    # @profileLoadingBar.setClass 'active'
-    # @profileLoaderView.show()
-
-
-  reviveVisitorViews:->
+  reviveVisitorViews:-> # put onboarding stuff here
+
+  displaySidebar:(show=yes,delay=250)->
+    @utils.wait delay, =>
+      @profileContentList["#{if show then '' else 'un'}setClass"] 'has-links'
+      @profileContentLinks["#{if show then 'un' else ''}setClass"] 'links-hidden'

   getAllowedTypes:(@profileUser)->
     allowedTypes = @profileUser.profile.staticPage?.showTypes or CONTENT_TYPES
+

   sanitizeStaticContent:(view)->
     view.$(".content-item > .has-markdown > span.data a").each (i,element)->
       $(element).attr target : '_blank'

+
+  addLogic:(type,callback=->)->
+      unless @controllers[type]?
+        @addLogicForType type, =>
+          @controllers[type]?.hideLazyLoader()
+          callback()
+      else
+        @controllers[type]?.hideLazyLoader()
+        @showWrapper @wrappers[type]
+        callback()

   addLogicForType:(type,callback=->)->
     switch type
@@ -630,6 +617,7 @@

       when 'static'
        @addStaticLogic callback
+

   addActivityLogic:(callback=->)->
     activityController = new ActivityListController
@@ -659,7 +647,7 @@
         bypass    : yes
       , (err,activities)=>
         @appendActivities err,activities,'activity'
-        @hideLoadingBar()
+

     @controllers['activity'] = activityController
     @wrappers['activity']    = activityListWrapper
@@ -667,8 +655,6 @@
     callback()

   addAboutLogic:(callback=->)->
-
-    @showLoadingBar()

     @wrappers['about'] = yes

@@ -681,7 +667,7 @@
             about : about or ''
           , @profileUser

-          @hideLoadingBar()
+

           @wrappers['about'] = aboutWrapper
           @showWrapper aboutWrapper


disable stdout and stderr with null value

--- bootstrap/mecano.coffee
+++ bootstrap/mecano.coffee
@@ -29,12 +29,11 @@
     m = (action, options) ->
       options.ssh ?= ctx.ssh
       options.log ?= ctx.log
-      options.stdout ?= ctx.log.out
-      options.stderr ?= ctx.log.err
+      options.stdout = ctx.log.out if typeof options.stdout is 'undefined'
+      options.stderr = ctx.log.err if typeof options.stderr is 'undefined'
       options.installed = cache['mecano:installed']
       options.updates = cache['mecano:updates']
       options
-
     [ 'chmod', 'chown', 'copy', 'download', 'execute',
       'extract', 'git', 'ini', 'krb5_addprinc', 'krb5_delprinc',
       'ldap_acl', 'ldap_index', 'ldap_schema', 'link', 'mkdir',


Teamwork: Only host user can invite someone to session.

--- client/Teamwork/Views/teamworkchatpane.coffee
+++ client/Teamwork/Views/teamworkchatpane.coffee
@@ -185,7 +185,7 @@
       tooltip  :
         title  : messages.botTooltip

-    if KD.isLoggedIn()
+    if @getDelegate().amIHost()
       @avatars.addSubView new KDCustomHTMLView
         cssClass : "tw-add-user"
         tooltip  :


Fix wombat crossing name

Fix wombat crossing name

--- dev/au.cson
+++ dev/au.cson
@@ -611,7 +611,7 @@

 'wombat-crossing_v2':
   category: 'warning'
-  name: 'Slippery motorcycles'
+  name: 'Wombat crossing'
   variation: 'victoria'
   elements: [
     { type: 'square-rounded', color: 'black', transform: '{square_to_diamond}' }


WorkspaceLayout: Forward cssClass options too while creating layouts and splits inside of them

--- client/Workspace/views/workspacelayout.coffee
+++ client/Workspace/views/workspacelayout.coffee
@@ -2,12 +2,14 @@

   init: ->
     @splitViews    = {}
-    {direction, sizes, views, splitName} = @getOptions().layoutOptions
+
+    {direction, sizes, views, cssClass, splitName} = @getOption 'layoutOptions'
+
     @baseSplitName = splitName
+    @addSubView @createSplitView \
+      {type: direction, sizes, cssClass, viewsConfig:views}, splitName

-    @addSubView @createSplitView direction, sizes, views, splitName
-
-  createSplitView: (type, sizes, viewsConfig, splitName) ->
+  createSplitView: ({type, sizes, viewsConfig, cssClass}, splitName) ->
     views = []

     viewsConfig.forEach (config, index) =>
@@ -15,7 +17,12 @@
         {options}   = config
         {splitName} = options

-        splitView = @createSplitView options.direction, options.sizes, config.views
+        splitView = @createSplitView
+          type        : options.direction
+          sizes       : options.sizes
+          cssClass    : options.cssClass
+          viewsConfig : config.views
+
         @splitViews[splitName] = splitView  if splitName
         views.push splitView
       else
@@ -25,7 +32,7 @@

         views.push wrapper

-    splitView = new SplitViewWithOlderSiblings { type, sizes, views }
+    splitView = new SplitViewWithOlderSiblings { type, sizes, views, cssClass }
     @splitViews[@baseSplitName] = splitView  if @baseSplitName
     splitView.on "ResizeDidStop", => @emitResizedEventToPanes()


Path locator for navigation menu

Path locator for navigation menu

--- client/app/MainApp/sidebar/sidebarview.coffee
+++ client/app/MainApp/sidebar/sidebarview.coffee
@@ -29,6 +29,7 @@
       view           : new NavigationList
         type         : "navigation"
         itemClass    : NavigationLink
+        testPath     : "navigation-list"
       wrapper        : no
       scrollView     : no
     ,


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/sprite.coffee
+++ src/sprite.coffee
@@ -55,8 +55,8 @@
     @param {PowerCanvas} canvas Reference to the canvas to draw the sprite on
     @param {Number} x Position on the x axis to draw the sprite
     @param {Number} y Position on the y axis to draw the sprite
-    @param {Number} width Width to tile the
-    @param {Number} height Height to tile the
+    @param {Number} width How far to tile the sprite on the x-axis
+    @param {Number} height Height to tile the sprite
     ###
     fill: (canvas, x, y, width, height, repeat="repeat") ->
       pattern = canvas.createPattern(image, repeat)


Add initial US signs info

Add initial US signs info

--- dev/us.cson
+++ dev/us.cson
@@ -0,0 +1,614 @@
+regulatory_no_entry:
+  category: 'regulatory'
+  name: 'no entry'
+  elements: [
+    { type: 'circle-bg', value: 'red' }
+    { type: 'circle-bar', value: 'white' }
+  ]
+
+regulatory_stop:
+  category: 'regulatory'
+  name: 'stop'
+  elements: [
+    { type: 'octagon', value: 'red' }
+    { type: 'stop', value: 'white' }
+  ]
+
+warning_merge:
+  category: 'warning'
+  name: 'merge'
+  elements: [
+    { type: 'diamond-bg', value: 'yellow' }
+    { type: 'merge', value: 'black' }
+  ]
+
+warning_pedestrian_crossing:
+  category: 'warning'
+  name: 'pedestrian crossing'
+  elements: [
+    { type: 'diamond-bg', value: 'yellow' }
+    { type: 'pedestrian', value: 'black' }
+  ]
+
+warning_traffic_light:
+  category: 'warning'
+  name: 'traffic light'
+  elements: [
+    { type: 'diamond-bg', value: 'yellow' }
+    { type: 'traffic-l-bg', valued: 'black' }
+    { type: 'traffic-l-g', valued: 'green' }
+    { type: 'traffic-l-r', valued: 'red' }
+    { type: 'traffic-l-y', valued: 'yellow' }
+  ]
+
+regulatory_no_parking:
+  category: 'regulatory'
+  name: 'no parking'
+  elements: [
+    { type: 'circle-bg', value: 'white' }
+    { type: 'circle-o', value: 'red' }
+    { type: 'parking', value: 'black' }
+    { type: 'circle-line-l', value: 'red' }
+  ]
+
+warning_road_narrows_right:
+  category: 'warning'
+  name: 'road narrows right'
+  elements: [
+    { type: 'diamond-bg', value: 'yellow' }
+    { type: 'narrow-road-r fit', value: 'black' }
+  ]
+
+warning_road_narrows_left:
+  category: 'warning'
+  name: 'road narrows left'
+  elements: [
+    { type: 'diamond-bg', value: 'yellow' }
+    { type: 'narrow-road-l fit', value: 'black' }
+  ]
+
+warning_road_narrows_both:
+  category: 'warning'
+  name: 'road narrows both'
+  elements: [
+    { type: 'diamond-bg', value: 'yellow' }
+    { type: 'narrow-road-both fit', value: 'black' }
+  ]
+
+warning_curve_turn_left:
+  category: 'warning'
+  name: 'curve turn left'
+  elements: [
+    { type: 'diamond-bg', value: 'yellow' }
+    { type: 'turn-l', value: 'black' }
+  ]
+
+warning_curve_turn_right:
+  category: 'warning'
+  name: 'curve turn right'
+  elements: [
+    { type: 'diamond-bg', value: 'yellow' }
+    { type: 'turn-r', value: 'black' }
+  ]
+
+warning_curve_turn_left:
+  category: 'warning'
+  name: 'curve turn left'
+  elements: [
+    { type: 'diamond-bg', value: 'yellow' }
+    { type: 'turn-l', value: 'black' }
+  ]
+
+regulatory_no_bicycles:
+  category: 'regulatory'
+  name: 'no bicycles'
+  elements: [
+    { type: 'circle-bg', value: 'white' }
+    { type: 'circle-o', value: 'red' }
+    { type: 'bicycle', value: 'black' }
+    { type: 'circle-line-l', value: 'red' }
+  ]
+
+regulatory_no_left_turn:
+  category: 'regulatory'
+  name: 'no left turn'
+  elements: [
+    { type: 'circle-bg', value: 'white' }
+    { type: 'circle-o', value: 'red' }
+    { type: 'turn-90-l', value: 'black' }
+    { type: 'circle-line-l', value: 'red' }
+  ]
+
+regulatory_no_turn_right:
+  category: 'regulatory'
+  name: 'no turn right'
+  elements: [
+    { type: 'circle-bg', value: 'white' }
+    { type: 'circle-o', value: 'red' }
+    { type: 'turn-90-r', value: 'black' }
+    { type: 'circle-line-l', value: 'red' }
+  ]
+
+regulatory_no_u_turn:
+  category: 'regulatory'
+  name: 'no u turn'
+  elements: [
+    { type: 'circle-bg', value: 'white' }
+    { type: 'circle-o', value: 'red' }
+    { type: 'u-turn', value: 'black' }
+    { type: 'circle-line-l', value: 'red' }
+  ]
+
+warning_added_lanes:
+  category: 'warning'
+  name: 'added lanes'
+  elements: [
+    { type: 'diamond-bg', value: 'yellow' }
+    { type: 'added-lanes', value: 'black' }
+  ]
+
+warning_cross_roads:
+  category: 'warning'
+  name: 'cross roads'
+  elements: [
+    { type: 'diamond-bg', value: 'yellow' }
+    { type: 'cross-roads', value: 'black' }
+  ]
+
+warning_side_road_perpendicular_right:
+  category: 'warning'
+  name: 'side road perpendicular right'
+  elements: [
+    { type: 'diamond-bg', value: 'yellow' }
+    { type: 'side-road-p-r', value: 'yellow' }
+  ]
+
+regulatory_no_trucks:
+  category: 'regulatory'
+  name: 'no trucks'
+  elements: [
+    { type: 'circle-bg', value: 'white' }
+    { type: 'circle-o', value: 'red' }
+    { type: 'truck fit', value: 'black' }
+    { type: 'circle-line-l', value: 'red' }
+  ]
+
+warning_turn_right:
+  category: 'warning'
+  name: 'turn right'
+  elements: [
+    { type: 'diamong-bg', value: 'yellow' }
+    { type: 'turn-90-r', value: 'black' }
+  ]
+
+warning_turn_left:
+  category: 'warning'
+  name: 'turn left'
+  elements: [
+    { type: 'diamong-bg', value: 'yellow' }
+    { type: 'turn-90-u', value: 'black' }
+  ]
+
+warning_fire_station:
+  category: 'warning'
+  name: 'fire station'
+  elements: [
+    { type: 'diamong-bg', value: 'yellow' }
+    { type: 'fire-station', value: 'black' }
+  ]
+
+warning_bicycles:
+  category: 'warning'
+  name: 'bicycles'
+  elements: [
+    { type: 'diamong-bg', value: 'yellow' }
+    { type: 'bicycle fit', value: 'black' }
+  ]
+
+warning_dead_end:
+  category: 'warning'
+  name: 'dead end'
+  elements: [
+    { type: 'diamond-bg', value: 'yellow' }
+    { type: 'dead-end', value: 'black' }
+  ]
+
+warning_light_rail_crossing:
+  category: 'warning'
+  name: 'light rail crossing'
+  elements: [
+    { type: 'diamond-bg', value: 'yellow' }
+    { type: 'light-rail', value: 'black' }
+  ]
+
+
+warning_curve_reverse_right:
+  category: 'warning'
+  name: 'curve reverse right'
+  elements: [
+    { type: 'diamond-bg', value: 'yellow' }
+    { type: 'curve-r-r', value: 'black' }
+  ]
+
+warning_curve_reverse_left:
+  category: 'warning'
+  name: 'curve reverse left'
+  elements: [
+    { type: 'diamond-bg', value: 'yellow' }
+    { type: 'curve-r-l', value: 'black' }
+  ]
+
+warning_horizontal_alignment_left:
+  category: 'warning'
+  name: 'horizontal alignment left'
+  elements: [
+    { type: 'diamond-bg', value: 'yellow' }
+    { type: 'h-align-l', value: 'black' }
+  ]
+
+warning_horizontal_alignment_right:
+  category: 'warning'
+  name: 'horizontal alignment right'
+  elements: [
+    { type: 'diamond-bg', value: 'yellow' }
+    { type: 'h-align-r', value: 'black' }
+  ]
+
+warning_highway_divided:
+  category: 'warning'
+  name: 'highway divided'
+  elements: [
+    { type: 'diamond-bg', value: 'yellow' }
+    { type: 'highway-divided', value: 'black' }
+  ]
+
+warning_steep_hill:
+  category: 'warning'
+  name: 'steep hill'
+  elements: [
+    { type: 'diamond-bg', value: 'yellow' }
+    { type: 'steep-hill', value: 'black' }
+  ]
+
+warning_low_clearance:
+  category: 'warning'
+  name: 'low clearance'
+  elements: [
+    { type: 'diamond-bg', value: 'yellow' }
+    { type: 'low-clearance', value: 'black' }
+  ]
+
+warning_offset_roads:
+  category: 'warning'
+  name: 'offset roads'
+  elements: [
+    { type: 'diamond-bg', value: 'yellow' }
+    { type: 'offset-roads', value: 'black' }
+  ]
+
+warning_slippery:
+  category: 'warning'
+  name: 'slipper'
+  elements: [
+    { type: 'diamond-bg', value: 'yellow' }
+    { type: 'slippery', value: 'black' }
+  ]
+
+regulatory_no_left_or_u_turn:
+  category: 'regulatory'
+  name: 'no left or u turn'
+  elements: [
+    { type: 'circle-bg', value: 'white' }
+    { type: 'circle-o', value: 'red' }
+    { type: 'no-l-or-u-turn', value: 'black' }
+    { type: 'circle-line-l', value: 'red' }
+  ]
+
+warning_winding_road:
+  category: 'warning'
+  name: 'winding road'
+  elements: [
+    { type: 'diamond-bg', value: 'yellow' }
+    { type: 'winding-road', value: 'black' }
+  ]
+
+warning_metric_clearance:
+  category: 'warning'
+  name: 'metric clearance'
+  elements: [
+    { type: 'diamond-bg', value: 'yellow' }
+    { type: 'winding-road', value: 'black' }
+  ]
+
+warning_intersection_circular:
+  category: 'warning'
+  name: 'intersection circular'
+  elements: [
+    { type: 'diamond-bg', value: 'yellow' }
+    { type: 'roundabout', value: 'black' }
+  ]
+
+warning_turn_reverse_right
+  category: 'warning'
+  name: 'turn reverse right'
+  elements: [
+    { type: 'diamond-bg', value: 'yellow' }
+    { type: 'turn-reverse-right', value: 'black' }
+  ]
+
+warning_turn_reverse_left
+  category: 'warning'
+  name: 'turn reverse left'
+  elements: [
+    { type: 'diamond-bg', value: 'yellow' }
+    { type: 'turn-reverse-left', value: 'black' }
+  ]
+
+warning_side_road_acute_angle_right
+  category: 'warning'
+  name: 'side road acute angle right'
+  elements: [
+    { type: 'diamond-bg', value: 'yellow' }
+    { type: 'side-road-a-a-r', value: 'black' }
+  ]
+
+warning_side_road_acute_angle_left
+  category: 'warning'
+  name: 'side road acute angle left'
+  elements: [
+    { type: 'diamond-bg', value: 'yellow' }
+    { type: 'side-road-a-a-l', value: 'black' }
+  ]
+
+warning_t_roads
+  category: 'warning'
+  name: 't roads'
+  elements: [
+    { type: 'diamond-bg', value: 'yellow' }
+    { type: 't-roads', value: 'black' }
+  ]
+
+regulatory_no_hitch_hiking:
+  category: 'regulatory'
+  name: 'no hitch hiking'
+  elements: [
+    { type: 'circle-bg', value: 'white' }
+    { type: 'circle-o', value: 'red' }
+    { type: 'hitchhike', value: 'black' }
+    { type: 'circle-line-l', value: 'red' }
+  ]
+
+warning_pass_left_or_right
+  category: 'warning'
+  name: 'pass left or right'
+  elements: [
+    { type: 'diamond-bg', value: 'yellow' }
+    { type: 'pass-l-r', value: 'black' }
+  ]
+
+warning_share_roads
+  category: 'warning'
+  name: 'share roads'
+  elements: [
+    { type: 'diamond-bg', value: 'yellow' }
+    { type: 'share-roads', value: 'black' }
+  ]
+
+warning_bickcle_slippery:
+  category: 'warning'
+  name: 'bicycle slippery'
+  elements: [
+    { type: 'diamond-bg', value: 'yellow' }
+    { type: 'bicycle-slippery', value: 'black' }
+  ]
+
+regulatory_no_pedestrian_crossing:
+  category: 'regulatory'
+  name: 'no pedestrian crossing'
+  elements: [
+    { type: 'circle-bg', value: 'white' }
+    { type: 'circle-o', value: 'red' }
+    { type: 'pedestrian', value: 'black' }
+    { type: 'circle-line-l', value: 'red' }
+  ]
+
+warning_workers_in_road_ahead
+  category: 'warning'
+  name: 'workers_in_road_ahead'
+  elements: [
+    { type: 'diamond-bg', value: 'yellow' }
+    { type: 'roadworks', value: 'black' }
+  ]
+
+warning_play_ground
+  category: 'warning'
+  name: 'play ground'
+  elements: [
+    { type: 'diamond-bg', value: 'yellow' }
+    { type: 'play-ground', value: 'black' }
+  ]
+
+warning_falling_rocks
+  category: 'warning'
+  name: 'falling rocks'
+  elements: [
+    { type: 'diamond-bg', value: 'yellow' }
+    { type: 'falling-rocks', value: 'black' }
+  ]
+
+warning_hairpin_curve
+  category: 'warning'
+  name: 'hairpin curve'
+  elements: [
+    { type: 'diamond-bg', value: 'yellow' }
+    { type: 'hairpin-curve', value: 'black' }
+  ]
+
+warning_end
+  category: 'warning'
+  name: 'end'
+  elements: [
+    { type: 'diamond-bg', value: 'yellow' }
+    { type: 'end', value: 'black' }
+  ]
+
+warning_loop_270_degree:
+  category: 'warning'
+  name: 'loop 270 degree'
+  elements: [
+    { type: 'diamond-bg', value: 'yellow' }
+    { type: 'loop-270', value: 'black' }
+  ]
+
+warning_y_roads
+  category: 'warning'
+  name: 'y roads'
+  elements: [
+    { type: 'diamond-bg', value: 'yellow' }
+    { type: 'y-roads', value: 'black' }
+  ]
+
+regulatory_no_straight_through:
+  category: 'regulatory'
+  name: 'no straight through'
+  elements: [
+    { type: 'circle-bg', value: 'white' }
+    { type: 'circle-o', value: 'red' }
+    { type: 'arrow-up', value: 'black' }
+    { type: 'circle-line-l', value: 'red' }
+  ]
+
+warning_trucks_rollover
+  category: 'warning'
+  name: 'trucks rollover'
+  elements: [
+    { type: 'diamond-bg', value: 'yellow' }
+    { type: 'trucks-rollover', value: 'black' }
+  ]
+
+warning_added_lane_from_entering_roadway:
+  category: 'warning'
+  name: 'added lane from entering roadway'
+  elements: [
+    { type: 'diamond-bg', value: 'yellow' }
+    { type: 'added-lane-entering-roadway', value: 'black' }
+  ]
+
+warning_flaggers_in_road_ahead
+  category: 'warning'
+  name: 'flaggers in road ahead'
+  elements: [
+    { type: 'diamond-bg', value: 'yellow' }
+    { type: 'flaggers', value: 'black' }
+  ]
+
+warning_skewed_railroads_crossing
+  category: 'warning'
+  name: 'skewed railroads crossing'
+  elements: [
+    { type: 'diamond-bg', value: 'yellow' }
+    { type: 'skewed-railroads', value: 'black' }
+  ]
+
+warning_trucks_rollover
+  category: 'warning'
+  name: 'trucks rollover'
+  elements: [
+    { type: 'diamond-bg', value: 'yellow' }
+    { type: 'truck-rollover', value: 'black' }
+  ]
+
+warning_lightground_railroad_crossing:
+  category: 'warning'
+  name: 'light rail crossing'
+  elements: [
+    { type: 'diamond-bg', value: 'yellow' }
+    { type: 'lightground-rail-crossing', value: 'black' }
+  ]
+
+regulatory_national_network_lane
+  category: 'regulatory'
+  name: 'national_network_lane'
+  elements: [
+    { type: 'circle-bg', value: 'white' }
+    { type: 'circle-o', value: 'green' }
+    { type: 'lorry', value: 'black' }
+  ]
+
+warning_motorcycle_crossing:
+  category: 'warning'
+  name: 'motorcycle crossing'
+  elements: [
+    { type: 'diamond-bg', value: 'yellow' }
+    { type: 'motorcycle', value: 'black' }
+  ]
+
+warning_railroad
+  category: 'warning'
+  name: 'railroad'
+  elements: [
+    { type: 'circle-bg', value: 'yellow' }
+    { type: 'circle-x', value: 'black' }
+    { type: 'railroad', value: 'black' }
+  ]
+
+regulatory_hazardous_material_prohibited:
+  category: 'regulatory'
+  name: 'hazardous material prohibited'
+  elements: [
+    { type: 'circle-bg', value: 'white' }
+    { type: 'circle-o', value: 'red' }
+    { type: 'hazardous-material', value: 'black' }
+    { type: 'circle-o', value: 'red' }
+  ]
+
+warning_truck_rollover_with_speed:
+  category: 'warning'
+  name: 'truck rollover with speed'
+  elements: [
+    { type: 'diamond-bg', value: 'yellow' }
+    { type: 'truck-rollover', value: 'black' }
+    { type: 'truck-rollover-speed', value: 'black' }
+  ]
+
+regulatory_no_rollerblading:
+  category: 'regulatory'
+  name: 'no rollerblading'
+  elements: [
+    { type: 'circle-bg', value: 'white' }
+    { type: 'circle-o', value: 'red' }
+    { type: 'rollerblading', value: 'black' }
+    { type: 'circle-line-l', value: 'red' }
+  ]
+
+warning_triple_2_traverse_curve
+  category: 'warning'
+  name: 'triple 2 traverse curve'
+  elements: [
+    { type: 'diamond-bg', value: 'yellow' }
+    { type: 't-2-traverse-curve', value: 'black' }
+  ]
+
+warning_double_reverse_curve
+  category: 'warning'
+  name: 'double reverse curve'
+  elements: [
+    { type: 'diamond-bg', value: 'yellow' }
+    { type: 'double-reverse-curve', value: 'black' }
+  ]
+
+warning_triple_2_traverse_curve
+  category: 'warning'
+  name: 'triple 2 traverse curve'
+  elements: [
+    { type: 'diamond-bg', value: 'yellow' }
+    { type: 't-2-traverse-curve', value: 'black' }
+  ]
+
+warning_single_2_reverse_curve
+  category: 'warning'
+  name: 'single 2 reverse curve'
+  elements: [
+    { type: 'diamond-bg', value: 'yellow' }
+    { type: 's-2-reverse-curve', value: 'black' }
+  ]


Handle set machine user event only in active state

--- client/ide/lib/collaborationcontroller.coffee
+++ client/ide/lib/collaborationcontroller.coffee
@@ -516,8 +516,6 @@

   broadcastMachineUserChange: (participants, state) ->

-    return  if @stateMachine?.state is 'Active'
-
     type = "#{if state then 'Set' else 'Unset'}MachineUser"

     @broadcastMessage {type, participants}
@@ -732,6 +730,8 @@

     @updateWorkspaceSnapshotModel()

+    @on 'SetMachineUser', @bound 'broadcastMachineUserChange'
+

   transitionViewsToActive: ->

@@ -749,6 +749,8 @@
   onCollaborationEnding: ->

     @chat.settingsPane.endSession.disable()
+
+    @off 'SetMachineUser'

     if @amIHost
       @endCollaborationForHost =>
@@ -938,7 +940,7 @@
     setMachineUser @mountedMachine, usernames, share, (err) =>
       return callback err  if err

-      @broadcastMachineUserChange usernames, share
+      @emit 'SetMachineUser', usernames, share

       callback null


conform to geordi api

conform to geordi api

--- geordi.coffee
+++ geordi.coffee
@@ -12,7 +12,7 @@
   eventData =
     tme: Date.now()
     projectToken: 'penguin'
-    userUD: userZooniverseId
+    userID: userZooniverseId
     type: 'competition'
     relatedID: 'opt-in'


Home: minor register form fixes

Home: minor register form fixes

--- client/landing/app/coffee/home/AppView.coffee
+++ client/landing/app/coffee/home/AppView.coffee
@@ -8,7 +8,7 @@
   constructor:->
     super

-    {router, appManager} = KD.singletons
+    {router} = KD.singletons

     @pricingButton = new KDButtonView
       title       : 'See Pricing'
@@ -19,7 +19,7 @@
       cssClass    : 'login-form register'
       buttonTitle : 'Sign up'
       callback    : (formData) =>
-        appManager.require 'Login', (controller) =>
+        router.requireApp 'Login', (controller) =>

           controller.getView().showPasswordModal formData, @signUpForm


Cleanup seeds store spec.

Cleanup seeds store spec.

--- spec/javascripts/application/seeds_store_spec.js.coffee
+++ spec/javascripts/application/seeds_store_spec.js.coffee
@@ -2,14 +2,9 @@
   beforeEach (done) ->
     @subject = new SeedsStore('test')
     expect(@subject.clear()).eventually.notify(done)
+  describe 'counts', ->
   it 'counts 0', (done) ->
     expect(@subject.countForAll()).eventually.equal(0).notify(done)
-  it 'counts n', (done) ->
-    @subject.update([{name: 'a'},{name: 'b'},{name: 'c'}])
-    expect(@subject.countForAll()).eventually.equal(3).notify(done)
-  it 'readPod', (done) ->
-    @subject.update([{name: 'a'}, {name: 'b'}])
-    expect(@subject.readPod('a')).eventually.eql([{name: 'a'}]).notify(done)
   describe 'large dataset', ->
     beforeEach () ->
       @listByName = ->
@@ -60,16 +55,22 @@
     beforeEach (done) ->
       expect(@subject.update(@listByName()))
         .eventually.notify(done)
-    beforeEach (done) ->
-      expect(@subject.updateCategories())
-        .eventually.notify(done)
-    it 'updateCategories() + categories()', (done) ->
-      expect(@subject.categories()).eventually
-        .eql([
-          {name: 'a', podsCount: 1},
-          {name: 'b', podsCount: 1},
-          {name: 'c', podsCount: 4}
-        ]).notify(done)
+    it 'counts n', (done) ->
+      expect(@subject.countForAll()).eventually.equal(6).notify(done)
+    it 'readPod', (done) ->
+      expect(@subject.readPod('c')).eventually
+        .eql([@listByName()[2]]).notify(done)
+    describe 'updateCategories()', ->
+      beforeEach (done) ->
+        expect(@subject.updateCategories())
+          .eventually.notify(done)
+      it 'categories()', (done) ->
+        expect(@subject.categories()).eventually
+          .eql([
+            {name: 'a', podsCount: 1},
+            {name: 'b', podsCount: 1},
+            {name: 'c', podsCount: 4}
+          ]).notify(done)
     expectReadAll = () ->
       describe 'readFromAll()', ->
         it 'from the beginning', (done) ->


use getBounds in multicursor

use getBounds in multicursor

--- src/modules/multi-cursor.coffee
+++ src/modules/multi-cursor.coffee
@@ -100,11 +100,11 @@
     @container.appendChild(cursor)
     return cursor

-  _moveCursor: (cursor, reference, side = 'left') ->
-    bounds = reference.getBoundingClientRect()
-    parentBounds = @quill.container.getBoundingClientRect()
-    cursor.elem.style.top = (bounds.top - parentBounds.top + @quill.container.scrollTop) + 'px'
-    cursor.elem.style.left = bounds[side] - parentBounds[side] + 'px'
+  _updateCursor: (cursor) ->
+    bounds = @quill.getBounds(cursor.index)
+    console.log('update', cursor.index, bounds.top)
+    cursor.elem.style.top = (bounds.top - @quill.container.scrollTop) + 'px'
+    cursor.elem.style.left = bounds.left + 'px'
     cursor.elem.style.height = bounds.height + 'px'
     flag = cursor.elem.querySelector('.cursor-flag')
     dom(cursor.elem).toggleClass('top', parseInt(cursor.elem.style.top) <= flag.offsetHeight)
@@ -112,22 +112,6 @@
                     .toggleClass('right', @quill.root.offsetWidth - parseInt(cursor.elem.style.left) <= flag.offsetWidth)
     this.emit(MultiCursor.events.CURSOR_MOVED, cursor)

-  _updateCursor: (cursor) ->
-    @quill.editor.checkUpdate()
-    [leaf, offset] = @quill.editor.doc.findLeafAt(cursor.index, true)
-    guide = document.createElement('span')
-    if leaf?
-      [leftNode, rightNode, didSplit] = dom(leaf.node).split(offset)
-      dom(guide).text(dom.ZERO_WIDTH_NOBREAK_SPACE)
-      leaf.node.parentNode.insertBefore(guide, rightNode)
-    else
-      dom(guide).text(dom.NOBREAK_SPACE)
-      @quill.root.appendChild(guide)
-    this._moveCursor(cursor, guide)
-    dom(guide).remove()
-    dom(leaf.node.parentNode).normalize() if didSplit
-    @quill.editor.selection.update(Quill.sources.SILENT)
-

 Quill.registerModule('multi-cursor', MultiCursor)
 module.exports = MultiCursor


More analytics hooks

More analytics hooks

--- web/coffee/problems.coffee
+++ web/coffee/problems.coffee
@@ -29,10 +29,13 @@
   apiCall "POST", "/api/problems/submit", {pid: input.data("pid"), key: input.val()}
   .done (data) ->
     if data['status'] is 1
+      ga('send', 'event', 'Problem', 'Solve', 'Basic')
       loadProblems()
       setTimeout( ->
         $("div[data-target='#" + input.data("pid") + "']").click()
       , 100)
+    else
+      ga('send', 'event', 'Problem', 'Wrong', 'Basic')
     apiNotify data
     apiCall "GET", "/api/achievements"
     .done (data) ->
@@ -68,6 +71,7 @@
   .done (data) ->
     loadProblems()
     apiNotify data
+    ga('send', 'event', 'Problem', 'Review', 'Basic')
     apiCall "GET", "/api/achievements"
     .done (data) ->
       if data['status'] is 1
@@ -76,6 +80,7 @@

 toggleHint = (e) ->
   pid = $(e.target).data("pid")
+  ga('send', 'event', 'Problem', 'OpenHint', 'Basic')
   apiCall "GET", "/api/problems/hint", {"pid": pid, "source": "basic"}
   #$("#"+pid+"-hint").toggle("fast")


commit時の出力を改善した。

commit時の出力を改善した。

--- gitlab.coffee
+++ gitlab.coffee
@@ -137,15 +137,22 @@
             send_msg type, dst['target'], "We will be missing #{bold(hook.name)} (#{bold(hook.email)}) on Gitlab"

       when "web"
-        message = ""
+        message = []
         branch = hook.ref.split("/")[2..].join("/")
         # if the ref before the commit is 00000, this is a new branch
-        # console.log("hook: %j", hook)
         if /^0+$/.test(hook.before)
-            message = "#{bold(hook.user_name)} pushed a new branch (#{bold(branch)}) to #{bold(hook.repository.name)} (#{underline(hook.repository.homepage)})"
+          message.push("#{bold(hook.user_name)} pushed a new branch (#{bold(branch)}) to #{bold(hook.repository.name)} (#{underline(hook.repository.homepage)})")
         else
-            message = "#{bold(hook.user_name)} pushed #{bold(hook.total_commits_count)} commits to #{bold(branch)} in #{bold(hook.repository.name)} (#{underline(hook.repository.homepage + '/compare/' + hook.before.substr(0,9) + '...' + hook.after.substr(0,9))})"
-        send_msg type, dst['target'], message
+          message.push("#{bold(hook.user_name)} pushed #{bold(hook.total_commits_count)} commits to #{bold(branch)} in #{bold(hook.repository.name)} (#{underline(hook.repository.homepage + '/compare/' + hook.before.substr(0,9) + '...' + hook.after.substr(0,9))})")
+          indent = "    "
+          for c in hook.commits
+            str = c.message.replace /\n/g, "\n#{indent}"
+            message.push("  - #{c.id}")
+            message.push("    #{str}")
+            # message.push("    #{c.message}")
+            message.push("    #{c.url}")
+
+        send_msg type, dst['target'], message.join("\n")

   robot.router.post "/gitlab/system", (req, res) ->
     handler "system", req, res


Ace: Require LineWidgets.

Ace: Require LineWidgets.

--- client/Ace/ace.coffee
+++ client/Ace/ace.coffee
@@ -50,6 +50,10 @@
         @once 'ace.ready', =>
           {@Range} = range

+      requirejs [ 'ace/line_widgets' ], (lineWidgets) =>
+        @once 'ace.ready', =>
+          @lineWidgetManager = new lineWidgets.LineWidgets @editor.getSession()
+          @lineWidgetManager.attach @editor

   setContent: (content, emitFileContentChangedEvent = yes) ->
     @suppressListeners = yes  unless emitFileContentChangedEvent