Fixed: details popup triggered react warnings

--- client/app/components/message_header.coffee
+++ client/app/components/message_header.coffee
@@ -53,7 +53,7 @@
         format = (user) ->
             items = []
             if user.name
-                key = user.address.replace(/\W/g, '')
+                key = user.address.replace /\W/g, ''
                 items.push "#{user.name} "
                 items.push span className: 'contact-address', key: key,
                     i className: 'fa fa-angle-left'
@@ -146,12 +146,12 @@
             div className: 'popup', 'aria-hidden': not @state.showDetails,
                 table null,
                     tbody null,
-                        row 'from',    @formatUsers(from), 'headers from'
-                        row 'to',      @formatUsers(to[0]), 'headers to', to.length if to.length
-                        row 'destTo',  @formatUsers(dest) for dest in to[1..] if to.length
-                        row 'cc',      @formatUsers(cc[0]), 'headers cc', cc.length if cc.length
-                        row 'destCc',  @formatUsers(dest) for dest in cc[1..] if cc.length
-                        row 'reply',   @formatUsers(reply), 'headers reply-to' if reply?
+                        row 'from', @formatUsers(from), 'headers from'
+                        row 'to', @formatUsers(to[0]), 'headers to', to.length if to.length
+                        row "destTo#{key}", @formatUsers(dest) for dest, key in to[1..] if to.length
+                        row 'cc', @formatUsers(cc[0]), 'headers cc', cc.length if cc.length
+                        row "destCc#{key}", @formatUsers(dest) for dest, key in cc[1..] if cc.length
+                        row 'reply', @formatUsers(reply), 'headers reply-to' if reply?
                         row 'created', @props.message.get('createdAt'), 'headers date'
                         row 'subject', @props.message.get('subject'), 'headers subject'


Make the REPL *CLI* use the global context so as to be consistent with the `node` REPL CLI.

Make the REPL *CLI* use the global context so as to (a) be consistent
with the `node` REPL CLI and, therefore, (b) make packages that modify
native prototypes (such as 'colors' and 'sugar') work as expected.

Note that, by contrast, programmatic use (`require 'repl'`) will
continue to default to a NON-global context - again, consistent with
node's behavior.

--- command.coffee
+++ command.coffee
@@ -25,7 +25,7 @@

 hidden = (file) -> /^\.|~$/.test file

