Send each formatted Sprint.ly item as a separate message

--- src/scripts/sprintly.coffee
+++ src/scripts/sprintly.coffee
@@ -59,8 +59,11 @@
   (err, res, body) ->
     if res.statusCode == 200
       payload = JSON.parse(body)
-      items = payload.map (item) ->
-        "##{item.number} #{item.score} #{if item.type isnt 'story' then "#{item.type}: " else ""}#{item.title} https://sprint.ly/#!/product/#{item.product.id}/item/#{item.number}"
-      msg.send items.join("\n")
+      for item in payload
+        message = "##{item.number} (#{item.score}) "
+        message += "#{item.type}: " if item.type isnt 'story'
+        message += item.title
+        message += " https://sprint.ly/#!/product/#{item.product.id}/item/#{item.number}"
+        msg.send(message)
     else
       msg.send "Something came up: #{body}"


Move convenience methods onto the ContextNodeModel

The _internal_ nodes drive the state of their public nodes, therefore
the methods do not need to be on the BaseNode (in which the public nodes
are derived from).

--- coffee/cilantro/models/context/nodes/base.coffee
+++ coffee/cilantro/models/context/nodes/base.coffee
@@ -13,15 +13,6 @@
             super(options)
             @set(attrs, silent: true)
             return
-
-        enable: ->
-            @set('enabled', true)
-
-        disable: ->
-            @set('enabled', false)
-
-        isEnabled: ->
-            @get('enabled') isnt false

         # Attempts to fetch a node relative to this one. The `query` is a set
         # of attributes the target node must match in order to be returned.
@@ -132,6 +123,15 @@
             # Save the initial attributes to the public node.
             @save(silent: true)

+        enable: ->
+            @set('enabled', true, save: true)
+
+        disable: ->
+            @set('enabled', false, save: true)
+
+        isEnabled: ->
+            @get('enabled') isnt false
+
         # Override to create a copy of the internal attributes for exposing
         # as `public` attributes.
         save: (options) ->


Rename itemForElement to viewForItem

Rename itemForElement to viewForItem

--- src/select-list-view.coffee
+++ src/select-list-view.coffee
@@ -119,7 +119,7 @@

       for i in [0...Math.min(filteredArray.length, @maxItems)]
         element = filteredArray[i]
-        item = @itemForElement(element)
+        item = @viewForItem(item)
         item.data('select-list-element', element)
         @list.append(item)


Added comments

Added comments

--- coffee/hello_world_application.coffee
+++ coffee/hello_world_application.coffee
@@ -26,12 +26,12 @@
       @initControllers()

       # Register all routes and start routing
+      @initRouter routes
       # You might pass Router/History options as the second parameter.
       # Chaplin enables pushState per default and Backbone uses / as
       # the root per default. You might change that in the options
       # if necessary:
       # @initRouter routes, pushState: false, root: '/subdir/'
-      @initRouter routes

       # Freeze the application instance to prevent further changes
       Object.freeze? this
@@ -39,13 +39,18 @@
     # Override standard layout initializer
     # ------------------------------------
     initLayout: ->
-      # Use an application-specific Layout class
+      # Use an application-specific Layout class. Currently this adds
+      # no features to the standard Chaplin Layout, it’s an empty placeholder.
       @layout = new Layout {@title}

     # Instantiate common controllers
     # ------------------------------
     initControllers: ->
       # These controllers are active during the whole application runtime.
+      # You don’t need to instantiate all controllers here, only special
+      # controllers which do not to respond to routes. They may govern models
+      # and views which are needed the whole time, for example header, footer
+      # or navigation views.
       # e.g. new NavigationController()

     # Create additional mediator properties


Remove unused exit by force

Remove unused exit by force

--- code/li.coffee
+++ code/li.coffee
@@ -135,8 +135,6 @@
   run: (args) ->
     [instance_name, config, hook] = [ args[2], args[3], args[4] ]
     finished = ->
-      console.log "runhook finished, exiting by force."
-      process.exit 0

     Linode.get instance_name, (instance) ->
       if hook?


Removed lodash require from translate

Removed lodash require from translate

--- src/code/utils/translate.coffee
+++ src/code/utils/translate.coffee
@@ -1,5 +1,4 @@
 urlParams = require './url-params'
