Re-enabled infinite scrolling.

Re-enabled infinite scrolling.

--- client/results.coffee
+++ client/results.coffee
@@ -1,9 +1,13 @@
+searchLimitIncreasing = false
+
 Deps.autorun ->
   # Every time search query is changed, we reset counts
   # (We don't want to reset counts on currentSearchLimit change)
   Session.get 'currentSearchQuery'
   Session.set 'currentSearchQueryCountPublications', 0
   Session.set 'currentSearchQueryCountPeople', 0
+
+  searchLimitIncreasing = false

 Deps.autorun ->
   Session.set 'currentSearchQueryReady', false
@@ -42,16 +46,21 @@

   $(@findAll '#pub-date').val($(@findAll '#date-range').slider('values', 0) + ' - ' + $(@findAll '#date-range').slider('values', 1))

+  if Session.get 'currentSearchQueryReady'
+    searchLimitIncreasing = false
+
 Template.results.created = ->
-  #$(window).on 'scroll.results', ->
-  #  if $(document).height() - $(window).scrollTop() <= 2 * $(window).height()
-  #    subscribeToNext 10
+  $(window).on 'scroll.results', ->
+    if $(document).height() - $(window).scrollTop() <= 2 * $(window).height()
+      increaseSearchLimit 10

 Template.results.destroyed = ->
   $(window).off 'scroll.results'

-subscribeToNext = (pageSize) ->
-  Session.set 'currentSearchLimit', (Session.get('currentSearchLimit') or 0) + pageSize
+increaseSearchLimit = (pageSize) ->
+  if not searchLimitIncreasing
+    searchLimitIncreasing = true
+    Session.set 'currentSearchLimit', (Session.get('currentSearchLimit') or 0) + pageSize

 Template.results.publications = ->
   if not Session.get('currentSearchLimit') or not Session.get('currentSearchQuery')


Explain what might cause the handshake to time out

--- client/tx/join.coffee
+++ client/tx/join.coffee
@@ -73,7 +73,9 @@
   # Sign message (with known private key, or with dialog asking user to do this
   # locally)
   sign_message bob, terms, iferr display_error, (sig) ->
-    ack_timer = setTimeout (-> display_error 'Handshake verification response timed out'), ACK_TIMEOUT
+    ack_timer = setTimeout (->
+      display_error 'Handshake verification response timed out. The other party might have closed the page.'
+    ), ACK_TIMEOUT
     handshake_reply channel, { pub: bob.pub, proof: sig, script }, (err) ->
       clearTimeout ack_timer
       return display_error err if err?


Bumped version

Bumped version

--- client/version.coffee
+++ client/version.coffee
@@ -1 +1 @@
-@version = '2.15.0'
+@version = '2.15.1'


commit

commit

--- lib/build/build-state-view.coffee
+++ lib/build/build-state-view.coffee
@@ -54,11 +54,14 @@
     @socket = io(@server)

     @socket.on 'connect', =>
-      console.log "socket connected."
+      console.log "socket connected. #{@access_token}"
       @socket.emit 'bind', @access_token

     @socket.on 'disconnect', ->
       console.log "socket disconnected."
+
+    @socket.on 'reconnect', ->
+      console.log "socket reconnect."

     @socket.on 'error', (err) ->
       console.log "socket error: #{err}"
@@ -91,7 +94,6 @@
   showState: (task) ->
     @loading.show()

-    console.log task.state
     @loading.hide() if task.state == 'failed'
     switch task.state
       when 'complete'


Whoops, fix typo.

Whoops, fix typo.

--- client/entry/ImageBlock.coffee
+++ client/entry/ImageBlock.coffee
@@ -30,7 +30,7 @@
                 is_pinned   = JSON.parse(el.dataset.pinned)

                 image_resize_fns.push ->
-                    if image_block.datset.is_loading is 'true'
+                    if image_block.dataset.is_loading is 'true'
                         return

                     height = content_el.offsetWidth / image_block.dataset.aspect_ratio


Allow tags to be saved on edit

Allow tags to be saved on edit

A consequence of PR #241 was that tags were no longer saved at all, because getBlogTags() received tags as a string even when there were several of them, causing them to be returned as an empty array.

--- client/views/admin/edit.coffee
+++ client/views/admin/edit.coffee
@@ -83,7 +83,7 @@

   attrs =
     title: $('[name=title]', $form).val()
-    tags: getBlogTags($('[name=tags]', $form).val())
+    tags: getBlogTags($('[name=tags]', $form).val().split(','))
     slug: slug
     description: $('[name=description]', $form).val()
     body: body


load typeahead and tagsinput to blogAdminEdit Template

--- client/views/admin/edit.coffee
+++ client/views/admin/edit.coffee
@@ -2,6 +2,27 @@
 # post that has not been saved yet.
 getPost = ->
   (Post.first Session.get('postId')) or {}
+
+getTags = ->
+  _.reduce(Post.all(), (datums, post) ->
+    _.each post.tags, (tag) ->
+      if !_.contains datums, tag
+        datums.push tag
+    datums
+  , [])
+
+# Find tags using typeahead
+substringMatcher = (strs) ->
+  (q, cb) ->
+    matches = []
+    pattern = new RegExp q, 'i'
+
+    _.each strs, (ele) ->
+      if pattern.test ele
+        matches.push
+          val: ele
+
+    cb matches

 # Add new lines after block elements for HTML mode
 prettyHtml = (html) ->
@@ -74,6 +95,20 @@
     $html.val prettyHtml(editor.scrubbed())
     $html.height $editable.height()

+Template.blogAdminEdit.rendered = ->
+  $('input[data-role="tagsinput"]').tagsinput()
+  $('input[data-role="tagsinput"]').tagsinput('input').typeahead(
+    highlight: true,
+    hint: false
+  ,
+    name: 'tags'
+    displayKey: 'val'
+    source: substringMatcher getTags()
+  ).bind('typeahead:selected', $.proxy (obj, datum) ->
+    this.tagsinput('add', datum.val)
+    this.tagsinput('input').typeahead('val', '')
+  , $('input[data-role="tagsinput"]'))
+
 Template.blogAdminEdit.helpers
   post: ->
     getPost()


Properly retrieve individual legislation by id.

--- routes/api/v1/legislation.coffee
+++ routes/api/v1/legislation.coffee
@@ -37,7 +37,7 @@

   # GET /api/v1/legislation/:id - Get all info on individual legilsation
   app.get '/api/v1/legislation/:legislation', (req, res) ->
-    res.json legislation
+    res.json req.legislation

   # GET /api/v1/legislation/:type/:number - Retrieve a legilation type
   # by number


Add some events.

Add some events.

--- index.coffee
+++ index.coffee
@@ -73,6 +73,7 @@

 loadData = (filename, buffer) ->
   draghint.classList.add 'hidden'
+  tracker.sendEvent 'play', filename
   extension = utils.getExtension filename
   rom = null
   if extension is 'zip'
@@ -87,8 +88,10 @@
   play rom, extension
   .catch (e) ->
     console.error e
+    tracker.sendEvent 'error', e

 load = (file) ->
+  tracker.sendEvent 'file'
   return if not file instanceof Blob
   draghint.classList.add 'hidden'
   reader = new FileReader()
@@ -98,6 +101,7 @@

 window.addEventListener 'drop', (event) ->
   return if draghint.classList.contains 'hidden'
+  tracker.sendEvent 'drop'
   event.preventDefault()
   draghint.classList.remove 'hover'
   if event.dataTransfer.files.length > 0


Fix CLI Refresh

Fix CLI Refresh

--- index.coffee
+++ index.coffee
@@ -73,7 +73,7 @@
     CONFIG = require("./_config.yml")

     mongoose = require('mongoose')
-    RefreshController = require('./web/controller/refresh')
+    RefreshController = require('./server/controller/refresh')

     mongoose.connect "mongodb://#{CONFIG.mongo.host}/#{CONFIG.mongo.db}", ->
       log.verbose 'Connected to Mongo'.grey


Add unified error handling.

Add unified error handling.

--- index.coffee
+++ index.coffee
@@ -71,29 +71,22 @@
       el.addEventListener 'touchend', press
     document.getElementById('overlay').appendChild(el)

-stop = ->
-  retro.stop()
-  # retro.core.deinit()
-  window.removeEventListener 'keyup', onkey
-  window.removeEventListener 'keydown', onkey
-  window.clearInterval autosaver
-
-writeSave = (retro) ->
-  try
-    return localForage.setItem retro.md5, new Uint8Array retro.core.serialize()
-  catch error
-    console.log error
-
-loadSave = (retro) ->
-  try
-    return localForage.getItem retro.md5
-  catch error
-    console.log error
-
 error = (e) ->
   loading.classList.add 'hidden'
   document.getElementById('error').classList.remove 'hidden'
   console.error e
+
+writeSave = (retro) ->
+  try
+    return localForage.setItem retro.md5, new Uint8Array retro.core.serialize()
+  catch err
+    error err
+
+loadSave = (retro) ->
+  try
+    return localForage.getItem retro.md5
+  catch err
+    error err

 play = (rom, extension) ->
   Promise.resolve()
@@ -108,7 +101,6 @@
       loadSave retro
       System.import settings.overlays[retro.name] + 'index.json!' if settings.overlays[retro.name] and 'ontouchstart' in window
     ]).then ([core, save, _overlay]) ->
-      stop() if retro.running
       createOverlay _overlay, settings.overlays[retro.name] if _overlay?
       document.getElementById('core-name').textContent = settings.extensions[extension]
       document.getElementById('system-info').textContent = JSON.stringify core.get_system_info(), null, '  '


reload on error.

reload on error.

--- index.coffee
+++ index.coffee
@@ -80,7 +80,7 @@
   .catch (e) ->
     console.error e
     alert "that file couldn't be loaded"
-    draghint.classList.remove 'hidden'
+    location.reload() # hacky but a fix

 load = (file) ->
   return if not file instanceof Blob


make user-agent configurable

make user-agent configurable

--- index.coffee
+++ index.coffee
@@ -5,7 +5,7 @@
 urlParser = require 'url'

 headers =
-	'user-agent': '404check.io (http://404check.io)'
+	'user-agent': process.env.USER_AGENT || 'node-sitemap-stream-parser';
 agentOptions =
 	keepAlive: true
 	gzip: true


Peter thinks this is "cleaner" What-ev-er.

--- index.coffee
+++ index.coffee
@@ -11,21 +11,21 @@

 class DeviceManager extends EventEmitter
   constructor: (@config) ->
-      @deviceProcesses = {}
-      @runningDevices = []
+    @deviceProcesses = {}
+    @runningDevices = []

   refreshDevices: (devices, callback) =>
     debug 'refreshDevices', _.pluck(devices, 'uuid')

-    @getDeviceOperations devices, @runningDevices, (devicesToStart, devicesToStop, devicesToRestart, unchangedDevices) =>
+    @getDevicesByOperation devices, (devicesToStart, devicesToStop, devicesToRestart, unchangedDevices) =>
       connectorsToInstall = _.uniq _.pluck devicesToStart, 'connector'

       async.series(
         [
-          (callback) => async.eachSeries connectorsToInstall, @installConnector, callback
+          (callback) => async.each connectorsToInstall, @installConnector, callback
           (callback) => async.each devicesToStop, @stopDevice, callback
-          (callback) => async.eachSeries devicesToStart, @setupDevice, callback
-          (callback) => async.eachSeries devicesToStart, @startDevice, callback
+          (callback) => async.each devicesToStart, @setupDevice, callback
+          (callback) => async.each devicesToStart, @startDevice, callback
           (callback) => async.each devicesToRestart, @restartDevice, callback
         ]
         (error, result)=>
@@ -34,14 +34,16 @@
           callback error, result
       )

-  getDeviceOperations: (newDevices=[], oldDevices=[], callback=->) =>
-    debug 'getDeviceOperations'
+  getDevicesByOperation: (newDevices=[], callback=->) =>
+    oldDevices = _.clone @runningDevices
+    debug 'getDevicesByOperation'
     debug 'newDevices', _.pluck(newDevices, 'name')
     debug 'oldDevices', _.pluck(oldDevices, 'name')

     devicesToProcess = _.clone newDevices
     async.map devicesToProcess, @deviceExists, (error, remainingDevices) =>
       return callback error if error?
+
       remainingDevices = _.compact remainingDevices
       debug 'devices that exist', _.pluck(remainingDevices, 'name')

@@ -50,7 +52,7 @@
       remainingDevices = _.difference remainingDevices, devicesToStop

       devicesToStart = _.reject remainingDevices, (device) =>
-        _.findWhere oldDevices, uuid: device.uuid
+        _.findWhere oldDevices, uuid: device.uuid, token: device.token

       debug 'devicesToStart:', _.pluck(devicesToStart, 'name')

@@ -104,12 +106,14 @@

     child = new (forever.Monitor)('command.js', foreverOptions)
     child.on 'stderr', (data) =>
-      debug 'stderr', device.uuid, data.toString()
-      @emit 'stderr', data.toString(), device
+      dataJSON = JSON.stringify data, null, 2
+      debug 'stderr', device.uuid, dataJSON
+      @emit 'stderr', dataJSON, device

     child.on 'stdout', (data) =>
