Add collection into graphite key

Add collection into graphite key

--- mongodb.coffee
+++ mongodb.coffee
@@ -20,7 +20,7 @@
 					# Ignore noisy command methods like authenticating, ismaster and ping
 					return _method.call this, db_command, options, callback

-				key = "mongo-requests.#{type}"
+				key = "mongo-requests.#{collection}.#{type}"
 				if db_command.query?
 					query = Object.keys(db_command.query).sort().join("_")
 					key += "." + query


Router: /Topics/slug routes to /Activity?q=slug

--- client/Main/routes.coffee
+++ client/Main/routes.coffee
@@ -63,7 +63,10 @@
       requireLogin ->
         {mainController} = KD.singletons
         mainController.doLogout()
-    '/:name?/Topics/:slug'   : createContentDisplayHandler 'Topics'
+    '/:name?/Topics/:slug'   : ({params:{name, slug}}) ->
+      route = "/Activity?q=#{slug}"
+      route = "#{name}/#{route}"  if name
+      @handleRoute route
     '/:slug/:name' : ({params, query}, model, route) ->
       (createContentDisplayHandler 'Members') arguments...
     '/:name?/Invitation/:inviteCode': ({params:{inviteCode, name}})->


Ace: Added trimTrailingWhitespaces method.

--- client/ace/lib/ace.coffee
+++ client/ace/lib/ace.coffee
@@ -698,6 +698,18 @@
         targetHandle.unsetClass 'saved'

+  trimTrailingWhitespaces: ->
+
+    doc   = @editor.getSession().getDocument()
+    lines = doc.getAllLines()
+
+    for line, lineNumber in lines
+      whiteSpaceIndex = line.search /\s+$/
+
+      if whiteSpaceIndex > -1
+        doc.removeInLine lineNumber, whiteSpaceIndex, line.length
+
+
   showGotoLine: ->

     unless @gotoLineModal


Added immediate response and some logging for slash commands

--- src/scripts/diagnostics.coffee
+++ src/scripts/diagnostics.coffee
@@ -111,11 +111,13 @@
       response_url = body.response_url
       if response_url
         comps = body.text || []
-        start = if comps[0] then moment comps[0] else moment().subtract 2, 'weeks'
-        end = if comps[1] then moment comps[1] else moment()
+        now = moment()
+        start = if comps[0] then moment(comps[0]) else now.subtract(2, 'weeks')
+        end = if comps[1] then moment(comps[1]) else now
         Organization.generateReport(start, end)
         .catch(
           (err) ->
+            Logger.log "POSTing to #{response_url}"
             robot.http(response_url)
             .header('Content-Type', 'application/json')
             .post({
@@ -130,6 +132,10 @@
                 "text": "Salary report generated for #{numberDone} employees"
             })
         )
