feat(connectors): changed helper code to get rest object from connector class instead of jquery.rest object

--- src/helpers.coffee
+++ src/helpers.coffee
@@ -1,9 +1,9 @@
 # Provide all required relationship fields
 # externalkey (modelname_id), modelplural for API, modelname, modelfunction
 #
-relationship_fields = (name, model, options = {}) -> [name + '_id', name + 's', name, window[model], options]
+relationship_fields = (name, model, connector, options = {}) -> [name + '_id', name + 's', name, window[model], connector, options]

-get_fn = (id_param, api_name, name, model, options = {}) ->
+get_fn = (id_param, api_name, name, model, connector, options = {}) ->
   (params = {}, callbackOrObservable) =>
     callback = (data) =>
       res = []
@@ -18,12 +18,18 @@

       callback = callbackOrObservable

+    api = connector.get api_name
+
+    unless api?
+      console.error 'No Connector found for resource "' + api_name + '" found: ', api
+      return false
+
     if options.belongs_to?
-      @.api()[api_name].read(options.belongs_to, @.id, params).done callback
+      api.read(options.belongs_to, @.id, params).done callback
     else
-      @.api()[api_name].read(@.id, params).done callback
+      api.read(@.id, params).done callback

-single_get_fn = (id_param, api_name, name, model, options = {}) ->
+single_get_fn = (id_param, api_name, name, model, connector, options = {}) ->
   (params = {}, callbackOrObservable) =>
     callback = (data) =>
       @[name] model(data)
@@ -36,9 +42,9 @@
       callback = callbackOrObservable
     ###

-    model.getOne @[id_param](), callback
+    model.get_one @[id_param](), callback

-lazy_get_fn = (id_param, api_name, name, model, options) ->
+lazy_get_fn = (id_param, api_name, name, model, connector, options) ->
   unless callback?
     callback = (data) =>
       res = []
@@ -46,9 +52,11 @@
         res.push model(m)
       @[api_name] res

-    @.api()[api_name].read(@.id).done callback
+    api = connector.get api_name

-lazy_single_get_fn = (id_param, api_name, name, model, options) ->
+    api.read(@.id).done callback
+
+lazy_single_get_fn = (id_param, api_name, name, model, connector, options) ->
   if typeof @[id_param] != 'function'
     console.error 'External key not an observable: ' + id_param
     return false
@@ -56,14 +64,18 @@
   unless @[id_param]()?
     console.error 'Tried to access empty relation key: ' + id_param
     return false
+
+  if typeof @[id_param]() == 'function'
+    console.error 'Circular referene? Quitting.'
+    return false

   unless callback?
     callback = (data) =>
       @[name] model(data)

-  model.getOne @[id_param](), callback
+  model.get_one @[id_param](), callback

-create_fn = (id_param, api_name, name, model, options) ->
+create_fn = (id_param, api_name, name, model, connector, options) ->
   (params = {}, callback, instant = false) =>
     unless callback?
       callback = (data) =>
@@ -76,12 +88,14 @@
     if model.encrypted_container?
       params = model.encrypt_container(params)

+    api = connector.get api_name
+
     if options.belongs_to.length > 0
-      @.api()[api_name].create(options.belongs_to, @.id, params).done callback
+      api.create(options.belongs_to, @.id, params).done callback
     else
-      @.api()[api_name].create(@.id, params).done callback
+      api.create(@.id, params).done callback

-update_fn = (id_param, api_name, name, model, options) ->
+update_fn = (id_param, api_name, name, model, connector, options) ->
   (id, params, callback) =>
     unless @.id?
       console.error 'Empty ID. Save parent model first!'
@@ -90,15 +104,17 @@
     if model.encrypted_container?
       params = model.encrypt_container(params)

+    api = connector.get api_name
+
     if options.belongs_to?
-      @.api()[api_name].update(options.belongs_to, @.id, id, params).done callback
+      api.update(options.belongs_to, @.id, id, params).done callback
     else
-      @.api()[api_name].update(@.id, id, params).done callback
+      api.update(@.id, id, params).done callback

     if @.after_update?
       @.after_update()

-destroy_fn = (id_param, api_name, name, model, options) ->
+destroy_fn = (id_param, api_name, name, model, connector, options) ->
   (id, callback) =>
     unless callback?
       callback = (data) =>
@@ -108,7 +124,9 @@
       console.error 'Empty ID. Save parent model first!'
       return

+    api = connector.get api_name
+
     if options.belongs_to?
-      @.api()[api_name].destroy(options.belongs_to, @.id, id).done callback
+      api.destroy(options.belongs_to, @.id, id).done callback
     else
-      @.api()[api_name].destroy(@.id, id).done callback+      api.destroy(@.id, id).done callback


fix(cid): Remove src="cid:" when we don't have the source

--- internal_packages/message-list/lib/message-item.cjsx
+++ internal_packages/message-list/lib/message-item.cjsx
@@ -83,6 +83,10 @@
       fileLink = "#{FileDownloadStore.pathForFile(file)}"
       body = body.replace(cidLink, fileLink)

+    # Remove any remaining cid:// references - we will not display them since they'll
+    # throw "unknown ERR_UNKNOWN_URL_SCHEME"
+    body = body.replace(/src=['"]cid:[^'"]*['"]/g, '')
+
     body

   _containsQuotedText: ->


Dickbutt

Dickbutt

--- scripts/dees.coffee
+++ scripts/dees.coffee
@@ -2,7 +2,7 @@

 module.exports = (robot) ->
   robot.enter (msg) ->
-    console.log(msg.user)
+    console.log(require('util').inspect(msg))
     # If its dusya optionally follwed by ANYTHING
     # if /^dusya/.test(msg.user.name.toLowerCase())
     #  msg.send "YOUR NUTS"


adjusting when to render subject-viewer

adjusting when to render subject-viewer

--- app/assets/javascripts/components/mark/index.cjsx
+++ app/assets/javascripts/components/mark/index.cjsx
@@ -59,8 +59,7 @@
     @beginClassification()

   render: ->
-
-    return null unless @state.currentSubjectSet?
+    return null unless @getCurrentSubject()?

     currentTask = @props.workflow.tasks[@state.taskKey] # [currentAnnotation?.task]

@@ -138,7 +137,7 @@
       subject_set_index: new_subject_set_index
       subject_index: new_subject_index
       taskKey: @props.workflow.first_task
-
+
   # User changed currently-viewed subject:
   handleViewSubject: (index) ->
     # console.log "HANDLE View Subject: subject", subject


Fix syntax error

Fix syntax error

--- app/assets/javascripts/lib/models/quantity.coffee
+++ app/assets/javascripts/lib/models/quantity.coffee
@@ -189,7 +189,7 @@

   # Returns the raw number with `digits` decimal places.
   toFixed: (digits) ->
-    @value.toFixed(digits
+    @value.toFixed(digits)

   # Used to coerce Quantity into numbers so that it may be used in expressions.
   #


Clean up about page image loader

Clean up about page image loader

--- app/controllers/about_page.coffee
+++ app/controllers/about_page.coffee
@@ -32,9 +32,9 @@
     @onPageChange()

   onPageChange: =>
+    return unless @el.is ':visible'
     setTimeout =>
       visibleProtoimages = @el.find 'img[data-src]:visible'
-      @log visibleProtoimages
       @processProtoimage img for img in visibleProtoimages

   processProtoimage: (img) ->


Fix typeo

Fix typeo

--- src/marbles/history.js.coffee
+++ src/marbles/history.js.coffee
@@ -101,7 +101,7 @@
     @fragment = fragment

     unless @_hasPushState
-      return (window.location.href = @options.root + fragment)
+      return (window.location.href = fragment)

     window.history[if options.replace then 'replaceState' else 'pushState']({}, document.title, fragment)


destroy old marks after clicking finished

--- app/controllers/classifier.coffee
+++ app/controllers/classifier.coffee
@@ -468,6 +468,7 @@
     radio.checked = false for radio in @classifierTypeRadios
     @sendClassification()
     @destroyFrames()
+    @destroyMarksInFrame(i) for i in [0,1,2,3]
     Subject.next()
     document.getElementById('frame-slider').value = 0 #reset slider to first frame
     @finishButton.prop 'disabled', true


disable comment posting to talk (view only)

--- app/controllers/classifier.coffee
+++ app/controllers/classifier.coffee
@@ -636,7 +636,9 @@
     # reset zoom parameters
     @zoomReset()

-  onClickJoinConvo: -> @joinConvoBtn.hide().siblings().show()
+  onClickJoinConvo: ->
+    # @joinConvoBtn.hide().siblings().show()
+
   onClickAltJoinConvo: -> @altJoinConvoBtn.hide().siblings().show()

   onClickSubmitTalk: ->


Removed bus station

Removed bus station

was confusing people, and added Wellington Rd South

--- app/data/locations/wellesley.cson
+++ app/data/locations/wellesley.cson
@@ -6,8 +6,8 @@
     "The nearest train station is Stockport, which is just over half a mile (a 15 minute walk) from Wellesley House."
     "Exit the station from the lower concourse where the main ticket office is."
     "Stay left on Station Road."
-    "At the end of the road (there's a McDonald's restaurant on the oopposite corner) turn left onto Wellington Road."
-    "Continue to walk along Wellington Road, passing over the bus station on your left and a row of shops on your right."
+    "At the end of the road (there's a McDonald's restaurant on the opposite corner) turn left onto Wellington Road South."
+    "Continue to walk along Wellington Road, passing a row of shops on your right."
     "Wellesley House is over the motorway flyover on the right hand side - you should see a large sign with the building's name on it."
   ]
   car: [


revert broken parts of cdde576182a99335149d9144ba8732dd7cc419b9

Thanks @alagopus.

--- coffee-script.coffee
+++ coffee-script.coffee
@@ -40,8 +40,7 @@
     return js unless options.header
   catch err
     err.message = "In #{options.filename}, #{err.message}" if options.filename
-    console.error err.message
-    process.exit 1
+    throw err
   header = "Generated by CoffeeScript #{@VERSION}"
   "// #{header}\n#{js}"


Add new API keys

Add new API keys

--- coffee/server.coffee
+++ coffee/server.coffee
@@ -52,9 +52,15 @@
 apiKeys =
   dnt: 'DNT'
   nrk: 'DNT'
+
   '30ad3a3a1d2c7c63102e09e6fe4bb253': 'TurApp'
-  '0fe3cf9a548f7e158a4a0f5f22a9e8bd': 'UTno'
   'b523ceb5e16fb92b2a999676a87698d1': 'Pingdom'
+
+  '4c802ac2315ab24db9c992cc6eea0278': 'DNT' # ETA
+  'de2986ac75c5af9d7f92a26f37dc1b77': 'DNT' # sherpa2.api
+  '5dd5a39057cb479c3c4bce7f9eae5e6c': 'DNT' # dev.ut.no
+  '146bbe01b477e9e07e85e0ddd3f5095a': 'DNT' # beta.ut.no
+  'e6fa27292ffbcc689c49179c47bc708e': 'DNT' # prod.ut.no

 app.get '/', (req, res) ->
   res.json message: 'Here be dragons'


Test:Account:AccountPage: Added zoomPhoto test.

--- tests/src/account/accountpage.coffee
+++ tests/src/account/accountpage.coffee
@@ -2,16 +2,18 @@
 helpers = require '../helpers/helpers.js'
 faker   = require 'faker'

+postSelector  = '[testpath=activity-list] section:nth-of-type(1) [testpath=ActivityListItemView]:first-child'
+linkSelector  = postSelector + ' .meta a.profile'
+

 module.exports =
+

   seePostsInAccountPage: (browser) ->

     user = helpers.beginTest(browser)
     post = helpers.postActivity(browser, no)

-    postSelector       = '[testpath=activity-list] section:nth-of-type(1) [testpath=ActivityListItemView]:first-child'
-    linkSelector       = postSelector + ' .meta a.profile'
     postInPageSelector = '[testpath=activity-list] [testpath=ActivityListItemView]:first-child'

     browser
@@ -21,3 +23,20 @@
       .waitForElementVisible   postInPageSelector, 25000
       .assert.containsText     postInPageSelector, post
       .end()
+
+
+  zoomPhoto: (browser) ->
+
+    user = helpers.beginTest(browser)
+    post = helpers.postActivity(browser, no)
+
+    browser
+      .waitForElementVisible   linkSelector, 25000
+      .click                   linkSelector
+      .waitForElementVisible   '.member.content-display', 25000
+      .waitForElementVisible   '.own-profile.app-sidebar', 25000
+      .waitForElementVisible   '.own-profile.app-sidebar span.avatarview img', 25000
+      .click                   '.own-profile.app-sidebar span.avatarview img'
+      .pause 2000
+      .waitForElementVisible   '.avatar-container.kddraggable .kdmodal-inner span.avatarview', 25000
+      .end()


fix possible division by 0

fix possible division by 0

--- app/assets/javascripts/prosperity/graph.js.coffee
+++ app/assets/javascripts/prosperity/graph.js.coffee
@@ -77,7 +77,7 @@
         sum += value unless key == 'x'
       for key, value of item
         if sum == 0
-          item[key] = 50 unless key == 'x'
+          item[key] = 100 / item.length unless key == 'x'
         else
           item[key] = (value / sum) * 100 unless key == 'x'


Readonly and autcomplete off for main date picker

--- app/assets/javascripts/room_reservation.js.coffee
+++ app/assets/javascripts/room_reservation.js.coffee
@@ -19,6 +19,12 @@
     update: -> $(this).closest("form").submit()
     opacity: 0.4
     cursor: 'move'
+  }
+
+  # Set date picker for reservation date to readonly and autocomplete off
+  $("#room_reservation_which_date").attr {
+    "readonly": "true",
+    "autocomplete": "off"
   }

   # Disable main form submit if no date was selected


