Fixed change direct_payment

Fixed change direct_payment

--- app/assets/javascripts/app/transaction.coffee
+++ app/assets/javascripts/app/transaction.coffee
@@ -275,8 +275,8 @@

   #
   setEvents: ->
+    self = this
     super()
-    self = this
     $('body').on('ajax-call', '.item_id', (event, resp) ->
       tr = $(this).parents('tr').get(0)
       mod = self.where(elem: tr)[0]


archived -> archive

archived -> archive

--- app/assets/javascripts/models/question.coffee
+++ app/assets/javascripts/models/question.coffee
@@ -25,6 +25,6 @@

   oppositeState: ->
     if @state() == null || /open/i.test(@state())
-      'Archived'
+      'Archive'
     else
       'Open'


Remove debugging message

Remove debugging message

--- app/assets/javascripts/modules/help.js.coffee
+++ app/assets/javascripts/modules/help.js.coffee
@@ -226,7 +226,6 @@
       $tip.find('[data-role=next]').show().toggleClass('disabled', index == queue.length - 1)

   showCurrent = ->
-    console.log queue.length, queue[index]
     if queue[index].wait
       wait(showCurrentImmediate)
     else if queue[index].waitOnAnimate
@@ -246,8 +245,6 @@
     e.preventDefault()
     startTour()

-
-
   add = (key, options={}) ->
     unless tourRunning
       options = $.extend({}, defaultHelpOptions, tourOptions[key], options, key: key)


adjust ajax requests latency user experience

--- app/assets/javascripts/picked_words.js.coffee
+++ app/assets/javascripts/picked_words.js.coffee
@@ -42,7 +42,7 @@
         timeout: 8000
         beforeSend: ->
           $('#messages').contents().remove()
-          p.list.children().fadeOut 'fast'
+          p.list.children().fadeOut()
           p.errorMesg.hide()
           p.nopicksMesg.delay(1000).fadeOut 'fast'
           p.loadingMesg.delay(1000).fadeIn('fast').spin {radius: 5, width: 3, top: 40}


Updated comment

Updated comment

--- app/assets/javascripts/player/movie.js.coffee
+++ app/assets/javascripts/player/movie.js.coffee
@@ -46,7 +46,7 @@

   data: ->
     unless @_data
-      # fallback for webworker
+      # Web Worker fallback
       d = @model.get('escaped_stdout_data')
       d = atob(d)
       d = ArchUtils.bz2.decode(d)


Added screenmask to DeviceComponent

Added screenmask to DeviceComponent


removed reference to removed file, setting to generate setters first #6

--- lib/my-package.coffee
+++ lib/my-package.coffee
@@ -1,7 +1,6 @@
 MyPackageView = require './my-package-view'
 BaseCommand = require './base-command'
 PhpParser = require './php-parser'
-ClassWritter = require './class-writter'
 TemplateManager = require './template-manager'

 module.exports =
@@ -10,6 +9,7 @@
     configDefaults:
         doNotTypeHint: ["mixed", "int","integer", "double", "float", "number", "string", "boolean", "bool", "numeric", "unknown"]
         camelCasedMethodNames: true
+        generateSettersFirst: false
         getterTemplate: "
 \ \ \ \ /**\n
 \ \ \ \ * Get the value of %description% \n
@@ -71,12 +71,17 @@

         cw = new TemplateManager(functions)

-        editor = atom.workspace.getActiveEditor()
+        generateSettersFirst = atom.config.get 'php-getters-setters.generateSettersFirst'

         code = ''
         for variable in variables
-            code += cw.writeGetter(variable)
-            code += cw.writeSetter(variable)
+
+            if generateSettersFirst
+                code += cw.writeSetter(variable)
+                code += cw.writeGetter(variable)
+            else
+                code += cw.writeGetter(variable)
+                code += cw.writeSetter(variable)

         @bc.writeAtEnd(code)


don't htmlEscape urls in assignment

don't htmlEscape urls in assignment

fixes CNVS-25850

Test plan:
 0. Go to Gradebook in a Course with Assignments.
 1. Click on the name of the assignment.
 2. Verify that the assignment show page loads.

Change-Id: Id876d582b538eea175bb207fbba58a8a4832a699
Reviewed-on: https://gerrit.instructure.com/68825
Reviewed-by: Spencer Olson <7cc0890d6f1f70810a023e7173eca5724d6c8e49@instructure.com>
Tested-by: Jenkins
Reviewed-by: Derek Bender <d24d8a7ee520e34b1cec38800225d9f36b552df7@instructure.com>
QA-Review: Pedro Fajardo <121b528f0c2f2e923ce3103793aca31b9a662ac4@instructure.com>
Reviewed-by: Jeremy Stanley <b3f594e10a9edcf5413cf1190121d45078c62290@instructure.com>
Product-Review: Keith T. Garner <a1a80143a840c140f31c3495ebbd9ec906e09fed@instructure.com>

--- app/coffeescripts/gradebook2/Gradebook.coffee
+++ app/coffeescripts/gradebook2/Gradebook.coffee
@@ -288,7 +288,12 @@
         @assignmentGroups[group.id] = group
         group.assignments = _.select group.assignments, (a) -> a.published
         for assignment in group.assignments
+          htmlUrl = assignment.html_url
+          submissionsDownloadUrl = assignment.submissions_download_url
           htmlEscape(assignment)
+          assignment.html_url = htmlUrl
+          assignment.submissions_download_url = submissionsDownloadUrl
+
           assignment.assignment_group = group
           assignment.due_at = tz.parse(assignment.due_at)
           @assignments[assignment.id] = assignment


Fixes slow or broken key jumps.

Fixes slow or broken key jumps.

- Removes collisions with vim-mode.
- Fixes #1.
- Fixes #13.

--- lib/jumpy-view.coffee
+++ lib/jumpy-view.coffee
@@ -20,6 +20,7 @@
     that = this
     for c in characters
       atom.workspaceView.command "jumpy:#{c}", (c) -> that.getKey(c)
+    @backedUpKeyBindings = _.clone(atom.keymap.keyBindings)

   getKey: (character) ->
       character = character.type.charAt(character.type.length - 1)
@@ -42,6 +43,7 @@
       atom.workspaceView.eachEditorView (e) ->
           e.find('.jumpy').remove()
           e.removeClass 'jumpy-specificity-1 jumpy-specificity-2 jumpy-jump-mode'
+      atom.keymap.keyBindings = @backedUpKeyBindings
       @detach()

   jump: ->
@@ -71,7 +73,12 @@
     @clearJumpMode()
     @detach()

+  turnOffSlowKeys: ->
+      atom.keymap.keyBindings = atom.keymap.keyBindings.filter (keymap) ->
+          keymap.command.indexOf('jumpy') > -1
+
   toggle: ->
+    @turnOffSlowKeys()
     $('#status-bar-jumpy').html("Jumpy: Jump Mode!")
     @allPositions = {}
     that = this


Revert "Use options to observe phpExecutablePath config"

This reverts commit 4f52e2ca452c18dedc15d01ffb9acdeed28d8964.

--- lib/linter-php.coffee
+++ lib/linter-php.coffee
@@ -1,5 +1,6 @@
 linterPath = atom.packages.getLoadedPackage("linter").path
 Linter = require "#{linterPath}/lib/linter"
+{CompositeDisposable} = require 'atom'

 class LinterPhp extends Linter
   # The syntax that the linter handles. May be a string or
@@ -14,12 +15,21 @@

   linterName: 'php'

-  options: ['phpExecutablePath']
-
   # A regex pattern used to extract information from the executable's output.
   regex: '(Parse|Fatal) (?<error>error):(\\s*(?<type>parse|syntax) error,?)?\\s*' +
          '(?<message>(unexpected \'(?<near>[^\']+)\')?.*) ' +
          'in .*? on line (?<line>\\d+)'
+
+  constructor: (editor) ->
+    super(editor)
+    @disposables = new CompositeDisposable
+
+    @disposables.add atom.config.observe 'linter-php.phpExecutablePath', =>
+      @executablePath = atom.config.get 'linter-php.phpExecutablePath'
+
+  destroy: ->
+    # atom.config.unobserve 'linter-php.phpExecutablePath'
+    @disposables.dispose();

   createMessage: (match) ->
     # message might be empty, we have to supply a value


cosmetic fixes

cosmetic fixes

--- extension/chrome/js/src/content.coffee
+++ extension/chrome/js/src/content.coffee
@@ -5,15 +5,15 @@
     	return

 	class MailButler
-		debug: true
-		dev: false
-		port: null
-		@MB_CLASS: 'mailbutler'
-		@MB_CLASS_NAV: MailButler.MB_CLASS + '-nav'
-		@MB_CLASS_THREAD: MailButler.MB_CLASS + '-thread'
-		@MB_CLASS_POPUP: MailButler.MB_CLASS + '-popup'
-		@MB_CLASS_MENU: MailButler.MB_CLASS + '-menu'
-		@MB_CLASS_PICKER: MailButler.MB_CLASS + '-picker'
+		debug: 	true
+		dev: 	false
+
+		@MB_CLASS: 			'mailbutler'
+		@MB_CLASS_NAV: 		MailButler.MB_CLASS + '-nav'
+		@MB_CLASS_THREAD: 	MailButler.MB_CLASS + '-thread'
+		@MB_CLASS_POPUP: 	MailButler.MB_CLASS + '-popup'
+		@MB_CLASS_MENU: 	MailButler.MB_CLASS + '-menu'
+		@MB_CLASS_PICKER: 	MailButler.MB_CLASS + '-picker'

 		@ID_PREFIX: 'mailbutler-id-'

@@ -579,8 +579,6 @@
 				chrome.extension.sendMessage
 					error: e.toString()
 				return
-
-			log 'scheduling mail...', data

 			loadingIcon?()

@@ -594,6 +592,8 @@
 				inbox:		!!props.inbox
 				archive:	!!props.archive
 				#callback:	'alert'
+
+			log 'scheduling mail...', data

 			# remove false values to transmit less data over the wire
 			_.each data, (val, key) ->


Update autoform-file.coffee

Update autoform-file.coffee

--- lib/client/autoform-file.coffee
+++ lib/client/autoform-file.coffee
@@ -1,5 +1,3 @@
-getCollection = undefined
-getDocument = undefined
 AutoForm.addInputType 'fileUpload',
   template: 'afFileUpload'
   valueOut: ->
@@ -7,139 +5,109 @@

 getCollection = (context) ->
   if typeof context.atts.collection == 'string'
-    return FS._collections[context.atts.collection] or window[context.atts.collection]
-  return
+    FS._collections[context.atts.collection] or window[context.atts.collection]

 getDocument = (context) ->
-  collection = undefined
-  id = undefined
-  ref = undefined
-  ref1 = undefined
-  collection = getCollection(context)
-  id = if (ref = Template.instance()) != null then (if (ref1 = ref.value) != null then (if typeof ref1.get == 'function' then ref1.get() else undefined) else undefined) else undefined
-  if collection != null then collection.findOne(id) else undefined
+  collection = getCollection context
+  id = Template.instance()?.value?.get?()
+  collection?.findOne(id)

 Template.afFileUpload.onCreated ->
-  self = undefined
-  self = this
-  @value = new ReactiveVar(@data.value)
-  @_stopInterceptValue = false
-  @_interceptValue = ((_this) ->
-    (ctx) ->
-      ref = undefined
-      t = undefined
-      if !_this._stopInterceptValue
-        t = Template.instance()
-        if t.value.get() != false and t.value.get() != ctx.value and (if (ref = ctx.value) != null then ref.length else undefined) > 0
-          t.value.set ctx.value
-          return _this._stopInterceptValue = true
-      return
-  )(this)
+  self = @
+  @value = new ReactiveVar @data.value

   @_insert = (file) ->
-    `var file`
-    collection = undefined
-    ref = undefined
-    collection = getCollection(self.data)
+    collection = getCollection self.data
+
     if Meteor.userId
       file.owner = Meteor.userId()
-    if typeof (if (ref = self.data.atts) != null then ref.onBeforeInsert else undefined) == 'function'
-      file = self.data.atts.onBeforeInsert(file) or file
-    file = new (FS.File)(file)
-    file.uploadedFrom = Meteor.userId()
-    maxChunk = 2097152
-    FS.config.uploadChunkSize = if file.original.size < 10 * maxChunk then file.original.size / 10 else maxChunk
+
+    if typeof self.data.atts?.onBeforeInsert is 'function'
+      file = (self.data.atts.onBeforeInsert file) or file
+
     collection.insert file, (err, fileObj) ->
-      ref1 = undefined
-      if typeof (if (ref1 = self.data.atts) != null then ref1.onAfterInsert else undefined) == 'function'
+      if typeof self.data.atts?.onAfterInsert is 'function'
         self.data.atts.onAfterInsert err, fileObj
+
       fileObj.update $set: metadata: owner: Meteor.userId()
-      if err
-        return console.log(err)
+
+      if err then return console.log err
       self.value.set fileObj._id

   @autorun ->
-    _id = undefined
     _id = self.value.get()
-    _id and Meteor.subscribe('autoformFileDoc', self.data.atts.collection, _id)
+    _id and Meteor.subscribe 'autoformFileDoc', self.data.atts.collection, _id
+
 Template.afFileUpload.onRendered ->
-  self = undefined
-  self = this
+  self = @
   $(self.firstNode).closest('form').on 'reset', ->
     self.value.set false
+
 Template.afFileUpload.helpers
   label: ->
     @atts.label or 'Choose file'
   removeLabel: ->
     @atts.removeLabel or 'Remove'
   value: ->
-    doc = undefined
-    doc = getDocument(this)
-    (if doc then doc.isUploaded() else undefined) and doc._id
+    doc = getDocument @
+    doc?.isUploaded() and doc._id
   schemaKey: ->
     @atts['data-schema-key']
   previewTemplate: ->
-    ref = undefined
-    ref1 = undefined
-    (if (ref = @atts) != null then ref.previewTemplate else undefined) or (if (if (ref1 = getDocument(this)) != null then ref1.isImage() else undefined) then 'afFileUploadThumbImg' else 'afFileUploadThumbIcon')
+    @atts?.previewTemplate or if getDocument(@)?.isImage() then 'afFileUploadThumbImg' else 'afFileUploadThumbIcon'
   previewTemplateData: ->
-    {
-      file: getDocument(this)
-      atts: @atts
-    }
+    file: getDocument @
+    atts: @atts
   file: ->
-    Template.instance()._interceptValue this
-    getDocument this
+    getDocument @
   removeFileBtnTemplate: ->
-    ref = undefined
-    (if (ref = @atts) != null then ref.removeFileBtnTemplate else undefined) or 'afFileRemoveFileBtnTemplate'
+    @atts?.removeFileBtnTemplate or 'afFileRemoveFileBtnTemplate'
   selectFileBtnTemplate: ->
-    ref = undefined
-    (if (ref = @atts) != null then ref.selectFileBtnTemplate else undefined) or 'afFileSelectFileBtnTemplate'
+    @atts?.selectFileBtnTemplate or 'afFileSelectFileBtnTemplate'
   selectFileBtnData: ->
-    {
-      label: @atts.label or 'Choose file'
-      accepts: @atts.accepts
-    }
+    label: @atts.label or 'Choose file'
+    accepts: @atts.accepts
   uploadProgressTemplate: ->
-    ref = undefined
-    (if (ref = @atts) != null then ref.uploadProgressTemplate else undefined) or 'afFileUploadProgress'
+    @atts?.uploadProgressTemplate or 'afFileUploadProgress'
+
 Template.afFileUpload.events
-  'dragover .js-af-select-file': (e) ->
+  "dragover .js-af-select-file": (e) ->
     e.stopPropagation()
     e.preventDefault()
-  'dragenter .js-af-select-file': (e) ->
+
+  "dragenter .js-af-select-file": (e) ->
     e.stopPropagation()
     e.preventDefault()
-  'drop .js-af-select-file': (e, t) ->
+
+  "drop .js-af-select-file": (e, t) ->
     e.stopPropagation()
     e.preventDefault()
-    t._insert new (FS.File)(e.originalEvent.dataTransfer.files[0])
+    t._insert new FS.File e.originalEvent.dataTransfer.files[0]
+
   'click .js-af-remove-file': (e, t) ->
     e.preventDefault()
     t.value.set false
+
   'fileuploadchange .js-file': (e, t, data) ->
-    t._insert new (FS.File)(data.files[0])
-Template.afFileUploadThumbImg.helpers url: ->
-  @file.url store: @atts.store
+    t._insert new FS.File data.files[0]
+
+Template.afFileUploadThumbImg.helpers
+  url: ->
+    @file.url store: @atts.store
+
 Template.afFileUploadThumbIcon.helpers
   url: ->
     @file.url store: @atts.store
   icon: ->
     switch @file.extension()
       when 'pdf'
-        return 'file-pdf-o'
+        'file-pdf-o'
       when 'doc', 'docx'
-        return 'file-word-o'
+        'file-word-o'
       when 'ppt', 'avi', 'mov', 'mp4'
-        return 'file-powerpoint-o'
+        'file-powerpoint-o'
       else
-        return 'file-o'
-    return
+        'file-o'
+
 Template.afFileSelectFileBtnTemplate.onRendered ->
   @$('.js-file').fileupload()
-# ---
-# generated by coffee-script 1.9.2
-
-# ---
-# generated by js2coffee 2.1.0


:green_heart: Fix broken travis tests

:green_heart: Fix broken travis tests

--- lib/color-buffer-element.coffee
+++ lib/color-buffer-element.coffee
@@ -10,6 +10,7 @@
     @displayedMarkers = []
     @usedMarkers = []
     @unusedMarkers = []
+    @ignoredScopes=[]
     @viewsByMarkers = new WeakMap

     @subscriptions.add atom.config.observe 'pigments.markerType', (type) =>
@@ -18,7 +19,7 @@
       else
         @classList.remove('above-editor-content')

-    @subscriptions.add atom.config.observe 'pigments.ignoredScopes', (@ignoredScopes) =>
+    @subscriptions.add atom.config.observe 'pigments.ignoredScopes', (@ignoredScopes=[]) =>
       @updateMarkers() if @attached

   attachedCallback: ->


Add Serialization of path options

Add Serialization of path options

--- lib/table-edit.coffee
+++ lib/table-edit.coffee
@@ -25,7 +25,7 @@
       type: 'integer'
       default: 2

-  activate: (state) ->
+  activate: ({@pathOptions}) ->
     TableEditor ?= require './table-editor'
     TableElement ?= require './table-element'
     TableElement.registerViewProvider()
@@ -69,20 +69,20 @@
         when 'small' then @getSmallTable()

   getChoiceForPath: (path) ->
-    @pathsOptions?[path]?.choice
+    @pathOptions?[path]?.choice

   storeChoiceForPath: (path, choice) ->
-    @pathsOptions ?= {}
-    @pathsOptions[path] ?= {}
-    @pathsOptions[path].choice = choice
+    @pathOptions ?= {}
+    @pathOptions[path] ?= {}
+    @pathOptions[path].choice = choice

   getOptionsForPath: (path) ->
-    @pathsOptions?[path]?.options
+    @pathOptions?[path]?.options

   storeOptionsForPath: (path, options) ->
-    @pathsOptions ?= {}
-    @pathsOptions[path] ?= {}
-    @pathsOptions[path].options = options
+    @pathOptions ?= {}
+    @pathOptions[path] ?= {}
+    @pathOptions[path].options = options

   getSmallTable: ->
     table = new TableEditor
@@ -137,3 +137,6 @@
     table.unlockModifiedStatus()

     return table
+
+  serialize: ->
+    {@pathOptions}


:lipstick: Rename scroll view outlet as body

--- lib/table-view.coffee
+++ lib/table-view.coffee
@@ -8,20 +8,20 @@
   @content: ->
     @div class: 'table-edit', =>
       @table outlet: 'tableHeaderView', class: 'table-edit-header'
-      @div outlet: 'scrollView', class: 'scroll-view', =>
+      @div outlet: 'body', class: 'scroll-view', =>

   initialize: (@table) ->
     @subscriptions = new CompositeDisposable
     @scroll = 0

     props = {@table, parentView: this}
-    @component = React.renderComponent(TableComponent(props), @scrollView[0])
+    @bodyComponent = React.renderComponent(TableComponent(props), @body[0])

     @subscriptions.add @table.onDidChangeRows @requestUpdate
     @subscriptions.add @table.onDidAddColumn @onColumnAdded
     @subscriptions.add @table.onDidRemoveColumn @onColumnRemoved

-    @subscriptions.add @asDisposable @scrollView.on 'scroll', @requestUpdate
+    @subscriptions.add @asDisposable @body.on 'scroll', @requestUpdate

     @subscribeToColumn(column) for column in @table.getColumns()

@@ -46,12 +46,12 @@
   setRowOverdraw: (@rowOverdraw) -> @requestUpdate(true)

   getFirstVisibleRow: ->
-    row = Math.floor(@scrollView.scrollTop() / @getRowHeight())
+    row = Math.floor(@body.scrollTop() / @getRowHeight())

   getLastVisibleRow: ->
-    scrollViewHeight = @scrollView.height()
+    scrollViewHeight = @body.height()

-    row = Math.floor((@scrollView.scrollTop() + scrollViewHeight) / @getRowHeight())
+    row = Math.floor((@body.scrollTop() + scrollViewHeight) / @getRowHeight())

   #     ######   #######  ##       ##     ## ##     ## ##    ##  ######
   #    ##    ## ##     ## ##       ##     ## ###   ### ###   ## ##    ##
@@ -151,10 +151,10 @@

   scrollTop: (scroll) ->
     if scroll?
-      @scrollView.scrollTop(scroll)
+      @body.scrollTop(scroll)
       @requestUpdate()

-    @scrollView.scrollTop()
+    @body.scrollTop()

   requestUpdate: (forceUpdate=false) =>
     @hasChanged = forceUpdate
@@ -175,7 +175,7 @@
     firstRow = Math.max 0, firstVisibleRow - @rowOverdraw
     lastRow = Math.min @table.getRowsCount(), lastVisibleRow + @rowOverdraw

-    @component.setState {
+    @bodyComponent.setState {
       firstRow
       lastRow
       rowHeight: @getRowHeight()


Sort by deprecated method calls

Sort by deprecated method calls

--- lib/deprecation-cop-view.coffee
+++ lib/deprecation-cop-view.coffee
@@ -24,7 +24,11 @@
     'Deprecation Cop'

   update: ->
-    for method, {count, message, stackTraces} of Grim.getLog()
+    methodList = []
+    methodList.push [method, metadata] for method, metadata of Grim.getLog()
+    methodList.sort (a, b) -> b[1].count - a[1].count
+
+    for [method, {count, message, stackTraces}] in methodList
       @list.append $$ ->
         @li class: 'list-nested-item collapsed', =>
           @div class: 'list-item', =>


repl modes

repl modes

--- lib/ui/console.coffee
+++ lib/ui/console.coffee
@@ -20,6 +20,7 @@
     @c = new @ink.Console
     @c.setGrammar atom.grammars.grammarForScopeName('source.julia')
     @c.view.getTitle = -> "Julia"
+    @c.modes = => @replModes
     @c.onEval (ed) => @eval ed
     @c.input()
     @loading.onWorking => @c.view.loading true
@@ -31,5 +32,14 @@
     if ed.getText()
       comm.withClient =>
         @c.done()
-        comm.msg 'eval-repl', {code: ed.getText()}, (result) =>
+        comm.msg 'eval-repl', {code: ed.getText(), mode: ed.inkConsoleMode}, (result) =>
           @c.input()
+
+  replModes:
+    ';':
+      name: 'shell'
+      icon: 'terminal'
+      grammar: atom.grammars.grammarForScopeName('source.shell')
+    '?':
+      name: 'help'
+      icon: 'question'


limit loading indicator to julia files

limit loading indicator to julia files

--- lib/ui/loading.coffee
+++ lib/ui/loading.coffee
@@ -35,6 +35,7 @@
   on: (ed) ->
     return if @edSubscription?
     @edSubscription = atom.workspace.observeTextEditors (ed) =>
+      return unless ed.getGrammar().scopeName == 'source.julia'
       @insert ed

   off: ->


Use Y.Selections and Y.List

Use Y.Selections and Y.List

--- lib/y-richtext.coffee
+++ lib/y-richtext.coffee
@@ -28,10 +28,14 @@
   _getModel: (Y, Operation) ->
     if not @_model?
       super
-      @_selections = new Y.Selections()
-      @_characters = new Characters content, @_selections
-      @setCursor @editor.getCursorPosition()
-      @_setModel model
+
+      @_set "selections", new Y.Selections()
+      @_set "characters", new Y.List()
+      @_set "cursors", new Y.List()
+
+      # set the cursor
+      @_setCursor @editor.getCursorPosition()
+      @_setModel @_model

       # listen to events on the model using the function propagateToEditor
       @_model.observe @propagateToEditor


Every message in all topics will be published to public

--- client/app/Applications/Chat.kdapplication/AppController.coffee
+++ client/app/Applications/Chat.kdapplication/AppController.coffee
@@ -5,6 +5,7 @@
 ###
 class Chat12345 extends AppController
   {mq} = bongo
+  PUBLIC = 'public'

   constructor:(options = {}, data)->
     options.view = new ChatView
@@ -23,7 +24,7 @@
     super name : 'Chat'#, type : 'background'

   loadView:(mainView)->
-    @joinChannel 'public'
+    @joinChannel PUBLIC

   joinChannel: (name) ->
     return @channels[name] if @channels[name]
@@ -46,6 +47,8 @@
     channel.view.on "ChatMessageSent", (messageBody) =>
       @parseMessageForChannels messageBody
       @broadcastOwnMessage channel, messageBody
+      if name isnt PUBLIC
+        @broadcastOwnMessage @channels[PUBLIC], messageBody

     @broadcaster.on channelName, (msg) ->
       channel.messageReceived msg


Use AppStorage singleton, initialPath support implemented as String

--- client/app/MainApp/filetree/controllers/findercontroller.coffee
+++ client/app/MainApp/filetree/controllers/findercontroller.coffee
@@ -25,6 +25,7 @@
     super options, data

     @kiteController = @getSingleton('kiteController')
+    @appStorage     = @getSingleton('mainController').getAppStorageSingleton 'Finder', '1.0'

     @treeController = new NFinderTreeController treeOptions, []
     @treeController.on "file.opened", (file)=> @setRecentFile file.path
@@ -38,23 +39,25 @@
     @viewLoaded = yes
     @utils.killWait @loadDefaultStructureTimer

-
     # temp hack, if page opens in develop section.
     @utils.wait 2500, =>
       @getSingleton("mainView").sidebar._windowDidResize()

-#    if @treeController.getOptions().useStorage
-#      appManager.on "AppManagerOpensAnApplication", (appInst)=>
-#        if appInst instanceof StartTabAppController and not @defaultStructureLoaded
-#          @loadDefaultStructure()
+  expandInitialPath:(path)->
+    pathArray = []
+    path      = path.split "/"
+    temp      = ''

-    # if @treeController.getOptions().useStorage
-    #   @loadDefaultStructure()
+    for chunk in path
+      pathArray.push temp+="/#{chunk}" if chunk
+
+    pathArray.splice 0, 1
+    pathArray

   reset:()->

-    delete @_storage
-    {initialPath} = @treeController.getOptions() # not used, fix this
+    {initialPath} = @treeController.getOptions()
+    @initialPath  = @expandInitialPath initialPath

     @mount = if KD.isLoggedIn()
       {nickname}    = KD.whoami().profile
@@ -86,24 +89,20 @@
     return unless KD.isLoggedIn()
     {nickname}     = KD.whoami().profile
     kiteController = KD.getSingleton('kiteController')
-    @fetchStorage (storage)=>
-      recentFolders = if storage.bucket?.recentFolders? and storage.bucket.recentFolders.length > 0
-        storage.bucket.recentFolders
-      else
-        [
-          "/Users/#{nickname}"
-          "/Users/#{nickname}/Sites"
-          "/Users/#{nickname}/Sites/#{nickname}.koding.com"
-          "/Users/#{nickname}/Sites/#{nickname}.koding.com/website"
-        ]
+    @appStorage.fetchValue 'recentFolders', (recentFolders)=>
+
+      unless Array.isArray recentFolders
+        unless @initialPath
+          {initialPath} = @treeController.getOptions()
+          @initialPath  = @expandInitialPath initialPath
+          recentFolders = @initialPath

       timer = Date.now()
-      # @utils.wait =>
-      # mount = @treeController.nodes["/Users/#{nickname}"].getData()

       @mount.emit "fs.fetchContents.started"

       @utils.killWait kiteFailureTimer if kiteFailureTimer
+
       kiteFailureTimer = @utils.wait 5000, =>
         @treeController.notify "Couldn't fetch files! Click to retry", 'clickable', "Sorry, a problem occured while communicating with servers, please try again later.", yes
         @mount.emit "fs.fetchContents.finished"
@@ -124,66 +123,49 @@
         kiteController.emit "UserEnvironmentIsCreated"
         @mount.emit "fs.fetchContents.finished"

-
   multipleLs:(pathArray, callback)->

     KD.getSingleton('kiteController').run
       withArgs  :
-        command : "ls \"#{pathArray.join("\" \"")}\" -lpva --group-directories-first --time-style=full-iso"
+        command : "ls \"#{pathArray.join("\" \"")}\" -Llpva --group-directories-first --time-style=full-iso"
     , callback
-
-  fetchStorage:(callback)->
-
-    unless @_storage
-      appManager.fetchStorage 'Finder', '1.0', (error, storage) =>
-        callback @_storage = storage
-    else
-      callback @_storage

   setRecentFile:(filePath, callback)->

-    @fetchStorage (storage)=>
-      # recentFiles = storage.getAt('bucket.recentFiles') or []
-      recentFiles = if storage.bucket?.recentFiles? then storage.bucket.recentFiles else []
-      unless filePath in recentFiles
-        recentFiles.pop() if recentFiles.length is @treeController.getOptions().maxRecentFiles
-        recentFiles.unshift filePath
+    recentFiles = @appStorage.getValue('recentFiles')
+    recentFiles = [] unless Array.isArray recentFiles

-      storage.update {
-        $set: 'bucket.recentFiles': recentFiles.slice(0,10)
-      }, callback
+    unless filePath in recentFiles
+      recentFiles.pop() if recentFiles.length is @treeController.getOptions().maxRecentFiles
+      recentFiles.unshift filePath
+
+    @appStorage.setValue 'recentFiles', recentFiles.slice(0,10), =>
+      @emit 'recentfiles.updated', recentFiles

   setRecentFolder:(folderPath, callback)->

-    @fetchStorage (storage)=>
+    recentFolders = @appStorage.getValue('recentFolders')
+    recentFolders = [] unless Array.isArray recentFolders

-      recentFolders = if storage.bucket?.recentFolders? then storage.bucket.recentFolders else []
+    unless folderPath in recentFolders
+      recentFolders.push folderPath

-      unless folderPath in recentFolders
-        recentFolders.push folderPath
+    recentFolders.sort (path)-> if path is folderPath then -1 else 0

-      recentFolders.sort (path)-> if path is folderPath then -1 else 0
-
-      storage.update {
-        $set: 'bucket.recentFolders': recentFolders
-      }, => #log "recentFolder set"
+    @appStorage.setValue 'recentFolders', recentFolders, callback

   unsetRecentFolder:(folderPath, callback)->

-    @fetchStorage (storage)=>
+    recentFolders = @appStorage.getValue('recentFolders')
+    recentFolders = [] unless Array.isArray recentFolders

-      recentFolders = if storage.bucket?.recentFolders? then storage.bucket.recentFolders else []
+    splicer = ->
+      recentFolders.forEach (recentFolderPath)->
+        if recentFolderPath.search(folderPath) > -1
+          recentFolders.splice recentFolders.indexOf(recentFolderPath), 1
+          splicer()
+          return
+    splicer()

-      splicer = ->
-        recentFolders.forEach (recentFolderPath)->
-          if recentFolderPath.search(folderPath) > -1
-            recentFolders.splice recentFolders.indexOf(recentFolderPath), 1
-            splicer()
-            return
-
-      splicer()
-      recentFolders.sort (path)-> if path is folderPath then -1 else 0
-
-      storage.update {
-        $set: 'bucket.recentFolders': recentFolders
-      }, => #log "recentFolder unset"
+    recentFolders.sort (path)-> if path is folderPath then -1 else 0
+    @appStorage.setValue 'recentFolders', recentFolders, callback


Fix logic in unmountVM

Fix logic in unmountVM

--- client/app/MainApp/filetree/controllers/findercontroller.coffee
+++ client/app/MainApp/filetree/controllers/findercontroller.coffee
@@ -148,19 +148,18 @@
       , yes

   unmountVm:(vmName)->
-    return warn 'No such VM!'  unless vmItem = @getVmNode vmName
+    vmItem = @getVmNode vmName
+    return warn 'No such VM!'  unless vmItem

     @updateMountState vmName, no
-
-    if vmItem
-      @stopWatching vmItem.data.path
-      FSHelper.deregisterVmFiles vmName
-      @treeController.removeNodeView vmItem
-      @vms = @vms.filter (vmData)-> vmData isnt vmItem.data
-
-      if @vms.length is 0
-        @noVMFoundWidget.show()
-        @emit 'EnvironmentsTabRequested'
+    @stopWatching vmItem.data.path
+    FSHelper.deregisterVmFiles vmName
+    @treeController.removeNodeView vmItem
+    @vms = @vms.filter (vmData)-> vmData isnt vmItem.data
+
+    if @vms.length is 0
+      @noVMFoundWidget.show()
+      @emit 'EnvironmentsTabRequested'

   updateVMRoot:(vmName, path, callback)->
     return warn 'VM name and new path required!'  unless vmName or path


FinderController: updateMachineRoot: skip if same path provided as root

--- client/finder/lib/filetree/controllers/nfindercontroller.coffee
+++ client/finder/lib/filetree/controllers/nfindercontroller.coffee
@@ -161,11 +161,15 @@

     return kd.warn 'Machine uid and new path required!'  unless uid or path

+    machineRoots = (@appStorage.getValue 'machineRoots') or {}
+
+    if machineRoots[uid] is path
+      return callback?()
+
+    machineRoots[uid] = path
+
     @unmountMachine uid
     callback?()
-
-    machineRoots = (@appStorage.getValue 'machineRoots') or {}
-    machineRoots[uid] = path

     if @getOptions().useStorage
       @appStorage.setValue 'machineRoots', machineRoots


Add implementation of DateEditor

Add implementation of DateEditor

--- bokehjs/src/coffee/widget/cell_editors.coffee
+++ bokehjs/src/coffee/widget/cell_editors.coffee
@@ -256,6 +256,47 @@

   class DateEditorView extends CellEditorView

+    emptyValue: new Date()
+
+    input: '<input type="text" />'
+
+    renderEditor: () ->
+      @calendarOpen = false
+
+      @$input.datepicker
+        showOn: "button"
+        buttonImageOnly: true
+        beforeShow: () => @calendarOpen = true
+        onClose: () => @calendarOpen = false
+      @$input.siblings(".bk-ui-datepicker-trigger").css("vertical-align": "middle")
+      @$input.width(@$input.width() - (14 + 2*4 + 4)) # img width + margins + edge distance
+      @$input.focus().select()
+
+    destroy: () ->
+      $.datepicker.dpDiv.stop(true, true)
+      @$input.datepicker("hide")
+      @$input.datepicker("destroy")
+      super()
+
+    show: () ->
+      if @calendarOpen
+        $.datepicker.dpDiv.stop(true, true).show()
+      super()
+
+    hide: () ->
+      if @calendarOpen
+        $.datepicker.dpDiv.stop(true, true).hide()
+      super()
+
+    position: (position) ->
+      if @calendarOpen
+        $.datepicker.dpDiv.css(top: position.top + 30, left: position.left)
+      super()
+
+    getValue: () -> return @$input.datepicker("getDate").getTime()
+
+    setValue: (val) -> @$input.datepicker("setDate", new Date(val))
+
   class DateEditor extends CellEditor
     type: 'DateEditor'
     default_view: DateEditorView


hack to avoid pane-splitting commands from throwing errors

--- lib/pane-mixin.coffee
+++ lib/pane-mixin.coffee
@@ -17,7 +17,9 @@
     atom.deserializers.add
       name: deserialiser
       deserialize: ({id}) ->
-        Pane.fromId id
+        pane = Pane.fromId id
+        return if pane.currentPane()
+        pane

   Pane.prototype.serialize = ->
     if @id


[MNOE-70] Anybody can access /admin/

[MNOE-70] Anybody can access /admin/

--- frontend-admin/src/app/index.config.coffee
+++ frontend-admin/src/app/index.config.coffee
@@ -10,16 +10,20 @@
     toastrConfig.progressBar = true
   )
   .config(($httpProvider) ->
-    $httpProvider.interceptors.push(($q, $window, $injector) ->
+    $httpProvider.interceptors.push(($q, $window, $injector, $log) ->
       return {
         responseError: (rejection) ->
+
           if rejection.status == 401
             # Inject the toastr service (avoid circular dependency)
             toastr = $injector.get('toastr')

+            # Redirect the user to the dashboard or login screen
+            $window.location.href = "/"
+
             # Display an error
-            toastr.error("User is not connected!")
-            console.log "User is not connected!"
+            toastr.error("You are no longer connected or not an administrator, you will be redirected to the dashboard.")
+            $log.error("User is not connected!")

           $q.reject rejection
       }


NumberAnimation impl bug with easing fixed

--- impl/base/level1/animation/number.coffee
+++ impl/base/level1/animation/number.coffee
@@ -45,7 +45,10 @@
 		data.progress = progress
 		running = progress isnt 1 or (anim._running and anim._loop and anim._when)

-		val = data.easing anim._duration * progress, data.from, (data.to - data.from), anim._duration
+		if progress is 1
+			val = data.to
+		else
+			val = data.easing anim._duration * progress, data.from, (data.to - data.from), anim._duration
 		target = anim._target

 		# if data.isIntegerProperty is true and progress isnt 1


omg. soo goood.

omg. soo goood.

--- mailer-service.coffee
+++ mailer-service.coffee
@@ -7,7 +7,7 @@
     meshblu = new MeshbluHttp auth
     {transportOptions, transporter} = config
     if transporter
-      transportOptions = require("nodemailer-#{data.transporter}-transport")(transportOptions)
+      transportOptions = require("nodemailer-#{transporter}-transport")(transportOptions)

     nodemailer.createTransport(transportOptions).sendMail message, (err, info) =>
       meshblu.message devices: ['*'], result: {err,info}, {}, callback


Client: Activity: added createchannel tests

--- client/activity/lib/flux/test/index.coffee
+++ client/activity/lib/flux/test/index.coffee
@@ -22,4 +22,5 @@
 require './channelparticipants/channelparticipantsdropdownvisibilitystore'
 # module tests
 require 'activity/flux/chatinput/tests'
+require 'activity/flux/createchannel/tests'


fixed a nasty bug in ioHelper that destroyed the parameter values

--- app/helper/ioHelper.coffee
+++ app/helper/ioHelper.coffee
@@ -15,17 +15,19 @@

   buildFilePath: (path,algorithm,params) ->
     algorithm = algorithm.replace(/\//g, '_')
+    tmpParams = JSON.parse(JSON.stringify(params))
     #join params with _
-    params.splice(params.indexOf('requireOutputImage'),1)
-    params = params.join('_').replace RegExp(' ', 'g'), '_'
-    filename = algorithm + '_' + params + '.json'
+    tmpParams.splice(tmpParams.indexOf('requireOutputImage'),1)
+    tmpParams = tmpParams.join('_').replace RegExp(' ', 'g'), '_'
+    filename = algorithm + '_' + tmpParams + '.json'
     return path + filename

   buildTempFilePath: (path,algorithm,params) ->
     algorithm = algorithm.replace(/\//g, '_')
     #join params with _
-    params = params.join('_').replace RegExp(' ', 'g'), '_'
-    filename = algorithm + '_' + params + '_temp.json'
+    tmpParams = JSON.parse(JSON.stringify(params))
+    tmpParams = tmpParams.join('_').replace RegExp(' ', 'g'), '_'
+    filename = algorithm + '_' + tmpParams + '_temp.json'
     return path + filename

   # ---


Fix bug where blocks stuck sliding

Fix bug where blocks stuck sliding

--- client/app/scripts/controllers/main.coffee
+++ client/app/scripts/controllers/main.coffee
@@ -154,7 +154,9 @@
       block.sliding = true
       $scope.game_info.sliding_block = true
       block_to_left = Tetromino.get_closest_block_to_left(block.x, block.y)
-      return if block_to_left && block_to_left.y == block.y - 1
+      if block_to_left && block_to_left.y == block.y - 1
+        stop_sliding(block)
+        return
       block.y--
       $timeout (-> stop_sliding(block)), 100

@@ -167,7 +169,9 @@
       block.sliding = true
       $scope.game_info.sliding_block = true
       block_to_right = Tetromino.get_closest_block_to_right(block.x, block.y)
-      return if block_to_right && block_to_right.y == block.y + 1
+      if block_to_right && block_to_right.y == block.y + 1
+        stop_sliding(block)
+        return
       block.y++
       $timeout (-> stop_sliding(block)), 100


Tests: Collaboration: Fix user file creation race condition.

--- client/test/lib/collaboration/start.coffee
+++ client/test/lib/collaboration/start.coffee
@@ -126,18 +126,22 @@
 module.exports =

-  before: -> utils.getUser()
+  before: (browser) ->
+
+    hostBrowser = process.env.__NIGHTWATCH_ENV_KEY is 'host_1'
+
+    if hostBrowser
+      utils.getUser()

   start: (browser) ->

-    browser.pause 2500 # wait for user.json creation
-
-    start(browser)
-    leave(browser)
-    waitAndEndSession(browser)
-
-    browser.end()
+    browser.pause 2500, -> # wait for user.json creation
+      start(browser)
+      leave(browser)
+      waitAndEndSession(browser)
+
+      browser.end()

   runCommandOnInviteUserTerminal: (browser) ->


Update accountProfile.coffee

Update accountProfile.coffee

--- client/views/account/accountProfile.coffee
+++ client/views/account/accountProfile.coffee
@@ -99,7 +99,7 @@
 Template.accountProfile.onRendered ->
 	Tracker.afterFlush ->
 		# this should throw an error-template
-		window.location.href = "/" if !RocketChat.settings.get("Accounts_AllowUserAvatarChange")
+		FlowRouter.go("home") if !RocketChat.settings.get("Accounts_AllowUserAvatarChange")
 		SideNav.setFlex "accountFlex"
 		SideNav.openFlex()


why are we removing all the spaces and adding backslashes? this is keeping me from using the package

--- lib/template-editor.view.coffee
+++ lib/template-editor.view.coffee
@@ -41,9 +41,6 @@

     sanitizeTemplate: (template) =>
         return template
-                    .replace(/[^\S\r\n]+$/gm, '')
-                    .replace(/\ /g, "\\ ")
-                    # .replace(/\n/g, "\\n")

     save: ->
         getterTemplate = @sanitizeTemplate(@find('#txtGetter').val())


Small wording change for spirals

Small wording change for spirals

--- app/lib/decals_tree.coffee
+++ app/lib/decals_tree.coffee
@@ -29,7 +29,7 @@
     @answer 'Dominant', icon: 'bulge_dominant', examples: 4

   @question 'Spiral', 'How tightly wound do the spiral arms appear?', leadsTo: 'How many spiral arms are there?', ->
-    @help "Astronomers classify galaxies by how tight their arms are - you might find it easiest to see them closest to the centre. Click to see larger images."
+    @help "Astronomers classify spiral galaxies by how tight their arms are - you might find it easiest to assess this by looking at the arms close to the centre. Click to see larger images."
     @answer 'Tight', icon: 'spiral_tight', examples: 5
     @answer 'Medium', icon: 'spiral_medium', examples: 6
     @answer 'Loose', icon: 'spiral_loose', examples: 4


Add timezone information to moment-timezone


ContentView.spec: better subview testing.

Testing should be independent, and detailed by the `it 'message'`.
(We should be able to know exactly what's failed by the message).

--- coffee/test/views/content_view.spec.coffee
+++ coffee/test/views/content_view.spec.coffee
@@ -24,15 +24,31 @@
   it 'should derive from athena.lib.View', ->
     expect(athena.lib.util.derives ContentView, athena.lib.View).toBe true

-  it 'should instantiate and render a shell.ContentView', ->
-    view = new ContentView options
+  describe 'ContentView::shellView subview', ->

-    expect(view.shellView).not.toBeDefined()
-    view.render()
-    expect(view.shellView).toBeDefined()
-    expect(view.shellView instanceof shell.ContentView).toBe true
-    expect(view.shellView.rendering).toBe true
-    expect(view.shellView.el.parentNode).toEqual view.el
+    it 'should NOT be defined on init', ->
+      view = new ContentView options
+      expect(view.shellView).not.toBeDefined()
+
+    it 'should be defined on render', ->
+      view = new ContentView options
+      view.render()
+      expect(view.shellView).toBeDefined()
+
+    it 'should be an instanceof shell.ContentView', ->
+      view = new ContentView options
+      view.render()
+      expect(view.shellView instanceof shell.ContentView).toBe true
+
+    it 'should be rendering with its parent', ->
+      view = new ContentView options
+      view.render()
+      expect(view.shellView.rendering).toBe true
+
+    it 'should be a DOM child of its parent', ->
+      view = new ContentView options
+      view.render()
+      expect(view.shellView.el.parentNode).toEqual view.el

   it 'should look good', ->
     # setup DOM


payments: payment controller implemented / many bugs fixed

--- components/inputs/KDInputRadioGroup.coffee
+++ components/inputs/KDInputRadioGroup.coffee
@@ -52,7 +52,7 @@
     @$("input[value='#{value}']").attr "checked", "checked"
     @$("input[value='#{value}']").prop "checked", yes
     @$(".kd-radio-holder").removeClass 'active'
-    @$(".kd-radio-holder.#{value}").addClass 'active'  if value
+    @$(".kd-radio-holder.#{value}").addClass 'active'  if value?

   getInputElements:->
     @getDomElement().find('input')


Bug fix: enemy escort attacked by LBAC not shown

--- lib/simulator2.coffee
+++ lib/simulator2.coffee
@@ -206,16 +206,10 @@
       attacks: attacks
       subtype: SupportTypeMap[flag]

-simulateLandBase = (enemyShip, kouku) ->
-  return unless kouku?
-  attacks = []
-  if kouku.api_stage3?
-    st3 = kouku.api_stage3
-    attacks = attacks.concat(simulateAerialAttack(enemyShip, st3.api_edam, st3.api_ebak_flag, st3.api_erai_flag, st3.api_ecl_flag))
-  return new Stage
-    type: StageType.LandBase
-    attacks: attacks
-    kouku: kouku
+simulateLandBase = (enemyFleet, enemyEscort, kouku) ->
+  stage = simulateAerial(null, null, enemyFleet, enemyEscort, kouku)
+  stage.type = StageType.LandBase
+  return stage

 getEngagementStage = (packet) ->
   picks = _.pick(packet, 'api_search', 'api_formation')
@@ -316,7 +310,7 @@
       stages.push getEngagementStage(packet)
       # Land base air attack
       for api_kouku in packet.api_air_base_attack || []
-        stages.push simulateLandBase(@enemyFleet, api_kouku)
+        stages.push simulateLandBase(@enemyFleet, @enemyEscort, api_kouku)
       # Aerial Combat
       stages.push simulateAerial(@mainFleet, @escortFleet, @enemyFleet, @enemyEscort, packet.api_kouku)
       # Aerial Combat 2nd


Inherit some of the necessary Store methods

--- library/exposure/session.coffee
+++ library/exposure/session.coffee
@@ -53,6 +53,12 @@
     # Also, the compounds for the composition system belong here.
     @compose RedisClient

+    # The dirty way of inheriting the prototypal properties of the
+    # abstract session store engine found in the standard Connect
+    # shipment. This is necessary in order to inherit the default
+    # implementations of some generic, engine agnostic methods.
+    _.extend @prototype, connect.session.Store.prototype
+
     # Part of the session engine interface contract implemenetation.
     # This method is supposed to be called when a session engine asks
     # the storage to destroy a session with the specified session ID.


Fix infinite loop

Fix infinite loop

--- app/views/dashboard.coffee
+++ app/views/dashboard.coffee
@@ -61,7 +61,6 @@
     @model = model
     @render()
     @model.tools.on 'add', @addTool
-    @model.tools.on 'reset', @removeTools

   stopSnap: =>
     @snap.remove() if @snap


修改压缩方法

修改压缩方法

--- lib/utils/util.coffee
+++ lib/utils/util.coffee
@@ -215,8 +215,9 @@
       stats = fs.statSync(filePath)
       if stats.isFile()
         fileName = pathM.basename(filePath)
-        fileZipPath = pathM.join node,fileName
-        zip.file(fileZipPath,fs.readFileSync(filePath))
+        # fileZipPath = pathM.join node,fileName
+        # zip.file(fileZipPath,fs.readFileSync(filePath))
+        zip.folder(node).file(fileName,fs.readFileSync(filePath))
       else
         folderZipPath = pathM.join node,pathM.basename(filePath)
         zip.folder(folderZipPath)


Fix dispose

Fix dispose

--- lib/whitespace.coffee
+++ lib/whitespace.coffee
@@ -31,12 +31,16 @@
         if atom.config.get('whitespace.ensureSingleTrailingNewline', scope: scopeDescriptor)
           @ensureSingleTrailingNewline(editor)

-    editorDestroyedSubscription = editor.onDidDestroy ->
+    editorDestroyedSubscription = editor.onDidDestroy =>
       bufferSavedSubscription.dispose()
+      @subscriptions.remove(bufferSavedSubscription)
       editorDestroyedSubscription.dispose()
-    bufferDestroyedSubscription = buffer.onDidDestroy ->
+      @subscriptions.remove(editorDestroyedSubscription)
+    bufferDestroyedSubscription = buffer.onDidDestroy =>
       bufferDestroyedSubscription.dispose()
+      @subscriptions.remove(bufferDestroyedSubscription)
       bufferSavedSubscription.dispose()
+      @subscriptions.remove(bufferSavedSubscription)

     @subscriptions.add(bufferSavedSubscription)
     @subscriptions.add(editorDestroyedSubscription)


put sync back

put sync back

--- backend/replication.coffee
+++ backend/replication.coffee
@@ -133,10 +133,9 @@
             if needTreeRebuild
                 #log.info 'Applying changes on the filesystem'
                 console.log 'Applying changes on the filesystem'
-                markRebuildTree = (err) ->
+                applyChanges (err) ->
                     needTreeRebuild = false
                     callback err if callback?
-                applyChanges markRebuildTree

         onComplete = (info) ->
             #log.info 'Replication is complete'
@@ -144,9 +143,8 @@
             if fromRemote and not toRemote
                 #log.info 'Applying changes on the filesystem'
                 console.log 'Applying changes on the filesystem'
-                finish = (err) ->
+                applyChanges (err) ->
                     callback err if callback?
-                applyChanges finish
             else
                 callback null if callback?

@@ -158,6 +156,6 @@
         url = urlParser.format(url) + 'cozy'
         replicator = replicate(url, options)
             .on 'change', onChange
-            #.on 'uptodate', onUptoDate # Called only for a continuous replication
+            .on 'uptodate', onUptoDate # Called only for a continuous replication
             .on 'complete', onComplete # Called only for a single replication
             .on 'error', onError


Added missing comments

Added missing comments

--- lib/providers/autocomplete-provider.coffee
+++ lib/providers/autocomplete-provider.coffee
@@ -58,6 +58,12 @@

     return suggestions

+  ###*
+   * Adds the suggestion the the suggestions array.
+   * @param {string} word        The word being currently typed.
+   * @param {object} element     The object returns from proxy.methods.
+   * @param {array} suggestions  An array of suggestions for the current word.
+  ###
   addSuggestion: (word, element, suggestions) ->
     returnValues = element.args.return.split('\\')


Little callback fix in sequence

Little callback fix in sequence

--- async/Sequence.coffee
+++ async/Sequence.coffee
@@ -207,9 +207,9 @@
                     console.warn "Here is the data:", data
                     console.warn "Here is the error:", error
                     console.error "================================================================="
+                    @_errorCallback?(error, data, @idx)
                     if @stopOnError
                         @interrupt()
-                        @_errorCallback?(error)

                 # ASYNC
                 # return value is of type 'CONTEXT': {done: $.post(...), context: {a: 10, b: 20}}
@@ -299,10 +299,14 @@
                 return callback.apply(context, args)
         return @

+    ###*
+    * Callback that gets called after the queue is processed but before the done callbacks are triggered.
+    * @method onEnd
+    *###
     onError: (callback, context, args...) ->
         if typeof callback is "function"
-            @_errorCallback = (error) ->
-                return callback.apply(context, [error].concat(args))
+            @_errorCallback = (error, data, index) ->
+                return callback.apply(context, [error, data, index].concat(args))
         return @

     ###*


Set contentEditable to false to avoid being detected as a mutation

--- src/trix/views/image_attachment_view.coffee
+++ src/trix/views/image_attachment_view.coffee
@@ -7,6 +7,7 @@
 class Trix.ImageAttachmentView extends Trix.AttachmentView
   render: ->
     @image = document.createElement("img")
+    @image.setAttribute("contenteditable", false)
     @image.onload = @recordOriginalDimensions
     @loadImagePreview()
     @updateAttributes()


Use strpos for faster event output filtering

--- lib/sensu-dashboard/assets/javascripts/views/events/list.coffee
+++ lib/sensu-dashboard/assets/javascripts/views/events/list.coffee
@@ -24,7 +24,8 @@
             record.get('client') == model.get('name')
         else if _.isString(model)
           resolved.filter (record) ->
-            liquidMetal.score(record.get('output'), model) > 0.7
+            output = record.get('output').toLowerCase()
+            output.indexOf(model.toLowerCase()) != -1

       resolved


Make empty pie chart appear on proposal page

--- lineman/app/js/controllers/proposal_pie_chart_controller.coffee
+++ lineman/app/js/controllers/proposal_pie_chart_controller.coffee
@@ -4,7 +4,8 @@
     { value : 0, color : "#90D490" },
     { value : 0, color : "#F0BB67" },
     { value : 0, color : "#D49090" },
-    { value : 0, color : "#dd0000"}
+    { value : 0, color : "#dd0000" },
+    { value : 0, color : "#cccccc" }
   ]

   $scope.pieChartOptions =
@@ -16,11 +17,12 @@
   refreshPieChartData = ->
     return unless $scope.proposal
     counts = $scope.proposal.voteCounts
-    # yeah - this is done to preseve the view binding on the pieChartData
+    hasAnyVotes = counts.yes + counts.abstain + counts.no + counts.block > 0
     $scope.pieChartData[0].value = counts.yes
     $scope.pieChartData[1].value = counts.abstain
     $scope.pieChartData[2].value = counts.no
     $scope.pieChartData[3].value = counts.block
+    $scope.pieChartData[4].value = if hasAnyVotes then 0 else 1

   $scope.$watch 'proposal.voteCounts', ->
     refreshPieChartData()


Precheck ranges just to be safe

Precheck ranges just to be safe

--- build/tasks/compile-packages-slug-task.coffee
+++ build/tasks/compile-packages-slug-task.coffee
@@ -3,6 +3,7 @@
 fs = require 'fs-plus'
 _ = require 'underscore-plus'
 normalizePackageData = require 'normalize-package-data'
+semver = require 'semver'

 OtherPlatforms = ['darwin', 'freebsd', 'linux', 'sunos', 'win32'].filter (platform) -> platform isnt process.platform

@@ -88,5 +89,10 @@
     metadata._atomKeymaps = getKeymaps(appDir)
     metadata._deprecatedPackages = require('../deprecated-packages')

+    for name, {version} of metadata._deprecatedPackages
+      unless semver.validRange(version)
+        invalidPackages = true
+        grunt.log.error("Invalid range: #{version} (#{name})")
+
     grunt.file.write(path.join(appDir, 'package.json'), JSON.stringify(metadata))
     not invalidPackages


hubot: handle "with" keyword and multi-word scenarii correctly

A non-greedy regex inside an optional capture doesn't work as
expected. Use several regexes instead.

--- hubot/warp.coffee
+++ hubot/warp.coffee
@@ -50,21 +50,21 @@

   warp_url = process.env.HUBOT_WARP_URL

-  response = (msg) ->
+  response = (msg, scenario, profile, margs, pargs) ->

-    scenario = msg.match[1]
+    scenario = scenario
       .split(/\ +/)
       .filter((a) -> a)
       .join("-")
     args = []
-    if msg.match[2]
-      args.push('profile=' + encodeURIComponent(msg.match[2]))
+    if profile
+      args.push('profile=' + encodeURIComponent(profile))

-    if msg.match[3]
-      args.push('matchargs=' + encodeURIComponent(arg)) for arg in msg.match[3].split(" ")
+    if margs
+      args.push('matchargs=' + encodeURIComponent(arg)) for arg in margs.split(" ")

-    if msg.match[4]
-      args.push('args=' + encodeURIComponent(arg)) for arg in msg.match[4].split(" ")
+    if pargs
+      args.push('args=' + encodeURIComponent(arg)) for arg in pargs.split(" ")

     warp = new Warp(scenario, msg)
     url = warp_url + "/scenarios/" + scenario + "/executions"
@@ -78,5 +78,13 @@
     es.onerror = ->
       es.close()

-  robot.respond /(?:warp me|engage ?!) (\S+)(?: to (\S+)(?: ([\S ]+?))?)?(?: with (.*))?$/i, response
-  robot.hear /(\S+)(?: to (\S+)(?: ([\S ]+?))?)?(?: with (.*))?[,\.] [eE]ngage ?!$/i, response
+  handle = (msg) ->
+    mo = msg.match[1].match /(.+?)(?: to (\S+)(?: ([\S ]+?))?)?(?: with (.*))$/i
+    if mo
+      return response msg, mo[1], mo[2], mo[3], mo[4]
+    mo = msg.match[1].match /(.+?)(?: to (\S+)(?: ([\S ]+?))?)?$/i
+    if mo
+      return response msg, mo[1], mo[2], mo[3]
+
+  robot.respond /(?:warp me|engage ?!) (.*)/i, handle
+  robot.hear    /(.*)[,\.] [eE]ngage ?!$/i, handle


added auction_result

added auction_result

--- node/fabricate.coffee
+++ node/fabricate.coffee
@@ -518,5 +518,21 @@
       id: _.uniqueId()
       name: "Bill Powers"
       type: "User"
-
+
+    when 'auction_result'
+      auction_date: "2012-05-23T00:00:00Z"
+      auction_dates_text: "May 23, 2012"
+      dates_text: "1985"
+      description: null
+      dimensions: {in:'20 1/10 × 15 9/10 in', cm:'51 × 40 1/2 cm'}
+      estimate_text: "€120,000 - 160,000"
+      external_url: "http://www.lempertz-online.de/gldet_gb.asp?v=k1026400009980073900011&mkat=0&mablos=737&msb=&martist=0"
+      id: uuid()
+      image_url: "http://static1.artsy.net/auction_lots/51d041844c91c616610005a0/thumbnail.jpg"
+      image_versions: [thumbnail]
+      medium_text: "Screenprint on canvas"
+      organization: "Lempertz"
+      price_realized_text: "€140,300"
+      title: "MADONNA PAINTING"
+
   , extObj


New content gets an id ('_NEW:*') so it can be added to a book

When the id updates on save the book updates its reference

--- bookish/models.coffee
+++ bookish/models.coffee
@@ -5,9 +5,12 @@

   # Custom Models defined above are mixed in using `BaseContent.initialize`
   BaseContent = Backbone.Model.extend
+    # New content is given an id before it is saved so it can be added to a book
+    isNew = -> not @id or @id.match(/^_NEW:/)
     initialize: ->
       throw 'BUG: No mediaType set' if not @mediaType
       throw 'BUG: No mediaType not registered' if not MEDIA_TYPES.get @mediaType
+      @id = "_NEW:#{@cid}" if not @id

   # This gets used by `DeferrableCollection` but is instantiated afterwards
   ALL_CONTENT = null
@@ -236,6 +239,21 @@
       @manifest.on 'add',   (model, collection) -> ALL_CONTENT.add model
       @manifest.on 'reset', (model, collection) -> ALL_CONTENT.add model

+      # If a model's id changes then update the `navTree` (it was a new model that got saved)
+      @manifest.on 'change:id', (model, newValue, oldValue) =>
+        navTree = JSON.parse @get('navTreeStr')
+        # Find the node that has an `id` to this model
+        recFind = (nodes) ->
+          for node in nodes
+            return node if model.id == oldValue
+            if node.children
+              found = recFind node.children
+              return found if found
+        node = recFind(navTree)
+        return console.error 'BUG: There is an entry in the tree but no corresponding model in the manifest' if not node
+        node.id = newValue
+        @set 'navTreeStr', JSON.stringify navTree
+
       @manifest.on 'change:title', (model, newValue, oldValue) =>
         navTree = JSON.parse @get('navTreeStr')
         # Find the node that has an `id` to this model
@@ -268,9 +286,6 @@
       else if mediaType
         config = model
         throw 'BUG: Media type not registered' if not MEDIA_TYPES.get mediaType
-        uuid = b = (a) ->
-          (if a then (a ^ Math.random() * 16 >> a / 4).toString(16) else ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, b))
-        config.id = uuid() if not config.id

         # Create the model from a config and add it to the manifest
         ContentType = MEDIA_TYPES.get(mediaType).constructor


Avoid unnecessary page reloads when navigating #anchor links back/forward

Fix #584.

--- lib/assets/javascripts/turbolinks.coffee
+++ lib/assets/javascripts/turbolinks.coffee
@@ -266,7 +266,7 @@
   else if position isnt false
     if typeof position is 'string' and (element = document.querySelector(position))
       element.scrollIntoView()
-    if document.location.hash
+    else if document.location.hash
       document.location.href = document.location.href
       rememberCurrentUrlAndState()
     else
@@ -624,7 +624,12 @@

 onHistoryChange = (event) ->
   if event.state?.turbolinks && event.state.url != currentState.url
-    if cachedPage = pageCache[(new ComponentUrl(event.state.url)).absolute]
+    previousUrl = new ComponentUrl(currentState.url)
+    newUrl = new ComponentUrl(event.state.url)
+
+    if newUrl.withoutHash() is previousUrl.withoutHash()
+      updateScrollPosition()
+    else if cachedPage = pageCache[newUrl.absolute]
       cacheCurrentPage()
       fetchHistory cachedPage, scroll: [cachedPage.positionX, cachedPage.positionY]
     else


Prevent crash on program exit

Prevent crash on program exit

--- lib/lib/stdin_command_interceptor.coffee
+++ lib/lib/stdin_command_interceptor.coffee
@@ -91,7 +91,7 @@
       # In Node 0.12.2 calling setImmediate is important for avoiding an occasional blocking read
       # to STDIN that locks up the program until more input comes in.
       setImmediate =>
-        @inputStream.write(key)
+        @inputStream?.write(key)

   # Called from outside by a SIGHUP handler. Has the same effect as CTRL-P CTRL-R, which causes
   # Galley to recheck all containers.


Major overhaul of the expression manipulation library

--- math/expression_manipulation_spec.coffee
+++ math/expression_manipulation_spec.coffee
@@ -1,208 +1,267 @@
 #= require lib/math/expression_manipulation
-
-it_plays_manipulation_role = (subject, math)->
+it_plays_manipulation_role = (options)->
   describe "acting as an expression manipulation", ->
     beforeEach ->
-      @subject = subject.call(@)
-
-    it "has an invoke method", ->
-      expect(typeof @subject.invoke).toEqual "function"
-
-    it "returns an expression", ->
-      ret = @subject.invoke(@components.build_expression())
-      unless ret instanceof @components.classes.expression
-        throw "The return value was not an instance of expression"
+      @subject = options.subject.call(@)
+
+    it "has a perform method", ->
+      expect(typeof @subject.perform).toEqual "function"
+
+    it "returns an ExpressionPosition object", ->
+      orig = options.expression_for_performance.call(@)
+      results = @subject.perform(orig)
+      unless results instanceof ttm.lib.math.ExpressionPosition
+        throw "The return value was not an instance of expression position"

 it_inserts_component_into_the_last_nested_open_expression = (specifics)->
   it "adds #{specifics.name} to the deepest final open expression", ->
     @exp = @exp_builder(open_expression: {open_expression: []})
-    exp = specifics.manipulator.call(@).invoke(@exp)
+    exp = specifics.manipulator.call(@).perform(@exp)
     expected = specifics.should_equal_after(@exp_builder)
-    expect(exp).toBeAnEqualExpressionTo expected
+    expect(exp.expression).toBeAnEqualExpressionTo expected

 describe "expression manipulations", ->
   beforeEach ->
-    #@math = ttm.require('lib/math')
     @components = ttm.lib.math.ExpressionComponentSource.build()
-    @manip = ttm.require('lib/math/expression_manipulation').build(@components)
-    @exp_builder = ttm.require('lib/math/build_expression_from_javascript_object').buildExpression
-    @expression_to_string = ttm.require('lib/math/expression_to_string').toString
+    @pos = ttm.lib.math.ExpressionPosition
+    @manip = ttm.require('lib/math/expression_manipulation').build(@components, @pos)
+
+    builder_lib = ttm.require('lib/math/build_expression_from_javascript_object')
+    @exp_builder = builder_lib.build(@comps).builderFunction()
+    @exp_pos_builder = builder_lib.build(@comps).builderFunctionExpressionWithPositionAsLast()
+    expression_to_string = ttm.require('lib/math/expression_to_string').toString
     @expect_value = (expression, value)->
-      expect(@expression_to_string(expression)).toEqual value
+      expect(expression_to_string(expression)).toEqual value

   describe "a proof-of-concept example", ->
     it "produces the correct result TODO make this larger", ->
       exp = @exp_builder()
-      exp = @manip.build_append_number(value: 1).invoke(exp)
-      exp = @manip.build_append_number(value: 0).invoke(exp)
-      exp = @manip.build_append_multiplication().invoke(exp)
-      exp = @manip.build_open_sub_expression().invoke(exp)
-      exp = @manip.build_append_number(value: 2).invoke(exp)
-      exp = @manip.build_append_addition().invoke(exp)
-      exp = @manip.build_append_number(value: 4).invoke(exp)
-      exp = @manip.build_close_sub_expression().invoke(exp)
+      position = ttm.lib.math.ExpressionPosition.build(position: exp.id(), expression: exp)
+
+      exp = @manip.build_append_number(value: 1).perform(exp, position).expression
+      exp = @manip.build_append_number(value: 0).perform(exp, position).expression
+      exp = @manip.build_append_multiplication().perform(exp, position).expression
+
+      exp = @manip.build_append_open_sub_expression().perform(exp, position).expression
+      exp = @manip.build_append_number(value: 2).perform(exp, position).expression
+      exp = @manip.build_append_addition().perform(exp, position).expression
+      exp = @manip.build_append_number(value: 4).perform(exp, position).expression
+      exp = @manip.build_close_sub_expression().perform(exp, position).expression
+
       expected = @exp_builder(10, '*', [2, '+', 4])
       expect(exp).toBeAnEqualExpressionTo expected

-  describe "exponentiate last element", ->
-    describe "on a single number-only expression", ->
-      beforeEach ->
-        @exp = @exp_builder(10)
-        @new_exp = @manip.build_exponentiate_last().invoke(@exp)
-
-      it "replaces the content of the expression with an exponentiation", ->
-        expect(@new_exp.first()).toBeInstanceOf @components.classes.exponentiation
-
-      it "provides the exponentiation its base", ->
-        expect(@new_exp.first().base()).toBeAnEqualExpressionTo @exp_builder([10]).first()
-
-    describe "on an expression that currently ends with an operator ", ->
-      it "replaces the trailing operator", ->
-        exp = @exp_builder(10, '+')
-        new_exp = @manip.build_exponentiate_last().invoke(exp)
-        expected = @exp_builder('^': [10, {open_expression: null}])
-        expect(new_exp).toBeAnEqualExpressionTo expected
-
-    describe "on an expression that has a trailing exponent", ->
-      it "manipulates expression correctly", ->
-        exp = @exp_builder('^': [10, {open_expression: null}])
-        new_exp = @manip.build_exponentiate_last().invoke(exp)
-        expected = @exp_builder('^': [10, {open_expression: null}])
-        expect(new_exp).toBeAnEqualExpressionTo expected
-
-  describe "append number", ->
-    describe "when the previous element is a closed expression", ->
-      beforeEach ->
-        @exp = @exp_builder([1])
-
-      it "adds a multiplication symbol between elements", ->
-        exp = @manip.build_append_number(value: 11).invoke(@exp)
-        expected = @exp_builder([1], '*', 11)
-        expect(exp).toBeAnEqualExpressionTo expected
-
-    describe "when the previous element is a nested open expression", ->
-      beforeEach ->
-        @exp = @exp_builder(open_expression: {open_expression: []})
-
-      it "adds the number at the correct place", ->
-        exp = @manip.build_append_number(value: 8).invoke(@exp)
-        expected = @exp_builder(open_expression: {open_expression: [8]})
-        expect(exp).toBeAnEqualExpressionTo expected
-
-    describe "when the previous element is an exponentiation ", ->
-      describe "with no power", ->
-        beforeEach ->
-          @exp = @exp_builder('^': [10, {open_expression:  null}])
-
-        it "inserts the number into the exponentiation", ->
-          new_exp = @manip.build_append_number(value: 11).invoke(@exp)
-          expected = @exp_builder('^': [10, {open_expression: 11}])
-          expect(new_exp).toBeAnEqualExpressionTo expected
-
-      describe "with a power", ->
-        beforeEach ->
-          @exp = @exp_builder('^': [10, 11])
-
-        it "inserts a multiplication and then the number", ->
-          new_exp = @manip.build_append_number(value: 7).invoke(@exp)
-          expected = @exp_builder({'^': [10,11]}, '*', 7)
-          expect(new_exp).toBeAnEqualExpressionTo expected
-
-    describe "when the previous element is a root", ->
-      it "inserts as root's radicand when radicand expression is open", ->
-        @exp = @exp_builder('root': [10, {open_expression: null}])
-        new_exp = @manip.build_append_number(value: 6).invoke(@exp)
-        expected = @exp_builder('root': [10, {open_expression: 6}])
-        expect(new_exp).toBeAnEqualExpressionTo expected
-
-    describe "when the prevous element is none of the other special cases", ->
-      it "adds the number as a new number", ->
-        exp = @exp_builder(1, '+', 3, '=')
-        manipulated_exp = @manip.build_append_number(value: 8).invoke(exp)
-        expected = @exp_builder(1, '+', 3, '=', 8)
-        expect(manipulated_exp).toBeAnEqualExpressionTo expected
-
-
-  describe "appending an equals", ->
-    it "inserts an equals sign into the equation", ->
-      @exp = @exp_builder(1, '+', 3)
-      new_exp = @manip.build_append_equals().invoke(@exp)
-      expected = @exp_builder(1, '+', 3, '=')
-      expect(new_exp).toBeAnEqualExpressionTo expected
-
-  describe "opening a new sub expression", ->
-    it "adds a sub-expression to the expression", ->
-      @exp = @exp_builder(1, '+')
-      new_exp = @manip.build_open_sub_expression().invoke(@exp)
-      expected = @exp_builder(1, '+', {open_expression: []})
-      expect(new_exp).toBeAnEqualExpressionTo expected
-
-    it "adds an expression that isOpen", ->
-      @exp = @exp_builder(1, '+')
-      new_exp = @manip.build_open_sub_expression().invoke(@exp)
-
-      exp = new_exp.nth(2)
-      expect(exp).toBeInstanceOf @components.classes.expression
-      expect(exp.isOpen()).toEqual true
-
-    describe "when adding to a sub-expression", ->
-      it "should add everything correctly", ->
-        exp = @exp_builder()
-        exp = @manip.build_append_number(value: 6).invoke(exp)
-        exp = @manip.build_append_addition().invoke(exp)
-
-        exp = @manip.build_open_sub_expression().invoke(exp)
-        exp = @manip.build_append_number(value: 6).invoke(exp)
-        exp = @manip.build_append_addition().invoke(exp)
-
-        exp = @manip.build_open_sub_expression().invoke(exp)
-        exp = @manip.build_close_sub_expression().invoke(exp)
-        exp = @manip.build_close_sub_expression().invoke(exp)
-        expected = @exp_builder(6, '+', [6, '+', []])
-
-        expect(exp).toBeAnEqualExpressionTo expected
-
-  describe "closing a sub expression ", ->
-    it "takes an open subexpression and closes it", ->
-      exp = @exp_builder({open_expression: []})
-      new_exp = @manip.build_close_sub_expression().invoke(exp)
-      expect(new_exp).toBeAnEqualExpressionTo @exp_builder([])
-
-    it "correctly handles nested open subexpressions", ->
-      exp = @exp_builder(open_expression: {open_expression: null})
-      new_exp = @manip.build_close_sub_expression().invoke(exp)
-      expected = @exp_builder(open_expression: [[]])
-      expect(new_exp).toBeAnEqualExpressionTo expected

   describe "appending multiplication", ->
-    it_plays_manipulation_role (test)->
-      @manip.build_append_multiplication()
+    it_plays_manipulation_role
+      subject: ->
+        @manip.build_append_multiplication()
+      expression_for_performance: ->
+        @exp_pos_builder(10)
+
+    it "does nothing if the expression is empty", ->
+      exp = @exp_builder()
+      position = ttm.lib.math.ExpressionPosition.build(position: exp.id(), expression: exp)
+      exp = @manip.build_append_multiplication().perform(exp, position).expression
+      expected = @exp_builder()
+      expect(exp).toBeAnEqualExpressionTo expected
+
+    it "appends a multiplication", ->
+      @exp = @exp_builder(10)

     it "adds multiplication to the end of the expression", ->
       exp = @exp_builder(1)
-      new_exp = @manip.build_append_multiplication().invoke(exp)
-      expect(new_exp.last() instanceof @components.classes.multiplication).toEqual true
+      results = @manip.build_append_multiplication().perform(exp)
+      expect(results.expression.last() instanceof @components.classes.multiplication).toEqual true

     it "correctly adds multiplication to an exponentiation", ->
       exp = @exp_builder('^': [1, 2])
-      new_exp = @manip.build_append_multiplication().invoke(exp)
+      new_exp = @manip.build_append_multiplication().perform(exp)
       expected = @exp_builder({'^': [1, 2]}, '*')
-      expect(new_exp).toBeAnEqualExpressionTo expected
+      expect(new_exp.expression).toBeAnEqualExpressionTo expected

     it_inserts_component_into_the_last_nested_open_expression(
       name: 'multiplication'
       manipulator: -> @manip.build_append_multiplication()
       should_equal_after: (builder)-> builder(open_expression: {open_expression: ['*']}))

+  describe "exponentiate last element", ->
+    describe "on a single number-only expression", ->
+      beforeEach ->
+        @new_exp = @manip.build_exponentiate_last().perform(@exp)
+
+      it "replaces the content of the expression with an exponentiation", ->
+        expect(@new_exp.first()).toBeInstanceOf @components.classes.exponentiation
+
+      it "provides the exponentiation its base", ->
+        expect(@new_exp.first().base()).toBeAnEqualExpressionTo @exp_builder([10]).first()
+
+    describe "on an expression that currently ends with an operator ", ->
+      it "replaces the trailing operator", ->
+        exp = @exp_builder(10, '+')
+        new_exp = @manip.build_exponentiate_last().perform(exp)
+        expected = @exp_builder('^': [10, {open_expression: null}])
+        expect(new_exp).toBeAnEqualExpressionTo expected
+
+    describe "on an expression that has a trailing exponent", ->
+      it "manipulates expression correctly", ->
+        exp = @exp_builder('^': [10, {open_expression: null}])
+        new_exp = @manip.build_exponentiate_last().perform(exp)
+        expected = @exp_builder('^': [10, {open_expression: null}])
+        expect(new_exp).toBeAnEqualExpressionTo expected
+
+  describe "append number", ->
+    it_plays_manipulation_role
+      subject: ->
+        @manip.build_append_number(value: 8)
+      expression_for_performance: ->
+        @exp_pos_builder(10)
+
+    it "inserts a number when the expression is empty", ->
+      @exp_pos = @exp_pos_builder()
+      exp = @manip.build_append_number(value: 8).perform(@exp_pos)
+      expected = @exp_builder(8)
+      expect(exp.expression()).toBeAnEqualExpressionTo expected
+
+    describe "when the previous element is a closed expression", ->
+      beforeEach ->
+        @exp_pos = @exp_pos_builder([1])
+
+      it "adds a multiplication symbol between elements", ->
+        exp = @manip.build_append_number(value: 11).perform(@exp_pos)
+        expected = @exp_builder([1], '*', 11)
+        expect(exp.expression()).toBeAnEqualExpressionTo expected
+
+  describe "append exponentiation", ->
+    it_plays_manipulation_role
+      subject: ->
+        @manip.build_append_exponentiation()
+      expression_for_performance: ->
+        @exp_pos_builder(10)
+
+    # this is x'd out because, for now, this should be failing
+    # we need to fix later to get cursor-manipulation code that
+    # is appropriate for the calculate and
+    # gets rid of most of the concept of open expressions
+    xdescribe "when the previous element is a nested open expression", ->
+      beforeEach ->
+        @exp_pos = @exp_pos_builder(open_expression: {open_expression: []})
+
+      it "adds the number at the correct place", ->
+        exp = @manip.build_append_number(value: 8).perform(@exp_pos.expression(), @exp_pos)
+        expected = @exp_builder(open_expression: {open_expression: [8]})
+        expect(exp.expression).toBeAnEqualExpressionTo expected
+
+    describe "when the pointed-at element is an exponentiation ", ->
+      # this is x'd out because, for now, this should be failing
+      # we need to fix later to get cursor-manipulation code that
+      # is appropriate for the calculate and
+      # gets rid of most of the concept of open expressions
+      xdescribe "with no power", ->
+        beforeEach ->
+          @exp = @exp_builder('^': [10, {open_expression:  null}])
+
+        it "inserts the number into the exponentiation", ->
+          new_exp = @manip.build_append_number(value: 11).perform(@exp)
+          expected = @exp_builder('^': [10, {open_expression: 11}])
+          expect(new_exp).toBeAnEqualExpressionTo expected
+
+      describe "with a power", ->
+        beforeEach ->
+          @exp_pos = @exp_pos_builder('^': [10, 11])
+
+        it "inserts a multiplication and then the number", ->
+          new_exp = @manip.build_append_number(value: 7).perform(@exp_pos)
+          expected = @exp_builder({'^': [10,11]}, '*', 7)
+          expect(new_exp.expression()).toBeAnEqualExpressionTo expected
+
+    describe "when the pointed-at element is a root", ->
+      # this is x'd out because, for now, this should be failing
+      # we need to fix later to get cursor-manipulation code that
+      # is appropriate for the calculate and
+      # gets rid of most of the concept of open expressions
+      xit "inserts as root's radicand when radicand expression is open", ->
+        @exp = @exp_builder('root': [10, {open_expression: null}])
+        new_exp = @manip.build_append_number(value: 6).perform(@exp)
+        expected = @exp_builder('root': [10, {open_expression: 6}])
+        expect(new_exp).toBeAnEqualExpressionTo expected
+
+    describe "when the pointed at element is not one of those special cases", ->
+      it "adds the number as a new number", ->
+        exp_pos = @exp_pos_builder(1, '+', 3, '=')
+        manipulated_exp = @manip.build_append_number(value: 8).perform(exp_pos)
+        expected = @exp_builder(1, '+', 3, '=', 8)
+        expect(manipulated_exp.expression()).toBeAnEqualExpressionTo expected
+
+
+  describe "appending an equals", ->
+    it "inserts an equals sign into the equation", ->
+      @exp = @exp_builder(1, '+', 3)
+      new_exp = @manip.build_append_equals().perform(@exp)
+      expected = @exp_builder(1, '+', 3, '=')
+      expect(new_exp).toBeAnEqualExpressionTo expected
+
+  describe "appending a sub expression", ->
+    it_plays_manipulation_role
+      subject: ->
+        @manip.build_append_open_sub_expression()
+      expression_for_performance: ->
+        @exp_pos_builder(10)
+
+    it "adds a sub-expression to the expression", ->
+      @exp = @exp_builder(1, '+')
+      new_exp = @manip.build_append_open_sub_expression().perform(@exp)
+      expected = @exp_builder(1, '+', {open_expression: []})
+      expect(new_exp).toBeAnEqualExpressionTo expected
+
+    it "adds an expression that isOpen", ->
+      @exp = @exp_builder(1, '+')
+      new_exp = @manip.build_append_open_sub_expression().perform(@exp)
+
+      exp = new_exp.nth(2)
+      expect(exp).toBeInstanceOf @components.classes.expression
+      expect(exp.isOpen()).toEqual true
+
+    describe "when adding to a sub-expression", ->
+      it "should add everything correctly", ->
+        exp = @exp_builder()
+        exp = @manip.build_append_number(value: 6).perform(exp)
+        exp = @manip.build_append_addition().perform(exp)
+
+        exp = @manip.build_append_open_sub_expression().perform(exp)
+        exp = @manip.build_append_number(value: 6).perform(exp)
+        exp = @manip.build_append_addition().perform(exp)
+
+        exp = @manip.build_append_open_sub_expression().perform(exp)
+        exp = @manip.build_close_sub_expression().perform(exp)
+        exp = @manip.build_close_sub_expression().perform(exp)
+        expected = @exp_builder(6, '+', [6, '+', []])
+
+        expect(exp).toBeAnEqualExpressionTo expected
+
+  describe "closing a sub expression ", ->
+    it "takes an open subexpression and closes it", ->
+      exp = @exp_builder({open_expression: []})
+      new_exp = @manip.build_close_sub_expression().perform(exp)
+      expect(new_exp).toBeAnEqualExpressionTo @exp_builder([])
+
+    it "correctly handles nested open subexpressions", ->
+      exp = @exp_builder(open_expression: {open_expression: null})
+      new_exp = @manip.build_close_sub_expression().perform(exp)
+      expected = @exp_builder(open_expression: [[]])
+      expect(new_exp).toBeAnEqualExpressionTo expected
+
+
   describe "appending division", ->
-    it_plays_manipulation_role (test)->
-      @manip.build_append_division()
+    it_plays_manipulation_role
+      subject: ->
+        @manip.build_append_division()

     it "adds a division to the expression", ->
       exp = @exp_builder(1)

-      new_exp = @manip.build_append_division().invoke(exp)
+      new_exp = @manip.build_append_division().perform(exp)
       expect(new_exp.last() instanceof @components.classes.division).toBeTruthy()

@@ -213,12 +272,13 @@

   describe "appending addition", ->
-    it_plays_manipulation_role (test)->
-      @manip.build_append_addition()
+    it_plays_manipulation_role
+      subject: ->
+        @manip.build_append_addition()

     it "adds addition to the end of the expression", ->
       exp = @exp_builder(1)
-      new_exp = @manip.build_append_addition().invoke(exp)
+      new_exp = @manip.build_append_addition().perform(exp)
       expect(new_exp.last() instanceof @components.classes.addition).toEqual true

     it_inserts_component_into_the_last_nested_open_expression(
@@ -229,12 +289,13 @@

   describe "appending subtraction", ->
-    it_plays_manipulation_role (test)->
-      @manip.build_append_subtraction()
+    it_plays_manipulation_role
+      subject: ->
+        @manip.build_append_subtraction()

     it "adds subtraction to the end of the expression", ->
       exp = @exp_builder(1)
-      new_exp = @manip.build_append_subtraction().invoke(exp)
+      new_exp = @manip.build_append_subtraction().perform(exp)
       expect(new_exp.last() instanceof @components.classes.subtraction).toEqual true

     it_inserts_component_into_the_last_nested_open_expression(
@@ -243,14 +304,15 @@
       should_equal_after: (builder)-> builder(open_expression: {open_expression: ['-']}))

   describe "appending a decimal", ->
-    it_plays_manipulation_role ->
-      @manip.build_append_decimal(value: 5)
+    it_plays_manipulation_role
+      subject: ->
+        @manip.build_append_decimal(value: 5)

     it "correctly adds a decimal to the value", ->
       exp = @components.build_expression()
-      exp = @manip.build_append_number(value: 1).invoke(exp)
-      exp = @manip.build_append_decimal().invoke(exp)
-      exp = @manip.build_append_number(value: 1).invoke(exp)
+      exp = @manip.build_append_number(value: 1).perform(exp)
+      exp = @manip.build_append_decimal().perform(exp)
+      exp = @manip.build_append_number(value: 1).perform(exp)
       @expect_value(exp, '1.1')

     it_inserts_component_into_the_last_nested_open_expression(
@@ -260,25 +322,24 @@

   describe "square expression", ->
-    beforeEach ->
-      @square = @manip.build_square()
-
-    it_plays_manipulation_role (test)->
-      @square
+    it_plays_manipulation_role
+      subject: ->
+        @manip.build_square()

     it "returns a squared expression", ->
       exp = @exp_builder(10)
-      squared = @square.invoke(exp)
+      squared = @manip.build_square().perform(exp)
       @expect_value(squared, '100')

   describe "AppendRoot", ->
-    it_plays_manipulation_role ->
-      @manip.build_append_root()
+    it_plays_manipulation_role
+      subject: ->
+        @manip.build_append_root()

     it "adds a mulitplication and root to the expression", ->
       exp = @exp_builder([1])

-      new_exp = @manip.build_append_root(degree: 2).invoke(exp)
+      new_exp = @manip.build_append_root(degree: 2).perform(exp)

       expect(new_exp.last() instanceof @components.classes.root).toBeTruthy()
       expect(new_exp.last(1) instanceof @components.classes.multiplication).toBeTruthy()
@@ -286,56 +347,84 @@
     it "adds a root with a radicand that is an open expression", ->
       exp = @exp_builder([1])

-      new_exp = @manip.build_append_root(degree: 2).invoke(exp)
+      new_exp = @manip.build_append_root(degree: 2).perform(exp)

       expect(new_exp.last() instanceof @components.classes.root).toBeTruthy()
       expect(new_exp.last().radicand().isOpen()).toBeTruthy()

   describe "AppendVariable", ->
-    it_plays_manipulation_role ->
-      @manip.build_append_variable()
+    it_plays_manipulation_role
+      subject: ->
+        @manip.build_append_variable()

     it "adds itself to an empty expression", ->
       exp = @exp_builder()
       append_var = @manip.build_append_variable(variable: "doot")
-      new_exp = append_var.invoke(exp)
+      new_exp = append_var.perform(exp)
       expected = @exp_builder(variable: "doot")
       expect(new_exp).toBeAnEqualExpressionTo expected

+  describe "Reset", ->
+    it_plays_manipulation_role
+      subject: ->
+        @manip.build_reset()
+
+      expression_for_performance: ->
+        false # not important at all
+
+    beforeEach ->
+      @reset = @manip.build_reset()
+      @result = @reset.perform()
+
+    it "returns an empty resulting expression", ->
+      expect(@result.expression()).toBeAnEqualExpressionTo @exp_builder()
+
+    it "returns an pointer which points to that expression", ->
+      expect(@result.position()).toEqual @result.expression().id()
+
+  describe "update position", ->
+    it_plays_manipulation_role
+      subject: ->
+        @manip.build_update_position()
+
+      expression_for_performance: ->
+        false
+
+
   describe "(moved over from other test file)", ->
-    describe "NumberManipulation", ->
-      it_plays_manipulation_role (test)->
-        @manip.build_append_number(value: 5)

     describe "NegationManipulation", ->
-      it_plays_manipulation_role (test)->
-        @manip.build_negate_last()
+      it_plays_manipulation_role
+        subject: ->
+          @manip.build_negate_last()

       it "will negate the last element", ->
         exp = @exp_builder(1)
-        new_exp = @manip.build_negate_last().invoke(exp)
+        new_exp = @manip.build_negate_last().perform(exp)
         expect(new_exp.last().value()).toEqual -1

     describe "AppendPi", ->
-      it_plays_manipulation_role (test)->
-        @manip.build_append_pi()
+      it_plays_manipulation_role
+        subject: ->
+          @manip.build_append_pi()

       it "adds a mulitplication and pi to the expression", ->
         exp = @exp_builder(1)
-        new_exp = @manip.build_append_pi().invoke(exp)
+        new_exp = @manip.build_append_pi().perform(exp)
         expect(new_exp.last() instanceof @components.classes.pi).toBeTruthy()
         expect(new_exp.last(1) instanceof @components.classes.multiplication).toBeTruthy()

     describe "SquareRootManipulation", ->
-      it_plays_manipulation_role (test)->
-        @manip.build_square_root()
+      it_plays_manipulation_role
+        subject: ->
+          @manip.build_square_root()

       it "finds the square root of an expression", ->
         exp = @exp_builder([4])
-        new_exp = @manip.build_square_root().invoke(exp)
+        new_exp = @manip.build_square_root().perform(exp)
         expect(new_exp.last().value()).toEqual '2'


remove eventmitter as superclass to prevent writing emitter variables  to the store on server restart

--- modules/http/lib/sessions/Session.coffee
+++ modules/http/lib/sessions/Session.coffee
@@ -3,15 +3,20 @@

 module.exports =

-    class Session extends events.EventEmitter
+    class Session

         constructor: (@SID, config)->
-            super()

             #console.log 'create session', @SID

             @token = floyd.tools.crypto.hash(floyd.tools.strings.uuid()+@SID)+@SID
-
+
+            emitter = new events.EventEmitter()
+
+            @emit = (args...)=> emitter.emit.apply emitter, args
+            @on = @addListener = (args...)=> emitter.addListener.apply emitter, args
+            @off = @removeListener = (args...)=> emitter.removeListener.apply emitter, args
+
             @public =
                 SID: @SID
                 TOKEN: @token


tests: Use RPi2 for the device.getDisplayName() tests

At some point we changed the name field of RPI1 and
had to update these tests to use the new one. We
also recently reverted some OS releases, which as a
result bought back the old name and broke the tests
again. So, I decided to just use RPi2 for this tests
since we don't expect its name to change soon but
also its one of the few devices that also has an alias.

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

--- tests/integration/models/device.spec.coffee
+++ tests/integration/models/device.spec.coffee
@@ -38,12 +38,12 @@
 		describe 'balena.models.device.getDisplayName()', ->

 			it 'should get the display name for a known slug', ->
-				promise = balena.models.device.getDisplayName('raspberry-pi')
-				m.chai.expect(promise).to.eventually.equal('Raspberry Pi (v1 / Zero / Zero W)')
+				promise = balena.models.device.getDisplayName('raspberry-pi2')
+				m.chai.expect(promise).to.eventually.equal('Raspberry Pi 2')

 			it 'should get the display name given a device type alias', ->
-				promise = balena.models.device.getDisplayName('raspberrypi')
-				m.chai.expect(promise).to.eventually.equal('Raspberry Pi (v1 / Zero / Zero W)')
+				promise = balena.models.device.getDisplayName('raspberrypi2')
+				m.chai.expect(promise).to.eventually.equal('Raspberry Pi 2')

 			it 'should eventually be undefined if the slug is invalid', ->
 				promise = balena.models.device.getDisplayName('asdf')
@@ -52,20 +52,20 @@
 		describe 'balena.models.device.getDeviceSlug()', ->

 			it 'should eventually be the slug from a display name', ->
-				promise = balena.models.device.getDeviceSlug('Raspberry Pi (v1 / Zero / Zero W)')
-				m.chai.expect(promise).to.eventually.equal('raspberry-pi')
+				promise = balena.models.device.getDeviceSlug('Raspberry Pi 2')
+				m.chai.expect(promise).to.eventually.equal('raspberry-pi2')

 			it 'should eventually be the slug if passing already a slug', ->
-				promise = balena.models.device.getDeviceSlug('raspberry-pi')
-				m.chai.expect(promise).to.eventually.equal('raspberry-pi')
+				promise = balena.models.device.getDeviceSlug('raspberry-pi2')
+				m.chai.expect(promise).to.eventually.equal('raspberry-pi2')

 			it 'should eventually be undefined if the display name is invalid', ->
 				promise = balena.models.device.getDeviceSlug('asdf')
 				m.chai.expect(promise).to.eventually.be.undefined

 			it 'should eventually be the slug if passing an alias', ->
-				promise = balena.models.device.getDeviceSlug('raspberrypi')
-				m.chai.expect(promise).to.eventually.equal('raspberry-pi')
+				promise = balena.models.device.getDeviceSlug('raspberrypi2')
+				m.chai.expect(promise).to.eventually.equal('raspberry-pi2')

 		describe 'balena.models.device.getSupportedDeviceTypes()', ->


Bongo: cosmetic update convert double quotes to single quote

--- node_modules_koding/bongo/lib/bongo.coffee
+++ node_modules_koding/bongo/lib/bongo.coffee
@@ -295,7 +295,7 @@

       {type, message} = data

-      exchange.publish "", message, type: type
+      exchange.publish '', message, type: type
       exchange.close()

@@ -551,7 +551,7 @@
       @fetchBrokerExchange (brokerExchange) =>
         # when internal error send a generic error message to client
         if message?.arguments[0]?.internal?
-          message.arguments[0] = {message:"Something went wrong!"}
+          message.arguments[0] = {message:'Something went wrong!'}

         brokerExchange.publish secretName, JSON.stringify(message, @replacer)

@@ -594,7 +594,7 @@
     .on 'dbClientUp', =>
       @dbClientDown = no
     .on 'error', ->
-      console.error "Model error!"
+      console.error 'Model error!'
       console.error err
       process.exit 1

@@ -631,13 +631,13 @@
           payload.method
           [payload.arguments..., callback]
           client.context
-          "n/a"
-          "kite.js"
+          'n/a'
+          'kite.js'
           client
         )

     .on 'error', (err) ->
-      console.error "Kite server error!"
+      console.error 'Kite server error!'
       console.error err
       process.exit 1


Simpler?

Simpler?

--- resources/assets/coffee/react/beatmap-discussions/header.coffee
+++ resources/assets/coffee/react/beatmap-discussions/header.coffee
@@ -108,11 +108,9 @@
   stats: ->
     bn = 'counter-box'

-    types = ['mine', 'resolved', 'pending', 'praises']
-    types.push('deleted') if @props.currentUser.isAdmin
-    types.push('total')
+    for type in ['mine', 'resolved', 'pending', 'praises', 'deleted', 'total']
+      continue if type == 'deleted' && !@props.currentUser.isAdmin

-    for type in types
       topClasses = "#{bn} #{bn}--beatmap-discussions #{bn}--#{type}"
       topClasses += ' js-active' if @props.mode == 'timeline' && @props.currentFilter == type


[module] refactored `printf` module

[module] refactored `printf` module

--- modules/printf.coffee
+++ modules/printf.coffee
@@ -10,10 +10,9 @@
  * @return {string} String with replaced placeholders
 ###
 module.exports = (string, placeholders...) ->
-  _placeholder = placeholders[0]
+  [placeholder, ...] = placeholders

-  if placeholders.length == 1 and Array.isArray _placeholder
-    return vsprintf string, _placeholder
+  if placeholders.length == 1 and Array.isArray placeholder
+    return vsprintf string, placeholder
   else
-    placeholders.unshift(string)
-    return sprintf.apply null, placeholders+    return sprintf string, placeholders...


try catch encodeURIComponent

try catch encodeURIComponent

--- services/web/app/coffee/Features/Editor/EditorController.coffee
+++ services/web/app/coffee/Features/Editor/EditorController.coffee
@@ -102,9 +102,15 @@
 			if docLines?
 				docLines = for line in docLines
 					if line.text?
-						line.text = unescape(encodeURIComponent(line.text))
+						try
+							line.text = unescape(encodeURIComponent(line.text))
+						catch err
+							logger.err err:err, project_id:project_id, doc_id:doc_id, fromVersion:fromVersion, line:line, "error encoding line.text uri component"
 					else
-						line = unescape(encodeURIComponent(line))
+						try
+							line = unescape(encodeURIComponent(line))
+						catch err
+							logger.err err:err, project_id:project_id, doc_id:doc_id, fromVersion:fromVersion, line:line, "error encoding line uri component"
 					line
 			callback(err, docLines, version, ops)


max-width: 100%, max-height: 100vh rules for rotated images

--- client/message.coffee
+++ client/message.coffee
@@ -491,13 +491,19 @@
     rotate = (settings.rotate ? 0) + (exifRotate ? 0)
     if rotate
       ## `rotate` is in clockwise degrees
-      radians = rotate * Math.PI / 180
+      radians = -rotate * Math.PI / 180
       ## Computation based on https://stackoverflow.com/a/3231438
       width = Math.abs(Math.sin radians) * image.height + Math.abs(Math.cos radians) * image.width
       height = Math.abs(Math.sin radians) * image.width + Math.abs(Math.cos radians) * image.height
-      scale = image.width / width
+      #scale = image.width / width
+      ## max-width: 100%
+      scale = image.parentNode.getBoundingClientRect().width / width
+      ## max-height: 100vh
+      if height * scale > window.innerHeight
+        scale *= window.innerHeight / (height * scale)
+      width *= scale
       height *= scale
-      image.style.transform = "translateY(#{(height - image.height)/2}px) scale(#{scale}) rotate(#{rotate}deg)"
+      image.style.transform = "translate(#{(width - image.width)/2}px, #{(height - image.height)/2}px) scale(#{scale}) rotate(#{rotate}deg)"
       image.parentNode.style.height = "#{height}px"
     else
       image.style.transform = null


add paste event

add paste event

--- lib/assets/javascripts/twine.js.coffee
+++ lib/assets/javascripts/twine.js.coffee
@@ -333,5 +333,5 @@
     return teardown: ->
       $(node).off eventName, onEventHandler

-for eventName in ['click', 'dblclick', 'mouseenter', 'mouseleave', 'mouseover', 'mouseout', 'mousedown', 'mouseup', 'submit', 'dragenter', 'dragleave', 'dragover', 'drop', 'drag', 'change', 'keypress', 'keydown', 'keyup', 'input', 'error', 'done', 'success', 'fail', 'blur', 'focus', 'load']
+for eventName in ['click', 'dblclick', 'mouseenter', 'mouseleave', 'mouseover', 'mouseout', 'mousedown', 'mouseup', 'submit', 'dragenter', 'dragleave', 'dragover', 'drop', 'drag', 'change', 'keypress', 'keydown', 'keyup', 'input', 'error', 'done', 'success', 'fail', 'blur', 'focus', 'load', 'paste']
   setupEventBinding(eventName)


Make the sign-message dialog non-dismissable by clicking the overlay

--- client/sign-message.coffee
+++ client/sign-message.coffee
@@ -39,6 +39,6 @@
     catch err then display_error err

   dialog.on 'hidden', -> do dialog.remove
-  dialog.modal()
+  dialog.modal backdrop: 'static'

 module.exports = sign_message_any


Set default value of start-selenium to no

--- client/test/bin/cmd.coffee
+++ client/test/bin/cmd.coffee
@@ -20,7 +20,7 @@

   .describe 'start-selenium', 'if enabled starts a selenium server process'
   .boolean 'start-selenium'
-  .default 'start-selenium', true
+  .default 'start-selenium', no

   .argv


Remove version checking in logger

Remove version checking in logger

--- packages/deft/src/coffee/log/Logger.coffee
+++ packages/deft/src/coffee/log/Logger.coffee
@@ -5,7 +5,7 @@

 ###*
 * Logger used by DeftJS.
-*
+*
 * Output is displayed in the console when using `ext-dev`/`ext-all-dev` or `sencha-debug`/`sencha-all-debug`.
 *
 * @private
@@ -73,23 +73,14 @@
 		return
 ,
 	->
-		if Ext.getVersion( 'extjs' )?
-			# Ext JS
-			@log = ( message, priority = 'info' ) ->
-				if priority is 'verbose'
-					priority = 'info'
-				if priority is 'deprecate'
-					priority = 'warn'
-				Ext.log(
-					msg: message
-					level: priority
-				)
-				return
-		else
-			# Sencha Touch
-			@log = ( message, priority = 'info' ) ->
-				if Ext.Logger? and Deft.isFunction( Ext.Logger.log )
-					Ext.Logger.log( message, priority )
-				return
-		return
+		@log = ( message, priority = 'info' ) ->
+			if priority is 'verbose'
+				priority = 'info'
+			if priority is 'deprecate'
+				priority = 'warn'
+			Ext.log(
+				msg: message
+				level: priority
+			)
+			return
 )


Updated with suggestions

Updated with suggestions

--- lib/operators/general-operators.coffee
+++ lib/operators/general-operators.coffee
@@ -55,7 +55,7 @@
   #
   # Returns nothing
   setTextRegister: (register, text) ->
-    if @motion? and @motion.isLinewise?()
+    if @motion?.isLinewise?()
       type = 'linewise'
       if text[-1..] isnt '\n'
         text += '\n'
@@ -104,7 +104,7 @@
       validSelection = true

     if validSelection?
-      text = @editor.getSelection().getText()
+      text = @editor.getSelectedText()
       @setTextRegister(@register, text)

       @editor.delete()


Refactor

Refactor

--- lib/Report.coffee
+++ lib/Report.coffee
@@ -3,7 +3,7 @@
 chalk    = require 'chalk'
 jsonlint = require 'jsonlint'
 Logger   = require './Logger'
-keys     = require './Keywords'
+KEYWORDS = require './Keywords'
 MSG      = require './Messages'

 DEFAULT =
@@ -47,14 +47,14 @@

   _validateRequiredKeys : (objt) ->
     haveRequiredValues = false
-    for key in keys.required when not objt[key]?
+    for key in KEYWORDS.required when not objt[key]?
       @logger.push 'error', MSG.required(@key)
       haveRequiredValues = true unless haveRequiredValues
     haveRequiredValues

   _validateMissingKeys : (objt) ->
     haveMissingValues = false
-    for key in keys.missing when not objt[key]?
+    for key in KEYWORDS.missing when not objt[key]?
       @logger.push 'warn', MSG.missing(@key)
       haveMissingValues = true unless haveMissingValues
     haveMissingValues


More analytics hooks

More analytics hooks

--- picoCTF-web/web/coffee/game_support.coffee
+++ picoCTF-web/web/coffee/game_support.coffee
@@ -36,6 +36,7 @@

   apiCall "POST", "/api/problems/feedback", postData
   .done (data) ->
+    ga('send', 'event', 'Problem', 'Review', 'Game');
     apiNotify data
     ig.game.getEntitiesByType(EntityQuestions)[0].CloseLearnPanel();
     ig.gui.element.action('showGroup', 'Group_QA');


Fixed complied files not being renamed on Windows (see https://github.com/jenius/roots/issues/588)

--- lib/config.coffee
+++ lib/config.coffee
@@ -11,6 +11,13 @@
 ###

 class Config
+  ###*
+   * This is an escaped version of the platform specific path seperator to be
+   * used in regular expressions. It is defined as a (derived) constant,
+   * because the path seperator does not change over the course of a run.
+  ###
+
+  PATH_SEPERATOR_REGEXP_STRING = path.sep.replace '\\', '\\\\'

   ###*
    * Creates a new instance of the roots config. This happens once, as soon as
@@ -141,10 +148,7 @@
     res.unshift(@output_path())
     res = res.join(path.sep)
     if ext
-      if (os.platform() is 'win32')
-        res = res.replace(///\.[^\#{path.sep}]*$///, ".#{ext}")
-      else
-        res = res.replace(///\.[^#{path.sep}]*$///, ".#{ext}")
+      res = res.replace(///\.[^#{PATH_SEPERATOR_REGEXP_STRING}]*$///, ".#{ext}")
     res

   ###*