omg, a job responds in a test

omg, a job responds in a test

--- test/integration/get-device-spec.coffee
+++ test/integration/get-device-spec.coffee
@@ -8,6 +8,7 @@

 describe 'MeshbluCoreDispatcher', ->
   beforeEach ->
+    @meshbluHttp = new MeshbluHttp()
     redisUri = process.env.REDIS_URI
     @dispatcher = new TestDispatcher

@@ -22,36 +23,17 @@

   describe 'GetDevice', ->
-    # @timeout 5000
+    beforeEach 'register devices', (done) ->
+      @meshbluHttp.register type: 'device:auth', (error, @authDevice) =>
+        discovererDeviceData = type: 'device:discoverer', discoverAsWhitelist: [@authDevice.uuid]
+        @meshbluHttp.register discovererDeviceData, (error, @discovererDevice) =>
+          discovereeDeviceData = type: 'device:discoveree', discoverWhitelist: [@discovererDevice.uuid]
+          @meshbluHttp.register discovereeDeviceData, (error, @discovererDevice) => done()
+
     beforeEach (done) ->
-      # console.log "meshbluHttp", @meshbluHttp
-      openParams =
-        discoverWhitelist: ['*']
-        configureWhitelist: ['*']
-        receiveWhitelist: ['*']
-        sendWhitelist: ['*']
-
-      @authData = _.defaults type: 'device:auth-device', openParams
-      @discovererData = _.extend {}, @authData, type: "device:device-discoverer"
-      @discovereeData = _.extend {}, @authData, type: "device:device-discoveree"
-
-      @meshbluHttp = new MeshbluHttp({
-        server: "meshblu.octoblu.com"
-        port: 443
-      })
-
-      @meshbluHttp.register @authData, (error, credentials) =>
-        console.log 'Register:authdevice', error, @authCredentials
-
-      @meshbluHttp.register @discovererData, (error, @deviceDiscoverer) =>
-        console.log 'Register:device-discoverer', error, @deviceDiscoverer
-
-      @meshbluHttp.register @discovereeData, (error, @deviceDiscoveree) =>
-        console.log 'Register:device-discoveree', error, @deviceDiscoveree
-
       auth =
-        uuid: 'hi'
-        token: 'earth'
+        uuid: @authDevice.uuid
+        token: @authDevice.token

       job =
         metadata:


color for loading message

color for loading message

--- lib/query_runner.coffee
+++ lib/query_runner.coffee
@@ -78,9 +78,8 @@
       sass = "[DEVELOPMENT] #{sass}"

     params =
-      text: sass
       as_user: true
-      attachments: [] # Override some Botkit stuff
+      attachments: [{text: sass, color: "#64518A", unfurl_links: false, unfurl_media: false}]
       unfurl_links: false
       unfurl_media: false


Identação, ordem de algumas coisas

Identação, ordem de algumas coisas

--- src/coffee/portal-sku-selector.coffee
+++ src/coffee/portal-sku-selector.coffee
@@ -23,6 +23,15 @@
 		warning: '.skuselector-warning'
 		warnUnavailable: '.skuselector-warn-unavailable'

+
+#
+# DUST FILTERS
+#
+_.extend dust.filters,
+	sanitize: (value) -> _.sanitize value
+	spacesToHyphens: (value) -> _.spacesToHyphens value
+
+
 #
 # CLASSES
 #
@@ -35,14 +44,14 @@

 		i = 0
 		@dimensions = ({
-		index: i++
-		name: dimensionName
-		nameClass: ""
-		values: productData.dimensionsMap[dimensionName]
-		availableValues: (true for value in productData.dimensionsMap[dimensionName])
-		validValues: (true for value in productData.dimensionsMap[dimensionName])
-		selected: undefined
-		inputType: productData.dimensionsInputType?[dimensionName]?.toLowerCase() || "radio"
+			index: i++
+			name: dimensionName
+			nameClass: ""
+			values: productData.dimensionsMap[dimensionName]
+			availableValues: (true for value in productData.dimensionsMap[dimensionName])
+			validValues: (true for value in productData.dimensionsMap[dimensionName])
+			selected: undefined
+			inputType: productData.dimensionsInputType?[dimensionName]?.toLowerCase() || "radio"
 		} for dimensionName in productData.dimensions)
 		dim.radio = (dim.inputType == "radio") for dim in @dimensions
 		dim.combo = (dim.inputType == "combo") for dim in @dimensions
@@ -129,7 +138,6 @@
 		# Build selectors from given select strings.
 		@select = _.mapObj $.skuSelector.selectors, (key, val) =>
 			( => $(val, @context) )
-		console.log @select

 		@select.inputs = => $('input, select', @context)
 		@select.itemDimension = (dimensionName) => $(".item-dimension-#{_.sanitize(dimensionName)}", @context)
@@ -144,7 +152,7 @@
 	# Renders the DOM elements of the Sku Selector
 	render: =>
 		dust.render "sku-selector", @data, (err, out) =>
-			console.log err if err
+			console.log "Sku Selector Dust error: ", err if err
 			@context.html out
 			@update()
 			@hideProductImage() unless @options.showProductImage
@@ -389,13 +397,6 @@

 #
-# LIQUID HELPERS
-#
-_.extend dust.filters,
-	sanitize: (value) -> _.sanitize value
-	spacesToHyphens: (value) -> _.spacesToHyphens value
-
-#
 # PLUGIN DEFAULTS
 #
 $.fn.skuSelector.defaults =


Use fat arrow when necessary

Use fat arrow when necessary

--- src/menu-manager.coffee
+++ src/menu-manager.coffee
@@ -46,10 +46,10 @@
   merge: (menu, item) ->
     item = _.deepClone(item)

-    if item.submenu? and match = _.find(menu, (i) -> i.submenu? and @normalizeLabel(i.label) == @normalizeLabel(item.label))
+    if item.submenu? and match = _.find(menu, (i) => i.submenu? and @normalizeLabel(i.label) == @normalizeLabel(item.label))
       @merge(match.submenu, i) for i in item.submenu
     else
-      menu.push(item) unless _.find(menu, (i) -> @normalizeLabel(i.label) == @normalizeLabel(item.label))
+      menu.push(item) unless _.find(menu, (i) => @normalizeLabel(i.label) == @normalizeLabel(item.label))

   # Private: OSX can't handle displaying accelerators for multiple keystrokes.
   # If they are sent across, it will stop processing accelerators for the rest


Make logger visible to all microservice methods

--- src/microservice.coffee
+++ src/microservice.coffee
@@ -42,6 +42,7 @@
       environment = process.env

     @config = @environmentToConfig environment
+    @log = @setupLogger()
     @express = @setupExpress()
     @srv = null
     @db = null
@@ -379,7 +380,7 @@
   setupExpress: () ->

     exp = express()
-    exp.log = @setupLogger()
+    exp.log = @log.child component: "express"

     exp.use @requestTimer
     exp.use @requestLogger


Order model updated

Order model updated

--- src/models/order.coffee
+++ src/models/order.coffee
@@ -1,7 +1,14 @@
 module.exports = (sequelize, DataTypes) ->

   sequelize.define 'Order',
-    first_name: DataTypes.STRING
-    last_name: DataTypes.STRING
-    item_title: DataTypes.STRING
-    total_price: DataTypes.STRING
+      bid: DataTypes.FLOAT.UNSIGNED
+      ask: DataTypes.FLOAT.UNSIGNED
+      value:
+        type: DataTypes.FLOAT.UNSIGNED
+        allowNull: false
+        validate:
+          notNull: true
+    , validate:
+        isCorrectOrder: ->
+          if @bid? is @ask?
+            throw new Error 'Order must be bid or ask (but not both)'


Use process.resourcesPath to find Atom shell root

--- src/module-cache.coffee
+++ src/module-cache.coffee
@@ -142,7 +142,7 @@
   else
     cache.builtins.atom = path.join(cache.resourcePath, 'exports', 'atom.js')

-  atomShellRoot = path.resolve(window.location.pathname, '..', '..', '..', 'atom')
+  atomShellRoot = path.join(process.resourcesPath, 'atom')

   commonRoot = path.join(atomShellRoot, 'common', 'api', 'lib')
   commonBuiltins = ['callbacks-registry', 'clipboard', 'crash-reporter', 'screen', 'shell']


fixed delete button always showing up, and adding disabled state for save button

--- src/components/task-plan/reading.cjsx
+++ src/components/task-plan/reading.cjsx
@@ -114,19 +114,19 @@
 ReadingFooter = React.createClass
   render: ->
     classes = []
-    classes.push('disabled') unless @props.onPublish
+    classes.push('disabled') unless @props.onSave
     classes = classes.join(' ')

     if @props.onPublish
-      publishButton = <BS.Button bsStyle="primary" className={classes} onClick={@props.onPublish}>Publish</BS.Button>
+      publishButton = <BS.Button bsStyle="primary" onClick={@props.onPublish}>Publish</BS.Button>
       saveStyle = "link"
     else
       saveStyle = "primary"

     if @props.onDelete
       deleteLink = <BS.Button bsStyle="link" onClick={@props.onDelete}>Delete</BS.Button>
-    if @props.onSave
-      saveLink = <BS.Button bsStyle={saveStyle} onClick={@props.onSave}>Save as Draft</BS.Button>
+
+    saveLink = <BS.Button bsStyle={saveStyle} className={classes} onClick={@props.onSave}>Save as Draft</BS.Button>

     <span>
       {publishButton}
@@ -153,14 +153,6 @@
     store: TaskPlanStore
     actions: TaskPlanActions

-  update: ->
-    id = @getId()
-    plan = TaskPlanStore.get(id)
-    if plan and id isnt plan.id
-      @setState({id: plan.id})
-    else
-      @setState({})
-
   setDueAt: (value) ->
     id = @getId()
     TaskPlanActions.updateDueAt(id, value)
@@ -173,7 +165,8 @@
   savePlan: ->
     id = @getId()
     TaskPlanActions.save(id)
-    @transitionTo('editReading', {id})
+    newPlanId = TaskPlanStore.get(id).id
+    @transitionTo('editReading', {id : newPlanId})

   publishPlan: ->
     id = @getId()
@@ -193,7 +186,7 @@
     saveable = plan?.title and plan?.due_at and plan?.settings?.page_ids?.length > 0
     publishable = saveable and not TaskPlanStore.isChanged(id)
     deleteable = not TaskPlanStore.isNew(id)
-    headerText = if id then 'Edit Reading' else 'Add Reading'
+    headerText = if TaskPlanStore.isNew(id) then 'Add Reading' else 'Edit Reading'
     topics = TaskPlanStore.getTopics(id)

     footer= <ReadingFooter


Simplify conditional

Simplify conditional

--- resources/assets/coffee/react/profile-page/main.coffee
+++ resources/assets/coffee/react/profile-page/main.coffee
@@ -129,11 +129,8 @@
   render: =>
     withMePage = @state.userPage.initialRaw.trim() != '' || @props.withEdit

-    profileOrder =
-      if _.isEmpty @state.user.account_history
-        @state.profileOrder
-      else
-        _.concat @state.profileOrder, 'account_standing'
+    profileOrder = @state.profileOrder.slice()
+    profileOrder.push 'account_standing' if !_.isEmpty @state.user.account_history

     extraPageParams =
       me:


Use the new way of requiring ipc

Use the new way of requiring ipc

Hopefully this works.

--- lib/tab-bar-view.coffee
+++ lib/tab-bar-view.coffee
@@ -1,5 +1,5 @@
 BrowserWindow = null # Defer require until actually used
-ipc = try require 'ipc-renderer' catch then require 'ipc'
+try require {ipcRenderer} = 'electron' catch then ipcRenderer = require 'ipc'

 {matches, closest, indexOf} = require './html-helpers'
 {CompositeDisposable} = require 'atom'
@@ -82,10 +82,10 @@
     @addEventListener "dblclick", @onDoubleClick
     @addEventListener "click", @onClick

-    ipc.on('tab:dropped', @onDropOnOtherWindow.bind(this))
+    ipcRenderer.on('tab:dropped', @onDropOnOtherWindow.bind(this))

   unsubscribe: ->
-    ipc.removeListener('tab:dropped', @onDropOnOtherWindow.bind(this))
+    ipcRenderer.removeListener('tab:dropped', @onDropOnOtherWindow.bind(this))
     @subscriptions.dispose()

   handleTreeViewEvents: ->


Mention being empty

Mention being empty

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

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


adding more quotes for offshore team

adding more quotes for offshore team

--- scripts/offshore.coffee
+++ scripts/offshore.coffee
@@ -12,6 +12,7 @@
 #   hubot sachin nsfw
 #   hubot who will be victorious
 #   hubot who will prevail
+#   hubot who will unleash righteous vengeance upon evil
 #
 # Author:
 #   Mateusz Kopij <sagasu>
@@ -37,3 +38,6 @@

   robot.respond /who will prevail/i, (msg)->
     msg.send "The Offshore team! :sachin: :prem: :kostas: :chris: :drmatt:"
+
+  robot.respond /who will unleash righteous vengeance upon evil/i, (msg)->
+    msg.send "The heroes of offshore team! :sachin: :prem: :kostas: :chris: :drmatt:"


