Add AMD & CommonJS support. Allow the entire template to be configured

--- src/please_wait.coffee
+++ src/please_wait.coffee
@@ -1,6 +1,15 @@
-do ->
-  "use strict"
-
+((root, factory) ->
+  if typeof exports is "object"
+    # CommonJS
+    factory exports
+  else if typeof define is "function" and define.amd
+    # AMD. Register as an anonymous module.
+    define ["exports"], factory
+  else
+    # Browser globals
+    factory root
+  return
+) this, (exports) ->
   PleaseWait = ->
     getTransitionEvent = ->
       el = document.createElement('fakeelement')
@@ -16,6 +25,25 @@
         return val if el.style[key]?

     _pleaseWait = {
+      defaultOptions: {
+        fadeOut: true,
+        backgroundColor: '#f46d3b',
+        logo: null,
+        template: """
+

+

+

+

+
+

+

+

+

+

+

+        """
+      }
+
       done: ->
         return if @_done || !@_loadingDiv?
         @_done = true
@@ -30,29 +58,16 @@
             @_loadingDone()
         else @_loadingDone()

-      status: (msg) ->
-        @_messageDiv.innerHTML = msg
-
       load: (options = {}) ->
-        @_options                          = options
-        @_options.fadeOut                  = true unless options.fadeOut?
-        @_loadingDiv                       = document.createElement("div")
+        @_options                          = @_setOptions(options)
+        @_loadingDiv                       = document.createElement("header")
         @_loadingDiv.className             = "pg-loading-screen"
-        @_loadingDiv.style.backgroundColor = options.backgroundColor || "#f46d3b"
-        @_messageDiv                       = document.createElement("div")
-        @_messageDiv.className             = "pg-loading-message"
-        spinner                            = document.createElement("div")
-        spinner.className                  = "pg-loading-spinner"
+        @_loadingDiv.style.backgroundColor = @_options.backgroundColor
+        @_loadingDiv.innerHTML             = @_options.template
         spinnerTemplate                    = null
-        loadingInnerDiv                    = document.createElement("div")
-        loadingInnerDiv.className          = "pg-loading-inner"
-        loadingCenterOuterDiv              = document.createElement("div")
-        loadingCenterOuterDiv.className    = "pg-loading-center-outer"
-        loadingCenterMiddleDiv             = document.createElement("div")
-        loadingCenterMiddleDiv.className   = "pg-loading-center-middle"
-        logoImg                            = new Image()
-        logoImg.className                  = "pg-loading-logo"
-        logoImg.src                        = options.logo
+        spinner                            = @_loadingDiv.getElementsByClassName("pg-loading-spinner")[0]
+        logo                               = @_loadingDiv.getElementsByClassName("pg-loading-logo")[0]
+        logo.src                           = @_options.logo

         for script in document.scripts
           if script.id == "pgLoadingSpinner"
@@ -66,23 +81,26 @@

         spinner.innerHTML = if spinnerTemplate?
           spinnerTemplate
-        else if options.spinnerTemplate?
-          options.spinnerTemplate
+        else if @_options.spinnerTemplate?
+          @_options.spinnerTemplate
         else
           throw new Error("You need to set a spinner template ID or spinnerTemplate")

-        loadingCenterMiddleDiv.appendChild(logoImg)
-        loadingCenterMiddleDiv.appendChild(spinner)
-        loadingCenterMiddleDiv.appendChild(@_messageDiv)
-        loadingCenterOuterDiv.appendChild(loadingCenterMiddleDiv)
-        loadingInnerDiv.appendChild(loadingCenterOuterDiv)
-        @_loadingDiv.appendChild(loadingInnerDiv)
         document.body.appendChild(@_loadingDiv)

       _loadingDone: ->
         document.body.removeChild(@_loadingDiv)
         @_stylesheet.disabled = true
         document.body.className += " pg-loaded"
+
+      _setOptions: (new_opts = {}) ->
+        options = @defaultOptions
+
+        # Set options
+        for k, v of new_opts
+          if options.hasOwnProperty(k) then options[k] = v
+
+        options
     }

     return {
@@ -91,4 +109,4 @@
       status : (msg) -> _pleaseWait.status(msg)
     }

-  window.pleaseWait = new PleaseWait()+  exports.pleaseWait = new PleaseWait()


increase login cookie check timeout from 10s to 45s.

--- salvus/page/account.coffee
+++ salvus/page/account.coffee
@@ -901,8 +901,8 @@
 if localStorage.remember_me
     $(".salvus-remember_me-message").show().find("span").text(localStorage.remember_me)
     $(".salvus-sign_in-form").hide()
-    # just in case, always show manual login screen after 10s.
-    setTimeout((()=>$(".salvus-remember_me-message").hide(); $(".salvus-sign_in-form").show()), 10000)
+    # just in case, always show manual login screen after 45s.
+    setTimeout((()=>$(".salvus-remember_me-message").hide(); $(".salvus-sign_in-form").show()), 45000)

 salvus_client.on "remember_me_failed", () ->
     $(".salvus-remember_me-message").hide()
@@ -910,3 +910,4 @@

 salvus_client.on "signed_in", () ->
     $(".salvus-remember_me-message").hide()
+    $(".salvus-sign_in-form").show()


make terminal history font bigger

make terminal history font bigger

--- salvus/page/console.coffee
+++ salvus/page/console.coffee
@@ -594,7 +594,7 @@

         @element.find("a[href=#paste]").click () =>
             id = uuid()
-            s = "
 Terminal Copy and Paste
Copy and paste in terminals works as usual: to copy, highlight text then press ctrl+c (or command+c); press ctrl+v (or command+v) to paste.

NOTE: When no text is highlighted, ctrl+c sends the usual interrupt signal.

You can copy the terminal history from here:

<textarea readonly style='font-size: 6pt;font-family: monospace;line-height: 8pt;cursor: auto;width: 97%' id='#{id}' rows=10></textarea>"
+            s = "
 Terminal Copy and Paste
Copy and paste in terminals works as usual: to copy, highlight text then press ctrl+c (or command+c); press ctrl+v (or command+v) to paste.

NOTE: When no text is highlighted, ctrl+c sends the usual interrupt signal.

You can copy the terminal history from here:

<textarea readonly style='font-family: monospace;cursor: auto;width: 97%' id='#{id}' rows=10></textarea>"
             bootbox.alert(s)
             elt = $("##{id}")
             elt.val(@value).scrollTop(elt[0].scrollHeight)


re-enable hash for saving.

re-enable hash for saving.

--- salvus/page/syncdoc.coffee
+++ salvus/page/syncdoc.coffee
@@ -329,18 +329,19 @@
                     else if resp.event == 'error'
                         cb(resp.error)
                     else if resp.event == 'success' or resp.event == 'codemirror_wrote_to_disk'
-                        cb()
-                        ###
+                        if not resp.hash?
+                            console.log("_save: please restart your project server to get updated hash support")
+                            cb(); return
                         if resp.hash?
                             live = @live()
                             if live.string?
                                 live = live.string()
                             hash = misc.hash_string(live)
+                            console.log("_save: remote hash=#{resp.hash}; local hash=#{hash}")
                             if hash != resp.hash
                                 cb("file changed during save")
                             else
                                 cb()
-                        ###
                     else
                         cb("unknown response type #{misc.to_json(resp)}")


make it so codes don't show momentarily when pasting content into worksheets

--- salvus/page/syncdoc.coffee
+++ salvus/page/syncdoc.coffee
@@ -945,8 +945,8 @@
                 # since we just canceled the change.
                 @remove_cell_flags_from_changeObj(changeObj, ACTION_FLAGS)
                 @_apply_changeObj(changeObj)
-                @sync () =>
-                    @process_sage_updates()
+                @process_sage_updates()
+                @sync()

     init_worksheet_buttons: () =>
         buttons = @element.find(".salvus-editor-codemirror-worksheet-buttons")


[background] fixed bug when setting options before manifests were loaded

--- src/coffee/background.coffee
+++ src/coffee/background.coffee
@@ -362,7 +362,7 @@
                 return false

             setStoredData 'option-' + request.option, request.value, ->
-                data.invalidateCache()
+                data?.invalidateCache()
                 updateTabs()
                 sendResponse { result: request.value }


Disable the integration test in windows.

--- build/tasks/spec-task.coffee
+++ build/tasks/spec-task.coffee
@@ -110,7 +110,7 @@
         args: ['/c', appPath, '--test', "--resource-path=#{resourcePath}", "--spec-directory=#{coreSpecsPath}", '--log-file=ci.log']
         opts:
           env: _.extend({}, process.env,
-            ATOM_INTEGRATION_TESTS_ENABLED: true
+            # ATOM_INTEGRATION_TESTS_ENABLED: true # TODO: enable the integration test in windows
           )

     grunt.log.ok "Launching core specs."


chore (Paths) Sort lines

chore (Paths) Sort lines

--- src/steroids/paths.coffee
+++ src/steroids/paths.coffee
@@ -84,16 +84,16 @@
     configJson: path.join @application.distDir, "config.json"

   @application.configs =
+    app: path.join @application.configDir, "app.coffee"
     appgyver: path.join @application.configDir, "appgyver.json"
+    appgyverSettings: path.join @application.distDir, "__appgyver_settings.json"
     application: path.join @application.configDir, "application.coffee"
+    bower: path.join @applicationDir, "bower.json"
     cloud: path.join @application.configDir, "cloud.json"
+    configAndroidXml: path.join @application.wwwDir, "config.android.xml"
+    configIosXml: path.join @application.wwwDir, "config.ios.xml"
     env: path.join @application.configDir, "env.json"
-    bower: path.join @applicationDir, "bower.json"
-    configIosXml: path.join @application.wwwDir, "config.ios.xml"
-    configAndroidXml: path.join @application.wwwDir, "config.android.xml"
     packageJson: path.join @applicationDir, "package.json"
-    appgyverSettings: path.join @application.distDir, "__appgyver_settings.json"
-    app: path.join @application.configDir, "app.coffee"
     structure: path.join @application.configDir, "structure.coffee"
     data:
       sandboxdb: path.join @application.configDir, "sandboxdb.yaml"


:white_check_mark: [regression catcher] registerCommandFromSpec doesn't load base.js

--- spec/fast-activation-spec.coffee
+++ spec/fast-activation-spec.coffee
@@ -62,14 +62,17 @@

   # * To reduce IO and compile-evaluation of js file on startup
   describe "requrie as minimum num of file as possible on startup", ->
-    shouldRequireFilesInOrdered = [
-      "lib/main.js"
-      "lib/settings.js"
-      "lib/vim-state.js"
-      "lib/json/command-table.json"
-    ]
-    if atom.inDevMode()
-      shouldRequireFilesInOrdered.push('lib/developer.js')
+    shouldRequireFilesInOrdered = null
+
+    beforeEach ->
+      shouldRequireFilesInOrdered = [
+        "lib/main.js"
+        "lib/settings.js"
+        "lib/vim-state.js"
+        "lib/json/command-table.json"
+      ]
+      if atom.inDevMode()
+        shouldRequireFilesInOrdered.push('lib/developer.js')

     it "THIS IS WORKAROUND FOR Travis-CI's", ->
       # HACK:
@@ -111,6 +114,30 @@
           files = shouldRequireFilesInOrdered.concat(extraShouldRequireFilesInOrdered)
           ensureRequiredFiles(files)

+    it "just referencing service function doesn't load base.js", ->
+      withCleanActivation (pack) ->
+        service = pack.mainModule.provideVimModePlus()
+        for key in Object.keys(service)
+          service.key
+        ensureRequiredFiles(shouldRequireFilesInOrdered)
+
+    it "calling service.getClass load base.js", ->
+      withCleanActivation (pack) ->
+        service = pack.mainModule.provideVimModePlus()
+        service.getClass("MoveRight")
+        extraShouldRequireFilesInOrdered = [
+          "lib/base.js"
+          "lib/json/file-table.json"
+          "lib/motion.js"
+        ]
+        ensureRequiredFiles(shouldRequireFilesInOrdered.concat(extraShouldRequireFilesInOrdered))
+
+    it "calling service.registerCommandFromSpec doesn't load base.js", ->
+      withCleanActivation (pack) ->
+        service = pack.mainModule.provideVimModePlus()
+        service.registerCommandFromSpec("SampleCommand", {prefix: 'vim-mode-plus-user', getClass: () => "SampleCommand"})
+        ensureRequiredFiles(shouldRequireFilesInOrdered)
+
   describe "command-table", ->
     # * Loading atom commands from pre-generated command-table.
     # * Why?


fix for sharing unique identifier issue

fix for sharing unique identifier issue

--- src/FileManager/FilesCtrl.coffee
+++ src/FileManager/FilesCtrl.coffee
@@ -31,7 +31,9 @@
   $scope.share = []
   userList     = []
   User.all().then (list) =>
