Comprehensions over break and continue

Comprehensions over break and continue

--- nodes.coffee
+++ nodes.coffee
@@ -186,7 +186,7 @@

   jumps: (o) ->
     for exp in @expressions
-      return true if exp.jumps o
+      return exp if exp.jumps o

   # An Expressions node does not return its entire body, rather it
   # ensures that the final expression is returned.
@@ -283,7 +283,7 @@

   jumps: (o) ->
     return no unless @value in ['break', 'continue', 'debugger']
-    not (o and (o.loop or o.block and (@value isnt 'continue')))
+    if not (o and (o.loop or o.block and (@value isnt 'continue'))) then this else no

   compile: ->
     if @value.reserved then "\"#{@value}\"" else @value
@@ -302,7 +302,7 @@

   isStatement:     YES
   makeReturn:      THIS
-  jumps:           YES
+  jumps:           THIS

   compile: (o, level) ->
     expr = @expression?.makeReturn()
@@ -1139,7 +1139,7 @@
     {expressions} = @body
     return no unless expressions.length
     for node in expressions
-      return yes if node.jumps loop: yes
+      return node if node.jumps loop: yes
     no

   # The main difference from a JavaScript *while* is that the CoffeeScript
@@ -1444,13 +1444,14 @@
   compileNode: (o) ->
     body          = Expressions.wrap [@body]
     lastJumps     = last(body.expressions)?.jumps()
+    @returns      = no if lastJumps and lastJumps instanceof Return
     source        = if @range then @source.base else @source
     scope         = o.scope
     name          = @name  and @name.compile o, LEVEL_LIST
     index         = @index and @index.compile o, LEVEL_LIST
     scope.find(name,  immediate: yes) if name and not @pattern
     scope.find(index, immediate: yes) if index
-    rvar          = scope.freeVariable 'results' if @returns and not lastJumps
+    rvar          = scope.freeVariable 'results' if @returns
     ivar          = (if @range then name else index) or scope.freeVariable 'i'
     name          = ivar if @pattern
     varPart       = ''
@@ -1472,7 +1473,7 @@
         lvar        = scope.freeVariable 'len'
         stepPart    = if @step then "#{ivar} += #{ @step.compile(o, LEVEL_OP) }" else "#{ivar}++"
         forPart     = "#{ivar} = 0, #{lvar} = #{svar}.length; #{ivar} < #{lvar}; #{stepPart}"
-    if @returns and not lastJumps
+    if @returns
       resultPart    = "#{@tab}#{rvar} = [];\n"
       returnResult  = "\n#{@tab}return #{rvar};"
       body          = Push.wrap rvar, body
@@ -1530,7 +1531,7 @@

   jumps: (o = {block: yes}) ->
     for [conds, block] in @cases
-      return yes if block.jumps o
+      return block if block.jumps o
     @otherwise?.jumps o

   makeReturn: ->


removed useless property accesses from for-of

--- nodes.coffee
+++ nodes.coffee
@@ -1380,12 +1380,13 @@
     source        = if range then @source.base else @source
     codeInBody    = @body.contains (n) -> n instanceof CodeNode
     scope         = o.scope
-    name          = (@name and @name.compile(o)) or scope.freeVariable 'i'
-    index         = @index and @index.compile o
+    name          = @name ?.compile o
+    index         = @index?.compile o
     scope.find(name,  immediate: yes) if name and not @pattern and (range or not codeInBody)
     scope.find(index, immediate: yes) if index
     rvar          = scope.freeVariable 'result' unless topLevel
-    ivar          = if codeInBody then scope.freeVariable 'i' else if range then name else index or scope.freeVariable 'i'
+    ivar          = (if codeInBody then null else if range then name else index) or
+                    scope.freeVariable 'i'
     varPart       = ''
     guardPart     = ''
     body          = Expressions.wrap([@body])


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/approachable.coffee
+++ src/approachable.coffee
@@ -8,7 +8,7 @@

       I[name] = I[name].approach(target, approachBy)

-  addCooldown: (name, options={target: 0, approachBy: 1}) ->
+  cooldown: (name, options={target: 0, approachBy: 1}) ->
     unless I.cooldowns[name]
       I.cooldowns[name] = options
       I[name] = value if options.value?


Organize requires

Organize requires

--- src/atom-package.coffee
+++ src/atom-package.coffee
@@ -1,11 +1,13 @@
+path = require 'path'
+
+_ = require 'underscore-plus'
+CSON = require 'season'
+fs = require 'fs-plus'
+{Emitter} = require 'emissary'
+Q = require 'q'
+
+{$} = require './space-pen-extensions'
 Package = require './package'
-fs = require 'fs-plus'
-path = require 'path'
-_ = require 'underscore-plus'
-Q = require 'q'
-{$} = require './space-pen-extensions'
-CSON = require 'season'
-{Emitter} = require 'emissary'
 ScopedProperties = require './scoped-properties'

 # Loads and activates a package's main module and resources such as


Different touch - different heads

Different touch - different heads

--- jarkle/client/webgl/web_gl_visualisation.coffee
+++ jarkle/client/webgl/web_gl_visualisation.coffee
@@ -71,6 +71,8 @@

     @touchMap = {}

+    @currentHeadIndex = 0
+
     @scene = new THREE.Scene()

     @_initCubes()
@@ -188,16 +190,20 @@

     cube = @_cycleCube(cartesianX, cartesianY)

+    userId = message.userId
+    unless @touchMap[userId]?
+      @currentHeadIndex = (@currentHeadIndex + 1) % 2
+      @touchMap[userId] =
+        heads: if @currentHeadIndex % 2 == 0 then @trailHeads else @foxHeads
     switch message.type
       when NoteMessenger.NOTE_START, NoteMessenger.NOTE_CONTINUE
-        @touchMap[message.identifier] =
+        @touchMap[userId][message.identifier] =
           on: true
           x: cartesianX
           y: cartesianY
           cube: cube
-          userId: message.userId
       when NoteMessenger.NOTE_END
-        @touchMap[message.identifier].on = false
+        @touchMap[userId][message.identifier].on = false

   _cycleCube: (x, y) ->
@@ -256,25 +262,26 @@
         cube.visible = false
         cube.active = false

-    headIndex = 0
     update = new Date().getTime()
-    for id, touchData of @touchMap
-      trailHead = @trailHeads[headIndex]
-      # Trail heads may have not loaded.
-      unless trailHead?
-        continue
-      if touchData.on
-        cube = @_cycleCube(touchData.x, touchData.y)
-        trailHead.position.copy(cube.position)
-        trailHead.traverse (obj) ->
-          obj.visible = true
-      else if trailHead.updatedOn != update
-        trailHead.position.z -= CUBE_DECREMENTS
-        trailHead.updatedOn = update
-        if trailHead.position.z < -CUBE_DISTANCE_LIMIT
+    for userId, touches of @touchMap
+      headIndex = 0
+      for id, touchData of touches
+        trailHead = touches.heads[headIndex]
+        # Trail heads may have not loaded.
+        unless trailHead?
+          continue
+        if touchData.on
+          cube = @_cycleCube(touchData.x, touchData.y)
+          trailHead.position.copy(cube.position)
           trailHead.traverse (obj) ->
-            obj.visible = false
-      headIndex = (headIndex + 1) % @trailHeads.length
+            obj.visible = true
+        else if trailHead.updatedOn != update
+          trailHead.position.z -= CUBE_DECREMENTS
+          trailHead.updatedOn = update
+          if trailHead.position.z < -CUBE_DISTANCE_LIMIT
+            trailHead.traverse (obj) ->
+              obj.visible = false
+        headIndex = (headIndex + 1) % touches.heads.length

     for foxHead, foxHeadIndex in @foxHeads
       if not foxHead.active


Remove unused varaible assignment

Remove unused varaible assignment

--- coffee/collection.coffee
+++ coffee/collection.coffee
@@ -39,7 +39,6 @@
       req.query.after = new Date(parseInt(req.query.after, 10)).toISOString()
     query.endret = {$gte:req.query.after}

-  fields = {}
   fields = endret: true, status: true, navn: true

   options =


Add comments to collection.post()

Add comments to collection.post()

--- coffee/collection.coffee
+++ coffee/collection.coffee
@@ -30,6 +30,8 @@

 exports.post = (req, res, next) ->
   return res.send 400, 'Missing Request Payload' if Object.keys(req.body).length is 0
+  # @TODO fix required fields
+  # @TODO check _id if existing
   req.col.insert req.body, safe: true, (err, doc) ->
     return next(err) if err
     return res.json 201, documents: doc, count: doc.length


Use atom.getConfigDirPath() instead of atom.config.configDirPath

--- lib/sync-settings.coffee
+++ lib/sync-settings.coffee
@@ -152,7 +152,7 @@
       path = require('path')
       files[path.basename(initPath)] = content: (@fileContent initPath) ? "# initialization file (not found)"
     if atom.config.get('sync-settings.syncSnippets')
-      files["snippets.cson"] = content: (@fileContent atom.config.configDirPath + "/snippets.cson") ? "# snippets file (not found)"
+      files["snippets.cson"] = content: (@fileContent atom.getConfigDirPath() + "/snippets.cson") ? "# snippets file (not found)"

     for file in atom.config.get('sync-settings.extraFiles') ? []
       ext = file.slice(file.lastIndexOf(".")).toLowerCase()
@@ -162,7 +162,7 @@
       cmtend = ""
       cmtend = "*/" if ext in [".css"]
       files[file] =
-        content: (@fileContent atom.config.configDirPath + "/#{file}") ? "#{cmtstart} #{file} (not found) #{cmtend}"
+        content: (@fileContent atom.getConfigDirPath() + "/#{file}") ? "#{cmtstart} #{file} (not found) #{cmtend}"

     @createClient().gists.edit
       id: @getGistId()
@@ -244,15 +244,15 @@
             fs.writeFileSync atom.styles.getUserStyleSheetPath(), file.content if atom.config.get('sync-settings.syncStyles')

           when 'init.coffee'
-            fs.writeFileSync atom.config.configDirPath + "/init.coffee", file.content if atom.config.get('sync-settings.syncInit')
+            fs.writeFileSync atom.getConfigDirPath() + "/init.coffee", file.content if atom.config.get('sync-settings.syncInit')

           when 'init.js'
-            fs.writeFileSync atom.config.configDirPath + "/init.js", file.content if atom.config.get('sync-settings.syncInit')
+            fs.writeFileSync atom.getConfigDirPath() + "/init.js", file.content if atom.config.get('sync-settings.syncInit')

           when 'snippets.cson'
-            fs.writeFileSync atom.config.configDirPath + "/snippets.cson", file.content if atom.config.get('sync-settings.syncSnippets')
-
-          else fs.writeFileSync "#{atom.config.configDirPath}/#{filename}", file.content
+            fs.writeFileSync atom.getConfigDirPath() + "/snippets.cson", file.content if atom.config.get('sync-settings.syncSnippets')
+
+          else fs.writeFileSync "#{atom.getConfigDirPath()}/#{filename}", file.content

       atom.config.set('sync-settings._lastBackupHash', res.history[0].version)


Use Script to load Sizzle.js

Use Script to load Sizzle.js

--- lib/zombie/sizzle.coffee
+++ lib/zombie/sizzle.coffee
@@ -1,6 +1,7 @@
 # Select document elements using Sizzle.js.
 fs = require("fs")
-sizzle = fs.readFileSync(__dirname + "/../../dep/sizzle.js", "utf8")
+vm = process.binding("evals")
+sizzle = new vm.Script(fs.readFileSync(__dirname + "/../../dep/sizzle.js", "utf8"), "sizzle.js")
 core = require("jsdom").dom.level3.core

 close = core.HTMLDocument.prototype.close
@@ -9,9 +10,9 @@
   window = @parentWindow

   # Load Sizzle and add window.find. This only works if we parsed a document.
-  if @documentElement
+  if window && @documentElement
     ctx = process.binding("evals").Script.createContext(window)
     ctx.window = window
     ctx.document = this
-    process.binding("evals").Script.runInContext sizzle, ctx
+    sizzle.runInContext ctx
     @find = (selector, context)-> new window.Sizzle(selector, context)


changed getData to getFormData

changed getData to getFormData

--- client/app/Applications/Account.kdapplication/account/accKiteList.coffee
+++ client/app/Applications/Account.kdapplication/account/accKiteList.coffee
@@ -70,11 +70,12 @@
     @emit "KiteUpdated", kite

   createKite : (form)->
-    data = form.getData()
+    data = form.getFormData()
     KD.remote.api.JKite.create
       description  : data.description
       kiteName     : data.kiteName
-      #count        : data.kiteCallLimit
+      #count        : data.kiteCallLimit
+      #isPublic     : data.publicPrivate
       (err, kite) =>
         if err
           @notify err.message, "fail"
@@ -112,7 +113,7 @@
       overlay   : yes
       cssClass  : "kite-kdmodal"
       width     : 500
-      height    : 187
+      height    : 233
       view      : form
       buttons   :
         "Create New Kite" :
@@ -134,6 +135,11 @@
     @addSubView kiteNameView  = new KDView
       cssClass : "modalformline"
     kiteNameView.addSubView kiteName  = new KDInputView name : "kiteName", placeholder : "Running Kite name..."
