Material: Add debug_blender_material()

Material: Add debug_blender_material()

--- engine/material.coffee
+++ engine/material.coffee
@@ -182,11 +182,11 @@
     #        * there are more types but of limited use outside Blender for now
     #   }
     #   There are two implicit vec3 attributes: vertex, vnormal.
-    constructor: (@context, data, @scene) ->
+    constructor: (@context, @data, @scene) ->
         @id = id++
         if @context.all_materials.indexOf(@) == -1
             @context.all_materials.push @
-        {@name, uniforms, attributes, params=[]} = data
+        {@name, uniforms, attributes, params=[]} = @data
         @shading_params_dict = {}
         @shading_params = for p in params
             @shading_params_dict[p.name] = new ShadingParams p
@@ -205,7 +205,7 @@
         @uv_multiplier = 1
         @shape_multiplier = 1
         @group_id = -1
-        @double_sided = not not data.double_sided
+        @double_sided = Boolean @data.double_sided

         var_model_view_matrix = "model_view_matrix"
         var_inv_model_view_matrix = ""
@@ -499,7 +499,7 @@

         # TODO: find which uniform
         # has conflicting precision between VS and FS in ANGLE/win32
-        @vs_code = vs = data.vertex or """
+        @vs_code = vs = @data.vertex or """
         #ifdef GL_ES
         precision highp float;
         precision highp int;
@@ -676,8 +676,7 @@
         return prog

     reupload: ->
-        @constructor(@name, @fs_code,
-        @uniforms_config, @attributes_config, @vs_code, @scene)
+        @constructor(@context, @data, @scene)

     destroy: ->
         @context.render_manager.gl.deleteProgram @_program
@@ -717,6 +716,28 @@
             # If matches weren't found, fat chance. We're not warning.

         return cloned
+
+    debug_blender_material: (varnum) ->
+        if not @fs_code.splice
+            throw "Not a Blender material"
+        [lib, code] = @orig_fs_code = @orig_fs_code or @fs_code[...]
+        re = new RegExp("\\btmp#{varnum}\\b", "g")
+        value = "tmp#{varnum}.x"
+        if re.test code
+            for line in code.split('\n') when re.test line
+                if line.length < 15
+                    if /float/.test line
+                        value = "tmp#{varnum}"
+                else
+                    console.log line.replace(re, "TMP"+varnum)
+            @fs_code[1] = code.replace('gl_FragColor = ',
+                "gl_FragColor = vec4(-#{value}, #{value}, #{value}*10.0-5.0, 1.0);//")
+            @reupload()
+        else
+            console.log "No such variable"
+        return
+
+

 module.exports = {Material,
 CD_MCOL, CD_MTFACE, CD_ORCO, CD_TANGENT, CD_SHAPE_KEY, CD_ARMATURE_DEFORM,


Load script synchronously (updated to match generator template)

--- index.coffee
+++ index.coffee
@@ -3,10 +3,9 @@

 module.exports = (robot, scripts) ->
     scriptsPath = path.resolve(__dirname, 'src')
-    fs.exists scriptsPath, (exists) ->
-        if exists
-            for script in fs.readdirSync(scriptsPath)
-                if scripts? and '*' not in scripts
-                    robot.loadFile(scriptsPath, script) if script in scripts
-                else
-                    robot.loadFile(scriptsPath, script)+    if fs.existsSync scriptsPath
+        for script in fs.readdirSync(scriptsPath).sort()
+            if scripts? and '*' not in scripts
+                robot.loadFile(scriptsPath, script) if script in scripts
+            else
+                robot.loadFile(scriptsPath, script)


Formats selected text only if any text is selected.

If no text is selected, format the entire file if and only if the file's
grammar is supported.

Resolves #40.

--- index.coffee
+++ index.coffee
@@ -55,6 +55,8 @@
   return formatter.stringify newObject, scope, sorted

 formatter.doEntireFile = (editor) ->
+  if not editor.getLastSelection().isEmpty()
+    return false
   grammars = atom.config.get('pretty-json.grammars') ? []
   return editor.getGrammar().scopeName in grammars


Use new defaults for voxel-debug gui in voxel-plugins-ui, kb-bindings-ui

All modules use the same datgui instance, but now they find it themselves.
Also kb-bindings-ui uses game.buttons by default. Avoids need to explicitly
pass these options arguments.

Requires:
deathcap/voxel-plugins-ui@a33147f09083cc47a4aace5c1479dc294e7f2518
deathcap/kb-bindings-ui@187c5d9fb08f4581a6fef8d14dde43f1ff5a54ac

--- index.coffee
+++ index.coffee
@@ -172,10 +172,9 @@
   }

   # the GUI window (built-in toggle with 'H')
-  gui = new datgui.GUI()
-  plugins.add 'voxel-debug', {gui:gui}
-  plugins.add 'voxel-plugins-ui', {gui:gui}
-  plugins.add 'kb-bindings-ui', {gui:gui, kb:game.buttons}
+  plugins.add 'voxel-debug', {}
+  plugins.add 'voxel-plugins-ui', {}
+  plugins.add 'kb-bindings-ui', {}

   plugins.loadAll()


Increase block ID size to 16-bits

Increase block ID size to 16-bits

Can go up to 32-bits with Uint32Array, but not sure if that much is
needed. Increased from 8 to 16-bits to make room for future expansion,
testing with it now to uncover any performance impact problems.