-    userList = list
+    for u in list
+      u.name = u.name + ' (id: ' + u._id[0..2] + ')'
+      userList.push u

   $scope.loadUsers = ($query)->
     results = []


fix type in fundraising.coffee

fix type in fundraising.coffee

--- scripts/fundraising.coffee
+++ scripts/fundraising.coffee
@@ -15,7 +15,7 @@
 #   Christopher De Cairos
 module.exports = (robot) ->

-  getTotal = (robot) - >
+  getTotal = (robot) ->
     robot.http("https://transaction-storage.mofoprod.net/eoy-2016-total")
       .get() (err, res, body) ->
         if err


update the lastSeenSha when commit completes

--- scripts/gh-book/app.coffee
+++ scripts/gh-book/app.coffee
@@ -173,6 +173,9 @@

       session.getBranch().writeMany(changedFiles, commitText, parentCommitSha)
       .done((val) =>
+        # Update the lastSeenSha so we do not load the commit we just made
+        remoteUpdater.lastSeenSha = val.sha
+
         # Fire the onSave event on all the changed models
         _.map models, (model) -> model.onSaved?()
         promise.resolve(val)


fix bug

fix bug

--- app/assets/javascripts/hanuman/views/survey_step.js.coffee
+++ app/assets/javascripts/hanuman/views/survey_step.js.coffee
@@ -11,6 +11,6 @@
       update: (event, ui) ->
         indexes = {}
         $(this).find(".item").each (index) ->
-          indexes[$(this).data("id")] = index\
+          indexes[$(this).data("id")] = index
         controller.updateSortOrder indexes
 )


Fix double-submission prevention not working.

--- app/assets/javascripts/prevent_double_submission.js.coffee
+++ app/assets/javascripts/prevent_double_submission.js.coffee
@@ -21,7 +21,7 @@
           button.data('text', null)

   initialize: ->
-    $('form').on('submit', event, ->
+    $('form').on('submit', (event) ->
       buttons = $(this).find(':button, :submit')
       App.PreventDoubleSubmission.disable_buttons(buttons)
     ).on('ajax:success', ->


Fixed up and down error where rows could move past size of array

--- app/assets/javascripts/schedules/ScheduleController.coffee
+++ app/assets/javascripts/schedules/ScheduleController.coffee
@@ -38,16 +38,18 @@
     if direction == "up"
       for day, dayIndex in @schedule.classSchedule
         if day.day == moveDay
-          temp = @schedule.classSchedule[dayIndex].classes[index - 1]
-          @schedule.classSchedule[dayIndex].classes[index - 1] = @schedule.classSchedule[dayIndex].classes[index]
-          @schedule.classSchedule[dayIndex].classes[index] = temp
+          if index > 0
+            temp = @schedule.classSchedule[dayIndex].classes[index - 1]
+            @schedule.classSchedule[dayIndex].classes[index - 1] = @schedule.classSchedule[dayIndex].classes[index]
+            @schedule.classSchedule[dayIndex].classes[index] = temp
           break
     else if direction == "down"
       for day, dayIndex in @schedule.classSchedule
         if day.day == moveDay
-          temp = @schedule.classSchedule[dayIndex].classes[index + 1]
-          @schedule.classSchedule[dayIndex].classes[index + 1] = @schedule.classSchedule[dayIndex].classes[index]
-          @schedule.classSchedule[dayIndex].classes[index] = temp
+          if index < @schedule.classSchedule[dayIndex].classes.length - 1
+            temp = @schedule.classSchedule[dayIndex].classes[index + 1]
+            @schedule.classSchedule[dayIndex].classes[index + 1] = @schedule.classSchedule[dayIndex].classes[index]
+            @schedule.classSchedule[dayIndex].classes[index] = temp
           break
     else alert("Something wrong happened! Please try again or contact the web administrator!")


Sync address data when completing order

Sync address data when completing order

--- app/assets/javascripts/sprangular/services/checkout.coffee
+++ app/assets/javascripts/sprangular/services/checkout.coffee
@@ -49,7 +49,10 @@
       params =
         goto: 'complete'
         'order[payments_attributes][][payment_method_id]': paymentMethodId
-        order: {}
+        order:
+          use_billing: order.shipToBillAddress
+          ship_address_attributes: order.actualShippingAddress().serialize()
+          bill_address_attributes: order.billingAddress.serialize()
         payment_source: {}

       @_addShippingRate(params, order)


make sure filters get cleared (when navigating away and back)

--- app/assets/javascripts/trade/routes/search_route.js.coffee
+++ app/assets/javascripts/trade/routes/search_route.js.coffee
@@ -7,4 +7,5 @@
       @controllerFor('units').load()
       @controllerFor('sources').load()
       @controllerFor('purposes').load()
+      @controllerFor('search').resetFilters()
     ])


add user to team

add user to team

--- scripts/link-github.coffee
+++ scripts/link-github.coffee
@@ -3,19 +3,28 @@
     @github = require('githubot')(@robot)
     @org = "tsoha-syksy2012"

+  formatRepoName: (username) ->
+    "harkka-#{username}"
+
   formatRepo: (username) ->
-    "#{@org}/#{username}"
+    "#{@org}/#{@formatRepoName username}"

   formatRepoUrl: (username) ->
     "/repos/#{@formatRepo username}"

   createRepository: (username, cb) ->
     @github.post "/orgs/#{@org}/repos", {
-      name: username,
+      name: @formatRepoName(username),
       has_wiki: false,
       auto_init: true,
       team_id: @getTeamId()
     }, cb
+
+  addNewStudent: (username, cb) ->
+    _this = this
+    @github.request "PUT", "/teams/#{@getTeamId()}/members/#{username}", {}, (data) ->
+      console.log "liitetty tiimiin"
+      _this.createRepository username, cb

   getRepository: (username, cb) ->
     @github.get @formatRepoUrl(username), cb
@@ -84,8 +93,9 @@
       user = users[0]
       user.username = msg.match[1].trim()
       msg.send "Selvä #{nick}, sinulle luodaan repository..."
-      tsohahub.createRepository user.username, (repository) ->
-        msg.send "#{nick}: #{repository.html_url}"
+      tsohahub.addNewStudent user.username, (repository) ->
+        msg.send "#{nick}: git clone #{repository.ssh_url} tsoha"
+        msg.send "#{nick}: luo vielä viikolle 1 haara komennolla: luo viikolle 1 haara"

   robot.respond /Mikä(.+)repo(.*)\?/i, (msg) ->
     nick = msg.message.user.name
@@ -101,8 +111,7 @@
     if users.length is 1
       user = users[0]
       tsohahub.createWeek user.username, msg.match[1].trim(), (pullRequest) ->
-        msg.send "#{nick}: Viikon edistymistä voit seurata: #{pullRequest.html_url}"
-
+        msg.send "#{nick}: Haara luotu, joten: git fetch && git checkout #{pullRequest.head.ref}"

   robot.respond /kommentoi/i, (msg) ->
     nick = msg.message.user.name
@@ -113,4 +122,4 @@
         if pullRequest?
           msg.send "tsohaohjaajat: #{pullRequest.html_url}"
         else
-          msg.send "#{nick}: Sinulla ei ole avointa pull requestia"
+          msg.send "#{nick}: Sinulla ei ole avointa pull requestia, kokeile: luo viikolle x haara"


check if we get an authorization error page on scheduling

--- extension/chrome/js/src/content.coffee
+++ extension/chrome/js/src/content.coffee
@@ -113,6 +113,8 @@
         resp items[M.STORE.LAST_VERSION]
         return
       return
+
+    @isAuthorisationErrorPage: (contents) -> /reauth/i.test contents

     @isAuthorisationErrorResponse: (resp) ->
       resp?.toLowerCase().indexOf "authorization" isnt -1
@@ -540,7 +542,7 @@
         dialog.title authDialog.title()

         authOkButton.on 'click', =>
-          @openAuthWindow action: 'setupNoSchedule'
+          @openAuthWindow {}
           dialog.close()
           return

@@ -590,10 +592,7 @@
     onScheduleError: (status, params, error, responseText) =>
       log 'There was an error', arguments
       if (status is 'parsererror' and M.isAuthorisationErrorPage responseText) or (M.isAuthorisationErrorResponse status)
-        params ?= {}
-        params.action = 'setup'
-        delete params.callback if params.callback
-        @gettingStarted params
+        @gettingStarted params ? {}
       else
         notification =
           action:   'notification'


change method name

change method name

--- extension/chrome/js/src/content.coffee
+++ extension/chrome/js/src/content.coffee
@@ -47,7 +47,7 @@
 		currentView: null

 		constructor: ->
-			window.addEventListener "message", @gmailrListener, false
+			window.addEventListener "message", @messageListener, false

 			chrome.extension.sendMessage {action: "setting", key: 'debug'}, (debug) =>
 				@debug = debug
@@ -70,7 +70,7 @@
 			chrome.extension.sendMessage {action: "setting", key: 'email'}, resp
 			return

-		gmailrListener: (e) =>
+		messageListener: (e) =>
 			if e.source is window
 				# We only accept messages from ourselves
 				#log "event", e


Drop support for old style metadata

Drop support for old style metadata

--- src/plugins/markdown-page.coffee
+++ src/plugins/markdown-page.coffee
@@ -76,12 +76,6 @@
     else
       metadata = ''
       markdown = content
-  else
-    # old style metadata
-    logger.warn 'Deprecation warning: page metadata should be encapsulated by at least three dashes (---)'
-    split_idx = content.indexOf '\n\n'
-    metadata = content.slice(0, split_idx)
-    markdown = content.slice(split_idx + 2)

   async.parallel
     metadata: (callback) ->


Axios get.

Axios get.

--- snippets/es6_snippets.cson
+++ snippets/es6_snippets.cson
@@ -11,3 +11,9 @@
     leftLabelHTML: "Ⓢ"
     rightLabelHTML: "ES6 ES6 Import axios from axios"
     body: "import axios from 'axios';\n$1"
+
+  "ES6: Axios get":
+    prefix: "axios-get"
+    leftLabelHTML: "Ⓢ"
+    rightLabelHTML: "ES6 ES6 Import axios from axios"
+    body: "axios.get('${1}').then(res => {\n\tconsole.log(res);\n}).catch(error => {\n\tconsole.log(error);\n});"


Fix case where configuration could accidentally persist from one invocation of a transform to another.

--- src/transformTools.coffee
+++ src/transformTools.coffee
@@ -20,6 +20,12 @@
     answer = {}
     answer[key] = a[key] for key of a
     answer[key] = b[key] for key of b
+    return answer
+
+clone = (a) ->
+    if !a then return a
+    answer = {}
+    answer[key] = a[key] for key of a
     return answer

@@ -64,7 +70,7 @@
             loadConfig.loadTransformConfigSync transformName, file, options

         if config?
-            configData ?= {config:{}}
+            configData = clone(configData) ? {config:{}}
             configData.config = merge configData.config, config

         if skipFile file, configData, options then return through()


remove parseInt

remove parseInt

--- command.coffee
+++ command.coffee
@@ -6,9 +6,6 @@
 MeshbluConfig = require 'meshblu-config'

 class Command
-  parseInt: (str) =>
-    parseInt str
-
   parseOptions: =>
     commander
       .version packageJSON.version


Authors have been updated

Authors have been updated

--- src/foundation-player.coffee
+++ src/foundation-player.coffee
@@ -1,5 +1,5 @@
 # foundation-player.js