+
+    # @addSubView publicOrPrivate   = new KDView
+    #   cssClass : "modalformline"
+    # publicOrPrivate.addSubView yesNo  = new KDOnOffSwitch {name : "publicPrivate", labels : ['Public', 'Private'] }
+

     # @addSubView kiteApiCallLimitView  = new KDView
     #   cssClass : "modalformline"


add comment

add comment

--- client/app/Applications/StartTab.kdapplication/views/appthumbview.coffee
+++ client/app/Applications/StartTab.kdapplication/views/appthumbview.coffee
@@ -97,8 +97,9 @@
             top  : 4
             left : -5
         click    : =>
+          # don't log 3rd party app developer errors
+          @utils.stopLoggingToRollbar()
           @showLoader()
-          @utils.stopLoggingToRollbar()
           @getSingleton("kodingAppsController").compileApp \
             manifest.name, (err)=>
               @hideLoader()


Removed session support, needs to improved for UI/UX.

--- client/landing/app/CommonViews/applicationview/applicationtabview.coffee
+++ client/landing/app/CommonViews/applicationview/applicationtabview.coffee
@@ -11,7 +11,8 @@

     super options, data

-    @isSessionEnabled = options.saveSession and options.sessionName
+    # TODO: fatihacet: session logic should refactored to ui and ux
+    @isSessionEnabled = no #options.saveSession and options.sessionName

     appView = @getDelegate()


Support variations of `\ref{}`, such as `\cref{}`, `\eqref{}`, etc..

--- services/web/public/coffee/ide/editor/directives/aceEditor/auto-complete/AutoCompleteManager.coffee
+++ services/web/public/coffee/ide/editor/directives/aceEditor/auto-complete/AutoCompleteManager.coffee
@@ -47,23 +47,24 @@
 					lineUpToCursor = editor.getSession().getTextRange(upToCursorRange)
 					commandFragment = getLastCommandFragment(lineUpToCursor)
 					if commandFragment
-						refMatch = commandFragment.match(/^~?\\ref{([^}]*, *)?(\w*)/)
+						refMatch = commandFragment.match(/^~?\\([a-z]*ref){([^}]*, *)?(\w*)/)
 						if refMatch
 							beyondCursorRange = new Range(pos.row, pos.column, pos.row, 99999)
 							lineBeyondCursor = editor.getSession().getTextRange(beyondCursorRange)
 							needsClosingBrace = !lineBeyondCursor.match(/^[^{]*}/)
-							currentArg = refMatch[1]
+							commandName = refMatch[1]
+							currentArg = refMatch[2]
 							result = []
 							result.push {
-								caption: "\\ref{}",
-								snippet: "\\ref{}",
+								caption: "\\#{commandName}{}",
+								snippet: "\\#{commandName}{}",
 								meta: "cross-reference",
 								score: 11000
 							}
 							for label in labelsManager.getAllLabels()
 								result.push {
-									caption: "\\ref{#{label}#{if needsClosingBrace then '}' else ''}",
-									value: "\\ref{#{label}#{if needsClosingBrace then '}' else ''}",
+									caption: "\\#{commandName}{#{label}#{if needsClosingBrace then '}' else ''}",
+									value: "\\#{commandName}{#{label}#{if needsClosingBrace then '}' else ''}",
 									meta: "cross-reference",
 									score: 10000
 								}


Fix duration calculate

Fix duration calculate

--- src/chart-d3-bar.coffee
+++ src/chart-d3-bar.coffee
@@ -76,7 +76,7 @@

     el = @getRootElement()
       .transition()
-      .duration(options.animationSteps * 17.333)
+      .duration(@duration())
       .ease(options.animationEasing)
     @transitBar(el, chartHeight, yScale)
     @transitBarBorder(el, chartHeight)


ClampBounds docs

ClampBounds docs

--- src/clamp_bounds.coffee
+++ src/clamp_bounds.coffee
@@ -10,7 +10,7 @@
     width: 5
     height: 17

-  # put
+  # put the player
   player.I.x = -400
   player.update()
 </pre></code>


ClampBounds docs

ClampBounds docs

--- src/clamp_bounds.coffee
+++ src/clamp_bounds.coffee
@@ -25,7 +25,7 @@
   # => 2.5 # half the player's width

   player.I.y
-  # => 311.5 # The default App.height ()
+  # => 311.5 # The default App.height (320)


removing call to deprecated sys.p() in the REPL, now it's 'puts inspect value'

--- repl.coffee
+++ repl.coffee
@@ -22,7 +22,7 @@
 run: (buffer) ->
   try
     val: CoffeeScript.run buffer.toString(), {no_wrap: true, globals: true, source: 'repl'}
-    p val if val isnt undefined
+    puts inspect val if val isnt undefined
   catch err
     puts err.stack or err.toString()
   print prompt


#805 Fix render bug

#805 Fix render bug

--- app/assets/javascripts/oxalis/model/binary/plane2d.coffee
+++ app/assets/javascripts/oxalis/model/binary/plane2d.coffee
@@ -302,16 +302,17 @@
     map = new Array(@MAP_SIZE)
     map[0] = undefined

-    for i in [Math.min(@cube.LOOKUP_DEPTH_UP, @cube.ZOOM_STEP_COUNT - zoomStep - 1)...0]
-
-      bucket = [
-        bucket_x >> i
-        bucket_y >> i
-        bucket_z >> i
-        zoomStep + i
-      ]
-
-      map[0] = bucket if @cube.isBucketLoadedByZoomedAddress(bucket)
+    if zoomStep < @cube.ZOOM_STEP_COUNT
+      for i in [Math.min(@cube.LOOKUP_DEPTH_UP, @cube.ZOOM_STEP_COUNT - zoomStep - 1)...0]
+
+        bucket = [
+          bucket_x >> i
+          bucket_y >> i
+          bucket_z >> i
+          zoomStep + i
+        ]
+
+        map[0] = bucket if @cube.isBucketLoadedByZoomedAddress(bucket)

     if zoomStep and @enhanceRenderMap(map, 0, [bucket_x, bucket_y, bucket_z, zoomStep], map[0], @cube.LOOKUP_DEPTH_DOWN)


cleanup of bullshit

cleanup of bullshit

--- app/assets/javascripts/uploadcare/widget/dialog.js.coffee
+++ app/assets/javascripts/uploadcare/widget/dialog.js.coffee
@@ -26,17 +26,15 @@
           .hide()
           .appendTo('body')

-        closeCallback = => @close()
-
-        @content.on 'click', (e) ->
+        @content.on 'click', (e) =>
           e.stopPropagation()
-          closeCallback(e) if e.target == e.currentTarget
+          @close() if e.target == e.currentTarget

         closeButton = @content.find('@uploadcare-dialog-close')
-        closeButton.on 'click', closeCallback
+        closeButton.on 'click', @close.bind(this)

-        $(window).on 'keydown', (e) ->
-          closeCallback(e) if e.which == 27 # Escape
+        $(window).on 'keydown', (e) =>
+          @close(e) if e.which == 27 # Escape

         @tabs = {}
         for tabName in @tabNames when tabName not of @tabs
@@ -74,8 +72,6 @@
       addTab: (name) ->
         {tabs} = uploadcare.widget

-        selectedFileCallback = @fileSelected.bind(this)
-
         tabCls = switch name
           when 'file' then tabs.FileTab
           when 'url' then tabs.UrlTab
@@ -85,7 +81,7 @@

         return false if not tabCls

-        tab = new tabCls(this, @settings, selectedFileCallback)
+        tab = new tabCls this, @settings, @fileSelected.bind(this)

         if tab
           $('<li>')


is not haskell, which is sad

is not haskell, which is sad

--- app/assets/javascripts/uploadcare/widget/dialog.js.coffee
+++ app/assets/javascripts/uploadcare/widget/dialog.js.coffee
@@ -29,7 +29,7 @@
           @__createDialog()

           @always @__closeDialog
-        .pipe(files.toFile, () -> 'dialog was closed')
+        .pipe(files.toFile, -> 'dialog was closed')
         .promise()


oauth: use bongo.Base instead of jgraphical

--- workers/social/lib/social/models/oauth.coffee
+++ workers/social/lib/social/models/oauth.coffee
@@ -1,8 +1,7 @@
-jraphical = require 'jraphical'
+bongo    = require 'bongo'
+{secure} = bongo

-module.exports = class OAuth extends jraphical.Module
-  {secure} = require 'bongo'
-
+module.exports = class OAuth extends bongo.Base
   @share()

   @set


payment: decorate each plan list item with an "Upgrade" button

--- client/app/MainApp/payment/planupgradeform.coffee
+++ client/app/MainApp/payment/planupgradeform.coffee
@@ -7,10 +7,20 @@
     @listController = new KDListViewController
       itemClass: GroupPlanListItem

+    @listView = @listController.getListView()
+
+    @listView.on 'ItemWasAdded', (item) =>
+      item.setControls new KDButtonView
+        title     : 'Upgrade'
+        callback  : =>
+          @emit 'PlanSelected', item.getData()
+
     super()

   pistachio: ->
     """
-    this is the plan upgrade form:
-    {{> @listController.getView()}}
+    <h2>
+      You must upgrade your plan:
+    </h2>
+    {{> @listView}}
     """


tweak(event-card): Use 12-hour time rather than 24-hour time

--- internal_packages/events/lib/event-component.cjsx
+++ internal_packages/events/lib/event-component.cjsx
@@ -53,7 +53,7 @@

-              {moment(@state.when['start_time']*1000).tz(Utils.timeZone).format("H:mm a z")}
+              {moment(@state.when['start_time']*1000).tz(Utils.timeZone).format("h:mm a z")}

             {@_renderEventActions()}


Moved build stack event

Moved build stack event

--- client/app/lib/providers/computecontroller.coffee
+++ client/app/lib/providers/computecontroller.coffee
@@ -696,10 +696,10 @@

       machine.getBaseKite( no ).disconnect()

+    Tracker.track Tracker.STACKS_BUILD
+
     stackId = stack._id
     call    = @getKloud().buildStack { stackId }
-
-    Tracker.track Tracker.STACKS_BUILD

     .then (res) =>


EnvironmentDataProvider: Search collaboration machine inside your machines also.

--- client/app/lib/userenvironmentdataprovider.coffee
+++ client/app/lib/userenvironmentdataprovider.coffee
@@ -108,15 +108,15 @@

   getMachineAndWorkspaceByChannelId: (channelId, callback) ->

-    machine = null
+    machine   = null
     workspace = null
+    data      = @getAllMachines()

-    @getCollaborationMachines (data) =>
-      for obj in data
-        for ws in obj.workspaces
-          if ws.channelId is channelId
-            machine   = obj.machine
-            workspace = ws
+    for obj in data
+      for ws in obj.workspaces
+        if ws.channelId is channelId
+          machine   = obj.machine
+          workspace = ws

     callback machine, workspace


&& -> and

&& -> and

--- client/coffeescript/templates/post_wrapper.coffee
+++ client/coffeescript/templates/post_wrapper.coffee
@@ -18,7 +18,7 @@
         posts.push(post._id)

     #If the currently showing post is not in this group, show one that is
-    if posts.length > 0 && not (Session.get("reply_#{@parent_id}") in posts)
+    if posts.length > 0 and not (Session.get("reply_#{@parent_id}") in posts)
       Session.set("reply_#{@parent_id}", posts[0])

     return posts


oauth: show error if browser has popups turned off

--- client/landing/app/MainApp/oauthcontroller.coffee
+++ client/landing/app/MainApp/oauthcontroller.coffee
@@ -10,6 +10,10 @@
         name       = "Login"
         size       = "height=643,width=1143"
         newWindow  = window.open url, name, size
+
+        unless newWindow
+          notify "Please disable your popup blocker and try again."
+
         newWindow.focus()

   # This is called from the popup to indicate the process is complete.


pass styles on through to div

pass styles on through to div

--- client/lanes/components/shared/ScreenWrapper.cjsx
+++ client/lanes/components/shared/ScreenWrapper.cjsx
@@ -8,6 +8,6 @@
             'screen-wrapper',  @props.identifier,
             'flex-vertically': @props.flexVertical
         )
-

+

             {@props.children}


fix semantic blocks

fix semantic blocks

--- scripts/views/workspace/content/aloha-edit.coffee
+++ scripts/views/workspace/content/aloha-edit.coffee
@@ -54,7 +54,7 @@
         Aloha.ready =>

           @$el.mahalo?()
-          @$el.aloha()
+          setTimeout((=>@$el.aloha()), 200)

           # Wait until Aloha is started before loading MathJax.
           MathJax?.Hub.Configured()


Reword the comment about what this does.

--- scripts/views/workspace/menu/toolbar-aloha.coffee
+++ scripts/views/workspace/menu/toolbar-aloha.coffee
@@ -14,10 +14,17 @@
       # If someone clicks the heading button, don't reload the page
       @$el.find('.btn.currentHeading').on 'click', (e) -> e.preventDefault()

