support for collateral_symbol parameter in wallet_market_submit_short

--- app/js/services/market_service.coffee
+++ app/js/services/market_service.coffee
@@ -302,8 +302,8 @@
             price_limit = 1.0 / short.short_price_limit if short.short_price_limit > 0.0
         else
             price_limit = short.short_price_limit
-        #console.log "---- before market_submit_short ----", account.name, short.quantity, actual_market.asset_base_symbol, short.collateral_ratio, price_limit
-        call = @wallet_api.market_submit_short(account.name, short.quantity, actual_market.asset_base_symbol, short.collateral_ratio, price_limit)
+        #console.log "---- before market_submit_short ----", account.name, short.quantity, actual_market.asset_base_symbol, short.collateral_ratio, price_limit, actual_market.asset_quantity_symbol
+        call = @wallet_api.market_submit_short(account.name, short.quantity, actual_market.asset_base_symbol, short.collateral_ratio, actual_market.asset_quantity_symbol, price_limit)
         return call

     post_ask: (ask, account, deferred) ->


Allow achievement query editing in achievement editor

--- app/schemas/models/achievement.coffee
+++ app/schemas/models/achievement.coffee
@@ -26,6 +26,7 @@
         {type: 'object'}
         {type: 'boolean'}
       ]
+  properties: {}
   additionalProperties: false
   definitions: {}


Minor fix.

Minor fix.

--- app/scripts/controllers/new_vm.coffee
+++ app/scripts/controllers/new_vm.coffee
@@ -173,7 +173,7 @@
           repository: installation_network
         }
       else
-        installation = null
+        installation = undefined

       data = {
         installation


Less moment.js?

Less moment.js?

--- resources/assets/coffee/react/_components/beatmap-basic-stats.coffee
+++ resources/assets/coffee/react/_components/beatmap-basic-stats.coffee
@@ -21,15 +21,14 @@

 # value is in second
 formatDuration = (value) ->
-  duration = moment(value * 1000).utcOffset(0)
+  s = value % 60
+  m = Math.floor(value / 60) % 60
+  h = Math.floor(value / 3600)

-  format =
-    if duration.hours() > 0
-      'h:mm:ss'
-    else
-      'm:ss'
-
-  duration.format format
+  if h > 0
+    "#{h}:#{_.padStart m, 2, 0}:#{_.padStart s, 2, 0}"
+  else
+    "#{m}:#{_.padStart s, 2, 0}"

 @BeatmapBasicStats = ({beatmapset, beatmap}) ->


Use _.defaults to simplify assigning default features

--- services/web/app/coffee/Features/Project/ProjectEditorHandler.coffee
+++ services/web/app/coffee/Features/Project/ProjectEditorHandler.coffee
@@ -15,15 +15,6 @@
 			deletedByExternalDataSource : project.deletedByExternalDataSource || false
 			deletedDocs: project.deletedDocs
 			members:     []
-
-		result.features = # defaults
-			collaborators: -1 # Infinite
-			versioning: false
-			dropbox:false
-			compileTimeout: 60
-			compileGroup:"standard"
-			templates: false
-			references: false

 		owner = null
 		for member in members
@@ -34,21 +25,15 @@
 		if owner?
 			result.owner = @buildUserModelView owner, "owner"

-		if owner?.features?
-			if owner.features.collaborators?
-				result.features.collaborators = owner.features.collaborators
-			if owner.features.versioning?
-				result.features.versioning = owner.features.versioning
-			if owner.features.dropbox?
-				result.features.dropbox = owner.features.dropbox
-			if owner.features.compileTimeout?
-				result.features.compileTimeout = owner.features.compileTimeout
-			if owner.features.compileGroup?
-				result.features.compileGroup = owner.features.compileGroup
-			if owner.features.templates?
-				result.features.templates = owner.features.templates
-			if owner.features.references?
-				result.features.references = owner.features.references
+		result.features = _.defaults(owner?.features or {}, {
+			collaborators: -1 # Infinite
+			versioning: false
+			dropbox:false
+			compileTimeout: 60
+			compileGroup:"standard"
+			templates: false
+			references: false
+		})

 		return result


Mix-in event broker to application.

Mix-in event broker to application.

--- src/chaplin/application.coffee
+++ src/chaplin/application.coffee
@@ -4,7 +4,8 @@
   'chaplin/dispatcher'
   'chaplin/views/layout'
   'chaplin/lib/router'
-], (Backbone, mediator, Dispatcher, Layout, Router) ->
+  'chaplin/lib/event_broker'
+], (Backbone, mediator, Dispatcher, Layout, Router, EventBroker) ->
   'use strict'

   # The application bootstrapper
@@ -14,6 +15,9 @@

     # Borrow the static extend method from Backbone
     @extend = Backbone.Model.extend
+
+    # Mixin an EventBroker
+    _(@prototype).extend EventBroker

     # The site title used in the document title
     title: ''


force return `null` (not just falsy) when currentUser not set.

--- src/client/current-user.coffee
+++ src/client/current-user.coffee
@@ -21,6 +21,8 @@
     @clear()

   get: (key) =>
+    # Ensure logged out user gives Null, not False
+    return null if !key and !@me
     getKey(@me, key)

   clear: ()=>


Fix allBuilds for ember-model

Fix allBuilds for ember-model

--- assets/scripts/app/models/repo.coffee
+++ assets/scripts/app/models/repo.coffee
@@ -23,8 +23,8 @@
   ).property('lastBuildId', 'lastBuildNumber')

   allBuilds: (->
-    recordArray = DS.RecordArray.create({ type: Travis.Build, content: Ember.A([]), store: Travis.store, isLoaded: true })
-    Travis.store.registerRecordArray(recordArray, Travis.Build)
+    recordArray = Ember.RecordArray.create({ modelClass: Travis.Build, content: Ember.A([]) })
+    Travis.Build.registerRecordArray(recordArray)
     recordArray
   ).property()


Remove loading icon.

Remove loading icon.

--- lms/static/coffee/src/discussion/views/discussion_thread_view.coffee
+++ lms/static/coffee/src/discussion/views/discussion_thread_view.coffee
@@ -29,6 +29,7 @@

   renderResponses: ->
     $.ajax @model.id, success: (data, textStatus, xhr) =>
+      @$(".loading").remove()
       comments = new Comments(data['content']['children'])
       comments.each @renderResponse


Debugging

Debugging

--- lib/arrivals.coffee
+++ lib/arrivals.coffee
@@ -13,8 +13,8 @@
   if not _.isArray res.data
     return console.error "Got invalid train data",res
   _.each res.data,(i)->
-    event_time = moment.tz(i.EVENT_TIME,"UTC").unix()
-    next_arr = moment.tz(i.NEXT_ARR,'hh:mm:ss A',"UTC").unix()
+    event_time = moment.tz(i.EVENT_TIME,"America/New_York").unix()
+    next_arr = moment.tz(i.NEXT_ARR,'hh:mm:ss A',"America/New_York").unix()
     console.log("This arrival is at:",next_arr,"compared to now:",moment().unix())
     Arrivals.update {
       train_id: i.TRAIN_ID


Remove unnecessary bluebird import

Remove unnecessary bluebird import

Signed-off-by: Marios Balamatsias <e5351d595bc582bda310ed831027b3dfc0f63346@gmail.com>

--- lib/commands.coffee
+++ lib/commands.coffee
@@ -78,8 +78,7 @@
 					toDefinition.image
 					toDefinition.partition
 					(_fs) ->
-						Bluebird = require('bluebird')
-						writeFileAsync = Bluebird.promisify(_fs.writeFile)
+						writeFileAsync = Promise.promisify(_fs.writeFile)
 						return writeFileAsync(toDefinition.path, content)
 				)


Using a static database for short term

Using a static database for short term

--- lib/content._coffee
+++ lib/content._coffee
@@ -10,6 +10,10 @@
 DIR_BY_ID_PATH = Path.join config.DATA_DIR, 'content-by-id'
 DIR_BY_URL_PATH = Path.join config.DATA_DIR, 'content-by-url'

+# TODO: this is a short term fix to support the content by url code-path
+# we should remove this when we move to a proper database
+DATABASE = require Path.join DIR_BY_URL_PATH, 'data.json'
+
 DIR_PATH_FROM_URL_TO_ID = Path.relative DIR_BY_URL_PATH, DIR_BY_ID_PATH

 # TODO: We should be generating random IDs instead of incrementing them.
@@ -37,7 +41,7 @@
     Path.join DIR_BY_ID_PATH, "#{id}.json"

 getFilePathForURL = (url) ->
-    Path.join DIR_BY_URL_PATH, "#{hashURL url}.json"
+    Path.join DIR_BY_ID_PATH, DATABASE["#{hashURL url}.json"].replace "../content-by-id", ""

 getRedisKeyForId = (id) ->
     "content:id:#{id}"


css adjustment

css adjustment