+        res.status 200
+        res.json {
+          "text": "Generating payroll..."
+        }
       else
         res.status 500
         res.json {
@@ -149,6 +155,7 @@
         Organization.sync()
         .catch(
           (err) ->
+            Logger.errorToSlack "Failed to resync", err
             robot.http(response_url)
             .header('Content-Type', 'application/json')
             .post({
@@ -157,12 +164,18 @@
         )
         .done(
           (status) ->
+            Logger.log "Options have been re-loaded"
+            Logger.log "POSTing to #{response_url}"
             robot.http(response_url)
             .header('Content-Type', 'application/json')
             .post({
               "text": "Re-synced with spreadsheet"
             })
         )
+        res.status 200
+        res.json {
+          "text": "Beginning to resync..."
+        }
       else
         res.status 500
         res.json {


emit "load" after redis-brain loads

emit "load" after redis-brain loads

--- src/scripts/redis-brain.coffee
+++ src/scripts/redis-brain.coffee
@@ -20,6 +20,7 @@
         throw err
       else if reply
         robot.brain.mergeData JSON.parse(reply.toString())
+        robot.brain.emit 'load', robot.brain.data

   robot.brain.on 'save', (data) ->
     client.set 'hubot:storage', JSON.stringify data


Typo in English locales related to attachments.

--- client/app/locales/en.coffee
+++ client/app/locales/en.coffee
@@ -347,7 +347,7 @@
       "gmail security link": "Enable access for less secure apps."

       # Plugins
-      'plugin name Gallery'            : 'Attachments gallery'
+      'plugin name Gallery'            : 'Attachment gallery'
       'plugin name medium-editor'      : 'Medium editor'
       'plugin name MiniSlate'          : 'MiniSlate editor'
       'plugin name Sample JS'          : 'Sample'
@@ -365,7 +365,7 @@
       "tooltip reply all": "Answer to all"
       "tooltip forward": "Forward"
       "tooltip remove message": "Remove"
-      "tooltip open attachments": "Open attachments list"
+      "tooltip open attachments": "Open attachment list"
       "tooltip open attachments": "Open attachment"
       "tooltip download attachment": "Download the attachment"
       "tooltip previous conversation": "Go to previous conversation"


selection working a bit

selection working a bit

--- bokehjs/demo/coffee/rects.coffee
+++ bokehjs/demo/coffee/rects.coffee
@@ -1,6 +1,6 @@
 require(['main'], (Bokeh) ->
   N= 630
-  N= 50000
+  N= 50
   r = new Bokeh.Random(123456789)

@@ -20,13 +20,13 @@
     type: 'rect',
     x: 'x'
     y: 'y'
-    width:  0.005
-    height: 0.005
+    width:  0.05
+    height: 0.05
     #angle: 0.1
     #fill_color: 'color'
     fill_color: '#0F0FF0'
     #line_color: 'color'
-    do_fill:false
+    do_fill:true
   }

   options = {


Disable logger during FUP

Disable logger during FUP

--- app/src/app.coffee
+++ app/src/app.coffee
@@ -30,7 +30,9 @@
       @_currentMode = newMode
       if @isInFirmwareUpdateMode()
         @_releaseWallet(no)
+        ledger.utils.Logger.setGlobalLoggersLevel(ledger.utils.Logger.Levels.NONE)
       else
+        ledger.utils.Logger.updateGlobalLoggersLevel()
         @connectDongle(ledger.app.dongle) if ledger.app.dongle?
       return


Add AppDataPath

Add AppDataPath

--- app.coffee
+++ app.coffee
@@ -5,6 +5,14 @@
 require('./modules/config').loadConfig()
 require('./modules/ui').initConfig()
 require('./modules/pac').generatePAC()
+
+gui = window.nwDispatcher.requireNwGui()
+fs = require('fs')
+# OSX: /Users/$user/Library/Application Support/poi
+global.AppDataPath = fs.realpathSync(gui.App.dataPath + '/..') + '/poi' if process.platform == 'darwin'
+# Win or Linux:
+global.AppDataPath = fs.realpathSync(process.execPath + '/..') if process.platform != 'darwin'
+
 proxy = require('./modules/proxy')
 proxy.createShadowsocksServer()
 proxy.createServer()


Success page was too skeuomorphic.

Success page was too skeuomorphic.

--- app.coffee
+++ app.coffee
@@ -107,7 +107,21 @@
     token = req.params[0]
     Auth.verifyRegistration(token)
       .then (user) ->
-        res.send("")
+        res.send("""
+<!DOCTYPE html>
+
+<meta charset="utf-8">
+<title>Password Reset Sent</title>
+<link href='http://fonts.googleapis.com/css?family=Lato:300' rel='stylesheet' type='text/css'>
+
Success!
You can go back to Nitro to log in.

+
+          """
       .fail (err) ->
         res.send(err)

@@ -229,7 +243,7 @@
           Mail.send
             to: email
             subject: "Nitro Password Reset"
-            html: "Please click the link below to reset your password
" + link
+            html: "Please click the link below to reset your password.
" + link
           res.send message

         .fail (err) ->


Fix typo

Fix typo

--- app.coffee
+++ app.coffee
@@ -46,7 +46,7 @@
     # code = req.body.code
     console.log uid

-    Users.get(uid)
+    User.get(uid)
       .then (user) ->
         user.changeProStatus(1)
         res.end()


Use epochs for DL deviance plots

Use epochs for DL deviance plots

--- src/ext/components/model-output.coffee
+++ src/ext/components/model-output.coffee
@@ -383,19 +383,19 @@
             renderPlot 'Scoring History - Deviance', no, _.plot (g) ->
               g(
                 g.path(
-                  g.position 'number_of_trees', 'training_deviance'
+                  g.position 'epochs', 'training_deviance'
                   g.strokeColor g.value '#1f77b4'
                 )
                 g.path(
-                  g.position 'number_of_trees', 'validation_deviance'
+                  g.position 'epochs', 'validation_deviance'
                   g.strokeColor g.value '#ff7f0e'
                 )
                 g.point(
-                  g.position 'number_of_trees', 'training_deviance'
+                  g.position 'epochs', 'training_deviance'
                   g.strokeColor g.value '#1f77b4'
                 )
                 g.point(
-                  g.position 'number_of_trees', 'validation_deviance'
+                  g.position 'epochs', 'validation_deviance'
                   g.strokeColor g.value '#ff7f0e'
                 )
                 g.from table
@@ -404,11 +404,11 @@
             renderPlot 'Scoring History - Deviance', no, _.plot (g) ->
               g(
                 g.path(
-                  g.position 'number_of_trees', 'training_deviance'
+                  g.position 'epochs', 'training_deviance'
                   g.strokeColor g.value '#1f77b4'
                 )
                 g.point(
-                  g.position 'number_of_trees', 'training_deviance'
+                  g.position 'epochs', 'training_deviance'
                   g.strokeColor g.value '#1f77b4'
                 )
                 g.from table


Reset the onLoadFinished handler after the first page is loaded to avoid cases where the page uses iframes which also trigger the callback

--- lib/guard/jasmine/phantomjs/run-jasmine.coffee
+++ lib/guard/jasmine/phantomjs/run-jasmine.coffee
@@ -234,6 +234,7 @@
 # Open web page and run the Jasmine test runner
 #
 page.open url, (status) ->
+  page.onLoadFinished = ->
   if status isnt 'success'
     console.log JSON.stringify({ error: "Unable to access Jasmine specs at #{ url }" })
     phantom.exit()


fix formula

fix formula

--- mechanics/001-binary-stars/csmain.coffee
+++ mechanics/001-binary-stars/csmain.coffee
@@ -27,9 +27,10 @@
         m2 = parseFloat(ps['m2'])
         a = parseFloat(ps['a'])
         e = parseFloat(ps['e'])
+        b = a * Math.sqrt(1 - e * e)
         M = m1 + m2
-        x1 = m2 / M * a
-        x2 = - m1 / M * a
+        y1 = m2 / M * b
+        y2 = - m1 / M * b

         v  = Math.sqrt(au.G * M / a * (1 + e) / (1 - e))
         v1 = 2 * v / M * m2
@@ -59,8 +60,8 @@

         vals = [
                 m1, m2,
-                x1, 0, 0, v1,
-                x2, 0, 0, v2
+                0, y1, 0, v1,
+                0, y2, 0, v2
         ]
         invoke = ->
             worker.postMessage(vals)


improved unread-changed event

improved unread-changed event

--- client/startup/alerts.coffee
+++ client/startup/alerts.coffee
@@ -1,17 +1,30 @@
 Meteor.startup ->

 	Tracker.autorun ->
+
 		unreadCount = 0
+		unreadAlert = false
+
 		subscriptions = ChatSubscription.find({}, { fields: { unread: 1 } })
-		(unreadCount += r.unread) for r in subscriptions.fetch()
+
+		for subscription in subscriptions.fetch()
+			unreadCount += subscription.unread
+			if subscription.alert is true
+				unreadAlert = '•'

 		rxFavico.set 'type', 'warn'
-		rxFavico.set 'count', unreadCount

 		if unreadCount > 0
 			document.title = '(' + unreadCount + ') Rocket.Chat'
+			rxFavico.set 'count', unreadCount
+			fireGlobalEvent 'unread-changed', unreadCount
+
+		else if unreadAlert isnt false
+			document.title = '(' + unreadAlert + ') Rocket.Chat'
+			rxFavico.set 'count', unreadAlert
+			fireGlobalEvent 'unread-changed', unreadAlert
+
 		else
 			document.title = 'Rocket.Chat'
-
-		fireGlobalEvent 'set-badge', unreadCount
-
+			rxFavico.set 'count', ''
+			fireGlobalEvent 'unread-changed', ''


Fix scroll in mobile

Fix scroll in mobile

--- client/views/app/room.coffee
+++ client/views/app/room.coffee
@@ -551,6 +551,10 @@
 			newMessage.className = "new-message not"
 	, 100

+	wrapper.addEventListener 'touchmove', ->
+		template.atBottom = false
+		onscroll()
+
 	wrapper.addEventListener 'mousewheel', ->
 		template.atBottom = false
 		onscroll()


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/color.coffee
+++ test/color.coffee
@@ -18,10 +18,9 @@
   equals color.b(), 134, 'blue channel should equal input blue value'
   equals color.a(), 0.45, 'alpha channel should equal input alpha value'

-###
 test "Color should parse hex starting with #", ->
   color = Color('#ffffff')
   equals color.r(), 255, 'red channel should equal numeric equivalent of ff'
   equals color.g(), 255, 'green channel should equal numeric equivalent of ff'
   equals color.b(), 255, 'blue channel should equal numeric equivalent of ff'
-###+


cleaning up array constructions

cleaning up array constructions

--- test/color.coffee
+++ test/color.coffee
@@ -258,17 +258,17 @@
   mixedColor = Color.mix(color1, color2)

   ok mixedColor.equals
-    r: -> 41
-    g: -> 72
-    b: -> 150
+    r: -> (83 / 2).round()
+    g: -> (145 / 2).round()
+    b: -> (150 / 2).round()
     a: -> 0.75
   , "Colors mix properly when not weighted"

   mixedColor2 = Color.mix(color1, color2, 0.1)

-  equals mixedColor2.r(), (3.3 + 45).floor()
-  equals mixedColor2.g(), (5.5 + 81).floor()
-  equals mixedColor2.b(), (10 + 180).floor()
+  equals mixedColor2.r(), (3.3 + 45).round()
+  equals mixedColor2.g(), (5.5 + 81).round()
+  equals mixedColor2.b(), (10 + 180).round()
   equals mixedColor2.a(), 0.1 + (0.5 * 0.9)

 # TODO: when taking in rgb values they are floored. should probably be rounded instead


where did that come from

where did that come from

--- src/scripts/filters/get-video-time.coffee
+++ src/scripts/filters/get-video-time.coffee
@@ -1,34 +0,0 @@
-angular.module('redditelly.filters')
-
-.filter 'getVideoTimefromURL', [ ->
-
-    # adapted from http://stackoverflow.com/a/5831191/1614967
-    youtubeRegexp = /https?:\/\/(?:[0-9A-Z-]+\.)?(?:youtu\.be\/|youtube(?:-nocookie)?\.com\S*[^\w\s-])([\w-]{11})(?=[^\w-]|$)(?![?=&+%\w.-]*(?:['"][^<>]*>|<\/a>))[?=&+%\w.-]*/ig
-
-    contains = (str, piece) ->
-        str.indexOf(piece) > -1
-
-    return (url='') ->
-        id = url.replace youtubeRegexp, '$1'
-
-        if contains id, ';'
-            pieces = id.split ';'
-
-            if contains pieces[1], '%'
-                # links like this: "http://www.youtube.com/attribution_link?a=pxa6goHqzaA&u=%2Fwatch%3Fv%3DdPdgx30w9sU%26feature%3Dshare"
-                # have the real query string URI encoded behind a ';'.
-                # at this point, `id is 'pxa6goHqzaA;u=%2Fwatch%3Fv%3DdPdgx30w9sU%26feature%3Dshare'
-                uriComponent = decodeURIComponent id.split(';')[1]
-                id = ('http://youtube.com' + uriComponent).replace youtubeRegexp, '$1'
-            else
-                # https://www.youtube.com/watch?v=VbNF9X1waSc&feature=youtu.be
-                # strip the ';feature=youtu.be'
-                id = pieces[0]
-
-        else if contains id, '#'
-            # id might look like '93LvTKF_jW0#t=1'
-            # and we want '93LvTKF_jW0'
-            id = id.split('#')[0]
-
-        return id
-]


Disable database

Disable database

--- app.coffee
+++ app.coffee
@@ -21,10 +21,10 @@

 app.get "/", routes.index

-models = require './models'
-rest = require "./routes/rest"
+# models = require './models'
+# rest = require "./routes/rest"

-rest.createRoutesFor "character", models.Character, app
+# rest.createRoutesFor "character", models.Character, app

 app.listen process.env.PORT, ->
   console.log "Express server listening on port %d in %s mode", app.address().port, app.settings.env


chart markers

chart markers

--- app.coffee
+++ app.coffee
@@ -45,15 +45,15 @@

         chartData = [] #[[0, 'Bruttobezug', 'Nettobezug', 'annotation']];
         for brutto in [0...7000]
-            if brutto % 200
+            if brutto % 500
                 continue;
             else
                 cC.setBrutto brutto
                 res = cC.calculate()
-                row = [brutto, brutto, null, res.netto]
+                row = [brutto, res.netto, ''+parseInt(res.netto), brutto, null]
                 console.log brutto, @brutto
                 if (brutto >= @brutto) && isCurrentPlotted == false
-                    row[2] = 'You'
+                    row[4] = 'You'
                     isCurrentPlotted = true
                 chartData.push row

@@ -65,6 +65,7 @@
         data.addColumn("number", "Bruttobezug");
         data.addColumn({ type: "string", role: "annotation" });
         data.addColumn("number", "Nettobezug");
+        data.addColumn({ type: "string", role: "annotation" });
         data.addRows(chartData)

         options = {
@@ -94,9 +95,9 @@
                     color: 'transparent'
                 }
             }
-            colors: ['#FA2A00','#3D1C00']
+            colors: ['#3D1C00','#FA2A00']
             lineWidth: 1
-            pointSize: 4
+            pointSize: 3
         }

         chart = new google.visualization.LineChart(document.querySelector('#chart'));


express.io + thrift

express.io + thrift

--- app.coffee
+++ app.coffee
@@ -20,6 +20,8 @@
   hello_func: (result) ->
     console.log("Client call: " + (++HelloHandler.call_counter));
     result(null, "Hello Apache Thrift for JavaScript " + HelloHandler.call_counter)
+
+    @req.io.broadcast 'new visitor'

 class TimesTwoHandler extends BaseHandler
 	dbl: (val, result) ->


Edit any generated emoji

Edit any generated emoji

--- app.coffee
+++ app.coffee
@@ -133,11 +133,12 @@
 	# TODO: animation (maybe have an update method on these objects? or have higher level descriptors?)

-emojis = (make_random_emoji() for [1..12])
-
 emojis_container = document.getElementById("emojis")

-for emoji in emojis
+selected_emoji_display = null
+
+make_emoji_display = (emoji)->
+
 	canvas = document.createElement("canvas")
 	ctx = canvas.getContext("2d")

@@ -149,37 +150,51 @@
 	canvas.width = size + spacing
 	canvas.height = size + spacing

-	draw_emoji(ctx, emoji, canvas.width/2, canvas.height/2, size)
-
-emoji = make_random_emoji()
-
-gui = new dat.GUI()
-mouth_folder = gui.addFolder('Mouth')
-mouth_folder.add(emoji.mouth, 'smile', -1, +1).name('Smile')
-mouth_folder.add(emoji.mouth, 'open').name('Open')
-mouth_folder.add(emoji.mouth, 'tongue', -1, +1).step(0.5)
-eyes_folder = gui.addFolder('Eyes')
-eyes_folder.add(emoji.eyes.left, 'type', {"Open": "open", "Wink": "wink"}).name('Left Eye')
-eyes_folder.add(emoji.eyes.left, 'offset_x', -1, 1).name('Left Eye X')
-eyes_folder.add(emoji.eyes.left, 'offset_y', -1, 1).name('Left Eye Y')
-eyes_folder.add(emoji.eyes.right, 'type', {"Open": "open", "Wink": "wink"}).name('Right Eye')
-eyes_folder.add(emoji.eyes.right, 'offset_x', -1, 1).name('Right Eye X')
-eyes_folder.add(emoji.eyes.right, 'offset_y', -1, 1).name('Right Eye Y')
-
-
-
-canvas = document.createElement("canvas")
-ctx = canvas.getContext("2d")
-
-canvas.classList.add("emoji")
-emojis_container.appendChild(canvas)
-
-size = 150
-spacing = size * 0.1 + 5
-canvas.width = size + spacing
-canvas.height = size + spacing
+	do update = =>
+		ctx.clearRect(0, 0, canvas.width, canvas.height)
+		draw_emoji(ctx, emoji, canvas.width/2, canvas.height/2, size)
+
+	this_emoji_display = {emoji, canvas, ctx, size, update}
+
+	canvas.style.cursor = "pointer"
+	canvas.addEventListener "click", (e)=>
+		selected_emoji_display = this_emoji_display
+		toggle_dat_gui_for_emoji(emoji)
+
+	this_emoji_display
+
+
+dat_gui = null
+dat_gui_open_for_emoji = null
+toggle_dat_gui_for_emoji = (emoji)->
+	if dat_gui and dat_gui_open_for_emoji is emoji
+		dat_gui.destroy()
+		dat_gui = null
+	else
+		open_dat_gui_for_emoji(emoji)
+open_dat_gui_for_emoji = (emoji)->
+	dat_gui?.destroy()
+	dat_gui = new dat.GUI()
+	dat_gui_open_for_emoji = emoji
+	mouth_folder = dat_gui.addFolder('Mouth')
+	mouth_folder.open()
+	mouth_folder.add(emoji.mouth, 'smile', -1, +1).name('Smile')
+	mouth_folder.add(emoji.mouth, 'open').name('Open')
+	mouth_folder.add(emoji.mouth, 'tongue', -1, +1).step(0.5)
+	eyes_folder = dat_gui.addFolder('Eyes')
+	eyes_folder.open()
+	eyes_folder.add(emoji.eyes.left, 'type', {"Open": "open", "Wink": "wink"}).name('Left Eye')
+	eyes_folder.add(emoji.eyes.left, 'offset_x', -1, 1).name('Left Eye X')
+	eyes_folder.add(emoji.eyes.left, 'offset_y', -1, 1).name('Left Eye Y')
+	eyes_folder.add(emoji.eyes.right, 'type', {"Open": "open", "Wink": "wink"}).name('Right Eye')
+	eyes_folder.add(emoji.eyes.right, 'offset_x', -1, 1).name('Right Eye X')
+	eyes_folder.add(emoji.eyes.right, 'offset_y', -1, 1).name('Right Eye Y')
+
+
+emojis = (make_random_emoji() for [1..12])
+emoji_displays = (make_emoji_display(emoji) for emoji in emojis)
+selected_emoji_display = emoji_displays[emoji_displays.length - 1]

 animate ->
-	ctx.clearRect(0, 0, canvas.width, canvas.height)
-	draw_emoji(ctx, emoji, canvas.width/2, canvas.height/2, size)
-
+	selected_emoji_display?.update()
+


Console log update url incase the open module fails for whatever reason

--- app.coffee
+++ app.coffee
@@ -77,6 +77,8 @@
     data = JSON.parse(data)
     if data.version != pkg.version
       cl 'This seems to be an old version, let me open the download page for you to get an update!', 'yellow'
+      cl "If a new window doesn't open for you, get the latest here.", 'yellow'
+      cl "https://github.com/dustinblackman/Championify/releases/latest", 'yellow'
       open('https://github.com/dustinblackman/Championify/releases/latest')
       enterToExit()
     else


Fix test description.

Fix test description.

--- src/spec/producttypegenerator.spec.coffee
+++ src/spec/producttypegenerator.spec.coffee
@@ -16,7 +16,7 @@
   it 'should return no languages for not localized property header', ->
     expect(@generator._languages('name', ['name', 'label.de', 'label.en', 'enumlabel.de', 'enumlabel.en', 'enumlabel.it'])).toEqual []

-  it 'should return no languages for not localized property header', ->
+  it 'should return JSON attribute definitions', ->

     attributeRow1 =
       name: 'gender'


added fix_orientation and scale_geometry

--- modules/gui/platforms/node/tools/images.coffee
+++ modules/gui/platforms/node/tools/images.coffee
@@ -1,13 +1,24 @@

 im = require 'imagemagick'

-module.exports =
+module.exports = images =

     ##
     ##
     ##
     convert: (args...)->
         im.convert.apply im, args
+
+    ##
+    ##
+    ##
+    fix_orientation: (file, done)->
+        temp = floyd.tools.files.tmp()
+        im.convert [file, '-auto-orient', temp], (err)=>
+            return done(err) if err
+
+            floyd.tools.files.mv temp, file
+            images.info file, done

     ##
     ##
@@ -68,6 +79,19 @@
     ##
     ##
     ##
+    scale_geometry: (geometry, factor)->
+        [width, parts] = geometry.split 'x'
+        (list = parts.split '+').unshift width
+
+        for i in [0..list.length-1]
+            list[i] = Math.round parseInt(list[i]) * factor
+
+        return floyd.tools.strings.vsprintf '%sx%s+%s+%s', list
+
+
+    ##
+    ##
+    ##
     info: (file, fn)->

         im.identify file, fn


restore JSV benchmarking, but only for draft 3

--- benchmarks/validators.coffee
+++ benchmarks/validators.coffee
@@ -42,16 +42,21 @@
         for i in [1..repeats]
           result = validator.validate(valid_doc, schema).errors

-    ## Only valid for draft3 ATM.
-    #jsv_bm = new Benchmark "JSV: valid document", (bm) ->
-      #bm.setup ->
-        #jsv = JSV.createEnvironment("json-schema-draft-03")
-        #jsv.createSchema(schema)
-      #bm.measure (validator) ->
-        #for i in [1..repeats]
-          #result = validator.validate(valid_doc).errors
+    if 3 == draft # JSV currently lacks draft 4 support

-    results = Benchmark.compare [jsck, jsonschema], {samples}
+      jsv_bm = new Benchmark "JSV: valid document", (bm) ->
+        bm.setup ->
+          jsv = JSV.createEnvironment("json-schema-draft-03")
+          jsv.createSchema(schema)
+        bm.measure (validator) ->
+          for i in [1..repeats]
+            result = validator.validate(valid_doc).errors
+
+    switch draft
+      when 3
+        results = Benchmark.compare [jsck, jsonschema, jsv_bm], {samples}
+      when 4
+        results = Benchmark.compare [jsck, jsonschema], {samples}

     console.log()
     for name, result of results


STL/dom reworking dom-ready so that it worked when a page is already loaded

--- stl/dom/src/event/ready.coffee
+++ stl/dom/src/event/ready.coffee
@@ -5,20 +5,30 @@
 # Copyright (C) 2011 Nikolay Nemshilov
 #
 Document.include
-  on: (name)->
-    if name is 'ready' and @_ir is undefined
-      doc   = @_
-      ready = bind(@emit, @, 'ready')
+  on: (name, callback)->
+    Element_events.on.apply(@, arguments)

-      if 'readyState' of doc
-        do ->
-          if (doc.readyState in ['loaded', 'complete'])
-            ready()
+    if name is 'ready'
+      doc = @_
+      id  = uid(doc)
+
+      if Ready_documents[id]
+        callback.apply(@) # if the document is alrady loaded
+      else
+        unless id of Ready_documents
+          Ready_documents[id] = false
+          ready = bind(@emit, @, 'ready')
+
+          if 'readyState' of doc
+            do ->
+              if (doc.readyState in ['loaded', 'complete'])
+                ready()
+              else
+                setTimeout(arguments.callee, 50)
           else
-            setTimeout(arguments.callee, 50)
-      else
-        doc.addEventListener('DOMContentLoaded', ready, false)
+            doc.addEventListener('DOMContentLoaded', ready, false)

-      @_ir = true # prevening from double initialization
+    return @

-    Element_events.on.apply(this, arguments)+# the ready documents index
+Ready_documents = []


Refactor calendar.js

Refactor calendar.js

--- app/assets/javascripts/calendar.js.coffee
+++ app/assets/javascripts/calendar.js.coffee
@@ -52,12 +52,8 @@
   selectMonth: (year, month, call) ->
     @selectedMonth = new Month(year, month)

-    if null == @startMonth
-      @startMonth = new Month(year, month)
-      @startMonth.subtract(@leftMargin)
-    else if (@selectedMonth.minus(@startMonth) < 0 || @selectedMonth.minus(@startMonth) >= 12)
-      @startMonth.set(@selectedMonth)
-      @startMonth.subtract(@leftMargin)
+    @startMonth = new Month(year, month)
+    @startMonth.subtract(@leftMargin)

     current = new Month(@startMonth.year, @startMonth.month)

@@ -70,9 +66,9 @@

     str = "
"
     str += "
"
-    str += "<<	"
+    str += "<<	"
     str += "" + current.year + "	"
     if colspan < 12
       nextYear = current.year + 1
@@ -81,9 +77,9 @@
       str += "" + nextYear + "	"
-    str += ">>	"
+    str += ">>	"
     str += "
"
     str += "
"
     for i in [0..11]
@@ -93,7 +89,7 @@
       else
         str += ""
         str += "
"
-        str += ""
+        str += ""
       str += current.month + "月"
       if !@selectedMonth.equals(current)
         str += ''
@@ -117,3 +113,7 @@
   if $('#calendar').length > 0
     @calendar = new Calendar()
     @calendar.selectMonth($('#calendar').data('initial-year'), $('#calendar').data('initial-month'), false)
+
+  $(document).on('click', '#calendar .calendar_selector', ->
+    document.calendar.selectMonth($(@).data('year'), $(@).data('month'), true)
+  )


skip fix

skip fix

--- bot.coffee
+++ bot.coffee
@@ -101,10 +101,7 @@
     bot.moderateRemoveDJ(currentDJ.id)
 ###
 userSkip = () ->
-    if currentDJ is null
-        return
-    console.log 'skip ' + currentDJ.username
-
+    console.log 'skipping someone'
     bot.skipSong((data) ->
         console.log 'skip callback'
         console.log data


chore: fix comment typos

chore: fix comment typos

--- _src/lib/node_cache.coffee
+++ _src/lib/node_cache.coffee
@@ -94,7 +94,7 @@
 		if (err = @_isInvalidKey( key ))?
 			throw err

-		# get data and incremet stats
+		# get data and increment stats
 		if @data[ key ]? and @_check( key, @data[ key ] )
 			@stats.hits++
 			_ret = @_unwrap( @data[ key ] )
@@ -148,7 +148,7 @@
 	# **Parameters:**
 	#
 	# * `key` ( String | Number ): cache key
-	# * `value` ( Any ): A element to cache. If the option `option.forceString` is `true` the module trys to translate it to a serialized JSON
+	# * `value` ( Any ): An element to cache. If the option `option.forceString` is `true` the module trys to translate it to a serialized JSON
 	# * `[ ttl ]` ( Number | String ): ( optional ) The time to live in seconds.
 	#
 	# **Example:**
@@ -204,7 +204,7 @@
 	#
 	# **Parameters:**
 	#
-	# * `keyValueSet` ( Object[] ): an array of object which includes key,value and ttl
+	# * `keyValueSet` ( Object[] ): an array of objects which include key, value, and ttl
 	#
 	# **Example:**
 	#
@@ -251,7 +251,7 @@
 	#
 	# **Parameters:**
 	#
-	# * `keys` ( String | Number | String|Number[] ): cache key to delete or a array of cache keys
+	# * `keys` ( String | Number | String|Number[] ): cache key to delete or an array of cache keys
 	#
 	# **Return**
 	#
@@ -357,7 +357,7 @@
 	#
 	# **Parameters:**
 	#
-	# * `key` ( String | Number ): cache key to check the ttl value
+	# * `key` ( String | Number ): cache key to check the ttl value of
 	#
 	# **Return**
 	#


reload js and coffee by default too

reload js and coffee by default too

--- options.coffee
+++ options.coffee
@@ -8,7 +8,7 @@
     config: "{harp,sites}/**/{config,compass}.rb"
     ensure: true # compile everything to be sure
   livereload:
-    watch: "{harp,sites}/**/*.{jade,html,css}"
+    watch: "{harp,sites}/**/*.{jade,html,css,js,coffee}"
   gulp:
     default: ["server", "compass", "livereload"]


Adding a rotate performance test.

Adding a rotate performance test.

--- tests/test_performance.coffee
+++ tests/test_performance.coffee
@@ -50,3 +50,7 @@
         for e2 in elements[idx..elements.length]
             e1.intersects(e2)
     return this
+
+JSLitmus.test 'rotate', () ->
+    (p.rotate(random(0, 360)) for p in polygons)
+    return this


Social: add a bridge function for fetching profile feed

--- workers/social/lib/social/models/socialapi/requests.coffee
+++ workers/social/lib/social/models/socialapi/requests.coffee
@@ -164,6 +164,12 @@
   if not data.name
     return callback { message: "Name should be set for topic search"}
   url = "#{SOCIAL_API_URL}/channel/search"
+  get url, data, callback
+
+fetchProfileFeed = (data, callback)->
+  if not data.accountId
+    return callback { message: "AccountId should be set"}
+  url = "#{SOCIAL_API_URL}/account/#{data.accountId}/posts"
   get url, data, callback

 post = (url, data, callback)->
@@ -183,6 +189,7 @@
   , wrapCallback callback

 module.exports = {
+  fetchProfileFeed
   searchTopics
   fetchPrivateMessages
   sendPrivateMessage


Fixed it

Fixed it

--- bot.coffee
+++ bot.coffee
@@ -18,7 +18,7 @@
   diff = 0
   config = {}
   for i in [2..5]
-    diff = diff + rand(1)
+    diff = diff + 1 + rand(1)
     config[i] =
       point: "#{rand(7-i)}#{diff}"
       orientation: "horizontal"


nearEl => $paragraph

nearEl => $paragraph

--- app/js/jail_iframe/classes/buttons.coffee
+++ app/js/jail_iframe/classes/buttons.coffee
@@ -186,7 +186,7 @@
       mouseleave: => @stopHovering()
       click: => @_onClick()

-    @nearEl = options.el
+    @$paragraph = $(options.el)

     @_fadeIn()
     @_updatePositionRegularly()
@@ -198,11 +198,11 @@
     FactlinkJailRoot.off 'factlink.factsLoaded factlinkAdded', @_destroyIfContainsFactlink

   _destroyIfContainsFactlink: =>
-    if $(@nearEl).find('.factlink').length > 0
+    if @$paragraph.find('.factlink').length > 0
       @destroy()

   _updatePosition: ->
-    contentBox = FactlinkJailRoot.contentBox(@nearEl)
+    contentBox = FactlinkJailRoot.contentBox(@$paragraph[0])

     @_showAtCoordinates contentBox.top, contentBox.left + contentBox.width

@@ -211,7 +211,7 @@
       @$boundingBox = FactlinkJailRoot.drawBoundingBox contentBox, 'green'

   _onClick: ->
-    text = $(@nearEl).text()
+    text = @$paragraph.text()
     siteTitle = window.document.title
     siteUrl = FactlinkJailRoot.siteUrl()


Add plantuml to atom.

Add plantuml to atom.

--- atom/packages.cson
+++ atom/packages.cson
@@ -6,11 +6,13 @@
   "docblockr"
   "file-icons"
   "highlight-selected"
+  "language-plantuml"
   "language-smarty"
   "linter"
   "linter-php"
   "minimap"
   "package-sync"
+  "plantuml-viewer"
   "seti-syntax"
   "seti-ui"
 ]


Add LaTeX grammar to Atom

Add LaTeX grammar to Atom

--- atom/packages.cson
+++ atom/packages.cson
@@ -31,6 +31,7 @@
   "language-sca"
   "language-svg"
   "language-tcl"
+  "language-tex"
   "language-viml"
   "language-x86"
   "linter"


Add Ruby snippet for test blocks

Add Ruby snippet for test blocks

--- atom/snippets.cson
+++ atom/snippets.cson
@@ -2,6 +2,14 @@
   "Hashrocket":
     prefix: "="
     body: " => "
+
+  "Test block":
+    prefix: "test"
+    body: """
+      test "$1" do
+        $2
+      end
+    """

 ".source.coffee":
   "Describe block":


Fixed incorrect variable name.

Fixed incorrect variable name.

--- src/coffee/expression/functionPluralities.coffee
+++ src/coffee/expression/functionPluralities.coffee
@@ -24,7 +24,7 @@
 #   "map".
 #   This is used by cross.
 #   (3, 1, 2) cross 8 = (3, 1, 2) cross (8, 8, 8)
-module.exports = functionPlurality =
+module.exports = expressionFunctionPluralities =
     concatenateBoolean: "concatenate"
     concatenateInteger: "concatenate"
     concatenateFloat: "concatenate"


preserve3d on stage

preserve3d on stage

--- oriDomi.coffee
+++ oriDomi.coffee
@@ -301,6 +301,7 @@
       shader.style.opacity = '0'
       shader.style.top = '0'
       shader.style.left = '0'
+      shader.style.pointerEvents = 'none'

     # The content holder is a clone of the target element.
     # Every panel will contain one.


removed random process.env key assignment

--- bin/command.coffee
+++ bin/command.coffee
@@ -44,8 +44,6 @@

 # use to extract the id from the command
 findId = require './find-id'
-
-process.env.TESTID_key2 = 'value2'

 # `nuc` uses itself for its own configuration values
 values = require('../lib') id:'nuc', defaults:


Making the CoffeeScript more idiomatic.

Making the CoffeeScript more idiomatic.

--- lib/formats/jsonschema-v4.coffee
+++ lib/formats/jsonschema-v4.coffee
@@ -11,7 +11,7 @@
   valueType = value.base?.typeSpecification?.name  # for top-level objects
   valueType = valueType or value.content?.valueDefinition?.typeDefinition?.typeSpecification?.name

-  if not valueType
+  unless valueType
     if value.content?.valueDefinition?.values?.length > 1
       valueType = 'array'
     else
@@ -45,7 +45,7 @@
         required: 'required' in (prop.content?.valueDefinition?.typeDefinition?.attributes or [])

   , (err, reprWrappers) ->
-    if err then return cb err
+    return cb err if err

     # prepare containers for the final representation of the object
     propsRepr = {}
@@ -54,20 +54,19 @@
     # unwrap info for each property and render what needs to be rendered
     for {name, repr, required} in reprWrappers
       propsRepr[name] = repr
-      if required
-        requiredRepr.push name
+      requiredRepr.push name if required

     # build the final object representation and send it to callback
     repr =
       type: 'object'
       properties: propsRepr
-    if requiredRepr.length > 0 then repr.required = requiredRepr
+    repr.required = requiredRepr if requiredRepr.length > 0
     cb null, repr

 transform = (type, symbolTable, options, cb) ->
   handleObject type, symbolTable, options, (err, repr) ->
-    if err then return cb err
+    return cb err if err
     repr["$schema"] = "http://json-schema.org/draft-04/schema#"
     cb null, repr


Match mode bug.

Match mode bug.

--- png-to-scr.coffee
+++ png-to-scr.coffee
@@ -354,7 +354,7 @@
                   # We're of single color. See if the neighbor was as well.
                   if neighborBlockData.colors.length is 1
                     # The neighbour was also single color, so we should just do what they did if our colors match.
-                    blockConvertSingleToInk = neighborConvertSingleToInk if attribute.ink is neighborAttribute.ink
+                    blockConvertSingleToInk = neighborConvertSingleToInk if attribute.ink is (if neighborConvertSingleToInk then neighborAttribute.ink else neighborAttribute.paper)

                   else
                     # If our color is neighbour's ink color, we should be too.


add test variables to fshelper

add test variables to fshelper

--- client/Finder/fs/fshelper.coffee
+++ client/Finder/fs/fshelper.coffee
@@ -212,11 +212,13 @@
     get    : (name)->
       "#{KD.config.uploadsUri}/#{KD.whoami().getId()}/#{name}"

-    upload : (name, content, callback)->
+    upload : (name, content, type, slug, callback)->
       vmController = KD.getSingleton 'vmController'
+      # TODO : change before merging
+      args = {name, bucket: "groups", "groupName", content}
       vmController.run
         method    : 's3.store'
-        withArgs  : {name, content}
+        withArgs  : args
       , (err, res)->
         if err then callback err
         else callback null, FSHelper.s3.get name


Added rpartition

Added rpartition


workaround

workaround

--- lib/debugger/stepper-view.coffee
+++ lib/debugger/stepper-view.coffee
@@ -42,14 +42,20 @@
     [ed, tab] = @edAndTab ed
     x.classList.remove 'debug' for x in [ed, tab]

+  attach: ->
+    @marker = @editor.markBufferPosition [@line, Infinity]
+    @editor.decorateMarker @marker,
+      type: 'overlay'
+      item: @view
+    # Work around markers being destroyed when closing a split editor
+    @mListener?.dispose()
+    @mListener = @marker.onDidDestroy => @attach() unless @destroyed
+
   constructor: (@editor, @line) ->
     @createView()
     @addClass @editor
     @fadeIn()
-    @marker = @editor.markBufferPosition [line, Infinity]
-    @editor.decorateMarker @marker,
-      type: 'overlay'
-      item: @view
+    @attach()

   fadeIn: ->
     @view.classList.add 'ink-hide'
@@ -68,6 +74,8 @@
     @animate => @marker.setHeadBufferPosition [line, Infinity]

   destroy: ->
+    @destroyed = true
+    @mListener.dispose()
     @rmClass @editor
     @fadeOut =>
       @marker.destroy()


DataAttr can now set multiple attrs

DataAttr can now set multiple attrs


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/state.coffee
+++ test/state.coffee
@@ -4,3 +4,15 @@

   equals callback != null, true, "callback shouldn't be null"

+test "State events should check if event can be fired", ->
+  car = {}
+
+  sm = StateMachine('state', car, { initial: 'parked' }, (machine) ->
+    machine.event('start', (event) ->
+      event.transition({ from: 'parked', to: 'idling' })
+
+    machine.event('stop', (event) ->
+      event.transition({ from: 'idling', to: 'parked' })
+
+    ok(car.can_start())
+    ok(!car.can_stop())


grammar fixed. repository was wrong

grammar fixed. repository was wrong

--- grammars/lo.cson
+++ grammars/lo.cson
@@ -16,24 +16,24 @@
     'name':'comment.line.double-dash.lo'
   }
   {
+    'name': 'string.quoted.double.lo'
     'begin': '"'
-    'end': '[^\\\\]?"'
-    'name': 'string.quoted.double.lo'
+    'beginCaptures':
+  	  '0':
+    		'name': 'punctuation.definition.string.begin.lo'
+  	'end': '"'
+  	'endCaptures':
+  	  '0':
+    		'name': 'punctuation.definition.string.end.lo'
     'patterns': [
-      { 'include' : '#escaped-char'}
       {
-        'begin':'\\\\\\('
-        'end':'\\)'
-        'name':'string.interpolated.lo'
-        'patterns' : [
-          { 'include':'$self' }
-        ]
+        'include':'#quoted'
       }
     ]
   }
   {
-    'begin': '\''
-    'end': '[^\\\\]?\''
+    'begin': "'"
+    'end': "'"
     'contentName': 'string.quoted.single.lo'
     'patterns': [
         'include' : '#escaped-char'
@@ -41,7 +41,7 @@
   }
   {
     'begin': '\`'
-    'end': '[^\\\\]?\`'
+    'end': '\`'
     'contentName': 'regexp.lo'
     'patterns': [
         'include' : '#escaped-char'
@@ -75,11 +75,37 @@
     'match': '\\b[a-zA-Z_][a-zA-Z0-9_]*\\b'
     'name': 'variable.language.lo'
   }
-  {
-    'repository' : [
-      'escaped-char' :
-        'name' : 'constant.character.escape'
-        'matches' : '\\\\([^u\(]|u[0-9a-zA-F]+;)'
+]
+'repository' :
+  'quoted':
+    'patterns':[
+      {
+        'name' : 'embedded.line.lo'
+        'contentName':'source.lo'
+        'begin':'\\\\\\('
+        'end':'\\)'
+        'beginCaptures':
+          '0':
+            'name': 'punctuation.section.embedded.begin.lo'
+        'endCaptures':
+          '0':
+            'name': 'punctuation.section.embedded.end.lo'
+        'patterns' : [
+          { 'include':'$self' }
+        ]
+      }
+      {
+        'include' : '#escaped-char'
+      }
     ]
-  }
-]
+  'escaped-char' :
+    'patterns':[
+      {
+        'name':'constant.character.escape.lo'
+        'match':'\\\\[\\\\tnr"\']'
+      }
+      {
+        'match':'\\\\u[0-9a-zA-F]+;'
+        'name':'constant.character.escape.lo'
+      }
+    ]


Make Amex goal-oriented.

Make Amex goal-oriented.

--- application/chrome/content/wesabe/fi-scripts/com/americanexpress.coffee
+++ application/chrome/content/wesabe/fi-scripts/com/americanexpress.coffee
@@ -7,7 +7,7 @@
       fid: "com.americanexpress.web"
       org: "American Express Cards"
       dispatchFrames: false
-      afterDownload: "logout"
+      afterLastGoal: "logout"
       includes: [
         "fi-scripts.com.americanexpress.login"
         "fi-scripts.com.americanexpress.accounts"


Add a count when output pane is collapsed

--- lib/features/typechecking.coffee
+++ lib/features/typechecking.coffee
@@ -1,6 +1,28 @@
 {MessagePanelView, LineMessageView} = require 'atom-message-panel'
 _ = require 'lodash'
 {log, isScalaSource} = require '../utils'
+
+class ErrorSummary
+  constructor: (@errors, @warnings) ->
+
+  @zero: new ErrorSummary 0, 0
+
+  add: (note) ->
+    switch note.severity.typehint
+      when "NoteError" then new ErrorSummary @errors + 1, @warnings
+      when "NoteWarn" then new ErrorSummary @errors, @warnings + 1
+      else new ErrorSummary @errors, @warnings
+
+  addNotes: (notes) ->
+    _.reduce notes,
+      (sum, note) -> sum.add note,
+      this
+
+  text: () ->
+    switch (@errors + @warnings)
+      when 0 then ''
+      else 'Errors: ' + @errors + ' Warnings: ' + @warnings
+

 module.exports =
 class TypeChecking
@@ -9,7 +31,7 @@
     @messages = new MessagePanelView
       title: 'Ensime'
     @messages.attach()
-
+    @summary = ErrorSummary.zero

   addScalaNotes: (msg) ->
     notes = msg.notes
@@ -31,6 +53,9 @@
     for file, notes of @notesByFile
       if(not file.includes('dep-src')) # TODO: put under flag
         addNoteToMessageView note for note in notes
+        @summary = @summary.addNotes(notes)
+
+    @messages.setSummary { summary: @summary.text() }

   hide: ->
     @messages?.hide()
@@ -40,6 +65,8 @@

   clearScalaNotes: ->
     @messages.clear()
+    @messages.setSummary { summary: '' }
+    @summary = ErrorSummary.zero

   # cleanup
   destroy: ->


Debug mode

Debug mode

--- gruntfile.coffee
+++ gruntfile.coffee
@@ -6,8 +6,6 @@
       build     : 'build',
       bower     : 'bower',
       extensions: 'extensions',
-
-      ide_nw    : 'examples/ide/node-webkit',
       version   : '',
       banner    : '/* <%= pkg.name %> v<%= pkg.version %> - <%= grunt.template.today("yyyy/m/d") %>\n' +
                 '   <%= pkg.homepage %>\n' +
@@ -87,10 +85,10 @@

     coffee:
-      core        : files: '<%=meta.build%>/<%=pkg.name%>.debug.js'             : '<%=meta.build%>/<%=pkg.name%>.debug.coffee'
+      core        : files: '<%=meta.bower%>/<%=pkg.name%>.debug.js'             : '<%=meta.build%>/<%=pkg.name%>.debug.coffee'
       spec        : files: '<%=meta.build%>/<%=pkg.name%>.spec.js'              : '<%= source.spec %>'
       # Extension
-      app         : files: '<%=meta.build%>/<%=pkg.name%>.app.debug.js'         : '<%=meta.build%>/<%=pkg.name%>.app.coffee'
+      app         : files: '<%=meta.bower%>/<%=pkg.name%>.app.debug.js'         : '<%=meta.build%>/<%=pkg.name%>.app.coffee'
       app_appnima : files: '<%=meta.build%>/<%=pkg.name%>.app.appnima.js'       : '<%=meta.build%>/<%=pkg.name%>.app.appnima.coffee'
       app_carousel: files: '<%=meta.build%>/<%=pkg.name%>.app.carousel.js'      : '<%=meta.build%>/<%=pkg.name%>.app.carousel.coffee'
       app_gmaps   : files: '<%=meta.build%>/<%=pkg.name%>.app.gmaps.js'         : '<%=meta.build%>/<%=pkg.name%>.app.gmaps.coffee'
@@ -102,11 +100,11 @@
       options:  banner: "<%= meta.banner %>"#, report: "gzip"
       core:
         options: mangle: true
-        files: '<%=meta.bower%>/<%=pkg.name%>.js'                               : '<%=meta.build%>/<%=pkg.name%>.debug.js'
+        files: '<%=meta.bower%>/<%=pkg.name%>.js'                               : '<%=meta.bower%>/<%=pkg.name%>.debug.js'
       # Extensions
       app:
         options: mangle: false
-        files: '<%=meta.bower%>/<%=pkg.name%>.app.js'                           : '<%=meta.build%>/<%=pkg.name%>.app.debug.js'
+        files: '<%=meta.bower%>/<%=pkg.name%>.app.js'                           : '<%=meta.bower%>/<%=pkg.name%>.app.debug.js'
       app_appnima:
         options: mangle: false
         files: '<%=meta.extensions%>/appnima/<%=pkg.name%>.app.appnima.js'      : '<%=meta.build%>/<%=pkg.name%>.app.appnima.js'


Updated task order

Updated task order

--- gruntfile.coffee
+++ gruntfile.coffee
@@ -41,5 +41,5 @@
   grunt.loadNpmTasks 'grunt-coffeelint'
   grunt.loadNpmTasks 'grunt-mocha'

-  grunt.registerTask 'test', ['coffeelint']
-  grunt.registerTask 'default', ['coffeelint', 'coffee', 'uglify']
+  grunt.registerTask 'test', ['coffeelint', 'mocha']
+  grunt.registerTask 'default', ['coffee', 'test', 'uglify']


another email oauth fix

another email oauth fix

--- salvus/hub.coffee
+++ salvus/hub.coffee
@@ -380,7 +380,7 @@
         opts.last_name = "User"

     if opts.emails?
-        opts.emails = (x.toLowerCase() for x in opts.emails when (x? and x.toLowerCase?))
+        opts.emails = (x.toLowerCase() for x in opts.emails when (x? and x.toLowerCase? and client_lib.is_valid_email_address(x)))

     opts.id = "#{opts.id}"  # convert to string (id is often a number)


minor

minor

--- scripts-src/standalone_process.coffee
+++ scripts-src/standalone_process.coffee
@@ -1,3 +1,5 @@
+#!/usr/bin/env coffee
+
 fs = require "fs"
 jss = require "JSONStream"
 stdio = require "stdio"


fix use of context helper in onChange

fix use of context helper in onChange

--- services/web/public/coffee/ide/editor/directives/aceEditor/auto-complete/AutoCompleteManager.coffee
+++ services/web/public/coffee/ide/editor/directives/aceEditor/auto-complete/AutoCompleteManager.coffee
@@ -142,8 +142,8 @@
 		onChange: (change) ->
 			cursorPosition = @editor.getCursorPosition()
 			end = change.end
-			context = Helpers.getContext(editor, end)
-			{lineUpToCursor, commandFragment, lineBeyondCursor, needsClosingBrace} = context
+			context = Helpers.getContext(@editor, end)
+			{lineUpToCursor, commandFragment, commandName, lineBeyondCursor, needsClosingBrace} = context
 			if lineUpToCursor.match(/.*%.*/)
 				return
 			lastCharIsBackslash = lineUpToCursor.slice(-1) == "\\"


About page fix

About page fix

--- client/landing/app/MainApp/kodingrouter.coffee
+++ client/landing/app/MainApp/kodingrouter.coffee
@@ -205,7 +205,8 @@

       '/'      : handleRoot
       ''       : handleRoot
-      '/About' : createStaticContentHandler 'Home', yes
+      # '/About' : createStaticContentHandler 'Home', yes
+      '/About' : createSectionHandler 'Activity'

       # verbs
       '/:name?/Login'     : ({params:{name}})->


Guard against null results

Guard against null results

--- build/tasks/spec-task.coffee
+++ build/tasks/spec-task.coffee
@@ -140,6 +140,9 @@
         [runCoreSpecs, runPackageSpecs]

     method specs, (error, results) ->
+      failedPackages = []
+      coreSpecFailed = []
+
       if process.env.ATOM_SPECS_TASK is 'packages'
         [failedPackages] = results
       else if process.env.ATOM_SPECS_TASK is 'core'
@@ -150,12 +153,12 @@
       console.log results
       elapsedTime = Math.round((Date.now() - startTime) / 100) / 10
       grunt.log.ok("Total spec time: #{elapsedTime}s using #{concurrency} cores")
-      failures = failedPackages ? []
-      failures.push "atom core" if coreSpecFailed
+      failures = failedPackages
+      failures.push "atom core" if coreSpecFailed.length > 0

       grunt.log.error("[Error]".red + " #{failures.join(', ')} spec(s) failed") if failures.length > 0

       if process.platform is 'win32' and process.env.JANKY_SHA1
         done()
       else
-        done(not coreSpecFailed and failedPackages?.length is 0)
+        done(not coreSpecFailed and failedPackages.length is 0)


Fix kassaStateToggle adding an extra history token on defaultValue

--- app/assets/javascripts/directives/kassa_state_toggle.coffee
+++ app/assets/javascripts/directives/kassa_state_toggle.coffee
@@ -6,7 +6,7 @@
       defaultValue = $attrs.kassaStateDefault || false

       unless $location.search()[stateKey]?
-       $location.search stateKey, defaultValue
+       $location.search(stateKey, defaultValue).replace()

       isInVisibleState = (key)->
         stateValue = $location.search()[key]


prepend http:// for link

prepend http:// for link

--- app/assets/javascripts/editor_plugin/link_creator.js.coffee
+++ app/assets/javascripts/editor_plugin/link_creator.js.coffee
@@ -12,7 +12,10 @@
     $("#link-form").on "submit", (event) =>
       event.preventDefault()
       @editor.restoreRange()
-      @editor.formator.exec 'createLink', @input.val()
+      url = @input.val()
+      if !url.match(/\w+:\/\//)
+        url = "http://#{url}"
+      @editor.formator.exec 'createLink', url
       @editor.formator.afterFormat()
       @input.val('')
       @close()


fix again

fix again

--- app/assets/javascripts/monster_mithril/helpers/popup.coffee
+++ app/assets/javascripts/monster_mithril/helpers/popup.coffee
@@ -45,8 +45,8 @@
     @$.model.errors {}
     @$.pop(true)
     @$.title "#{@_action} #{@_controller.singularize()}".titleize()
-    @reindex()
-  reindex:=>
+    @reindex(data)
+  reindex:(data)=>
     switch @_action
       when 'edit'
         if @can_pull('edit')


OX-721 seperating queue arrays

OX-721 seperating queue arrays

--- app/assets/javascripts/oxalis/model/binary/pullqueue.coffee
+++ app/assets/javascripts/oxalis/model/binary/pullqueue.coffee
@@ -12,7 +12,7 @@
   BUCKET_TIME_SMOOTHER : .125

   cube : null
-  queue : []
+  queue : null

   dataSetName : ""

@@ -21,6 +21,8 @@

   constructor : (@dataSetName, @cube, @dataLayerName, @testData) ->
+
+    @queue = []

   swap : (a, b) ->


White-space cleanup

White-space cleanup

--- phantom.coffee
+++ phantom.coffee
@@ -33,8 +33,6 @@
       page._onResourceRequested = page.onResourceRequested
       page.onResourceRequested = (fn, cb) -> page._onResourceRequested.apply(page, [fn.toString(), cb])
       cb page
-
-

 module.exports =
   create: ->


correctly generate empty lines in code blocks

--- lib/markdown-preview-view.coffee
+++ lib/markdown-preview-view.coffee
@@ -164,7 +164,10 @@
       grammar = atom.syntax.selectGrammar("foo.#{extension}", text)

       codeBlock.empty()
-      for tokens in grammar.tokenizeLines(text)
-        codeBlock.append(EditorView.buildLineHtml({ tokens, text }))
+
+      for tokens in grammar.tokenizeLines(text).slice(0, -1)
+        lineText = (token.value for token in tokens).join('')
+        htmlEolInvisibles = ''
+        codeBlock.append(EditorView.buildLineHtml({ tokens, text: lineText, htmlEolInvisibles }))

     html


whitespace gardening

whitespace gardening

--- scripts/gh-book/remote-updater.coffee
+++ scripts/gh-book/remote-updater.coffee
@@ -63,7 +63,3 @@
                           lastEditedBy: commit.author.login

                         model.set attributes, {parse:true}
-
-
-
-


Keep track of the file if it gets renamed

subscribeToFilePath is a bit misleading here...it doesn't actually
subscribe to the file path changing in any way.  Now we subscribe to the
rename event and fire did-change-title so that the Markdown Preview
titles get updated properly.

--- lib/markdown-preview-view.coffee
+++ lib/markdown-preview-view.coffee
@@ -53,6 +53,7 @@
   subscribeToFilePath: (filePath) ->
     @file = new File(filePath)
     @emitter.emit 'did-change-title'
+    @disposables.add @file.onDidRename(=> @emitter.emit 'did-change-title')
     @handleEvents()
     @renderMarkdown()


Better console out for get.coffee

Better console out for get.coffee

--- script/get.coffee
+++ script/get.coffee
@@ -1,5 +1,6 @@
 msgpack = require 'msgpack'
 redis = require 'redis'
+util = require 'util'
 _ = require '../lib/util'

 opts = require('optimist')
@@ -87,7 +88,7 @@
         value = unpack_fields value, fields
       if accept value
         console.log "\n-- #{key} --\n"
-        console.log value
+        console.log util.inspect(value, false, null, true)
         sources key, callback
       else
         callback()


Unicodify warning messages.

Unicodify warning messages.

--- lib/models/commits/commit.coffee
+++ lib/models/commits/commit.coffee
@@ -27,7 +27,7 @@
   confirmReset: ->
     atom.confirm
       message: "Soft-reset head to #{@shortID()}?"
-      detailedMessage: @get("message")
+      detailedMessage: @message()
       buttons:
         "Reset": @reset
         "Cancel": null
@@ -38,7 +38,7 @@
   confirmHardReset: ->
     atom.confirm
       message: "Do you REALLY want to HARD-reset head to #{@shortID()}?"
-      detailedMessage: @get("message")
+      detailedMessage: @message()
       buttons:
         "Cancel": null
         "Reset": @hardReset


Compare state with started branches

Compare state with started branches

--- lib/SubtreeResults.coffee
+++ lib/SubtreeResults.coffee
@@ -1,7 +1,13 @@
+debug = require 'debug'
+log =
+  collection: debug 'collection'
+  values: debug 'values'
+
 class SubtreeResults
   constructor: (@tasks, @choice) ->
     @tasks = 1 unless @tasks
     @finished = false
+    @started = []
     @branches = []
     @aborted = []
     @fulfilled = []
@@ -36,10 +42,12 @@
     while i < @tasks
       fulfilled = if @fulfilled[i] then Object.keys(@fulfilled[i]).length else 0
       rejected = if @rejected[i] then Object.keys(@rejected[i]).length else 0
-      branched = if @branches[i] then Object.keys(@branches[i]).length else 0
+      started = if @started[i] then Object.keys(@started[i]).length else 0
       aborted = if @aborted[i] then Object.keys(@aborted[i]).length else 0
-      #console.log "#{@choice.treeId} #{@choice} #{i} #{fulfilled} #{rejected} #{aborted} #{branched}"
-      if fulfilled + rejected + aborted < branched + 1
+      complete = false if started is 0
+
+      log.collection "#{@choice?.treeId} #{@choice} ##{i} #{fulfilled + rejected >= started - aborted} (#{fulfilled} fulfilled + #{rejected} rejected / #{started} started - #{aborted} aborted)"
+      if fulfilled + rejected < started - aborted
         complete = false
       i++
     @finished = complete
@@ -88,6 +96,7 @@
     return f

   handleResult: (collection, idx, choice, value, callback = ->) ->
+    log.values "#{@choice?.treeId} ##{idx} #{choice} resulted in #{typeof value} %s", value
     return if @finished
     path = if choice then choice.toString() else ''
     collection[idx] = {} unless collection[idx]
@@ -101,6 +110,10 @@
       path = if c then c.toString() else ''
       @branches[idx] = {} unless @branches[idx]
       @branches[idx][path] = c
+    tree.started (c) =>
+      path = if c then c.toString() else ''
+      @started[idx] = {} unless @started[idx]
+      @started[idx][path] = c
     tree.aborted (a) =>
       path = if a.choice then a.choice.toString() else ''
       @aborted[idx] = {} unless @aborted[idx]


Consume keyboard events for marks.

Consume keyboard events for marks.

We were leaking both the `keypress` and the `keyup` events when marks
are created and used.

--- content_scripts/marks.coffee
+++ content_scripts/marks.coffee
@@ -52,6 +52,7 @@
             else
               localStorage[@getLocationKey keyChar] = @getMarkString()
               @showMessage "Created local mark", keyChar
+          DomUtils.consumeKeyup event

   activateGotoMode: ->
     @mode = new Mode
@@ -81,6 +82,7 @@
                 @showMessage "Jumped to local mark", keyChar
               else
                 @showMessage "Local mark not set", keyChar
+          DomUtils.consumeKeyup event

 root = exports ? window
 root.Marks =  Marks


soak errors if notifiable doesn't implement sendNotification

--- server/app/bongo/models/bucket.coffee
+++ server/app/bongo/models/bucket.coffee
@@ -98,8 +98,7 @@
                         else if isOwn
                           callback null, bucket
                         else
-                          console.log 'this is an important code path'
-                          anchor.sendNotification 'ActivityIsAdded'
+                          anchor.sendNotification? 'ActivityIsAdded'
                           anchor.addActivity activity, (err)->
                             if err
                               callback err


Fixed #330 refreshing grid via extended filters

Conflicts:
	wzk/resource/Query.coffee

--- wzk/resource/Query.coffee
+++ wzk/resource/Query.coffee
@@ -105,6 +105,8 @@
   ###
   filter: (filter) ->
     if filter.getValue()
+      prev = @filters[filter.getName()]
+      @uri.removeParameter(prev.getParamName()) if prev?
       @uri.setParameterValue filter.getParamName(), filter.getValue()
       @filters[filter.getName()] = filter
     else


code cleanups

code cleanups

--- coffeescripts/contextmenu.coffee
+++ coffeescripts/contextmenu.coffee
@@ -1,7 +1,7 @@
 # thunderbird_labels integration for contextmenu plugin (with help from Philip Weir)
 rcmail.addEventListener 'contextmenu_init', (menu) ->
   # dentify the folder list context menu
-  console.log(menu.menu_name);
+  #console.log(menu.menu_name);
   if menu.menu_name == 'messagelist'
     menu.addEventListener 'init', (p) ->
       if p.ref.menu_name == 'tb_label_popup'
@@ -24,10 +24,10 @@
           if (p.ref.list_object)
             p.ref.list_selection(false, prev_sel)
             rcmail.env.display_next = prev_display_next
-          undefined
+          return

       $(p.ref.container).append(labels)
-      undefined
+      return

     menu.addEventListener 'activate', (p) ->
       # the thunderbird_labels commands do not match the pattern used in the Roundcube core so some custom handling is needed.
@@ -57,13 +57,14 @@

               ref.timers['submenu_show'] = window.setTimeout (a, e) ->
                   ref.submenu(a, e)
-                  undefined
+                  return
                 , ref.mouseover_timeout, $(p.el), e
+              return

             $(p.el).mouseout (e) ->
               $(this).blur()
               clearTimeout(ref.timers['submenu_show'])
-              undefined
+              return

         # contextmenu plugin automatically hide things that it does not think look like Roundcube commands
         # make sure the labels command is visible
@@ -71,4 +72,4 @@

         # check if the command is enabled
         rcmail.commands['plugin.thunderbird_labels.rcm_tb_label_submenu']
-  undefined
+  return


Shorten condition

Shorten condition

--- lib/project/results-model.coffee
+++ lib/project/results-model.coffee
@@ -4,7 +4,7 @@

 class Result
   @create: (result) ->
-    if result? and result.matches?.length then new Result(result) else null
+    if result?.matches?.length then new Result(result) else null

   constructor: (result) ->
     _.extend(this, result)


Add event_options in MYOU_PARAMS.

Add event_options in MYOU_PARAMS.

--- engine/myou.coffee
+++ engine/myou.coffee
@@ -64,7 +64,7 @@
         data_dir = MYOU_PARAMS.data_dir or './data'
         data_dir = MYOU_PARAMS.data_dir = data_dir.replace(/\/$/g, '')

-        @events = new Events root
+        @events = new Events root, MYOU_PARAMS.event_options
         @main_loop.run()

     load_scene: (name, load_physics=true) ->


thing

thing

--- web/coffee/certificate.coffee
+++ web/coffee/certificate.coffee
@@ -120,8 +120,8 @@
         image: 'syndis'
         width: 200
         margin: [
-          170
-          80
+          175
+          75
           0
           0
         ]


Try all the wolfram result options

Try all the wolfram result options

--- scripts/rc.coffee
+++ scripts/rc.coffee
@@ -127,14 +127,17 @@
   client.query expr, (err, res) ->
     return cb(err, null) if err
     console.log("Results: %j", res.map (e) -> e.title)
-    decimal = res.filter (e) ->
+    decimals = res.filter (e) ->
       e.title == 'Result' or
       e.title.match /Decimal approximation/
-    console.log("Result options: %j", decimal.map (e) -> [e.title, e?.subpods?[0]?.value])
-    decimal = decimal?[0]?.subpods?[0]?.value
-    decimal = parseFloat(decimal)
-    return cb("Couldn't make #{expr} into decimal", null) if isNaN(decimal)
-    cb(false, decimal)
+    console.log("Result options: %j", decimals.map (e) -> [e.title, e?.subpods?[0]?.value])
+    for option in decimals
+      console.log("Trying option #{option.title}")
+      decimal = option.subpods?[0]?.value
+      decimal = parseFloat(decimal)
+      if not isNaN(decimal)
+        return cb(false, decimal)
+    return cb("Couldn't make #{expr} into decimal", null)

 ## Drivers
 ##


Test: githubGistsDataService

Test: githubGistsDataService

--- test/spec/services/githubgistsdata.coffee
+++ test/spec/services/githubgistsdata.coffee
@@ -6,11 +6,16 @@
   beforeEach module 'arashike-blog'

   # instantiate service
-  githubGistsData = {}
-  beforeEach inject (_githubGistsData_) ->
-    githubGistsData = _githubGistsData_
+  githubGistsDataService = {}
+  beforeEach inject (_githubGistsDataService_) ->
+    githubGistsDataService = _githubGistsDataService_

-  it 'should do something', ->
-    expect !!githubGistsData
+  it 'should be exists', ->
+    expect !!githubGistsDataService
       .to
       .equal true
+
+  it 'should have gists property', ->
+    expect githubGistsDataService
+      .to.has.property 'gists'
+      .a 'array'


Only display summary column for global slot on ss

--- web/volume/spreadsheet.coffee
+++ web/volume/spreadsheet.coffee
@@ -435,7 +435,7 @@
                 d.summary = (rrr.record.displayName for rrr in recs when rrr.id != rr.id).join(', ')
               row.add('slot', d)
               any = true
-            unless any
+            unless any || slot == volume.top
               nor ||= new Row()
               d = populateSlotData(slot)
               d.summary = (rrr.record.displayName for rrr in recs).join(', ')
@@ -557,11 +557,18 @@
             return
           pre = ID + '-' + info.i + '_'
           post = if info.hasOwnProperty('n') then '_' + info.n else ''
-          for mi in [0..l-1] by 1
-            info.m = info.cols.start+mi
-            info.id = pre + (info.cols.start+mi) + post
+          generateCell = (i) ->
+            info.m = info.cols.start+i
+            info.id = pre + info.m + post
             info.cell = info.tr.appendChild(document.createElement('td'))
             generateText(info)
+            return
+          if info.category.id == 'slot' && info.slot.top
+            generateCell(l-1) # summary
+            info.cell.setAttribute("colspan", l)
+            return
+          for mi in [0..l-1] by 1
+            generateCell(mi)
           return

         # Fill out Rows[i].


Home: back to new one

Home: back to new one

--- client/Intro/includes.coffee
+++ client/Intro/includes.coffee
@@ -1,4 +1,4 @@
 module.exports = [
-  "main.coffee",
-  "main.styl"
+  "home.coffee",
+  "styl/home.styl"
 ]


Add #map route

Add #map route

--- js/routers/router.coffee
+++ js/routers/router.coffee
@@ -13,4 +13,8 @@
     require ["cs!views/map"], ->
       views.map.render()

+  map: ->
+    require ["cs!views/map"], ->
+      views.map.render()
+
 return new Router


Mark packages as missing where applicable

--- client/modules/pkgman.coffee
+++ client/modules/pkgman.coffee
@@ -50,8 +50,10 @@
 		catch error

 			# Suppress missing package errors.
-			# `TODO`: Should we let this throw?
-			continue if error.toString() is "Error: Cannot find module '#{name}'"
+			if error.toString() is "Error: Cannot find module '#{name}'"
+				debug "Missing package #{name}."
+				continue
+
 			throw error

 		modules[name] = module_


Revert "remove player for now"

Revert "remove player for now"

This reverts commit 2183fa7c373089aaeff1f3a60834cdd9fc522356.

--- source/javascripts/interactive.coffee
+++ source/javascripts/interactive.coffee
@@ -108,6 +108,7 @@
       xstat  = el.attr 'data-x'
       ystat  = el.attr 'data-y'
       team   = el.attr 'data-team'
+      player = el.attr 'data-player'

       if not ystat
         ystat = d3.shuffle(__metrics)[0]
@@ -123,10 +124,11 @@

       url = "#{document.location.origin}/interactive?y=#{ystat.k}&x=#{xstat.k}"
       url += "&team=#{team.toLowerCase()}" if team
+      url += "&player=#{player}" if player

       d3.select('.js-url').attr 'value', url

-      {xstat, ystat, team}
+      {xstat, ystat, team, player}

     d3.selectAll('.js-stats .js-stat').on 'change', (e) ->
       select = d3.select this
@@ -168,6 +170,7 @@
         pid  = data.player_id

         el.attr 'data-team', team
+        el.attr 'data-player', pid
         redraw()

       yrow.on 'click', highlight


Force user sign in.

Force user sign in.

--- app/controllers/classifier.coffee
+++ app/controllers/classifier.coffee
@@ -47,6 +47,8 @@
     $(window).on 'hashchange', =>
       setTimeout @afterHashChange

+    @onUserSignIn()
+
   onSubjectSelect: (@subject) =>
     @afterHashChange()


consolidated removeKnownAsteroids(), removeGhostMarks() into single method: removeElementsOfClass()

--- app/controllers/classifier.coffee
+++ app/controllers/classifier.coffee
@@ -108,7 +108,7 @@
       exit: ->
         @el.find('button[name="to-select"]').removeClass 'hidden'
         @el.find('.what-kind').hide()
-        @removeKnownAsteroids()
+        @removeElementsOfClass(".known-asteroid")

     asteroidTool:
       enter: ->
@@ -221,9 +221,6 @@
       svgElement.el.setAttribute 'from-frame', mark.frame
       svgElement.el.setAttribute 'from-asteroid', @currSighting.id

-  removeGhostMarks: ->
-    ghostMark.remove() for ghostMark in [ @el.find(".ghost-mark")... ]
-
   onCreateMark: (mark) =>
     @currSighting.pushSighting mark

@@ -231,7 +228,7 @@
     @destroyMarksInFrame mark.frame
     @updateIconsForDestroyMark mark.frame
     @currSighting.clearSightingsInFrame mark.frame
-    @removeGhostMarks()
+    @removeElementsOfClass(".ghost-mark")
     if @state is 'asteroidTool' and @currSighting.annotations.length < @numFrames
       @doneButton.prop 'disabled', true

@@ -239,7 +236,7 @@
     surfaceIndex = +@markingSurfaceList.indexOf tool.surface
     tool.mark.id = @currSighting.id
     tool.mark.on 'change', =>
-      @removeGhostMarks()
+      @removeElementsOfClass(".ghost-mark")
       @addGhostMark(tool.mark)

     switch @state
@@ -415,7 +412,7 @@
         classifier.el.find(".asteroid-frame-#{i}").removeClass 'current-asteroid-frame'

   onClickAsteroidDone: ->
-    @removeGhostMarks()
+    @removeElementsOfClass(".ghost-mark")
     @currSighting.displaySummary()
     if @currSighting.annotations.length is 0
       @currSighting = null
@@ -495,7 +492,7 @@

   showSummary: ->
     @resetMarkingSurfaces() # remove previous marks
-    @removeKnownAsteroids()
+    @removeElementsOfClass(".known-asteroid")
     console.log "known asteroids: ", @Subject.current.metadata.known_asteroids
     if @Subject.current.metadata.known_asteroids.length isnt 0
       @knownAsteroidMessage.show()
@@ -547,7 +544,7 @@
     @hotpixelCount.html("#{hotpixelCount}"+ "
" + "Line#{if hotpixelCount is 1 then '' else 's'}")

   onClickNextSubject: ->
-    @removeKnownAsteroids()
+    @removeElementsOfClass(".known-asteroid")
     element.hide() for element in [@summaryContainer, @nextSubjectButton, @rightPanelSummary]
     @summaryImageContainer.empty()
     @leftPanel.find(".answers:lt(4)").css 'pointer-events', 'auto'
@@ -567,13 +564,13 @@
   stopLoading: ->
     @el.removeClass 'loading'

-  removeKnownAsteroids: ->
-    console.log 'removing known-asteroid'
-    knownAsteroid.remove() for knownAsteroid in [@el.find('.known-asteroid')...]
+  removeElementsOfClass: (class_name) ->
+    console.log 'removing class: ', class_name
+    element.remove() for element in [@el.find(class_name)...]

   # FIX: lots of redundant code
   # showKnownAsteroids: ->
-  #   @removeKnownAsteroids()
+  #   @removeElementsOfClass("known-asteroid")
   #   return if @Subject.current.metadata.known_asteroids is undefined

   #   for knownAsteroid in [@Subject.current.metadata.known_asteroids...]


remove preventDefault from onKeyDown

remove preventDefault from onKeyDown

--- app/controllers/classifier.coffee
+++ app/controllers/classifier.coffee
@@ -260,17 +260,11 @@
   onKeyDown: (e) =>
     return if @el.hasClass 'playing'  # disable while playing
     switch e.which
-      when KEYS.one
-        @activateFrame(0)
-      when KEYS.two
-        @activateFrame(1)
-      when KEYS.three
-        @activateFrame(2)
-      when KEYS.four
-        @activateFrame(3)
-      when KEYS.space
-        @onClickPlay()
-    e.preventDefault()
+      when KEYS.one   then @activateFrame(0)
+      when KEYS.two   then @activateFrame(1)
+      when KEYS.three then @activateFrame(2)
+      when KEYS.four  then @activateFrame(3)
+      when KEYS.space then @onClickPlay()

   onUserChange: (e, user) =>
     Subject.next() unless @classification?


Disable keyboard shortcuts in contentEditable elements

--- app/controllers/classifier.coffee
+++ app/controllers/classifier.coffee
@@ -52,7 +52,7 @@
     83: 'S', 84: 'T', 85: 'U', 87: 'W', 88: 'X', 89: 'Y'
     61: '=', 173: '-', 188: '<', 190: '>', 219: '[', 221: ']'

-  textFieldSelector = 'input[type="text"], input[type="password"], input:not("[type]"), textarea, select'
+  textFieldSelector = 'input[type="text"], input[type="password"], input:not("[type]"), textarea, select, [contenteditable]'

   onKeyDown: (e) =>
     return if e.altKey or e.ctrlKey or e.metaKey


Delete contact controller

Delete contact controller

--- app/js/controllers/contact.coffee
+++ app/js/controllers/contact.coffee
@@ -1,10 +0,0 @@
-angular.module("app").controller "ContactController", ($scope, $location, $stateParams, Wallet, Utils) ->
-
-    $scope.utils = Utils
-    Wallet.get_account($stateParams.name).then (acct) ->
-        $scope.account = acct
-
-    $scope.toggleVoteUp = (name) ->
-        approve = !Wallet.approved_delegates[name]
-        Wallet.approve_delegate(name, approve).then ->
-            $scope.trust_level = approve


NEW: meta, link, manifest options

NEW: meta, link, manifest options

--- src/art/engine/full_screen_app.coffee
+++ src/art/engine/full_screen_app.coffee
@@ -1,7 +1,7 @@
 Foundation = require 'art-foundation'

-{Promise, parseQuery, log} = Foundation
-{Meta} = Foundation.Browser.DomElementFactories
+{merge, Promise, parseQuery, log} = Foundation
+{Meta, Link} = Foundation.Browser.DomElementFactories

 module.exports = class FullScreenApp

@@ -44,15 +44,13 @@

     appReadyPromise

-  @writeDom: ({title, styleSheets, scripts, fontFamilies})->
+  @writeDom: ({title, styleSheets, scripts, fontFamilies, meta, link, manifest})->

     document.title = title || "Art App"
     scripts ||= []
     styleSheets ||= []
     fontFamilies ||= []

-    styleSheetLinks = for sheetUrl in styleSheets
-      "<link rel='stylesheet' href='#{sheetUrl}' />"

     scriptLinks = for scriptUrl in scripts when scriptUrl
       "<script type='text/javascript' src='#{scriptUrl}'></script>"
@@ -71,22 +69,30 @@

     newLine = "\n    "

-    nameContentMetas =
+    nameContentMetas = merge
       "viewport": "user-scalable=no, width=device-width, initial-scale=1.0"
       "apple-mobile-web-app-capable": "yes"
       "apple-mobile-web-app-status-bar-style": "black-translucent"
       "format-detection": "telephone=no"
+      meta

     document.head.appendChild Meta charset: "utf-8"
     document.head.appendChild Meta "http-equiv": "X-UA-Compatible", content: "IE=edge,chrome=1"
     for name, content of nameContentMetas
       document.head.appendChild Meta name: name, content: content

+    for sheetUrl in styleSheets
+      document.head.appendChild Link
+        rel: 'stylesheet'
+        href: sheetUrl
+
+    for rel, info of link || {}
+      document.head.appendChild Link
+        rel: rel
+        info
+
     html = """
-      <html>
-        <head>
-          #{styleSheetLinks.join newLine}
-        </head>
+      <html #{if manifest then "manifest='#{manifest}'" else ""}>

         <style>
           html {


Prevent port errors when some tests have failed.

--- test/tests.coffee
+++ test/tests.coffee
@@ -56,20 +56,31 @@
     enableDestroy httpInst
     chatServer1 = new ChatService { http : httpInst }
     httpInst.listen port
+    cleanup = (err) ->
+      chatServer1.close ->
+        httpInst.destroy done, err
+    process.once 'uncaughtException', cleanup
     socket1 = ioClient.connect url1, makeParams(user1)
     socket1.on 'loginConfirmed', (u) ->
-      chatServer1.close ->
-        httpInst.destroy done
       expect(u).equal(user1)
+      process.removeListener 'uncaughtException', cleanup
+      cleanup()

   it 'should integrate with an existing io', (done) ->
     io = socketIO port
     chatServer1 = new ChatService { io : io }
+    cleanup =  (err) ->
+      chatServer1.close ->
+        chatServer1.close()
+        io.close()
+        done err
+    process.once 'uncaughtException', cleanup
     socket1 = ioClient.connect url1, makeParams(user1)
     socket1.on 'loginConfirmed', (u) ->
-      chatServer1.close()
-      io.close()
-      done()
+      expect(u).equal(user1)
+      process.removeListener 'uncaughtException', cleanup
+      cleanup()
+

   it 'should spawn a new io server', (done) ->
     chatServer = new ChatService { port : port }
@@ -79,12 +90,12 @@
       done()

   it 'should disconnect on server shutdown', (done) ->
-    chatServer1 = new ChatService { port : port }
+    chatServer = new ChatService { port : port }
     socket1 = ioClient.connect url1, makeParams(user1)
     socket1.on 'loginConfirmed', ->
       socket1.on 'disconnect', () ->
         done()
-      chatServer1.close()
+      chatServer.close()
       chatServer = null

   it 'should reject empty user query', (done) ->


Initialize config

Initialize config

--- server/app.coffee
+++ server/app.coffee
@@ -12,11 +12,12 @@
 exports.createApp = ->
   app = express()

+  require('./initializers/logging')(app)
   require('./initializers/mongo')(app.get('env'))
+  require('./initializers/config')(app)

   bindRoutesForApp = require('./route_bindings.coffee')

-  require('./initializers/logging')(app)
   app.use express.static(path.join(__dirname, "public"))

   app.engine "hbs", hbs.express3(


Cria alias de this (context) para passar para handlers

--- src/coffee/portal-sku-selector.coffee
+++ src/coffee/portal-sku-selector.coffee
@@ -61,6 +61,9 @@
 		s = @findSelectableSkus()
 		return if s.length is 1 then s[0] else undefined

+	getSelectedDimension: (dimension) =>
+		@selectedDimensionsMap[dimension]
+
 	setSelectedDimension: (dimension, value) =>
 		@selectedDimensionsMap[dimension] = value

@@ -86,23 +89,28 @@
 			dimensionSanitized = sanitize(dimension)

 			dimensionList.find('.specification').text(dimension)
-			dimensionList.find('.topic').addClass("dimensionSanitized").addClass("item-dimension-#{dimensionSanitized}")
-			dimensionList.find('.skuList').addClass('.group_' + dimensionIndex); dimensionIndex++
+			dimensionList.find('.topic').addClass("#{dimensionSanitized}").addClass("item-dimension-#{dimensionSanitized}")
+			dimensionList.find('.skuList').addClass("group_#{dimensionIndex}")
+			dimensionIndex++
 			for value, i in dimensionValues
 				skuList = skuListBase.clone()
 				valueSanitized = sanitize(value)

 				skuList.find('input').attr('data-dimension', dimension)
 					.attr('dimension', dimensionSanitized)
-					.attr('name', 'dimension-' + dimensionSanitized)
+					.attr('name', "dimension-#{dimensionSanitized}")
 					.attr('data-value', value)
 					.attr('value', valueSanitized)
 					.attr('specification', valueSanitized)
 					.attr('id', "#{@productId}_#{dimensionSanitized}_#{i}")
-					.addClass("input-dimension-#{dimensionSanitized} sku-selector skuespec_#{valueSanitized} ")
-				skuList.find('label').attr('for', "#{@productId}_#{dimensionSanitized}_#{i}")
-					.text(value)
-					.addClass("dimension-#{dimensionSanitized} espec_#{dimensionIndex} skuespec_#{valueSanitized}")
+					.addClass("input-dimension-#{dimensionSanitized}")
+					.addClass("sku-selector")
+					.addClass("skuespec_#{valueSanitized}")
+				skuList.find('label').text(value)
+					.attr('for', "#{@productId}_#{dimensionSanitized}_#{i}")
+					.addClass("dimension-#{dimensionSanitized}")
+					.addClass("espec_#{dimensionIndex}")
+					.addClass("skuespec_#{valueSanitized}")

 				skuList.appendTo(dimensionList.find('.skuList'))

@@ -136,11 +144,12 @@
 						selectors.itemDimensionValueInput(dimension, value, context).removeClass('item_unavaliable')
 						selectors.itemDimensionValueLabel(dimension, value, context).removeClass('disabled item_unavaliable')

-
 #
 # PLUGIN ENTRY POINT
 #
 $.fn.skuSelector = (productId, name, dimensions, skus, options = {}) ->
+	context = this
+
 	options = $.extend($.fn.skuSelector.defaults, options)
 	this.addClass('sku-selector-loading')
 	skuSelectorObj = new SkuSelector(productId, name, dimensions, skus)
@@ -165,9 +174,9 @@
 	buyButtonHandler = (event) =>
 		selectedSku = skuSelectorObj.findSelectedSku()
 		if selectedSku
-			return options.addSkuToCart(selectedSku.sku, this)
+			return options.addSkuToCart(selectedSku.sku, context)
 		else
-			options.selectors.warning(this).show().text('Por favor, escolha: ' + skuSelectorObj.findUndefinedDimensions()[0])
+			options.selectors.warning(context).show().text('Por favor, escolha: ' + skuSelectorObj.findUndefinedDimensions()[0])
 			return false

 	# Handles changes in the dimension inputs
@@ -181,53 +190,56 @@
 		selectedSku = skuSelectorObj.findSelectedSku()
 		undefinedDimensions = skuSelectorObj.findUndefinedDimensions()

-		options.selectors.warning(this).hide()
-		options.selectors.warnUnavailable(this).filter(':visible').hide()
+		options.selectors.warning(context).hide()
+		options.selectors.warnUnavailable(context).filter(':visible').hide()

 		# Trigger event for interested scripts
 		if selectedSku and undefinedDimensions.length is 0
 			$(this).trigger('skuSelected', [selectedSku, dimensionName])
 			if options.warnUnavailable and not selectedSku.available
-				options.selectors.warnUnavailable(this).find('#notifymeSkuId').val(selectedSku.sku)
-				options.selectors.warnUnavailable(this).show()
+				options.selectors.warnUnavailable(context).find('#notifymeSkuId').val(selectedSku.sku)
+				options.selectors.warnUnavailable(context).show()

 		# Limpa classe de selecionado para todos dessa dimensao
-		options.selectors.itemDimensionInput(dimensionName, this).removeClass('checked sku-picked')
-		options.selectors.itemDimensionLabel(dimensionName, this).removeClass('checked sku-picked')
+		options.selectors.itemDimensionInput(dimensionName, context).removeClass('checked sku-picked')
+		options.selectors.itemDimensionLabel(dimensionName, context).removeClass('checked sku-picked')

 		# Coloca classes corretas em si
-		options.selectors.itemDimensionValueInput(dimensionName, dimensionValue, this).addClass('checked sku-picked')
-		options.selectors.itemDimensionValueLabel(dimensionName, dimensionValue, this).addClass('checked sku-picked')
-
-		skuSelectorObj.disableInvalidInputs(this, options.selectors)
+		options.selectors.itemDimensionValueInput(dimensionName, dimensionValue, context).addClass('checked sku-picked')
+		options.selectors.itemDimensionValueLabel(dimensionName, dimensionValue, context).addClass('checked sku-picked')
+
+		skuSelectorObj.disableInvalidInputs(context, options.selectors)

 		# Select first available dimensions
 		if selectedSku
 			for dimension in undefinedDimensions
-				selectDimension(options.selectors.itemDimensionInput(dimension, this))
-
-			updatePrice(selectedSku, options, this)
+				selectDimension(options.selectors.itemDimensionInput(dimension, context))
+
+			updatePrice(selectedSku, options, context)

 	# Handles submission in the warn unavailable form
 	warnUnavailableSubmitHandler = (e) ->
 		e.preventDefault()
-		options.selectors.warnUnavailable(this).find('#notifymeLoading').show()
-		options.selectors.warnUnavailable(this).find('form').hide()
+		options.selectors.warnUnavailable(context).find('#notifymeLoading').show()
+		options.selectors.warnUnavailable(context).find('form').hide()
 		xhr = options.warnUnavailablePost(e.target)
-		xhr.done -> options.selectors.warnUnavailable(this).find('#notifymeSuccess').show()
-		xhr.fail -> options.selectors.warnUnavailable(this).find('#notifymeError').show()
-		xhr.always -> options.selectors.warnUnavailable(this).find('#notifymeLoading').hide()
+		xhr.done -> options.selectors.warnUnavailable(context).find('#notifymeSuccess').show()
+		xhr.fail -> options.selectors.warnUnavailable(context).find('#notifymeError').show()
+		xhr.always -> options.selectors.warnUnavailable(context).find('#notifymeLoading').hide()
 		return false

 	# Binds handlers
-	options.selectors.buyButton(this).click(buyButtonHandler)
+	options.selectors.buyButton(this)
+		.click(buyButtonHandler)

 	for dimension in skuSelectorObj.dimensions
-		options.selectors.itemDimensionInput(dimension, this).change(dimensionChangeHandler)
+		options.selectors.itemDimensionInput(dimension, this)
+			.change(dimensionChangeHandler)

 	if options.warnUnavailable
-		options.selectors.warnUnavailable(this).find('form').submit(warnUnavailableSubmitHandler)
+		options.selectors.warnUnavailable(this).find('form')
+			.submit(warnUnavailableSubmitHandler)

 	# Select first dimension
 	if options.selectOnOpening or skuSelectorObj.findSelectedSku()
@@ -255,11 +267,11 @@
 		price: (context) -> $('.skuselector-price', context)
 		warning: (context) -> $('.skuselector-warning', context)
 		warnUnavailable: (context) -> $('.skuselector-warn-unavailable', context)
-		itemDimensionListItem: (dimensionName, context) -> $('.item-dimension-' + sanitize(dimensionName), context)
-		itemDimensionInput: (dimensionName, context) -> $('.item-dimension-' + sanitize(dimensionName) + ' input', context)
-		itemDimensionLabel: (dimensionName, context) -> $('.item-dimension-' + sanitize(dimensionName) + ' label', context)
-		itemDimensionValueInput: (dimensionName, valueName, context) -> $('.item-dimension-' + sanitize(dimensionName) + ' input[value="' + sanitize(valueName) + '"]', context)
-		itemDimensionValueLabel: (dimensionName, valueName, context) -> $('.item-dimension-' + sanitize(dimensionName) + ' label.skuespec_' + sanitize(valueName), context)
+		itemDimensionListItem: (dimensionName, context) -> $(".item-dimension-#{sanitize(dimensionName)}", context)
+		itemDimensionInput: (dimensionName, context) -> $(".item-dimension-#{sanitize(dimensionName)} input", context)
+		itemDimensionLabel: (dimensionName, context) -> $(".item-dimension-#{sanitize(dimensionName)} label", context)
+		itemDimensionValueInput: (dimensionName, valueName, context) -> $(".item-dimension-#{sanitize(dimensionName)} input[value='#{sanitize(valueName)}']", context)
+		itemDimensionValueLabel: (dimensionName, valueName, context) -> $(".item-dimension-#{sanitize(dimensionName)} label.skuespec_#{sanitize(valueName)}", context)

 	# Called when we failed to receive variations.


clean code a little

clean code a little

--- src/coffee/services/validation.coffee
+++ src/coffee/services/validation.coffee
@@ -17,25 +17,28 @@
       removeError = (fieldName) ->
         delete errors[fieldName]
         events.handle(fieldName, "ok")
-
+        true
+
+      setError = (fieldName, msg) ->
+        errors[fieldName] = msg
+        events.handle(fieldName, "error", msg)
+        false
+
       initValidation = (field, services) ->
         fields[field.name] = field
         if field.validation? then field.validation.map (rule) ->
-          if validators[rule.validator]? and validators[rule.validator].init?
-            validators[rule.validator].init(field.name, rule, services)
-
+          vl = validators.get(rule.validator)
+          if vl? and vl.init? then vl.init(field.name, rule, services)

       validateField = (fieldName, values) ->
         field = fields[fieldName]
         if field.validation?
           for rule in field.validation
-            if validators[rule.validator] and validators[rule.validator].validator?
-              if !validators[rule.validator].validator(fieldName, values, rule)
-                errors[fieldName] = rule.options.message
-                events.handle(fieldName, "error",rule.options.message)
-                return false
-        removeError(fieldName)
-        return true
+            vl = validators.get(rule.validator)
+            if vl and vl.validator?
+              if !vl.validator(fieldName, values, rule)
+                return setError(fieldName, rule.options.message)
+        return removeError(fieldName)

       validateFields = (fieldList, values) ->
         result = true


Fix bad robot context

Fix bad robot context

--- app/templates/src/template.coffee
+++ app/templates/src/template.coffee
@@ -18,7 +18,7 @@
   constructor: (@robot) -><% if (needEnvVariable) { %>
     @config = process.env.HUBOT_<%= scriptNameUppercased %>_SETTING or 'whatever the default value should be'<% } %>
     initialized = false
-    robot.brain.on 'loaded', =>
+    @robot.brain.on 'loaded', =>
       if not initialized
         initialized = true
         <%= scriptNameCamelized %> = @robot.brain.get '<%= scriptNameCamelized %>'


feedback note on builder is only for homework

--- src/components/task-plan/builder.cjsx
+++ src/components/task-plan/builder.cjsx
@@ -109,6 +109,9 @@

         </BS.Col>
       </BS.Row>
+
+    if plan.type is 'homework'
+      feedbackNote = @renderFeedbackNote()

       <BS.Row>
@@ -171,7 +174,7 @@
             min={TaskPlanStore.getOpensAt(@props.id)}
             value={commonDueAt}/>
         </BS.Col>
-        {@renderFeedbackNote() unless plan.type is 'external'}
+        {feedbackNote}

       </BS.Row>
       <BS.Row>


show event for service

show event for service


Do not use zero length avatars, fixes #327

--- lib/atom-io-client.coffee
+++ lib/atom-io-client.coffee
@@ -103,6 +103,7 @@
       options.force = not @online()

     cached = localStorage.getItem(@cacheKeyForPath(packagePath))
+    debugger
     cached = if cached then JSON.parse(cached)
     if cached? and (not @online() or options.force or (Date.now() - cached.createdOn < @expiry))
       cached ?= data: {}
@@ -129,7 +130,7 @@
       for imagePath in files
         filename = path.basename(imagePath)
         [..., createdOn] = filename.split('-')
-        if Date.now() - parseInt(createdOn) < @expiry
+        if Date.now() - parseInt(createdOn) < @expiry and fs.getSizeSync(filename) > 0
           return callback(null, imagePath)
       callback(null, null)


Set resource on controller actions

Set resource on controller actions

--- src/controllers/mixins/actions.coffee
+++ src/controllers/mixins/actions.coffee
@@ -9,6 +9,7 @@
     _index: (callback) ->
       @scoped (err, scope) =>
         scope.all (err, resources) =>
+          @resources = resources
           callback.apply @, [null, resources]

     show: ->
@@ -21,6 +22,8 @@
       @findResource (err, resource) =>
         unless resource
           return @_notFoundHandler()
+
+        @resource = resource

         callback.apply @, [err, resource]

@@ -39,6 +42,8 @@
         scope.create data, (err, resource) =>
           if err
             return callback.apply @, [err, null]
+
+          @resource = resource

           callback.apply @, [null, resource]

@@ -59,6 +64,8 @@
         resource.updateAttributes data, (err, resource) =>
           if err
             return callback.apply @, [err]
+
+          @resource = resource

           callback.apply @, [err, resource]


Add a language picker and Spanish translations

--- project.coffee
+++ project.coffee
@@ -4,6 +4,18 @@
 t7e = require 't7e'
 enUs = require './lib/en-us'
 t7e.load enUs
+
+languageManager = new zooniverse.LanguageManager({
+  translations: {
+    en: { label: "English", strings: enUs }
+    es: { label: "Español", strings: "./translations/es.json" }
+  }
+})
+
+languageManager.on('change-language', (e, code, languageStrings) ->
+  t7e.load(languageStrings)
+  t7e.refresh()
+)

 translate = t7e


removed leftover garbage

removed leftover garbage


Remove differential token store

Remove differential token store

--- lib/differential-token-store.coffee
+++ lib/differential-token-store.coffee
@@ -1,52 +0,0 @@
-RefCountedTokenList = require './ref-counted-token-list'
-
-# This computes the XOR of tokens between buffer change events.
-# Used in the SymbolProvider.
-#
-# The expectation is that on a
-# * buffer::onWillChange event, all the relevant tokens will be _removed_ from this object
-# * buffer::onDidChange event, all the relevant tokens will be _added_ to this object
-#
-# The complexity here is in bufferRow tracking, which is not perfect.
-module.exports =
-class DifferentialTokenStore
-  tokensForRemoval: new RefCountedTokenList
-  tokensForAddition: new RefCountedTokenList
-
-  clear: ->
-    @tokensForRemoval.clear()
-    @tokensForAddition.clear()
-
-  add: (token, editorPath, bufferRow) ->
-    @updateReferenceCounters(@tokensForRemoval, @tokensForAddition, token, editorPath, bufferRow)
-
-  remove: (token, editorPath, bufferRow) ->
-    @updateReferenceCounters(@tokensForAddition, @tokensForRemoval, token, editorPath, bufferRow)
-
-  updateReferenceCounters: (counterToRemoveFrom, counterToAddTo, token, editorPath, bufferRow) ->
-    return unless token.value.trim()
-
-    tokenKey = @keyForToken(token)
-    if counterToRemoveFrom.removeToken(tokenKey)
-      tokenWrapper = counterToRemoveFrom.getTokenWrapper(tokenKey)
-
-      # BufferRow tracking is not bullet proof as the row for a give token may
-      # have changed. An error here will throw off the locality scoring.
-      bufferRows = tokenWrapper?.bufferRowsForEditorPath?[editorPath]
-      removeItemFromArray(bufferRows, bufferRow) if bufferRows?
-    else
-      counterToAddTo.addToken(token, tokenKey)
-
-      # Abusing the token list to keep track of buffer rows. The token list
-      # makes a wrapper object for each token.
-      tokenWrapper = counterToAddTo.getTokenWrapper(tokenKey)
-      tokenWrapper.bufferRowsForEditorPath ?= {}
-      tokenWrapper.bufferRowsForEditorPath[editorPath] ?= []
-      tokenWrapper.bufferRowsForEditorPath[editorPath].unshift(bufferRow)
-
-  keyForToken: (token) =>
-    token.value + token.scopes.join(',')
-
-removeItemFromArray = (array, item) ->
-  index = array.indexOf(item)
-  array.splice(index, 1) if index > -1


Detach from status bar when pane removed

--- lib/image-editor-status-view.coffee
+++ lib/image-editor-status-view.coffee
@@ -20,6 +20,9 @@
         else
           @imageSizeStatus.parent().hide()

+      @subscribe atom.workspaceView, 'pane:before-item-destroyed', =>
+        @detach()
+
   attach: =>
     statusBar = atom.workspaceView.statusBar
     if statusBar


Refactorings that improves the code!

Refactorings that improves the code!

--- app/assets/javascripts/roofpig/CubeExp.js.coffee
+++ app/assets/javascripts/roofpig/CubeExp.js.coffee
@@ -5,41 +5,40 @@

   PIECE_NAMES = ['B','BL','BR','D','DB','DBL','DBR','DF','DFL','DFR','DL','DR','F','FL','FR','L','R','U','UB','UBL','UBR','UF','UFL','UFR','UL','UR']

-  constructor: (expressions) ->
+  constructor: (expressions = "") ->
     @matches = {}
-    if expressions
-      for expression in expressions.split(" ")
-        exp = this._parse(expression)
+    for piece in PIECE_NAMES
+      @matches[piece] = {}

-        switch exp.type
-          when 'XYZ'
-            this._add_match(exp.piece, exp.type_filter, exp.sides)
-          when 'XYZ*'
-            [s1, s2, s3] = exp.piece.split('')
-            for piece in [s1+s2+s3, s1+s2, s1+s3, s2+s3, s1, s2, s3]
+    for expression in expressions.split(" ")
+      exp = this._parse(expression)
+
+      switch exp.type
+        when 'XYZ'
+          this._add_match(exp.piece, exp.type_filter, exp.sides)
+        when 'XYZ*'
+          [s1, s2, s3] = exp.piece.split('')
+          for piece in [s1+s2+s3, s1+s2, s1+s3, s2+s3, s1, s2, s3]
+            this._add_match(piece, exp.type_filter)
+        when 'X*'
+          for piece in PIECE_NAMES
+            if piece.indexOf(exp.piece[0]) > -1
               this._add_match(piece, exp.type_filter)
-          when 'X*'
-            for piece in PIECE_NAMES
-              if piece.indexOf(exp.piece[0]) > -1
-                this._add_match(piece, exp.type_filter)
-          when '*'
-            for piece in PIECE_NAMES
-              this._add_match(piece, exp.type_filter)
-          when 'x'
-            for piece in PIECE_NAMES
-              if piece.indexOf(exp.piece) > -1
-                this._add_match(piece, exp.type_filter, exp.piece)
-          else
-            console.log("Couldn't understand CubeExp '#{expression}', and ignored it.")
+        when '*'
+          for piece in PIECE_NAMES
+            this._add_match(piece, exp.type_filter)
+        when 'x'
+          for piece in PIECE_NAMES
+            if piece.indexOf(exp.piece[0]) > -1
+              this._add_match(piece, exp.type_filter, exp.piece)
+        else
+          console.log("Ignored unrecognized CubeExp '#{expression}'.")

   _add_match: (piece, type_filter, sides = piece) ->
     piece_type = 'mec'[piece.length-1]
     if not type_filter || type_filter.indexOf(piece_type) > -1
-      if @matches[piece]
-        for side in sides.split('')
-          @matches[piece] += side if @matches[piece].indexOf(side) == -1
-      else
-        @matches[piece] = sides
+      for side in sides.split('')
+        @matches[piece][side] = true

   _parse: (expression) ->
     result = {}
@@ -57,5 +56,4 @@
     result

   matches_sticker: (piece, side) ->
-    matching_stickers = @matches[standardize_name(piece)] || ""
-    matching_stickers.indexOf(side_name(side)) > -1
+    @matches[standardize_name(piece)][side_name(side)]?


highlight new articles load from aside

highlight new articles load from aside

--- app/assets/javascripts/views/article_view.coffee
+++ app/assets/javascripts/views/article_view.coffee
@@ -14,4 +14,5 @@
       data: 'bare=1'
       success: (html) =>
         @$el.html(html)
+        $('pre code').each (i, e) -> hljs.highlightBlock(e)
         $("#spinner").hide()


simplify EH filtering logic a bit more

simplify EH filtering logic a bit more

--- app/assets/javascripts/views/dashboard.js.coffee
+++ app/assets/javascripts/views/dashboard.js.coffee
@@ -130,11 +130,7 @@
     @filterEH = flag
     @selectedCategories.each (category) =>
       category.get('measures').each (measure) =>
-        if @filterEH
-          if measure.get('type') != 'eh'
-            measure.get('submeasures').each (submeasure) ->
-              submeasure.get('query').fetch()
-        else
+        unless @filterEH and measure.get('type') is 'eh'
           measure.get('submeasures').each (submeasure) ->
             submeasure.get('query').fetch()
     @render()


Fix CroppedImage require in ArticleListItem

--- app/pages/lab/field-guide/article-list-item.cjsx
+++ app/pages/lab/field-guide/article-list-item.cjsx
@@ -1,6 +1,6 @@
 React = require 'react'
 createReactClass = require 'create-react-class'
-CroppedImage = require '../../../components/cropped-image'
+CroppedImage = require('../../../components/cropped-image').default

 ArticleListItem = createReactClass
   getDefaultProps: ->


some cleanup

some cleanup

--- apps/feature/client/filter.coffee
+++ apps/feature/client/filter.coffee
@@ -6,16 +6,16 @@
     'click a' : 'triggerArtworkFilter'

   sortHash:
-    'artist-a-to-z' : (model) ->
-      model.get('artist').sortable_id
-    'most-bids': (model) ->
-      - model.get('saleArtwork').get('bidder_positions_count')
-    'least-bids': (model) ->
-      model.get('saleArtwork').get('bidder_positions_count')
-    'highest-current-bid' : (model) ->
-      - model.get('saleArtwork').get('highest_bid_amount_cents')
-    'lowest-current-bid' : (model) ->
-      model.get('saleArtwork').get('highest_bid_amount_cents')
+    'artist-a-to-z' : (a) ->
+      a.get('artist').sortable_id
+    'most-bids': (a) ->
+      - a.get('saleArtwork').get('bidder_positions_count')
+    'least-bids': (a) ->
+      a.get('saleArtwork').get('bidder_positions_count')
+    'highest-current-bid' : (a) ->
+      - a.get('saleArtwork').get('highest_bid_amount_cents')
+    'lowest-current-bid' : (a) ->
+      a.get('saleArtwork').get('highest_bid_amount_cents')

   initialize: (options) ->
     { @artworks, @reRender, @startingSearch } = options
@@ -31,4 +31,4 @@
     return unless @sortHash[sortId]
     @artworks.comparator = @sortHash[sortId]
     @artworks.sort()
-    @reRender(@artworks)
+    @reRender @artworks


Improvements for core/server/model.coffee

--- src/core/server/model.coffee
+++ src/core/server/model.coffee
@@ -1,6 +1,7 @@
 "use strict"

 {assign, defineProperty} = Object
+{isPlainObject, isEmpty} = require "lodash"

 snakecase = require "snake-case"
 requireHelper = require "../helper/require"
@@ -36,13 +37,25 @@
 # @param string name
 # @param function schema
 #
-# @return object
+# @return Sequlize.Model
 ###
-define = (name, schema, opts = {}) ->
-  opts = assign {}, opts, timestamps: off, underscored: yes
+define = (name, schema, custom = {}) ->
+  custom = assign {}, custom, timestamps: off, underscored: yes

-  return sequelize.define snakecase("#{name}"),
-    schema(types), opts
+  unless name or typeof name is "string"
+    throw new TypeError "Schema name should be a string and cannot be empty."
+
+  unless isPlainObject(schema) or isEmpty(schema)
+    throw new TypeError "
+      Schema \"#{name}\" should be a plain object and cannot be empty.
+    "
+
+  name = snakecase "#{name}"
+  schema = schema types
+
+  {schema, options} = schema if "schema" of schema and "options" of schema
+
+  return sequelize.define name, schema, assign {}, options, custom

 ###
 # Define and return all models from core/database/schema
@@ -54,7 +67,7 @@

   for own __name, __sch of schemas
     unless typeof __sch is "function"
-      warn "Schema must return a function in \"#{__name}\""
+      warn "Schema should be a function in \"#{__name}\"."
       continue

     res[__name] = define __name, __sch unless __name of res