adds option to remove progress bars for failed uploads

--- app/assets/javascripts/s3_direct_upload.js.coffee
+++ app/assets/javascripts/s3_direct_upload.js.coffee
@@ -19,6 +19,7 @@
     additional_data: null
     before_add: null
     remove_completed_progress_bar: true
+    remove_failed_progress_bar: false

   $.extend settings, options

@@ -60,6 +61,8 @@
       fail: (e, data) ->
         content = build_content_object $uploadForm, data.files[0], data.result
         content.error_thrown = data.errorThrown
+
+        data.context.remove() if data.context && settings.remove_failed_progress_bar # remove progress bar
         $uploadForm.trigger("s3_upload_failed", [content])

       formData: (form) ->


Make interface more responsive

Make interface more responsive

By resetting the job status of a certain metric at start of the
calculation the progress bar gets filled up again. This improves
the responsiveness a bit makes the interface more lively.

Signed-off-by: Konrad Reiche <4ff0213f56e2082fcd8c882d04c546881c579ce4@gmail.com>

--- app/assets/javascripts/admin/repositories/scheduler.js.coffee
+++ app/assets/javascripts/admin/repositories/scheduler.js.coffee
@@ -76,6 +76,15 @@
       button.prop("disabled", false)

   ###
+  Resets the job status of the given metric back to zero progress.
+  ###
+  resetStatus = (metric) ->
+    $("##{metric}.label").hide()
+    $(".schedule-job.#{metric}").prop("disabled", true)
+    $(".progress-bar.#{metric}.#{ANALYZE}").css("width", "0%")
+    $(".progress-bar.#{metric}.#{COMPUTE}").css("width", "0%")
+
+  ###
   Schedules a new job to compute the metric for the current repository.
   ###
   scheduleJob = (event) ->
@@ -84,20 +93,23 @@
     metric = button.data("metric")

     url = "/admin/repositories/#{id}/metrics/#{metric}/schedule"
-    $.post url, statusLoop
+    $.post url, statusLoop(metric)

   ###
   Starts the status loop.
   ###
-  statusLoop = () ->
+  statusLoop = (metric) ->
+    return () =>

-    $.getJSON "/admin/repositories/#{id}/status", (response) ->
-      updateElements(response)
+      resetStatus(metric)
+      $.getJSON "/admin/repositories/#{id}/status", (response) ->
+        updateElements(response)

-      if not finished(response)
-        setTimeout(statusLoop, 500)
-      else
-        updateElements(response)
+        if not finished(response)
+          setTimeout(statusLoop(metric), 500)
+        else
+          updateElements(response)
+
   ###
   Checks the current status to see if all jobs have terminated.
   ###


A couple more parser tests

A couple more parser tests

--- test/parser.coffee
+++ test/parser.coffee
@@ -29,4 +29,28 @@
       command.should.have.property('argstr', 'yellow bird in cuckoo clock')
       command.should.have.property('dobjstr', 'yellow bird')
       command.should.have.property('prepstr', 'in')
-      command.should.have.property('iobjstr', 'cuckoo clock')+      command.should.have.property('iobjstr', 'cuckoo clock')
+
+    it 'should convert " to \'say\'', ->
+      command = parser.parse('"Hello, World!')
+      command.should.be.a('object')
+      command.should.have.property('verb', 'say')
+      command.should.have.property('argstr', 'Hello, World!')
+
+    it 'should convert : to \'emote\'', ->
+      command = parser.parse(':smiles')
+      command.should.be.a('object')
+      command.should.have.property('verb', 'emote')
+      command.should.have.property('argstr', 'smiles')
+
+    it 'should convert ; to \'eval\'', ->
+      command = parser.parse(';2+2')
+      command.should.be.a('object')
+      command.should.have.property('verb', 'eval')
+      command.should.have.property('argstr', '2+2')
+
+    it 'should convert # to \'edit #\'', ->
+      command = parser.parse('#4.get')
+      command.should.be.a('object')
+      command.should.have.property('verb', 'edit')
+      command.should.have.property('argstr', '#4.get')


more tests

more tests

--- test/routes.coffee
+++ test/routes.coffee
@@ -15,5 +15,12 @@
       assert.isArray results.projects
       )
   .get("/data").expect(200)
+  .get("/missing").expect(404)
+  .put("/anywhere").expect(405)
+  .post("/anywhere").expect(405)
+  #TODO: delete method, api-easy doesn't have yet
+  #.get("/css/").expect(200) # vfs-served directory json (can't be tested yet)
+  .head("/css/screen.css").expect(200)
+  .get("/css/screen.css").expect(200)

   .export(module)


Don't request customers if list is already populated

--- app/assets/javascripts/darkswarm/services/customers.js.coffee
+++ app/assets/javascripts/darkswarm/services/customers.js.coffee
@@ -4,6 +4,7 @@
     byID: {}

     index: (params={}) ->
+      return @all if @all.length
       Customer.index params, (data) => @load(data)
       @all


fix editing appointment slot

fix editing appointment slot

test plan:
 - enable new scheduler
 - as a teacher, click an appointment slot and press "Edit"
 - enter a custom limit for the number of users in the slot
 - ensure you can confirm the dialog by pressing Enter

fixes CNVS-32284

Change-Id: I8bf2d42c43e724581c5393946bc195b304de31d4
Reviewed-on: https://gerrit.instructure.com/93112
Tested-by: Jenkins
Reviewed-by: Clay Diffrient <9dff2e5c98626d20d2848250c411e8009465bb87@instructure.com>
QA-Review: Heath Hales <8ca81b68c7e2592ac614bd07f22fcc735ca55f93@instructure.com>
Product-Review: Jeremy Stanley <b3f594e10a9edcf5413cf1190121d45078c62290@instructure.com>

--- app/coffeescripts/calendar/EditApptCalendarEventDialog.coffee
+++ app/coffeescripts/calendar/EditApptCalendarEventDialog.coffee
@@ -31,6 +31,9 @@
             click: @save
           }
         ]
+      @dialog.submit (event) =>
+        event.preventDefault()
+        @save()

     show: -> @dialog.dialog('open')


Get rid of blatantly awful commands like 'annoy me'

--- scripts/example.coffee
+++ scripts/example.coffee
@@ -8,71 +8,50 @@
 #
 #   These are from the scripting documentation: https://github.com/github/hubot/blob/master/docs/scripting.md

-module.exports = (robot) ->
+# module.exports = (robot) ->

   robot.hear /badger/i, (res) ->
     res.send "Badgers? BADGERS? WE DON'T NEED NO STINKIN BADGERS"

-  robot.respond /open the (.*) doors/i, (res) ->
-    doorType = res.match[1]
-    if doorType is "pod bay"
-      res.reply "I'm afraid I can't let you do that."
-    else
-      res.reply "Opening #{doorType} doors"
+#   robot.respond /open the (.*) doors/i, (res) ->
+#     doorType = res.match[1]
+#     if doorType is "pod bay"
+#       res.reply "I'm afraid I can't let you do that."
+#     else
+#       res.reply "Opening #{doorType} doors"

   robot.hear /I like pie/i, (res) ->
     res.emote "makes a freshly baked pie"

-  lulz = ['lol', 'rofl', 'lmao']
+#  lulz = ['lol', 'rofl', 'lmao']

-  robot.respond /lulz/i, (res) ->
-    res.send res.random lulz
+#   robot.respond /lulz/i, (res) ->
+#     res.send res.random lulz

-  robot.topic (res) ->
-    res.send "#{res.message.text}? That's a Paddlin'"
+#   robot.topic (res) ->
+#     res.send "#{res.message.text}? That's a Paddlin'"

-  enterReplies = ['Hi', 'Target Acquired', 'Firing', 'Hello friend.', 'Gotcha', 'I see you']
-  leaveReplies = ['Are you still there?', 'Target lost', 'Searching']
+#   enterReplies = ['Hi', 'Target Acquired', 'Firing', 'Hello friend.', 'Gotcha', 'I see you']
+#   leaveReplies = ['Are you still there?', 'Target lost', 'Searching']

-  robot.enter (res) ->
-    res.send res.random enterReplies
-  robot.leave (res) ->
-    res.send res.random leaveReplies
+#   robot.enter (res) ->
+#     res.send res.random enterReplies
+#   robot.leave (res) ->
+#     res.send res.random leaveReplies

-  answer = process.env.HUBOT_ANSWER_TO_THE_ULTIMATE_QUESTION_OF_LIFE_THE_UNIVERSE_AND_EVERYTHING
+#   answer = process.env.HUBOT_ANSWER_TO_THE_ULTIMATE_QUESTION_OF_LIFE_THE_UNIVERSE_AND_EVERYTHING

-  robot.respond /what is the answer to the ultimate question of life/, (res) ->
-    unless answer?
-      res.send "Missing HUBOT_ANSWER_TO_THE_ULTIMATE_QUESTION_OF_LIFE_THE_UNIVERSE_AND_EVERYTHING in environment: please set and try again"
-      return
-    res.send "#{answer}, but what is the question?"
+#   robot.respond /what is the answer to the ultimate question of life/, (res) ->
+#     unless answer?
+#       res.send "Missing HUBOT_ANSWER_TO_THE_ULTIMATE_QUESTION_OF_LIFE_THE_UNIVERSE_AND_EVERYTHING in environment: please set and try again"
+#       return
+#     res.send "#{answer}, but what is the question?"

-  robot.respond /you are a little slow/, (res) ->
-    setTimeout () ->
-      res.send "Who you calling 'slow'?"
-    , 60 * 1000
-
-  annoyIntervalId = null
-
-  robot.respond /annoy me/, (res) ->
-    if annoyIntervalId
-      res.send "AAAAAAAAAAAEEEEEEEEEEEEEEEEEEEEEEEEIIIIIIIIHHHHHHHHHH"
-      return
-
-    res.send "Hey, want to hear the most annoying sound in the world?"
-    annoyIntervalId = setInterval () ->
-      res.send "AAAAAAAAAAAEEEEEEEEEEEEEEEEEEEEEEEEIIIIIIIIHHHHHHHHHH"
-    , 1000
-
-  robot.respond /unannoy me/, (res) ->
-    if annoyIntervalId
-      res.send "GUYS, GUYS, GUYS!"
-      clearInterval(annoyIntervalId)
-      annoyIntervalId = null
-    else
-      res.send "Not annoying you right now, am I?"
-
+#   robot.respond /you are a little slow/, (res) ->
+#     setTimeout () ->
+#       res.send "Who you calling 'slow'?"
+#     , 60 * 1000

   robot.router.post '/hubot/chatsecrets/:room', (req, res) ->
     room   = req.params.room


Deleting example.coffee file

Deleting example.coffee file

--- scripts/example.coffee
+++ scripts/example.coffee
@@ -1,106 +0,0 @@
-# Description:
-#   Example scripts for you to examine and try out.
-#
-# Notes:
-#   They are commented out by default, because most of them are pretty silly and
-#   wouldn't be useful and amusing enough for day to day huboting.
-#   Uncomment the ones you want to try and experiment with.
-#
-#   These are from the scripting documentation: https://github.com/github/hubot/blob/master/docs/scripting.md
-
-module.exports = (robot) ->
-
-  # robot.hear /badger/i, (res) ->
-  #   res.send "Badgers? BADGERS? WE DON'T NEED NO STINKIN BADGERS"
-  #
-  # robot.respond /open the (.*) doors/i, (res) ->
-  #   doorType = res.match[1]
-  #   if doorType is "pod bay"
-  #     res.reply "I'm afraid I can't let you do that."
-  #   else
-  #     res.reply "Opening #{doorType} doors"
-  #
-  # robot.hear /I like pie/i, (res) ->
-  #   res.emote "makes a freshly baked pie"
-  #
-  # lulz = ['lol', 'rofl', 'lmao']
-  #
-  # robot.respond /lulz/i, (res) ->
-  #   res.send res.random lulz
-  #
-  # robot.topic (res) ->
-  #   res.send "#{res.message.text}? That's a Paddlin'"
-  #
-  #
-  # enterReplies = ['Hi', 'Target Acquired', 'Firing', 'Hello friend.', 'Gotcha', 'I see you']
-  # leaveReplies = ['Are you still there?', 'Target lost', 'Searching']
-  #
-  # robot.enter (res) ->
-  #   res.send res.random enterReplies
-  # robot.leave (res) ->
-  #   res.send res.random leaveReplies
-  #
-  # answer = process.env.HUBOT_ANSWER_TO_THE_ULTIMATE_QUESTION_OF_LIFE_THE_UNIVERSE_AND_EVERYTHING
-  #
-  # robot.respond /what is the answer to the ultimate question of life/, (res) ->
-  #   unless answer?
-  #     res.send "Missing HUBOT_ANSWER_TO_THE_ULTIMATE_QUESTION_OF_LIFE_THE_UNIVERSE_AND_EVERYTHING in environment: please set and try again"
-  #     return
-  #   res.send "#{answer}, but what is the question?"
-  #
-  # robot.respond /you are a little slow/, (res) ->
-  #   setTimeout () ->
-  #     res.send "Who you calling 'slow'?"
-  #   , 60 * 1000
-  #
-  # annoyIntervalId = null
-  #
-  # robot.respond /annoy me/, (res) ->
-  #   if annoyIntervalId
-  #     res.send "AAAAAAAAAAAEEEEEEEEEEEEEEEEEEEEEEEEIIIIIIIIHHHHHHHHHH"
-  #     return
-  #
-  #   res.send "Hey, want to hear the most annoying sound in the world?"
-  #   annoyIntervalId = setInterval () ->
-  #     res.send "AAAAAAAAAAAEEEEEEEEEEEEEEEEEEEEEEEEIIIIIIIIHHHHHHHHHH"
-  #   , 1000
-  #
-  # robot.respond /unannoy me/, (res) ->
-  #   if annoyIntervalId
-  #     res.send "GUYS, GUYS, GUYS!"
-  #     clearInterval(annoyIntervalId)
-  #     annoyIntervalId = null
-  #   else
-  #     res.send "Not annoying you right now, am I?"
-  #
-  #
-  # robot.router.post '/hubot/chatsecrets/:room', (req, res) ->
-  #   room   = req.params.room
-  #   data   = JSON.parse req.body.payload
-  #   secret = data.secret
-  #
-  #   robot.messageRoom room, "I have a secret: #{secret}"
-  #
-  #   res.send 'OK'
-  #
-  # robot.error (err, res) ->
-  #   robot.logger.error "DOES NOT COMPUTE"
-  #
-  #   if res?
-  #     res.reply "DOES NOT COMPUTE"
-  #
-  # robot.respond /have a soda/i, (res) ->
-  #   # Get number of sodas had (coerced to a number).
-  #   sodasHad = robot.brain.get('totalSodas') * 1 or 0
-  #
-  #   if sodasHad > 4
-  #     res.reply "I'm too fizzy.."
-  #
-  #   else
-  #     res.reply 'Sure!'
-  #
-  #     robot.brain.set 'totalSodas', sodasHad+1
-  #
-  # robot.respond /sleep it off/i, (res) ->
-  #   robot.brain.set 'totalSodas', 0
-  #   res.reply 'zzzzz'