--- index.coffee
+++ index.coffee
@@ -44,6 +44,7 @@
   console.log 'creating game'
   game = createGame {
     lightsDisabled: true
+    arrayType: Uint16Array
     #generate: voxel.generator['Valley']
     #generateVoxelChunk: terrain {chunkSize: 32, chunkDistance: 2, seed: 42}
     useAtlas: false


Added possibility to include dirty

Added possibility to include dirty

--- index.coffee
+++ index.coffee
@@ -8,7 +8,6 @@
     bucket : {}
     dirty : {}
     deleted : []
-

     obliterate : (cb) ->
       do (@filename, cb) ->
@@ -24,7 +23,7 @@

     store : (cb) ->
-      do (@fileName, @bucket, @dirty, cb) ->
+      do (@fileName, @bucket, @dirty, @deleted, cb) ->

         _.each @deleted, (id) -> @dirty[id] = {deleted:true, id:id}

@@ -67,7 +66,12 @@
     deleteById : (id) ->
       deleted.push id

-    getById : (id) -> clone @bucket[id]
+    getById : (id, includeDirty = false) ->
+      if includeDirty
+        included = _.extend {}, @bucket, @dirty
+        clone included[id]
+      else
+        clone @bucket[id]

     getByIds : (list) ->
       _.map list, (id) -> clone @bucket[id]
@@ -77,8 +81,11 @@
       itm = clone itm if itm?
       itm

-    where : (properties) ->
-      _.map _.where(@bucket, properties), (itm) -> clone(itm)
+    where : (properties, includeDirty = false) ->
+      if includeDirty
+        _.map _.where(_.extend({}, @bucket, @dirty), properties), (itm) ->clone(itm)
+      else
+        _.map _.where(@bucket, properties), (itm) -> clone(itm)

     set : (object) ->
       @dirty[object.id] = clone(object)


change deviceName assignment

change deviceName assignment

--- backend/filesystem.coffee
+++ backend/filesystem.coffee
@@ -23,7 +23,7 @@
     # Changes is the queue of operations, it contains
     # files that are being downloaded, and files to upload.
     changes: async.queue (task, callback) ->
-        deviceName ?= config.getDeviceName()
+        deviceName = config.getDeviceName()

         switch task.operation
             when task.operation is 'rebuild'


[neo4j] removed tag slicing for relations

--- server/lib/server/graph/decorators/base.coffee
+++ server/lib/server/graph/decorators/base.coffee
@@ -57,26 +57,30 @@
         followers       : @followerCount()

     if @datum.relationData.reply
-      snapshot.replies = @decorateAdditions @datum.relationData.reply
+      snapshot.replies = @decorateAdditions @sliceAdditions @datum.relationData.reply

     if @datum.relationData.tag
       snapshot.tags = @decorateAdditions @datum.relationData.tag

     if @datum.relationData.opinion
-      snapshot.opinions = @decorateAdditions @datum.relationData.opinion
+      snapshot.opinions = @decorateAdditions @sliceAdditions @datum.relationData.opinion

     return snapshot

   decorateAdditions:(additions)->
     results = []
-    additions = additions.reverse()
-    for addition in additions.slice(-3)
+    for addition in additions
       addition.bongo_ = {constructorName : addition.name}
       addition.meta   = @decorateSnapshotMeta addition

       results.push addition

     return results
+
+  sliceAdditions:(additions)->
+    additions = additions.reverse()
+    return additions.slice(-3)
+

   decorateSnapshotMeta:(data)->
     snapshotMeta            = data.meta


Unset instead of set null

Unset instead of set null

--- src/client/views/change_photo.coffee
+++ src/client/views/change_photo.coffee
@@ -2,6 +2,6 @@
   'click [name="change-photo"]': (event) ->
     event.preventDefault()
     Meteor.users.update Meteor.userId(),
-      $set:
+      $unset:
         'profile.image': null


Fixed photo bug

Fixed photo bug

--- client/tournaments/registrantBadge/registrantBadge.coffee
+++ client/tournaments/registrantBadge/registrantBadge.coffee
@@ -17,7 +17,7 @@
 Template.registrantBadge.photoFilename = ->
   photo = Session.get('active-user').profile.photoFilename
   if photo
-    return allcourt.photoRoot
+    return allcourt.photoRoot + photo
   else
     return ''


Remove unecessary double quotes

Remove unecessary double quotes

--- lib/actions/help.coffee
+++ lib/actions/help.coffee
@@ -61,7 +61,7 @@
 	return result

 exports.general = ->
-	console.log("Usage: resin [COMMAND] [OPTIONS]\n")
+	console.log('Usage: resin [COMMAND] [OPTIONS]\n')
 	console.log('Commands:\n')

 	for command in capitano.state.commands


fix(db): fix bug where seutp queries sometimes aren't run

--- src/flux/stores/database-connection.coffee
+++ src/flux/stores/database-connection.coffee
@@ -32,9 +32,11 @@
     @_isConnected = false
     databaseManager = remote.getGlobal('application').databaseManager

-    ## TODO Make this a nicer migration-based system
-    if atom.isMainWindow()
-      databaseManager.addSetupQueries(@_databasePath, @_setupQueries())
+    # TODO Make this a nicer migration-based system
+    # It's important these queries always get added. Don't worry, they'll
+    # only run if the DB doesn't exist yet, and even if they do run they
+    # all have `IF NOT EXISTS` clauses in them.
+    databaseManager.addSetupQueries(@_databasePath, @_setupQueries())

     databaseManager.prepare @_databasePath, =>
       @_isConnected = true


TBCampaign: Added one more digit to GB counter.

--- client/Intro/tbcampaignhomepageview.coffee
+++ client/Intro/tbcampaignhomepageview.coffee
@@ -12,8 +12,7 @@
     [left]     = leftInTB.split " "
     left       = "99.99"  if left is "100.00"
     [tb, gb]   = left.split "."
-
-    tb = ["0", tb.first]  if tb.length is 1
+    tb         = ["0", tb.first]  if tb.length is 1

     return """

#{tb[0] or 0}

@@ -21,6 +20,7 @@

,

#{gb[0] or 0}

#{gb[1] or 0}

+
9

     """

   getDaysLeft: ->


KD: bind3 => bind (this was a leftover from testing)

--- core/KD.coffee
+++ core/KD.coffee
@@ -1,4 +1,4 @@
-Function::bind3 or= (context) ->
+Function::bind or= (context) ->
   if 1 < arguments.length
     args = [].slice.call arguments, 1
     return => @apply context, if arguments.length then args.concat [].slice.call arguments else args


Show inspector using HTML

Show inspector using HTML

--- src/config/inspector.coffee
+++ src/config/inspector.coffee
@@ -87,7 +87,6 @@
   i: (world) -> world.get('split')
   width: (world) -> world.get('device').width - world.get('size')
   height: (world) -> world.get('size') + my.control.spacing
-  path: (world, args) -> world.get('rect_path')
   fill: my.color.background
   stroke: "black"
   _AUTHORITY: ROW_AUTHORITY
@@ -107,7 +106,6 @@
         }
         {name: (world) -> world.get('current').label()}
         {
-          path: (world) -> world.get('rect_path')
           fill: (world) -> world.get('current').get('fill')
           name: (world) -> world.get('current').get('fill')
           name_style: {x: 30, y: 30, fill: "white", stroke: "white"}


Add comment explaining why the odd double-json is necessary in trigger_Event

--- core/app/assets/javascripts/base/account_popup_opener.coffee
+++ core/app/assets/javascripts/base/account_popup_opener.coffee
@@ -14,6 +14,9 @@

 $(document).on 'account_success', (e) ->
   FactlinkApp.refreshCurrentUser JSON.parse(e.originalEvent.detail)
+  # detail is sent across not as object but as JSON, because it is a
+  # potentially cross-window object, and IE imposes complex security
+  # restrictions on those (e.g. http://forum.jquery.com/topic/obscure-ie-bug-in-jquery-1-4)
   mp_track 'User: Sign in'

 $(document).on 'account_error', (e) ->


fix(build-flow): Add missing resolve statement

--- build/tasks/publish-nylas-build-task.coffee
+++ build/tasks/publish-nylas-build-task.coffee
@@ -34,6 +34,7 @@
         if error
           reject(error)
           return
+        resolve()

   postToSlack = (msg) ->
     new Promise (resolve, reject) ->


Removed popover stuff

Removed popover stuff

--- core/app/backbone/views/evidence/add_evidence_view.coffee
+++ core/app/backbone/views/evidence/add_evidence_view.coffee
@@ -64,18 +64,4 @@
     return unless @$el.is(':visible')
     return if @model.get('showBox')

-    @popoverAdd '.js-supporting-button',
-      side: 'right'
-      margin: 10
-      container: @ui.buttons
-      contentView: new TextView text: 'Add supporting argument'
-      popover_className: 'add-supporting-argument button button-success js-supporting-tooltip evidence-add-tooltip'
-
-    @popoverAdd '.js-weakening-button',
-      side: 'left'
-      margin: 10
-      container: @ui.buttons
-      contentView: new TextView text: 'Add weakening argument'
-      popover_className: 'add-weakening-argument button button-danger js-weakening-tooltip evidence-add-tooltip'
-
     @_popoversRendered = true


Rewrote padding to use pixels as per @EamonNerbonne's comments

--- core/app/backbone/views/wheel/base_fact_wheel_view.coffee
+++ core/app/backbone/views/wheel/base_fact_wheel_view.coffee
@@ -133,11 +133,11 @@
       y: origin[1] - radius * Math.sin(angle)

     @canvas.customAttributes.arc = (percentage, percentageOffset, radius) =>
-      padding = 32/@options.radius
-      percentage = percentage - padding
-
-      startAngle = percentageOffset * 2 * Math.PI / 100
-      endAngle = (percentageOffset + percentage) * 2 * Math.PI / 100
+      paddingPixels = 2
+      paddingRadians = paddingPixels / @options.radius
+
+      startAngle = percentageOffset / 100 * 2 * Math.PI
+      endAngle = startAngle + percentage / 100 * 2 * Math.PI - paddingRadians

       origin = [@boxSize() / 2, @boxSize() / 2]


forgotten to remove a pointless file in example

--- examples/testbake.coffee
+++ examples/testbake.coffee
@@ -1,20 +0,0 @@
-brownie = require 'brownie'
-dir = __dirname
-brownie.bake
-  namespace   : 'QQ'
-  target      : './output.js'
-  domains     :
-    app         :  dir+'/app_code/'
-    shared      :  dir+'/shared_code/'
-  treeTarget  : './treetarget.txt'
-  DOMLoadWrap : (code) -> "(function(){"+code+"})();"
-  minifier    : (code) -> code.replace(/\n/,'')
-  minify      : true
-  domPrefix   : true
-  localTests  : true # app::helper has inlined tests
-
-# NB: basePoint and mainDomain not defined here
-# => brownie will look for 'main.coffee' in the 'app' domain.
-
-# To regenerate output, just run this script
-


Fixed signin popover class

Fixed signin popover class

--- core/app/backbone/views/popovers/react_signin_popover.coffee
+++ core/app/backbone/views/popovers/react_signin_popover.coffee
@@ -70,6 +70,7 @@
   render: ->
     if @state.opened && !currentSession.signedIn()
       ReactPopover className: 'white-popover', attachment: 'right',
-        ReactSigninLinks(className: "signin-popover")
+        _div ['signin-popover'],
+          ReactSigninLinks()
     else
       _span()


Allow config of all nuspec fields

Allow config of all nuspec fields

--- index.coffee
+++ index.coffee
@@ -15,17 +15,17 @@

     done = @async()

-    {appDirectory, loadingGif, outputDirectory, iconUrl} = grunt.config(@name)
+    {appDirectory, authors, description, iconUrl, loadingGif, outputDirectory, owners} = grunt.config(@name)
     outputDirectory ?= path.resolve('.')
     loadingGif ?= path.resolve(__dirname, '..', 'resources', 'install-spinner.gif')

     metadata = grunt.file.readJSON(path.join(appDirectory, 'resources', 'app', 'package.json'))

-    metadata.authors ?= metadata.author?.name ? metadata.author ? ''
-    metadata.description ?= ''
+    metadata.authors ?= authors ? metadata.author?.name ? metadata.author ? ''
+    metadata.description ?= description ? ''
     metadata.exe ?= "#{metadata.name}.exe"
     metadata.iconUrl ?= iconUrl ? 'https://raw.githubusercontent.com/atom/atom-shell/master/atom/browser/resources/win/atom.ico'
-    metadata.owners ?= metadata.authors
+    metadata.owners ?= owners ? metadata.authors

     template = _.template(grunt.file.read(path.resolve(__dirname, '..', 'template.nuspec')))
     nuspecContent = template(metadata)


:art:

:art:

--- index.coffee
+++ index.coffee
@@ -107,7 +107,7 @@
       if rom
         stop() if player
         return Promise.all([
-          System.import(cores[extension])
+          System.import cores[extension]
           localForage.getItem md5 rom
         ]).then ([core, save]) ->
           player = play core, rom, save


minor corrections to the index module

minor corrections to the index module

--- index.coffee
+++ index.coffee
@@ -43,6 +43,11 @@
 # information on how this is being done. See the modules in the
 # framework library to see the structure of the built hieararchy.
 do => module.exports = @collectPackages __dirname
+
+# Do some aliasing after loading the versy basics of the well known
+# framework internals. In this case, the aliasing is done to expose
+# the automatic package/module loader API to the framework users, so
+# that they can make use of it for loading their own libraries/code.
 module.exports.collectPackages = @collectPackages
 module.exports.collectModules = @collectModules
 module.exports.cachedKernel = @cachedKernel
@@ -51,10 +56,10 @@
 # framework is indeed loaded and are not missing. This is precaution
 # to make sure that the framework is in usable state, once is loaded.
 # You can refer to this definitions from the outside of the framework.
-assert nucleus = module.exports.nucleus
-assert membrane = module.exports.membrane
-assert exposure = module.exports.exposure
-assert semantic = module.exports.semantic
+assert _.isObject nucleus = module.exports.nucleus
+assert _.isObject membrane = module.exports.membrane
+assert _.isObject exposure = module.exports.exposure
+assert _.isObject semantic = module.exports.semantic

 # Alias the cakefile routine for the easy access to the primary way
 # of referencing the built in Cakefile library, which is standard.


Remove unused require()s

Remove unused require()s

--- index.coffee
+++ index.coffee
@@ -1,19 +1,12 @@
 # vim: set shiftwidth=2 tabstop=2 softtabstop=2 expandtab:

-ever = require 'ever'
-datgui = require 'dat-gui'
-
-ItemPile = require 'itempile'
-Inventory = require 'inventory'
-InventoryWindow = require 'inventory-window'
-{Recipe, AmorphousRecipe, PositionalRecipe, CraftingThesaurus, RecipeLocator} = require 'craftingrecipes'
-
-createGame = require 'voxel-engine'
 createPlugins = require 'voxel-plugins'

 # plugins (loaded by voxel-plugins; listed here for browserify)
+require 'voxel-engine'
 require 'voxel-registry'
 require 'voxel-carry'
+require 'craftingrecipes'
 require 'voxel-workbench'
 require 'voxel-chest'
 require 'voxel-inventory-hotbar'


fix variable riff, add addTabMaps

fix variable riff, add addTabMaps

--- src/marktab.coffee
+++ src/marktab.coffee
@@ -118,7 +118,7 @@
 				part = part.match(variableNamePattern)[0]
 				i += part.length
 				tabMapPart = this.parseVariable(part)
-				this.mergeTabMaps(tabMap, tabMapPart)
+				tabMap = this.addTabMaps(tabMap, tabMapPart)
 				continue
 			else
 				throw "unknown pattern: " + part
@@ -150,6 +150,14 @@
 			max = size if size > max
 		max

+	addTabMaps: (a, b) ->
+		result = this.normalizeTabMap(a)
+		aLen = this.longestString(a)
+		for strNum, strNotesArr of b
+			for fret, i in strNotesArr
+				result[strNum][aLen + i] = fret
+		result
+
 	# merges two tabMaps together, with the source overwriting the destination
 	mergeTabMaps: (dest, source) ->
 		for stringNum, stringNotes of source


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/metered.coffee
+++ src/metered.coffee
@@ -97,7 +97,7 @@
         radius: radius
         stroke:
           color: borderColor
-          width: 1.5
+          width: borderWidth

   ###*
   Configures a meter to be drawn each overlay event.


Update es-419.coffee

Update es-419.coffee

now its done, thanks.

--- app/locale/es-419.coffee
+++ app/locale/es-419.coffee
@@ -334,7 +334,7 @@
     tip_move_forward: "Hagas lo que hagas, siempre sigue hacia delante. - Martin Luther King Jr."
     tip_google: "¿Tienes un problema que no puedes resolver? ¡Googléalo!"
     tip_adding_evil: "Agregando una pizca de maldad."
-    tip_hate_computers: "Eso es la razon sobre la gente que piensa que odia las computadoras. Lo que ellos odian de verdad, es los pesimos programadores. - Larry Niven"
+    tip_hate_computers: "Esa es la razón por la cual la gente piensa que odia las computadoras. Lo que ellos odian de verdad, es los pesimos programadores. - Larry Niven"

   game_menu:
     inventory_tab: "Inventario"


Fix #2

Fix #2

--- lib/errors/multi_validation_error.coffee
+++ lib/errors/multi_validation_error.coffee
@@ -2,7 +2,7 @@
 ValidationError = require('./validation_error.coffee')

 module.exports = class MultiValidationError extends ValidationError
-  constructor: (message, value, validator, @errors) ->
+  constructor: (message, value, validator, @errors = []) ->
     super(@errorMessages().join("\n"), value, validator)

   errorMessages: =>


fixed nested geo_map color bug

fixed nested geo_map color bug

--- src/core/fetch/color.coffee
+++ src/core/fetch/color.coffee
@@ -37,9 +37,11 @@
         value = fetchValue(vars, o, vars.color.value, colorLevel)
       else
         value = id
-      if value isnt undefined and value isnt null
+
+      if !(value instanceof Array) and value isnt undefined and value isnt null
         color = getColor(value)
-        colors.push color  if colors.indexOf(color) < 0
+        colors.push color if colors.indexOf(color) < 0
+        break
       i--

     returnColor = if colors.length is 1 then colors[0] else vars.color.missing


Handle connection options

Handle connection options

--- source/sqlizer.coffee
+++ source/sqlizer.coffee
@@ -97,16 +97,19 @@
   #

   if options.find.method
-    Model.sqlFind = (filter, callback) ->
+    Model.sqlFind = (filter, options, callback) ->
+      if _.isFunction(options) and not callback
+        callback = options
+        options = {}
       query = @__buildQuery filter
       connector = @getDataSource().connector
       self = @
-      connector.execute query.text, query.values, {}, (err, rows) ->
+      connector.execute query.text, query.values, options, (err, rows) ->
         return callback err if err
         objects = _.map rows, (item) ->
           connector.fromRow self.definition.name, item
         if filter?.include
-          connector.getModelDefinition(self.definition.name).model.include objects, filter.include, {}, callback
+          connector.getModelDefinition(self.definition.name).model.include objects, filter.include, options, callback
         else
           callback null, objects


add failing test for #338

add failing test for #338

--- spec/BaconSpec.coffee
+++ spec/BaconSpec.coffee
@@ -731,6 +731,13 @@
         once = (x) -> Bacon.once(x)
         Bacon.fromArray([1, 2]).toProperty(0).flatMap(once)
       [0, 1, 2], unstable)
+  describe "works in a complex scenario #338", ->
+    expectStreamEvents(
+      ->
+        a = activate(series(2, ["a", "A"]))
+        b = activate(series(2, ["b", "B"])).delay(1).toProperty()
+        a.flatMapLatest((a) -> b.map((b) -> a + b))
+      ["ab", "Ab", "AB"], unstable)
   it "toString", ->
     expect(Bacon.constant(1).flatMap(->).toString()).to.equal("Bacon.constant(1).flatMap(function)")

@@ -3027,4 +3034,7 @@
 times = (x, y) -> x * y
 add = (x, y) -> x + y
 id = (x) -> x
-
+activate = (obs) ->
+  obs.onValue(->)
+  obs
+


lots of rework as I try to figure out the right structure

--- lib/flickr-robot.coffee
+++ lib/flickr-robot.coffee
@@ -4,16 +4,15 @@
 # Copyright (c) 2012 Christian Wilcox
 # Licensed under the MIT license.

+fs = require('fs')
+path = require('path')
+Flickr = require('flickr-with-uploads').Flickr

