Fixed: event's time and description are now in a separate span

--- client/app/views/calendar_view.coffee
+++ client/app/views/calendar_view.coffee
@@ -205,16 +205,28 @@
         @cal.fullCalendar 'unselect'
         @popover = null

-    onEventRender: (event, element) ->
+    onEventRender: (event, $element) ->
+        # TODO: use the new spinner, instead of this.
         if event.isSaving? and event.isSaving
-            spinTarget = $(element).find '.fc-event-time'
+            spinTarget = $element.find '.fc-event-time'
             spinTarget.addClass 'spinning'
             spinTarget.html " "
             spinTarget.spin "tiny"

-        $(element).attr 'title', event.title
-
-        return element
+        # Title and time are joined in the .fc-title for some reason.
+        # Let's split them to put them in the right element.
+        $displayedElement = $element.find '.fc-title'
+        titleAndTime = $displayedElement.html()
+        [time, title...] = titleAndTime.split ' '
+        title = title.join ' '
+
+        # Append the right values to the right elements.
+        $element.find('.fc-time').html time
+        $element.find('.fc-title').html title
+
+        $element.attr 'title', event.title
+
+        return $element

     onEventDragStop: (event, jsEvent, ui, view) ->
         event.isSaving = true


change tools dropdown to be 3 ULs

change tools dropdown to be 3 ULs

--- client/code/view/dataset/tools.coffee
+++ client/code/view/dataset/tools.coffee
@@ -1,6 +1,6 @@
 class Cu.View.DatasetTools extends Backbone.View
   className: 'dropdown-menu pull-right'
-  tagName: 'ul'
+  tagName: 'div'
   id: 'dataset-tools'

   initialize: ->
@@ -9,7 +9,11 @@

   render: ->
     @addToolInstance @model
