Fix generate doc tasks

Fix generate doc tasks

--- _docs/_tasks/generate.coffee
+++ _docs/_tasks/generate.coffee
@@ -9,6 +9,7 @@
         classPrefix: ""

 pkg = require "../../package.json"
+index = require "../index.json"

 oMarkdownCompilationOptions =
     gfm: yes
@@ -20,9 +21,8 @@

     grunt.registerTask "generate", "Generate Kouto Swiss documentation page.", ->
         sDocFilePath = "docs/docs.html"
-        aChapters = [ "reset", "functions", "utilities", "grid" ]
         oDocChapters = {}
-        iChapters = 0
+        iChapters = index.chapters.length
         iTopics = 0
         # 1. get all the markdown doc files
         grunt.file.recurse "./_docs", ( sAbsolutePath, sRootDir, sSubDir, sFilename ) ->
@@ -37,13 +37,12 @@

         # 2. generate navigation menu
         ( $menu = $ "#wrapper header nav > ul" ).empty()
-        for sChapterName in aChapters
-            iChapters++
+        for sChapterName in index.chapters
             ( $chapterNav = $ "

" )
                 .find( "strong" )
                     .text( sChapterName.toLowerCase() )
             $chapterTopicsNav = $chapterNav.find "ul"
-            for sTopicName, sContent of oDocChapters[ sChapterName ]
+            for sTopicName in index[ sChapterName ]
                 iTopics++
                 ( $topicNavLink = $ "

" )
                     .find( "a" )
@@ -54,13 +53,13 @@

         # 3. insert doc articles
         ( $docs = $ "#wrapper div.docs" ).empty()
-        for sChapterName in aChapters
+        for sChapterName in index.chapters
             ( $chapterTitle = $ "

" ).text sChapterName
             $docs.append $chapterTitle
-            for sTopicName, sContent of oDocChapters[ sChapterName ]
+            for sTopicName in index[ sChapterName ]
                 ( $topicArticle = $ "

" )
                     .attr( "id", sTopicName )
-                    .html( sContent )
+                    .html( oDocChapters[ sChapterName ][ sTopicName ] )
                 $docs.append $topicArticle

         # 4. update version number


snap: exclude .sage/

snap: exclude .sage/

--- salvus/snap.coffee
+++ salvus/snap.coffee
@@ -868,7 +868,7 @@
             winston.debug("monitor_snapshot_queue: creating index for #{project_id}")
             t = misc.walltime()
             bup
-                args    : ['on', user, 'index', '--one-file-system', '.']        # --one-file-system option below so that sshfs-mounted filesystems (etc.) don't get sucked up.
+                args    : ['on', user, 'index', '--one-file-system', '.']        # --one-file-system option below so that sshfs-mounted filesystems (etc.) don't get suckedup.
                 bup_dir : bup_active
                 cb      : (err) ->
                     winston.debug("monitor_snapshot_queue: time to index #{project_id}: #{misc.walltime(t)} s")
@@ -897,7 +897,7 @@
                         cb(err)
                     else
                         modified_files = (x.slice(2) for x in output.stdout.trim().split('\n'))
-                        modified_files = (x for x in modified_files when x != "")
+                        modified_files = (x for x in modified_files when (x != "" and x != ".sage/"))
                         n = modified_files.length
                         winston.debug("monitor_snapshot_queue: #{n} modified files for #{project_id}; modified files = #{misc.trunc(misc.to_json(modified_files),512)}")
                         if n == 0


Fix last commit -_-

Fix last commit -_-


Fix: 更新時にキャッシュを無視できていなかったのを修正

Fix: 更新時にキャッシュを無視できていなかったのを修正

--- src/gui/js/repo.coffee
+++ src/gui/js/repo.coffee
@@ -87,10 +87,10 @@
     errorMsg: ""
     plist: {}
   methods:
-    get: ->
+    get: (force = false) ->
       @loading = true
       @error = false
-      plist.get(repo, lang).then( (obj) =>
+      plist.get(repo, lang, force).then( (obj) =>
         return plist.filter(obj)
       ).then( (obj) =>
         @loading = false
@@ -164,7 +164,7 @@
 )

 document.getElementById("reload").addEventListener("click", ->
-  r.get()
+  r.get(true)
   return
 )
 document.getElementById("apply").addEventListener("click", ->


add max() to TableGateway

add max() to TableGateway

--- src/access/table-gateway.coffee
+++ src/access/table-gateway.coffee
@@ -174,7 +174,12 @@
         s = sql.delete(@sqlAlias).where(predicate)
         return @db.bindOrCall(s, 'noData', cb)

-    count: (cb = null) -> @newSelect().select(sql.count(1)).tryCall('scalar', cb)
+    count: (cb) -> @newSelect().select(sql.count(1)).tryCall('scalar', cb)
+
+    # SHOULD: add all SQL unary aggregation
+    max: (column, cb) ->
+        F.demandNotNil(column, "column")
+        @newSelect().select(sql.max(column)).tryCall('scalar', cb)

     all: (cb) -> @newSelect().tryCall('allObjects', cb)
     allRows: (cb) -> @newSelect().tryCall('allRows', cb)


modify log

modify log

--- service/log.coffee
+++ service/log.coffee
@@ -1,2 +1,17 @@
-exports = (items...) ->
-    console.log items+util = require 'util'
+#ansi = require 'ansi'
+#cursor = ansi process.stdout
+
+#借助util, log 现在支持各种Object\String\Number了喵～
+log = (msg...) ->
+  util.log "#{util.format msg}"
+
+debug = (msg...) ->
+	util.debug "#{util.format msg}"
+
+error = (msg...) ->
+	util.error "#{util.format msg}"
+
+module.exports = log
+module.exports.debug = debug
+module.exports.error = error


Fix default values for tablength and line length

--- settings/rust.cson
+++ settings/rust.cson
@@ -10,6 +10,6 @@
        ^ \\s* (\\s* /[*] .* [*]/ \\s*)* \\}
       |^ \\s* (\\s* /[*] .* [*]/ \\s*)* \\)
       '
-    'tabLength': 4
+    'tabLength': 2
     'softTabs': true
-    'preferredLineLength': 99
+    'preferredLineLength': 80


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/metered.coffee
+++ src/metered.coffee
@@ -14,7 +14,7 @@

 enemy.include Metered

-someOt
+someOtherObject

 player.meter 'health'
 # => Sets up a health meter that will be drawn during the player overlay event


take_params is much much faster now (2.65% --> 0.03% of execution time). While splice is slow, slice is very very fast. :)

--- src/methods.coffee
+++ src/methods.coffee
@@ -103,10 +103,9 @@
     }

   take_params: (caller_stack) ->
-    params = new Array @param_bytes
     start = caller_stack.length - @param_bytes
-    for i in [0...@param_bytes] by 1
-      params[i] = caller_stack[start + i]
+    end = start + @param_bytes
+    params = caller_stack.slice(start, end)
     # this is faster than splice()
     caller_stack.length -= @param_bytes
     params


Fix #3978; populating i18n is now almost instant

--- app/models/CocoModel.coffee
+++ app/models/CocoModel.coffee
@@ -333,7 +333,7 @@
       sum += @populateI18N(value, schema.items, path+'/'+index) for value, index in data

     @set('i18n', data.i18n) if addedI18N and not path # need special case for root i18n
-    @updateI18NCoverage()
+    @updateI18NCoverage() if not path  # only need to do this at the highest level
     sum

   @getReferencedModel: (data, schema) ->


check for errors on mating

check for errors on mating

--- app/services/pokemon.coffee
+++ app/services/pokemon.coffee
@@ -89,4 +89,14 @@

         changes

+    mate: (father, mother) ->
+        if not father or not mother
+            throw "It takes two to tango"
+        if father.get('gender') isnt 'male' or mother.get('gender') isnt 'female'
+            throw "Gender mismatch, one male and one female required for mating"
+        if not breedsAreCompatible(father.get('breed'), mother.get('breed'))
+            throw "Breeds aren't compatible for breeding"
+
+
+
 `export default PokemonService`


better variables names and using argv value passed in to perform optional steps

--- src/package.coffee
+++ src/package.coffee
@@ -1,33 +1,36 @@
 fs           = require('fs')
 path         = require('path')
 uglify       = require('uglify-js')
-stitch       = require('../assets/stitch')
+stitchFile   = require('../assets/stitch')
 Dependency   = require('./dependency')
 Stitch       = require('./stitch')
 {toArray}    = require('./utils')
 mime         = require('connect').static.mime

 class Package
-  constructor: (name, config = {}) ->
-    @name         = name
-    @require      = config.require
-    @libs         = toArray(config.libs || [])
-    @paths        = toArray(config.paths || [])
-    @modules      = toArray(config.modules || [])
-    @target       = config.target
-    @jsAfter      = config.jsAfter or ""
-    @url          = config.url or ""
-    # determine content type based on target
-    @contentType  = mime.lookup(@target)
-    # check required config values
+  constructor: (name, config = {}, argv = {}) ->
+    @name        = name
+    @argv        = argv
+    # set config values
+    @require     = config.require
+    @target      = config.target
+    @libs        = toArray(config.libs || [])
+    @paths       = toArray(config.paths || [])
+    @modules     = toArray(config.modules || [])
+    @jsAfter     = config.jsAfter or ""
+    @url         = config.url or ""
+    # TODO: sanity checkes on config values??
+    # determine content type based on target file name
+    @contentType = mime.lookup(@target)

   compileModules: ->
-    @dependency or= new Dependency(@modules)
-    @stitch       = new Stitch(@paths)
-    @stuff        = @dependency.resolve().concat(@stitch.resolve())
-    stitch(identifier: @require, modules: @stuff)
+    @depend or= new Dependency(@modules)
+    _stitch   = new Stitch(@paths)
+    _modules  = @depend.resolve().concat(_stitch.resolve())
+    stitchFile(identifier: @require, modules: _modules)

   compileLibs: ->
+    # TODO: be able to handle being given a folder and loading each file...can this compile coffeescript??
     (fs.readFileSync(lib, 'utf8') for lib in @libs).join("\n")

   compile: (minify = false) ->
@@ -44,16 +47,15 @@
           _path  = require.resolve(path.resolve(_path))
           delete require.cache[_path]
           result.push require(_path)
+        # TODO: do we want a minify option for css??
         result.join("\n")
     catch ex
       console.trace ex
-      # only return when in server mode, otherwise exit
-      # TODO: pass in mode as argument instead of checking process.argv??
-      command = process.argv[2]
-      switch command
+      # only return when in server/watch mode, otherwise exit
+      switch @argv.command
         when "server" then return "console.log(\"#{ex}\");"
         when "watch"  then return ""
-        when "build"  then process.exit(1)
+        else process.exit(1)

   isJavascript: ->
     @contentType is "application/javascript"
@@ -62,7 +64,7 @@
     fs.unlinkSync(@target) if fs.existsSync(@target)

   build: (minify = false) ->
-    console.log "Building #{@name} to target: #{@target}"
+    console.log "Building '#{@name}' target: #{@target}"
     source = @compile(minify)
     fs.writeFileSync(@target, source) if source

@@ -72,7 +74,6 @@
       require('watch').watchTree dir, { persistent: true, interval: 1000 },  (file, curr, prev) =>
         @build() if curr and (curr.nlink is 0 or +curr.mtime isnt +prev?.mtime)

-  # TODO: move this to a separate middleware class, pass in package to call compile and content type on..
   middleware: (req, res, next) =>
     str = @compile()
     res.charset = 'utf-8'


adjust arc epsilon to a more reasonable value. fixes #7

--- src/plotter.coffee
+++ src/plotter.coffee
@@ -6,13 +6,14 @@
 Macro = require './macro-tool'
 # standard tool functions
 tool = require './standard-tool'
+# coordinate scale factor
+coordFactor = require('./svg-coord').factor
+

 # constants
 HALF_PI = Math.PI/2
 THREEHALF_PI = 3*HALF_PI
 TWO_PI = 2*Math.PI
-# error epsilon
-arcEps = 0.0000001
 # assumed units
 ASSUMED_UNITS = 'in'

@@ -378,9 +379,10 @@

   # draw an arc with the start point, end point, and center offset
   drawArc: (sx, sy, ex, ey, i, j) ->
-    # use a seriously low epsilon constant
-    # this value is too small and causing problems. re-evaluate
-    arcEps = 1.01 * 10**-( (@parser?.format.places[1] ? 6 ) - 1 )
+    # lets try this for arc point comparison epsilon
+    # this value seems strict enough to prevent invalid arcs but forgiving
+    # enough to let most gerbers draw
+    arcEps = 1.5 * coordFactor * 10**-(@parser?.format.places[1] ? 7)
     t = @tools[@currentTool]
     # throw an error if the tool is rectangular
     if not @region and not t.trace['stroke-width']


move old gerber parser and adjust requires for rewrite

--- src/plotter.coffee
+++ src/plotter.coffee
@@ -1,7 +1,7 @@
 # svg plotter class

 # use the gerber parser class to parse the file
-Parser = require './gerber-parser'
+Parser = require './gerber-parser-old'
 # unique id generator
 unique = require './unique-id'
 # aperture macro class


Don't mutate list during iteration

Don't mutate list during iteration

I saw a situation where this was calling `destroy()` on `undefined`—
presumably because destroying one caused the list to be mutated
elsewhere and the indexes to shift.

--- src/project.coffee
+++ src/project.coffee
@@ -30,8 +30,8 @@
     @consumeServices(packageManager)

   destroyed: ->
-    buffer.destroy() for buffer in @buffers
-    repository?.destroy() for repository in @repositories
+    buffer.destroy() for buffer in @buffers.slice()
+    repository?.destroy() for repository in @repositories.slice()
     @rootDirectories = []
     @repositories = []


more specific about running coffeescript

--- src/notebook.coffee
+++ src/notebook.coffee
@@ -157,6 +157,7 @@

       cell.rendered()

+    # TODO cell type
     add_input_cell = (notebook, opts={}) ->
       if opts.reuse
         if opts.after?
@@ -170,11 +171,11 @@
       cell

     # run an input cell above the current cell
-    run_before = (current_cell, code) ->
+    eval_coffeescript_before = (current_cell, code) ->
       cell = add_input_cell current_cell.notebook, code: code, before: current_cell
       run cell

-    run_after = (current_cell, code) ->
+    eval_coffeescript_after = (current_cell, code) ->
       cell = add_input_cell current_cell.notebook, code: code, after: current_cell
       run cell

@@ -194,7 +195,7 @@
       $el.append $link
       $el.append $code
       $link.on 'click', ->
-        run_after cell.output_cell ? cell, 'permalink'
+        eval_coffeescript_after cell.output_cell ? cell, 'permalink'

       editor = ed.create_editor $code.get 0

@@ -253,6 +254,7 @@
       input_cell.$el.attr 'data-cell-number', input_cell.number
       input_cell.output_cell = output_cell

+      # TODO cell type
       run_context = context.create_run_context [input_cell.context, {input_cell, output_cell}, create_notebook_run_context input_cell]
       eval_coffeescript_into_output_cell run_context, string
       input_cell.notebook.cell_run.push input_cell
@@ -299,10 +301,13 @@
       notebook = cell.notebook
       run_context =
         notebook: notebook
+        # TODO rename
         set_code: (code) ->
+          # TODO coffeescript
           cell = add_input_cell notebook, code: code, after: run_context.output_cell
           focus_cell cell
         run: (code) ->
+          # TODO coffeescript
           cell = add_input_cell notebook, code: code, after: run_context.output_cell
           run cell
         # TODO does it make sense to use output cells here?
@@ -374,13 +379,13 @@
       handle_file: handle_file

       save: (cell) ->
-        run_before cell, 'save'
+        eval_coffeescript_before cell, 'save'

       context_help: (cell, token) ->
         if graphite.has_docs token
-          run_before cell, "docs '#{token}'"
+          eval_coffeescript_before cell, "docs '#{token}'"
         else if cell.context.imported_context_fns[token]?
-          run_before cell, "help #{token}"
+          eval_coffeescript_before cell, "help #{token}"

       move_focus: (cell, offset) ->
         new_cell = input_cell_at_offset cell, offset


Form: Fixed a bug where the block level wasn't being set correctly if the root form were inside a block with higher level than 1. See #49993

--- src/elements/Form/SimpleForm.coffee
+++ src/elements/Form/SimpleForm.coffee
@@ -34,6 +34,9 @@
 				check: Boolean
 			top: {}
 			bottom: {}
+			blockLevelOffset:
+				check: "Integer"
+				default: 0

 	readOpts: ->
@@ -368,9 +371,7 @@
 					append(get_append(field._form.right), hint_div)

 			if field.renderAsBlock()
-				level = field.getFormDepth() + 1
-				if not level
-					level = 1
+				level = field.getFormDepth() + @getBlockLevelOffset() + 1

 				if level > 3
 					level = 3
@@ -688,6 +689,13 @@
 				return true
 		return false

+	getBlockLevelOffset: ->
+		parentBlockLevelOffset = @getForm()?.getBlockLevelOffset() or 0
+		return parentBlockLevelOffset + @_blockLevelOffset
+
+	setBlockLevelOffset: (@_blockLevelOffset) ->
+		return @_blockLevelOffset
+
 	destroy: ->
 		if @table
 			@unregisterTableListeners()


default values for package in config is in class property

--- src/_Config/PackageExtension.coffee
+++ src/_Config/PackageExtension.coffee
@@ -5,11 +5,8 @@
 class PackageExtension extends Extension

-	loadConfiguration: ->
-		config = @getConfig()
-
-		defaultsPackage =
-			skip: false
+	defaultsPackage:
+		skip: false
 			application: null
 			base: null
 			style:
@@ -25,8 +22,12 @@
 				begin: []		# deprecated
 				end: []			# deprecated

+
+	loadConfiguration: ->
+		config = @getConfig()
+
 		for name, pckg of config
-			config[name] = @configurator.merge(pckg, defaultsPackage)
+			config[name] = @configurator.merge(pckg, @defaultsPackage)

 			if pckg.coreModules != null
 				throw new Error 'Config: coreModules section is deprecated. Please take a look in new documentation.'


Use $timeout instead of setTimeout which makes it possible to remove apply()

--- app/assets/javascripts/admin/order_cycles/controllers/order_cycle_exchanges_controller.js.coffee
+++ app/assets/javascripts/admin/order_cycles/controllers/order_cycle_exchanges_controller.js.coffee
@@ -1,5 +1,5 @@
 angular.module('admin.orderCycles')
-  .controller 'AdminOrderCycleExchangesCtrl', ($scope, $controller, $filter, $window, $location, OrderCycle, Enterprise, EnterpriseFee, Schedules, RequestMonitor, ocInstance, StatusMessage) ->
+  .controller 'AdminOrderCycleExchangesCtrl', ($scope, $controller, $filter, $window, $location, $timeout, OrderCycle, Enterprise, EnterpriseFee, Schedules, RequestMonitor, ocInstance, StatusMessage) ->
     $controller('AdminEditOrderCycleCtrl', {$scope: $scope, ocInstance: ocInstance, $location: $location})

     $scope.supplier_enterprises = Enterprise.producer_enterprises
@@ -38,11 +38,9 @@
       OrderCycle.addDistributor($scope.new_distributor_id)

     $scope.setPickupTimeFieldDirty = (index) ->
-      setTimeout ->
-        last_outgoing_exchange_pickup_time_field_name = "order_cycle_outgoing_exchange_" + index + "_pickup_time"
-        $scope.order_cycle_form[last_outgoing_exchange_pickup_time_field_name].$setDirty()
-        $scope.$apply()
-      , 1
+      $timeout ->
+        pickup_time_field_name = "order_cycle_outgoing_exchange_" + index + "_pickup_time"
+        $scope.order_cycle_form[pickup_time_field_name].$setDirty()

     $scope.removeDistributionOfVariant = (variant_id) ->
       OrderCycle.removeDistributionOfVariant(variant_id)


Remove debug

Remove debug

--- src/protocol.coffee
+++ src/protocol.coffee
@@ -47,7 +47,6 @@
   debug 'sendpackets', graphId, packets

   Promise.all(Object.keys(packets).map((port) ->
-    console.log port, packets
     return client.protocol.runtime.packet
       event: 'data'
       port: port


Respond to code review; fix demo bugs - fix bug in _syncAttributeProperties introduced in late cleanup that prevented codapName from being synchronized under some circumstances - call _syncAttributeProperties after successful node/attribute rename - add missing existential operator suggested on code review - add comment suggested on code review

--- src/code/models/codap-connect.coffee
+++ src/code/models/codap-connect.coffee
@@ -79,7 +79,7 @@
       # ret==null is indication of timeout, not an indication that the data set
       # doesn't exist.
       if !ret or ret.success
-        @_initialSyncAttributeProperties ret.values?.collections?[1].attrs
+        @_initialSyncAttributeProperties ret.values?.collections?[1]?.attrs
         @initGameHandler ret
       else
         @_createDataContext()
@@ -187,7 +187,7 @@
   _syncAttributeProperties: (attrProps, initialSync) ->
     nodesToSync = if initialSync \
                     then _.filter @graphStore.nodeKeys, (node) -> not node.codapID or not node.codapName \
-                    else _.map @graphStore.nodeKeys, (node) -> node
+                    else _.map @graphStore.nodeKeys, (node) -> node # map nodeKeys to array of nodes
     if nodesToSync?.length
       _.each attrProps, (attr) =>
         # check for id match
@@ -208,9 +208,9 @@
             node.codapID = attr.id if not node.codapID
             node.codapName = attr.name
           # sync name/title, but only if it's changed on the CODAP side
-          else if not initialSync and (node.codapName isnt attr.name)
+          else if node.codapName isnt attr.name
             node.codapName = attr.name
-            if (node.title isnt attr.name)
+            if not initialSync and (node.title isnt attr.name)
               @graphStore._changeNode node, { title: attr.name }
           if initialSync
             _.remove nodesToSync, (node) -> node.codapID and node.codapName
@@ -227,8 +227,11 @@
         action: 'update'
         resource: "dataContext[Sage Simulation].collection[Samples].attribute[#{codapKey}]"
         values: { name: node.title }
-      @codapPhone.call message, (response) ->
-        if not response.success
+      @codapPhone.call message, (response) =>
+        if response.success
+          if response?.values?.attrs
+            @_syncAttributeProperties response.values.attrs
+        else
           console.log "Error: CODAP attribute rename failed!"

   _timeStamp: ->


Mixin functions removed since it is not being used

--- src/coffee/jsEngine/functions.coffee
+++ src/coffee/jsEngine/functions.coffee
@@ -1,18 +1,11 @@
-mixin = (inheritorClass, inheritClass) ->
-  functionName = undefined
-  inheritorClass::inheritedClasses.push inheritClass
-  Array::push.apply inheritorClass::inheritedClasses, inheritClass::inheritedClasses
-  for functionName of inheritClass::
-    inheritorClass::[functionName] = inheritClass::[functionName]  if typeof inheritClass::[functionName] is "function"  if inheritClass::hasOwnProperty(functionName)
-  return
 nameSpace = (name) ->
   i = undefined
   name = name.split(".")
-
+
   # Create namespace if missing
   i = 0
   while i < name.length
-
+
     # Create eval string
     str = name.slice(0, i + 1).join(".")
     eval str + " = {}"  if eval("window." + str) is undefined


JRewardCampaign: add one and some generic helpers, add more examples

--- workers/social/lib/social/models/rewards/rewardcampaign.coffee
+++ workers/social/lib/social/models/rewards/rewardcampaign.coffee
@@ -1,10 +1,12 @@
-{Model}   = require 'bongo'
-jraphical = require 'jraphical'
+{Model}     = require 'bongo'
+jraphical   = require 'jraphical'
+KodingError = require '../../error'

 module.exports = class JRewardCampaign extends jraphical.Module

-  # An example to create in client side by an administrator
-  #
+  # Examples for working with campaigns
+  #
+  # # Create
   #
   # KD.remote.api.JRewardCampaign.create(
   #   {
@@ -21,6 +23,33 @@
   #     console.log(err, campaign);
   #   }
   # );
+  #
+  # # Find & Modify
+  #
+  # var campaign = null;
+  #
+  # KD.remote.api.JRewardCampaign.one(
+  #   {
+  #     name: "register"
+  #   }, function(err, _campaign){
+  #     if (err) {
+  #       console.warn(err);
+  #     }
+  #     else if (_campaign) {
+  #       campaign = _campaign;
+  #       console.log("Campaign assigned to local variable `campaign`");
+  #     }
+  #     else {
+  #       console.error("No campaign found.");
+  #     }
+  #   }
+  # );
+  #
+  # if (campaign) {
+  #   campaign.update({isActive: false}, log);
+  # }
+  #
+

   {signature, secure} = require 'bongo'

@@ -79,6 +108,10 @@
           (signature Object, Function)
           (signature Object, Object, Function)
         ]
+        one             :
+          (signature Object, Function)
+        some            :
+          (signature Object, Object, Function)
         isValid         :
           (signature String, Function)
       instance          :
@@ -96,6 +129,18 @@

   DEFAULT_CAMPAIGN = "register"

+  deleteExistingCampaign = (data, callback)->
+
+    {name} = data
+
+    return callback new KodingError "Name not provided"  unless name
+
+    JRewardCampaign.one {name}, (err, campaign)->
+
+      return callback err  if err
+      return callback null unless campaign
+
+      campaign.remove callback

   # Private Methods
@@ -128,6 +173,18 @@
   # --------------

   # Static Methods
+
+  @some$ = permit 'manage campaign',
+
+    success: (client, selector, options, callback) ->
+      @some selector, options, callback
+
+
+  @one$ = permit 'manage campaign',
+
+    success: (client, selector, callback) ->
+      @one selector, callback
+

   @create = permit 'manage campaign',


Add test for pushState behavior

Add test for pushState behavior

--- test/javascript/turbolinks_visit_test.coffee
+++ test/javascript/turbolinks_visit_test.coffee
@@ -364,6 +364,19 @@
       done = null
     @Turbolinks.visit('iframe2.html')

+  test "doesn't pushState when URL is the same", (done) ->
+    load = 0
+    @document.addEventListener 'page:load', =>
+      load += 1
+      if load is 1
+        assert.equal @history.length, @originalHistoryLength
+        setTimeout (=> @Turbolinks.visit('iframe.html#test')), 0
+      else if load is 2
+        assert.equal @history.length, @originalHistoryLength + 1
+        done()
+    @originalHistoryLength = @history.length
+    @Turbolinks.visit('iframe.html')
+
   # Temporary until mocha fixes skip() in async tests or PhantomJS fixes scrolling inside iframes.
   return if navigator.userAgent.indexOf('PhantomJS') != -1


Text tab in displayed page

Text tab in displayed page

--- src/main/js/web/FreesheetWeb.coffee
+++ src/main/js/web/FreesheetWeb.coffee
@@ -6,6 +6,7 @@

 freesheet = new Freesheet()
 isLogging = false
+sheetNo = 0

 sheets = (name) -> freesheet.sheets(name)
 createSheet = (name) -> freesheet.createSheet(name)
@@ -24,13 +25,24 @@
                                                    <button class="hide-worksheets">Hide worksheets</button>

""").appendTo $('body')
   newWorksheet = (sheet) ->
+    sheetNo = sheetNo + 1
     sectionEl = $("""

#{sheet.name}

+

+
+

+
Worksheet

+
Text

+

+
+

+

+

+

+
+

""").appendTo container
-    worksheetId = sheet.name.replace /\s/g, '_'
-    worksheetEl = $("""

""").appendTo sectionEl
-    handsontableEl = $("

").appendTo worksheetEl
-    new TableWorksheet(handsontableEl, sheet)
+    new TableWorksheet sectionEl.find('.worksheet'), sheet, sectionEl.find('.text')

   logWorksheetChanges = (sheet) ->
     sheet.onValueChange (name, value) -> if isLogging then console.log "[#{sheet.name}] #{name} = ", value


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/metered.coffee
+++ src/metered.coffee
@@ -15,7 +15,7 @@
 enemy.include Metered

 player.meter 'health'
-# => Sets up
+# => Sets up a health mete

 player.include(Bounded)


rm debugging information

rm debugging information

--- src/methods.coffee
+++ src/methods.coffee
@@ -437,7 +437,7 @@
     @return_type = str2type return_str

   reflector: (rs) ->
-    rv = rs.set_obj 'java/lang/reflect/Method', {
+    rs.set_obj 'java/lang/reflect/Method', {
       # XXX: missing checkedExceptions, annotations, parameterAnnotations, annotationDefault
       clazz: rs.init_class_object c2t @class_name
       name: rs.init_string @name, true
@@ -447,8 +447,6 @@
       slot: parseInt((i for i,v of rs.class_lookup(@class_name).methods when v is @)[0])
       signature: rs.init_string @raw_descriptor
     }
-    console.log '>>>', @name, @raw_descriptor, rs.get_obj(rv).fields
-    rv

   param_bytes: () ->
     type_size = (t) -> (if t.toString() in ['D','J'] then 2 else 1)


Forgot the existential operator

Forgot the existential operator

--- src/mixable.coffee
+++ src/mixable.coffee
@@ -32,7 +32,7 @@
         extend @::, mixin::
       else
         extend @::, mixin
-      mixin.included @ if mixin.included
+      mixin.included @ if mixin.included?

 # Expose the public API
 root         = exports ? window


fixing copypasta bug

fixing copypasta bug

--- src/opcodes.coffee
+++ src/opcodes.coffee
@@ -531,11 +531,11 @@
     new_execute =
       if @field_spec.type not in ['J','D']
         (rs) ->
-          val = rs.pop().get_field @, name, cls
+          val = rs.pop().get_field rs, name, cls
           rs.push val
       else
         (rs) ->
-          val = rs.pop().get_field @, name, cls
+          val = rs.pop().get_field rs, name, cls
           rs.push2 val, null
     new_execute.call(@, rs)
     @execute = new_execute


Check for url before replacing

Check for url before replacing

--- src/package.coffee
+++ src/package.coffee
@@ -30,7 +30,7 @@
       normalizePackageData ?= require 'normalize-package-data'
       normalizePackageData(metadata)
       if metadata.repository?.type is 'git'
-        metadata.repository.url = metadata.repository.url.replace(/^git\+/, '')
+        metadata.repository.url = metadata.repository.url?.replace(/^git\+/, '')
       metadata

   @loadMetadata: (packagePath, ignoreErrors=false) ->


fixed paths

fixed paths

--- src/reports.coffee
+++ src/reports.coffee
@@ -185,7 +185,7 @@
   text

 renderTemplate = (templateName, templateData, callback) ->
-  templateDir = path.join(__dirname, '../templates', templateName)
+  templateDir = "#{appRoot}/templates/#{templateName}"
   template = new EmailTemplate(templateDir)
   template.render templateData, (err, result) ->
     if err


Don't add explicit 'Private:' annotations since it is the default

--- src/row-map.coffee
+++ src/row-map.coffee
@@ -1,9 +1,8 @@
 {spliceWithArray} = require 'underscore-plus'

-# Private: Used by the display buffer to map screen rows to buffer rows and
-# vice-versa. This mapping may not be 1:1 due to folds and soft-wraps. This
-# object maintains an array of regions, which contain `bufferRows` and
-# `screenRows` fields.
+# Used by the display buffer to map screen rows to buffer rows and vice-versa.
+# This mapping may not be 1:1 due to folds and soft-wraps. This object maintains
+# an array of regions, which contain `bufferRows` and `screenRows` fields.
 #
 # Rectangular Regions:
 # If a region has the same number of buffer rows and screen rows, it is referred
@@ -82,7 +81,6 @@
     spliceWithArray(@regions, index, count, regions)
     @mergeAdjacentRectangularRegions(index - 1, index + regions.length)

-  # Private:
   traverseToBufferRow: (targetBufferRow) ->
     bufferRows = 0
     screenRows = 0
@@ -93,7 +91,6 @@
       screenRows += region.screenRows
     {index, screenRows, bufferRows}

-  # Private:
   traverseToScreenRow: (targetScreenRow) ->
     bufferRows = 0
     screenRows = 0
@@ -104,7 +101,6 @@
       screenRows += region.screenRows
     {index, screenRows, bufferRows}

-  # Private:
   mergeAdjacentRectangularRegions: (startIndex, endIndex) ->
     for index in [endIndex..startIndex]
       if 0 < index < @regions.length


no_implicit_parens: fix for new cs2 parser

--- src/rules/no_implicit_parens.coffee
+++ src/rules/no_implicit_parens.coffee
@@ -34,6 +34,7 @@
                     t = tokenApi.peek(i)
                     sameLine = t[2].first_line is token[2].first_line
                     genCallStart = t[0] is 'CALL_START' and t.generated
+
                     if not t? or genCallStart and sameLine
                         return true


project_tasks/fullpath: files missing -_-

--- src/smc-webapp/project_tasks.coffee
+++ src/smc-webapp/project_tasks.coffee
@@ -77,7 +77,7 @@
         else
             base = 'https://cloud.sagemath.com'
         {join} = require('path')
-        path = join("#{window.smc_base_url ? '/'}", "projects", "#{@project_id}", "#{misc.encode_path(path)}")
+        path = join("#{window.smc_base_url ? '/'}", "projects", "#{@project_id}", 'files', "#{misc.encode_path(path)}")
         return "#{base}" + (if path[0] == '/' then '' else '/') + path

     # returns the URL for the file at the given path


a little comment

a little comment

--- src/setup_key.iced
+++ src/setup_key.iced
@@ -38,6 +38,12 @@
     # Throw an error if any of the above checks failed.
     await athrow err, esc defer() if err?

+    # --- We'll likely factor this stuff out, but for now.....
+    # Load key into a temporary keyring
+    # Verify the credentials
+    # Load into the master keyring
+    # Nuke the temporary
+
     cb null

   #------------


add session timeout configuration

add session timeout configuration

--- src/sm-cmds.coffee
+++ src/sm-cmds.coffee
@@ -50,6 +50,9 @@
   })
   winstonLogger.setLevels(winstonLogger.config.syslog.levels)
   robot.logger = winstonLogger
+  sessiontimeout=process.env.SESSTIONTIMEOUT
+  if !sessiontimeout
+    sessiontimeout="30m"
   console.log('start to load sm-cmd.coffee')
   if not robot.sm_ext
     SmExt = require "../lib/sm-#{robot.adapterName}"
@@ -76,8 +79,8 @@
       return
     if  robot.e.auth != null && robot.e.auth.create_basic_auth_config != null && robot.e.auth.create_basic_auth_config != undefined
       verb = "GET"
-      auth_method = robot.e.auth.create_basic_auth_config auth_url, verb
-      robot.logger.debug('Use create_basic_auth_config to create auth config for:'+auth_url)
+      auth_method = robot.e.auth.create_basic_auth_config auth_url, verb, sessiontimeout
+      robot.logger.debug('Use create_basic_auth_config to create auth config for:'+auth_url+" and sessiontimeout is:"+sessiontimeout)
     else
       auth_method={
         type: "basic_auth",


Cache it

Cache it

--- apps/auctions/routes.coffee
+++ apps/auctions/routes.coffee
@@ -41,6 +41,7 @@

 @redirect = (req, res) ->
   new Backbone.Collection().fetch
+    cache: true
     url: "#{API_URL}/api/v1/sets/contains?item_type=Sale&item_id=#{req.params.id}"
     error: res.backboneError
     success: (collection, response, options) ->


Cleanup

Cleanup

--- apps/sitemaps/routes.coffee
+++ apps/sitemaps/routes.coffee
@@ -167,7 +167,6 @@
     .query(published_at_since: moment().subtract(7, 'days').format('YYYY-MM-DD'))
     .end (err, sres) ->
       return next err if err
-      return callback() if sres.body.results.length is 0
       streamResults sres.body.results, res
       res.write('{}]')
       res.end()


Unused import

Unused import

--- src/modules/todo/TodoModule.coffee
+++ src/modules/todo/TodoModule.coffee
@@ -1,5 +1,3 @@
-infect = require "infect"
-
 Module = require "../../Module.coffee"

 module.exports = new Module()


fix email share

fix email share

--- src/layout/mixins/done_dialog.coffee
+++ src/layout/mixins/done_dialog.coffee
@@ -11,7 +11,7 @@
       facebook:
         app_id:  1510955112514265
       email:
-        description: text + "\n\nCheck it out here!: " + document.location.href
+        description: text + "\n\nCheck it out here!: " + changelog.url()

 module.exports.world = (level) ->
   goals = level.get 'goal'


Accept multiple references in a message

Accept multiple references in a message

--- src/phabricator.coffee
+++ src/phabricator.coffee
@@ -22,22 +22,16 @@
     user: process.env.HUBOT_PHABRICATOR_USER
     api: process.env.HUBOT_PHABRICATOR_API
     cert: process.env.HUBOT_PHABRICATOR_CERT
+    # logger: console
   }
   conduit = createCanduit config,
     (error, conduit) ->

   # object (the TDPQFV bit is the things we recognize as prefixes)
-  robot.hear /(?:^|[\[\s])([TDPQFV][0-9]+|r[A-Z]+[a-f0-9]+)(?:\s*(-v))?(?=\W|$)/, (msg) ->
-    conduit.exec 'phid.lookup', {names: [msg.match[1]]}, (error, result) ->
+  robot.hear /(?:^|[\[\s])([TDPQFV][0-9]+|r[A-Z]+[a-f0-9]+)(?:\s*(-v))?(?=\W|$)/g, (msg) ->
+    conduit.exec 'phid.lookup', {names: (match.trim() for match in msg.match)}, (error, result) ->
       if error
         return
-      for phid, info of result
-        msg.send '^ ' + info.fullName + ' - ' + info.uri
-        # switch info.type
-          # could do this... lot of extra work just for the username of the person it's assigned to
-          # when 'TASK'
-          #   conduit.exec 'maniphest.info', {task_id: info.name.slice(1)}, (error, result) ->
-          #     owner = result.ownerPHID
-          #     # fetch owner via phid.info...
-
-          #     msg = "^ " + info.name + " - " + result.title + ". Assigned to " + owner['fullName'] + ". " + result.uri
+      hits = ("^ #{info.fullName} - #{info.uri}" for phid, info of result).join("\n")
+      if hits
+        msg.send hits


This image is no longer available

This image is no longer available

--- src/victory.coffee
+++ src/victory.coffee
@@ -18,7 +18,6 @@
   "http://danceswithfat.files.wordpress.com/2011/08/victory.jpg",
   "http://wakpaper.com/large/Victory_wallpapers_166.jpg",
   "http://stuffpoint.com/family-guy/image/56246-family-guy-victory-is-his.gif",
-  "http://art.ngfiles.com/images/138/oman1996_victory-cat.png",
   "http://thelavisshow.files.wordpress.com/2012/06/victorya.jpg",
   "http://alookintomymind.files.wordpress.com/2012/05/victory.jpg",
   "http://rack.3.mshcdn.com/media/ZgkyMDEzLzA4LzA1L2QwL2JyYWRwaXR0LmJjMmQyLmdpZgpwCXRodW1iCTg1MHg1OTA-CmUJanBn/1a5a0c57/968/brad-pitt.jpg",


Add Jelly Fish

Add Jelly Fish

--- webapp/components/art/art-pieces.coffee
+++ webapp/components/art/art-pieces.coffee
@@ -217,12 +217,24 @@
 			media: "Oil and encaustic on canvas with wood framing"
 			"isFeatured" : true
 		},{
-			"name" : "Bird Creature with Babies",
-			"description" : "Abstract bird creature protecting babies within.",
-			"galleryId" : "Beautiful_Creatures",
-			"filename" : "2016-07-bird-creature-with-babies-oil-and-encaustic.jpg",
+			"name" : "Jelly Fish"
+			"description" : "Abstract jelly."
+			"galleryId" : "Beautiful_Creatures"
+			"filename" : "2016-08-jelly-fish-oil-and-encaustic.jpg"
 			"price" : "700"
 			"tags" : "",
+			widthInInches: 26
+			heightInInches: 36
+			depthInInches: 4
+			media: "Oil and encaustic on canvas with wood framing"
+			"isFeatured" : true
+		},{
+			"name" : "Bird Creature with Babies"
+			"description" : "Abstract bird creature protecting babies within."
+			"galleryId" : "Beautiful_Creatures"
+			"filename" : "2016-07-bird-creature-with-babies-oil-and-encaustic.jpg"
+			"price" : "700"
+			"tags" : ""
 			widthInInches: 26
 			heightInInches: 36
 			depthInInches: 4


tests: Expect Error as 'error'

tests: Expect Error as 'error'

--- spec/runner.coffee
+++ spec/runner.coffee
@@ -78,7 +78,7 @@
       page = ""
       options = {}
       solver.runJob filter, page, options, (err, solution, details) ->
-        chai.expect(err).to.be.a 'object'
+        chai.expect(err).to.be.a 'error'
         chai.expect(err.message).to.contain 'window.jsJobRun'
         done()

@@ -89,7 +89,7 @@
       page = ""
       options = {}
       solver.runJob filter, page, options, (err, solution, details) ->
-        chai.expect(err).to.be.a 'object'
+        chai.expect(err).to.be.a 'error'
         chai.expect(err.message).to.contain 'window.jsJobRun'
         done()

@@ -99,7 +99,7 @@
       page = ""
       options = {}
       solver.runJob filter, page, options, (err, solution, details) ->
-        chai.expect(err).to.be.a 'object'
+        chai.expect(err).to.be.a 'error'
         chai.expect(err.message).to.contain 'this is my error'
         done()

@@ -109,7 +109,7 @@
       page = ""
       options = {}
       solver.runJob filter, page, options, (err, solution, details) ->
-        chai.expect(err).to.be.a 'object'
+        chai.expect(err).to.be.a 'error'
         chai.expect(err.message).to.contain 'this error was thrown'
         chai.expect(err.stack).to.contain 'mythrowingfunction'
         chai.expect(err.stack).to.contain 'return-thrown-error.js:3'
@@ -122,7 +122,7 @@
       options = {}
       solver.runJob filter, page, options, (err, solution, details) ->
         chai.expect(solution).to.not.exist
-        chai.expect(err).to.be.a 'object'
+        chai.expect(err).to.be.a 'error'
         chai.expect(err.message).to.contain 'solution nor error'
         done()

@@ -144,7 +144,7 @@
       options = {}
       solver.runJob filter, page, options, (err, solution, details) ->
         chai.expect(solution).to.not.exist
-        chai.expect(err).to.be.an 'object'
+        chai.expect(err).to.be.an 'error'
         chai.expect(err.message).to.contain 'thrown in polySolvePage'
         done()

@@ -156,7 +156,7 @@
       options = {}
       solver.runJob filter, page, options, (err, solution, details) ->
         chai.expect(solution).to.not.exist
-        chai.expect(err).to.be.an 'object'
+        chai.expect(err).to.be.an 'error'
         chai.expect(err.message).to.contain 'thrown in a setTimeout'
         chai.expect(err.message).to.contain 'poly: main start'
         chai.expect(err.stack, 'stack should not duplicate message').to.not.contain 'poly: main start'
@@ -170,7 +170,7 @@
       options = {}
       solver.runJob filter, page, options, (err, solution, details) ->
         chai.expect(solution).to.not.exist
-        chai.expect(err).to.be.an 'object'
+        chai.expect(err).to.be.an 'error'
         chai.expect(err.message).to.contain 'function'
         chai.expect(err.message).to.contain 'line'
         done()
@@ -182,7 +182,7 @@
       options = {}
       solver.runJob filter, page, options, (err, solution, details) ->
         chai.expect(solution).to.not.exist
-        chai.expect(err).to.be.an 'object'
+        chai.expect(err).to.be.an 'error'
         chai.expect(err.message).to.contain 'function'
         chai.expect(err.message).to.contain 'line'
         done()
@@ -193,7 +193,7 @@
       page = ""
       options = {}
       solver.runJob filter, page, options, (err, solution, details) ->
-        chai.expect(err).to.be.a 'object'
+        chai.expect(err).to.be.a 'error'
         chai.expect(err.message).to.contain 'custom error'
         chai.expect(err).to.include.keys ['myproperty', 'otherproperty']
         chai.expect(err.myproperty).to.equal 'myvalue'
@@ -364,7 +364,7 @@
       page = ""
       options = {}
       solver.runJob filter, page, options, (err, solution, details) ->
-        chai.expect(err).to.be.a 'object'
+        chai.expect(err).to.be.a 'error'
         chai.expect(err.message).to.contain 'TIMEOUT'
         done()

@@ -406,7 +406,7 @@
       options = {}
       solver.runJob filter, page, options, (err, solution, details) ->
         chai.expect(solution).to.not.exist
-        chai.expect(err).to.be.a 'object'
+        chai.expect(err).to.be.a 'error'
         chai.expect(err.message).to.include 'hard timeout'
         chai.expect(err.message).to.include 'logged before infinite loop'
         chai.expect(err.message).to.include 'poly: starting'
@@ -478,7 +478,7 @@
       chai.expect(Object.keys(solver.jobs)).to.have.length 0
       solver.runJob filter, page, options, (err, outdata, details) ->
         chai.expect(outdata).to.not.exist
-        chai.expect(err).to.be.a 'object'
+        chai.expect(err).to.be.a 'error'
         chai.expect(err.message).to.not.include 'returned falsy'
         chai.expect(err.message).to.not.include 'and no Error'
         chai.expect(err.message).to.include 'child process terminated'
@@ -518,7 +518,7 @@
     options = {}
     solver.runJob filter, page, options, (err, outdata, details) ->
       chai.expect(outdata).to.not.exist
-      chai.expect(err).to.be.a 'object'
+      chai.expect(err).to.be.a 'error'
       chai.expect(err.message).to.include 'JsJob cancelled due to Runner.stop()'
       chai.expect(err.type).to.equal "cancelled" # so apps can detect this kind of error and ignore it, as likely they intended to do stop()
       done()


fixed namespacing

fixed namespacing

--- src/middleware/stats.coffee
+++ src/middleware/stats.coffee
@@ -13,10 +13,10 @@
   logger.info 'sending counts to statsd for ' + domain + '.' + ctx.authorisedChannel._id
   transactionStatus = ctx.transactionStatus
   try
-    sdc.increment domain # Overall Counter
-    sdc.increment domain + '.' + transactionStatus #Overall Transaction Status
-    sdc.increment domain + '.' + ctx.authorisedChannel._id # Per channel
-    sdc.increment domain + '.' + ctx.authorisedChannel._id + '.' + transactionStatus # Per Channel Status
+    sdc.increment domain + '.Channels' # Overall Counter
+    sdc.increment domain + '.Channels.' + transactionStatus #Overall Transaction Status
+    sdc.increment domain + '.Channels.' + ctx.authorisedChannel._id # Per channel
+    sdc.increment domain + '.Channels.' + ctx.authorisedChannel._id + '.' + transactionStatus # Per Channel Status
   catch error
     logger.error error

@@ -25,10 +25,10 @@
   logger.info 'sending durations to statsd for ' + domain + '.' + ctx.authorisedChannel._id
   transactionStatus = ctx.transactionStatus
   try
-    sdc.timing domain , timer # Overall Timer
-    sdc.timing domain + '.' + transactionStatus, timer # Overall Transaction Status
-    sdc.timing domain + '.' + ctx.authorisedChannel._id, timer # Per Channel
-    sdc.timing domain + '.' + ctx.authorisedChannel._id + '.' + transactionStatus, timer #Per Channel Status
+    sdc.timing domain + '.Channels'  , timer # Overall Timer
+    sdc.timing domain + '.Channels.' + transactionStatus, timer # Overall Transaction Status
+    sdc.timing domain + '.Channels.' + ctx.authorisedChannel._id, timer # Per Channel
+    sdc.timing domain + '.Channels.' + ctx.authorisedChannel._id + '.' + transactionStatus, timer #Per Channel Status
   catch error
     logger.error error


Configure zoo port

Configure zoo port

--- actions/hdp_zookeeper.coffee
+++ actions/hdp_zookeeper.coffee
@@ -14,6 +14,7 @@
   ctx.config.hdp.zookeeper_conf_dir ?= '/etc/zookeeper/conf'
   ctx.config.hdp.zookeeper_log_dir ?= '/var/log/zookeeper'
   ctx.config.hdp.zookeeper_pid_dir ?= '/var/run/zookeeper'
+  ctx.config.hdp.zookeeper_port ?= 2181

 ###
 Install
@@ -66,7 +67,7 @@
   {java_home} = ctx.config.java
   { hadoop_group,
     zookeeper_user, zookeeper_data_dir, zookeeper_pid_dir, zookeeper_log_dir,
-    zookeeper_myid
+    zookeeper_myid, zookeeper_port
   } = ctx.config.hdp
   do_zoo_cfg = ->
     # hosts = for host, i in hosts
@@ -89,7 +90,7 @@
       # the directory where the snapshot is stored.
       dataDir=#{zookeeper_data_dir}
       # the port at which the clients will connect
-      clientPort=2181
+      clientPort=#{zookeeper_port}
       #{mapping}
       """
       destination: '/etc/zookeeper/conf/zoo.cfg'


add (module bridge) Support popLayer method, openLayer method changes current location, added closeModal method support

--- src/bridges/ModuleBridge.coffee
+++ src/bridges/ModuleBridge.coffee
@@ -30,13 +30,17 @@
     switch message.method
       when "ping"
         successOptions.message = "PONG"
+      when "popLayer"
+        window.history.back()
       when "openLayer"
-        window.open message.parameters.url, "_blank"
+        window.location.href = message.parameters.url
       when "openURL"
         window.open message.parameters.url, "_blank"
       when "openModal"
         #window.open message.parameters.url, "_blank"
         steroids.component.helper.openModal message.parameters.url, {}
+      when "closeModal"
+        steroids.component.helper.closeModal()
       else
         console.log "ModuleBridge: unsupported API method: #{message.method}" unless message.method in failSilentlyMethods
         failed = true


todo item for getting println working

todo item for getting println working

--- src/runtime.coffee
+++ src/runtime.coffee
@@ -37,6 +37,7 @@
       if c.type is 'String'
         str = class_data.constant_pool.constant_pool[c.value].value
         cls = @class_lookup 'java/lang/String'
+        #TODO: actually make a real string object here (as in `new String(str);`)
         @string_pool[c.value] = {'type': 'java/lang/String', 'value':str}

   curr_frame: () -> _.last(@meta_stack)


Check instanceof Backbone.Model instead of Chaplin.Model in serializeAttributes function

Hi, was there a particular reason to check there for Chaplin.Model instead of Backbone.Model?

Using Backbone.Model there will allow easier mixin of Chaplin.Model with other classes inherited from Backbone.Model.

For example, in my case:

Backbone.Model -> Backbone.RelationalModel (https://github.com/PaulUithol/Backbone-relational)
Backbone.Model -> Chaplin.Model

If I want to have Chaplin.RelationalModel, then I do it in following way:

```javascript
var RelationalModel = Backbone.RelationalModel.extend();

_.extend(RelationalModel.prototype, utils.getObjectWithOwnProperties(Chaplin.Model.prototype));

return RelationalModel;
```

```javascript
getObjectWithOwnProperties: function(obj) {
            var props = {};

            for (var prop in obj){
                if (obj.hasOwnProperty(prop)) {
                    props[prop] = obj[prop];
                }
            }

            return props;
        }
```

So my RelationalModel will be of type Backbone.Model, but not of type Chaplin.Model, and serialization will work incorrectly.
The proposed change fixes this situation.

--- src/chaplin/models/model.coffee
+++ src/chaplin/models/model.coffee
@@ -39,7 +39,7 @@
         modelStack.push model
       # Map model/collection to their attributes
       for key, value of attributes
-        if value instanceof Model
+        if value instanceof Backbone.Model
           # Don’t change the original attribute, create a property
           # on the delegator which shadows the original attribute
           delegator ?= utils.beget attributes


change log level from trace to error mode for ES

--- src/coffee/elasticsearch.coffee
+++ src/coffee/elasticsearch.coffee
@@ -7,7 +7,7 @@
 			es = new elasticsearch.Client
 				host: config.host+":"+config.port
 				apiVersion: '1.3'
-				log: 'trace'
+				log: 'error'
 			console.log config
 			es.create
 				index: config.index


Remove empty js file

Remove empty js file

--- app/assets/javascripts/organization_invitation_request.js.coffee
+++ app/assets/javascripts/organization_invitation_request.js.coffee
@@ -1,3 +0,0 @@
-# Place all the behaviors and hooks related to the matching controller here.
-# All this logic will automatically be available in application.js.
-# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/


promisified

promisified

--- lib/Context.coffee
+++ lib/Context.coffee
@@ -482,10 +482,18 @@
         lookup: (name, identity, done)->
             if !done && typeof identity is 'function'
                 done = identity
-                identity = @identity
-
-            if !identity || !identity.id || !identity.token
+                identity = null
+            identity ?= @identity
+
+            if !identity.id || !identity.token
                 return done new Error '2. parameter is not identity'
+
+            if !done ## recurse promisifyed
+                return new Promise (resolve, reject)=>
+                    @lookup name, identity, (err, ctx)=>
+                        reject(err) if err
+                        resolve floyd.tools.objects.promisify ctx
+            ##

             ## myself
             if name is @id


use common footer text formatting when hovering or not

--- app/assets/javascripts/states/monitoring/helpers/graph.js.coffee
+++ app/assets/javascripts/states/monitoring/helpers/graph.js.coffee
@@ -11,6 +11,12 @@

   format_value = (val) ->
     _.add_commas(_.str.sprintf("%0.2f", val))
+
+  format_footer = (date, val, unit_label) ->
+    date = new Date(date) if !_.isDate(date)
+    date = strftime("%Y/%m/%d %H:%M:%S", date)
+    val = format_value(val)
+    _.str.sprintf("%s%s @ %s", val, unit_label, date)

   vals = metric.tuples()
   if !vals || vals.length == 0
@@ -25,8 +31,10 @@
       unit_label = "%"
     else
       unit_label = " " + unit
-  last_val = format_value(vals[vals.length-1][1])
-  footer_text = _.str.sprintf("Last Value: %s%s", last_val, unit_label)
+
+  last_date   = vals[vals.length-1][0]
+  last_val    = vals[vals.length-1][1]
+  footer_text = format_footer(last_date, last_val, unit_label)
   footer.text(footer_text)

   # draw graph
@@ -119,13 +127,9 @@
   g._bixby_touch_enabled = false # default to disabled

   # set callbacks - have to do this after initial graph created
-  xOptView = g.optionsViewForAxis_('x')
-  xvf = xOptView('valueFormatter')
   opts = {
     highlightCallback: (e, x, pts, row) ->
-      val = format_value(pts[0].yval)
-      date = xvf(x, xOptView, "", g)
-      footer.text(_.str.sprintf("%s, val = %s%s", date, val, unit_label))
+      footer.text(format_footer(x, pts[0].yval, unit_label))

     unhighlightCallback: (e) ->
       footer.text(footer_text)


Remove trailing slash from url

Remove trailing slash from url

Fixes #815

--- src/scripts/github-issue-link.coffee
+++ src/scripts/github-issue-link.coffee
@@ -32,7 +32,7 @@
     github.get "#{base_url}/repos/#{bot_github_repo}/issues/" + issue_number, (issue_obj) ->
       issue_title = issue_obj.title
       unless process.env.HUBOT_GITHUB_API
-        url = "https://github.com/"
+        url = "https://github.com"
       else
         url = base_url.replace /\/api\/v3/, ''
       msg.send "Issue #{issue_number}: #{issue_title} #{url}/#{bot_github_repo}/issues/#{issue_number}"


Spawn bower only if bower.json exists

Spawn bower only if bower.json exists

--- src/codio.coffee
+++ src/codio.coffee
@@ -19,7 +19,7 @@
   # If bower.json is present, run bower install detached.
   bowerPath = path.resolve cwd, 'bower.json'
   fs.exists bowerPath, (exists) ->
-    process.nextTick ->
+    if exists then process.nextTick ->
       # config.interactive is to prevent bower asking for
       # permission to collect statistics anonymously
       child = spawn 'bower',


render must return something

render must return something

--- src/smc-webapp/project_warnings.cjsx
+++ src/smc-webapp/project_warnings.cjsx
@@ -56,18 +56,18 @@

     render: ->
         if not require('./customize').commercial
-            return null
+            return
         quotas = @props.get_total_project_quotas(@props.project_id)
         project_status = @props.project_map?.get(@props.project_id)?.get('status')
         if not quotas?.memory? or not project_status?
-            return null
+            return
         else
             rss = project_status.get('memory')?.get('rss')
             if not rss
-                return
+                return
             memory = Math.round(rss/1000)
         if quotas.memory > memory + 5
-            return null
+            return

         <Alert bsStyle='danger' style={alert_style}>
             <Icon name='exclamation-triangle' /> WARNING: This project is running low on RAM memory.  Upgrade memory in @actions(project_id: @props.project_id).set_active_tab('settings')} style={cursor:'pointer'}>settings, restart your project or kill some processes.


fix message in RAM warning to be professional and helpful

--- src/smc-webapp/project_warnings.cjsx
+++ src/smc-webapp/project_warnings.cjsx
@@ -69,5 +69,5 @@
             return null

         <Alert bsStyle='danger' style={alert_style}>
-            <Icon name='exclamation-triangle' /> WARNING: This project is running out of RAM. Please increase the quota in @actions(project_id: @props.project_id).set_active_tab('settings')} style={cursor:'pointer'}>settings or else suffer?
+            <Icon name='exclamation-triangle' /> WARNING: This project is running out of RAM.  Upgrade memory in @actions(project_id: @props.project_id).set_active_tab('settings')} style={cursor:'pointer'}>settings, restart your project or kill some processes.
         </Alert>


Log error if DB.get promise is rejected.

--- src/node_modules/api.coffee
+++ src/node_modules/api.coffee
@@ -7,6 +7,7 @@
 YAML = require 'js-yaml'
 qs = require 'qs'

+logger = require '../../lib/log.coffee'
 Crypto = require '../../lib/crypto.coffee'
 sendMail = require '../../lib/send-mail.coffee'
 utils = require '../scripts/utils.coffee'
@@ -80,8 +81,10 @@
           startkey: getKey params.slug, date.add(1, durationKey)
     query.slug = params.slug if params.slug
     query.descending = true
-    DB.get(view, query).then (body) ->
-      docs: body.map setArticleParams
+    DB.get view, query
+      .then (body) ->
+        docs: body.map setArticleParams
+      .catch logger.error
   fetchLocaleStrings: (_lang) ->
     lang = Lang.negotiateLang _lang
     if !lang


remove stray functions

remove stray functions

--- lib/atomizr.coffee
+++ lib/atomizr.coffee
@@ -373,18 +373,6 @@
     editor.setGrammar(atom.grammars.grammarForScopeName('text.xml.subl'))
     @renameFile(editor, "sublime-snippet")

-  addTrailingTabstops: (input) ->
-    unless input.match(/\$\d+$/g) is null and atom.config.get('atomizr.addTrailingTabstops') is not false
-      # nothing to do here
-      return input
-
-    return "#{input}$0"
-
-  removeTrailingTabstops: (input) ->
-    if input.match(/\$\d+$/g) is null or atom.config.get('atomizr.removeTrailingTabstops') is false
-      # nothing to do here
-      return input
-
     return input.replace(/\$\d+$/g, "")

   renameFile: (editor, extension) ->


auto select first item on initial open

auto select first item on initial open

--- lib/ui.coffee
+++ lib/ui.coffee
@@ -99,10 +99,6 @@

     @disposables.add @onDidMoveToItemArea  => @setReadOnly(true)

-    # [FIXME] While in dev
-    # @disposables.add atom.workspace.onDidChangeActivePaneItem (item) ->
-    #   console.log 'activePaneItemChange', item
-
     @disposables.add(
       @registerCommands()
       @observeChange()
@@ -189,8 +185,11 @@
       @focus()

   activateProviderPane: ->
-    if (pane = @provider.getPane()) and pane.isAlive()
+    if pane = @provider.getPane()
       pane.activate()
+      activeItem = pane.getActiveItem()
+      if isTextEditor(activeItem)
+        activeItem.scrollToCursorPosition()

   destroy: ->
     return if @destroyed
@@ -309,12 +308,6 @@
     if moveToPrompt
       @moveToPrompt()

-    # In case prompt accidentaly mutated
-    eof = @editor.getEofBufferPosition()
-    if eof.isLessThan(@itemAreaStart)
-      eof = @setPrompt().end
-      @moveToPrompt()
-
     filterSpec = getFilterSpecForQuery(@getQuery())

     Promise.resolve(@cachedItems ? @provider.getItems()).then (items) =>
@@ -335,8 +328,12 @@

   renderItems: (items) ->
     texts = items.map (item) => @provider.viewForItem(item)
-    itemArea = new Range(@itemAreaStart, @editor.getEofBufferPosition())
     @withIgnoreChange =>
+      if @editor.getLastBufferRow() is 0
+        # Need to recover query prompt
+        @setPrompt()
+        @moveToPrompt()
+      itemArea = new Range(@itemAreaStart, @editor.getEofBufferPosition())
       range = @editor.setTextInBufferRange(itemArea, texts.join("\n"), undo: 'skip')
       @editorLastRow = range.end.row

@@ -376,7 +373,8 @@
           # Destroy cursors on prompt to protect query from mutation on 'find-and-replace:select-all'( cmd-alt-g ).
           for selection in @editor.getSelections() when onPrompt(selection.getBufferRange())
             selection.destroy()
-          @setPrompt(@lastNarrowQuery) # Recover query
+          @withIgnoreChange =>
+            @setPrompt(@lastNarrowQuery) # Recover query
         else
           @refresh().then =>
             if @autoPreviewOnQueryChange and @isActive()
@@ -453,7 +451,6 @@

   syncToEditor: (editor) ->
     return if @preventSyncToEditor
-    return if @isActive() # Prevent UI cursor from being moved while UI is active.
     if item = @findClosestItemForEditor(editor)
       @selectItem(item)
       @moveToSelectedItem() unless @isActive()
@@ -561,10 +558,7 @@
   setPrompt: (text='') ->
     if @editor.getLastBufferRow() is 0
       text += "\n"
-
-    range = null
-    @withIgnoreChange =>
-      range = @editor.setTextInBufferRange(@getPromptRange(0), text)
+    range = @editor.setTextInBufferRange(@getPromptRange(0), text)
     range

   setSyncToEditor: (editor) ->


Upgraded to new config API

Upgraded to new config API

--- lib/caniuse.coffee
+++ lib/caniuse.coffee
@@ -3,21 +3,59 @@
 module.exports =
   caniuseView: null

-  configDefaults:
-    showIe: true
-    showFirefox: true
-    showChrome: true
-    showSafari: true
-    showOpera: false
-    showIosSaf: true
-    showOpMini: false
-    showAndroid: true
-    showOpMob: false
-    showBb: false
-    showAndChr: true
-    showAndFf: false
-    showIeMob: false
-    showAndUc : false
+  config:
+    showIe:
+      title: 'Show IE'
+      type:'boolean'
+      default:  true
+    showFirefox:
+      type:'boolean'
+      default:  true
+    showChrome:
+      type:'boolean'
+      default:  true
+    showSafari:
+      type:'boolean'
+      default:  true
+    showOpera:
+      type:'boolean'
+      default:  false
+    showIosSaf:
+      title: 'Show iOS Safari'
+      type:'boolean'
+      default:  true
+    showOpMini:
+      title: 'Show Opera Mini'
+      type:'boolean'
+      default:  false
+    showAndroid:
+      title: 'Android Browser'
+      type:'boolean'
+      default:  true
+    showOpMob:
+      title: 'Show Opera Mobile'
+      type:'boolean'
+      default:  false
+    showBb:
+      title: 'Show Blackberry Browser'
+      type:'boolean'
+      default:  false
+    showAndChr:
+      title: 'Show Chrome for Android'
+      type:'boolean'
+      default:  true
+    showAndFf:
+      title: 'Show Firefox for Android'
+      type:'boolean'
+      default:  false
+    showIeMob:
+      title: 'Show IE Mobile'
+      type:'boolean'
+      default:  false
+    showAndUc:
+      title: 'Show UC Browser for Android'
+      type:'boolean'
+      default:  false

   activate: (state) ->
     atom.workspaceView.command 'caniuse:show', =>


Output into custom directory

Output into custom directory

--- lib/command.coffee
+++ lib/command.coffee
@@ -33,6 +33,24 @@
     console.warn o.output
     return

+
+writeFile = (dir, currfile, coffee) ->
+  outputdir = opts.output || '.'
+  try
+    if (outputdir.search '/') == -1
+      outputdir = outputdir.concat '/'
+    newPath = outputdir + dir + '/'
+    try
+      fs.statSync(newPath).isDirectory()
+    catch e
+      file.mkdirsSync(newPath)
+    currfile = (currfile.split '.')[0] + '.coffee'
+    newFile = newPath + currfile
+    console.warn "writing %s ", newFile
+    fs.writeFileSync(newFile, coffee, encoding);
+  catch e
+    console.warn e
+
 batch = () ->

   callback = (dirPath, dirs, files) ->
@@ -43,8 +61,7 @@
           console.warn "read file %s", readf
           contents = fs.readFileSync(readf, encoding);
           output = js2coffee.build(contents)
-          newFileName =  dirPath + '/' + (f.split '.')[0] + '.coffee'
-          fs.writeFileSync(newFileName, output, encoding)
+          writeFile(dirPath, f, output)
       catch e
         console.warn e


use review route to preload stats store for review page

--- src/flux/task-teacher-review.coffee
+++ src/flux/task-teacher-review.coffee
@@ -1,6 +1,17 @@
+{TaskPlanStatsActions, TaskPlanStatsStore} = require './task-plan-stats'
+_ = require 'underscore'
+
+
 {CrudConfig, makeSimpleStore, extendConfig} = require './helpers'

 TaskTeacherReviewConfig = {
+  _loaded: (obj, id) ->
+    planStats = _.clone(obj)
+    _.each planStats.stats.periods, (period) ->
+      delete period.current_pages.exercises if period.current_pages.exercises?
+      delete period.spaced_pages.exercises if period.spaced_pages.exercises?
+
+    TaskPlanStatsActions.loaded(planStats, id)

 }


typo fix

typo fix

--- src/parser/js-parser.coffee
+++ src/parser/js-parser.coffee
@@ -108,7 +108,7 @@
   functiondef: (line, convention, commitUrl) ->
     convention = {lang: this.lang} unless convention
     (convention.functiondef =
-      title: "Function followed by one space vs. Function follwed by no space"
+      title: "Function followed by one space vs. Function followed by no space"
       column: [
         {
           key: "onespace", display: "One space",


Removed old code cruft

Removed old code cruft

--- src/thumblr.coffee
+++ src/thumblr.coffee
@@ -13,15 +13,6 @@
     zoomFactor: 0.5
     formatter: "thumb_%d.png"

-  _titles: 0 # value must be set in the constructor for instances
-  _privateStuff: ()->
-    console.log "Private by convention only"
-
-    ###
-    Public Members
-    ###
-    get_count: ->
-      # Do stuff
   browserOptions: {}
   queue: null


Link to form validation source

Link to form validation source

--- src/rules/color.coffee
+++ src/rules/color.coffee
@@ -1,6 +1,8 @@
 # Color validator
 #
 # Test color validity
+#
+# From: https://github.com/formvalidation/formvalidation/blob/master/src/js/validator/color.js

 VanillaFormValidator.addRule 'color', (value, opts) ->


Fix bug that prevented to go to position 0 (missing ?)

--- lib/editors.coffee
+++ lib/editors.coffee
@@ -73,15 +73,15 @@
     console.log
     cursor = @_cursors.cursors[param.id]
     if cursor? and cursor.color == param.color
-      fun = (index) -> @_cursors.moveCursor param.id, index
+      fun = (index) => @_cursors.moveCursor param.id, index
     else
-      if cursor.color != param.color
+      if cursor? and cursor.color? and cursor.color != param.color
         @removeCursor param.id

-      fun = (index) ->  @_cursors.setCursor( param.id, index,
+      fun = (index) =>  @_cursors.setCursor( param.id, index,
         param.author, param.color)

-    if param.index
+    if param.index?
       fun param.index

   removeCursor: (id) ->


Implement new tool tip in scatterplot

Implement new tool tip in scatterplot

--- src/scatterplot.coffee
+++ src/scatterplot.coffee
@@ -94,7 +94,7 @@
     # function to remove chart
     chart.remove = () ->
                       svg.remove()
-                      indtip.destroy()
+                      d3panels.tooltip_destroy(indtip)
                       null

     # return the chart function


Make interpolated class selectors for buttons, make square a wrapper for exponent

--- src/javascripts/math_buttons.coffee
+++ src/javascripts/math_buttons.coffee
@@ -30,28 +30,28 @@
   addition: (opts)->
     @button({
       value: '+'
-      class: 'jc--button jc--button-operation'
+      class: 'jc--button jc--button-operation jc--button-add'
     }, opts)

   subtraction: (opts)->
     @button({
       value: '-'
       label: '−'
-      class: 'jc--button jc--button-operation'
+      class: 'jc--button jc--button-operation jc--button-subtract'
     }, opts)

   multiplication: (opts)->
     @button({
       value: '*'
       label: '×'
-      class: 'jc--button jc--button-operation'
+      class: 'jc--button jc--button-operation jc--button-multiply'
     }, opts)

   division: (opts)->
     @button({
       value: '/'
       label: '÷'
-      class: 'jc--button jc--button-operation'
+      class: 'jc--button jc--button-operation jc--button-divide'
     }, opts)

   equals: (opts)->
@@ -64,13 +64,13 @@
   lparen: (opts)->
     @button({
       value: '('
-      class: 'jc--button jc--button-other jc--button-parentheses'
+      class: 'jc--button jc--button-other jc--button-rParen'
     }, opts)

   rparen: (opts)->
     @button({
       value: ')'
-      class: 'jc--button jc--button-other jc--button-parentheses'
+      class: 'jc--button jc--button-other jc--button-lParen'
     }, opts)

   pi: (opts)->
@@ -87,13 +87,6 @@
       class: 'jc--button jc--button-other jc--button-fraction'
     }, opts)

-  square: (opts)->
-    @button({
-      value: 'square'
-      label: 'x2'
-      class: 'jc--button jc--button-other jc--button-square'
-    }, opts)
-
   caret: (opts)->
     @button({
       value: '^'
@@ -106,17 +99,22 @@
     @button({
       value: 'exponent'
       label: "#{base}#{power}"
-      class: 'jc--button jc--button-other jc--button-exponent'
+      class: 'jc--button jc--button-other jc--button-exponent jc--button-exponent-#{base}to#{power}'
     }, opts)
+
+  square: (opts)->
+    opts ||= {}
+    opts.base = 'x'
+    opts.power = '2'
+    @exponent(opts)

   root: (opts)->
     degree = if opts.degree then "#{opts.degree}" else ""
     radicand = opts.radicand || 'x'
-
     @button({
       value: 'root'
       label: "#{degree}√#{radicand}"
-      class: 'jc--button jc--button-other jc--button-root'
+      class: 'jc--button jc--button-other jc--button-root jc--button-root-#{degree}of#{radicand}'
     }, opts)

   # EQ builder vars
@@ -145,10 +143,7 @@
     }, opts)

   fn: (opts)->
-    value = if opts.name
-      "function[#{opts.name}]"
-    else
-      "function"
+    value = if opts.name then "function[#{opts.name}]" else "function"
     @button({
       value: value
       label: 'ƒ'


Using moment for date formatting, updated queue output string

--- src/scripts/aws.coffee
+++ src/scripts/aws.coffee
@@ -6,9 +6,9 @@
 # Environment variables:
 #   HUBOT_AWS_ACCESS_KEY_ID     - The Amazon access key id
 #   HUBOT_AWS_SECRET_ACCESS_KEY - The Amazon secret key for the given id
+#   HUBOT_AWS_SQS_REGIONS - Comma separated list of regions to query
 #
-#
-# package.json needs to have "aws2js":"0.6.12" and "moment":"1.6.2"
+# package.json needs to have "aws2js":"0.6.12" and "moment":"1.6.2" and "underscore":"1.3.3"
 #
 # It's highly recommended to use a read-only IAM account for this purpose
 # https://console.aws.amazon.com/iam/home?#
@@ -18,64 +18,74 @@
 key = process.env.HUBOT_AWS_ACCESS_KEY_ID
 secret = process.env.HUBOT_AWS_SECRET_ACCESS_KEY

+_ = require 'underscore'
 moment = require 'moment'
 aws = require 'aws2js'
 sqs = aws
   .load('sqs', key, secret)
   .setApiVersion('2011-10-01')

+getRegionQueues = (region, msg) ->
+  sqs.setRegion(region).request 'ListQueues', {}, (error, queues) ->
+    if error?
+      msg.send 'Failed to list queues for region #{region} - error #{error}'
+      return
+
+    urls = _.flatten [queues.ListQueuesResult?.QueueUrl ? []]
+
+    msg.send "Found #{urls.length} queues for region #{region}..."
+
+    urls.forEach (url) ->
+
+      url = url['#']
+      name = url.split '/'
+      name = name[name.length - 1]
+      path = url.replace "https://sqs.#{region}.amazonaws.com", ''
+
+      queue =
+        Version: '2011-10-01'
+        AttributeName: ['All']
+
+      sqs.setRegion(region).setQueue(path + '/')
+      .request 'GetQueueAttributes', queue, (error, attributes) ->
+        if error?
+          msg.send "Can't read queue attributes [#{name}] (path #{path})" +
+            " - #{url} - #{error}"
+          return
+
+        info = attributes.GetQueueAttributesResult.Attribute
+        for index, attr of info
+          switch attr.Name
+            when 'ApproximateNumberOfMessages' \
+              then msgCount = attr.Value
+            when 'ApproximateNumberOfMessagesNotVisible' \
+              then inFlight = attr.Value
+
+        queue.MaxNumberOfMessages = 1
+        queue.VisibilityTimeout = 0
+
+        sqs.setRegion(region).setQueue(path + '/')
+        .request 'ReceiveMessage', queue, (error, result) ->
+
+          queueDesc = "[SQS: #{name}] - [#{msgCount}] total msgs" +
+            " / [#{inFlight}] in flight"
+          if error?
+            timestamp = "unavailable - #{error}"
+          else
+            sqsmsg = result.ReceiveMessageResult.Message
+            if sqsmsg? and sqsmsg.Attribute?
+              timestamp = (att for att in sqsmsg.Attribute \
+                when att.Name == 'SentTimestamp')
+              timestamp = (moment parseFloat timestamp[0].Value)
+                .format 'ddd, L LT'
+            else
+              timestamp = 'none available'
+
+          msg.send "#{queueDesc} / oldest msg ~[#{timestamp}] / #{url}"
+
+defaultRegions = 'us-east-1,us-west-1,us-west-2,eu-west-1,ap-southeast-1,ap-northeast-1'
+
 module.exports = (robot) ->
   robot.respond /(^|\W)sqs status(\z|\W|$)/i, (msg) ->
-
-    sqs.request 'ListQueues', {}, (error, queues) ->
-      if error?
-        msg.send 'Failed to list queues'
-        return
-
-      queues.ListQueuesResult.QueueUrl.forEach (url) ->
-
-        url = url['#']
-        name = url.split '/'
-        name = name[name.length - 1]
-        path = url.replace 'https://queue.amazonaws.com', ''
-
-        queue =
-          Version: '2011-10-01'
-          AttributeName: ['All']
-
-        sqs.setQueue(path + '/')
-        .request 'GetQueueAttributes', queue, (error, attributes) ->
-          if error?
-            msg.send "Can't read queue attributes [#{name}] (path #{path})" +
-              " - #{url} - #{error}"
-            return
-
-          info = attributes.GetQueueAttributesResult.Attribute
-          for index, attr of info
-            switch attr.Name
-              when 'ApproximateNumberOfMessages' \
-                then msgCount = attr.Value
-              when 'ApproximateNumberOfMessagesNotVisible' \
-                then inFlight = attr.Value
-
-          queue.MaxNumberOfMessages = 1
-          queue.VisibilityTimeout = 0
-
-          sqs.setQueue(path + '/')
-          .request 'ReceiveMessage', queue, (error, result) ->
-
-            queueDesc = "[SQS: #{name}] - [#{msgCount}] total msgs" +
-              " / [#{inFlight}] in flight"
-            if error?
-              timestamp = "unavailable - #{error}"
-            else
-              sqsmsg = result.ReceiveMessageResult.Message
-              if sqsmsg? and sqsmsg.Attribute?
-                timestamp = (att for att in sqsmsg.Attribute \
-                  when att.Name == 'SentTimestamp')
-                timestamp = (moment parseFloat timestamp[0].Value)
-                  .format 'ddd, L LT'
-              else
-                timestamp = 'none available'
-
-            msg.send "#{queueDesc} / oldest msg ~[#{timestamp}] / #{url}"
+    regions = process.env?.HUBOT_AWS_SQS_REGIONS ? defaultRegions
+    getRegionQueues region, msg for region in regions.split ','


fix indentation

fix indentation

--- src/stackmat.js.coffee
+++ src/stackmat.js.coffee
@@ -174,17 +174,19 @@
     getPacket(bits[1..(bits.length - 1)])

 class Stackmat.Timer
-  supported = =>
-    !!(navigator.getUserMedia || navigator.webkitGetUserMedia ||
-      navigator.mozGetUserMedia || navigator.msGetUserMedia)
+  supported = ->
+    !!(navigator.getUserMedia ||
+       navigator.webkitGetUserMedia ||
+       navigator.mozGetUserMedia ||
+       navigator.msGetUserMedia)

   audioContext = =>
     if (typeof AudioContext == "function")
-        new AudioContext()
+      new AudioContext()
     else if (typeof webkitAudioContext == "function")
-        new webkitAudioContext();
+      new webkitAudioContext();
     else
-        throw new Error('AudioContext not supported. :(')
+      throw new Error('AudioContext not supported. :(')

   constructor: (options) ->
     if !supported()
@@ -202,7 +204,7 @@
     @rs232Decoder = new Stackmat.RS232Decoder(audioContext().sampleRate / 1200)
     @stackmatSignalDecoder = new Stackmat.SignalDecoder()

-    navigator.webkitGetUserMedia {audio: true}, (stream) => # TODO move to start()?
+    navigator.webkitGetUserMedia {audio: true}, (stream) => # TODO don't limit to webkit
       microphone = audioContext().createMediaStreamSource(stream)
       @device = new Stackmat.AudioHardware(microphone, @signalFetched)


Basic audio looping. Looks like there's no easy ggapless solution, but this will do for now

--- src/sound.coffee
+++ src/sound.coffee
@@ -1,9 +1,12 @@
+# TODO: issues with sounds attempting to be played before they're loaded
+#
+# TODO: docs
+
 # Ex:
 #   Kona.Sounds.load {
 #     'fire' : 'enemy_fire.ogg'
 #   }
 #   Kona.Sounds.play('fire')
-
 Kona.Sounds =
   sounds: {}

@@ -27,7 +30,7 @@
   # --------------------
   @defaults:
     autoplay: false
-    duration: 5000
+    duration: -1
     formats: []
     loop: false
     placeholder: '--'
@@ -69,11 +72,13 @@
   # Instance methods
   # --------------------
   # Options:
-  #  - formats
-  #  - (x) loop
-  #  - autoplay
-  #  - preload
-  #  - volume
+  #
+  #   * __formats__ - (Array[String]) TODO
+  #   * __loop__ - (Boolean) Whether or not to continuously play the sound in a loop
+  #   * __autoplay__ - (Boolean) Whether or not to play the sound as soon as its loaded
+  #   * __preload__ - (Boolean) TODO
+  #   * __volume__ - (Integer) A number from 1-100 indicating the volume to play the sound at (default 100)
+  #
   constructor: (src, opts={}) ->
     @supported  = Kona.Sound.isSupported()

@@ -91,7 +96,7 @@
     else
       @addSource(@el, src)

-    # @loop() if opts.loop
+    @loop() if opts.loop == true
     @el.autoplay = 'autoplay' if opts.autoplay == true
     @el.preload  = if opts.preload == true then 'auto' else 'none'
     @setVolume(opts.volume)
@@ -188,6 +193,8 @@
   #   r = Math.pow(10, decimal || 0)
   #   return Math.round(((total / 100) * percent) * r) / r

-  # loop: ->
-
-  # unloop: ->
+  loop: ->
+    @el.loop = 'loop'
+
+  unloop: ->
+    @el.loop = null


small comment fix

small comment fix

--- lib/orpheus.coffee
+++ lib/orpheus.coffee
@@ -348,7 +348,7 @@
 		# Redis multi commands
 		@_commands = []

-		# Query flags
+		# Initialize the query flags
 		@flags = {}

 		@validation_errors = new OrpheusValidationErrors


Allow for an initial zero

Allow for an initial zero

--- app/assets/javascripts/angular/directives/smartNumber.js.coffee
+++ app/assets/javascripts/angular/directives/smartNumber.js.coffee
@@ -6,7 +6,7 @@
     defaultOptions = smartNumberConfig.defaultOptions

     # 0 = tab, 8 = backspace , 13 = enter, 46 = delete, 37 = left arrow, 39 = right arrow, 65 = A
-    controlKeys = [0,8,13, 46, 37, 39, 65]
+    controlKeys = [0,8,13, 46, 37, 39, 65]

     # 37 = left arrow, 39 = right arrow, 9 = enter, 33 = page up, 34 = page down
     inertKeys = [37, 39, 9, 33, 34]
@@ -58,7 +58,7 @@
             for own option, value of scope.$eval(attrs.smartNumberOptions)
                 options[option] = value
         options
-
+
     isNumber = (val) ->
         !isNaN(parseFloat(val)) && isFinite(val)

@@ -85,15 +85,11 @@
       inertKeys.indexOf(which) >= 0

     invalidInput = (elem, event) ->
-      invalidKey(event) or invalidZero(elem, event)
+      invalidKey(event)

     # invalid actions
     invalidKey = (event) ->
       isNotDigit(event.which) && isNotControlKey(event.which)
-
-    invalidZero = (elem, event) ->
-      # entering 0 in the first place
-      elem[0].selectionStart == 0 and event.which == 48

     maxDigitsReached = (elem, maxDigits) ->
       elem.val().length >= maxDigits
@@ -109,7 +105,7 @@
         validRegex = new RegExp regexString

         (val) -> validRegex.test val
-
+
     makeMaxNumber = (maxNumber) ->
         (val, number) -> number <= maxNumber

@@ -212,7 +208,7 @@

     makeIsValid = (options) ->
         validations = []
-
+
         if options.maxDecimals?
             validations.push makeMaxDecimals options.maxDecimals
         if options.max?
@@ -221,7 +217,7 @@
             validations.push makeMinNumber options.min
         if options.maxDigits?
             validations.push makeMaxDigits options.maxDigits
-
+
         (val) ->
             return false unless isNumber val
             return false if hasMultipleDecimals val
@@ -229,7 +225,7 @@
             for i in [0...validations.length]
                 return false unless validations[i] val, number
             true
-
+
     addCommasToInteger = (val) ->
         decimals = `val.indexOf('.') == -1 ? '' : val.replace(/^-?\d+(?=\.)/, '')`
         wholeNumbers = val.replace /(\.\d+)$/, ''
@@ -298,9 +294,9 @@

               # do nothing if the key pressed isn't a permissable operation
               return if invalidInput(elem, event)
-
+
               # find the cursor position
-              initialPosition = elem[0].selectionStart
+              initialPosition = elem[0].selectionStart

               # do different stuff if ctrl is pressed
@@ -341,7 +337,7 @@

                   ## replacing a selected area with a number character
                   if elem[0].selectionEnd > elem[0].selectionStart # text is highlighted
-
+
                     # get the string before the selection and figure out how many commas it has
                     originalCursorPosition = elem[0].selectionStart
                     stringBeforeCursor = elem.val().slice(0, originalCursorPosition)
@@ -403,7 +399,7 @@

                     # trigger final change event
                     triggerChange(elem)
-
+
                 if isDelete(event) # delete is pressed
                   if elem[0].selectionEnd > elem[0].selectionStart # text is highlighted
                     # get the string before the selection and figure out how many commas it has
@@ -435,7 +431,7 @@

                     # trigger final change event
                     triggerChange(elem)
-
+
             elem.on 'keypress', (event) ->
               killEvent(event)


Follow up: Fixed issue #1709 - Use 'samaccountname' as login default.

--- app/assets/javascripts/app/controllers/_integration/ldap.coffee
+++ app/assets/javascripts/app/controllers/_integration/ldap.coffee
@@ -418,7 +418,7 @@
       if !_.isArray(user_attributes[key])
         user_attributes[key] = [user_attributes[key]]
     user_attributes_local =
-      "#{@wizardConfig['user_uid']}": 'login'
+      'samaccountname': 'login'
     length = user_attributes.source.length-1
     for count in [0..length]
       if user_attributes.source[count] && user_attributes.dest[count]
@@ -449,7 +449,7 @@
   buildRowsUserMap: (user_attribute_map) =>

     # show static login row
-    userUidDisplayValue = @wizardConfig.wizardData.backend_user_attributes[ @wizardConfig['user_uid'] ]
+    userUidDisplayValue = @wizardConfig.wizardData.backend_user_attributes['samaccountname']

     el = [
       $(App.view('integration/ldap_user_attribute_row_read_only')(
@@ -458,7 +458,7 @@
       ))
     ]
     for source, dest of user_attribute_map
-      continue if source == @wizardConfig['user_uid']
+      continue if source == 'samaccountname'
       continue if !(source of @wizardConfig.wizardData.backend_user_attributes)
       el.push @buildRowUserAttribute(source, dest)
     el


Improved code layout.

Improved code layout.

--- app/assets/javascripts/app/controllers/reset_password.js.coffee
+++ app/assets/javascripts/app/controllers/reset_password.js.coffee
@@ -4,9 +4,9 @@
   className: 'container'

   events:
-    'submit form': 'submit',
-    'click .submit': 'submit',
-    'click .retry': 'rerender',
+    'submit form': 'submit'
+    'click .submit': 'submit'
+    'click .retry': 'rerender'

   constructor: ->
     super
@@ -25,9 +25,9 @@
     @html App.view('reset_password')()

     new App.ControllerForm(
-      el: @el.find('#form-password'),
-      model: { configure_attributes: configure_attributes },
-      autofocus: true,
+      el:        @el.find('#form-password')
+      model:     { configure_attributes: configure_attributes }
+      autofocus: true
     )

   rerender: (e) ->
@@ -40,26 +40,26 @@

     # get data
     App.Com.ajax(
-      id:   'password_reset',
-      type: 'POST',
-      url:  '/api/users/password_reset',
-      data: JSON.stringify(params),
-      processData: true,
-      success: @success,
-      error: @error,
+      id:   'password_reset'
+      type: 'POST'
+      url:  '/api/users/password_reset'
+      data: JSON.stringify(params)
+      processData: true
+      success: @success
+      error:   @error
     )
-
+
   success: (data, status, xhr) =>
     @html App.view('generic/hero_message')(
-      head:    'We\'ve sent password reset instructions to your email address',
+      head:    'We\'ve sent password reset instructions to your email address'
       message: 'If you don\'t receive instructions within a minute or two, check your email\'s spam and junk filters, or try resending your request.'
-    );
+    )

   error: (data, status, xhr) =>
     @html App.view('generic/hero_message')(
-      head:    'Problem',
+      head:    'Problem'
       message: 'Username or email address invalid, please go back and try again.'
-    );
+    )

 App.Config.set( 'reset_password', Index, 'Routes' )

@@ -67,8 +67,8 @@
   className: 'container'

   events:
-    'submit form': 'submit',
-    'click .submit': 'submit',
+    'submit form': 'submit'
+    'click .submit': 'submit'

   constructor: ->
     super
@@ -81,11 +81,11 @@
     params = {}
     params['token'] = @token
     App.Com.ajax(
-      id:   'password_reset_verify',
-      type: 'POST',
-      url:  '/api/users/password_reset_verify',
-      data: JSON.stringify(params),
-      processData: true,
+      id:   'password_reset_verify'
+      type: 'POST'
+      url:  '/api/users/password_reset_verify'
+      data: JSON.stringify(params)
+      processData: true
       success: @render_success
       error:   @render_failed
     )
@@ -98,16 +98,16 @@
     @html App.view('reset_password_change')()

     new App.ControllerForm(
-      el: @el.find('#form-password-change'),
-      model: { configure_attributes: configure_attributes },
-      autofocus: true,
+      el:        @el.find('#form-password-change')
+      model:     { configure_attributes: configure_attributes }
+      autofocus: true
     )

   render_failed: ->
     @html App.view('generic/hero_message')(
-      head:    'Failed!',
+      head:    'Failed!'
       message: 'Token is not valid!'
-    );
+    )

   submit: (e) ->
     e.preventDefault()
@@ -116,25 +116,25 @@

     # get data
     App.Com.ajax(
-      id:   'password_reset_verify',
-      type: 'POST',
-      url:  '/api/users/password_reset_verify',
-      data: JSON.stringify(params),
-      processData: true,
+      id:   'password_reset_verify'
+      type: 'POST'
+      url:  '/api/users/password_reset_verify'
+      data: JSON.stringify(params)
+      processData: true
       success: @render_changed_success
       error:   @render_changed_failed
     )

   render_changed_success: (data, status, xhr) =>
     @html App.view('generic/hero_message')(
-      head:    'Woo hoo! Your password has been changed!',
-      message: 'Please try to login!',
-    );
+      head:    'Woo hoo! Your password has been changed!'
+      message: 'Please try to login!'
+    )

-  render_changed_failed: ->
+  render_changed_failed: =>
     @html App.view('generic/hero_message')(
-      head:    'Failed!',
-      message: 'Ask your admin!',
-    );
+      head:    'Failed!'
+      message: 'Ask your admin!'
+    )

 App.Config.set( 'password_reset_verify/:token', Verify, 'Routes' )


Improved code layout.

Improved code layout.

--- app/assets/javascripts/app/controllers/taskbar_widget.js.coffee
+++ app/assets/javascripts/app/controllers/taskbar_widget.js.coffee
@@ -69,7 +69,7 @@
           if !key
             throw "No such key attributes found for task item"
           order.push key
-        App.TaskManager.reorder( order  )
+        App.TaskManager.reorder( order )

     @el.sortable( dndOptions )


The tick method does not need to return anything.

--- app/assets/javascripts/modules/timelet/models/timelet.js.coffee
+++ app/assets/javascripts/modules/timelet/models/timelet.js.coffee
@@ -31,8 +31,6 @@

   # Decrements the timer value by one until it reaches zero.
   #
-  # @return [Boolean] `false` if the timer finished, otherwise `true`
-  #
   tick: =>
     @set timer: (@get('timer') - 1)
     @stop() if @isFinished()
@@ -44,7 +42,6 @@

     @set running: true
     @runner = setInterval @tick, 1000
-    true

   # Stops the timer.
   #


fix animation

fix animation

--- lib/results.coffee
+++ lib/results.coffee
@@ -14,7 +14,9 @@
       'inline-results:clear-all': => @removeAll()

     @subs.add atom.commands.add '.ink.inline',
-      'inline-results:clear': (e) => e.currentTarget.result.destroy()
+      'inline-results:clear': (e) ->
+        result = e.currentTarget.result
+        setTimeout (-> result.destroy()), 0

   deactivate: ->
     @subs.dispose()


Fault when vparam returns invalid value or type

--- lib/session.coffee
+++ lib/session.coffee
@@ -432,6 +432,25 @@
             return counter = 0

           if _returnValue.value?
+            if not Array.isArray(_returnValue.value)
+              _returnValue.value = [_returnValue.value]
+
+            if not _returnValue.value[1]
+              if typeof _returnValue.value[0] is 'number'
+                _returnValue.value[1] = 'xsd:int'
+              else if typeof _returnValue.value[0] is 'boolean'
+                _returnValue.value[1] = 'xsd:boolean'
+              else if _returnValue.value[0] instanceof Date
+                _returnValue.value[1] = 'xsd:dateTime'
+              else
+                _returnValue.value[1] = 'xsd:string'
+
+            if not _returnValue.value[0]? or
+                _returnValue.value[1] not in ['xsd:int', 'xsd:unsignedInt', 'xsd:boolean', 'xsd:string', 'xsd:dateTime']
+              if counter & 1
+                callback(null, {code: 'script', message: 'Invalid virtual parameter value attribute'})
+              return counter = 0
+
             ret.value = device.sanitizeParameterValue(_returnValue.value)
           else if provision[1].value? or provision[2].value?
             if counter & 1


:lipstick:

:lipstick:

--- src/text-buffer.coffee
+++ src/text-buffer.coffee
@@ -694,25 +694,25 @@
     @transact =>
       row = 0
       column = 0
-      startPosition = [0, 0]
+      currentPosition = [0, 0]

       lineDiff = diff.diffLines(currentText, newText)
       changeOptions = normalizeLineEndings: false

       for change in lineDiff
         lineCount = change.value.match(/\n/g)?.length ? 0
-        startPosition[0] = row
-        startPosition[1] = column
+        currentPosition[0] = row
+        currentPosition[1] = column

         if change.added
-          @change([startPosition, startPosition], change.value, changeOptions)
+          @change([currentPosition, currentPosition], change.value, changeOptions)
           row += lineCount
           column = computeBufferColumn(change.value)

         else if change.removed
-          endBufferRow = row + lineCount
-          endBufferColumn = column + computeBufferColumn(change.value)
-          @change([startPosition, [endBufferRow, endBufferColumn]], '', changeOptions)
+          endRow = row + lineCount
+          endColumn = column + computeBufferColumn(change.value)
+          @change([currentPosition, [endRow, endColumn]], '', changeOptions)

         else
           row += lineCount


Don't prompt to save when a window close is requested

Fixes #942

--- src/text-editor.coffee
+++ src/text-editor.coffee
@@ -677,7 +677,7 @@
   # this editor.
   shouldPromptToSave: ({windowCloseRequested}={}) ->
     if windowCloseRequested
-      @isModified()
+      false
     else
       @isModified() and not @buffer.hasMultipleEditors()


--- .config/init.coffee
+++ .config/init.coffee
@@ -63,6 +63,11 @@
 exec "cd #{__dirname}/.. && make could-you-not"

+# Clear .DS_Store junk from desktop when saving files
+atom.workspace.observeTextEditors (editor) ->
+	editor.onDidSave -> setTimeout (-> exec "~/.files/bin/dsclean ~/Desktop"), 50
+
+
 # Register command to toggle bracket-matcher
 atom.commands.add "body", "user:toggle-bracket-matcher", ->
 	ed = atom.workspace.getActiveTextEditor()


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/text_effect.coffee
+++ src/text_effect.coffee
@@ -12,7 +12,7 @@
 ###*
 Updates the position of the text based on the effect velocity. Updates the
 alpha based on the elapsed time since the effect creation. If rotationalVelocity
-is provided then
+is provided then the text rotation is updated as well.

 @name update
 @methodOf TextEffect#


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/text_effect.coffee
+++ src/text_effect.coffee
@@ -20,7 +20,7 @@
 ###

 ###*
-Draws the textShadow text
+Draws the textShadow text and the

 @name update
 @methodOf TextEffect#


fixed hint correction

fixed hint correction

--- src/trivia-game.coffee
+++ src/trivia-game.coffee
@@ -123,7 +123,7 @@
         # Compute score as ((answerLength - hintLength)/answerLength) * value
         answerLength = @currentQ.validAnswer.length
         if @hintLength? and @hintLength > 1
-            rawAdjustedValue = Math.floor(((answerLength - @hintLength) / answerLength) * value)
+            rawAdjustedValue = Math.floor(((answerLength - @hintLength - 1) / answerLength) * value)
             # Round to nearest 100
             adjustedValue = Math.floor(rawAdjustedValue/100)*100
         else
@@ -132,7 +132,7 @@
         resp.reply "YOU ARE CORRECT!!! The answer is #{@currentQ.answer}, you scored #{adjustedValue} points."
         @robot.logger.debug "#{name} answered correctly."
         if adjustedValue != value
-            resp.send "Hints Used = #{@hintLength}, original points = #{value}, adjusted points = #{adjustedValue}"
+            resp.send "Hints Used = #{@hintLength-1}, original points = #{value}, adjusted points = #{adjustedValue}"

         user = resp.envelope.user.mention_name.toLowerCase().trim()
         newScore = @scoreKeeper.add(user, adjustedValue)


Favor the use of textContent over innerText for the text binder.

--- src/binders.coffee
+++ src/binders.coffee
@@ -57,10 +57,10 @@
         el.value = if value? then value else ''

 Rivets.binders.text = (el, value) ->
-  if el.innerText?
+  if el.textContent?
+    el.textContent = if value? then value else ''
+  else
     el.innerText = if value? then value else ''
-  else
-    el.textContent = if value? then value else ''

 Rivets.binders.if =
   block: true


Adapting minification step using uglify-js 2.x

--- src/toaster/utils/minify-util.coffee
+++ src/toaster/utils/minify-util.coffee
@@ -1,9 +1,9 @@
-uglify = require("uglify-js").uglify
-uglify_parser = require("uglify-js").parser
+uglify = require 'uglify-js'

 module.exports = class MinifyUtil
   @min:( contents )->
-    ast = uglify_parser.parse contents
-    ast = uglify.ast_mangle ast
-    ast = uglify.ast_squeeze ast
-    compiled = uglify.gen_code ast+
+    ast = uglify.parse contents
+    stream = uglify.OutputStream()
+    ast.print stream
+    compiled = stream.toString()


Add freeOnly to static mock

Add freeOnly to static mock

--- static-mock.coffee
+++ static-mock.coffee
@@ -22,10 +22,11 @@
   broadName: () -> ''
   get: (prop) -> props[prop]
   isOnPremiumServer: () -> false
+  freeOnly: -> false

 exports.view =
   forumLink: () -> 'http://discourse.codecombat.com/'
   isMobile: () -> false
   showAds: () -> false
   isOldBrowser: () -> false
-  isIPadBrowser: () -> false+  isIPadBrowser: () -> false