-_ = require 'lodash'

 languageFiles = {
   'de': require './lang/de'


don't do clue highlighting when selecting multiple cells

--- src/coffee/client/puzzle.coffee
+++ src/coffee/client/puzzle.coffee
@@ -429,7 +429,9 @@
                 answerLengths[number] = count
                 number = null

-        if @state.grid_focus == null
+        if @state.grid_focus == null or \
+                @state.grid_focus.focus.row != @state.grid_focus.anchor.row or
+                @state.grid_focus.focus.col != @state.grid_focus.anchor.col
             return {
                 primaryNumber: null,
                 secondaryNumber: null,


implement condition for smart-compression

--- src/coffee/ezBreadcrumbs.coffee
+++ src/coffee/ezBreadcrumbs.coffee
@@ -336,16 +336,25 @@
             # execute custom code after the plugin has loaded
             o.opts.onAfterLoad(_this) if $.isFunction(o.opts.onAfterLoad)

-            o.smartCompressionTimer = window.setInterval( ->
-                unless o.isParentsHidden()
-                    # trigger the resize event after the plugin has loaded
-                    o.browserWindow.trigger o.resizeKey
-
-                    window.clearInterval o.smartCompressionTimer
-            , 200)
-
-            # trigger the resize event after the plugin has loaded
-            # o.browserWindow.trigger o.resizeKey
+            # initialize the compression timer variable
+            o.smartCompressionTimer = null
+
+            # trigger the resize event after the plugin has loaded and the element has no hidden parents
+            if not o.isParentsHidden()
+                o.browserWindow.trigger o.resizeKey
+            else
+                # check if the element's parents is not hidden anymore every 200ms
+                o.smartCompressionTimer = window.setInterval( ->
+                    unless o.isParentsHidden()
+                        # clear the interval
+                        window.clearInterval o.smartCompressionTimer
+
+                        # trigger the resize event after the plugin has loaded and the element has no hidden parents
+                        o.browserWindow.trigger o.resizeKey
+
+                        # immediately set to null after clearing timer to prevent memory leaks
+                        o.smartCompressionTimer = null
+                , 200)

         o.resize = (e) ->
             current = $ this


fix bug on resizing up to the unwrap width

--- src/coffee/ezBreadcrumbs.coffee
+++ src/coffee/ezBreadcrumbs.coffee
@@ -210,18 +210,31 @@
                 if hiddenItems.length > 0
                     hiddenItems   = $ hiddenItems.get().reverse()
                     releaseItems  = []
-                    childrenWidth = o.getChildrenWidth()
-
-                    hiddenItems.each ->
-                        crumb = $ this
-                        width = crumb.data "#{ pluginName.toLowerCase() }-width"
-
-                        if typeof width isnt "undefined" and (childrenWidth + width) <= current.width()
-                            releaseItems.unshift(crumb.detach().get(0))
-
-                            childrenWidth += width
-                        else
-                            return false
+                    childrenWidth = 0
+
+                    if hiddenItems.length is 1
+                        childrenWidth = o.getChildrenWidth(true)
+
+                        itemWidth = hiddenItems.data "#{ pluginName.toLowerCase() }-width"
+
+                        if typeof itemWidth isnt "undefined" and (childrenWidth + itemWidth) <= current.width()
+                            releaseItems.unshift(hiddenItems.detach().get(0))
+
+                            childrenWidth += itemWidth
+
+                    else
+                        childrenWidth = o.getChildrenWidth()
+
+                        hiddenItems.each ->
+                            crumb = $ this
+                            width = crumb.data "#{ pluginName.toLowerCase() }-width"
+
+                            if typeof width isnt "undefined" and (childrenWidth + width) <= current.width()
+                                releaseItems.unshift(crumb.detach().get(0))
+
+                                childrenWidth += width
+                            else
+                                return false

                     if releaseItems.length > 0
                         # trigger first the beforeDecompress callback
@@ -374,13 +387,15 @@

             return null

-        o.getChildrenWidth = ->
+        o.getChildrenWidth = (includeWrapper = false) ->
             totalWidth = 0
-
-            o.el
-                .children()
-                .each ->
-                    totalWidth += ($ this).outerWidth(true)
+            filter     = o.el.children()
+            selected   = null
+
+            selected = if includeWrapper then filter.filter(":not(.#{ o.opts.holder.class })") else filter
+
+            selected.each ->
+                totalWidth += ($ this).outerWidth(true)

             return totalWidth


Fixes #3267 -- Remove source maps of deleted source files

--- command.coffee
+++ command.coffee
@@ -258,12 +258,15 @@
   sources.splice index, 1
   sourceCode.splice index, 1
   unless opts.join
-    jsPath = outputPath source, base
-    try
-      fs.unlinkSync jsPath
-    catch err
-      throw err unless err.code is 'ENOENT'
+    silentUnlink outputPath source, base
+    silentUnlink outputPath source, base, '.map'
     timeLog "removed #{source}"
+
+silentUnlink = (path) ->
+  try
+    fs.unlinkSync path
+  catch err
+    throw err unless err.code is 'ENOENT'

 # Get the corresponding output JavaScript path for a source file.
 outputPath = (source, base, extension=".js") ->


Fixed test for mutualfund

Fixed test for mutualfund

--- frontend/test/spec/services/mutualfund.coffee
+++ frontend/test/spec/services/mutualfund.coffee
@@ -1,14 +1,14 @@
 'use strict'

-describe 'Service: Mutualfund', ->
+describe 'Service: MutualFund', ->

   # load the service's module
   beforeEach module 'frontendApp'

   # instantiate service
-  Mutualfund = {}
-  beforeEach inject (_Mutualfund_) ->
-    Mutualfund = _Mutualfund_
+  MutualFund = {}
+  beforeEach inject (_MutualFund_) ->
+    MutualFund = _MutualFund_

   it 'should do something', ->
-    expect(!!Mutualfund).toBe true
+    expect(!!MutualFund).toBe true


get it working in docker

get it working in docker

--- build-index.coffee
+++ build-index.coffee
@@ -9,13 +9,14 @@
 CsvParse = require 'csv-parse'

 pypi_url = 'http://pypi.local'
+pypi_url = 'http://pypi.python.org'

 module.exports = Indexer =

 	getCondaPackages: () ->
 		result = {}
-		conda_output = child_process.execSync ' conda search ".*" --names-only '
+		conda_output = child_process.execSync 'sudo conda search ".*" --names-only '
 		names = _.without(conda_output.toString().split('\n').slice(1), '')  # skip first line of output
 		(result[name] = {
 			name: name.trim(),
@@ -43,12 +44,10 @@
 						timeout: 600 * 1000
 					request.get opts, (err, response, body) ->
 						if err?
-							error = new Error("Error: could not get #{package_name} from pypi. status: #{response?.statusCode}, #{err.message}")
-							error.err = err
-							return cb error, null
+							logger.log name: package_name, "Could not get #{package_name} from pypi. status: #{response?.statusCode}, #{err.message}"
 						info =
 							name: package_name,
-							details: if response.statusCode == 200 then body else {info: {}}
+							details: if response?.statusCode == 200 then body else {info: {}}
 						cb null, info
 				(err, results) ->
 					if err?
@@ -96,7 +95,7 @@
 			callback(null, python_packages)

 	getAptCranPackages: () ->
-		child_process.execSync ' sudo apt-get update '
+		child_process.execSync ' apt-get update '
 		apt_packages = child_process.execSync ' apt-cache search "r-cran-.*" | grep "^r-cran.*$"'
 		lines = apt_packages.toString().split('\n')
 		packages = lines.map (line) ->


Sensible defaults

Sensible defaults

--- app/scripts/alchemy/defaultConf.coffee
+++ app/scripts/alchemy/defaultConf.coffee
@@ -52,15 +52,15 @@
                                 "#f800df", "#ff8d8f",
                                 "#ffcd00", "#184fff",
                                 "#ff7e00"])
-    clusterControl: true
+    clusterControl: false

     #Stats
     nodeStats: false
     edgeStats: false

     # Filtering
-    edgeFilters: true
-    nodeFilters: true
+    edgeFilters: false
+    nodeFilters: false
     edgesToggle: false
     nodesToggle: false

@@ -69,16 +69,25 @@

     # Nodes
     nodeCaption: 'caption'
-    nodeStyle: {}
-    nodeColour: null
-    nodeMouseOver: 'caption' # partially implemented
-    nodeRadius: 10 # partially implemented
+    nodeStyle:
+        "all":
+            "radius": 10,
+            "color"  : "#68B9FE"
+            "borderColor": "#127DC1"
+            "borderWidth": (radius) ->
+                radius / 3
+            "captionColor": "#FFFFFF",
+            "captionBackground": null
+            "captionSize": 12
+    nodeColour: null # WILL BE DEPRECATED IN 1.0
+    nodeMouseOver: 'caption'
+    nodeRadius: 10 # WILL BE DEPRECATED IN 1.0
     nodeTypes: null
     rootNodes: 'root'
     rootNodeRadius: 15

     # Edges
-    edgeCaption: 'caption' # in progress
+    edgeCaption: 'caption'
     edgeClick: 'default'
     edgeStyle: (d) ->
         null
@@ -87,7 +96,7 @@
     edgeWidth: (d)-> 4
     edgeOverlayWidth: 20
     directedEdges: false
-    edgeArrowSize: 5 # pixel length of arrow
+    edgeArrowSize: 5

     # Search
     search: false


payment: emit success event when you close congrats modal

--- client/Main/payment/modal.coffee
+++ client/Main/payment/modal.coffee
@@ -74,4 +74,7 @@

     @form.showSuccess isUpgrade

+    @once 'KDModalViewDestroyed', =>
+      @emit 'PaymentWorkflowFinished', @state

+


setSettings sets maxSeverity on all emitters

--- src/Observatory.coffee
+++ src/Observatory.coffee
@@ -79,8 +79,9 @@
       if s.printToConsole is true then @subscribeLogger @_consoleLogger else @unsubscribeLogger @_consoleLogger
     # now setting max severity for all emitters
     for k,v of @emitters
-      console.log k
-    @emitters.Toolbox?.maxSeverity = @settings.maxSeverity
+      #console.log v
+      v.maxSeverity = @settings.maxSeverity
+    #@emitters.Toolbox?.maxSeverity = @settings.maxSeverity

   # Returns default logger to use in the app via warn(), debug() etc calls
   getDefaultLogger: -> @_defaultEmitter


get_server_settings; also improve concurrent logging

--- src/smc-hub/postgres.coffee
+++ src/smc-hub/postgres.coffee
@@ -67,6 +67,12 @@
         dbg = @_dbg("connect"); dbg()
         async.series([
             (cb) =>
+                if @_client?
+                    @_client.end(cb)
+                else
+                    cb()
+            (cb) =>
+                @_concurrent_queries = 0
                 dbg("first make sure db exists")
                 @_ensure_database_exists(cb)
             (cb) =>
@@ -109,6 +115,8 @@
             conflict : undefined # if given, then values must be given; appends something like this to query:
                                  #     ON CONFLICT (name) DO UPDATE SET value=EXCLUDED.value'
             cb     : undefined
+        dbg = @_dbg("_query('#{opts.query}') (concurrent=#{@_concurrent_queries})")
+        dbg()
         if opts.params? and not misc.is_array(opts.params)
             opts.cb("params must be an array")
             return
@@ -174,16 +182,20 @@
                 opts.params = p
                 opts.query += " WHERE #{z.join(' AND ')}"

-        dbg = @_dbg("_query('#{opts.query}')")
-        dbg("doing query (concurrent=#{@_concurrent_queries})")
+        dbg("query='#{opts.query}', params=#{misc.to_json(opts.params)}")
         @_concurrent_queries += 1
-        @_client.query opts.query, opts.params, (err, result) =>
+        try
+            @_client.query opts.query, opts.params, (err, result) =>
+                @_concurrent_queries -= 1
+                if err
+                    dbg("done (concurrent=#{@_concurrent_queries}) -- error: #{err}")
+                else
+                    dbg("done (concurrent=#{@_concurrent_queries}) -- success")
+                opts.cb?(err, result)
+        catch e
+            # this should never ever happen
+            dbg("EXCEPTION in @_client.query: #{e}")
             @_concurrent_queries -= 1
-            if err
-                dbg("done (concurrent=#{@_concurrent_queries}) -- error: #{err}")
-            else
-                dbg("done (concurrent=#{@_concurrent_queries}) -- success")
-            opts.cb?(err, result)
         return

     _ensure_database_exists: (cb) =>
@@ -562,7 +574,15 @@
             cb     : opts.cb

     get_server_setting: (opts) =>
-        throw Error("NotImplementedError")
+        opts = defaults opts,
+            name  : required
+            cb    : required
+        @_query
+            query : 'SELECT value FROM server_settings'
+            where :
+                "name = $::TEXT" : opts.name
+            cb    : (err, result) =>
+                opts.cb(err, result?.rows[0]?.value)

     get_site_settings: (opts) =>
         throw Error("NotImplementedError")


DataActivate now works with multiple arguments

--- src/cuffs/bindings.coffee
+++ src/cuffs/bindings.coffee
@@ -195,24 +195,31 @@
     class DataActivate extends Binding
         # Gives an element an `active` class depending on if the
         # context property matches the attribute, eg:
-        #

+        #

         @bind 'data-activate'
         constructor: (node)->
             super node
-            [@watchName, @watchValue] = @attr.split '=='
+            @values = (att.split('==') for att in @attr.split(','))
+

         activate: (value)->
-            if value == @watchValue
+            if value in (v[1] for v in @values)
                 $(@node).addClass 'active'
             else
                 $(@node).removeClass 'active'

         applyContext: (context)->
-            context.watch @watchName, (value)=> @activate value
-            $(@node).click ()=>
-                context.set @watchName, @watchValue
-            @activate context.get @watchName
+            for v in @values
+                context.watch v[0], (value)=> @activate value
+
+            console.log @node, @attr, @values
+
+            $(@node).bind 'click', =>
+                debugger
+                context.set @values[0][0], @values[0][1]
+
+            @activate context.get @values[0][0]

     class DataStyle extends Binding
         # Adds inline styling to a node, eg:
@@ -270,7 +277,7 @@
             $(node).remove()

         applyContext: (context)->
-            context.set @templateName, this
+            context.root().set @templateName, this

         render: (context)->
             clone = @template.cloneNode true


Added a DataHover binding for mouseenter/leave

--- src/cuffs/bindings.coffee
+++ src/cuffs/bindings.coffee
@@ -135,6 +135,25 @@
                 context.get(@funcName, false)(args...)
             this

+    class DataHover extends Binding
+        # Call a method on the context when we hover over an
+        # element, eg:
+        # Click Me
+        # If the mouse enters, the first argument will be `true`,
+        # if the mouse leaves, the first argument will be `false`.
+        @bind 'data-hover'
+        constructor: (node)->
+            super node
+            [@funcName, @funcArgs] = @attr.split(':')
+            @funcArgs or= ''
+
+        applyContext: (context)->
+            getArgs = =>
+                (context.get(arg) for arg in @funcArgs.split ',' when arg.trim())
+            $(@node).mouseenter =>
+                context.get(@funcName, false)(true, getArgs()...)
+            $(@node).mouseleave =>
+                context.get(@funcName, false)(false, getArgs()...)

     class DataSet extends Binding
         # Sets a certain value on the model. Sometimes useful. Eg:


Built a method filtering workflow that allows included modules to inject hooks around important functions such as prepare

--- src/spine/controller.coffee
+++ src/spine/controller.coffee
@@ -5,6 +5,8 @@
   constructor: (opts={})->
     @_nodeOpts = opts
     @_node = null
+
+    @filters = @.constructor.filters || {}

     # Delegate the Node transitions to this object.
     @node().beforeActivate = =>
@@ -36,7 +38,18 @@
     delete opts.children if opts.children

     super opts
-    @prepare()
+
+    @callWithFilters 'prepare'
+
+  callWithFilters: (methName, args...) ->
+    @callFilter("before_#{methName}")
+    @[methName].apply @, args
+    @callFilter("after_#{methName}")
+
+  callFilter: (methName)->
+    return unless @filters[methName]
+    for funcName in @filters[methName]
+      @[funcName].call @

   node: ->
     unless @_node


Reduce variation of buffer row to screen row conversion error message

--- src/display-buffer.coffee
+++ src/display-buffer.coffee
@@ -293,10 +293,9 @@
     for screenRow in [startScreenRow...endScreenRow]
       unless screenLine = @screenLines[screenRow]
         throw new Error """
-          No screen line exists for screen row #{screenRow}, converted from buffer position (#{row}, #{column})
-          Soft wrap enabled: #{@getSoftWrap()}
-          Fold count: #{@findFoldMarkers().length}
-          Last buffer row: #{@getLastRow()}
+          No screen line exists when converting a buffer row to a screen row
+          Soft wrap enabled?: #{@getSoftWrap()}
+          Folds exist?: #{@findFoldMarkers().length > 0}
         """
       maxBufferColumn = screenLine.getMaxBufferColumn()
       if screenLine.isSoftWrapped() and column > maxBufferColumn


domain

domain

--- config/crowler.coffee
+++ config/crowler.coffee
@@ -6,6 +6,7 @@
 ###

 debug = require('debug')('stdin/config/crowler')
+domain = require('domain')
 _    = require 'underscore'
 Watcher = require 'rss-watcher'
 request = require 'request'
@@ -20,33 +21,37 @@
     @createWatcher(feed)

   createWatcher : (feed)->
-    watcher = new Watcher(feed.url)
-    watcher.set
-      interval:60*3 # @todo frequency moduleがオカシイ
-    watcher.on 'new article',(article)=>
-      # あれば追加しない
-      Page.findOne link:article.link,=>
-        return debug "Already Registerd Link Is Published.#{article.link}"
-        debug "New Article. #{article.link}"
-        Page.upsertOneWithFeed article,feed,(err,page)=>
-          return app.emit 'error', err if err
-          @setToJubatus page
+    d = domain.create()
+    d.on 'error',(err)->
+      app.emit 'error',err
+    d.run ->
+      watcher = new Watcher(feed.url)
+      watcher.set
+        interval:60*3 # @todo frequency moduleがオカシイ
+      watcher.on 'new article',(article)=>
+        # あれば追加しない
+        Page.findOne link:article.link,=>
+          return debug "Already Registerd Link Is Published.#{article.link}"
+          debug "New Article. #{article.link}"
+          Page.upsertOneWithFeed article,feed,(err,page)=>
+            return app.emit 'error', err if err
+            @setToJubatus page

-          # 擬似Populate
-          pageObject = page.toObject()
-          page.feed = feed
-          app.emit 'new article',
-            page:page
+            # 擬似Populate
+            pageObject = page.toObject()
+            page.feed = feed
+            app.emit 'new article',
+              page:page

-    watcher.run (err,articles)=>
-      return app.emit 'error', err if err
+      watcher.run (err,articles)=>
+        return app.emit 'error', err if err

-      for article in articles
-        Page.upsertOneWithFeed article,feed,(err,page)=>
-          return app.emit 'error', err if err
-          @setToJubatus page
-          app.emit 'new feed',
-            feed:feed
+        for article in articles
+          Page.upsertOneWithFeed article,feed,(err,page)=>
+            return app.emit 'error', err if err
+            @setToJubatus page
+            app.emit 'new feed',
+              feed:feed

   initialize : ->
     Feed.find {}, (err,feeds)=>


changed rules for mincha on Rosh Hashana

--- js/mincha.coffee
+++ js/mincha.coffee
@@ -43,9 +43,15 @@
     mincha: round_down_to_5_minutes(sunset.subtract(3, 'hours')),
     arbit: moment(zmanim.set_hakochabim).add(10, 'minutes')
   else if hebrew_date.isRoshHashana()
-    hadlakat_nerot_is_after_set_hakochabim(sunset, hebrew_date, zmanim)
-    mincha: (round_down_to_5_minutes(if hebrew_date.isShabbat() then begin_seudat_shelishit_samuch_lemincha_ketana(hebrew_date, zmanim).subtract(2, 'hours') else sunset.subtract(1, 'hours'))),
-    arbit: if hebrew_date.isShabbat() then sunset else (if hebrew_date.is1stDayOfYomTob() || hebrew_date.isErebShabbat() then null else (show_mosaei_yom_tob(sunset, zmanim); round_down_to_5_minutes(moment(zmanim.set_hakochabim).subtract(10, 'minutes'))))
+    if hebrew_date.is1stDayOfYomTob()
+      hadlakat_nerot_is_after_set_hakochabim(sunset, hebrew_date, zmanim)
+      if hebrew_date.isShabbat()
+        mincha: round_down_to_5_minutes(begin_seudat_shelishit_samuch_lemincha_ketana(hebrew_date, zmanim).subtract(2, 'hours')), arbit: sunset
+      else
+        mincha: round_down_to_5_minutes(sunset.subtract(85, 'minutes'))
+    else
+      mincha: round_down_to_5_minutes(sunset.subtract(55, 'minutes')),
+      arbit: (if hebrew_date.isErebShabbat() then null else (show_mosaei_yom_tob(sunset, zmanim); round_down_to_5_minutes(moment(zmanim.set_hakochabim).subtract(10, 'minutes'))))
   else if hebrew_date.is9Ab()
     mincha: moment(sunset).subtract(45, 'minutes'), arbit: sunset
   else if hebrew_date.isTaanitEster()


is publishing update

is publishing update

--- src/flux/task-plan.coffee
+++ src/flux/task-plan.coffee
@@ -391,7 +391,7 @@
       not ((isPublishedOrPublishing and isPastDue) or @_isDeleteRequested(id))

     isPublishing: (id) ->
-      @_changed[id].is_publish_requested or PlanPublishStore.isPublishing(id)
+      @_changed[id]?.is_publish_requested or PlanPublishStore.isPublishing(id)

     canDecreaseTutorExercises: (id) ->
       plan = @_getPlan(id)


this is cleaner

this is cleaner

--- src/flux/task-plan.coffee
+++ src/flux/task-plan.coffee
@@ -48,7 +48,8 @@
   _server_copy: {}

   _loaded: (obj, planId) ->
-    @_server_copy[planId] = JSON.stringify(obj or {})
+    @_server_copy[planId] = JSON.stringify(obj) if _.isObject(obj)
+
     obj

   # Somewhere, the local copy gets taken apart and rebuilt.


Remove unnecessary temporary variable

Remove unnecessary temporary variable

--- src/context-menu-manager.coffee
+++ src/context-menu-manager.coffee
@@ -167,8 +167,7 @@
           if keystrokes.includes(' ')
             item.label += " [#{_.humanizeKeystroke(keystrokes)}]"
           else
-            accelerator = MenuHelpers.acceleratorForKeystroke(keystrokes)
-            item.accelerator = accelerator
+            item.accelerator = MenuHelpers.acceleratorForKeystroke(keystrokes)
       if Array.isArray(item.submenu)
         @addAccelerators(item.submenu)


Fix minor backwards compat crasher

Fix minor backwards compat crasher

--- src/core/LiterallyCanvas.coffee
+++ src/core/LiterallyCanvas.coffee
@@ -378,6 +378,8 @@
   loadSnapshot: (snapshot) ->
     return unless snapshot

+    snapshot.colors ?= @colors
+
     for k in ['primary', 'secondary', 'background']
       @setColor(k, snapshot.colors[k])


allow connecting to more than 5 campfire rooms

--- src/hubot/campfire.coffee
+++ src/hubot/campfire.coffee
@@ -89,6 +89,7 @@
         "Authorization" : self.authorization

       options =
+        "agent"  : false
         "host"   : "streaming.campfirenow.com"
         "port"   : 443
         "path"   : "/room/#{id}/live.json"


:lipstick:

:lipstick:

--- lib/buffer-find-and-replace-view.coffee
+++ lib/buffer-find-and-replace-view.coffee
@@ -141,10 +141,10 @@
     @findEditor.selectAll()
     @findEditor.focus()
   focusReplace: =>
-    if @hasClass('replace-mode')
-      @findEditor.clearSelections()
-      @replaceEditor.selectAll()
-      @replaceEditor.focus()
+    return unless @hasClass('replace-mode')
+    @findEditor.clearSelections()
+    @replaceEditor.selectAll()
+    @replaceEditor.focus()

   search: ->
     pattern = @findEditor.getText()


enh: really do steps during animation

enh: really do steps during animation

--- application.coffee
+++ application.coffee
@@ -521,13 +521,20 @@
     Tinterp = interpolateHyperbolic T

     steps = generations * stepsPerGen
+    framesBeforeGeneration = stepsPerGen
+
     timerId = setInterval (=>
       observer.navigateTo @startChain, @startOffset
       p = 1.0-steps/(stepsPerGen * generations)
       observer.modifyView Tinterp(p)
       drawEverything()
       steps -=1
-      #console.log "cur tfm: #{JSON.stringify observer.getViewOffsetMatrix()}"
+      framesBeforeGeneration -= 1
+      if framesBeforeGeneration is 0
+        console.log "Next generaion!"
+        doStep()
+        framesBeforeGeneration = stepsPerGen
+
       if steps < 0
         clearInterval timerId
         console.log "End animation"


Watch server unit tests

Watch server unit tests

--- gruntfile.coffee
+++ gruntfile.coffee
@@ -32,6 +32,7 @@
           'test/server/*.coffee'
           'test/helpers/*.coffee'
         ]
+        tasks: ['test:server']

   # Load NPM Tasks
   grunt.loadNpmTasks 'grunt-karma'


Update TwoTitle control shadowOffset

Update TwoTitle control shadowOffset

--- Framework/Views/Controls/TwoLineTitle_Framework_iOS.coffee
+++ Framework/Views/Controls/TwoLineTitle_Framework_iOS.coffee
@@ -9,7 +9,7 @@
       text: ' '
       textAlign: 'center'
       font: { fontSize: 15, fontWeight: 'bold' }
-      shadowColor: '#222', shadowOffset: { x: 1, y: 1 }
+      shadowColor: '#222', shadowOffset: { x: 0, y: -1 }
       wordWrap: false
       ellipsize: true
     })
@@ -21,7 +21,7 @@
       text: ' '
       textAlign: 'center'
       font: { fontSize: 11, fontWeight: 'bold' }
-      shadowColor: '#222', shadowOffset: { x: 0, y: 1 }
+      shadowColor: '#222', shadowOffset: { x: 0, y: -1 }
     })

     @bigtitle = Ti.UI.createLabel({
@@ -30,7 +30,7 @@
       text: ' '
       textAlign: 'center'
       font: { fontSize: 17, fontWeight: 'bold' }
-      shadowColor: '#222', shadowOffset: { x: 0, y: 1 }
+      shadowColor: '#222', shadowOffset: { x: 0, y: -1 }
     })
     @bigtitle.hide()