-Flickr = require('flickr-with-uploads').Flickr
-client = new Flickr('0RjUImXvsYx2P8Gi4eZScFh9fkLJltDV', 'mbu87dOB0FWncTRJ',
-    '3XF0pqP4daZf9oIlx-a7H1uMLeGrBidkJU', 'KpslBxHoh4QYk6ad')
-
-exports.awesome = ->
-  'awesome'
-
-exports.readOptions = (callback) ->
-  fs.readFile(path.join(__dirname, '.env'), 'utf8', (err, text) ->
+# internal helper functions
+readOptions = (callback) ->
+  pth = path.join(__dirname, "../.env")
+  console.log "env file: #{pth}"
+  fs.readFile(pth, 'utf8', (err, text) ->
     opts = {}
     if not err
       text.split(/\n/).forEach((line) ->
@@ -23,8 +22,41 @@
     callback(err, opts)
   )

-exports.api = (method_name, data, callback) ->
+# read client login info and get client
+client = readOptions((err, opts) ->
+  console.log "options: #{opts}"
+#  client = new Flickr('0RjUImXvsYx2P8Gi4eZScFh9fkLJltDV', 'mbu87dOB0FWncTRJ',
+#    '3XF0pqP4daZf9oIlx-a7H1uMLeGrBidkJU', 'KpslBxHoh4QYk6ad')
+#  console.log "connected to client #{client}"
+#  client
+  null
+)
+
+api = (method_name, data, callback) ->
   # overloaded as (method_name, data, callback)
   client.createRequest(method_name, data, true, callback).send()

-exports.get_f = ->
+get_photos = ->
+  params =
+    user: "test"
+  api('get_photos', params, (err, response) ->
+    if err
+      console.error("Could not upload photo:", err.toString())
+    else
+      # usually, the method name is precisely the name of the API method, as they are here:
+      api('flickr.people.getPhotos', {
+          photo_id: response.photoid
+        }, (err, response) ->
+        api('flickr.photosets.addPhoto', {photoset_id: 1272356126, photo_id: response.photo.id}, (err) ->
+          console.log("Full photo info:", response.photo)
+        )
+      )
+  )
+
+
+exports.awesome = ->
+#  get_photos()
+  try
+    get_photos()
+  catch error
+  'awesome'


Use relative require paths

Use relative require paths

--- lib/fuzzy-finder.coffee
+++ lib/fuzzy-finder.coffee
@@ -41,7 +41,7 @@
   createView:  ->
     unless @fuzzyFinderView
       @loadPathsTask?.abort()
-      FuzzyFinderView  = require 'fuzzy-finder/lib/fuzzy-finder-view'
+      FuzzyFinderView  = require './fuzzy-finder-view'
       @fuzzyFinderView = new FuzzyFinderView()
       if @projectPaths?.length > 0 and not @fuzzyFinderView.projectPaths?
         @fuzzyFinderView.projectPaths = @projectPaths


snap: pause 30 seconds before retrying if encounter a lock

--- salvus/snap.coffee
+++ salvus/snap.coffee
@@ -693,7 +693,7 @@
                     bup_dir : bup_active
                     cb      : (err, info) ->
                         rollback_info = info
-                        fs.writeFile rollback_file, misc.to_json(rollback_info), cb
+                        cb(err)

             # get deployed location of project (which can change at any time!)
             (cb) ->
@@ -715,8 +715,10 @@
                     cb       : (err) ->
                         if err
                             retry_later = true
-                            # put back in the queue to try again later
-                            snapshot_project(project_id:project_id, cb:cb)  # cb is way above.
+                            winston.debug("Couldn't lock #{project_id}, so put back in the queue to try again later (in 30 seconds)")
+                            f = () ->
+                                snapshot_project(project_id:project_id, cb:cb)  # cb is way above.
+                            setTimeout(f, 30000)
                         c(err)

             # create index
@@ -728,6 +730,10 @@
                     cb   : (err) ->
                         winston.debug("time to index #{project_id}: #{misc.walltime(t)} s")
                         cb(err)
+
+            # write rollback file
+            (cb) ->
+                fs.writeFile rollback_file, misc.to_json(rollback_info), cb

             # save
             (cb) ->
@@ -1279,7 +1285,7 @@
 # Ensure that we maintain and update snapshots of projects, according to our rules.
 snapshot_active_projects = (cb) ->
     project_ids = undefined
-    winston.info("snapshot_active_projects...")
+    winston.debug("checking for recently modified project.")
     async.series([
         (cb) ->
             database.select
@@ -1290,19 +1296,19 @@
                     project_ids = (r[0] for r in results)
                     cb(err)
         (cb) ->
-            winston.info("recently modified projects: #{misc.to_json(project_ids)}")
+            winston.debug("recently modified projects: #{misc.to_json(project_ids)}")

             v = []
             for id in project_ids
                 if age_of_most_recent_snapshot_in_seconds(id) >= program.snap_interval
                     v.push(id)
-            winston.info("projects needing snapshots: #{misc.to_json(v)}")
+            winston.debug("projects needing snapshots: #{misc.to_json(v)}")
             snapshot_projects
                 project_ids : v
                 cb          : cb
     ], (err) ->
         if err
-            winston.info("Error snapshoting active projects -- #{err}")
+            winston.debug("Error snapshoting active projects -- #{err}")
             # TODO: We need to trigger something more drastic somehow at some point...?

         setTimeout(snapshot_active_projects, 10000)  # check every 10 seconds


Better error handling

Better error handling

--- lib/pg-hoff-autocomplete-provider.coffee
+++ lib/pg-hoff-autocomplete-provider.coffee
@@ -61,7 +61,12 @@
                 return suggestions

             .catch (err) ->
-                console.log 'catch', err
+                if new RegExp(/ECONNREFUSED/).test(err)
+                    console.error 'Cannot autocomplete because of connection refused.'
+                else if new RegExp(/Cannot read property/).test(err)
+                    console.error 'Cannot autocomplete probably because you are not connected to any servers.'
+                else
+                    console.error 'Cannot autocomplete because', err

 #    onDidInsertSuggestion: ({editor, triggerPosition, suggestion}) ->
 #        console.log 'onDidInsertSuggestion'


Handle decoration updates in the editor-component

--- src/editor-component.coffee
+++ src/editor-component.coffee
@@ -330,6 +330,7 @@
     @subscribe editor, 'decoration-added', @onDecorationChanged
     @subscribe editor, 'decoration-removed', @onDecorationChanged
     @subscribe editor, 'decoration-changed', @onDecorationChanged
+    @subscribe editor, 'decoration-updated', @onDecorationChanged
     @subscribe editor, 'character-widths-changed', @onCharacterWidthsChanged
     @subscribe editor.$scrollTop.changes, @onScrollTopChanged
     @subscribe editor.$scrollLeft.changes, @requestUpdate


Fix DBG section decode

Fix DBG section decode

--- src/editor/mrbviewer.coffee
+++ src/editor/mrbviewer.coffee
@@ -68,7 +68,8 @@
       r = {}
       m = []
       i = 0
-      [r.rite_binary_header, i] = @_loadRiteBinaryHeader(arrayBuf, i)
+      ctx = {}
+      [r.rite_binary_header, i] = @_loadRiteBinaryHeader(ctx, arrayBuf, i)
       if r.rite_binary_header.binary_identify == "RITE" and
          r.rite_binary_header.binary_version == "0003"
         s = 0
@@ -76,9 +77,9 @@
         while (s == 0) or r[n].section_identify != "END\0"
           try
             n = "section_#{s}"
-            [r[n], i] = @_loadRiteSection(arrayBuf, i)
+            [r[n], i] = @_loadRiteSection(ctx, arrayBuf, i)
             s += 1
-          catch
+          catch error
             break
         if arrayBuf.byteLength > i
           m.push("Found #{arrayBuf.byteLength - i} bytes junk after END section")
@@ -105,42 +106,56 @@
     # Big-endian
     (a[0] << 8) | a[1]

-  _loadRiteBinaryHeader: (arrayBuf, offset) ->
+  _stringize = (a) ->
+    String.fromCharCode.apply(null, a)
+
+  _loadRiteBinaryHeader: (ctx, arrayBuf, offset) ->
     h = new Uint8Array(arrayBuf, offset, 22)
     [{
-      binary_identify: String.fromCharCode.apply(null, h.subarray(0, 4))
-      binary_version: String.fromCharCode.apply(null, h.subarray(4, 8))
+      binary_identify: _stringize(h.subarray(0, 4))
+      binary_version: _stringize(h.subarray(4, 8))
       binary_crc: _bin_to_u16_be(h.subarray(8, 10))
       binary_size: _bin_to_u32_be(h.subarray(10, 14))
-      compiler_name: String.fromCharCode.apply(null, h.subarray(14, 18))
-      compiler_version: String.fromCharCode.apply(null, h.subarray(18, 22))
+      compiler_name: _stringize(h.subarray(14, 18))
+      compiler_version: _stringize(h.subarray(18, 22))
     }, offset + h.byteLength]

-  _loadRiteSection: (arrayBuf, offset) ->
+  _loadRiteSection: (ctx, arrayBuf, offset) ->
     h = new Uint8Array(arrayBuf, offset, 8)
     r = {
       offset: offset
-      section_identify: String.fromCharCode.apply(null, h.subarray(0, 4))
+      section_identify: _stringize(h.subarray(0, 4))
       section_size: _bin_to_u32_be(h.subarray(4, 8))
     }
     h = new Uint8Array(arrayBuf, offset + 8, r.section_size - 8)
     switch r.section_identify
       when "IREP"
-        r.rite_version = String.fromCharCode.apply(null, h.subarray(0, 4))
-        @_loadIrepRecord(r, h.subarray(4), offset + 4)
+        r.rite_version = _stringize(h.subarray(0, 4))
+        ctx.last_irep = r
+        @_loadIrepRecord(ctx, r, h.subarray(4), offset + 4)
+      when "DBG\0"
+        r.filenames_len = _bin_to_u16_be(h.subarray(0, 2))
+        r.filenames = []
+        i = 2
+        for n in [0...(r.filenames_len)] by 1
+          l = _bin_to_u16_be(h.subarray(i, i + 2))
+          i += 2
+          r.filenames.push(_stringize(h.subarray(i, i + l)))
+          i += l
+        r.__irep = ctx.last_irep
+        ctx.last_dbg = r
+        @_loadDebugRecord(ctx, r, h.subarray(i))
       when "LVAR"
         r.syms_len = _bin_to_u32_be(h.subarray(0, 4))
         r.record = []
         i = 4
-        n = 0
-        while n < r.syms_len
+        for n in [0...(r.syms_len)] by 1
           l = _bin_to_u16_be(h.subarray(i, i + 2))
           i += 2
           r.record.push(
-            {symbol: String.fromCharCode.apply(null, h.subarray(i, i + l))}
+            {symbol: _stringize(h.subarray(i, i + l))}
           )
           i += l
-          n += 1
         while i < r.section_size
           n = _bin_to_u16_be(h.subarray(i, i + 2))
           r.record[n]?.lv = _bin_to_u16_be(h.subarray(i + 2, i + 4)) if n < 0xffff
@@ -148,7 +163,7 @@
     offset += r.section_size
     return [r, offset]

-  _loadIrepRecord: (r, h, offset) ->
+  _loadIrepRecord: (ctx, r, h, offset) ->
     r.record_size = _bin_to_u32_be(h.subarray(0, 4))
     r.number_of_locals = _bin_to_u16_be(h.subarray(4, 6))
     r.number_of_regs = _bin_to_u16_be(h.subarray(6, 8))
@@ -157,35 +172,65 @@
     pad = (4 - (offset + 14) % 4) % 4
     r.iseq = {number_of_opcodes: n, padding_bytes: pad, opcodes: []}
     i = 14 + pad
-    while n > 0
+    for n in [0...(r.iseq.number_of_opcodes)] by 1
       c = _bin_to_u32_be(h.subarray(i, i + 4))
       [c1,c2] = @_decodeRiteInst(c)
-      c1 = c1.replace("\t", " ") + "                                        "
-      r.iseq.opcodes.push("#{c1.substring(0, 20)}# #{c2}")
+      [c0,c1] = c1.split("\t")
+      r.iseq.opcodes.push(sprintf("%-11s %-11s # %s", c0, c1 or "", c2))
       i += 4
-      n -= 1
     n = _bin_to_u32_be(h.subarray(i, i + 4))
     i += 4
     r.pool = {length_of_pool: n, values: []}
-    while n > 0
-      n -= 1
+    for n in [0...(r.pool.length_of_pool)] by 1
+      null  # FIXME
     n = _bin_to_u32_be(h.subarray(i, i + 4))
     i += 4
     r.syms = {number_of_syms: n, symbols: []}
-    while n > 0
+    for n in [0...(r.syms.number_of_syms)] by 1
       len = _bin_to_u16_be(h.subarray(i, i + 2))
       i += 2
       if len == 0xffff
         r.syms.symbols.push(null)
       else
-        r.syms.symbols.push(String.fromCharCode.apply(null, h.subarray(i, i + len)))
+        r.syms.symbols.push(_stringize(h.subarray(i, i + len)))
         i += (len + 1)
-      n -= 1
-    n = 0
-    while n < r.number_of_child_ireps
-      @_loadIrepRecord(r["child_#{n}"] = {}, h.subarray(i), offset + i)
-      n += 1
-    return
+    for n in [0...(r.number_of_child_ireps)] by 1
+      i += @_loadIrepRecord(ctx, r["child_#{n}"] = {}, h.subarray(i), offset + i)
+    return i
+
+  _loadDebugRecord: (ctx, r, h) ->
+    r.file_count = _bin_to_u16_be(h.subarray(4, 6))
+    r.files = {}
+    i = 6
+    for n in [0...(r.file_count)] by 1
+      m = ctx.last_dbg?.filenames[_bin_to_u16_be(h.subarray(i + 4, i + 6))]
+      m or= "file_##{n}"
+      f = r.files[m] = {
+        start_pos: _bin_to_u32_be(h.subarray(i, i + 4))
+      }
+      i += 6
+      f.line_entry_count = _bin_to_u32_be(h.subarray(i, i + 4))
+      t = h[i + 4]
+      f.line_type = ["mrb_debug_line_ary", "mrb_debug_line_flat_map"][t] or "unknown"
+      i += 5
+      if t == 0
+        f.lines = []
+        for x in [0...(f.line_entry_count)] by 1
+          f.lines.push(_bin_to_u16_be(h.subarray(i, i + 2)))
+          i += 2
+      if t == 1
+        f.lines = []
+        for x in [0...(f.line_entry_count)] by 1
+          f.lines.push({
+            start_pos: _bin_to_u32_be(h.subarray(i, i + 4))
+            line: _bin_to_u16_be(h.subarray(i + 4, i + 6))
+          })
+          i += 6
+    for n in [0...(r.__irep?.number_of_child_ireps or 0)] by 1
+      k = "child_#{n}"
+      i += @_loadDebugRecord(ctx, r[k] = {__irep: r.__irep[k]}, h.subarray(i))
+    delete r.__irep
+    return i

   _decodeRiteInst: (code) ->
     op = (code) & 0x7f


slightly improved logging

slightly improved logging

--- src/torconsensus.iced
+++ src/torconsensus.iced
@@ -126,7 +126,7 @@
         skipEmptyLines: false

     lr.on 'error', (err) =>
-      console.log "lr had " + err
+      console.error "LineReading had error:" + err

     lr.on 'line', (line) =>
       sl = S(line)
@@ -165,4 +165,4 @@
       @readIndex++

     lr.on 'end', () =>
-      console.log "===> " + @authorities.length + " " + @routers.length
+      console.log "Authorities ===> " + @authorities.length + " Routers ===> " + @routers.length


:lipstick:

:lipstick:

--- spec/cson-spec.coffee
+++ spec/cson-spec.coffee
@@ -19,7 +19,7 @@

     describe "when the object contains a function", ->
       it "it gets filtered away, when not providing a visitor function", ->
-        expect(CSON.stringify(a:  -> 'function')).toBe '{}'
+        expect(CSON.stringify(a: -> 'function')).toBe '{}'

     describe "when formatting an undefined key", ->
       it "does not include the key in the formatted CSON", ->


Also work with \onlinecite{}

Also work with \onlinecite{}

--- lib/latexer-hook.coffee
+++ lib/latexer-hook.coffee
@@ -6,7 +6,7 @@
   class LatexerHook
     beginRex: /\\begin{([^}]+)}/
     refRex: /\\(ref|eqref|[cCvV]ref)({|{[^}]+,)$/
-    citeRex: /\\(cite|textcite|citet|citep|citet\*|citep\*)(\[[^\]]+\])?({|{[^}]+,)$/
+    citeRex: /\\(cite|textcite|onlinecite|citet|citep|citet\*|citep\*)(\[[^\]]+\])?({|{[^}]+,)$/
     constructor: (@editor) ->
       @disposables = new CompositeDisposable
       @disposables.add @editor.onDidChangeTitle => @subscribeBuffer()


Remove unneeded rootView variable

Remove unneeded rootView variable

--- spec/tabs-spec.coffee
+++ spec/tabs-spec.coffee
@@ -7,7 +7,7 @@
   [editor, buffer, tabs] = []

   beforeEach ->
-    rootView = new RootView(require.resolve('fixtures/sample.js'))
+    new RootView(require.resolve('fixtures/sample.js'))
     rootView.open('sample.txt')
     rootView.simulateDomAttachment()
     atom.loadPackage("tabs")


transport: Add test for multiple producers

--- spec/transport.coffee
+++ spec/transport.coffee
@@ -17,8 +17,44 @@
 connectAll = (clients, callback) ->
   connect = (c, cb) ->
     c.connect cb
+
   async.map clients, connect, callback

+zip = () ->
+  lengthArray = (arr.length for arr in arguments)
+  length = Math.min(lengthArray...)
+  for i in [0...length]
+    arr[i] for arr in arguments
+
+#
+createConnectClients = (address, names, callback) ->
+  createConnect = (name, cb) ->
+    client = transport.getClient address
+    client.connect (err) ->
+      cb err, client
+
+  async.map names, createConnect, (err, clients) ->
+    return callback err if err
+    ret = {}
+    for nc in zip names, clients
+      ret[nc[0]] = nc[1]
+    return callback null, ret
+
+createBindQueues = (broker, queueMapping, callback) ->
+  createBindQueue = (det, cb) ->
+    [client, srcQ, tgtQ] = det
+    client.createQueue 'outqueue', srcQ, (err) ->
+      return cb err if err
+      broker.bindQueue srcQ, tgtQ, cb
+
+  async.map queueMapping, createBindQueue, callback
+
+sendPackets = (packets, callback) ->
+  send = (p, cb) ->
+    [client, queue, data] = p
+    client.sendToQueue queue, data, cb
+
+  async.map packets, send, callback

 describe 'Transport', ->

@@ -117,3 +153,49 @@
                   chai.expect(err).to.be.a 'null'
                 sender.sendToQueue outQueue, payload, (err) ->
                   chai.expect(err).to.be.a 'null'
+
+
+      describe 'multiple outqueues bound to one inqueue', ->
+        it 'all sent on outqueues shows up on inqueue', (done) ->
+          @timeout 3000
+          senders = [ 'sendA', 'sendB', 'sendC' ]
+          clientNames = ['receive']
+          clientNames.push.apply clientNames, senders
+          createConnectClients address, clientNames, (err, clients) ->
+            chai.expect(err).to.be.a 'null'
+
+            expect = [ {name:'sendA'}, {name:'sendB'}, {name:'sendC'} ]
+
+            received = []
+            onReceive = (msg) ->
+              clients.receive.ackMessage msg
+              chai.expect(msg).to.include.keys 'data'
+              received.push msg.data
+              if received.length == expect.length
+                received.sort (a,b) ->
+                  return -1 if a.name < b.name
+                  return 1 if a.name > b.name
+                  return 0
+                chai.expect(received).to.eql expect
+                done()
+
+            inQueue = 'inqueue27'
+
+            clients.receive.createQueue 'inqueue', inQueue, (err) ->
+              chai.expect(err).to.not.exist
+              clients.receive.subscribeToQueue inQueue, onReceive, (err) ->
+                chai.expect(err).to.not.exist
+
+                # Bind all outqueues to same inqueue
+                queueMapping = []
+                for name in senders
+                  queueMapping.push [ clients[name], name, inQueue ]
+                createBindQueues broker, queueMapping, (err) ->
+                  chai.expect(err).to.not.exist
+
+                  packets = []
+                  for name in senders
+                    packets.push [ clients[name], name, { name: name } ]
+                  sendPackets packets, (err) ->
+                    chai.expect(err).to.not.exist
+


Fixes Dialog error message icon. [ci skip]

--- app/assets/javascripts/alchemy/alchemy.dialog.js.coffee
+++ app/assets/javascripts/alchemy/alchemy.dialog.js.coffee
@@ -127,7 +127,7 @@
           error_header = "#{xhr.statusText} (#{xhr.status})"
         error_body = "Please check log and try again."
     $errorDiv = $("
")
-    $errorDiv.append ''
+    $errorDiv.append ""
     $errorDiv.append "
#{error_header}
"
     $errorDiv.append "
#{error_body}
"
     @dialog_body.html $errorDiv


Fire "empty" and "dropped" events when stopAll() called

If 'interrupt' is true then it will also fire 'dropped' events for jobs
that are running (as their callbacks won't be called on completion).

--- src/Bottleneck.coffee
+++ src/Bottleneck.coffee
@@ -10,7 +10,7 @@
 		@_nextRequest = Date.now()
 		@_nbRunning = 0
 		@_queues = @_makeQueues()
-		@_timeouts = []
+		@_running = []
 		@_unblockTime = 0
 		@penalty = (15 * @minTime) or 5000
 		@interrupt = false
@@ -38,17 +38,19 @@
 			next = (@_getFirst @_queues).shift()
 			if queued == 1 then @_trigger 'empty', []
 			done = false
-			index = -1 + @_timeouts.push setTimeout =>
-				completed = =>
-					if not done
-						done = true
-						delete @_timeouts[index]
-						@_nbRunning--
-						@_tryToRun()
-						if not @interrupt then next.cb?.apply {}, Array::slice.call arguments, 0
-				if @limiter? then @limiter.submit.apply @limiter, Array::concat next.task, next.args, completed
-				else next.task.apply {}, next.args.concat completed
-			, wait
+			index = -1 + @_running.push
+				timeout: setTimeout =>
+					completed = =>
+						if not done
+							done = true
+							delete @_running[index]
+							@_nbRunning--
+							@_tryToRun()
+							if not @interrupt then next.cb?.apply {}, Array::slice.call arguments, 0
+					if @limiter? then @limiter.submit.apply @limiter, Array::concat next.task, next.args, completed
+					else next.task.apply {}, next.args.concat completed
+				, wait
+				job: next
 			true
 		else false
 	submit: (args...) => @submitPriority.apply {}, Array::concat MIDDLE_PRIORITY, args
@@ -94,8 +96,17 @@
 		if @events[name]? then @events[name].push cb else @events[name] = [cb]
 		@
 	stopAll: (@interrupt=@interrupt) ->
-		(clearTimeout a for a in @_timeouts)
+		(clearTimeout a.timeout for a in @_running)
 		@_tryToRun = ->
 		@check = @submit = @submitPriority = @schedule = @schedulePriority = -> false
+
+		if @interrupt
+			(@_trigger 'dropped', [a.job] for a in @_running)
+
+		job = null
+		while job = (@_getFirst @_queues).shift()
+			@_trigger 'dropped', [job]
+
+		@_trigger 'empty', []

 module.exports = Bottleneck


No one reads this shit :crying_cat_face:

Lucky the panels are square

--- scripts/ayp.coffee
+++ scripts/ayp.coffee
@@ -249,7 +249,7 @@
   # cb invoked as `cb(err, image)`. `err` is only set on failure
   buildPanel: (lines, cb) =>
     # Setup a transparant frame that we'll composite characters and text into.
-    frame = GD.createTrueColor(AYP_PANEL_WIDTH, AYP_PANEL_WIDTH)
+    frame = GD.createTrueColor(AYP_PANEL_WIDTH, AYP_PANEL_HEIGHT)
     frame.saveAlpha(1)
     clear = frame.colorAllocateAlpha(0, 0, 0, 127)
     frame.fill(0, 0, clear)


Show sidebar when the 'new search' button is clicked

--- app/assets/javascripts/rglossa/views/corpus_view.coffee
+++ app/assets/javascripts/rglossa/views/corpus_view.coffee
@@ -2,12 +2,12 @@

   didInsertElement: ->
     @$().on('click', '#hide-criteria-button, button[data-search]', @hideSidebar)
-    @$().on('click', '#show-criteria-button', @showSidebar)
+    @$().on('click', '#show-criteria-button, #new-search-button', @showSidebar)

   willRemoveElement: ->
     @$().off('click', '#hide-criteria-button, button[data-search]', @hideSidebar)
-    @$().off('click', '#show-criteria-button', @showSidebar)
+    @$().off('click', '#show-criteria-button, #new-search-button', @showSidebar)

   hideSidebar: ->


fix issue #5005 - persist upload images in text editor

--- app/modules/components/wysiwyg/wysiwyg.directive.coffee
+++ app/modules/components/wysiwyg/wysiwyg.directive.coffee
@@ -60,14 +60,14 @@
         pre = document.createElement('pre')
         code = document.createElement('code')

-        console.log range.startContainer.parentNode.nextSibling
-
         if !range.startContainer.parentNode.nextSibling
             $('<br/>').insertAfter(range.startContainer.parentNode)

-        start = range.startContainer.parentNode.nextSibling
-
-        code.appendChild(range.extractContents())
+        start = range.endContainer.parentNode.nextSibling
+
+        extract = range.extractContents()
+
+        code.appendChild(extract)

         pre.appendChild(code)

@@ -78,7 +78,7 @@
     refreshCodeBlocks = (mediumInstance) ->
         # clean empty <p> content editable adds it when range.extractContents has been execute it
         for mainChildren in mediumInstance.elements[0].children
-            if mainChildren && mainChildren.tagName.toLowerCase() == 'p' && !mainChildren.innerText.length
+            if mainChildren && mainChildren.tagName.toLowerCase() == 'p' && !mainChildren.innerHTML.trim().length
                 mainChildren.parentNode.removeChild(mainChildren)

         preList = mediumInstance.elements[0].querySelectorAll('pre')


Namespace page routes

Namespace page routes

--- apps/about2/index.coffee
+++ apps/about2/index.coffee
@@ -10,8 +10,8 @@
 app.set 'view engine', 'jade'

 # Markdown pages
-app.get '/about2/press', routes.page('press-list')
-app.get '/about2/events', routes.page('events')
+app.get '/about2/page/press', routes.page('press-list')
+app.get '/about2/page/events', routes.page('events')

 app.get '/about2', routes.index
 app.get /^\/about2((?!\/edit).)*$/, routes.index # Scroll routes


updates to fix fullscreen_exit event, and to allow additional sendbeacon method

--- src/videojs.ga.coffee
+++ src/videojs.ga.coffee
@@ -32,6 +32,9 @@
   eventCategory = options.eventCategory || dataSetupOptions.eventCategory || 'Brightcove Player'
   # if you didn't specify a name, it will be 'guessed' from the video src after metadatas are loaded
   defaultLabel = options.eventLabel || dataSetupOptions.eventLabel
+
+	# check if should be using another method for sendbecon
+	sendbeaconOverride = options.sendbeaconOverride || false

   # init a few variables
   percentsAlreadyTracked = []
@@ -190,12 +193,14 @@
     if @isFullscreen?() || @isFullScreen?()
       sendbeacon( getEventName('fullscreen_enter'), false, currentTime )
     else
-      sendbeacon( getEventName('fullscreen_enter'), false, currentTime )
+      sendbeacon( getEventName('fullscreen_exit'), false, currentTime )
     return

   sendbeacon = ( action, nonInteraction, value ) ->
     # videojs.log action, " ", nonInteraction, " ", value
-    if window.ga
+		if sendbeaconOverride
+			sendbeaconOverride(eventCategory, action, eventLabel, value, nonInteraction)
+    else if window.ga
       ga 'send', 'event',
         'eventCategory' 	: eventCategory
         'eventAction'		  : action


Add Assertion url to data return for badges

--- apps/users/routes.coffee
+++ apps/users/routes.coffee
@@ -2,6 +2,7 @@
 util = require 'util'
 fs = require 'fs'
 authenticate = require '../middleware/authenticate'
+configuration = require '../../lib/configuration'
 _ = require 'underscore'

 routes = (app) ->
@@ -14,7 +15,11 @@
       User.findOne {username: username}, (err, user) ->
         next(err) if err?
         if user?
-          formatResponse req, res, user.badges
+          badges = user.badges.map ->
+            badge = @toJSON();
+            badge.assertion = "http://#{configuration.get('hostname')}/users/#{user.email_hash}/badges/#{badge.slug}"
+            badge
+          formatResponse req, res, badges
         else
           formatResponse req, res, []


Return $vexContent here like vex.open does

--- coffee/vex.dialog.coffee
+++ coffee/vex.dialog.coffee
@@ -71,6 +71,8 @@
     if options.focusFirstInput
         $vexContent.find('input[type="text"], input[type="submit"]').first().focus()

+    return $vexContent
+
 dialog.alert = (options) ->
     if typeof options is 'string'
         options = message: options


Call sqlite.verbose() only when DEBUG environment variable is set.

--- db.coffee
+++ db.coffee
@@ -21,7 +21,8 @@
 debug = (require 'debug')('Chatbot:db')
 api = require './api'

-sqlite = (require 'sqlite3').verbose()
+sqlite = require 'sqlite3'
+do sqlite.verbose if process.env.DEBUG

 db = new sqlite.Database config.database


Remove not used ViewerTopBar class

Remove not used ViewerTopBar class

--- client/Viewer/viewertopbar.coffee
+++ client/Viewer/viewertopbar.coffee
@@ -1,33 +0,0 @@
-class ViewerTopBar extends JView
-  constructor:(options,data)->
-    options.cssClass = 'viewer-header top-bar clearfix'
-    super options,data
-
-    @addressBarIcon = new KDCustomHTMLView
-      tagName   : "span"
-      cssClass  : "address-bar-icon"
-
-    @pageLocation = new KDHitEnterInputView
-      type      : "text"
-      callback  : =>
-        @parent.openPath @pageLocation.getValue()
-        @pageLocation.focus()
-
-    @refreshButton = new KDCustomHTMLView
-      tagName   : "a"
-      attributes:
-        href    : "#"
-      cssClass  : "refresh-link"
-      click     : => @parent.refreshIFrame()
-
-  setPath:(path)->
-    @pageLocation.unsetClass "validation-error"
-    @pageLocation.setValue "#{path}"
-
-  pistachio:->
-
-    """
-    {{> @addressBarIcon}}
-    {{> @pageLocation}}
-    {{> @refreshButton}}
-    """


Update Checkins.coffee

Update Checkins.coffee

--- client/components/Checkins.coffee
+++ client/components/Checkins.coffee
@@ -66,9 +66,6 @@
             Не забудьте с собой паспорт (если паспорта еще нет, то свидетельство о рождении) -- его могут спросить охранники на входе!
             </p>

-            <Alert bsStyle="danger">
-                30 декабря и 6 января занятий не будет. Ниже запись на 13 января.
-            </Alert>
             {
             if @state.result?.error
                 <Alert bsStyle="danger">


width split ui

width split ui

--- coffee/operations/width_split_column.coffee
+++ coffee/operations/width_split_column.coffee
@@ -33,10 +33,10 @@

      @$.elems.inputsDiv = @div ''

-     @button on: {click: @$.on.cancel}, 'Cancel'
-     @$.elems.btn = @button '.button-primary', 'Split',
+     @$.elems.btn = @button '.u-full-width.button-primary', 'Split',
       on: {click: @$.on.apply}
       style: {display: 'none'}
+     @button '.u-full-width', on: {click: @$.on.cancel}, 'Cancel'

     @_setData() if @table?

@@ -83,7 +83,7 @@
      elems = {}
      elems.div = @div ->
       @label for: "split-#{n}", "Split #{n + 1}"
-      elems.input = @input "#split-#{n}",
+      elems.input = @input "#split-#{n}.u-full-width",
        value: "#{split}"
        type: 'number'
        on: {change: @$.on.change}


This is going to get annoying

This is going to get annoying

--- atom.symlink/config.cson
+++ atom.symlink/config.cson
@@ -1,7 +1,7 @@
 'editor':
-  'fontSize': 15
   'showIndentGuide': true
   'fontFamily': 'Menlo'
+  'fontSize': 15
 'core':
   'excludeVcsIgnoredPaths': true
   'ignoredNames': [
@@ -31,5 +31,5 @@
 'welcome':
   'showOnStartup': false
 'release-notes':
-  'viewedVersion': '0.85.0'
+  'viewedVersion': '0.89.0'
 'tree-view': {}


Extracted method

Extracted method

--- core/app/backbone/models/discussion.coffee
+++ core/app/backbone/models/discussion.coffee
@@ -3,11 +3,11 @@
     @_fact = opts.fact
     @_type = opts.type

-    @getInteractorsEvidence().opinionaters().fetch()
+    @_fetchOpinionaters()

     @_wheel = @_fact.getFactWheel()
     @_wheel.on 'sync', =>
-      @getInteractorsEvidence().opinionaters().fetch()
+      @_fetchOpinionaters()

   evidence:  -> @_evidence ?= @_getEvidence()

@@ -26,3 +26,6 @@
       when 'weakening' then @_interactorsPage('disbelieves')
       when 'doubting' then @_interactorsPage('doubts')

+  _fetchOpinionaters: ->
+    @getInteractorsEvidence().opinionaters().fetch()
+


Fix constraints inside instance declaration by substituting fixed vars and also updating classParams as reduction during definitionTranslation adds more information

--- src/js/app/compilers/teascript/compiler.coffee
+++ src/js/app/compilers/teascript/compiler.coffee
@@ -137,10 +137,13 @@
   visitExpressions ast, (expression) ->
     expressions.push "#{collapse toHtml expression} :: #{highlightType expression.tea}" if expression.tea
   types: values mapMap (__ highlightType, _type), subtractMaps ctx._scope(), builtInDefinitions()
-  subs: subToObject mapSub highlightType, ctx.substitution
+  subs: printSubstitution ctx.substitution
   fails: ctx.substitution.fails
   ast: expressions
   deferred: ctx.deferredBindings()
+
+printSubstitution = (sub) ->
+  subToObject mapSub highlightType, sub

 mapTypingBare = (fn, string) ->
   ast = astize tokenize string
@@ -523,6 +526,9 @@

   classParamsForType: (constraint) ->
     nestedLookupInMap @classParams, typeNamesOfNormalized constraint
+
+  updateClassParams: ->
+    @classParams = substituteVarNames this, @classParams

 expressionCompile = (ctx, expression) ->
   throw new Error "invalid expressionCompile args" unless ctx instanceof Context and expression?
@@ -1937,15 +1943,21 @@
   vars = subtractSets (findFree type), ctx.allBoundTypeVariables()
   quantify vars, type

+substituteVarNames = (ctx, varNames) ->
+  subbed = (name) =>
+    (inSub ctx.substitution, name)?.name or name
+  rehashMap subbed, varNames
+
 # Takes a set of fixed type variables, a set of type variables which
 # should be quantified and a list of constraints
 # returns deferred and retained constraints
 deferConstraints = (ctx, fixedVars, quantifiedVars, constraints) ->
   reducedConstraints = reduceConstraints ctx, constraints
   throw new Error "could not reduce constraints in deferConstraints" unless reducedConstraints
+  subbedFixedVars = substituteVarNames ctx, fixedVars
   isFixed = (constraint) ->
-    # log fixedVars, constraint, (findFree constraint)
-    isSubset fixedVars, (findFree constraint)
+    # log subbedFixedVars, constraint, (findFree constraint)
+    isSubset subbedFixedVars, (findFree constraint)
   [deferred, retained] = partition isFixed, reducedConstraints
   # TODO: handle ambiguity when reducedConstraints include variables not in
   # fixedVars or quantifiedVars
@@ -2304,6 +2316,7 @@
 irDefinitionTranslate = (ctx, {type, expression}) ->
   finalType = substitute ctx.substitution, type
   reducedConstraints = reduceConstraints ctx, finalType.constraints
+  ctx.updateClassParams()
   # TODO: what about the class dictionaries order?
   counter = {}
   classParams = newMap()
@@ -3409,11 +3422,17 @@
 isMapEmpty = (set) ->
   set.size is 0

-mapSet =
 mapMap = (fn, set) ->
   initialized = newMap()
   for key, val of set.values
     addToMap initialized, key, fn val
+  initialized
+
+mapSet =
+rehashMap = (fn, set) ->
+  initialized = newMap()
+  for key, val of set.values
+    addToMap initialized, (fn key), val
   initialized

 filterSet =
@@ -4982,6 +5001,43 @@
   """
   "(length (magic \\C (Map)))", 1

+  'functional deps with instance constraints'
+  """
+    Stack (data [a]
+      Nil
+      Node [value: a tail: (Stack a)])
+
+    Eq (class [a]
+      = (fn [x y] (: (Fn a a Bool))))
+
+    num-eq (instance (Eq Num)
+      = (macro [x y]
+        (: (Fn Num Num Bool))
+        (Js.binary "===" x y)))
+
+    Collection (class [collection item]
+      elem? (fn [what in]
+        (: (Fn item collection Bool))))
+
+    Bag (class [bag item]
+      fold (fn [with initial over]
+        (: (Fn (Fn a item a) a bag a))))
+
+    stack-collection (instance (Collection (Stack a) a)
+      {(Eq a)}
+      elem? (fn [what in]
+        (fold found-or-equals False in)
+        found-or-equals (fn [found item]
+          (= what item))))
+
+    stack-bag (instance (Bag (Stack a) a)
+      fold (fn [with initial over]
+        (match over
+          Nil initial
+          (Node x xs) (fold with (with initial x) xs))))
+  """
+  "(elem? 2 (Node 2 Nil))", yes
+
   'nested pattern matching'
   """
     f (fn [x]


Removed anti-Y bias. Improved randomness by reducing probability of duplicate Ts.

--- coffee/lib/abstract-chosen.coffee
+++ coffee/lib/abstract-chosen.coffee
@@ -103,7 +103,7 @@
     new_id

   generate_random_char: ->
-    chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZ"
+    chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
     rand = Math.floor(Math.random() * chars.length)
     newchar = chars.substring rand, rand+1


Splatting in syntax

Splatting in syntax

--- src/js/app/compilers/teascript/compiler.coffee
+++ src/js/app/compilers/teascript/compiler.coffee
@@ -2101,6 +2101,10 @@

   if hasName
     macroName = ctx.definitionName()
+    [param] = _terms paramTuple
+    if isSplat param
+      splatted = yes
+      paramTuple = tuple_ splatToName param

     macroSource = call_ (token_ 'fn'), (join [paramTuple], rest)
     macroCompiled = (termCompile ctx, macroSource)
@@ -2109,7 +2113,10 @@
     macroFn = eval compiledMacro
     if macroFn
       ctx.declareMacro ctx.definitionPattern(), (ctx, call) ->
-        constantToSource macroFn (_arguments call)...
+        if splatted
+          constantToSource macroFn Immutable.List (_arguments call)
+        else
+          constantToSource macroFn (_arguments call)...
     else
       malformed ctx, ctx.definitionPattern(), 'Macro failed to compile'


make grunt copy non-coffeescript files when changed in tower source, fixes #229

--- grunt.coffee
+++ grunt.coffee
@@ -62,7 +62,7 @@
         dest: 'lib'
     watch:
       packageJSON:
-        files: ['packages/**/package.json']
+        files: ['packages/**/package.json', 'packages/tower-generator/server/generators/**/templates/**/*']
         tasks: ['copy:packageJSON']
     build:
       client: {}


Perform GutterComponent DOM updates manually

--- src/gutter-component.coffee
+++ src/gutter-component.coffee
@@ -16,29 +16,14 @@
   measuredWidth: null

   render: ->
-    {presenter} = @props
-    @newState = presenter.state.gutter
-    @oldState ?= {lineNumbers: {}}
-
-    {scrollHeight, backgroundColor} = @newState
-
     div className: 'gutter',
-      div className: 'line-numbers', ref: 'lineNumbers', style:
-        height: scrollHeight
-        WebkitTransform: @getTransform()
-        backgroundColor: backgroundColor
-
-  getTransform: ->
-    {scrollTop} = @newState
-    "translate3d(0px, #{-scrollTop}px, 0px)"
+      div className: 'line-numbers', ref: 'lineNumbers'

   componentWillMount: ->
     @lineNumberNodesById = {}

   componentDidMount: ->
-    {@maxLineNumberDigits} = @newState
-    @appendDummyLineNumber()
-    @updateLineNumbers()
+    @updateSync()

     node = @getDOMNode()
     node.addEventListener 'click', @onClick
@@ -48,6 +33,24 @@
     @updateSync()

   updateSync: ->
+    @newState = @props.presenter.state.gutter
+    @oldState ?= {lineNumbers: {}}
+
+    @lineNumbersNode ?= @refs.lineNumbers.getDOMNode()
+    @appendDummyLineNumber() unless @dummyLineNumberNode?
+
+    if @newState.scrollHeight isnt @oldState.scrollHeight
+      @lineNumbersNode.style.height = @newState.scrollHeight + 'px'
+      @oldState.scrollHeight = @newState.scrollHeight
+
+    if @newState.scrollTop isnt @oldState.scrollTop
+      @lineNumbersNode.style['-webkit-transform'] = "translate3d(0px, #{-@newState.scrollTop}px, 0px)"
+      @oldState.scrollTop = @newState.scrollTop
+
+    if @newState.backgroundColor isnt @oldState.backgroundColor
+      @lineNumbersNode.style.backgroundColor = @newState.backgroundColor
+      @oldState.backgroundColor = @newState.backgroundColor
+
     if @newState.maxLineNumberDigits isnt @oldState.maxLineNumberDigits
       @updateDummyLineNumber()
       node.remove() for id, node of @lineNumberNodesById


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/controllable.coffee
+++ test/controllable.coffee
@@ -24,7 +24,8 @@
 test "should move player right while key is pressed", ->

-test "should move player up while key is pre"
+test "should move player up while key is pressed", ->
+

 module()


no cookies

no cookies

--- components/auth_modal/view.coffee
+++ components/auth_modal/view.coffee
@@ -1,6 +1,5 @@
 _ = require 'underscore'
 Backbone = require 'backbone'
-Cookies = require 'cookies-js'
 { parse } = require 'url'
 ModalView = require '../modal/view.coffee'
 Form = require '../mixins/form.coffee'


Add 'rhino' as a command.

Add 'rhino' as a command.

--- browser/frontend.coffee
+++ browser/frontend.coffee
@@ -1,6 +1,7 @@
 root = this

 # To be initialized on document load
+stdout = null
 user_input = null
 controller = null
 editor = null
@@ -59,6 +60,12 @@
     dataType: 'text'
     beforeSend: (jqXHR) -> jqXHR.overrideMimeType('text/plain; charset=x-user-defined')
     success: (data) -> class_cache['!javac'] = process_bytecode data
+  }
+  $.ajax "third_party/classes/com/sun/tools/script/shell/Main.class", {
+    type: 'GET'
+    dataType: 'text'
+    beforeSend: (jqXHR) -> jqXHR.overrideMimeType('text/plain; charset=x-user-defined')
+    success: (data) -> class_cache['!rhino'] = process_bytecode data
   }

 try_path = (path) ->
@@ -187,6 +194,8 @@
         controller.message "Could not load '#{fname}'.\n", 'error', true
     }

+  stdout = (str) -> controller.message str, '', true # noreprompt
+
   user_input = (n_bytes, resume) ->
     oldPrompt = controller.promptLabel
     controller.promptLabel = ''
@@ -221,13 +230,11 @@
     unless RELEASE?
       return "Usage: javac <source file>" unless args[0]?
       return compile_source args[0], cb
-    stdout = (str) -> controller.message str, '', true # noreprompt
     rs = new runtime.RuntimeState(stdout, user_input, read_classfile)
     # hack: use a special class name that won't clash with real ones
     jvm.run_class(rs, '!javac', args, -> controller.reprompt())
   java: (args, cb) ->
     return "Usage: java class [args...]" unless args[0]?
-    stdout = (str) -> controller.message str, '', true # noreprompt
     rs = new runtime.RuntimeState(stdout, user_input, read_classfile)
     jvm.run_class(rs, args[0], args[1..], -> controller.reprompt())
   javap: (args) ->
@@ -235,6 +242,9 @@
     raw_data = DoppioFile.load("#{args[0]}.class").read()
     return ["Could not find class '#{args[0]}'.",'error'] unless raw_data?
     disassembler.disassemble process_bytecode raw_data
+  rhino: (args, cb) ->
+    rs = new runtime.RuntimeState(stdout, user_input, read_classfile)
+    jvm.run_class(rs, '!rhino', args, -> controller.reprompt())
   list_cache: ->
     (name for name of raw_cache).join '\n'
   clear_cache: (args) ->


Take dpr into account for translateZ and perspective

--- framer/LayerStyle.coffee
+++ framer/LayerStyle.coffee
@@ -114,7 +114,7 @@
 		translate3d(
 			#{roundToZero(layer._properties.x * dpr)}px,
 			#{roundToZero(layer._properties.y * dpr)}px,
-			#{roundToZero(layer._properties.z)}px)
+			#{roundToZero(layer._properties.z * dpr)}px)
 		scale3d(
 			#{roundToZero(layer._properties.scaleX * layer._properties.scale)},
 			#{roundToZero(layer._properties.scaleY * layer._properties.scale)},
@@ -122,11 +122,11 @@
 		skew(#{roundToZero(layer._properties.skew)}deg,#{roundToZero(layer._properties.skew)}deg)
 		skewX(#{roundToZero(layer._properties.skewX)}deg)
 		skewY(#{roundToZero(layer._properties.skewY)}deg)
-		translateZ(#{roundToZero(layer._properties.originZ)}px)
+		translateZ(#{roundToZero(layer._properties.originZ * dpr)}px)
 		rotateX(#{roundToZero(layer._properties.rotationX)}deg)
 		rotateY(#{roundToZero(layer._properties.rotationY)}deg)
 		rotateZ(#{roundToZero(layer._properties.rotationZ)}deg)
-		translateZ(#{roundToZero(-layer._properties.originZ)}px)
+		translateZ(#{roundToZero(-layer._properties.originZ * dpr)}px)
 		"

 	webkitTransformForce2d: (layer) ->
@@ -152,7 +152,7 @@
 		"#{layer._properties.originX * 100}% #{layer._properties.originY * 100}%"

 	webkitPerspective: (layer) ->
-		"#{layer._properties.perspective}"
+		"#{layer._properties.perspective * Framer.CurrentContext.pixelMultiplier}"

 	webkitPerspectiveOrigin: (layer) ->
 		"#{layer._properties.perspectiveOriginX * 100}% #{layer._properties.perspectiveOriginY * 100}%"


- fixes #119 :  * added autorepair for localstorage (browserstore) where the files list would accidentally get deleted (the files list, not the files)  * the source of the issue was fixed a little way back (was a side effect in autosave)

- experimented with export to zip file of localstorage projects:
 * added jsZip lib
 * all the basic components work (fetch data, compress, parts of the ui)
 * deactivated for now, as Ui side needs a bit of cleanup, and the size of the generate zip files need to be checked
 * will likely be reused in some shape or the other in the upcomming store system refac

--- dropBoxStore.coffee
+++ dropBoxStore.coffee
@@ -6,7 +6,6 @@
   reqRes = require 'core/messaging/appReqRes'

   Project = require 'core/projects/project'
-

   class DropBoxLibrary extends Backbone.Collection
     """
@@ -32,7 +31,7 @@
       console.log "_____________"

   class DropBoxStore extends Backbone.Model
-    attributeNames: ['name','loggedIn']
+    attributeNames: ['name','loggedIn','isDataDumpAllowed']
     buildProperties @

     idAttribute: 'name'
@@ -41,6 +40,7 @@
       storeType: "Dropbox"
       tooltip:"Store to the Dropbox Cloud based storage: requires login"
       loggedIn:false
+      isDataDumpAllowed:false

     constructor:(options)->
       super options


A few small tweaks to the grammar.

A few small tweaks to the grammar.

--- grammars/dart.cson
+++ grammars/dart.cson
@@ -151,17 +151,17 @@
         'name': 'constant.numeric.dart'
       }
       {
-        'match': '\\b[A-Z][a-zA-Z]+\\b'
+        'match': '\\b_?[A-Z_0-9]+\\b'
+        'name': 'support.constant.dart'
+      }
+      {
+        'match': '\\b_?[A-Z][a-zA-Z]+\\b'
         'name': 'support.class.dart'
       }
       {
         'match': '(_?[a-z][a-zA-Z]+)(?=\\\(\\\[)'
         'name': 'entity.name.function.dart'
       }
-      {
-        'match': '\\b[A-Z_0-9]+\\b'
-        'name': 'support.constant.dart'
-      }
     ]
   'keywords':
     'patterns': [
@@ -210,7 +210,7 @@
         'name': 'keyword.operator.assignment.bitwise.dart'
       }
       {
-        'match': '(===?|!==?|<=?|>=?)'
+        'match': '(==|!=|<=?|>=?)'
         'name': 'keyword.operator.comparison.dart'
       }
       {
@@ -242,7 +242,7 @@
         'name': 'storage.modifier.dart'
       }
       {
-        'match': '\\b(?:void|bool|num|int|double|Dynamic|var|String)\\b'
+        'match': '\\b(?:void|bool|num|int|double|dynamic|var)\\b'
         'name': 'storage.type.primitive.dart'
       }
     ]


Removing operators until a better solution presents itself


Permit blank elements in 3-part title strings

--- grammars/roff.cson
+++ grammars/roff.cson
@@ -109,7 +109,7 @@
 	# Group of strings delimited with an arbitrary character
 	"3-part-title":
 		name: "string.3-part.other.roff"
-		match: '\\G[ \t]*?(.)((?:(?!\\1).)+)(\\1)((?:(?!\\1).)+)(\\1)((?:(?!\\1).)+)(\\1)'
+		match: '\\G[ \t]*(.)((?:(?!\\1).)*)(\\1)((?:(?!\\1).)*)(\\1)((?:(?!\\1).)*)(\\1)'
 		captures:
 			1: name: "punctuation.definition.string.begin.roff"
 			2: name: "entity.string.left.roff", patterns: [include: "#escapes"]


Highlight embedded fold markers and field-names

"Field name" refers very loosely to anything with a "[A-Z]\w+:" pattern.
Sequences like "Maintainer:" are highlighted in Vim, so they may as well
be highlighted in Atom too.

--- grammars/viml.cson
+++ grammars/viml.cson
@@ -41,6 +41,7 @@
 	main:
 		patterns: [
 			{include: "#todo"}
+			{include: "#folds"}
 			{include: "#comments"}
 			{include: "#pathname"}
 			{include: "#strings"}
@@ -81,22 +82,47 @@
 	comments:
 		patterns: [{

+			# Fully-commented line with a leading field-name
+			name: "comment.line.quotes.viml"
+			begin: '^\\s*(")(?=(\\s*[A-Z]\\w+)+:)'
+			end:   "((:))(.*)$"
+			contentName: "support.constant.field.viml"
+			beginCaptures:
+				1: name: "punctuation.definition.comment.viml"
+			endCaptures:
+				1: name: "support.constant.field.viml"
+				2: name: "punctuation.separator.key-value.colon.viml"
+				3: patterns: [include: "#commentInnards"]
+		},{
+
 			# Fully-commented line
 			name: "comment.line.quotes.viml"
-			patterns: [include: "#todo"]
 			begin: '^\\s*(")'
 			end:   '$'
 			beginCaptures:
-				1: name: "punctuation.definition.comment.vim"
+				1: name: "punctuation.definition.comment.viml"
+			patterns: [include: "#commentInnards"]
 		},{
 			# Trailing comment, no closing quote
 			name: "comment.inline.quotes.viml"
-			patterns: [include: "#todo"]
+			patterns: [include: "#commentInnards"]
 			begin: '(?<!^)\\s*(")(?=[^\\n"]*$)'
 			end:   "$"
 			beginCaptures:
 				1: name: "punctuation.definition.comment.vim"
 		}]
+
+	commentInnards:
+		patterns: [
+			{include: "#folds"}
+			{include: "#todo"}
+		]
+
+
+	# Fold markers, with possible labels
+	folds:
+		name: "entity.name.fold.heading.viml"
+		match: "(?<=\").*?{{{\\d*|(?<=\")\\s*}}}"

 	# Embedded interpreter directive


Fix for Twitter change.

Fix for Twitter change.

--- config/social/jkn2-twitter.coffee
+++ config/social/jkn2-twitter.coffee
@@ -11,7 +11,7 @@
   regexps: "^https?://([a-z]+\\.)?twitter.com/?"
   selectors: do ->
     selectors = [ "div.new-tweets-bar" ]
-    for context in [ "div.home-stream", "div.profile-stream", "div.list-stream", "div.search-stream" ]
+    for context in [ "div.home-stream", "div.profile-stream", "div.list-stream", "div.search-stream", "li.stream-item" ]
       for content in [ "div.original-tweet", "li.conversation-root", "li.js-simple-tweet", "div.QuoteTweet" ]
         selectors.push "#{context} #{content}"
     selectors


submit chart form on change

submit chart form on change

--- app/assets/javascripts/chart_generation.coffee
+++ app/assets/javascripts/chart_generation.coffee
@@ -13,6 +13,10 @@
   $(".btn-group#chart-list ul.dropdown-menu li#sugar_level").click ->
     drawCharts('sugar_level', 'Sugar level')

+  $('form#new_chart_form').on('change', ->
+    $(this).submit();
+  )
+
 drawCharts = (chartData, chartTitle) ->
   $.getJSON("/dashboard.json", (data) ->
     new Chart($("canvas#chart").get(0).getContext("2d")).Line(data[chartData])


Output failed tests from grunt

Output failed tests from grunt

--- tasks/bootcamp.coffee
+++ tasks/bootcamp.coffee
@@ -40,6 +40,7 @@
               return true

           else
+            grunt.log.warn tests.specs
             showChecklist tests.checklist
             grunt.fail.warn tests.stats
             return false


Extract more keycode constants

Extract more keycode constants

--- app/assets/javascripts/photo_overlay.js.coffee
+++ app/assets/javascripts/photo_overlay.js.coffee
@@ -1,3 +1,6 @@
+LEFT_ARROW_KEY_CODE = 37
+RIGHT_ARROW_KEY_CODE = 39
+
 class @PhotoOverlay extends Overlay
   constructor: (selector, options) ->
     @selector = selector
@@ -16,8 +19,8 @@
     $('body').on 'keyup', (event) ->
       return if window.isFormElement(event.target)
       switch event.which
-        when 37 then self.previous()
-        when 39 then self.next()
+        when LEFT_ARROW_KEY_CODE then self.previous()
+        when RIGHT_ARROW_KEY_CODE then self.next()

     @dom.el.on 'click', @dom.nextButtonSelector, (event) ->
       self.next()


Tweak #2783.

Tweak #2783.

Tiny tweaks...

1. The test is clearer like this.
2. Use a more CoffeeScript-ish approach to sub-string.

--- content_scripts/link_hints.coffee
+++ content_scripts/link_hints.coffee
@@ -818,11 +818,11 @@
     nodeName = element.nodeName.toLowerCase()

     if nodeName == "input"
-      if element.labels?.length > 0
+      if element.labels? and element.labels.length > 0
         linkText = element.labels[0].textContent.trim()
         # Remove trailing ":" commonly found in labels.
         if linkText[linkText.length-1] == ":"
-          linkText = linkText.substr 0, linkText.length-1
+          linkText = linkText[...linkText.length-1]
         showLinkText = true
       else if element.getAttribute("type")?.toLowerCase() == "file"
         linkText = "Choose File"


set up drag events on new links

set up drag events on new links

--- src/main/webapp/lib/views/MapMarkerView.coffee
+++ src/main/webapp/lib/views/MapMarkerView.coffee
@@ -92,8 +92,6 @@
     @latLng = @marker.getPosition()
     @marker.setTitle @_getTitle()
     $a.map.panTo(@latLng)
-    console.log @model
-    console.log @latLng
     @model.updatePosition(@latLng)

   ################# The following handles the show and hide of node layers


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/animation.coffee
+++ test/animation.coffee
@@ -17,6 +17,8 @@
   milliseconds = 300

   setTimeout ->
+    animation.update()
+
     equals animation.frames[0], 1

     start()


refactored statds api class

refactored statds api class

--- src/api/statsd.coffee
+++ src/api/statsd.coffee
@@ -147,6 +147,7 @@

 convertToRequiredFormat = (raw, requiredFormat) ->
   data = []
+  i = 0
   switch requiredFormat
     when "retrieveAverageLoadTimePerHour" then _.forEach raw.data, (item) ->
       data.push


using deepEqual where I would have like to before, if I had known about it

--- test/arguments.coffee
+++ test/arguments.coffee
@@ -43,10 +43,10 @@
 #### Parameter List Features

 test "splats", ->
-  eq 2, (((splat...) -> splat) 0,1,2)[2]
-  eq 3, (((_, _, splat...) -> splat) 0,1,2,3)[1]
-  eq 1, (((splat..., _, _) -> splat) 0,1,2,3)[1]
-  eq 2, (((_, _, splat..., _) -> splat) 0,1,2,3)[0]
+  deepEqual [0, 1, 2], (((splat...) -> splat) 0, 1, 2)
+  deepEqual [2, 3], (((_, _, splat...) -> splat) 0, 1, 2, 3)
+  deepEqual [0, 1], (((splat..., _, _) -> splat) 0, 1, 2, 3)
+  deepEqual [2], (((_, _, splat..., _) -> splat) 0, 1, 2, 3)

 test "@-parameters: automatically assign an argument's value to a property of the context", ->
   nonce = {}


Show "buffer in conflict" alert after edit session is visible

--- src/app/editor.coffee
+++ src/app/editor.coffee
@@ -386,9 +386,6 @@

     @activeEditSession = @editSessions[index]

-    if @activeEditSession.buffer.isInConflict()
-      @showBufferConflictAlert(@activeEditSession)
-
     @activeEditSession.on "buffer-contents-change-on-disk", =>
       @showBufferConflictAlert(@activeEditSession)

@@ -397,6 +394,9 @@

     @trigger 'editor-path-change'
     @renderWhenAttached()
+
+    if @attached and @activeEditSession.buffer.isInConflict()
+      @showBufferConflictAlert(@activeEditSession)

   showBufferConflictAlert: (editSession) ->
     message = editSession.getPath()


collision with regular bounds

collision with regular bounds

--- test/collision.coffee
+++ test/collision.coffee
@@ -57,7 +57,7 @@
   start = Point(0, 0)
   direction = Point(1, 0)

-  p Collision.rayRectangle start, rect.bounds()
+  p1 = Collision.rayRectangle start, rect.bounds()

 module()


collision with regular bounds

collision with regular bounds

--- test/collision.coffee
+++ test/collision.coffee
@@ -61,4 +61,6 @@

   equals p1.y, 0
   equals p1.x, 20
+
+
 module()


collision with regular bounds

collision with regular bounds

--- test/collision.coffee
+++ test/collision.coffee
@@ -57,7 +57,7 @@
   start = Point(0, 0)
   direction = Point(1, 0)

-  p1 = Collision.rayRectangle start, direction, bounds
+  p1 = Collision.rayRectangle start, direction, rect.bounds()

   equals p1.y, 0
   equals p1.x, 20


Fix misplaced input transaction reset

Fix misplaced input transaction reset

--- lib/motions/general-motions.coffee
+++ lib/motions/general-motions.coffee
@@ -37,8 +37,9 @@
     @complete = true

 class MoveLeft extends Motion
-  @vimState.resetInputTransactions()
-  execute: (count=1) ->
+
+  execute: (count=1) ->
+    @vimState.resetInputTransactions()
     _.times count, =>
       {row, column} = @editor.getCursorScreenPosition()
       @editor.moveCursorLeft() if column > 0


sending all events to particles

sending all events to particles

--- test/timed_events.coffee
+++ test/timed_events.coffee
@@ -8,7 +8,7 @@

   4.times ->
     gameObject.trigger "update", 1
-
+    gameObject.trigger ""

 # Clear out the module
 module()


Process batches of the import CSV one after another

Start the HTTP request for processing next batches only after the
previous batch has completed.

A con of this approach is that the user needs to keep their browser open
for later parts of the large CSV to be processed, but this is better
than a higher chance of requests timing out.

--- app/assets/javascripts/admin/product_import/controllers/import_form_controller.js.coffee
+++ app/assets/javascripts/admin/product_import/controllers/import_form_controller.js.coffee
@@ -54,16 +54,24 @@
     total = ams_data.item_count
     $scope.chunks = Math.ceil(total / $scope.batchSize)

-    i = 0
+    # Process only the first batch.
+    $scope.processBatch($scope.step, 0, $scope.chunks)

-    while i < $scope.chunks
-      start = (i * $scope.batchSize) + 1
-      end = (i + 1) * $scope.batchSize
-      if $scope.step == 'import'
-        $scope.processImport(start, end)
-      if $scope.step == 'save'
-        $scope.processSave(start, end)
-      i++
+  $scope.processBatch = (step, batchIndex, batchCount) ->
+    start = (batchIndex * $scope.batchSize) + 1
+    end = (batchIndex + 1) * $scope.batchSize
+    withNextBatch = batchIndex + 1 < batchCount
+
+    promise = if step == 'import'
+      $scope.processImport(start, end)
+    else if step == 'save'
+      $scope.processSave(start, end)
+
+    processNextBatch = ->
+      $scope.processBatch(step, batchIndex + 1, batchCount)
+
+    # Process next batch whether or not processing of the current batch succeeds.
+    promise.then(processNextBatch, processNextBatch) if withNextBatch

   $scope.processImport = (start, end) ->
     $http(


added orchestrations

added orchestrations

--- src/middleware/stats.coffee
+++ src/middleware/stats.coffee
@@ -22,7 +22,24 @@
     if ctx.routes?
       for route in ctx.routes
         sdc.increment domain + '.Channels.' + ctx.authorisedChannel._id + '.nonPrimaryRoutes.' + route.name # Per non-primary route
-        sdc.increment domain + '.Channels.' + ctx.authorisedChannel._id + '.nonPrimaryRoutes.' + route.name + '.StatusCodes' + route.response.status # Per route response status
+        sdc.increment domain + '.Channels.' + ctx.authorisedChannel._id + '.nonPrimaryRoutes.' + route.name + '.StatusCodes.' + route.response.status # Per route response status
+
+        if route.orchestrations?
+          for orchestration in route.orchestrations
+            sdc.increment domain + '.Channels.' + ctx.authorisedChannel._id + '.nonPrimaryRoutes.' + route.name + '.orchestrations.' + orchestration.name
+            sdc.increment domain + '.Channels.' + ctx.authorisedChannel._id + '.nonPrimaryRoutes.' + route.name + '.orchestrations.' + orchestration.name + '.StatusCodes.' + orchestration.Status
+            sdc.increment domain + '.Channels.' + ctx.authorisedChannel._id + '.nonPrimaryRoutes.' + route.name + '.StatusCodes.' + route.response.status + '.orchestrations.' + orchestration.name
+            sdc.increment domain + '.Channels.' + ctx.authorisedChannel._id + '.nonPrimaryRoutes.' + route.name + '.StatusCodes.' + route.response.status + '.orchestrations.' + orchestration.name + '.StatusCodes.' + orchestration.Status
+
+
+    if ctx.mediatorResponse?
+      if ctx.mediatorResponse.orchestrations?
+        for orchestration in ctx.mediatorResponse.orchestrations
+          orchestrationStatus = orchestration.Status
+          sdc.increment domain + '.Channels.' + ctx.authorisedChannel._id + '.orchestrations.' + orchestration.name
+          sdc.increment domain + '.Channels.' + ctx.authorisedChannel._id + '.orchestrations.' + orchestration.name + '.StatusCodes.' + orchestrationStatus
+          sdc.increment domain + '.Channels.' + ctx.authorisedChannel._id + '.Statuses.' + transactionStatus + '.orchestrations.' + orchestration.name
+          sdc.increment domain + '.Channels.' + ctx.authorisedChannel._id + '.Statuses.' + transactionStatus + '.orchestrations.' + orchestration.name + '.StatusCodes.' + orchestrationStatus

   catch error
     logger.error error, done
@@ -40,14 +57,27 @@
     #Collect stats for non-primary routes
     if ctx.routes?
       for route in ctx.routes
-        sdc.timing domain + '.Channels.' + ctx.authorisedChannel._id + '.nonPrimaryRoutes.' + route.name, timer # Per Channel
-        sdc.timing domain + '.Channels.' + ctx.authorisedChannel._id + '.nonPrimaryRoutes.' + route.name + '.StatusCodes' + route.response.status, timer # Per Channel
+        sdc.timing domain + '.Channels.' + ctx.authorisedChannel._id + '.nonPrimaryRoutes.' + route.name, timer
+        sdc.timing domain + '.Channels.' + ctx.authorisedChannel._id + '.nonPrimaryRoutes.' + route.name + '.StatusCodes' + route.response.status, timer
+
+        if route.orchestrations?
+          for orchestration in route.orchestrations
+            orchestratrionDuration = orchestration.response.timestamp - orchestration.request.timestamp
+            orchestrationStatus = orchestration.Status
+            sdc.timing domain + '.Channels.' + ctx.authorisedChannel._id + '.nonPrimaryRoutes.' + route.name + '.orchestrations.' + orchestration.name, orchestratrionDuration
+            sdc.timing domain + '.Channels.' + ctx.authorisedChannel._id + '.nonPrimaryRoutes.' + route.name + '.orchestrations.' + orchestration.name + '.StatusCodes.' + orchestrationStatus , orchestratrionDuration
+            sdc.timing domain + '.Channels.' + ctx.authorisedChannel._id + '.nonPrimaryRoutes.' + route.name + '.StatusCodes' + route.response.status + '.orchestrations.' + orchestration.name, orchestratrionDuration
+            sdc.timing domain + '.Channels.' + ctx.authorisedChannel._id + '.nonPrimaryRoutes.' + route.name + '.StatusCodes' + route.response.status + '.orchestrations.' + orchestration.name + '.StatusCodes.' + orchestrationStatus , orchestratrionDuration
+
+

     if ctx.mediatorResponse?
       if ctx.mediatorResponse.orchestrations?
-        for ochestration in ctx.mediatorResponse.orchestrations
-          orchestratrionDuration = ochestration.response.timestamp - ochestration.request.timestamp
-          sdc.timing domain + '.Channels.' + ctx.authorisedChannel._id + '.' + ochestration.name, orchestratrionDuration
+        for orchestration in ctx.mediatorResponse.orchestrations
+          orchestratrionDuration = orchestration.response.timestamp - orchestration.request.timestamp
+          orchestrationStatus = orchestration.Status
+          sdc.timing domain + '.Channels.' + ctx.authorisedChannel._id + '.orchestrations.' + orchestration.name, orchestratrionDuration
+          sdc.timing domain + '.Channels.' + ctx.authorisedChannel._id + '.orchestrations.' + orchestration.name + '.StatusCodes.' + orchestrationStatus , orchestratrionDuration

   catch error


social: don't string concat a non-string

--- workers/social/lib/social/main.coffee
+++ workers/social/lib/social/main.coffee
@@ -1,122 +1,122 @@
-log = -> logger.info arguments...
-
-{argv} = require 'optimist'
-
-{exec} = require 'child_process'
-{extend} = require 'underscore'
-
-process.on 'uncaughtException', (err)->
-  exec './beep'
-  console.log err, err?.stack
-
-Bongo = require 'bongo'
-Broker = require 'broker'
-
-KONFIG = require('koding-config-manager').load("main.#{argv.c}")
-Object.defineProperty global, 'KONFIG', value: KONFIG
-{mq, mongo, email, social} = KONFIG
-
-mongo += '?auto_reconnect'
-
-mqOptions = extend {}, mq
-mqOptions.login = social.login if social?.login?
-
-broker = new Broker mqOptions
-
-processMonitor = (require 'processes-monitor').start
-  name : "Social Worker #{process.pid}"
-  stats_id: "worker.social." + process.pid
-  interval : 30000
-  librato: KONFIG.librato
-  limit_hard  :
-    memory   : 300
-    callback : (name,msg,details)->
-      console.log "[SOCIAL WORKER #{name}] Using excessive memory, exiting."
-      process.exit()
-  die :
-    after: "non-overlapping, random, 3 digits prime-number of minutes"
-    middleware : (name,callback) -> koding.disconnect callback
-    middlewareTimeout : 15000
-  # WE'RE NOT SURE IF THIS SOFT LIMIT WAS A GOOD IDEA OR NOT
-  # limit_soft:
-  #   memory: 200
-  #   callback: (name, msg, details) ->
-  #     console.log "[SOCIAL WORKER #{name}] Using too much memory, accepting no more new jobs."
-  #     process.send?({pid: process.pid, exiting: yes})
-  #     koding.disconnect()
-  #     setTimeout ->
-  #       process.exit()
-  #     , 20000
-  # DISABLED TO TEST MEMORY LEAKS
-  # die :
-  #   after: "non-overlapping, random, 3 digits prime-number of minutes"
-  #   middleware : (name,callback) -> koding.disconnect callback
-  #   # TEST AMQP WITH THIS CODE. IT THROWS THE CHANNEL ERROR.
-  #   # middleware : (name,callback) ->
-  #   #   koding.disconnect ->
-  #   #     console.log "[SOCIAL WORKER #{name}] is reached end of its life, will die in 10 secs."
-  #   #     setTimeout ->
-  #   #       callback null
-  #   #     ,10*1000
-  #   middlewareTimeout : 15000
-  # toobusy:
-  #   interval: 10000
-  #   callback: ->
-  #     console.log "[SOCIAL WORKER #{name}] I'm too busy, accepting no more new jobs."
-  #     process.send?({pid: process.pid, exiting: yes})
-  #     koding.disconnect()
-  #     setTimeout ->
-  #       process.exit()
-  #      , 20000
-
-koding = new Bongo
-  root        : __dirname
-  mongo       : mongo
-  models      : './models'
-  resourceName: social.queueName
-  mq          : broker
-  fetchClient :(sessionToken, context, callback)->
-    [callback, context] = [context, callback] unless callback
-    context ?= 'koding'
-    callback ?= ->
-    koding.models.JUser.authenticateClient sessionToken, context, (err, account)->
-      if err
-        koding.emit 'error', err
-      else
-        callback {sessionToken, connection:delegate:account}
-
-koding.on 'authenticateUser', (client, callback)->
-  {delegate} = client.connection
-  callback delegate
-
-# koding.on 'auth', (exchange, sessionToken)->
-#   koding.fetchClient sessionToken, (client)->
-#     {delegate} = client.connection
-
-#     # if delegate instanceof koding.models.JAccount
-#     #   koding.models.JAccount.emit "AccountAuthenticated", delegate
-
-#     koding.handleResponse exchange, 'changeLoggedInState', [delegate]
-koding.connect ->
-  if KONFIG.misc?.claimGlobalNamesForUsers
-    require('./models/account').reserveNames console.log
-
-  if KONFIG.misc?.updateAllSlugs
-    require('./traits/slugifiable').updateSlugsByBatch 100, [
-      require './models/tag'
-      require './models/app'
-      require './models/messages/codesnip'
-      require './models/messages/discussion'
-      require './models/messages/tutorial'
-    ]
-
-  if KONFIG.misc?.debugConnectionErrors then
-    # console.log 'ffaafafafaf'
-    # TEST AMQP WITH THIS CODE. IT THROWS THE CHANNEL ERROR.
-    # koding.disconnect ->
-    #   console.log "[SOCIAL WORKER #{name}] is reached end of its life, will die in 10 secs."
-    #   setTimeout ->
-    #     process.exit()
-    #   ,10*1000
-
-console.log "Koding Social Worker #{process.pid} has started."
+log = -> logger.info arguments...
+
+{argv} = require 'optimist'
+
+{exec} = require 'child_process'
+{extend} = require 'underscore'
+
+process.on 'uncaughtException', (err)->
+  exec './beep'
+  console.log err, err?.stack
+
+Bongo = require 'bongo'
+Broker = require 'broker'
+
+KONFIG = require('koding-config-manager').load("main.#{argv.c}")
+Object.defineProperty global, 'KONFIG', value: KONFIG
+{mq, mongo, email, social} = KONFIG
+
+mongo += '?auto_reconnect'  if 'string' is typeof mongo
+
+mqOptions = extend {}, mq
+mqOptions.login = social.login if social?.login?
+
+broker = new Broker mqOptions
+
+processMonitor = (require 'processes-monitor').start
+  name : "Social Worker #{process.pid}"
+  stats_id: "worker.social." + process.pid
+  interval : 30000
+  librato: KONFIG.librato
+  limit_hard  :
+    memory   : 300
+    callback : (name,msg,details)->
+      console.log "[SOCIAL WORKER #{name}] Using excessive memory, exiting."
+      process.exit()
+  die :
+    after: "non-overlapping, random, 3 digits prime-number of minutes"
+    middleware : (name,callback) -> koding.disconnect callback
+    middlewareTimeout : 15000
+  # WE'RE NOT SURE IF THIS SOFT LIMIT WAS A GOOD IDEA OR NOT
+  # limit_soft:
+  #   memory: 200
+  #   callback: (name, msg, details) ->
+  #     console.log "[SOCIAL WORKER #{name}] Using too much memory, accepting no more new jobs."
+  #     process.send?({pid: process.pid, exiting: yes})
+  #     koding.disconnect()
+  #     setTimeout ->
+  #       process.exit()
+  #     , 20000
+  # DISABLED TO TEST MEMORY LEAKS
+  # die :
+  #   after: "non-overlapping, random, 3 digits prime-number of minutes"
+  #   middleware : (name,callback) -> koding.disconnect callback
+  #   # TEST AMQP WITH THIS CODE. IT THROWS THE CHANNEL ERROR.
+  #   # middleware : (name,callback) ->
+  #   #   koding.disconnect ->
+  #   #     console.log "[SOCIAL WORKER #{name}] is reached end of its life, will die in 10 secs."
+  #   #     setTimeout ->
+  #   #       callback null
+  #   #     ,10*1000
+  #   middlewareTimeout : 15000
+  # toobusy:
+  #   interval: 10000
+  #   callback: ->
+  #     console.log "[SOCIAL WORKER #{name}] I'm too busy, accepting no more new jobs."
+  #     process.send?({pid: process.pid, exiting: yes})
+  #     koding.disconnect()
+  #     setTimeout ->
+  #       process.exit()
+  #      , 20000
+
+koding = new Bongo
+  root        : __dirname
+  mongo       : mongo
+  models      : './models'
+  resourceName: social.queueName
+  mq          : broker
+  fetchClient :(sessionToken, context, callback)->
+    [callback, context] = [context, callback] unless callback
+    context ?= 'koding'
+    callback ?= ->
+    koding.models.JUser.authenticateClient sessionToken, context, (err, account)->
+      if err
+        koding.emit 'error', err
+      else
+        callback {sessionToken, connection:delegate:account}
+
+koding.on 'authenticateUser', (client, callback)->
+  {delegate} = client.connection
+  callback delegate
+
+# koding.on 'auth', (exchange, sessionToken)->
+#   koding.fetchClient sessionToken, (client)->
+#     {delegate} = client.connection
+
+#     # if delegate instanceof koding.models.JAccount
+#     #   koding.models.JAccount.emit "AccountAuthenticated", delegate
+
+#     koding.handleResponse exchange, 'changeLoggedInState', [delegate]
+koding.connect ->
+  if KONFIG.misc?.claimGlobalNamesForUsers
+    require('./models/account').reserveNames console.log
+
+  if KONFIG.misc?.updateAllSlugs
+    require('./traits/slugifiable').updateSlugsByBatch 100, [
+      require './models/tag'
+      require './models/app'
+      require './models/messages/codesnip'
+      require './models/messages/discussion'
+      require './models/messages/tutorial'
+    ]
+
+  if KONFIG.misc?.debugConnectionErrors then
+    # console.log 'ffaafafafaf'
+    # TEST AMQP WITH THIS CODE. IT THROWS THE CHANNEL ERROR.
+    # koding.disconnect ->
+    #   console.log "[SOCIAL WORKER #{name}] is reached end of its life, will die in 10 secs."
+    #   setTimeout ->
+    #     process.exit()
+    #   ,10*1000
+
+console.log "Koding Social Worker #{process.pid} has started."


add missing underscore require

add missing underscore require

this was missed because local testing happened in the notebook and its
version of underscore was picked up

--- bokehjs/src/coffee/ticking/fixed_ticker.coffee
+++ bokehjs/src/coffee/ticking/fixed_ticker.coffee
@@ -1,3 +1,4 @@
+_ = require "underscore"
 AbstractTicker = require "./abstract_ticker"

 class FixedTicker extends AbstractTicker.Model


Scene: Add disable() with same usage as enable()

--- engine/scene.coffee
+++ engine/scene.coffee
@@ -382,6 +382,15 @@
             @physics_enabled = true
         return

+    disable: (items...) ->
+        {render, physics, all} =
+            @_ensure_items items, ['render', 'physics', 'all']
+        if render or all
+            @enabled = false
+        if physics or all
+            @physics_enabled = false
+        return
+
     instance_probe: ->
         if @background_probe
             return @background_probe


Fixed toggling columns when BulkChange is avtivated on a grid #417

--- wzk/ui/grid/GridColumnsManager.coffee
+++ wzk/ui/grid/GridColumnsManager.coffee
@@ -50,7 +50,7 @@
     @storageKey = @composeStorageKey()
     @filteringText = ''
     @filteringTextEl = null
-    @storage = new wzk.stor.LocalStorage @dom.getWindow()['localStorage']
+    @storage = new wzk.stor.LocalStorage(@dom.getWindow()['localStorage'])

   ###*
     @param {Element} el
@@ -66,7 +66,8 @@
     @cols = @getOrCreateState()

     @filterStates = @getInitialFilterStates()
-    @filteringTextEl = @dom.getElement([wzk.ui.grid.GridColumnsManager.CLS.COLUMNS_FILTERING_TEXT, @grid.table.id].join('-'))
+    @filteringTextEl = @dom.getElement(
+      [wzk.ui.grid.GridColumnsManager.CLS.COLUMNS_FILTERING_TEXT, @grid.table.id].join('-'))
     unless @filteringTextEl
       @dom.getWindow().console.warn('Missing filtering text element for #' + @el.id)
       return
@@ -252,7 +253,9 @@
     @protected
   ###
   showOrHideHeader: =>
-    (goog.style.setElementShown(@headers[i], @cols[checkbox['name']]) for checkbox, i in @checkboxes when @headers[i]?)
+    for header in @headers
+      if @cols[header.className]?
+        goog.style.setElementShown(header, @cols[header.className])

   ###*
     @protected


fix #247

fix #247

--- src/modules/keyboard.coffee
+++ src/modules/keyboard.coffee
@@ -47,10 +47,10 @@
     this.addHotkey([dom.KEYS.DELETE, dom.KEYS.BACKSPACE], (range, hotkey) =>
       if range?
         if range.start != range.end
-          @quill.deleteText(range.start, range.end)
+          @quill.deleteText(range.start, range.end, Quill.sources.USER)
         else
           start = if (hotkey.key == dom.KEYS.BACKSPACE) then range.start - 1 else range.start
-          @quill.deleteText(start, start + 1)
+          @quill.deleteText(start, start + 1, Quill.sources.USER)
       return false
     )


updates

updates

--- index.coffee
+++ index.coffee
@@ -113,7 +113,12 @@
 	 					else
 	 						user = existingUser[0]
 	 						user.logged_out_at = Date.now()
+
 	 						user.save () ->
+	 							pubnub.publish
+				                   channel: user.identifier
+				                   message: "logout"
+
 	 							v0.responses.response res

 handleBrowserLogout = (req, res) ->


let the extension know we're good to go via postmessage

--- index.coffee
+++ index.coffee
@@ -133,7 +133,7 @@
 	 					else
 	 						#user already exists.  Let's use that.
 	 						req.$session.user = existingUser[0]
-	 						v0.responses.respond res
+	 						v0.responses.respond res, "<script type='text/javascript'>addEventListener('message', function(e) { e.source.postMessage({auth: true}, e.origin); });</script>"


Test loading in server environment

Test loading in server environment

--- index.coffee
+++ index.coffee
@@ -113,7 +113,7 @@
     'voxel-pickaxe': {}
     'voxel-daylight': {ambientColor: 0x888888, directionalColor: 0xffffff}

-    'voxel-land': {populateTrees: true}
+    #'voxel-land': {populateTrees: true} # TODO: server
     'voxel-clientmc': {url: 'ws://localhost:1234', onDemand: true}

     'voxel-console': {}
@@ -128,7 +128,7 @@
     'voxel-gamemode': {}
     'voxel-walk': {}
     'voxel-inventory-hotbar': {inventorySize:10}
-    'voxel-inventory-dialog': {}
+    #'voxel-inventory-dialog': {} # TODO: server
     'voxel-reach': { reachDistance: 8 }
     # left-click hold to mine
     'voxel-mine':


remove unused file

remove unused file

--- index.coffee
+++ index.coffee
@@ -1,14 +0,0 @@
-Tracker = require "./lib/tracker"
-Project = require "./lib/project"
-
-tracker = new Tracker "978f492d5d2a080ced6e8fbb801700fc"
-
-tracker.projects
-  failure: (error) ->
-    console.log error
-  success: (jsonString) ->
-    list = for options in JSON.parse jsonString
-      new Project options
-
-    for project in list when project.name is 'Core'
-      project.stories()