-# Author: Alexander Egorov
+# Authors: Alexander Egorov, Rita Kondratyeva

 (($, window) ->
   # Define the plugin class


Make shodow proxy props exportable to capture them in state

--- framer/Layer.coffee
+++ framer/Layer.coffee
@@ -378,7 +378,6 @@
 	for shadowProp in ["X", "Y", "Blur", "Spread", "Color", "Type"]
 		do (shadowProp) =>
 			@define "shadow#{shadowProp}",
-				exportable: false
 				get: ->
 					return null if not @shadows? or @shadows.length is 0
 					@shadows[0][shadowProp.toLowerCase()]


wercker hickup

wercker hickup

--- framer/Utils.coffee
+++ framer/Utils.coffee
@@ -830,7 +830,7 @@

 # Coordinate system
-
+
 # convert a point from a layer to the context level, with rootContext enabled you can make it cross to the top context
 Utils.convertPointToContext = (point = {}, layer, rootContext=false, includeLayer=true) ->
 	point = _.defaults(point, {x:0, y:0, z:0})


fixing typo in baseUrl for production

fixing typo in baseUrl for production

--- server/src/passport.coffee
+++ server/src/passport.coffee
@@ -7,7 +7,7 @@
   done null, user

 if process.env.NODE_ENV is 'production'
-  baseUri = 'https://footsnet.herokuapp.com'
+  baseUri = 'https://foosnet.herokuapp.com'
 else
   baseUri = "http://localhost:#{process.env.PORT}"


Remove all references of FlowRouter.getParam from room

--- client/views/app/room.coffee
+++ client/views/app/room.coffee
@@ -314,25 +314,17 @@
 						if i.msRequestFullscreen
 							i.msRequestFullscreen()

-
-
-	'click .chat-new-messages': (event) ->
-		console.log 'room click .chat-new-messages' if window.rocketDebug
-		# chatMessages = $('#chat-window-' + this._id + ' .messages-box .wrapper')
-		# chatMessages.animate({scrollTop: chatMessages[0].scrollHeight}, 'normal')
-		$('#chat-window-' + FlowRouter.getParam('_id') + ' .input-message').focus()
-
 	'click .toggle-favorite': (event) ->
 		console.log 'room click .toggle-favorite' if window.rocketDebug
 		event.stopPropagation()
 		event.preventDefault()
-		Meteor.call 'toogleFavorite', FlowRouter.getParam('_id'), !$('i', event.currentTarget).hasClass('favorite-room')
+		Meteor.call 'toogleFavorite', @_id, !$('i', event.currentTarget).hasClass('favorite-room')

 	'click .join': (event) ->
 		console.log 'room click .join' if window.rocketDebug
 		event.stopPropagation()
 		event.preventDefault()
-		Meteor.call 'joinRoom', FlowRouter.getParam('_id')
+		Meteor.call 'joinRoom', @_id

 	"click .burger": ->
 		console.log 'room click .burger' if window.rocketDebug
@@ -344,28 +336,20 @@

 	'focus .input-message': (event) ->
 		console.log 'room focus .input-message' if window.rocketDebug
-		KonchatNotification.removeRoomNotification(FlowRouter.getParam('_id'))
+		KonchatNotification.removeRoomNotification @_id

 	'keyup .input-message': (event) ->
-		console.log 'room keyup .input-message',FlowRouter.getParam('_id') if window.rocketDebug
-		ChatMessages.keyup(FlowRouter.getParam('_id'), event, Template.instance())
+		console.log 'room keyup .input-message', @_id if window.rocketDebug
+		ChatMessages.keyup(@_id, event, Template.instance())

 	'keydown .input-message': (event) ->
-		console.log 'room keydown .input-message',FlowRouter.getParam('_id') if window.rocketDebug
-		ChatMessages.keydown(FlowRouter.getParam('_id'), event, Template.instance())
-
-	# 'keydown .input-message-editing': (event) ->
-	# 	console.log 'room keydown .input-message-editing',this._id if window.rocketDebug
-	# 	ChatMessages.keydownEditing(this._id, event)
-
-	# 'blur .input-message-editing': (event) ->
-	# 	console.log 'room blur keydown blur .input-message-editing',this._id if window.rocketDebug
-	# 	ChatMessages.stopEditingLastMessage()
+		console.log 'room keydown .input-message', @_id if window.rocketDebug
+		ChatMessages.keydown(@_id, event, Template.instance())

 	'click .message-form .icon-paper-plane': (event) ->
 		console.log 'room click .message-form .icon-paper-plane' if window.rocketDebug
 		input = $(event.currentTarget).siblings("textarea")
-		ChatMessages.send(FlowRouter.getParam('_id'), input.get(0))
+		ChatMessages.send(this._id, input.get(0))

 	'click .add-user': (event) ->
 		console.log 'room click click .add-user' if window.rocketDebug
@@ -390,7 +374,7 @@
 		if event.keyCode is 27 # esc
 			Session.set('editRoomTitle', false)
 		else if event.keyCode is 13 # enter
-			renameRoom FlowRouter.getParam('_id'), $(event.currentTarget).val()
+			renameRoom @_id, $(event.currentTarget).val()

 	'blur #room-title-field': (event) ->
 		console.log 'room blur #room-title-field' if window.rocketDebug
@@ -428,7 +412,7 @@

 	'click button.load-more': (e) ->
 		console.log 'room click button.load-more' if window.rocketDebug
-		RoomHistoryManager.getMore FlowRouter.getParam('_id')
+		RoomHistoryManager.getMore @_id

 	'autocompleteselect #user-add-search': (event, template, doc) ->
 		console.log 'room autocompleteselect #user-add-search' if window.rocketDebug
@@ -511,8 +495,9 @@
 			ChatMessages.deleteMsg(msg)

 	'click .start-video': (event) ->
-		webrtc.to = FlowRouter.getParam('_id').replace(Meteor.userId(), '')
-		webrtc.room = FlowRouter.getParam('_id')
+		_id = Template.instance().data._id
+		webrtc.to = _id.replace(Meteor.userId(), '')
+		webrtc.room = _id
 		webrtc.start(true)

 	'click .stop-video': (event) ->


allowing childs to be annotated with state transitions individually

--- lib/assets/javascripts/nice_imp_jquery.js.coffee
+++ lib/assets/javascripts/nice_imp_jquery.js.coffee
@@ -45,38 +45,54 @@
 		e_ref = $(elem_ref)
 		return if !e? || !e_ref?

-		# get transition style
-		transition = e.data('state-transition')
+		filter = '[data-state-transition][data-state-transition!="none"]'
+		animated_elements = e.find('*').andSelf().filter(filter)
+
+		styles = []
+		durations = []

-		if transition != "none" 	# skip animation stuff when elment is marked with "none"
+		animated_elements.each (index,elem) =>
+
+			# get transition style
+			a_e = $(elem)
+			transition = a_e.data('state-transition')

 			# get custom defined transition definitions
 			if( NiceTransitions? )
 				transition_def = if(transition) then NiceTransitions[transition] else NiceTransitions.default
-
+
 			# if no custom definitions exist - generate a default to do something at least
 			if( !transition_def? )
 				if transition?
-					console.log("Custom Transition Definition for #{transition} is missing! Please create a NiceTransitions class and configure your transitions.")
-
+					console.log("Custom Transition Definition for \"#{transition}\" is missing! Please create a NiceTransitions class and configure your transitions.")
+
 				# rescue default transition
 				transition_def =
 					duration: 200
 					properties:
 						opacity: 0.0
-
+
+			durations.push transition_def.duration
+
 			# get current style values and apply start values
-			styles = {}
+			style = {}
 			for style_key, style_val of transition_def.properties
-				old_value = e.css(style_key)
-				e.css(style_key, style_val)
-				styles[style_key] = old_value
+				old_value = a_e.css(style_key)
+				a_e.css(style_key, style_val)
+				style[style_key] = old_value
+
+			console.log(style)
+
+			styles.push style

 		# insert element
 		action(e_ref, e)

-		# apply transition
-		e.animate(styles,transition_def.duration) if styles?
+		# animate
+		console.log(styles)
+
+		animated_elements.each (index,elem) =>
+			$(elem).animate(styles[index],durations[index]) if styles[index]?

 ## add event listener
 document.addEventListener "nice.dom.InsertAfterEvent", NiceJquery.insert_after, false


create fakeClient for guests

create fakeClient for guests

--- server/lib/server/client.coffee
+++ server/lib/server/client.coffee
@@ -36,9 +36,17 @@

 generateFakeClient = (req, res, callback)->

-  fakeClient = {}
+  fakeClient    =
+    context     :
+      group     : 'koding'
+      user      : 'guest-1'
+    connection  :
+      delegate  : null
+      groupName : 'koding'
+
   {clientId} = req.cookies
-  return callback null, {} unless clientId?
+
+  return callback null, fakeClient unless clientId?

   bongo.models.JSession.fetchSession clientId, (err, session)->
     return handleError err, callback if err


Allow event listeners chaining

Allow event listeners chaining

--- source/quo.gestures.coffee
+++ source/quo.gestures.coffee
@@ -59,6 +59,7 @@
     events.forEach (event_name) ->
       $$.fn[event_name] = (callback) ->
         $$(document.body).delegate @selector, event_name, callback
+    @

   _handle = (eventName, target, data) ->
     for name, handler of HANDLERS when handler[eventName]


:art:

:art:

--- spec/tree-view-spec.coffee
+++ spec/tree-view-spec.coffee
@@ -2555,11 +2555,7 @@
         expect(zetaEntries).toEqual(["zeta.txt"])

       it "squashes two dir names when the first only contains a single dir", ->
-        if path.sep is '\\'
-          # First escape the backslashes for Coffeescript, then escape them for jQuery
-          betaDir = findDirectoryContainingText(treeView.roots[0], "alpha\\beta")
-        else
-          betaDir = findDirectoryContainingText(treeView.roots[0], "alpha#{path.sep}beta")
+        betaDir = findDirectoryContainingText(treeView.roots[0], "alpha#{path.sep}beta")
         betaDir.expand()
         betaEntries = [].slice.call(betaDir.children[1].children).map (element) ->
           element.innerText
@@ -2567,11 +2563,7 @@
         expect(betaEntries).toEqual(["beta.txt"])

       it "squashes three dir names when the first and second only contain single dirs", ->
-        if path.sep is '\\'
-          # First escape the backslashes for Coffeescript, then escape them for jQuery
-          epsilonDir = findDirectoryContainingText(treeView.roots[0], "gamma\\delta\\epsilon")
-        else
-          epsilonDir = findDirectoryContainingText(treeView.roots[0], "gamma#{path.sep}delta#{path.sep}epsilon")
+        epsilonDir = findDirectoryContainingText(treeView.roots[0], "gamma#{path.sep}delta#{path.sep}epsilon")
         epsilonDir.expand()
         epsilonEntries = [].slice.call(epsilonDir.children[1].children).map (element) ->
           element.innerText


:memo: mention subfolder in spec

:memo: mention subfolder in spec

--- spec/tree-view-spec.coffee
+++ spec/tree-view-spec.coffee
@@ -954,7 +954,7 @@

         expect(treeView.find('.file:contains(ignored.txt)').length).toBe 1

-    describe "when the project's path is the repository's working directory", ->
+    describe "when the project's path is a subfolder of the repository's working directory", ->
       [ignoreFile] = []

       beforeEach ->


Don't use os.tmpdir() as project path

Don't use os.tmpdir() as project path

This directory can be huge and cause the spec to take forever to build
the views for it (~10s on my machine).

Instead create a new temp folder under it since any folder is fine to use
for this specific spec.

--- spec/tree-view-spec.coffee
+++ spec/tree-view-spec.coffee
@@ -78,9 +78,10 @@
       describe "when the project is assigned a path because a new buffer is saved", ->
         it "creates a root directory view but does not attach to the root view", ->
           atom.workspaceView.openSync()
-          atom.workspaceView.getActivePaneItem().saveAs(path.join(os.tmpdir(), 'test.txt'))
+          projectPath = temp.mkdirSync('atom-project')
+          atom.workspaceView.getActivePaneItem().saveAs(path.join(projectPath, 'test.txt'))
           expect(treeView.hasParent()).toBeFalsy()
-          expect(treeView.root.getPath()).toBe os.tmpdir().substring(0, os.tmpdir().length - 1)
+          expect(fs.absolute(treeView.root.getPath())).toBe fs.absolute(projectPath)
           expect(treeView.root.parent()).toMatchSelector(".tree-view")

     describe "when the root view is opened to a file path", ->


fixed spell miss

fixed spell miss

--- src/editor/navigator.coffee
+++ src/editor/navigator.coffee
@@ -12,7 +12,7 @@

   refreshPage: (index) ->
     doc = Scena.Document.currentDocument()
-    capsula = cloneAndInsert.call(@, index, doc.pageAt(index))
+    capsule = cloneAndInsert.call(@, index, doc.pageAt(index))
     @dom.removeChild(@dom.children[index + 1])

   updateAll: ->
@@ -41,17 +41,19 @@
     for e in cloned.getElementsByTagName('*')
       e.removeAttribute('id')
     cloned.removeAttribute('id')
-    innerCcapsula = document.createElement('div')
-    innerCcapsula.appendChild(cloned)
-    outterCapsula = document.createElement('div')
-    outterCapsula.setAttribute('draggable', 'true')
-    outterCapsula.addEventListener('dragstart', @pageDragStart, false)
-    outterCapsula.addEventListener('dragenter', @pageDragEnter, false)
-    outterCapsula.addEventListener('dragleave', @pageDragLeave, false)
-    outterCapsula.addEventListener('dragover', @pageDragOver, false)
-    outterCapsula.appendChild(innerCcapsula)
-    @dom.insertBefore(outterCapsula, @dom.children[before])
-    return outterCapsula
+    innerCapsule = document.createElement('div')
+    innerCapsule.appendChild(cloned)
+    innerCapsule.classList.add('innerCapsule')
+    outerCapsule = document.createElement('div')
+    outerCapsule.setAttribute('draggable', 'true')
+    outerCapsule.addEventListener('dragstart', @pageDragStart, false)
+    outerCapsule.addEventListener('dragenter', @pageDragEnter, false)
+    outerCapsule.addEventListener('dragleave', @pageDragLeave, false)
+    outerCapsule.addEventListener('dragover', @pageDragOver, false)
+    outerCapsule.classList.add('outerCapsule')
+    outerCapsule.appendChild(innerCapsule)
+    @dom.insertBefore(outerCapsule, @dom.children[before])
+    return outerCapsule

   getCurrentPage: ->
     @currentIndex


removed unused stuff from run

removed unused stuff from run

--- src/scripts/run.coffee
+++ src/scripts/run.coffee
@@ -1,16 +1,4 @@
 angular.module('app').run ['$rootScope', '$log', ($rootScope, $log) ->
-	$rootScope.$on 'error:unauthorized', (event, response) ->
-		#$log.error 'unauthorized'
-
-	$rootScope.$on 'error:forbidden', (event, response) ->
-		#$log.error 'forbidden'
-
-	$rootScope.$on 'error:403', (event, response) ->
-		#$log.error '403'
-
-	$rootScope.$on 'success:ok', (event, response) ->
-		#$log.info 'success'
-
 	# fire an event related to the current route
 	$rootScope.$on '$routeChangeSuccess', (event, currentRoute, priorRoute) ->
 		$rootScope.$broadcast "#{currentRoute.controller}$routeChangeSuccess", currentRoute, priorRoute


最新のユーザ名の変更が反映されてなかったので修正。 fixes #4279

最新のユーザ名の変更が反映されてなかったので修正。 fixes #4279

--- src/adapters/direct.coffee
+++ src/adapters/direct.coffee
@@ -38,7 +38,9 @@

    withAuthor = (callback) ->
      (talk, user, msg) ->
-       envelope = self.robot.brain.userForId(user.id, user)
+       envelope = self.robot.brain.userForId(user.id)
+       envelope.name = user.name
+       envelope.email = user.email
        envelope.room = talk.id
        envelope.rooms ?= {}
        envelope.rooms[talk.id] =


[skip] Prevented cancelling edit for a view if for whatever reason that view was in edit mode and is being edited again (5m)

--- vendor/assets/javascripts/neat/collection_editor.coffee
+++ vendor/assets/javascripts/neat/collection_editor.coffee
@@ -151,7 +151,7 @@

   beforeEdit: (view)->
-    if @viewInEdit
+    if @viewInEdit and @viewInEdit isnt view
       @debug "cancelling edit for ##{$(@viewInEdit.el).attr('id')} (#{@indexOfViewInEdit()})"
       @viewInEdit.cancelEdit()
     @viewInEdit = view


add HUBOT_DATADOG_GRAPH_USE_SLACK option

--- src/scripts/datadog-graph.coffee
+++ src/scripts/datadog-graph.coffee
@@ -4,6 +4,7 @@
 # Configuration:
 #   HUBOT_DATADOG_GRAPH_API_KEY
 #   HUBOT_DATADOG_GRAPH_APPLICATION_KEY
+#   HUBOT_DATADOG_GRAPH_USE_SLACK
 #
 # Commands:
 #   hubot datadog graph <graph> - take a graph snapshot using the Datadog API
@@ -21,6 +22,7 @@
 config = parseConfig 'datadog-graph',
   apiKey: null
   applicationKey: null
+  useSlack: false

 module.exports = (robot) ->
   basePattern = 'd(?:ata)?d(?:og)?\\s+(?:graph|s(?:nap)?s(?:hot)?)\\s+'
@@ -64,7 +66,8 @@

     callGraphSnapshotAPI query, start, end
     .then (json) ->
-      res.send json.snapshot_url
+      suffix = if config.useSlack then '#png' else ''
+      res.send json.snapshot_url + suffix
     .catch (e) ->
       res.robot.logger.error e
       res.send 'hubot-datadog-graph: error'


Activity: Status Update Content Display minor fixes

--- client/Social/Activity/ContentDisplays/StatusUpdate.coffee
+++ client/Social/Activity/ContentDisplays/StatusUpdate.coffee
@@ -9,9 +9,9 @@

     super options,data

-    @activityItem = new StatusActivityItemView {delegate: this}, @getData()
+    @activityItem = new StatusActivityItemView delegate: this, @getData()

-    @activityItem.on 'ActivityIsDeleted', =>
+    @activityItem.on 'ActivityIsDeleted', ->
       KD.singleton('router').back()

   viewAppended: JView::viewAppended


Client: Activity: Added create public/private channes success event listenes

--- client/activity/lib/flux/stores/channelthreadsstore.coffee
+++ client/activity/lib/flux/stores/channelthreadsstore.coffee
@@ -3,6 +3,7 @@
 toImmutable          = require 'app/util/toImmutable'
 actions              = require '../actions/actiontypes'
 generateDummyMessage = require 'app/util/generateDummyMessage'
+createChannelActions = require 'activity/flux/createchannel/actions/actiontypes'

 ###*
  * A thin data structure to hold message list associated with given channelId.
@@ -46,6 +47,8 @@
     @on actions.LOAD_CHANNEL_SUCCESS, @addNewThread
     @on actions.LOAD_FOLLOWED_PUBLIC_CHANNEL_SUCCESS, @addNewThread
     @on actions.LOAD_FOLLOWED_PRIVATE_CHANNEL_SUCCESS, @addNewThread
+    @on createChannelActions.CREATE_PRIVATE_CHANNEL_SUCCESS, @addNewThread
+    @on createChannelActions.CREATE_PUBLIC_CHANNEL_SUCCESS, @addNewThread

   ###*


managed: Callback with a listener error, rather than an the logging error

--- client/app/lib/providers/managed/managedkitechecker.coffee
+++ client/app/lib/providers/managed/managedkitechecker.coffee
@@ -158,9 +158,8 @@

     # Be paranoid about duplicating timers.
     if @_delaying or @_ticking
-      kd.warn "ManagedKiteChecker: _startDelay called with pre-existing
-        timerId"
-      return
+      return @_callListener() new Error "ManagedKiteChecker:
+        _startDelay called with pre-existing timer or interval"

     @_delaying = yes
     @_timerId  = kd.utils.wait @getOption('delay'), =>
@@ -180,9 +179,8 @@

     # Be paranoid about duplicating timers.
     if @_delaying or @_ticking
-      kd.warn "ManagedKiteChecker: _startDelay called with pre-existing
-        timerId"
-      return
+      return @_callListener() new Error "ManagedKiteChecker:
+        _startTicking called with pre-existing timer or interval"

     @_ticking = yes
     @_timerId = kd.utils.repeat @getOption('interval'), @bound 'tick'


Fixed bug with server-side event inference.

--- src/server/browser_server.coffee
+++ src/server/browser_server.coffee
@@ -60,10 +60,10 @@
         lastIdx = params.length - 1
         for param, idx in params
             if name == 'PageLoaded'
-                str = Util.inspect(param).replace /[^\}],\n/g, (str) ->
+                str = Util.inspect(param, false, null).replace /[^\}],\n/g, (str) ->
                     str[0]
             else