--- lib/eslogger.coffee
+++ lib/eslogger.coffee
@@ -359,7 +359,7 @@
         h1      { margin: 8px 0; padding: 0; }
         p       { font-family: monospace; border-bottom: 1px solid #eee; padding: 2px 0; margin: 0; color: #111; }
         p span  { width: 120px; display: inline-block; text-align: right; font-weight: bold; }
-        .search p span  { width: 200px; display: inline-block; text-align: right; font-weight: bold; }
+        .search p span  { width: 160px; display: inline-block; text-align: right; font-weight: bold; }
         p > i   { color: #666; }
         p:hover { color: #000; background-color: #fff; }
         a       { text-decoration: none; color: #249; }


fix jumpy to-bottom transition

fix jumpy to-bottom transition

--- lib/assets/javascripts/up/motion.js.coffee
+++ lib/assets/javascripts/up/motion.js.coffee
@@ -122,59 +122,77 @@

   animation('none', none)

-  animation('fade-in', ($element, options) ->
-    $element.css(opacity: 0)
-    $element.animate({ opacity: 1 }, options)
-    $element.promise()
-  )
-
-  animation('fade-out', ($element, options) ->
-    $element.css(opacity: 1)
-    $element.animate({ opacity: 0 }, options)
-    $element.promise()
-  )
-
-  animation('move-to-bottom', ($element, options) ->
-    $element.css(top: '0%')
-    $element.animate({ top: '100%' }, options)
-    $element.promise()
-  )
-
-  animation('move-to-left', ($element, options) ->
-    $element.css(left: '0%')
-    $element.animate({ left: '-100%' }, options)
-    $element.promise()
-  )
-
-  animation('move-from-left', ($element, options) ->
-    $element.css(left: '-100%')
-    $element.animate({ left: '0%' }, options)
-    $element.promise()
-  )
-
-  animation('move-to-right', ($element, options) ->
-    $element.css(left: '0%')
-    $element.animate({ left: '100%' }, options)
-    $element.promise()
-  )
-
-  animation('move-from-right', ($element, options) ->
-    $element.css(left: '100%')
-    $element.animate({ left: '0%' }, options)
-    $element.promise()
-  )
-
-  animation('roll-down', ($element, options) ->
-    fullHeight = $element.height()
+  animation('fade-in', ($ghost, options) ->
+    $ghost.css(opacity: 0)
+    $ghost.animate({ opacity: 1 }, options)
+    $ghost.promise()
+  )
+
+  animation('fade-out', ($ghost, options) ->
+    $ghost.css(opacity: 1)
+    $ghost.animate({ opacity: 0 }, options)
+    $ghost.promise()
+  )
+
+  animation('move-to-top', ($ghost, options) ->
+    $ghost.css('margin-top': '0%')
+    $ghost.animate({ 'margin-top': '-100%' }, options)
+    $ghost.promise()
+  )
+
+  animation('move-from-top', ($ghost, options) ->
+    $ghost.css('margin-top': '-100%')
+    $ghost.animate({ 'margin-top': '0%' }, options)
+    $ghost.promise()
+  )
+
+  animation('move-to-bottom', ($ghost, options) ->
+    $ghost.css('margin-top': '0%')
+    $ghost.animate({ 'margin-top': '100%' }, options)
+    $ghost.promise()
+  )
+
+  animation('move-from-bottom', ($ghost, options) ->
+    $ghost.css('margin-top': '100%')
+    $ghost.animate({ 'margin-top': '0%' }, options)
+    $ghost.promise()
+  )
+
+  animation('move-to-left', ($ghost, options) ->
+    $ghost.css('margin-left': '0%')
+    $ghost.animate({ 'margin-left': '-100%' }, options)
+    $ghost.promise()
+  )
+
+  animation('move-from-left', ($ghost, options) ->
+    $ghost.css('margin-left': '-100%')
+    $ghost.animate({ 'margin-left': '0%' }, options)
+    $ghost.promise()
+  )
+
+  animation('move-to-right', ($ghost, options) ->
+    $ghost.css('margin-left': '0%')
+    $ghost.animate({ 'margin-left': '100%' }, options)
+    $ghost.promise()
+  )
+
+  animation('move-from-right', ($ghost, options) ->
+    $ghost.css('margin-left': '100%')
+    $ghost.animate({ 'margin-left': '0%' }, options)
+    $ghost.promise()
+  )
+
+  animation('roll-down', ($ghost, options) ->
+    fullHeight = $ghost.height()
     oldStyle =
-      height: $element.css('height')
-      overflow: $element.css('overflow')
-    $element.css(
+      height: $ghost.css('height')
+      overflow: $ghost.css('overflow')
+    $ghost.css(
       height: '0px'
       overflow: 'hidden'
     )
-    $element.animate({ height: "#{fullHeight}px" }, options)
-    $element.promise().then(-> $element.css(oldStyle))
+    $ghost.animate({ height: "#{fullHeight}px" }, options)
+    $ghost.promise().then(-> $ghost.css(oldStyle))
   )

   transition('none', none)


be explicit with ext

be explicit with ext

--- lib/monet_web/assets/js/application.coffee
+++ lib/monet_web/assets/js/application.coffee
@@ -1,5 +1,5 @@
-#= require jquery
-#= require bootstrap
+#= require jquery.js
+#= require bootstrap.js

 DEFAULT_OPTIONS = {
   placement: 'bottom'


require objectId from bongo

require objectId from bongo

--- workers/social/testhelper/index.coffee
+++ workers/social/testhelper/index.coffee
@@ -4,10 +4,9 @@
 JAccount                = require '../lib/social/models/account'
 JSession                = require '../lib/social/models/session'
 Bongo                   = require 'bongo'
-ObjectId                = require('mongodb').BSONPure.ObjectID

 { expect }              = require 'chai'
-{ daisy }               = Bongo
+{ daisy, ObjectId }     = Bongo
 { argv }                = require 'optimist'
 { env : { MONGO_URL } } = process
 KONFIG                  = require('koding-config-manager').load("main.#{argv.c}")


:bug: remaining old name

:bug: remaining old name

--- lib/operator.coffee
+++ lib/operator.coffee
@@ -334,14 +334,15 @@
   acceptPresetOccurrence: false

   execute: ->
-    if marker = @vimState.occurrence.getMarkerAtPoint(@editor.getCursorBufferPosition())
+    {@occurrenceManager} = @vimState
+    if marker = @occurrenceManager.getMarkerAtPoint(@editor.getCursorBufferPosition())
       marker.destroy()
     else
       if @isMode('visual') and text = @editor.getSelectedText()
         pattern = new RegExp(_.escapeRegExp(text), 'g')
       pattern ?= getWordPatternAtCursor(@editor.getLastCursor(), singleNonWordChar: true)

-      @vimState.occurrence.addPattern(pattern)
+      @occurrenceManager.addPattern(pattern)
       @activateMode('normal')

 # Delete


:bug: incorrect parent

:bug: incorrect parent

--- lib/operator.coffee
+++ lib/operator.coffee
@@ -1182,6 +1182,7 @@
     @restorePoint(selection)

 class ToggleRangeMarker extends CreateRangeMarker
+  @extend()
   getRangeMarkerAtCursor: ->
     point = @editor.getCursorBufferPosition()

@@ -1199,7 +1200,7 @@
       @vimState.removeRangeMarker(rangeMarkerToRemove)
       @abort()

-class ToggleRangeMarkerAtCursor extends CreateRangeMarker
+class ToggleRangeMarkerAtCursor extends ToggleRangeMarker
   @extend()
   target: 'InnerWord'


Fix invalid copy in config

Fix invalid copy in config

--- lib/provider.coffee
+++ lib/provider.coffee
@@ -3,18 +3,18 @@
   id: 'aligner-python' # package name
   config:
     ':-alignment':
-      title: 'Padding for =>'
+      title: 'Padding for :'
       description: 'Pad left or right of the character'
       type: 'string'
       enum: ['left', 'right']
       default: 'right'
     ':-leftSpace':
-      title: 'Left space for =>'
+      title: 'Left space for :'
       description: 'Add 1 whitespace to the left'
       type: 'boolean'
       default: false
     ':-rightSpace':
-      title: 'Right space for =>'
+      title: 'Right space for :'
       description: 'Add 1 whitespace to the right'
       type: 'boolean'
       default: true


Use @getEditor method to get editor instance

--- lib/searcher.coffee
+++ lib/searcher.coffee
@@ -16,12 +16,13 @@
     @isearchKeymaps?.dispose()

   incrementSearch: (c)->
-    if @isearchTile
+    editor = @getEditor()
+    if (atom.views.getView editor).classList.contains "searching"
       @isearchWord += c
       @searchNext @isearchWord

   activateIsearch: (forward)->
-    editor = atom.workspace.getActiveTextEditor()
+    editor = @getEditor()
     (atom.views.getView editor).classList.add "searching"
     @isforward = forward
     @isearchWord = @isearchLast if @isearchTile? and not @isearchWord
@@ -33,7 +34,7 @@
       @isearchTile = null
       @isearchLast = @isearchWord
       @isearchWord = ""
-      editor = atom.workspace.getActiveTextEditor()
+      editor = @getEditor()
       (atom.views.getView editor).classList.remove "searching"
       return true


better name for lastHeartbeat variable

better name for lastHeartbeat variable

--- lib/wakatime.coffee
+++ lib/wakatime.coffee
@@ -28,11 +28,11 @@
         setupEventHandlers()
         console.log 'WakaTime v'+VERSION+' loaded.'

-lastAction = 0
+lastHeartbeat = 0
 lastFile = ''

 enoughTimePassed = (time) ->
-    return lastAction + 120000 < time
+    return lastHeartbeat + 120000 < time

 setupConfig = () ->
     unless atom.config.get("wakatime.apikey")?
@@ -202,7 +202,7 @@
                     # else
                     #     console.log(args)
                 )
-                lastAction = time
+                lastHeartbeat = time
                 lastFile = file.path
         )


handle exceptions when binding to events in new editor

--- lib/wakatime.coffee
+++ lib/wakatime.coffee
@@ -25,21 +25,22 @@

 setupEventHandlers = () ->
     atom.workspace.eachEditor (editor) =>
-        buffer = editor.getBuffer()
-        buffer.on 'saved', (e) =>
-            file = e.file
-            time = Date.now()
-            sendHeartbeat(file, time, true)
-        buffer.on 'changed', (e) =>
-            item = atom.workspaceView.getActivePaneItem()
-            if item?
-                buffer = item.getBuffer()
-                if buffer? and buffer.file?
-                    file = buffer.file
-                    if file
-                        time = Date.now()
-                        if enoughTimePassed(time) or lastFile isnt file.path
-                            sendHeartbeat(file, time)
+        try
+            buffer = editor.getBuffer()
+            buffer.on 'saved', (e) =>
+                file = e.file
+                time = Date.now()
+                sendHeartbeat(file, time, true)
+            buffer.on 'changed', (e) =>
+                item = atom.workspaceView.getActivePaneItem()
+                if item?
+                    buffer = item.getBuffer()
+                    if buffer? and buffer.file?
+                        file = buffer.file
+                        if file
+                            time = Date.now()
+                            if enoughTimePassed(time) or lastFile isnt file.path
+                                sendHeartbeat(file, time)

 sendHeartbeat = (file, time, isWrite) ->
     if file.path is undefined or fileIsIgnored(file.path)


check err on _putElement for file in project

--- services/web/app/coffee/Features/Project/ProjectEntityHandler.coffee
+++ services/web/app/coffee/Features/Project/ProjectEntityHandler.coffee
@@ -165,7 +165,10 @@
 					logger.err err:err, project_id: project._id, folder_id: folder_id, file_name: fileName, fileRef:fileRef, "error uploading image to s3"
 					return callback(err)
 				ProjectEntityHandler._putElement project, folder_id, fileRef, "file", (err, result)=>
-					tpdsUpdateSender.addFile {project_id:project._id, file_id:fileRef._id, path:result.path.fileSystem, project_name:project.name, rev:fileRef.rev}, ->
+					if err?
+						logger.err err:err, project_id: project._id, folder_id: folder_id, file_name: fileName, fileRef:fileRef, "error adding file with project"
+						return callback(err)
+					tpdsUpdateSender.addFile {project_id:project._id, file_id:fileRef._id, path:result?.path?.fileSystem, project_name:project.name, rev:fileRef.rev}, ->
 						callback(err, fileRef, folder_id)

 	replaceFile: (project_id, file_id, fsPath, callback)->


When submitting a task, if the arguments box is empty ('') send [] rather than [''] as arguments

--- SingularityUI/app/models/Request.coffee
+++ SingularityUI/app/models/Request.coffee
@@ -66,8 +66,11 @@
             contentType: 'application/json'

         if typeof confirmedOrPromptData is 'string'
+          if confirmedOrPromptData != ''
             options.data = JSON.stringify([confirmedOrPromptData])
-            options.processData = false
+          else
+            options.data = '[]'
+          options.processData = false

         $.ajax options


fix validation of filename input

fix validation of filename input

--- SingularityUI/app/models/Request.coffee
+++ SingularityUI/app/models/Request.coffee
@@ -451,7 +451,7 @@
                 fileName = @data.filename.trim()
                 message = @data.message

-                if fileName and fileName.length is 0 and @data.autoTail is 'on'
+                if ((fileName and fileName.length is 0) or not filename) and @data.autoTail is 'on'
                     $(window.noFilenameError).removeClass('hide')
                     return false


Change if condition to eliminate 'notes' bug

If an event has content in the 'notes' section, the widget tries to parse that content because it contains a ':'. I've replaced that with a RegEx to match 12hr Date Strings.

--- Upcoming-iCal-Events.widget/main.coffee
+++ Upcoming-iCal-Events.widget/main.coffee
@@ -87,7 +87,7 @@
             if ( name.length > 25 )
                 name = name.substr(0,25) + "..."
             # Trim date and format output
-            if ( ':' in newarray[i+1] )
+            if (/(((0[1-9])|(1[0-2])):([0-5])(0|5)\s(A|P)M)/.test(newarray[i+1]))
                 date = ((newarray[i+1].split("at"))[1])
                 date = "at" + date.substr(0,9)
             # If its an allday event, date field is empty


update initial render, fix duplicate issues

--- Upcoming-iCal-Events.widget/main.coffee
+++ Upcoming-iCal-Events.widget/main.coffee
@@ -1,5 +1,5 @@
 # Author: Connor Beardsmore <connor.beardsmore@gmail.com>
-# Last Modified: 23/06/16
+# Last Modified: 22/03/18

 # Bash command to pull events from icalBuddy
 # Set +2 to how many days you want to show
@@ -41,7 +41,6 @@

 # Initial render for heading
 render: (output) -> """
-
 Upcoming Events

 """

 # Update when refresh occurs
@@ -49,6 +48,9 @@
     lines = output.split('\n')
     bullet = lines[0][0]
     dom = $(domEl)
+
+    dom.empty()
+    dom.append("""
 Upcoming Events
""")

     # Show which calendar you pulled from before event name
     SHOW_CALENDER = false
@@ -113,6 +115,5 @@
             if (SHOW_DATE_TIME)
                 final += date

-            # Add this HTML to previous, only if it doesn't already exist
-            if (!dom.text().includes(final))
-                dom.append("""
#{final}
""")+            # Add this HTML to previous
+            dom.append("""
#{final}
""")


stash

stash

--- admin/static/coffee/dataexplorer.coffee
+++ admin/static/coffee/dataexplorer.coffee
@@ -1,3 +1,14 @@
+# no element found when closing connection
+
+# split query result into:
+# QueryResult/CursorResult/ValueResult/PositionedResult
+
+# switching back to the dataexplorer shows the "enable the profiler" message instead of the previous query's profile
+# the load next batch button flashes a message I can't read and switches to the dashboard
+# server errors show as "[object Object]"
+
+# container limit -> global constant
+
 # ATN: remove jquery migration:
 #   $.browser
 # ATN: change query nomenclature:
@@ -28,7 +39,6 @@
 # returned/displayed/skipped has extra comma and is generally ugly
 # removing id_connection may have introduced bugs
 # round-trip time for cursors is inconsistent
-# get rid of current_results and allow looking at previous results
 # cursor_timed_out is only called on connection errors

 # save_query gets called too often with no effect -> symptom that it is
@@ -45,7 +55,7 @@
         current_query: null
         query_result: null
         cursor_timed_out: true
-        view: 'profile' # ATN 'tree'
+        view: 'raw' # ATN 'tree'
         history_state: 'hidden'
         last_keys: []
         last_columns_size: {}
@@ -92,39 +102,21 @@
                     @profile = result.profile
                     value = result.value
                 else
-                    value = result
+                    @profile = null
+                    value = result
                 if typeof value._next is 'function' # if it's a cursor
+                    @type = 'cursor'
                     @results = []
                     @cursor = value
                     @is_feed = @cursor.toString() == '[object Feed]'
                     @missing = 0
                     @ended = false
-                    @start_index = 0
                 else
-                    @start_index = 0
+                    @type = 'value'
                     @value = value
                     @ended = true
                 @ready = true
                 @trigger 'ready', @
-
-        # Remove n elements from the result set of a cursor Waits for
-        # n+1 elements, or the end before returning
-        #
-        # Note: the callbacks are not ordered. In this code, `f` is
-        # never called: `qr.shift 2 f; cb = -> qr.shift 1 cb`
-        shift: (n, k) =>
-            cb = =>
-                if @results.length > n or @ended
-                    @off 'end', cb
-                    ret = @results[0...n]
-                    @results = @results[n...]
-                    @start_index += n
-                    k @, ret
-                else
-                    @once 'add', cb
-                    @more()
-            @once 'end', cb
-            cb()

         # Discard the results
         discard: =>
@@ -136,13 +128,6 @@
             delete @results
             @cursor?.close()
             delete @cursor
-
-        # Ask for more results from the cursor
-        more: (n) =>
-            prev_missing = @missing
-            @missing = prev_missing + (n ? 1)
-            if prev_missing <= 0
-                @fetch_next()

         # Gets the next result from the cursor
         fetch_next: =>
@@ -159,17 +144,15 @@
                                 return
                             @results.push row
                             @trigger 'add', @, row
-                            @missing--
-                            if @missing >= 0
-                                @fetch_next()
                 catch error
                     @set_error error

         set_error: (error) =>
+            @type = 'error'
             @error = error
             @trigger 'error', @, error
             @discard_results = true
-            @ended = true
+            @ended = true

     class @Container extends Backbone.View
         id: 'dataexplorer'
@@ -668,7 +651,8 @@
             $(window).mousedown @handle_mousedown
             @keep_suggestions_on_blur = false

-            @render()
+            # ATN: this seems unecessary: render is called later
+            # @render()

             @databases_available = {}
             @fetch_data()
@@ -2543,7 +2527,6 @@
                     }

         abort_query: =>
-            @state.query_result?.discard()
             @driver_handler.close_connection()

         # Function that execute the queries in a synchronous way.
@@ -2638,7 +2621,7 @@
                             raw_query: @raw_queries[@index]
                             driver_handler: @driver_handler
                             events:
-                                error: (err) =>
+                                error: (query_result, err) =>
                                     @results_view_wrapper.render_error(@query, err)

                         @state.query_result = query_result
@@ -2884,9 +2867,13 @@
             @query_result = args.query_result
             if @query_events?
                 for own event, handler of @query_events()
-                    @query_result.on event, handler
-
-        current_result: []
+                    if event is 'ready'
+                        handler @query_result
+                    else
+                        @listenTo @query_result, event, handler
+            @fetch_batch_rows()
+
+
         max_datum_threshold: 1000

         # Return whether there are too many datums
@@ -2944,6 +2931,27 @@
             $('.'+classname_to_change).css 'max-width', 'none'
             dom_element.css 'max-width', 'none'
             @parent.set_scrollbar()
+
+        current_batch: =>
+            switch @query_result.type
+                when 'value'
+                    return @query_result.value
+                when 'cursor'
+                    return @query_result.results[@query_result.position .. @query_result.position + @parent.container.limit]
+
+        fetch_batch_rows:  =>
+            if @query_result.results?.length - @query_result.position < @parent.container.limit
+                @query_result.once 'add', (query_result, row) =>
+                    @add_row row
+                    @fetch_batch_rows()
+
+        show_next_batch: =>
+            @query_result.position += @parent.container.limit
+            @fetch_batch_rows()
+            @render()
+
+        add_row: =>
+            throw "Unimplemented add_row for #{@.constructor.name}"

     class TreeView extends ResultView
         className: 'results tree_view_container'
@@ -3308,12 +3316,17 @@
                             @$('.value-'+expandable_columns[0]['col']).css 'max-width', current_size+max_size-20
                         expandable_columns = []

-            @
+            return @

     class RawView extends ResultView
         className: 'results raw_view_container'

         template: Handlebars.templates['dataexplorer_result_raw-template']
+
+        initialize: (args...) =>
+            super args...
+            @render()
+            @fetch_batch_rows (row) => @render()

         init_after_dom_rendered: =>
             height = @$('.raw_view_textarea')[0].scrollHeight
@@ -3321,8 +3334,11 @@
                 @$('.raw_view_textarea').height(height)

         render: =>
-            @$el.html @template JSON.stringify(@parent.results)
-            @
+            @$el.html @template JSON.stringify @current_batch()
+            return @
+
+        add_row: (row) =>
+            @render()

     class ProfileView extends ResultView
         className: 'results profile_view_container'
@@ -3402,6 +3418,7 @@
             'click .link_to_table_view': 'show_table'
             'click .link_to_raw_view': 'show_raw'
             'click .activate_profiler': 'activate_profiler'
+            'click .more_results_link': 'show_next_batch'

         initialize: (args) =>
             @container = args.container
@@ -3501,7 +3518,8 @@

         render_error: (query, err, js_error) =>
             @view_object?.remove()
-            @view_object = undefined
+            @view_object = null
+            @query_result?.discard()
             @$el.html @error_template
                 query: query
                 error: err.toString().replace(/^(\s*)/, '')
@@ -3552,7 +3570,6 @@
             @$('.tab-content').html @view_object.render().$el
             @init_after_dom_rendered()
             @set_scrollbar()
-            # ATN @delegateEvents() ?

         # ATN: conitnue ripping this into pieces
         render_result: (args) =>
@@ -3580,6 +3597,9 @@

         handle_mousemove: (event) =>
             @view_object?.handle_mousedown?(event)
+
+        show_next_batch: (event) =>
+            @view_object?.show_next_batch()

     class OptionsView extends Backbone.View
         dataexplorer_options_template: Handlebars.templates['dataexplorer-options-template']


Fix for when a user is redirecting port 80 to rethinkdb's http port. Related to #63

--- admin/static/coffee/dataexplorer.coffee
+++ admin/static/coffee/dataexplorer.coffee
@@ -883,7 +883,7 @@
         connect: (data) =>
             server =
                 host: window.location.hostname
-                port: parseInt window.location.port
+                port: if window.location.port is '' then 80 else parseInt window.location.port

             that = @
             if data? and data.reconnecting is true


update to use the changed id #hint to #hint_define

--- cf/spell.coffee
+++ cf/spell.coffee
@@ -26,7 +26,7 @@
   update_question = (data) ->
     word = random_question(data)
     update_keyHandler()
-    $("p#hint").empty().append(word['hint'])
+    $("p#hint_define").empty().append(word['hint_define'])

   update_keyHandler = () ->


Removing borders of accumulative and open/close graphs

--- app/coffee/directives/graphs.coffee
+++ app/coffee/directives/graphs.coffee
@@ -212,6 +212,9 @@
                     fit: true
             })
         options =
+            grid:
+                borderWidth: { top: 0, right: 1, left:0, bottom: 0 }
+                borderColor: '#ccc'
             legend:
                 position: "nw"
             xaxis:
@@ -293,6 +296,8 @@
             }
         ]
         options =