the "reset" button should force reload

the "reset" button should force reload

--- client/landing/app/MainApp/filetree/controllers/resourcescontroller.coffee
+++ client/landing/app/MainApp/filetree/controllers/resourcescontroller.coffee
@@ -28,7 +28,7 @@

     vmController = KD.getSingleton("vmController")
     vmController.resetVMData()
-    vmController.fetchVMs (err, vms)=>
+    vmController.fetchVMs yes, (err, vms)=>
       log "Found these vms:", err, vms
       return  unless vms
       # vms.sort cmp


collision

collision

--- src/collision.coffee
+++ src/collision.coffee
@@ -31,10 +31,8 @@

   @name rectangular
   @methodOf Collision
-
   @param {Object} a The first rectangle
   @param {Object} b The second rectangle
-
   @returns {Boolean} true if the rectangles overlap, false otherwise
   ###
   rectangular: (a, b) ->


Fix mylist id type in MylistAtom class's instance.

--- lib/mylist_atom.coffee
+++ lib/mylist_atom.coffee
@@ -15,7 +15,7 @@
     b = $(@xml)
     @title = b.find('title').eq(0).text().substring("マイリスト　".length).slice(0, -("‐ニコニコ動画".length))
     @subtitle = b.find('subtitle').text()
-    @mylist_id = b.find('link').eq(0).attr('href').split('/')[4]
+    @mylist_id = Number b.find('link').eq(0).attr('href').split('/')[4]
     @updated = b.find('updated').eq(0).text()
     @author = b.find('author name').text()


Minor bugfix in last commit

Minor bugfix in last commit

--- app/utils/install-intercom.coffee
+++ app/utils/install-intercom.coffee
@@ -1,3 +1,5 @@
+`import ENV from 'irene/config/environment';`
+
 installIntercom = ->
   if !ENV.enableIntercom
     return console.log "Intercom Disabled"


Added subscriptions and notfound hook on data null

--- app/lib/router.coffee
+++ app/lib/router.coffee
@@ -48,58 +48,84 @@
   # before hooks

   Router.onBeforeAction 'loading'
-  Router.onBeforeAction filters.isLoggedIn, only: [ 'logout', 'lobby', 'quiz' ]
-  Router.onBeforeAction filters.isLoggedOut, only: [ 'login', 'signup' ]
+  Router.onBeforeAction 'dataNotFound',
+    only: [ 'game', 'quiz' ]
+  Router.onBeforeAction filters.isLoggedIn,
+    only: [ 'logout', 'lobby', 'single', 'duel', 'quiz' ]
+  Router.onBeforeAction filters.isLoggedOut,
+    only: [ 'login', 'signup' ]

   # routes

   Router.map ->
     # lobby
-    @route 'lobby', path: '/'
+    @route 'lobby',
+      path: '/'
+
+      waitOn: ->
+        if Meteor.user()?
+          [
+            Meteor.subscribe 'challenges'
+            Meteor.subscribe 'games'
+            Meteor.subscribe 'users'
+          ]

     # highscore
-    @route 'highscores'
+    @route 'highscores',
+      waitOn: -> Meteor.subscribe 'games'

     #game types
-    @route 'single'
-    @route 'duel'
+    @route 'single',
+      waitOn: ->
+      [
+        Meteor.subscribe 'challenges'
+        Meteor.subscribe 'games'
+      ]

-    # DEBUG route
-    @route 'quizzes', path: '/quizzes/'
+    @route 'duel',
+      waitOn: ->
+        [
+          Meteor.subscribe 'challenges'
+          Meteor.subscribe 'games'
+        ]
+
+    # quizzes (debug)
+    @route 'quizzes',
+      waitOn: -> Meteor.subscribe 'quizzes'

     # quiz
     @route 'quiz',
-      path: '/quiz/:_id/'
+      path: '/quiz/:_id'
+
+      waitOn: ->
+        [
+          Meteor.subscribe 'games'
+          Meteor.subscribe 'quizzes'
+          Meteor.subscribe 'questions'
+          Meteor.subscribe 'sounds'
+        ]
+
+      data: ->
+        Quizzes.findOne @params._id
+
+      onRun: ->
+        id = @params._id
+        Deps.nonreactive ->
+          Session.set 'currentQuizId', id

       onBeforeAction: (pause) ->
-
-        # Find the quiz
-        quizId = @params._id
-        quiz = Quizzes.findOne quizId
-        unless quiz?
-          @render 'notFound'
-          pause()
-          return
+        quiz = @data()
+        return unless quiz?

         # Check that the quiz has started and hasn't run out
         now = new Date()
         unless (quiz.startDate < now and now < quiz.endDate)
-          @render 'notAvailable'
+          @redirect 'lobby'
+          console.log 'quiz unavailable'
+          FlashMessages.sendError 'Denne quiz er ikke tilgængelig'
           pause()
           return
-
-        # Check if player is already playing a quiz
-        currentQuizId = Session.get('currentQuizId')
-        if currentQuizId?
-          # player is already playing a quiz
-          if currentQuizId != @params._id
-            # TODO: This should render an error
-            # message that the user is already playing a different quiz
-            @render 'notFound'
-            pause()
-        else
-          Session.set('currentQuizId', quizId)

         unless Session.get 'currentQuestion'
           Session.set 'currentQuestion', 0
@@ -108,26 +134,24 @@
     @route 'game',
       path: '/game/:_id/:action'

-      data: ->
-        Games.findOne @params._id
+      waitOn: ->
+        [
+          Meteor.subscribe 'games'
+          Meteor.subscribe 'quizzes'
+          Meteor.subscribe 'users'
+        ]
+
+      data: -> Games.findOne @params._id
+
+      onRun: ->
+        id = @params._id
+        Deps.nonreactive ->
+          Session.set 'gameId', id

       onBeforeAction: (pause) ->
         unless @params.action in ['result']
           @render 'notFound'
           pause()
-
-        unless @data()?
-          @render 'notFound'
-          pause()
-
-      waitOn: ->
-        Meteor.subscribe 'games'
-        Meteor.subscribe 'quizzes'
-
-      onRun: ->
-        id = @params._id
-        Deps.nonreactive ->
-          Session.set 'gameId', id

       action: ->
         @render @params.action


synchronizing!

synchronizing!

--- src/sync.coffee
+++ src/sync.coffee
@@ -21,7 +21,6 @@
                 @writeResults t, results, cb

     runRequest: (cb) ->
-        console.log 'router.run': @request, cb: cb
         @router.runRemotely @request, cb

@@ -47,7 +46,7 @@
         seenOffsets = {}
         walk = (offset) =>
             console.log "walk", offset
-            @request.rsm.after ?= offset
+            @request.rsm.after = offset
             @runRequest (err, results) =>
                 console.log "ranRequest", err, results
                 if err
@@ -61,14 +60,14 @@
                         if err
                             return cb results

-                        offset = results.rsm?.last
-                        if offset
+                        nextOffset = results.rsm?.last
+                        if nextOffset
                             # Remote supports RSM, walk:
-                            if seenOffsets.hasOwnProperty(offset)
+                            if seenOffsets.hasOwnProperty(nextOffset)
                                 cb new Error("RSM offset loop detected for #{@request.node}: #{offset} already seen")
                             else
-                                seenOffsets[offset] = true
-                                walk offset
+                                seenOffsets[nextOffset] = true
+                                walk nextOffset
                         else
                             # No RSM support, done:
                             console.log("No RSM last")


performance boost for Component

performance boost for Component

--- src/component.coffee
+++ src/component.coffee
@@ -8,7 +8,7 @@
 {Symbol, Map, Set} = require 'es6'

 symbols = require './symbols'
-bPool = Symbol 'pool of disposed components'
+# bPool = Symbol 'pool of disposed components'
 bData = Symbol 'data array for the component'

 identities = fast.clone(require './primes').reverse()
@@ -23,7 +23,7 @@

 	{fields, identity} = parseDefinition definition

-	componentPool = []
+	# componentPool = []

 	# Create prototype object for instances of the component type
 	componentPrototype = Object.create basePrototype
@@ -31,15 +31,15 @@
 		Object.defineProperty componentPrototype, field, createDataProperty(i)

 	ComponentType = (data) ->
-		if not data and componentPool.length
-			component = componentPool.pop()
-		else
-			component = Object.create componentPrototype
-			initializeData component, fields, data
-			Object.setPrototypeOf component, ComponentType.prototype
+		#
+		component = this
+		unless component instanceof ComponentType
+			component = new ComponentType data
+		initializeData component, fields, data
 		return component

-	ComponentType[ bPool ] = componentPool
+	ComponentType.prototype = componentPrototype
+	# ComponentType[ bPool ] = componentPool
 	ComponentType[ symbols.bFields ] = fields
 	ComponentType[ symbols.bName ] = name
 	ComponentType[ symbols.bIdentity ] = identity
@@ -91,7 +91,7 @@
 	return unless data = this[ bData ]
 	data.length = 0
 	delete this[ symbols.bChanged ]
-	this[ symbols.bType ][ bPool ].push this
+	# this[ symbols.bType ][ bPool ].push this

 basePrototype =
 	toString: -> this[ symbols.bType ].toString() + if data = this[ bData ]


a temp commit to figure out how to make things work with cdx

--- src/coffee/ui_model.coffee
+++ src/coffee/ui_model.coffee
@@ -3,20 +3,47 @@
   type : 'DataTable'
   initialize : (attrs, options)->
     super(attrs, options)
+    @register_property('offset', ['data_slice'],
+      (() -> return @get('data_slice')[0]), false
+    )
+    @register_property('chunksize', ['data_slice'],
+      (() -> return @get('data_slice')[1] - @get('data_slice')[0]),
+      false
+    )
   defaults :
     data_source : null
     columns : []
+    data_slice : [0, 100]
+    total_rows : 0
+    url : ''
   default_view : Continuum.ui.DataTableView
+  convert_raw_data : (arraydata) ->
+    #converts raw data from blaze into object array data source data,
+    # raw : {'data' : [[1,2,3],[2,3,4]] #2d array, 'colnames' : ['a', 'b', 'c']}
+    # converted : [{'a': 1, 'b' : 2, 'c' :3}, {'a' : 2, 'b' : 3, 'c': 4}]
+    transformed = []
+    for row in arraydata['data']
+      transformedrow = {}
+      for temp in _.zip(row, arraydata['colnames'])
+        [val, colname] = temp
+        transformedrow[colname] = val
+      transformed.push(transformedrow)
+    return transformed
+
   load : (offset) ->