-                str = Util.inspect(param).replace(/[\n\t]/g, '')
+                str = Util.inspect(param, false, null).replace(/[\n\t]/g, '')
                 #str = Util.inspect(param)
             @rpcLog.write(str)
             if idx == lastIdx
@@ -181,7 +181,6 @@

     AddEventListener : (event) ->
-        return if @browserLoading
         {target, type} = event
         return if !clientEvents[type]

@@ -192,7 +191,7 @@
         else
             target.__registeredListeners.push([targetId, type])

-        if target._attachedToDocument
+        if !@browserLoading && target._attachedToDocument
             @broadcastEvent('AddEventListener', targetId, type)

     EnteredTimer : () ->


Added code to move the ball and paddle like Breakout


Fixed not working text modules.

Fixed not working text modules.

--- app/assets/javascripts/app/controllers/agent_ticket_create.js.coffee
+++ app/assets/javascripts/app/controllers/agent_ticket_create.js.coffee
@@ -215,12 +215,12 @@
       template_id: template['id'],
     )

+    @formDefault = @formParam( @el.find('.ticket-create') )
+
     # show text module UI
     new App.TextModuleUI(
-      el: @el,
-    )
-
-    @formDefault = @formParam( @el.find('.ticket-create') )
+      el: $('.ticket-create')
+    )

   localUserInfo: (params) =>
     @userInfo(
@@ -286,7 +286,7 @@

     # show errors in form
     if errors
-      @log 'error new', errors
+      console.log 'error new', errors
       @formValidate( form: e.target, errors: errors )

     # save ticket, create article
@@ -305,8 +305,7 @@
             link:    "#ticket/zoom/#{@id}"
             timeout: 12000,

-          # create new create screen
-#          ui.render()
+          # close ticket create task
           App.TaskManager.remove( ui.task_key )

           # scroll to top


Add comments in memory on click.

Add comments in memory on click.

--- app/assets/javascripts/controllers/message_task_controller.js.coffee
+++ app/assets/javascripts/controllers/message_task_controller.js.coffee
@@ -4,6 +4,10 @@
   actions:
     clearMessageContent: -> null
     postComment: ->
+      userId = Tahi.currentUser.id.toString()
+      commenter = @store.all('user').findBy('id', userId)
       commentFields =
+        commenter: commenter
+        messageTask: @get('model')
         body: @get('newCommentBody')
       newComment = @store.createRecord('comment', commentFields)


Update collapse.coffee

Update collapse.coffee

--- app/assets/javascripts/martian_components/components/collapse.coffee
+++ app/assets/javascripts/martian_components/components/collapse.coffee
@@ -8,6 +8,14 @@
     @text           = $('.text', @el).first()
     @collapsedText  = $('.text.collapse', @el)
     @collapseToggle = $('.toggle-wrapper svg', @el)
+    @modalToggle  = $('> [data-toggle="modal"]', @inner)
+    @modalDismiss = $("[data-dismiss][data-target='#{@modalToggle.data('target')}']")
+    @modal        = $(@modalToggle.data('target'))
+
+    @modal.on 'shown.bs.modal',          => @modalDismiss.show()
+    @modal.on 'hide.bs.modal',           => @modalDismiss.hide()
+    @modalDismiss.on 'touchstart click', => @modal.modal('hide')
+    @collapseToggle.on 'click',          => @onCollapseToggleClick()

     if @collapsedText.length
       @collapsedText.collapse(toggle: false)
@@ -15,26 +23,9 @@
       @text.dotdotdot(watch: 'window')
       @fitText()

-    @initModal()
-    @onCollapseToggleClick()
-
   refresh: ->
     @closeCollapse()
     setTimeout (=> @fitText()), 300
-
-  initModal: ->
-    @modalToggle  = $('> [data-toggle="modal"]', @inner)
-    @modalDismiss = $("[data-dismiss][data-target='#{@modalToggle.data('target')}']")
-    @modal        = $(@modalToggle.data('target'))
-
-    @modal.on 'shown.bs.modal', =>
-      @modalDismiss.show()
-
-    @modal.on 'hide.bs.modal', =>
-      @modalDismiss.hide()
-
-    @modalDismiss.on 'touchstart click', =>
-      @modal.modal('hide')

   fitText: =>
     return if @collapsedText.length
@@ -46,13 +37,13 @@
   computeTextCollapseHeight: ->
     @text.css 'height', 0

-    collapse_or_modal_toggle =
+    collapseOrModalToggle =
       if @collapseToggle.is(':visible')
         @collapseToggle
       else
         @modalToggle

-    diff = collapse_or_modal_toggle.offset().top - @text.offset().top - 30
+    diff = collapseOrModalToggle.offset().top - @text.offset().top - 30
     if diff < 160 then 160 else diff

   computeTextFullHeight: ->
@@ -96,16 +87,15 @@
     ), 300

   onCollapseToggleClick: ->
-    @collapseToggle.on 'click', =>
-      if @collapsedText.length
-        @collapsedText.collapse('toggle')
+    if @collapsedText.length
+      @collapsedText.collapse('toggle')

-        if @collapseToggle.attr('class')?.length
-          @collapseToggle.attr('class', '')
-        else
-          @collapseToggle.attr('class', 'text-open')
+      if @collapseToggle.attr('class')?.length
+        @collapseToggle.attr('class', '')
       else
-        if @text.open then @closeCollapse() else @openCollapse()
+        @collapseToggle.attr('class', 'text-open')
+    else
+      if @text.open then @closeCollapse() else @openCollapse()

   onResponsiveSizeChange: ->
     @refresh()


Fix imports for polyfill_placeholder

Fix imports for polyfill_placeholder

--- app/assets/utensils/polyfill_placeholder/polyfill_placeholder.coffee
+++ app/assets/utensils/polyfill_placeholder/polyfill_placeholder.coffee
@@ -1,5 +1,6 @@

 #= require utensils/utensils
+#= require utensils/bindable

 # ## HTML5 placeholder feature fallback
 class utensils.PolyfillPlaceholder


Added gif support for 9gag.

Added gif support for 9gag.