-# The help banner that is printed when `coffee` is called without arguments.
+# The help banner that is printed in conjunction with `-h`/`--help`.
 BANNER = '''
   Usage: coffee [options] path/to/script.coffee -- [args]

@@ -65,15 +65,19 @@
 # `--` will be passed verbatim to your script as arguments in `process.argv`
 exports.run = ->
   parseOptions()
-  return forkNode()                      if opts.nodejs
-  return usage()                         if opts.help
-  return version()                       if opts.version
-  return require('./repl').start()       if opts.interactive
+  # Make the REPL *CLI* use the global context so as to (a) be consistent with the
+  # `node` REPL CLI and, therefore, (b) make packages that modify native prototypes
+  # (such as 'colors' and 'sugar') work as expected.
+  replCliOpts = useGlobal: yes
+  return forkNode()                             if opts.nodejs
+  return usage()                                if opts.help
+  return version()                              if opts.version
+  return require('./repl').start(replCliOpts)   if opts.interactive
   if opts.watch and not fs.watch
     return printWarn "The --watch feature depends on Node v0.6.0+. You are running #{process.version}."
-  return compileStdio()                  if opts.stdio
-  return compileScript null, sources[0]  if opts.eval
-  return require('./repl').start()       unless sources.length
+  return compileStdio()                         if opts.stdio
+  return compileScript null, sources[0]         if opts.eval
+  return require('./repl').start(replCliOpts)   unless sources.length
   literals = if opts.run then sources.splice 1 else []
   process.argv = process.argv[0..1].concat literals
   process.argv[0] = 'coffee'


Edited src/command.coffee via GitHub

Edited src/command.coffee via GitHub

--- command.coffee
+++ command.coffee
@@ -30,7 +30,7 @@
   ['-i', '--interactive',     'run an interactive CoffeeScript REPL']
   ['-o', '--output [DIR]',    'set the directory for compiled JavaScript']
   ['-j', '--join [FILE]',     'concatenate the scripts before compiling']
-  ['-w', '--watch',           'watch scripts and repeat enabled tasks for any that change']
+  ['-w', '--watch',           'watch scripts for changes and rerun commands']
   ['-p', '--print',           'print the compiled JavaScript to stdout']
   ['-l', '--lint',            'pipe the compiled JavaScript through JavaScript Lint']
   ['-s', '--stdio',           'listen for and compile scripts over stdio']


Hidden files no longer queued for --join (fixes #2263)

--- command.coffee
+++ command.coffee
@@ -98,9 +98,10 @@
         throw err if err and err.code isnt 'ENOENT'
         return if err?.code is 'ENOENT'
         index = sources.indexOf source
+        files = files.filter (file) -> not hidden file
         sources[index..index] = (path.join source, file for file in files)
         sourceCode[index..index] = files.map -> null
-        for file in files when not hidden file
+        files.forEach (file) ->
           compilePath (path.join source, file), no, base
     else if topLevel or path.extname(source) is '.coffee'
       watch source, base if opts.watch


Fix progress var instantiation in send-draft task

--- src/flux/tasks/send-draft.coffee
+++ src/flux/tasks/send-draft.coffee
@@ -37,8 +37,6 @@
 class SendDraftTask extends Task

   constructor: (@draft, @uploads) ->
-    @_progress = new MultiRequestProgressMonitor()
-    Object.defineProperty(@, 'progress', { get: -> @_progress.value() })
     super

   label: ->
@@ -63,8 +61,11 @@
     .catch(@_onError)

   _uploadAttachments: =>
+    @_progress = new MultiRequestProgressMonitor()
+    Object.defineProperty(@, 'progress', { get: -> @_progress.value() })
+
     Promise.all @uploads.map ({targetPath, size}) =>
-      NylasAPI.makeRequest
+      NylasAPI.makeRequest(
         path: "/files"
         accountId: @draft.accountId
         method: "POST"
@@ -77,10 +78,12 @@
         started: (req) =>
           @_progress.add(targetPath, size, req)
         timeout: 20 * 60 * 1000
+      )
+      .then((file) =>
+        @draft.files.push(file)
+      )
       .finally =>
         @_progress.remove(targetPath)
-      .then (file) =>
-        @draft.files.push(file)

   _sendAndCreateMessage: =>
     NylasAPI.makeRequest


Fix vm plan termination, also bring back group VMs

--- workers/social/lib/social/models/vm.coffee
+++ workers/social/lib/social/models/vm.coffee
@@ -259,9 +259,10 @@
                   vm.remove callback
                 else
                   sub.terminate (err, sub)->
-                  if err
-                    return callback new KodingError 'Unable to update payment (3)'
-                  vm.remove callback
+                    if err
+                      return callback new KodingError 'Unable to update payment (3)'
+                    else
+                      vm.remove callback
           else
             vm.remove callback

@@ -309,24 +310,24 @@
         if err then handleError err
         else user.update { $set: { uid } }, handleError

-    # JGroup.on 'GroupCreated', ({group, creator})->
-    #   group.fetchBundle (err, bundle)->
-    #     console.log err, bundle
-    #     if err then handleError err
-    #     else if bundle and bundle.sharedVM
-    #       creator.fetchUser (err, user)->
-    #         if err then handleError err
-    #         else
-    #           addVm {
-    #             user
-    #             type    : 'group'
-    #             target  : group
-    #             planCode  : 'free'
-    #             planOwner : "group_#{group._id}"
-    #             sudo    : yes
-    #             name    : group.slug
-    #             groups  : wrapGroup group
-    #           }
+    JGroup.on 'GroupCreated', ({group, creator})->
+      group.fetchBundle (err, bundle)->
+        console.log err, bundle
+        if err then handleError err
+        else if bundle and bundle.sharedVM
+          creator.fetchUser (err, user)->
+            if err then handleError err
+            else
+              addVm {
+                user
+                type    : 'group'
+                target  : group
+                planCode  : 'free'
+                planOwner : "group_#{group._id}"
+                sudo    : yes
+                name    : group.slug
+                groups  : wrapGroup group
+              }

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


avatararea: switched to simplified accountpopup

--- client/app/lib/avatararea/avatararea.coffee
+++ client/app/lib/avatararea/avatararea.coffee
@@ -1,7 +1,7 @@
 kd = require 'kd'
 KDCustomHTMLView = kd.CustomHTMLView
 AvatarAreaIconLink = require './avatarareaiconlink'
-AvatarPopupGroupSwitcher = require './avatarpopupgroupswitcher'
+AccountPopup = require './accountpopup'
 AvatarView = require '../commonviews/avatarviews/avatarview'
 JView = require '../jview'
 PopupNotifications = require '../notifications/popupnotifications'
@@ -17,7 +17,8 @@

     super options, data

-    account = @getData()
+    { mainView } = kd.singletons
+    account      = @getData()

     @avatar = new AvatarView
       tagName    : 'div'
@@ -29,18 +30,17 @@
         height   : 25
     , account

-    @groupSwitcherPopup = new AvatarPopupGroupSwitcher
-      cssClass : "group-switcher"
+    @accountPopup = new AccountPopup

-    @groupsSwitcherIcon = new AvatarAreaIconLink
-      cssClass   : 'groups acc-dropdown-icon'
+    @accountIcon = new AvatarAreaIconLink
+      cssClass   : 'acc-dropdown-icon'
       attributes :
-        title    : 'Your groups'
+        title    : 'Account'
         testpath : 'AvatarAreaIconLink'
-      delegate   : @groupSwitcherPopup
+      delegate   : @accountPopup

     @notificationsPopup = new PopupNotifications
-      cssClass : "notification-list"
+      cssClass : 'notification-list'

     @notificationsIcon = new AvatarAreaIconLink
       cssClass   : 'notifications acc-notification-icon'
@@ -48,31 +48,15 @@
         title    : 'Notifications'
       delegate   : @notificationsPopup

-    @once 'viewAppended', =>
-      mainView = kd.getSingleton 'mainView'
-      mainView.addSubView @groupSwitcherPopup
+
+    @on 'viewAppended', ->
+
+      mainView.addSubView @accountPopup
       mainView.addSubView @notificationsPopup
-      @groupSwitcherPopup.listControllerPending.on 'PendingGroupsCountDidChange', (count)=>
-        if count > 0
-        then @groupSwitcherPopup.invitesHeader.show()
-        else @groupSwitcherPopup.invitesHeader.hide()
-        @groupsSwitcherIcon.updateCount count

-      @attachListeners()
-
-    kd.getSingleton('mainController').on 'accountChanged', =>
-      @groupSwitcherPopup.listController.removeAllItems()
-
-      # Commenting out these lines because of
-      # removal of the groups links from avatar popup. ~Umut
-      # @groupSwitcherPopup.populateGroups()
-      # @groupSwitcherPopup.populatePendingGroups()
-
-  attachListeners:->
-
-    @notificationsPopup.on 'NotificationCountDidChange', (count)=>
-      kd.utils.killWait @notificationsPopup.loaderTimeout
-      @notificationsIcon.updateCount count
+      @notificationsPopup.on 'NotificationCountDidChange', (count)=>
+        kd.utils.killWait @notificationsPopup.loaderTimeout
+        @notificationsIcon.updateCount count

   pistachio: ->
@@ -82,6 +66,6 @@
     """
     {{> @avatar}}
     #{profile.firstName}
-    {{> @groupsSwitcherIcon}}
+    {{> @accountIcon}}
     {{> @notificationsIcon}}
     """


Initial functionality completed

Initial functionality completed

--- src/youtube-tracker.coffee
+++ src/youtube-tracker.coffee
@@ -22,16 +22,31 @@
   if not GOOGLE_API_KEY?
     return robot.logger.error "hubot-youtube-tracker: Missing GOOGLE_API_KEY in environment.  Please set and try again."

-  robot.respond /(?:tracker|ytt)(?: chan)? (.*)/i, (msg) ->
-    query = msg.match[1]
+  robot.respond /(?:tracker|ytt) recent (.*)/i, (res) ->
+    query = res.match[1]
+    # get channel
+    QueryYoutube res, '/channels', { part: 'contentDetails,snippet', forUsername: query }, (channelObject) ->
+      channelUrl = 'https://youtube.com/channel/' + channelObject.items[0].id + '/videos'
+      username = channelObject.items[0].snippet.title
+      uploadsId = channelObject.items[0].contentDetails.relatedPlaylists.uploads
+      # get playlist
+      QueryYoutube res, '/playlistItems', { part: 'snippet', playlistId: uploadsId, maxResults: 5 }, (playlistObject) ->
+        videos = []
+        for item in playlistObject.items
+          videos.push( { title: item.snippet.title, id: item.snippet.resourceId.videoId } )
+        resstr = "#{username}: #{channelUrl}"
+        for v in videos
+          resstr += '\n' + v.title + " | https://youtu.be/" + v.id
+        res.send resstr

 QueryYoutube = (res, api, params = {}, handler) ->
+  params.part = 'contentDetails,snippet'
   params.key = GOOGLE_API_KEY
   res.http("https://www.googleapis.com/youtube/v3#{api}")
     .query(params)
     .get() (err, httpRes, body) ->
       if err or httpRes.statusCode isnt 200
-        err = "503 Service Unavailable" if httpStatus is 503
+        err = "503 Service Unavailable" if httpRes.statusCode is 503
         res.reply "An error occurred while attemping to process this request."
         return res.robot.logger.error "hubot-youtube-tracker: #{err}"


go kites: Session timeout via ping/pong.

--- client/landing/app/MainApp/kite/kite.coffee
+++ client/landing/app/MainApp/kite/kite.coffee
@@ -79,6 +79,7 @@
     callback = switch method
       when 'ready'            then @bound 'handleReady'
       when 'error'            then @bound 'handleError'
+      when 'ping'             then @bound 'handlePing'
       when 'pong'             then @bound 'handlePong'
       when 'cycleChannel'     then @bound 'cycleChannel'
       else (@localStore.get method) ? ->
@@ -117,6 +118,12 @@

   handleError: (err) -> console.error err

+  handlePing: ->
+    @channel.publish JSON.stringify
+      method      : 'pong'
+      arguments   : []
+      callbacks   : {}
+
   handlePong: ->
     @channel.emit 'pong'
     @lastPong = Date.now()


--- src/resources/forms/orbeon/newbuilder/resources/form-builder.coffee
+++ src/resources/forms/orbeon/newbuilder/resources/form-builder.coffee
@@ -1,5 +1,7 @@
 Event = YAHOO.util.Event
 YD = YAHOO.util.Dom
+AjaxServer = ORBEON.xforms.server.AjaxServer
+Controls = ORBEON.xforms.Controls

 currentGridTd = null

@@ -13,6 +15,7 @@
     # Keep track the grid id the mouse is over
     currentGridTd = null

+    # Position cell editor controls on the right cell on the mouse over
     Event.addListener document, "mouseover", (event) ->
         target = Event.getTarget event
         gridTd =
@@ -34,3 +37,12 @@
             # We're outside of a grid td: hide cell editor controls
             _.each cellEditorChildren, (child) -> child.style.display = "none"
             currentGridTd = null
+
+    # On click on any of the cell editor controls, make the cell the current one
+    _.each cellEditorChildren, (child) ->
+        Event.addListener child, "click", () ->
+            # Send a DOMActivate to the closest xforms-activable ancestor
+            activable = YD.getAncestorByClassName child, "xforms-activable"
+            form = Controls.getForm activable
+            event = new AjaxServer.Event form, activable.id, null, null, "DOMActivate"
+            AjaxServer.fireEvents [event]


Remove debug code on discover javascript

--- spec/javascripts/discover/index_spec.coffee
+++ spec/javascripts/discover/index_spec.coffee
@@ -39,7 +39,6 @@
       tags = [$('
'), $('
')]
       search = $('<input value="something">')
       @url = @view.generateUrl(filter, near, category, tags, search)
-      console.log @url

     it 'should return the url', ->
       expect(@url).toEqual('/discover/recommended/near/Kansas City/category/transit/tags/tag1,tag2/search/something')


test camera reset after unload

test camera reset after unload

--- spec/javascripts/jax/context_spec.js.coffee
+++ spec/javascripts/jax/context_spec.js.coffee
@@ -98,6 +98,13 @@
     it 'should make context accessible to views', ->
       @context.redirectTo 'two/index'
       expect(@context.view.context).toBe @context
+
+    describe 'scene unloading', ->
+      it 'should reset the camera', ->
+        @context.redirectTo 'two'
+        @context.activeCamera.position = [1,1,1]
+        @context.unloadScene()
+        expect(@context.activeCamera.position).toEqualVector [0,0,0]

     describe 'to the index action in the same controller', ->
       it "should reload the scene as a special case", ->


fix columns_hider

fix columns_hider

--- app/assets/javascripts/ab_admin/core/columns_hider.js.coffee
+++ app/assets/javascripts/ab_admin/core/columns_hider.js.coffee
@@ -43,7 +43,7 @@
       @data_el.append(html)

   columnNames: ->
-    _.map $('#list th'), (el) ->
+    _.map $('#list > thead > tr > th'), (el) ->
       $el = $(el)
       {disabled: $el.hasClass('hide_cell'), name: $.trim($el.text().replace(/[▼▲]/g, ''))}

@@ -67,7 +67,7 @@
     $("#list > thead > tr > th:nth-child(#{i}), #list > tbody > tr > td:nth-child(#{i})").hide()

   showAll: ->
-    $("#list tr th, #list tr td").show()
+    $("#list > thead > tr > th, #list > thead > tr > td").show()

 $ ->
   window.columns_hider = new ColumnsHider()


updated start position

updated start position

--- src/javascript/game/states/adventure.coffee
+++ src/javascript/game/states/adventure.coffee
@@ -78,9 +78,10 @@
     ]

     # Samus start position
-    brinstarEntrance = {x:648,y:191}
-    nearMorphBall = {x:400,y:175}
-    onBridge = {x:1466,y:95}
+    # brinstarEntrance = {x:648,y:191}
+    brinstarEntrance = {x:648,y:431}
+    # nearMorphBall = {x:400,y:175}
+    # onBridge = {x:1466,y:95}
     samusStartPos = brinstarEntrance
     # samusStartPos = nearMorphBall
     # samusStartPos = onBridge


Redirecionando o usuário para a pesquisa do mês do último lançamento realizado

--- controllers/lancamento.coffee
+++ controllers/lancamento.coffee
@@ -38,7 +38,7 @@
 				novoLancamento.save (erro) ->
 					throw erro if erro

-					res.redirect '/lancamento'
+					res.redirect "/lancamento/#{mes}/#{ano}"

 		salvarCredito: (req, res) ->
 			lancamento = req.body.lancamento
@@ -52,4 +52,4 @@
 				novoLancamento.save (erro) ->
 					throw erro if erro

-					res.redirect '/lancamento'+					res.redirect "/lancamento/#{mes}/#{ano}"


Fix CollectionView imports

Fix CollectionView imports

--- coffee/chaplin/views/collection_view.coffee
+++ coffee/chaplin/views/collection_view.coffee
@@ -2,7 +2,7 @@
   'jquery',
   'underscore',
   'chaplin/views/view'
-], ($, _, utils, View) ->
+], ($, _, View) ->
   'use strict'

   # General class for rendering Collections. Derive this class and


Block export button while exports are in progress

--- coffee/cilantro/ui/workflows/results.coffee
+++ coffee/cilantro/ui/workflows/results.coffee
@@ -23,6 +23,7 @@
         requestTimeout: 10000   # Max time(ms) for unmonitored exports
         monitorDelay: 500       # In milliseconds
         monitorTimeout: 60000   # Max time(ms) to monitor exports
+        numPendingDownloads: 0

         ui:
             columns: '.columns-modal'
@@ -61,10 +62,16 @@
                     statusContainer.find('.label-success').show()

         onExportFinished: (exportTypeTitle) ->
+            @numPendingDownloads = @numPendingDownloads - 1
+
             if @hasExportErrorOccurred(exportTypeTitle)
                 @changeExportStatus(exportTypeTitle, "error")
             else
                 @changeExportStatus(exportTypeTitle, "success")
+
+            # If all the downloads are finished, re-enable the export button
+            if @numPendingDownloads == 0
+                $('[data-toggle=export]').prop('disabled', false)

         hasExportErrorOccurred: (exportTypeTitle) ->
             id = "#export-download-#{ exportTypeTitle }"
@@ -170,6 +177,10 @@
                 $('.export-options-modal .alert-block').show()

             else
+                # Disable export button until the downloads finish
+                $("[data-toggle=export]").prop('disabled', true)
+                @numPendingDownloads = selectedTypes.length
+
                 @ui.exportOptions.modal('hide')

                 @initializeExportStatusIndicators(selectedTypes)


not sending null to backend

not sending null to backend

--- frontend/src/scripts/coffee/scoreboard.coffee
+++ frontend/src/scripts/coffee/scoreboard.coffee
@@ -83,8 +83,8 @@
   m.sortUsers = -> sortUsers m

   m.validateFlag = (d, e) ->
-    publicId = parseInt(myRealEscapeString($("#publicId").val()))
-    flagHash = myRealEscapeString($("#flagHash").val())
+    publicId = parseInt(myRealEscapeString($("#publicId").val())) || ""
+    flagHash = myRealEscapeString($("#flagHash").val()) || ""
     validateFlagWthServer publicId, flagHash, (response) ->
       if response.msg == "flag validated"
         m.reload()


When opening/closing entries, give priority to the animations before making any changes in the angularjs model layer.

The reason is that the animations are what the user is most aware of, and it makes sense priorizing them to achieve a better UX.

--- app/assets/javascripts/ng-services/ng-OpenEntrySvc.js.coffee
+++ app/assets/javascripts/ng-services/ng-OpenEntrySvc.js.coffee
@@ -10,9 +10,9 @@
   # PRIVATE FUNCTION - Set an entry as closed.
   #--------------------------------------------
   close = (entry)->
+    animationsSvc.close_entry entry
     index = $rootScope.open_entries.indexOf entry
     $rootScope.open_entries.splice index, 1 if index != -1
-    animationsSvc.close_entry entry

   service =

@@ -20,6 +20,8 @@
     # Set an entry as open
     #---------------------------------------------
     open: (entry)->
+      animationsSvc.open_entry entry
+
       # Close any other open entry, unless the user has selected the "open all entries by default" config option
       if $rootScope.open_entries && !$rootScope.open_all_entries
         for e in $rootScope.open_entries
@@ -31,8 +33,6 @@
       # Otherwise there is at most a single open entry.
       else
         $rootScope.open_entries = [entry]
-
-      animationsSvc.open_entry entry

     #---------------------------------------------
     # Set an entry as closed


Fixed auto-close reply form in javascript

--- app/assets/javascripts/suj/commentable/commentable.js.coffee
+++ app/assets/javascripts/suj/commentable/commentable.js.coffee
@@ -3,7 +3,7 @@
   return false
 )

-$(document).on('ajax:success', '.suj-commentable-item .suj-commentable-reply-form', (evt, data, status, xhr) ->
+$(document).on('ajax:success', '.suj-commentable-item .suj-commentable-form', (evt, data, status, xhr) ->
   $(this).toggle()
   return false
 )


Adapt to Paw JS API version 0.2.0

Adapt to Paw JS API version 0.2.0

--- PHPcURLCodeGenerator.coffee
+++ PHPcURLCodeGenerator.coffee
@@ -1,4 +1,10 @@
-require "mustache.js"
+# in API v0.2.0 and below (Paw 2.2.2 and below), require had no return value
+((root) ->
+  if root.bundle?.minApiVersion('0.2.0')
+    root.Mustache = require("./mustache")
+  else
+    require("mustache.js")
+)(this)

 addslashes = (str) ->
     ("#{str}").replace(/[\\"]/g, '\\$&')


Export whole Framework in load

Export whole Framework in load

--- core/KD.coffee
+++ core/KD.coffee
@@ -167,6 +167,11 @@
     KD.error = error = if console?.error then console.error.bind(console) else noop
     return "Logs are enabled now."

+  exportKDFramework:->
+    (window[item] = KD.classes[item] for item of KD.classes)
+    KD.exportKDFramework = -> "Already exported."
+    "KDFramework loaded successfully."
+
 KD.enableLogs() if not KD.config?.suppressLogs

 prettyPrint = noop


setRemoteDescription() tests for Remote.Peer

--- src/tests/spec/public/models/remote.peer.spec.coffee
+++ src/tests/spec/public/models/remote.peer.spec.coffee
@@ -242,3 +242,35 @@
 				expect(callArgs[2]).toBe(fakeController.id)
 				expect(callArgs[3]).toBe('a')

+		describe 'when setting a remote description', ->
+			it 'should set the remote description for RTC connection', ->
+				peer = new Peer(fakeController, '1', true, FakeRTCPeerConnection)
+				spyOn(peer._connection, 'setRemoteDescription')
+
+				peer.setRemoteDescription('a')
+
+				expect(peer._connection.setRemoteDescription).toHaveBeenCalled()
+				callArgs = peer._connection.setRemoteDescription.mostRecentCall.args
+				expect(callArgs.length).toBe(1)
+				expect(callArgs[0]).toBe('a')
+
+			it 'should create an answer if we are not the connector', ->
+				peer = new Peer(fakeController, '1', true, FakeRTCPeerConnection)
+				spyOn(peer._connection, 'createAnswer')
+				peer._isConnector = true
+
+				peer.setRemoteDescription('a')
+
+				expect(peer._connection.createAnswer).not.toHaveBeenCalled()
+
+				peer._isConnector = false
+
+				peer.setRemoteDescription('a')
+
+				expect(peer._connection.createAnswer).toHaveBeenCalled()
+				callArgs = peer._connection.createAnswer.mostRecentCall.args
+				expect(callArgs.length).toBe(3)
+				expect(callArgs[0]).toEqual(peer._onLocalDescription)
+				expect(callArgs[1]).toBe(null)
+				expect(callArgs[2]).toEqual({})
+


bootstrap added a new file a couple weeks ago.

--- src/tower/server/generator/generators/tower/app/appGenerator.coffee
+++ src/tower/server/generator/generators/tower/app/appGenerator.coffee
@@ -143,6 +143,7 @@
           @get "https://raw.github.com/twitter/bootstrap/master/less/#{stylesheet}.less", "bootstrap/#{stylesheet}.less" for stylesheet in [
             "accordion",
             "alerts",
+            "badges",
             "bootstrap",
             "breadcrumbs",
             "button-groups",


api_path ends in '/'

api_path ends in '/'

--- client/lanes/Config.coffee
+++ client/lanes/Config.coffee
@@ -8,7 +8,7 @@
         settings: "object"

     modelTypeIdentifier: -> 'system-settings'
-    url: -> Lanes.config.api_path + '/system-settings'
+    url: -> Lanes.config.api_path + 'system-settings'
     initialize: ->
         @on('change:settings', @setDefaultSettings)
         @setDefaultSettings()


fullpieagent testing

fullpieagent testing

--- widgets/fullpie_agent/fullpie_agent.coffee
+++ widgets/fullpie_agent/fullpie_agent.coffee
@@ -190,8 +190,8 @@
       d.y = -Math.cos(a) * (that.radius - 20)
       d.b = d.oy = d.y + 5
       d.t = d.y - bbox.height - 5
-      console.log 'd.x' d.x
-      console.log 'd.y' d.y
+      console.log 'd.x', d.x
+      console.log 'd.y', d.y
       ### check whether the default position
          overlaps any other labels
       ###


fullpieagent testing

fullpieagent testing

--- widgets/fullpie_agent/fullpie_agent.coffee
+++ widgets/fullpie_agent/fullpie_agent.coffee
@@ -79,7 +79,7 @@
         if totalLabel.empty()
             totalLabel = svg.append('g').attr('class', 'totalLabel')

-        path = pathGroup.selectAll('path.pie').data(piedata).attr('filter','url(#dropshadow)')
+        path = pathGroup.selectAll('path.pie').data(piedata)
         path.enter().append('path').attr('class', 'pie').attr('fill', (d, i) -> color i)

         path.transition().duration(1500).attrTween 'd', (d,i) ->
@@ -128,6 +128,7 @@
                     return arc b
             )
             .remove()
+            .attr('filter','url(#dropshadow)')

         labels = labelGroup.selectAll('text').data(piedata.sort((p1, p2) ->
             p1.startAngle - p2.startAngle


Agent: testing pointers with switcher

Agent: testing pointers with switcher

--- widgets/fullpie_agent/fullpie_agent.coffee
+++ widgets/fullpie_agent/fullpie_agent.coffee
@@ -17,8 +17,8 @@

         # Define the container
         @container = $(@node).parent()
-        #console.log 'node',$(@node)
-        #console.log 'container',@container
+        console.log 'node',$(@node)
+        console.log 'container',@container

         #
         # CONFIG ZONE


fullpieagent testing

fullpieagent testing

--- widgets/fullpie_agent/fullpie_agent.coffee
+++ widgets/fullpie_agent/fullpie_agent.coffee
@@ -2,7 +2,7 @@
   #@accessor 'value'
   @accessor 'data'
   onData: (data) ->
-    $(@node).fadeOut().fadeIn()
+    #$(@node).fadeOut().fadeIn()
     #@buildPieStructure()
     #@render(data.data)
     @update(data.data)


lets just go with a css solution

lets just go with a css solution

--- lib/assets/javascripts/attachinary.js.coffee
+++ lib/assets/javascripts/attachinary.js.coffee
@@ -17,7 +17,7 @@
                 <img
                   src="<%= $.cloudinary.url(files[i].public_id, { "version": files[i].version, "format": 'jpg'}) %>"
                   alt=""/>
-
+

               <% } %>
               <a href="#" data-remove="<%= files[i].public_id %>">Remove


using currentTarget instead of target

using currentTarget instead of target

--- jsapp/xlform_model_view/view.surveyApp.coffee
+++ jsapp/xlform_model_view/view.surveyApp.coffee
@@ -599,7 +599,7 @@
         evt.target.href = "data:text/csv;charset=utf-8,#{encodeURIComponent(@survey.toCSV())}"
     saveButtonClick: (evt)->
       # Save = store CSV in local storage.
-      icon = $(evt.target).find('i')
+      icon = $(evt.currentTarget).find('i')
       icon.addClass 'fa-spinner fa-spin blue'
       icon.removeClass 'fa-check-circle green'
       @onSave.apply(@, arguments)


fixing minor bug which triggers trackjs errors

--- jsapp/xlform_model_view/view.surveyApp.coffee
+++ jsapp/xlform_model_view/view.surveyApp.coffee
@@ -576,8 +576,11 @@
         rowEl.slideUp 175, "swing", ()=>
           rowEl.remove()
           @survey.rows.remove matchingRow
-          if parent != @ && parent.rows.length == 0
-            parent_view = @__rowViews.get(parent.cid)._deleteGroup()
+          if parent.constructor.kls == "Group" && parent.rows.length == 0
+            parent_view = @__rowViews.get(parent.cid)
+            if !parent_view
+              trackJs?.console.warn("parent view is not defined", matchingRow.get('name').get('value'))
+            parent_view._deleteGroup()
         @set_multioptions_label()

     groupSelectedRows: ->


cleans up coffee code

cleans up coffee code

--- app/assets/javascripts/debts.js.coffee
+++ app/assets/javascripts/debts.js.coffee
@@ -7,5 +7,4 @@
     setTimeout ->
       instance.dpDiv.css({'z-index': 100})
    })
-  # $('#ui-datepicker-div').css({ 'z-index':100 })
   # $('.datepicker').datepicker({ showAnim: 'slideDown'})


Check if path exists before moving

Check if path exists before moving

fs.move() no longer throws an exception if the target exists
so check for it explicitly when the dialog is confirmed.

--- src/packages/tree-view/lib/tree-view.coffee
+++ src/packages/tree-view/lib/tree-view.coffee
@@ -220,6 +220,14 @@
       iconClass: 'move'
       onConfirm: (newPath) =>
         newPath = project.resolve(newPath)
+        if oldPath is newPath
+          dialog.close()
+          return
+
+        if fs.exists(newPath)
+          dialog.showError("Error: #{newPath} already exists. Try a different path.")
+          return
+
         directoryPath = fs.directory(newPath)
         try
           fs.makeTree(directoryPath) unless fs.exists(directoryPath)


Fix comment wording.

Fix comment wording.

--- src/Witness/Scripts/witness/Scenario.coffee
+++ src/Witness/Scripts/witness/Scenario.coffee
@@ -10,7 +10,7 @@
 		assertions = (new Witness.Assertion action for action in @thens)
 		tryAllAssertions = new Witness.TryAll assertions
 		sequence = new Witness.Sequence [].concat @givens, @whens, tryAllAssertions
-		# The dispose whens must *always* run, even if the previous sequence fails.
+		# The disposes must *always* run, even if the previous sequence fails.
 		# So combine them using a TryAll.
 		if @disposes.length > 0
 			@aggregateAction = new Witness.TryAll [].concat sequence, disposes


submit notes forms by pressing ctrl+enter

--- app/assets/javascripts/notes.js.coffee
+++ app/assets/javascripts/notes.js.coffee
@@ -52,6 +52,12 @@

     # fetch notes when tab becomes visible
     $(document).on "visibilitychange", @visibilityChange
+
+    @notes_forms = '.js-main-target-form textarea, .js-discussion-note-form textarea'
+    $(document).on('keypress', @notes_forms, (e)->
+      if event.keyCode == 10 || (event.ctrlKey && event.keyCode == 13)
+        $(@).parents('form').submit()
+    )

   cleanBinding: ->
     $(document).off "ajax:success", ".js-main-target-form"
@@ -67,6 +73,7 @@
     $(document).off "click", ".js-discussion-reply-button"
     $(document).off "click", ".js-add-diff-note-button"
     $(document).off "visibilitychange"
+    $(document).off "keypress", @notes_forms

   initRefresh: ->


fix some issues
1. modify the message's format of System Control api
2. 'muted' cannot be set

Signed-off-by: Manson Li <f2a598d7717cc6757bef28b84a9ab02e5efc6048@matchstick.tv>

--- lib/dial/handler/SystemControlHandler.coffee
+++ lib/dial/handler/SystemControlHandler.coffee
@@ -52,7 +52,7 @@
                         else
                             @respondBadRequest req, res, "missing level"
                     when "SET_MUTED"
-                        if message.muted
+                        if message.muted isnt undefined
                             platform.setMuted message.muted
                             @respond req, res, 200
                         else
@@ -65,8 +65,10 @@
         message =
             success: true
             request_type: type
-            level: platform.getVolume()
-            muted: platform.getMuted()
+        if type is "GET_VOLUME"
+            message["level"] = platform.getVolume()
+        if type is "GET_MUTED"
+            message["muted"] = platform.getMuted()
         body = JSON.stringify message
         headers =
             "Content-Type": "application/json"


Update to new fbp-spec multi-suite format

--- components/RunTests.coffee
+++ components/RunTests.coffee
@@ -34,7 +34,13 @@
       do callback
       return

-    suites = specs.map (s) -> fbpSpec.testsuite.loadYAML s.code
+    suites = []
+    for s in specs
+      suites = suites.concat fbpSpec.testsuite.loadYAML s.code
+    unless suites.length
+      do callback
+      return
+
     # Send out the initial suites
     out.send
       suites: suites


Events: Using event.target.touches instead event.touches to get compatibility with Safari.

--- engine/events.coffee
+++ engine/events.coffee
@@ -77,7 +77,7 @@

         touch_start = (event)=>
             event.preventDefault()
-            for t in event.touches
+            for t in event.target.touches
                 touch = {}
                 touch.touching = true
                 touch.id = t.identifier
@@ -96,7 +96,7 @@
                 touch.movement_since_touch = 0
                 touch.touch_target = touch.target = t.target
                 @touch.touch_events[touch.id] = touch
-            @touch.touches = event.touches.length
+            @touch.touches = event.target.touches.length

         if enable_touch
             root_element.addEventListener 'touchstart', touch_start, false
@@ -105,7 +105,7 @@
             event.preventDefault()
             for touch in @touch.touch_events
                 touch.touching = 0
-            for t in event.touches
+            for t in event.target.touches
                 touch = @touch.touch_events[t.identifier]
                 touch.touching = true
                 touch.force = 0
@@ -113,16 +113,17 @@
                 touch.radius_y = 0
                 touch.rel_x = 0
                 touch.rel_y = 0
-            @touch.touches = event.touches.length
+            @touch.touches = event.target.touches.length

         if enable_touch
             root_element.addEventListener 'touchend', touch_end, false
+            root_element.addEventListener 'touchleave', touch_end, false
             root_element.addEventListener 'touchcancel', touch_end, false

         touch_move = (event)=>
             event.preventDefault()

-            for t in event.touches
+            for t in event.target.touches
                 touch = {}
                 touch.id = t.identifier
                 touch.touching = true
@@ -146,7 +147,7 @@
                     touch.rel_y = 0
                 touch.movement_since_touch = Math.abs(touch.rel_x) + Math.abs(touch.rel_y)
                 @touch.touch_events[touch.id] = touch
-            @touch.touches = event.touches.length
+            @touch.touches = event.target.touches.length

         if enable_touch
             root_element.addEventListener 'touchmove', touch_move, false


values are reflecting in the model and view together

--- jsapp/xlform_model_view/view.rowDetail.coffee
+++ jsapp/xlform_model_view/view.rowDetail.coffee
@@ -40,6 +40,61 @@
       @
     html: ()->
       $viewTemplates.$$render('xlfDetailView', @)
+    listenForCheckboxChange: (opts={})->
+      el = opts.el || @$('input[type=checkbox]').get(0)
+      $el = $(el)
+      changing = false
+      reflectValueInEl = ()=>
+        if !changing
+          if @model.get('value') in  $configs.truthyValues
+            $el.prop('checked', true)
+      @model.on 'change:value', reflectValueInEl
+      reflectValueInEl()
+      $el.on 'change', ()=>
+        changing = true
+        @model.set('value', $el.prop('checked'))
+        changing = false
+    listenForInputChange: (opts={})->
+      # listens to checkboxes and input fields and ensures
+      # the model's value is reflected in the element and changes
+      # to the element are reflected in the model (with transformFn
+      # applied)
+      el = opts.el || @$('input').get(0)
+      $el = $(el)
+      transformFn = opts.transformFn || false
+      inputType = opts.inputType
+      inTransition = false
+
+      changeModelValue = ($elVal)=>
+        # preventing race condition
+        if !inTransition
+          inTransition = true
+          @model.set('value', $elVal)
+          reflectValueInEl(true)
+          inTransition = false
+
+      reflectValueInEl = (force=false)=>
+        # This should never change the model value
+        if force || !inTransition
+          modelVal = @model.get('value')
+          if inputType is 'checkbox'
+            if !_.isBoolean(modelVal)
+              modelVal = modelVal in $configs.truthyValues
+            # triggers element change event
+            $el.prop('checked', modelVal)
+          else
+            # triggers element change event
+            $el.val(modelVal)
+
+      reflectValueInEl()
+      @model.on 'change:value', reflectValueInEl
+
+      $el.on 'change', ()=>
+        $elVal = $el.val()
+        if transformFn
+          $elVal = transformFn($elVal)
+        changeModelValue($elVal)
+      ``

     insertInDOM: (rowView)->
       rowView.rowExtras.append(@el)
@@ -71,7 +126,7 @@
         placement: 'right'
         rows: 3

-  viewRowDetail.DetailViewMixins.hint =
+  viewRowDetail.DetailViewMixins.hint = viewRowDetail.DetailViewMixins.default =
     html: ->
       @$el.addClass("card__settings__fields--active")
       """
@@ -83,8 +138,7 @@

       """
     afterRender: ->
-      # $viewUtils.makeEditable @, @model, 'code', {}
-      @$('input').eq(0).val(@model.get('value'))
+      @listenForInputChange()

   viewRowDetail.DetailViewMixins.relevant =
     html: ->
@@ -126,14 +180,15 @@

       """
     afterRender: ->
-      $viewUtils.makeEditable @, @model, 'code', {}
-
-  viewRowDetail.DetailViewMixins.name = viewRowDetail.DetailViewMixins.default =
+      @listenForInputChange()
+      # $viewUtils.makeEditable @, @model, 'code', {}
+
+  viewRowDetail.DetailViewMixins.name =
     html: ->
       @fieldTab = "active"
-      @listenTo @model, "change:value", ()=>
-        @render()
-        @afterRender()
+      # @listenTo @model, "change:value", ()=>
+      #   @render()
+      #   @afterRender()
       @$el.addClass("card__settings__fields--#{@fieldTab}")
       """

@@ -145,7 +200,30 @@
       """
     afterRender: ->
       @$el.find('input').eq(0).val(@model.get("value"))
+      @listenForInputChange(transformFn: $modelUtils.sluggify)
       # $viewUtils.makeEditable @, @model, "code", transformFunction: $modelUtils.sluggify
+    insertInDom: (rowView)->
+      rowView.rowExtras.append(@el)
+
+  viewRowDetail.DetailViewMixins.default =
+    html: ->
+      @fieldTab = "active"
+      # @listenTo @model, "change:value", ()=>
+      #   @render()
+      #   @afterRender()
+      @$el.addClass("card__settings__fields--#{@fieldTab}")
+      """
+

+        #{@model.key}:
+
+          <input type="text" name="#{@model.key}" id="#{@cid}" class="text" />
+
+

+      """
+    afterRender: ->
+      @$el.find('input').eq(0).val(@model.get("value"))
+      @listenForInputChange()
+
     insertInDom: (rowView)->
       rowView.rowExtras.append(@el)

@@ -171,10 +249,12 @@

       """
     afterRender: ->
-      inp = @$el.find("input")
-      # to be moved into the model when XLF.configs.truthyValues is refactored
-      isTrueValue = (@model.get("value") || "").toLowerCase() in $configs.truthyValues
-      inp.prop("checked", isTrueValue)
-      inp.change ()=> @model.set("value", inp.prop("checked"))
+      @listenForCheckboxChange()
+
+      # inp = @$el.find("input")
+      # # to be moved into the model when XLF.configs.truthyValues is refactored
+      # isTrueValue = (@model.get("value") || "").toLowerCase() in $configs.truthyValues
+      # inp.prop("checked", isTrueValue)
+      # inp.change ()=> @model.set("value", inp.prop("checked"))

   viewRowDetail


Send root survey always

Send root survey always

--- jsapp/xlform/src/view.row.coffee
+++ jsapp/xlform/src/view.row.coffee
@@ -137,7 +137,7 @@

     add_row_to_question_library: (evt) =>
       evt.stopPropagation()
-      @ngScope?.add_row_to_question_library @model, @model.collection._parent._initialParams
+      @ngScope?.add_row_to_question_library @model, @model.getSurvey()._initialParams

   class GroupView extends BaseRowView
     className: "survey__row survey__row--group  xlf-row-view xlf-row-view--depr"


Remove mini class to give id enough room

--- src/packages/collaboration/lib/prompt.coffee
+++ src/packages/collaboration/lib/prompt.coffee
@@ -9,7 +9,7 @@
   @activate: -> new Prompt

   @content: ->
-    @div class: 'overlay from-top mini', =>
+    @div class: 'overlay from-top', =>
       @subview 'miniEditor', new Editor(mini: true)
       @div class: 'message', outlet: 'message', 'Enter a session id to join'


Remove unused ConceptSearch subclass for ConceptPanel

--- src/coffee/cilantro/ui/concept/panel.coffee
+++ src/coffee/cilantro/ui/concept/panel.coffee
@@ -6,14 +6,6 @@
 ], (c, index, search, templates...) ->

     templates = c._.object ['panel'], templates
-
-
-    class ConceptSearch extends search.ConceptSearch
-        events:
-            'typeahead:autocompleted input': 'autocomplete'
-
-        autocomplete: (event, datum) ->
-            c.publish c.CONCEPT_FOCUS, datum.id

     # Takes a collection of c.models.ConceptModel objects
@@ -37,8 +29,9 @@
             # When a search occurs, the index is filtered relative to the
             # response which contains a listing of IDs that the search
             # has matched.
-            @search.show new ConceptSearch
+            @search.show new search.ConceptSearch
                 collection: @collection
+
                 handler: (query, resp) =>
                     @index.currentView.filter(query, resp)


update

update

--- lms/static/coffee/src/discussion/utils.coffee
+++ lms/static/coffee/src/discussion/utils.coffee
@@ -249,10 +249,10 @@
           $3
       else if RE_DISPLAYMATH.test(text)
         text = text.replace RE_DISPLAYMATH, ($0, $1, $2, $3) ->
-          processedText += $1 + processor("$$" + $2 + "$$", 'display')
-          #bug fix, ordering is off
-          #processedText =  processor("$$" + $2 + "$$", 'display') + processedText
-          #processedText = $1 + processedText
+          #processedText += $1 + processor("$$" + $2 + "$$", 'display')
+          bug fix, ordering is off
+          processedText =  processor("$$" + $2 + "$$", 'display') + processedText
+          processedText = $1 + processedText
           $3
       else
         processedText += text


Made parseFactNodes private method

Made parseFactNodes private method

--- app/js/jail_iframe/util/replace.coffee
+++ app/js/jail_iframe/util/replace.coffee
@@ -66,57 +66,9 @@

   spans

-# Function to select the found ranges
-FactlinkJailRoot.selectRanges = (ranges, id) ->
-  # Loop through ranges (backwards)
-  matches = []
-  results = []
-  uniqueMatchId = 0
-
-  i = 0
-  while i < ranges.length
-    # Check if the given factlink is not already selected
-    # (fixes multiple check marks when editing a factlink)
-    re = /(^|\s)factlink(\s|$)/
-    if re.test(ranges[i].startContainer.parentNode.className)
-      matches.push {} # Dirty hack: we should still skip one
-      continue
-
-    # Helper for posible extra matches within the current startNode
-    matches = findRangesStartingInContainer(ranges, i, ranges[i].startContainer)
-
-    #process all matches starting in ranges[i].startContainer
-    # Walk backwards over the matches to make sure the node references will stay intact
-    k = matches.length - 1
-
-    while k >= 0
-      @parseFactNodes matches[k], results, uniqueMatchId++
-      k--
-
-    i += matches.length
-
-  # This is where the actual parsing takes place
-  # this.results holds all the textNodes containing the facts
-  elements = {}
-  ret = []
-  i = 0
-  len = results.length
-
-  while i < len
-    res = results[i]
-    elements[res.matchId] = elements[res.matchId] or []
-
-    # Insert the fact-span
-    elements[res.matchId] = elements[res.matchId].concat(insertFactSpan(res.startOffset, res.endOffset, res.node, id))
-    i++
-
-  for matchId of elements when elements.hasOwnProperty(matchId)
-    ret.push new FactlinkJailRoot.Fact(id, elements[matchId])
-
-  ret

 # Function that tracks the DOM for nodes containing the fact
-FactlinkJailRoot.parseFactNodes = (range, results, matchId) ->
+parseFactNodes = (range, results, matchId) ->
   # Only parse the nodes if the startNode is already found,
   # this boolean is used for tracking
   foundStart = false
@@ -148,3 +100,53 @@
       # need to walk the DOM anymore
       if foundStart && node == range.endContainer
         return false
+
+
+# Function to select the found ranges
+FactlinkJailRoot.selectRanges = (ranges, id) ->
+  # Loop through ranges (backwards)
+  matches = []
+  results = []
+  uniqueMatchId = 0
+
+  i = 0
+  while i < ranges.length
+    # Check if the given factlink is not already selected
+    # (fixes multiple check marks when editing a factlink)
+    re = /(^|\s)factlink(\s|$)/
+    if re.test(ranges[i].startContainer.parentNode.className)
+      matches.push {} # Dirty hack: we should still skip one
+      continue
+
+    # Helper for posible extra matches within the current startNode
+    matches = findRangesStartingInContainer(ranges, i, ranges[i].startContainer)
+
+    #process all matches starting in ranges[i].startContainer
+    # Walk backwards over the matches to make sure the node references will stay intact
+    k = matches.length - 1
+
+    while k >= 0
+      parseFactNodes matches[k], results, uniqueMatchId++
+      k--
+
+    i += matches.length
+
+  # This is where the actual parsing takes place
+  # this.results holds all the textNodes containing the facts
+  elements = {}
+  ret = []
+  i = 0
+  len = results.length
+
+  while i < len
+    res = results[i]
+    elements[res.matchId] = elements[res.matchId] or []
+
+    # Insert the fact-span
+    elements[res.matchId] = elements[res.matchId].concat(insertFactSpan(res.startOffset, res.endOffset, res.node, id))
+    i++
+
+  for matchId of elements when elements.hasOwnProperty(matchId)
+    ret.push new FactlinkJailRoot.Fact(id, elements[matchId])
+
+  ret


added SequenceValidator to the index

added SequenceValidator to the index

--- lib/index.coffee
+++ lib/index.coffee
@@ -12,6 +12,7 @@
   NegateValidator:       require('./validators/negate_validator.coffee')
   PresenceValidator:     require('./validators/presence_validator.coffee')
   RangeValidator:        require('./validators/range_validator.coffee')
+  SequenceValidator:     require('./validators/sequence_validator.coffee')
   StructValidator:       require('./validators/struct_validator.coffee')

   DelegatedValidationError: require('./errors/delegated_validation_error.coffee')


it works on second compile

it works on second compile

--- lib/index.coffee
+++ lib/index.coffee
@@ -44,11 +44,12 @@
     _respond = (key, obj, json) ->
       @roots.config.locals ||= {}
       @roots.config.locals.records ||= {}
-      @roots.config.locals[key] = json
-      console.log @
+      @roots.config.locals.records[key] = json

-    _to = (json, path) ->
-      keys = path.split "/"
-      pos = json
-      pos = pos[key] for key in keys
-      return pos
+    # _to = (json, path) ->
+    #   keys = path.split "/" if path?
+    #   pos = json
+    #   for key of keys
+    #     pos = pos[key]
+    #   console.log pos
+    #   return pos


Fixed bug in fetch returning the wrong version

--- lib/index.coffee
+++ lib/index.coffee
@@ -244,7 +244,6 @@

         @getOps cName, docName, snapshot.v, (err, opData) ->
           return callback? err if err
-          snapshot.v += opData.length
           err = ot.apply snapshot, d for d in opData
           callback err, snapshot


Add function to update user info through socket.io

--- app/controllers/sync.coffee
+++ app/controllers/sync.coffee
@@ -87,6 +87,13 @@
     else
       do callback

+  # Fetch the user info from the server
+  updateInfo: ->
+    @emit 'info', null, (info) ->
+      Setting.set('user_name', info.name)
+      Setting.set('user_email', info.email)
+      Setting.set('pro', info.pro)
+
   # Check an event, and if it is a model update add it to the queue
   # TODO: Add optimization for duplicate events with timestamps
   addToQueue: (name, args) ->


Reset scroll position to top of window when navigating to new view

--- lib/assets/javascripts/routers/main.js.coffee
+++ lib/assets/javascripts/routers/main.js.coffee
@@ -9,7 +9,9 @@
     @navigate('/profile', { replace: true, trigger: true })

   profile: =>
+    window.scrollTo(0, 0)
     new Marbles.Views.Profile container: TentAdmin.config.container

   apps: =>
+    window.scrollTo(0, 0)
     new Marbles.Views.Apps container: TentAdmin.config.container


try setting the setNoDelay option for terminals...

--- src/smc-project/console_server_child.coffee
+++ src/smc-project/console_server_child.coffee
@@ -35,6 +35,11 @@
         args     : required
         path     : undefined
         filename : undefined
+
+    # I noticed that LATELY sometimes we don't see output until hitting input from the client side.
+    # Setting the socket to immediately send when written to might help.
+    # See https://nodejs.org/api/net.html#net_socket_setnodelay_nodelay
+    socket.setNoDelay()

     fn = misc.path_split(opts.filename).tail
     env = misc.merge({SMC_FILENAME: fn}, process.env)


added 'finalized' test

added 'finalized' test

--- src/test/hubot-jenkins-notifier_test.coffee
+++ src/test/hubot-jenkins-notifier_test.coffee
@@ -107,6 +107,22 @@
         "parameters":{"branch":"master"}
       }
     }
+  },
+  {
+    "name": "started-finalized",
+    "expected_out": false,
+    "body": {
+      "name":"JobName",
+      "url":"JobUrl",
+      "build":{
+        "number":2,
+        "phase":"STARTED",
+        "status":"FINALIZED",
+        "url":"job/project%20name/5",
+        "full_url":"http://ci.jenkins.org/job/project%20name/5"
+        "parameters":{"branch":"master"}
+      }
+    }
   }
 ]


Remove 'Success!' debug message

Remove 'Success!' debug message

--- lib/latex.coffee
+++ lib/latex.coffee
@@ -116,9 +116,6 @@
     if @shouldOpenResult() and opener = @getOpener()
       opener.open(result.outputFilePath)

-    # TODO: Display a more visible success message.
-    console.info 'Success!' unless atom.inSpecMode()
-
   showError: (error) ->
     # TODO: Introduce proper error and warning handling.
     console.error error unless atom.inSpecMode()


remove debug code

remove debug code

--- app/js/services/info.coffee
+++ app/js/services/info.coffee
@@ -45,7 +45,7 @@

     watch_for_updates: =>
         @interval (=>
-            if false and !@is_refreshing
+            if !@is_refreshing
                 @refresh_info()
         ), 2500


Restyled some code, per @markijbema's comment

--- app/js/util/scrollto.coffee
+++ app/js/util/scrollto.coffee
@@ -1,13 +1,10 @@
 Factlink.scrollTo = (fact_id) ->
   $('body')
     ._scrollable()
-    .scrollTo(
-      "span[data-factid=#{fact_id}]",
-      800,
+    .scrollTo "span[data-factid=#{fact_id}]", 800,
       offset:
         top:-100
         axis: 'y'
-    )

 $(window).bind 'factlink.factsLoaded', ->
   if Factlink? and FactlinkConfig? and FactlinkConfig.scrollto


Fixed removed "fact" variable

Fixed removed "fact" variable

--- app/js/views/balloon.coffee
+++ app/js/views/balloon.coffee
@@ -3,14 +3,16 @@
     @_mouseOutTimeoutID = null
     @_loadingTimeoutID = null
     @_loading = false
+    @_fact = fact
+
     @_initializeTemplate()

   _initializeTemplate: ->
     @$el = $(Factlink.templates.indicator)
     @$el.appendTo(Factlink.el)
-    @$el.bind "mouseenter", -> fact.focus()
-    @$el.bind "mouseleave", -> fact.blur()
-    @$el.bind "click", -> fact.click()
+    @$el.bind "mouseenter", => @_fact.focus()
+    @$el.bind "mouseleave", => @_fact.blur()
+    @$el.bind "click", => @_fact.click()

   _hideAll: ->
     @$el.closest("#fl").find(".fl-popup").hide()
@@ -28,7 +30,7 @@
   hide: (callback) ->
     window.clearTimeout @_mouseOutTimeoutID
     @$el.fadeOut "fast", callback
-    fact?.stopHighlighting()
+    @_fact?.stopHighlighting()

   isVisible: ->
     @$el.is ":visible"


add/remove event listeners when needed, still need to fix hovering/moving events

--- app/lib/canvas-graph.coffee
+++ app/lib/canvas-graph.coffee
@@ -92,12 +92,14 @@
     @startingPoint = @toCanvasXPoint(e) - MIN_WIDTH
     @dragging = false

-    @element.addEventListener 'mouseover', (e) => @hovering = true
-    @element.addEventListener 'mouseout', (e) => @hovering = false
-    @element.addEventListener 'mousedown', (e) => @onMouseDown(e)
+    @element.addEventListener 'mouseover', => @hovering = true
+    @element.addEventListener 'mouseout', => @hovering = false
+
+    @element.addEventListener 'mousedown',
+      @onMouseDown
+      window.addEventListener 'mousemove', @onMouseMove
+      window.addEventListener 'mouseup', @onMouseUp
     @element.addEventListener 'click', (e) => @canvasGraph.marks.remove(@) if @pointerYInElement(e) < 15
-    window.addEventListener 'mousemove', (e) => @onMouseMove(e)
-    window.addEventListener 'mouseup', (e) => @onMouseUp(e)

   draw: (e) ->
     markLeftX = Math.min @startingPoint, @toCanvasXPoint(e)
@@ -124,7 +126,7 @@
     @dataXMin = @canvasGraph.toDataXCoord(@canvasXMin)
     @dataXMax = @canvasGraph.toDataXCoord(@canvasXMax)

-  updateCursor: (e) ->
+  updateCursor: (e) =>
     if @pointerYInElement(e) < 15
       @element.style.cursor = "pointer"
     else if ((Math.abs @pointerXInElement(e) - (@canvasXMax-@canvasXMin)) < 10) || @pointerXInElement(e) < 10
@@ -132,14 +134,16 @@
     else
       @element.style.cursor = "move"

-  onMouseMove: (e) ->
+  onMouseMove: (e) =>
     e.preventDefault()
     @draw(e) if @dragging
     @move(e) if @moving
     @updateCursor(e) if @hovering

-  onMouseDown: (e) ->
+  onMouseDown: (e) =>
     e.preventDefault()
+
+    console.log "E from onmousedown", e
     @element.parentNode.appendChild(@element) #move to end of container for z index
     if (Math.abs @pointerXInElement(e) - (@canvasXMax-@canvasXMin)) < 10
       @startingPoint = @canvasXMin
@@ -151,7 +155,7 @@
       @moving = true
       @pointerOffset = (@toCanvasXPoint(e) - @canvasXMin)

-  onMouseUp: (e) ->
+  onMouseUp: (e) =>
     e.preventDefault()
     for mark in @canvasGraph.marks.all
       mark.dragging = false
@@ -159,8 +163,10 @@
     if (@ in @canvasGraph.marks.all) then @canvasGraph.marks.update(@) else @canvasGraph.marks.add(@)
     @dragging = false
     @moving = false
+    window.removeEventListener 'mousemove', @onMouseMove
+    window.removeEventListener 'mouseup', @onMouseUp

-  toCanvasXPoint: (e) -> e.pageX-@canvas.getBoundingClientRect().left - window.scrollX
+  toCanvasXPoint: (e) -> e.pageX - @canvas.getBoundingClientRect().left - window.scrollX

   pointerXInElement: (e) -> e.offsetX || e.clientX - e.target.offsetLeft + window.pageXOffset - @canvas.getBoundingClientRect().left


Do not recreate quadtree function everytime

D3 quadtree works by generating a function that can be called on each
tick.  We were creating a new quadtree every tick.  Instead I've moved
it up to the layout constructor so a new one is made only when we make a
significant enough change to the layout to make a new one.

--- app/scripts/alchemy/core/layout.coffee
+++ app/scripts/alchemy/core/layout.coffee
@@ -20,7 +20,12 @@
         nodes = alchemy._nodes
         @k = Math.sqrt Math.log(_.size(alchemy._nodes)) / (conf.graphWidth() * conf.graphHeight())
         @_clustering = new alchemy.clustering
-        @d3NodeInternals = _.keys alchemy._nodes
+
+        # Set up quad tree
+        if conf.collisionDetection
+            @d3NodeInternals = _.values alchemy._nodes
+            @q = d3.geom.quadtree @d3NodeInternals
+
         if conf.cluster
             @_charge = () -> @_clustering.layout.charge
             @_linkStrength = (edge) -> @_clustering.layout.linkStrength(edge)
@@ -57,7 +62,7 @@
     collide: (node) =>
         node = node._d3
         conf = alchemy.conf
-        r = 2 * (node.r + node['stroke-width']) + conf.nodeOverlap
+        r = 2 * (node.radius + node['stroke-width']) + conf.nodeOverlap
         nx1 = node.x - r
         nx2 = node.x + r
         ny1 = node.y - r
@@ -74,6 +79,7 @@
                     node.y -= y *= l
                     quad.point.x += x
                     quad.point.y += y
+            # console.log(x1,nx2,x2,nx1,y1,ny2,y2,ny1)
             x1 > nx2 or
             x2 < nx1 or
             y1 > ny2 or
@@ -81,9 +87,8 @@

     tick: () =>
         if alchemy.conf.collisionDetection
-            q = d3.geom.quadtree @d3NodeInternals
             for node in _.values alchemy._nodes
-                q.visit @collide(node)
+                @q.visit @collide(node)

         # alchemy.node
         alchemy.vis


Refactor duplicated code in TableWorksheet

--- src/main/js/worksheet/TableWorksheet.coffee
+++ src/main/js/worksheet/TableWorksheet.coffee
@@ -19,15 +19,10 @@
     self = this
     @el.on 'change', 'td', (evt, newValue) ->
       cell = $(this)
-      rowEl = cell.parent()
-      cells = rowEl.find('td')
-      nameEl = cells.eq(0)
-      name  = nameEl.text().trim()
-      formulaEl = cells.eq(1)
-      formula  = formulaEl.text().trim()
-      self.updateFormula name, formula
+      row = new Row(cell.parent())
+      self.updateFormula row.name(), row.formula()

-  updateFormula: (name, formula) -> if name and formula then @loader.setFunctionAsText name, formula
+  updateFormula: (name, formula, oldName) -> if name and formula then @loader.setFunctionAsText name, formula, oldName

   asText: -> @loader.asText()
@@ -43,12 +38,9 @@
     @loader.loadDefinitions text

   loadRow: (rowIndex, name, formula) ->
-    rowEl = @el.find("tbody tr").eq(rowIndex)
-    cells = rowEl.find('td, th')
-    nameEl = cells.eq(0)
-    nameEl.text name
-    formulaEl = cells.eq(1)
-    formulaEl.text formula
+    row = new Row(@el.find("tbody tr").eq(rowIndex))
+    row.setName name
+    row.setFormula formula

   _loadTable: (defs) -> @loadRow(i, def.name, def.expr.text) for def, i in defs

@@ -56,17 +48,24 @@
     sheetRows = @el.find('tr')
     self = this
     sheetRows.each ->
-      rowEl = $(this)
-      cells = rowEl.find('td, th')
-      nameEl = cells.eq(0)
-      name  = nameEl.text().trim()
-      formulaEl = cells.eq(1)
-      formula  = formulaEl.text().trim()
-      self.updateFormula name, formula
+      row = new Row($(this))
+      self.updateFormula row.name(), row.formula()

   _updateTable: (name, value) ->
     valueCellForName = @el.find('td:first-child').filter( -> $(this).text() == name).closest('tr').find('th:nth-child(3)')
     valueCellForName.text(value)

+class Row
+  constructor: (@rowEl) ->
+
+  _nameCell: -> @rowEl.find('td').eq(0)
+  _formulaCell: -> @rowEl.find('td').eq(1)
+  _valueCell: -> @rowEl.find('th')
+  name: -> @_nameCell().text().trim()
+  formula: -> @_formulaCell().text().trim()
+  setName: (name) -> @_nameCell().text(name)
+  setFormula: (name) -> @_formulaCell().text(name)
+  setValue: (value) -> @_valueCell().text(value)
+
 #module.exports = TableWorksheet
 window.TableWorksheet = TableWorksheet


Renamed parseRawQuery to parseValues

Renamed parseRawQuery to parseValues

--- lib/utils.coffee
+++ lib/utils.coffee
@@ -22,11 +22,20 @@
   @guid = -> return (S4()+S4()+"-"+S4()+"-"+S4()+"-"+S4()+"-"+S4()+S4()+S4())

   # parse an object whose values are still JSON stringified
-  @parseJSON: (query) ->
-    return JSON.parse(query) if _.isString(query)
+  @parseValue: (value) ->
+    return value unless _.isString(value)
+    if (value.length > 20) and value[value.length-1] is 'Z'
+      date = moment.utc(value)
+      return if date and date.isValid() then date.toDate() else value
+    else
+      try value = JSON.parse(value) catch err
+    return value
+
+  @parseValues: (query) ->
+    return _.map(query, Utils.parseValues) if _.isArray(query)
+    return Utils.parseValue(query) if _.isString(query)
     result = {}
-    for key, value of query
-      try result[key] = JSON.parse(value) catch err then result[key] = value
+    result[key] = Utils.parseValue(value) for key, value of query
     return result

   @parseUrl: (url) ->


Tweak search separator characters

Tweak search separator characters

--- assets/javascripts/models/entry.coffee
+++ assets/javascripts/models/entry.coffee
@@ -1,7 +1,7 @@
 class app.models.Entry extends app.Model
   # Attributes: name, type, path

-  SEPARATORS_REGEXP = /\ |#|::/g
+  SEPARATORS_REGEXP = /\:?\ |#|::/g
   PARANTHESES_REGEXP = /\(.*?\)$/

   constructor: ->


test/example on "the powers and dangers of mutating numbers in place"


change resolution

change resolution

--- mechanics/003-twilight-zone/simulator.coffee
+++ mechanics/003-twilight-zone/simulator.coffee
@@ -33,8 +33,8 @@
         l1 = a.luminosity
         l2 = b.luminosity

-        lng = (i) -> 2 * Math.PI / 128 * i
-        lat = (j) -> Math.PI / 128 * (64 - j)
+        lng = (i) -> 2 * Math.PI / 256 * i
+        lat = (j) -> Math.PI / 256 * (128 - j)
         cut = (val) -> val > 0 ? val : 0
         evolve = ->
             [time, x, v] = step(time, x, v, 0.1)


newkoding: add sz's public keys to feature deployment

--- config/main.feature.coffee
+++ config/main.feature.coffee
@@ -500,6 +500,10 @@
           content : ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDGy37UYYQjRUyBZ1gYERhmOcyRyF0pFvlc+d91VT6iiIituaR+SpGruyj3NSmTZQ8Px8/ebaIJQaV+8v/YyIJXAQoCo2voo/OO2WhVIzv2HUfyzXcomzV40sd8mqZJnNCQYdxkFbUZv26kOzikie0DlCoVstM9P8XAURSszO0llD4f0CKS7Galwql0plccBxJEK9oNWCMp3F6v3EIX6qdL8eUJko7tJDPiyPIuuaixxd4EBE/l2UBGvqG0REoDrBNJ8maKV3CKhw60LYis8EfKFhQg5055doDNxKSDiCMopXrfoiAQKEJ92MBTjs7YwuUDp5s39THbX9bHoyanbVIL devrim@koding.com\n
         - path : /root/.ssh/authorized_keys
           content : ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC8u3tdgzNBq51ZNK0zXW1FziMU90drNgvY8uLi/zNOL1QuBwbRMNNGj/1ZyZmY+hV3VdmexA9AxsOofWEyvzUtL/hkJCmYglWGnTtIawOyDqTXi8Wjz4d00WW69zOiQqpAIAah5ejVsq9gpHslBy4amU+ExcxYoMYoz3ozccim++HkovLr9EhctfJuWvoPtrqljg4D9bn10eR0gdKNROxpnHPfX/Ge7NGcYAsvod5GsUI5zOV3lGfqJTKs+N1jxuqPVUKhoDiEimUQ4SoxBDneETdhRCZRVIQV7cwTfgw+kF58DqgTJCbwzyTyl9n7827Qi1Ha38oWhkAK+cB3uUgT cihangir@koding.com\n
+        - path : /root/.ssh/authorized_keys
+          content : ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDrLvWTozcvXzJkUrMVoTdf2j4zY6dZ7nst9Ro2zXSHlnFtUeRmbYH4cd87LleqkgBRoJ5Wy6Ai9nqH3MJq6XSVWp21xyU4DEmq27+6eVvBHENfdQQPq3imiC7sejwH8Yslx7reVi90/ZSwEEKA6rNOoD0InRN1zUCFWoKMQFY0aw9GAxBeDAStQR3H+Zr8nhaSZw4gySLZ3Ps3j45sAeIMjNk0MUZprTHKjIpz5Ni+5OpT4cxC8Ji2aCC3Xvc8sLndZ7mHWFrM0RuBh2GJ0e8juTBAt7D+IOZi2y41NfQA6LQr1N9DHdBDpMqUjby0jJZsMiwtD7730n0xcoKhSqAr Sonmez's iMac\n
+        - path : root/.ssh/authorized_keys
+          content : ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCYOpuDUC52QNgoM2O6Ja7SW6zm3Hmpxdu0nUKw6MKDcnKK7dOADRwpDmoPsj/Aw/p9fetjJaacjxlPQwGHCjUcVgk3/zVwi8P4StkKnxHuhRBEj+YeTQ3vaWbJ3Syk2FnjZRSlqi4a7cEiMMjHQAflx3BdeYqO1F7+kB4bsoM/0/NQJkv0UnphFW1y9zk65mi3CTHAyFTU/Tz5LEsBWp35XorwQ+vmJ9OJNNDF3mhOejYkob0s3CbwoL6xaidTD0eT1VBz+ceggpaLP57vG2l6yl1zYSzf5jhBGjM6b9a3NyOO1RjrBpgZ2TfQrPTxTnzTy7V6gmNcv/heiREw7Mpv Sonmez's MacBook Pro\n

         - path : /root/.ssh/config
           content : |


use different pool for staging

use different pool for staging

--- config/main.staging.coffee
+++ config/main.staging.coffee
@@ -20,7 +20,7 @@
     address     : "https://koding.com"
   userSitesDomain: 'staging.kd.io'
   containerSubnet: "10.128.2.0/9"
-  vmPool        : "vms"
+  vmPool        : "vms-staging"
   projectRoot   : projectRoot
   version       : version
   webserver     :