+            grid:
+                borderWidth: 0
             xaxis:
                 tickSize: [1, "day"],
                 min: today().subtract('days', 27),


update.

update.

--- src/main/public/nz/SpriteCharacter.coffee
+++ src/main/public/nz/SpriteCharacter.coffee
@@ -303,7 +303,8 @@

     eh.addBallet(info)

-  _eventDispatch: ->
+  _damage: (d)->
+    @character.hp -= d
     eh = @getRoot().eventHandler
     eh.refreshStatus()
     if @character.hp <= 0
@@ -311,13 +312,9 @@
     return

   _hitBallet: (shooter,ballet) ->
-    console.log "hit ballet #{@character.name}"
-    @character.hp -= @character.armor.defense - attacker.shot.damage
-    @_eventDispatch()
+    @_damage(@character.armor.defense - shooter.shot.damage)
     return

   _hitWeapon: (attacker) ->
-    console.log "hit weapon #{@character.name}"
-    @character.hp -= @character.armor.defense - attacker.weapon.damage
-    @_eventDispatch()
-    return
+    @_damage(@character.armor.defense - attacker.weapon.damage)
+    return


activity: add sidebar item when machines rendered

--- client/Social/Activity/AppView.coffee
+++ client/Social/Activity/AppView.coffee
@@ -103,8 +103,10 @@
         if err then router.handleNotFound router.getCurrentPath()
         else
           # put after #koding #changelog