-    @$el.append('
More tools…
')
+    @$el.html """

+

+

+
More tools…

+
"""
     @

   addMenuItem: (toolModel) =>
@@ -34,9 +38,13 @@
           addedAnItem = true
       if not addedAnItem and toolModel.isBasic()
         v = new Cu.View.PluginTile { model: toolModel, dataset: @model }
-        @$el.append v.render().el
+        $('.archetypes', @$el).append v.render().el
     , 0

   addToolInstance: (instance) =>
     v = new Cu.View.ToolMenuItem model: instance
-    @$el.append v.render().el
+    if instance instanceof Cu.Model.Dataset
+      # So that the tool that imported is at the top.
+      $('.tools', @$el).prepend v.render().el
+    else
+      $('.tools', @$el).append v.render().el


removed redundant file

removed redundant file

--- app/assets/javascripts/trade/controllers/visible_sandbox_shipments_controller.js.coffee
+++ app/assets/javascripts/trade/controllers/visible_sandbox_shipments_controller.js.coffee
@@ -1,2 +0,0 @@
-Trade.VisibleSandboxShipmentsController = Ember.ArrayController.extend
-  content: null


Remove dangling log and cleanup whitespaces

--- client/landing/app/Applications/Activity.kdapplication/widgets/codesnippetwidget.coffee
+++ client/landing/app/Applications/Activity.kdapplication/widgets/codesnippetwidget.coffee
@@ -1,9 +1,9 @@
 class ActivityCodeSnippetWidget extends KDFormView
-
+
   constructor:->

     super
-
+
     @labelTitle = new KDLabelView
       title         : "Title:"
       cssClass      : "first-label"
@@ -12,13 +12,13 @@
       name          : "title"
       placeholder   : "Give a title to your code snippet..."
       validate      :
-        rules       :
+        rules       :
           required  : yes
         messages    :
           required  : "Code snippet title is required!"

     @labelDescription = new KDLabelView
-      title : "Description:"
+      title : "Description:"

     @description = new KDInputView
       label       : @labelDescription
@@ -36,17 +36,17 @@
     @cancelBtn = new KDButtonView
       title    : "Cancel"
       style    : "modal-cancel"
-      callback : =>
+      callback : =>
         @reset()
         @parent.getDelegate().emit "ResetWidgets"
-
+
     @submitBtn = new KDButtonView
       style : "clean-gray"
       title : "Share your Code Snippet"
       type  : 'submit'
-
+
     @heartBox = new HelpBox
-      subtitle    : "About Code Sharing"
+      subtitle    : "About Code Sharing"
       tooltip     :
         title     : "Easily share your code with other members of the Koding community. Once you share, user can easily open or save your code to their own environment."

@@ -103,7 +103,6 @@
     subViews = @tagController.itemWrapper.getSubViews().slice()
     for item in subViews
       if item.getData().title is oldSyntax
-        log "Removing ...", oldSyntax
         @tagController.removeFromSubmitQueue(item)
         break

@@ -151,13 +150,13 @@
   widgetShown:->

     unless @ace then @loadAce() else @refreshEditorView()
-
+
   snippetCount = 0

   loadAce:->

     @loader.show()
-
+
     @aceWrapper.addSubView @ace = new Ace {}, FSHelper.createFileFromPath "localfile:/codesnippet#{snippetCount++}.txt"

     @ace.on "ace.ready", =>


api/cache: removed unused var

api/cache: removed unused var

--- lib/api/cache.coffee
+++ lib/api/cache.coffee
@@ -5,8 +5,6 @@
 restify = require 'restify'

 lruCache = require '../lru'
-
-host = 'whatthelru.com'

 module.exports =
   create: (req, res, next) ->


Fix typo

Fix typo

--- scripts/favorite-images.coffee
+++ scripts/favorite-images.coffee
@@ -3,5 +3,5 @@
 # dancing cat
 #
 module.export = (robot) ->
-  robot.responsd /show me dancing cat/i, (msg) ->
+  robot.respond /show me dancing cat/i, (msg) ->
     msg.send "https://d1ij7zv8zivhs3.cloudfront.net/assets/2719117/original/animated-dancing%20cat.gif"


Fix bug with match-range of function's name

If a function's name ended in non-alphabetic symbols, they'd trigger the
start of a right-hand argument sequence. E.g., "makecard0" was tokenised
as "makecar" and "d0". This created uneven/buggy-looking highlighting in
themes which style function names differently.

--- grammars/apl.cson
+++ grammars/apl.cson
@@ -400,9 +400,8 @@

 						(?# 17: entity.function.arguments.right.apl)
 						(
-							[A-Za-z_∆⍙][\\w∆⍙¯]*
+							(?<=\\s|\\])[A-Za-z_∆⍙][\\w∆⍙¯]*
 							|
-							\\s*
 							(\\()   (?# 18: punctuation.definition.arguments.begin.apl)
 							(?:\\s*[A-Za-z_∆⍙][\\w∆⍙¯]*\\s*)*
 							(\\))   (?# 19: punctuation.definition.arguments.end.apl)


remove static @property from PropertyTween, fix #11

--- src/scripts/editor/PropertyTween.coffee
+++ src/scripts/editor/PropertyTween.coffee
@@ -7,10 +7,9 @@
   tpl_property = require 'text!templates/propertyTween.tpl.html'

   class PropertyTween
-    # @property: Static property definition (ex: {name: 'x', label: 'x', val: 0})
     # @instance_property: The current property on the data object.
     # @object: The parent object.
-    constructor: (@property, @instance_property, @object, @timer, @key_val = false) ->
+    constructor: (@instance_property, @object, @timer, @key_val = false) ->
       @render()

     render: () =>


DEMO: add longer tab text

DEMO: add longer tab text


fix a long-standing typo in the Twitter scraper's entity parsing code

--- scripts/preview/twitter.coffee
+++ scripts/preview/twitter.coffee
@@ -54,7 +54,7 @@
           lastIndex = end

       if index > lastIndex
-        html += tweet.text.substring(lastIndex, i).escapeEntities()
+        html += tweet.text.substring(lastIndex, index).escapeEntities()

       # span? more hax.
       $('<span>').html(html)


clean on first run; workaround issues with gulp.watch that affected livereload (see https://github.com/shama/gaze/issues/66)

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -13,7 +13,7 @@
 components_path = "#{root}/bower_components"
 modules_path = "#{root}/node_modules"
 semantic_path = "#{components_path}/semantic/build/packaged"
-dist_path = "#{root}/dist"
+dist_path = "dist"

 err = (x...) -> gutil.log(x...); gutil.beep(x...)

@@ -73,9 +73,8 @@
   .pipe(autoprefixer("last 2 versions", "ie 8", "ie 9"))
   .pipe(gulp.dest(dist_path))

-gulp.task 'clean', (callback) ->
+gulp.task 'clean', ->
   rimraf.sync(dist_path)
-  callback()

 gulp.task 'copy', ->
   gulp.src("#{src_path}/*.html").pipe(gulp.dest(dist_path))
@@ -93,13 +92,10 @@
     env:
       PORT: process.env.PORT or 3000

-gulp.task 'dev', ['copy', 'css', 'watch', 'server', 'js-dev']
+gulp.task 'default', ['clean', 'copy', 'css', 'server', 'js-dev', 'watch']

-gulp.task 'default', ['dev']
-
-gulp.task 'watch', ->
+gulp.task 'watch', ['copy'], ->
   livereload.listen()
-  gulp.watch("#{dist_path}/**").on('change', livereload.changed)
-  # gulp.watch ["#{src_path}/**/*.coffee", "#{src_path}/**/*.cjsx", "#{src_path}/**/*.js"], ['js-dev']
+  gulp.watch(["#{dist_path}/**/*"]).on('change', livereload.changed)
   gulp.watch ["#{src_path}/**/*.less"], ['css']
   gulp.watch ["#{src_path}/**/*.html"], ['copy']


Fix gulp file

Fix gulp file

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -18,7 +18,7 @@
     './public/src/app/*/*.coffee'
   ])
   .pipe($.coffee(bare: true))
-  .pipe(gulp.dest(out + '/js/'))
+  .pipe(gulp.dest('./public/dist/'))

 gulp.task 'default', ['stylesheets', 'coffee']


Update electron version

Update electron version

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -1,5 +1,5 @@
 POI_VERSION = '1.0.0'
-ELECTRON_VERSION = '0.26.1'
+ELECTRON_VERSION = '0.27.2'
 SYSTEM_BIT =
   win32: 'ia32'
   linux: 'x64'


Remove prefix from git tag

Remove prefix from git tag

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -74,7 +74,7 @@
     # read only one file to get the version number
     .pipe($.filter('package.json'))
     # **tag it in the repository**
-    .pipe($.tagVersion())
+    .pipe($.tagVersion({prefix: ''}))
     # push the tags to master
     .pipe($.git.push('origin', 'master', { args: '--tags' }))


fix(bower): use latest version when conflicted

Once a bower install has been performed
updating a version and performing a subsequent install
results in a conflict.

Apply the forceLatest bower option to always perfer the latest
and not result in an error.

However, downgrading a component will still show an error
as there is no current option to force the ONE you want.

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -255,12 +255,15 @@
 	options =
 		directory: BOWER_DIRECTORY

+	bowerOptions =
+		forceLatest: true
+
 	components = []

 	components.push "#{component}##{version}" for version, files of value for component, value of BOWER_COMPONENTS

 	bower
-		.commands.install components, {}, options
+		.commands.install components, bowerOptions, options
 		.on 'error', onError

 # build the app


gulpfile.coffeeを整形

gulpfile.coffeeを整形

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -1,10 +1,10 @@
-gulp     = require 'gulp'
+gulp       = require 'gulp'

 browserify = require 'browserify'
 reactify   = require 'coffee-reactify'
-source   = require 'vinyl-source-stream'
-buffer   = require 'vinyl-buffer'
-uglify   = require 'gulp-uglify'
+source     = require 'vinyl-source-stream'
+buffer     = require 'vinyl-buffer'
+uglify     = require 'gulp-uglify'

 gulp.task 'js', ->
   browserify './public/javascripts/src/main.cjsx'


little header issue

little header issue

--- src/smc-webapp/tasks/desc-rendered.cjsx
+++ src/smc-webapp/tasks/desc-rendered.cjsx
@@ -34,7 +34,7 @@

 header_part = (s) ->
-    lines = s.split('\n')
+    lines = s.trim().split('\n')
     for i in [0...lines.length]
         if lines[i].trim() == ''
             if i == lines.length - 1


Handle case where RequireJS passes error object w/o a modules property

--- tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/console.coffee
+++ tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/console.coffee
@@ -169,8 +169,9 @@
         message += """: #{err.message}"""

       if err.requireType
-        message += """, modules #{err.requireModules.join(", ")}"""
-
+        modules = err.requireModules
+        if modules and modules.length > 0
+          message += """, modules #{modules.join(", ")}"""

       exports.error message


Collaboration: Register user session id to participants list.

--- client/ide/AppController.coffee
+++ client/ide/AppController.coffee
@@ -755,9 +755,19 @@

       log 'acetz: participants:', @participants.asArray()

+      @registerSessionId()
       @listenChangeEvents()

-
+  registerSessionId: ->
+    collaborators = @rtm.getCollaborators @realTimeDoc
+
+    for collaborator in collaborators when collaborator.isMe
+      participants = @participants.asArray()
+
+      for user, index in participants when user.nickname is KD.nick()
+        user.sessionId = collaborator.sessionId
+        @participants.remove index
+        @participants.insert index, user

   addParticipant: (initalizeList = no) ->


IDE: AppController: just create only one machineStateModal

--- client/ide/AppController.coffee
+++ client/ide/AppController.coffee
@@ -267,13 +267,15 @@
             @mountMachine machineItem
           else

-            @createMachineStateModal {
-              state, container, machineItem, initial: yes
-            }
-
-            if state is NotInitialized
-              @machineStateModal.once 'MachineTurnOnStarted', =>
-                KD.getSingleton('mainView').activitySidebar.initiateFakeCounter()
+            unless @machineStateModal
+
+              @createMachineStateModal {
+                state, container, machineItem, initial: yes
+              }
+
+              if state is NotInitialized
+                @machineStateModal.once 'MachineTurnOnStarted', =>
+                  KD.getSingleton('mainView').activitySidebar.initiateFakeCounter()

           computeController.on "public-#{machineId}", (event) =>


Fix for possible null reference exception

--- client/ide/AppController.coffee
+++ client/ide/AppController.coffee
@@ -686,4 +686,4 @@
     for ideView in @ideViews
       pane = ideView.tabView.getActivePane()
       if pane and pane.view instanceof IDE.TerminalPane
-        pane.view.webtermView.terminal.updateSize()+        pane.view.webtermView.terminal?.updateSize()


refactored schema and added rawMessage

refactored schema and added rawMessage

--- src/model/auditRecords.coffee
+++ src/model/auditRecords.coffee
@@ -6,15 +6,16 @@
   "codeSystemName": { type: String, required: false }
   "displayName": { type: String, required: false }

-eventTypeCodeSchema = new Schema
+codeType = new Schema
   "code": { type: String, required: false }
   "displayName": { type: String, required: false }
   "codeSystemName": { type: String, required: false }

-RoleIDCodeSchema = new Schema
-  "code": { type: String, required: false }
-  "displayName": { type: String, required: false }
-  "codeSystemName": { type: String, required: false }
+eventTypeCodeSchema = codeType
+
+RoleIDCodeSchema = codeType
+
+ParticipantObjectIDTypeCodeSchema = codeType

 activeParticipantSchema = new Schema
   "UserID": { type: String, required: false }
@@ -35,16 +36,12 @@
   "ParticipantObjectQuery": { type: String, required: false }
   "ParticipantObjectDetail": ParticipantObjectDetailSchema

-ParticipantObjectIDTypeCodeSchema = new Schema
-  "code": { type: String, required: false }
-  "displayName": { type: String, required: false }
-  "codeSystemName": { type: String, required: false }
-
 ParticipantObjectDetailSchema = new Schema
   "type": { type: String, required: false }
   "value": { type: String, required: false }

 auditRecordSchema = new Schema
+  "rawMessage":  { type: String, required: false }
   "EventIdentification":
     "EventDateTime": { type: Date, required: true, default: Date.now }
     "EventOutcomeIndicator": { type: String, required: false }


Make package work with both versions of the display buffer

--- lib/file-view.coffee
+++ lib/file-view.coffee
@@ -3,6 +3,8 @@
 SymbolsView = require './symbols-view'
 TagGenerator = require './tag-generator'
 {match} = require 'fuzzaldrin'
+
+# TODO: remove references to logical display buffer when it is released.

 module.exports =
 class FileView extends SymbolsView
@@ -55,12 +57,23 @@
       @attach()

   serializeEditorState: (editor) ->
+    editorElement = atom.views.getView(editor)
+    if editorElement.logicalDisplayBuffer
+      scrollTop = editorElement.getScrollTop()
+    else
+      scrollTop = editor.getScrollTop()
+
     bufferRanges: editor.getSelectedBufferRanges()
-    scrollTop: atom.views.getView(editor).getScrollTop()
+    scrollTop: scrollTop

   deserializeEditorState: (editor, {bufferRanges, scrollTop}) ->
+    editorElement = atom.views.getView(editor)
+
     editor.setSelectedBufferRanges(bufferRanges)
-    atom.views.getView(editor).setScrollTop(scrollTop)
+    if editorElement.logicalDisplayBuffer
+      editorElement.setScrollTop(scrollTop)
+    else
+      editor.setScrollTop(scrollTop)

   getEditor: -> atom.workspace.getActiveTextEditor()


List: on-click event if list isn't in selection mode.

--- lib/list/list.coffee
+++ lib/list/list.coffee
@@ -15,6 +15,8 @@
 List = Component.extend WithConfigMixin,
     tagName: 'ul'

+    attributeBindings: ['style']
+
     classNameBindings: ['styleClasses']
     styleClasses: (->
         @get('config.list.listClasses')?.join(" ")
@@ -27,6 +29,11 @@
     # @type Item
     ###
     selected: undefined
+
+    ###*
+    # True if this list supports selection
+    ###
+    selection: true

     ###*
     # List can be bound to models, models can be a property or an object that the list is bound to.
@@ -85,20 +92,24 @@
     # @see selected-idx
     ###
     select: (item) ->
-        #TODO: Why we initially having an undefined item?
-        return if not item or @get('selected') is item
-        Em.debug "Selecting tab: #{item.get('index')}"
-        #the if condition is because the user may pass another object initially to bind the selected property
-        @get('selected').sendAction 'on-deselect', @get('selected') if @get('selected')?.sendAction
-        @set 'selected', item
-        @get('selected').sendAction 'on-select', @get('selected')
-        @set 'selected-idx', item.get 'index'
-
-        @get('items').forEach((i) =>
-            return if @get('selected') is i
-            i.sendAction 'on-selection-change', i, @get('selected')
-        )
-
+        #Handle no selection state
+        if not @get('selection')
+            item.sendAction 'on-click', item
+        else
+            #TODO: Why we initially having an undefined item?
+            return if not item or @get('selected') is item
+            Em.debug "Selecting tab: #{item.get('index')}"
+            #the if condition is because the user may pass another object initially to bind the selected property
+            @get('selected').sendAction 'on-deselect', @get('selected') if @get('selected')?.sendAction
+            @set 'selected', item
+            @get('selected').sendAction 'on-select', @get('selected')
+            @set 'selected-idx', item.get 'index'
+
+            @get('items').forEach((i) =>
+                return if @get('selected') is i
+                i.sendAction 'on-selection-change', i, @get('selected')
+            )
+
     notifyModelsChange: (->
         run.next(@, ->
             #Why this causes the models observer to stop notifying for changes?


for searching on home page, consider only whether beginning matches

--- static/js/filters/propsFilter.js.coffee
+++ static/js/filters/propsFilter.js.coffee
@@ -9,7 +9,8 @@
         while i < keys.length
           prop = keys[i]
           text = props[prop].toLowerCase()
-          if item[prop].toString().toLowerCase().indexOf(text) != -1
+          # Check if the property starts with the query
+          if item[prop].toString().toLowerCase().indexOf(text) == 0
             itemMatches = true
             break
           i++
@@ -19,6 +20,7 @@
     else
       # Let the output be the input untouched
       out = items
+
     out

 # Used for our Angular select library


fixed perms-request for the first embed and on edit

--- client/landing/app/Applications/Activity.kdapplication/widgets/statuswidget.coffee
+++ client/landing/app/Applications/Activity.kdapplication/widgets/statuswidget.coffee
@@ -18,7 +18,7 @@

     # saves the URL of the previous request to avoid
     # multiple embed calls for one URL
-    @previousURL = []
+    @previousURL = ""

     # prevents multiple calls to embed.ly functionality
     # at the same time
@@ -174,7 +174,7 @@

           @embedBox.embedLinks.setLinks firstUrl

-          unless @previousURL is firstUrl
+          unless (@previousURL in firstUrl)
             @embedBox.embedUrl firstUrl?[0], {
               maxWidth: 525
             }, (embedData)=>
@@ -183,7 +183,9 @@
               @embedLinks?.linkList?.items?[0]?.setFavicon embedData.favicon_url

               @requestEmbedLock = off
-              @previousURL = firstUrl
+              @previousURL = firstUrl?[0]
+          else
+            @requestEmbedLock = off
         else
           @requestEmbedLock = off
       ,50


:art: and :racehorse:

:art: and :racehorse:

--- lib/vim-state.coffee
+++ lib/vim-state.coffee
@@ -39,7 +39,7 @@
         @activateVisualMode('characterwise') if @mode is 'normal'
     , 100)

-    @subscriptions.add @editor.onDidChangeCursorPosition @ensureCursorIsWithinLine
+    @subscriptions.add @editor.onDidChangeCursorPosition ({cursor}) => @ensureCursorIsWithinLine(cursor)
     @subscriptions.add @editor.onDidAddCursor @ensureCursorIsWithinLine

     @editorElement.classList.add("vim-mode")
@@ -663,13 +663,13 @@
     @editor.insertText(text) if text?

   ensureCursorIsWithinLine: (cursor) =>
-    return if @processing or @mode is 'visual' or @mode is 'insert'
-
-    if cursor.cursor? then cursor = cursor.cursor # when called by @editor.onDidChangeCursorPosition
+    return if @processing or @mode isnt 'normal'

     {goalColumn} = cursor
     if cursor.isAtEndOfLine() and not cursor.isAtBeginningOfLine()
+      @processing = true # to ignore the cursor change (and recursion) caused by the next line
       cursor.moveLeft()
+      @processing = false
     cursor.goalColumn = goalColumn

 # This uses private APIs and may break if TextBuffer is refactored.


fix #490 event.target no longer hold editorElement

because shadow boundary was removed

--- lib/vim-state.coffee
+++ lib/vim-state.coffee
@@ -213,37 +213,35 @@
     } = {}
     @emitter.emit 'did-destroy'

+  isInterestingEvent: ({target, type}) ->
+    if @mode is 'insert'
+      false
+    else
+      @editor? and
+        target?.closest?('atom-text-editor') is @editorElement and
+        not @isMode('visual', 'blockwise') and
+        not type.startsWith('vim-mode-plus:')
+
+  checkSelection: (event) ->
+    return if @operationStack.isProcessing()
+    return unless @isInterestingEvent(event)
+
+    if haveSomeNonEmptySelection(@editor)
+      submode = swrap.detectVisualModeSubmode(@editor)
+      if @isMode('visual', submode)
+        @updateCursorsVisibility()
+      else
+        @activate('visual', submode)
+    else
+      @activate('normal') if @isMode('visual')
+
+  saveProperties: (event) ->
+    return unless @isInterestingEvent(event)
+    for selection in @editor.getSelections()
+      swrap(selection).saveProperties()
+
   observeSelection: ->
-    isInterestingEvent = ({target, type}) =>
-      if @mode is 'insert'
-        false
-      else
-        @editor? and
-          target is @editorElement and
-          not @isMode('visual', 'blockwise') and
-          not type.startsWith('vim-mode-plus:')
-
-    onInterestingEvent = (fn) ->
-      (event) -> fn() if isInterestingEvent(event)
-
-    _checkSelection = =>
-      return if @operationStack.isProcessing()
-      if haveSomeNonEmptySelection(@editor)
-        submode = swrap.detectVisualModeSubmode(@editor)
-        if @isMode('visual', submode)
-          @updateCursorsVisibility()
-        else
-          @activate('visual', submode)
-      else
-        @activate('normal') if @isMode('visual')
-
-    _saveProperties = =>
-      for selection in @editor.getSelections()
-        swrap(selection).saveProperties()
-
-    checkSelection = onInterestingEvent(_checkSelection)
-    saveProperties = onInterestingEvent(_saveProperties)
-
+    checkSelection = @checkSelection.bind(this)
     @editorElement.addEventListener('mouseup', checkSelection)
     @subscriptions.add new Disposable =>
       @editorElement.removeEventListener('mouseup', checkSelection)


Remove the fadeIn() and fadeOut() EventWrapper methods

--- tapestry-core/src/main/preprocessed-coffeescript/org/apache/tapestry5/t5-core-dom.coffee
+++ tapestry-core/src/main/preprocessed-coffeescript/org/apache/tapestry5/t5-core-dom.coffee
@@ -61,36 +61,6 @@
     element.dispatchEvent event
     return not event.defaultPrevented

-  # Currently don't want to rely on Scriptaculous, since our needs are pretty minor.
-  animate = (element, styleName, initialValue, finalValue, duration, callbacks) ->
-    styles = {}
-    range = finalValue - initialValue
-    initialTime = Date.now()
-    first = true
-    animator = ->
-      elapsed = Date.now() - initialTime
-      if elapsed >= duration
-        styles[styleName] = finalValue
-        element.setStyle styles
-        window.clearInterval timeoutID
-        callbacks.oncomplete and callbacks.oncomplete()
-
-      # TODO: Add an easein/easeout function
-
-      newValue = initialValue + range * (elapsed / duration)
-      styles[styleName] = newValue
-
-      element.setStyle styles
-
-      if first
-        callbacks.onstart and callbacks.onstart()
-        first = false
-
-    timeoutID = window.setInterval animator
-
-    styles[styleName] = initialValue
-    element.setStyle styles
-
   # converts a selector to an array of DOM elements
   parseSelectorToElements = (selector) ->
     if _.isString selector
@@ -447,38 +417,6 @@
 #endif

       triggerReflow()
-
-      return this
-
-    # Runs an animation to fade-in the element over the specified duration.
-    #
-    # * duration - animation duration time, in seconds
-    # * callback - function invoked after the animation is complete
-    fadeIn: (duration, callback) ->
-#if jquery
-      @$.fadeIn duration * 1000, ->
-        callback and callback()
-#elseif prototype
-      animate @element, "opacity", 0, 1, duration * 1000,
-        onstart: => @element.show()
-        oncomplete: callback
-#endif
-
-      return this
-
-    # Runs an animation to fade out an element over the specified duration. The element should already
-    # be visible and fully opaque.
-    #
-    # * duration - animation duration time, in seconds
-    # * callback - function invoked after the animation is complete
-    fadeOut: (duration, callback) ->
-#if jquery
-      @$.fadeOut duration * 1000, ->
-        callback and callback()
-#elseif prototype
-      animate @element, "opacity", 1, 0, duration * 1000,
-        oncomplete: callback
-#endif

       return this


Added a showAll method to allow clearing a radio button to re-show everything when the visibility actions is set to hide.

--- vendor/assets/javascripts/visibility_map.coffee
+++ vendor/assets/javascripts/visibility_map.coffee
@@ -1,5 +1,5 @@
 (($) ->
-  'use strict'
+  'use strict'
   class GenericVisibilityChecker
     constructor: (element) ->
       @element = element
@@ -7,17 +7,17 @@
       @action  = @element.data('visibility-map-action') ? 'show'
       @map     = @element.data('visibility-map')
       @allFields = $($.unique $.map @map, (val) => $(val, @scope).get())
-
+
     check: ->
       fieldsForValue = $ $.unique $.map @getValue(), (value) => $(@map[value], @scope).get()
-
+
       if @action is 'show'
         toShow = fieldsForValue
         toHide = @allFields.not(fieldsForValue)
       else
         toHide = fieldsForValue
         toShow = @allFields.not(fieldsForValue)
-
+
       toShow.show()
       toShow.trigger('visibility.show')

@@ -27,33 +27,37 @@
     hideAll: ->
       @allFields.hide()
       @allFields.trigger('visibility.hide')
-
+
+    showAll: ->
+      @allFields.show()
+      @allFields.trigger('visibility.show')
+
     getValue: ->
       $.makeArray(@element.val())
-
+
   class CheckboxVisibilityChecker extends GenericVisibilityChecker
     getValue: ->
       $.map $("input[type='checkbox'][name='#{@element.attr('name')}']:checked"), (inputElement) ->
         $(inputElement).val()
-
+
   $.fn.setVisibility = (action) ->
     @each ->
       data = $(this).data('visibility-checker')
       unless data?
-        checkerClass = if $(this).is("input[type='checkbox']") then CheckboxVisibilityChecker else GenericVisibilityChecker
+        checkerClass = if $(this).is("input[type='checkbox']") then CheckboxVisibilityChecker else GenericVisibilityChecker
         $(this).data('visibility-checker', data = new checkerClass $(this))
       action ?= 'check'
       data[action]()
-
+
   $ ->
     $('input[data-visibility-map]:checked, select[data-visibility-map]').setVisibility()
-
+
     $(document.body).on('visibility.show', (e) ->
       $(':input:not([data-visibility-map-no-auto-enable])', $(e.target)).prop('disabled', false)
     ).on('visibility.hide', (e) ->
       $(':input', $(e.target)).prop('disabled', true)
     )
-
+
     $(document.body).on 'change', '[data-visibility-map]', (e) ->
       $(this).setVisibility()
-) jQuery+) jQuery


payment: better error msg when they can't downgrade, do actual check

--- workers/social/lib/social/models/payment.coffee
+++ workers/social/lib/social/models/payment.coffee
@@ -102,9 +102,9 @@
         return callback err  if err

         for name in ["alwaysOn", "storage", "total"]
-          if usage[name] > 0
+          if usage[name] > plan[name]
             return callback {
-              "message"   : "Can't change plan due to excessive resource usage",
+              "message"   : "Sorry, your request to downgrade can't be processed becasue you're currently using more resources than the plan you're downgrading to allows."
               "allowed"   : plan[name]
               "usage"     : usage[name]
               "planTitle" : planTitle


The update of insertOrUpdate doesn't do anything with roles, no need to check there for last admin

--- packages/rocketchat-lib/server/methods/insertOrUpdateUser.coffee
+++ packages/rocketchat-lib/server/methods/insertOrUpdateUser.coffee
@@ -97,11 +97,6 @@

 			return _id
 		else
-			# prevent removing admin role of last admin
-			adminCount = Meteor.users.find({ roles: { $in: ['admin'] } }).count()
-			if adminCount is 1 and userData.role isnt 'admin'
-				throw new Meteor.Error 'error-action-not-allowed', 'Leaving the app without admins is not allowed', { method: 'insertOrUpdateUser', action: 'Remove_last_admin' }
-
 			#update user
 			updateUser = $set: {}


By default assume the images are presized; else wait for them to load

--- components/carousel/view.coffee
+++ components/carousel/view.coffee
@@ -29,6 +29,7 @@
   prefixWidth: 0
   stopPositions: []
   transitionDuration: '0.5s'
+  hasDimensions: true

   initialize: (options) ->
     @$window = $(window)
@@ -51,11 +52,18 @@
     @setActive 0

     # When images are loaded, do the math
-    @$el.imagesLoaded?()?.always =>
-      @setStops()
-      @$el.addClass 'is-done'
+    if @hasDimensions
+      @complete()
+    else
+      @$el.imagesLoaded?()?.always => @complete()

     @bindEvents()
+
+  complete: ->
+    @setStops()
+    @$el
+      .removeClass('is-loading')
+      .addClass 'is-done'

   render: ->
     if @length < @minLength
@@ -66,7 +74,7 @@
       .addClass('is-loading')
       .html template(carouselFigures: @collection.models, height: @height)

-    @postRender()
+    _.defer => @postRender()

     this

@@ -115,7 +123,6 @@
     @firstDecoyPosition = Math.floor((windowWidth - _.first(widths)) / 2) - (totalLefts)

     @shiftCarousel @stopPositions[@active], false
-    @$el.removeClass 'is-loading'

   bindEvents: ->
     # Bind key events to cycle the carousel


Bump the report downloads poll interval to 20 seconds

TNL-565

--- lms/static/coffee/src/instructor_dashboard/data_download.coffee
+++ lms/static/coffee/src/instructor_dashboard/data_download.coffee
@@ -153,7 +153,7 @@

     @$report_downloads_table = @$section.find ".report-downloads-table"

-    POLL_INTERVAL = 1000 * 60 * 5 # 5 minutes in ms
+    POLL_INTERVAL = 20000 # 20 seconds, just like the "pending instructor tasks" table
     @downloads_poller = new window.InstructorDashboard.util.IntervalManager(
       POLL_INTERVAL, => @reload_report_downloads()
     )


SidebarMachineBox: Collapse workspace list by default.

--- client/Social/Activity/sidebar/sidebarmachinebox.coffee
+++ client/Social/Activity/sidebar/sidebarmachinebox.coffee
@@ -28,6 +28,8 @@

     @listView = @listController.getView()
     @addSubView @listView
+
+    @collapseList()

   addWorkspace: (wsData) -> @listController.addItem wsData


Change default port to 3034

Change default port to 3034

--- config/settings.defaults.coffee
+++ config/settings.defaults.coffee
@@ -4,7 +4,7 @@
 module.exports =
 	internal:
 		packageindexer:
-			port: 3022
+			port: 3034
 			host: "localhost"

 	mongo:


add config variable for keep alive frequency

--- src/scripts/keep-alive.coffee
+++ src/scripts/keep-alive.coffee
@@ -6,7 +6,7 @@
 #   None
 #
 # Configuration:
-#   None
+#   HUBOT_KEEP_ALIVE_FREQUENCY
 #
 # Commands:
 #   hubot keep `http://ninjas-20.herokuapp.com` alive - Add inputted url to the collection of urls set to be pinged
@@ -19,7 +19,7 @@
 HTTP = require "http"
 URL  = require "url"

-frequency = 60000
+frequency = process.env.HUBOT_KEEP_ALIVE_FREQUENCY || 60000

 ping = (url) ->
   parsedUrl = URL.parse(url)


Fix constructor.

Fix constructor.

--- src/scripts/responders.coffee
+++ src/scripts/responders.coffee
@@ -24,10 +24,11 @@

 class Responders
   constructor: (@robot) ->
-    @robot.brain.data.responders ||= {}
-
-    for pattern, responder of @robot.brain.data.responders
-      @add(pattern, responder.callback)
+    @robot.brain.data.responders = {}
+    @robot.brain.on 'loaded', (data) =>
+      for pattern, responder of data.responders
+        delete responder.index
+        @add(pattern, responder.callback)

   responders: ->
     @robot.brain.data.responders
@@ -38,7 +39,8 @@
   remove: (pattern) ->
     responder = @responder(pattern)
     if responder
-      @robot.listeners.splice(responder.index, 1, (->))
+      if responder.index
+        @robot.listeners.splice(responder.index, 1, (->))
       delete @responders()[pattern]
     responder


Ensure pseudo-placeholder clicks fall through to editor element

Fixes #859

--- src/trix/elements/trix_editor_element.coffee
+++ src/trix/elements/trix_editor_element.coffee
@@ -70,6 +70,7 @@
       content: attr(placeholder);
       color: graytext;
       cursor: text;
+      pointer-events: none;
     }

     %t a[contenteditable=false] {


:white_check_mark: Use querySelector instead of jQuery

--- spec/bookmarks-view-spec.coffee
+++ spec/bookmarks-view-spec.coffee
@@ -211,7 +211,8 @@
         expect(atom.workspace.getActivePane()).not.toEqual pane1

         atom.commands.dispatch workspaceElement, 'bookmarks:view-all'
-        $(workspaceElement).find('.bookmarks-view .bookmark').mousedown().mouseup()
+        bookmarkElement = workspaceElement.querySelector('.bookmarks-view .bookmark')
+        atom.commands.dispatch bookmarkElement, 'core:confirm'

         waitsFor ->
           atom.workspace.getActivePane() is pane1


Verify that decorations are what we expect

--- spec/stack-ide-atom-spec.coffee
+++ spec/stack-ide-atom-spec.coffee
@@ -1,3 +1,4 @@
+Atom = require('atom')
 AtomStackIde = require '../lib/stack-ide-atom'
 _ = require 'lodash'

@@ -35,9 +36,16 @@
         textEditorElement = atom.views.getView(textEditor)
         atom.commands.dispatch(textEditorElement, 'stack-ide-atom:source-errors')

+      decorations = null
+
       waitsFor ->
         decorations = _.filter textEditor.getDecorations(), (decoration) ->
           decoration.properties.type == 'highlight' and
           decoration.properties.class == 'sia-error'

         return decorations.length > 0
+
+      runs ->
+        expect(decorations.length).toBe 1
+        correctRange = decorations[0].getMarker().getBufferRange().isEqual(new Atom.Range([3,7], [3,8]))
+        expect(correctRange).toBe true


Remove call through on console.log spies

--- spec/apm-cli-spec.coffee
+++ spec/apm-cli-spec.coffee
@@ -4,7 +4,7 @@
 describe 'apm command line interface', ->
   describe 'when no arguments are present', ->
     it 'prints a usage message', ->
-      spyOn(console, 'log').andCallThrough()
+      spyOn(console, 'log')
       spyOn(console, 'error')
       apm.run([])
       expect(console.log).not.toHaveBeenCalled()
@@ -13,7 +13,7 @@

   describe 'when the version flag is specified', ->
     it 'prints a usage message', ->
-      spyOn(console, 'log').andCallThrough()
+      spyOn(console, 'log')
       spyOn(console, 'error')
       apm.run(['-h'])
       expect(console.log).not.toHaveBeenCalled()
@@ -31,7 +31,7 @@

   describe 'when an unrecognized command is specified', ->
     it 'prints an error message', ->
-      spyOn(console, 'log').andCallThrough()
+      spyOn(console, 'log')
       spyOn(console, 'error')
       apm.run(['this-will-never-be-a-command'])
       expect(console.log).not.toHaveBeenCalled()


:green_heart: Fixed wrong scopes

:green_heart: Fixed wrong scopes

--- spec/grammar-spec.coffee
+++ spec/grammar-spec.coffee
@@ -14,26 +14,26 @@

   it "tokenizes regexp replace", ->
     {tokens} = grammar.tokenizeLine('s/text/test/')
-    expect(tokens[0]).toEqual value: "s", scopes: ["source.perl", "string.regexp.replaceXXX.simple_delimiter.perl", "punctuation.definition.string.perl", "support.function.perl"], bufferDelta: 1, hasPairedCharacter: false, isAtomic: undefined, isHardTab: undefined, isSoftWrapIndentation: undefined, screenDelta: 1
-    expect(tokens[1]).toEqual value: "/", scopes: ["source.perl", "string.regexp.replaceXXX.simple_delimiter.perl", "punctuation.definition.string.perl"], bufferDelta: 1, hasPairedCharacter: false, isAtomic: undefined, isHardTab: undefined, isSoftWrapIndentation: undefined, screenDelta: 1
-    expect(tokens[2]).toEqual value: "text", scopes: ["source.perl", "string.regexp.replaceXXX.simple_delimiter.perl"], bufferDelta: 4, hasPairedCharacter: false, isAtomic: undefined, isHardTab: undefined, isSoftWrapIndentation: undefined, screenDelta: 4
-    expect(tokens[3]).toEqual value: "/", scopes: ["source.perl", "string.regexp.replaceXXX.simple_delimiter.perl", "punctuation.definition.string.perl"], bufferDelta: 1, hasPairedCharacter: false, isAtomic: undefined, isHardTab: undefined, isSoftWrapIndentation: undefined, screenDelta: 1
-    expect(tokens[4]).toEqual value: "test", scopes: ["source.perl", "string.regexp.replaceXXX.simple_delimiter.perl"], bufferDelta: 4, hasPairedCharacter: false, isAtomic: undefined, isHardTab: undefined, isSoftWrapIndentation: undefined, screenDelta: 4
-    expect(tokens[5]).toEqual value: "/", scopes: ["source.perl", "string.regexp.replaceXXX.simple_delimiter.perl", "punctuation.definition.string.perl"], bufferDelta: 1, hasPairedCharacter: false, isAtomic: undefined, isHardTab: undefined, isSoftWrapIndentation: undefined, screenDelta: 1
+    expect(tokens[0]).toEqual value: "s", scopes: ["source.perl", "string.regexp.replaceXXX.simple_delimiter.perl", "punctuation.definition.string.perl", "support.function.perl"]
+    expect(tokens[1]).toEqual value: "/", scopes: ["source.perl", "string.regexp.replaceXXX.simple_delimiter.perl", "punctuation.definition.string.perl"]
+    expect(tokens[2]).toEqual value: "text", scopes: ["source.perl", "string.regexp.replaceXXX.simple_delimiter.perl"]
+    expect(tokens[3]).toEqual value: "/", scopes: ["source.perl", "string.regexp.replaceXXX.format.simple_delimiter.perl", "punctuation.definition.string.perl"]
+    expect(tokens[4]).toEqual value: "test", scopes: ["source.perl", "string.regexp.replaceXXX.format.simple_delimiter.perl"]
+    expect(tokens[5]).toEqual value: "/", scopes: ["source.perl", "string.regexp.replaceXXX.format.simple_delimiter.perl", "punctuation.definition.string.perl"]

     {tokens} = grammar.tokenizeLine('s_text_test_')
     expect(tokens[0]).toEqual value: "s", scopes: ["source.perl", "string.regexp.replaceXXX.simple_delimiter.perl", "punctuation.definition.string.perl", "support.function.perl"], bufferDelta: 1, hasPairedCharacter: false, isAtomic: undefined, isHardTab: undefined, isSoftWrapIndentation: undefined, screenDelta: 1
     expect(tokens[1]).toEqual value: "_", scopes: ["source.perl", "string.regexp.replaceXXX.simple_delimiter.perl", "punctuation.definition.string.perl"], bufferDelta: 1, hasPairedCharacter: false, isAtomic: undefined, isHardTab: undefined, isSoftWrapIndentation: undefined, screenDelta: 1
     expect(tokens[2]).toEqual value: "text", scopes: ["source.perl", "string.regexp.replaceXXX.simple_delimiter.perl"], bufferDelta: 4, hasPairedCharacter: false, isAtomic: undefined, isHardTab: undefined, isSoftWrapIndentation: undefined, screenDelta: 4
-    expect(tokens[3]).toEqual value: "_", scopes: ["source.perl", "string.regexp.replaceXXX.simple_delimiter.perl", "punctuation.definition.string.perl"], bufferDelta: 1, hasPairedCharacter: false, isAtomic: undefined, isHardTab: undefined, isSoftWrapIndentation: undefined, screenDelta: 1
-    expect(tokens[4]).toEqual value: "test", scopes: ["source.perl", "string.regexp.replaceXXX.simple_delimiter.perl"], bufferDelta: 4, hasPairedCharacter: false, isAtomic: undefined, isHardTab: undefined, isSoftWrapIndentation: undefined, screenDelta: 4
-    expect(tokens[5]).toEqual value: "_", scopes: ["source.perl", "string.regexp.replaceXXX.simple_delimiter.perl", "punctuation.definition.string.perl"], bufferDelta: 1, hasPairedCharacter: false, isAtomic: undefined, isHardTab: undefined, isSoftWrapIndentation: undefined, screenDelta: 1
+    expect(tokens[3]).toEqual value: "_", scopes: ["source.perl", "string.regexp.replaceXXX.format.simple_delimiter.perl", "punctuation.definition.string.perl"], bufferDelta: 1, hasPairedCharacter: false, isAtomic: undefined, isHardTab: undefined, isSoftWrapIndentation: undefined, screenDelta: 1
+    expect(tokens[4]).toEqual value: "test", scopes: ["source.perl", "string.regexp.replaceXXX.format.simple_delimiter.perl"], bufferDelta: 4, hasPairedCharacter: false, isAtomic: undefined, isHardTab: undefined, isSoftWrapIndentation: undefined, screenDelta: 4
+    expect(tokens[5]).toEqual value: "_", scopes: ["source.perl", "string.regexp.replaceXXX.format.simple_delimiter.perl", "punctuation.definition.string.perl"], bufferDelta: 1, hasPairedCharacter: false, isAtomic: undefined, isHardTab: undefined, isSoftWrapIndentation: undefined, screenDelta: 1

     {tokens} = grammar.tokenizeLine('s/text/test/gxr')
     expect(tokens[0]).toEqual value: "s", scopes: ["source.perl", "string.regexp.replaceXXX.simple_delimiter.perl", "punctuation.definition.string.perl", "support.function.perl"], bufferDelta: 1, hasPairedCharacter: false, isAtomic: undefined, isHardTab: undefined, isSoftWrapIndentation: undefined, screenDelta: 1
     expect(tokens[1]).toEqual value: "/", scopes: ["source.perl", "string.regexp.replaceXXX.simple_delimiter.perl", "punctuation.definition.string.perl"], bufferDelta: 1, hasPairedCharacter: false, isAtomic: undefined, isHardTab: undefined, isSoftWrapIndentation: undefined, screenDelta: 1
     expect(tokens[2]).toEqual value: "text", scopes: ["source.perl", "string.regexp.replaceXXX.simple_delimiter.perl"], bufferDelta: 4, hasPairedCharacter: false, isAtomic: undefined, isHardTab: undefined, isSoftWrapIndentation: undefined, screenDelta: 4
-    expect(tokens[3]).toEqual value: "/", scopes: ["source.perl", "string.regexp.replaceXXX.simple_delimiter.perl", "punctuation.definition.string.perl"], bufferDelta: 1, hasPairedCharacter: false, isAtomic: undefined, isHardTab: undefined, isSoftWrapIndentation: undefined, screenDelta: 1
-    expect(tokens[4]).toEqual value: "test", scopes: ["source.perl", "string.regexp.replaceXXX.simple_delimiter.perl"], bufferDelta: 4, hasPairedCharacter: false, isAtomic: undefined, isHardTab: undefined, isSoftWrapIndentation: undefined, screenDelta: 4
-    expect(tokens[5]).toEqual value: "/", scopes: ["source.perl", "string.regexp.replaceXXX.simple_delimiter.perl", "punctuation.definition.string.perl"], bufferDelta: 1, hasPairedCharacter: false, isAtomic: undefined, isHardTab: undefined, isSoftWrapIndentation: undefined, screenDelta: 1
+    expect(tokens[3]).toEqual value: "/", scopes: ["source.perl", "string.regexp.replaceXXX.format.simple_delimiter.perl", "punctuation.definition.string.perl"], bufferDelta: 1, hasPairedCharacter: false, isAtomic: undefined, isHardTab: undefined, isSoftWrapIndentation: undefined, screenDelta: 1
+    expect(tokens[4]).toEqual value: "test", scopes: ["source.perl", "string.regexp.replaceXXX.format.simple_delimiter.perl"], bufferDelta: 4, hasPairedCharacter: false, isAtomic: undefined, isHardTab: undefined, isSoftWrapIndentation: undefined, screenDelta: 4
+    expect(tokens[5]).toEqual value: "/", scopes: ["source.perl", "string.regexp.replaceXXX.format.simple_delimiter.perl", "punctuation.definition.string.perl"], bufferDelta: 1, hasPairedCharacter: false, isAtomic: undefined, isHardTab: undefined, isSoftWrapIndentation: undefined, screenDelta: 1
     expect(tokens[6]).toEqual value: "gxr", scopes: ["source.perl", "string.regexp.replace.perl", "punctuation.definition.string.perl", "keyword.control.regexp-option.perl"], bufferDelta: 3, hasPairedCharacter: false, isAtomic: undefined, isHardTab: undefined, isSoftWrapIndentation: undefined, screenDelta: 3


Remove disabled tests (for the time being)

--- spec/latexmk-spec.coffee
+++ spec/latexmk-spec.coffee
@@ -6,10 +6,6 @@
     return null

 describe "latexmk", ->
-  describe "run", ->
-    xit "...", ->
-      return
-
   describe "constructArgs", ->
     beforeEach ->
       @filePath = "foo.tex"


Tests for W and B motions

Tests for W and B motions

--- spec/motions-spec.coffee
+++ spec/motions-spec.coffee
@@ -119,6 +119,40 @@
         it "selects the whitespace", ->
           expect(vimState.getRegister('"').text).toBe ' '

+  describe "the W keybinding", ->
+    beforeEach -> editor.setText("cde1+- ab \n xyz\n\nzip")
+
+    describe "as a motion", ->
+      beforeEach -> editor.setCursorScreenPosition([0, 0])
+
+      it "moves the cursor to the beginning of the next word", ->
+        keydown('W', shift:true)
+        expect(editor.getCursorScreenPosition()).toEqual [0, 7]
+
+        keydown('W', shift:true)
+        expect(editor.getCursorScreenPosition()).toEqual [1, 1]
+
+        keydown('W', shift:true)
+        expect(editor.getCursorScreenPosition()).toEqual [2, 0]
+
+        keydown('W', shift:true)
+        expect(editor.getCursorScreenPosition()).toEqual [2, 0]
+
+    describe "as a selection", ->
+      describe "within a word", ->
+
+        it "selects to the end of the whole word", ->
+          editor.setCursorScreenPosition([0, 0])
+          keydown('y')
+          keydown('W', shift:true)
+          expect(vimState.getRegister('"').text).toBe 'cde1+- '
+
+        it "doesn't go past the end of the file", ->
+          editor.setCursorScreenPosition([2, 0])
+          keydown('y')
+          keydown('W', shift:true)
+          expect(vimState.getRegister('"').text).toBe ''
+
   describe "the e keybinding", ->
     beforeEach -> editor.setText("ab cde1+- \n xyz\n\nzip")

@@ -280,6 +314,41 @@
           expect(vimState.getRegister('"').text).toBe 'ab '
           expect(editor.getCursorScreenPosition()).toEqual [0, 1]

+  describe "the B keybinding", ->
+    beforeEach -> editor.setText("cde1+- ab \n xyz-123\n\n zip")
+
+    describe "as a motion", ->
+      beforeEach -> editor.setCursorScreenPosition([4, 1])
+
+      it "moves the cursor to the beginning of the previous word", ->
+        keydown('B', shift:true)
+        expect(editor.getCursorScreenPosition()).toEqual [3, 1]
+
+        keydown('B', shift:true)
+        expect(editor.getCursorScreenPosition()).toEqual [2, 0]
+
+        keydown('B', shift:true)
+        expect(editor.getCursorScreenPosition()).toEqual [1, 1]
+
+        keydown('B', shift:true)
+        expect(editor.getCursorScreenPosition()).toEqual [0, 7]
+
+        keydown('B', shift:true)
+        expect(editor.getCursorScreenPosition()).toEqual [0, 0]
+
+    describe "as a selection", ->
+      it "selects to the beginning of the whole word", ->
+        editor.setCursorScreenPosition([1, 8])
+        keydown('y')
+        keydown('B', shift:true)
+        expect(vimState.getRegister('"').text).toBe 'xyz-123'
+
+      it "doesn't go past the beginning of the file", ->
+        editor.setCursorScreenPosition([0, 0])
+        keydown('y')
+        keydown('B', shift:true)
+        expect(vimState.getRegister('"').text).toBe ''
+
   describe "the ^ keybinding", ->
     beforeEach ->
       editor.setText("  abcde")


Remove progress spec on Project::open

Remove progress spec on Project::open

--- spec/project-spec.coffee
+++ spec/project-spec.coffee
@@ -200,18 +200,6 @@
         runs ->
           expect(editor.buffer.getPath()).toBeUndefined()
           expect(newBufferHandler).toHaveBeenCalledWith(editor.buffer)
-
-    it "returns number of read bytes as progress indicator", ->
-      filePath = atom.project.getDirectories()[0]?.resolve 'a'
-      totalBytes = 0
-      promise = atom.project.open(filePath)
-      promise.progress (bytesRead) -> totalBytes = bytesRead
-
-      waitsForPromise ->
-        promise
-
-      runs ->
-        expect(totalBytes).toBe fs.statSync(filePath).size

   describe ".bufferForPath(path)", ->
     [buffer] = []


Add spec for publish with invalid package.json

--- spec/publish-spec.coffee
+++ spec/publish-spec.coffee
@@ -23,6 +23,19 @@

   afterEach ->
     server.close()
+
+  it "validates the package's package.json file", ->
+    packageToPublish = temp.mkdirSync('apm-test-package-')
+    fs.writeFileSync(path.join(packageToPublish, 'package.json'), '}{')
+    process.chdir(packageToPublish)
+    callback = jasmine.createSpy('callback')
+    apm.run(['publish'], callback)
+
+    waitsFor 'waiting for publish to complete', 600000, ->
+      callback.callCount is 1
+
+    runs ->
+      expect(callback.mostRecentCall.args[0].message).toBe 'Error parsing package.json file: Unexpected token }'

   it "validates the package is in a Git repository", ->
     packageToPublish = temp.mkdirSync('apm-test-package-')


Cleaned up setting of defaults in makeTooltipForView

--- core/app/backbone/factlink/make_tooltip_for_view.coffee
+++ core/app/backbone/factlink/make_tooltip_for_view.coffee
@@ -1,11 +1,11 @@
 Backbone.Factlink ||= {}

 Backbone.Factlink.makeTooltipForView = (parentView, options) ->
-  _.defaults options, $offsetParent: parentView.$el
+  _.defaults options,
+    $offsetParent: parentView.$el
+    $tooltipElement: parentView.$(options.selector)

-  tooltipOpener = new Backbone.Factlink.TooltipOpener _.extend {},
-    $tooltipElement: parentView.$(options.selector), options
-
+  tooltipOpener = new Backbone.Factlink.TooltipOpener options
   tooltipOpener.render()

   parentView.on 'close', => tooltipOpener.close()


implement the ability to table-define the scope for remotes

--- library/membrane/remote.coffee
+++ library/membrane/remote.coffee
@@ -37,8 +37,8 @@
 http = require "http"
 util = require "util"

-# A decorator style of routine that is used to capture the
-# source code of classes, functions and other first class
+# The decorator style of routine that is used to capture the
+# source code of classes, functions and the other first class
 # citizens for later transportation on the other environment.
 # Typically this would be the browser JS engine environment.
 # Call it with an empty function argument wrapping target.
@@ -69,11 +69,14 @@
     wrongCompiled = "using external with classes"
     assert.ok _.isFunction(compiled), notFunction
     assert not compiled.__super__?, wrongCompiled
-    wrapper = "function() { return #{compiled} }"
+    p = (ds) -> ("var #{k} = #{v}" for k, v in ds)
+    tabled = (d) -> _.sprintf wrapper, p(d).join ";\n"
+    wrapper = "function() { %s; return #{compiled}}"
     assert compiled.remote = Object.create {}
     assert compiled.remote.compiled = compiled
     assert compiled.remote.compile = compiler
-    assert compiled.remote.source = wrapper
+    assert compiled.remote.source = tabled {}
+    assert compiled.remote.tabled = tabled
     assert compiled.remote.meta ?= Object()
     compiled.remote.symbol = compiled.name
     assert _.isFunction compiled; compiled


Fixed bug where debugger didn't work after closing/re-opening

--- lib/atom-slime-repl-view.coffee
+++ lib/atom-slime-repl-view.coffee
@@ -178,4 +178,6 @@

   closeRepl: ->
     if @swank.connected
+      @closeDebugTab()
+      @subs.dispose()
       @swank.quit()


Use Disposable::dispose instead of ::off

--- lib/auto-update-packages.coffee
+++ lib/auto-update-packages.coffee
@@ -41,7 +41,7 @@
       @enableAutoUpdate()

   disableAutoUpdate: ->
-    @configSubscription?.off()
+    @configSubscription?.dispose()
     @configSubscription = null

     clearInterval(@autoUpdateCheck) if @autoUpdateCheck


:guardsman: Prevent attaching a buffer if the editor element is undefined

Closes #70

--- lib/color-buffer-element.coffee
+++ lib/color-buffer-element.coffee
@@ -79,6 +79,7 @@

   attach: ->
     return if @parentNode?
+    return unless @editorElement?
     @getEditorRoot().querySelector('.lines')?.appendChild(this)

   detach: ->


KDView: Code clean-up for MR #659.

KDView: Code clean-up for MR #659.

--- client/Framework/core/KDView.coffee
+++ client/Framework/core/KDView.coffee
@@ -727,22 +727,16 @@
     dragDir      = @dragState.direction
     dragGlobDir  = dragDir.global
     dragCurDir   = dragDir.current
-
     {axis}       = @getOptions().draggable
-    if axis
-      if axis is "x"
-        draggedDistance = Math.abs x
-      else
-        draggedDistance = Math.abs y
+
+    draggedDistance = if axis
+      if axis is "x" then Math.abs x else Math.abs y
+    else Math.max Math.abs(x), Math.abs(y)
+
+    if draggedDistance < 20 and not @beingDragged
+      @dragIsAllowed = @beingDragged = no
     else
-      draggedDistance   = Math.max Math.abs(x), Math.abs(y)
-
-    if draggedDistance < 20 and not @beingDragged
-      @dragIsAllowed = no
-      @beingDragged  = no
-    else
-      @dragIsAllowed = yes
-      @beingDragged  = yes
+      @dragIsAllowed = @beingDragged = yes

     if x > dragRelPos.x
       dragCurDir.x  = 'right'


payment: show downgrade error modal if there is an error, else show regular flow [fixes #79234382]

--- client/Main/payment/workflow.coffee
+++ client/Main/payment/workflow.coffee
@@ -48,9 +48,32 @@

   start: ->

+    isUpgrade = PaymentWorkflow.isUpgrade @state.currentPlan, @state.planTitle
+
+    if isUpgrade
+    then @startRegularFlow()
+    else @startDowngradeFlow()
+
+
+  startRegularFlow: ->
+
     @modal = new PaymentModal { @state }
     @modal.on 'PaymentWorkflowFinished', @bound 'finish'
     @modal.on "PaymentSubmitted",        @bound 'handlePaymentSubmit'
+
+
+  startDowngradeFlow: ->
+
+    { paymentController } = KD.singletons
+
+    paymentController.canChangePlan @state.planTitle, (err) =>
+
+      if err?
+        @state.error = err
+        @modal = new PaymentDowngradeErrorModal { @state }
+        return
+
+      @startRegularFlow()

   handlePaymentSubmit: (formData) ->


Minor: code styling

Minor: code styling

--- client/landing/Login/AppView.coffee
+++ client/landing/Login/AppView.coffee
@@ -364,6 +364,8 @@
                 name                  : 'password'
                 placeholder           : 'password'
                 validate              :
+                  events              :
+                    passwordCheck     : 'keyup'
                   rules               :
                     passwordCheck     : (input, event)=>

@@ -373,9 +375,6 @@
                       else
                         unless input.getValue() is passwordForm.inputs.confirm.getValue()
                           modal.setTitle "Looks good, please confirm it."
-
-                  events              :
-                    passwordCheck     : "keyup"

                 nextElement           :
                   confirm             :
@@ -384,21 +383,19 @@
                     name              : 'passwordConfirm'
                     placeholder       : 'confirm password'
                     validate          :
+                      events          :
+                        passwordCheck : 'keyup'
                       rules           :
                         passwordCheck : (input, event)=>

                           passwordForm = modal.modalTabs.forms.password
                           if passwordForm.inputs.password.getValue().length >= 8
                             if input.getValue() is passwordForm.inputs.password.getValue()
-                              modal.setTitle "Both look good!"
+                              modal.setTitle 'Both look good!'
                               passwordForm.buttons.submit.enable()
                             else
-                              modal.setTitle "Passwords should match."
+                              modal.setTitle 'Passwords should match.'
                               passwordForm.buttons.submit.disable()
-
-                      events          :
-                        passwordCheck : "keyup"
-

             buttons           :
               submit          :


Copy fixes

Copy fixes

--- client/landing/introapp/main.coffee
+++ client/landing/introapp/main.coffee
@@ -45,7 +45,7 @@

     buttons.addSubView gitHubSignupButton = new KDButtonView
       cssClass  : "github"
-      partial   : "Sign up with gitHub"
+      partial   : "Sign up with GitHub"
       callback  : -> KD.getSingleton("oauthController").openPopup "github"

     @addSubView buttons
@@ -107,7 +107,7 @@
       slogan     : "for Developers"
       subSlogan  : """

-          You can have an amazing VM that is better than your laptop.  It's connected to internet 100x faster.  You can share it with anyone you wish. Clone git repos.  Test and iterate on your code without breaking your setup.
+          You will have an amazing virtual machine that is better than your laptop.  It's connected to the internet 100's of times faster.  You can share it with anyone you wish. Clone git repos.  Test and iterate on your code without breaking your setup.

           It's free. Koding is your new localhost, in the cloud.
@@ -195,4 +195,4 @@
 KD.introView = new IntroView
 KD.introView.appendToDomBody()

-KD.utils.defer -> KD.introView.setClass 'in'+KD.utils.defer -> KD.introView.setClass 'in'


edit mode link display fixed

edit mode link display fixed

--- client/landing/app/Applications/Activity.kdapplication/widgets/tutorialwidget.coffee
+++ client/landing/app/Applications/Activity.kdapplication/widgets/tutorialwidget.coffee
@@ -336,7 +336,7 @@
   switchToEditView:(activity)->
     @submitBtn.setTitle "Edit Tutorial"
     @addCustomData "activity", activity
-    {title, body, tags} = activity
+    {title, body, tags, link} = activity

     @tagController.reset()
     @tagController.setDefaultValue tags or []
@@ -344,6 +344,7 @@
     fillForm = =>
       @inputDiscussionTitle.setValue Encoder.htmlDecode title
       @inputContent.setValue Encoder.htmlDecode body
+      @inputTutorialEmbedLink.setValue Encoder.htmlDecode link?.link_url
       @inputContent.generatePreview()

     fillForm()


Teamwork: Removed double Run button.

Teamwork: Removed double Run button.

--- client/landing/app/Applications/Teamwork.kdapplication/Views/facebookteamwork.coffee
+++ client/landing/app/Applications/Teamwork.kdapplication/Views/facebookteamwork.coffee
@@ -95,8 +95,6 @@
     panel.header.addSubView @runButton = new KDButtonViewWithMenu
       title               : "Run"
       menu                :
-        "Run"             :
-          callback        : => @run()
         "Run on Facebook" :
           callback        : => @runOnFB()
       callback            : => @run()


Update profile stats when submitting a classification

--- overrides.coffee
+++ overrides.coffee
@@ -127,6 +127,16 @@
       if page.workflow is 'field'
         page.decisionTree.tasks.species.clearFilters()
         page.decisionTree.tasks.species.confirmButton.disabled = true
+
+    page.on page.FINISH_SUBJECT,  (e, page) ->
+      group_id = page.subjectViewer.subject.group_id
+      if User.current?.project.groups[group_id]
+        User.current?.project.groups[group_id].classification_count++
+      else
+        User.current?.project.groups[group_id] = {title: '', classification_count: 1}
+      profile_stats.el.html ''
+      profile_stats.setGroupMessages()
+      profile_stats.renderTemplate()

 # disable the species confirm until a value is chosen.
 field_page.el.on field_page.decisionTree.CHANGE, ({originalEvent: {detail}}) ->


Cordova alert will bause a crash if passed in something else than a string

--- testApp/app/controllers/steroids.coffee
+++ testApp/app/controllers/steroids.coffee
@@ -19,10 +19,10 @@
       list.appendChild(el)

   @testSteroidsDefined: () ->
-    navigator.notification.alert steroids?
+    navigator.notification.alert "#{steroids?}"

   @testCordovaDefined: () ->
-    navigator.notification.alert cordova?
+    navigator.notification.alert "#{cordova?}"

   @testJSCoreBridgeImplDefined: () ->
     navigator.notification.alert __JSCoreBridgeImpl?


Add mouseover with raw data to data cells

--- lib/pg-hoff-results-view.coffee
+++ lib/pg-hoff-results-view.coffee
@@ -81,6 +81,7 @@
         td.textContent = text
         try
             td.textContent = Type[typeName].format(text) if Type[typeName]?.format and atom.config.get('pg-hoff.formatColumns')
+            td.setAttribute 'title', text
         catch err
             console.error 'Could not format as ' + Type[typeName].name, text


database: log all transactions to see if something is DOS'ing; also, switch to no retries/reconnects; just use what the driver provides.

--- salvus/cassandra.coffee
+++ salvus/cassandra.coffee
@@ -389,8 +389,8 @@
             keyspace        : undefined
             username        : undefined
             password        : undefined
-            query_timeout_s : 45    # any query that doesn't finish after this amount of time (due to cassandra/driver *bugs*) will be retried a few times (same as consistency causing retries)
-            query_max_retry : 5    # max number of retries
+            query_timeout_s : 30    # any query that doesn't finish after this amount of time (due to cassandra/driver *bugs*) will be retried a few times (same as consistency causing retries)
+            query_max_retry : 3    # max number of retries
             consistency     : undefined
             verbose         : false # quick hack for debugging...
             conn_timeout_ms : 15000  # Maximum time in milliseconds to wait for a connection from the pool.
@@ -410,10 +410,14 @@
         @connect()

     reconnect: (cb) =>
-        if not @conn?
+        winston.debug("reconnect to database server")
+        if not @conn? or not @conn.shutdown?
+            winston.debug("directly connecting")
             @connect(cb)
             return
-        @conn.shutdown? () =>
+        winston.debug("reconnect to database server -- first shutting down")
+        @conn.shutdown (err) =>
+            winston.debug("reconnect to database server -- shutdown returned #{err}")
             delete @conn
             @connect(cb)

@@ -663,7 +667,26 @@
         @select(opts)

     cql: (query, vals, consistency, cb) =>
-        #winston.debug("cql: '#{query}'")
+        if typeof vals == 'function'
+            cb = vals
+            vals = []
+            consistency = undefined
+        if typeof consistency == 'function'
+            cb = consistency
+            consistency = undefined
+        if not consistency?
+            consistency = @consistency
+
+        winston.debug("cql: '#{misc.trunc(query,100)}', consistency=#{consistency}")
+        @conn.execute query, vals, { consistency: consistency }, (err, results) =>
+            if err?
+                winston.error("cql: ERROR ('#{query}',params=#{misc.to_json(vals).slice(0,1024)}) error = \n#{err}")
+            else
+                results = results.rows
+            cb?(err, results)
+
+    cql0: (query, vals, consistency, cb) =>
+        winston.debug("cql: '#{query}'")
         if typeof vals == 'function'
             cb = vals
             vals = []


Added one search test with $page

Added one search test with $page

--- lib/test_generators/page.coffee
+++ lib/test_generators/page.coffee
@@ -85,5 +85,5 @@
           assert.ok(!err, 'no errors')
           assert.equal(data.total_rows, 1, 'has the correct total_rows')
           assert.equal(data.rows.length, 1, 'has the correct row.length')
-          assert.deepEqual(data.rows[0], model.toJSON(), 'has the correct value')
+          assert.deepEqual(expected = JSON.stringify(model.toJSON()), actual = JSON.stringify(data.rows[0]), "Expected: #{util.inspect(expected)}. Actual: #{util.inspect(actual)}")
           done()


refactor: changed game to match according to rules

--- api/server.coffee
+++ api/server.coffee
@@ -16,7 +16,7 @@
 )

 app = express()
-app.get "/game", (req, res) ->
+app.get "/match", (req, res) ->
   current = table.child('current_games')
   current.once('value', (nameSnapshot) ->
     game = nameSnapshot.val()
@@ -29,7 +29,7 @@
     ]
   )
   return
-app.get "/game/away", (req, res) ->
+app.get "/match/away", (req, res) ->
   away = table.child('current_games')
   away.once('value', (nameSnapshot) ->
     game = nameSnapshot.val()
@@ -42,10 +42,9 @@
         name: person.name
       ]
     )
-
   )
   return
-app.get "/game/away/add", (req, res) ->
+app.get "/match/away/add", (req, res) ->
   awayAdd = table.child('current_games')
   awayAdd.once('value', (nameSnapshot) ->
     game = nameSnapshot.val()
@@ -55,14 +54,13 @@
     game[id].away_score = score
     awayAdd.child(id).set(game[id], =>
       res.send [
-        score: score
+        away_score: score
       ]
     )
-
   )
   return

-app.get "/game/home", (req, res) ->
+app.get "/match/home", (req, res) ->
   home = table.child('current_games')
   home.once('value', (nameSnapshot) ->
     game = nameSnapshot.val()
@@ -75,7 +73,43 @@
         name: person.name
       ]
     )
-
+  )
+  return
+
+app.get "/match/home/add", (req, res) ->
+  add = table.child('current_games')
+  add.once('value', (nameSnapshot) ->
+    game = nameSnapshot.val()
+    id = Object.keys(game)[0]
+    score = game[id].home_score
+    score = score + 1
+    game[id].home_score = score
+    add.child(id).set(game[id], =>
+      res.send [
+        home_score: score
+      ]
+    )
+  )
+  return
+
+app.get "/match/home/sub", (req, res) ->
+  add = table.child('current_games')
+  add.once('value', (nameSnapshot) ->
+    game = nameSnapshot.val()
+    id = Object.keys(game)[0]
+    score = game[id].home_score
+    score = score - 1
+    if score < 0
+      res.send [
+        error: "You can't go negative."
+      ]
+      return
+    game[id].home_score = score
+    add.child(id).set(game[id], =>
+      res.send [
+        home_score: score
+      ]
+    )
   )
   return


minor style changes in the cakefile

minor style changes in the cakefile

--- library/nucleus/cakefile.coffee
+++ library/nucleus/cakefile.coffee
@@ -101,10 +101,10 @@
         assert _.isObject generator.stdout.pipe process.stdout
         assert _.isObject generator.stderr.pipe process.stderr
         assert _.isObject generator.on "exit", (status) ->
-            failure = "Failed to generate documentation".red
-            success = "Generated documentation successfuly".green
-            logger.error failure if status isnt 0
-            logger.info success if status is 0
+            failure = "Failed to generate documentation"
+            success = "Generated documentation successfuly"
+            return logger.error failure.red if status isnt 0
+            logger.info success.green if status is 0

     # This is one of the major tasks in this Cakefile, it implements
     # the compilatation of the library source code from CoffeeScript
@@ -122,10 +122,10 @@
         assert _.isObject compiler.stdout.pipe process.stdout
         assert _.isObject compiler.stderr.pipe process.stderr
         assert _.isObject compiler.on "exit", (status) ->
-            failure = "Failed to compile library".red
-            success = "Compiled library successfuly".green
-            logger.error failure if status isnt 0
-            logger.info success if status is 0
+            failure = "Failed to compile framework library"
+            success = "Compiled framework library successfuly"
+            return logger.error failure.red if status isnt 0
+            logger.info success.green if status is 0

     # This task launches an instance of application where this task
     # is invoked at. It should be either an application build within


fix(errorHandling): check for response

fix(errorHandling): check for response

since it might not be defined in case of an timeout

--- src/coffee/services/base.coffee
+++ src/coffee/services/base.coffee
@@ -529,7 +529,7 @@
   # body - {Object} A JSON object containing the HTTP API resource or error messages
   _wrapResponse: (resolve, reject, originalRequest, error, response, body) ->
     responseJson =
-      if @_stats.includeHeaders
+      if @_stats.includeHeaders and response
         http:
           request:
             method: response.request.method


Disable polling for EarnedAchievements, not currently used.

If we start using achievements in Ozaria, we can re-enable this.

--- app/models/CocoModel.coffee
+++ app/models/CocoModel.coffee
@@ -325,6 +325,7 @@
     return if _.isString @url then @url else @url()

   @pollAchievements: ->
+    return  # Not needed until/unlesss we start using achievements in Ozaria
     return if application?.testing

     CocoCollection = require 'collections/CocoCollection'
@@ -343,7 +344,7 @@
         console.error 'Miserably failed to fetch unnotified achievements', arguments
       cache: false

-  CocoModel.pollAchievements = _.debounce CocoModel.pollAchievements, 500
+  CocoModel.pollAchievements = _.debounce CocoModel.pollAchievements, 3000

   #- Internationalization


empty pin now gives sane error.

empty pin now gives sane error.

--- app/models/pin-model.coffee
+++ app/models/pin-model.coffee
@@ -10,7 +10,7 @@
       return callback(error) if error?
       @db.insert _.extend({pin: hash}, attributes), callback

-  checkPin: (uuid, pin, callback=->)=>
+  checkPin: (uuid, pin='', callback=->)=>
     @db.findOne { uuid: uuid }, (error, res)=>
       return callback(error) if error?
       @bcrypt.compare pin, res.pin, callback


selection text bug fixed

selection text bug fixed

--- app/models/selection.coffee
+++ app/models/selection.coffee
@@ -8,6 +8,7 @@
   endPosition: DS.attr()

   text: ( ->
+    return '' unless @get('paragraph.text')
     @get('paragraph.text').slice(@get('startPosition'), @get('endPosition'))
   ).property('paragraph', 'paragraph.text', 'startPosition', 'endPosition')


Fix conditionals

Fix conditionals

--- app/pages/project/home.cjsx
+++ app/pages/project/home.cjsx
@@ -52,7 +52,7 @@
             <small>at {@props.project.redirect}</small>
           </a>
         # For Gravity Spy
-        else if @props.project.experimental_tools.indexOf 'workflow assignment' isnt -1
+        else if @props.project.experimental_tools.indexOf('workflow assignment') isnt -1
           if @props.user?
             currentWorkflowAtLevel = @state.workflows?.filter (workflow) =>
               workflow if workflow.id is @props.preferences?.settings.workflow_id
@@ -67,6 +67,10 @@
                   onClick={@handleWorkflowSelection.bind this, workflow}
                 >
                   {workflow.display_name}
+                </Link>
+              else # Show something if workflow config levels aren't set.
+                <Link to={"/projects/#{@props.project.slug}/classify"} className="call-to-action standard-button">
+                  Get started!
                 </Link>
           else
             <Link to={"/projects/#{@props.project.slug}/classify"} className="call-to-action standard-button">


Do not walk dotfiles in coffee dirs.

Do not walk dotfiles in coffee dirs.

--- src/compilers/hem/stitch.coffee
+++ src/compilers/hem/stitch.coffee
@@ -7,14 +7,15 @@
 class Stitch
   constructor: (@paths = []) ->
     @paths = (resolve(path) for path in @paths)
-
+
   resolve: ->
     flatten(@walk(path) for path in @paths)

   # Private

   walk: (path, parent = path, result = []) ->
-    for child in fs.readdirSync(path)
+    # Walk over directory excluding dotfiles
+    for child in fs.readdirSync(path) when child.match(/^([^\.].*|$)/)
       child = join(path, child)
       stat  = fs.statSync(child)
       if stat.isDirectory()
@@ -28,11 +29,11 @@
   constructor: (@filename, @parent) ->
     @ext = extname(@filename).slice(1)
     @id  = modulerize(@filename.replace(@parent + '/', ''))
-
+
   compile: ->
     compilers[@ext](@filename)
-
+
   valid: ->
     !!compilers[@ext]
-
+
 module.exports = Stitch


Remove unused inclusion of $window in OffcanvasCtrl

--- app/assets/javascripts/darkswarm/controllers/offcanvas_controller.js.coffee
+++ app/assets/javascripts/darkswarm/controllers/offcanvas_controller.js.coffee
@@ -1,4 +1,4 @@
-Darkswarm.controller "OffcanvasCtrl", ($scope, $window) ->
+Darkswarm.controller "OffcanvasCtrl", ($scope) ->
   $scope.menu = $(".left-off-canvas-menu")

   $scope.setOffcanvasMenuHeight = ->


Fixed #62. Stop tutorial when user with classifications navigates directly to classification page.

--- app/classify.cjsx
+++ app/classify.cjsx
@@ -17,6 +17,7 @@

   wrapper: null
   body: null
+  html: null
   main: null

   getInitialState: ->
@@ -43,7 +44,7 @@
     @html = document.getElementsByTagName('html')[0]
     @main = document.getElementsByClassName('main')[0]

-    unless @props.user?.classification_count > 0
+    if User.current is null
       @openTutorial 'general'

     @setInitialSkipPreference(@props)


Fixed modal always referring to last recent plunk

--- assets/lib/plunker.coffee
+++ assets/lib/plunker.coffee
@@ -1,6 +1,6 @@
 $ ->
   jQuery.getJSON "//#{location.hostname}/api/v1/plunks", (json) ->
-    for plunk in json
+    for plunk in json then do (plunk) ->
       $li = $("

").addClass("span3").addClass("plunk")
       $a = $("").attr("href", plunk.url).addClass("thumbnail").appendTo($li)
       $img = $("")
@@ -29,7 +29,7 @@

       $a.on "click", ->
         $modal = $("

").addClass("modal")
-        $header = $("
×
#{plunk.description}

")
+        $header = $("
×
#{plunk.description or 'Untitled'}

")
           .addClass("modal-header")
           .appendTo($modal)
         $body = $("

")


Update# update map zoom

Update# update map zoom


ActivityAppController: Fixed overriding flags object of activityController.

--- client/landing/app/Applications/Activity.kdapplication/AppController.coffee
+++ client/landing/app/Applications/Activity.kdapplication/AppController.coffee
@@ -143,20 +143,18 @@
       #to-do add isExempt control.
       #@isExempt (exempt)=>
       #if exempt or @getFilter() isnt activityTypes
-      groupObj = KD.getSingleton('groupsController').getCurrentGroup()
-
-      options =
-        to     : options.to or Date.now()
-        group  :
-          slug : groupObj?.slug or "koding"
-          id   : groupObj.getId()
-        limit  : 20
-        facets : @getActivityFilter()
-
-      if KD.getSingleton('activityController').flags?.showExempt?
-        options.withExempt = KD.getSingleton('activityController').flags.showExempt
-      else
-        options.withExempt = false
+      groupObj     = KD.getSingleton("groupsController").getCurrentGroup()
+      options      =
+        to         : options.to or Date.now()
+        group      :
+          slug     : groupObj?.slug or "koding"
+          id       : groupObj.getId()
+        limit      : 20
+        facets     : @getActivityFilter()
+        withExempt : no
+
+      if KD.getSingleton("activityController").flags.showExempt
+        options.withExempt = yes

       eventSuffix = "#{@getFeedFilter()}_#{@getActivityFilter()}"


Show loginscreen when clicked Click here to start tab in Guest Mode

--- client/landing/app/Applications/StartTab.kdapplication/AppController.coffee
+++ client/landing/app/Applications/StartTab.kdapplication/AppController.coffee
@@ -7,7 +7,7 @@
     multiple     : yes
     preCondition :
       condition  : (cb)-> cb KD.isLoggedIn()
-      failure    : (cb)-> KD.getSingleton('router').handleRoute "/Activity"
+      failure    : (cb)-> KD.requireLogin onFailMsg: 'Login to start...' # getSingleton('router').handleRoute "/Activity"

   constructor:(options = {}, data)->


fix for mr suggestions

fix for mr suggestions

--- client/landing/app/Applications/WebTerm.kdapplication/webtermappview.coffee
+++ client/landing/app/Applications/WebTerm.kdapplication/webtermappview.coffee
@@ -79,13 +79,18 @@

       if query.fullscreen
         windowController = KD.getSingleton("windowController")
-        windowController.unloadListeners = []
+        windowController.clearUnloadListeners()
+
         windowController.on "clientIdChanged", =>
           window.parent.postMessage "clientIdChanged", "*"
+
         KD.getSingleton("mainView").enableFullscreen()
+
         if window.parent?.postMessage
+
           window.parent.postMessage "fullScreenTerminalReady", "*"
           window.parent.postMessage "loggedIn", "*"  if KD.isLoggedIn()
+
           @on "KDObjectWillBeDestroyed", ->
             window.parent.postMessage "fullScreenWillBeDestroyed", "*"


[fix] Admin page new template group routing.

--- js/admin.coffee
+++ js/admin.coffee
@@ -95,7 +95,7 @@
 #add new group to save data array
 newGroup = (groupcode,groupname,description,privacy) ->
   $.ajax(
-    url: "/admin/newTemplateGroup",
+    url: "newTemplateGroup",
     type: 'POST',
     data:
       c: groupcode


Add command for statuses of monitored services

--- scripts/monitors.coffee
+++ scripts/monitors.coffee
@@ -103,3 +103,22 @@
   robot.respond /.*稼働状況.*/i, (msg) ->
     #getMonitor
     msg.send "現在の稼働状況は..."
+    lines = []
+    alive = 0
+    dead = 0
+    missing = 0
+    for url, monitor of robot.brain.data.monitors
+      m = new Monitor url
+      switch m.data.status
+        when "alive" then alive += 1
+        when "dead" then dead += 1
+        when "missing" then missing += 1
+      lines.push m.toString()
+
+    text = lines.join "\n"
+
+    if dead + missing > 0
+      text += "\n\n"
+      text += "#{dead + missing}台のサーバーでエラーが発生しています。"
+
+    setTimeout (-> msg.send text), 1000


Only list the type once when there's parallelism, and hopefully don't fail when there's no type

--- js/inner.coffee
+++ js/inner.coffee
@@ -381,13 +381,18 @@
    # hack - how can an action know its type is different from the previous, when
    # it doesn't even have access to the build
   different_type: (action) =>
-    lastType = null
+    last = null
+    breakLoop = false
     for s in @steps()
-      if s.actions()[0] == action
+      for a in s.actions()
+        if a == action
+          breakLoop = true # no nested breaks in CS
+          break
+        last = a
+      if breakLoop
         break
-      lastType = s.actions()[0].type()
-
-    not (lastType == action.type())
+
+    last? and not (last.type() == action.type())

   urlForBuildNum: (num) =>
     "#{@project_path()}/#{num}"


Add a default plan

Add a default plan

--- js/inner.coffee
+++ js/inner.coffee
@@ -459,11 +459,21 @@
         b.price - a.price # most expensive first
       ap

+    @defaultPlan = @komp =>
+      for p in @availablePlans()
+        if p.default?
+          return p
+
     @selectedPlan = @komp =>
       if @chosenPlan()?
         @chosenPlan()
       else if @existingPlanName()? and @availablePlans()?
         @availablePlans()[@existingPlanName()]
+      else if @defaultPlan()?
+        @defaultPlan()
+      else
+        @plans()[2] # always show something - pick the 3rd most expensive
+

     @userMatrix = @komp =>


Fix typo

Fix typo

--- js/inner.coffee
+++ js/inner.coffee
@@ -433,8 +433,7 @@
         total += @availablePlans()[info.plan].price
       total / 100

-
-    # use computer observable because knockout select boxes make it hard to do otherwise
+    # use computed observable because knockout select boxes make it hard to do otherwise
     @selectOrganization = @komp
       write: (value) =>
         @selectedOrganization(value)


Realtime output, bitches

Realtime output, bitches

--- js/inner.coffee
+++ js/inner.coffee
@@ -285,7 +285,7 @@

     @build_channel.bind('newAction', (json) => @newAction json)
     @build_channel.bind('updateAction', (json) => @updateAction json)
-    # @build_channel.bind('appendAction', (json) => @appendAction json)
+    @build_channel.bind('appendAction', (json) => @appendAction json)

   newAction: (json) =>
     if not @steps()[json.step]?
@@ -300,6 +300,9 @@

   updateAction: (json) =>
     @steps()[json.step].actions()[json.index].updateObservables(json.log)
+
+  appendAction: (json) =>
+    @steps()[json.step].actions()[json.index].out.push(json.out)

   # TODO: CSRF protection
   retry_build: (data, event) =>


Remove unstyled wrapper

Remove unstyled wrapper

--- resources/assets/coffee/react/beatmap-discussions/events.coffee
+++ resources/assets/coffee/react/beatmap-discussions/events.coffee
@@ -32,13 +32,11 @@
                   key: "date-#{lastCreatedAtString}"
                   className: 'beatmapset-events__title'
                   lastCreatedAtString
-              div
+              el Event,
                 key: event.id
-                className: 'beatmapset-events__event'
-                el Event,
-                  event: event
-                  discussions: @props.discussions
-                  mode: 'discussions'
-                  time: event.created_at
-                  users: @props.users
+                event: event
+                discussions: @props.discussions
+                mode: 'discussions'
+                time: event.created_at
+                users: @props.users
             ]


Used destructuring assignment to simplify code.

--- LinkedList.coffee
+++ LinkedList.coffee
@@ -10,18 +10,14 @@
 	if position < 0 or position > this.length then return
 	nodeToAdd = new Node(value)
 	if position is 0
-		# If empty, oldHead is undefined
-		oldHead = this.head
-		this.head = nodeToAdd
-		nodeToAdd.next = oldHead
+		# If list empty, old this.head is undefined
+		[this.head, nodeToAdd.next] = [nodeToAdd, this.head]
 	else
 		currentNode = this.head
 		# Get to the node before the insertion position.
 		for i in [0...position - 1]
 			currentNode = currentNode.next
-		oldCurrentNodeNext = currentNode.next
-		currentNode.next = nodeToAdd
-		nodeToAdd.next = oldCurrentNodeNext
+		[currentNode.next, nodeToAdd.next] = [nodeToAdd, currentNode.next]
 	this.length++
 	return nodeToAdd.value

@@ -37,7 +33,7 @@
 		for i in [0...position - 1]
 			currentNode = currentNode.next
 		nodeToRemove = currentNode.next
-		currentNode.next = nodeToRemove.next
+		currentNode.next = currentNode.next.next
 	this.length--
 	return nodeToRemove.value


bug found

bug found

--- coffeequate/tests/spec/nodes.coffee
+++ coffeequate/tests/spec/nodes.coffee
@@ -73,9 +73,12 @@
 				expect(add.solve("x")[0].toString()).toEqual("-3")
 				expect(add.solve("x").length).toEqual(1)
 				add = parse.stringToExpression("(x + 3) * (x + -2)")
-
 				expect(add.solve("x")[0].toString()).toEqual("2")
 				expect(add.solve("x")[1].toString()).toEqual("-3")
+
+				# Projectile motion for t?
+				add = parse.stringToExpression("u * t + a * t ** 2 * 0.5 + -s")
+				console.log(add.solve("t").toString())

 		describe "representing multiplication", ->


Comment-out stubbing constructor.

Comment-out stubbing constructor.

--- test/spec/router_spec.coffee
+++ test/spec/router_spec.coffee
@@ -180,20 +180,21 @@
         router.match /url/, {}
       ).to.throwError()

-      spy = sinon.spy(Route, 'constructor')
-
-      url = /url/
-      options = {controller: 'c', action: 'a'}
-      url2 = /url2/
-      options2 = {}
-
-      valid1 = router.match url, options
-      expect(spy).was.called()
-      expect(Route.constructor).was.calledWith url, 'c', 'a'
-
-      valid2 = router.match url2, 'c#a', options2
-      expect(Route).was.calledWith url2, 'c', 'a', options2
-      spy.restore()
+      # TODO: stub constructor somehow. It does not work now.
+      # spy = sinon.spy(Route, 'constructor')
+
+      # url = /url/
+      # options = {controller: 'c', action: 'a'}
+      # url2 = /url2/
+      # options2 = {}
+
+      # valid1 = router.match url, options
+      # expect(spy).was.called()
+      # expect(Route.constructor).was.calledWith url, 'c', 'a'
+
+      # valid2 = router.match url2, 'c#a', options2
+      # expect(Route).was.calledWith url2, 'c', 'a', options2
+      # spy.restore()

     # Tests for passed route
     # -----------------------


Workspace: Added loader to finder for clients.

--- client/landing/app/CommonViews/workspace/panes/collaborativeclientfinderpane.coffee
+++ client/landing/app/CommonViews/workspace/panes/collaborativeclientfinderpane.coffee
@@ -6,14 +6,15 @@

     super options, data

-    @container = new KDView
-      cssClass : "client-finder-pane"
-      partial  : "Fetching remote file tree"
+    @container    = new KDView
+      cssClass    : "client-finder-pane"

-    panel              = @getDelegate()
-    workspace          = panel.getDelegate()
-    {@sessionKey}      = @getOptions()
-    @workspaceRef      = workspace.firepadRef.child @sessionKey
+    panel         = @getDelegate()
+    workspace     = panel.getDelegate()
+    {@sessionKey} = @getOptions()
+    @workspaceRef = workspace.firepadRef.child @sessionKey
+
+    @createLoader()

     @workspaceRef.on "value", (snapshot) =>
       files = snapshot.val()?.files
@@ -31,6 +32,19 @@
       view = @fileTree.getView()
       @container.updatePartial ""
       @container.addSubView view
+
+  createLoader: ->
+    @container.addSubView loaderContainer = new KDView
+      cssClass    : "loader-container"
+
+    loaderContainer.addSubView new KDLoaderView
+      showLoader  : yes
+      size        :
+        width     : 32
+
+    loaderContainer.addSubView new KDCustomHTMLView
+      tagName     : "p"
+      partial     : "Fetching remote file tree"

   pistachio: ->
     """


Fixed an issue in Safari where object was not being serialized. Turns out that you have to expand it out by doing object.find("*").serialize() in order for it to work.

--- app/assets/javascripts/ask.js.coffee
+++ app/assets/javascripts/ask.js.coffee
@@ -64,8 +64,7 @@
     Toggle between 'edit' question and 'preview' question
   ###
   createQuestionPreview = (question) ->
-    if question.children('preview-question-fields').length == 0 # if we don't have a question preview yet
-      asker = question.serialize().match(/^\w+/)[0] # get asker name
+    if question.children('.preview-question-fields').length == 0 # if we don't have a question preview yet
       question_type = question.find('.select-question-type select').val() # get question type
       question_details = question.serializeObject(/^.*?\[\d+\]/)  # get question details and choices

@@ -129,7 +128,7 @@

 jQuery.fn.serializeObject = (ignore_regex = null) ->
-  arrayData = @serializeArray()
+  arrayData = @.find("*").serializeArray()
   objectData = {}

   pushData = (node, hash_tail, value) ->


Use data guards instead

Use data guards instead

--- app/assets/javascripts/orders.coffee
+++ app/assets/javascripts/orders.coffee
@@ -83,7 +83,7 @@

-          <select name="order_detail[#{currentNumRows}][item_id]" class="item form-control row-#{currentNumRows} unique">
+          <select name="order_detail[#{currentNumRows}][item_id]" class="item form-control row-#{currentNumRows}" data-guard="different">
             <option value="">Select an item...</option>
           </select>

@@ -166,6 +166,4 @@
   populateQuantity selected, quantity_element

 $(document).on "page:change", ->
-  if $("#new-order-table").length > 0
-    $.guard(".unique").using("different");
-    addNewOrderRow()
+  addNewOrderRow() if $("#new-order-table").length > 0


Missed one data table hook

Missed one data table hook

--- app/assets/javascripts/orders.coffee
+++ app/assets/javascripts/orders.coffee
@@ -87,5 +87,3 @@
   element = findLastCategory()
   # populateCategories element
   addListeners element
-
-  $('.data-table').DataTable()


enhance: first tour

enhance: first tour

--- app/components/submission-box.coffee
+++ app/components/submission-box.coffee
@@ -1,13 +1,15 @@
 `import Ember from 'ember'`
 `import ENV from 'irene/config/environment'`
+`import tourName from 'irene/utils/tour-name';`

 SubmissionBoxComponent = Ember.Component.extend
   classNames: ["box"]
   onboard: Ember.inject.service()

   didInsertElement: ->
-    if localStorage['alreadyShown'] in [false, undefined]
+    name = tourName(ENV.TOUR.newScan)
+    if localStorage[name] in ["false", undefined]
       this.set('onboard.activeTour', ENV.TOUR.newScan)
-      localStorage.setItem('alreadyShown', true)
+      localStorage.setItem(name, "true")

 `export default SubmissionBoxComponent`


Fixed a bug with the casting screen.

Fixed a bug with the casting screen.

--- app/lib/surface/CastingScreen.coffee
+++ app/lib/surface/CastingScreen.coffee
@@ -35,7 +35,6 @@
     if new Date().getTime() - @t0 > 500
       createjs.Tween.removeTweens @progressBar
       createjs.Tween.get(@progressBar).to({scaleX:e.progress}, 200)
-      @update()

   makeProgressBar: ->
     BAR_PIXEL_HEIGHT = 3


Comments

Comments

--- extension/chrome/js/src/content.coffee
+++ extension/chrome/js/src/content.coffee
@@ -50,6 +50,7 @@
 		constructor: ->
 			window.addEventListener "message", @messageListener, false

+			# Get the settings
 			chrome.storage.local.get null, (items) =>
 				_.each items, (value, key) =>
 					switch key
@@ -61,10 +62,12 @@
 					return
 				return

+			# Get the selected options
 			chrome.storage.sync.get M.STORE.BOX_SETTING, (items) =>
 				@settingProps = items[M.STORE.BOX_SETTING]
 				return

+			# Get the last used presets
 			M.getLastUsed (lastUsed) =>
 				@lastUsed = lastUsed
 				return


* Make lines bend.

* Make lines bend.

--- vendor/assets/javascripts/balloons.js.coffee
+++ vendor/assets/javascripts/balloons.js.coffee
@@ -4,13 +4,14 @@
   # Balloon
   #
   class Balloon
+    @createdFilter = false
+
     constructor: (@targetElement, @options) ->
+      @createFilter() unless Balloon.createdFilter
       @createBalloon()
       @createArrow()
       @position()
-
-      @createFilter()
-
+
       # Setup events.
       $(window).on 'resize', =>
         @position()
@@ -62,11 +63,12 @@
       # Move the points to the edge of the elements.
       from = @pointOnRectangle(origFrom, @balloon.outerWidth() * balloonInset, @balloon.outerHeight() * balloonInset, origTo)
       to = @pointOnRectangle(origTo, @targetElement.outerWidth() * targetInset, @targetElement.outerHeight() * targetInset, origFrom)
+      middle = @perpendicularOffset(from, to, 20)

       segments = []
       segments.push("M", from.x, from.y)
-      segments.push("L", to.x, to.y)
-      #segments.push("S", (from.x + to.x)/2, (from.y + to.y)/2 + 100, to.x, to.y)
+      #segments.push("L", to.x, to.y)
+      segments.push("C", middle.x, middle.y, middle.x, middle.y, to.x, to.y)
       @arrow.attr("d", segments.join(" "))
       #@arrow.attr("d", "M100 100 C 20 20, 40 30, 150 10 M150 10 L120 0 M150 10 L125 30") #lineFunction(lineData))

@@ -91,7 +93,22 @@

       {x: rectCenter.x + x, y: rectCenter.y + y}

-
+    # Find a point that is offset from the middle of the line.
+    perpendicularOffset: (from, to, offset) ->
+      mx = from.x + (to.x - from.x)/2
+      my = from.y + (to.y - from.y)/2
+      dx = from.x - to.x
+      dy = from.y - to.y
+      dist = Math.sqrt(dx*dx + dy*dy)
+      dx /= dist
+      dy /= dist
+
+      if dx > 0
+        dx = -dx
+        dy = -dy
+
+      {x: mx + offset * dy, y: my - offset * dx}
+
     createFilter: ->
       ns = "http://www.w3.org/2000/svg"
       r1 = new DOMParser().parseFromString('<svg  xmlns="' + ns + '">
@@ -106,23 +123,8 @@

       newNode = document.importNode(r1.documentElement, true)
       document.body.appendChild(newNode)
-
-    ###
-
-      <svg class="tour-arrow">
-        <defs>
-        <filter id="chalk" height="2" width="1.6" color-interpolation-filters="sRGB" y="-0.5" x="-0.3">
-          <feTurbulence baseFrequency="0.42065" seed="115" result="result1" numOctaves="1" type="turbulence"/>
-          <feOffset result="result2" dx="-5" dy="-5"/>
-          <feDisplacementMap scale="10" yChannelSelector="G" in2="result1" xChannelSelector="R" in="SourceGraphic"/>
-        </filter></defs>
-
-        <path d="M10 10 C 20 20, 40 30, 150 10 M150 10 L120 0 M150 10 L125 30" class="arrow" filter="url(#chalk)"/>
-      </svg>
+      Balloon.createdFilter = true

-    ###
-
-
   window.Balloon = Balloon


Clean up & add comments

Clean up & add comments

--- src/modules/todo/module.coffee
+++ src/modules/todo/module.coffee
@@ -7,15 +7,20 @@
 class TodoModule extends Marionette.Module

     initialize: ->
-        this.todoRegionId = 'todo-region'
+        this.todoRegionId = 'todo-module-region'

     onStart: ->
+        # encapsulate each module in a container
+        # so you can do what you want without
+        # affecting other modules
         this._createContainer()
         this._addRegion()
         this._addLayout()

     onStop: ->
+        # remove region & container when stopping
+        # unload of module could be important in big app / modules
         this._removeRegion()
         this._destroyContainer()

@@ -29,13 +34,13 @@
     _addRegion: ->
         this.app.addRegions todoRegion: '#' + this.todoRegionId

-    _addLayout: () ->
-        this.app.todoRegion.show(new TodoLayout)
+    _addLayout: ->
+        this.app.todoRegion.show new TodoLayout

     _destroyContainer: ->
-        node = document.getElementById(this.todoRegionId)
+        node = document.getElementById this.todoRegionId
         node?.parentElement.removeChild node

     _removeRegion: ->


Replace 'queen' with 'zap' in our code word list

--- server/lib/utils.coffee
+++ server/lib/utils.coffee
@@ -10,7 +10,7 @@

   getCodeCamel: (numWords=3) ->
     # 250 words
-    words = 'angry apple arm army art baby back bad bag ball bath bean bear bed bell best big bird bite blue boat book box boy bread burn bus cake car cat chair city class clock cloud coat coin cold cook cool corn crash cup dark day deep desk dish dog door down draw dream drink drop dry duck dust east eat egg enemy eye face false farm fast fear fight find fire flag floor fly food foot fork fox free fruit full fun funny game gate gift glass goat gold good green hair half hand happy heart heavy help hide hill home horse house ice idea iron jelly job jump key king lamp large last late lazy leaf left leg life light lion lock long luck map mean milk mix moon more most mouth music name neck net new next nice night north nose old only open page paint pan paper park party path pig pin pink place plane plant plate play point pool power pull push queen rain ready red rest rice ride right ring road rock room run sad safe salt same sand sell shake shape share sharp sheep shelf ship shirt shoe shop short show sick side silly sing sink sit size sky sleep slow small snow sock soft soup south space speed spell spoon star start step stone stop sweet swim sword table team thick thin thing think today tooth top town tree true turn type under want warm watch water west wide win word yes zoo'.split(' ')
+    words = 'angry apple arm army art baby back bad bag ball bath bean bear bed bell best big bird bite blue boat book box boy bread burn bus cake car cat chair city class clock cloud coat coin cold cook cool corn crash cup dark day deep desk dish dog door down draw dream drink drop dry duck dust east eat egg enemy eye face false farm fast fear fight find fire flag floor fly food foot fork fox free fruit full fun funny game gate gift glass goat gold good green hair half hand happy heart heavy help hide hill home horse house ice idea iron jelly job jump key king lamp large last late lazy leaf left leg life light lion lock long luck map mean milk mix moon more most mouth music name neck net new next nice night north nose old only open page paint pan paper park party path pig pin pink place plane plant plate play point pool power pull push rain ready red rest rice ride right ring road rock room run sad safe salt same sand sell shake shape share sharp sheep shelf ship shirt shoe shop short show sick side silly sing sink sit size sky sleep slow small snow sock soft soup south space speed spell spoon star start step stone stop sweet swim sword table team thick thin thing think today tooth top town tree true turn type under want warm watch water west wide win word yes zap zoo'.split(' ')
     _.map(_.sample(words, numWords), (s) -> s[0].toUpperCase() + s.slice(1)).join('')

   objectIdFromTimestamp: (timestamp) ->


Adjust specs to changed parameter names.

--- src/spec/elasticio.spec.coffee
+++ src/spec/elasticio.spec.coffee
@@ -5,9 +5,9 @@
 describe "elasticio file integration", ->
   it "with no attachments nor body", (done) ->
     cfg =
-      clientId: 'some'
-      clientSecret: 'stuff'
-      projectKey: 'here'
+      sphereClientId: 'some'
+      sphereClientSecret: 'stuff'
+      sphereProjectKey: 'here'
     msg = ''
     elasticio.process msg, cfg, (next) ->
       expect(next.status).toBe false
@@ -16,9 +16,9 @@

   it "single attachment - 2 entries", (done) ->
     cfg =
-      clientId: Config.config.client_id
-      clientSecret: Config.config.client_secret
-      projectKey: Config.config.project_key
+      sphereClientId: Config.config.client_id
+      sphereClientSecret: Config.config.client_secret
+      sphereProjectKey: Config.config.project_key
     xml = '
 <row>
   abc
@@ -44,9 +44,9 @@
 describe "elasticio mapping integration", ->
   it "single entry", (done) ->
     cfg =
-      clientId: Config.config.client_id
-      clientSecret: Config.config.client_secret
-      projectKey: Config.config.project_key
+      sphereClientId: Config.config.client_id
+      sphereClientSecret: Config.config.client_secret
+      sphereProjectKey: Config.config.project_key

     msg =
       body:


easier random password

easier random password

--- models/models.coffee
+++ models/models.coffee
@@ -82,7 +82,7 @@
     alphabet: 'abcdefghijklmnopqrstuvwxyz'.split('')
     vowels: 'aeiou'.split('')
     consonants: _.without alphabet, vowels...
-    syllables: for i in [0..3]
+    syllables: for i in [0..2]
       consonants[Math.floor consonants.length * Math.random()] +
       vowels[Math.floor vowels.length * Math.random()] +
       alphabet[Math.floor alphabet.length * Math.random()]