removed ios4 failing vm

removed ios4 failing vm

--- karma.conf.coffee
+++ karma.conf.coffee
@@ -2,7 +2,7 @@
 module.exports = (config) ->
   browsers = ['Chrome','Firefox']
   if process.env.TRAVIS
-    browsers = 'SL_Chrome,SL_Safari,SL_Firefox,SL_IE_9,SL_IE_10,SL_ANDROID_4,SL_IOS_4,SL_IOS_5,SL_IOS_6'.split(",")
+    browsers = 'SL_Chrome,SL_Safari,SL_Firefox,SL_IE_9,SL_IE_10,SL_ANDROID_4,SL_IOS_5,SL_IOS_6'.split(",")

   config.set
     frameworks: ['qunit']
@@ -61,13 +61,6 @@
         version: '4.0'
         'device-orientation': 'portrait'

-      'SL_IOS_4':
-        base: 'SauceLabs'
-        browserName: 'iphone'
-        platform: 'OS X 10.6'
-        version: '4'
-        'device-orientation': 'portrait'
-
       'SL_IOS_5':
         base: 'SauceLabs'
         browserName: 'iphone'


fix(templates): fix when the "no templates" message is displayed

--- internal_packages/composer-templates/lib/preferences-templates.cjsx
+++ internal_packages/composer-templates/lib/preferences-templates.cjsx
@@ -241,7 +241,7 @@
           else @_renderName()
       }
       {if @state.editState isnt "new" then editor}
-      {if @state.editState is "new" then noTemplatesMessage}
+      {if @state.templates.length==0 then noTemplatesMessage}
     </section>

     <section className="templates-instructions">


Remove terminal-plus package from Atom

Remove terminal-plus package from Atom

--- atom/packages.cson
+++ atom/packages.cson
@@ -21,5 +21,4 @@
   "pigments"
   "pretty-json"
   "sort-lines"
-  "terminal-plus"
 ]


Switch from emmet to emmet-simplified

Switch from emmet to emmet-simplified

This avoids numerous key-binding conflicts

--- atom/packages.cson
+++ atom/packages.cson
@@ -3,7 +3,7 @@
   "atom-react-native-autocomplete"
   "auto-indent"
   "autocomplete-modules"
-  "emmet"
+  "emmet-simplified"
   "expand-region"
   "file-icons"
   "git-diff-popup"


Add missing tab stop to Liquid elsif snippet

--- atom/snippets.cson
+++ atom/snippets.cson
@@ -857,7 +857,7 @@

   'elsif':
     'prefix': 'elseif'
-    'body': '{% elsif %}'
+    'body': '{% elsif ${1:condition} %}$2'

   'else':
     'prefix': 'else'


Different blobstore key

Different blobstore key

--- lib/integrated-learn-environment.coffee
+++ lib/integrated-learn-environment.coffee
@@ -19,7 +19,7 @@

   activate: (state) ->
     @oauthToken = atom.config.get('integrated-learn-environment.oauthToken')
-    path = atom.blobStore.get("url", "invalidation-key-1")
+    path = atom.blobStore.get("learnOpenUrl", "learn-open-url-key")
     console.log('Data mf::', path, path.toString())

     @term = new Terminal("wss://ile.learn.co:4463?token=" + @oauthToken)


Don't call shutdown on close event. The window is closing anyway.

--- src/atom/window.coffee
+++ src/atom/window.coffee
@@ -19,11 +19,7 @@
   startup: (url) ->
     @setUpKeymap()
     @attachRootView(url)
-
-    $(window).on 'close', =>
-      @shutdown()
-      @close()
-
+    $(window).on 'close', => @close()
     $(window).focus()
     atom.windowOpened this


Only autosave if our provider can save

Only autosave if our provider can save

Previously, if autosave was on and you opened a read-only example,
the Save As dialog would popup on each autosave attempt.

--- src/code/client.coffee
+++ src/code/client.coffee
@@ -154,7 +154,7 @@
       interval = Math.round(interval / 1000)
     if interval > 0
       saveIfDirty = =>
-        if @state.dirty and @state.metadata
+        if @state.dirty and @state.metadata?.provider?.can 'save'
           @save()
       @_autoSaveInterval = setInterval saveIfDirty, (interval * 1000)


Add notnnov to tree

Add notnnov to tree

--- server/cron/downloadMaterials.coffee
+++ server/cron/downloadMaterials.coffee
@@ -489,7 +489,8 @@

         groups =
             lic40: "Лицей 40",
-            zaoch: "ННГУ и другие школьники",
+            zaoch: "Нижегородские школьники",
+            notnnov: "Остальные школьники"
             stud: "Студенты и старше"
         tables = ["1А,1Б", "1В,1Г", "2", "3", "4", "5", "6", "7", "8", "main", "reg", "byWeek"]
         materials = []


fix publish user_data  under "spotlight"

fix publish user_data  under "spotlight"

--- server/publications/spotlight.coffee
+++ server/publications/spotlight.coffee
@@ -8,7 +8,7 @@

 	subHandleUsers = RocketChat.models.Users.findUsersByNameOrUsername(new RegExp(selector.name.$regex, 'i'), { limit: 10, fields: { name: 1, username: 1, status: 1 }, sort: { name: 1 } }).observeChanges
 		added: (id, fields) ->
-			data = { type: 'u', uid: id, name: fields.username + ' - ' + fields.name, status: fields.status }
+			data = { type: 'u', uid: id, username: fields.username, name: fields.username + ' - ' + fields.name, status: fields.status }
 			self.added("autocompleteRecords", id, data)
 		removed: (id) ->
 			self.removed("autocompleteRecords", id)


levels and default game states

levels and default game states


worksheet: factor out keyboard handling.

--- salvus/page/worksheet1.coffee
+++ salvus/page/worksheet1.coffee
@@ -14,6 +14,27 @@
     page = $("#worksheet1")
     worksheet1 = $("#worksheet1")
     worksheet = undefined