-          @sidebar.addItem data, 2
-          kallback data
+          unless KD.utils.isChannelCollaborative data
+            @sidebar.whenMachinesRendered().then =>
+              @sidebar.addItem data, 2
+              kallback data
     else
       kallback item.getData()


shortcuts: :horse:

shortcuts: :horse:

--- client/app/lib/shortcuts/index.coffee
+++ client/app/lib/shortcuts/index.coffee
@@ -13,19 +13,19 @@

 module.exports =

-# Manages keyboard shortcuts.
-#
-# Wraps over a _shortcuts_ instance by default (if not passed explicitly
-# within opts.shortcuts), and makes sure we are listening and dispatching
-# correct keyboard events depending on an application's config.
-#
-# This also exposes convenience proxy methods to the underlying _keyconfig_
-# instance, and persists the state of a keyconfig#Collection to the app storage.
-#
 class ShortcutsController extends events.EventEmitter

   klass = this

+  # Manages keyboard shortcuts.
+  #
+  # Wraps over a _shortcuts_ instance by default (if not passed explicitly
+  # within opts.shortcuts), and makes sure we are listening and dispatching
+  # correct keyboard events depending on an application's config.
+  #
+  # This also exposes convenience proxy methods to the underlying _keyconfig_
+  # instance, and persists the state of a keyconfig#Collection to the app storage.
+  #
   constructor: (opts={}) ->

     super()


Fixed: events couldn't be created by drag and drop

--- client/app/views/calendar_view.coffee
+++ client/app/views/calendar_view.coffee
@@ -173,19 +173,18 @@
             when 'agendaWeek' then 'calendarweek'

     onSelect: (startDate, endDate, jsEvent, view) =>
-        # In month view, default to 10:00 - 18:00 instead of fullday event.
+        # In month view, default to 10:00 - 11:00 instead of fullday event.
         if @view is 'month'
             # startDate and endDate are dates, we add time part to create an
             # ambiguous date string.

-            # endDate is the next day, it's the startDate date that we need.
-            endDate = startDate.format() + 'T10:00:00.000'
-            startDate = startDate.format() + 'T11:00:00.000'
-
+            # endDate has +1 day for an unknown reason
+            endDate.subtract 1, 'days'
+            startDate = startDate.format() + 'T10:00:00.000'
+            endDate = endDate.format() + 'T11:00:00.000'

         start = helpers.ambiguousToTimezoned startDate
         end = helpers.ambiguousToTimezoned endDate
-
         @showPopover
             type: 'event'
             start: start


Handle serialization in order to limit depth

Otherwise circular data structures cause infinite loop

--- client/lanes/models/Collection.coffee
+++ client/lanes/models/Collection.coffee
@@ -39,6 +39,15 @@
     clone: ->
         new @constructor( @invoke( 'clone' ), @options )

+    serialize: (options = {depth: 1}) ->
+        options.depth += 1
+        this.map (model) ->
+            if model.serialize
+                model.serialize(options)
+            else
+                out = _.extend({}, model)
+                delete out.collection
+                out
 }

 class ModelsCollection
@@ -186,10 +195,13 @@
         super

     _prepareModel: (attrs, options = {}) ->
-        _.extend(attrs, @associationFilter) if @associationFilter
+        if @associationFilter
+#            needsReset =  _.isEmpty(attrs)
+            _.extend(attrs, @associationFilter)
         model = super
         if @options.inverse_name
-            model.set(@options.inverse_name, this.parent)
+            model.set(@options.inverse_name, this.parent, options)
+#        model.changeMonitor.reset() if needsReset
         model

     fetch: (options) ->


do not sort episodes for playlist

do not sort episodes for playlist

the should come sorted from the backend

--- src/javascripts/podcast.coffee
+++ src/javascripts/podcast.coffee
@@ -19,7 +19,7 @@
   getEpisodes: () ->
     if @attributes.episodes? && @attributes.episodes.length
       deferred = $.Deferred()
-      @episodes = _.orderBy(@attributes.episodes, 'number', 'desc')
+      @episodes = @attributes.episodes
       deferred.resolve()
       deferred.promise()
     else if @feed?


Dropped extra whitespace

Dropped extra whitespace

--- src/observable/property.coffee
+++ src/observable/property.coffee
@@ -164,7 +164,7 @@
       @changeEvent().clearHandlers()
   observeAndFire: (handler) ->
     @observe(handler)
-    handler.call( @base, @value, @value, @key)
+    handler.call(@base, @value, @value, @key)
   observe: (handler) ->
     @changeEvent().addHandler(handler)
     @getValue() unless @sources?


Fixed Amazon referral code for mobile

Fixed Amazon referral code for mobile

--- app/assets/javascripts/mobile.js.coffee
+++ app/assets/javascripts/mobile.js.coffee
@@ -9,7 +9,6 @@

 #= require backbone/sugar
 #= require sugar
-#= require sugar/posts/amazon_associates
 #= require sugar/timestamps

 window.toggleNavigation = ->
@@ -139,5 +138,16 @@

       container.spoilerApplied = true

+  # Amazon Associates referral code
+  if referral_id = Sugar.Configuration.AmazonAssociatesId
+    $('.post .body a').each ->
+      link = this
+      if !$.data(link, 'amazon_associates_referral_id') && link.href.match /https?:\/\/([\w\d\-\.])*amazon\.com/
+        $.data(link, 'amazon_associates_referral_id', referral_id)
+        return if link.href.match /(\?|&)tag=/
+
+        link.href += if link.href.match(/\?/) then '&' else '?'
+        link.href += 'tag=' + referral_id
+
   resizeYoutube()
   Sugar.init()


allow not only ul but also ol

allow not only ul but also ol

--- app/assets/javascripts/task_list.coffee
+++ app/assets/javascripts/task_list.coffee
@@ -185,7 +185,7 @@
 updateTaskList = ($item) ->
   $container = $item.closest '.js-task-list-container'
   $field     = $container.find '.js-task-list-field:last'
-  index      = 1 + $container.find('> div > ul .task-list-item-checkbox, > div > blockquote > ul .task-list-item-checkbox').index($item)
+  index      = 1 + $container.find('> div > ul .task-list-item-checkbox, > div > blockquote > ul .task-list-item-checkbox, > div > ol .task-list-item-checkbox, > div > blockquote > ol .task-list-item-checkbox').index($item)
   checked    = $item.prop 'checked'

   event = $.Event 'tasklist:change'


Fix backlog stats unexpected behavior.

Fix backlog stats unexpected behavior.

--- app/coffee/controllers/taskboard.coffee
+++ app/coffee/controllers/taskboard.coffee
@@ -28,8 +28,11 @@

     calculateTotalPoints = (us) ->
         total = 0
-        for roleId, pointId of us.points
+
+        for role in $scope.constants.computableRolesList
+            pointId = us.points[role.id]
             total += $scope.constants.points[pointId].value
+
         return total

     formatUserStoryTasks = ->
@@ -83,14 +86,15 @@
                 us = $scope.userstories[usId]
                 completedPoints += calculateTotalPoints(us)

-        $scope.stats =
-            totalPoints: totalPoints
-            completedPoints: completedPoints.toFixed(0)
+        $scope.stats = {
+            totalPoints: totalPoints.toFixed(1)
+            completedPoints: completedPoints.toFixed(1)
             percentageCompletedPoints: ((completedPoints*100) / totalPoints).toFixed(1)
             totalUss: totalUss
             compledUss: compledUss.toFixed(0)
             totalTasks: totalTasks
             completedTasks: completedTasks
+        }

     loadTasks = ->


Use self for api call instead of js_env voodoo

this fixes invalid api requests when creating developer_keys

moral: always turn ids into strings before they reach js

https://instructure.slack.com/archives/eng/p1441219421001143

Fixes PLAT-1227

Test Plan:
make sure you can create dev keys on beta

Change-Id: I6cc49e105e509178c4544bdebbeda1d142d82416
Reviewed-on: https://gerrit.instructure.com/62385
Reviewed-by: Cody Cutrer <c4fe2b1d90ef8f2548c8aeabfa633434316f0305@instructure.com>
QA-Review: August Thornton <9adc7a1161ddf32ff608de792a7e50179545f026@instructure.com>
Tested-by: Jenkins
Product-Review: Brad Horrocks <7b5c5d1f2eba6c540b8e53f17d87a9bd7dbf604b@instructure.com>

--- app/coffeescripts/developer_keys.coffee
+++ app/coffeescripts/developer_keys.coffee
@@ -50,7 +50,7 @@
     return '/api/v1/developer_keys'

   accountEndpoint = ->
-    return "/api/v1/accounts/#{ ENV.DOMAIN_ROOT_ACCOUNT_ID }/developer_keys"
+    return "/api/v1/accounts/self/developer_keys"

   isAccountAdminLevel = ->
     return window.location.pathname.indexOf('/accounts') == 0


Fix execjs error when running the specs under latest build dependencies.

--- spec/javascripts/mercury/toolbar.button_spec.js.coffee
+++ spec/javascripts/mercury/toolbar.button_spec.js.coffee
@@ -3,7 +3,7 @@
   template 'mercury/toolbar.button.html'

   beforeEach ->