--- src/scripts/9gag.coffee
+++ src/scripts/9gag.coffee
@@ -6,13 +6,16 @@
 #   "soupselect": "0.2.0"
 #
 # Configuration:
-#   None
+#   NINEGAG_NO_GIFS (optional; default is "false")
 #
 # Commands:
 #   hubot 9gag me - Returns a random meme image
 #
 # Author:
 #   EnriqueVidal
+#
+# Contributors:
+#   dedeibel (gif support)

 Select      = require( "soupselect" ).select
 HTMLParser  = require "htmlparser"
@@ -37,16 +40,24 @@
       location = response.headers['location']
       return send_meme( message, location, response_handler )

-    img_src = get_meme_image( body, ".badge-item-img" )
+    no_gifs = process.env.NINEGAG_NO_GIFS
+    selectors = ["a img.badge-item-img"]
+    if no_gifs != '0' && no_gifs != 't' && no_gifs != 'true'
+      selectors.unshift("div.badge-animated-container-animated img")
+
+    img_src = get_meme_image( body, selectors )

     if img_src.substr(0, 4) != "http"
       img_src = "http:#{img_src}"

     response_handler img_src

-get_meme_image = (body, selector)->
+get_meme_image = (body, selectors)->
   html_handler  = new HTMLParser.DefaultHandler((()->), ignoreWhitespace: true )
   html_parser   = new HTMLParser.Parser html_handler

   html_parser.parseComplete body
-  Select( html_handler.dom, selector )[0].attribs.src
+  for selector in selectors
+    img_container = Select( html_handler.dom, selector )
+    if img_container && img_container[0]
+      return img_container[0].attribs.src


Default to dev IDs

Default to dev IDs