Add command: team card

Add command: team card

--- scripts/strength.coffee
+++ scripts/strength.coffee
@@ -124,3 +124,29 @@
         text += "#{rank_index+1}位：#{strength_title}\n"
       text += "\n"
     msg.send text
+
+  robot.respond /Team card/i, (msg) ->
+    AsciiTable = require 'ascii-table'
+    table = new AsciiTable 'Team Card'
+
+    users = []
+    headings = ["#"]
+    for user_name, strength_title of robot.brain.data.strength
+      users.push user_name
+      headings.push user_name
+
+    table.setHeading.apply(table, headings)
+
+    for strength_name, strength of StrengthList.get()
+      row = [strength.title]
+      for user_name in users
+        for strenght_title in robot.brain.data.strength[user_name]
+          if strength_title.toString() == strength.title.toString()
+            row.push "◯"
+          else
+            row.push " "
+      table.addRow.apply(table, row)
+
+    table.setAlign 1, AsciiTable.RIGHT
+
+    msg.send table.toString()


instructions book returning back to first page fixed

--- client/app/MainApp/book/bookview.coffee
+++ client/app/MainApp/book/bookview.coffee
@@ -110,7 +110,7 @@
   fillPage:(index)->

     cachePage index+1
-    index or= BookView.lastIndex
+    index ?= BookView.lastIndex
     BookView.lastIndex = index
     page = @getPage index


klient: Added new clientSubscribe method

--- client/app/lib/kite/kites/klient.coffee
+++ client/app/lib/kite/kites/klient.coffee
@@ -10,6 +10,8 @@
     exec               : 'exec'
     ping               : 'kite.ping'
     systemInfo         : 'kite.systemInfo'
+
+    clientSubscribe    : 'client.Subscribe'

     fsReadDirectory    : 'fs.readDirectory'
     fsGlob             : 'fs.glob'