+
+    extraKeys =
+        "Shift-Enter":(editor) -> execute_code()
+        "Up":(editor) ->
+            if editor.getCursor().line == 0
+                focus_previous_cell()
+            else
+                throw CodeMirror.Pass
+        "Down":(editor) ->
+            if editor.getCursor().line >= editor.lineCount() - 1
+                focus_next_cell()
+            else
+                throw CodeMirror.Pass
+
+        "Esc":(editor) ->
+            interrupt_session()
+
+        "Tab":(editor) ->
+            # decide if we can "tab complete"
+            throw CodeMirror.Pass
+

     activate_salvus_cell = (cell) ->
         input = cell.find(".salvus-cell-input"
@@ -30,26 +51,8 @@
             lineWrapping   : true
             undoDepth      : 200
             autofocus      : false
-            extraKeys      :
-                'Shift-Enter':(editor) -> execute_code()
-                "Up":(editor) ->
-                    if editor.getCursor().line == 0
-                        focus_previous_cell()
-                    else
-                        throw CodeMirror.Pass
-                "Down":(editor) ->
-                    if editor.getCursor().line >= editor.lineCount() - 1
-                        focus_next_cell()
-                    else
-                        throw CodeMirror.Pass
-
-                "Esc":(editor) ->
-                    interrupt_session()
-
-                "Tab":(editor) ->
-                    # decide if we can "tab complete"
-                    throw CodeMirror.Pass
-
+            extraKeys      : extraKeys
+
         cell.data('editor',editor)
         editor.on "focus", (e) ->
             last_active_cell = active_cell = cell


adding createState test for empty args

adding createState test for empty args

--- test/giphy.spec.coffee
+++ test/giphy.spec.coffee
@@ -184,12 +184,23 @@
         @giphy.sendMessage.should.not.have.been.called

     describe '.createState', ->
-      it 'returns a valid state instance', ->
+      it 'returns a valid state instance for non-empty args', ->
         msg = { match: [ null, 'test' ] }
         state = @giphy.createState msg
         should.exist state
         state.msg.should.eql msg
         state.input.should.eql msg.match[1]
+        should.equal state.endpoint, undefined
+        should.equal state.args, undefined
+        should.equal state.options, undefined
+        should.equal state.uri, undefined
+
+      it 'returns a valid state instance for empty args', ->
+        msg = { match: [ null, null ] }
+        state = @giphy.createState msg
+        should.exist state
+        state.msg.should.eql msg
+        state.input.should.eql ''
         should.equal state.endpoint, undefined
         should.equal state.args, undefined
         should.equal state.options, undefined


chore: update the unit tests

chore: update the unit tests

--- test/utils.test.coffee
+++ test/utils.test.coffee
@@ -399,7 +399,7 @@
         utils.objectUnion([a, b, c], out)

       elapse = new Date() - start
-      expect(elapse).to.be.at.most(1000)
+      expect(elapse).to.be.at.most(1300)

     it "should be fast (refCopy)", ->
       count = 100000


Updated benchmark

Updated benchmark

--- tests/benchmark.coffee
+++ tests/benchmark.coffee
@@ -2,7 +2,7 @@
 cheerio = require("../src/cheerio")

 fs = require "fs"
-basic = fs.readFileSync "./testdata/basic.html", 'utf8'
+basic = fs.readFileSync "./files/basic/basic.html", 'utf8'

 # Quick and dirty timer - shows htmlparser is about 8x faster than jsdom


Add review job

Add review job

--- config/jobs.cson
+++ config/jobs.cson
@@ -25,3 +25,9 @@
   message: [
     "おはようございます！今日の目標は？ @everyone",
     "おはようさんです！今日は何しますか？ @everyone"]
+
+review:
+  time: "0 18 * * 1-5"
+  message: [
+    "お疲れ様です。今日の目標は達成できましたか？ @everyone",
+    "もう夕方ですね、今日はどんな事をしましたか？ @everyone"]


Find refs over a scope of FUNCTION

Find refs over a scope of FUNCTION

--- lib/Parser.coffee
+++ lib/Parser.coffee
@@ -52,22 +52,31 @@
     target

   @traverseScope: (node, target) ->
-    dests = null
+    dests = []
+
+    isBreak = false
     node.traverseChildren true, (child) ->
-      return true unless child instanceof Code
-      return true unless Parser.hasDeclarations child, target
-      dests = Parser.findSameLiterals child, target
-      false
-    unless dests?
-      dests = Parser.findSameLiterals node, target
+      return false if isBreak
+
+      unless child instanceof Code
+        if Parser.isSameLiteral child, target
+          dests.push child
+        return true
+
+      if Parser.hasDeclarations child, target
+        if Parser.isContains child, target
+          dests = Parser.findSameLiterals child, target
+          isBreak = true
+        return false
+      else
+        return true
+
     dests

   @findSameLiterals: (node, target) ->
     dests = []
     node.traverseChildren true, (child) ->
-      if child instanceof Literal and \
-         child isnt target and \
-         child.value is target.value
+      if Parser.isSameLiteral child, target
         dests.push child
     dests

@@ -75,6 +84,19 @@
     node.compileNode node
     return true for variable in node.scope.variables when variable.name is target.value
     false
+
+  @isContains: (node, target) ->
+    isContains = false
+    node.traverseChildren true, (child) ->
+      isContains or= child is target
+      !isContains
+    isContains
+
+  @isSameLiteral: (a, b) ->
+    a instanceof Literal and \
+    b instanceof Literal and \
+    a isnt b and \
+    a.value is b.value

   nodes: null


update

update

--- lib/Paypal.coffee
+++ lib/Paypal.coffee
@@ -163,7 +163,19 @@
     }, opts ? {})

     @makeAPIrequest @getParams(opts), (err, response) ->
-      console.log response
+      return callback err, response if err
+      return callback err ? true, response if response["ACK"] isnt "Success"
+      callback err, response
+
+  updateRecurringPaymentsProfile: (id, opts, callback)->
+    throw new Error "Missing checkout token" unless token
+
+    opts = @_merge({
+      METHOD:    "UpdateRecurringPaymentsProfile"
+      PROFILEID: id
+    }, opts ? {})
+
+    @makeAPIrequest @getParams(opts), (err, response) ->
       return callback err, response if err
       return callback err ? true, null if response["ACK"] isnt "Success"
       callback err, response


Add commented jade / roy / less lines to example config.

--- template/base/config.coffee
+++ template/base/config.coffee
@@ -22,6 +22,8 @@
         '\.js$': languages.JavaScriptLanguage
         '\.coffee$': languages.CoffeeScriptLanguage
         '\.eco$': languages.EcoLanguage