-      # If any action is marked with data-content, add a popover to it to
-      # aid discoverability. If a flash-action event is received for this
-      # action, generated by the toolbar when a plugin requests that the user's
-      # attention be drawn to a button, show the popover. But only do it once.
+      # This will attach a popover to any button in the toolbar that has a
+      # data-content attribute. This essentially defers what buttons
+      # have a popover to the toolbar template itself. It allows the design of
+      # a UI that can occasionally point to a button and tell the user what it
+      # does, that is, it aids in discoverability. This uses the new `flash`
+      # feature in the aloha toolbar. Whenever a plugin asks the toolbar to
+      # flash a button, we catch that event and show the popover. We only show
+      # the popover on the first flash event. Finally, we also glue a handler
+      # to the .close element inside that popover, because bootstrap does not
+      # handle this natively.
+
       @$el.find('.action[data-content]')
       .popover(
         placement: 'bottom',


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/controllable.coffee
+++ src/controllable.coffee
@@ -4,6 +4,7 @@

   self.bind "update", ->
     if keydown.left
+      I.x


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/controllable.coffee
+++ src/controllable.coffee
@@ -37,7 +37,7 @@
     I.velocity.y = 0

     if keydown.left
-      I.velocity.x =
+      I.velocity.x = -I.speed
       I.x -= I.speed

     if keydown.right


background: renaming first_bus to firstBus - also adding function for loading affiliation icon

--- cs/background.coffee
+++ cs/background.coffee
@@ -73,6 +73,13 @@
         News.unreadCountAndNotify items
         News.refreshNewsIdList items

+loadAffiliationIcon = ->
+  symbol = ls.affiliationSymbol
+  if symbol isnt undefined and symbol isnt ''
+    Browser.setIcon ls.affiliationSymbol
+  else
+    if DEBUG then console.log 'ERROR: tried to load empty/undefined affiliation icon'
+
 # Document ready, go!
 $ ->
   # Setting the timeout for all AJAX and JSON requests
@@ -104,37 +111,36 @@
     ls.showBus = 'true'

   # If any of these properties are undefined we'll reset all of them
-  first_bus_props = [
-    ls.first_bus,
-    ls.first_bus_name,
-    ls.first_bus_direction,
-    ls.first_bus_active_lines,
-    ls.first_bus_inactive_lines,
+  firstBusProps = [
+    ls.firstBus,
+    ls.firstBusName,
+    ls.firstBusDirection,
+    ls.firstBusActiveLines,
+    ls.firstBusInactiveLines,
   ]
-  second_bus_props = [
-    ls.second_bus,
-    ls.second_bus_name,
-    ls.second_bus_direction,
-    ls.second_bus_active_lines,
-    ls.second_bus_inactive_lines,
+  secondBusProps = [
+    ls.secondBus,
+    ls.secondBusName,
+    ls.secondBusDirection,
+    ls.secondBusActiveLines,
+    ls.secondBusInactiveLines,
   ]
   firstBusOk = true
   secondBusOk = true
-  # Lol, CoffeeScript at it's best
-  firstBusOk = false for prop in first_bus_props when prop is undefined
-  secondBusOk = false for prop in second_bus_props when prop is undefined
+  firstBusOk = false for prop in firstBusProps when prop is undefined
+  secondBusOk = false for prop in secondBusProps when prop is undefined
   if !firstBusOk
-    ls.first_bus = 16011333
-    ls.first_bus_name = 'Gløshaugen Nord'
-    ls.first_bus_direction = 'til byen'
-    ls.first_bus_active_lines = JSON.stringify [5, 22]
-    ls.first_bus_inactive_lines = JSON.stringify [169]
+    ls.firstBus = 16011333
+    ls.firstBusName = 'Gløshaugen Nord'
+    ls.firstBusDirection = 'til byen'
+    ls.firstBusActiveLines = JSON.stringify [5, 22]
+    ls.firstBusInactiveLines = JSON.stringify [169]
   if !secondBusOk
-    ls.second_bus = 16010333
-    ls.second_bus_name = 'Gløshaugen Nord'
-    ls.second_bus_direction = 'fra byen'
-    ls.second_bus_active_lines = JSON.stringify [5, 22]
-    ls.second_bus_inactive_lines = JSON.stringify [169]
+    ls.secondBus = 16010333
+    ls.secondBusName = 'Gløshaugen Nord'
+    ls.secondBusDirection = 'fra byen'
+    ls.secondBusActiveLines = JSON.stringify [5, 22]
+    ls.secondBusInactiveLines = JSON.stringify [169]

   if ls.showOffice is undefined
     ls.showOffice = 'true'
@@ -163,11 +169,9 @@
   # Open options page after install
   if ls.everConnected is undefined and !DEBUG
     Browser.openTab 'options.html'
-
   # Open Infoscreen if the option is set
   if ls.useInfoscreen is 'true'
     Browser.openTab 'infoscreen.html'
-
   # Open Chatter if the option is set
   if ls.openChatter is 'true'
     Browser.openBackgroundTab 'http://webchat.freenode.net/?channels=online'
@@ -175,10 +179,7 @@
   # Set default vars for main loop
   ls.everConnected = ls.wasConnected = 'false'

-  # Load affiliation icon
-  symbol = ls.affiliationSymbol
-  if symbol isnt undefined and symbol isnt ''
-      Browser.setIcon symbol
+  loadAffiliationIcon()

   # Reload the page once every day
   setInterval ( ->
@@ -192,6 +193,7 @@
   window.updateOfficeAndMeetings = updateOfficeAndMeetings
   window.updateCoffeeSubscription = updateCoffeeSubscription
   window.updateNews = updateNews
+  window.loadAffiliationIcon = loadAffiliationIcon

   # Enter main loop, keeping everything up-to-date
   mainLoop()


lodchart: add a couple of comments; pointsize default -> 0

--- lodchart/lodchart.coffee
+++ lodchart/lodchart.coffee
@@ -13,8 +13,8 @@
   lightrect = d3.rgb(230, 230, 230)
   linecolor = "darkslateblue"
   linewidth = 2
-  pointcolor = "#E9CFEC"
-  pointsize = 1
+  pointcolor = "#E9CFEC" # pink
+  pointsize = 0 # default = no visible points at markers
   xlab = "Chromosome"
   ylab = "LOD score"
   yscale = d3.scale.linear()


Changed interpolation to be more accurate on linecharts

--- metrics-ui/ui/js/components/linechart-d3.coffee
+++ metrics-ui/ui/js/components/linechart-d3.coffee
@@ -157,7 +157,8 @@
             .x((d) => scales.xscale(new Date(d.day*1000)))
             .y((d) => yscale(n.func(d)))

-          lfunc.interpolate('monotone')
+          lfunc.tension(0.9)
+          lfunc.interpolate('cardinal')

           # Draw to plot
           d3.select(el).select("path.line#{n.name}")


fixing broken range literal expansions.

fixing broken range literal expansions.

--- nodes.coffee
+++ nodes.coffee
@@ -584,7 +584,7 @@
         return "[#{ range.join(', ') }]"
       else
         o.index = i
-        body.compileSimple o
+        body = @compileSimple o
     else
       clause = "#@fromVar <= #@toVar ?"
       body   = "var #i = #@fromVar; #clause #i <#@equals #@toVar : #i >#@equals #@toVar; #clause #i += 1 : #i -= 1"


made --nodes output soak-aware

made --nodes output soak-aware

--- nodes.coffee
+++ nodes.coffee
@@ -117,7 +117,8 @@
   toString: (idt, override) ->
     idt or= ''
     children = (child.toString idt + TAB for child in @collectChildren()).join('')
-    '\n' + idt + (override or @class) + children
+    klass = override or @class + if @soakNode or @exist then '?' else ''
+    '\n' + idt + klass + children

   eachChild: (func) ->
     return unless @children
@@ -1159,7 +1160,7 @@
   isComplex: -> @operator isnt '!' or @first.isComplex()

   isMutator: ->
-    ends(@operator, '=') and not (@operator in ['===', '!=='])
+    ends(@operator, '=') and @operator not in ['===', '!==']

   isChainable: ->
     include(@CHAINABLE, @operator)


destructuring assignment no longer uses a temporary variable for simple LHS

--- nodes.coffee
+++ nodes.coffee
@@ -143,6 +143,9 @@
   isComplex       : YES
   topSensitive    : NO

+  # Is this node used to assign a certain variable?
+  assigns: NO
+
 #### Expressions

 # The expressions body is the list of expressions that forms the body of an
@@ -253,6 +256,8 @@
   isReserved: ->
     !!@value.reserved

+  assigns: (name) -> name is @value
+
   compileNode: (o) ->
     idt = if @isStatement(o) then @idt() else ''
     end = if @isStatement(o) then ';' else ''
@@ -323,6 +328,9 @@

   isComplex: ->
     @base.isComplex() or @hasProperties()
+
+  assigns: (name) ->
+    not @properties.length and @base.assigns name

   makeReturn: ->
     if @properties.length then super() else @base.makeReturn()
@@ -702,12 +710,15 @@
   compileNode: (o) ->
     top = del o, 'top'
     o.indent = @idt 1
-    nonComments = prop for prop in @properties when (prop not instanceof Comment)
-    lastNoncom  =  last nonComments
+    nonComments = prop for prop in @properties when prop not instanceof Comment
+    lastNoncom  = last nonComments
     props = for prop, i in @properties
-      join   = ",\n"
-      join   = "\n" if (prop is lastNoncom) or (prop instanceof Comment)
-      join   = '' if i is @properties.length - 1
+      join = if i is @properties.length - 1
+        ''
+      else if prop is lastNoncom or prop instanceof Comment
+        '\n'
+      else
+        ',\n'
       indent = if prop instanceof Comment then '' else @idt 1
       if prop instanceof Value and prop.tags.this
         prop = new Assign prop.properties[0].name, prop, 'object'
@@ -715,8 +726,12 @@
         prop = new Assign prop, prop, 'object'
       indent + prop.compile(o) + join
     props = props.join('')
-    obj   = '{' + (if props then '\n' + props + '\n' + @idt() else '') + '}'
+    obj   = "{#{ if props then '\n' + props + '\n' + @idt() else '' }}"
     if top then "(#{obj})" else obj
+
+  assigns: (name) ->
+    for prop in @properties when prop.assigns name then return yes
+    no

 #### ArrayLiteral

@@ -751,6 +766,10 @@
       "[\n#{o.indent}#{objects}\n#{@tab}]"
     else
       "[#{objects}]"
+
+  assigns: (name) ->
+    for obj in @objects when obj.assigns name then return yes
+    no

 #### Class

@@ -898,16 +917,19 @@
       return new Assign(obj, value).compile o
     top     = del o, 'top'
     otop    = merge o, top: yes
-    valVar  = o.scope.freeVariable 'ref'
-    assigns = ["#{valVar} = #{ value.compile o }"]
+    valVar  = value.compile o
+    assigns = []
     splat   = false
+    if not IDENTIFIER.test(valVar) or @variable.assigns(valVar)
+      assigns.push "#{ ref = o.scope.freeVariable 'ref' } = #{valVar}"
+      valVar = ref
     for obj, i in objects
       # A regular array pattern-match.
       idx = i
       if isObject
         if obj instanceof Assign
           # A regular object pattern-match.
-          [obj, idx] = [obj.value, obj.variable.base]
+          {variable: {base: idx}, value: obj} = obj
         else
           # A shorthand `{a, b, @c} = val` pattern-match.
           idx = if obj.tags.this then obj.properties[0].name else obj
@@ -936,6 +958,9 @@
     ref   = o.scope.freeVariable 'ref'
     val   = @value.compile(o)
     "([].splice.apply(#{name}, [#{from}, #{to}].concat(#{ref} = #{val})), #{ref})"
+
+  assigns: (name) ->
+    @[if @context is 'object' then 'value' else 'variable'].assigns name

 #### Code

@@ -1039,8 +1064,9 @@

   constructor: (name) ->
     super()
-    name = new Literal name unless name.compile
-    @name = name
+    @name = if name.compile then name else new Literal name
+
+  assigns: (name) -> @name.assigns name

   compileNode: (o) ->
     if @index? then @compileParam(o) else @name.compile(o)


Issue #843. If a for-body ends in a pure-statement, no need to try and return results.

--- nodes.coffee
+++ nodes.coffee
@@ -1417,7 +1417,9 @@
   # comprehensions. Some of the generated code can be shared in common, and
   # some cannot.
   compileNode: (o) ->
-    hasCode       = @body.contains (node) -> node instanceof Code
+    body          = Expressions.wrap [@body]
+    hasCode       = body.contains (node) -> node instanceof Code
+    hasPure       = last(body.expressions)?.containsPureStatement()
     source        = if @range then @source.base else @source
     scope         = o.scope
     name          = @name  and @name.compile o, LEVEL_LIST
@@ -1425,12 +1427,11 @@
     unless hasCode
       scope.find(name,  immediate: yes) if name and not @pattern
       scope.find(index, immediate: yes) if index
-    rvar          = scope.freeVariable 'results' if @returns
+    rvar          = scope.freeVariable 'results' if @returns and not hasPure
     ivar          = (if @range then name else index) or scope.freeVariable 'i'
     varPart       = ''
     guardPart     = ''
     defPart       = ''
-    body          = Expressions.wrap [@body]
     idt1          = @tab + TAB
     if @range
       forPart = source.compile merge(o, {index: ivar, @step})
@@ -1448,10 +1449,10 @@
         lvar        = scope.freeVariable 'len'
         stepPart    = if @step then "#{ivar} += #{ @step.compile(o, LEVEL_OP) }" else "#{ivar}++"
         forPart     = "#{ivar} = 0, #{lvar} = #{svar}.length; #{ivar} < #{lvar}; #{stepPart}"
-    if @returns
+    if @returns and not hasPure
       resultPart    = "#{@tab}#{rvar} = [];\n"
       returnResult  = '\n' + (new Return(new Literal(rvar)).compile o, LEVEL_PAREN)
-      body          = Push.wrap rvar, body if @returns
+      body          = Push.wrap rvar, body
     if @guard
       body          = Expressions.wrap [new If @guard, body]
     if hasCode


#1643: Clean up Assign#compile and Assign#compilePatternMatch

--- nodes.coffee
+++ nodes.coffee
@@ -940,14 +940,14 @@
       return @compileSplice       o if @variable.isSplice()
       return @compileConditional  o if @context in ['||=', '&&=', '?=']
     name = @variable.compile o, LEVEL_LIST
-    unless @context or @variable.unwrapAll().isAssignable()
-      throw SyntaxError "\"#{ @variable.compile o }\" cannot be assigned."
-    hasProps = (isValue and @variable.unwrapAll().hasProperties?()) or (@variable instanceof Splat and @variable.name.unwrapAll().hasProperties?())
-    unless @context or hasProps
-      if @param
-        o.scope.add name, 'var'
-      else
-        o.scope.find name
+    unless @context
+      unless (varBase = @variable.unwrapAll()).isAssignable()
+        throw SyntaxError "\"#{ @variable.compile o }\" cannot be assigned."
+      unless varBase.hasProperties?()
+        if @param
+          o.scope.add name, 'var'
+        else
+          o.scope.find name
     if @value instanceof Code and match = METHOD_DEF.exec name
       @value.klass = match[1] if match[1]
       @value.name  = match[2] ? match[3] ? match[4] ? match[5]
@@ -1032,8 +1032,7 @@
         val = new Value new Literal(vvar), [new (if acc then Access else Index) idx]
       if name? and name in ['arguments','eval'].concat RESERVED
         throw new SyntaxError "assignment to a reserved word: #{obj.compile o} = #{val.compile o}"
-      isSoak = (soakNode = obj.name or obj) and soakNode.base?.soak or soakNode.properties?[0]?.soak
-      assigns.push new Assign(obj, val, null, param: @param).compile o, if isSoak then LEVEL_ACCESS else LEVEL_TOP
+      assigns.push new Assign(obj, val, null, param: @param).compile o, LEVEL_LIST
     assigns.push vvar unless top
     code = assigns.join ', '
     if o.level < LEVEL_LIST then code else "(#{code})"


using '== null' to check for soaked accessor chains instead of '== undefined' -- shorter, and slightly safer

--- nodes.coffee
+++ nodes.coffee
@@ -228,7 +228,7 @@
 ValueNode: exports.ValueNode: inherit Node, {
   type: 'Value'

-  SOAK: " == undefined ? undefined : "
+  SOAK: " == null ? undefined : "

   constructor: (base, properties) ->
     @children:   flatten [@base: base, @properties: (properties or [])]


console is not available in webworkers

console is not available in webworkers

--- coffeescript/004-webworkers/multiplication.coffee
+++ coffeescript/004-webworkers/multiplication.coffee
@@ -12,7 +12,5 @@
 ], (_, multi) ->
     console.info('binding: onmessage');
     global.onmessage = (e) ->
-        console.info('input:' + e.data);
         result = _.reduce(_.values(e.data), ((memo, elem) -> memo * elem), 1)
-        console.info('results:' + result);
         postMessage(result)


Require left-click to break, right-click to place. See issue #1

Still requires holding Control to highlight adjacent block for placement
(so control-right-click, not only right-click yet..)

--- index.coffee
+++ index.coffee
@@ -46,6 +46,16 @@

   return game

+ACTION_BREAK = 0
+ACTION_INTERACT = 1
+ACTION_PICK = 2
+getAction = (kb_state) ->
+  switch
+    when kb_state['fire'] && kb_state['firealt'] then ACTION_PICK  # TODO: block picking
+    when kb_state['fire'] then ACTION_BREAK
+    when kb_state['firealt'] then ACTION_INTERACT
+    else ACTION_BREAK
+
 defaultSetup = (game, avatar) ->
   console.log "entering setup"

@@ -75,14 +85,18 @@

   game.on 'fire', (target, state) ->
     console.log "fire #{target}, #{state}"
-    position = blockPosPlace
-    if position
-      game.createBlock position, currentMaterial
-    else
-      position = blockPosErase
-      game.setBlock(position, 0) if position
+    console.log "state #{JSON.stringify(state)}"

-
+    console.log "action = #{getAction state}"
+
+    switch getAction(state)
+      when ACTION_BREAK
+        if blockPosErase?
+          game.setBlock blockPosErase, 0
+      when ACTION_INTERACT
+        if blockPosPlace?
+          game.createBlock blockPosPlace, currentMaterial if blockPosPlace? # TODO: always get adjacent
+
   game.on 'tick', () ->
     walk.render target.playerSkin
     vx = Math.abs target.velocity.x


Remove unnecessary log line.

Remove unnecessary log line.

--- index.coffee
+++ index.coffee
@@ -48,7 +48,6 @@
         if err
           console.error err
           return next err
-        console.log list
         accountList = list
         next()
     chooseAccount: (next) ->


template cache

template cache

--- index.coffee
+++ index.coffee
@@ -40,6 +40,7 @@
   mailer = nodemailer.createTransport options.account

   i18n_data = {}
+  template_cache = {}
   priority_cache = {}

   for language_info in options.language_infos
@@ -115,21 +116,21 @@
       file_path: file_path
     }

-  renderTemplate = (template_name, view_data, callback) ->
-    {engine, file_path} = getTemplateInfo template_name
+  getTemplate = (template_file, callback) ->
+    if template_cache[template_file]
+      return callback null, template_cache[template_file]

+    fs.readFile template_file, callback
+
+  renderTemplate = (engine, template_source, view_data) ->
     if engine == 'jade'
-      jade.renderFile file_path, view_data, (err, content) ->
-        callback err, content
+      return jade.render template_source, view_data

     else if engine == 'html'
-      fs.readFile file_path, (err, content) ->
-        return callback err if err
-        content = _.template(content) view_data
-        callback null, content
+      return _.template(template_source) view_data

     else
-      callback new Error 'Unknown Engine'
+      throw new Error 'Unknown Engine'

   return {
     sendMail: (template_name, to_address, view_data, i18n_options, callback) ->
@@ -139,10 +140,12 @@
       m = ->
         return moment.apply(@, arguments).locale(i18n_options.language).tz(i18n_options.timezone)

-      renderTemplate template_name, _.extend({t: t, m: m}, view_data), (err, mail_body) ->
+      {engine, file_path, file_name} = getTemplateInfo template_name
+
+      getTemplate file_path, (err, template_source) ->
         return callback err if err

-        {file_name} = getTemplateInfo template_name
+        mail_body = renderTemplate engine, template_source, _.extend({t: t, m: m}, view_data)

         mailer.sendMail
           from: options.send_from


Why the heck isn't this running...?

Why the heck isn't this running...?

--- index.coffee
+++ index.coffee
@@ -31,7 +31,7 @@
 			require "#{__dirname}/api/v0/models/keys.coffee"
 			require "#{__dirname}/api/v0/models/users.coffee"

-			console.log "#{configs.name} now running at #{configs.host}:#{configs.port}"
+			console.log "#{configs.name} is now running at #{configs.host}:#{configs.port}"
 		else if err & kickoffTries < 5
 			console.log "Mongoose didn't work.  That's a bummer.  Let's try it again in half a second"
 			setTimeout () ->


Removed useless file\!

Removed useless file\!

--- spec/javascripts/helpers/jasmine_themer.js.coffee
+++ spec/javascripts/helpers/jasmine_themer.js.coffee
@@ -1,8 +0,0 @@
-jQuery ->
-
-
-  boot = ->
-    $('.html-reporter').wrap $('

').addClass 'container'
-
-  $('body').one('DOMNodeInserted', boot)
-


Remove spec for deleted file

Remove spec for deleted file

--- spec/javascripts/lib/forms/form_field_spec.coffee
+++ spec/javascripts/lib/forms/form_field_spec.coffee
@@ -1,75 +0,0 @@
-require ['lib/forms/form_field'], (FormField) ->
-
-  describe 'LP Form Field', ->
-
-    describe 'Form Field object', ->
-
-      it 'is defined', ->
-        expect(FormField).toBeDefined()
-
-    describe 'initialising a Form Field object', ->
-
-      beforeEach ->
-        loadFixtures 'form_validation.html'
-
-      it 'initialises when the element exists', ->
-        spyOn(FormField.prototype, '_initialize')
-
-        fields = $('.js-field')
-        formField = new FormField(fields[0])
-
-        expect(FormField.prototype._initialize).toHaveBeenCalled()
-
-      it 'does not when the element does not exist', ->
-        spyOn(FormField.prototype, '_initialize')
-
-        formField = new FormField()
-
-        expect(FormField.prototype._initialize).not.toHaveBeenCalled()
-
-    describe 'creating field input objects from the field', ->
-
-      beforeEach ->
-        loadFixtures 'form_validation.html'
-
-      it 'has a field input object for each input in the field', ->
-        fields = $('.js-field')
-
-        formField_input = new FormField(fields[0])
-        formField_multiSelect = new FormField(fields[2])
-
-        expect(formField_input.inputs.length).toBe 1
-        expect(formField_multiSelect.inputs.length).toBe 2
-
-    describe 'validating a field', ->
-
-      fields = null
-
-      beforeEach ->
-        loadFixtures 'form_validation.html'
-        fields = $('.js-field')
-
-      it 'is not valid if all inputs are invalid', ->
-        formField_input = new FormField(fields[0])
-        formField_multiSelect = new FormField(fields[2])
-
-        expect(formField_input.isValid()).toBe false
-        expect(formField_multiSelect.isValid()).toBe false
-
-      it 'is not valid if some inputs are invalid', ->
-        formField_multiSelect = new FormField(fields[2])
-
-        $('#first-select').val(1);
-
-        expect(formField_multiSelect.isValid()).toBe false
-
-      it 'is valid if the fields are valid', ->
-        formField_input = new FormField(fields[0])
-        formField_multiSelect = new FormField(fields[2])
-
-        $('#required').val('some text')
-        $('#first-select').val(1);
-        $('#second-select').val(1);
-
-        expect(formField_input.isValid()).toBe true
-        expect(formField_multiSelect.isValid()).toBe true


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- source/javascripts/_test/string_extensions.coffee
+++ source/javascripts/_test/string_extensions.coffee
@@ -51,7 +51,7 @@
   equals "31.".toInt(), 31

-  equals "09".toInt(), 31
+  equals "009".toInt(), 9

 test "#underscore", ->
   equals "Pro-tip".underscore(), "pro_tip"


Support single string activationEvents

Support single string activationEvents

--- package.cson
+++ package.cson
@@ -1,3 +1,3 @@
 'main': './lib/grammar-selector'
 'description': 'Select the grammar to use for the current editor.'
-'activationEvents': ['grammar-selector:show']
+'activationEvents': 'grammar-selector:show'


Fixed the add field limit JS

Fixed the add field limit JS

--- app/assets/javascripts/application.js.coffee
+++ app/assets/javascripts/application.js.coffee
@@ -412,7 +412,7 @@
         # Are there add limits
         add_limit_attr = question.find(".list-add").attr("data-add-limit")

-        li_size = question.find(".list-add > li")
+        li_size = question.find(".list-add > li").size()

         if ((typeof(add_limit_attr) != typeof(undefined)) && add_limit_attr != false)


Delete unused functions

Delete unused functions

--- src/docker-utils.coffee
+++ src/docker-utils.coffee
@@ -14,29 +14,6 @@

 exports.docker = docker
 dockerProgress = new DockerProgress(socketPath: config.dockerSocket)
-
-createContainerDisposed = (config) ->
-	docker.createContainerAsync(config)
-	.tap (container) ->
-		container.startAsync()
-	.disposer (container) ->
-		container.removeAsync(force: true)
-
-# Trailing slashes are important to rsync
-rootDir = ({Driver, Id}) ->
-	switch Driver
-		when 'aufs'
-			"/var/lib/docker/#{Driver}/mnt/#{Id}/"
-		when 'btrfs'
-			"/var/lib/docker/#{Driver}/subvolumes/#{Id}/"
-		when 'devicemapper'
-			"/var/lib/docker/#{Driver}/mnt/#{Id}/rootfs/"
-		when 'overlay'
-			"/var/lib/docker/#{Driver}/#{Id}/merged/"
-		when 'vfs'
-			"/var/lib/docker/#{Driver}/dir/#{Id}/"
-		else
-			throw new Error("Unsupported driver: #{Driver}")

 # Create an array of (repoTag, image_id, created) tuples like the output of `docker images`
 listRepoTagsAsync = ->


StackedBar: increase right margin

StackedBar: increase right margin

Adds more right margin to the chart to allow for longer legends such as
"E/MWh".

--- app/assets/javascripts/d3/stacked_bar.coffee
+++ app/assets/javascripts/d3/stacked_bar.coffee
@@ -15,7 +15,7 @@
       top: 20
       bottom: 20
       left: 30
-      right: 40
+      right: 60

     legend_margin: 20


Disable highlighting by default

Disable highlighting by default

--- app/assets/javascripts/gl_dropdown.js.coffee
+++ app/assets/javascripts/gl_dropdown.js.coffee
@@ -88,6 +88,7 @@
     {
       # If no input is passed create a default one
       @filterInput = @$(FILTER_INPUT)
+      @highlight = false
     } = @options

     self = @
@@ -252,7 +253,8 @@
       if selected
         cssClass = "is-active"

-      text = @highlightTextMatches(text, @filterInput.val())
+      if @highlight
+        text = @highlightTextMatches(text, @filterInput.val())

       html = "<li>"
       html += "<a href='#{url}' class='#{cssClass}'>"


inicializando tooltip

inicializando tooltip

--- app/assets/javascripts/tooltipinit.js.coffee
+++ app/assets/javascripts/tooltipinit.js.coffee
@@ -2,11 +2,12 @@

 window.Helpers.TooltipHelper = {
   init: ->
-    $(".mostrar").each ->
-      console.log $(this)
-      $(this).insertBefore "

"
-      $(this).insertAfter "

"
-      $(this).tooltip()
+  	$("[data-toggle=tooltip]").tooltip placement: "top"
+    # $(".mostrar").each ->
+    #   console.log $(this)
+    #   $(this).insertBefore "

"
+    #   $(this).insertAfter "

"
+    #   $(this).tooltip()
 }

 jQuery window.Helpers.TooltipHelper.init


cleaner bug fix

cleaner bug fix

--- app/assets/javascripts/validations.js.coffee
+++ app/assets/javascripts/validations.js.coffee
@@ -6,8 +6,7 @@
     if(remaining >= 0)
       $(".character-counter").text(pluralize_characters(remaining) + " left")
     else
-      num = num * (-1)
-      $(".character-counter").text(pluralize_characters(remaining) + " too long")
+      $(".character-counter").text(pluralize_characters(-remaining) + " too long")

 pluralize_characters = (num) ->
   if(num == 1)


Fix bug #1028: Not show last sprint name at the same time of a validation error

--- app/coffee/modules/backlog/lightboxes.coffee
+++ app/coffee/modules/backlog/lightboxes.coffee
@@ -30,6 +30,7 @@

 CreateEditSprint = ($repo, $confirm, $rs, $rootscope, lightboxService, $loading) ->
     link = ($scope, $el, attrs) ->
+        hasErrors = false
         createSprint = true
         $scope.sprint = {
             project: null
@@ -41,7 +42,10 @@
         submit = (event) ->
             form = $el.find("form").checksley()
             if not form.validate()
+                hasErrors = true
+                $el.find(".last-sprint-name").addClass("disappear")
                 return
+            hasErrors = false

             newSprint = angular.copy($scope.sprint)

@@ -132,7 +136,7 @@
             $el.find(".last-sprint-name").addClass("disappear")

         $el.on "keyup", ".sprint-name", (event) ->
-            if $el.find(".sprint-name").val().length > 0
+            if $el.find(".sprint-name").val().length > 0 or hasErrors
                 $el.find(".last-sprint-name").addClass("disappear")
             else
                 $el.find(".last-sprint-name").removeClass("disappear")


JSLabel's bug fix

JSLabel's bug fix

--- JSKit/03_JSLabel.coffee
+++ JSKit/03_JSLabel.coffee
@@ -57,7 +57,7 @@
 			if (@_text.string == "" && $(@_viewSelector+"_text").length)
 				@_text.setText(@S($(@_viewSelector+"_text").val()))
 			disp = @_text.string
-			tag = "

"
+			tag = "

"
 			x = 0
 			y = 0


FIX: Enable gif file upload on submission

--- src/app/common/file-uploader/file-uploader.coffee
+++ src/app/common/file-uploader/file-uploader.coffee
@@ -71,7 +71,7 @@
         icon:       'fa-file-code-o'
         name:       'code'
       image:
-        extensions: ['png', 'bmp', 'tiff', 'tif', 'jpeg', 'jpg']
+        extensions: ['png', 'bmp', 'tiff', 'tif', 'jpeg', 'jpg', 'gif']
         name:       'image'
         icon:       'fa-file-image-o'
       zip:


[clean] debug

[clean] debug

--- app/scripts/helpers/templates_helpers.coffee
+++ app/scripts/helpers/templates_helpers.coffee
@@ -11,7 +11,6 @@

     filterWeatherDays: (list, dayTime) ->
       delta = {day: 0, night: 4}[dayTime]
-      console.log delta
       _.chain(list)
         .filter( (item, index) ->
           (index + delta + 8) % 8 == 0


Try to auto-login to doc store on mount

Try to auto-login to doc store on mount

--- src/code/providers/document-store-provider.coffee
+++ src/code/providers/document-store-provider.coffee
@@ -13,12 +13,22 @@
 DocumentStoreAuthorizationDialog = React.createFactory React.createClass
   displayName: 'DocumentStoreAuthorizationDialog'

+  getInitialState: ->
+    docStoreAvailable: false
+
+  componentWillMount: ->
+    @props.provider._onDocStoreLoaded =>
+      @setState docStoreAvailable: true
+
   authenticate: ->
     @props.provider.authorize()

   render: ->
     (div {},
-      (button {onClick: @authenticate}, 'Authorization Needed')
+      if @state.docStoreAvailable
+        (button {onClick: @authenticate}, 'Authorization Needed')
+      else
+        'Trying to log into the Document Store...'
     )

 class DocumentStoreProvider extends ProviderInterface
@@ -35,10 +45,14 @@
   @Name: 'documentStore'

   authorized: (@authCallback) ->
+    @_checkLogin()

   authorize: ->
     @_showLoginWindow()
-    @_checkLogin()
+
+  _onDocStoreLoaded: (@docStoreLoadedCallback) ->
+    if @_docStoreLoaded
+      @docStoreLoadedCallback()

   _loginSuccessful: (data) ->
     if @_loginWindow then @_loginWindow.close()
@@ -52,9 +66,10 @@
       xhrFields:
         withCredentials: true
       success: (data) ->
+        provider.docStoreLoadedCallback()
         provider._loginSuccessful(data)
       error: ->
-        # nothing yet
+        provider.docStoreLoadedCallback()

   _loginWindow: null

@@ -98,7 +113,7 @@
             @_loginWindow.close()
             @_checkLogin()
         catch e
-          console.log e
+          # console.log e

       poll = setInterval pollAction, 200


Do not aply search filter when opening

Do not aply search filter when opening

--- vendor/assets/javascripts/chosen/single.js.coffee
+++ vendor/assets/javascripts/chosen/single.js.coffee
@@ -39,6 +39,7 @@
     @$container.$search[0].value = if selected then selected.label else ""

     if selected and not selected.blank
+      @search_value = selected.label
       @$container.removeClass("placeholder")
     else
       @$container.addClass("placeholder")


Usage of eval() is forbidden by FirefoxOS CSP

In privileged and certified FirefoxOS apps, the Content Security Policy forbids the usage of potentially hazardous functions such as eval(). This is a quick fix for the only occurrence of eval() in QuoJS.

--- src/quo.gestures.coffee
+++ src/quo.gestures.coffee
@@ -139,8 +139,8 @@
         diff = parseInt(GESTURE.initial_angle - angle, 10)
         if Math.abs(diff) > 20 or GESTURE.angle_difference isnt 0
             i = 0
-            symbol = if GESTURE.angle_difference < 0 then "-" else "+"
-            eval "diff " + symbol + "= 180;" while Math.abs(diff - GESTURE.angle_difference) > 90 and i++ < 10
+            symbol = if GESTURE.angle_difference < 0 then -1 else 1
+            diff += 180 * symbol while Math.abs(diff - GESTURE.angle_difference) > 90 and i++ < 10
             GESTURE.angle_difference = parseInt(diff, 10)
             _trigger "rotating", angle: GESTURE.angle_difference


Escape help commands in the right places. Fixes #343

--- src/scripts/help.coffee
+++ src/scripts/help.coffee
@@ -52,8 +52,7 @@

 module.exports = (robot) ->
   robot.respond /help\s*(.*)?$/i, (msg) ->
-    cmds = robot.helpCommands().map (cmd) ->
-      cmd.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>')
+    cmds = robot.helpCommands()

     if msg.match[1]
       cmds = cmds.filter (cmd) ->
@@ -67,7 +66,9 @@
     msg.send emit

   robot.router.get '/hubot/help', (req, res) ->
-    cmds = robot.helpCommands()
+    cmds = robot.helpCommands().map (cmd) ->
+      cmd.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>')
+
     emit = "
#{cmds.join '

'}
"

     emit = emit.replace /hubot/ig, "#{robot.name}"


Improved punch feedback

Improved punch feedback

--- src/scripts/time.coffee
+++ src/scripts/time.coffee
@@ -81,6 +81,10 @@
                           res.message.user.name
       return
     if canPunchHere res.message.user.name, res.message.user.room
+      Logger.reactToMessage 'clock4',
+                            res.message.user.name,
+                            res.message.rawMessage.channel,
+                            res.message.id
       msg = res.match.input
       msg = msg.replace REGEX.ibizan, ''
       tz = res.message.user.slack.tz
@@ -104,11 +108,7 @@
         article = 'an'
       Logger.log "Successfully generated #{article} #{modeQualifier}-punch
                   for @#{user.slack}: #{punch.description(user)}"
-      Logger.reactToMessage 'clock4',
-                            res.message.user.name,
-                            res.message.rawMessage.channel,
-                            res.message.id
-
+
       sendPunch punch, user, res
     else
       if res.router_res
@@ -151,11 +151,6 @@
             "text": punchEnglish
           }
         else
-          Logger.reactToMessage 'clock4',
-                                res.message.user.name,
-                                res.message.rawMessage.channel,
-                                res.message.id,
-                                'reactions.remove'
           Logger.reactToMessage 'dog2',
                                 res.message.user.name,
                                 res.message.rawMessage.channel,
@@ -179,18 +174,17 @@
           user.directMessage "\n#{errorMsg}\nYou can see more details on
                               <#{Organization.spreadsheet.url}|the spreadsheet>.",
                              Logger
-          Logger.reactToMessage 'clock4',
-                                res.message.user.name,
-                                res.message.rawMessage.channel,
-                                res.message.id,
-                                'reactions.remove'
           Logger.reactToMessage 'x',
                                 res.message.user.name,
                                 res.message.rawMessage.channel,
                                 res.message.id
     )
     .done()
-
+    Logger.reactToMessage 'clock4',
+                          res.message.user.name,
+                          res.message.rawMessage.channel,
+                          res.message.id,
+                          'reactions.remove'

   # respond to mode
   robot.respond REGEX.modes, (res) ->


Renamed op to opData in applyOp code

Renamed op to opData in applyOp code

--- src/server/model.coffee
+++ src/server/model.coffee
@@ -91,41 +91,41 @@

   # Its important that all ops are applied in order. This helper method creates the op submission queue
   # for a single document. This contains the logic for transforming & applying ops.
-  makeOpQueue = (docName, doc) -> queue (op, callback) ->
-    return callback 'Version missing' unless op.v >= 0
+  makeOpQueue = (docName, doc) -> queue (opData, callback) ->
+    return callback 'Version missing' unless opData.v >= 0

-    op.meta ||= {}
-    op.meta.ts = Date.now()
-
-    return callback 'Op at future version' if op.v > doc.v
+    opData.meta ||= {}
+    opData.meta.ts = Date.now()
+
+    return callback 'Op at future version' if opData.v > doc.v

     # Punt the transforming work back to the client if the op is too old.
-    return callback 'Op too old' if op.v + options.maximumAge < doc.v
+    return callback 'Op too old' if opData.v + options.maximumAge < doc.v

     # We'll need to transform the op to the current version of the document. This
     # calls the callback immediately if opVersion == doc.v.
-    getOps docName, op.v, doc.v, (error, ops) ->
+    getOps docName, opData.v, doc.v, (error, ops) ->
       return callback error if error

-      unless doc.v - op.v == ops.length
+      unless doc.v - opData.v == ops.length
         # This should never happen. It indicates that we didn't get all the ops we
         # asked for. Its important that the submitted op is correctly transformed.
         console.error "Could not get old ops in model for document #{docName}"
-        console.error "Expected ops #{op.v} to #{doc.v} and got #{ops.length} ops"
+        console.error "Expected ops #{opData.v} to #{doc.v} and got #{ops.length} ops"
         return callback 'Internal error'

       if ops.length > 0
         try
           # If there's enough ops, it might be worth spinning this out into a webworker thread.
           for oldOp in ops
-            op.op = doc.type.transform op.op, oldOp.op, 'left'
-            op.v++
+            opData.op = doc.type.transform opData.op, oldOp.op, 'left'
+            opData.v++
         catch error
           console.error error.stack
           return callback error.message

       try
-        snapshot = doc.type.apply doc.snapshot, op.op
+        snapshot = doc.type.apply doc.snapshot, opData.op
       catch error
         console.error error.stack
         return callback error.message
@@ -135,16 +135,16 @@
       #
       # This should never happen in practice, but its a nice little check to make sure everything
       # is hunky-dory.
-      unless op.v == doc.v
+      unless opData.v == doc.v
         # This should never happen.
         console.error "Version mismatch detected in model. File a ticket - this is a bug."
-        console.error "Expecting #{op.v} == #{doc.v}"
+        console.error "Expecting #{opData.v} == #{doc.v}"
         return callback 'Internal error'

       #newDocData = {snapshot, type:type.name, v:opVersion + 1, meta:docData.meta}
       writeOp = db?.writeOp or (docName, newOpData, callback) -> callback()

-      writeOp docName, op, (error) ->
+      writeOp docName, opData, (error) ->
         if error
           # The user should probably know about this.
           console.warn "Error writing ops to database: #{error}"
@@ -158,18 +158,18 @@
         # All the heavy lifting is now done. Finally, we'll update the cache with the new data
         # and (maybe!) save a new document snapshot to the database.

-        doc.v = op.v + 1
+        doc.v = opData.v + 1
         doc.snapshot = snapshot

-        doc.ops.push op
+        doc.ops.push opData
         doc.ops.shift() if db and doc.ops.length > options.numCachedOps

-        model.emit 'applyOp', docName, op, snapshot, oldSnapshot
-        doc.eventEmitter.emit 'op', op, snapshot, oldSnapshot
+        model.emit 'applyOp', docName, opData, snapshot, oldSnapshot
+        doc.eventEmitter.emit 'op', opData, snapshot, oldSnapshot

         # The callback is called with the version of the document at which the op was applied.
         # This is the op.v after transformation, and its doc.v - 1.
-        callback null, op.v
+        callback null, opData.v

         # I need a decent strategy here for deciding whether or not to save the snapshot.
         #


Workspace: Remove unused methods and signatures.

--- workers/social/lib/social/models/workspace.coffee
+++ workers/social/lib/social/models/workspace.coffee
@@ -16,21 +16,9 @@

     sharedMethods  :
       static       :
-        create     : [
-          (signature Object, Function)
-          (signature Object, Object, Function)
-        ]
-        some       :
-          (signature Object, Object, Function)
-      instance     :
-        update     : [
-          (signature Object, Function)
-          (signature Object, Object, Function)
-        ]
-        remove     : [
-          (signature Function)
-          (signature Object, Function)
-        ]
+        create     : signature Object, Function
+        some       : signature Object, Object, Function
+      instance     : []
     sharedEvents   :
       static       : []
       instance     : []
@@ -46,9 +34,3 @@
   some$: secure (client, query = {}, callback) ->
     query.owner = client.connection.delegate.profile.nickname
     JWorkspace.some query, callback
-
-  update$: secure (client, data, callback) ->
-    @update { $set: data }, callback
-
-  remove$: secure (client, callback)->
-    @remove callback


Fix errors when setting/clearing datepicker on changeDate event

--- src/coffee/cilantro/ui/field/controls/date.coffee
+++ src/coffee/cilantro/ui/field/controls/date.coffee
@@ -6,7 +6,7 @@
     class DateControl extends range.RangeControl
         # Add the change event from the datepickers to existing range events
         _events:
-            'changeDate .range-lower, .range-upper': 'change'
+            'changeDate .range-lower,.range-upper': 'change'

         initialize: ->
             super
@@ -54,7 +54,17 @@
         # applied to the datepicker rather than the textbox. The datepicker
         # will handle the updating of the textbox.
         setLowerBoundValue: (value) ->
-            @ui.lowerBound.datepicker('setDate', new Date(value))
+            # Since the changeDate event is causing this method to receive a
+            # query object we need to read the value of the date here. The
+            # setValue method is always getting an array with the .range-lower
+            # and .range-upper jquery objects in it so checking for empty
+            # values is done here to keep setValue generic.
+            dateString = value?.val()
+
+            if dateString? and dateString != ""
+                @ui.lowerBound.datepicker('setDate', new Date(dateString))
+            else
+                @ui.lowerBound.datepicker('_clearDate', this)

         # Override the default behavior so the date is formatted correctly
         # for use in the placeholder.
@@ -71,7 +81,17 @@
         # applied to the datepicker rather than the textbox. The datepicker
         # will handle the updating of the textbox.
         setUpperBoundValue: (value) ->
-            @ui.upperBound.datepicker('setDate', new Date(value))
+            # Since the changeDate event is causing this method to receive a
+            # query object we need to read the value of the date here. The
+            # setValue method is always getting an array with the .range-lower
+            # and .range-upper jquery objects in it so checking for empty
+            # values is done here to keep setValue generic.
+            dateString = value?.val()
+
+            if dateString? and dateString != ""
+                @ui.upperBound.datepicker('setDate', new Date(dateString))
+            else
+                @ui.upperBound.datepicker('_clearDate', this)

     {DateControl}


SocialWorker: add nodejs cpe, mem, gc metric monitoring

--- workers/datadog.coffee
+++ workers/datadog.coffee
@@ -1,13 +1,16 @@
+os              = require 'os'
 { argv }        = require 'optimist'
 KONFIG          = require('koding-config-manager').load("main.#{argv.c}")
-{ MetricsBase } = require 'koding-datadog'
+monitor         = require('appmetrics').monitor()
+{ DogStatsD
+  MetricsBase } = require 'koding-datadog'

 module.exports = class Metrics extends MetricsBase

   @prefix : 'socialWorker'

-  @populateTags : (tags) ->
+  @populateCommonTags : (tags) ->

     tags ?= []
     tags.push "version:#{KONFIG.version}"
@@ -27,7 +30,7 @@
     return "#{@prefix}.#{constructorName}.#{methodName}.#{type}"

-  @methodMetrics : (opts) ->
+  @getMethodMetrics : (opts) ->

     return {
       increment    :
@@ -39,6 +42,94 @@

   @sendMethodMetrics : (opts) ->

-    tags       = @populateTags()
+    tags       = @populateCommonTags()
     metricName = @generateName opts
-    @sendMetrics @methodMetrics(opts), metricName, tags
+    @sendMetrics @getMethodMetrics(opts), metricName, tags
+
+
+  @getNodejsProcessMetrics : ->
+
+    memUsage = process.memoryUsage()
+
+    return {
+      gauge       :
+        rss       : memUsage.rss
+        heapUsed  : memUsage.heapUsed
+        heapTotal : memUsage.heapTotal
+    }
+
+
+  @populateNodejsTags : (tags) ->
+
+    tags ?= []
+
+    tagList = {
+      os      : [ 'type', 'release', 'hostname' ]
+      process : [ 'cwd', 'pid', 'argv', 'title', 'getuid', 'getgid',
+        'version', 'platform', 'execPath' ]
+    }
+
+    for key, value of tagList
+      for prop in value
+        tagName  = "#{key}.#{prop}"
+        tagValue = ''
+
+        source = switch
+          when key is 'process'  then process
+          when key is 'os'       then os
+
+        tagValue = switch
+          when not source[prop]                   then 0
+          when Array.isArray source[prop]         then source[prop].join()
+          when typeof source[prop] is 'function'  then source[prop]()
+          else                                         source[prop]
+
+        tagValue = @sanitizeTagValue tagValue
+        tags.push "#{tagName}:#{tagValue}"
+
+    return tags
+
+
+  @monitorNodejs : ->
+
+    tags = @populateCommonTags()
+    tags = @populateNodejsTags(tags)
+
+    monitor.on 'eventloop', (eventloop) =>
+      eventloopMetrics =
+        gauge           :
+          'latency.min' : eventloop.latency.min
+          'latency.max' : eventloop.latency.max
+          'latency.avg' : eventloop.latency.avg
+
+      @sendMetrics eventloopMetrics, 'nodejs.eventloop', tags
+
+    monitor.on 'memory', (memory) =>
+      memMetrics =
+        gauge                :
+          'process.private'  : memory.private
+          'process.physical' : memory.physical
+          'process.virtual'  : memory.virtual
+          'system.used'      : memory.physical_used
+          'system.total'     : memory.physical_total
+
+      @sendMetrics memMetrics, 'nodejs.memory', tags
+
+    monitor.on 'gc', (gc) =>
+      gcMetrics =
+        gauge      :
+          size     : gc.size
+          used     : gc.used
+          duration : gc.duration
+
+      @sendMetrics gcMetrics, 'nodejs.gc', tags
+
+    monitor.on 'cpu', (cpu) =>
+      cpuMetrics =
+        gauge     :
+          process : cpu.process
+          system  : cpu.system
+
+      @sendMetrics cpuMetrics, 'nodejs.cpu', tags
+
+


Only broadcast date change if it's valid [GATES-34]

--- src/core/javascripts/directives/helpers.js.coffee
+++ src/core/javascripts/directives/helpers.js.coffee
@@ -157,8 +157,8 @@
     scope.$watch 'bb_date.js_date', (newval, oldval) ->
       ndate = moment(newval)
       if !scope.bb_date.date.isSame(ndate)
-        scope.bb_date.date = ndate
-        scope.$broadcast('dateChanged', moment(ndate))
+        scope.bb_date.date = ndate
+        scope.$broadcast('dateChanged', moment(ndate)) if moment(ndate).isValid()


Change keybinding

Change keybinding

--- keymaps/atom-bemmet.cson
+++ keymaps/atom-bemmet.cson
@@ -8,4 +8,4 @@
 # For more detailed documentation see
 # https://atom.io/docs/latest/behind-atom-keymaps-in-depth
 'atom-workspace':
-  'cmd-B': 'atom-bemmet:convert'
+  'shift-cmd-C': 'atom-bemmet:convert'


Remove unused params

Remove unused params

--- resources/assets/coffee/_classes/nav2.coffee
+++ resources/assets/coffee/_classes/nav2.coffee
@@ -78,7 +78,7 @@
     @currentMenu == 'nav2-login-box'

-  stickLogin: (_e, target) =>
+  stickLogin: =>
     return unless @loginBoxVisible()

     @loginBox.style.position =


add vote method to bands controller

add vote method to bands controller

--- controllers/bands.coffee
+++ controllers/bands.coffee
@@ -7,24 +7,32 @@
   Band.find().sort(created_at: 'descending').exec(
     (err, result) ->
       return res.status(500).json error: err if err?
-      return res.status(200).json results
+      return res.status(200).json result
   )

 # GET /api/bands/:id
 exports.show = (req, res) ->
   Band.findById req.params.id, (err, result) ->
     return res.status(500).json error: err if err?
-    return res.status(200).json results
+    return res.status(200).json result

 # POST /api/bands
 exports.create = (req, res) ->
-  data1 = _.merge {}, req.body, {submitted_by: req.body.submitted_by} # {submitted_by: req.user._id}
-  new Band(data1).save (err, result1) ->
+  userId = req.user._id
+  req.body.submitted_by = userId
+  new Band(req.body).save (err, result1) ->
     return res.status(500).json error: err if err?
-    data2 = {parent: result1._id}
-    new Vote(data2).save (err, result2) ->
+    data = {parent: result1._id, user_id: userId}
+    new Vote(data).save (err, result2) ->
       return res.status(500).json error: err if err?
       return res.status(200).json {band: result1, vote: result2}
+
+# POST /api/bands/:id/vote
+exports.vote = (req, res) ->
+  data = {parent: req.params.id, user_id: req.user._id}
+  new Vote(data).save (err, result) ->
+    return res.status(500).json error: err if err?
+    return res.status(200).json result

 # PUT /api/bands/:id
 exports.update = (req, res) ->


room check

room check

--- scripts/room-check.coffee
+++ scripts/room-check.coffee
@@ -1,4 +1,4 @@
 module.exports = (robot) ->
   robot.respond /room check/i, (msg) ->
-    msg.send msg.user.room
+    msg.send msg.message.user.room


Using elapsed time in delayed events

Using elapsed time in delayed events

--- src/timed_events.coffee
+++ src/timed_events.coffee
@@ -18,6 +18,8 @@
       while event.lastFired < I.age + elapsedTime
         self.sendOrApply(fn)
         event.lastFired += period
+
+  self

   ###*
   Execute fn every n frames.


Closes #1082. Puts back use of function calls in parameter lists.

--- lexer.coffee
+++ lexer.coffee
@@ -369,9 +369,9 @@
       switch tok[0]
         when ')'
           stack.push tok
-        when '('
+        when '(', 'CALL_START'
           if stack.length then stack.pop()
-          else
+          else if tok[0] is '('
             tok[0] = 'PARAM_START'
             return this
     this


Adding unsafe writes to the logger in hope.

--- src/winston-veoo.coffee
+++ src/winston-veoo.coffee
@@ -15,7 +15,7 @@
   if err
     console.log("Could not connect to redis for dynamic mongo info #{err}, #{mongo_log_url}")
   else
-    db = mongoose.createConnection(mongo_log_url, (err) ->
+    db = mongoose.createConnection(mongo_log_url, {db:{safe:false}}, (err) ->
       if err
         console.log("could not connect to DB: " + err)
     )


fullpieagent testing

fullpieagent testing

--- widgets/fullpie_agent/fullpie_agent.coffee
+++ widgets/fullpie_agent/fullpie_agent.coffee
@@ -62,7 +62,7 @@
     (t) ->
       b = i(t)
       arc b
-  update: (dataSet) ->
+  updateITWORKS: (dataSet) ->
     if !dataSet
         dataSet = @get("data")
     if !dataSet
@@ -242,3 +242,183 @@
     #    console.log 'else', 'M' + d.r - 2 + ',' + d.b + 'L' + d.l + 2 + ',' + d.b + ' ' + d.cx + ',' + d.cy
     #    'M' + d.r - 2 + ',' + d.b + 'L' + d.l + 2 + ',' + d.b + ' ' + d.cx + ',' + d.cy
     return
+  update: (dataSet) ->
+    if !dataSet
+        dataSet = @get("data")
+    if !dataSet
+        return
+    $(@node).children("svg").remove();
+    width = 750
+    height = 450
+    radius = Math.min(width, height) / 2
+    radiuso = 135
+    radiusi = 90
+    color = d3.scale.category20()
+    pie = d3.layout.pie().value((d) -> d.value)
+    arc = d3.svg.arc().innerRadius(radiusi).outerRadius(radiuso)
+    svg = d3.select(@node).append('svg').attr('width', width).attr('height', height).append('g').attr('transform', 'translate(' + width / 2 + ',' + height / 2 + ')')
+    console.log 'update pie', dataSet
+    that = this
+    piedata = pie(dataSet[0].value)
+    console.log 'update pie', piedata
+    #create a marker element if it doesn't already exist
+    defs = svg.select('defs')
+    if defs.empty()
+      defs = svg.append('defs')
+    marker = defs.select('marker#circ')
+    if marker.empty()
+      defs.append('marker').attr('id', 'circ').attr('markerWidth', 6).attr('markerHeight', 6).attr('refX', 3).attr('refY', 3).append('circle').attr('cx', 3).attr('cy', 3).attr 'r', 3
+    #Create/select <g> elements to hold the different types of graphics
+    #and keep them in the correct drawing order
+    pathGroup = svg.select('g.piePaths')
+    if pathGroup.empty()
+      pathGroup = svg.append('g').attr('class', 'piePaths')
+    pointerGroup = svg.select('g.pointers')
+    if pointerGroup.empty()
+      pointerGroup = svg.append('g').attr('class', 'pointers')
+    labelGroup = svg.select('g.labels')
+    if labelGroup.empty()
+      labelGroup = svg.append('g').attr('class', 'labels')
+    path = pathGroup.selectAll('path.pie').data(piedata)
+    #console.log("path",this.path.empty())
+    path.enter().append('path').attr('class', 'pie').attr 'fill', (d, i) ->
+      color i
+    path.attr('d',arc)
+        .exit()
+    #@path.transition().duration(1500).attrTween 'd', that.pieTween
+    #path.transition()
+    #    .duration(1000)
+    #    #.attrTween("d", pieTween)
+    #    .attrTween("d", (d,i) ->
+    #        #console.log("d",d)
+    #        #console.log("i",i)
+    #        i = d3.interpolate({startAngle: 0, endAngle: 0}, {startAngle: d.startAngle, endAngle: d.endAngle})
+    #        return (t) ->
+    #            b = i(t)
+    #            return arc(b)
+    #    )
+    #@path.exit().transition().duration(300).attrTween('d', that.removePieTween).remove()
+    #path.exit()
+    #    .transition()
+    #    .duration(300)
+    #    .attrTween("d", (d,i) ->
+    #        i = d3.interpolate({startAngle: d.startAngle,endAngle: d.endAngle},{startAngle: 2 * Math.PI,endAngle: 2 * Math.PI})
+    #        return (t) ->
+    #            b = i(t)
+    #            return arc(b)
+    #    )
+    #    .remove()
+    labels = labelGroup.selectAll('text').data(piedata.sort((p1, p2) ->
+      p1.startAngle - p2.startAngle
+    ))
+    labels.enter().append('text').attr 'text-anchor', 'middle'
+    labels.exit().remove()
+    labelLayout = d3.geom.quadtree().extent([
+      [
+        -width
+        -height
+      ]
+      [
+        width
+        height
+      ]
+    ]).x((d) ->
+      d.x
+    ).y((d) ->
+      d.y
+    )([])
+    #create an empty quadtree to hold label positions
+    maxLabelWidth = 0
+    maxLabelHeight = 0
+    labels.text((d) ->
+      # Set the text *first*, so we can query the size
+      # of the label with .getBBox()
+      d.value
+    ).each((d, i) ->
+      # Move all calculations into the each function.
+      # Position values are stored in the data object
+      # so can be accessed later when drawing the line
+      ### calculate the position of the center marker ###
+      a = (d.startAngle + d.endAngle) / 2
+      #trig functions adjusted to use the angle relative
+      #to the "12 o'clock" vector:
+      #console.log 'ts: a', a
+      d.cx = Math.sin(a) * (radius - 75)
+      d.cy = -Math.cos(a) * (radius - 75)
+      ### calculate the default position for the label,
+         so that the middle of the label is centered in the arc
+      ###
+      bbox = @getBBox()
+      #bbox.width and bbox.height will
+      #describe the size of the label text
+      labelRadius = radius - 20
+      d.x = Math.sin(a) * labelRadius
+      d.l = d.x - bbox.width / 2 - 2
+      d.r = d.x + bbox.width / 2 + 2
+      d.y = -Math.cos(a) * (radius - 20)
+      d.b = d.oy = d.y + 5
+      d.t = d.y - bbox.height - 5
+      #console.log 'ts: d', d
+      ### check whether the default position
+         overlaps any other labels
+      ###
+      conflicts = []
+      labelLayout.visit (node, x1, y1, x2, y2) ->
+        #recurse down the tree, adding any overlapping
+        #node is the node in the quadtree,
+        #node.point is the value that we added to the tree
+        #x1,y1,x2,y2 are the bounds of the rectangle that
+        #this node covers
+        if x1 > d.r + maxLabelWidth / 2 or x2 < d.l - maxLabelWidth / 2 or y1 > d.b + maxLabelHeight / 2 or y2 < d.t - maxLabelHeight / 2
+          return true
+        #don't bother visiting children or checking this node
+        p = node.point
+        v = false
+        h = false
+        if p
+          #p is defined, i.e., there is a value stored in this node
+          h = p.l > d.l and p.l <= d.r or p.r > d.l and p.r <= d.r or p.l < d.l and p.r >= d.r
+          #horizontal conflict
+          v = p.t > d.t and p.t <= d.b or p.b > d.t and p.b <= d.b or p.t < d.t and p.b >= d.b
+          #vertical conflict
+          if h and v
+            conflicts.push p
+          #add to conflict list
+        return
+      if conflicts.length
+        console.log d, ' conflicts with ', conflicts
+        rightEdge = d3.max(conflicts, (d2) ->
+          `var maxLabelHeight`
+          `var maxLabelWidth`
+          d2.r
+        )
+        d.l = rightEdge
+        d.x = d.l + bbox.width / 2 + 5
+        d.r = d.l + bbox.width + 10
+      else
+        console.log 'no conflicts for ', d
+      ### add this label to the quadtree, so it will show up as a conflict
+         for future labels.
+      ###
+      labelLayout.add d
+      maxLabelWidth = Math.max(maxLabelWidth, bbox.width + 10)
+      maxLabelHeight = Math.max(maxLabelHeight, bbox.height + 10)
+      return
+    )
+    #.transition()
+    .attr('x', (d) ->
+      d.x
+    ).attr 'y', (d) ->
+      d.y
+    pointers = pointerGroup.selectAll('path.pointer').data(piedata)
+    pointers.enter().append('path').attr('class', 'pointer').style('fill', 'none').style('stroke', 'black').attr 'marker-end', 'url(#circ)'
+    pointers.exit().remove()
+    #pointers.transition().attr 'd', (d) ->
+    #  console.log 'ts: d', d
+    #  if d.cx > d.l
+    #    console.log 'if d.cx > d.l','M' + d.l + 2 + ',' + d.b + 'L' + d.r - 2 + ',' + d.b + ' ' + d.cx + ',' + d.cy
+    #    'M' + d.l + 2 + ',' + d.b + 'L' + d.r - 2 + ',' + d.b + ' ' + d.cx + ',' + d.cy
+    #  else
+    #    console.log 'else', 'M' + d.r - 2 + ',' + d.b + 'L' + d.l + 2 + ',' + d.b + ' ' + d.cx + ',' + d.cy
+    #    'M' + d.r - 2 + ',' + d.b + 'L' + d.l + 2 + ',' + d.b + ' ' + d.cx + ',' + d.cy
+    return


fullpieagent testing

fullpieagent testing

--- widgets/fullpie_agent/fullpie_agent.coffee
+++ widgets/fullpie_agent/fullpie_agent.coffee
@@ -18,7 +18,7 @@

     width = 400 #width
     height = 400 #height
-    radius = 300 #radius
+    radius = 200 #radius

     color = d3.scale.ordinal()
       #.domain([1,10])


fullpieagent testing

fullpieagent testing

--- widgets/fullpie_agent/fullpie_agent.coffee
+++ widgets/fullpie_agent/fullpie_agent.coffee
@@ -604,8 +604,7 @@
         #x1,y1,x2,y2 are the bounds of the rectangle that
         #this node covers
         if x1 > d.r + maxLabelWidth / 2 or x2 < d.l - maxLabelWidth / 2 or y1 > d.b + maxLabelHeight / 2 or y2 < d.t - maxLabelHeight / 2
-          return true
-        #don't bother visiting children or checking this node
+          return true #don't bother visiting children or checking this node
         p = node.point
         v = false
         h = false


fullpieagent testing

fullpieagent testing

--- widgets/fullpie_agent/fullpie_agent.coffee
+++ widgets/fullpie_agent/fullpie_agent.coffee
@@ -72,7 +72,7 @@
     @radius = Math.min(@width, @height) / 2
     @radiuso = 135
     @radiusi = 90
-    @color = d3.scale.category20()
+    color = d3.scale.category20()
     @pie = d3.layout.pie()#.sort(null)
     @arc = d3.svg.arc().innerRadius(@radiusi).outerRadius(@radiuso)
     @svg = d3.select(@node).append('svg').attr('width', @width).attr('height', @height).append('g').attr('transform', 'translate(' + @width / 2 + ',' + @height / 2 + ')')


add analytics stub to fix test

add analytics stub to fix test

--- src/mobile/apps/home/test/client_view.test.coffee
+++ src/mobile/apps/home/test/client_view.test.coffee
@@ -11,8 +11,8 @@
     benv.setup =>
       benv.expose
         $: benv.require 'jquery'
+        analytics: { track: sinon.stub() }
         Element: window.Element
-
       Backbone.$ = $
       benv.render resolve(__dirname, '../templates/page.jade'), {
         heroUnits: []


don't create vms when people join to the koding group

they are already created at that point

--- workers/social/lib/social/models/vm.coffee
+++ workers/social/lib/social/models/vm.coffee
@@ -488,7 +488,7 @@
     JGroup.on 'MemberAdded', ({group, member})->
       member.fetchUser (err, user)->
         if err then handleError err
-        else if group.slug in ['koding','guests']
+        else if group.slug is 'guests'
           # Following is just here to register this name in the counters collection
           ((require 'koding-counter') {
             db          : JVM.getClient()


Before converting vm call removeRelatedDomains on old vm to delete domains related to guest vm

--- workers/social/lib/social/models/vm.coffee
+++ workers/social/lib/social/models/vm.coffee
@@ -346,9 +346,9 @@
           return callback err, []  if err or not vm
           JVM.fetchDomains {hostnameAlias: vm.hostnameAlias}, callback

-  @removeRelatedDomains = (vm)->
+  @removeRelatedDomains = (vm, callback=->)->
     vmInfo = @parseAlias vm.hostnameAlias
-    return  unless vmInfo
+    return callback null  unless vmInfo

     # Create same aliases based on vm info
     aliasesToDelete = @createAliases vmInfo
@@ -365,6 +365,7 @@

     JDomain = require './domain'
     JDomain.remove selector, (err)->
+      callback err
       return console.error "Failed to delete domains:", err  if err

   remove: (callback)->
@@ -501,24 +502,36 @@

       JVM.one {hostnameAlias}, (err, vm)=>
         return console.error err  if err or not vm
-        console.log "Old vm found, passing..."
-        JAccount.one {'profile.nickname':username}, (err, account)=>
-          return console.error err  if err or not account
-          console.log "New account found, passing..."
-          vm.update {$set: {hostnameAlias: newHostNameAlias}}, (err)=>
-            return console.error err  if err
-            console.log "VM hostnameAlias updated, passing..."
-            nameFactory = (require 'koding-counter')
-              db          : JVM.getClient()
-              offset      : 0
-              counterName : "koding~#{username}~"
-            nameFactory.next (err, uid)=>
+        # Old vm found
+
+        # Removing old vm domains...
+        JVM.removeRelatedDomains vm, (err)=>
+          if err
+            console.error "Failed to remove old domains for #{hostnameAlias}"
+
+          JAccount.one {'profile.nickname':username}, (err, account)=>
+            return console.error err  if err or not account
+            # New account found
+
+            vm.update {$set: {hostnameAlias: newHostNameAlias}}, (err)=>
               return console.error err  if err
-              console.log "Counter created with #{uid}, passing..."
-              JVM.ensureDomainSettings {
-                type:'user', nickname:username, groupSlug:'koding'
-                account, vm
-              }
+              # VM hostnameAlias updated
+
+              nameFactory = (require 'koding-counter')
+                db          : JVM.getClient()
+                offset      : 0
+                counterName : "koding~#{username}~"
+              nameFactory.next (err, uid)=>
+                return console.error err  if err
+                # Counter created
+
+                JVM.ensureDomainSettings {
+                  type:'user', nickname:username, groupSlug:'koding'
+                  account, vm
+                }
+
+                console.log """Migration completed for
+                               #{hostnameAlias} to #{newHostNameAlias}"""

     JGroup.on 'GroupDestroyed', (group)->
       group.fetchVms (err, vms)->


Fixed the hour options in the date/time picker  (#119)


handler: remove api test, duplicate with options

--- test/api/handler.coffee
+++ test/api/handler.coffee
@@ -1,13 +0,0 @@
-
-nikita = require '../../src'
-test = require '../test'
-
-describe 'api handler', ->
-
-  it.skip 'end', ->
-    # CoffeeScript bug #4616
-    # Test is disabled unti it's fixed
-    n = nikita()
-    .call header: 'h1', handler: (->)
-    .call header: 'h2', handler: (->)
-    .promise()


Delay the model for 1 minute

Delay the model for 1 minute

--- app/mixins/socket.coffee
+++ app/mixins/socket.coffee
@@ -80,6 +80,10 @@
       location.reload()

     show_feedback: ->
-      @get("controllers.application.feedback").send("showModal")
+      that = @
+      setTimeout ->
+        that.get("controllers.application.feedback").send("showModal")
+      ,
+        60 * 1000

 `export default SocketMixin`


Minor sockets bugfix

Minor sockets bugfix

--- app/mixins/socket.coffee
+++ app/mixins/socket.coffee
@@ -18,11 +18,10 @@

     file_new: (data)->
       Notify.info "New file added"
-      file = @store.normalize "file", data
+      file = @store.push "file", @store.normalize "file", data
       file.get "analyses"
-      @store.push "file", file
       @store
-        .find 'project', data.project
+        .find 'project', data.project.id
         .then (project) ->
           project.set "lastFile", file


Delete unused/dead dashboard file

Delete unused/dead dashboard file

--- app/pages/dashboard.cjsx
+++ app/pages/dashboard.cjsx
@@ -1,88 +0,0 @@
-React = require 'react'
-LineGraph = require '../dashboard/line-graph/line-graph'
-LineGraphLegend = require '../dashboard/line-graph/legend'
-
-PROJECT_CLASSIFICATIONS =
-  data: [
-    {average_classifications: 7, user_classifications: 5, date: "08/13/2014"}
-    {average_classifications: 7, user_classifications: 2, date: "08/14/2014"}
-    {average_classifications: 7, user_classifications: 8, date: "08/15/2014"}
-    {average_classifications: 7, user_classifications: 4, date: "08/16/2014"}
-    {average_classifications: 4, user_classifications: 9, date: "08/17/2014"}
-    {average_classifications: 4, user_classifications: 5, date: "08/18/2014"}
-    {average_classifications: 7, user_classifications: 2, date: "08/19/2014"}
-    {average_classifications: 2, user_classifications: 8, date: "08/20/2014"}
-    {average_classifications: 7, user_classifications: 0, date: "08/21/2014"}
-    {average_classifications: 7, user_classifications: 9, date: "08/22/2014"}
-    {average_classifications: 3, user_classifications: 3, date: "08/23/2014"}
-    {average_classifications: 7, user_classifications: 20, date: "08/24/2014"}
-    {average_classifications: 7, user_classifications: 15, date: "08/25/2014"}
-    {average_classifications: 4, user_classifications: 15, date: "08/26/2014"}
-    {average_classifications: 7, user_classifications: 12, date: "08/27/2014"}
-    {average_classifications: 7, user_classifications: 20, date: "08/28/2014"}
-    {average_classifications: 7, user_classifications: 3, date: "08/29/2014"}
-    {average_classifications: 7, user_classifications: 5, date: "08/31/2014"}
-    {average_classifications: 6, user_classifications: 7, date: "09/01/2014"}
-    {average_classifications: 7, user_classifications: 16, date: "09/02/2014"}
-    {average_classifications: 7, user_classifications: 18, date: "09/03/2014"}
-    {average_classifications: 9, user_classifications: 25, date: "09/04/2014"}
-    {average_classifications: 7, user_classifications: 18, date: "09/05/2014"}
-    {average_classifications: 7, user_classifications: 13, date: "09/06/2014"}
-    {average_classifications: 8, user_classifications: 13, date: "09/07/2014"}
-    {average_classifications: 7, user_classifications: 14, date: "09/08/2014"}
-    {average_classifications: 7, user_classifications: 17, date: "09/09/2014"}
-    {average_classifications: 7, user_classifications: 19, date: "09/10/2014"}
-    {average_classifications: 2, user_classifications: 5, date: "09/11/2014"}
-  ]
-
-PROJECT_STATS_DATA = [
-  {name: "Total Classifications", value: "26,127"}
-  {name: "Total Users", value: "3,145"}
-  {name: "Total Subjects", value: "92,301"}
-  {name: "Percent Complete", value: "27%"}
-]
-
-USER_STATS_DATA = [
-  {name: "Total Classifications", value: "1,231"}
-  {name: "Average Daily Classifications", value: "15"}
-  {name: "Total Talk Comments", value: "71"}
-  {name: "Classifications Today", value: "5"}
-]
-
-module.exports = React.createClass
-  displayName: 'Dashboard'
-
-  statsItem: (d, i) ->
-

{d.value}

{d.name}

-
-  render: ->
-

-

-
{@props.project.name} Statistics

-
{PROJECT_STATS_DATA.map(@statsItem)}

-

-
-

-
Your Statistics

-
{USER_STATS_DATA.map(@statsItem)}

-

-
-
Classification Count

-
-      <LineGraph
-        data={PROJECT_CLASSIFICATIONS.data}
-        xKey="date"
-        yKeys={["average_classifications", "user_classifications"]}
-        dataColors={["lightgrey", "rgb(51, 187, 255)"]}
-        pointRadius={10}
-        height={300}
-        yLines={5}
-        yLabel="Classifications" />
-
-      <LineGraphLegend
-        data={[
-          {color: 'lightgrey', text: 'Community Average'},
-          {color: 'rgb(51, 187, 255)', text: 'Your Totals'}
-        ]}
-      />
-


fix for bug in statistics for tokens containing :

git-svn-id: 00f01ee4f729ccc52c3e336777c3e81b5adc88db@256676 ef392f78-ee14-0410-84e5-9db46baae5bb

--- app/scripts/model.coffee
+++ app/scripts/model.coffee
@@ -250,7 +250,15 @@
                 minWidth : minWidth

         groups = _.groupBy _.keys(data.total.absolute), (item) ->
-            item.replace(/(:.+?)(\/|$| )/g, "$2")
+            fields = item.split("/")
+            newFields = []
+            for [reduceVal, field] in _.zip reduceVals, fields
+                if reduceVal in ["saldo", "prefix", "suffix", "lex", "lemma"]
+                    newFields.push field.replace(/(:.+?)($| )/g, "$2")
+                else
+                    newFields.push field
+            newFields.join("/")
+            return newFields

         wordArray = _.keys groups


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/game_object.coffee
+++ test/game_object.coffee
@@ -20,6 +20,8 @@
   o = GameObject()

   equals o.closest([]), null
+
+

 test "[event] create", 2, ->
   GameObject


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/game_object.coffee
+++ test/game_object.coffee
@@ -71,7 +71,7 @@
 test "[event] destroy", 2, ->
   o = GameObject
   o.bind "destroy", ->
-      ok true, "destroyed event is fired on destroy"
+    ok true, "destroyed event is fired on destroy"
   .destroy()

   GameObject


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/game_object.coffee
+++ test/game_object.coffee
@@ -36,7 +36,7 @@
 test "#sendOrApply", ->
   o = GameObject()

-  o.sendOrApply ""
+  o.sendOrApply "pos"

 test "[event] create", 2, ->
   GameObject


Ensure that different features produce different results

--- services/web/test/UnitTests/coffee/Project/ProjectControllerTests.coffee
+++ services/web/test/UnitTests/coffee/Project/ProjectControllerTests.coffee
@@ -497,7 +497,10 @@
 		it 'should produce the expected results', ->
 			result = @ids.map (i) =>
 				@ProjectController._isInPercentageRollout('abcd', i, 50)
-			expect(result).to.deep.equal [
+			expect(
+				@ids.map (i) =>
+					@ProjectController._isInPercentageRollout('abcd', i, 50)
+			).to.deep.equal [
 				false,
 				false,
 				false,
@@ -518,6 +521,31 @@
 				true,
 				false,
 				true
-			]
-
-
+				]
+			expect(
+				@ids.map (i) =>
+					@ProjectController._isInPercentageRollout('efgh', i, 50)
+			).to.deep.equal [
+				false,
+				false,
+				false,
+				false,
+				true,
+				false,
+				false,
+				true,
+				false,
+				false,
+				true,
+				true,
+				true,
+				false,
+				true,
+				false,
+				true,
+				true,
+				false,
+				false
+			]
+
+


Convert to coffeescript

Convert to coffeescript

--- test/representer.coffee
+++ test/representer.coffee
@@ -7,7 +7,7 @@
     parser: (x) -> { parsed: !x.parsed }
     builder: (x) -> { built: !x.built }

-repObject =
+exampleObject =
   links: [
     { rels: [ "item" ], href: "/item1" }
     { rels: [ "item" ], href: "/item2" }
@@ -20,4 +20,9 @@
       data = { built: false }
       rep = new Representer(formats, data)
       newFormat = rep.toFormat("application/vnd.fake-format")
-      expect(newFormat.built).to.equal true+      expect(newFormat.built).to.equal true
+
+  describe "#hasLinks", ->
+    it "should respond true if there are links", ->
+      rep = new Representer(formats, data)
+      expect(rep.hasLinks()).to.be true


fix helper to use new real/effective user in global window

--- test/unit/helper.coffee
+++ test/unit/helper.coffee
@@ -11,11 +11,15 @@
   global.Backbone = require 'backbone'
   global.Backbone.setDomLibrary global.jQuery

-  global.user =
+  auser =
     shortName: 'test'
     apiKey: 'fakeapikey'
     email: 'test@example.com'
     displayName: 'Tesuto Tesoto-San'
+
+  global.user =
+    effective: auser
+    real: auser

 fakeWindow()


added SHARELATEX_EMAIL_SMTP_TLS_REJECT_UNAUTH and SHARELATEX_EMAIL_SMTP_IGNORE_TLS

--- server-ce/settings.coffee
+++ server-ce/settings.coffee
@@ -443,7 +443,7 @@
 #     http://www.nodemailer.com/docs/transports

-if process.env["SHARELATEX_EMAIL_FROM_ADDRESS"]
+if process.env["SHARELATEX_EMAIL_FROM_ADDRESS"]?

 	settings.email =
 		fromAddress: process.env["SHARELATEX_EMAIL_FROM_ADDRESS"]
@@ -457,12 +457,16 @@
 			host: process.env["SHARELATEX_EMAIL_SMTP_HOST"]
 			port: process.env["SHARELATEX_EMAIL_SMTP_PORT"],
 			secure: process.env["SHARELATEX_EMAIL_SMTP_SECURE"]
+			ignoreTLS: process.env["SHARELATEX_EMAIL_SMTP_IGNORE_TLS"]

-if process.env["SHARELATEX_EMAIL_SMTP_USER"] or process.env["SHARELATEX_EMAIL_SMTP_PASS"]
+if process.env["SHARELATEX_EMAIL_SMTP_USER"]? or process.env["SHARELATEX_EMAIL_SMTP_PASS"]?
 	settings.email.parameters.auth =
-			user: process.env["SHARELATEX_EMAIL_SMTP_USER"]
-			pass: process.env["SHARELATEX_EMAIL_SMTP_PASS"]
-
+		user: process.env["SHARELATEX_EMAIL_SMTP_USER"]
+		pass: process.env["SHARELATEX_EMAIL_SMTP_PASS"]
+
+if process.env["SHARELATEX_EMAIL_SMTP_TLS_REJECT_UNAUTH"]?
+	settings.email.parameters.tls =
+		rejectUnauthorized: process.env["SHARELATEX_EMAIL_SMTP_TLS_REJECT_UNAUTH"]

 # Password Settings
 # -----------