Machine: fix ::getOwner use new username field in users list except koding and managed providers [completes #103461012]

--- client/app/lib/providers/machine.coffee
+++ client/app/lib/providers/machine.coffee
@@ -118,15 +118,17 @@
     switch @provider
       when 'koding', 'managed'
         return @data.credential
+      else # Use users array for other types of providers ~ GG
+        for user in @jMachine.users when user.owner
+          return user.username

   _ruleChecker: (rules) ->

-    for user in @jMachine.users
-      if user.id is globals.userId
-        for rule in rules
-          return no  unless user[rule]
-        return yes
+    for user in @jMachine.users when user.id is globals.userId
+      for rule in rules
+        return no  unless user[rule]
+      return yes

     return no


Remove Laravel references

Remove Laravel references

Strips a few Laravel references, changes Liobot to Octobot, and removes the laravel-specific !relsched and !wiki commands.

The !rules command links to a gist that also contains a lot of references we won't want, but it's useful too. I'll probably fork it and update before I change the link here.

--- scripts/triggers.coffee
+++ scripts/triggers.coffee
@@ -1,5 +1,5 @@
 # Description:
-#   LioBot heeds the call of the last op
+#   Octobot heeds the call of the last op
 #
 # Commands:
 #   !<trigger> - Display response to intended user
@@ -12,8 +12,8 @@
   '!dataja'       : "Don't ask to ask, Just ask!"
   '!tryit'        : "Try it and see™. You learn much more by experimentation than by asking without having even tried."
   '!explain'      : "Explain your business goals, such as what website feature you're making, instead of the technical problem you're facing. We can give you much better answers this way."
-  '!help'         : "Before asking for help, use http://kopy.io to provide us the information we need to help you - Laravel version, expected/actual behavior, and all relevant code. Paste the link here when done. Thanks!"
-  '!helpme'       : "Before asking for help, use http://kopy.io to provide us the information we need to help you - Laravel version, expected/actual behavior, and all relevant code. Paste the link here when done. Thanks!"
+  '!help'         : "Before asking for help, use http://kopy.io to provide us the information we need to help you - October build, expected/actual behavior, and all relevant code. Paste the link here when done. Thanks!"
+  '!helpme'       : "Before asking for help, use http://kopy.io to provide us the information we need to help you - October build, expected/actual behavior, and all relevant code. Paste the link here when done. Thanks!"
   '!paste'        : "You may paste your code at http://kopy.io"
   '!dontspam'     : "Please don't paste your code here. Instead, use http://kopy.io"
   '!pb'           : "Please avoid using pastebin.com as it is slow and forces others to look at ads. Please use http://kopy.io - Thanks!"
@@ -22,13 +22,11 @@
   # Helpers
   '!ugt'          : "It is always morning when someone comes into a channel. We call that Universal Greeting Time http://www.total-knowledge.com/~ilya/mips/ugt.html"
   '!nick'         : "Hello! You're currently using a nick that's difficult to distinguish. Please type in \"/nick your_name\" so we can easily identify you"
-  '!wiki'         : "Check out our wiki at http://wiki.laravel.io!"
-  '!welcome'      : "Hello, I'm #{process.env.HUBOT_IRC_NICK}, the Laravel IRC Bot!  Welcome to Laravel :).  If you have any questions, type !help to see how to best ask for assistance.  If you need to paste code, check !paste for more info.  Thanks!"
+  '!welcome'      : "Hello, I'm #{process.env.HUBOT_IRC_NICK}, the OctoberCMS IRC Bot!  Welcome to October :).  If you have any questions, type !help to see how to best ask for assistance.  If you need to paste code, check !paste for more info.  Thanks!"
   '!wysiwyg'      : "What you see is what you get"
-  '!relsched'     : "Here is our release schedule: http://wiki.laravel.io/Laravel_4#Release_Schedule"
   '!massassign'   : "Getting a MassAssignmentException? Find out how to protect your input at: http://wiki.laravel.io/FAQ_(Laravel_4)#MassAssignmentException"
-  '!docscontrib'  : "Want to contribute to the documentation? Awesome! Fork and submit a pull request at http://github.com/laravel/docs"
-  '!whoisliobot'  : "Hello! The Laravel.io team created me to help you! You can find my code at https://github.com/laravelio/liobot"
+  '!docscontrib'  : "Want to contribute to the documentation? Awesome! Fork and submit a pull request at https://github.com/octobercms/docs"
+  '!whoisoctobot'  : "Hello! The October team created me to help you! You can find my code at https://github.com/daftspunk/Octobot"

   # Fun
   '!no'           : "NOOOOOOOOO! http://www.youtube.com/watch?v=umDr0mPuyQc"


Fix PERIDOT connection failure on Chromebook

--- src/peridotboard.coffee
+++ src/peridotboard.coffee
@@ -29,7 +29,7 @@
   ###
   connect: (port, callback) ->
     return callback?(true) if @isConnected
-    @canarium.open(port.name, (result) =>
+    @canarium.open(port.path, (result) =>
       return callback?(false) unless result
       @canarium.avm.option({forceConfigured: true}, (result) =>
         return callback?(false) unless result


Remove firmware update check in connecting vc

--- app/src/controllers/onboarding/device/onboarding_device_connecting_view_controller.coffee
+++ app/src/controllers/onboarding/device/onboarding_device_connecting_view_controller.coffee
@@ -25,15 +25,20 @@

   _navigateContinue: ->
     @_stopTimer()
-    ledger.app.wallet?.isFirmwareUpdateAvailable (isAvailable) =>
-      if isAvailable
-        ledger.app.router.go '/onboarding/device/update'
+    ledger.app.wallet.getState (state) =>
+      if state == ledger.wallet.States.LOCKED
+        ledger.app.router.go '/onboarding/device/pin'
       else
-        ledger.app.wallet.getState (state) =>
-          if state == ledger.wallet.States.LOCKED
-            ledger.app.router.go '/onboarding/device/pin'
-          else
-            ledger.app.router.go '/onboarding/management/welcome'
+        ledger.app.router.go '/onboarding/management/welcome'
+#    ledger.app.wallet?.isFirmwareUpdateAvailable (isAvailable) =>
+#      if isAvailable
+#        ledger.app.router.go '/onboarding/device/update'
+#      else
+#        ledger.app.wallet.getState (state) =>
+#          if state == ledger.wallet.States.LOCKED
+#            ledger.app.router.go '/onboarding/device/pin'
+#          else
+#            ledger.app.router.go '/onboarding/management/welcome'

   _navigateForged: ->
     @_stopTimer()
@@ -41,12 +46,13 @@
       if error?
         ledger.app.router.go '/onboarding/device/forged'
       else
-        ledger.app.wallet?.isFirmwareOverwriteOrUpdateAvailable (isAvailable) =>
-          if isAvailable and not ledger.fup.versions.Nano.CurrentVersion.Beta
-            ledger.app.setExecutionMode(ledger.app.Modes.FirmwareUpdate)
-            ledger.app.router.go '/update/index', {hidePreviousButton: yes}
-          else
-            @_navigateContinue()
+        @_navigateContinue()
+#        ledger.app.wallet?.isFirmwareOverwriteOrUpdateAvailable (isAvailable) =>
+#          if isAvailable and not ledger.fup.versions.Nano.CurrentVersion.Beta
+#            ledger.app.setExecutionMode(ledger.app.Modes.FirmwareUpdate)
+#            ledger.app.router.go '/update/index', {hidePreviousButton: yes}
+#          else
+#            @_navigateContinue()

   _navigateError: ->
     @_stopTimer()


Replace broken with broken_query

Replace broken with broken_query

--- admin/static/coffee/dataexplorer.coffee
+++ admin/static/coffee/dataexplorer.coffee
@@ -163,7 +163,7 @@
         # The size of the history is infinite per session. But we will just save @size_history queries in localStorage
         save_query: (args) =>
             query = args.query
-            broken = args.broken
+            broken_query = args.broken_query
             # Remove empty lines
             query = query.replace(/^\s*$[\n\r]{1,}/gm, '')
             if query[query.length-1] is '\n' or query[query.length-1] is '\r'
@@ -172,14 +172,14 @@
                 if @history.length is 0 or @history[@history.length-1].query isnt query and @regex.white.test(query) is false
                     @history.push
                         query: query
-                        broken: broken
+                        broken_query: broken_query
                     if @history.length>@size_history
                         window.localStorage.rethinkdb_history = JSON.stringify @history.slice @history.length-@size_history
                     else
                         window.localStorage.rethinkdb_history = JSON.stringify @history
                     @history_view.add_query
                         query: query
-                        broken: broken
+                        broken_query: broken_query

         clear_history: =>
             @history.length = 0
@@ -1548,7 +1548,7 @@
                 @results_view.render_error(@query, err)
                 @save_query
                     query: @raw_query
-                    broken: true
+                    broken_query: true

         execute_portion: =>
             @saved_data.cursor = null
@@ -1565,7 +1565,7 @@
                     @results_view.render_error(@raw_queries[@index], err)
                     @save_query
                         query: @raw_query
-                        broken: true
+                        broken_query: true
                     return false

                 @index++
@@ -1592,7 +1592,7 @@
                         @results_view.render_error(@raw_queries[@index-1], error)
                         @save_query
                             query: @raw_query
-                            broken: true
+                            broken_query: true

         rdb_global_callback: (error, cursor) =>
@@ -1601,7 +1601,7 @@
                 @results_view.render_error(@raw_queries[@index-1], error)
                 @save_query
                     query: @raw_query
-                    broken: true
+                    broken_query: true
                 return false

             if @index is @queries.length # @index was incremented in execute_portion
@@ -1635,7 +1635,7 @@
                     # Successful query, let's save it in the history
                     @save_query
                         query: @raw_query
-                        broken: false
+                        broken_query: false
             else
                 @execute_portion()

@@ -1644,7 +1644,7 @@
                 @results_view.render_error(@query, error)
                 @save_query
                     query: @raw_query
-                    broken: true
+                    broken_query: true
                 return false

             if data isnt undefined
@@ -1671,7 +1671,7 @@
             # Successful query, let's save it in the history
             @save_query
                 query: @raw_query
-                broken: false
+                broken_query: false

         evaluate: (query) =>
@@ -2475,7 +2475,7 @@
                 for query, i in @history
                     @$('.history_list').append @dataexplorer_query_li_template
                         query: query.query
-                        broken: query.broken
+                        broken_query: query.broken_query
                         id: i
                         num: i+1
             @delegateEvents()
@@ -2489,12 +2489,12 @@

         add_query: (args) =>
             query = args.query
-            broken = args.broken
+            broken_query = args.broken_query
             that = @
             is_at_bottom = @$('.history_list').height() is @$('.nano > .content').scrollTop()+@$('.nano').height()
             @$('.history_list').append @dataexplorer_query_li_template
                 query: query
-                broken: broken
+                broken_query: broken_query
                 id: @history.length-1
                 num: @history.length
             if @state is 'visible'


Fix typo

Fix typo

--- admin/static/coffee/tables/index.coffee
+++ admin/static/coffee/tables/index.coffee
@@ -283,7 +283,7 @@
     class @TableView extends Backbone.View
         className: 'table_container'
         template: Handlebars.templates['table-template']
-        @initialize: =>
+        initialize: =>
             @listenTo @model, 'change', @render

         render: =>


Look in ~/.knit for config files not ~/.

--- src/configuration.coffee
+++ src/configuration.coffee
@@ -24,7 +24,7 @@
     candidates.push (path.resolve "./#{ name }")
     # ~/.knit/ if we have a HOME environment variable
     if process.env.HOME
-      candidates.push (path.resolve "#{ process.env.HOME }/#{ name }")
+      candidates.push (path.resolve "#{ process.env.HOME }/.knit/#{ name }")
     # Knit paths
     candidates.push(path.resolve(path.join(p, name))) for p in knitPaths
     # Standard Knit configs to make it easy to bundle useful config


Fix getSupportedVersions tests when using unstable sorting

Without this the tests might fail on staging when the
versions array contains items of the same normalized
semver value, depending on the length of the array.

Change-type: patch
Signed-off-by: Thodoris Greasidis <5791bdc4541e79bd43d586e3c3eff1e39c16665f@balena.io>

--- tests/integration/models/os.spec.coffee
+++ tests/integration/models/os.spec.coffee
@@ -90,15 +90,24 @@

 		describe 'given a valid device slug', ->

+			expectSorted = (array, comparator) ->
+				# re-sorting could fail when the system is not using a stable
+				# sorting algorithm, in which case items of the same value
+				# might swap positions in the array
+				array.forEach (item, i) ->
+					if i == 0
+						return
+
+					previousItem = array[i - 1]
+					m.chai.expect(comparator(previousItem, item)).to.be.lte(0)
+
 			areValidVersions = (osVersions) ->
 				m.chai.expect(osVersions).to.be.an('object')
 				m.chai.expect(osVersions).to.have.property('versions').that.is.an('array')
 				m.chai.expect(osVersions.versions).to.not.have.lengthOf(0)

-				sortedVersions = osVersions.versions.slice().sort(osVersionRCompare)
-				m.chai.expect(osVersions.versions).to.deep.equal(sortedVersions)
-
-				m.chai.expect(osVersions).to.have.property('latest').that.is.a('string')
+				expectSorted(osVersions.versions, osVersionRCompare)
+
 				m.chai.expect(osVersions).to.have.property('latest').that.is.a('string')
 				m.chai.expect(osVersions).to.have.property('recommended').that.is.a('string')
 				m.chai.expect(osVersions).to.have.property('default').that.is.a('string')


:construction::blush: Updated in browser at strd6.github.io/editor

--- pixie.cson
+++ pixie.cson
@@ -1,4 +1,4 @@
-version: "0.4.3"
+version: "0.4.4-pre.0"
 entryPoint: "main"
 remoteDependencies: [
   "https://cdnjs.cloudflare.com/ajax/libs/coffee-script/1.7.1/coffee-script.min.js"


修正userData的改变引起c-repeat不正常的BUG

修正userData的改变引起c-repeat不正常的BUG

--- src/core/dom-util.coffee
+++ src/core/dom-util.coffee
@@ -83,30 +83,20 @@
 				store = userData[id]
 				return store?[key]
 		else if key and typeof key is "object"
-			if not id
-				id = cola.uniqueId()
-				if node.nodeType is 8
-					if i > -1
-						node.nodeValue = text.substring(0, i + 1) + id
-					else
-						node.nodeValue = if text then text + "|" + id else "|" + id
+			id = cola.uniqueId()
+			if node.nodeType == 8
+				if i > -1
+					node.nodeValue = text.substring(0, i + 1) + id
 				else
-					node.setAttribute(USER_DATA_KEY, id)
-
-				userData[id] = store = {
-					__cleanStamp: cleanStamp
-				}
-				userData.size++
+					node.nodeValue = if text then text + "|" + id else "|" + id
 			else
-				store = userData[id]
-				if not store
-					userData[id] = store = {
-						__cleanStamp: cleanStamp
-					}
-
+				node.setAttribute(USER_DATA_KEY, id)
+
+			userData[id] = store = {
+				__cleanStamp: cleanStamp
+			}
 			for k, v of key
 				store[k] = v
-
 	else if arguments.length is 1
 		if id
 			return userData[id]


:rocket::egg: Updated in browser at strd6.github.io/editor

--- load-n-play-midi.coffee
+++ load-n-play-midi.coffee
@@ -19,6 +19,7 @@
   badApple = "http://whimsy.space/danielx/data/clOXhtZz4VcunDJZdCM8T5pjBPKQaLCYCzbDod39Vbg"
   waltz = "http://whimsy.space/danielx/data/qxIFNrVVEqhwmwUO5wWyZKk1IwGgQIxqvLQ9WX0X20E"
   jordan = "http://whimsy.space/danielx/data/FhSh0qeVTMu9Xwd4vihF6shaPJsD_rM8t1OSKGl-ir4"
+  aquarius = "http://whimsy.space/danielx/data/ZZXoIXhXFbo0pWGn-m938Vgox_NmJiYkZ9g3UkR0PrU"
   # Bad Apple 36MB MIDI

   require("./sample")().then (buffer) ->
@@ -27,7 +28,7 @@
     , (err) ->
       console.error 'Iam error'

-  Ajax.getBuffer(jordan)
+  Ajax.getBuffer(aquarius)
   .then (buffer) ->
     array = new Uint8Array(buffer)
     midiFile = MidiFile(array)


Ensure ? can be used at the end of a key with JSON

--- tests/batman/model/encoders_test.coffee
+++ tests/batman/model/encoders_test.coffee
@@ -4,6 +4,8 @@
       @encode 'name', 'cost'
       @accessor 'excitingName'
         get: -> @get('name').toUpperCase()
+    class @FlakyProduct extends @Product
+      @encode 'broken?'

 test "keys marked for encoding should be encoded", ->
   p = new @Product {name: "Cool Snowboard", cost: 12.99}
@@ -61,6 +63,17 @@
   equal p.get('rails_is_silly'), 'yup'
   Batman.Model::_batman.decoders = oldDecoders

+test "key ending with ? marked for encoding should be encoded", ->
+  p = new @FlakyProduct {name: "Vintage Snowboard", cost: 122.99, "broken?": true}
+  deepEqual p.toJSON(), {name: "Vintage Snowboard", cost: 122.99, "broken?": true}
+
+test "key ending with ? marked for decoding should be decoded", ->
+  p = new @FlakyProduct
+  json = {name: "Vintage Snowboard", cost: 122.99, "broken?": true}
+
+  p.fromJSON(json)
+  ok p.get('broken?'), "Cool Snowboard"
+
 QUnit.module "Batman.Model: encoding: custom encoders/decoders"
   setup: ->
     class @Product extends Batman.Model


Remove collision check if sun is definitely higher

--- src/scenery/City.coffee
+++ src/scenery/City.coffee
@@ -167,6 +167,21 @@
     .attr(z: -598, w: 256)

   @addBackground(0, @level.visibleHeight - 225 - 16, e, .25)
+
+levelGenerator.defineElement 'cityNoCollision', ->
+  bg = Crafty.e('2D, WebGL, cityLayer2, Horizon, Flipable')
+    .attr(z: -505) #, blur: 1.2)
+    .colorDesaturation(Game.backgroundColor)
+    .saturationGradient(.6, .6)
+  @addBackground(0, @level.visibleHeight - 350, bg, .375)
+
+  e = Crafty.e('2D, WebGL, city, Horizon')
+    .colorDesaturation(Game.backgroundColor)
+    .saturationGradient(.4, .4)
+    .crop(0, 0, 511, 288)
+    .attr(z: -305, w: 513)
+
+  @addBackground(0, @level.visibleHeight - 310, e, .5)

 levelGenerator.defineElement 'city', ->
   bg = Crafty.e('2D, WebGL, cityLayer2, Collision, SunBlock, Horizon, Flipable')
@@ -794,10 +809,8 @@

     @addElement 'cityFrontTop'
     @addElement 'cityFrontBlur'
-    @addElement 'water'
-
     @addElement 'cityDistance'
-    @addElement 'city'
+    @addElement 'cityNoCollision'

 levelGenerator.defineBlock class extends CityScenery
   name: 'City.SkylineBase'


More popup cloud fixes

More popup cloud fixes

--- src/scenery/City.coffee
+++ src/scenery/City.coffee
@@ -21,7 +21,7 @@
     )
     if Math.random() < 0.7
       c1 = c1.flip('X')
-    @addBackground(100 + (Math.random() * 300), y, c1, .375)
+    @addBackground(300 + (Math.random() * 300), y, c1, .375)

   if v < .6
     s = (Math.random() * .20) + .15


A user in "what role" is specified in a message text by their name, not the user who send the text.

--- src/scripts/auth.coffee
+++ src/scripts/auth.coffee
@@ -88,7 +88,7 @@

   robot.respond /(what role does|what roles does) @?(.+) (have)\?*$/i, (msg) ->
     name = msg.match[2].trim()
-    user = robot.brain.userForId(msg.message.user.id)
+    user = robot.brain.userForName(name)
     return msg.reply "#{name} does not exist" unless user?
     user.roles or= []


Updated the location of the users list

Updated the location of the users list

Apparently the location of the users list has changed and this script has not been updated to show that. The help for this script also says that it will mention every person in the room. This will in fact mention everyone on the server.

--- src/scripts/yell.coffee
+++ src/scripts/yell.coffee
@@ -21,7 +21,7 @@
   _ = require 'underscore'

   robot.respond /yell (.*)/i, (msg) ->
-    users = _.reject((_.values _.pluck robot.brain.users, 'name'), (name) -> name == msg.message.user.name)
+    users = _.reject((_.values _.pluck robot.brain.data.users, 'name'), (name) -> name == msg.message.user.name)
     msg.send if users.length then users.join(', ') + ": #{msg.match[1]}" else "If a tree falls in a forest and no one is around to hear it, does it make a sound?"


Fix bugs

Fix bugs

--- index.coffee
+++ index.coffee
@@ -127,10 +127,10 @@

   track_strategy(context, 'ask_for_seed')
   seed = parseInt context.text.replace(/[\+|\s|\-|\D|\,]/g, '')
-  if _.isNumber(feed)
+  if _.isNumber(seed)
     context.game_responses.seed = seed

-  return context.send _.sample(game_questions.feed)
+  return context.send _.sample(game_questions.seed)

 tick_strategy = (context)->


Fixing build error

Fixing build error

--- index.coffee
+++ index.coffee
@@ -1,4 +1,4 @@
-Router   = require './components/Router/Router.cjsx'
+Router   = require './components/Router/Router'

 module.exports =
   default: require './components/FileUploader/FileUploaderContainer.cjsx'


Change to new API. Only 1 parse and robots only return sitemaps

--- index.coffee
+++ index.coffee
@@ -48,7 +48,9 @@

 		@_download url, parserStream

-parseSitemaps = (urls, url_cb, done) ->
+exports.parseSitemaps = (urls, url_cb, done) ->
+	urls = [urls] unless urls instanceof Array
+
 	sitemapParser = new sitemapParser url_cb, (sitemap) ->
 		queue.push sitemap

@@ -57,12 +59,10 @@
 		done()
 	queue.push urls

-exports.parseSitemap = (url, url_cb, done) ->
-	parseSitemaps [url], url_cb, done
-
-exports.parseRobot = (url, url_cb, done) ->
+exports.sitemapsInRobots = (url, cb) ->
 	request.get url, (err, res, body) ->
+		return cb err if err
 		matches = []
 		body.replace /^Sitemap:\s?([^\s]+)$/igm, (m, p1) ->
 			matches.push(p1)
-		parseSitemaps matches, url_cb, done+		cb null, matches


don't crash if no rules

don't crash if no rules

--- index.coffee
+++ index.coffee
@@ -35,7 +35,7 @@
 module.exports = pass = (ast, options) ->
   myOptions = options.overrideActionPlugin
   rules = ast.rules
-  override = myOptions.rules
+  override = myOptions.rules or {}

   return override ast, options  if isFunction override


Pass error through ahead/behind.

Pass error through ahead/behind.

--- index.coffee
+++ index.coffee
@@ -78,7 +78,7 @@
   register '_aheadBehind',
     update: (done) ->
       git.remoteBranch (err, remoteBranch) ->
-        return done null, null unless remoteBranch
+        return done err, null unless remoteBranch

         command = "git rev-list --left-right --count #{remoteBranch.trim()}...HEAD"
         Impromptu.exec command, (err, result) ->


Add error checking to robots parsing

Add error checking to robots parsing

--- index.coffee
+++ index.coffee
@@ -68,6 +68,7 @@
 exports.sitemapsInRobots = (url, cb) ->
 	request.get url, (err, res, body) ->
 		return cb err if err
+		return cb "statusCode: #{res.statusCode}" if res.statusCode isnt 200
 		matches = []
 		body.replace /^Sitemap:\s?([^\s]+)$/igm, (m, p1) ->
 			matches.push(p1)


Fix compatibility issues with master.

Fix compatibility issues with master.

--- index.coffee
+++ index.coffee
@@ -1,7 +1,7 @@
 sparkmd5 = require 'sparkmd5'
 JSZip = require 'jszip'
+localForage = require 'localforage'
 require 'x-game'
-localForage = require 'localforage'

 settings = require './settings.json!'
 utils = require './utils'
@@ -28,31 +28,12 @@
 window.retro = retro = document.createElement 'canvas', 'x-game'
 document.body.appendChild retro

-cores =
-  'snes9x-next': require 'snes9x-next'
-  'vba-next': require 'vba-next'
-  'nestopia': require 'nestopia'
-  'gambatte': require 'gambatte'
-
-readFile = (file) ->
-  new Promise (resolve, reject) ->
-    reader = new FileReader()
-    reader.readAsArrayBuffer file
-    reader.onload = (event) ->
-      resolve new Uint8Array reader.result
-    reader.onerror = reject
-
-readFileEntry = (entry) ->
-  new Promise (resolve, reject) ->
-    entry.file resolve, reject
-  .then (file) ->
-    readFile file
-
-getSave = (filename) ->
-  new Promise (resolve, reject) ->
-    chrome.syncFileSystem.requestFileSystem (fs) ->
-      fs.root.getFile filename, create: true, resolve, reject
-    , reject
+onkey = (event) ->
+  if retro.player and settings.keys.hasOwnProperty event.which
+    pressed = event.type == 'keydown'
+    retro.player.inputs[0].buttons[settings.keys[event.which]] ?= {}
+    retro.player.inputs[0].buttons[settings.keys[event.which]].pressed = pressed
+    event.preventDefault()

 autosaver = 0

@@ -63,49 +44,36 @@
   window.removeEventListener 'keydown', onkey
   window.clearInterval autosaver

-onkey = (event) ->
-  if retro.player and settings.keys.hasOwnProperty event.which
-    pressed = event.type == 'keydown'
-    retro.player.inputs[0].buttons[settings.keys[event.which]] ?= {}
-    retro.player.inputs[0].buttons[settings.keys[event.which]].pressed = pressed
-    event.preventDefault()
-
-init = (rom, extension, save) ->
-  stop() if retro.running
-  Promise.resolve cores[settings.extensions[extension]]
-  .then (core) ->
-    retro.core = core
-    core.load_game rom if rom
-    core.unserialize new Uint8Array save if save?
-    core.set_input_poll ->
-      gamepads = navigator.getGamepads() if navigator.getGamepads
-      retro.player.inputs = gamepads if gamepads and gamepads[0]
-    retro.player.inputs = [
-      buttons: {}
-    ]
-    window.addEventListener 'keydown', onkey
-    window.addEventListener 'keyup', onkey
-    retro
-
 play = (rom, extension) ->
   Promise.resolve()
   .then ->
-    throw new Error 'no rom!' if not rom
+    throw 'no rom!' if not rom
     retro.md5 = sparkmd5.ArrayBuffer.hash rom
-    localForage.getItem retro.md5
-    .then (save) ->
-      init rom, extension, save
-      .then (retro) ->
-        autosaver = setInterval ->
-          localForage.setItem retro.md5, new Uint8Array retro.core.serialize()
-        , 1000
-        retro.start()
+    Promise.all([
+      System.import settings.extensions[extension]
+      localForage.getItem retro.md5
+    ]).then ([core, save]) ->
+      stop() if retro.running
+      retro.core = core
+      core.load_game rom if rom
+      core.unserialize new Uint8Array save if save?
+      core.set_input_poll ->
+        gamepads = navigator.getGamepads() if navigator.getGamepads
+        retro.player.inputs = gamepads if gamepads and gamepads[0]
+      retro.player.inputs = [
+        buttons: {}
+      ]
+      autosaver = setInterval ->
+        localForage.setItem retro.md5, new Uint8Array core.serialize()
+      , 1000
+      window.addEventListener 'keydown', onkey
+      window.addEventListener 'keyup', onkey
+      retro.start()

 loadData = (filename, buffer) ->
   draghint.classList.add 'hidden'
   extension = utils.getExtension filename
   rom = null
-  tracker.sendEvent 'play', filename
   if extension is 'zip'
     zip = new JSZip buffer
     for file in zip.file /.*/ # any way to predict name of file?
@@ -118,14 +86,16 @@
   play rom, extension
   .catch (e) ->
     console.error e
+    alert "that file couldn't be loaded"
     location.reload() # hacky but a fix

 load = (file) ->
   return if not file instanceof Blob
   draghint.classList.add 'hidden'
-  readFile file
-  .then (buffer) ->
-    loadData file.name, buffer
+  reader = new FileReader()
+  reader.addEventListener 'load', (event) ->
+    loadData file.name, (new Uint8Array reader.result)
+  reader.readAsArrayBuffer file

 window.addEventListener 'drop', (event) ->
   return if draghint.classList.contains 'hidden'


introduce update method in DB

introduce update method in DB

--- src/db.appsscript.coffee
+++ src/db.appsscript.coffee
@@ -36,6 +36,10 @@
 	@removeMail: (mail) ->
 		ret = @DB.remove mail if mail.type is @TYPE_MAIL
 		ret
+
+	@update: (entity) ->
+		return unless entity.type in [@TYPE_MAIL]
+		@DB.save entity

 	@now: ->
 		(new Date()).getTime()


references, how do they work. closes #35

--- src/coffee/controllers/player-equipment-ctrl.coffee
+++ src/coffee/controllers/player-equipment-ctrl.coffee
@@ -22,6 +22,7 @@
       {name: 'piety'},
       {name: 'enchantLevel'},
       {name: 'shopSlot'},
+      {name: 'overflowSlot'},
       {name: '_calcScore'},
       {name: '_baseScore'}

@@ -50,7 +51,9 @@
       _.map keys, (key) -> "#{key} (#{item[key]})"
       .join ', '

-    $scope.getEquipmentAndTotals = (items) ->
+    $scope.getEquipmentAndTotals = (oldItems) ->
+
+      items = _.clone oldItems

       test = _.reduce items, (prev, cur) ->
         for key, val of cur


added rgb and hsl functions

added rgb and hsl functions

--- ink.coffee
+++ ink.coffee
@@ -3,6 +3,90 @@
 #
 ansiRegex = require 'ansi-regex'
 supportsColor = require 'supports-color'
+
+SHADE_AMT = 0.1
+
+applyDomain = (val, lb, ub, tlb, tub)->
+  Math.round ((val - lb) / (ub - lb)) * (tub - tlb)
+
+InkRender = (args...)->
+  console.log 'RENDER', @
+
+# thanks to Mohsen at http://stackoverflow.com/a/9493060/510036
+rgbToHsl = (r, g, b)->
+  r /= 255
+  g /= 255
+  b /= 255
+  max = Math.max r, g, b
+  min = Math.min r, g, b
+  l = (max + min) / 2
+  if max is min then h = s = 0 # achromatic
+  else
+    d = max - min
+    s = if l > 0.5 then d / (2 - max - min) else d / (max + min)
+    switch max
+      when r then h = (g - b) / d + (if g < b then 6 else 0)
+      when g then h = (b - r) / d + 2
+      when b then h = (r - g) / d + 4
+    h /= 6
+  return [h, s, l]
+
+hslToRgb = (h, s, l)->
+  if s is 0 then r = g = b = l # achromatic
+  else
+    hue2rgb = (p, q, t)->
+      if t < 0 then t += 1
+      if t > 1 then t -= 1
+      if t < (1/6) then return p + (q - p) * 6 * t
+      if t < (1/2) then return q
+      if t < (2/3) then return p + (q - p) * ((2/3) - t) * 6
+      return p
+    q = if l < 0.5 then l * (1 + s) else l + s - l * s
+    p = 2 * l - q
+    r = hue2rgb p, q, h + (1/3)
+    g = hue2rgb p, q, h
+    b = hue2rgb p, q, h - (1/3)
+  return [
+    Math.round r * 255
+    Math.round g * 255
+    Math.round b * 255
+  ]
+
+class Ink
+  constructor: ->
+    @codes =
+      fg: {}
+      bg: {}
+    @side = 'fg'
+
+  reset:-> @codes = {}
+
+  bold:-> @codes.bold = on
+  thin:-> @codes.bold = off
+
+  fg:-> @side = 'fg'
+  bg:-> @side = 'bg'
+
+  bright:-> # TODO
+  light:-> @codes[@side].mult =     2.0
+  dim:-> @codes[@side].mult =       1.0
+  lighter:->
+    @codes[@side].mult +=           SHADE_AMT
+    (amt)=>
+      --amt
+      @codes[@side].mult +=         amt * SHADE_AMT
+
+  black:-> @codes[@side].color =    [0,   0,   0  ]
+  red:-> @codes[@side].color =      [127, 0,   0  ]
+  green:-> @codes[@side].color =    [0,   127, 0  ]
+  yellow:-> @codes[@side].color =   [127, 127, 0  ]
+  blue:-> @codes[@side].color =     [0,   0,   127]
+  magenta:-> @codes[@side].color =  [127, 0,   127]
+  cyan:-> @codes[@side].color =     [0,   127, 127]
+  white:-> @codes[@side].color =    [127, 127, 127]
+
+# ... and a teaspoon of Javascript black voodoo magic ...
+module.exports = {}

 defineFnProp = (obj, name, thisArg, fn)->
   if not fn and thisArg instanceof Function
@@ -13,41 +97,9 @@
     get: -> fn.call thisArg
   return obj

-InkRender = (args...)->
-  console.log 'RENDER', @
-
-class InkFGBG
-  constructor: (@ink, @side)->
-    console.log 'FGBG:', @side
-
-  red: ->
-    console.log 'RED'
-
-class Ink
-  bold: ->
-    console.log 'BOLD'
-
-  reset: ->
-    console.log 'RESET'
-
-  fg: ->
-    new InkFGBG @, 'fg'
-
-  bg: ->
-    new InkFGBG @, 'bg'
-
-# ... and a teaspoon of Javascript black voodoo magic ...
-module.exports = {}
-
 mkGetter = (ink, fn, getter)->->
-  fgbg = fn.call ink
-  if fgbg instanceof InkFGBG
-    fgbggetter = InkRender.bind ink
-    for name, gfn of InkFGBG.prototype
-      defineFnProp fgbggetter, name, mkGetter ink, gfn, getter
-    fgbggetter
-  else
-    getter
+  fn.call ink
+  return getter

 mkExport = (name, fn)->->
   ink = new Ink
@@ -55,7 +107,7 @@
   for name, gfn of Ink.prototype
     defineFnProp getter, name, mkGetter ink, gfn, getter
   fgbg = fn.call ink
-  return if fgbg instanceof InkFGBG then fgbg else getter
+  return getter

 for name, fn of Ink.prototype
   defineFnProp module.exports, name, mkExport name, fn


Only apply spoiler tags once

Only apply spoiler tags once

--- app/assets/javascripts/sugar/posts/spoiler.js.coffee
+++ app/assets/javascripts/sugar/posts/spoiler.js.coffee
@@ -1,12 +1,13 @@
 $(Sugar).bind 'ready postsloaded', ->

   $('.spoiler').each ->
-    $(this)
-      .wrapInner('')
-      .prepend('Spoiler! ')
+  	if $(this).find('.innerSpoiler').length == 0
+	    $(this)
+	      .wrapInner('')
+	      .prepend('Spoiler! ')

-    $(this).find('.innerSpoiler').css('visibility', 'hidden')
-    $(this).hover ->
-      $(this).find('.innerSpoiler').css('visibility', 'visible')
-    , ->
-      $(this).find('.innerSpoiler').css('visibility', 'hidden')
+	    $(this).find('.innerSpoiler').css('visibility', 'hidden')
+	    $(this).hover ->
+	      $(this).find('.innerSpoiler').css('visibility', 'visible')
+	    , ->
+	      $(this).find('.innerSpoiler').css('visibility', 'hidden')


adaptation a chaque redimensionnement de la bannière

--- app/assets/javascripts/_utils.js.coffee
+++ app/assets/javascripts/_utils.js.coffee
@@ -117,7 +117,8 @@

   # automatically adapt margin of content to height of the banner
   # (useful because the banner adapt itself when scrolling and different height of banners are used)
-  $('#content').css('marginTop', $('#banner').outerHeight(true))
+  window.onresize = ->
+    $('#content').css('marginTop', $('#banner').outerHeight(true))

   # send new message (invite friends)
   $('.popup').on('click', ->


Make sure application_serializer is always required first

--- app/assets/javascripts/e_tahi.js.coffee
+++ app/assets/javascripts/e_tahi.js.coffee
@@ -2,6 +2,8 @@
 #= require ./store
 #= require ./event_stream_actions
 #= require_tree ./mixins
+#= require ./serializers/application_serializer
+#= require_tree ./serializers
 #= require_tree ./models
 #
 #= require ./controllers/task_controller


Fix case on require

Fix case on require

--- app/assets/javascripts/map-notes.coffee
+++ app/assets/javascripts/map-notes.coffee
@@ -1,6 +1,6 @@
 #= require 'leaflet/dist/leaflet-src.js'
 #= require 'proj4/dist/proj4-src.js'
-#= require 'Proj4Leaflet/src/proj4leaflet.js'
+#= require 'proj4leaflet/src/proj4leaflet.js'
 #= require 'leaflet-draw/dist/leaflet.draw-src.js'
 #= require 'polarmap/dist/polarmap-src.js'
 #= require 'autosize'


adding gratuitous animation to the pie chart mouse overs

--- app/assets/javascripts/result.js.coffee
+++ app/assets/javascripts/result.js.coffee
@@ -1,15 +1,29 @@
-window.HQuery =
+window.HQuery =
   drawChart: (type, title, keys, values) ->
     r = Raphael "chart"
     r.g.txtattr.font = "12px 'Fontin Sans', Fontin-Sans, sans-serif"
     r.g.text(320, 100, title).attr({"font-size": 20})
-
+
     switch type
       when 'pie'
-        r.g.piechart 320, 240, 100, values, {legend: keys, legendpos: "west"}
+        fin = ->
+          @sector.stop()
+          @sector.scale 1.1, 1.1, @cx, @cy
+          if @label
+            @label[0].stop()
+            @label[0].scale 1.5
+            @label[1].attr {"font-weight": 800}
+        fout = ->
+          @sector.animate {scale: [1, 1, @cx, @cy]}, 500, "bounce"
+          if @label
+            @label[0].animate {scale: 1}, 500, "bounce"
+            @label[1].attr {"font-weight": 400}
+
+        pie = r.g.piechart 320, 240, 100, values, {legend: keys, legendpos: "west"}
+        pie.hover fin, fout
       when 'bar'
-        fin = -> this.flag = r.g.popup(@bar.x, @bar.y, @bar.value or "0").insertBefore(this)
-        fout = -> this.flag.animate {opacity: 0}, 300, ->
+        fin = -> @flag = r.g.popup(@bar.x, @bar.y, @bar.value or "0").insertBefore(this)
+        fout = -> @flag.animate {opacity: 0}, 300, ->
                                                   this.remove()
         chart = r.g.barchart 100, 100, 300, 300, values
         chart.label keys


Modifying parameters passed down to IoTAppPublisher so that we use the bluprint uuid instead of the flow uuid as a key when publishing IoT Apps

--- src/controllers/iot-app-controller.coffee
+++ src/controllers/iot-app-controller.coffee
@@ -54,14 +54,19 @@

   publish: (req, res) =>
     {appId, version}  = req.params
+    {flowId} = req.body
+    debug("Publishing for AppId #{appId} Version: #{version}")
+    debug("Generating new token for Flow", flowId)
     meshbluHttp       = @_createMeshbluHttp req.meshbluAuth

     meshbluHttp.generateAndStoreTokenWithOptions appId, {tag: 'nanocyte-flow-deploy-service'}, (error, {token}={}) =>
+      debug("Error on generate and store token", error) if error?
       return res.status(error.code ? 403).send(error.message) if error?

-      options         = appId: appId, appToken: token, version: version
+      options         = appId: appId, flowId: flowId, appToken: token, version: version
       iotAppPublisher = @_createIotAppPublisher options
       iotAppPublisher.publish (error) =>
+        debug("Published the IoTApp and we got an error", error) if error
         return res.status(error.code ? 422).send(error.message) if error?
         res.sendStatus(201)


allow docs with raw parameter fns

allow docs with raw parameter fns

--- ops.coffee
+++ ops.coffee
@@ -87,6 +87,7 @@
 cmd 'docs', 'Shows the documentation for a graphite function or parameter', (name) ->
   if name?
     name = name.to_js_string() if name.to_js_string?
+    name = name._lead_op?.name if name._lead_op?
     dl = lead.graphite.function_docs[name]
     if dl?
       pres = dl.getElementsByTagName 'pre'


activity: fix blockquote marker length [#75889126]

--- client/Social/Activity/views/inputview.coffee
+++ client/Social/Activity/views/inputview.coffee
@@ -90,15 +90,17 @@

     for part, index in value.split '```'
       blockquote = index %% 2 is 1
-      read += part.length + (if blockquote then 0 else 3)
+      read += part.length + (if blockquote then 0 else 6)
       break  if read > position

     if blockquote
     then @insertNewline()
     else @emit 'Enter'

+
   insertNewline: ->
     document.execCommand 'insertText', no, "\n"
+

   getPosition: ->
     {startContainer, startOffset} = KD.utils.getSelectionRange()


Deprecate the kiteController.run, but try to fetch region if its not provided

--- client/app/MainApp/kite/kitecontroller.coffee
+++ client/app/MainApp/kite/kitecontroller.coffee
@@ -89,9 +89,13 @@
     correlationName = options.correlationName or ""

     if options.kiteName is "os" and not correlationName
+      warn "THIS METHOD DEPRECATED, PLEASE USE vmController.run with vmName !"
       warn """OS kite call requested without providing
               correlationName, using default if exists."""
-      correlationName = KD.getSingleton('vmController').defaultVmName
+
+      vmc = KD.getSingleton('vmController')
+      correlationName  = vmc.defaultVmName
+      options.kiteName = "os-#{vmc.vmRegions[vmc.defaultVmName]}"

     kite = @getKite options.kiteName, correlationName


Fix subnets view when comming from object lookup

--- plugins/networking/app/assets/javascripts/networking/plugin/subnets.coffee
+++ plugins/networking/app/assets/javascripts/networking/plugin/subnets.coffee
@@ -240,3 +240,10 @@

 $(document).on 'modal:contentUpdated', (e) ->
   $('*[data-network-subnets]').each () -> new Subnets(this)
+
+$ ->
+  # init the subnets also on plain pages like from object lookup and wait until policy
+  if typeof policy == 'undefined'
+    setTimeout ( ->
+      $('*[data-network-subnets]').each () -> new Subnets(this)
+    ), 1000


Shift to ending state when social channel is deleted

--- client/ide/lib/collaborationcontroller.coffee
+++ client/ide/lib/collaborationcontroller.coffee
@@ -287,7 +287,9 @@

   bindSocialChannelEvents: ->

-    @socialChannel.on 'AddedToChannel', @bound 'participantAdded'
+    @socialChannel
+      .on 'AddedToChannel', @bound 'participantAdded'
+      .on 'ChannelDeleted', @bound 'channelDeleted'

   participantAdded: (participant) ->
@@ -300,6 +302,11 @@
       {nickname} = account.profile
       @statusBar.createParticipantAvatar nickname, no
       @watchParticipant nickname
+
+
+  channelDeleted: ->
+
+    @stateMachine.transition 'Ending'

   bindRealtimeEvents: ->


Use name in item_dial

Use name in item_dial

--- client/templates/items_dial/items_dial.coffee
+++ client/templates/items_dial/items_dial.coffee
@@ -61,7 +61,7 @@
         if @name then @name else @emails[0].address
   item_title: ->
     if Router.current().route.getName() is "projectPage"
-        @emails[0].address
+        if @name then @name else @emails[0].address
     else
         @title
   svg_icon: ->


Update course schema

Update course schema

--- app/schemas/models/course.schema.coffee
+++ app/schemas/models/course.schema.coffee
@@ -14,6 +14,7 @@
   screenshot: c.path { title: 'URL', description: 'Link to course screenshot.'}
   adminOnly: { type: 'boolean', description: 'Deprecated in favor of releasePhase.' }
   releasePhase: { enum: ['beta', 'released'], description: "How far along the course's development is, determining who sees it." }
+  isOzaria: { type: 'boolean', description: 'Is this an ozaria course' }

 c.extendBasicProperties CourseSchema, 'Course'
 c.extendTranslationCoverageProperties CourseSchema


example/netsniff.coffee: Format the output to HAR 1.2 specification.

--- examples/netsniff.coffee
+++ examples/netsniff.coffee
@@ -1,31 +1,109 @@
+if not Date::.toISOString
+    Date::.toISOString = ->
+        pad = (n) ->
+            if n < 10 then '0' + n else n
+        ms = (n) ->
+            if n < 10 then '00' + n else (if n < 100 then '0' + n else n)
+        @getFullYear() + '-' +
+        pad(@getMonth() + 1) + '-' +
+        pad(@getDate()) + 'T' +
+        pad(@getHours()) + ':' +
+        pad(@getMinutes()) + ':' +
+        pad(@getSeconds()) + '.' +
+        ms(@getMilliseconds()) + 'Z'
+
+createHAR = (address, title, startTime, resources) ->
+    entries = []
+
+    resources.forEach (resource) ->
+        request = resource.request
+        startReply = resource.startReply
+        endReply = resource.endReply
+
+        if not request or not startReply or not endReply
+            return
+
+        entries.push
+            startedDateTime: request.time.toISOString()
+            time: endReply.time - request.time
+            request:
+                method: request.method
+                url: request.url
+                httpVersion: 'HTTP/1.1'
+                cookies: []
+                headers: request.headers
+                queryString: []
+                headersSize: -1
+                bodySize: -1
+
+            response:
+                status: endReply.status
+                statusText: endReply.statusText
+                httpVersion: 'HTTP/1.1'
+                cookies: []
+                headers: endReply.headers
+                redirectURL: ''
+                headersSize: -1
+                bodySize: startReply.bodySize
+                content:
+                    size: startReply.bodySize
+                    mimeType: endReply.contentType
+
+            cache: {}
+            timings:
+                blocked: 0
+                dns: -1
+                connect: -1
+                send: 0
+                wait: startReply.time - request.time
+                receive: endReply.time - startReply.time
+                ssl: -1
+
+    log:
+        version: '1.2'
+        creator:
+            name: 'PhantomJS'
+            version: phantom.version.major + '.' + phantom.version.minor + '.' + phantom.version.patch
+
+        pages: [
+            startedDateTime: page.startTime.toISOString()
+            id: address
+            title: title
+            pageTimings: {}
+         ]
+        entries: entries
+
 page = new WebPage()
-requests = []
-responses = []

 if phantom.args.length is 0
     console.log 'Usage: netsniff.js <some URL>'
     phantom.exit()
 else
-    address = phantom.args[0]
+    page.address = phantom.args[0]
+    page.resources = []

     page.onLoadStarted = ->
-        page.startTime = Date.now()
+        page.startTime = new Date()

     page.onResourceRequested = (req) ->
-        req.time = Date.now() - page.startTime
-        requests.push req
+        page.resources[req.id] =
+            request: req
+            startReply: null
+            endReply: null

     page.onResourceReceived = (res) ->
-        res.time = Date.now() - page.startTime
-        responses.push res
+        if res.stage is 'start'
+            page.resources[res.id].startReply = res
+        if res.stage is 'end'
+            page.resources[res.id].endReply = res

-    page.open address, (status) ->
+    page.open page.address, (status) ->
         if status isnt 'success'
             console.log 'FAIL to load the address'
         else
-            console.log 'All requests:'
-            console.log JSON.stringify requests, undefined, 4
-            console.log ''
-            console.log 'All responses:'
-            console.log JSON.stringify responses, undefined, 4
+            page.title = page.evaluate ->
+                document.title
+
+            har = createHAR page.address, page.title, page.startTime, page.resources
+            console.log JSON.stringify har, undefined, 4
         phantom.exit()


sending all events to particles

sending all events to particles

--- src/timed_events.coffee
+++ src/timed_events.coffee
@@ -37,6 +37,7 @@
   ###
   every: (period, fn) ->
     return unless period > 0
+
     I.everyEvents.push
       fn: fn
       every: period


Nicer comment `believe` and `disbelieve` methods.

--- core/app/backbone/models/comment.coffee
+++ core/app/backbone/models/comment.coffee
@@ -7,10 +7,5 @@

   can_destroy: -> @creator().get('id') == currentUser.get('id')

-  believe: ->
-    @set('opinion', 'believes')
-    @save()
-
-  disbelieve: ->
-    @set('opinion', 'disbelieves')
-    @save()
+  believe: -> @save opinion: 'believes'
+  disbelieve: -> @save opinion: 'disbelieves'


fix indentation

fix indentation

--- services/web/app/coffee/Features/Project/ProjectEntityUpdateHandler.coffee
+++ services/web/app/coffee/Features/Project/ProjectEntityUpdateHandler.coffee
@@ -141,17 +141,17 @@
 				callback null, doc, folder_id

 	uploadFile: (project_id, folder_id, fileName, fsPath, linkedFileData, userId, callback = (error, fileRef, fileStoreUrl) ->)->
-			if not SafePath.isCleanFilename fileName
-				return callback new Errors.InvalidNameError("invalid element name")
-			fileRef = new File(
-				name: fileName
-				linkedFileData: linkedFileData
-			)
-			FileStoreHandler.uploadFileFromDisk project_id, fileRef._id, fsPath, (err, fileStoreUrl)->
-				if err?
-					logger.err err:err, project_id: project_id, folder_id: folder_id, file_name: fileName, fileRef:fileRef, "error uploading image to s3"
-					return callback(err)
-				callback(null, fileRef, fileStoreUrl)
+		if not SafePath.isCleanFilename fileName
+			return callback new Errors.InvalidNameError("invalid element name")
+		fileRef = new File(
+			name: fileName
+			linkedFileData: linkedFileData
+		)
+		FileStoreHandler.uploadFileFromDisk project_id, fileRef._id, fsPath, (err, fileStoreUrl)->
+			if err?
+				logger.err err:err, project_id: project_id, folder_id: folder_id, file_name: fileName, fileRef:fileRef, "error uploading image to s3"
+				return callback(err)
+			callback(null, fileRef, fileStoreUrl)

 	_addFileAndSendToTpds: (project_id, folder_id, fileName, fileRef, callback = (error) ->)->
 		ProjectEntityMongoUpdateHandler.addFile project_id, folder_id, fileRef, (err, result, project) ->
@@ -234,8 +234,8 @@
 					next(project_id, folder_id, fileName, fsPath, linkedFileData, userId, fileRef, fileStoreUrl, callback)
 		withLock: (project_id, folder_id, fileName, fsPath, linkedFileData, userId, fileRef, fileStoreUrl, callback = (error, fileRef, folder_id, path, fileStoreUrl) ->)->
 			ProjectEntityUpdateHandler._addFileAndSendToTpds project_id, folder_id, fileName, fileRef, (err, result, project) ->
-					return callback(err) if err?
-					callback(null, fileRef, folder_id, result?.path?.fileSystem, fileStoreUrl)
+				return callback(err) if err?
+				callback(null, fileRef, folder_id, result?.path?.fileSystem, fileStoreUrl)

 	upsertDoc: wrapWithLock (project_id, folder_id, docName, docLines, source, userId, callback = (err, doc, folder_id, isNewDoc)->)->
 		ProjectLocator.findElement project_id: project_id, element_id: folder_id, type: "folder", (error, folder) ->


Message mentions_users if they are mentioned in a pull request diff comment

--- src/github-notify.coffee
+++ src/github-notify.coffee
@@ -289,7 +289,7 @@
   # handle new comments and new issue assignments
   robot.router.post '/hubot/gh-notify', (req, res) ->
     payload = req.body
-    # event can be issues, issue_comment, pull_request
+    # event can be issues, issue_comment, pull_request, pull_request_review_comment
     event = req.headers['x-github-event']

     # get users who subscribed for mentions
@@ -307,7 +307,7 @@
         new_what = 'PR'
       userInfos = on_commented_issue(robot, issue, payload.repository, mentions_users)
       private_messages robot, userInfos, "You have been mentioned in a new #{new_what} by #{issue.user.login} in #{payload.repository.full_name}: #{issue.html_url}."
-    else if (event is 'issue_comment' and payload.action is 'created')
+    else if ((event is 'issue_comment' or event is 'pull_request_review_comment') and payload.action is 'created')
       userInfos = on_commented_issue(robot, payload.comment, payload.repository, mentions_users)
       private_messages robot, userInfos, "You have been mentioned in a new comment by #{payload.comment.user.login} in #{payload.repository.full_name}: #{payload.comment.html_url}."
     else if ((event is 'issues' or event is 'pull_request') and payload.action is 'assigned')


cistrans: slight revisions

cistrans: slight revisions

  - in plot of phe vs gen, put dark vertical lines behind white
    horizontal ones

  - in the cis-trans plot, make all points blue, rather than having
    chromosomes alternate blue/green

--- cistrans/cistrans.coffee
+++ cistrans/cistrans.coffee
@@ -46,7 +46,7 @@
   lightGray = d3.rgb(230, 230, 230)
   darkGray = d3.rgb(200, 200, 200)
   darkblue = "darkslateblue"
-  darkgreen = "darkgreen"
+  darkgreen = "darkslateblue" # "darkgreen" # <- I didn't like having separate colors for each chr in cis/trans plot
   pink = "hotpink"
   altpink = "#E9CFEC"
   purple = "#8C4374"
@@ -409,6 +409,7 @@
     plotPXG = (marker) ->
       d3.selectAll(".plotPXG").remove()

+      pxgXaxis = svg.append("g").attr("class", "probe_data").attr("class", "plotPXG").attr("id", "pxg_xaxis")
       pxgYscale = d3.scale.linear()
                      .domain([d3.min(probe_data.pheno),
                               d3.max(probe_data.pheno)])
@@ -461,7 +462,6 @@
       for i of means
         means[i] /= n[i]

-      pxgXaxis = svg.append("g").attr("class", "probe_data").attr("class", "plotPXG").attr("id", "pxg_xaxis")
       pxgXaxis.selectAll("empty")
               .data(means)
               .enter()


fix tracker lack of ip bug

fix tracker lack of ip bug

Conflicts:

	src/tracker/reki.coffee

--- src/tracker/reki.coffee
+++ src/tracker/reki.coffee
@@ -177,7 +177,8 @@
         .ZREMRANGEBYSCORE(key_peer, 0, t - ANNOUNCE_INTERVAL * DROP_COUNT *1000)

       ip = get_vars['ip'] #REALLY SHOULD CHECK THIS IS A VALID IP
-      ip = req.client.remoteAddress if !ip?
+      ip = req.connection.remoteAddress if !ip?
+      return if !ip?
       #ip = process.env['HTTP_X_REAL_IP'] if ip is '127.0.0.1'

       peer_entry = { 'ip' : ip, 'port' : port, 'peer_id' : get_vars['peer_id'], 'compacted' : compact(ip, port) }


Do not show unknowns in review

Do not show unknowns in review

--- client/components/Review.coffee
+++ client/components/Review.coffee
@@ -121,7 +121,7 @@
     constructor: (props) ->
         super(props)
         @state =
-            results: props.data.ok
+            results: (r for r in props.data.ok when r.userList != "unknown")
         @gotoNext = @gotoNext.bind this

     gotoNext: () ->


Include aspect-ratio fix

Include aspect-ratio fix

--- client/ImageBlock.coffee
+++ client/ImageBlock.coffee
@@ -32,6 +32,10 @@

                     unless is_pinned
                         content_el.style.height = "#{ height }px"
+                    else if window.innerWidth < 1024
+                        image_el.style.height = "#{ height }px"
+                    else
+                        image_el.style.height = "100vh"

                     if not image_block.dataset.loaded and visibilityCheck(image_block)
                         if content_el.offsetWidth / px_ratio > 640


Only attempt to trigger EVENT_ANY listeners if we have any

--- chrome/src/gmailr.coffee
+++ chrome/src/gmailr.coffee
@@ -208,10 +208,12 @@
       (@observers[type] ?= []).push cb

     notify: (type, args...) ->
+      # trigger specific event
       if @observers[type]
         for listener in @observers[type]
           listener?.apply? @, args
-      if type isnt @EVENT_ANY
+      # trigger those listening on any event
+      if type isnt @EVENT_ANY and @observers[@EVENT_ANY]
         for listener in @observers[@EVENT_ANY]
           listener?.call? @, type, args
       return


Separate out the passes so they don't confuse each other.

In particular, renaming was interfering with global variable detection.

--- js2py.coffee
+++ js2py.coffee
@@ -128,6 +128,7 @@

   catchClauses = [] # TODO make this more precise

+  # passes that need to know the current function go here
   estraverse.replace c,
     enter: (c, parent) ->
       switch c.type
@@ -138,6 +139,32 @@
         when 'ThisExpression'
           if currentFunction.hoistedName
             return currentFunction.hoistedName
+
+    leave: (c, parent) ->
+      switch c.type
+        when 'Program'
+          visibleFunctions.pop()
+          currentFunction = visibleFunctions[visibleFunctions.length - 1]
+          return
+        when 'FunctionDeclaration'
+          visibleFunctions.pop()
+          currentFunction = visibleFunctions[visibleFunctions.length - 1]
+          return
+        when 'UpdateExpression'
+          if c.argument.type is 'Identifier' and c.argument.name not of currentFunction.vars
+            currentFunction.globalVars[c.argument.name] = 'number'
+          return
+        when 'AssignmentExpression'
+          if c.left.type is 'Identifier'
+            if c.left.name not of currentFunction.vars
+              currentFunction.globalVars[c.left.name] = tryGetType c.right
+            else
+              currentFunction.vars[c.left.name] = tryGetType c.right
+            return
+
+  estraverse.replace c,
+    enter: (c, parent) ->
+      switch c.type
         when 'Identifier'
           if RESERVED_IDENTS.indexOf(c.name) >= 0 and not c.generated
             c.name += '__py__'
@@ -282,27 +309,6 @@

     leave: (c, parent) ->
       switch c.type
-        when 'Program'
-          visibleFunctions.pop()
-          currentFunction = visibleFunctions[visibleFunctions.length - 1]
-          return
-        when 'FunctionDeclaration'
-          visibleFunctions.pop()
-          currentFunction = visibleFunctions[visibleFunctions.length - 1]
-          return
-        when 'UpdateExpression'
-          if c.argument.type is 'Identifier' and c.argument.name not of currentFunction.vars
-            currentFunction.globalVars[c.argument.name] = 'number'
-          return
-        when 'AssignmentExpression'
-          if c.left.type is 'Identifier'
-            if c.left.name not of currentFunction.vars
-              currentFunction.globalVars[c.left.name] = tryGetType c.right
-            else
-              currentFunction.vars[c.left.name] = tryGetType c.right
-            return
-          if c.left.type is 'MemberExpression' and c.left.property.name is 'prototype'
-            return null
         when 'ForStatement'
           body = ensure_block c.body
           body.body.unshift {


[Cartilage.View] Added a base view class that handles cleanup when dismiss() is called.


Main: Exclude messages with type join/leave from isMyPost check

--- client/Main/KD.extend.coffee
+++ client/Main/KD.extend.coffee
@@ -139,7 +139,7 @@
     else if target?.originId?
       KD.whoami()._id is target.originId

-  isMyPost: (post) -> post.account._id is KD.whoami().getId()
+  isMyPost: (post) -> post.account._id is KD.whoami().getId() and post.typeConstant not in ['join', 'leave']

   isMyChannel: (channel) -> channel.creatorId is KD.whoami().socialApiId


Make request status as 1 line log - more readable

--- client/app/initialize.coffee
+++ client/app/initialize.coffee
@@ -22,11 +22,9 @@
     timing = window.performance?.timing
     now = Math.ceil window.performance?.now()
     if timing?
-        message = """
-Response at #{timing.responseEnd - timing.navigationStart}ms
-Onload at #{timing.loadEventStart - timing.navigationStart}ms
-Page loaded in #{now}ms
-"""
+        message  = "Response: #{timing.responseEnd - timing.navigationStart}ms"
+        message += ", Onload: #{timing.loadEventStart - timing.navigationStart}ms"
+        message += ", Page loaded: #{now}ms"
         window.cozyMails.logInfo message

 # Init Web Intents


Fix the validation flow in the WSServer

Fix the validation flow in the WSServer

--- src/ws/websocket.coffee
+++ src/ws/websocket.coffee
@@ -68,20 +68,20 @@
             Db.get 'user', uid, (res) =>
                 res.owner = owner
                 @send sock, { type : 'authenticated', params : res }
-        sock._nextPing   = setTimeout (() =>
-            @sendPing sock
-        ), 60000
-        if not @socks[rid]?
-            @socks[rid] = { }
-        else
-            peers = []
-            for _uid of @socks[rid]
-                if @socks[rid][_uid]?
-                    @send @socks[rid][_uid], { type : 'peer.create' , params : { id : uid , name : name } }
-                    if _uid isnt uid
-                        peers.push { id : _uid , name : @socks[rid][_uid].name }
-            @send sock, { type : 'peer.list' , params : { peers : peers } }
-        @socks[rid][uid] = sock
+                sock._nextPing   = setTimeout (() =>
+                    @sendPing sock
+                ), 60000
+                if not @socks[rid]?
+                    @socks[rid] = { }
+                else
+                    peers = []
+                    for _uid of @socks[rid]
+                        if @socks[rid][_uid]?
+                            @send @socks[rid][_uid], { type : 'peer.create' , params : { id : uid , name : name } }
+                            if _uid isnt uid
+                                peers.push { id : _uid , name : @socks[rid][_uid].name }
+                    @send sock, { type : 'peer.list' , params : { peers : peers } }
+                @socks[rid][uid] = sock

     close : (sock, reason = 'Session closed') =>
         if sock._timeout?


Updating bitbucket-activity configuration

--- src/scripts/bitbucket-activity.coffee
+++ src/scripts/bitbucket-activity.coffee
@@ -7,7 +7,7 @@
 #
 # Configuration:
 #   HUBOT_BITBUCKET_USER
-#   HUBOT_BITBUCKET_USER
+#   HUBOT_BITBUCKET_PASSWORD
 #
 # Commands:
 #   hubot repo show <repo> - shows activity of repository


Improve routes for alerts custom paths

Improve routes for alerts custom paths

--- src/services/routes/routes.svc.coffee
+++ src/services/routes/routes.svc.coffee
@@ -36,6 +36,11 @@
         del: null
         # retrieve local kpis data
         local: null
+
+      alerts:
+        index: null
+        create: null
+        del: null

       organizations:
         appInstancesSync: null
@@ -159,12 +164,20 @@

         alerts:
           index: () ->
-            "#{defaults.mnoHub}/impac/alerts"
+            if defaults.alerts.index
+              defaults.alerts.index
+            else
+              "#{defaults.mnoHub}#{defaults.impacPrefix}/alerts"
           create: (kpi_id) ->
-            "#{defaults.mnoHub}/impac/kpis/#{kpi_id}/alerts"
-
-          delete: (alert_id) ->
-            "#{defaults.mnoHub}/impac/alerts/#{alert_id}"
+            if defaults.alerts.create
+              defaults.alerts.create.replace(':kpi_id', kpi_id)
+            else
+              "#{defaults.mnoHub}#{defaults.impacPrefix}/kpis/#{kpi_id}/alerts"
+          delete: (id) ->
+            if defaults.alerts.del
+              defaults.alerts.del.replace(':id', id)
+            else
+              "#{defaults.mnoHub}#{defaults.impacPrefix}/alerts/#{id}"

       service.organizations =
         appInstancesSync: (uid) ->


fix a bug fix a bug  模拟器bug

fix a bug fix a bug  模拟器bug

--- lib/emulator/emulator-view.coffee
+++ lib/emulator/emulator-view.coffee
@@ -25,12 +25,6 @@
       #       @button 'Debug', class: 'btn btn-primary inline-block-tight', click: 'onClickDebug'

   initialize: (serializeState) ->
-    #observe tree-view side toggle event
-    @subscribe atom.config.observe 'tree-view.showOnRightSide', callNow: false, (newValue) =>
-      @detach()
-      @attach()
-      @element.dataset.showOnLeftSide = newValue
-
     @url.text('http://localhost:3000')
     @onClickRefresh()

@@ -45,7 +39,14 @@
     @detach()

   toggle: ->
-    if @hasParent() then @detach() else @attach()
+    unless @oneInput?
+      @oneInput = "oneInput"
+      @subscribe atom.config.observe 'tree-view.showOnRightSide', callNow: false, (newValue) =>
+        @detach()
+        @attach()
+        @element.dataset.showOnLeftSide = newValue
+    else
+      if @hasParent() then @detach() else @attach()

   getTargetUrl: ->
     @url.text()


Activity: minor code styling

Activity: minor code styling

--- client/Social/Activity/sidebar/activitysidebar.coffee
+++ client/Social/Activity/sidebar/activitysidebar.coffee
@@ -143,22 +143,19 @@
       cssClass : 'kdlistitemview-sidebar-item public-feed-link'
     ,
       name     : 'koding_public_feed'
-      id       : "#{KD.getGroup().socialApiChannelId ? 1}"
-      channel  : KD.singleton "socialapi"
+      id       : KD.getGroup().socialApiChannelId ? '1'
+      channel  : KD.singleton 'socialapi'

     @public.addSubView new KDCustomHTMLView
-      cssClass : 'count'
+      cssClass : 'count hidden'
       tagName  : 'cite'
       partial  : '1'

-    # # load initial public feed
-    # KD.utils.defer -> activityController.emit 'SidebarItemClicked', @public
-

   addHotTopics: ->

-    @wrapper.addSubView @sections['hot'] = new ActivitySideView
+    @wrapper.addSubView @sections.hot = new ActivitySideView
       title      : 'HOT'
       cssClass   : 'hot topics'
       itemClass  : SidebarTopicItem
@@ -172,7 +169,7 @@

   addFollowedTopics: ->

-    @wrapper.addSubView @sections['followedTopics'] = new ActivitySideView
+    @wrapper.addSubView @sections.followedTopics = new ActivitySideView
       title      : 'Followed Topics'
       cssClass   : 'followed topics'
       itemClass  : SidebarTopicItem
@@ -186,7 +183,7 @@

   addFollowedPosts: ->

-    @wrapper.addSubView @sections['followedPosts'] = new ActivitySideView
+    @wrapper.addSubView @sections.followedPosts = new ActivitySideView
       title      : 'Followed Posts'
       cssClass   : 'threads users'
       itemClass  : SidebarPinnedItem
@@ -200,7 +197,7 @@

   addMessages: ->

-    @wrapper.addSubView @sections['messages'] = new ActivitySideView
+    @wrapper.addSubView @sections.messages = new ActivitySideView
       title      : 'Messages'
       cssClass   : 'inbox users'
       itemClass  : SidebarMessageItem
@@ -215,7 +212,7 @@

   addChat: ->

-    @wrapper.addSubView @sections['chat'] = new ActivitySideView
+    @wrapper.addSubView @sections.chat = new ActivitySideView
       title    : 'Chat'
       cssClass : 'chat users'
       itemClass : SidebarChatMemberItem


sidebar: seperate ws unreadCount update function

--- client/Social/Activity/sidebar/activitysidebar.coffee
+++ client/Social/Activity/sidebar/activitysidebar.coffee
@@ -124,8 +124,6 @@

     item.setUnreadCount? unreadCount

-    @setWorkspaceUnreadCount data, unreadCount  if @workspaceItemChannelMap[data._id]
-

   setWorkspaceUnreadCount: (data, unreadCount) ->

@@ -158,9 +156,11 @@
         when 'announcement' then 2
         else 0

-      item = @addItem data, index
-
-      @setUnreadCount item, data, unreadCount
+      if KD.utils.isChannelCollaborative data
+        @setWorkspaceUnreadCount data, unreadCount
+      else
+        item = @addItem data, index
+        @setUnreadCount item, data, unreadCount

   # when a comment is added to a post
@@ -214,8 +214,11 @@
       # but it's left there just to be more cautious. ~Umut
       @fetchWorkspaces()  if update.isParticipant and isPrivateMessage

-      item = @addItem channel, index
-      @setUnreadCount item, channel, unreadCount
+      if KD.utils.isChannelCollaborative channel
+        @setWorkspaceUnreadCount channel, unreadCount
+      else
+        item = @addItem channel, index
+        @setUnreadCount item, channel, unreadCount

   accountRemovedFromChannel: (update) ->


CoverImage: always select a size even if the aspect ratios don't work out.

--- client/CoverImage.coffee
+++ client/CoverImage.coffee
@@ -48,7 +48,7 @@
                         comparison_dimension = comparison_dimension * px_ratio

                         for size in sizes
-                            if size[comparison_size] > comparison_dimension
+                            if size[comparison_size] > comparison_dimension or size is sizes[sizes.length - 1]
                                 selected_size = size[0]
                                 break


Fix getCookie.

Fix getCookie.

--- coffee/chaplin/lib/utils.coffee
+++ coffee/chaplin/lib/utils.coffee
@@ -85,12 +85,12 @@

     # Get a cookie by its name
     getCookie: (key) ->
-      keyPosition = document.cookie.indexOf "#{key}="
-      return false if keyPosition is -1
-      start = keyPosition + key.length + 1
-      end = document.cookie.indexOf ';', start
-      end = document.cookie.length if end is -1
-      decodeURIComponent(document.cookie.substring(start, end))
+      pairs = document.cookie.split('; ')
+      for pair in pairs
+        val = pair.split('=')
+        if decodeURIComponent(val[0]) is key
+          return decodeURIComponent(val[1] or '')
+      null

     # Set a session cookie


Migration: Removed unknown data from unknown method

--- client/Main/utils.coffee
+++ client/Main/utils.coffee
@@ -536,7 +536,7 @@

       # embedly supports many error types. we could display those to the user
       when 'error'
-        log 'Embedding error ', data.error_type, data.error_message
+        log 'Embedding error '
         return 'error'

       else


Router: Simplify machine getter function

--- client/ide/routes.coffee
+++ client/ide/routes.coffee
@@ -54,17 +54,13 @@

   getMachine = (label, username) ->
-    machine = null

     for m in KD.userMachines

-      hasSameLabel = (m.label is label) or (m.slug is label)
-      hasSameUser  = m.getOwner() is username
-
-      if hasSameLabel and hasSameUser
-        machine = m
-
-    return machine
+      sameLabel = (m.label is label) or (m.slug is label)
+      sameUser  = m.getOwner() is username
+
+      return m  if sameLabel and sameUser

   loadIDE = (data) ->


SocialWorker: JPermissionSet: replace sinkrow with async

--- workers/social/lib/social/models/group/permissionset.coffee
+++ workers/social/lib/social/models/group/permissionset.coffee
@@ -1,20 +1,7 @@
-
-{ Model, secure, dash, daisy } = require 'bongo'
+async                    = require 'async'
+cacheManager             = require 'cache-manager'
+{ Model, secure, }       = require 'bongo'
 { Module, Relationship } = require 'jraphical'
-
-cacheManager = require 'cache-manager'
-
-# class JPermission extends Model
-#   @set
-#     indexes   :
-#       module  : 'sparse'
-#       title   : 'sparse'
-#       roles   : 'sparse'
-#     schema    :
-#       module  : String
-#       title   : String
-#       body    : String
-#       roles   : [String]

 module.exports = class JPermissionSet extends Module

@@ -129,15 +116,16 @@
     # permission checker helper, walks on the all required permissions
     # if one of them passes, breaks the loop and returns true
     kallback = (current, main) ->
-
-      queue = advanced.map ({ permission, validateWith, superadmin }) -> ->
+      hasPermission = no
+
+      queue = advanced.map ({ permission, validateWith, superadmin }) -> (fin) ->

         if superadmin

           # if permission requires superadmin and current group is not 'koding'
           # or if somehow 'koding' group (main) not exists then pass ~ GG
           if currentGroup isnt MAIN_GROUP or not main
-            return queue.next()
+            return fin()

           # if permission requires superadmin then do the permission check on
           # main group and permissionSet (which is 'koding' group) ~ GG
@@ -151,17 +139,13 @@
         validateWith ?= anyValidator

         validateWith.call target, client, group, permission, permissionSet, args,
-          (err, hasPermission) ->
-            if err then queue.next err
-            else if hasPermission
-              callback null, yes  # we can stop here.  One permission is enough.
-            else queue.next()
-
-      queue.push ->
-        # if we ever get this far, it means the user doesn't have permission.
-        callback null, no
-
-      daisy queue
+          (err, hasPermission_) ->
+            hasPermission = yes  if hasPermission_
+            fin err
+
+      async.parallel queue, (err) ->
+        return callback err  if err
+        callback null, hasPermission

     # set groupName from given target or client
     client.groupName = getGroupnameFrom target, client


[Resurrection] Removed loggeding home static page

--- workers/social/lib/social/render/loggedin/kodinghome.coffee
+++ workers/social/lib/social/render/loggedin/kodinghome.coffee
@@ -15,44 +15,9 @@
       <title>Koding</title>
       #{getStyles()}

-
+

-
-
-

-

-
-

-
-

-
-

-
-          #{getSidebar account}
-
-

-

-

-

-                  #{getStatusWidget account}
-                  #{getInnerNav()}
-

-

-
Latest Activity

-

-

-
Loading...

-

-

-

-

-

-

-

-

-

+

       #{KONFIG.getConfigScriptTag { roles: ['guest'], permissions: [] } }
       #{scripts}


Fix config service.

Fix config service.

--- app/static/script/service/config.coffee
+++ app/static/script/service/config.coffee
@@ -2,16 +2,15 @@

 angular.module('app')
   .factory 'config', ['$http', '$log', ($http, $log) ->
-    @config = {}
+    config = {}

     # TODO Use Gulp to generate config.js
     $http.get('/config.json', {async: false, cache: true})
       .success (response) =>
-        $log.debug 'Configuration loaded.'
-        @config = response
+        $log.info 'Configuration loaded.'
+        _.extend config, response
       .error (response) ->
         $log.error 'Could not load configuration!'

-    get: =>
-      @config
+    config
   ]


improve script task

improve script task

--- app/templates/gulp/tasks/scripts.coffee
+++ app/templates/gulp/tasks/scripts.coffee
@@ -6,8 +6,8 @@
 path        = require('../gulpconfig').path
 webpackConf = require '../webpack.config.base'

-runWebPack = (entries, config, done) ->
-  config = assign {entry: entries}, config, webpackConf
+runWebPack = (config, done) ->
+  config = assign config, webpackConf

   webpack(config).run (err, stats) ->
     if err
@@ -29,7 +29,7 @@
   entries =
     "main": "./#{path.dev.js}/main.js"

-  runWebPack(entries, {}, done)
+  runWebPack({ entry: entries }, done)


:art: Merge exec and execNode tests

:art: Merge exec and execNode tests

--- spec/helper-spec.coffee
+++ spec/helper-spec.coffee
@@ -1,50 +1,40 @@
 fs = require 'fs'
 helpers = require '../lib/helpers'
 testFile = __dirname + '/fixtures/test.txt'
-testContents = fs.readFileSync(testFile).toString().trim()
+testContents = fs.readFileSync(testFile).toString()
 describe 'linter helpers', ->
-  describe '::exec', ->
+  describe '::exec*', ->
     it 'cries when no argument is passed', ->
-      expect ->
-        helpers.exec()
-      .toThrow()
+      expect( -> helpers.exec()).toThrow()
+      expect( -> helpers.execNode()).toThrow()
     it 'works', ->
+      waitsForPromise ->
+        helpers.execNode(__dirname + '/fixtures/something.js').then (data) ->
+          expect(data).toBe('STDOUT')
       waitsForPromise ->
         helpers.exec('cat', [testFile]).then (text) ->
           expect(text).toBe(testContents)
     it 'lets you choose streams', ->
       waitsForPromise ->
+        helpers.execNode(__dirname + '/fixtures/stderr.js', [], {stream: 'stderr'}).then (data) ->
+          expect(data).toBe('STDERR')
+      waitsForPromise ->
         helpers.exec('cat', [testFile], stream: 'stderr').then (text) ->
           expect(text).toBe('')
     it 'accepts stdin', ->
+      waitsForPromise ->
+        helpers.execNode(__dirname + '/fixtures/something.js', ['input'], {stream: 'stdout', stdin: 'Wow'}).then (data) ->
+          expect(data).toBe('STDOUTWow')
       waitsForPromise ->
         helpers.exec('cat', [], stream: 'stdout', stdin: testContents).then (text) ->
           expect(text).toBe(testContents)
     it "throws if stderr is written to but wasn't expected", ->
       waitsForPromise ->
+        helpers.execNode(__dirname + '/fixtures/stderr.js', []).catch (message) ->
+          expect(message).toBe('STDERR')
+      waitsForPromise ->
         helpers.exec(__dirname + '/fixtures/stderr.sh', []).catch (message) ->
           expect(message).toBe("STDERR\n")
-  describe '::execNode', ->
-    it 'cries when no argument is passed', ->
-      expect ->
-        helpers.execNode()
-      .toThrow()
-    it 'works', ->
-      waitsForPromise ->
-        helpers.execNode(__dirname + '/fixtures/something.js').then (data) ->
-          expect(data).toBe('STDOUT')
-    it 'lets you choose streams', ->
-      waitsForPromise ->
-        helpers.execNode(__dirname + '/fixtures/stderr.js', [], {stream: 'stderr'}).then (data) ->
-          expect(data).toBe('STDERR')
-    it 'accepts stdin', ->
-      waitsForPromise ->
-        helpers.execNode(__dirname + '/fixtures/something.js', ['input'], {stream: 'stdout', stdin: 'Wow'}).then (data) ->
-          expect(data).toBe('STDOUTWow')
-    it "throws if stderr is written to but wasn't expected", ->
-      waitsForPromise ->
-        helpers.execNode(__dirname + '/fixtures/stderr.js', []).catch (message) ->
-          expect(message).toBe('STDERR')
   describe '::parse', ->
     it 'cries when no argument is passed', ->
       expect ->


Added some more tests for numbers.

Added some more tests for numbers.


Remove explicit atom.workspace.destroy call

--- spec/spec-helper.coffee
+++ spec/spec-helper.coffee
@@ -111,9 +111,6 @@

   atom.workspaceView?.remove?()
   atom.workspaceView = null
-
-  atom.workspace?.destroy()
-  atom.workspace = null
   delete atom.state.workspace

   atom.project?.destroy()


Avoid mutating Trix.config

Avoid mutating Trix.config

--- src/trix/views/attachment_view.coffee
+++ src/trix/views/attachment_view.coffee
@@ -91,7 +91,7 @@

   getCaptionConfig: ->
     type = @attachment.getType()
-    config = Trix.config.attachments[type]?.caption ? {}
+    config = Trix.copyObject(Trix.config.attachments[type]?.caption)
     config.name = true if type is "file"
     config


div fix

div fix

--- coffee/lib/formatters.coffee
+++ coffee/lib/formatters.coffee
@@ -85,7 +85,7 @@
     IMG_PREVIEW_FORMATTER:
       [/([^<]+)<\/a>/g
       , (_m, link, ext, alt) ->
-        "<div class=\"preview\"
"
+        "

"
       ]

     markdown: (raw) ->


Update index.coffee

Update index.coffee

Adds missing EmailInput and RadioInput requires in Form Components index.coffee.

--- modules/components/forms/index.coffee
+++ modules/components/forms/index.coffee
@@ -1,14 +1,16 @@
 module.exports =
-  CheckboxInput:         require './checkbox_input'
-  ErrorSummary:          require './error_summary'
-  FieldErrors:           require './field_errors'
-  FieldHint:             require './field_hint'
-  FieldWrapper:          require './field_wrapper'
-  Label:                 require './label'
-  NumberInput:           require './number_input'
-  PasswordInput:         require './password_input'
-  SelectInput:           require './select_input'
-  SubForm:               require './subform'
-  SubFormArray:          require './subform_array'
-  TextInput:             require './text_input'
-  TextAreaInput:         require './textarea_input'
+  CheckboxInput: require './checkbox_input'
+  EmailInput:    require './email_input'
+  ErrorSummary:  require './error_summary'
+  FieldErrors:   require './field_errors'
+  FieldHint:     require './field_hint'
+  FieldWrapper:  require './field_wrapper'
+  Label:         require './label'
+  NumberInput:   require './number_input'
+  PasswordInput: require './password_input'
+  RadioInput:    require './radio_input'
+  SelectInput:   require './select_input'
+  SubForm:       require './subform'
+  SubFormArray:  require './subform_array'
+  TextInput:     require './text_input'
+  TextAreaInput: require './textarea_input'


Update wrapper

Update wrapper

--- templates/coffee.coffee
+++ templates/coffee.coffee
@@ -42,13 +42,23 @@
   args    = _.filter(_.pluck(options.dependencies, 'argument'))
   globals = _.filter(_.pluck(options.dependencies, 'global'))

-  s "((root, factory) ->"
+  s "((factory) ->"
+  s ""
+  s "  # Browser and WebWorker"
+  s "  root = if typeof self is 'object' and self?.self is self"
+  s "    self"
+  s ""
+  s "  # Server"
+  s "  else if typeof global is 'object' and global?.global is global"
+  s "    global"
   s ""
   s "  # AMD"
   s "  if typeof define is 'function' and define.amd"

+  # https://github.com/jashkenas/backbone/blob/master/backbone.js
+
   if deps.length
-    s "    define [#{deps.map(amdRequire).join(', ')}], (#{args.join(', ')}) ->"
+    s "    define [#{deps.concat('exports').map(amdRequire).join(', ')}], (#{args.join(', ')}) ->"
     s (if options.global
       "      root.#{options.global} = "
     else "      ") + "factory(#{['root'].concat(args).join(', ')})"
@@ -62,7 +72,8 @@

   s ""
   s "  # CommonJS"
-  s "  else if typeof module is 'object' && typeof module.exports is 'object'"
+  s "  else if typeof module is 'object' and module isnt null and"
+  s "          module.exports? and typeof module.exports is 'object'"

   s (if options.global
     "    module.exports = "
@@ -80,7 +91,7 @@
   s "  # No return value"
   s "  return"
   s ""
-  s ")(this, (#{['__root__'].concat(args).join(', ')}) ->"
+  s ")((#{['__root__'].concat(args).join(', ')}) ->"

   s options.indent + options.contents
   unless options.global


fixed a typo which was causing a crash

fixed a typo which was causing a crash

--- components/ConCat.coffee
+++ components/ConCat.coffee
@@ -23,7 +23,7 @@
     @inPorts.delimiter.on 'data', (data) =>
       @delimiter = data
     @inPorts.addstring.on 'data', (data) =>
-      @addString = data
+      @addstring = data
     @inPorts.in.on 'data', (data) =>
       @outPorts.out.send data.concat @delimiter, @addstring
     @inPorts.in.on 'disconnect', =>


Add missing documentation for sleep/mute functionality, improve snark documentation

--- kills.coffee
+++ kills.coffee
@@ -2,8 +2,9 @@
 #
 # OscarRobo losses - Ask about recent losses by your corp
 # OscarRobo kills - Ask about recent kills by your corp
+# OscarRobo sleep - Mute OscarRobo's snark for 15 minutes
 # I hate <target> - Oscar will insult <target>
-# (and in random conversation) - Oscar will otherwise snark in channel, spam with image macros, and othwerwise be a twit until banned
+# ...(and in random conversation) - Oscar will otherwise snark in channel, spam with image macros, and othwerwise be a twit until muted

 # Must Set HUBOT_CORP_ID environment var in hubot's ENV
 # Must Set HUBOT_OPERATOR_EMAIL environment var as well
@@ -93,7 +94,7 @@
     if (awake(robot))
       msg.send "http://imagemacros.files.wordpress.com/2010/02/roger_american_dad_technical_problems.png"

-  robot.respond /stfu|shut up|mute/i, (msg) ->
+  robot.respond /stfu|shut up|mute|sleep/i, (msg) ->
     wake = moment().add(15, 'm')
     robot.brain.set 'sleepUntil', wake
     msg.send "Do you want me to sit in a corner and rust or just fall apart where I'm standing?"


Add group select query

Add group select query

--- coffee/collection.coffee
+++ coffee/collection.coffee
@@ -21,6 +21,9 @@
       query['tags.0'] = $ne: req.query.tag.substr(1)
     else
       query['tags.0'] = req.query.tag
+
+  if typeof req.query.gruppe is 'string' and req.query.gruppe isnt ''
+    query['grupper'] = req.query.gruppe

   if typeof req.query.after is 'string' and req.query.after isnt ''
     if not isNaN(req.query.after)


use `Router.currentParams()`

use `Router.currentParams()`

--- tutor/src/components/navbar/index.cjsx
+++ tutor/src/components/navbar/index.cjsx
@@ -2,7 +2,7 @@
 BS = require 'react-bootstrap'
 {Link} = require 'react-router'
 _ = require 'underscore'
-
+Router = require '../../router'
 CourseName = require './course-name'
 ServerErrorMonitoring = require './server-error-monitoring'
 UserActionsMenu = require './user-actions-menu'
@@ -58,7 +58,7 @@

   render: ->
     {course} = @state
-    {courseId} = @props.params or {}
+    {courseId} = Router.currentParams()

     brand = <Link to='/dashboard' className='navbar-brand'>
               <i className='ui-brand-logo'></i>


pause stream while writing to mongo

pause stream while writing to mongo

--- app/coffee/MongoAWS.coffee
+++ app/coffee/MongoAWS.coffee
@@ -57,7 +57,9 @@
 				if line.length > 2
 					ops.push(JSON.parse(line))
 				if ops.length == MongoAWS.bulkLimit
+					download.pause()
 					MongoAWS.handleBulk ops.slice(0), () ->
+						download.resume()
 					ops.splice(0,ops.length)
 			.on 'end', () ->
 				MongoAWS.handleBulk ops, callback


Fixes typo

Fixes typo

--- test/ImageParser.coffee
+++ test/ImageParser.coffee
@@ -57,7 +57,7 @@
     fs.createReadStream('./test/ImageParserTest.jpg').pipe(fs.createWriteStream(path.join(testPath ,'testImage.jpg')));

     node = new TreeNode()
-    parser = new ImageParser({createThumbnail: true})
+    parser = new ImageParser({createThumbnails: true})

     parser.parse(path.join(testPath, 'testImage.jpg'), node)
     .then ->


another resolve loop fix

another resolve loop fix

--- noddy/service/oabutton/resolve.coffee
+++ noddy/service/oabutton/resolve.coffee
@@ -154,13 +154,14 @@
       else
         _doi sr
         return meta if suitable
-    while done isnt sources.length
-      if suitable
-        return meta
-      else
-        future = new Future();
-        Meteor.setTimeout (() -> future.return()), 500
-        future.wait()
+    if parallel
+      while done isnt sources.length
+        if suitable
+          return meta
+        else
+          future = new Future();
+          Meteor.setTimeout (() -> future.return()), 500
+          future.wait()

   if titles
     if not meta.title and meta.doi
@@ -212,9 +213,7 @@
             future = new Future();
             Meteor.setTimeout (() -> future.return()), 500
             future.wait()
-      else
-        return meta # this happens when no suitable result could be found
-
+

   if not meta.url and journal and meta.journal and 'doaj' in sources # can check DOAJ for journal
     try


allow flush to complete in acceptance test

--- test/acceptance/coffee/DeletingAProjectTests.coffee
+++ test/acceptance/coffee/DeletingAProjectTests.coffee
@@ -148,7 +148,7 @@
 						# after deleting the project and putting it in the queue, flush the queue
 						setTimeout () ->
 							DocUpdaterClient.flushOldProjects (error, res, body) =>
-								done()
+							setTimeout done, 1000 # allow time for the flush to complete
 						, 100
 				, 200


fixed silly test problem

fixed silly test problem

--- test/legacy/driver/fractalCacheHelpers.mocha.coffee
+++ test/legacy/driver/fractalCacheHelpers.mocha.coffee
@@ -19,8 +19,8 @@

 toSegmentTreeWithMeta = ({prop, loading, meta, splits}) ->
   rootSegment = SegmentTree.fromJS({prop})
-  rootSegment.mata = {}
-  rootSegment.mata[k] = v for k, v of meta
+  rootSegment.meta = {}
+  rootSegment.meta[k] = v for k, v of meta
   rootSegment.markLoading() if loading
   rootSegment.setSplits(splits.map(toSegmentTreeWithMeta)) if splits
   return rootSegment


change experiment to version 3

change experiment to version 3

--- app/lib/experiments.coffee
+++ app/lib/experiments.coffee
@@ -9,7 +9,7 @@
   Define the active experiment here by using a string which exists in http://experiments.zooniverse.org/active_experiments
   If no experiments should be running right now, set this to null, false or ""
   ###
-  ACTIVE_EXPERIMENT: "SerengetiMessagingExperiment2"
+  ACTIVE_EXPERIMENT: "SerengetiMessagingExperiment3"
   #ACTIVE_EXPERIMENT: "SerengetiBlanksExperiment1"
   ###
   The URL of the experiment server to use