-    Mercury.displayRect = {0, 0, 500, 200}
+    Mercury.displayRect = {top: 0, left: 0, width: 500, height: 200}
     Mercury.Toolbar.Button.contexts.foo = -> true
     @region = {
       type: -> 'full'


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/active_bounds.coffee
+++ src/active_bounds.coffee
@@ -1,3 +1,9 @@
+Bounded module
+@name Bounded
+@module
+@constructor
+@param {Object} I Instance variables
+@param {Core} self Reference to including object
 ActiveBounds = (I={}, self) ->
   Object.reverseMerge I,
     activeBounds: Rectangle


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/active_bounds.coffee
+++ src/active_bounds.coffee
@@ -22,6 +22,7 @@
       height: 20
       activeBounds: Rectangle
         x: 30
+        y: 20

 ActiveBounds module


reduced complexity on provisioner.coffee

--- workers/social/lib/social/models/computeproviders/provisioner.coffee
+++ workers/social/lib/social/models/computeproviders/provisioner.coffee
@@ -240,6 +240,18 @@
       @update $set: { accessLevel }, callback

+  updateHelper =
+
+    updateKallback : (slug, callback) ->
+      return (err) ->
+        if slug and err and err.name is 'MongoError' and err.code is 11001
+          return callback new KodingError \
+            "Slug `#{slug}` in use, provide different one"
+
+        return callback err  if err?
+        callback null
+
+
   update$: permit

     advanced: [
@@ -269,14 +281,6 @@
         slug  = "#{delegate.profile.nickname}/#{slug}"
         fieldsToUpdate.slug = slug

-      @update $set : fieldsToUpdate, (err) ->
-
-        if slug and err and err.name is 'MongoError' and err.code is 11001
-          return callback new KodingError \
-            "Slug `#{slug}` in use, provide different one"
-
-        return callback err  if err?
-
-        callback null
-
-
+      @update $set : fieldsToUpdate, updateHelper.updateKallback slug, callback
+
+


JProvisioner: make slug checker a seperate method

--- workers/social/lib/social/models/computeproviders/provisioner.coffee
+++ workers/social/lib/social/models/computeproviders/provisioner.coffee
@@ -122,25 +122,34 @@
     return [err, content, contentSum]

-  checkData = (delegate, data, callback)->
-
-    {type, content, slug, label} = data
-
-    [err, content, contentSum] = checkContent type, content
-    if err? then return callback err
-
-    slug ?= (require 'hat') 32
-    slug  = "#{delegate.profile.nickname}/#{slug}"
+  checkSlug = (slug, callback)->

     JProvisioner.count { slug }, (err, res)->
       return callback err  if err?
       if not res? or res > 0
-        callback new KodingError "Slug `#{slug}` in use, provide different one"
-      else
-        callback null, {
-          type, slug, label, content, contentSum
-          originId : delegate.getId()
-        }
+      then callback new KodingError \
+        "Slug `#{slug}` in use, provide different one"
+      else callback null
+
+
+  checkData = (delegate, data, callback)->
+
+    {type, content, slug, label, accessLevel} = data
+
+    [err, content, contentSum] = checkContent type, content
+    if err? then return callback err
+
+    accessLevel ?= 'private'
+
+    slug ?= (require 'hat') 32
+    slug  = "#{delegate.profile.nickname}/#{slug}"
+
+    checkSlug slug, (err)->
+      return callback err  if err?
+      callback null, {
+        type, slug, label, content, contentSum
+        originId : delegate.getId(), accessLevel
+      }

   @create = permit 'create provisioner',


Use robot.respond instead of robot.hear.

Untested. Hopefully this'll work.

--- src/scripts/dimmerworld.coffee
+++ src/scripts/dimmerworld.coffee
@@ -18,6 +18,6 @@
 module.exports = (robot) ->
   d = new dimmer()

-  robot.hear /^dimmer: (.*)/i, (msg) ->
+  robot.respond /^dimmer/i, (msg) ->
     data = msg.match[1].trim()
     d.write(data, (d) => msg.send(d.message))


fix get servername

fix get servername

--- src/server/server_utils.coffee
+++ src/server/server_utils.coffee
@@ -1,5 +1,7 @@
 os = require('os')
 dns = require('dns')
+
+async = require('async')

 exports.getLocalHostName = (callback)->


fixed maximum call stack size exceeded in members tab of group dashboard (happens esp. with koding group)

--- client/landing/app/Applications/Groups.kdapplication/Views/groupsmemberpermissionsview.coffee
+++ client/landing/app/Applications/Groups.kdapplication/Views/groupsmemberpermissionsview.coffee
@@ -38,37 +38,40 @@
     groupData.fetchRoles (err, roles)=>
       return warn err if err
       list.getOptions().roles = roles
-      groupData.fetchUserRoles (err, userRoles)=>
-        return warn err if err
-        userRolesHash = {}
-        for userRole in userRoles
-          if not userRolesHash[userRole.targetId]
-            userRolesHash[userRole.targetId] = []
-          userRolesHash[userRole.targetId].push userRole.as
-
-        list.getOptions().userRoles = userRolesHash
-        callback()

   fetchSomeMembers:(selector={})->
     options =
       limit : 20
       sort  : { timestamp: -1 }
-    groupData = @getData()
-    groupData.fetchMembers selector, options, (err, members)=>
+    @getData().fetchMembers selector, options, (err, members)=>
       return warn err if err
       @loader.hide()
       @loaderText.hide()
       @listController.hideLazyLoader()
       if members.length > 0
-        @listController.instantiateListItems members
-        @timestamp = new Date members.last.timestamp_
-        log @timestamp
-        @emit 'teasersLoaded'
+        ids = (member._id for member in members)
+        @getData().fetchUserRoles ids, (err, userRoles)=>
+          return warn err if err
+          userRolesHash = {}
+          for userRole in userRoles
+            userRolesHash[userRole.targetId] ?= []
+            userRolesHash[userRole.targetId].push userRole.as
+
+          list = @listController.getListView()
+          list.getOptions().userRoles ?= []
+          list.getOptions().userRoles = _.extend(
+            list.getOptions().userRoles, userRolesHash
+          )
+
+          @listController.instantiateListItems members
+          @timestamp = new Date members.last.timestamp_
+          @emit 'teasersLoaded'

   refresh:->
     @listController.removeAllItems()
     @timestamp = new Date 0
-    @fetchRoles @bound 'fetchSomeMembers'
+    @fetchRoles()
+    @fetchSomeMembers()

   continueLoadingTeasers:->
     @fetchSomeMembers {timestamp: $lt: @timestamp.getTime()}


Auth popup tells main window when it's closed

--- src/app/common/elements/header/HeaderController.coffee
+++ src/app/common/elements/header/HeaderController.coffee
@@ -1,3 +1,6 @@
+_popup = undefined
+_id = undefined
+
 HeaderCtrl = ($window, $mdSidenav, $mdMedia, UserService, LogoutService) ->
 	"use strict"
 	self = this
@@ -5,13 +8,35 @@
 	self.$mdMedia = $mdMedia
 	self.user = UserService
 	self.logout = LogoutService.logout
-	self.login = () ->
+	self.login = ->
+		if _popup and !_popup.closed then _popup.close()
+		if _id then $window.clearInterval _id
 		url = "/api/login"
 		name = "_blank"
-		specs = "height=400,menubar=no,scrollbars=no,statusbar=no,titlebar=no"
-		specs += "toolbar=no,width=300"
-		$window.open url, name, specs
-		return true
+		json =
+			height: 400
+			width: 300
+			menubar: "no"
+			scrollbars: "no"
+			statusbar: "no"
+			titlebar: "no"
+			toolbar: "no"
+		specs = ""
+		specs += "#{k}=#{v}," for own k, v of json
+		_popup = $window.open url, name, specs
+		onClose = ->
+			if _popup.closed
+				alert "Closed"
+				### TODO
+				Last check. Get identity of token. Confirm identity to enable token.
+				If you GET /api/identity fails because 401, then authentication failed.
+				Give toast. If it succeeds, ask the user if they want to confirm the
+				identity of the ton account that is about to be authorized. If they
+				confirm, mark the token in the db as confirmed. If they deny, delete
+				the token.
+				###
+				$window.clearInterval _id
+		_id = $window.setInterval onClose, 500
 	self

 HeaderCtrl.$inject = [


adapted toast messages

adapted toast messages

--- app/assets/javascripts/admin/views/dataset/dataset_upload_view.coffee
+++ app/assets/javascripts/admin/views/dataset/dataset_upload_view.coffee
@@ -112,13 +112,11 @@
         formData : new FormData(form)
         dataType : "json"
       ).then(
-        =>
-          Toast.message("Success")
-        (xhr) =>
-          Toast.message(xhr.response.messages)
+        ->
+          Toast.success()
+        (xhr) ->
+          Toast.error(xhr.response.messages)
       ).always(
         =>
           @ui.spinner.addClass("hidden")
       )
-
-


remove getDeleteButtonPosition

remove getDeleteButtonPosition

--- app/assets/javascripts/components/mark/tools/text-row-tool/index.cjsx
+++ app/assets/javascripts/components/mark/tools/text-row-tool/index.cjsx
@@ -62,7 +62,7 @@
         </Draggable>

         { if @props.selected
-          <DeleteButton tool={this} getDeleteButtonPosition={@getDeleteButtonPosition} />
+          <DeleteButton tool={this} />
         }

       </g>


erged master --> dev

erged master --> dev

--- app/assets/javascripts/oxalis/view/action-bar/merge_modal_view.coffee
+++ app/assets/javascripts/oxalis/view/action-bar/merge_modal_view.coffee
@@ -150,7 +150,6 @@
       @project    .show(@projectSelectionView)
     )

-
   mergeTaskType : ->

     taskTypeId = @ui.tasktype.find("select :selected").prop("id")


now we save everything before reloading the page

--- app/assets/javascripts/type_station/editables/entity_editor.js.coffee
+++ app/assets/javascripts/type_station/editables/entity_editor.js.coffee
@@ -84,6 +84,7 @@
             message: 'Are you sure you want to save changes?'
             callback: (value) ->
               if value
+                self.ts.save()
                 for k,v of data
                   model.set(k, { field: k, value: v, type: self.fields[k].type })
                 model.save ->
@@ -101,11 +102,16 @@
           json['_type'] = self.data.type if self.data.type

           if valid
-            vex.dialog.confirm
-              message: 'Are you sure you want to create this?'
-              callback: (value) ->
-                if value
-                  createEntityCall(self.data.create_url, json)
+            if self.ts.isChanged()
+              vex.dialog.buttons.YES.text = 'Save'
+              vex.dialog.confirm
+                message: 'You have unsaved changes, Do you want to save them before creating this?'
+                callback: (value) ->
+                  if value
+                    self.ts.save()
+                    createEntityCall(self.data.create_url, json)
+            else
+              createEntityCall(self.data.create_url, json)
           else
             vex.dialog.buttons.YES.text = 'Ok'
             vex.dialog.alert


fix(app): prevent uncaught exception when kind is not supplied

--- src/clabot-sample.coffee
+++ src/clabot-sample.coffee
@@ -25,7 +25,7 @@
   # Makes no sense, yet. Extensible in the future.
   if project isnt 'clabot' then project = 'clabot'
   kind    = req.params.kind?.toLowerCase()
-  kind    = kind.charAt(0).toUpperCase() + kind.slice 1
+  kind    = kind.charAt(0).toUpperCase() + kind.slice 1 if kind
   if kind isnt 'Entity' then kind = 'Individual'

   res.render 'form.jade',


Client: Activity: Focus participant input when visible

--- client/activity/lib/components/channelparticipantavatars/index.coffee
+++ client/activity/lib/components/channelparticipantavatars/index.coffee
@@ -106,7 +106,10 @@

     if @state.addNewParticipantMode is yes
     then @setState addNewParticipantMode: no
-    else @setState addNewParticipantMode: yes
+    else
+      @setState addNewParticipantMode: yes
+      textInput = React.findDOMNode @refs.textInput
+      textInput.focus()

   onShowMoreParticipantButtonClick: (event) ->


Payment: minor commenting

Payment: minor commenting

--- client/Account/views/billing.coffee
+++ client/Account/views/billing.coffee
@@ -88,7 +88,9 @@
     paymentController.creditCard (err, cc) =>

       card = null  if err?
-
+
+      # intentional if/else ifs
+      # to denote the edge cases - SY
       if subscription.provider is 'paypal'
         card = null


Adding optional polygon fill color.

Adding optional polygon fill color.


add wusbc to product list

add wusbc to product list

--- assets/coffeescript/product_list.coffee
+++ assets/coffeescript/product_list.coffee
@@ -35,6 +35,15 @@
       weight: 5
       title_video: "Download sample videos"
       link_video: "https://drive.google.com/file/d/0B4Q4b_aBOfU-TmJvSFhFeWp1NEk/view?usp=sharing"
+    wusbc:
+      id: 'wusbc'
+      title_store: "usb-c mount"
+      description_store: "USB-C plug to connect to a wide range of cameras/sensors. Ships with standard tripod screw and mount for Intel RealSense D400 sensors. (**RealSense sensor not included.)"
+      tech_specs:
+        sensor: "No camera/sensor included."
+      title_cart: "World Camera: USB-C mount"
+      description_cart: "USB-C plug to connect to a wide range of cameras/sensors. Ships with standard tripod screw and mount for Intel RealSense D400 sensors. (**RealSense ensor not included.)"
+      weight: 2
     # w3d:
     #   id: 'w3d'
     #   title_store: '3d'
@@ -470,6 +479,53 @@
         discount: edu_discount
       img: [pupil_img_path,'pupil_w3d_e200b.jpg'].join('/')

+    pupil_wusbc_enone:
+      cost: 1540
+      title_product: 'Pupil Headset'
+      sub_products:
+        world_camera: world_cam_data.wusbc
+        eye_camera: eye_cam_data.enone
+      img: [pupil_img_path,'pupil_wusbc_enone.jpg'].join('/')
+    pupil_wusbc_e200:
+      cost: 2190
+      title_product: 'Pupil Headset'
+      sub_products:
+        world_camera: world_cam_data.wusbc
+        eye_camera: eye_cam_data.e200
+      img: [pupil_img_path,'pupil_wusbc_e200.jpg'].join('/')
+    pupil_wusbc_e200b:
+      cost: 3090
+      title_product: 'Pupil Headset'
+      sub_products:
+        world_camera: world_cam_data.wusbc
+        eye_camera: eye_cam_data.e200b
+      img: [pupil_img_path,'pupil_wusbc_e200b.jpg'].join('/')
+
+    pupil_wusbc_enone_edu:
+      cost: 950
+      title_product: 'Pupil Headset'
+      sub_products:
+        world_camera: world_cam_data.wusbc
+        eye_camera: eye_cam_data.enone
+        discount: edu_discount
+      img: [pupil_img_path,'pupil_wusbc_enone.jpg'].join('/')
+    pupil_wusbc_e200_edu:
+      cost: 1600
+      title_product: 'Pupil Headset'
+      sub_products:
+        world_camera: world_cam_data.wusbc
+        eye_camera: eye_cam_data.e200
+        discount: edu_discount
+      img: [pupil_img_path,'pupil_wusbc_e200.jpg'].join('/')
+    pupil_wusbc_e200b_edu:
+      cost: 2500
+      title_product: 'Pupil Headset'
+      sub_products:
+        world_camera: world_cam_data.wusbc
+        eye_camera: eye_cam_data.e200b
+        discount: edu_discount
+      img: [pupil_img_path,'pupil_wusbc_e200b.jpg'].join('/')
+
     oculusdk2_e120:
       cost: 800
       title_product: 'Oculus Rift DK2 Add-on'
@@ -511,7 +567,7 @@
       title_store: 'HTC Vive Binocular Add-on'
       description_store: "Add binocular eye tracking to your HTC Vive and Vive PRO VR headset. Includes binocular 120hz eye tracking cameras, clip-on attachment rings with IR illuminators and USB connector clip. No tools required!"
       description_cart: "120hz binocular eye tracking add-on for your HTC Vive and Vive PRO VR headset."
-      img: [vr_ar_img_path,'htcviveb.jpg'].join('/')
+      img: [vr_ar_img_path,'htcviveb.jpg'].join('/')
     hololens_w120_e120b:
       cost: 1750
       title_product: 'HoloLens Binocular Add-on'


Better use of participant object

Better use of participant object

--- src/packages/collaboration/lib/participant-view.coffee
+++ src/packages/collaboration/lib/participant-view.coffee
@@ -14,7 +14,7 @@
       @div class: 'volume-container lighter', outlet: 'volumeContainer', =>
         @div class: 'volume', outlet: 'volume'

-  initialize: (@session, {id, email, clientId, siteId}) ->
+  initialize: (@session, @participant) ->
     @session.waitForStream (stream) =>
       @video[0].src = URL.createObjectURL(stream)

@@ -26,9 +26,9 @@
     @toggleAudioButton.click @onClickToggleAudio

     map = @session.getClientIdToSiteIdMap()
-    @setSiteId(map.get(clientId))
+    @setSiteId(map.get(@participant.clientId))
     map.on 'changed', ({key}={}) =>
-      @setSiteId(map.get(clientId)) if key == clientId
+      @setSiteId(map.get(@participant.clientId)) if key == @participant.clientId

   setSiteId: (siteId) ->
     return unless siteId


Remove broken test

Remove broken test

--- client/test/src/section_view.coffee
+++ client/test/src/section_view.coffee
@@ -153,30 +153,3 @@

   view.close()
 )