-      debug 'stdout', device.uuid, data.toString()
-      @emit 'stdout', data.toString(), device
+      dataJSON = JSON.stringify data, null, 2
+      debug 'stdout', device.uuid, dataJSON
+      @emit 'stdout', dataJSON, device

     debug 'forever', {uuid: device.uuid, name: device.name}, 'starting'
     child.start()
@@ -179,7 +183,7 @@
       debug 'restartDevice error:', error if error?
       @startDevice device, callback

-  stopDevice : (device, callback=->) =>
+  stopDevice: (device, callback=->) =>
     debug 'stopDevice', device.uuid
     deviceProcess = @deviceProcesses[device.uuid]
     return callback null, device.uuid unless deviceProcess?
@@ -200,6 +204,6 @@
     callback null, uuid

   stopDevices: (callback=->) =>
-    async.eachSeries @runningDevices, @stopDevice, callback
+    async.each @runningDevices, @stopDevice, callback

 module.exports = DeviceManager


'process' var -> 'childProcess'

'process' var -> 'childProcess'

--- index.coffee
+++ index.coffee
@@ -32,21 +32,21 @@
 	# run process file
 	running = true
 	processPath = pathUtils.join __dirname, './process.coffee'
-	process = cp.spawn 'coffee', [processPath, processOpts], stdio: ['ipc']
+	childProcess = cp.spawn 'coffee', [processPath, processOpts], stdio: ['ipc']

 	stderr = ''
-	process.stderr.on 'data', (data) ->
+	childProcess.stderr.on 'data', (data) ->
 		stderr += data

-	process.on 'exit', ->
+	childProcess.on 'exit', ->
 		if running
 			running = false
 			log.end logtime
 			return callback stderr

-	process.on 'message', (result) ->
+	childProcess.on 'message', (result) ->
 		running = false
-		process.kill()
+		childProcess.kill()
 		log.end logtime
 		processData = JSON.parse result
 		stack = new utils.async.Stack


allow link formatter to sanitize url

allow link formatter to sanitize url

--- src/modules/link-tooltip.coffee
+++ src/modules/link-tooltip.coffee
@@ -8,10 +8,12 @@

 exitEditMode = ->
   if @tooltipLink.innerText != @tooltipInput.value
-    @tooltipLink.innerText = @tooltipLink.href = @tooltipInput.value
     @editor.setSelection(@savedRange, true)
     @editor.selection.format('link', @tooltipInput.value, { source: 'user' })
-    @toolbar.emit(Scribe.Toolbar.events.FORMAT, 'link', @tooltipInput.value)
+    formats = @savedRange.getFormats()
+    if formats['link']?
+      @tooltipLink.innerText = @tooltipLink.href = formats['link']
+      @toolbar.emit(Scribe.Toolbar.events.FORMAT, 'link', formats['link'])
     @editor.setSelection(null, true)
   Scribe.DOM.removeClass(@tooltip, 'editing')


yo dawg, no maps in your detail maps

yo dawg, no maps in your detail maps

--- js/models/map.coffee
+++ js/models/map.coffee
@@ -16,11 +16,10 @@
       mapPosition = @loadMapPosition()

       @geolocate = new GeoLocateControl().instance;