--- src/scripts/site.coffee
+++ src/scripts/site.coffee
@@ -29,13 +29,13 @@

       projects: [
         new Project
-          # id: '4fa4088d54558f3d6a000001' # Dev
-          id: '4fdf8fb3c32dab6c95000001' # Prod
+          id: '4fa4088d54558f3d6a000001' # Dev
+          # id: '4fdf8fb3c32dab6c95000001' # Prod

           workflows: [
             new Workflow
-              # id: '4fa408de54558f3d6a000002' # Dev
-              id: '4fdf8fb3c32dab6c95000002' # Prod
+              id: '4fa408de54558f3d6a000002' # Dev
+              # id: '4fdf8fb3c32dab6c95000002' # Prod
               controller: new Classifier
                 el: '#classifier'
                 tutorialSteps: tutorialSteps


upload: fix bug related to code duplication

--- src/smc-webapp/smc-dropzone.cjsx
+++ src/smc-webapp/smc-dropzone.cjsx
@@ -54,7 +54,8 @@

-    postUrl : ->
+    postUrl: ->
+        # DANGER: code duplication with class below!
         dest_dir = misc.encode_path(@props.current_path)
         postUrl  = window.smc_base_url + "/#{@props.project_id}/raw/.smc/upload?dest_dir=#{dest_dir}"
         return postUrl
@@ -110,8 +111,9 @@
         return misc.merge(with_defaults, @props.config)

     postUrl: ->
+        # DANGER: code duplication with class above!
         dest_dir = misc.encode_path(@props.dest_path)
-        postUrl  = window.smc_base_url + "/upload?project_id=#{@props.project_id}&dest_dir=#{dest_dir}"
+        postUrl  = window.smc_base_url + "/#{@props.project_id}/raw/.smc/upload?dest_dir=#{dest_dir}"
         return postUrl

     componentDidMount: ->


Removed extraneous console output meant for debugging

--- src/scripts/time.coffee
+++ src/scripts/time.coffee
@@ -361,7 +361,6 @@
     earlyToday = moment({hour: 0, minute: 0, second: 0})
     now = moment({hour: 0, minute: 0, second: 0}).add(1, 'days')
     report = user.toRawPayroll(earlyToday, now)
-    console.log report
     headers = HEADERS.payrollreports

     loggedAny = false


noop change

noop change

--- src/art/engine/file/model.coffee
+++ src/art/engine/file/model.coffee
@@ -1 +1 @@
-module.exports = class Model extends require '..//core/element'
+module.exports = class Model extends require '../core/element'


Tip should always use event delegation...

Forbid not using it. Make it easier for plugin consumer to fall into
'pit of success.'

--- src/js/jquery.hlf.tip.coffee
+++ src/js/jquery.hlf.tip.coffee
@@ -71,9 +71,6 @@
       # - `triggerContent` can be the name of the trigger element's attribute or a
       #   function that provides custom content when given the trigger element.
       triggerContent: null
-      # - `shouldDelegate` is by default and encouraged to be on for improving
-      #   event handling performance.
-      shouldDelegate: on
       # - `ms.duration` are the durations of sleep and wake animations.
       # - `ms.delay` are the delays before sleeping and waking.
       ms:
@@ -197,10 +194,10 @@
       # Process `$triggers` and setup content, event, and positioning aspects.
       processTrigger = ($trigger) =>
         return no if not $trigger.length
+      @_bindTriggers()
         $trigger.addClass @classNames.trigger
         @_saveTriggerContent $trigger
         @_updateDirectionByTrigger $trigger
-        if @shouldDelegate is no then @_bindTrigger $trigger
       # Do this for initially provided triggers.
       @$triggers.each (i, el) => processTrigger $(el)
       # If `doLiveUpdate` is inferred to be true, process triggers added in the
@@ -220,7 +217,6 @@
         @_mutationObserver.observe @$context[0],
           childList: yes
           subtree: yes
-      if @shouldDelegate then @_bindTrigger()

     # `_defaultHtml` provides a basic html structure for tip content. It can be
     # customized via the `tipTemplate` external option, or by subclasses using
@@ -257,25 +253,15 @@
       if content?
         $trigger.data @attr('content'), content

-    # `_bindTrigger` links each trigger to the tip for: 1) possible appearance
+    # `_bindTriggers` links each trigger to the tip for: 1) possible appearance
     # changes during mouseenter, mouseleave (uses special events) and 2)
     # following on mousemove only if `doFollow` is on. Also note for our
     # `onMouseMove` handler, it's throttled by `requestAnimationFrame` when
-    # available, otherwise manually at hopefully 60fps. It does direct binding
-    # by default, can also do delegation (preferred) if `$trigger` isn't given
-    # but `$triggerContext` is.
-    _bindTrigger: ($trigger) ->
-      $bindTarget = $trigger
-      if not $bindTarget?
-        if @$context
-          $bindTarget = @$context
-          selector = ".#{@classNames.trigger}"
-        else
-          @debugLog 'invalid argument(s)'
-          return no
-      selector ?= null
+    # available, otherwise manually at hopefully 60fps.
+    _bindTriggers: ->
+      selector = ".#{@classNames.trigger}"
       # Base bindings.
-      $bindTarget.on [
+      @$context.on [
           @evt('truemouseenter')
           @evt('truemouseleave')
         ].join(' '),
@@ -296,7 +282,7 @@
               @_onTriggerMouseMove event
         else
           onMouseMove = _.throttle @_onTriggerMouseMove, 16
-        $bindTarget.on 'mousemove', selector, onMouseMove
+        @$context.on 'mousemove', selector, onMouseMove

     # `_bind` adds event handlers to `$tip`, mostly so state can be updated such
     # that the handlers on `_$currentTrigger` make an exception. The desired
@@ -634,19 +620,14 @@
           offset.top += $trigger.outerHeight()
       offset

-    # Extend `_bindTrigger` to get initial position for snapping. This is only
+    # Extend `_bindTriggers` to get initial position for snapping. This is only
     # for snapping without snapping to the trigger, which is only what's
     # currently supported. See `afterShow` hook.
-    _bindTrigger: ($trigger) ->
-      didBind = super $trigger
-      return no if didBind is no
-      $bindTarget = $trigger
-      if not $bindTarget? and @$context
-        $bindTarget = @$context
-        selector = ".#{@classNames.trigger}"
-      selector ?= null
+    _bindTriggers: ->
+      super()
+      selector = ".#{@classNames.trigger}"
       # Modify base binding.
-      $bindTarget.on @evt('truemouseleave'), selector, { selector },
+      @$context.on @evt('truemouseleave'), selector, { selector },
         (event) => @_offsetStart = null

     # Extend `_positionToTrigger` to set `cursorHeight` to 0, since it won't


Fixup 9134fea: forgot to include snapTip.toString in changes.

Optimize and update with renaming.

--- src/js/jquery.hlf.tip.coffee
+++ src/js/jquery.hlf.tip.coffee
@@ -73,12 +73,11 @@
   #   axis-snapping. On by default.
   hlf.snapTip =
     debug: off
-    toString: (context) ->
+    toString: _.memoize (context) ->
       switch context
-        when 'event'  then '.hlf.snapTip'
-        when 'data'   then 'hlfSnapTip'
+        when 'event'  then '.hlf.snap-tip'
+        when 'data'   then 'hlf-snap-tip'
         when 'class'  then 'js-snap-tips'
-        when 'log'    then 'hlf-snap-tip:'
         else 'hlf.snapTip'

     defaults: do (pre='js-snap-tip-') ->


[+] sort :  if null then empty text for localCompare

--- src/methods/sort.coffee
+++ src/methods/sort.coffee
@@ -6,6 +6,8 @@
     va = if type != 'text' then a.getAttribute(t) else a.innerHTML
     vb = if type != 'text' then b.getAttribute(t) else b.innerHTML
     r = if way is 'ASC' then 1 else -1
+    va ?= ''
+    vb ?= ''
     r * va.localeCompare vb
   for i of actives
     @element.appendChild actives[i]


:memo: Mention why ipc is used

:memo: Mention why ipc is used

--- src/text-editor-component.coffee
+++ src/text-editor-component.coffee
@@ -445,6 +445,9 @@

     @subscribe @props.editor.onDidChangeSelectionRange =>
       if selectedText = @props.editor.getSelectedText()
+        # This uses ipc.send instead of clipboard.writeText because
+        # clipboard.writeText is a sync ipc call on Linux and that
+        # will slow down selections.
         ipc.send('write-text-to-selection-clipboard', selectedText)

   observeConfig: ->


Unify gutter drag logic

Unify gutter drag logic

This ensures the same approach is used to drag the gutter for all modes
of selection.

--- src/text-editor-component.coffee
+++ src/text-editor-component.coffee
@@ -419,61 +419,36 @@
       @onGutterClick(event)

   onGutterClick: (event) =>
-    clickedRow = @screenPositionForMouseEvent(event).row
-    clickedBufferRow = @editor.bufferRowForScreenRow(clickedRow)
-
+    clickedBufferRow = @editor.bufferRowForScreenRow(@screenPositionForMouseEvent(event).row)
     @editor.setSelectedBufferRange([[clickedBufferRow, 0], [clickedBufferRow + 1, 0]], preserveFolds: true)
+    @handleGutterDrag(clickedBufferRow)
+
+  onGutterMetaClick: (event) =>
+    clickedBufferRow = @editor.bufferRowForScreenRow(@screenPositionForMouseEvent(event).row)
+    @editor.addSelectionForBufferRange([[clickedBufferRow, 0], [clickedBufferRow + 1, 0]], preserveFolds: true)
+    @handleGutterDrag(clickedBufferRow)
+
+  onGutterShiftClick: (event) =>
+    clickedBufferRow = @editor.bufferRowForScreenRow(@screenPositionForMouseEvent(event).row)
+    tailBufferPosition = @editor.getLastSelection().getTailBufferPosition()
+
+    if clickedBufferRow < tailBufferPosition.row
+      @editor.selectToBufferPosition([clickedBufferRow, 0], true)
+    else
+      @editor.selectToBufferPosition([clickedBufferRow + 1, 0], true)
+
+    @handleGutterDrag(tailBufferPosition.row, tailBufferPosition.column)
+
+  handleGutterDrag: (tailRow, tailColumn) ->
+    tailPosition = [tailRow, tailColumn] if tailColumn?

     @handleDragUntilMouseUp (screenPosition) =>
-      dragRow = screenPosition.row
-      dragBufferRow = @editor.bufferRowForScreenRow(dragRow)
-      if dragBufferRow < clickedBufferRow # dragging up
-        @editor.setSelectedBufferRange([[dragBufferRow, 0], [clickedBufferRow + 1, 0]], reversed: true, preserveFolds: true, autoscroll: false)
+      dragRow = @editor.bufferPositionForScreenPosition(screenPosition).row
+      if dragRow < tailRow
+        @editor.getLastSelection().setBufferRange([[dragRow, 0], tailPosition ? [tailRow + 1, 0]], reversed: true, autoscroll: false, preserveFolds: true)
       else
-        @editor.setSelectedBufferRange([[clickedBufferRow, 0], [dragBufferRow + 1, 0]], reversed: false, preserveFolds: true, autoscroll: false)
+        @editor.getLastSelection().setBufferRange([tailPosition ? [tailRow, 0], [dragRow + 1, 0]], reversed: false, autoscroll: false, preserveFolds: true)
       @editor.getLastCursor().autoscroll()
-
-  onGutterMetaClick: (event) =>
-    clickedRow = @screenPositionForMouseEvent(event).row
-    clickedBufferRow = @editor.bufferRowForScreenRow(clickedRow)
-
-    bufferRange = new Range([clickedBufferRow, 0], [clickedBufferRow + 1, 0])
-    rowSelection = @editor.addSelectionForBufferRange(bufferRange, preserveFolds: true)
-
-    @handleDragUntilMouseUp (screenPosition) =>
-      dragRow = screenPosition.row
-      dragBufferRow = @editor.bufferRowForScreenRow(dragRow)
-
-      if dragBufferRow < clickedBufferRow # dragging up
-        rowSelection.setBufferRange([[dragBufferRow, 0], [clickedBufferRow + 1, 0]], preserveFolds: true)
-      else
-        rowSelection.setBufferRange([[clickedBufferRow, 0], [dragBufferRow + 1, 0]], preserveFolds: true)
-
-      # The merge process will possibly destroy the current selection because
-      # it will be merged into another one. Therefore, we need to obtain a
-      # reference to the new selection that contains the originally selected row
-      rowSelection = _.find @editor.getSelections(), (selection) ->
-        selection.intersectsBufferRange(bufferRange)
-
-  onGutterShiftClick: (event) =>
-    clickedRow = @screenPositionForMouseEvent(event).row
-    clickedBufferRow = @editor.bufferRowForScreenRow(clickedRow)
-    tailPosition = @editor.getLastSelection().getTailScreenPosition()
-    tailBufferPosition = @editor.bufferPositionForScreenPosition(tailPosition)
-
-    if clickedRow < tailPosition.row
-      @editor.selectToBufferPosition([clickedBufferRow, 0])
-    else
-      @editor.selectToBufferPosition([clickedBufferRow + 1, 0])
-
-    @handleDragUntilMouseUp (screenPosition) =>
-      dragRow = screenPosition.row
-      dragBufferRow = @editor.bufferRowForScreenRow(dragRow)
-      if dragRow < tailPosition.row # dragging up
-        @editor.setSelectedBufferRange([[dragBufferRow, 0], tailBufferPosition], preserveFolds: true)
-      else
-        @editor.setSelectedBufferRange([tailBufferPosition, [dragBufferRow + 1, 0]], preserveFolds: true)
-

   onStylesheetsChanged: (styleElement) =>
     return unless @performedInitialMeasurement


Starting to fix; storing temporarily

Starting to fix; storing temporarily

--- source/coffee/index.coffee
+++ source/coffee/index.coffee
@@ -5,6 +5,7 @@

 $ ->
   IndexModel = ->
+    @wsTemp = ko.observableArray([])
     @wsItems = ko.observableArray([])

     # TODO: Map from data source
@@ -24,19 +25,26 @@
         key: (data) ->
           ko.utils.unwrapObservable data.id
         create: (options) ->
-          createNode options.data
+          ret = createNode options
+          console.log "Ret: ", ret, "Options: ", options
+          console.log "Id: ", options.data
+          ret.children if options.parent?()?.id?() is not "1" else ret

-    createNode = (node) ->
+    createNode = (options) =>
+      @node = options.data
       ko.mapping.fromJS
-        nodes: ({n, actions: []} for n in node.children ? [] when "container" in n.facts.backends)
+        nodes: ({n, actions: [], status: "good"} for n in @node.children ? [] when "container" in n.facts.backends)
+        children: (n for n in @node.children ? [] when "container" not in n.facts.backends)
         actions: []
-      , {}, ko.mapping.fromJS node, mapping
+      , {}, ko.mapping.fromJS @node, mapping

     @siteActive = ntrapy.selector (data) =>
       console.log "Triggered with: ", data
-      switch data?.name
+      switch data.name
         when "Workspace"
-          @getMappedData "http://roush.propter.net:8080/nodes/1/tree", @wsItems, mapping
+          @getMappedData "http://roush.propter.net:8080/nodes/1/tree", @wsTemp, mapping, =>
+            console.log @wsTemp()
+            #@wsItems = [@wsTemp()[0].children]
     , @siteNav()[0] # Set to first by default

     @getMappedData = (url, pin, map={}, cb=null) ->
@@ -46,10 +54,13 @@

     # Template accessor that avoids data-loading race
     @getTemplate = ko.computed =>
-      @siteActive().template ? {} # Needs .template?() if @siteNav is mapped
+      @siteActive()?.template ? {} # Needs .template?() if @siteNav is mapped

     # Preload data
-    @getMappedData "http://roush.propter.net:8080/nodes/1/tree", @wsItems, mapping
+    @getMappedData "http://roush.propter.net:8080/nodes/1/tree", @wsTemp, mapping, =>
+      console.log @wsTemp()
+      #@wsItems = [@wsTemp()[0].children]
+      #@siteActive @siteNav()[0]

     @ # Return ourself


LoopbackPromised contains classes with its static properties

--- src/loopback-promised.coffee
+++ src/loopback-promised.coffee
@@ -281,6 +281,9 @@
         POST   : 'post'
         HEAD   : 'head'

-LoopbackPromised.Promise = Promise
+LoopbackPromised.Promise               = Promise
+LoopbackPromised.LoopbackClient        = LoopbackClient
+LoopbackPromised.LoopbackUserClient    = LoopbackUserClient
+LoopbackPromised.LoopbackRelatedClient = LoopbackRelatedClient

 module.exports = LoopbackPromised


WIP: Clear history when loading from scratch, but not when reloading

--- src/text-buffer.coffee
+++ src/text-buffer.coffee
@@ -1707,8 +1707,6 @@

     @finishLoading(changeEvent, checkpoint, patch)

-    @setHistoryProvider(new HistoryShim())
-
   load: (options) ->
     unless options?.internal
       Grim.deprecate('The .load instance method is deprecated. Create a loaded buffer using TextBuffer.load(filePath) instead.')
@@ -1790,6 +1788,8 @@
       # TODO Remove Demeter violation
       @history.history.setTextInRange(start, end, @getText())

+      @setHistoryProvider(new HistoryShim())
+
     @loaded = true
     @emitter.emit('did-reload')
     this


Stars stop fading once level is complete

--- src/game/states/game.coffee
+++ src/game/states/game.coffee
@@ -57,6 +57,7 @@
     #Plot empty stars
     @player = @add.sprite x, y, 'player'
     @constellation = []
+    @levelcomplete = false
     @drawConstellation(LEVELS[0])

     #add sounds
@@ -81,7 +82,7 @@
      	 @xdistance = Math.abs(@player.x - star.x)
      	 @ydistance = Math.abs(@player.y - star.y)

-     	 if @xdistance < 20 && @ydistance < 20
+     	 if (@xdistance < 20 && @ydistance < 20) || @levelcomplete
         #if Player on star
      	       @game.add.tween(star).to({alpha:1},100,Phaser.Easing.Cubic.Out,true)
      	 else
@@ -93,6 +94,7 @@

      if won
      	@add.text(10, 10, "Congratulations! Level Complete!", { font: "15px Arial", fill: "#ff0044", align: "center" })
+     	@levelcomplete = true

      if @cursors.left.isDown
         @player.body.moveLeft(200)


Only fetch pkginfo for a given package once per invocation

--- src/generate-package.coffee
+++ src/generate-package.coffee
@@ -3,6 +3,7 @@
 semver = require 'semver'

 knownExprs = {}
+pkginfos = {}

 generatePackage = (name, range, callback) ->
   # Avoid dependency cycles
@@ -15,66 +16,73 @@
     knownExprs["#{name}-#{range}"] = str
     callback knownExprs

-  #!!! TODO: Handle the non npmjs.org case
-  http.get "http://registry.npmjs.org/#{name}", (res) ->
-    unless res.statusCode is 200
-      return error "Failed to get package info for #{name}: #{http.STATUS_CODES[res.statusCode]}"
-    res.on 'error', (err) ->
-      error "Error getting package info for #{name}: #{err}"
-    res.setEncoding "utf8"
-    readInfo = ->
-      json = res.read res.headers['content-length']
-      if json?
-        try
-          pkginfo = JSON.parse json
-        catch error
-          return error "Error parsing pkginfo for #{name} as JSON: #{error}"
-        unless pkginfo.versions? and pkginfo.versions instanceof Object
-          return error "Package info for #{name} has missing or improper versions field"
+  handleInfo = ->
+    pkginfo = pkginfos[name]
+    unless 'versions' of pkginfo and pkginfo.versions instanceof Object
+      return error "Package info for #{name} has missing or improper versions field"

-        versions = (key for own key, value of pkginfo.versions)
-        version = semver.maxSatisfying versions, range
-        unless version?
-          return error "Could not find version matching #{range} for #{name} in #{JSON.stringify versions}"
-        deps = pkginfo.versions[version].dependencies ? {}
-        unless deps instanceof Object
-          return error "Non-object dependencies field for version #{version} of #{name}"
-        patchLatest = false
-        for nm, rng of deps
-          if rng is 'latest'
-            deps[nm] = '*'
-            patchLatest = true
-        pkgCount = 1 # in the no dependency case, there is one package to add
-        finishedCallback = ->
-          pkgCount -= 1
-          if pkgCount is 0
-            callback knownExprs
-        for nm, rng of deps
-          unless knownExprs["#{nm}-#{rng}"]
-            pkgCount += 1
-            generatePackage nm, rng, finishedCallback
-        http.get "http://registry.npmjs.org/#{name}/-/#{name}-#{version}.tgz", (res) ->
-          unless res.statusCode is 200
-            return error "Failed to get tarball for #{name}-#{version}: #{http.STATUS_CODES[res.statusCode]}"
-          res.on 'error', (err) ->
-            error "Error getting tarball for #{name}-#{version}: #{err}"
-          hash = crypto.createHash 'sha256'
-          hash.on 'error', (err) ->
-            error "Error getting hash for #{name}-#{version}: #{err}"
-          readHash = ->
-            hashBuffer = hash.read 32
-            if hashBuffer?
-              knownExprs["#{name}-#{range}"] =
-                hash: hashBuffer
-                patchLatest: patchLatest
-                dependencies: deps
-                version: version
-                name: name
-              finishedCallback()
-          res.pipe hash
-          hash.on 'readable', readHash
-          readHash()
-    res.on 'readable', readInfo
-    readInfo()
+    versions = (key for own key, value of pkginfo.versions)
+    version = semver.maxSatisfying versions, range
+    unless version?
+      return error "Could not find version matching #{range} for #{name} in #{JSON.stringify versions}"
+    deps = pkginfo.versions[version].dependencies ? {}
+    unless deps instanceof Object
+      return error "Non-object dependencies field for version #{version} of #{name}"
+    patchLatest = false
+    for nm, rng of deps
+      if rng is 'latest'
+        deps[nm] = '*'
+        patchLatest = true
+    pkgCount = 1 # in the no dependency case, there is one package to add
+    finishedCallback = ->
+      pkgCount -= 1
+      if pkgCount is 0
+        callback knownExprs
+    for nm, rng of deps
+      unless knownExprs["#{nm}-#{rng}"]
+        pkgCount += 1
+        generatePackage nm, rng, finishedCallback
+    http.get "http://registry.npmjs.org/#{name}/-/#{name}-#{version}.tgz", (res) ->
+      unless res.statusCode is 200
+        return error "Failed to get tarball for #{name}-#{version}: #{http.STATUS_CODES[res.statusCode]}"
+      res.on 'error', (err) ->
+        error "Error getting tarball for #{name}-#{version}: #{err}"
+      hash = crypto.createHash 'sha256'
+      hash.on 'error', (err) ->
+        error "Error getting hash for #{name}-#{version}: #{err}"
+      readHash = ->
+        hashBuffer = hash.read 32
+        if hashBuffer?
+          knownExprs["#{name}-#{range}"] =
+            hash: hashBuffer
+            patchLatest: patchLatest
+            dependencies: deps
+            version: version
+            name: name
+          finishedCallback()
+      res.pipe hash
+      hash.on 'readable', readHash
+      readHash()
+
+  if name of pkginfos
+    handleInfo()
+  else
+    #!!! TODO: Handle the non npmjs.org case
+    http.get "http://registry.npmjs.org/#{name}", (res) ->
+      unless res.statusCode is 200
+        return error "Failed to get package info for #{name}: #{http.STATUS_CODES[res.statusCode]}"
+      res.on 'error', (err) ->
+        error "Error getting package info for #{name}: #{err}"
+      res.setEncoding "utf8"
+      readInfo = ->
+        json = res.read res.headers['content-length']
+        if json?
+          try
+            pkginfos[name] = JSON.parse json
+          catch error
+            return error "Error parsing pkginfo for #{name} as JSON: #{error}"
+          handleInfo()
+      res.on 'readable', readInfo
+      readInfo()

 module.exports = generatePackage


Ignore clicks on contenteditable links

Ignore clicks on contenteditable links

--- src/turbolinks/controller.coffee
+++ src/turbolinks/controller.coffee
@@ -196,6 +196,7 @@
   clickEventIsSignificant: (event) ->
     not (
       event.defaultPrevented or
+      event.target.isContentEditable or
       event.which > 1 or
       event.altKey or
       event.ctrlKey or


Fix issue with undefined metadata

Fix issue with undefined metadata

--- src/notification.coffee
+++ src/notification.coffee
@@ -47,7 +47,7 @@
 	    	IPVersion: req.connection?.address()?.family

 	   @events[0].context ||= req.url
-	   @events[0].userId ||= req.headers["x-forwarded-for"] || req.connection?.remoteAddress
+	   @events[0].userId ||= req?.headers?["x-forwarded-for"] || req.connection?.remoteAddress

 	deliver: (cb) ->
 		cb = null unless Utils.typeOf(cb) == "function"


Clarify where this is available

Clarify where this is available

--- src/null-grammar.coffee
+++ src/null-grammar.coffee
@@ -1,6 +1,6 @@
 Grammar = require './grammar'

-# A grammar with no patterns that is always available
+# A grammar with no patterns that is always available from a {GrammarRegistry}
 module.exports =
 class NullGrammar extends Grammar
   constructor: (registry) ->


Close button now closes panels (and not only stops the process)

--- lib/Components/CancelButton.coffee
+++ lib/Components/CancelButton.coffee
@@ -4,7 +4,7 @@
 exports.create = (_debugger) ->

   cancel = () ->
-    _debugger.processManager.cleanup()
+    _debugger.cleanup()

   hg.state({
     channels: {


Fix expected position replacing selection linewise

The cursor should be placed after the inserted text, not at the start
of it.

--- spec/operators-spec.coffee
+++ spec/operators-spec.coffee
@@ -571,7 +571,7 @@

       it "replaces the current selection", ->
         expect(editor.getText()).toBe "0 345\n2"
-        expect(editor.getCursorScreenPosition()).toEqual [0, 2]
+        expect(editor.getCursorScreenPosition()).toEqual [1, 0]

     describe "with linewise contents", ->
       beforeEach ->


Mock setImmediate to fix specs with react editor enabled

--- spec/bookmarks-view-spec.coffee
+++ spec/bookmarks-view-spec.coffee
@@ -4,6 +4,7 @@
   [editorView, editor, displayBuffer] = []

   beforeEach ->
+    spyOn(window, 'setImmediate').andCallFake (fn) -> fn()
     atom.workspaceView = new WorkspaceView
     atom.workspace = atom.workspaceView.model
     atom.workspaceView.openSync('sample.js')


:green_heart: Fix tests

:green_heart: Fix tests

--- spec/bookmarks-view-spec.coffee
+++ spec/bookmarks-view-spec.coffee
@@ -8,6 +8,13 @@
     values(editor.decorationsStateForScreenRowRange(0, editor.getLastScreenRow()))
       .filter (decoration) -> decoration.properties.class is 'bookmarked'
       .map (decoration) -> decoration.screenRange
+
+  getBookmarkedLineNodes = (editorElement) ->
+    # TODO: Remove this after light DOM for editors ships on stable.
+    if editorElement.lightDOM
+      editorElement.querySelectorAll('.bookmarked')
+    else
+      editorElement.shadowRoot.querySelectorAll('.bookmarked')

   beforeEach ->
     spyOn(window, 'setImmediate').andCallFake (fn) -> fn()
@@ -162,13 +169,13 @@

     it "toggles proper classes on proper gutter row", ->
       editor.setCursorBufferPosition([3, 10])
-      expect(editorElement.shadowRoot.querySelectorAll('.bookmarked').length).toBe 0
+      expect(getBookmarkedLineNodes(editorElement).length).toBe 0

       atom.commands.dispatch editorElement, 'bookmarks:toggle-bookmark'
       lines = []

       waitsFor ->
-        lines = editorElement.shadowRoot.querySelectorAll('.bookmarked')
+        lines = getBookmarkedLineNodes(editorElement)
         lines.length is 1

       runs ->
@@ -176,27 +183,27 @@
         atom.commands.dispatch editorElement, 'bookmarks:toggle-bookmark'

       waitsFor ->
-        editorElement.shadowRoot.querySelectorAll('.bookmarked').length is 0
+        getBookmarkedLineNodes(editorElement).length is 0

     it "clears all bookmarks", ->
       editor.setCursorBufferPosition([3, 10])
       atom.commands.dispatch editorElement, 'bookmarks:toggle-bookmark'

       waitsFor ->
-        editorElement.shadowRoot.querySelectorAll('.bookmarked').length is 1
+        getBookmarkedLineNodes(editorElement).length is 1

       runs ->
         editor.setCursorBufferPosition([5, 0])
         atom.commands.dispatch editorElement, 'bookmarks:toggle-bookmark'

       waitsFor ->
-        editorElement.shadowRoot.querySelectorAll('.bookmarked').length is 2
+        getBookmarkedLineNodes(editorElement).length is 2

       runs ->
         atom.commands.dispatch editorElement, 'bookmarks:clear-bookmarks'

       waitsFor ->
-        editorElement.shadowRoot.querySelectorAll('.bookmarked').length is 0
+        getBookmarkedLineNodes(editorElement).length is 0

   describe "when a bookmark is invalidated", ->
     it "creates a marker when toggled", ->


Access package manager methods via atom.packages

--- spec/tree-view-spec.coffee
+++ spec/tree-view-spec.coffee
@@ -54,8 +54,8 @@
     describe "when the project has no path", ->
       beforeEach ->
         atom.project.setPath(undefined)
-        atom.deactivatePackage("tree-view")
-        treeView = atom.activatePackage("tree-view").mainModule.createView()
+        atom.packages.deactivatePackage("tree-view")
+        treeView = atom.packages.activatePackage("tree-view").mainModule.createView()

       it "does not attach to the root view or create a root node when initialized", ->
         expect(treeView.hasParent()).toBeFalsy()
@@ -79,16 +79,16 @@

     describe "when the root view is opened to a file path", ->
       it "does not attach to the root view but does create a root node when initialized", ->
-        atom.deactivatePackage("tree-view")
+        atom.packages.deactivatePackage("tree-view")
         atom.packages.packageStates = {}
         atom.rootView.openSync('tree-view.js')
-        treeView = atom.activatePackage("tree-view").mainModule.createView()
+        treeView = atom.packages.activatePackage("tree-view").mainModule.createView()
         expect(treeView.hasParent()).toBeFalsy()
         expect(treeView.root).toExist()

     describe "when the root view is opened to a directory", ->
       it "attaches to the root view", ->
-        treeView = atom.activatePackage("tree-view").mainModule.createView()
+        treeView = atom.packages.activatePackage("tree-view").mainModule.createView()
         expect(treeView.hasParent()).toBeTruthy()
         expect(treeView.root).toExist()

@@ -97,9 +97,9 @@
         dotGit = path.join(temp.mkdirSync('repo'), '.git')
         fs.makeTreeSync(dotGit)
         atom.project.setPath(dotGit)
-        atom.deactivatePackage("tree-view")
+        atom.packages.deactivatePackage("tree-view")
         atom.packages.packageStates = {}
-        {treeView} = atom.activatePackage("tree-view").mainModule
+        {treeView} = atom.packages.activatePackage("tree-view").mainModule
         expect(treeView).toBeFalsy()

   describe "serialization", ->


Add a failing test spec for #705

Add a failing test spec for #705

--- spec/tree-view-spec.coffee
+++ spec/tree-view-spec.coffee
@@ -2354,6 +2354,11 @@
       iotaDirPath = path.join(lambdaDirPath, "iota")
       kappaDirPath = path.join(lambdaDirPath, "kappa")

+      muDirPath = path.join(rootDirPath, "mu")
+      nuDirPath = path.join(muDirPath, "nu")
+      xiDirPath1 = path.join(muDirPath, "xi")
+      xiDirPath2 = path.join(nuDirPath, "xi")
+
       fs.makeTreeSync(zetaDirPath)
       fs.writeFileSync(zetaFilePath, "doesn't matter")

@@ -2369,6 +2374,11 @@
       fs.makeTreeSync(lambdaDirPath)
       fs.makeTreeSync(iotaDirPath)
       fs.makeTreeSync(kappaDirPath)
+
+      fs.makeTreeSync(muDirPath)
+      fs.makeTreeSync(nuDirPath)
+      fs.makeTreeSync(xiDirPath1)
+      fs.makeTreeSync(xiDirPath2)

       atom.project.setPaths([rootDirPath])

@@ -2410,6 +2420,16 @@
           element.innerText

         expect(lambdaEntries).toEqual(["iota", "kappa"])
+
+      describe "when a directory is reloaded", ->
+        it "squashes the directory names the last of which is same as an unsquashed directory", ->
+          muDir = $(treeView.roots[0].entries).find('.directory:contains(mu):first')
+          muDir[0].expand()
+          muDir[0].reload()
+          muEntries = [].slice.call(muDir[0].children[1].children).map (element) ->
+            element.innerText
+
+          expect(muEntries).toEqual(["nu#{path.sep}xi", "xi"])

   describe "Git status decorations", ->
     [projectPath, modifiedFile, originalFileContent] = []


early fall animation

early fall animation

--- web/scripts/gameVisual.coffee
+++ web/scripts/gameVisual.coffee
@@ -1,6 +1,7 @@
 class window.GameVisual
     ticker = 0 #counts the number of frames that have passed
     imgArray = [] #array of arrays of preloaded images
+    univImg = [] #array of universally used images
     objArray = [] #array of objects that permeate the game board
     frameClock = null #reference to the setInterval object that runs the game
     lyr1 = null #object for referencing the first of the two canvas objects to be used as a frame
@@ -55,6 +56,7 @@
     #preload all the necessary images.  Sounds will eventually be included in here as well.
     ###
     initResources: (config) ->
+        univImg[0] = "img/x.png"
         tmp = []
         for key,imgar of config
             tmp = []
@@ -125,6 +127,7 @@
     #   < 3 4 1 >
     #       2
     #       v
+    #   5 -> Falling Animation
     #More documenation to be added when the code is more concrete and permanent
     ###
     class charObj
@@ -132,6 +135,7 @@
             @antickerAdd = 0
             @ldir = @dir
             @cstate = 4
+            @fallticker = 0
             return

         absPos: (@xpos,@ypos) ->
@@ -143,12 +147,27 @@
             return

         current: (anticker) ->
+            objState = []
             num = 0
             if @animated and ((anticker + @antickerAdd) % (2 * ar)) >= ar
                 num = 1
             num = num + (2 * @dir)
             num = num % @animarray.length
-            return @animarray[num]
+
+            objState[0] = @animarray[num]
+            objState[1] = @xSize
+            objState[2] = @ySize
+
+            if @cstate == 5
+                @fallticker++
+                objState[1] = @xSize/(@fallticker)
+                objState[2] = @ySize/(@fallticker)
+                if @fallticker > 20
+                    objState[0] = univImg[0]
+            else
+                @fallticker = 0
+
+            return objState

         imFace: (@dir) ->
             return
@@ -198,8 +217,8 @@
     drawChar: (frame) ->
         td = frame.getContext('2d')
         for obj in objArray by -1
-            img = obj.current(@ticker)
-            td.drawImage(img,obj.xpos+obj.xOff,obj.ypos+obj.yOff,obj.xSize,obj.ySize)
+            s = obj.current(@ticker)
+            td.drawImage(s[0],obj.xpos+obj.xOff,obj.ypos+obj.yOff,s[1],s[2])
         return

     chckMv: (config) ->


add userHasVotedFor to band schema

add userHasVotedFor to band schema

--- models/band.coffee
+++ models/band.coffee
@@ -16,6 +16,9 @@
     type: Schema.ObjectId
     ref: 'Vote'
   ]
+  userHasVotedFor:
+    type: Boolean
+    default: false
   vote_count:
     type: Number
     default: 0


yay new teams!

yay new teams!

--- models/team.coffee
+++ models/team.coffee
@@ -41,7 +41,7 @@
 TeamSchema.static 'canRegister', (next) ->
   Team.count {}, (err, count) ->
     return next err if err
-    max = 320 + 1 # +1 because team fortnight labs doesn't count
+    max = 325 + 1 # +1 because team fortnight labs doesn't count
     next null, count < max, max - count

 TeamSchema.static 'uniqueName', (name, next) ->


changed the cryptic messages #53

changed the cryptic messages #53

--- models/team.coffee
+++ models/team.coffee
@@ -282,7 +282,7 @@
 TeamSchema.pre 'save', (next) ->
   if @peopleIds.length + @emails.length == 0
     error = new mongoose.Document.ValidationError this
-    error.errors.emails = 'min'
+    error.errors.emails = 'You have to insert at least one email contact for your team'
     next error
   else
     next()
@@ -296,7 +296,7 @@
       next()
     else
       error = new mongoose.Document.ValidationError this
-      error.errors._base = 'max'
+      error.errors._base = 'We have reached the maximum amount of teams at the moment'
       next error

 ## unique name
@@ -308,7 +308,7 @@
       next()
     else
       error = new mongoose.Document.ValidationError this
-      error.errors.name = 'unique'
+      error.errors.name = 'This team name is already in use'
       next error

 ## unique slug


zooming, panning stuff

zooming, panning stuff

--- docs/coffee/vis.coffee
+++ docs/coffee/vis.coffee
@@ -87,15 +87,20 @@
     tx = translation[0]
     ty = translation[1]

-    if translation[0] < -600
-      tx = -600
-    else if translation[0] > 1100
-      tx = 1100
-
-    if translation[1] < -400
-      ty = -400
-    else if translation[1] > 500
-      ty = 500
+    minX = -600 * d3.event.scale
+    maxX = 1100 * d3.event.scale
+    minY = -400 * d3.event.scale
+    maxY = 800 * d3.event.scale
+
+    if translation[0] < minX
+      tx = minX
+    else if translation[0] > maxX
+      tx = maxX
+
+    if translation[1] < minY
+      ty = minY
+    else if translation[1] > maxY
+      ty = maxY
     child.attr 'transform', 'translate(' + [tx, ty] + ')' + ' scale(' + d3.event.scale + ')'
     return


Fixing silly mistake with the comments.

Fixing silly mistake with the comments.

--- nodes.coffee
+++ nodes.coffee
@@ -542,7 +542,7 @@
       join:   "\n" if (prop is last_noncom) or (prop instanceof CommentNode)
       join:   '' if i is @properties.length - 1
       indent: if prop instanceof CommentNode then '' else @idt 1
-      prop:   new AssignNode prop, prop, 'object' unless prop instanceof AssignNode
+      prop:   new AssignNode prop, prop, 'object' unless prop instanceof AssignNode or prop instanceof CommentNode
       indent + prop.compile(o) + join
     props: props.join('')
     inner: if props then '\n' + props + '\n' + @idt() else ''


Issue #1547 'use strict' duplicate formal parameter are prohibited

updated error message (thanks @davidchambers)

code style fixes

--- nodes.coffee
+++ nodes.coffee
@@ -1162,8 +1162,10 @@
     o.scope.shared  = del(o, 'sharedScope')
     o.indent        += TAB
     delete o.bare
-    vars   = []
-    exprs  = []
+    params     = []
+    exprs      = []
+    for name in @paramNames() # this step must be performed before the others
+      unless o.scope.check name then o.scope.parameter name
     for param in @params when param.splat
       o.scope.add p.name.value, 'var', yes for p in @params when p.name.value
       splats = new Assign new Value(new Arr(p.asReference o for p in @params)),
@@ -1180,11 +1182,15 @@
           lit = new Literal ref.name.value + ' == null'
           val = new Assign new Value(param.name), param.value, '='
           exprs.push new If lit, val
-      vars.push ref unless splats
+      params.push ref unless splats
     wasEmpty = @body.isEmpty()
     exprs.unshift splats if splats
     @body.expressions.unshift exprs... if exprs.length
-    o.scope.parameter vars[i] = v.compile o for v, i in vars unless splats
+    o.scope.parameter params[i] = p.compile o for p, i in params
+    uniqs = []
+    for name in @paramNames()
+      throw SyntaxError "multiple parameters named '#{name}'" if name in uniqs
+      uniqs.push name
     @body.makeReturn() unless wasEmpty or @noReturn
     if @bound
       if o.scope.parent.method?.bound
@@ -1194,11 +1200,17 @@
     idt   = o.indent
     code  = 'function'
     code  += ' ' + @name if @ctor
-    code  += '(' + vars.join(', ') + ') {'
+    code  += '(' + params.join(', ') + ') {'
     code  += "\n#{ @body.compileWithDeclarations o }\n#{@tab}" unless @body.isEmpty()
     code  += '}'
     return @tab + code if @ctor
     if @front or (o.level >= LEVEL_ACCESS) then "(#{code})" else code
+
+  # A list of parameter names, excluding those generated by the compiler.
+  paramNames: ->
+    names = []
+    names.push param.names()... for param in @params
+    names

   # Short-circuit `traverseChildren` method to prevent it from crossing scope boundaries
   # unless `crossScope` is `true`.
@@ -1223,7 +1235,8 @@
     node = @name
     if node.this
       node = node.properties[0].name
-      node = new Literal '_' + node.value if node.value.reserved
+      if node.value.reserved
+        node = new Literal o.scope.freeVariable node.value
     else if node.isComplex()
       node = new Literal o.scope.freeVariable 'arg'
     node = new Value node
@@ -1232,6 +1245,36 @@

   isComplex: ->
     @name.isComplex()
+
+  # Finds the name or names of a `Param`; useful for detecting duplicates.
+  # In a sense, a destructured parameter represents multiple JS parameters,
+  # thus this method returns an `Array` of names.
+  # Reserved words used as param names, as well as the Object and Array
+  # literals used for destructured params, get a compiler generated name
+  # during the `Code` compilation step, so this is necessarily an incomplete
+  # list of a parameter's names.
+  names: (name = @name)->
+    atParam = (obj) ->
+      {value} = obj.properties[0].name
+      return if value.reserved then [] else [value]
+    # * simple literals `foo`
+    return [name.value] if name instanceof Literal
+    # * at-params `@foo`
+    return atParam(name) if name instanceof Value
+    names = []
+    for obj in name.objects
+      # * assignments within destructured parameters `{foo:bar}`
+      if obj instanceof Assign
+        names.push obj.variable.base.value
+      # * destructured parameters within destructured parameters `[{a}]`
+      else if obj.isArray() or obj.isObject()
+        names.push @names(obj.base)...
+      # * at-params within destructured parameters `{@foo}`
+      else if obj.this
+        names.push atParam(obj)...
+      # * simple destructured parameters {foo}
+      else names.push obj.base.value
+    names

 #### Splat


Fixing Issue #509. Double-evaluation for function calls within IndexNodes as the left-hand-assignment part of a compound-assignment operation.

--- nodes.coffee
+++ nodes.coffee
@@ -62,15 +62,18 @@
   # in multiple places, ensure that the expression is only ever evaluated once,
   # by assigning it to a temporary variable.
   compileReference: (o, options) ->
+    options or= {}
     pair = if not ((this instanceof CallNode or @contains((n) -> n instanceof CallNode)) or
                   (this instanceof ValueNode and (not (@base instanceof LiteralNode) or @hasProperties())))
       [this, this]
+    else if this instanceof ValueNode and options.assignment
+      this.cacheIndexes(o)
     else
       reference = literal o.scope.freeVariable()
       compiled  = new AssignNode reference, this
       [compiled, reference]
-    return pair unless options and options.precompile
-    [pair[0].compile(o), pair[1].compile(o)]
+    return [pair[0].compile(o), pair[1].compile(o)] if options.precompile
+    pair

   # Convenience method to grab the current indentation level, plus tabbing in.
   idt: (tabs) ->
@@ -335,6 +338,18 @@
     node = o.chainRoot.base or o.chainRoot.variable
     while node instanceof CallNode then node = node.variable
     node is this
+
+  # If the value node has indexes containing function calls, and the value node
+  # needs to be used twice, in compound assignment ... then we need to cache
+  # the value of the indexes.
+  cacheIndexes: (o) ->
+    copy = new ValueNode @base, @properties.slice 0
+    for prop, i in copy.properties
+      if prop instanceof IndexNode and prop.contains((n) -> n instanceof CallNode)
+        [index, indexVar] = prop.index.compileReference o
+        this.properties[i] = new IndexNode index
+        copy.properties[i] = new IndexNode indexVar
+    [this, copy]

   # Override compile to unwrap the value when possible.
   compile: (o) ->
@@ -1094,10 +1109,11 @@
   # operands are only evaluated once, even though we have to reference them
   # more than once.
   compileAssignment: (o) ->
-    [first, second] = [@first.compile(o), @second.compile(o)]
+    [first, firstVar] = @first.compileReference o, precompile: yes, assignment: yes
+    second = @second.compile o
     o.scope.find(first) if first.match(IDENTIFIER)
-    return "#first = #{ ExistenceNode.compileTest(o, @first) } ? #first : #second" if @operator is '?='
-    "#first = #first #{ @operator.substr(0, 2) } #second"
+    return "#first = #{ ExistenceNode.compileTest(o, @first) } ? #firstVar : #second" if @operator is '?='
+    "#first = #firstVar #{ @operator.substr(0, 2) } #second"

   # If this is an existence operator, we delegate to `ExistenceNode.compileTest`
   # to give us the safe references for the variables.


made `[a..., b...] = c` throw syntax error

--- nodes.coffee
+++ nodes.coffee
@@ -806,6 +806,10 @@
         val   = new Literal utility('slice') + ".call(#{valVar}, #{i}#{rest})"
         splat = "#{ivar} < #{i} ? #{ivar} = #{i} : #{ivar}++"
       else
+        if obj instanceof Splat
+          obj = obj.name.compile o
+          throw SyntaxError \
+            "multiple splats are disallowed in an assignment: #{obj} ..."
         if typeof idx is 'number'
           idx = new Literal splat or idx
           acc = no


another possible edge case undefined issue...

--- src/smc-webapp/course/students_panel.cjsx
+++ src/smc-webapp/course/students_panel.cjsx
@@ -284,7 +284,7 @@
                 x.first_name = user?.get('first_name') ? ''
                 x.last_name  = user?.get('last_name') ? ''
                 if x.project_id?
-                    x.last_active = @props.redux.getStore('projects').get_last_active(x.project_id)?.get(x.account_id)?.getTime()
+                    x.last_active = @props.redux.getStore('projects').get_last_active(x.project_id)?.get(x.account_id)?.getTime?()
                     upgrades = @props.redux.getStore('projects').get_total_project_quotas(x.project_id)
                     if upgrades?
                         x.hosting = upgrades.member_host


Remove this keystroke handling

Remove this keystroke handling

--- fmc/fmc-webui/src/main/webapp/app/controllers/controls/dialog.coffee
+++ fmc/fmc-webui/src/main/webapp/app/controllers/controls/dialog.coffee
@@ -116,17 +116,6 @@
         @accept_btn.removeClass("primary")
         @cancel_btn.addClass("primary")
         @cancel_btn.addClass("secondary")
-        $("body").unbind("keyup")
-        $("body").keyup (event) =>
-          if @dialog.is(":visible")
-            if event.which == 13
-              @do_hide()
-            else if event.which == 27
-              @do_hide()
-            else
-              true
-          else
-            false

   window.FON.ConfirmationDialog = ConfirmationDialog


Bring tests back.

Bring tests back.

--- test/postprocessor.test.coffee
+++ test/postprocessor.test.coffee
@@ -19,7 +19,7 @@
     it 'should export Postprocessor class', () ->
         expect(Postprocessor).to.be.a('function')

-    it.only 'should wrap parser', () ->
+    it 'should wrap parser', () ->
         spy = sinon.spy(parser, 'parse')
         postprocessor = new Postprocessor(parser)


Get 100% mediator coverage.

Get 100% mediator coverage.

--- test/spec/mediator_spec.coffee
+++ test/spec/mediator_spec.coffee
@@ -46,3 +46,13 @@
       mediator.publish eventName, payload

       expect(spy).was.neverCalledWith payload
+
+    it 'should support sealing itself', ->
+      expect(mediator.seal).to.be.a 'function'
+      old = Object.seal
+      Object.seal = undefined
+      mediator.seal()
+      expect(-> mediator.a = 1; delete mediator.a).to.not.throwError()
+      Object.seal = old
+      mediator.seal()
+      expect(-> mediator.a = 1).to.throwError()


bug fix in scaffold generator

bug fix in scaffold generator

--- src/steroids/generators/DataModule.coffee
+++ src/steroids/generators/DataModule.coffee
@@ -48,7 +48,7 @@

       # TODO: should structure differently
       if @scriptExt != null
-        @do_generation(resolve)
+        @do_generation(resolve, reject)

       else
         inquirer = require "inquirer"
@@ -74,9 +74,9 @@
         inquirer.prompt promptList, (answers) =>
           @scriptExt = answers.scriptExt

-          @do_generation(resolve)
+          @do_generation(resolve, reject)

-  do_generation: (resolve) ->
+  do_generation: (resolve, reject) ->
     #TODO: should be Resource.forName but Resource cannot require Provider if Provider requires Resource
     Provider.resourceForName(@resourceName).then (resource)=>