-
-test("Blurring title triggers delaySave", (done)->
-  oldTitle = "old title"
-  report = new Backbone.Models.Report({
-    sections: [
-      new Backbone.Models.Section(title: oldTitle)
-    ]
-  })
-  section = report.get('sections').models[0]
-
-  delaySaveStub = sinon.stub(Backbone.Views.TextEditView::, 'delaySave')
-
-  view = createAndShowSectionViewForSection(section)
-
-  # Edit the title
-  newTitle = 'new title'
-  $.when(
-    $('#test-container').find(".content-text-field").text(newTitle).trigger('blur')
-  ).done(->
-    assert.ok(
-      delaySaveStub.calledOnce,
-      "Expected delaySave to be called once, but was called #{delaySaveStub.callCount}"
-    )
-    delaySaveStub.restore()
-    done()
-  )
-)


Add snippet to read STDIN in NodeJS

Add snippet to read STDIN in NodeJS

This is why Perl shits all over JavaScript for text-processing:

    # JavaScript
    process.stdin.on("readable", () => {
        let chunk = process.stdin.read();
        if(null !== chunk){
            console.log(chunk); // Ugh
        }
    });

    # Perl
    while(<>){
        print; # Problem?
    }

--- user/snippets.cson
+++ user/snippets.cson
@@ -88,6 +88,10 @@
 	For:
 		prefix: "for"
 		body: "for(let i = 0, l = ${1:array}.length; i < l; ++i){\n\tlet ${2:item} = ${1:array}[i];\n\t$3\n}"
+	"Read Stdin":
+		prefix: "stdin"
+		body: 'process.stdin.setEncoding("UTF8");\nprocess.stdin.on("readable", () => {\n\tlet chunk = process.stdin.read();\n\tif(null !== chunk){\n\t\t$1\n\t}\n})\n'
+

 # CoffeeScript/CSON


add tests

add tests

--- components/article/test/view.coffee
+++ components/article/test/view.coffee
@@ -94,7 +94,8 @@
               type: 'embed',
               layout: 'overflow',
               url: 'http://files.artsy.net/data.pdf',