-      @instance = new OpenLayers.Map
-        div: @divname
-        theme: null
-        projection: defaults.projection
-        controls: [
+
+      @controls = []
+      if(@divname == "map")
+        @controls = [
           new OpenLayers.Control.Attribution()
           new OpenLayers.Control.TouchNavigation({
             dragPanOptions: {
@@ -30,6 +29,12 @@
           @geolocate
           new OpenLayers.Control.Zoom()
         ]
+
+      @instance = new OpenLayers.Map
+        div: @divname
+        theme: null
+        projection: defaults.projection
+        controls: @controls
         layers: [
           mmlTaustakartta
         ]


Tracing Dict/List/Object changes bug fixes

--- input.coffee
+++ input.coffee
@@ -229,36 +229,30 @@
 		(name) ->
 			cache[name] ||= "on#{utils.capitalize(name)}Change"

-	trace: (val, prop) ->
-		if val instanceof Dict
-			val.onChange onChange, @
-			@traces.push val, 'onChange'
-		else if val instanceof List
-			val.onChange onChange, @
-			@traces.push val, 'onChange'
-			val.onInsert onChange, @
-			@traces.push val, 'onInsert'
-			val.onPop onChange, @
-			@traces.push val, 'onPop'
-		val
-
 	traceProp: (obj, prop) ->
 		if obj
 			if obj instanceof Dict
+				obj.onChange onChange, @
+				@traces.push obj, 'onChange'
+
 				val = obj.get prop
 			else if obj instanceof List
+				obj.onChange onChange, @
+				@traces.push obj, 'onChange'
+				obj.onInsert onChange, @
+				@traces.push obj, 'onInsert'
+				obj.onPop onChange, @
+				@traces.push obj, 'onPop'
+
 				val = obj.get prop
-
-			if val is undefined
-				val = obj[prop]
-
-			if val
-				@trace val, prop
-
+			else if obj
 				signal = getNamedSignal prop
 				if typeof obj[signal] is 'function'
 					obj[signal] onChange, @
 					@traces.push obj, signal
+
+			if val is undefined
+				val = obj[prop]
 		val

 	render: ->
@@ -266,7 +260,7 @@
 			if storage instanceof Element
 				storage.onAttrsChange onChange, @
 			else if storage instanceof Dict
-				@trace storage, ''
+				@traceProp storage, ''

 		@update()


don't dump gc allocation stats unless debug = 3

--- lib/closure-conversion.coffee
+++ lib/closure-conversion.coffee
@@ -1452,6 +1452,6 @@
                 tree = pass.visit tree
                 debug.log 2, "after: #{passType.name}"
                 debug.log 2, -> escodegen.generate tree
-                __ejs.GC.dumpAllocationStats "after #{passType.name}" if __ejs?
+                debug.log 3, -> __ejs.GC.dumpAllocationStats "after #{passType.name}" if __ejs?

         tree


Collaboration: Handle pane removed for participants.

--- client/ide/AppController.coffee
+++ client/ide/AppController.coffee
@@ -885,22 +885,29 @@

     amIWatchingChangeOwner = not myWatchMap or myWatchMap.keys().length is 0 or origin in myWatchMap.keys()

+    log 'change arrived', type
+
     if amIWatchingChangeOwner
       targetPane = @getPaneByChange change

-      switch type
-
-        when 'NewPaneCreated'
-          @createPaneFromChange change
-
-        when 'TabChanged'
-          paneView = targetPane.parent
-          tabView  = paneView.parent
-          ideView  = tabView.parent
-
-          ideView.suppressChangeHandlers = yes
+      if type is 'NewPaneCreated'
+        @createPaneFromChange change
+
+      else if type in ['TabChanged', 'PaneRemoved']
+        paneView = targetPane?.parent
+        tabView  = paneView?.parent
+        ideView  = tabView?.parent
+
+        return unless ideView
+
+        ideView.suppressChangeHandlers = yes
+
+        if type is 'TabChanged'
           tabView.showPane paneView
-          ideView.suppressChangeHandlers = no
+        else
+          tabView.removePane paneView
+
+        ideView.suppressChangeHandlers = no

       targetPane?.handleChange? change, @rtm, @realTimeDoc


AppController: Make a method to cache created IDEViews.

--- client/ide/AppController.coffee
+++ client/ide/AppController.coffee
@@ -79,7 +79,7 @@
       panel.once 'viewAppended', =>
         ideView = panel.getPaneByName 'editorPane'
         @setActiveTabView ideView.tabView
-        @ideViews.push ideView
+        @registerIDEView ideView

         splitViewPanel = ideView.parent.parent
         splitViewPanel.addSubView @statusBar = new IDE.StatusBar
@@ -101,7 +101,7 @@
       type      : type
       views     : [ null, newIDEView ]

-    @ideViews.push newIDEView
+    @registerIDEView newIDEView

     splitView.once 'viewAppended', ->
       splitView.panels.first.attach ideView
@@ -150,7 +150,7 @@
       ideView.tabView.addPane pane

     @setActiveTabView ideView.tabView
-    @ideViews.push ideView
+    @registerIDEView ideView

     if parentSplitView and panelIndexInParent
       parentSplitView.options.views[panelIndexInParent] = ideView
@@ -244,6 +244,9 @@
     requiredIndex = keyCodeMap.indexOf keyEvent.keyCode

     @activeTabView.showPaneByIndex requiredIndex
+
+  registerIDEView: (ideView) ->
+    @ideViews.push ideView

   forEachSubViewInIDEViews_: (callback = noop, paneType) ->
     for ideView in @ideViews


IDE: fix race condition

IDE: fix race condition

--- client/ide/AppController.coffee
+++ client/ide/AppController.coffee
@@ -1821,8 +1821,6 @@

   handleParticipantLeaveAction: ->
-    # remove the leaving participant's info from the collaborative doc
-    @removeParticipant KD.nick()

     options   =
       title   : 'Are you sure?'
@@ -1837,6 +1835,8 @@
       KD.singletons.socialapi.channel.leave options, (err) =>
         return KD.showError err  if err
         @setMachineUser [KD.whoami()], no, => @quit()
+        # remove the leaving participant's info from the collaborative doc
+        @removeParticipant KD.nick()

   removeMachineNode: ->


kbd prefs: update legend when Reset is pressed

--- client/prefs-ui-keyboard.coffee
+++ client/prefs-ui-keyboard.coffee
@@ -87,7 +87,7 @@
         else                    'US'
     )
   $('.reset', $e).button().click ->
-    $pk.val prefs.prefixKey.getDefault()
+    $pk.val(prefs.prefixKey.getDefault()).change() # fire a "change" event to update the legend
     loadBQMap keymap.getDefaultBQMap()
     false
   $lc = $('#keyboard-locale').val(prefs.keyboardLocale()).change ->


Fix apostrophes in messages.

Fix apostrophes in messages.

--- src/core/server.coffee
+++ src/core/server.coffee
@@ -150,7 +150,7 @@
     group = tree._[content.__plugin.group]

     if not key?
-      throw new Error "Content #{ content.filename } not found in it's parent tree!"
+      throw new Error "Content #{ content.filename } not found in its parent tree!"

     loadContent env, filepath, (error, newContent) ->
       if error?
@@ -165,7 +165,7 @@

       # also in the trees plugin group
       if not replaceInArray(group, content, newContent)
-        throw new Error "Content #{ content.filename } not found in it's plugin group!"
+        throw new Error "Content #{ content.filename } not found in its plugin group!"

       # keep the lookup map fresh
       delete lookup[normalizeUrl(content.url)]


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/core_object.coffee
+++ src/core_object.coffee
@@ -151,5 +151,5 @@
     @param {Module} Module the module to include. A module is a constructor that takes two parameters, I and self, and returns an object containing the public methods to extend the including object with.
     ###
     include: (modules...) ->
-      for mo
+      for Module
       self.extend Module(I, self)


Replace deprecated atom.workspaceView references.

--- lib/open-terminal-here.coffee
+++ lib/open-terminal-here.coffee
@@ -12,18 +12,19 @@
   },

   activate: ->
-    atom.workspaceView.command "open-terminal-here:open", => @open()
+    atom.commands.add '.tree-view .selected',
+      'open-terminal-here:open': (event) => @open(event.currentTarget)

-  open: ->
+  open: (target) ->
     isDarwin = document.body.classList.contains("platform-darwin")
     isWin32 = document.body.classList.contains("platform-win32")

-    filepath = atom.workspaceView.find('.tree-view .selected').views()?[0][0].getPath?()
+    filepath = target.getPath?() ? target.item?.getPath()

     dirpath = filepath

     if fs.lstatSync(filepath).isFile()
-        dirpath = path.dirname(filepath)
+      dirpath = path.dirname(filepath)

     return if not dirpath


fixed AMD support of mvc plugin

fixed AMD support of mvc plugin

--- src/plugins/scaleApp.mvc.coffee
+++ src/plugins/scaleApp.mvc.coffee
@@ -1,63 +1,71 @@
-scaleApp = window?.scaleApp or require? "../scaleApp"
+createPlugin = (scaleApp) ->
+  class Model extends scaleApp.Mediator

-class Model extends scaleApp.Mediator
+    constructor: (obj) ->
+      super()
+      @id = obj?.id or scaleApp.util.uniqueId()
+      @[k] = v for k,v of obj when not @[k]?

-  constructor: (obj) ->
-    super()
-    @id = obj?.id or scaleApp.util.uniqueId()
-    @[k] = v for k,v of obj when not @[k]?
+    set: (key, val, silent=false) ->
+      switch typeof key
+        when "object"
+          @set k,v,true for k,v of key
+          @emit Model.CHANGED, (k for k,v of key) if not silent
+        when "string"
+          if not (key in ["set","get"]) and @[key] isnt val
+            @[key] = val
+            @emit Model.CHANGED, [key] if not silent
+        else console?.error? "key is not a string"
+      @

-  set: (key, val, silent=false) ->
-    switch typeof key
-      when "object"
-        @set k,v,true for k,v of key
-        @emit Model.CHANGED, (k for k,v of key) if not silent
-      when "string"
-        if not (key in ["set","get"]) and @[key] isnt val
-          @[key] = val
-          @emit Model.CHANGED, [key] if not silent
-      else console?.error? "key is not a string"
-    @
+    change: (cb, context) ->
+      if typeof cb is "function"
+        @on Model.CHANGED, cb, context
+      else if arguments.length is 0
+        @emit Model.CHANGED

-  change: (cb, context) ->
-    if typeof cb is "function"
-      @on Model.CHANGED, cb, context
-    else if arguments.length is 0
-      @emit Model.CHANGED
+    notify: -> @change()

-  notify: -> @change()
+    get: (key) -> @[key]

-  get: (key) -> @[key]
+    toJSON: ->
+      json = {}
+      json[k]=v for own k,v of @
+      json

-  toJSON: ->
-    json = {}
-    json[k]=v for own k,v of @
-    json
+    @CHANGED: "changed"

-  @CHANGED: "changed"
+  class View

-class View
+    constructor: (model) -> @setModel model if model

-  constructor: (model) -> @setModel model if model
+    setModel: (@model) -> @model.change (-> @render()), @

-  setModel: (@model) -> @model.change (-> @render()), @
+    render: ->

-  render: ->
+  class Controller

-class Controller
+    constructor: (@model, @view) ->

-  constructor: (@model, @view) ->
+  p =
+    Model: Model
+    View: View
+    Controller: Controller

-p =
-  Model: Model
-  View: View
-  Controller: Controller
+  plugin =
+    id: "mvc"
+    base:p
+    sandbox: (@sb) -> p

-plugin =
-  id: "mvc"
-  base:p
-  sandbox: (@sb) -> p
+# AMD support
+if define?.amd?
+  define ['scaleApp'], (sa) -> createPlugin sa

-scaleApp.plugin.register plugin if window?.scaleApp?
-module.exports = plugin if module?.exports?
-(define -> plugin) if define?.amd?
+# Browser support
+else if window?.scaleApp?
+  sa = window.scaleApp
+  sa.plugin.register createPlugin sa
+
+# Node.js support
+else if module?.exports?
+  module.exports = createPlugin require "../scaleApp"


changed dates

changed dates

--- src/model/statsd.coffee
+++ src/model/statsd.coffee
@@ -40,8 +40,8 @@
 }`

 exports.retrieveChannelMetrics = `function *(type, channelId) {
-  var path = "/render?target=transformNull(summarize(stats.counters." + domain + ".Channels." + channelId + ".count,'1hour'))&from=-1days&format=json";
-  path += "&target=transformNull(summarize(stats.timers." + domain + ".Channels." + channelId + ".mean,'1hour'))";
+  var path = "/render?target=transformNull(summarize(stats.counters." + domain + ".Channels." + channelId + ".count,'1day'))&from=-1weeks&format=json";
+  path += "&target=transformNull(summarize(stats.timers." + domain + ".Channels." + channelId + ".mean,'1day'))";
   var data = [];
   var raw = yield fetchData(path);
   var i = 0;


closeAll function is now calling the API closeAllModal method

--- src/models/Modal.coffee
+++ src/models/Modal.coffee
@@ -41,12 +41,10 @@
       parameters: options
       successCallbacks: [callbacks.onSuccess]
       failureCallbacks: [callbacks.onFailure]
-
+
   closeAll: (options={}, callbacks={})->
-    options.closeAll = true
     steroids.nativeBridge.nativeCall
-      method: "closeModal"
+      method: "closeAllModal"
       parameters: options
       successCallbacks: [callbacks.onSuccess]
       failureCallbacks: [callbacks.onFailure]
-


process: always use objectMode: false

process: always use objectMode: false

--- lib/transforms/process.coffee
+++ lib/transforms/process.coffee
@@ -1,9 +1,10 @@
+_ = require 'underscore'
 {Duplex} = require 'stream'

 # TODO: error handling. exit code? stderr?
 class Process extends Duplex
   constructor: (@stream_opts, @process) ->
-    super objectMode: false
+    super _(@stream_opts).extend(objectMode: false)
     @on 'pipe', (source) => source.unpipe(@).pipe @process.stdin
   pipe: (dest, options) => @process.stdout.pipe dest, options


disable manual input of date

disable manual input of date

--- src/components/tutor-input.cjsx
+++ src/components/tutor-input.cjsx
@@ -185,6 +185,7 @@

     if not @props.disabled
       dateElem = <DatePicker
+          readOnly={true}
           minDate={min}
           maxDate={max}
           onFocus={@expandCalendar}
@@ -202,7 +203,7 @@
       displayValue = value.format(TutorDateFormat)

-      <input type='text' disabled className={classes} value={displayValue}/>
+      <input type='text' disabled readonly className={classes} value={displayValue}/>

{@props.label}

         Required Field


CompositeDisposables are essential

CompositeDisposables are essential

--- src/composite-disposable.coffee
+++ src/composite-disposable.coffee
@@ -1,4 +1,4 @@
-# Public: An object that aggregates multiple {Disposable} instances together
+# Essential: An object that aggregates multiple {Disposable} instances together
 # into a single disposable, so they can all be disposed as a group.
 module.exports =
 class CompositeDisposable


Only call show on active item when attached

Closes atom/settings-view#258

--- src/pane-element.coffee
+++ src/pane-element.coffee
@@ -4,13 +4,18 @@

 class PaneElement extends HTMLElement
   createdCallback: ->
+    @attached = false
     @subscriptions = new CompositeDisposable
     @initializeContent()
     @subscribeToDOMEvents()
     @createSpacePenShim()

   attachedCallback: ->
+    @attached = true
     @focus() if @model.isFocused()
+
+  detachedCallback: ->
+    @attached = false

   initializeContent: ->
     @setAttribute 'class', 'pane'
@@ -52,7 +57,7 @@
     view = @model.getView(item).__spacePenView
     otherView.hide() for otherView in $itemViews.children().not(view).views()
     $itemViews.append(view) unless view.parent().is($itemViews)
-    view.show()
+    view.show() if @attached
     view.focus() if @hasFocus()

   itemRemoved: ({item, index, destroyed}) ->


Add atom-pane commands via atom.commands again

--- src/pane-element.coffee
+++ src/pane-element.coffee
@@ -42,32 +42,6 @@

   createSpacePenShim: ->
     @__spacePenView = new PaneView(this)
-
-    addCommands = (handlersByName) =>
-      for name, handler of handlersByName
-        do (handler) =>
-          @__spacePenView.command name, => handler.apply(this, arguments)
-
-    addCommands(
-      'pane:save-items': -> @getModel().saveItems()
-      'pane:show-next-item': -> @getModel().activateNextItem()
-      'pane:show-previous-item': -> @getModel().activatePreviousItem()
-      'pane:show-item-1': -> @getModel().activateItemAtIndex(0)
-      'pane:show-item-2': -> @getModel().activateItemAtIndex(1)
-      'pane:show-item-3': -> @getModel().activateItemAtIndex(2)
-      'pane:show-item-4': -> @getModel().activateItemAtIndex(3)
-      'pane:show-item-5': -> @getModel().activateItemAtIndex(4)
-      'pane:show-item-6': -> @getModel().activateItemAtIndex(5)
-      'pane:show-item-7': -> @getModel().activateItemAtIndex(6)
-      'pane:show-item-8': -> @getModel().activateItemAtIndex(7)
-      'pane:show-item-9': -> @getModel().activateItemAtIndex(8)
-      'pane:split-left': -> @getModel().splitLeft(copyActiveItem: true)
-      'pane:split-right': -> @getModel().splitRight(copyActiveItem: true)
-      'pane:split-up': -> @getModel().splitUp(copyActiveItem: true)
-      'pane:split-down': -> @getModel().splitDown(copyActiveItem: true)
-      'pane:close': -> @getModel().close()
-      'pane:close-other-items': -> @getModel().destroyInactiveItems()
-    )

   getModel: -> @model

@@ -132,4 +106,24 @@
   hasFocus: ->
     this is document.activeElement or @contains(document.activeElement)

+atom.commands.add 'atom-pane',
+  'pane:save-items': -> @getModel().saveItems()
+  'pane:show-next-item': -> @getModel().activateNextItem()
+  'pane:show-previous-item': -> @getModel().activatePreviousItem()
+  'pane:show-item-1': -> @getModel().activateItemAtIndex(0)
+  'pane:show-item-2': -> @getModel().activateItemAtIndex(1)
+  'pane:show-item-3': -> @getModel().activateItemAtIndex(2)
+  'pane:show-item-4': -> @getModel().activateItemAtIndex(3)
+  'pane:show-item-5': -> @getModel().activateItemAtIndex(4)
+  'pane:show-item-6': -> @getModel().activateItemAtIndex(5)
+  'pane:show-item-7': -> @getModel().activateItemAtIndex(6)
+  'pane:show-item-8': -> @getModel().activateItemAtIndex(7)
+  'pane:show-item-9': -> @getModel().activateItemAtIndex(8)
+  'pane:split-left': -> @getModel().splitLeft(copyActiveItem: true)
+  'pane:split-right': -> @getModel().splitRight(copyActiveItem: true)
+  'pane:split-up': -> @getModel().splitUp(copyActiveItem: true)
+  'pane:split-down': -> @getModel().splitDown(copyActiveItem: true)
+  'pane:close': -> @getModel().close()
+  'pane:close-other-items': -> @getModel().destroyInactiveItems()
+
 module.exports = PaneElement = document.registerElement 'atom-pane', prototype: PaneElement.prototype


Improving edge-handling

Improving edge-handling

--- src/core/watcher-windows.coffee
+++ src/core/watcher-windows.coffee
@@ -4,13 +4,11 @@
 	@listenPaths = []
 	@callback = null
 	constructor: (directories, callback, verbose = false, silence_exceptions = false) ->
-		if WatcherWindows.WINDOWS_EDGE == null
-			WatcherWindows.WINDOWS_EDGE = require "edge"
-
-		console.log("Here WatcherWindows")
-
-
-		@windowsWatcher = new fileSystemWatcher(WatcherWindows.WINDOWS_EDGE)
+		if WatcherWindows.WINDOWS_EDGE?
+			@windowsWatcher = new fileSystemWatcher(WatcherWindows.WINDOWS_EDGE)
+		else
+			@windowsWatcher = new fileSystemWatcher()
+
 		@setup_watcher(directories, callback, verbose, silence_exceptions)

 	start: () ->


Added link target selectionbox to hallolink widget

I ran into an issue of all links set by hallo being 'targetless'. The users I intend my implementation for cannot be asked to use the htmlview plugin for setting link targets, so the only (workable) solution I could come up with was to expand the hallolink widget to provide a selectionbox for choosing the desired link target.

Im fairly new to HalloJS and even n00-er to coffeescript, so excuse me if I failed to produce the exact right syntax.
If so, please correct my wrongs. I hope my (extremely minimal) contribution is perceived useful by others.

--- src/plugins/link.coffee
+++ src/plugins/link.coffee
@@ -32,6 +32,10 @@
         <form action=\"#\" method=\"post\" class=\"linkForm\">
           <input class=\"url\" type=\"text\" name=\"url\"
             value=\"#{@options.defaultUrl}\" />
+          <select name=\"linktarget\">
+            <option value=\"_self\">open in this tab</option>
+            <option value=\"_blank\">open in a new tab</option>
+          </select>
           <input type=\"submit\" id=\"addlinkButton\" value=\"#{butTitle}\"/>
         </form>
"
       urlInput = jQuery('input[name=url]', dialog)
@@ -42,9 +46,12 @@
         false

       dialogSubmitCb = (event) ->
+
+        urlTarget = jQuery('select[name=linktarget] option:selected', dialog)
         event.preventDefault()

         link = urlInput.val()
+        target = urlTarget.val()
         dialog.dialog('close')

         widget.options.editable.restoreSelection(widget.lastSelection)
@@ -60,10 +67,11 @@
             # following check will work around ie and ff bugs when using
             # "createLink" on an empty selection
             if widget.lastSelection.collapsed
-              linkNode = jQuery("#{link}")[0]
+              linkNode = jQuery("#{link}")[0]
               widget.lastSelection.insertNode linkNode
             else
               document.execCommand "createLink", null, link
+              widget.lastSelection.startContainer.parentNode.target = target
           else
             widget.lastSelection.startContainer.parentNode.href = link
         widget.options.editable.element.trigger('change')


fix variation filtering

fix variation filtering

elements can have variations, add documented in
https://doc.qeda.org/core/component/#-variations-
the issue was that when generating, the variation specified when
the element was add (e.g. qeda add element@variation) was ignored.
this change fixes the issue.

WARNING:
when an element has variations, and no variation is specified,
the element is added in all variations.
but when generating, the variations overwrite each other.
no warning is output.
this issue has not been fixed (the generator does not know which
variation is generated to add this information in the file name,
...)

--- src/qeda-library.coffee
+++ src/qeda-library.coffee
@@ -171,7 +171,7 @@
   # Load element description from remote repository
   #
   load: (element, force = false) ->
-    obj = @loadYaml element, force
+    [obj, filterVariation] = @loadYaml element, force

     objs = []
     if obj.variations?
@@ -224,7 +224,7 @@

     @check obj

-    # Load base descrition
+    # Load base description
     if obj.base?
       unless typeof obj.base is 'string' then obj.base = obj.base.toString()
       bases = obj.base.replace(/\s+/g, '').split(',')
@@ -242,7 +242,7 @@
     if element.indexOf('/') isnt -1
       obj.group ?= element.substr(0, element.indexOf('/')).toUpperCase()

-    obj
+    [obj, filterVariation]

   #
   # Merge two objects


Finding item index and setting attrs bug fixes

--- style.coffee
+++ style.coffee
@@ -308,7 +308,7 @@
 			item

 	updateText: ->
-		if @waiting or @children.length
+		if @waiting or @children.length or @node.query('[neft:style]')
 			return

 		obj = @getTextObject()
@@ -393,25 +393,30 @@
 					continue

 				internalProp = getInternalProperty prop
+				objPropVal = if internalProp or obj then obj[internalProp] else obj[prop]

 				if val of ATTR_PRIMITIVE_VALUES
 					val = ATTR_PRIMITIVE_VALUES[val]

-				propType = typeof obj[prop]
+				propType = typeof objPropVal
 				if propType is 'object'
-					propType = typeof obj[prop]?.valueOf()
+					propType = typeof objPropVal?.valueOf()
 				switch propType
 					when 'number'
 						baseVal = val
-						val = parseFloat val
+						if typeof val isnt 'number'
+							val = parseFloat val
 						if isNaN(val) and baseVal isnt 'NaN'
 							val = baseVal
 					when 'boolean'
 						val = !!val
 					when 'string'
-						val = val+''
-
-				if typeof obj[prop] is 'function' and typeof val is 'function'
+						if val?
+							val = val+''
+						else
+							val = ''
+
+				if typeof val is 'function' and typeof obj[prop] is 'function'
 					obj[prop] val
 					@attrListeners.push obj, prop, val
 				else
@@ -577,25 +582,33 @@

 	findItemIndex = (node, item, parent) ->
 		tmpIndexNode = node
-		parent = parent.children.target or parent
+		parent = parent.children._target or parent
+		tmpSiblingNode = tmpIndexNode
+
+		# by parents
 		while tmpIndexNode
-			tmpSiblingNode = tmpIndexNode
-			while tmpSiblingNode = tmpSiblingNode._nextSibling
-				if tmpSiblingItem = tmpSiblingNode._documentStyle?.item
-					if tmpSiblingTargetItem = findItemWithParent(tmpSiblingItem, parent)
-						item.index = tmpSiblingTargetItem.index
-						return
-
-			# tmpSiblingNode = tmpIndexNode
-			# while tmpSiblingNode = tmpSiblingNode._previousSibling
-			# 	if tmpSiblingItem = tmpSiblingNode._documentStyle?.item
-			# 		if tmpSiblingTargetItem = findItemWithParent(tmpSiblingItem, parent)
-			# 			item.index = tmpSiblingTargetItem.index + 1
-			# 			return
+			# by next sibling
+			while tmpSiblingNode
+				if tmpSiblingNode isnt node
+					# get sibling item
+					if tmpSiblingItem = tmpSiblingNode._documentStyle?.item
+						if tmpSiblingTargetItem = findItemWithParent(tmpSiblingItem, parent)
+							item.index = tmpSiblingTargetItem.index
+							return true
+					# check children of special tags
+					else if tmpSiblingNode.name in ['neft:blank', 'neft:fragment', 'neft:use']
+						tmpIndexNode = tmpSiblingNode
+						tmpSiblingNode = tmpIndexNode.children[0]
+						continue
+				# check next sibling
+				tmpSiblingNode = tmpSiblingNode._nextSibling
+			# no sibling found, but parent is styled
 			if tmpIndexNode isnt node and tmpIndexNode.style
 				return
+			# check parent
 			tmpIndexNode = tmpIndexNode._parent
-		return
+			tmpSiblingNode = tmpIndexNode._nextSibling
+		return false

 	findItemParent: ->
 		if @waiting
@@ -611,8 +624,8 @@
 					if style.node isnt tmpNode
 						item = item._parent

-				if tmpNode.name isnt 'neft:fragment'
-					tmpNode._documentStyle ?= @
+				unless tmpNode.name in ['neft:blank', 'neft:fragment', 'neft:use']
+					tmpNode._documentStyle ||= @

 				if item
 					@item.parent = item


Fix help command replaces prefix twice

Fix help command replaces prefix twice

--- src/scripts/help.coffee
+++ src/scripts/help.coffee
@@ -66,8 +66,8 @@

     prefix = robot.alias or robot.name
     cmds = cmds.map (cmd) ->
-      cmd = cmd.replace /^hubot/, prefix
-      cmd.replace /hubot/ig, robot.name
+      cmd = cmd.replace /hubot/ig, robot.name
+      cmd.replace new RegExp("^#{robot.name}"), prefix

     emit = cmds.join "\n"


Remove inappropriate none

Remove inappropriate none

--- src/scripts/text.coffee
+++ src/scripts/text.coffee
@@ -8,7 +8,6 @@
 #   HUBOT_SMSIFIED_USERNAME
 #   HUBOT_SMSIFIED_PASSWORD
 #   HUBOT_SMSIFIED_SENDERADDRESS
-#   None
 #
 # Commands:
 #   hubot text <phonenumber> <message> - Sends <message> to <phonenumber>.


Refactored model to use new queue code.

Refactored model to use new queue code.

--- src/server/model.coffee
+++ src/server/model.coffee
@@ -6,6 +6,7 @@
 p = -> #require('util').debug
 i = -> #require('util').inspect

+queue = require './syncqueue'
 types = require '../types'
 db = require './db'
 Events = require('./events')
@@ -119,21 +120,7 @@
 				# The op is up to date already. Apply and submit.
 				submit()

-	pendingOps = {} # docName -> {busy:bool, queue:[[op, callback], [op, callback], ...]}
-
-	flushOps = (docName) ->
-		state = pendingOps[docName]
-
-		p "flushOps #{docName} state #{i state}"
-		return if state.busy || state.queue.length == 0
-		p "continuing..."
-		state.busy = true
-
-		[opData, callback] = state.queue.shift()
-		applyOpInternal docName, opData, (error, version) ->
-			callback(error, version) if callback?
-			state.busy = false
-			flushOps docName
+	queues = {} # docName -> syncQueue

 	# Apply an op to the specified document.
 	# The callback is passed (error, applied version #)
@@ -144,10 +131,10 @@
 	# model.applyOp 'doc', OPC
 	@applyOp = (docName, opData, callback) ->
 		p "applyOp #{docName} op #{i opData}"
+
 		# Its important that all ops are applied in order.
-		pendingOps[docName] ||= {busy:false, queue:[]}
-		pendingOps[docName].queue.push [opData, callback]
-		flushOps docName
+		queues[docName] ||= queue (opData, callback) -> applyOpInternal docName, opData, callback
+		queues[docName](opData, callback)

 	# Perminantly deletes the specified document.
 	# If listeners are attached, they are removed.


tweak countdown code

tweak countdown code

--- public/javascripts/index.coffee
+++ public/javascripts/index.coffee
@@ -8,15 +8,18 @@
     $('#countdown').each ->
       $this = $(this)

-      pluralize = (str, count) ->
-        str + (if parseInt(count) != 1 then 's' else '')
+      pluralize = (count, str) ->
+        count + ' ' + str + (if parseInt(count) != 1 then 's ' else ' ')

+      names = ['day', 'hour', 'minute', 'second']
       do tick = ->
-        names = ['day', 'hour', 'minute', 'second']
         secs = (start - (new Date)) / 1000
         if secs > 0
-          left = $.map([secs / 86400, secs % 86400 / 3600, secs % 3600 / 60, secs % 60], (num, i) -> [Math.floor(num), pluralize(names[i], num)]).join(' ')
-          $this.text left + ' from now'
+          parts = [secs / 86400, secs % 86400 / 3600, secs % 3600 / 60, secs % 60]
+          $this.html null
+          $.each parts, (i, num) ->
+            $this.append pluralize(Math.floor(num), names[i])
+          $this.append 'from now'
           setTimeout tick, 800
         else
           $this.html $('
GOGOGOGOGOGO
')


thats what i call a async bug!

thats what i call a async bug!

--- src/test/xml.coffee
+++ src/test/xml.coffee
@@ -1,6 +1,11 @@
 path = require 'path'
 { createReadStream } = require 'fs'
 { Tag, Builder } = require '../asyncxml'
+
+# the problem is that at the point when an array is printed (eg, in an error) it
+# can differ from its original state, because something async happened after the
+# test is done (and before the error is printed).
+copyarr = (arr) -> Array.prototype.slice.call arr

 module.exports =
@@ -338,8 +343,8 @@
     'api events': (æ) ->
         xml = new Builder
         xml.on 'end', ->
-            æ.deepEqual results.add,   ['add',   "root", "childA", "childB"]
-            æ.deepEqual results.close, ['close', "childB", "childA", "root"]
+            æ.deepEqual copyarr(results.add),   ['add',   "root", "childA", "childB"]
+            æ.deepEqual copyarr(results.close), ['close', "childB", "childA", "root"]
             æ.done()

         results = add:['add'], close:['close']


Simplify childNode inspection

Simplify childNode inspection

--- src/trix/dom.coffee
+++ src/trix/dom.coffee
@@ -59,10 +59,7 @@
     if container.nodeType is Node.TEXT_NODE
       container.trixPosition + offset
     else
-      if container is @element and container.childNodes.length is 0
-        offset
-      else
-        container.childNodes[offset].trixPosition
+      container.childNodes[offset]?.trixPosition ? offset

   findContainerAndOffsetForPosition: (position) ->
     index = 0


Shell: added toString

Shell: added toString

--- coffee/src/shells/shell.coffee
+++ coffee/src/shells/shell.coffee
@@ -44,6 +44,9 @@
     # a URL to a thumbnail for the piece of media
     thumbnail: acorn.config.img.acorn

+
+  toString: =>
+    "#{@shellid()} #{@title()}"

   # -- factory constructors --


Fix indentation

Fix indentation

Fix indentation so that the text appears properly in the hubot script catalog.

--- src/scripts/beeradvocate.coffee
+++ src/scripts/beeradvocate.coffee
@@ -1,20 +1,20 @@
 # Description:
-# An alternate to the existing beer script, this will scrape beer advocate for the most recent accessed beer.
-# It returns the name of the beer, a picture of the beer and a link to the beer. Hubot is now full of
-# different options for beer scripts. Removing the ? (optional) after (a|advocate) will force the command
-# to be 'beer advocate me' and thereby allow this script to coexist with other beer scripts peacefully.
+#   An alternate to the existing beer script, this will scrape beer advocate for the most recent accessed beer.
+#   It returns the name of the beer, a picture of the beer and a link to the beer. Hubot is now full of
+#   different options for beer scripts. Removing the ? (optional) after (a|advocate) will force the command
+#   to be 'beer advocate me' and thereby allow this script to coexist with other beer scripts peacefully.
 #
 # Dependencies:
-# None
+#   None
 #
 # Configuration:
-# None
+#   None
 #
 # Commands:
-# beer me - returns the latest beer discussed on beer advocate with picture
+#   beer me - returns the latest beer discussed on beer advocate with picture
 #
 # Author:
-# whyjustin
+#   whyjustin

 module.exports = (robot) ->
     robot.respond /beer (a|advocate)?( me)?/i, (msg) ->


expose kernel instance to the global scope

--- library/nucleus/kernel.coffee
+++ library/nucleus/kernel.coffee
@@ -179,8 +179,8 @@
         assert @application = @constructor.APPLICATION
         assert branding = [@framework.name, "smisome1"]
         types = [@framework.version, @framework.codename]
+        GenericKernel.kernel = this # one kernel per proc
         this.interceptExceptions.call this, initializer
-        GenericKernel.kernel = @ # one kernel per proc
         return asciify branding..., (error, banner) =>
             util.puts banner.toString().blue unless error
             identify = "Running version %s, codename: %s"


Fix a misnomer method name

Fix a misnomer method name

--- library/nucleus/kernel.coffee
+++ library/nucleus/kernel.coffee
@@ -131,7 +131,7 @@
         sigterm = "Received the SIGTERM (terminate signal)"
         process.on "SIGINT", => @shutdownKernel sigint
         process.on "SIGTERM", => @shutdownKernel sigterm
-        assert @setupRoutableEnvirovnment()
+        assert _.isObject @setupScaffolding()
         this.constructor.configure().apply this
         assert not _.isEmpty @setupConnectPipeline()
         assert not _.isEmpty @setupListeningServers()
@@ -309,7 +309,7 @@
     # the determined scope and the router, which is then are wired
     # in with the located and instantiated services. Please refer
     # to the implementation on how and what is being done exactly.
-    setupRoutableEnvirovnment: ->
+    setupScaffolding: ->
         tag = nconf.get "NODE_ENV" or null
         missing = "No NODE_ENV variable found"
         assert not _.isEmpty(tag), missing


factor out the connect pipe line above the CC

--- library/nucleus/kernel.coffee
+++ library/nucleus/kernel.coffee
@@ -141,9 +141,9 @@
         process.on "SIGINT", => @shutdownKernel sigint
         process.on "SIGTERM", => @shutdownKernel sigterm
         assert _.isObject @setupScaffolding()
+        assert _.isObject @setupConnectPipeline()
         this.constructor.configure().apply this
         return @kernelPreemption.call this, =>
-            assert not _.isEmpty @setupConnectPipeline()
             assert not _.isEmpty @setupListeningServers()
             assert not _.isEmpty @setupSocketServers()
             assert not _.isEmpty @setupHotloadWatcher()


codemirror mode class

codemirror mode class

--- coffee/codemirror-syntax.coffee
+++ coffee/codemirror-syntax.coffee
@@ -1,210 +1,226 @@
-CodeMirror.defineMode "docscript", ((cmCfg, modeCfg) ->
-  operator = "tag strong"
-  operatorInline = "string"
-  htmlMode = CodeMirror.getMode cmCfg, name: "xml", htmlMode: true
-
-  matchBlock = (stream, state) ->
-   stack = state.stack
-
-   match = stream.match /^<<</
-   if match
-    stack.push indentation: stream.indentation(), type: 'html'
-    stream.skipToEnd()
-    state.htmlState = CodeMirror.startState htmlMode
-    return operator
-
-   match = stream.match /^\+\+\+/
-   if match
-    stack.push indentation: stream.indentation(), type: 'special'
-    stream.skipToEnd()
-    return operator
-
-   match = stream.match /^>>>/
-   if match
-    stack.push indentation: stream.indentation(), type: 'sidenote'
-    stream.skipToEnd()
-    return operator
-
-   match = stream.match /^```/
-   if match
-    stack.push indentation: stream.indentation(), type: 'code'
-    stream.skipToEnd()
-    return operator
-
+OPERATOR = "tag strong"
+OPERATOR_INLINE = "string"
+
+class Mode
+ constructor: (CodeMirror) ->
+  @CodeMirror = CodeMirror
+  @CodeMirror.defineMode "docscript", (@defineMode.bind this), "xml"
+  @CodeMirror.defineMIME "text/x-docscript", "docscript"
+
+ defineMode: (cmCfg, modeCfg) ->
+  @htmlMode = @CodeMirror.getMode cmCfg, name: "xml", htmlMode: true
+  @getMode()
+
+ matchBlock: (stream, state) ->
+  stack = state.stack
+
+  match = stream.match /^<<</
+  if match
+   stack.push indentation: stream.indentation(), type: 'html'
+   stream.skipToEnd()
+   state.htmlState = @CodeMirror.startState @htmlMode
+   return OPERATOR
+
+  match = stream.match /^\+\+\+/
+  if match
+   stack.push indentation: stream.indentation(), type: 'special'
+   stream.skipToEnd()
+   return OPERATOR
+
+  match = stream.match /^>>>/
+  if match
+   stack.push indentation: stream.indentation(), type: 'sidenote'
+   stream.skipToEnd()
+   return OPERATOR
+
+  match = stream.match /^```/
+  if match
+   stack.push indentation: stream.indentation(), type: 'code'
+   stream.skipToEnd()
+   return OPERATOR
+
+  return null
+
+ matchStart: (stream, state) ->
+  match = stream.match /^\!/
+  if match
+   state.media = true
+   return OPERATOR
+  match = stream.match /^\* /
+  if match
+   @clearState state
+   return OPERATOR
+  match = stream.match /^- /
+  if match
+   @clearState state
+   return OPERATOR
+  match = stream.match /^#/
+  if match
+   stream.eatWhile '#'
+   @clearState state
+   state.heading = true
+   return "#{OPERATOR} header"
+
+ matchInline: (stream, state) ->
+  match = stream.match /^``/
+  if match
+   state.code = not state.code
+   return OPERATOR_INLINE
+
+  if state.code
    return null

-  matchStart = (stream, state) ->
-   match = stream.match /^\!/
-   if match
-    state.media = true
-    return operator
-   match = stream.match /^\* /
-   if match
-    clearState state
-    return operator
-   match = stream.match /^- /
-   if match
-    clearState state
-    return operator
-   match = stream.match /^#/
-   if match
-    stream.eatWhile '#'
-    clearState state
-    state.heading = true
-    return "#{operator} header"
-
-  matchInline = (stream, state) ->
-   match = stream.match /^``/
-   if match
-    state.code = not state.code
-    return operatorInline
-
+  match = stream.match /^\*\*/
+  if match
+   state.bold = not state.bold
+   return OPERATOR_INLINE
+
+  match = stream.match /^--/
+  if match
+   state.italics = not state.italics
+   return OPERATOR_INLINE
+
+  match = stream.match /^__/
+  if match
+   state.subscript = not state.subscript
+   return OPERATOR_INLINE
+
+  match = stream.match /^\^\^/
+  if match
+   state.superscript = not state.superscript
+   return OPERATOR_INLINE
+
+  match = stream.match /^<</
+  if match
+   state.link = true
+   return OPERATOR_INLINE
+
+  match = stream.match /^>>/
+  if match
+   state.link = false
+   return OPERATOR_INLINE
+
+  return null
+
+ clearState: (state) ->
+  state.bold = false
+  state.italics = false
+  state.subscript = false
+  state.superscript = false
+  state.code = false
+  state.link = false
+
+ startState: ->
+  stack: []
+  htmlState: null
+  start: true
+
+  bold: false
+  italics: false
+  subscript: false
+  superscript: false
+  code: false
+  link: false
+
+  heading: false
+  media: false
+
+ blankLine: (state) ->
+  @clearState state
+
+ token: (stream, state) ->
+  if state.media
+   stream.skipToEnd()
+   state.media = false
+   return "link"
+
+  if stream.sol()
+   state.start = true
+   if state.heading
+    state.heading = false
+    @clearState state
+
+   s = stream.eatSpace()
+   if stream.eol()
+    @clearState state
+
+   return "" if s
+
+  stack = state.stack
+
+  if state.start
+   while stack.length > 0
+    if stack[stack.length - 1].indentation >= stream.indentation()
+     stack.pop()
+    else
+     break
+
+   types =
+    sidenote: false
+    html: false
+    special: false
+    code: false
+
+   for t in stack
+    types[t.type] = true
+
+   if not types.code and not types.html
+    match = @matchBlock stream, state
+    return match if match?
+
+  types =
+   sidenote: false
+   html: false
+   special: false
+   code: false
+
+  for t in stack
+   types[t.type] = true
+
+  l = ""
+
+  if types.html
+   l = @htmlMode.token stream, state.htmlState
+   l = "#{l}"
+  else if types.code
+   stream.skipToEnd()
+   l = "meta"
+  else
+   if state.start
+    match = @matchStart stream, state
+    return match if match
+
+   match = @matchInline stream, state
+   return match if match?
+
+   stream.next()
+   state.start = false
+
+   if state.heading
+    l += " header"
+   if state.bold
+    l += " strong"
+   if state.italics
+    l += " em"
+   if state.link
+    l += " link"
    if state.code
-    return null
-
-   match = stream.match /^\*\*/
-   if match
-    state.bold = not state.bold
-    return operatorInline
-
-   match = stream.match /^--/
-   if match
-    state.italics = not state.italics
-    return operatorInline
-
-   match = stream.match /^__/
-   if match
-    state.subscript = not state.subscript
-    return operatorInline
-
-   match = stream.match /^\^\^/
-   if match
-    state.superscript = not state.superscript
-    return operatorInline
-
-   match = stream.match /^<</
-   if match
-    state.link = true
-    return operatorInline
-
-   match = stream.match /^>>/
-   if match
-    state.link = false
-    return operatorInline
-
-   return null
-
-  clearState = (state) ->
-   state.bold = false
-   state.italics = false
-   state.subscript = false
-   state.superscript = false
-   state.code = false
-   state.link = false
-
+    l += " meta"
+
+  return l
+
+
+ getMode: ->
+  self = this

   mode =
-    fold: "indent"
-    startState: ->
-     stack: []
-     htmlState: null
-     start: true
-
-     bold: false
-     italics: false
-     subscript: false
-     superscript: false
-     code: false
-     link: false
-
-     heading: false
-     media: false
-
-    blankLine: (state) ->
-     clearState state
-
-    token: (stream, state) ->
-     if state.media
-      stream.skipToEnd()
-      state.media = false
-      return "link"
-
-     if stream.sol()
-      state.start = true
-      if state.heading
-       state.heading = false
-       clearState state
-
-      s = stream.eatSpace()
-      if stream.eol()
-       clearState state
-
-      return "" if s
-
-     stack = state.stack
-
-     if state.start
-      while stack.length > 0
-       if stack[stack.length - 1].indentation >= stream.indentation()
-        stack.pop()
-       else
-        break
-
-      types =
-       sidenote: false
-       html: false
-       special: false
-       code: false
-
-      for t in stack
-       types[t.type] = true
-
-      if not types.code and not types.html
-       match = matchBlock stream, state
-       return match if match?
-
-     types =
-      sidenote: false
-      html: false
-      special: false
-      code: false
-
-     for t in stack
-      types[t.type] = true
-
-     l = ""
-
-     if types.html
-      l = htmlMode.token stream, state.htmlState
-      l = "#{l}"
-     else if types.code
-      stream.skipToEnd()
-      l = "meta"
-     else
-      if state.start
-       match = matchStart stream, state
-       return match if match
-
-      match = matchInline stream, state
-      return match if match?
-
-      stream.next()
-      state.start = false
-
-      if state.heading
-       l += " header"
-      if state.bold
-       l += " strong"
-      if state.italics
-       l += " em"
-      if state.link
-       l += " link"
-      if state.code
-       l += " meta"
-
-     return l
-
-  mode
-), "xml"
-
-CodeMirror.defineMIME "text/x-docscript", "docscript"
+   fold: "indent"
+   startState: @startState
+   blankLine: (state) -> self.blankLine state
+   token: (stream, state) -> self.token stream, state
+
+  return mode
+
+
+new Mode CodeMirror
+


fix for logging

fix for logging

--- coffeescript/lib/bot/bot.coffee
+++ coffeescript/lib/bot/bot.coffee
@@ -11,7 +11,7 @@
       logHeader()
       @physics = physics.create()
       @throttles = []
-      @curvedVelocityFactor = 0.46
+      @curvedVelocityFactor = 0.48

 #      if @color is 'red'
 #        @throttle = 0.7
@@ -46,13 +46,13 @@
     crash: (data, control) ->
       winston.verbose data
       if data.data.color is @color
-        winston.info "#{logPrefix()};Crash, Curved vel old #{@curvedVelocityFactor}"
+        winston.info "#{@logPrefix()};Crash, Curved vel old #{@curvedVelocityFactor}"
         @reduceCurvedVelocityFactor()
-        winston.info "#{logPrefix()};Crash, Curved vel new #{@curvedVelocityFactor}"
+        winston.info "#{@logPrefix()};Crash, Curved vel new #{@curvedVelocityFactor}"
       control.ignore()

     reduceCurvedVelocityFactor: ->
-      @curvedVelocityFactor = 0.97 * @curvedVelocityFactor
+      @curvedVelocityFactor = 0.985 * @curvedVelocityFactor

     switchDirection: ->
       if @switchRight


Allow fbp-protocol-client to send secret with the request

--- components/ListenLibrary.coffee
+++ components/ListenLibrary.coffee
@@ -70,6 +70,6 @@
       @outPorts.error.send new Error 'No Runtime available'
       @outPorts.error.disconnect()
       return
-    @runtime.sendComponent 'list', ''
+    @runtime.sendComponent 'list', {}

 exports.getComponent = -> new ListenLibrary


various bugfixing

various bugfixing

--- lib/atom-ternjs-provider.coffee
+++ lib/atom-ternjs-provider.coffee
@@ -9,14 +9,14 @@
     suggestionsArr = []
     currentSuggestionIndex: 0
     disposables: []
-    maxItems = 1000
-    force = false
+    maxItems: 1000
+    force: false
     # automcomplete-plus
-    autocompletePlus = null
+    autocompletePlus: null
     selector: '.source.js'
     disableForSelector: '.source.js .comment'
     inclusionPriority: 1
-    excludeLowerPriority: true
+    excludeLowerPriority: false

     init: (manager) ->
         @manager = manager
@@ -34,7 +34,7 @@
         catch e then return false
         return true

-    fixPrefix: (prefix) ->
+    checkPrefix: (prefix) ->
         return '' if prefix.match(/(\s|;|\.|\"|\')$/) or prefix.replace(/\s/g, '').length is 0
         prefix

@@ -46,12 +46,11 @@
         if !@isValidPrefix(prefix) and !@force
             @manager.documentation.hide()
             return []
-
-        prefix = @fixPrefix(prefix)
+        prefix = @checkPrefix(prefix)

         that = this

-        new Promise (resolve) ->
+        return new Promise (resolve) ->
             that.manager.client.update(editor.getURI(), editor.getText()).then =>
                 that.manager.client.completions(editor.getURI(), {line: bufferPosition.row, ch: bufferPosition.column}).then (data) =>
                     that.clearSuggestions()
@@ -64,7 +63,7 @@
                         that.manager.documentation.hide()
                         return
                     for obj, index in data.completions
-                        if index == maxItems
+                        if index == that.maxItems
                             break
                         obj = that.fixCompletion(obj)

@@ -132,7 +131,7 @@
         @clearSuggestions()

     getMaxIndex: (length) ->
-        Math.min(maxItems, length)
+        Math.min(@maxItems, length)

     addSelector: (selector) ->
         @selector = @selector + ',' + selector


isModified only for file

isModified only for file

--- lib/code-context-builder.coffee
+++ lib/code-context-builder.coffee
@@ -28,7 +28,7 @@
     codeContext.argType = argType

     if argType == 'Line Number Based'
-      editor.save() if editor?.isModified()
+      editor.save()
     else if codeContext.selection.isEmpty() and codeContext.filepath?
       codeContext.argType = 'File Based'
       editor.save() if editor?.isModified()


Add log_in to mobile routes

Add log_in to mobile routes

--- mobile/apps/auth/index.coffee
+++ mobile/apps/auth/index.coffee
@@ -20,6 +20,9 @@
 app.post '/forgot_password', routes.submitForgotPassword
 app.get '/reset_password', routes.resetPassword

+console.log 'loginPagePath', loginPagePath
+
 # Login and signup pages
+app.get '/log_in', routes.login
 app.get loginPagePath, routes.login
 app.get signupPagePath, routes.signUp


:bug: Prevent generation of a returned array in drawLineDecorations method

--- lib/mixins/canvas-drawer.coffee
+++ lib/mixins/canvas-drawer.coffee
@@ -257,6 +257,8 @@
     for decoration in decorations
       context.fillStyle = @getDecorationColor(decoration)
       context.fillRect(0,y,canvasWidth,lineHeight)
+
+    return

   # Internal: Draws a highlight decoration on the passed-in context.
   #


Forgot to remove some scaffolding :P

Forgot to remove some scaffolding :P

--- lib/routers/books-router.coffee
+++ lib/routers/books-router.coffee
@@ -44,7 +44,6 @@
 			startPage = page-2
 			endPage = page+2

-		console.log "#{startPage} #{page} #{endPage}"
 		res.render 'books',
 			active: 'books'
 			page: page


:lipstick:

:lipstick:

--- lib/selection-count-view.coffee
+++ lib/selection-count-view.coffee
@@ -17,8 +17,8 @@
     @updateCount()

   updateCount: =>
-    editor = atom.workspace.getActiveEditor()
-    if count = editor?.getSelection().getText().length
+    count = atom.workspace.getActiveEditor()?.getSelection().getText().length
+    if count > 0
       @text("(#{count})").show()
     else
       @hide()


more tests

more tests

--- test/spec/updateGraph.coffee
+++ test/spec/updateGraph.coffee
@@ -1,6 +1,6 @@
 do ->
     afterEach ->
-        d3.select(".alchemy").remove()
+        d3.select("#alchemy").remove()

     describe "alchemy.updateGraph()", ->


add several values for following commits

--- src/environment.coffee
+++ src/environment.coffee
@@ -16,6 +16,11 @@
         footer: 'BOTTOM PANEL'
         leftHeader: 'LEFT PANEL'
         rightHeader: 'RIGHT PANEL'
+    #The Startup display
+    STARTUP:
+        size: 50
+        font: 'sans-serif'
+        text: 'Click here to Start'
     #default Sprite settings
     IMAGE_PATH: ""
     SPRITE_DEFAULT_CONFIG:
@@ -48,8 +53,9 @@
         borderColor: "white"
         borderAlpha: 1.0
         borderVisible: yes
-        fontName: "Arial"
+        fontName: "sans-serif"
         fontSize: 8
+        fontSpacing: 4
         fontColor: "white"
         fontAlpha: 1.0
         marginsTop: 5
@@ -66,6 +72,8 @@
     USE_AUDIO_TAG: false
     SOUND_DEFAULT_CONFIG:
         url: ""
+        loop: false
+        volume: 1.0
         playOnLoad: false
     #default timer settings
     TIMER_START_ON_CONSTRUCTION: yes
@@ -83,3 +91,62 @@
             fontSize: 'small'
             backgroundColor: 'silver'
 #end environment object
+
+#the Greenhorn engine style object
+_style =
+    main:
+        width: '74%'
+        height: '60%'
+        display: 'inline-block'
+        marginTop: '5%'
+        marginLeft: '13%'
+        border: "5px solid #{env.ENGINE.accentColor}"
+        borderRadius: '15px'
+        fontFamily: 'Tahoma, Geneva, sans-serif'
+        backgroundColor: env.ENGINE.foregroundColor
+    title:
+        width: '100%'
+        textAlign: 'center'
+        cssFloat: 'left'
+        clear: 'both'
+        display: 'initial'
+        marginTop: '1%'
+        marginBottom: '0px'
+        paddingBottom: '1%'
+        borderRadius: 'inherit'
+        borderBottom: "1px solid #{env.ENGINE.accentColor}"
+    panel:
+        width: '15%'
+        height: '78%'
+        cssFloat: 'left'
+        display: 'initial'
+        margin: '1%'
+        overflow: 'auto'
+        whiteSpace: 'pre'
+        fontSize: '.70em'
+    panelHeader:
+        textAlign: 'center'
+        marginTop: '0'
+        marginBottom: '5px'
+        paddingBottom: '2px'
+        borderBottom: "2px solid #{env.ENGINE.backgroundColor}"
+    canvas:
+        width: '65%'
+        height: '78%'
+        display: 'initial'
+        cssFloat: 'left'
+        borderRight: "1px solid #{env.ENGINE.accentColor}"
+        borderLeft: "1px solid #{env.ENGINE.accentColor}"
+        backgroundColor: env.ENGINE.backgroundColor
+    footer:
+        width: '100%'
+        display: 'initial'
+        textAlign: 'center'
+        cssFloat: 'left'
+        clear: 'both'
+        paddingTop: '1%'
+        marginBottom: '1%'
+        borderRadius: 'inherit'
+        borderTop: "1px solid #{env.ENGINE.accentColor}"
+        fontSize: '1em'
+#end _style object


feat(client libs): support symlinks in client libs folder

--- src/tasks/copy/copy-libs.coffee
+++ src/tasks/copy/copy-libs.coffee
@@ -3,8 +3,9 @@
 	log = require "#{config.req.helpers}/log"

 	runTask = (src, dest, appOrRb) ->
-		defer = q.defer()
-		gulp.src src
+		defer   = q.defer()
+		srcOpts = { follow: true }
+		gulp.src src, srcOpts
 			.pipe gulp.dest dest
 			.on 'end', ->
 				# console.log "copied #{appOrRb} libs".yellow


don't forget about the mailer

don't forget about the mailer

--- library/membrane/emailer.coffee
+++ library/membrane/emailer.coffee
@@ -55,6 +55,14 @@
     # Once inherited from, the inheritee is not abstract anymore.
     @abstract yes

+    # Allows to configure custom connection options for e-mailer.
+    # This is making sense if you want to have a service-isolated
+    # Mongo connection, using `EMAIL_ENVELOPE_SERVICE` and this
+    # connection is supposed to be wired into a different email
+    # provider or account. This variable is used to supply that.
+    # It should be a function, returning a mailer config object.
+    @EMAIL_CONFIG = undefined
+
     # These defintions are the presets available for configuring
     # the email envelope getting functions. Please set the special
     # class value `EMAIL_ENVELOPE` to either one of these values or
@@ -76,7 +84,8 @@
         @constructor.EMAIL_ENVELOPE ?= -> kernel
         envelope = this.constructor.EMAIL_ENVELOPE
         envelope = try envelope.apply this, arguments
-        config = try nconf.get("emailer") or undefined
+        amc = @constructor.MONGO_CONFIG or -> undefined
+        config = try nconf.get("emailer") or try amc()
         return next() unless _.isObject config or null
         return next() unless _.isObject kernel.emailer
         noConfig = "missing the emailer configuration"
@@ -99,7 +108,8 @@
         @constructor.EMAIL_ENVELOPE ?= -> kernel
         envelope = this.constructor.EMAIL_ENVELOPE
         envelope = envelope.apply this, arguments
-        config = nconf.get("emailer") or undefined
+        amc = @constructor.MONGO_CONFIG or -> null
+        config = try nconf.get("emailer") or amc()
         return next() unless _.isObject config or 0
         return next() if _.isObject envelope.emailer
         {transport, configure} = config or Object()


adding logging to scripts

adding logging to scripts

--- server/api/scripts/2noddy.coffee
+++ server/api/scripts/2noddy.coffee
@@ -5,6 +5,8 @@
     action: () ->
       # clapi/accounts -> noddy/accounts
       API.es.reindex 'clapi', 'accounts', API.es._mapping, 'noddy/accounts', false
+      API.log {msg:'Scripting accounts from clapi into noddy complete', level:info, notify:true}
+      return true

 API.add 'scripts/2noddy/bebejam',
   get:
@@ -14,6 +16,8 @@
       for tp in API.es.types 'noddy_dev'
         if tp.indexOf('bebejam_') isnt -1
           API.es.reindex 'noddy_dev', tp, API.es._mapping, 'bebejam/' + tp.replace('bebejam_','')
+      API.log {msg:'Scripting dev bebejam into its own index for noddy complete', level:info, notify:true}
+      return true

 _oab_requests_sherpa = (res) ->
   res._scripts ?= {}
@@ -71,8 +75,12 @@
       # oab_support is the same format so does not need reprocessing, but will need reindexing anyway to match new mapping
       API.es.reindex 'oab', 'support'

-      # oab_history should be snapshotted and backed up, then totally wiped
-      # same with oab_availability - just add a note to stats page of how many we had before our update (7243384) (1114919 without gsot.gbv.de) (from 27/10/2016 to 05/03/2018)
+      API.log {msg:'Scripting oab from oab into oab, but udpated and mapped for noddy, complete', level:info, notify:true}
+      return true
+
+      # wipe history and availability, the oab index folder has been backed up
+      # add a note to stats page of how many availabilities we had before our update
+      # (7243384) (1114919 without gsot.gbv.de) (from 27/10/2016 to 05/03/2018)

       # check file store capability for oabutton author request file upload


Refactor tooltip generation, add tooltip on edit drag.

--- app/assets/javascripts/leaflet/reactive_measure.js.coffee
+++ app/assets/javascripts/leaflet/reactive_measure.js.coffee
@@ -144,7 +144,7 @@
       perimeter: g.perimeter()
       area: g.area()

-    @__updateTooltipMeasure center, measure
+    @__tooltipMeasure.__updateTooltipMeasure center, measure, @options

   addHooks: () ->
@@ -161,60 +161,63 @@
     @__removeHooks.apply this, arguments
     return

-  __updateTooltipMeasure: (latLng, measure = {}) ->
+L.Edit.Poly.include
+  __addHooks: L.Edit.Poly.prototype.addHooks
+  __removeHooks: L.Edit.Poly.prototype.removeHooks
+
+  __onHandlerDrag: ->
+    center = @_poly.__getCenter()
+
+    g = new L.GeographicUtil.Polygon @_poly.getLatLngsAsArray()
+
+    measure =
+      perimeter: g.perimeter()
+      area: g.area()
+
+    @__tooltipMeasure.__updateTooltipMeasure center, measure, @options
+
+
+  addHooks: () ->
+    @__addHooks.apply this, arguments
+    if L.EditToolbar.reactiveMeasure
+      @__tooltipMeasure = new L.Tooltip @_poly._map, onTop: true
+      this._poly.on 'editdrag', @__onHandlerDrag
+
+  removeHooks: () ->
+    if L.EditToolbar.reactiveMeasure
+      this._poly.off 'editdrag'
+      @__tooltipMeasure.dispose()
+
+    @__removeHooks.apply this, arguments
+
+
+L.LatLng.prototype.toArray = ->
+  [@lat, @lng]
+
+L.Tooltip.include
+  __initialize: L.Tooltip.prototype.initialize
+
+  initialize: (map,options = {}) ->
+    @__initialize.apply this, arguments
+
+    if options.onTop
+      L.DomUtil.addClass(@_container, 'leaflet-draw-tooltip-top')
+
+  __updateTooltipMeasure: (latLng, measure = {}, options = {}) ->
     labelText =
       text: ''
     #TODO: use L.drawLocal to i18n tooltip
     if measure['perimeter']
-      labelText['text'] += "#{L.GeometryUtil.readableDistance(measure['perimeter'], @options.metric, @options.feet)}"
+      labelText['text'] += "#{L.GeometryUtil.readableDistance(measure.perimeter, !!options.metric, !!options.feet)}"

     if measure['area']
-      labelText['text']  += "#{L.GeometryUtil.readableArea(measure['area'], @options.metric)}"
+      labelText['text']  += "#{L.GeometryUtil.readableArea(measure.area, !!options.metric)}"

     if latLng
-      @__tooltipMeasure.updateContent labelText
-      @__tooltipMeasure.updatePosition latLng
-
-    return
-
-L.Edit.Poly.include
-  __addHooks: L.Edit.Poly.prototype.addHooks
-  __removeHooks: L.Edit.Poly.prototype.removeHooks
-
-  __onHandlerDrag: (e) ->
-    new_poly = e.target
-    latLngs = new_poly.getLatLngs()
-    geod = GeographicLib.Geodesic.WGS84
-
-    poly = geod.Polygon(false)
-    for latlng in latLngs
-      poly.AddPoint latlng['lat'], latlng['lng']
-
-    poly = poly.Compute(false, true)
-    console.log 'Perimeter/area of polygon are ' + poly.perimeter.toFixed(3) + ' m / ' + poly.area.toFixed(1) + ' m^2.'
-
-  addHooks: () ->
-    @__addHooks.apply this, arguments
-    if L.EditToolbar.reactiveMeasure
-      this._poly.on 'editdrag', @__onHandlerDrag, this
-
-  removeHooks: () ->
-    if L.EditToolbar.reactiveMeasure
-      this._poly.off 'editdrag'
-    @__removeHooks.apply this, arguments
-
-
-L.LatLng.prototype.toArray = ->
-  [@lat, @lng]
-
-L.Tooltip.include
-  __initialize: L.Tooltip.prototype.initialize
-
-  initialize: (map,options = {}) ->
-    @__initialize.apply this, arguments
-
-    if options.onTop
-      L.DomUtil.addClass(@_container, 'leaflet-draw-tooltip-top')
+      @updateContent labelText
+      @updatePosition latLng
+
+    return

 ###
 #Add Configuration options


auth pop

auth pop

--- app/assets/javascripts/swell_media/custom/login.js.coffee
+++ app/assets/javascripts/swell_media/custom/login.js.coffee
@@ -67,7 +67,7 @@

 	$('body').on(
 		'click',
-		'a.auth-popup',
+		'a.auth-pop',
 	(e) ->
 		popupCenter($(this).attr("href"), $(this).attr("data-width") || 600, $(this).attr("data-height") || 400, "authPopup");
 		e.stopPropagation(); return false;


move sort/filter strip to bottom

move sort/filter strip to bottom

Former-commit-id: 0c3b49c0769229c003d2c7411fe4f4f8dd4134d0 [formerly 4ba463fae06ccad6f3c98799f821f9bc789456fd] [formerly 754825a5eed1b702941ef06a08761305e57a937c [formerly 754825a5eed1b702941ef06a08761305e57a937c [formerly 592db9e56e455ea5904d81c90323e8fa07a0ef4d]]]
Former-commit-id: 18091603ef73608f52441cef92ca66b0103757ff [formerly b0cd96ae2b4a6fb0066e32c48645c680236c92ad]
Former-commit-id: 13c73b71b3d33d07fa690ba4a35cebbac8398d5d
Former-commit-id: 4fd9e68e6327d19a7b027ea15ddaad3d4e4c4a53

--- app/assets/javascripts/views_list/proposallistview.coffee
+++ app/assets/javascripts/views_list/proposallistview.coffee
@@ -61,14 +61,12 @@
   render : ->
     # @undelegateEvents()
     super
+    @render_pagination()
     @render_header()
-    @render_pagination()

   #TODO: do this when login as admin
   render_header : ->
-    #@$el.find('.m-proposals-list-header').remove()
-
     $heading_el = @proposals_header_template({
       is_admin : ConsiderIt.roles.is_admin
       selected_sort : @sort_selected
@@ -79,7 +77,7 @@
     if $cur_heading.length > 0
       $cur_heading.replaceWith $heading_el
     else
-      @$el.prepend($heading_el)
+      @$el.append($heading_el)

     can_create = ConsiderIt.current_user.is_logged_in() && (ConsiderIt.roles.is_admin || ConsiderIt.roles.is_manager || ConsiderIt.current_tenant.get('enable_user_conversations'))


expanding docs

expanding docs

--- src/engine_single_camera.coffee
+++ src/engine_single_camera.coffee
@@ -1,3 +1,13 @@
+###*
+The SingleCamera module provides provides a single camera view of the game.
+Its transform can be adjusted to view different areas and provide various camera effects.
+
+@name SingleCamera
+@fieldOf Engine
+@module
+@param {Object} I Instance variables
+@param {Object} self Reference to the engine
+###
 Engine.SingleCamera = (I, self) ->
   # Set some default properties
   Object.reverseMerge I,
@@ -20,6 +30,9 @@

       # TODO This should be triggering a camera draw
       # camera.trigger "draw", I.canvas
+      # possibly even with before and after.
+      # This is where per camera effects like shake,
+      # flash and fade will come in.

   # No public methods
   return {}


Refactor specs

Refactor specs

--- spec/bounded_context_spec.coffee
+++ spec/bounded_context_spec.coffee
@@ -176,7 +176,7 @@
       it 'should execute the query directly on the correct read aggregate repository', ->
         BoundedContext = eventric 'BoundedContext'
         class FooReadAggregateRepository
-          findByExample: sandbox.stub()
+          findById: sandbox.stub()
         class ExampleBoundedContext extends BoundedContext
           readAggregateRepositories:
             'Aggregate': FooReadAggregateRepository
@@ -184,15 +184,12 @@
         exampleBoundedContext.initialize()

         query =
-          name: 'Aggregate:findByExample'
+          name: 'Aggregate:findById'
           id: 42
-          params:
-            foo: 'bar'
-

         exampleBoundedContext.query query

-        expect(FooReadAggregateRepository::findByExample.calledWith query.id, query.params).to.be.ok()
+        expect(FooReadAggregateRepository::findById.calledWith query.id).to.be.ok()

     describe 'has a registered handler', ->
@@ -200,23 +197,23 @@
         BoundedContext = eventric 'BoundedContext'
         exampleApplicationService =
           queries:
-            'Aggregate:findByExample': 'aggregateFindByExample'
-          aggregateFindByExample: sandbox.stub()
+            'customQuery': 'customQueryMethod'
+          customQueryMethod: sandbox.stub()

         class ExampleBoundedContext extends BoundedContext
           applicationServices: [
             exampleApplicationService
           ]
+        exampleBoundedContext = new ExampleBoundedContext
+        exampleBoundedContext.initialize()

         query =
-          name: 'Aggregate:findByExample'
+          name: 'customQuery'
           params:
             foo: 'bar'
-        exampleBoundedContext = new ExampleBoundedContext
-        exampleBoundedContext.initialize()
         exampleBoundedContext.query query

-        expect(exampleApplicationService.aggregateFindByExample.calledWith query.params).to.be.ok()
+        expect(exampleApplicationService.customQueryMethod.calledWith query.params).to.be.ok()

   describe 'onDomainEvent', ->


:white_check_mark: for required modules on startup :warning:

--- spec/fast-activation-spec.coffee
+++ spec/fast-activation-spec.coffee
@@ -1,59 +1,139 @@
+# [DANGER]
+# What I'm doing in this test-spec is SUPER hacky, and I don't like this.
+#
+# - What I'm doing and why
+#  - Invalidate require.cache to "observe required file on startup".
+#  - Then restore require.cache to original state.
+#
+# - Just invalidating is not enough unless restoreing other spec file fail.
+#
+# - What happens just invalidate require.cache and NOT restored to original require.cache?
+#  - For module such like `globlal-state.coffee` it instantiated at required time.
+#  - Invalidating require.cache for `global-state.coffee` means, it's reloaded again.
+#  - This 2nd reload return DIFFERENT globalState instance.
+#  - So globalState is now no longer globally referencing same same object, it's broken.
+#  - This situation is caused by explicit cache invalidation and not happen in real usage.
+#
+# - I know this spec is still super hacky and I want to find safer way.
+#  - But I need this spec to detect unwanted file is required at startup( vmp get slower startup ).
+describe "dirty work for fast package activation", ->
+  withCleanActivation = null
+  ensureRequiredFiles = null

-describe "dirty work for fast package activation", ->
-  [main] = []
   beforeEach ->
-    waitsForPromise ->
-      atom.packages.activatePackage('vim-mode-plus').then (pack) ->
-        main = pack.mainModule
+    runs ->
+      packPath = atom.packages.loadPackage('vim-mode-plus').path
+      getRequiredLibOrNodeModulePaths = ->
+        Object.keys(require.cache).filter (p) ->
+          p.startsWith(packPath + 'lib') or p.startsWith(packPath + 'node_modules')
+
+      withCleanActivation = (fn) ->
+        savedCache = {}
+        runs ->
+          getRequiredLibOrNodeModulePaths().forEach (p) ->
+            savedCache[p] = require.cache[p]
+            delete require.cache[p]
+
+        waitsForPromise ->
+          atom.packages.activatePackage('vim-mode-plus').then (pack) ->
+            return fn(pack)
+
+        runs ->
+          oldPaths = Object.keys(savedCache)
+          newPaths = getRequiredLibOrNodeModulePaths()
+          newPaths.forEach (p) ->
+            if p in oldPaths
+              require.cache[p] = savedCache[p]
+            else
+              delete require.cache[p]
+
+      ensureRequiredFiles = (files) ->
+        should = files.map((file) -> packPath + file)
+        expect(getRequiredLibOrNodeModulePaths()).toEqual(should)

   describe "requrie as minimum num of file as possible on startup", ->
+    shouldRequireFilesInOrdered = [
+      "lib/main.coffee"
+      "lib/base.coffee"
+      "node_modules/delegato/lib/delegator.js"
+      "node_modules/mixto/lib/mixin.js"
+      "lib/settings.coffee"
+      "lib/global-state.coffee"
+      "lib/vim-state.coffee"
+      "lib/mode-manager.coffee"
+      "lib/command-table.coffee"
+    ]
+    if atom.inDevMode()
+      shouldRequireFilesInOrdered.push('lib/developer.coffee')
+
     # * To reduce IO and compile-evaluation of js file on startup
-    describe "with no editor opened", ->
-    beforeEach ->
-      waitsForPromise ->
-        atom.packages.activatePackage('vim-mode-plus')
+    it "require minimum set of files", ->
+      withCleanActivation ->
+        ensureRequiredFiles(shouldRequireFilesInOrdered)

-  fdescribe "command-table", ->
+    it "[one editor opened] require minimum set of files", ->
+      withCleanActivation ->
+        atom.workspace.open().then ->
+          files = shouldRequireFilesInOrdered.concat('lib/status-bar-manager.coffee')
+          ensureRequiredFiles(files)
+
+    it "[after motion executed] require minimum set of files", ->
+      withCleanActivation ->
+        atom.workspace.open().then (e) ->
+          atom.commands.dispatch(e.element, 'vim-mode-plus:move-right')
+          extraShouldRequireFilesInOrdered = [
+            "lib/status-bar-manager.coffee"
+            "lib/operation-stack.coffee"
+            "lib/selection-wrapper.coffee"
+            "lib/utils.coffee"
+            "node_modules/underscore-plus/lib/underscore-plus.js"
+            "node_modules/underscore/underscore.js"
+            "lib/blockwise-selection.coffee"
+            "lib/motion.coffee"
+            "lib/cursor-style-manager.coffee"
+          ]
+          files = shouldRequireFilesInOrdered.concat(extraShouldRequireFilesInOrdered)
+          ensureRequiredFiles(files)
+
+  describe "command-table", ->
     # * Loading atom commands from pre-generated command-table.
     # * Why?
     #  vmp adds about 300 cmds, which is huge, dynamically calculating and register cmds
     #  took very long time.
     #  So calcluate non-dynamic par then save to command-table.coffe and load in on startup.
     #  When command are executed, necessary command class file is lazy-required.
-    beforeEach ->
-      waitsForPromise ->
-        atom.packages.activatePackage('vim-mode-plus').then (pack) ->
-          main = pack.mainModule
-
     describe "initial classRegistry", ->
       it "contains one entry and it's Base class", ->
-        Base = main.provideVimModePlus().Base
-        classRegistry = Base.getClassRegistry()
-        keys = Object.keys(classRegistry)
-        expect(keys).toHaveLength(1)
-        expect(keys[0]).toBe("Base")
-        expect(classRegistry[keys[0]]).toBe(Base)
+        withCleanActivation (pack) ->
+          Base = pack.mainModule.provideVimModePlus().Base
+          classRegistry = Base.getClassRegistry()
+          keys = Object.keys(classRegistry)
+          expect(keys).toHaveLength(1)
+          expect(keys[0]).toBe("Base")
+          expect(classRegistry[keys[0]]).toBe(Base)

     describe "fully populated classRegistry", ->
       it "generateCommandTableByEagerLoad populate all registry eagerly", ->
-        Base = main.provideVimModePlus().Base
-        oldRegistries = Base.getClassRegistry()
-        oldRegistriesLength = Object.keys(oldRegistries).length
-        expect(Object.keys(oldRegistries)).toHaveLength(1)
+        withCleanActivation (pack) ->
+          Base = pack.mainModule.provideVimModePlus().Base
+          oldRegistries = Base.getClassRegistry()
+          oldRegistriesLength = Object.keys(oldRegistries).length
+          expect(Object.keys(oldRegistries)).toHaveLength(1)

-        Base.generateCommandTableByEagerLoad()
-        newRegistriesLength = Object.keys(Base.getClassRegistry()).length
-        expect(newRegistriesLength).toBeGreaterThan(oldRegistriesLength)
+          Base.generateCommandTableByEagerLoad()
+          newRegistriesLength = Object.keys(Base.getClassRegistry()).length
+          expect(newRegistriesLength).toBeGreaterThan(oldRegistriesLength)

     describe "make sure cmd-table is NOT out-of-date", ->
       it "generateCommandTableByEagerLoad return table which is equals to initially loaded command table", ->
-        Base = main.provideVimModePlus().Base
-        [oldCommandTable, newCommandTable] = []
+        withCleanActivation (pack) ->
+          Base = pack.mainModule.provideVimModePlus().Base
+          [oldCommandTable, newCommandTable] = []

-        oldCommandTable = Base.commandTable
-        newCommandTable = Base.generateCommandTableByEagerLoad()
-        loadedCommandTable = require('../lib/command-table')
+          oldCommandTable = Base.commandTable
+          newCommandTable = Base.generateCommandTableByEagerLoad()
+          loadedCommandTable = require('../lib/command-table')

-        expect(oldCommandTable).not.toBe(newCommandTable)
-        expect(loadedCommandTable).toEqual(oldCommandTable)
-        expect(loadedCommandTable).toEqual(newCommandTable)
+          expect(oldCommandTable).not.toBe(newCommandTable)
+          expect(loadedCommandTable).toEqual(oldCommandTable)
+          expect(loadedCommandTable).toEqual(newCommandTable)


Correct propTypes, check toggleExercise existance

--- src/components/exercise-card.cjsx
+++ src/components/exercise-card.cjsx
@@ -8,12 +8,13 @@

 ExerciseCard = React.createClass

-  PropTypes:
+  propTypes:
     displayFeedback: React.PropTypes.bool
     panelStyle: React.PropTypes.string
     header:     React.PropTypes.element
     displayAllTags: React.PropTypes.bool
     hideAnswers: React.PropTypes.bool
+    toggleExercise: React.PropTypes.func
     className: React.PropTypes.className
     exercise:   React.PropTypes.shape(
       content: React.PropTypes.object
@@ -46,7 +47,7 @@

   onClick: (ev) ->
     # don't toggle exercise selection click target is a link (such as "Report an error")
-    @props.toggleExercise() unless ev.target.tagName is 'A'
+    @props.toggleExercise?() unless ev.target.tagName is 'A'

   render: ->
     content = @props.exercise.content


Add options prop to loadable-item for passing to store

With this property options can be passed to the store.  It's
intended to be used for stores that need to pass multiple
parameters to their endpoints.

For instance the learning guide for a teacher viewing a student
must pass the courseId and roleId to the endpoint when making a request.

--- src/components/loadable-item.cjsx
+++ src/components/loadable-item.cjsx
@@ -13,6 +13,7 @@
   displayName: 'LoadableItem'
   propTypes:
     id: React.PropTypes.string.isRequired
+    options: React.PropTypes.object
     store: React.PropTypes.object.isRequired
     actions: React.PropTypes.object.isRequired
     renderItem: React.PropTypes.func.isRequired
@@ -25,20 +26,20 @@
   componentDidUpdate: (oldProps) -> @reload(oldProps)

   reload: (oldProps) ->
-    {id, store, load, actions} = @props
-
+    {id, store, load, actions, options} = @props
     return unless id?

     # Skip reloading if all the props are the same (the case in the Calendar for some reason)
-    if oldProps.id is id and oldProps.store is store and oldProps.actions is actions and oldProps.load is load
-      return
+    return if oldProps.id is id and oldProps.store is store and oldProps.actions is actions and
+      oldProps.load is load and _.isEqual(oldProps.options, options)

     load ?= actions.load
-    unless store.isNew(id)
-      load(id)
+    unless store.isNew(id, options)
+      load(id, options)

   render: ->
-    {id, store, actions, load, isLoaded, isLoading, renderItem, saved, renderLoading, renderError, renderBug, update} = @props
+    { id, store, actions, load, isLoaded, isLoading, renderItem,
+      saved, renderLoading, renderError, renderBug, update, options} = @props

     load ?= actions.load
     isLoaded ?= store.isLoaded
@@ -48,15 +49,15 @@
       # if id is undefined, render as loading. loadableItem is waiting for id to be retrieved.
       return true unless id?

-      if store.get(id)
+      if store.get(id, options)
         false
-      else if isLoading(id)
+      else if isLoading(id, options)
         true
-      else if isLoaded(id)
+      else if isLoaded(id, options)
         false
-      else if store.isUnknown(id) or store.reload(id)
+      else if store.isUnknown(id, options) or store.reload(id, options)
         true
-      else if store.isNew(id) and store.get(id).id and saved
+      else if store.isNew(id, options) and store.get(id, options).id and saved
         # If this store was just created, then call the onSaved prop
         saved()
       else


:green_heart: Remove uncertainty in test

--- spec/minimap-element-spec.coffee
+++ spec/minimap-element-spec.coffee
@@ -736,7 +736,9 @@
       describe 'and when preferredLineLength >= 16384', ->
         beforeEach ->
           atom.config.set 'minimap.preferredLineLength', 16384
-          nextAnimationFrame()
+          waitsFor -> minimapElement.frameRequested
+          runs ->
+            nextAnimationFrame()

         it 'adjusts the width of the minimap', ->
           expect(minimapElement.offsetWidth).toBeCloseTo(editorElement.offsetWidth / 11, -1)


ContinuumView has moved

ContinuumView has moved

--- bokeh/server/static/coffee/usercontext/usercontext.coffee
+++ bokeh/server/static/coffee/usercontext/usercontext.coffee
@@ -1,5 +1,5 @@
 base = require("../base")
-ContinuumView = base.ContinuumView
+ContinuumView = require("../common/continuum_view").ContinuumView
 HasParent = base.HasParent
 HasProperties = base.HasProperties
 load_models = base.load_models


simplify points before drawing

simplify points before drawing

--- app/graph.coffee
+++ app/graph.coffee
@@ -58,6 +58,7 @@
     areaMode: 'none'
     areaOffset: 'zero'
     drawNullAsZero: false
+    simplify: 0.5
     #lineWidth: 1

   draw: (container, data, params) ->
@@ -185,16 +186,21 @@
           d.y0 = y0
           d.value = y)

+    if params.simplify
+      simplify = _.partial simplifyPoints, params.simplify
+    else
+      simplify = _.identity
+
     if params.drawNullAsZero
       transform_value = (v) -> v ? 0
-      filter_scatter_values = _.identity
-      expand_line_values = _.identity
+      filter_scatter_values = simplify
+      expand_line_values = simplify
     else
       # TODO this won't work well with stack
       transform_value = (v) -> v
       filter_scatter_values = (values) ->
-        _.filter values, (d) -> d.value?
-      expand_line_values = expandIsolatedValuesToLineSegments
+        simplify _.filter values, (d) -> d.value?
+      expand_line_values = _.compose simplify, expandIsolatedValuesToLineSegments

     value_min = null
     value_max = null
@@ -208,8 +214,6 @@
         value_max = Math.max value, value_max
         {value, time: time, x: x(time), original: datapoint}
       bisector = d3.bisector (d) -> d.time
-      lineValues = expand_line_values values
-      scatterValues = filter_scatter_values values
       lineMode =
         if params.areaMode is 'all' or params.areaMode is 'stacked'
           'area'
@@ -217,13 +221,12 @@
           'area'
         else
           'line'
-
       lineFn = if lineMode is 'line'
         line
       else
         area
       targetColor = color targetIndex
-      {values, bisector, name: s.target, lineValues, scatterValues, lineMode, lineFn, color: targetColor}
+      {values, bisector, name: s.target, lineMode, lineFn, color: targetColor}

     if params.areaMode is 'stacked'
       stack targets
@@ -239,6 +242,14 @@
       value_min = Math.round(value_min) - 1
       value_max = Math.round(value_max) + 1
     y.domain [params.yMin ? value_min, params.yMax ? value_max]
+
+    _.each targets, (target) ->
+      {values} = target
+      _.each values, (v) ->
+        if v.value?
+          v.y = y v.value
+      target.lineValues = expand_line_values values
+      target.scatterValues = filter_scatter_values values

     svg = d3.select(container).append('svg')
         .attr('width', width + margin.left + margin.right)
@@ -423,3 +434,25 @@
   if segment_length is 1
     result.push previous
   result
+
+simplifyPoints = (minDistance, values) ->
+  previous = null
+  result = []
+
+  _.each values, (v) ->
+    if previous?
+      if previous.y? != v.y? # discontinuity
+        result.push v
+        if v.y?
+          previous = v
+      else if v.y?
+        deltaX = Math.abs v.x - previous.x
+        deltaY = Math.abs v.y - previous.y
+        if Math.sqrt(deltaX * deltaX + deltaY * deltaY) > minDistance
+          previous = v
+          result.push v
+    else
+      previous = v
+      result.push v
+
+  result


Limit slide duration

Limit slide duration

--- app/slide.coffee
+++ app/slide.coffee
@@ -13,6 +13,10 @@
       duration: 10
       hidden: false
     }, data)
+    if @data.duration > 30
+      @data.duration = 30
+    if @data.duration < 1
+      @data.duration = 1

   toJSON: ->
     @data


Add levels to the js-compiler

Add levels to the js-compiler

--- src/js/compilers/js/main.coffee
+++ src/js/compilers/js/main.coffee
@@ -1,4 +1,8 @@
-define ->
+define [
+	'Util'
+], (
+	Util
+) ->
 	'use strict'

 	compileColor = (color) ->
@@ -15,7 +19,7 @@
 		lines = []
 		push = lines.push.bind lines

-		push "const colors = {"
+		push "var colors = {"

 		colors.forEach (color) ->
 			push "#{compileColor color},"
@@ -29,7 +33,7 @@
 	compileObject = ({ name, data, blocking }) ->
 		colorData = for line in data
 			for color in line
-				"...colors['#{color}']"
+				"colors['#{color}']"

 		"""
 			'#{name}': {
@@ -43,7 +47,7 @@
 		lines = []
 		push = lines.push.bind lines

-		push "const objects = {"
+		push "var objects = {"

 		objects.forEach (object) ->
 			push "#{compileObject object},"
@@ -54,10 +58,76 @@
 		lines.join '\n'

+	compileLevelSize = (levels) ->
+		(
+			levels.map ({ name, data }) ->
+				"""
+					'#{name}' : {
+						x: #{data[0].length},
+						y: #{data.length},
+					}
+				"""
+		).join ',\n'
+
+
+	compileLevel = (legend, { name, data }) ->
+		lines = data.map (line) ->
+			Array::map.call line, (item) ->
+				legend[item]
+
+		"'#{name}': #{JSON.stringify lines}"
+
+
+	compileLevels = (legend, levels) ->
+		legendIndexed = Util.arrayToObject legend, 'name', 'objectName'
+
+		lines = []
+		push = lines.push.bind lines
+
+		push "{"
+
+		levels.forEach (level) ->
+			push "#{compileLevel legendIndexed, level},"
+			return
+
+		push "}"
+
+		lines.join '\n'
+
+
 	compile = (spec) ->
+		params = Util.arrayToObject spec.params, 'name', 'parts'
+
 		"""
 			#{compileColors spec.colors}
 			#{compileObjects spec.objects}
+
+			var sprites = {}
+
+			var params = {
+				scale: #{params['scale'][0]},
+				tileSize: {
+					x: #{spec.objects[0].data[0].length},
+					y: #{spec.objects[0].data.length},
+				},
+				cameraSize: {
+					x: #{params['camera'][0]},
+					y: #{params['camera'][1]},
+				},
+				levelSize: #{compileLevelSize spec.levels},
+			}
+
+			// state
+			var state = {
+				player: {
+					position: {
+						x: #{params['start_location'][0]},
+						y: #{params['start_location'][1]},
+						level: "#{params['start_location'][2]}",
+					},
+				},
+				levels: #{compileLevels spec.legend, spec.levels},
+			}
 		"""

 	{


fixed coffelint error

fixed coffelint error

--- client/admin/lib/views/invitations/inviteditemview.coffee
+++ client/admin/lib/views/invitations/inviteditemview.coffee
@@ -71,7 +71,7 @@
       cssClass   : 'avatarview'
       attributes :
         src      : "//gravatar.com/avatar/#{hash}?s=#{size}&d=#{defaultAvatarUri}"
-
+
     @timeAgoView  = new KDTimeAgoView { click: @bound 'showSettings' }, modifiedAt or createdAt

     if statusType is 'pending'


Start vim-mode in insert mode

Start vim-mode in insert mode

--- atom/config.cson
+++ atom/config.cson
@@ -29,6 +29,8 @@
   "markdown-deluxe":
     fontFamily: "Hack"
   "expand-region": {}
+  "vim-mode":
+    startInInsertMode: true
 "source.gfm":
   editor:
     softWrap: true


Add dash as word boundary for CSS/Less/Sass

Also use the same word boundary characters for all of these languages.


Updated paths to Python linters in Atom.

--- atom/config.cson
+++ atom/config.cson
@@ -13,8 +13,6 @@
     ]
     disabledPackages: [
       "metrics"
-      "autocomplete"
-      "linter-jshint"
       "language-django"
     ]
     themes: [
@@ -47,6 +45,7 @@
     lintOnFly: false
   "linter-flake8":
     executableDir: "/Users/jonklo/Code/tienda/bin/"
+    executablePath: "/Users/jonklo/Code/tienda/bin/flake8"
     ignoreErrorCodes: [
       "E128"
       "E261"
@@ -54,7 +53,7 @@
     maxLineLength: 160
   "linter-pylint":
     rcFile: "/Users/jonklo/Code/tienda/config/linters/pylint.rc"
-    executable: "/Users/jonklo/Code/tienda/bin/pylint"
+    executable: "/usr/local/bin/pylint"
   whitespace:
     ignoreWhitespaceOnCurrentLine: false
   "atom-beautify":