-    @loaddeferred = $.Deferred()
-    data_source = @get_ref('data_source')
-    $.when(data_source.loaddeferred).then(() =>
-      console.log('setting columns', data_source.get('columns'))
-      @set('columns', data_source.get('columns'))
-      @loaddeferred.resolve()
+    slice = [offset, offset + @get('chunksize')]
+    $.get("/data" + @get('url'), {data_slice : JSON.stringify(slice)},
+      (data) =>
+        arraydata = JSON.parse(data)
+        transformed = @convert_raw_data(arraydata)
+        @set(
+          data_slice : slice
+          columns : arraydata['colnames']
+          total_rows: arraydata['shape'][0]
+        )
+        @get_ref('data_source').set('data', transformed)
+        return null
     )
-    return @loaddeferred
-
 class DataTables extends Backbone.Collection
   model : DataTable


small fixes... ready to close #138

small fixes... ready to close #138

--- lib/precompiler.coffee
+++ lib/precompiler.coffee
@@ -1,17 +1,17 @@
 jade = require "jade"
 fs = require "fs"
-util = require "util"
 path = require "path"
 _ = require "underscore"
 mkdirp = require "mkdirp"
 compressor = require './utils/compressor'

-# @api private
-# precompiles jade templates to javascript functions
-# then writes them to a file.
+###*
+ * compile jade templates into JS functions for use on the client-side, and
+   save it to a specified file
+###
 module.exports = ->
-  global.options.debug.log "precompiling templates", "yellow"
-  return false if typeof global.options.templates is "undefined"
+  global.options.debug.log 'precompiling templates', 'yellow'
+  return false if not global.options.templates?
   template_dir = path.join process.cwd(), global.options.templates

   precompiler = new Precompiler(
@@ -25,7 +25,7 @@
   buf = compressor buf, 'js'

   # TODO: make output folder dynamic
-  output_path = path.normalize("public/js/templates.js")
+  output_path = path.normalize('public/js/templates.js')
   mkdirp.sync path.dirname(output_path)
   fs.writeFileSync output_path, buf

@@ -35,13 +35,14 @@
    * deals with setting up the variables for options
    * @param {Object} options = {} an object holding all the options to be
      passed to the compiler. 'templates' must be specified.
+   * @constructor
   ###
   constructor: (options = {}) ->
     defaults =
       include_helpers: true
       inline: false
       debug: false
-      namespace: "templates"
+      namespace: 'templates'
       templates: undefined # an array of template filenames

     _.extend @, defaults, options
@@ -49,6 +50,7 @@
   ###*
    * loop through all the templates specified, compile them, and add a wrapper
    * @return {String} the source of a JS object which holds all the templates
+   * @public
   ###
   compile: ->
     buf = ["""
@@ -67,6 +69,7 @@
    * compile individual templates
    * @param {String} template the full filename & path of the template to be compiled
    * @return {String} source of the template function
+   * @private
   ###
   compileTemplate: (template) ->
     templateNamespace = path.basename(template, '.jade').replace(/\//g, '.') # Replaces '/' with '.'
@@ -77,23 +80,24 @@
   ###*
    * Gets Jade's helpers and combines them into string
    * @return {String} source of Jade's helpers
+   * @private
   ###
   helpers: ->
-    obj = [
+    buf = [
       jade.runtime.attrs.toString().replace(/exports\./g,''),
       jade.runtime.escape.toString()
     ]

     if @debug
-      obj.push jade.runtime.rethrow.toString()
+      buf.push jade.runtime.rethrow.toString()

-    obj.push ["""
+    buf.push """
     var jade = {
       attrs: attrs,
       escape: escape #{
         if @debug then ',\n  rethrow: rethrow' else ''
       }
     };
-    """]
+    """

-    return obj.join('\n')
+    return buf.join('\n')


Clean up and update snippets config

Clean up and update snippets config

--- user/snippets.cson
+++ user/snippets.cson
@@ -1,23 +1,3 @@
-# Your snippets
-#
-# Atom snippets allow you to enter a simple prefix in the editor and hit tab to
-# expand the prefix into a larger code block with templated values.
-#
-# You can create a new snippet in this file by typing "snip" and then hitting
-# tab.
-#
-# An example CoffeeScript snippet to expand log to console.log:
-#
-# '.source.coffee':
-#   'Console log':
-#     'prefix': 'log'
-#     'body': 'console.log $1'
-#
-# This file uses CoffeeScript Object Notation (CSON).
-# If you are unfamiliar with CSON, you can read more about it here:
-# https://github.com/bevry/cson#what-is-cson
-
-
 # Universal
 "*":
 	"Node Script":
@@ -80,15 +60,15 @@
 	DocBlock:
 		prefix: "/*"
 		body: "/**\n * $1\n */"
-	Babel:
-		prefix: "babel"
-		body: '"use babel";\n"use strict";\n\n'
-	Dir:
+	"Console - Dir":
 		prefix: "dir"
 		body: "console.dir($1);$0"
-	Info:
+	"Console - Info":
 		prefix: "info"
 		body: "console.info($1);$0"
+	"Console - Trace":
+		prefix: "trace"
+		body: "console.trace($1);$0"
 	Spec:
 		prefix: "it"
 		body: 'it("$1", () => {\n\t$2\n});'


Search completion; tweak default setting.

--- background_scripts/settings.coffee
+++ background_scripts/settings.coffee
@@ -93,7 +93,9 @@
     searchEngines: [
       "w: http://www.wikipedia.org/w/index.php?title=Special:Search&search=%s Wikipedia"
       ""
-      "# More examples:"
+      "# More examples."
+      "#"
+      "# (Vimium has built-in completion for these.)"
       "#"
       "# g: http://www.google.com/search?q=%s Google"
       "# l: http://www.google.com/search?q=%s&btnI I'm feeling lucky..."


Implement monadic ↓ as split.

Implement monadic ↓ as split.

--- src/vocabulary/drop.coffee
+++ src/vocabulary/drop.coffee
@@ -2,18 +2,19 @@
 {isInt, repeat, prod} = require '../helpers'
 {DomainError, RankError} = require '../errors'

-# Drop (`↓`)
-#
-# 4↓'OVERBOARD'            <=> 'BOARD'
-# ¯5↓'OVERBOARD'           <=> 'OVER'
-# ⍴10↓'OVERBOARD'          <=> ,0
-# 0 ¯2↓ 3 3 ⍴ 'ONEFATFLY'  <=> 3 1 ⍴ 'OFF'
-# ¯2 ¯1↓ 3 3 ⍴ 'ONEFATFLY' <=> 1 2 ⍴ 'ON'
-# 1↓ 3 3 ⍴ 'ONEFATFLY'     <=> 2 3 ⍴ 'FATFLY'
-# 1 1↓ 2 3 4⍴"ABCDEFGHIJKLMNOPQRSTUVWXYZ"   <=> 1 2 4 ⍴ 'QRSTUVWX'
-# ¯1 ¯1↓ 2 3 4⍴"ABCDEFGHIJKLMNOPQRSTUVWXYZ" <=> 1 2 4 ⍴ 'ABCDEFGH'
 @['↓'] = (omega, alpha, axis) ->
   if alpha
+
+    # Drop (`↓`)
+    #
+    # 4↓'OVERBOARD'            <=> 'BOARD'
+    # ¯5↓'OVERBOARD'           <=> 'OVER'
+    # ⍴10↓'OVERBOARD'          <=> ,0
+    # 0 ¯2↓ 3 3 ⍴ 'ONEFATFLY'  <=> 3 1 ⍴ 'OFF'
+    # ¯2 ¯1↓ 3 3 ⍴ 'ONEFATFLY' <=> 1 2 ⍴ 'ON'
+    # 1↓ 3 3 ⍴ 'ONEFATFLY'     <=> 2 3 ⍴ 'FATFLY'
+    # 1 1↓ 2 3 4⍴"ABCDEFGHIJKLMNOPQRSTUVWXYZ"   <=> 1 2 4 ⍴ 'QRSTUVWX'
+    # ¯1 ¯1↓ 2 3 4⍴"ABCDEFGHIJKLMNOPQRSTUVWXYZ" <=> 1 2 4 ⍴ 'ABCDEFGH'
     if alpha.shape.length > 1
       throw RankError()
     a = alpha.toArray()
@@ -39,4 +40,21 @@
       new APLArray omega.data, shape, omega.stride, offset

   else
-    throw Error 'Not implemented'
+
+    # Split (`↓`)
+    #
+    # ↓1 2 3 <=> ⊂1 2 3
+    # ↓(1 2)(3 4) <=> ⊂(1 2)(3 4)
+    # ↓2 2⍴⍳4 <=> (0 1)(2 3)
+    # ↓2 3 4⍴⍳24 <=> 2 3⍴(0 1 2 3)(4 5 6 7)(8 9 10 11)(12 13 14 15)(16 17 18 19)(20 21 22 23)
+    if omega.shape.length is 0
+      throw NonceError 'Split of scalar not implemented'
+    oshape = omega.shape[...omega.shape.length - 1]
+    obound = oshape.reduce ((a, b) -> a * b), 1
+    ishape = omega.shape[omega.shape.length - 1]
+    array = omega.toArray()
+    data = []
+    for i in [0...obound]
+      offset = i * ishape
+      data.push new APLArray array[offset...offset + ishape]
+    new APLArray data, oshape


prevent bug

prevent bug

--- index.cjsx
+++ index.cjsx
@@ -242,7 +242,7 @@
     render: ->
       <div>
       <link rel='stylesheet' href={join(relative(ROOT, __dirname), 'assets', 'hensei-nikki.css')} />
-        <TabbedArea activeKey={@state.selectedKey} onSelect={@handleSelectTab}>
+        <TabbedArea activeKey={@state.selectedKey} onSelect={@handleSelectTab} animation={false}>
           <TabPane eventKey={1} tab={__ 'Records'}>
             <HenseiList indexKey={0}
                         selectedKey={@state.selectedKey}


Fix initialize when disabled

Fix initialize when disabled

--- index.cjsx
+++ index.cjsx
@@ -12,7 +12,8 @@
   if process.env.DEBUG?
     window.akashicRecordsWindow.openDevTools
       detach: true
-initialAkashicRecordsWindow()
+if config.get('plugin.Akashic.enable', true)
+  initialAkashicRecordsWindow()

 module.exports =
   name: 'Akashic'


stop dynamic loading of locale temporarily till we figure out dynamicRequire is failing

--- app/locale/locale.coffee
+++ app/locale/locale.coffee
@@ -10,11 +10,11 @@
   'en': require('./en') # Include these in the main bundle
   'en-US': require('./en-US')
   'en-GB': { nativeDescription: 'English (UK)', englishDescription: 'English (UK)' }
-  'zh-HANS': { nativeDescription: '简体中文', englishDescription: 'Chinese (Simplified)' }
+  'zh-HANS': require('./zh-HANS')
   'zh-HANT': { nativeDescription: '繁體中文', englishDescription: 'Chinese (Traditional)' }
-  'ru': { nativeDescription: 'русский', englishDescription: 'Russian' }
+  'ru': require('./ru')
   'es-ES': { nativeDescription: 'español (ES)', englishDescription: 'Spanish (Spain)' }
-  'es-419': { nativeDescription: 'español (América Latina)', englishDescription: 'Spanish (Latin America)' }
+  'es-419': require('./es-419')
   'fr': { nativeDescription: 'français', englishDescription: 'French' }
   'pt-PT': { nativeDescription: 'Português (Portugal)', englishDescription: 'Portuguese (Portugal)' }
   'pt-BR': { nativeDescription: 'Português (Brasil)', englishDescription: 'Portuguese (Brazil)' }


opps made mistakes.

opps made mistakes.

--- scripts/loud.coffee
+++ scripts/loud.coffee
@@ -48,11 +48,10 @@

       when 'ban'
         robot.brain.get('louds_banned').push(data) if data not in robot.brain.get('louds_banned')
-        deleteLoud(data, 'louds')
+        deleteLoud(data, 'louds') if data in robot.brain.get('louds')

       when 'unban'
-        robot.brain.get('louds_banned').push(data) if data in robot.brain.get('louds_banned')
-        deleteLoud(data, 'louds_banned')
+        deleteLoud(data, 'louds_banned') if data in robot.brain.get('louds_banned')

       when 'nuke'
         if process.env.DEBUG != 'true'


Didn't initialize louds_banned...

Didn't initialize louds_banned...

--- scripts/loud.coffee
+++ scripts/loud.coffee
@@ -71,3 +71,7 @@
   robot.brain.once 'loaded', (data) ->
     if not robot.brain.get('louds')?
       robot.brain.set('louds', [])
+    if not robot.brain.get('louds_banned')?
+      robot.brain.set('louds_banned', [])
+
+  getRandomLoud = (res) ->


Stop before emptying

Stop before emptying

--- lib/script-view.coffee
+++ lib/script-view.coffee
@@ -37,6 +37,7 @@
     @output.empty()
     # Close any existing process and start a new one
     @stop()
+    @output.empty()
     @start()

   close: ->


View: reverted due to an error on formview

--- src/core/view.coffee
+++ src/core/view.coffee
@@ -93,7 +93,7 @@

     super options, data

-    @getData()?.on? 'update', @bound 'render'
+    data?.on? 'update', @bound 'render'

     @defaultInit()
     @devHacks()


l2regex

--- scripts/sigh.coffee
+++ scripts/sigh.coffee
@@ -4,7 +4,7 @@
 #   ivey

 module.exports = (robot) ->
-  robot.hear /\w\*?sigh\*?\w/i, (msg) ->
+  robot.hear /\bsigh\b/i, (msg) ->
     msg.send "http://media.tumblr.com/60425e6ef4dc35ac83bd3ef23ef3cc8f/tumblr_inline_misumkoT5k1qz4rgp.gif"


Add missing param

Add missing param

--- lib/assets/javascripts/easy_app/bootstrap.js.coffee
+++ lib/assets/javascripts/easy_app/bootstrap.js.coffee
@@ -1,7 +1,7 @@
 root = exports ? this

 # Autoload modal boxes
-$(document).on 'click', '.modal-box', ->
+$(document).on 'click', '.modal-box', (event) ->
   event.preventDefault()
   draggable = $(this).data('draggable')
   size      = $(this).data('modal-size')


replaced render function in stubView to store the viewMessage. Also added a new method to stubView to return the lastMessage inserted

--- lib/calatrava/templates/kernel/spec/stubView.coffee
+++ lib/calatrava/templates/kernel/spec/stubView.coffee
@@ -2,6 +2,8 @@

 stubView =
   create: (name) ->
+    lastMessage = null
+
     boundEvents: {}
     fieldValues: {}
     trigger: (event, args...) ->
@@ -10,7 +12,10 @@
     bind: (event, handler) ->
       @boundEvents[event] = handler

-    render: jasmine.createSpy("#{name} render")
+    render: (viewMessage) ->
+      lastMessage = viewMessage
+
+    lastMessage: () -> lastMessage

     fieldContains: (name, value) -> @fieldValues[name] = value
     get: (name, callback) ->


Bugfix isSimulation again

Bugfix isSimulation again

--- lib/messages.coffee
+++ lib/messages.coffee
@@ -166,7 +166,7 @@
   message.id = id
   diffid = MessagesDiff.insert message
   message._id = diffid
-  notifyMessageUpdate message unless @isSimulation
+  notifyMessageUpdate message if Meteor.isServer  ## client in simulation
   diffid

 _messageParent = (child, parent, position = null, oldParent = true, importing = false) ->


throw out event emitter stuff

throw out event emitter stuff


Fixed bug.

Fixed bug.

--- tasks/shortStats.coffee
+++ tasks/shortStats.coffee
@@ -12,7 +12,7 @@
     # docslimited = JSON.parse(JSON.stringify(docs.splice(0, 50)))
     doc = {
       name: 'eloleaderboard'
-      data: docslimited
+      data: docs
     }
     Statistics.update({name: 'eloleaderboard'}, doc, {upsert:true}, (err)->
         if err


unescape given strings, reify newlines

unescape given strings, reify newlines

--- lib/text-buffer.coffee
+++ lib/text-buffer.coffee
@@ -166,6 +166,7 @@
     onGiveAction: (index, content, paran) -> @protectCursor =>
         goal = @findGoal index
         if content.length > 0
+            content = content.replace(/\\n/g, '\n')
             log 'Text Buffer', "handling give-action #{paran} #{content}"
             goal.setContent content
         if paran


#48 clean up leftover

#48 clean up leftover

--- lib/text-object.coffee
+++ lib/text-object.coffee
@@ -168,9 +168,9 @@
       from = from.translate([0, -1])

     # In case cursor is on one of pair char, we adjust `from` point to be inclusive.
-    charAtCursor = characterAtPoint(@editor, from)
-    if charAtCursor in @pair
-      switch @getPairState(@pair, characterAtPoint(@editor, from), from)
+    characterAtCursor = characterAtPoint(@editor, from)
+    if characterAtCursor in @pair
+      switch @getPairState(@pair, characterAtCursor, from)
         when 'open' then from = from.translate([0, +1])
         when 'close' then from = from.translate([0, -1])


disable stripe a/b test

disable stripe a/b test

--- js/inner/billing.coffee
+++ js/inner/billing.coffee
@@ -243,7 +243,11 @@

   loadStripe: () =>
     $.getScript "https://js.stripe.com/v1/"
-    if VM.ab().stripe_v3()
+    # Stripe has a bug in v3 that makes ajax sends use url/form-encoded
+    # instead of application/json, this breaks our ajax calls, so users
+    # can't change their containers. If we want a true result, we'll
+    # have to restart the test with a different name.
+    if false #VM.ab().stripe_v3()
       $.getScript "https://checkout.stripe.com/v3/checkout.js"
     else
       $.getScript "https://checkout.stripe.com/v2/checkout.js"


set trial-parallelism to 2 for the plan template, so that we don't have to hack around it

--- js/inner/project.coffee
+++ js/inner/project.coffee
@@ -25,7 +25,6 @@
     parallel: 1
     paying_user: null
     loaded_paying_user: null
-    trial_parallelism: null
     retried_build: null
     branches: null
     default_branch: null


also register existing users

also register existing users

--- js/libs/ab-tests.coffee
+++ js/libs/ab-tests.coffee
@@ -68,3 +68,10 @@
         first_page_load: true
     else
       mixpanel.unregister 'first_page_load'
+
+    if mixpanel.get_property('mp_name_tag')
+      mixpanel.register
+        existing_user: true
+    else
+      mixpanel.register
+        existing_user: false


set cursor start of original selection on . repeat  and cleanup

--- lib/operator.coffee
+++ lib/operator.coffee
@@ -495,39 +495,31 @@
     @pattern ?= ///#{settings.get('numberRegex')}///g

   mutateSelection: (selection) ->
-    pattern = ///#{settings.get('numberRegex')}///g
-    {cursor} = selection
-
-    bufferRange = selection.getBufferRange()
-
-    ranges = []
-    @eachNumberInSelection selection, (matchText, replace) =>
-      newText = @getNextNumber(matchText)
-      ranges.push(replace(newText))
-
-    if @isMode('visual')
-      point = bufferRange.start
-    else
-      if ranges.length
-        point = ranges[0].end.translate([0, -1])
+    initialPoint = @mutationManager.getInitialPointForSelection(selection)
+    pattern = @getPattern()
+    scanRange = selection.getBufferRange()
+
+    newRange = null
+    @editor.scanInBufferRange pattern, scanRange, ({matchText, range, stop, replace}) =>
+      if @target.is('CurrentSelection')
+        replace(@getNextNumber(matchText))
+      else
+        return unless range.end.isGreaterThan(initialPoint)
+        newRange = replace(@getNextNumber(matchText))
+        stop()
+
+    if @target.is('CurrentSelection')
+      point = scanRange.start
+    else
+      if newRange?
+        point = newRange.end.translate([0, -1])
       else
         point = @mutationManager.getInitialPointForSelection(selection)
-    cursor.setBufferPosition(point)
+    selection.cursor.setBufferPosition(point)

   getNextNumber: (numberString) ->
     number = parseInt(numberString, 10) + @step * @getCount()
     String(number)
-
-  eachNumberInSelection: (selection, fn) ->
-    scanRange = selection.getBufferRange()
-    initialPoint = @mutationManager.getInitialPointForSelection(selection)
-    @editor.scanInBufferRange @getPattern(), scanRange, ({matchText, range, stop, replace}) =>
-      if @target.is('CurrentSelection')
-        fn(matchText, replace)
-      else
-        return unless range.end.isGreaterThan(initialPoint)
-        fn(matchText, replace)
-        stop()

 class Decrease extends Increase
   @extend()


Modify the program

Modify the program

--- src/config/controls.coffee
+++ src/config/controls.coffee
@@ -26,8 +26,11 @@
       if action? then action['name'] else world.get('_LABEL')
     click: (world, args) ->
       action = world.get('action')
-      current = world.get('current')
-      current.call(action['do'], action)
+      turtle = world.get('current')
+      program = turtle.get('program')
+      turtle.call(action['do'], action)
+      program.push world.get('action_key')
+      console.log 'click', turtle, action, program
   }
   _CHILDREN: [
     {


scrapped non-standard constructor

scrapped non-standard constructor

--- lib/views/index.coffee
+++ lib/views/index.coffee
@@ -26,18 +26,15 @@
                     @div outlet: "content", class: "content"

         ### CONSTRUCTOR ###
-        constructor: ( { @page } ) ->
-            # Check if we have a valid requested page
+
+        ### INITIALIZE ###
+        initialize: ({@page}) ->
+            # Let us check what kind of view we want to generate
+            super
             if @page?
                 # Get the uri path without the forward slash
                 @controller = @page.substring( 1 ).split( "/" )[0]
-
-            # Call the super
-            super
-
-        ### INITIALIZE ###
-        initialize: ->
-            # Let us check what kind of view we want to generate
+                        # Let us check what kind of view we want to generate
             if @controller?
                 # We have a valid controller, so let us create the view
                 # based off that


Fix scss code in html not aligning

Fix scss code in html not aligning

Fixes #5

--- lib/provider.coffee
+++ lib/provider.coffee
@@ -1,5 +1,5 @@
 module.exports =
-  selector: ['.source.css.scss', '.source.sass']
+  selector: ['.source.css.scss', '.source.sass', '.text.html.basic'] # .text.html.basic is a temporary hack to support inline
   id: 'aligner-scss' # package name
   config:
     ':-enabled':


clean up

clean up

--- src/data-type.coffee
+++ src/data-type.coffee
@@ -130,27 +130,7 @@
   0x3E:
     type: 'FLT8'
     name: 'Float'
-    declaration: (parameter) ->
-      'float'
-    writeParameterData: (buffer, parameter) ->
-      # ParamMetaData (TYPE_INFO)
-      buffer.writeUInt8(typeByName.FloatN.id)
-      buffer.writeUInt8(8)
-
-      # ParamLenData
-      if parameter.value?
-        buffer.writeUInt8(8)
-        bigNumber = parameter.value;
-        high32bits = 0;
-        for i in [63..31]
-          if(bigNumber - Math.pow(2, i) < 0)
-          else
-            bigNumber -= Math.pow(2, i);
-            high32bits += Math.pow(2, i - 32);
-        buffer.writeUInt32LE(bigNumber)
-        buffer.writeUInt32LE(high32bits)
-      else
-        buffer.writeUInt8(0)
+
   0x7A:
     type: 'MONEY4'
     name: 'SmallMoney'
@@ -167,10 +147,8 @@
       # ParamLenData
       if parameter.value?
         buffer.writeUInt8(8)
-        bigNumber = parameter.value;
-        high32bits = 0;
-        buffer.writeUInt32LE(bigNumber%4294967296)
-        buffer.writeUInt32LE(Math.floor(bigNumber/4294967296))
+        buffer.writeUInt32LE(parameter.value%4294967296)
+        buffer.writeUInt32LE(Math.floor(parameter.value/4294967296))
       else
         buffer.writeUInt8(0)


Add optional state key to AtomEnvironment::saveState and ::loadState

--- src/atom-environment.coffee
+++ src/atom-environment.coffee
@@ -874,12 +874,12 @@

     @blobStore.save()

-  saveState: (options) ->
+  saveState: (options, storageKey) ->
     new Promise (resolve, reject) =>
       if @enablePersistence and @project
         state = @serialize(options)
         savePromise =
-          if storageKey = @getStateKey(@project?.getPaths())
+          if storageKey ?= @getStateKey(@project?.getPaths())
             @stateStore.save(storageKey, state)
           else
             @applicationDelegate.setTemporaryWindowState(state)
@@ -887,9 +887,9 @@
       else
         resolve()

-  loadState: ->
+  loadState: (stateKey) ->
     if @enablePersistence
-      if stateKey = @getStateKey(@getLoadSettings().initialPaths)
+      if stateKey ?= @getStateKey(@getLoadSettings().initialPaths)
         @stateStore.load(stateKey).then (state) =>
           if state
             state


[experiment] activateOnStart config param

--- lib/settings.coffee
+++ lib/settings.coffee
@@ -88,13 +88,16 @@
     caseSensitivityForNarrowQuery:
       default: 'inherit'
       enum: ['inherit', 'smartcase', 'sensitive', 'insensitive']
+    activateOnStart:
+      default: 'always'
+      enum: ['inherit', 'never', 'always', 'on-input']
     negateNarrowQueryByEndingExclamation: false
     autoPreview: true
     autoPreviewOnQueryChange: true
     closeOnConfirm: true
     revealOnStartCondition:
       default: 'always'
-      enum: ['never', 'always', 'on-input']
+      enum: ['inherit', 'never', 'always', 'on-input']

   _.deepExtend(properties, otherProperties) if otherProperties?

@@ -125,6 +128,10 @@
     enum: ['smartcase', 'sensitive', 'insensitive']
     description: "Case sensitivity of your query in narrow-editor"

+  activateOnStart:
+    default: 'always'
+    enum: ['never', 'always', 'on-input']
+
   confirmOnUpdateRealFile: true

   # Per providers settings


Fix regression in breadcrumb code for listings.

--- src/listing.coffee
+++ src/listing.coffee
@@ -35,7 +35,7 @@
     i = 0
     while i < uri.length
         i++
-        continue unless path[i]
+        continue unless uri[i]
         breadcrumbs.push {
             name: uri[i]
             path: uri[0..i].join('/') + '/'


Avoid to use ScopeDescriptor from atom core

--- lib/snippets.coffee
+++ lib/snippets.coffee
@@ -1,5 +1,5 @@
 path = require 'path'
-{ScopeDescriptor, Emitter, Disposable, CompositeDisposable, File} = require 'atom'
+{Emitter, Disposable, CompositeDisposable, File} = require 'atom'
 _ = require 'underscore-plus'
 async = require 'async'
 CSON = require 'season'
@@ -181,6 +181,15 @@
       @storeUnparsedSnippetsByPrefix(snippetsByPrefix, filePath, selector)
     return

+  getScopeChain: (object) ->
+    scopesArray = object?.getScopesArray?()
+    scopesArray ?= Array.from(object)
+    scopesArray
+      .map (scope) ->
+        scope = ".#{scope}" unless scope[0] is '.'
+        scope
+      .join(' ')
+
   storeUnparsedSnippetsByPrefix: (value, path, selector) ->
     unparsedSnippets = {}
     unparsedSnippets[selector] = {"snippets": value}
@@ -192,8 +201,7 @@
       @scopedPropertyStore.removePropertiesForSourceAndSelector(path, scopeSelector)

   parsedSnippetsByPrefixForScope: (descriptor) ->
-    scopeDescriptor = ScopeDescriptor.fromObject(descriptor)
-    unparsedSnippetsByPrefix = @scopedPropertyStore.getPropertyValue(scopeDescriptor.getScopeChain(), "snippets")
+    unparsedSnippetsByPrefix = @scopedPropertyStore.getPropertyValue(@getScopeChain(descriptor), "snippets")
     unparsedSnippetsByPrefix ?= {}
     snippets = {}
     for prefix, attributes of unparsedSnippetsByPrefix


Show tooltips quicker

Show tooltips quicker

Issue #117

--- lib/tab-view.coffee
+++ lib/tab-view.coffee
@@ -87,7 +87,7 @@
         title: itemPath
         html: false
         delay:
-          show: 2000
+          show: 1000
           hide: 100
         placement: 'bottom'


Use path.basename() instead of fsUtils.base()

--- lib/tab-view.coffee
+++ lib/tab-view.coffee
@@ -1,6 +1,7 @@
 $ = require 'jquery'
 {View} = require 'space-pen'
 fsUtils = require 'fs-utils'
+path = require 'path'

 module.exports =
 class TabView extends View
@@ -46,7 +47,7 @@
     if fileNameText?
       duplicates = @editor.getEditSessions().filter (session) -> fileNameText is session.buffer.getBaseName()
       if duplicates.length > 1
-        directory = fsUtils.base(fsUtils.directory(@editSession.getPath()))
+        directory = path.basename(fsUtils.directory(@editSession.getPath()))
         fileNameText = "#{fileNameText} - #{directory}" if directory
     else
       fileNameText = 'untitled'


Provide suggestions on blank prefix

Provide suggestions on blank prefix

--- release/lib/texton-autocomplete.coffee
+++ release/lib/texton-autocomplete.coffee
@@ -65,10 +65,13 @@
     else
       return []
     p.then((res) =>
-      scoredItems = []
-      for item in res
-        score = fuzzaldrin.score(item.text, prefix)
-        if score > 0
-          scoredItems.push({item, score})
-      scoredItems.sort((a, b) => b.score - a.score)
-      scoredItems.map((res) => mapResult(res.item)))
+      if prefix.length is 0
+        res.map((r) => mapResult(r))
+      else
+        scoredItems = []
+        for item in res
+          score = fuzzaldrin.score(item.text, prefix)
+          if score > 0
+            scoredItems.push({item, score})
+        scoredItems.sort((a, b) => b.score - a.score)
+        scoredItems.map((res) => mapResult(res.item)))


fix(macUtil): Removed unnecessary module variable

--- src/util.coffee
+++ src/util.coffee
@@ -4,10 +4,8 @@
 ## Contains various miscellaneous utility functions and extensions.
 ##

-util = angular.module "Mac.Util", []
-
 # Expose an object with a bunch of utility functions on it.
-util.factory "util", [
+angular.module("Mac.Util", []).factory "util", [
   "$filter"
   (
     $filter


update inspect-status doc

update inspect-status doc

--- src/layout/inspect_status.coffee
+++ src/layout/inspect_status.coffee
@@ -1,8 +1,9 @@
+#
 # inspect_status.coffee
 #
 # Role: show and edit status of current sprite
-# Responsibility:
-# * track current sprite
+#
+# Responsibility: display and edit status
 #

 {my} = require '../my'


Actually check for the last comment

Actually check for the last comment

--- app/talk/discussion.cjsx
+++ app/talk/discussion.cjsx
@@ -227,6 +227,7 @@

   comment: (data, i) ->
     active = +data.id is +@props.location.query?.comment
+    scrollToLastComment = (i is @state.comments.length - 1) && @state.shouldScrollToBottom
     <Comment
       {...@props}
       project={@props.project}
@@ -244,7 +245,7 @@
       onLikeComment={@onLikeComment}
       onUpdateComment={@onUpdateComment}
       onDeleteComment={@onDeleteComment}
-      scrollIntoView = {@state.shouldScrollToBottom || active}
+      scrollIntoView = {scrollToLastComment || active}
     />

   onClickDeleteDiscussion: ->


Fix typo at keymaps/atomatigit.cson.

Fix typo at keymaps/atomatigit.cson.

--- keymaps/atomatigit.cson
+++ keymaps/atomatigit.cson
@@ -39,7 +39,7 @@
 '.platform-darwin .atomatigit .file-list-view':
   'cmd-shift-z': 'atomatigit:show'

-'.platform-win32 .atomatigit .file-list-view, platform-linux .atomatigit .file-list-view':
+'.platform-win32 .atomatigit .file-list-view, .platform-linux .atomatigit .file-list-view':
   'ctrl-shift-z': 'atomatigit:show'

 '.atomatigit .branch-list-view':


view.visible performance fix

view.visible performance fix

Replaced use of jQuery $el.is(":visible") with a simpler
$el.style.display == "block" to significantly improve typing performance
in complex pages.

--- src/view.coffee
+++ src/view.coffee
@@ -44,7 +44,7 @@
   #
   # @return [Boolean]
   visible: ->
-    @$el.is(":visible")
+    @this.$el.style.display == "block"

   highlighted: ->
     @$el.find(".cur").length > 0


Edit functional test.

Edit functional test.

--- test/module.func.coffee
+++ test/module.func.coffee
@@ -33,7 +33,7 @@
     inspect vm.status.state

   addVm = (api, name) ->
-    templates = api.templates.findAll name: 'template_http'
+    templates = api.templates.findAll name: 'prealloc_template'
     template = templates[0].update()

     clusters = api.clusters.findAll name: 'local_cluster'
@@ -57,7 +57,12 @@
     startStop vms[0]

     newVm = addVm api, 'custom_vm_1'
-    inspect newVm?.$properties
+    if newVm?
+      console.log '---- VM creation report ----'
+      inspect newVm.$properties
+      console.log '----------- Nics -----------'
+      inspect nic.$properties for nic in newVm.nics.getAll()
+

   do fiber.run


Decay doesn't affect you if your rating is <1000.

--- server/schedule.coffee
+++ server/schedule.coffee
@@ -20,8 +20,10 @@
       ratings.getRatings ids, (err, oldRatings) ->
         return  if err
         newRatings = oldRatings.map (rating) ->
-          rating -= 10
-          Math.max(rating, DEFAULT_RATING)
+          if rating < DEFAULT_RATING
+            rating
+          else
+            Math.max(rating - 10, DEFAULT_RATING)
         ratings.setRatings ids, newRatings, ->
           job.emit('finished')


Capitalize transport type labels.

Capitalize transport type labels.

--- web/js/views/route_graph_view.coffee
+++ web/js/views/route_graph_view.coffee
@@ -140,10 +140,10 @@
     # TODO: This should be somewhere in i18n
     _transportTypeLabel: (type) ->
       switch type
-        when 'walk' then 'kävely'
-        when '2' then "ratikka"
-        when '6' then "metro"
-        when '7' then "lautta"
-        when '12' then "juna"
+        when 'walk' then 'Kävely'
+        when '2' then "Ratikka"
+        when '6' then "Metro"
+        when '7' then "Lautta"
+        when '12' then "Juna"
         else "bussi"


bugfix problems with Content-Type timing while caching

--- modules/gui/platforms/node/lib/StylusContext.coffee
+++ modules/gui/platforms/node/lib/StylusContext.coffee
@@ -22,14 +22,13 @@

                 @delegate '_addRoute', @data.route, (req, res, next)=>

+                    res.ctype = 'text/css'
+
                     res.cache.lastModified floyd.tools.files.stat(@data.file).mtime, ()=>

                         stylus(floyd.tools.files.read @data.file).render (err, style)=>

-                            res.ctype = 'text/css'
-
-                            res.cache.etag()
-
                             res.send style
-
+
+                done()


remove debugging

remove debugging

--- services/track-changes/test/acceptance/coffee/AppendingUpdatesTests.coffee
+++ services/track-changes/test/acceptance/coffee/AppendingUpdatesTests.coffee
@@ -6,8 +6,6 @@
 ObjectId = mongojs.ObjectId
 Settings = require "settings-sharelatex"
 request = require "request"
-console.log "hiiiiis"
-console.log Settings.redis.history
 rclient = require("redis").createClient(Settings.redis.history) # Only works locally for now

 TrackChangesApp = require "./helpers/TrackChangesApp"
@@ -87,7 +85,6 @@

 		describe "when the updates are recent and from the same user", ->
 			beforeEach (done) ->
-				console.log 1
 				TrackChangesClient.pushRawUpdates @project_id, @doc_id, [{
 					op: [{ i: "b", p: 6 }]
 					meta: { ts: Date.now(), user_id: @user_id }
@@ -101,13 +98,9 @@
 					meta: { ts: Date.now(), user_id: @user_id }
 					v: 8
 				}], (error) =>
-					console.log 2, error
-					throw error if error?
-					console.log 3
+					throw error if error?
 					TrackChangesClient.flushAndGetCompressedUpdates @project_id, @doc_id, (error, @updates) =>
-						console.log 4, error
 						throw error if error?
-						console.log 5
 						done()

 			it "should combine all the updates into one pack", ->


Clean up

Clean up

--- services/web/app/coffee/Features/Collaborators/CollaboratorsHandler.coffee
+++ services/web/app/coffee/Features/Collaborators/CollaboratorsHandler.coffee
@@ -30,19 +30,36 @@
 			return callback(error) if error?
 			return callback new Errors.NotFoundError("no project found with id #{project_id}") if !project?
 			members = []
-			members.push { id: project.owner_ref.toString(), privilegeLevel: PrivilegeLevels.OWNER, source: Sources.OWNER }
-			# read-and-write
+			members.push {
+				id: project.owner_ref.toString(),
+				privilegeLevel: PrivilegeLevels.OWNER,
+				source: Sources.OWNER
+			}
 			for member_id in project.collaberator_refs or []
-				members.push { id: member_id.toString(), privilegeLevel: PrivilegeLevels.READ_AND_WRITE, source: Sources.INVITE }
+				members.push {
+					id: member_id.toString(),
+					privilegeLevel: PrivilegeLevels.READ_AND_WRITE,
+					source: Sources.INVITE
+				}
+			for member_id in project.readOnly_refs or []
+				members.push {
+					id: member_id.toString(),
+					privilegeLevel: PrivilegeLevels.READ_ONLY,
+					source: Sources.INVITE
+				}
 			if project.publicAccesLevel == PublicAccessLevels.TOKEN_BASED
 				for member_id in project.tokenAccessReadAndWrite_refs or []
-					members.push { id: member_id.toString(), privilegeLevel: PrivilegeLevels.READ_AND_WRITE, source: Sources.TOKEN }
-			# read-only
-			for member_id in project.readOnly_refs or []
-				members.push { id: member_id.toString(), privilegeLevel: PrivilegeLevels.READ_ONLY, source: Sources.INVITE }
-			if project.publicAccesLevel == PublicAccessLevels.TOKEN_BASED
+					members.push {
+						id: member_id.toString(),
+						privilegeLevel: PrivilegeLevels.READ_AND_WRITE,
+						source: Sources.TOKEN
+					}
 				for member_id in project.tokenAccessReadOnly_refs or []
-					members.push { id: member_id.toString(), privilegeLevel: PrivilegeLevels.READ_ONLY, source: Sources.TOKEN }
+					members.push {
+						id: member_id.toString(),
+						privilegeLevel: PrivilegeLevels.READ_ONLY,
+						source: Sources.TOKEN
+					}
 			return callback null, members

 	getMemberIds: (project_id, callback = (error, member_ids) ->) ->


replace mocks with stubs in UpdateMerger tests

--- services/web/test/unit/coffee/ThirdPartyDataStore/UpdateMergerTests.coffee
+++ services/web/test/unit/coffee/ThirdPartyDataStore/UpdateMergerTests.coffee
@@ -83,30 +83,32 @@
 			@splitDocLines = @docLines.split("\n")
 			@fs.readFile = sinon.stub().callsArgWith(2, null, @docLines)

-		it 'should set the doc text in the editor controller', (done)->
-			@editorController.setDoc = ->
-			mock = sinon.mock(@editorController).expects("setDoc").withArgs(@project_id, @doc_id, @user_id, @splitDocLines, @source).callsArg(5)
+			@editorController.setDoc = sinon.stub().callsArg(5)

 			@update.write(@docLines)
 			@update.end()

-			@updateMerger.p.processDoc @project_id, @doc_id, @user_id, @update, "path", @source, ->
-				mock.verify()
+		it 'should set the doc text in the editor controller', (done)->
+			@updateMerger.p.processDoc @project_id, @doc_id, @user_id, @update, "path", @source, =>
+				@editorController.setDoc
+					.calledWith(@project_id, @doc_id, @user_id, @splitDocLines, @source)
+					.should.equal true
 				done()

 		it 'should create a new doc when it doesnt exist', (done)->
 			folder = {_id:"adslkjioj"}
 			docName = "main.tex"
 			path = "folder1/folder2/#{docName}"
-			@editorController.mkdirpWithoutLock = sinon.stub().withArgs(@project_id).callsArgWith(2, null, [folder], folder)
-			@editorController.addDocWithoutLock = ->
-			mock = sinon.mock(@editorController).expects("addDocWithoutLock").withArgs(@project_id, folder._id, docName, @splitDocLines, @source, @user_id).callsArg(6)
+			@editorController.mkdirpWithoutLock = sinon.stub().callsArgWith(2, null, [folder], folder)
+			@editorController.addDocWithoutLock = sinon.stub().callsArg(6)

-			@update.write(@docLines)
-			@update.end()
-
-			@updateMerger.p.processDoc @project_id, undefined, @user_id, @update, path, @source, ->
-				mock.verify()
+			@updateMerger.p.processDoc @project_id, undefined, @user_id, @update, path, @source, =>
+				@editorController.mkdirpWithoutLock
+					.calledWith(@project_id)
+					.should.equal true
+				@editorController.addDocWithoutLock
+					.calledWith(@project_id, folder._id, docName, @splitDocLines, @source, @user_id)
+					.should.equal true
 				done()

 	describe 'processFile :', (done)->
@@ -137,7 +139,6 @@
 				done()

 	describe 'delete entity :', (done)->
-
 		beforeEach ->
 			@path = "folder/doc1"
 			@type = "mock-type"
@@ -145,6 +146,7 @@
 			@entity_id = "entity_id_here"
 			@entity = _id:@entity_id
 			@projectLocator.findElementByPath = (project_id, path, cb)=> cb(null, @entity, @type)
+			@editorController.deleteEntityWithoutLock = sinon.stub().callsArg(5)

 		it 'should get the element id', ->
 			@projectLocator.findElementByPath = sinon.spy()
@@ -153,9 +155,8 @@

 		it 'should delete the entity in the editor controller with the correct type', (done)->
 			@entity.lines = []
-			mock = sinon.mock(@editorController).expects("deleteEntityWithoutLock").withArgs(@project_id, @entity_id, @type, @source, @user_id).callsArg(5)
-			@updateMerger.deleteUpdate @user_id, @project_id, @path, @source, ->
-				mock.verify()
+			@updateMerger.deleteUpdate @user_id, @project_id, @path, @source, =>
+				@editorController.deleteEntityWithoutLock
+					.calledWith(@project_id, @entity_id, @type, @source, @user_id)
+					.should.equal true
 				done()
-
-


cache on for self closing optimized test

--- test/selfclosing.coffee
+++ test/selfclosing.coffee
@@ -21,15 +21,15 @@
   describe '#img(name, attr)', ->
     it 'should render', ->
       t = -> img()
-      cc.render(t, optimized: true).should.equal ''
+      cc.render(t, optimized: true, cache: on).should.equal ''
     it 'should render with attributes', ->
       t = -> img src: 'http://foo.jpg.to'
       cc.render(t).should.equal ''
   describe '#br()', ->
     it 'should render', ->
       t = -> br()
-      cc.render(t, optimized: true).should.equal '
'
+      cc.render(t, optimized: true, cache: on).should.equal '
'
   describe '#link()', ->
     it 'should render with attributes', ->
       t = -> link href: '/foo.css', rel: 'stylesheet'
-      cc.render(t, optimized: true).should.equal '<link href="/foo.css" rel="stylesheet" />'
+      cc.render(t, optimized: true, cache: on).should.equal '<link href="/foo.css" rel="stylesheet" />'


fix arg handling test

fix arg handling test

--- test/server.test.coffee
+++ test/server.test.coffee
@@ -31,16 +31,16 @@
     expect(server.target[1]).to.be 'test.2'

   it 'accepts options as options:', ->
-    {server} = Server.args_to_params args: [target: 'test', options: {yMax: 9999}]
+    {client, server} = Server.args_to_params args: [target: 'test', options: {yMax: 9999}]
     expect(server.target.length).to.be 1
     expect(server.target[0]).to.be 'test'
-    expect(server.yMax).to.be 9999
+    expect(client.yMax).to.be 9999

   it 'treats everything except targets as options', ->
-    {server} = Server.args_to_params args: [target: 'test', yMax: 9999]
+    {client, server} = Server.args_to_params args: [target: 'test', yMax: 9999]
     expect(server.target.length).to.be 1
     expect(server.target[0]).to.be 'test'
-    expect(server.yMax).to.be 9999
+    expect(client.yMax).to.be 9999

   it 'accepts variadic string arguments as targets', ->
     {server} = Server.args_to_params args: ['a', 'b', 'c']


Always return array from Git.getLineDiffs()

--- src/app/git.coffee
+++ src/app/git.coffee
@@ -144,7 +144,7 @@
     @getRepo().getAheadBehindCounts()

   getLineDiffs: (path, text) ->
-    @getRepo().getLineDiffs(@relativize(path), text)
+    @getRepo().getLineDiffs(@relativize(path), text) ? []

 _.extend Git.prototype, Subscriber
 _.extend Git.prototype, EventEmitter


:memo: Throughly document BufferedNodeProcess

--- src/buffered-node-process.coffee
+++ src/buffered-node-process.coffee
@@ -1,23 +1,46 @@
 BufferedProcess = require './buffered-process'
 path = require 'path'

-# Public: Like BufferedProcess, but accepts a node script instead of an
-# executable, on Unix which allows running scripts and executables, this seems
-# unnecessary, but on Windows we have to separate scripts from executables since
-# it doesn't support shebang strings.
+# Public: Like {BufferedProcess}, but accepts a Node script instead of an
+# executable.
+#
+# This may seem unnecessary but on Windows we have to have separate executables
+# for each script without this since Windows doesn't support shebang strings.
 module.exports =
 class BufferedNodeProcess extends BufferedProcess
+  # Executes the given Node script.
+  #
+  # * options
+  #    + command:
+  #      The path to the Javascript script to execute.
+  #    + args:
+  #      The array of arguments to pass to the script (optional).
+  #    + options:
+  #      The options Object to pass to Node's `ChildProcess.spawn` (optional).
+  #    + stdout:
+  #      The callback that receives a single argument which contains the
+  #      standard output of the script. The callback is called as data is
+  #      received but it's buffered to ensure only complete lines are passed
+  #      until the source stream closes. After the source stream has closed
+  #      all remaining data is sent in a final call (optional).
+  #    + stderr:
+  #      The callback that receives a single argument which contains the
+  #      standard error of the script. The callback is called as data is
+  #      received but it's buffered to ensure only complete lines are passed
+  #      until the source stream closes. After the source stream has closed
+  #      all remaining data is sent in a final call.
+  #    + exit:
+  #      The callback which receives a single argument containing the exit
+  #      status (optional).
   constructor: ({command, args, options, stdout, stderr, exit}) ->
     node =
       if process.platform is 'darwin'
-        # On OS X we use the helper process to run script, because it doesn't
-        # create an icon on the Dock.
+        # Use a helper to prevent an icon from appearing on the Dock
         path.resolve(process.resourcesPath, '..', 'Frameworks',
                      'Atom Helper.app', 'Contents', 'MacOS', 'Atom Helper')
       else
         process.execPath

-    # Tell atom-shell to run like upstream node.
     options ?= {}
     options.env ?= Object.create(process.env)
     options.env['ATOM_SHELL_INTERNAL_RUN_AS_NODE'] = 1


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

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

   player = GameObject

-  player.include(Me)
+  player.include Metered

   player.position()
   # => {x: 50, y: 40}


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/engine_selector.coffee
+++ src/engine_selector.coffee
@@ -14,7 +14,7 @@
       this.each (item) ->
         item.I[attr] = value

-  closest: (selector) ->
+  closest: (selector, position) ->

   ###*
   Get a selection of GameObjects that match the specified selector criteria. The selector language


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/engine_selector.coffee
+++ src/engine_selector.coffee
@@ -59,8 +59,7 @@
   @param {String} selector
   @returns {Array} An array of the objects found
   ###
-  each: (selector) ->
-    ;
+

   find: (selector) ->
     results = []


Reply to presence probes

Reply to presence probes

- Client JIDs are marked as online with 2 different resources: "abc" and "def"
- Server JIDs are just marked online

--- test/test_server.coffee
+++ test/test_server.coffee
@@ -29,7 +29,7 @@
     # Construct a new test server with a configuration suitable for unit
     # testing.
     constructor: ->
-        config =
+        @config =
             modelBackend: "postgres"
             modelConfig:
                 host: "localhost"
@@ -51,7 +51,7 @@
                 file: "test-suite.log"
             checkCreateNode: -> true
             autosubscribeNewUsers: []
-        @server = server.startServer config
+        @server = server.startServer @config

         # Cache IQs sent by the buddycloud server
         @iqs =
@@ -169,6 +169,22 @@
     send: (data) ->
         stanza = if data instanceof ltx.Element then data else ltx.parse data
         switch stanza.name
+            when "presence"
+                stanza.attrs.should.have.property "to"
+                stanza.attrs.should.have.property "type"
+                if stanza.attrs.type is "probe"
+                    # Reply to presence probes. If the probed JID is a client,
+                    # reply with 2 online resources: "abc" and "def".
+                    to = stanza.attrs.from ? @config.xmpp.jid
+                    bareFrom = stanza.attrs.to
+                    if "@" in bareFrom
+                        froms = [bareFrom + "/abc", bareFrom + "/def"]
+                    else
+                        froms = [bareFrom]
+                    for from in froms
+                        p = new ltx.Element("presence", from: from, to: to)
+                        @emit "stanza", p.root()
+
             when "iq"
                 stanza.attrs.should.have.property "from"
                 stanza.attrs.should.have.property "to"
@@ -193,8 +209,7 @@
                                 var: feature
                         iq = @makeIq("result", stanza.attrs.to, stanza.attrs.from, id)
                             .cnode(queryEl)
-                            .root()
-                        @emit "stanza", iq
+                        @emit "stanza", iq.root()
                     if stanza.getChild("query", "http://jabber.org/protocol/disco#items")? and stanza.attrs.to of @disco.items
                         items = @disco.items[stanza.attrs.to]
                         queryEl = new ltx.Element("query", xmlns: "http://jabber.org/protocol/disco#items")
@@ -202,7 +217,6 @@
                             queryEl.c "item", item
                         iq = @makeIq("result", stanza.attrs.to, stanza.attrs.from, id)
                             .cnode(queryEl)
-                            .root()
-                        @emit "stanza", iq
+                        @emit "stanza", iq.root()
             else
                 @emit "got-stanza", stanza


Update common.coffee

Update common.coffee

--- src/fs_utils/common.coffee
+++ src/fs_utils/common.coffee
@@ -8,18 +8,18 @@
 sysPath = require 'path'
 {isWindows} = require '../helpers'

-# Short-cut to `exists` function that works on both node 0.6 and 0.8+.
+# Shortcut to `exists` function that works on both node 0.6 and 0.8+.
 exports.exists = fs.exists or sysPath.exists

 # Directory separator.
 exports.sep = sysPath.sep or (if isWindows then '\\' else '/')

-# Create file if it doesn't exist and writes data to it.
-# Would also create a parent directories if they don't exist.
+# Writes data into a file.
+# Creates the file and/or all parent directories if they don't exist.
 #
-# path - String. Path to file that would be written.
+# path - String. Path to the file.
 # data - String. Data to be written.
-# callback(error, path, data) - would be executed on error or on
+# callback(error, path, data) - Executed on error or on
 #    successful write.
 #
 # Example


Add environment support.

Add environment support.

--- src/natives.coffee
+++ src/natives.coffee
@@ -345,6 +345,15 @@
               error "TODO(Object::wait): respect the timeout param (#{timeout})"
             rs.wait _this
       ]
+      ProcessEnvironment: [
+        o 'environ()[[B', (rs) ->
+            env_arr = []
+            # convert to an array of strings of the form [key, value, key, value ...]
+            for k, v of process.env
+              env_arr.push new JavaArray c2t('[B'), rs, util.bytestr_to_array k
+              env_arr.push new JavaArray c2t('[B'), rs, util.bytestr_to_array v
+            new JavaArray c2t('[[B'), rs, env_arr
+      ]
       reflect:
         Array: [
           o 'newArray(L!/!/Class;I)L!/!/Object;', (rs, _this, len) ->


Don't render score elements in a loop

Don't render score elements in a loop

--- resources/assets/coffee/react/beatmapset-page/score.coffee
+++ resources/assets/coffee/react/beatmapset-page/score.coffee
@@ -22,36 +22,36 @@
   mixins: [React.addons.PureRenderMixin]

   render: ->
+    hits = Hits.generate score: @props.score, playmode: @props.playmode
+
     div className: 'beatmapset-score',
-      for elem in ['position', 'flag', 'player', 'mods', 'rank', 'score', 'accuracy', 'hits']
-        className = "beatmapset-score__element beatmapset-score__element--#{elem}"
-        className += ' hidden-xs' if elem == 'hits' || elem == 'accuracy'
+      div className: 'beatmapset-score__element beatmapset-score__element--position',
+        "##{@props.position}"

-        contents =
-          switch elem
-            when 'position'
-              "##{@props.position}"
-            when 'flag'
-              if @props.score.user.data.country
-                el FlagCountry,
-                  country: @props.countries[@props.score.user.data.country]
-                  classModifiers: ['scoreboard']
-            when 'player'
-              a
-                href: laroute.route 'users.show', users: @props.score.user.data.id
-                @props.score.user.data.username
-            when 'mods'
-              el Mods,
-                mods: @props.score.mods
-                classModifiers: ['small', 'reversed']
-            when 'rank'
-              div className: "badge-rank badge-rank--#{@props.score.rank}"
-            when 'score'
-              @props.score.score.toLocaleString()
-            when 'accuracy'
-              "#{_.round @props.score.accuracy * 100, 2}%"
-            when 'hits'
-              hits = Hits.generate score: @props.score, playmode: @props.playmode
-              hits.values
+      div className: 'beatmapset-score__element beatmapset-score__element--flag',
+        if @props.score.user.data.country
+          el FlagCountry,
+            country: @props.countries[@props.score.user.data.country]
+            classModifiers: ['scoreboard']

-        div className: className, key: elem, contents
+      div className: 'beatmapset-score__element beatmapset-score__element--player',
+        a
+          href: laroute.route 'users.show', users: @props.score.user.data.id
+          @props.score.user.data.username
+
+      div className: 'beatmapset-score__element beatmapset-score__element--mods',
+        el Mods,
+          mods: @props.score.mods
+          classModifiers: ['small', 'reversed']
+
+      div className: 'beatmapset-score__element beatmapset-score__element--rank',
+        div className: "badge-rank badge-rank--#{@props.score.rank}"
+
+      div className: 'beatmapset-score__element beatmapset-score__element--score',
+        @props.score.score.toLocaleString()
+
+      div className: 'beatmapset-score__element beatmapset-score__element--accuracy hidden-xs',
+        "#{_.round @props.score.accuracy * 100, 2}%"
+
+      div className: 'beatmapset-score__element beatmapset-score__element--hits hidden-xs',
+        hits.values


using variable name identifier instead of require for the stitch namespace option, if undefined it should resort to "require"

--- src/package.coffee
+++ src/package.coffee
@@ -12,7 +12,7 @@
     @name        = name
     @argv        = argv
     # set config values
-    @require     = config.require
+    @identifier  = config.identifier
     @target      = config.target
     @libs        = toArray(config.libs || [])
     @paths       = toArray(config.paths || [])
@@ -27,7 +27,7 @@
     @depend or= new Dependency(@modules)
     _stitch   = new Stitch(@paths)
     _modules  = @depend.resolve().concat(_stitch.resolve())
-    stitchFile(identifier: @require, modules: _modules)
+    stitchFile(identifier: @identifier, modules: _modules)

   compileLibs: ->
     # TODO: be able to handle being given a folder and loading each file...can this compile coffeescript??


Add package name to Grim.deprecate call for activationEvents

--- src/package.coffee
+++ src/package.coffee
@@ -465,7 +465,7 @@
           @activationCommands[selector].push(commands...)

     if includeDeprecatedAPIs and @metadata.activationEvents?
-      deprecate """
+      deprecate("""
         Use `activationCommands` instead of `activationEvents` in your package.json
         Commands should be grouped by selector as follows:
         ```json
@@ -474,7 +474,7 @@
             "atom-text-editor": ["foo:quux"]
           }
         ```
-      """
+      """, {packageName: @name})
       if _.isArray(@metadata.activationEvents)
         for eventName in @metadata.activationEvents
           @activationCommands['atom-workspace'] ?= []


Added connection ping feature.

Added connection ping feature.

--- app/assets/javascripts/app/lib/websocket.js.coffee
+++ app/assets/javascripts/app/lib/websocket.js.coffee
@@ -56,19 +56,19 @@
     console.log 'send websockend ping'
     @send( { action: 'ping' } )

-    # check if ping is back within 30 sec.
+    # check if ping is back within 2 min
     if @check_id
       clearTimeout(@check_id)
     check = =>
       console.log 'no websockend ping response, reconnect...'
       @close()
-    @check_id = @delay check, 60000
+    @check_id = @delay check, 120000

   pong: ->
     console.log 'received websockend ping'

-    # test again after 10 sec.
-    @delay @ping, 30000
+    # test again after 1 min
+    @delay @ping, 60000

   connect: =>
 #    console.log '------------ws connect....--------------'
@@ -103,7 +103,7 @@
       @queue = []

       # send ping to check connection
-      @delay @ping, 30000
+      @delay @ping, 60000

     @ws.onmessage = (e) =>
       pipe = JSON.parse( e.data )


callTags are not needed for bundling anymore

--- app/assets/javascripts/controllers/calls.js.coffee
+++ app/assets/javascripts/controllers/calls.js.coffee
@@ -18,18 +18,13 @@

   bundlePairs: ( ->
     @get('model').forEach (bridge) =>
-      ctag = bridge.get('callTag')
-      oId  = bridge.get('originId')
-      return unless ctag && oId
+      oId = bridge.get('originId')
+      return unless oId

-      origin = @store.getById('call', oId)
-      @connectBridgeTo(origin, bridge)
-      @setCurrentCall(bridge) if @tagMatches(origin, ctag)
-  ).observes('model.@each.{callTag,originId}')
-
-
-  tagMatches: (origin, ctag) ->
-    origin && origin.get('callTag') == ctag
+      if (origin = @store.getById 'call', oId)
+        @connectBridgeTo(origin, bridge)
+        @setCurrentCall(bridge)
+  ).observes('model.@each.originId')

   connectBridgeTo: (origin, bridge) ->


always rewrite window.uploadcare

always rewrite window.uploadcare

--- app/assets/javascripts/uploadcare/namespace.coffee
+++ app/assets/javascripts/uploadcare/namespace.coffee
@@ -1,4 +1,4 @@
-window.uploadcare ||= {}
+window.uploadcare = {}

 uploadcare.namespace = (path, fn) ->


kontrol: query should be nested inside args

--- src/kontrol/kontrol.coffee
+++ src/kontrol/kontrol.coffee
@@ -42,8 +42,8 @@
   createKites: (kiteDescriptors) ->
     (@createKite k for k in kiteDescriptors)

-  fetchKites: (query = {}, callback) ->
-    @kite.tell 'getKites', [query], (err, result) =>
+  fetchKites: (args = {}, callback) ->
+    @kite.tell 'getKites', [args], (err, result) =>
       if err?
         callback err
         return
@@ -56,8 +56,8 @@
       return
     return

-  fetchKite: (query = {}, callback) ->
-    @fetchKites query, (err, kites) ->
+  fetchKite: (args = {}, callback) ->
+    @fetchKites args, (err, kites) ->
       if err?
         callback err
         return
@@ -70,10 +70,10 @@
       return
     return

-  watchKites: (query = {}, callback) ->
+  watchKites: (args = {}, callback) ->
     changes = new EventEmitter
     handler = @createUpdateHandler changes
-    @kite.tell 'getKites', [query, handler], (err, result) =>
+    @kite.tell 'getKites', [args, handler], (err, result) =>
       if err?
         callback err
         return


basic kontrol: WIP implementation

basic kontrol: WIP implementation

--- src/kontrol/kontrol.coffee
+++ src/kontrol/kontrol.coffee
@@ -36,18 +36,18 @@
         type      : 'token'
         key       : token

-  fetchKites: (selector = {}, callback) ->
-    @kite.tell 'getKites', [selector], (err, { kites }) =>
+  fetchKites: (query = {}, callback) ->
+    @kite.tell 'getKites', [query], (err, { kites: kiteDescriptors }) =>
       if err?
         callback err
         return

-      callback null, (@createKite k for k in kites)
+      callback null, @kiteify kiteDescriptors
       return
     return

-  fetchKite: (selector = {}, callback) ->
-    @fetchKites selector, (err, kites) ->
+  fetchKite: (query = {}, callback) ->
+    @fetchKites query, (err, kites) ->
       if err?
         callback err
         return
@@ -60,14 +60,37 @@
       return
     return

-  watchKites: (selector = {}, callback) ->
+  watchKites: (query = {}, callback) ->
     changes = new EventEmitter
-    onEvent = @createUpdateHandler changes, callback
+    handler = @createUpdateHandler changes
+    @kite.tell 'getKites', [query, handler], (err, result) =>
+      if err?
+        callback err
+        return
+
+      { kites: kiteDescriptors, watcherID } = result
+
+      callback null, {
+        action    : @constructor.actions.REGISTER
+        kites     : @kiteify kiteDescriptors
+        changes
+        watcherID
+      }

   cancelWatcher: (id, callback) ->

-  createUpdateHandler: (changes, callback) -> (err, change) =>
-    # TODO: implement
+  createUpdateHandler: (changes) -> (err, { action, kite: kiteDescriptor, token }) =>
+    debugger  # this is not reliable AFAICT
+    if err
+      changes.emit 'error', err
+      return
+
+    [kite] = @kiteify [kiteDescriptor]
+
+    eventName = @constructor.actions[action]
+    changes.emit eventName, kite
+
+  kiteify: (kiteDescriptors) -> (@createKite k for k in kiteDescriptors)

   @actions      =
     REGISTER    : 'register'


Remove get/setSoftWrap from Project

Remove get/setSoftWrap from Project

The config should now be the place that soft wrap settings is interacted
with.

--- src/project.coffee
+++ src/project.coffee
@@ -40,7 +40,6 @@
     path.join(config.get('core.projectHome'), repoName)

   softTabs: true
-  softWrap: false
   rootDirectory: null
   editSessions: null
   ignoredPathRegexes: null
@@ -198,16 +197,6 @@
   #
   # softTabs - A {Boolean} which, if `true`, sets soft tabs
   setSoftTabs: (@softTabs) ->
-
-  # Identifies if the project is using soft wrapping.
-  #
-  # Returns a {Boolean}.
-  getSoftWrap: -> @softWrap
-
-  # Sets the project to use soft wrapping.
-  #
-  # softTabs - A {Boolean} which, if `true`, sets soft wrapping
-  setSoftWrap: (@softWrap) ->

   # Given a path to a file, this constructs and associates a new `EditSession`, showing the file.
   #


add `v` to version message

add `v` to version message

--- src/publish.coffee
+++ src/publish.coffee
@@ -54,7 +54,7 @@
   #            argument and a the generated tag string as the second argument.
   versionPackage: (version, callback) ->
     process.stdout.write 'Preparing and tagging a new version '
-    versionArgs = ['version', version, '-m', 'Prepare %s release']
+    versionArgs = ['version', version, '-m', 'Prepare v%s release']
     @fork @atomNpmPath, versionArgs, (code, stderr='', stdout='') =>
       if code is 0
         @logSuccess()


fix(container): fixed export on edit for multicontainers

--- src/lib/containable.coffee
+++ src/lib/containable.coffee
@@ -68,7 +68,7 @@
       items.push c
       c.__updated.subscribe =>
         callback = DelayedSave.apply(self, [options, self, datahandler])
-        callback self[name](), field
+        callback self[name]().map((e) -> e.export()), field

   ObservableArray self, name, items

@@ -88,10 +88,10 @@
     self[name].push c
     c.__updated.subscribe =>
       callback = DelayedSave.apply(self, [options, self, datahandler])
-      callback self[name](), field
+      callback self[name]().map((e) -> e.export()), field

   self[name].subscribe =>
     callback = DelayedSave.apply(self, [options, self, datahandler])
-    callback self[name](), field
+    callback self[name]().map((e) -> e.export()), field

 module.exports = Containable if module?


bounded documentation

bounded documentation

--- src/bounded.coffee
+++ src/bounded.coffee
@@ -16,7 +16,8 @@
   @name bounds
   @methodOf Bounded#

-  @param {String} event The event to listen to.
+  @param {number} xOffset the amount
+  @param {number} yOffset
   @param {Function} callback The function to be called when the specified event
   is triggered.
   ###


change to isoweek

change to isoweek

--- src/reports.coffee
+++ src/reports.coffee
@@ -96,8 +96,8 @@

 fetchChannelReport = (channel, user, flag, callback) ->
   if flag == 'dailyReport'
-    from = moment().subtract(1, 'days').startOf('isoWeek').toDate()
-    to = moment().subtract(1, 'days').endOf('isoWeek').toDate()
+    from = moment().subtract(1, 'days').startOf('day').toDate()
+    to = moment().subtract(1, 'days').endOf('day').toDate()
     period = 'day'
   else
     from = moment().subtract(1, 'days').startOf('isoWeek').toDate()


Got to use the same export object for it to work

--- src/require.coffee
+++ src/require.coffee
@@ -21,7 +21,7 @@
   css: (file) -> __read file
   js:  (file) ->
     code    = __read file
-    exports = {}
+    exports = __modules[file] # Use existing object (if one exists)
     module  = exports: exports

     src  = "function define(cb){cb.call(this,require,exports)};"


Minor fix to block_addr.

Minor fix to block_addr.

--- src/runtime.coffee
+++ src/runtime.coffee
@@ -327,4 +327,4 @@
       if address < addr
         return block_addr
       block_addr = addr
-    rs.mem_blocks[block_addr]
+    UNSAFE? || throw new Error "Invalid memory access at #{address}"


Update from `sendfile` to `sendFile`

Update from `sendfile` to `sendFile`

--- src/service.coffee
+++ src/service.coffee
@@ -50,7 +50,7 @@

   if configuration.static.catchAll?
     glp.service.all '*', (request, response, next) ->
-      response.sendfile configuration.static.catchAll,
+      response.sendFile configuration.static.catchAll,
         root: configuration.root

   address = chalk.white 'http://localhost:' + configuration.static.port