-              height: ''
+              height: '600'
+              mobile_height: '1100'
             }
             {
               type: 'callout',
@@ -182,6 +183,16 @@
       @view.refreshWindowSize()
       @fillwidth.callCount.should.be.above 1

+  describe '#embedMobileHeight', ->
+
+    it 'sets iframe height to desktop height on large screens', ->
+      @view.$el.find('iframe').height().should.equal 600
+
+    it 'sets iframe height to mobile height on small screens', ->
+      @view.windowWidth = 400
+      @view.embedMobileHeight()
+      @view.$el.find('iframe').height().should.equal 1100
+
   describe '#resetImageSetPreview', ->

     it 'on large screens, images are full height', ->


fixed start and stop buttons

fixed start and stop buttons

--- demo.coffee
+++ demo.coffee
@@ -229,17 +229,18 @@
       details.collector stats, details.rows
   if u.done && loaded
     drawChart e.innerHTML for e in sibs
+    byId('start').innerHTML = 'start'
+    byId('stop').style.display = 'none'
   else if makeCharts
     drawChart()
 window.start = ->
-  if u
+  if u && u.running
     u.stop()
     evoData.generation = 0
     clearCharts()
     u.erase()
-  else
-    byId('stop').style.display = 'inline'
-    byId('start').innerHTML = 'restart'
+  byId('stop').style.display = 'inline'
+  byId('start').innerHTML = 'restart'
   makeUniverse()
   u.run()
   byId('stop').innerHTML = 'stop'


energy chart implemented

energy chart implemented

--- demo.coffee
+++ demo.coffee
@@ -169,12 +169,11 @@
         specs.rows = []
       e = byId id
       id.replace /\d+$/, ''
-      id = specs.id = "id#{resetNum}"
+      id = specs.id = "#{id}#{resetNum}"
       div = create 'div', 'chart'
       div.id = id
       e.parentNode.replaceChild div, e
 drawChart = ->
-  data = new google.visualization.DataTable()
   for title, specs of evoData.charts[chartType]
     id     = specs.id
     rows   = trimData specs.rows
@@ -184,6 +183,7 @@
     vtitle = specs.vtitle
     width  = specs.width || 1000
     height = specs.height || 400
+    data = new google.visualization.DataTable()
     data.addColumn 'number', 'X'
     data.addColumn 'number', n for n in names
     data.addRows rows


Fixed #396 a loading flash message is not fadeoutable

--- wzk/net/FlashMiddleware.coffee
+++ wzk/net/FlashMiddleware.coffee
@@ -65,28 +65,35 @@
     @param {number} status
   ###
   error: (status) ->
-    if @hasDefaultMsgFor status
-      strStatus = String status
+    if @hasDefaultMsgFor(status)
+      strStatus = String(status)
       @flash.addError(@msgs[strStatus]) if @msgs[strStatus]?
     else
-      @flash.addError @msgs['error']
+      @flash.addError(@msgs['error'])

   loading: ->
-    [@createInfoFlash @msgs['loading'], 'loading', false]
+    [@createInfoFlash(@msgs['loading'], 'loading', false, false)]

   offline: ->
-    [@createInfoFlash @msgs['offline'], 'info']
+    [@createInfoFlash(@msgs['offline'], 'info', true, true)]

   ###*
     @param {string} msgTxt
     @param {string=} cls
-    @param {boolean=} closable
+    @param {boolean} closable
+    @param {boolean} fadeout
     @return {wzk.ui.FlashMessage}
   ###
-  createInfoFlash: (msgTxt, cls, closable = true) ->
-    msg = new wzk.ui.FlashMessage dom: @flash.getDomHelper(), msg: msgTxt, severity: 'info', fadeOut: true, closable: closable
-    msg.addClass cls if cls?
-    @flash.addChild msg
+  createInfoFlash: (msgTxt, cls, fadeOut, closable) ->
+    msg = new wzk.ui.FlashMessage({
+      dom: @flash.getDomHelper(),
+      msg: msgTxt,
+      severity: 'info',
+      fadeOut: fadeOut,
+      closable: closable
+    })
+    msg.addClass(cls) if cls?
+    @flash.addChild(msg)
     msg

   clearAll: ->


Makes observe method polymorphic

Makes observe method polymorphic

--- src/observable/observable_base.coffee
+++ src/observable/observable_base.coffee
@@ -183,7 +183,12 @@
     callback._binded = fn
     $(window).on @_getEventName(prop), fn

-  observe: (keypath, callback) ->
+  observe: ->
+    { keypath, callback } = Leaf.Utils.polymorphic
+      'f':  'callback'
+      'sf': 'keypath callback'
+    , arguments
+
     { obj, prop } = @getProperty keypath
     obj._observe prop, callback


auto_open_dev_tools only from settings.coffee

--- electron.coffee
+++ electron.coffee
@@ -58,7 +58,8 @@

         win.setMenuBarVisibility false
         # Open the DevTools.
-        if isDebug and settings.auto_open_dev_tools then win.webContents.openDevTools()
+        if settings.auto_open_dev_tools
+            win.webContents.openDevTools({mode:'detach'})

         # Emitted when the window is closed.
         win.on 'closed', =>


Run yum upload synchronously

Run yum upload synchronously

--- core/yum.coffee
+++ core/yum.coffee
@@ -14,6 +14,7 @@
 ###
 Dependencies: proxy
 ###
+module.exports.push 'phyla/core/profile' # In case yum make use of environmental variables
 module.exports.push 'phyla/core/proxy'
 module.exports.push 'phyla/core/network'

@@ -102,7 +103,7 @@
   do_upload = ->
     each()
     .files(copy)
-    .parallel(10)
+    .parallel(1)
     .on 'item', (filename, next) ->
       basename = path.basename filename
       return next() if basename.indexOf('.') is 0


App inject it self as optional arg into filters #47

--- wzk/app/App.coffee
+++ wzk/app/App.coffee
@@ -15,22 +15,30 @@
     @reg = new wzk.app.Register()
     @proc.add @reg.process
     @regOnce = new wzk.app.Register()
+    @xhrFac = null
+    @doc = null
+    @opts = app: @

   ###*
     @param {Window} win
     @param {Object=} msgs
   ###
   run: (win, msgs = {}) ->
-
     dom = new wzk.dom.Dom win.document
     flash = new wzk.ui.Flash dom: dom
-    xhrFac = new wzk.net.XhrFactory flash, msgs
+    @xhrFac = new wzk.net.XhrFactory flash, msgs

     @registerStandardComponents flash

-    doc = win.document
-    @proc.once @regOnce.process, doc, doc, xhrFac
-    @proc.process doc, doc, xhrFac
+    @doc = win.document
+    @proc.once @regOnce.process, @doc, @doc, @xhrFac
+    @proc.process @doc, @doc, @xhrFac, @opts
+
+  ###*
+    @param {Element} el
+  ###
+  process: (el) ->
+    @proc.process el, @doc, @xhrFac, @opts

   ###*
     @param {string} selector


delete console log

delete console log

--- src/Game.coffee
+++ src/Game.coffee
@@ -117,7 +117,6 @@
       @snap(data)

     @socket.on 'scores', (data) =>
-      console.log @player.id
       maxScore = Math.max.apply(Math, data.scores.map((p)->p.score))
       for s in data.scores
         if s.player.uuid isnt @player.id


transact ctrl fix

transact ctrl fix

--- client/admin/controllers/transact.ng.coffee
+++ client/admin/controllers/transact.ng.coffee
@@ -3,6 +3,7 @@
   this.transact = (amount) ->
     Meteor.users.update $stateParams.userId,
       $inc: 'profile.balance': amount
+  return


Refactor, wip.

Refactor, wip.

--- client/app/MainApp/paymentcontroller.coffee
+++ client/app/MainApp/paymentcontroller.coffee
@@ -228,77 +228,75 @@
               modal.destroy()
               callback?()

-  confirmPayment: (type, plan, callback=->)->
-    paymentController = KD.getSingleton('paymentController')
-    group             = KD.getSingleton("groupsController").getCurrentGroup()
-
-    getBalance = (type, cb)->
-      if type is 'user'
-        KD.remote.api.JRecurlyPlan.getUserBalance cb
-      else
-        KD.remote.api.JRecurlyPlan.getGroupBalance group, cb
-
-    askBillingInfo = (cb)->
-      paymentModal = paymentController.createPaymentMethodModal {}, (newData, onError, onSuccess)->
-        if type is 'group'
-          group.setBillingInfo newData, (err, result)->
-            if err
-              onError err
-            else
-              onSuccess result
-              cb yes
-        else
-          KD.remote.api.JRecurlyPlan.setUserAccount newData, (err, result)->
-            if err
-              onError err
-            else
-              onSuccess result
-              cb yes
-
-    buildModal = (content, needBilling, willCharge, cb)->
-      modal           = new KDModalView
-        title         : "Confirm VM Creation"
-        content       : "
#{content}
"
-        overlay       : yes
-        buttons       :
-          No          :
-            title     : "Cancel"
-            cssClass  : "modal-clean-gray"
-            callback  : =>
-              modal.destroy()
+  getBalance: (type, group, cb)->
+    if type is 'user'
+      KD.remote.api.JRecurlyPlan.getUserBalance cb
+    else
+      KD.remote.api.JRecurlyPlan.getGroupBalance group, cb
+
+  setBillingInfo: (type, group, cb)->
+    @createPaymentMethodModal {}, (newData, onError, onSuccess)->
+      if type is 'group'
+        group.setBillingInfo newData, (err, result)->
+          if err
+            onError err
+          else
+            onSuccess result
+            cb yes
+      else
+        KD.remote.api.JRecurlyPlan.setUserAccount newData, (err, result)->
+          if err
+            onError err
+          else
+            onSuccess result
+            cb yes
+
+  getBillingInfo: (type, group, cb)->
+    if type is 'group'
+      group.getBillingInfo cb
+    else
+      KD.remote.api.JRecurlyPlan.getUserAccount cb
+
+  createPaymentConfirmationModal: (type, group, plan, content, needBilling, willCharge, cb)->
+    modal           = new KDModalView
+      title         : "Confirm VM Creation"
+      content       : "
#{content}
"
+      overlay       : yes
+      buttons       :
+        No          :
+          title     : "Cancel"
+          cssClass  : "modal-clean-gray"
+          callback  : =>
+            modal.destroy()
+            cb()
+        Billing     :
+          title     : "Enter Billing Info"
+          cssClass  : "modal-clean-green hidden"
+          callback  : =>
+            @setBillingInfo type, group, (success)->
+              if success
+                modal.buttons.Yes.show()
+                modal.buttons.Billing.hide()
+        Yes         :
+          title     : "OK, create the VM"
+          cssClass  : "modal-clean-green hidden"
+          callback  : =>
+            modal.destroy()
+            @makePayment type, plan, willCharge, ->
               cb()
-          Billing     :
-            title     : "Enter Billing Info"
-            cssClass  : "modal-clean-green"
-            callback  : =>
-              askBillingInfo (success)->
-                if success
-                  modal.buttons.Yes.show()
-                  modal.buttons.Billing.hide()
-          Yes         :
-            title     : "OK, create the VM"
-            cssClass  : "modal-clean-green"
-            callback  : =>
-              modal.destroy()
-              paymentController.makePayment type, plan, willCharge, ->
-                cb()
-
-      if needBilling
-        modal.buttons.Billing.show()
-        modal.buttons.Yes.hide()
-      else
-        modal.buttons.Billing.hide()
-
-    getBillingInfo = (cb)->
-      if type is 'group'
-        group.getBillingInfo cb
-      else
-        KD.remote.api.JRecurlyPlan.getUserAccount cb
+
+    if needBilling
+      modal.buttons.Billing.show()
+    else
+      modal.buttons.Yes.show()
+
+  confirmPayment:(type, plan, callback=->)->
+    group = KD.getSingleton("groupsController").getCurrentGroup()

     group.canCreateVM
       type     : type
       planCode : plan.code
-    , (err, status)->
+    , (err, status)=>
       if not err and status
         content = """

                        You already subscribed for an additional
@@ -311,10 +309,10 @@
                   """
         buildModal content, no, no, callback
       else
-        getBillingInfo (err, account)->
+        @getBillingInfo type, group, (err, account)=>
           billing = err or not account or not account.cardNumber

-          getBalance type, (err, balance)->
+          @getBalance type, group, (err, balance)=>
             if not err and balance > 0
               charge = (plan.feeMonthly - balance) / 100
               balance = balance / 100
@@ -331,14 +329,14 @@
                                 Do you want to continue?

                            """
-                buildModal content, billing, yes, callback
+                @createPaymentConfirmationModal type, group, plan, content, billing, yes, callback
               else
                 content += """

                                 You won't be charged. Do you
                                 want to continue?

                            """
-                buildModal content, no, no, callback
+                @createPaymentConfirmationModal type, group, plan, content, no, no, callback
             else
               charge = plan.feeMonthly / 100
               content = """

@@ -346,7 +344,7 @@
                              you want to continue?

                         """
-              buildModal content, billing, yes, callback
+              @createPaymentConfirmationModal type, group, plan, content, billing, yes, callback

   makePayment: (type, plan, willCharge, callback)->
     vmController = KD.getSingleton('vmController')


Teambuilder: Default to 6 pokemon. Hide add pokemon if pokemon >= 6

--- client/app/js/views/teambuilder_view.coffee
+++ client/app/js/views/teambuilder_view.coffee
@@ -58,7 +58,7 @@
       teamJSON = JSON.parse(teamJSON)
       @collection.reset(teamJSON)
     else
-      @addNewPokemon()
+      @addNewPokemon()  for i in [1..6]

   addEmptyPokemon: =>
     @collection.add(new Pokemon())
@@ -235,6 +235,12 @@
       $listItem.addClass("active")  if @selected == i
       pokemon_list.append($listItem)

+    # Hide add pokemon if there's 6 pokemon
+    if @collection.length < 6
+      @$(".add_pokemon").show()
+    else
+      @$(".add_pokemon").hide()
+
   renderPokemon: (pokemon) =>
     view = @getPokemonView(pokemon)
     if view.length == 0


ChangeTeam modal: coffee lint fix

ChangeTeam modal: coffee lint fix

--- client/app/lib/changeteam/controller.coffee
+++ client/app/lib/changeteam/controller.coffee
@@ -17,8 +17,8 @@
         groups = groups.filter (group) -> group.slug isnt 'koding'
         callback null, groups
     options.noItemFoundWidget = new kd.CustomHTMLView
-        tagName  : 'p'
-        cssClass : 'no-item-found'
-        partial  : 'No item found!'
+      tagName  : 'p'
+      cssClass : 'no-item-found'
+      partial  : 'No item found!'

     super options, data


IDEView: Fix toggleFullScreen issue.

IDEView: Fix toggleFullScreen issue.

--- client/ide/lib/views/tabview/ideview.coffee
+++ client/ide/lib/views/tabview/ideview.coffee
@@ -651,7 +651,7 @@

     label                 = if @isFullScreen then 'Exit Fullscreen' else 'Enter Fullscreen'
     items[label]          =
-      callback            : @bound 'toggleFullscreen'
+      callback            : => @toggleFullscreen()

     return items


pricing: make planSelected method more readable

--- client/Pricing/AppView.coffee
+++ client/Pricing/AppView.coffee
@@ -192,7 +192,10 @@
       .router
       .handleRoute '/Login'  unless KD.isLoggedIn()

-    return @loadPlan @lazyBound 'planSelected', options  unless @state.currentPlan?
+    # wait for loading the current plan,
+    # call this method until it's ready.
+    unless @state.currentPlan?
+      return @loadPlan => @planSelected options

     isCurrentPlan =
       options.planTitle    is @state.currentPlan and


stack-editor: no need to wait for groupcontroller ready

--- client/stack-editor/lib/editor/index.coffee
+++ client/stack-editor/lib/editor/index.coffee
@@ -54,22 +54,6 @@
     options.selectedProvider = selectedProvider ?= 'aws'

     super options, data
-
-    @isMine = yes
-    kd.singletons.groupsController.ready =>
-
-      { groupsController } = kd.singletons
-
-      @isMine = isAdmin() or isMine(stackTemplate)
-
-      if not @isMine and stackTemplate
-        @tabView.setClass 'StackEditorTabs isntMine'
-        @warningView.show()
-        @deleteStack.hide()
-        @saveButton.setClass 'isntMine'
-        @inputTitle.setClass 'template-title isntMine'
-        @titleActionsWrapper.hide()
-

     @setClass 'edit-mode'  if inEditMode = @getOption 'inEditMode'


test/teamshelper: build stack by checking vm title

--- client/test/lib/helpers/teamshelpers.coffee
+++ client/test/lib/helpers/teamshelpers.coffee
@@ -312,6 +312,36 @@
       .pause 1000
       .click saveButton
       .pause 2000, -> done()
+
+
+  buildStack: (browser, done) ->
+
+    sidebarSelector = '.SidebarTeamSection'
+    sidebarStackSection = "#{sidebarSelector} .SidebarSection-body"
+    buildStackModal = '.kdmodal.env-modal.env-machine-state.kddraggable.has-readme'
+    buildStackButton = "#{buildStackModal} .kdbutton.turn-on.state-button.solid.green.medium.with-icon"
+    progressBarSelector = "#{buildStackModal} .progressbar-container"
+    vmSelector = "#{sidebarStackSection} .SidebarMachinesListItem cite"
+
+    browser
+      .click '#main-sidebar'
+      .waitForElementVisible sidebarSelector, 20000
+      .click sidebarStackSection
+
+    browser.getAttribute vmSelector, 'title', (result) =>
+      ###
+        Machine status: 'status'
+        substring function remove 'Machine status:'
+        from title attribute
+      ###
+      status = result.value.substring 16
+
+      switch status
+        when 'NotInitialized' then @turnOnVm browser, yes, done
+        when 'Running' then done()
+        when 'Stopping' then @waitUntilVmStopping browser, done
+        when 'Stopped' then @turnOnVm browser, no, done
+        when 'Starting' then @waitUntilVmRunning browser, done

   createStack: (browser, done) ->


reset cache after sending mail

reset cache after sending mail

--- src/daily-report/states/preview/controller.coffee
+++ src/daily-report/states/preview/controller.coffee
@@ -13,6 +13,7 @@
   todaysGoals
   previousGoals
   sections
+  dailyReportCache
 ) ->
   $scope.message = message

@@ -51,9 +52,12 @@
           $mdToast.show errorFeedback
           $mdDialog.cancel()
         else
+          dailyReportCache.todaysGoals = null
+          dailyReportCache.previousGoals = null
+          dailyReportCache.sections = null
           dailyReport.metadata =
             previousGoals: todaysGoals
           dailyReport.save()
-          sentFeedback = $mdToast.simple().content('Email sent')
+          sentFeedback = $mdToast.simple().position('top right').content('Email sent')
           $mdToast.show sentFeedback
           $mdDialog.cancel()


Dict.set(fieldName, value)

Dict.set(fieldName, value)

--- dict.coffee
+++ dict.coffee
@@ -136,6 +136,12 @@
         @_replaceKeys newKeys

     set: (fields) ->
+        if not J.util.isPlainObject(fields) and arguments.length > 1
+            # Support set(fieldName, value) syntax
+            fieldName = fields
+            value = arguments[1]
+            fields = {}
+            fields[fieldName] = value
         unless J.util.isPlainObject fields
             throw new Meteor.Error "Invalid setter: #{fields}"
         if @readOnly
@@ -144,6 +150,12 @@
         @_forceSet fields

     setOrAdd: (fields) ->
+        if not J.util.isPlainObject(fields) and arguments.length > 1
+            # Support set(fieldName, value) syntax
+            fieldName = fields
+            value = arguments[1]
+            fields = {}
+            fields[fieldName] = value
         unless J.util.isPlainObject fields
             throw new Meteor.Error "Invalid setter: #{fields}"
         if @readOnly


Flesh out kiosk config

Flesh out kiosk config

--- kiosk-config.coffee
+++ kiosk-config.coffee
@@ -20,6 +20,65 @@
 # * Save responses via email or database
 # * Display advertising via iframes

+# Question Types
+# ==============
+# * multiple (checkboxes)
+# * select (radio button)
+# * sentence (single line)
+# * paragraph (multi-line)
+# * email (validation)
+
+
 exports.module = {
+  owner: {
+    name: "Joe Random User"
+    email: "joe@self-service-kiosk.com"
+  }
+  ads: [
+    {kind: "video", name: "Acuvue", url: "http://youtu.be/Dg81rnLYZys" }
+    {kind: "text", name: "Johnson & Johnson"}
+  ]
+  background_color: "blue"
+  font_color: "white"
+  font_family: "Optima"

+  timeout: 300 # how many seconds until it reverts to home screen
+
+  home: {
+    title: "FREE Vision Check"
+    description: "
+    Plus
+      Health Tips
+        Symptoms
+          Conditions
+            Valuable Coupons
+    " # format with Markdown
+    select: "Tap Anywhere To Begin"
+  }
+  questions: {
+    sex: {
+      title: "Gender"
+      why: "This helps us better estimate your health risks"
+      select: {M: "Male", F: "Female", other: "Other"}
+    }
+    dry_eyes: {
+      title: "Do you have dry eyes?"
+      description: "Do your eyes itch?  Do they have redness?"
+      why: "This helps us better estimate your health risks"
+      select: {yes: "Yes", no: "No", other: "Not Sure"}
+    }
+    dry_eyes_duration: {
+      when: {dry_eyes: 'yes'}
+      title: "How long have you had dry eyes?"
+      description: "Do your eyes itch?  Do they have redness?"
+      why: "This helps us better estimate your health risks"
+      select: {
+        m6:"6 months or less"
+        m12: "1 year"
+        m24: "Longer"
+        other: "Not sure"
+      }
+    }
+  # result = {sex: 'M', dry_yes: 'yes', dry_eyes_duration: 'm12'}
+  }
 }


user questions should be validated in edit profile as well

--- @client/auth.coffee
+++ @client/auth.coffee
@@ -944,7 +944,7 @@
     # Note that we don't have server side validation because
     # the questions are all defined on the client.
     @local.errors = []
-    if auth.ask_questions && auth.form in ['create account', 'create account via invitation']
+    if auth.ask_questions # && auth.form in ['create account', 'create account via invitation']
       questions = customization('auth_questions')
       for question in questions
         if question.required


Add fancy cursors to Magento snippets

Add fancy cursors to Magento snippets

--- .atom/snippets.cson
+++ .atom/snippets.cson
@@ -20,14 +20,17 @@
 '.text.html':
   'Translation Method':
     'prefix': 'trans'
-    'body': '<?php echo $this->__(\'\') ?>'
+    'body': '<?php echo $this->__(\'${1:text}\') ?>'
   'Get URL':
     'prefix': 'geturl'
-    'body': '<?php echo $this->getUrl(\'\') ?>'
+    'body': '<?php echo $this->getUrl(\'${1:ref}\') ?>'
   'Get Child HTML':
     'prefix': 'chtml'
-    'body': '<?php echo $this->getChildHtml(\'\') ?>'
+    'body': '<?php echo $this->getChildHtml(\'${1:child}\') ?>'
 '.text.xml':
   'Magento layout block':
     'prefix': 'block'
-    'body': '<block type="" name="" as="" template=""></block>'
+    'body': '<block type="${1:type}" name="${2:name}" as="${3:alias}" template="${4:template}">$5</block>'
+  'Magento layout reference':
+    'prefix': 'ref'
+    'body': '<reference name="${1:name}">$2</reference>'


Filter messages from File-Icons' verbose logging

--- .config/init.coffee
+++ .config/init.coffee
@@ -78,3 +78,6 @@
 # Retrieve the contents of the current editor
 Object.defineProperty global, "text",
 	get: -> atom.workspace.getActiveTextEditor().buffer.getText()
+
+
+global.__fileIconsDebugFilter = /Adding grammar/


remove useless state var

remove useless state var

--- lib/commands.coffee
+++ lib/commands.coffee
@@ -12,8 +12,7 @@
       'linter:lint': => @lint()

     # Default values
-    @firstRun = true
-    @index = 0
+    @index = null

   toggleLinter: ->
     @linter.getActiveEditorLinter()?.toggleStatus()
@@ -53,10 +52,10 @@
       atom.notifications.addError error.message, {detail: error.stack, dismissable: true}

   nextError: ->
-    if @firstRun
-      @firstRun = false
+    if @index?
+      @index++
     else
-      @index++
+      @index = 0
     messages = @linter.views.messages
     message = messages[@index %% messages.length]
     return unless message.filePath
@@ -65,10 +64,10 @@
       atom.workspace.getActiveTextEditor().setCursorBufferPosition(message.range.start)

   previousError: ->
-    if @firstRun
-      @firstRun = false
+    if @index?
+      @index--
     else
-      @index--
+      @index = 0
     messages = @linter.views.messages
     message = messages[@index %% messages.length]
     return unless message.filePath


Fix text of ISYTDS.

Fix text of ISYTDS.

--- coffeescripts/cards-en.coffee
+++ coffeescripts/cards-en.coffee
@@ -1216,7 +1216,7 @@

     condition_translations =
         '''I'll Show You the Dark Side''':
-            text: '''When this card is assigned, if it is not already in play, the player who dealt it searches the Damage deck for 1 Damage card with the Pilot trait and may place it faceup on this card. Then shuffle the damage deck.%LINEBREAK%When you suffer critical damage from an attack, you are instead dealt the chosen faceup Damage card.%LINEBREAK%When there is no Damage card on this card, remove it.'''
+            text: '''When this card is assigned, if it is not already in play, the player who assigned it searches the Damage deck for 1 Damage card with the Pilot trait and may place it faceup on this card. Then shuffle the damage deck.%LINEBREAK%When you suffer critical damage during an attack, you are instead dealt the chosen faceup Damage card.%LINEBREAK%When there is no Damage card on this card, remove it.'''
         'Suppressive Fire':
             text: '''When attacking a ship other than "Captain Rex," roll 1 fewer attack die.%LINEBREAK% When you declare an attack targeting "Captain Rex" or when "Captain Rex" is destroyed, remove this card.%LINEBREAK%At the end of the Combat phase, if "Captain Rex" did not perform an attack this phase, remove this card.'''
         'Fanatical Devotion':


Fix spelling errors.

Fix spelling errors.

--- coffeescripts/cards-es.coffee
+++ coffeescripts/cards-es.coffee
@@ -63,7 +63,7 @@
         "Imperial Raider Expansion Pack": "Pack de Expansión Incursor Imperial"
         "K-Wing Expansion Pack": "Pack de Expansión Ala-K"
         "TIE Punisher Expansion Pack": "Pack de Expansión Castigador TIE"
-        "Khiraxz Fighter Expansion Pack": "Pack de Expansión Caza Khiraxz"
+        "Kihraxz Fighter Expansion Pack": "Pack de Expansión Caza Kihraxz"
         "Hound's Tooth  Expansion Pack": "Pack de Expansión Diente de Perro"
     ui:
         shipSelectorPlaceholder: "Selecciona una nave"
@@ -173,7 +173,7 @@
     exportObj.renameShip 'M3-A Interceptor', 'Interceptor M3-A'
     exportObj.renameShip 'StarViper', 'Víbora Estelar'
     exportObj.renameShip 'Aggressor', 'Agresor'
-    exportObj.renameShip 'Khiraxz Fighter', 'Caza Khiraxz'
+    exportObj.renameShip 'Kihraxz Fighter', 'Caza Kihraxz'

     pilot_translations =
         "Wedge Antilles":
@@ -993,7 +993,7 @@
         "Shield Technician":
             name: "Técnico de Escudos"
             text: """%HUGESHIPONLY%%LINEBREAK%Cuando lleves a cabo una acción de recuperación, en vez de retirar todas tus fichas de Energía, puedes elegir qué cantidad de fichas de Energía deseas retirar."""
-        "Gran Moff Tarkin":
+        "Grand Moff Tarkin":
             text: """%HUGESHIPONLY%%IMPERIALONLY%%LINEBREAK%Al comienzo de la fase de Combate, puedes elegir otra nave que tengas a alcance 1-4. Escoge entre retirar 1 ficha de Concentración de la nave elegida o asignarle 1 ficha de Concentración a esa nave."""
         "Captain Needa":
             name: "Capitán Needa"