Add virtual and static reference to Models and Tasks.

--- src/index.coffee
+++ src/index.coffee
@@ -67,6 +67,13 @@
   }
 } = {}) ->
   winston.info "Registering model #{modelName}."
+
+  modelSchema.statics.Models = @Models
+  modelSchema.statics.Tasks  = @Tasks
+  modelSchema.virtual 'Models'
+    .get => @Models
+  modelSchema.virtual 'Tasks'
+    .get => @Tasks
   @Models[modelName] = model = @mongoose.model modelName, modelSchema

   _.each apiExposed.methods, (method) => switch method


Remove debug log.

Remove debug log.

--- src/index.coffee
+++ src/index.coffee
@@ -7,7 +7,6 @@
 create = (skeleton, path) ->
   skeleton ?= sysPath.join(__dirname, '..', 'skeletons', 'brunch-with-chaplin')
   path ?= '.'
-  console.log 'a', skeleton, path
   initSkeleton skeleton, path

 module.exports = {


refactor(index): fetch remote settings after page load to not affect its load time

--- src/index.coffee
+++ src/index.coffee
@@ -35,10 +35,8 @@
   for m in modules
     m.run? settings.get m._definedKeys

-
-# Fetching remote settings is slower than local, but is also not guaranteed
-# to be that way.
-# TODO: ensure that this isn't overwritten by local settings.
-settings.fetchRemote()
-.then ->
-  settings.saveLocal()
+  # Start fetching remote settings after the DOM is ready so the page
+  # doesn't load slower.
+  settings.fetchRemote()
+  .then ->
+    settings.saveLocal()


add configure callback option

add configure callback option

--- src/index.coffee
+++ src/index.coffee
@@ -22,6 +22,8 @@
     html: html
     src: [rivetsSrc],
     done: (errs, window) ->
+      if options.configure
+        options.configure(window.rivets)
       root = window.document.documentElement
       window.rivets.bind(root, data, options)
       if options.fullDoc


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/tween.coffee
+++ src/tween.coffee
@@ -11,9 +11,6 @@
   Object.reverseMerge I,
     activeTweens: {}

-  # Inherit from game object
-  self = GameObject(I)
-
   # Add events and methods here
   self.bind "update", ->
     for property, data of I.activeTweens


Reading options from @config.plugins.coffeelint

Deprecating @config.coffeelint to follow current brunch conventions

--- src/index.coffee
+++ src/index.coffee
@@ -13,7 +13,11 @@
   extension: 'coffee'

   constructor: (@config) ->
-    cfg = @config?.coffeelint ? {}
+    cfg = @config?.plugins?.coffeelint ? @config?.coffeelint ? {}
+
+    if @config?.coffeelint
+      console.warn "Warning: config.coffeelint is deprecated, move it to config.plugins.coffeelint"
+
     @options = cfg.options
     @globals = cfg.globals
     @pattern = cfg.pattern ? ///^#{@config.paths.app}.*\.coffee$///


Minor

Minor

--- src/index.coffee
+++ src/index.coffee
@@ -174,6 +174,7 @@
 # Similar to `Object.assign()`, but immutable.
 #
 # Usage: `merge(obj1: Object, obj2: Object): Object`
+#
 # Variadic: `merge(obj1: Object, ...objects: Object[]): Object`
 #
 # The unmodified `obj1` is returned if `obj2` does not *provide something
@@ -209,6 +210,7 @@
 # (even if they are `null`).
 #
 # Usage: `addDefaults(obj: Object, defaults: Object): Object`
+#
 # Variadic: `addDefaults(obj: Object, ...defaultObjects: Object[]): Object`
 #
 # ```js


Remove unnecessary return calls

Remove unnecessary return calls

--- src/index.coffee
+++ src/index.coffee
@@ -36,12 +36,19 @@
         results = [] if error
         fn error, results

+        return
+      return
+    return
+
   findById: (table, id, fn) ->
     {fn} = utils.normalize fn

     @find table, {_id: id}, {limit: 1}, (error, results) ->
       results = results[0] ? false
       fn error, results
+
+      return
+    return

   save: (table, params, fn) ->
     {params, fn} = utils.normalize params, fn
@@ -54,6 +61,10 @@
         results = params if results is 1
         fn error, results

+        return
+      return
+    return
+
   remove: (table, params, fn) ->
     {params, fn} = utils.normalize params, fn

@@ -64,8 +75,13 @@
         results = false if error
         fn error, results > 0

+        return
+      return
+    return
+
   removeById: (table, id, fn) ->
     @remove table, _id: objectId(id), fn
+    return

   count: (table, params, fn) ->
     {params, fn} = utils.normalize params, fn
@@ -77,11 +93,18 @@
         results = false if error
         fn error, parseInt(results, 10) or 0

+        return
+      return
+    return
+
   collection: (table, fn) ->
     {fn} = utils.normalize fn

     connect @, table, (collection) ->
       fn collection
+
+      return
+    return

   close: ->
     return false if not @db
@@ -110,7 +133,8 @@
 objectId = (value) ->
   if utils.is.str(value)
     value = new mongodb.ObjectID value
-  value
+
+  return value

 prepare = (params) ->
   return null if not params
@@ -131,6 +155,6 @@
   else
     params._id = objectId params._id

-  params
+  return params

 module.exports = Easymongo


api: optional callback

api: optional callback

--- src/index.coffee
+++ src/index.coffee
@@ -4,7 +4,8 @@

 module.exports = (options, callback) ->
   k = new Krb5 options
-  k.kinit callback if callback
+  if callback
+    k.kinit (err) -> callback err, k
   k

 module.exports.spnego = (options, callback) ->


update input

update input

--- src/input.coffee
+++ src/input.coffee
@@ -7,7 +7,7 @@
   input = modules.create 'input', ({fn}) ->
     fn 'text_input', 'A text input field', ->
       $input = $ '<input type="text"/>'
-      @output $input
+      @div $input
       property = $input.asEventStream('keyup').map((e) -> $(event.target).val()).toProperty("")
       property.set_value = (val) -> $input.val val
       @value property
@@ -23,20 +23,21 @@
         $option.text v ? k
         $option.attr 'value', k
         $option
-      @output $select
+      @div $select
       @value $select.asEventStream('change').map((e) -> $(event.target).val()).toProperty($select.val())

     fn 'button', 'A button', (value) ->
       $button = $ '<button>'
-      @output $button
+      @div $button
       $button.text value
       @value $button.asEventStream('click')

     fn 'live', 'Updates when the property changes', (property, fn) ->
       $output = $ "<div class='live'/>"
+      # FIXME needs to use immediately rendered list
       @nested_item $output, ->
         unless property.onValue?
           property = Bacon.combineTemplate property
         property.onValue @keeping_context (v) ->
           $output.empty()
-          fn v
+          @apply_to fn, [v]


Cancel tour mixin

Cancel tour mixin

--- app/router.coffee
+++ app/router.coffee
@@ -1,7 +1,8 @@
 `import Ember from 'ember';`
 `import ENV from 'irene/config/environment';`
+`import CancelTour from 'ember-onboarding/mixins/onboard-router';`

-Router = Ember.Router.extend
+Router = Ember.Router.extend CancelTour,
   location: ENV.locationType

 Router.map ->


newline

newline

--- src/utils.coffee
+++ src/utils.coffee
@@ -24,7 +24,6 @@
     return false

-
 # avoids pulling in test dependencies and test code
 cutTests = (code) ->
   #TODO:? this can eventually use burrito if popular, but for now this is fine.


add helper function isThenable

add helper function isThenable


rudimentary frame viewer

rudimentary frame viewer

--- app/controllers/classifier.coffee
+++ app/controllers/classifier.coffee
@@ -35,7 +35,7 @@

   elements:
     '.subject'                      : 'subjectContainer'
-    '.frame-image'                  : 'foo123'
+    # '.frame-image'                  : 'imageFrames'   # not being used (yet?)
     'button[name="play-frames"]'    : 'playButton'
     'button[name="finish-marking"]' : 'finishButton'
     'button[name="no-tags"]'        : 'noTagsButton'
@@ -103,7 +103,6 @@
     @play() # still needs to be built!

   play: ->
-
     console.log "SUBJECTS:"
     for src, i in DEV_SUBJECTS
       console.log "  SUBJECT-" + i + ": " + src
@@ -128,31 +127,29 @@
     @playTimeouts.splice 0
     @el.removeClass 'playing'

-  # activate: somehow doesn't work. defined somewhere else?
   activateFrame: (@active) ->
-    # console.log "Active = " + @active
+    @active = modulus +@active, @classification.subject.location.standard.length
+    console.log "-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-"
+    for image, i in @el.find('.frame-image')
+      # console.log "SHOWING FRAME: " + @active
+      @hideFrame(image.id)

-    # there are no satellite images for AZ
-    # @satelliteImage.add(@satelliteToggle).removeClass 'active'
+    @showFrame("frame-id-"+@active)

-    @active = modulus +@active, @classification.subject.location.standard.length
+  # A VERY DODGY WAY OF HIDING/SHOWING FRAMES:
+  hideAllFrames: ->
+    @hideFrame("frame-id-0")
+    @hideFrame("frame-id-1")
+    @hideFrame("frame-id-2")
+    @hideFrame("frame-id-3")
+
+  showFrame: (img_id) ->
+    console.log "SHOW " + img_id
+    document.getElementById(img_id).style.visibility="visible"

-    console.log "@el " + @el.find('.frame-image').length
-    for image, i in @el.find('.frame-image')
-      console.log "(i, image): " + i + ", " + image
-      @setActiveClasses image, i, @active
-
-    # for button, i in @toggles
-    #   @setActiveClasses button, i, @active
-
-  setActiveClasses: (el, elIndex, activeIndex) ->
-    # el = $(el)
-
-    # console.log "+elIndex < +activeIndex " + +elIndex < +activeIndex
-    el.toggleClass 'before', +elIndex < +activeIndex
-    el.toggleClass 'active', +elIndex is +activeIndex
-    el.toggleClass 'after', +elIndex > +activeIndex
-
+  hideFrame: (img_id) ->
+    console.log "HIDE " + img_id
+    document.getElementById(img_id).style.visibility="hidden"

   onClickFinishMarking: ->
     @showSummary()


hide slider when zoomed out (oops!)

hide slider when zoomed out (oops!)

--- app/controllers/classifier.coffee
+++ app/controllers/classifier.coffee
@@ -135,8 +135,8 @@
         range:
           min: @canvasGraph.smallestX
           max: @canvasGraph.largestX #- @zoomRange
-
-    @el.find(".noUi-handle").hide()
+      @el.find(".noUi-handle").hide()
+
     @insertMetadata()
     @el.find('.do-you-see-a-transit').fadeIn()
     @el.find('#no-transits').fadeIn() #prop('disabled',false)


Fix missing argument bug

Fix missing argument bug

--- app/controllers/milestones.coffee
+++ app/controllers/milestones.coffee
@@ -1,5 +1,5 @@
 module.exports = (app) ->
-  new: (req, res) ->
+  new: (req, res, next) ->
     app.models.project
     .findOneById(req.query.project)
     .exec (err, project) ->


Prompt should change when setPrompt is called

--- app/javascripts/cs_console.coffee
+++ app/javascripts/cs_console.coffee
@@ -58,6 +58,7 @@

   # Sets the prompt
   setPrompt: (prompt)=>
+    @console.setLine(@currentLine, @console.getLine(@currentLine).replace(new RegExp(@options.prompt), prompt))
     @options.prompt = prompt

   # Focus the typer input box


Replace Markdown brs with spaces.

Replace Markdown brs with spaces.

--- lib/blog.coffee
+++ lib/blog.coffee
@@ -143,6 +143,7 @@
             commitGraft =
               '.message': ($elt) -> $elt.html markdown.parse(commit.message)
               '.message[class+]': if commit.hasParent then '' else 'main-commit'
+              '.message br': ($elt) -> $elt.replaceWith(' ')

             if ! commit.hasParent
               commitGraft['.files'] = ($files) -> $files.remove()


tweak for CS specific style

tweak for CS specific style

remove unneeded dot after the at symbol

change fat arrow to regular arrow, binding is unnecessary there and adds an extra function call around it

change double quotes to single quote because there's no string interpolation happening

instead of passing an empty array to constructor, make that the default param value

--- examples/implementation_languages/coffeescript/coffeescript_json.coffee
+++ examples/implementation_languages/coffeescript/coffeescript_json.coffee
@@ -41,46 +41,46 @@
 JsonLexer = new Lexer allTokens

 class JsonParserCoffeeScript extends Parser
-  constructor: (input) ->
+  constructor: (input = []) ->
     super input, allTokens

-    @RULE "json", =>
-      @.OR [
-        {ALT: => @.SUBRULE @.object}
-        {ALT: => @.SUBRULE @.array}
+    @RULE 'json', ->
+      @OR [
+        {ALT: -> @SUBRULE @object}
+        {ALT: -> @SUBRULE @array}
       ]

-    @RULE "object", =>
-      @.CONSUME LCurly
-      @.MANY_SEP Comma, =>
-        @.SUBRULE2 @.objectItem
-      @.CONSUME RCurly
+    @RULE 'object', ->
+      @CONSUME LCurly
+      @MANY_SEP Comma, ->
+        @SUBRULE2 @objectItem
+      @CONSUME RCurly

-    @RULE "objectItem", =>
-      @.CONSUME StringLiteral
-      @.CONSUME Colon
-      @.SUBRULE @.value
+    @RULE 'objectItem', ->
+      @CONSUME StringLiteral
+      @CONSUME Colon
+      @SUBRULE @value

-    @RULE "array", =>
-      @.CONSUME LSquare
-      @.MANY_SEP Comma, =>
-        @.SUBRULE @.value
-      @.CONSUME RSquare
+    @RULE 'array', ->
+      @CONSUME LSquare
+      @MANY_SEP Comma, ->
+        @SUBRULE @value
+      @CONSUME RSquare

-    @RULE "value", =>
-      @.OR [
-        {ALT: => @.CONSUME StringLiteral}
-        {ALT: => @.CONSUME NumberLiteral}
-        {ALT: => @.SUBRULE @.object}
-        {ALT: => @.SUBRULE @.array}
-        {ALT: => @.CONSUME True}
-        {ALT: => @.CONSUME False}
-        {ALT: => @.CONSUME Null}
+    @RULE 'value', ->
+      @OR [
+        {ALT: -> @CONSUME StringLiteral}
+        {ALT: -> @CONSUME NumberLiteral}
+        {ALT: -> @SUBRULE @object}
+        {ALT: -> @SUBRULE @array}
+        {ALT: -> @CONSUME True}
+        {ALT: -> @CONSUME False}
+        {ALT: -> @CONSUME Null}
       ]

     Parser.performSelfAnalysis(@)

-parser = new JsonParserCoffeeScript([])
+parser = new JsonParserCoffeeScript

 module.exports = (text) ->
   lexResult = JsonLexer.tokenize text


Fixed bug in Punch test where timezone wasn't being created correctly

--- tests/models/test_punch.coffee
+++ tests/models/test_punch.coffee
@@ -256,7 +256,7 @@
     beforeEach ->
       start = moment().hour(7)
       end = moment().hour(18)
-      timetable = new Timetable(start, end, 'Eastern')
+      timetable = new Timetable(start, end, moment.tz.zone('America/New_York'))
       timetable.setVacation(13, 0)
       timetable.setSick(5, 0)
       timetable.setUnpaid(0)


bug file mapvizlogs handle multiple logs for same user (mintime)

--- tools/mapviz/mapvizlogs.coffee
+++ tools/mapviz/mapvizlogs.coffee
@@ -59,7 +59,7 @@
     if logline
       user = logline[1]
       logfilename = logline[2]
-      userid = users[userid]?.id
+      userid = users[user]?.id
       if not userid?
         userid = ++nextuserid
         users[user] = {id: userid}


Add [] for disambigity

Add [] for disambigity

--- test/long.coffee
+++ test/long.coffee
@@ -5,7 +5,7 @@

 describe 'Long sequences', ->
   it 'are invalid', ->
-    random.ranges (5*i+1 for i in [5..6]).concat [32]
+    random.ranges [].concat(5*i+1 for i in [5..6]).concat [32]
     .forEach (range)->
        for i in [1..108]
          z = random range.min, range.max


add tests for mock deep clone

add tests for mock deep clone

--- test/mock.coffee
+++ test/mock.coffee
@@ -1,5 +1,6 @@
 _           = require 'underscore'
 assert      = require 'assert'
+async      = require 'async'
 Clever      = require "#{__dirname}/../index"
 Understream = require 'understream'

@@ -25,12 +26,47 @@
       assert.deepEqual _(data).invoke('toJSON'), require("#{__dirname}/mock_data/students")
       done()

+  it "deep copies data", (done) ->
+    async.waterfall [
+      (cb_wf) =>
+        @clever.Student.find().exec (err, students) ->
+          assert.ifError err
+          name = students[0].get 'name'
+          assert.equal name.first, "John"
+          name.first = 'WRONG NAME'
+          cb_wf()
+      (cb_wf) =>
+        @clever.Student.find().exec (err, students) ->
+          assert.ifError err
+          name = students[0].get 'name'
+          assert.equal name.first, "John"
+          cb_wf()
+    ], done
+
+
   it "supports GETting properties", (done) ->
     @clever.Student.find().exec (err, students) => # TODO: get findOne working
       students[0].properties (err, data) =>
         assert.ifError err
         assert.deepEqual data, _(require("#{__dirname}/mock_data/studentproperties")).findWhere({student: students[0].get('id')}).data
-      done()
+        done()
+
+  it "supports deep copies of properties", (done) -> # depends on previous test
+    async.waterfall [
+      (cb_wf) =>
+        @clever.Student.find().exec (err, students) =>
+          students[0].properties (err, data) =>
+            assert.ifError err
+            assert.equal data.foo, "bar"
+            data.foo = 'WRONG FOO'
+            cb_wf()
+      (cb_wf) =>
+        @clever.Student.find().exec (err, students) =>
+          students[0].properties (err, data) =>
+            assert.ifError err
+            assert.equal data.foo, "bar"
+            cb_wf()
+    ], done

   it "supports PUTting properties", (done) ->
     @clever.Student.find().exec (err, students) =>


Tests.

Tests.

--- test/test.coffee
+++ test/test.coffee
@@ -20,3 +20,4 @@
 assertEquals '<>"&', fullEntities.decode('<>"&')
 assertEquals '<>"&©', fullEntities.decode('<>"&©')
 assertEquals '<>"&©', fullEntities.decode('<>"&©')
+assertEquals '<<', fullEntities.decode('<<')


dont push to url state when scrolling to react-root-container

--- src/components/scroll-to-mixin.cjsx
+++ src/components/scroll-to-mixin.cjsx
@@ -60,7 +60,7 @@
     GetPositionMixin.getTopPosition(el) - _.result(@, 'getScrollTopOffset', DEFAULT_TOP_OFFSET)

   scrollToTop: ->
-    @scrollToSelector('#react-root-container')
+    @scrollToSelector('#react-root-container', updateHistory: false)

   scrollToElement: (el, options = {} ) ->
     win       = @props.windowImpl


Snap screen to pixels

Snap screen to pixels

--- framer/Components/DeviceComponent.coffee
+++ framer/Components/DeviceComponent.coffee
@@ -194,9 +194,9 @@
 			@screenBackground.width = @screen.width + 40
 			@screenBackground.height = @screen.height + 40
 			@setHand(@selectedHand) if @selectedHand and @_orientation is 0
-			centerLayer(@screenBackground)
-			centerLayer(@screen)
-			centerLayer(@screenMask)
+			centerLayer(@screenBackground, true)
+			centerLayer(@screen, true)
+			centerLayer(@screenMask, true)

 			pixelRatio = @_device.devicePixelRatio ? 1
 			screenSizeChanged = screenSizeChanged or @_context.devicePixelRatio isnt pixelRatio


fix Pagecomponent.wrap

fix Pagecomponent.wrap

--- framer/Components/ScrollComponent.coffee
+++ framer/Components/ScrollComponent.coffee
@@ -432,7 +432,11 @@
 		layer.image = null

 	# Set the original layer as the content layer for the scroll
-	scroll.setContentLayer(layer)
+	if instance.constructor.name is "PageComponent"
+		for l in layer.children
+			scroll.addPage(l)
+	else
+		scroll.setContentLayer(layer)

 	# https://github.com/motif/Company/issues/208


window.location.hash handlinh

window.location.hash handlinh

--- assets/app.coffee
+++ assets/app.coffee
@@ -10,7 +10,7 @@
     window.app = @
     if window.location.hash
       query = window.location.hash.replace('#/', '')
-      query = '' if query and query.toLowerCase() is 'css-reference'
+      query = '' if (query and query.toLowerCase() is 'css-reference')
       app.load(query, true, 'replace')

   bindEvents:->


Fix check for regex in template helpers

Fix check for regex in template helpers

--- client/helpers.coffee
+++ client/helpers.coffee
@@ -23,7 +23,7 @@
     pattern =
       class: Match.Optional String
       className: Match.Optional String
-      regex: Match.Optional RegExp, String
+      regex: Match.Optional Match.OneOf RegExp, String
       name: Match.Optional String
       path: Match.Optional String


2338: Fix distinct answer check

2338: Fix distinct answer check

It's now possible to have more than one location for a response_id on the map
in the case of select_multiple questions.

--- app/assets/javascripts/backbone/views/dashboard_map_view.js.coffee
+++ app/assets/javascripts/backbone/views/dashboard_map_view.js.coffee
@@ -16,7 +16,7 @@
     })

     # keep track of which response ids we've rendered
-    @response_ids = {}
+    @distinct_answers = {}

     # add the markers and keep expanding the bounding rectangle
     bounds = new google.maps.LatLngBounds()
@@ -43,8 +43,8 @@
   add_answer: (answer) ->
     [response_id, loc] = answer

-    # only add each response once
-    return if @response_ids[response_id]
+    # only add each response_id/lat/long once
+    return if @distinct_answers[answer]

     # get float values from string
     split = loc.split(' ')
@@ -64,8 +64,8 @@
     # setup event listener to show info window
     google.maps.event.addListener(m, 'click', => this.show_info_window(m))

-    # keep track of the response id
-    @response_ids[response_id] = true
+    # keep track of the response_id/lat/long
+    @distinct_answers[answer] = true

     return m


hide next schedule when changing types

hide next schedule when changing types

--- app/assets/javascripts/states/runbooks/views/run_command.js.coffee
+++ app/assets/javascripts/states/runbooks/views/run_command.js.coffee
@@ -7,14 +7,14 @@
   ui:
     actions: "div.actions"
     run: "button#run"
-    schedule:
-      btn: "button#schedule"
-      div: "div.schedule"
     spinner: "i.spinner"
     results: "div.results"
     args:  "div.args textarea"
     stdin: "div.stdin textarea"
     env:   "div.env textarea"
+    schedule:
+      btn: "button#schedule"
+      div: "div.schedule"
     create_schedule: "button#create_schedule"
     next_schedule: "div.next_schedule"
     calendar: "button.calendar"
@@ -48,10 +48,12 @@
     "click natural.radio": ->
       @ui.natural.div.show()
       @ui.cron.div.hide()
+      @ui.next_schedule.hide()

     "click cron.radio": ->
       @ui.cron.div.show()
       @ui.natural.div.hide()
+      @ui.next_schedule.hide()

     "keyup cron.text": _.debounceR 250, (e) ->
       _.unique_val e.target, (val) => @validate_schedule("cron", val)


fix jumping time when changing time display mode

--- src/javascripts/progress_bar.coffee
+++ src/javascripts/progress_bar.coffee
@@ -128,6 +128,7 @@
     @handleDrop(event)

   handlePickup: (event) =>
+    return if (event.target.className == 'progress-bar-time-played')
     $(@app.elem).on 'mousemove', @handleDrag
     $(@app.elem).on 'mouseup', @handleLetgo
     $(@app.elem).on 'mouseleave', @handleLetgo


Fix POI download

Fix POI download

--- web2/src/actions/poi.coffee
+++ web2/src/actions/poi.coffee
@@ -4,6 +4,7 @@
 JSZip = require 'jszip'
 request = require 'superagent'

+geocaches = require '../geocache.coffee'
 server = require '../backend.coffee'

 class PoiActions extends Actions
@@ -22,7 +23,7 @@
     submit: (types) ->
         new Promise (resolve, reject) =>
             files = types.map (type) ->
-                url: server.url "/poi.csv?type=#{type}"
+                url: server.url "/poi.csv?typeIds[]=#{geocaches.types[type]}"
                 name: "#{type}.csv"
             zip = new JSZip()
             zipFolder = zip.folder 'poi'
@@ -38,9 +39,10 @@
                     @setFilename next.name
                     request
                         .get next.url
+                        .accept 'text/csv'
                         .end (err, res) ->
                             return reject err if err?
-                            h next.name, data
+                            h next.name, res.text
             h()

         ###


Minor update

Minor update

--- index.cjsx
+++ index.cjsx
@@ -42,25 +42,29 @@
     {method, path, body, postBody} = e.detail
     {_ships, _decks, _teitokuLv} = window
     switch path
+      # Update slotitem info
       when '/kcsapi/api_start2'
         if body.api_mst_slotitem?
-          hash = hashCode encodeURIComponent JSON.stringify body.api_mst_slotitem
-          console.log hash # -975254872
+          start = (new Date()).getTime()
+          hash = hashCode JSON.stringify body.api_mst_slotitem
+          end = (new Date()).getTime()
+          console.log "the cost of hashCode: #{end-start}ms" if process.env.DEBUG?
+          console.log "hashcode is #{hash}" if process.env.DEBUG?
           try
-            yield request.getAsync("http://#{KCWIKI_HOST}/comHash.action?hash=#{hash}").spread (response, body) ->
-              if body is "\"update\""
-                console.log body
+            yield request.getAsync("http://#{KCWIKI_HOST}/comHash.action?hash=#{hash}").spread (response, data) ->
+              console.log "comHash.action reply: #{data}" if process.env.DEBUG?
+              if data is "\"update\""
+                console.log data
+                # console.log JSON.stringify body.api_mst_slotitem
                 request.postAsync "http://#{KCWIKI_HOST}/updateData.action",
                   form:
                     data: JSON.stringify body.api_mst_slotitem
                   headers:
                     'User-Agent': "Kcwiki Reporter v#{REPORTER_VERSION}"
                 .spread (response, body) ->
-                  console.log body
+                  console.log "updateData.action reply: #{body}" if process.env.DEBUG?
           catch err
             console.log err
-
-
       # Battle Result
       when '/kcsapi/api_req_combined_battle/airbattle', '/kcsapi/api_req_combined_battle/battle', '/kcsapi/api_req_combined_battle/midnight_battle', '/kcsapi/api_req_combined_battle/sp_midnight', '/kcsapi/api_req_sortie/battle', '/kcsapi/api_req_battle_midnight/battle', '/kcsapi/api_req_battle_midnight/sp_midnight', '/kcsapi/api_req_sortie/airbattle', '/kcsapi/api_req_combined_battle/battle_water'
         # Report enemy ship data


improvement

improvement

--- index.cjsx
+++ index.cjsx
@@ -98,6 +98,7 @@
   pluginWillUnload: ->
     for key, id of CONFIG
       config.set(key)
+    scheduler._tasks = []

   handleResponse: (e) ->
     {method, path, body, postBody} = e.detail
@@ -149,16 +150,9 @@
       audioFN = convertFilename ship_id, id
       setConfig(key, "http://#{server}/kcs/sound/kc#{shipFilename}/#{audioFN}.mp3")

+    {$ships} =  window
     @setState
-      hasHourlyVoice: _.find(getStore('const').$ships, (sh) -> return sh.api_id == ship_id)?.api_voicef > 1
-
-    if @state.hasHourlyVoice
-      [0...24].map (hour) ->
-        audioFN = convertFilename ship_id, (hour + 30)
-        audio = "http://#{server}/kcs/sound/kc#{shipFilename}/#{audioFN}.mp3"
-        config.set("plugin.secretary.hourly_voice.#{hour}", audio)
-    else
-      config.set("plugin.secretary.hourly_voice", [])
+      hasHourlyVoice: $ships[ship_id]?.api_voicef > 1

   handleShipChange: (e) ->
     ship_id = parseInt(e.target.value)
@@ -198,18 +192,30 @@
     return unless config.get('plugin.secretary.enable', false)
     return unless config.get('plugin.secretary.hourly_voice_enable', false)
     return unless @state.hasHourlyVoice
+    return unless @state.notifySecretary >= 0
+
     if arguments.length == 0
       nowHour = new Date().getHours()
     else
       nowHour = new Date(time).getHours()
-    audio = config.get("plugin.secretary.hourly_voice", [])?[nowHour]
-    return unless audio
+
+    if @state.notifySecretary # case for 0
+      ship_id = @state.notifySecretary
+    else
+      ship_id = @state.fleetSecretary
+
+    admiral_id = parseInt(window._nickNameId) || 0
+    server = SERVERS[(ship_id + admiral_id) % SERVERS.length]
+    shipFilename = @state.shipgraph?[ship_id]?.api_filename
+    return unless server
+    return unless shipFilename
+    audioFN = convertFilename ship_id, (nowHour + 30)
     pad = "00"
     nowHourString = nowHour.toString()
     if nowHourString.length < pad.length
       nowHourString = (pad + nowHourString).slice(-pad.length)
     notify __("It's %s now", "#{nowHourString}00"),
-      audio: audio
+      audio: "http://#{server}/kcs/sound/kc#{shipFilename}/#{audioFN}.mp3"

   render: ->
     <div id='secretary' className='secretary'>


Don't insert a null update if there are no updates or raw updates

--- services/track-changes/app/coffee/ConversionManager.coffee
+++ services/track-changes/app/coffee/ConversionManager.coffee
@@ -59,6 +59,10 @@
 				if !lastCompressedUpdate?
 					lastCompressedUpdate = rawUpdates.shift()

+				if !lastCompressedUpdate?
+					# No saved versions, no raw updates, nothing to do
+					callback()
+
 				compressedUpdates = [lastCompressedUpdate]
 				for rawUpdate in rawUpdates
 					lastCompressedUpdate = compressedUpdates.pop()
@@ -69,5 +73,5 @@
 				ConversionManager.insertCompressedUpdates doc_id, compressedUpdates, (error) ->
 					return callback(error) if error?
 					console.log doc_id, "Pushed doc ops", length
-					callback null, true
+					callback()


Initialize app-started promise before reloading AtomWindow

--- src/main-process/atom-window.coffee
+++ src/main-process/atom-window.coffee
@@ -255,8 +255,9 @@
   isSpecWindow: -> @isSpec

   reload: ->
-    @browserWindow.reload()
     @applicationStartedPromise = new Promise (resolve) =>
       @browserWindow.once('window:application-started', resolve)
+    @browserWindow.reload()
+    @applicationStartedPromise

   toggleDevTools: -> @browserWindow.toggleDevTools()


:art:

:art:

--- src/main-process/atom-window.coffee
+++ src/main-process/atom-window.coffee
@@ -40,7 +40,7 @@
     if process.platform is 'linux'
       options.icon = @constructor.iconPath

-    if @applyTitleBarSetting()
+    if @shouldHideTitleBar()
       options.titleBarStyle = 'hidden'

     @browserWindow = new BrowserWindow options
@@ -202,7 +202,7 @@
     [width, height] = @browserWindow.getSize()
     {x, y, width, height}

-  applyTitleBarSetting: ->
+  shouldHideTitleBar: ->
     not @isSpec and
     process.platform is 'darwin' and
     global.atomApplication.config.get('core.useCustomTitleBar')


PR #617: Added debug statement

PR #617: Added debug statement

--- src/middleware/authorisation.coffee
+++ src/middleware/authorisation.coffee
@@ -6,6 +6,7 @@
 atna = require 'atna-audit'
 config = require '../config/config'
 config.authentication = config.get('authentication')
+config.logger = config.get('logger')
 utils = require '../utils'
 auditing = require '../auditing'

@@ -144,6 +145,8 @@

     if approvedChannels.length > 0
       # authorisation succeeded
+      if config.logger.level is 'debug' then logger.debug "Channels that match request: #{(approvedChannels.map (c) -> c.name)}"
+
       ctx.authorisedChannel = channelWithHighestPriority approvedChannels
       logger.info "The request, '" + ctx.request.path + "' is authorised to access " + ctx.authorisedChannel.name
     else


Deploy steroids.config.copyToUserFiles attribute to Anka

--- src/steroids/DeployConverter.coffee
+++ src/steroids/DeployConverter.coffee
@@ -55,6 +55,7 @@
     open_clicked_links_in_new_layer: @config.open_clicked_links_in_new_layer ? "false"
     shake_gesture_enabled_during_development: @config.shake_gesture_enabled_during_development ? "false"
     tabs: @tabsCloudSchemaRepresentation()
+    copy_to_user_files: @userFilesObject(@config)
     hosts: []

   tabsCloudSchemaRepresentation: =>
@@ -91,5 +92,12 @@
     else
       @config.statusBar.style

+  userFilesObject: (config)->
+    userFilesObject = []
+
+    for file in config.copyToUserFiles
+      userFilesObject.push file
+
+    return userFilesObject

 module.exports = DeployConverter


Use @presenter from NavGroup instead of @options.viewClass

More consistent with TabGroup

--- src/views/ui/nav_group/index.coffee
+++ src/views/ui/nav_group/index.coffee
@@ -34,7 +34,7 @@

   render: =>

-    window = new @options.viewClass
+    viewClass = @presenter.get 'viewClass'
       controller: @controller

     @view.window = window.render().view


Apply effect in tag version as well.

Apply effect in tag version as well.

--- assets/js/src/main.coffee
+++ assets/js/src/main.coffee
@@ -10,7 +10,7 @@
   else
     FastClick.attach Uno.app

-  if Uno.is('page', 'home') or Uno.is('page', 'paged')
+  if Uno.is('page', 'home') or Uno.is('page', 'paged') or Uno.is('page', 'tag')
     Uno.timeAgo '#posts-list time'

   if Uno.is 'page', 'post'


Cleaned up the test so that there are no unecessary return statements

--- src/test-xfuse.coffee
+++ src/test-xfuse.coffee
@@ -36,11 +36,13 @@
             xfuse.setApiToken null
         "API Token should be null" : (xfuse) ->
             assert.isNull xfuse.getApiToken()
+            return
 .addBatch
     "When accessing xfuse" :
         topic : () ->
             xfuse.setApiToken null
             xfuse.setsiteUrl null
+            return
         "with no api token" :
             "and looking for data" :
                 topic : () ->
@@ -49,6 +51,7 @@
                 "should get an error" : (err, res) ->
                     assert.isNotNull err
                     assert.include err, "message"
+                    return
 .addBatch
     "with an api token" :
             topic : () ->
@@ -76,6 +79,7 @@
                     assert.include res, "user"
                     assert.include res.user, "id"
                     assert.equal '20', res.user.id, "user id should be valid"
+                    return
             "updating an existing user" :
                 topic : () ->
                     xfuse.put "/user/20", putData, @callback
@@ -84,6 +88,7 @@
                     assert.isNull err
                     assert.include res, "user"
                     assert.equal putData.external_id, res.user.external_id, "new external id should match"
+                    return
             "inserting a new user" :
                 topic : () ->
                     xfuse.post "/user", postData, @callback
@@ -99,6 +104,7 @@
                     Saving the id of the test user, so we can delete it on the next test
                     ###
                     testUser = res.user.id
+                    return
             "deleting test user" :
                 topic : () ->
                     xfuse.delete "/user/" + testUser, @callback
@@ -106,6 +112,7 @@
                 "should not get any errors" : (err, res) ->
                     assert.isNull err
                     assert.include res, "success"
+                    return
                 "and the user should no longer exist" :
                     topic : () ->
                         xfuse.get "/user/" + testUser, @callback
@@ -116,4 +123,5 @@
                         assert.include err, "error"
                         assert.include err.error, "message"
                         assert.include err.error, "error_id"
+                        return
 .export(module)


Pass spec strings directly to jsdom.env.

Closes #157. Patch provided by Omar Khan.

--- src/testrunner.coffee
+++ src/testrunner.coffee
@@ -1,4 +1,4 @@
-util = require 'util'
+sys = require 'sys'
 fs = require 'fs'
 path = require 'path'
 coffee = require 'coffee-script'
@@ -6,12 +6,6 @@

 helpers = require './helpers'
 {TerminalReporter} = require '../vendor/reporter'
-
-
-# TODO: find a better way to do this.
-# https://github.com/brunch/brunch/pull/111#issuecomment-2244266
-TEMP = '/tmp/brunchtest'
-SPECFILE = path.join TEMP, 'specs.js'

 exports.run = (options, callback) ->
@@ -32,36 +26,27 @@
         getSpecFiles filepath

   getSpecFiles testdir
-  # Remove temporary folder if it already exists
-  try
-    if fs.statSync('/tmp/brunchtest').isDirectory()
-      for f in fs.readdirSync TEMP
-        fs.unlinkSync path.join TEMP, f
-      fs.rmdirSync TEMP

-  fs.mkdir TEMP, 0755, ->
-    # Write specs to temporary folder.
-    fs.writeFileSync SPECFILE, specs.join '\n'
-    # Run specs in fake browser.
-    jsdom.env
-      html: path.join brunchdir, 'index.html'
-      scripts: [
-        path.resolve options.buildPath, 'web/js/app.js'
-        path.resolve __dirname, '../vendor/jasmine.js'
-        SPECFILE
-      ]
-      done: (error, window) ->
-        helpers.logError error if error?
-        # If we're testing brunch itself, we don't need to view the output.
-        # TODO: move this to TerminalReporter.
-        stream = if global.jasmine then (->) else util.print
+  # Run specs in fake browser.
+  jsdom.env
+    html: path.join brunchdir, 'index.html'
+    scripts: [
+      path.resolve options.buildPath, 'web/js/app.js'
+      path.resolve __dirname, '../vendor/jasmine.js'
+    ]
+    src: specs
+    done: (error, window) ->
+      helpers.logError error if error?
+      # If we're testing brunch itself, we don't need to view the output.
+      # TODO: move this to TerminalReporter.
+      stream = if global.jasmine then (->) else sys.print

-        jasmineEnv = window.jasmine.getEnv()
-        jasmineEnv.reporter = new TerminalReporter
-          print: stream
-          verbose: options.verbose
-          color: yes
-          onComplete: null
-          stackFilter: null
-        jasmineEnv.execute()
-        callback?()
+      jasmineEnv = window.jasmine.getEnv()
+      jasmineEnv.reporter = new TerminalReporter
+        print: stream
+        verbose: options.verbose
+        color: yes
+        onComplete: null
+        stackFilter: null
+      jasmineEnv.execute()
+      callback?()


don’t propose upgrade to older version

don’t propose upgrade to older version

--- src/ui/version.coffee
+++ src/ui/version.coffee
@@ -5,6 +5,10 @@
   headers:
     'User-Agent': 'request'
   url: 'https://api.github.com/repos/yakyak/yakyak/releases/latest'
+
+versionToInt = (version) ->
+  [major, minor, micro] = version.split('.')
+  version = (micro * 10^3) + (minor * 10^6) + (major * 10^9)

 check = ->
   request.get options,  (err, res, body) ->
@@ -14,9 +18,10 @@
     releasedVersion = tag.substr(1) # remove first "v" char
     localVersion = require('electron').remote.require('electron').app.getVersion()
     versionAdvertised = window.localStorage.versionAdvertised or null
-    if (releasedVersion isnt localVersion) and (releasedVersion isnt versionAdvertised)
+    higherVersionAvailable = versionToInt(releasedVersion) > versionToInt(localVersion)
+    if higherVersionAvailable and (releasedVersion isnt versionAdvertised)
       window.localStorage.versionAdvertised = releasedVersion
-      alert "A new yakyak version is available, please upgrade #{localVersion} => #{releasedVersion}"
+      alert "A new yakyak version is available, please upgrade #{localVersion} to #{releasedVersion}"
     else
       console.log "YakYak local version is #{localVersion}, released version is #{releasedVersion}"


fix indentation

fix indentation

--- app/app.coffee
+++ app/app.coffee
@@ -19,8 +19,8 @@
                   world.drawTrip Cities.BER, Cities['Moskau'], ->
                     world.repeatRoundTrip rtThfFmo, ->
                       fn = ->
-                          world.focusWorld ->
-                            world.drawTrip Cities.BER, Cities['New York'], ->
-                              world.drawTrip Cities.BER, Cities['Rio de Janeiro'], ->
-                                world.drawTrip Cities.BER, Cities['Sydney']
+                        world.focusWorld ->
+                          world.drawTrip Cities.BER, Cities['New York'], ->
+                            world.drawTrip Cities.BER, Cities['Rio de Janeiro'], ->
+                              world.drawTrip Cities.BER, Cities['Sydney']
                       setTimeout(fn, 5000)


Document separation of multiple attribute arguments

--- lib/assets/javascripts/unpoly/layer.coffee
+++ lib/assets/javascripts/unpoly/layer.coffee
@@ -693,7 +693,7 @@
     | `event`    | An `up:layer:dismissed` event                   |

   @param [up-accept-event]
-    One or more event types that will cause this overlay to automatically be
+    One or more space-separated event types that will cause this overlay to automatically be
     [accepted](/closing-overlays) when a matching event occurs within the overlay.

     The [overlay result value](/closing-overlays#overlay-result-values)
@@ -702,7 +702,7 @@
     See [Closing when an event is emitted](/closing-overlays#closing-when-an-event-is-emitted).

   @param [up-dismiss-event]
-    One or more event types that will cause this overlay to automatically be
+    One or more space-separated event types that will cause this overlay to automatically be
     [dismissed](/closing-overlays) when a matching event occurs within the overlay.

     The [overlay result value](/closing-overlays#overlay-result-values)
@@ -711,7 +711,7 @@
     See [Closing when an event is emitted](/closing-overlays#closing-when-an-event-is-emitted).

   @param [up-accept-location]
-    One or more [URL patterns](/url-patterns) that will cause this overlay to automatically be
+    One or more space-separated [URL patterns](/url-patterns) that will cause this overlay to automatically be
     [accepted](/closing-overlays) when the overlay reaches a matching [location](/up.layer.location).

     The [overlay result value](/closing-overlays#overlay-result-values)
@@ -721,7 +721,7 @@
     See [Closing when a location is reached](/closing-overlays#closing-when-a-location-is-reached).

   @param [up-dismiss-location]
-    One or more [URL patterns](/url-patterns) that will cause this overlay to automatically be
+    One or more space-separated [URL patterns](/url-patterns) that will cause this overlay to automatically be
     [dismissed](/closing-overlays) when the overlay reaches a matching [location](/up.layer.location).

     The [overlay result value](/closing-overlays#overlay-result-values)


delay reload until active org is ready

delay reload until active org is ready

--- src/views/main.coffee
+++ src/views/main.coffee
@@ -43,7 +43,8 @@
     m.on Events.Change, (name, val)=>
       if name == 'activeOrg'
         @client.setKey @data.get('orgs')[val]['live-secret-key']
-        window.location.reload()
+        requestAnimationFrame ->
+          window.location.reload()

   logout: ->
     m.trigger Events.Logout, res


Add pages to surf results if we have collection count

--- backbone.tableview.coffee
+++ backbone.tableview.coffee
@@ -230,9 +230,9 @@
             pageTo   = @data.page
             max = ""
         else
-            maxPage  = Math.ceil max / @data.size
-            pageFrom = @data.page
-            pageTo   = @data.page
+            maxPage  = Math.ceil(max / @data.size) || 1
+            pageFrom = _.max 1, @data.page - 2
+            pageTo   = _.min maxPage, @data.page + 2
             max = " of " + max + " entries"
         pages = ({number: i, active: (i == @data.page && "active") || ""} for i in _.range pageFrom, pageTo + 1)
         $("#pagination-main", @$el).html @paginationTemplate


Make more room for title and search bar

Make more room for title and search bar

--- backbone.tableview.coffee
+++ backbone.tableview.coffee
@@ -46,7 +46,7 @@
     tagName: "div"
     titleTemplate: _.template "
<%= model %>
"
     searchTemplate: _.template """
-        <input type="text" class="search-query pull-right" placeholder="<%= model.detail || model %>" value="<%= data[model.query || "q"] || "" %>"></input>
+        <input type="text" class="search-query input-block-level pull-right" placeholder="<%= model.detail || model %>" value="<%= data[model.query || "q"] || "" %>"></input>
     """
     paginationTemplate: _.template """

@@ -92,14 +92,14 @@
     """
     template: _.template """

-

+

                 <%= title %>

-

-

-
-

+

+

+
+

                 <%= search %>


show "Spawn" button on a Mac

show "Spawn" button on a Mac

--- client/connect.coffee
+++ client/connect.coffee
@@ -164,9 +164,7 @@
     if !$list.find(':selected').length then $list.focus().find('option').eq(0).attr 'selected', true; $list.change()

     D.socket
-      .on '*proxyInfo', (x) ->
-        proxyInfo = x; $('#spawnSection').toggle /^(linux|x11|android)/i.test x.platform
-        return
+      .on '*proxyInfo', (x) -> proxyInfo = x; $('#spawnSection').toggle !/^win/i.test x.platform; return
       .on '*confirmHijack', ({addr}) ->
         $("<p>#{addr || 'An IDE '} is already using this proxy.  Would you like to take it over?</p>").dialog
           title: 'Confirmation', modal: 1, buttons: [


moved http response outside of validateGroupData function

--- servers/lib/server/handlers/createteam.coffee
+++ servers/lib/server/handlers/createteam.coffee
@@ -125,7 +125,8 @@
     client.sessionToken        = result.newToken
     client.connection.delegate = result.account

-    return  if validateGroupData(body, res) isnt yes
+    if (validationResult = validateGroupData body, res) isnt yes
+      return res.status(400).send(validationResult)

     JGroup.create client,
       slug            : slug
@@ -169,10 +170,10 @@
 validateGroupData = (body, res) ->

   if not body.slug
-    return res.status(400).send 'Group slug can not be empty.'
+    return 'Group slug can not be empty.'

   else if not body.companyName
-    return res.status(400).send 'Company name can not be empty.'
+    return 'Company name can not be empty.'

   else true


… to Like

… to Like

--- client/landing/app/Applications/Activity.kdapplication/views/activityactions.coffee
+++ client/landing/app/Applications/Activity.kdapplication/views/activityactions.coffee
@@ -65,7 +65,7 @@
       cssClass  : "action-link"
       attributes:
         href    : "#"
-      partial   : "..."
+      partial   : "Like"
     , options
     super options,data


:art: Make linter a global variable As suggested by @joefitzgerald

--- spec/buffer-modifying-provider-spec.coffee
+++ spec/buffer-modifying-provider-spec.coffee
@@ -1,12 +1,11 @@
 describe 'buffer modifying linter', ->
-  getModuleMain = -> atom.packages.getActivePackage('linter').mainModule.instance
+  linter = null
   beforeEach ->
     waitsForPromise ->
-      atom.packages.activatePackage('linter')
+      atom.packages.activatePackage('linter').then (pack) -> linter = pack.mainModule.instance
     waitsForPromise ->
       atom.workspace.open('test.txt')
   it 'triggers before other linters', ->
-    linter = getModuleMain()
     last = null
     bufferModifying =
       grammarScopes: ['*']
@@ -30,7 +29,6 @@
       linter.getActiveEditorLinter().lint(false).then ->
         expect(last).toBe('normal')
   it 'runs in sequence', ->
-    linter = getModuleMain()
     activeEditor = atom.workspace.getActiveTextEditor()
     wasTriggered = false
     first =


Changes atom tree-view config

Changes atom tree-view config

--- .atom/config.cson
+++ .atom/config.cson
@@ -2,6 +2,7 @@
   "atom-clock":
     dateFormat: "HH:mm"
   core:
+    projectHome: "/home/noah/dropbox/workspace"
     telemetryConsent: "limited"
   editor:
     fontSize: 15
@@ -22,7 +23,6 @@
     rightEditorColor: "green"
     scrollSyncType: "Vertical + Horizontal"
     syncHorizontalScroll: true
-  "tree-view":
-    hideVcsIgnoredFiles: true
+  "tree-view": {}
   welcome:
     showOnStartup: false


Removed atom config from master branch

Removed atom config from master branch

--- .atom/config.cson
+++ .atom/config.cson
@@ -1,6 +0,0 @@
-"*":
-  core: {}
-  editor:
-    invisibles: {}
-    showIndentGuide: true
-    showInvisibles: true


atom: add delete-to-end-of-line

atom: add delete-to-end-of-line

--- .atom/keymap.cson
+++ .atom/keymap.cson
@@ -27,3 +27,5 @@
 #   '': 'ide-haskell-cabal:clean'
 #   '': 'ide-haskell-cabal:test'

+'atom-text-editor':
+  'ctrl-shift-delete': 'editor:delete-to-end-of-line'


Unindent. :-o

Unindent. :-o

--- src/lexer.coffee
+++ src/lexer.coffee
@@ -135,7 +135,7 @@
     number = match[0]
     lexedLength = number.length
     if is_binary = /0b([01]+)/.exec number
-        number = (parseInt is_binary[1], 2).toString()
+      number = (parseInt is_binary[1], 2).toString()
     @token 'NUMBER', number
     lexedLength


adding participation pledge UI

adding participation pledge UI

--- @client/dashboard/forum_settings.coffee
+++ @client/dashboard/forum_settings.coffee
@@ -256,6 +256,75 @@
             ' The authors of proposals, points, and comments are still shown, but opinions of others are hidden. Hosts, like you, however, will be able to see the opinions of everyone.'

+      ########################
+      # Participation pledge
+
+      do =>
+        key = "#{subdomain.name}-pledge_taken"
+
+        question_index = ->
+          for tag, idx in subdomain.customizations.user_tags
+            if tag.key == key
+              return idx
+          return null
+
+        DIV className: 'input_group checkbox',
+
+          LABEL
+            className: 'toggle_switch'
+
+            INPUT
+              id: 'enable_civility_pledge'
+              type: 'checkbox'
+              name: 'enable_civility_pledge'
+              defaultChecked: question_index() != null
+              onChange: (ev) ->
+                subdomain.customizations ||= {}
+                subdomain.customizations.user_tags ?= []
+                if ev.target.checked
+                  pledge =
+                    key: key
+                    no_opinion_view: true
+                    visibility: "host-only"
+                    self_report:
+                      input: "boolean"
+                      question: 'I pledge to be civil and to use only one account'
+                      required: true
+                  subdomain.customizations.user_tags.push pledge
+                else
+                  idx = question_index()
+                  if idx != null
+                    subdomain.customizations.user_tags.splice(idx, 1)
+                    if subdomain.customizations.user_tags.length == 0
+                      delete subdomain.customizations.user_tags
+
+                save subdomain
+
+
+            SPAN
+              className: 'toggle_switch_circle'
+
+
+          LABEL
+            className: 'indented'
+
+            htmlFor: 'enable_civility_pledge'
+            B null,
+              'Enable civility pledge.'
+            DIV
+              className: 'explanation'
+              ' Newly registered participants must agree to be civil and to use only one account.'
+
+
+
+
+
+
+
+
+
+
+

       #####################
       # CONTRIBUTION PHASE


rejoin group on reconnect

rejoin group on reconnect

--- src/scripts/services/socket.coffee
+++ src/scripts/services/socket.coffee
@@ -3,6 +3,7 @@
     connet: (api, event) ->
       socket = io "//#{api}"

-      socket.on "connect", -> console.log "Up and running"
+      socket.on "connect", ->
+        console.log "Up and running"
+        socket.emit "join", event
       socket.on "disconnect", -> console.log "Down and stopped"
-      socket.emit "join", event


just get doc_id for quick check to see if it exists

--- app/coffee/DocManager.coffee
+++ app/coffee/DocManager.coffee
@@ -101,7 +101,7 @@
 					callback null, modified, rev

 	deleteDoc: (project_id, doc_id, callback = (error) ->) ->
-		DocManager.getDoc project_id, doc_id, { version: false }, (error, doc) ->
+		DocManager.getDoc project_id, doc_id, { _id: true }, (error, doc) ->
 			return callback(error) if error?
 			return callback new Errors.NotFoundError("No such project/doc to delete: #{project_id}/#{doc_id}") if !doc?
 			MongoManager.markDocAsDeleted project_id, doc_id, callback


Fix loadVMs

Fix loadVMs

--- client/app/MainApp/filetree/controllers/resourcescontroller.coffee
+++ client/app/MainApp/filetree/controllers/resourcescontroller.coffee
@@ -39,6 +39,7 @@
             cb null, data

       async.parallel stack, (err, result)=>
+        log "result", result
         @instantiateListItems result  unless err
         @deselectAllItems()


Closes #88.Find disabled on non HTML documents

--- extension/packages/find.coffee
+++ extension/packages/find.coffee
@@ -8,6 +8,10 @@

 # Create and inserts into DOM find controls and handlers
 injectFind = (document, cb) ->
+  # Find only works on HTML documents, not XUL documents
+  if document not instanceof HTMLDocument
+    return
+
   # First get starting range - it might begin where last search ended
   startFindRng = getStartFindRng(document.defaultView)


Remove unused getDocOpsLength method

Remove unused getDocOpsLength method

--- app/coffee/RedisManager.coffee
+++ app/coffee/RedisManager.coffee
@@ -151,8 +151,5 @@
 			[length, _] = results
 			callback(error, length)

-	getDocOpsLength: (doc_id, callback = (error, length) ->) ->
-		rclient.llen keys.docOps(doc_id: doc_id), callback
-
 	getDocIdsInProject: (project_id, callback = (error, doc_ids) ->) ->
 		rclient.smembers keys.docsInProject(project_id: project_id), callback


changed Bongo to allow for JSystemStatus to start. it has no schema.

--- node_modules_koding/bongo/lib/bongo.coffee
+++ node_modules_koding/bongo/lib/bongo.coffee
@@ -49,13 +49,13 @@
   @secure =(fn)->
     fn.isSecurityEnabled = yes
     fn
-
+
   {@asynchronizeOwnMethods} = require './util'
-
+
   {BongoError}    = require './errortypes'

   Client = require './client'
-
+
   @throwIt =(it)-> throw it if it?

   constructor:(options)->
@@ -156,7 +156,7 @@
         when 3 then 'service'
         when 4 then 'event'
         else        "additionalProperty#{i-4}"
-
+
     parseRoutingKey = (routingKey)->
       routingKey.split('.').reduce (acc, edge, i)->
         acc[getEdgeMeaning i] = edge
@@ -258,7 +258,7 @@

   handleAuth:(secretName, sessionToken)->
     @fetchBrokerExchange (brokerExchange)->
-      brokerExchange.publish
+      brokerExchange.publish
     # @emit 'auth', secretName, sessionToken

   handleRequest:(secretName, message)->
@@ -352,7 +352,7 @@
     description.options =
       broadcastable   : konstructor.getBroadcastable?()
       slugTemplate    : konstructor.slugTemplate
-      schema          : konstructor.describeSchema()
+      schema          : konstructor.describeSchema?()
       pid             : process.pid
     description

@@ -382,5 +382,5 @@
     # implement dispatch method

   setClient:(rest...)-> Model.setClient rest...
-
+
   use:(fn...)-> @stack.push fn...


Bongo: stringifiy the message before sending

--- node_modules_koding/bongo/lib/bongo.coffee
+++ node_modules_koding/bongo/lib/bongo.coffee
@@ -190,7 +190,7 @@
       messages = pendingUpdateInstancesMessages[routingKey]
       @pendingUpdateInstancesMessages[routingKey] = []
       if messages.length > 0
-        @mq.emit 'updateInstances', routingKey, messages, autoDelete: no
+        @mq.emit 'updateInstances', routingKey, JSON.stringify(messages), autoDelete: no
     , @updateInstancesMessageThrottleMs

     return this


Define properties with a function call.

Define properties with a function call.

--- app/javascripts/helpers.coffee
+++ app/javascripts/helpers.coffee
@@ -1,9 +1,9 @@
-define ['angular', 'lodash', 'moment'], (ng, _, moment) ->
+define ['angular', 'lodash', 'underscore.string', 'moment'], (ng, _, _s, moment) ->
   helpers = ng.module 'qiprofile.helpers', []
-
+
   helpers.factory 'ObjectHelper', ->
     # The exists implementation is defined as a local variable,
-    # since it is used in the copyNonNullPublicProperties function.
+    # since it is used in the aliasPublicDataProperties function.
     _exists = (value) ->
       typeof value != 'undefined' and value != null

@@ -13,28 +13,49 @@
     # @returns whether the value is neither undefined nor null
     exists: _exists

-    # Copies the given source object properties into the
-    # destination object, with the following exceptions:
-    # * properties which begin with an underscore are not copied
-    #   (including the _id field)
-    # * null source property values are not copied
+    # Aliases the source object properties which are not
+    # already defined in the destination object.
     #
     # @param source the copy source object
     # @param dest the copy destination object
-    copyNonNullPublicProperties: (source, dest) ->
-      # @param obj the object to check
-      # @returns the properties which do not begin with an
-      #   underscore and have a non-null value
-      nonNullPublicProperties = (obj) ->
-        Object.getOwnPropertyNames(obj).filter (prop) ->
-          prop[0] != '_' and _exists(obj[prop])
+    # @param filter an optional function that filters the
+    #   properties to alias
+    aliasProperties: (source, dest, filter=null) ->
+      # @param prop the source property to alias in the destination
+      defineAlias = (prop) ->
+        Object.defineProperty dest, prop,
+          enumerable: true
+          get: -> source[prop]
+          set: (val) -> source[prop] = val
+
+      # The properties to alias.
+      srcProps = Object.getOwnPropertyNames(source)
+      destProps = Object.getOwnPropertyNames(dest)
+      aliasProps = _.difference(srcProps, destProps)
+      if filter
+        aliasProps = aliasProps.filter(filter)
+      # Make the virtual properties.
+      # Note - each property must be defined in a call to the defineAlias
+      # function. That function body cannot be inlined in the loop below
+      # since Coffeescript defines the iteration variable prop in a scope
+      # outside of the loop, which in turn implies that the alias getter
+      # and setter have a function closure which refers to a shared prop
+      # variable which will resolve to the last iteration value.
+      for prop in aliasProps
+        defineAlias(prop)

-      # Copy the detail properties into the parent object.
-      srcProps = nonNullPublicProperties(source)
-      destProps = nonNullPublicProperties(dest)
-      copyProps = _.difference(srcProps, destProps)
-      for prop in copyProps
-        dest[prop] = source[prop]
+    # Aliases the source object properties which are not already
+    # defined in the destination object and satisfy thew following
+    # conditions:
+    # * the property name does not begin with an underscore (_)
+    #   or dollar sign ($)
+    # * the source property value is not a function
+    #
+    # @param source the copy source object
+    # @param dest the copy destination object
+    aliasPublicDataProperties: (source, dest) ->
+      this.aliasProperties source, dest, (prop) ->
+        prop[0] not in '_$' and not _.isFunction(source[prop])

     # Parses the JSON data into a Javascript object and creates
     # camelCase property aliases for underscore property names.
@@ -46,26 +67,58 @@
     # @param data the REST JSON data
     # @returns the Javascript object
     fromJson: (data) ->
-      camelizeProperties = (obj) ->
+      # Aliases underscore properties with camelCase properties.
+      #
+      # @param obj the input object to modify
+      # @returns the input object
+      camelizeProperties = (obj, visited={}) ->
         camelizeProperty = (obj, prop) ->
-          alias = prop[0] + _.camelize(prop[1..-1])
-          Object.addProperty obj, alias,
-            get: -> this[prop]
-            set: (value) -> this[prop] = value
+          # The camelCase alias.
+          alias = prop[0] + _s.camelize(prop.substring(1))
+          # If the camelCase alias is not yet defined, then
+          # create the new property.
+          if not obj.hasOwnProperty(alias)
+            # Make the camelCase property.
+            Object.defineProperty obj, alias,
+              enumerable: true
+              get: -> obj[prop]
+              set: (value) -> obj[prop] = value
+            obj[alias] = obj[prop]
+            # Preserve the underscore property, but remove
+            # it from the enumerable list.
+            Object.defineProperty obj, prop,
+              enumerable: false
+              value: obj[prop]
+              writable: true

-        for key of obj
-          # If the key has a non-leading underscore and is not
-          # a function, then the key is an underscore property
-          # name.
-          if key.lastIndexOf('_') > 0 and not _.isFunction(obj.prop)
-            camelizeProperty(obj, prop)
-
+        # Only unvisited plain objects are wrapped.
+        if obj and obj.constructor is Object and not visited[obj._id]
+          # If the object has an id, then mark it as visited.
+          visited[obj._id] = true if obj._id
+          # Add camelCase aliases to underscore properties.
+          for key of obj
+            # If the key has a non-leading underscore, then
+            # camelize the underscore property.
+            if key.lastIndexOf('_') > 0
+              camelizeProperty(obj, key)
+            # Recurse.
+            val = obj[key]
+            if _.isArray(val)
+              camelizeProperties(item, visited) for item in val
+            else
+              camelizeProperties(val, visited)
+        # Return the input object.
+        obj
+
+      # The initial parsed Javascript object.
       obj = ng.fromJson(data)
-      if '_items' in obj
-        [camelizeProperties(item) for item in obj._items]
+      # If the object is a REST array with _items, then return
+      # the camelized items. Otherwise, returns the camelized
+      # object.
+      if obj.hasOwnProperty('_items')
+        camelizeProperties(item) for item in obj._items
       else
         camelizeProperties(obj)
-      obj

   helpers.factory 'DateHelper', ['ObjectHelper', (ObjectHelper) ->


set new entry post button to disabled on click

--- public/dreamcatcher/controllers/entries/newedit_entry_controller.coffee
+++ public/dreamcatcher/controllers/entries/newedit_entry_controller.coffee
@@ -85,9 +85,10 @@
   clearState: ->
     @entryCookie.clear()

-  '#entry_submit click': ->
+  '#entry_submit click': (el) ->
     @posted = true
     @clearState()
+    el.attr('disabled',true)

   '#books-list change': (el) ->
     $('#books-list-button').css {


Upgrades based on players

Upgrades based on players

--- app/scripts/scenes/City.coffee
+++ app/scripts/scenes/City.coffee
@@ -42,6 +42,8 @@
         ':General:We dropped an upgrade to show the weapon systems'
       ]).on 'Finished', =>
         @level.addComponent Crafty.e('PowerUp').powerUp(contains: 'lasers'), x: 640, y: 300
+        if Crafty('PlayerControlledShip').length > 1
+          @level.addComponent Crafty.e('PowerUp').powerUp(contains: 'lasers'), x: 640, y: 100

   level.generateBlocks


remove console.logs from tests

remove console.logs from tests

--- services/document-updater/test/unit/coffee/ZipManager/ZipManager.coffee
+++ services/document-updater/test/unit/coffee/ZipManager/ZipManager.coffee
@@ -24,13 +24,9 @@
 				"redis-sharelatex" : createClient: () =>
 					rclient ?=
 						auth:-> # only assign one rclient
-						multi: () =>
-							console.log 'returning rclient', rclient
-							rclient
+						multi: () => rclient
 						set: (key, value) => rclient.store[key] = value
-						get: (key) =>
-							console.log 'GET', key
-							rclient.results.push rclient.store[key]
+						get: (key) =>	rclient.results.push rclient.store[key]
 						incr: (key) => rclient.store[key]++
 						exec: (callback) =>
 							callback.apply(null, [null, rclient.results])


Changed testpie colors

Changed testpie colors


Guard ::hasParent()

Guard ::hasParent()

--- lib/atomatigit.coffee
+++ lib/atomatigit.coffee
@@ -22,11 +22,11 @@

   close: ->
     return @errorNoGitRepo() unless atom.project.getRepo()
-    @repoView.detach() if @repoView.hasParent()
+    @repoView.detach() if @repoView?.hasParent()

   focus: ->
     return @errorNoGitRepo() unless atom.project.getRepo()
-    atom.workspaceView.appendToRight(@repoView) unless @repoView.hasParent()
+    atom.workspaceView.appendToRight(@repoView) unless @repoView?.hasParent()
     @repo.reload().then =>
       @repoView.focus()


Unlisten to operations events

Unlisten to operations events

--- app/src/controllers/wallet/operations/wallet_operations_index_view_controller.coffee
+++ app/src/controllers/wallet/operations/wallet_operations_index_view_controller.coffee
@@ -13,6 +13,11 @@
     super
     @_updateAccountName()
     @_listenEvents()
+
+  onDetach: ->
+    super
+    ledger.app.off 'wallet:transactions:new wallet:operations:sync:done', @_debouncedUpdateOperations
+    ledger.preferences.instance.off 'currencyActive:changed', @_debouncedUpdateOperations

   showOperation: (params) ->
     dialog = new WalletOperationsDetailDialogViewController(params)


Create a new credit card on Balanced when submitting CreditCard form

--- app/assets/javascripts/neighborly/balanced/creditcard/payments.js.coffee
+++ app/assets/javascripts/neighborly/balanced/creditcard/payments.js.coffee
@@ -8,6 +8,9 @@

   initialize: ->
     _.bindAll(this, 'validate', 'submit')
+
+    $.getScript 'https://js.balancedpayments.com/v1/balanced.js', ->
+      balanced.init('/v1/marketplaces/TEST-MP24PC81sknFKEuhffrbAixq')

     this.$button = this.$('input[type=submit]')
     this.$form = this.$('form')
@@ -27,3 +30,10 @@
   submit: (e)=>
     e.preventDefault()

+    creditCardData =
+      card_number:      $('#payment_card_number').val()
+      expiration_month: $('#payment_expiration_month').val()
+      expiration_year:  $('#payment_expiration_year').val()
+      security_code:    $('#payment_security_code').val()
+
+    balanced.card.create creditCardData, (response)


Keep user state persistent on the client.

--- services/web/public/coffee/ide/review-panel/controllers/ReviewPanelController.coffee
+++ services/web/public/coffee/ide/review-panel/controllers/ReviewPanelController.coffee
@@ -593,9 +593,11 @@
 			if userId == ide.$scope.user.id
 				$scope.editor.wantTrackChanges = newValue

-		_setEveryoneTCState = (newValue) ->
+		_setEveryoneTCState = (newValue, isLocal = false) ->
 			$scope.reviewPanel.trackChangesOnForEveryone = newValue
-			$scope.reviewPanel.trackChangesState = {}
+			for userId, userState of $scope.reviewPanel.trackChangesState
+				userState.value = newValue
+				userState.syncState = if isLocal then UserTCSyncState.PENDING else UserTCSyncState.SYNCED
 			$scope.editor.wantTrackChanges = newValue

 		applyClientTrackChangesStateToServer = () ->
@@ -625,8 +627,7 @@
 				_setUserTCState($scope.project.owner._id, state[$scope.project.owner._id] ? false)

 		$scope.toggleTrackChangesForEveryone = (onForEveryone) ->
-			console.log onForEveryone
-			_setEveryoneTCState onForEveryone
+			_setEveryoneTCState onForEveryone, true
 			applyClientTrackChangesStateToServer()

 		window.toggleTrackChangesForUser = # DEBUG LINE


payment: add products modal

payment: add products modal

--- client/app/Applications/Dashboard.kdapplication/controllers/productscontroller.coffee
+++ client/app/Applications/Dashboard.kdapplication/controllers/productscontroller.coffee
@@ -31,6 +31,15 @@
         else
           konstructor.create productData, handleResponse

+    showAddProductsModal = (plan) =>
+      modal = new GroupPlanAddProductsModal {}, plan
+      modal.on 'ProductsAdded', reload
+
+      @fetchProducts 'product', (err, products) ->
+        return  if KD.showError err
+
+        modal.setProducts products
+
     categoryView = view.getCategoryView category

     categoryView
@@ -39,7 +48,7 @@

       .on("EditRequested", handleEdit)

-      .on("AddProductsRequested", @bound 'showAddProductsModal')
+      .on("AddProductsRequested", showAddProductsModal)

       .on "DeleteRequested", (data) ->
         confirmDelete data, ->
@@ -108,14 +117,4 @@
     KD.remote.api["JPayment#{ category.capitalize() }"]

   fetchProducts: (category, callback) ->
-    KD.getGroup().fetchProducts category, callback
-
-  showAddProductsModal: (plan) ->
-
-    modal = new GroupPlanAddProductsModal {}, plan
-    modal.on 'ProductsAdded', -> debugger
-
-    @fetchProducts 'product', (err, products) ->
-      return  if KD.showError err
-
-      modal.setProducts products+    KD.getGroup().fetchProducts category, callback


Fixed title

Fixed title

--- client/landing/app/Applications/Groups.kdapplication/Views/paymentsettingsview.coffee
+++ client/landing/app/Applications/Groups.kdapplication/Views/paymentsettingsview.coffee
@@ -43,7 +43,7 @@
           click             : =>
             @updateBillingInfo group
         history             :
-          label             : "Billing Method"
+          label             : "Payment History"
           tagName           : "a"
           partial           : "Show Payment History"
           itemClass         : KDCustomHTMLView


Initialize context menu properly, fixed #29

--- src/links.coffee
+++ src/links.coffee
@@ -32,7 +32,7 @@
 @previewAmount = 0
 @previewLinks = {}

-onInstalledHandler = ->
+createContextMenu = ->
 	chrome.contextMenus.create({contexts:["all"], id:"parent", title:chrome.i18n.getMessage("menu_main")})
 	chrome.contextMenus.create({contexts:["all"], parentId:"parent", id:"all", title:chrome.i18n.getMessage("menu_sub_all")})
 	chrome.contextMenus.create({contexts:["all"], parentId:"parent", type:"separator"})
@@ -89,6 +89,6 @@
 openLink = (url, currentTab) ->
 	chrome.tabs.create({url:url,index:currentTab.index+1})

-chrome.runtime.onInstalled.addListener(onInstalledHandler)
+createContextMenu()
 chrome.extension.onMessage.addListener(onMessageHandler)
 chrome.contextMenus.onClicked.addListener(onClickHandler)


Remove unused method

Remove unused method

--- src/login.coffee
+++ src/login.coffee
@@ -45,9 +45,6 @@
     readPromise = Q.denodeify(read)
     readPromise(options)

-  getUserAgent: ->
-    "AtomPackageManager/#{require('../package.json').version}"
-
   welcomeMessage: (state) =>
     return Q(state) if state.token


update ember simpleauth interface call

update ember simpleauth interface call

--- app/initializers/auth.coffee
+++ app/initializers/auth.coffee
@@ -5,7 +5,7 @@
 auth =
   name: "authentication",
   initialize: (container, application) ->
-    Em.SimpleAuth.setup application, {
+    Em.SimpleAuth.setup container, application, {
       crossOriginWhitelist: [window.ENV.server]
       routeAfterLogin: "index"
       routeAfterLogout: "login"


fix redis bug

fix redis bug

--- bin/FaceLiveServer.coffee
+++ bin/FaceLiveServer.coffee
@@ -12,7 +12,11 @@
   client = redis.createClient()
   usr = req.body.username
   facePass = req.body.facePass
-  client.set usr, facePass
+  client.set usr, facePass, (err, Keys_repliys) ->
+    if err
+      throw err
+    else
+      res.send "complete #{usr}"

 http.listen port, ->
   console.log "listening on *:", port


Fix race condition: session destruction when moving between levels


Fix coursesHelper date calculations

Fix coursesHelper date calculations

--- app/lib/coursesHelper.coffee
+++ app/lib/coursesHelper.coffee
@@ -181,7 +181,8 @@
               courseProgress[userID].started = true unless isPractice
               courseProgress[levelID].started = true
               courseProgress[levelID][userID].started = true
-              courseProgress[levelID][userID].lastPlayed = new Date(Math.max(_.map(sessions, 'changed')))
+              dates = _.map(sessions, (s) -> new Date(s.get('changed')))
+              courseProgress[levelID][userID].lastPlayed = new Date(Math.max(dates...))
               courseProgress[levelID].numStarted += 1

             if _.find(sessions, (s) -> s.completed()) # have finished this level
@@ -191,7 +192,7 @@
               courseProgress[levelID].completed &&= true #no-op
               # courseProgress[levelID].numCompleted += 1
               courseProgress[levelID][userID].completed = true
-              dates = (s.get('dateFirstCompleted') || s.get('changed') for s in sessions)
+              dates = (new Date(s.get('dateFirstCompleted') || s.get('changed')) for s in sessions)
               courseProgress[levelID][userID].dateFirstCompleted = new Date(Math.max(dates...))
             else # level started but not completed
               courseProgress.completed = false unless isPractice


hotfix for levels where no hero-b and users dont have sessions (#728)


fix newdataset test

fix newdataset test

--- test/integration/new_dataset.coffee
+++ test/integration/new_dataset.coffee
@@ -46,4 +46,4 @@
           iframe = browser.query('iframe')
           text = $(iframe).contents().find('body').text()
           text.should.include 'Add your SSH key'
-          text.should.include 'ehg.'
+          text.should.match /\w+@box\.scraperwiki\.com/g


only require jsdom if it is going to be used; it's a very heavy dependency to put on a ton of code that doesn't use it.

--- src/smc-util-node/misc_node.coffee
+++ src/smc-util-node/misc_node.coffee
@@ -30,8 +30,6 @@
 assert  = require('assert')
 fs      = require('fs')
 net     = require('net')
-jsdom = require("jsdom")
-
 winston = require('winston')
 async   = require('async')

@@ -778,7 +776,7 @@
         cb(_jQuery_cached)
     else
         jquery_file = fs.readFileSync("../static/jquery/jquery.min.js", "utf-8")
-        jsdom.env
+        require("jsdom").env
           html: "<html></html>",
           src: [jquery_file],
           done: (err, window) ->


webapp/latex: not json serializing callback functions

--- src/smc-webapp/latex/editor.coffee
+++ src/smc-webapp/latex/editor.coffee
@@ -583,14 +583,14 @@
                 if @latex_editor.has_uncommitted_changes()
                     delete @_last_update_preview  # running latex on stale version
                     @get_rnw_concordance_error = false
-                #@dbg("update_preview(#{misc.to_json(opts)}): run_latex")
+                #@dbg("update_preview(#{misc.to_json(force:force, only_compile:only_compile)}): run_latex")
                 @run_latex
                     command      : @load_conf_doc().latex_command
                     cb           : cb
             (cb) =>
                 if only_compile
                     cb(); return
-                #@dbg("update_preview(#{misc.to_json(opts)}): preview update")
+                #@dbg("update_preview(#{misc.to_json(force:force, only_compile:only_compile)}): preview update")
                 preview_button.icon_spin(true, disable=true)
                 @preview.update
                     cb: cb


Fixed FlashMiddleware build errors

Fixed FlashMiddleware build errors

--- wzk/net/FlashMiddleware.coffee
+++ wzk/net/FlashMiddleware.coffee
@@ -61,13 +61,13 @@
     [@createInfoFlash @msgs['offline'], 'info']

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


removed stale watcher

removed stale watcher

--- src/steroids/ProjectCreator.coffee
+++ src/steroids/ProjectCreator.coffee
@@ -26,33 +26,15 @@
         cmd: steroids_cmd
         args: ["update"]
         debug: steroidsCli.debugEnabled
+        stdout: true
+        stderr: true

       steroidsCli.log  "\nChecking for Steroids updates and installing project NPM dependencies. Please wait."

-      staleCounter = 0
-      lastLine = session.stdout.toString().split('\n').slice(-1)[0]
-
-      loading = setInterval () =>
-        latestLastLine = session.stdout.toString().split('\n').slice(-1)[0]
-        if latestLastLine == lastLine
-          staleCounter++
-
-          if staleCounter > @maxStaleUpdateCount
-            clearInterval(loading)
-            steroidsCli.debug session.stdout
-            reject new Error "\nSetup up took too long - try running 'steroids update' manually in the project directory."
-        else
-          staleCounter = 0
-
-        process.stdout.write('.')
-      , @updateLoadingInterval
-
       session.on 'exit', ->
-        clearInterval(loading)
         steroidsCli.debug "#{session.cmd} exited with code #{session.code}"

         if session.code != 0 || session.stdout.match(/npm ERR!/)
-          steroidsCli.log session.stdout
           reject new Error "\nSomething went wrong - try running 'steroids update' manually in the project directory."

         resolve()


chore(web): more stable browserify

chore(web): more stable browserify

--- apps/actor-web/gulpfile.coffee
+++ apps/actor-web/gulpfile.coffee
@@ -1,4 +1,5 @@
 argv = require('yargs').argv
+assign = require 'lodash.assign'
 autoprefixer = require 'gulp-autoprefixer'
 browserify = require 'browserify'
 buffer = require 'vinyl-buffer'
@@ -8,7 +9,6 @@
 gulp = require 'gulp'
 gutil = require 'gulp-util'
 gulpif = require 'gulp-if'
-assign = require 'lodash.assign'
 minifycss = require 'gulp-minify-css'
 sass = require 'gulp-sass'
 source = require 'vinyl-source-stream'
@@ -40,14 +40,22 @@

 jsBundleFile = 'app-js.js'

-gulp.task 'js', ->
-  bundler.bundle()
-    .pipe(source(jsBundleFile))
-    .pipe(buffer())
-    .pipe(gulpif(!argv.production, sourcemaps.init({loadMaps: true})))
-      .pipe(gulpif(argv.production, uglify()))
-    .pipe(gulpif(!argv.production, sourcemaps.write('./')))
-    .pipe(gulp.dest('./dist/assets/js'))
+gulp.task 'browserify', ->
+  watcher = watchify(bundler)
+
+  watcher.on 'update', ->
+    updateStart = Date.now()
+    console.log('Browserify started')
+    watcher.bundle()
+      .pipe(source(jsBundleFile))
+        ## uglify
+      .pipe(gulp.dest('./dist/assets/js'))
+      .pipe(connect.reload())
+    console.log('Browserify ended', (Date.now() - updateStart) + 'ms')
+  .bundle()
+  .pipe(source(jsBundleFile))
+  .pipe(gulp.dest('./dist/assets/js'))
+  .pipe(connect.reload())

 gulp.task 'sass', ->
   gulp.src ['./app/**/*.scss']
@@ -68,28 +76,10 @@
     .pipe gulp.dest './dist/'
     .pipe connect.reload()

-gulp.task 'watchify', ->
-  watcher = watchify(bundler)
-  watcher
-    .on 'error', gutil.log.bind(gutil, 'Browserify Error')
-    .on 'update', ->
-      watcher.bundle()
-        .pipe(source(jsBundleFile))
-        .pipe(buffer())
-        .pipe(sourcemaps.init({loadMaps: true}))
-        .pipe(sourcemaps.write('./'))
-        .pipe(gulp.dest('./dist/assets/js'))
-
-      gutil.log("Updated JavaScript sources")
-    .bundle()
-    .pipe(source(jsBundleFile))
-    .pipe(gulp.dest('./dist/assets/js'))
-    .pipe connect.reload()

 gulp.task 'watch', ['server'], ->
   gulp.watch ['./app/**/*.coffee'], ['coffee']
   gulp.watch ['./app/**/*.scss'], ['sass']
-  gulp.watch ['./app/**/*.js', './app/**/*.jsx'], ['watchify']
   gulp.watch ['./index.html', './app/**/*.html'], ['html']

 gulp.task 'assets', ->
@@ -117,9 +107,9 @@
     root: ['./dist/', './bower_components/', './node_modules/']
     livereload: true

-gulp.task 'build', ['assets', 'coffee', 'js', 'sass', 'html', 'usemin']
+gulp.task 'build', ['assets', 'coffee', 'browserify', 'sass', 'html', 'usemin']

-gulp.task 'build:dev', ['assets', 'coffee', 'js', 'sass', 'html']
+gulp.task 'build:dev', ['assets', 'coffee', 'browserify', 'sass', 'html']

 gulp.task 'dev', ['build:dev', 'server', 'watch']


Ensure global var is always set

Ensure global var is always set

--- test/src/sauce_labs_reporter.coffee
+++ test/src/sauce_labs_reporter.coffee
@@ -1,3 +1,4 @@
+window.global_test_results = {}
 failures = []

 QUnit.testStart (testDetails) ->


add getNextLevelupTimes test

add getNextLevelupTimes test

--- test/test_stamina-calculator.coffee
+++ test/test_stamina-calculator.coffee
@@ -63,3 +63,18 @@
       next_max_date = @stamina_calculator.convertToDate(next_max_second).getTime()
       expect(next_max_date).to.eql((new Date(50*@stamina_recovery_time)).getTime() * 1000)
       done()
+
+  describe 'getNextLevelupTimes', ->
+    beforeEach (done) ->
+      @stamina_calculator = new StaminaCalculator @stamina_recovery_time
+      done()
+
+    it.skip 'correct data', (done)->
+      next_levelup_clear_times = @stamina_calculator.getNextLevelupTimes(86 * 25, 86)
+      expect(next_levelup_clear_times).to.eql(25)
+      done()
+
+    it.skip 'small data', (done) ->
+      next_levelup_clear_times = Math.ceil(@stamina_calculator.getNextLevelupTimes(1, 86))
+      expect(next_levelup_clear_times).to.eql(1)
+      done()


handles error on leaderboard

handles error on leaderboard

--- apps/leaderboard/routes.coffee
+++ apps/leaderboard/routes.coffee
@@ -10,4 +10,6 @@
   accounts.fetch
     success: ->
       res.render 'index',
-        accounts: accounts+        accounts: accounts
+    error: (err, response) ->
+      next err


Get rid of env variable not meant to be part of this branch

--- src/model.coffee
+++ src/model.coffee
@@ -8,7 +8,7 @@
   # Build a new model with the provided storage backend and order. A markov model's
   # order is the number of prior states that will be examined to determine the
   # probabilities of the next state.
-  constructor: (@storage, @ply, @mintrain) ->
+  constructor: (@storage, @ply) ->

   # Split a line of text into whitespace-separated, nonempty words.
   _words: (phrase) ->


Use multi-line interpolation comment blocks correctly (-_-｡)

--- src/model.coffee
+++ src/model.coffee
@@ -102,10 +102,10 @@
           model = @storageManager.storage(collectionName).get(pointer)

           if not model && not options.silent
-            Utils.throwError("
+            Utils.throwError("""
               Unable to find #{field} with id #{id} in our cached {details.collectionName} collection.
               We know about #{@storageManager.storage(details.collectionName).pluck('id').join(', ')}
-            ")
+            """)

           model
       else
@@ -118,11 +118,11 @@
             models.push(model)
             notFoundIds.push(id) unless model
           if notFoundIds.length && not options.silent
-            Utils.throwError("
+            Utils.throwError("""
               Unable to find #{field} with ids #{notFoundIds.join(', ')} in our
               cached #{details.collectionName} collection.  We know about
               #{@storageManager.storage(details.collectionName).pluck('id').join(', ')}
-            ")
+            """)
         if options.order
           klass = @storageManager.getCollectionDetails(details.collectionName).klass
           comparator = klass.getComparatorWithIdFailover(options.order)


Select first result when spotlight result is available

--- src/desktop/components/search_bar/view.coffee
+++ src/desktop/components/search_bar/view.coffee
@@ -18,6 +18,12 @@
     displayKind: true
     displayEmptyItem: false
     shouldDisplaySuggestions: true
+
+  Keys: {
+    Enter: 13
+    Left: 37
+    Right: 39
+  }

   initialize: (options) ->
     return unless @$el.length
@@ -74,23 +80,23 @@

   checkSubmission: (e) ->
     @hideSuggestions()
-
-    return if !(e.which is 13) or @selected?
-
-    if e.which is 13 and @enableSpotlightAutocomplete
+    return if !(e.which is @Keys.Enter) or @selected?
+
+    unless @isEmpty()
+      @trigger 'search:entered', encodeURIComponent(@$input.val())
+
+  maybeHideSpotlight: (e) ->
+    return unless @enableSpotlightAutocomplete
+    return if e.which is @Keys.Left or e.which is @Keys.Right
+
+    if e.which is @Keys.Enter
       if @spotlightSearchResult
         location.assign @spotlightSearchResult.href()
         @$input.val(@spotlightSearchResult.get('display'))
         @hideSpotlight()
+        return
       else
         @emptyItemClick()
-      return
-
-    unless @isEmpty()
-      @trigger 'search:entered', encodeURIComponent(@$input.val())
-
-  maybeHideSpotlight: (e) ->
-    return if e.which is 37 or e.which is 39 or e.which is 13 # cursor left, right, enter

     letterPressed = String.fromCharCode(e.which).toLowerCase()
     if letterPressed is @$spotlightRemainingText.text().charAt(0).toLowerCase()
@@ -147,7 +153,7 @@
   # (rather than actually moving the cursor down
   # which would overwrite the user's typing)
   maybeHighlight: ->
-    @$('.tt-suggestion:first').addClass('tt-cursor') if @autoselect
+    @$('.tt-suggestion:first').addClass('tt-cursor') if @autoselect or (@enableSpotlightAutocomplete and @spotlightSearchResult)

   feedbackString: ->
     @__feedbackString__ ?= if @mode? and @mode isnt 'suggest'


Fixing the issue that's preventing the MobileWeb app from loading correctly.

--- lib/calatrava/templates/shell/pages/converter/page.conversionForm.coffee
+++ lib/calatrava/templates/shell/pages/converter/page.conversionForm.coffee
@@ -32,5 +32,10 @@
     console.log('getting...', field)
     $page.find("#" + field).val()

-  show: -> console.log('showing...')
-  hide: -> console.log('hiding...')
+  show: ->
+    console.log('showing...')
+    $page.show()
+
+  hide: ->
+    console.log('hiding...')
+    $page.hide()


should be handling boundfuncs, need to go back in time to figure out why somethings just crashing

--- src/nodes.coffee
+++ src/nodes.coffee
@@ -989,6 +989,7 @@
   # Ensure that all functions bound to the instance are proxied in the
   # constructor.
   addBoundFunctions: (o) ->
+    #FIXME: create ctor if none exists
     for bvar in @boundFuncs
       lhs = (new Value (new Literal "this"), [new Access bvar]).compile o
       @ctor.body.unshift new Literal "#{lhs} = #{utility 'bind'}(#{lhs}, this)"
@@ -1012,18 +1013,28 @@
     o.indent += TAB

     members = []
+    addMember = (assign)=>
+      members.push assign
+      if assign.variable.isNamed "constructor"
+        @ctor = assign.value
+      if assign.value instanceof Code and assign.value.bound
+        @boundFuncs.push assign.variable
+        assign.value.bound = no
+
     for node in @body.expressions
       if node instanceof Assign
-        members.push node
+        addMember node
       else if node instanceof Value and node.base instanceof Object
         for assign in node.base.properties
           if assign instanceof Assign
-            members.push assign
+            addMember assign
           else
             assign.warn "not sure what this is; not generating"
             continue
       else
-        node.warn "cannot be in class definition"
+        node.warn "cannot be in class definition; not generating"
+
+    @addBoundFunctions()

     for assign, assign_index in members
       answer.push @makeCode "\n\n" if assign_index isnt 0


Remove double call to start()

Remove double call to start()

--- framer/Extras/Preloader.coffee
+++ framer/Extras/Preloader.coffee
@@ -53,8 +53,6 @@
 				@brand.scale = scale

 			Canvas.onResize(layout)
-
-			@start()

 	@define "progress",
 		get: -> @_mediaLoaded.length / @_media.length or 0


Clean up run loop call

Clean up run loop call

--- app/views/application.coffee
+++ app/views/application.coffee
@@ -1,10 +1,10 @@
 View = Em.View.extend
   classNames: ['application-wrapper']
   didInsertElement: ->
-    Em.run.scheduleOnce 'afterRender', =>
-      @doResize()
+    $(window).on 'resize', =>
+      Em.run @, ->
+        Em.run.debounce @, @doResize, 200
+        Em.run.scheduleOnce 'afterRender', @, ->
+          @doResize()

-      $(window).on 'resize', =>
-        Em.run.debounce @, @doResize, 200
-
-`export default View`+`export default View`