+        #'\.jade$': languages.JadeLanguage
+        #'\.roy$': languages.RoyLanguage
       order:
         before: [
           'vendor/scripts/console-helper.js'
@@ -34,6 +36,7 @@
       languages:
         '\.css$': languages.CSSLanguage
         '\.styl$': languages.StylusLanguage
+        #'\.less$': languages.LESSLanguage
       order:
         before: ['vendor/styles/normalize.css']
         after: ['vendor/styles/helpers.css']


Label: New parameter "markdown_opts", this overwrites CUI.defaults.marked_opts.

--- src/elements/Label/Label.coffee
+++ src/elements/Label/Label.coffee
@@ -128,6 +128,8 @@
 				mandatory: true
 				default: false
 				check: Boolean
+			markdown_opts:
+				check: "PlainObject"
 			tooltip:
 				check: "PlainObject"
 			group:
@@ -162,6 +164,12 @@
 		@__currentText = null
 		assert(xor(isNull(@_text), isNull(@_content)), "new Label", "opts.text and opts.content cannot both be set.", opts: @opts)

+		if @_markdown_opts
+			@__markdown_opts = copyObject(CUI.defaults.marked_opts, false)
+			CUI.mergeMap(@__markdown_opts, @_markdown_opts)
+		else
+			@__markdown_opts = CUI.defaults.marked_opts
+
 		if @_manage_overflow
 			assert(@_multiline, "new Label", "opts.multiline needs to be set for opts.manage_overflow", opts: @opts)
 		@
@@ -170,7 +178,7 @@
 		if isEmpty(@__currentText)
 			@empty("content")
 		else if markdown
-			@setContent(CUI.DOM.htmlToNodes(window.marked(@__currentText, CUI.defaults.marked_opts)))
+			@setContent(CUI.DOM.htmlToNodes(window.marked(@__currentText, @__markdown_opts)))
 			@addClass("cui-label-markdown")
 		else
 			@setContent($text(@__currentText))


report an error in the console if we can't parse box.json

--- client/code/model/boxable.coffee
+++ client/code/model/boxable.coffee
@@ -3,9 +3,14 @@
     if @_publishToken?
       callback @_publishToken
     else
-      @exec("cat ~/box.json", {dataType: 'json'}).success (settings) ->
-        @_publishToken = settings.publish_token
-        callback @_publishToken
+      @exec "cat ~/box.json",
+        dataType: 'json'
+        success: (settings) ->
+          @_publishToken = settings.publish_token
+          callback @_publishToken
+        error: (jqXhr, textStatus, errorThrown) ->
+          console.warn "Couldn't parse box.json!", errorThrown
+

   exec: (cmd, args) ->
     # Returns an ajax object, onto which you can


Fix $.popup open callback timing problem.

--- lib/Popup.coffee
+++ lib/Popup.coffee
@@ -75,5 +75,8 @@
     element = this
     options = {}
     options.close = -> close.call element if close
-    open.call element, element if open
-    popup $(this), id, (if items.call then items.call(element, e.altKey || e.shiftKey) else items), options+    if popupID == id
+      closePopup()
+    else
+      popup $(this), id, (if items.call then items.call(element, e.altKey || e.shiftKey) else items), options
+      open.call element, element if open


Revert previous change to join()

Revert previous change to join()

--- src/ext/modules/routines.coffee
+++ src/ext/modules/routines.coffee
@@ -243,7 +243,7 @@
 H2O.Routines = (_) ->
   #TODO move these into Flow.Async
   _fork = (f, args...) -> Flow.Async.fork f, args
-  _join = (args, go) -> Flow.Async.join args, Flow.Async.applicate go
+  _join = (args..., go) -> Flow.Async.join args, Flow.Async.applicate go
   _call = (go, args...) -> Flow.Async.join args, Flow.Async.applicate go
   _apply = (go, args) -> Flow.Async.join args, go
   _isFuture = Flow.Async.isFuture


moving token replacer to utils

moving token replacer to utils

--- lib/Utils.coffee
+++ lib/Utils.coffee
@@ -26,14 +26,14 @@
     cb(err, null) if err

     while /#\{(.*?)\}/gm.test(data)
-      finds = @arrayUnique(data.match(/#\{(.*?)\}/gm))
+      finds = ut.arrayUnique(data.match(/#\{(.*?)\}/gm))
       replaces = finds.map((f)=>
         str = f.substring(2, f.length-1)
         return if tokens[str] isnt undefined then tokens[str] else ''
       )

       for find, idx in finds
-        data = data.replace(new RegExp(@escapeRegExp(find),'gm'), replaces[idx])
+        data = data.replace(new RegExp(ut.escapeRegExp(find),'gm'), replaces[idx])

     cb(null, data)
   )


socialapi: increment/decrement like count per event

--- client/Main/messageeventmanager.coffee
+++ client/Main/messageeventmanager.coffee
@@ -25,13 +25,13 @@
     fn event

-  addLike: ({accountOldId, count}) ->
+  addLike: ({accountOldId}) ->

     message = @getData()

     {like} = message.interactions

-    like.actorsCount = count
+    like.actorsCount++
     like.actorsPreview.unshift accountOldId  if accountOldId not in like.actorsPreview
     like.isInteracted = yes  if KD.whoami().getId() is accountOldId

@@ -39,13 +39,12 @@
     message.emit "update"

-  removeLike: ({accountOldId, count}) ->
-
+  removeLike: ({accountOldId}) ->
     message = @getData()

     {like} = message.interactions

-    like.actorsCount   = count
+    like.actorsCount--
     like.actorsPreview = like.actorsPreview.filter (id) -> id isnt accountOldId

     like.isInteracted = no  if KD.whoami().getId() is accountOldId


Social: implement fetching followed topics

--- client/Main/socialapicontroller.coffee
+++ client/Main/socialapicontroller.coffee
@@ -12,7 +12,7 @@
       return callback {message: "Channel id is not set for request"}
     getCurrentGroup (group)->
       options.groupName = group.slug
-      channelApiFuncMultiRes "fetchActivity", options, callback
+      channelApiActivitiesResFunc "fetchActivity", options, callback

   fetchGroupActivity = (options, callback)->
     getCurrentGroup (group)->
@@ -20,32 +20,31 @@
         return callback {message: "Group doesnt have socialApiChannelId"}
       options.id        = group.socialApiChannelId
       options.groupName = group.slug
-      channelApiFuncMultiRes "fetchActivity", options, callback
+      channelApiActivitiesResFunc "fetchActivity", options, callback

   fetchChannels = (options, callback)->
     getCurrentGroup (group)->
       options.groupName = group.slug
-      {SocialChannel} = KD.remote.api
-      SocialChannel.fetchChannels options, (err, result)->
-        return callback err if err
-        return callback null, mapChannels result
+      channelApiChannelsResFunc 'fetchChannels', options, callback

   fetchPopularTopics = (options, callback)->
     getCurrentGroup (group)->
       options.groupName = group.slug
-      {SocialChannel} = KD.remote.api
-      SocialChannel.fetchPopularTopics options, (err, result)->
-        return callback err if err
-        return callback null, mapChannels result
+      channelApiChannelsResFunc 'fetchPopularTopics', options, callback

-  messageApiFunc = (name, rest..., callback)->
+  messageApiMessageResFunc = (name, rest..., callback)->
     KD.remote.api.SocialMessage[name] rest..., (err, res)->
       return callback null, mapActivity res

-  channelApiFuncMultiRes = (name, rest..., callback)->
+  channelApiActivitiesResFunc = (name, rest..., callback)->
     KD.remote.api.SocialChannel[name] rest..., (err, result)->
       return callback err if err
       return callback null, mapActivities result
+
+  channelApiChannelsResFunc = (name, rest..., callback)->
+    KD.remote.api.SocialChannel[name] rest..., (err, result)->
+      return callback err if err
+      return callback null, mapChannels result

   mapActivities = (messages)->
     # if no result, no need to do something
@@ -100,15 +99,12 @@
     return revivedChannels

   message:
-    edit                  :(rest...)-> messageApiFunc 'edit', rest...
-    post                  :(rest...)-> messageApiFunc 'post', rest...
-    reply                 :(rest...)-> messageApiFunc 'reply', rest...
-    delete                :(rest...)->
-      KD.remote.api.SocialMessage.delete rest...
-    like                  :(rest...)->
-      KD.remote.api.SocialMessage.like rest...
-    unlike                :(rest...)->
-      KD.remote.api.SocialMessage.unlike rest...
+    edit   :(rest...)-> messageApiMessageResFunc 'edit', rest...
+    post   :(rest...)-> messageApiMessageResFunc 'post', rest...
+    reply  :(rest...)-> messageApiMessageResFunc 'reply', rest...
+    delete :(rest...)-> KD.remote.api.SocialMessage.delete rest...
+    like   :(rest...)-> KD.remote.api.SocialMessage.like rest...
+    unlike :(rest...)-> KD.remote.api.SocialMessage.unlike rest...

   channel:
     list               : fetchChannels
@@ -116,8 +112,12 @@
     fetchGroupActivity : fetchGroupActivity
     fetchPopularTopics : fetchPopularTopics
     listPinnedMessages : (rest...)->
-      channelApiFuncMultiRes 'listPinnedMessages', rest...
+      channelApiActivitiesResFunc 'listPinnedMessages', rest...
     pin                : (rest...)->
       KD.remote.api.SocialChannel.pinMessage rest...
     unpin              : (rest...)->
       KD.remote.api.SocialChannel.unpinMessage rest...
+    follow             : (rest...)->
+      KD.remote.api.SocialChannel.follow rest...
+    unfollow           : (rest...)->
+      KD.remote.api.SocialChannel.unfollow rest...


Social: add a new function for searching topics

--- client/Main/socialapicontroller.coffee
+++ client/Main/socialapicontroller.coffee
@@ -177,3 +177,5 @@
       KD.remote.api.SocialChannel.unfollow rest...
     fetchFollowedChannels: (rest...)->
       channelApiChannelsResFunc 'fetchFollowedChannels', rest...
+    searchTopics: (rest...)->
+      channelApiChannelsResFunc 'searchTopics', rest...


Minor: code style

Minor: code style

--- client/Main/socialapicontroller.coffee
+++ client/Main/socialapicontroller.coffee
@@ -405,7 +405,7 @@

     fetchActivities      : channelRequesterFn
       fnName             : 'fetchActivities'
-      validateOptionsWith: ["id"]
+      validateOptionsWith: ['id']
       mapperFn           : mapActivities

     fetchPopularPosts    : channelRequesterFn


Kloud::info: take `not found` error into account on kloud.info, this needs to be fixed in Kloud side as well but it will make it better until it's improved there

--- client/app/lib/kite/kites/kloud.coffee
+++ client/app/lib/kite/kites/kloud.coffee
@@ -185,6 +185,20 @@
             @_reconnectedOnce = yes

           KiteLogger.failed 'kloud', 'info'
+
+        # If kite somehow unregistered from Kontrol and Kloud is failing
+        # to find it in Kontrol registry we are getting this `not found`
+        # at this point we can assume that the machine is Stopped. But on
+        # the other hand, Kloud should also mark this machine as Stopped if
+        # it couldn't find it in Kontrol registry ~ GG FIXME: FA
+        else if err.message is 'not found'
+
+          @resolveRequestingInfos machineId, State: Machine.State.Stopped
+          KiteLogger.failed 'kloud', 'info'
+
+          kd.warn '[kloud:info] failed, Kite not found in Kontrol registry!', err
+
+          return

         kd.warn '[kloud:info] failed, sending current state back:', { currentState, err }
         @resolveRequestingInfos machineId, State: currentState


Use INBOX as default mailbox

Use INBOX as default mailbox

--- client/app/stores/account_store.coffee
+++ client/app/stores/account_store.coffee
@@ -104,11 +104,16 @@

         account = _accounts.get(accountID) or @getDefault()
         mailboxes = account.get('mailboxes')
-        favorites = account.get('favorites')
-        defaultID = if favorites? then favorites[0]
+        mailbox = mailboxes.filter (mailbox) ->
+            return mailbox.get('label') is 'INBOX'
+        if mailbox.count() is 1
+            return mailbox.first()
+        else
+            favorites = account.get('favorites')
+            defaultID = if favorites? then favorites[0]

-        return if defaultID then mailboxes.get defaultID
-        else mailboxes.first()
+            return if defaultID then mailboxes.get defaultID
+            else mailboxes.first()

     getSelected: -> return _selectedAccount


Fix JS error

Fix JS error

--- client/app/stores/account_store.coffee
+++ client/app/stores/account_store.coffee
@@ -75,10 +75,11 @@
         updated = mailboxes.withMutations (map) ->
             for boxid, deltas of diff when deltas.nbTotal + deltas.nbUnread
                 box = map.get boxid
-                box = box.merge
-                    nbTotal: box.get('nbTotal') + deltas.nbTotal
-                    nbUnread: box.get('nbUnread') + deltas.nbUnread
-                map.set boxid, box
+                if box?
+                    box = box.merge
+                        nbTotal: box.get('nbTotal') + deltas.nbTotal
+                        nbUnread: box.get('nbUnread') + deltas.nbUnread
+                    map.set boxid, box

         unless updated is mailboxes
             account = account.set 'mailboxes', updated


Perform mongo search with case-insenstive option i

--- client/controllers/curatorInbox.coffee
+++ client/controllers/curatorInbox.coffee
@@ -91,6 +91,7 @@
   "keyup #curator-inbox-article-filter, input #curator-inbox-article-filter": (event, template) ->
     template.textFilter.set
       $regex: $(event.target).val()
+      $options: 'i'

   "click .curator-filter-calendar-icon": (event, template) ->
     calendarState = template.calendarState


Try toString.

Try toString.

--- scripts/whitelist_room.coffee
+++ scripts/whitelist_room.coffee
@@ -18,13 +18,13 @@
   robot.receiveMiddleware (context, next, done) ->
     # Unless the room is in the whitelist
     room = context.response.message.room
-    if room in whitelist
+    if room.toString() in whitelist
       robot.logger.debug 'Received message from whitelisted room.'
       next(done)
     else
       # We're done
       robot.logger.debug 'Received message from non-whitelisted room: ' + room
       robot.logger.debug 'Whitelist: ' + whitelist
-      robot.logger.debug room == process.env.HUBOT_ROOM_WHITELIST
+      robot.logger.debug room.toString() == process.env.HUBOT_ROOM_WHITELIST
       context.response.message.finish()
       done()


fix about page updates group i18n

fix about page updates group i18n

- check visibility by .offsetParent

--- lib/about.coffee
+++ lib/about.coffee
@@ -21,11 +21,12 @@
       releaseNotes.text = @defA.About.releaseNotes

       updatesGroup = about.querySelector('.about-updates')
-      if updatesGroup
-        updatesGroup.querySelector('span.about-updates-label').textContent = @defA.About.newUpdate
-        updatesGroup.querySelector('a.about-updates-release-notes').textContent = @defA.About.releaseNotes
-        updatesGroup.querySelector('button').textContent = @defA.About.restartInstall
-        updatesGroup.querySelector('.about-auto-updates span').textContent = @defA.About.automaticDownloadUpdates
+      if updatesGroup.offsetParent isnt null  # element visibility
+        try
+          updatesGroup.querySelector('.about-auto-updates span').textContent = @defA.About.automaticDownloadUpdates
+          updatesGroup.querySelector('span.about-updates-label').textContent = @defA.About.newUpdate
+          updatesGroup.querySelector('a.about-updates-release-notes').textContent = @defA.About.releaseNotes
+          updatesGroup.querySelector('button').textContent = @defA.About.restartInstall

       buttons = about.querySelector('.about-actions.group-item .btn-group')
       buttons.childNodes[0].textContent = @defA.About.license


Improve the wording of a test description.

--- test/TinyMockJS.test.coffee
+++ test/TinyMockJS.test.coffee
@@ -243,7 +243,7 @@
       o.my_method.should.not.equal(existing_method)
       o.my_method()                 # otherwise we'll get a 'my_method not called' error

-  it "throws an error if method_name does not already exist on an instance that was not passed in by mock()", ->
+  it "throws an error if method_name does not already exist on an instance of a class that was not passed in by mock()", ->
     class Klass
       some_method: -> "some method"
     k = new Klass()


Fix for quotes escaping in json parameter

--- lib/build.coffee
+++ lib/build.coffee
@@ -246,7 +246,8 @@
                   else
                       if fileParams.fileNameParsed not in params[fileParams.metaDataType].items && fileParams.fileNameParsed.length > 0
                           params[fileParams.metaDataType].items.push(fileParams.fileNameParsed)
-          params = JSON.stringify(params);
+          params = JSON.stringify(params).replace(/"/g, '\\"');;
+          params =
           if @child then @abort(=> @startNewBuild(optype, params, 'buildSeveral')) else @startNewBuild(optype, params, 'buildSeveral')

   stop: ->


tests for crud store for extending the crudstore and using the _loaded function

--- test/crud-store.spec.coffee
+++ test/crud-store.spec.coffee
@@ -2,9 +2,18 @@

 {CrudConfig, makeSimpleStore, extendConfig} = require '../src/flux/helpers'

-CrudConfig = CrudConfig()
+TestCrudConfig = CrudConfig()
+{actions:CrudActions, store:CrudStore} = makeSimpleStore(TestCrudConfig)

-{actions:CrudActions, store:CrudStore} = makeSimpleStore(CrudConfig)
+
+ExtendedConfig =
+  _loaded: (obj, id) ->
+      nested : obj
+  exports:
+    testExtendedStore : ()->
+
+extendConfig(ExtendedConfig, new CrudConfig())
+{actions: ExtendedActions, store: ExtendedStore} = makeSimpleStore(ExtendedConfig)

 describe 'CRUD Store', ->
@@ -107,7 +116,23 @@
     expect(CrudStore.get(id)).to.deep.equal({hello:'bar'})
     expect(CrudStore.getChanged(id)).to.deep.equal({})

+
   it 'should be loaded when a new item is created', ->
     id = CrudStore.freshLocalId()
     CrudActions.create(id, {hello:'bar'})
     expect(CrudStore.isLoaded(id)).to.be.true
+
+
+  it 'should have additional actions if the config has been extended', ->
+    expect(ExtendedActions._loaded).to.be.a('function')
+
+
+  it 'should additional store functions if the config has been extended', ->
+    expect(ExtendedStore.testExtendedStore).to.be.a('function')
+
+
+  it 'should change what is loaded if _loaded function is defined', ->
+    id = 0
+    nestedStore = {hello: 'bar'}
+    ExtendedActions.loaded(nestedStore, id)
+    expect(ExtendedStore.get(id).nested).to.deep.equal(nestedStore)


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/engine_selector.coffee
+++ test/engine_selector.coffee
@@ -7,12 +7,12 @@
     x: 0
     y: 0

-  engine.add
+  e1engine.add
     rad: "Awesome"
     x: 5
     y: 10

-  obj3 = engine.add
+  engine.add
     rad: "Awesome"
     x: 5
     y: 10


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/engine_selector.coffee
+++ test/engine_selector.coffee
@@ -24,7 +24,7 @@
     if index is 0
       equal obj, obj1
     else if index is 1
-      equs
+      equal

 test "#find", ->
   engine = Engine()


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/engine_selector.coffee
+++ test/engine_selector.coffee
@@ -22,7 +22,7 @@
   indexSum = 0

   engine.each (obj, index) ->
-
+    counter

 test "#find", ->
   engine = Engine()


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/engine_selector.coffee
+++ test/engine_selector.coffee
@@ -3,7 +3,8 @@
 test "engine#find", ->
   engine = Engine()

-  e
+  engine.add
+

 module()


integrationSpec updated

integrationSpec updated

--- test/integrationSpec.coffee
+++ test/integrationSpec.coffee
@@ -82,4 +82,14 @@
 # todo: rewrite integration tests:
 #   - move JE describe in beforeEach
 #   - retrieve needed vars from multiple places in JE's describe
-#   - and then test this in it blocks+#   - and then test this in it blocks
+
+# Planning DSL example:
+# describe 'TestModule', ->
+#   args    .are 'empty'
+#   skies   .are 'blue'
+#   func    .is  -> new TestModule()
+#   subject .is  func.module()
+
+#   it -> is_expected.toBeTruthy()
+#   it -> expect(func).toBeDefined()


This was also missed in a hasty push.

This was also missed in a hasty push.

--- test/spec/cli.system.coffee
+++ test/spec/cli.system.coffee
@@ -302,7 +302,7 @@
           expect(stdout).to.eql "" +
             "info stat lib/test.js (0.334KB): 19 " +
             "lines, 19 loc, ? comments (language: JavaScript) (vile-stat)\n" +
-            "info stat package.json (0.048KB): ? lines, ? " +
+            "info stat package.json (0.024KB): ? lines, ? " +
             "loc, ? comments (language: JSON) (vile-stat)\n"
           expect(code).to.eql 0
           done()


Added tests for Utils.isDataUrl

Added tests for Utils.isDataUrl

Added tests for Utils.isDataUrl for a gif and png

--- test/tests/UtilsTest.coffee
+++ test/tests/UtilsTest.coffee
@@ -401,6 +401,15 @@
 			Utils.isFileUrl("file:///Users/koen/Desktop/index.html").should.equal(true)
 			Utils.isFileUrl("http://apple.com/index.html").should.equal(false)
 			Utils.isFileUrl("https://apple.com/index.html").should.equal(false)
+
+	describe "isDataUrl", ->
+		it "should work", ->
+			dataUrlGif = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7"
+			dataUrlPng = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAABmJLR0QAAAAAAAD5Q7t/AAAACXBIWXMAAAsSAAALEgHS3X78AAAADUlEQVQI12P4z8DwHwAFAAH/cpxSZwAAAABJRU5ErkJggg=="
+			Utils.isDataUrl(dataUrlGif).should.equal(true)
+			Utils.isDataUrl(dataUrlPng).should.equal(true)
+			Utils.isDataUrl("file:///Users/koen/Desktop/foo.gif").should.equal(false)
+			Utils.isDataUrl("http://data.com/1x1.png").should.equal(false)

 	describe "isRelativeUrl", ->
 		it "should work", ->


ie8 does not like script as test tag

ie8 does not like script as test tag

--- test/unit/normalizer.coffee
+++ test/unit/normalizer.coffee
@@ -209,8 +209,8 @@
         initial:  'Bold'
         expected: 'Bold'
       'switch inline':
-        initial:  ''
-        expected:  'var test = "true";'
+        initial:  'A'
+        expected:  'A'
       'switch block':
         initial:  '
Test
'
         expected: '
Test
'


:art: Cleanup

:art: Cleanup

- Fixes #153
- Fixes #146
- Fixes #125

--- lib/gofmt.coffee
+++ lib/gofmt.coffee
@@ -69,9 +69,9 @@

     {stdout, stderr, messages} = @dispatch.executor.execSync(cmd, cwd, env, args)

-    console.log @name + ' - stdout: ' + stdout if stdout? and stdout.trim() isnt ''
+    console.log(@name + ' - stdout: ' + stdout) if stdout? and stdout.trim() isnt ''
     messages = @mapMessages(stderr, cwd) if stderr? and stderr.trim() isnt ''
-    @emit @name + '-complete', editor, saving
+    @emit(@name + '-complete', editor, saving)
     callback(null, messages)

   mapMessages: (data, cwd) =>


JSON.parse the out stream

JSON.parse the out stream

--- lib/index.coffee
+++ lib/index.coffee
@@ -41,7 +41,8 @@
   join_keys = if _(options.on).isObject() then _(options.on).pairs()[0] else [options.on, options.on]
   options.on = _.object ['left', 'right'], join_keys

-  out = new PassThrough objectMode: true
+  head = new PassThrough objectMode: true
+  out = _(head).stream().map((line) -> _(line).map JSON.parse).stream() # Parse each object in the pair

   file_names = []
   streams = []
@@ -54,18 +55,17 @@
     [have_join_key, dont_have_join_key] = partition validated, ([key, obj]) -> key?

     streams.push _(dont_have_join_key).stream()
-      # The other stream gets stringified and parsed since it goes to disk - do so here as
-      # well for consistency
-      .map(([key, obj]) -> JSON.parse obj)
+      .map(([key, obj]) -> obj)
       # Objects without a join key can't pair with anything, so we only want to keep them if
       # we are keeping objects that don't pair
-      .filter((obj) -> options.type is 'full' or stream_type is options.type)
-      .map((obj) -> if stream_type is 'left' then [obj, null] else [null, obj])
-      # Don't just pipe into out because we also pipe into out later and node streams don't
+      .filter(-> options.type is 'full' or stream_type is options.type)
+      # The string null gets turned into actual null later when these are parsed
+      .map((obj) -> if stream_type is 'left' then [obj, 'null'] else ['null', obj])
+      # Don't just pipe into head because we also pipe into head later and node streams don't
       # handle multiple sources for a stream very well. See understream.combine for how it
       # would need to be handled if we wanted to do it (which we don't because it adds
       # unnecessary complexity)
-      .each (obj) -> out.push obj
+      .each (obj) -> head.push obj

     streams.push _(have_join_key).stream()
       .map((obj, cb) -> setImmediate delimmed_key_and_obj, key, options.delim, obj, cb)
@@ -74,7 +74,7 @@
     file_names.push file_name

   async.each streams, ((stream, cb_e) -> stream.run cb_e), (err) ->
-    return out.emit 'error', err if err
+    return head.emit 'error', err if err
     spawn_opts = [
       '-o', '1.2,2.2'     # Display only the stringified objects
       '-e', 'null'        # Replace any missing data fields with null
@@ -91,9 +91,8 @@
       .split('\n')
       .filter((line) -> line) # Filter out trailing newline
       .map((line) -> line.split '\t') # Split out left and right into a pair
-      .map((line) -> _(line).map JSON.parse) # Parse each object in the pair
-      .pipe(out)
+      .pipe(head)
       .run (err) ->
         async.each file_names, rimraf, -> # Swallow errors from deleting files
-        out.emit 'error', err if err
+        head.emit 'error', err if err
   out


don't use sparse index

don't use sparse index

--- lib/index.coffee
+++ lib/index.coffee
@@ -50,7 +50,9 @@
 	pathMongoose()
 	return if schema._useFakeRemove is true
 	schema.add {deletedAt: Date}
-	schema.path('deletedAt').index {sparse: true}
+#	schema.path('deletedAt').index {sparse: true}
+	schema.path('deletedAt').index {sparse: false}
+

 	schema.plugin queryHook,
 		preQuery: (op, next) ->


fix a small bug with key matching and ordering

--- lib/index.coffee
+++ lib/index.coffee
@@ -105,7 +105,7 @@
   ###

   contains_keys = (set1, set2) ->
-    _.isEqual(_.intersection(set2, set1), set1.sort())
+    _.isEqual(_.intersection(set2, set1).sort(), set1.sort())

   ###*
    * Loads the configuration for the specified deployer from the shipfile.


Better exit wrapping

Better exit wrapping

--- lib/index.coffee
+++ lib/index.coffee
@@ -19,15 +19,21 @@
   log = (type, data) -> plugin.logger[type] stripEof data.toString()
   plugin.command = replaceAll plugin.command, bumped, key for key of keywords

+  error = false
+  errorMessage = null
+
   cmd = spawn plugin.command, plugin.options

   cmd.stdout.on 'data', (data) -> log 'success', data

   cmd.stderr.on 'data', (data) -> log 'error', data

-  cmd.on 'error', cb
+  cmd.on 'error', (err) ->
+    error = true
+    errorMessage = err.message or err

-  cmd.on 'close', (code) ->
-    return cb() unless code
-    log 'error', "Process exited with code #{code}"
+  cmd.on 'exit', (code) ->
+    return cb() unless error or code
+    errorMessage ?= "Process exited with code #{code}"
+    log 'error', errorMessage
     cb true


proper config

proper config

--- lib/browse.coffee
+++ lib/browse.coffee
@@ -6,8 +6,18 @@
 shell = require 'shell'

 module.exports = BrowsePackages =
+  config:
+    fileManager:
+      title: "File manager"
+      description: "Specify the full path to a custom file manager"
+      type: "string"
+      default: ""
+    notify:
+      title: "Verbose Mode"
+      description: "Show info notifications for all actions"
+      type: "boolean"
+      default: false
   self: 'browse'
-  verbose: null
   subscriptions: null

   activate: ->
@@ -26,7 +36,6 @@
     @subscriptions = null

   browsePackages: ->
-    @verbose = atom.config.get('browse.notify')
     packageDir = atom.packages.getPackageDirPaths()[0]

     # Does packages folder exist?
@@ -94,7 +103,7 @@
     configFile = atom.config.getUserConfigPath()
     configPath = path.dirname(configFile)

-    if @fileManager isnt null
+    if configPath
       # Does config folder exist?
       try
         fs.accessSync(configPath, fs.F_OK)
@@ -109,7 +118,7 @@
     # Custom file manager
     fileManager = atom.config.get('browse.fileManager')

-    if fileManager?
+    if fileManager
       exec "\"#{fileManager}\" \"#{path}\""
       return

@@ -131,7 +140,7 @@
     # Custom file manager
     fileManager = atom.config.get('browse.fileManager')

-    if fileManager?
+    if fileManager
       exec "\"#{fileManager}\" \"#{path}\""
       return


:art: Uppercase variable name

:art: Uppercase variable name

--- lib/panel.coffee
+++ lib/panel.coffee
@@ -19,8 +19,8 @@
       return unless Message.Position
       return if @Type is 'file' and (not Message.CurrentFile)
       P = Message.Position
-      range = [[P[0][0] - 1, P[0][1] - 1], [P[1][0] - 1, P[1][1]]]
-      Marker = @Linter.ActiveEditor.markBufferRange range, {invalidate: 'never'}
+      Range = [[P[0][0] - 1, P[0][1] - 1], [P[1][0] - 1, P[1][1]]]
+      Marker = @Linter.ActiveEditor.markBufferRange Range, {invalidate: 'never'}

       @Decorations.push @Linter.ActiveEditor.decorateMarker(
         Marker, type: 'line-number', class: 'line-number-' + Message.Type.toLowerCase()


Linted

Linted

--- client/src/utils/TaskUtil.coffee
+++ client/src/utils/TaskUtil.coffee
@@ -43,8 +43,9 @@

     return "" if not tags?
     tagsList = ""
-    includedTags = tags.filter (tag) -> not tag.isExcluded
-                       .map (tag) -> tag.label
+    includedTags = tags
+        .filter (tag) -> not tag.isExcluded
+        .map (tag) -> tag.label

     includedTags.forEach (tag) ->
         if includedTags.indexOf(tag) is 0


now, this property doesn't have to include the key

--- app/assets/javascripts/classes/model_backed_view.js.coffee
+++ app/assets/javascripts/classes/model_backed_view.js.coffee
@@ -13,7 +13,7 @@
   errors_messages: (->
     messages = []
     for key, value of @get("raw_errors")
-      messages.push "#{key}: #{value.join(", ")}"
+      messages.push "#{value.join(", ")}"
     messages
   ).property("raw_errors")


reverting bad early commit with incorrect comment

--- app/assets/javascripts/hanuman/conditional-logic.js.coffee
+++ app/assets/javascripts/hanuman/conditional-logic.js.coffee
@@ -15,7 +15,7 @@
         condition = this
         #console.log condition
         questionId = condition.question_id
-        $triggerContainer = $ruleElement.closest('form').find("[data-question-id=" + questionId + "]")
+        $triggerContainer = $ruleElement.closest('.panel').find("[data-question-id=" + questionId + "]")
         # getting element on edit and new page
         $triggerElement = $triggerContainer.find(".form-control")
         # getting element on show page


add missing user namespace

add missing user namespace

--- app/assets/javascripts/initializers/current_user.js.coffee
+++ app/assets/javascripts/initializers/current_user.js.coffee
@@ -8,7 +8,7 @@
         currentUserId = data.user.id
         store = container.lookup('store:main')
         store.pushPayload 'user', data
-        container.register('foo:current', ->
+        container.register('user:current', ->
           @store.getById('user', currentUserId)
         , instantiate: false)
         application.inject('controller', 'getCurrentUser', 'user:current')


Improved italics regex, consistent with WP regex

--- app/assets/javascripts/views/clouds/chat/message.js.coffee
+++ app/assets/javascripts/views/clouds/chat/message.js.coffee
@@ -55,26 +55,30 @@

     content = content + "\\n"

+    # Use actual newlines
+    content = content.replace(/\\n/ig, "\n");
+
     # Tab fix
     content = content.replace(/\\t/ig,"    ")

     # Greentext
-    content = content.replace(/((^>|\\n>)[\w\d\s\!\'\"\'\.\,\-\_\/\?\:\&\^\<\>\=\#\%\+\*\~\\√\◊\(\)\[\]\{\}]*\\n)/ig,"$1")
+    content = content.replace(/((^>|\n>)[\w\d\s\!\'\"\'\.\,\-\_\/\?\:\&\^\<\>\=\#\%\+\*\~\\√\◊\(\)\[\]\{\}]*\n)/ig," $1 ")

     # Redacted
-    content = content.replace(/(\[REDACTED\])/ig,"[REDACTED]")
+    content = content.replace(/(\[REDACTED\])/ig," [REDACTED] ")

     # Italic
-    content = content.replace(/((^)|(\s))[\/]{1}([^\/\\n]+)[\/]{1}(($)|(\s))/ig," $4 ")
+    content = content.replace(/\B\/\b([^\/\n]+)\b\/\B/ig, (orig, text) -> "  #{text}  ")
+    #content = content.replace(/((^)|(\s))[\/]{1}([^\/\n]+)[\/]{1}(($)|(\s))/ig,"  $4  ")

     elem = $("

")

     if message.selfReference()
-      content = content.replace(/\\n$/ig,"").replace(/\\n/ig," ")
+      content = content.replace(/\n$/ig,"").replace(/\n/ig," ")
       content = content.replace(/^\/me/i,message.user().get('name'))
       elem.html(content)
     else
-      content = content.replace(/\\n$/ig,"").replace(/\\n/ig,"
")
+      content = content.replace(/\n$/ig,"").replace(/\n/ig,"
")
       elem.html(content)

     @.$('.chat-message-content').append(elem)


update order of experimental features

update order of experimental features

--- lib/config.coffee
+++ lib/config.coffee
@@ -156,14 +156,14 @@
         default: false
         description: "Diffs will be shown for the current branch against a branch you choose. The `Diff branch files` command will allow choosing which file to compare. The file feature requires the 'split-diff' package to be installed."
       autoFetch:
-        order: 5
+        order: 4
         title: "Auto-fetch"
         type: "integer"
         default: 0
         maximum: 60
         description: "Automatically fetch remote repositories every `x` minutes (`0` will disable this feature)"
       autoFetchNotify:
-        order: 6
+        order: 5
         title: "Auto-fetch notification"
         type: "boolean"
         default: false


Send error if can't render 'index' as error (we already call next() if catchAll is true

--- server/routes/frontend.coffee
+++ server/routes/frontend.coffee
@@ -46,7 +46,7 @@
     res.render 'index', templateData, (err, html) ->
       console.log 'Buckets caught an error and it trying to render the index', err if err
       if err
-        next()
+        res.send 404, err
       else
         res.send 404, html


messaging on undefined query error

messaging on undefined query error

--- lib/cursor.coffee
+++ lib/cursor.coffee
@@ -30,7 +30,10 @@
     else if query.find or query.cursor
       return {find: query.find or {}, cursor: query.cursor or {}}
     else
-      @validateQuery(query)
+      try
+        @validateQuery(query)
+      catch e
+        throw new Error "#{e}, #{util.inspect(query)}"
       parsed_query = {find: {}, cursor: {}}
       for key, value of query
         if key[0] isnt '$' then (parsed_query.find[key] = value) else (parsed_query.cursor[key] = value)


Fix enter key on shared author entry

Fix enter key on shared author entry

--- scripts/edit_mod.coffee
+++ scripts/edit_mod.coffee
@@ -58,6 +58,12 @@
     xhr.send(data)
 , false)

+document.getElementById('new-shared-author').addEventListener('keypress', (e) ->
+    if e.keyCode == 13
+        e.preventDefault()
+        document.getElementById('add-shared-author').click()
+, false)
+
 a.addEventListener('click', (e) ->
     e.preventDefault()
     target = e.target
@@ -66,7 +72,7 @@
     xhr = new XMLHttpRequest()
     xhr.open('POST', '/api/mod/' + window.mod_id + '/revoke')
     xhr.onload = () ->
-        #window.location = window.location
+        window.location = window.location
     form = new FormData()
     form.append('user', target.dataset.user)
     xhr.send(form)


improve delay ms logging

improve delay ms logging

--- services/real-time/app.coffee
+++ services/real-time/app.coffee
@@ -96,17 +96,17 @@
 		, 10000

 forceDrain = ->
-	forceDrainMsDelay = parseInt(Settings.forceDrainMsDelay, 10)
-	logger.log {delay_ms:forceDrainMsDelay}, "starting force drain after timeout"
+	logger.log {delay_ms:Settings.forceDrainMsDelay}, "starting force drain after timeout"
 	setTimeout ()->
 		logger.log "starting drain"
 		DrainManager.startDrain(io, 4)
 	, Settings.forceDrainMsDelay
-
+
 if Settings.forceDrainMsDelay?
-	logger.log "drainBeforeShutdown enabled"
+	Settings.forceDrainMsDelay = parseInt(Settings.forceDrainMsDelay, 10)
+	logger.log forceDrainMsDelay: Settings.forceDrainMsDelay,"forceDrainMsDelay enabled"
 	for signal in ['SIGINT', 'SIGHUP', 'SIGQUIT', 'SIGUSR1', 'SIGUSR2', 'SIGTERM', 'SIGABRT']
-		logger.log signal: signal, "received interrupt, cleaning up"
 		process.on signal, ->
+			logger.log signal: signal, "received interrupt, cleaning up"
 			shutdownCleanly(signal)
 			forceDrain()


add class

add class


Update utils.coffee

Update utils.coffee

--- lib/utils.coffee
+++ lib/utils.coffee
@@ -32,11 +32,11 @@

     return JSON.parse(fs.readFileSync(path))

-  @unique: (array, test) ->
+  @unique: (array, callback) ->
     result = []
     seen = []
     for item in array
-      computed = test(item)
+      computed = callback(item)
       if seen.indexOf(computed)
         seen.push(computed)
         result.push(item)


Ignore hidden directories (./.dir like ./.git)

--- lib/utils.coffee
+++ lib/utils.coffee
@@ -57,6 +57,9 @@
         if path.basename(file) == 'project.pros'
           results.push path.dirname file
       ),
-      ((d) -> traversal d)
+      ((d) ->
+        if !path.basename(d).startsWith '.'
+          try traversal d
+          catch e then)
     traversal project for project in atom.project.getPaths()
     return results


Added Global Value Sets

Added Global Value Sets

--- lib/utils.coffee
+++ lib/utils.coffee
@@ -176,6 +176,7 @@
       ,'aura' : 'AuraDefinitionBundle'
       ,'documents' : 'Document'
       ,'email' : 'EmailTemplate'
+      ,'globalValueSets' : 'GlobalValueSet'
     }
     result = null
     if folderMapping.hasOwnProperty folderName


set singleNonWordChar is default

set singleNonWordChar is default

--- lib/utils.coffee
+++ lib/utils.coffee
@@ -573,7 +573,7 @@
   if not wordRegex? and not nonWordCharacters? # Complement from cursor
     cursor ?= editor.getLastCursor()
     {wordRegex, nonWordCharacters} = _.extend(options, buildWordPatternByCursor(cursor, options))
-  singleNonWordChar ?= false
+  singleNonWordChar ?= true

   characterAtPoint = getCharacterAtBufferPosition(editor, point)
   nonWordRegex = new RegExp("[#{_.escapeRegExp(nonWordCharacters)}]+")


process block-comments before single-line-comments

--- lib/utils.coffee
+++ lib/utils.coffee
@@ -136,6 +136,16 @@
         else if (match = line.match blockLineMatcher)?
           currSegment.comments.push match[2]

+      # Match that line to the language's multi line comment syntax, if it exists
+      else if language.multiLineComment? and (match = line.match blockStartMatcher)?
+
+        if currSegment.code.length > 0
+          segments.push currSegment
+          currSegment = new @Segment
+
+        currSegment.comments.push match[2]
+        inBlock = true
+
       # Match that line to the language's single line comment syntax.
       #
       # However, we treat all comments beginning with } as inline code commentary.
@@ -156,16 +166,6 @@

         else
           currSegment.code.push line
-
-      # Match that line to the language's multi line comment syntax, if it exists
-      else if language.multiLineComment? and (match = line.match blockStartMatcher)?
-
-        if currSegment.code.length > 0
-          segments.push currSegment
-          currSegment = new @Segment
-
-        currSegment.comments.push match[2]
-        inBlock = true

       else
         currSegment.code.push line


do not retrigger background check if window scrolls

--- components/light_detector/index.coffee
+++ components/light_detector/index.coffee
@@ -28,6 +28,7 @@
     BackgroundCheck.init
       targets: targets
       images: background
+      windowEvents: false
       classes:
         light: 'is-light'
         dark: 'is-dark'


Save to search history on invocation of occurrence command

--- lib/motion.coffee
+++ lib/motion.coffee
@@ -995,6 +995,7 @@
           patternForOccurence = @matches.pattern # preserve before cancel
           operation = command.operation
           operation += 'InARangeMarker' if @getScanRangeType() is 'range-marker'
+          @vimState.searchHistory.save(@input)
           @vimState.searchInput.cancel()
           @vimState.operationStack.run(operation, {patternForOccurence})


add BaseModel.isHovered

add BaseModel.isHovered

--- src/3d/BaseModel.coffee
+++ src/3d/BaseModel.coffee
@@ -53,6 +53,9 @@
   isPressed: (raycaster) ->
     raycaster.intersectObject(@mesh).length > 0

+  isHovered: (raycaster) ->
+    raycaster.intersectObject(@mesh).length > 0
+
   attachParticle: (particle) ->
     if @mesh?
       @particle = particle


add some options for future use

add some options for future use

--- lib/ripper.coffee
+++ lib/ripper.coffee
@@ -37,6 +37,12 @@
       # 'es7.comprehensions': true
       # 'es7.doExpressions': true
       # 'es7.functionBind': true
+    # Other options which maybe useful
+    # ecmaVersion: Infinity
+    # allowHashBang: true
+    # plugins:
+    #   jsx: true
+    #   flow: true

   constructor: ->
     @context = new Context


update for single socket

update for single socket

--- virtual-file-system/lib/onmessage.coffee
+++ virtual-file-system/lib/onmessage.coffee
@@ -18,8 +18,7 @@
   sync,
 }

-module.exports = onmessage = (event, virtualFileSystem) ->
-  message = event.data
+module.exports = onmessage = (message, virtualFileSystem) ->

   try
     {type, data} = JSON.parse(message)
@@ -33,4 +32,3 @@
     console.error "Unhandled message type: #{type}"
   else
     strategy(virtualFileSystem, data)
-


allow subspaces in slackbot command space

--- lib/bot.coffee
+++ lib/bot.coffee
@@ -47,7 +47,8 @@
   looker.refreshCommands = ->
     return unless looker.customCommandSpaceId
     console.log "Refreshing custom commands for #{looker.url}..."
-    looker.client.get("spaces/#{looker.customCommandSpaceId}", (space) ->
+
+    addCommandsForSpace = (space, category) ->
       for partialDashboard in space.dashboards
         looker.client.get("dashboards/#{partialDashboard.id}", (dashboard) ->

@@ -56,6 +57,7 @@
             description: dashboard.description
             dashboard: dashboard
             looker: looker
+            category: category

           command.helptext = (dashboard.filters || "").map((f) ->
             "<#{f.title.toLowerCase()}>"
@@ -64,7 +66,15 @@
           customCommands[command.name] = command

         console.log)
+
+    looker.client.get("spaces/#{looker.customCommandSpaceId}", (space) ->
+      addCommandsForSpace(space, "Shortcuts")
+      looker.client.get("spaces/#{looker.customCommandSpaceId}/children", (children) ->
+        for child in children
+          addCommandsForSpace(child, child.name)
+      console.log)
     console.log)
+
   looker.client = new LookerClient(
     baseUrl: looker.apiBaseUrl
     clientId: looker.clientId
@@ -148,24 +158,26 @@

     else
       help = """
-      _I've got some built-in commands:_\n
+      *Built-in Commands*\n
       • *find* <look search term> — _Shows the top five Looks matching the search._
       • *q* <model_name>/<view_name>/<field>[<filter>] — _Runs a custom query._\n\n
       """

-      if _.values(customCommands).length > 0
-        help += "_And there are some fancy quick shortcuts people have set up:_\n\n"
-
-      for command in _.sortBy(_.values(customCommands), "name")
-        help += "• *<#{command.looker.url}/dashboards/#{command.dashboard.id}|#{command.name}>* #{command.helptext}"
-        if command.description
-          help += " — _#{command.description}_"
+      groups = _.groupBy(customCommands, 'category')
+
+      for groupName, groupCommmands of groups
+        help += "\n *#{groupName}*\n"
+        for command in _.sortBy(_.values(groupCommmands), "name")
+          help += "• *<#{command.looker.url}/dashboards/#{command.dashboard.id}|#{command.name}>* #{command.helptext}"
+          if command.description
+            help += " — _#{command.description}_"
+          help += "\n"
         help += "\n"

       spaces = lookers.filter((l) -> l.customCommandSpaceId ).map((l) ->
         "<#{l.url}/spaces/#{l.customCommandSpaceId}|this space>"
       ).join(" or ")
-      help += "\n_To add your own shortcuts, add a dashboard to #{spaces}._"
+      help += "\n_To add your own commands, add a dashboard to #{spaces}._"

       context.replyPrivate({text: help, parse: "none", attachments: []})


Add open PDF viewer tab

Add open PDF viewer tab

--- lib/viewer.coffee
+++ lib/viewer.coffee
@@ -58,6 +58,22 @@
     @window.setMenu(null)
     @window.setTitle("""Atom-LaTeX PDF Viewer - [#{@latex.mainFile}]""")

+  openViewerNewTab: ->
+    if !@latex.manager.findMain()
+      return
+
+    pdfPath = """#{@latex.mainFile.substr(
+      0, @latex.mainFile.lastIndexOf('.'))}.pdf"""
+    if !fs.existsSync pdfPath
+      return
+
+    if !@getUrl()
+      return
+
+    pane = atom.workspace.getActivePane().splitRight()
+    @tabView = new PDFView(@url)
+    pane.addItem(@tabView)
+
   getUrl: ->
     try
       { address, port } = @latex.server.http.address()
@@ -66,3 +82,23 @@
       @latex.server.openTab = true
       return false
     return true
+
+class PDFView
+  constructor: (url) ->
+    @element = document.createElement 'iframe'
+    @element.setAttribute 'src', url
+    @element.setAttribute 'width', '100%'
+    @element.setAttribute 'height', '100%'
+    @element.setAttribute 'frameborder', 0
+
+  getTitle: ->
+    return 'Atom-LaTeX PDF Viewer'
+
+  serialize: ->
+    return @element.getAttribute 'src'
+
+  destroy: ->
+    @element.remove()
+
+  getElement: ->
+    return @element


first implementation

first implementation

--- lib/cli.coffee
+++ lib/cli.coffee
@@ -1,13 +1,63 @@
-program = require 'commander'
+program  = require 'commander'
+readline = require 'readline'
+
+request = require 'request'
+cheerio = require 'cheerio'

 program
   .version('0.0.0')
   .option('-t, --timeout <n>', 'Timeout (s)', parseInt)
   .parse(process.argv)

+msDiff = (t1, t2) ->
+  s = t2[0] - t1[0]
+  mms = t2[1] - t1[1]
+  return (s*1e9 + mms)/1000/1000
+
+normalizeStatusCode = (status) ->
+  if      200 <= status and status < 300
+    return "200"
+  else if 400 <= status and status < 500
+    return "400"
+  else if 500 <= status and status < 600
+    return "500"
+  else
+    return "***"
+
 run = () ->
-  if program.timeout
-    console.log "Timeout  : " + program.timeout
-  console.log   "Arguments: " + program.args
+  rl = readline.createInterface
+    input:    process.stdin
+    output:   process.stdout
+    terminal: false
+
+  rl.on 'line', (json) ->
+    data = JSON.parse json
+
+    site  = data.site
+    url   = data.url
+    stats = data.stats
+
+    t1 = process.hrtime()
+    request.get
+      url: url
+    , (error, res, body) ->
+      t2 = process.hrtime()
+
+      result = {}
+      result.site = site
+      result.url  = url
+      result.time = msDiff(t1, t2)
+      result.code = normalizeStatusCode(res.statusCode)
+
+      $ = cheerio.load body
+      result.stats = {}
+      Object.keys(stats).forEach (key) ->
+        value = $(stats[key]['selector']).text()
+        if      stats[key]['type'] == 'numeric'
+          result.stats[key] = parseFloat(value)
+        else if stats[key]['type'] == 'string'
+          result.stats[key] = value
+
+      console.log JSON.stringify(result)

 module.exports.run = run


Remove code omitted from 68a39707817ab8693e1ddd9381611c0bfb46ebaa.

--- content_scripts/vimium_frontend.coffee
+++ content_scripts/vimium_frontend.coffee
@@ -368,9 +368,6 @@

   enterVisualLineMode: ->
     new VisualLineMode
-
-  enterEditMode: ->
-    @focusInput 1, EditMode

   focusInput: do ->
     # Track the most recently focused input element.


Update example alert contents.

Update example alert contents.

--- jigs/modal/jig.coffee
+++ jigs/modal/jig.coffee
@@ -13,10 +13,9 @@

   alert = new Alert
     parent: document.body
-    title: 'Something great is happening!'
-    content: 'You just won the lottery! I hope you\'re happy!'
-    ok: 'Yes, I am!'
-    buttonStyle: 'danger-flat'
+    title: 'Hello there!'
+    content: 'This is a sample alert box.'
+    ok: 'Thanks'

   new Button
     label: 'Show Backdrop'


list fix

list fix

--- js/parser.coffee
+++ js/parser.coffee
@@ -284,6 +284,11 @@

       when TYPES.list
        if @node.type isnt TYPES.list
+        if @node.type is TYPES.block and
+           @node.paragraph is false and
+           @node.parent().type is TYPES.listItem
+         @prevNode = @node
+         @node = @node.parent()
         @addNode new List map: @map, ordered: line.ordered, indentation: line.indentation

        @addNode new ListItem map: @map, ordered: line.ordered, indentation: line.indentation + 1


Modals fade in / out

Modals fade in / out

--- core/app/backbone/modules/modal.coffee
+++ core/app/backbone/modules/modal.coffee
@@ -4,7 +4,7 @@
     template: 'widgets/modal_wrapper'

     events:
-      "click .js-layer": "close"
+      "click .js-layer": "fadeOut"
       "click": "stopPropagation"

     regions:
@@ -19,11 +19,19 @@

     onRender: ->
       @modalRegion.show @options.content_view
-      @ui.modal.show()
-      @ui.layer.show()
+      @ui.modal.fadeIn 'fast'
+      @ui.layer.fadeIn 'fast'

     stopPropagation: (e) ->
       e.stopPropagation()
+
+    # We cannot fade the wrapping region, e.g. using a CrossFadeRegion, because
+    # that would create a stacking context, but currently @ui.modal and @ui.layer
+    # both have set z-indexes.
+    fadeOut: ->
+      @ui.layer.fadeOut 'fast'
+      @ui.modal.fadeOut 'fast', =>
+        @modalRegion.close()

   FactlinkApp.addRegions
     modalRegion: "#modal_region"
@@ -32,11 +40,11 @@
     Modal.close()

   Modal.show = (title, content_view)->
-    wrapped_view = new FactlinkApp.Modal.WrapperView
+    @wrapped_view = new FactlinkApp.Modal.WrapperView
       title: title
       content_view: content_view

-    FactlinkApp.modalRegion.show wrapped_view
+    FactlinkApp.modalRegion.show @wrapped_view

   Modal.close = ->
-    FactlinkApp.modalRegion.close()
+    @wrapped_view?.fadeOut()


Improved: variable name

Improved: variable name

--- client/app/views/tutorial.coffee
+++ client/app/views/tutorial.coffee
@@ -25,7 +25,7 @@
     ]

     currentAppIndex: 0
-    installedApps: []
+    installedFromTutorial: []

     constructor: (options) ->
         super()
@@ -34,7 +34,7 @@

     reset: ->
         @currentAppIndex = 0
-        @installedApps = []
+        @installedFromTutorial = []
         @render()

     afterRender: ->
@@ -60,16 +60,16 @@

     installApp: ->
         currentApp = @getCurrentApp()
-        @installedApps.push currentApp
+        @installedFromTutorial.push currentApp

         application = @marketApps.findWhere slug: currentApp
         @processInstall application, false

         # Installs Sync if Calendar or Contacts is installed
         relatedToSync = currentApp in ['calendar', 'contacts']
-        alreadyInList = 'sync' in @installedApps
+        alreadyInList = 'sync' in @installedFromTutorial
         if relatedToSync and not alreadyInList
-            @installedApps.push 'sync'
+            @installedFromTutorial.push 'sync'

             application = @marketApps.findWhere slug: 'sync'
             @processInstall application, false


Use real terminal for payment

Use real terminal for payment

--- client/components/Payment.coffee
+++ client/components/Payment.coffee
@@ -83,7 +83,7 @@

Вы оплачиваете один месяц занятий на algoprog.ru. Стоимость месяца для вас составляет {amount} рублей.

             <form name="TinkoffPayForm" onSubmit={@pay} id="payForm">
-                <input type="hidden" name="terminalkey" value="1539978299062DEMO"/>
+                <input type="hidden" name="terminalkey" value="1539978299062"/>
                 <input type="hidden" name="frame" value="true"/>
                 <input type="hidden" name="language" value="ru"/>
                 <input type="hidden" name="order" value={order}/>


uiid -> uuid

uiid -> uuid

--- src/models/EventsSupport.coffee
+++ src/models/EventsSupport.coffee
@@ -33,7 +33,7 @@
           webview: new steroids.views.WebView
             location: params.target.webview.location
             id: params.target.webview.id
-            uiid: params.target.webview.uiid
+            uuid: params.target.webview.uuid
       }

       if params.source? and params.source.webview?
@@ -41,7 +41,7 @@
           webview: new steroids.views.WebView
             location: params.source.webview.location
             id: params.source.webview.id
-            uiid: params.source.webview.uiid
+            uuid: params.source.webview.uuid
         }

       if params.target.tab?
@@ -94,4 +94,4 @@
       method: "removeEventListener"
       parameters:parameters
       successCallbacks: [eventListenerRemoved]
-      failureCallbacks: [errorRemovingEventListener]+      failureCallbacks: [errorRemovingEventListener]


actually support overriding with options

--- src/models/views/WebView.coffee
+++ src/models/views/WebView.coffee
@@ -22,7 +22,7 @@
   preload: (options={}, callbacks={}) ->
     steroids.debug "preload called for WebView #{JSON.stringify @}"

-    proposedId = @location || options.id
+    proposedId = options.id || @location

     setIdOnSuccess = () =>
       steroids.debug "preload success: setting id"
@@ -32,7 +32,7 @@
       method: "preloadLayer"
       parameters:
         id: proposedId
-        url: @location || options.location
+        url: options.location || @location
       successCallbacks: [setIdOnSuccess, callbacks.onSuccess]
       failureCallbacks: [callbacks.onFailure]


fix typo

fix typo

--- lib/ink.coffee
+++ lib/ink.coffee
@@ -30,7 +30,7 @@
   breakpoints: once(=> require './debugger/breakpoints')
   Pannable: once(=> require('./plots/canopy').Pannable)
   KaTeX: once(=> require('./util/katexify'))
-  profiler: once(=> require './plots/profiler')
+  Profiler: once(=> require './plots/profiler')
   tree: once(=> require './tree')
   goto: once(=> require './util/gotodef')
   Opener: once(=> require('./util/opener'))


Team: order change, stefan, lee added

Team: order change, stefan, lee added

--- client/landing/About/team.coffee
+++ client/landing/About/team.coffee
@@ -40,11 +40,11 @@
         username  : 'szkl'
         title     : 'Software Engineer'
       ,
+        username  : 'leventyalcin'
+        title     : 'System Administrator'
+      ,
         username  : 'burakcan'
         title     : 'CSS Wizard'
-      ,
-        username  : 'leventyalcin'
-        title     : 'System Administrator'
       ,
         username  : 'samet'
         title     : 'Software Engineer'
@@ -67,6 +67,12 @@
         username  : 'niltuncer'
         title     : 'Business Analyst'
       ,
+        username  : 'leeolayvar'
+        title     : 'Community Manager'
+      ,
+        username  : 'stefanbc'
+        title     : 'Community Manager'
+      ,
         username  : 'zeynep'
         title     : 'Finance Assistant'
       ,
@@ -84,15 +90,9 @@
       # ,
       #   username  : 'aleksey-m'
       #   title     : 'System Administrator'
-      # ,
-      #   username  : 'leeolayvar'
-      #   title     : 'Community Manager'
-      # ,
-      #   username  : 'stefanbc'
-      #   title     : 'Community Manager'
       ]

-    suspended     : []
+    # suspended     : []

       #   username  : 'aleksey-m'
       #   title     : 'Sys Admin & node.js Developer'


Always show nsfw filter option

Always show nsfw filter option

--- resources/assets/lib/beatmaps/search-panel.coffee
+++ resources/assets/lib/beatmaps/search-panel.coffee
@@ -187,6 +187,10 @@
         name: 'status'
         options: filters.statuses

+      @renderFilter
+        name: 'nsfw'
+        options: filters.nsfw
+
       a
         className: 'beatmapsets-search__expand-link'
         href: '#'
@@ -217,10 +221,6 @@
           name: 'played'
           options: filters.played

-        @renderFilter
-          name: 'nsfw'
-          options: filters.nsfw
-

   setHeaderPinned: (_event, pinned) =>
     if pinned && document.activeElement == @inputRef.current


spec fix

spec fix

--- spec/coffee/point_spec.coffee
+++ spec/coffee/point_spec.coffee
@@ -79,7 +79,7 @@
       point.data(data)
       point.data().forEach (s1) ->
         series_2_keys = (x.key for x in s1.values)
-        expect(series_2_keys).toEqual(['1', '3', '2'])
+        expect(series_2_keys.sort()).toEqual(['1', '2', '3'])

     it 'sorts the third series', ->
       point.series_2_domain(['X', 'Y', 'Z'])