deleted unused trash

deleted unused trash

--- addon/services/autox-session-context.coffee
+++ addon/services/autox-session-context.coffee
@@ -36,10 +36,6 @@

   fetchChannelable: (key) ->
     return @get("model")?.get key
-    # id = @get("model.relationships.#{key}.data.id")
-    # type = @get("model.relationships.#{key}.data.type")
-    # return RSVP.resolve() if isBlank(id) or isBlank(type)
-    # @get("store").findRecord singularize(type), id

   connect: (key) ->
     @fetchChannelable(key)


replace reserved words , fixes #84

replace reserved words , fixes #84

--- app/assets/javascripts/dante/editor.js.coffee
+++ app/assets/javascripts/dante/editor.js.coffee
@@ -248,11 +248,11 @@
     a is b

   #set focus and caret position on element
-  setRangeAt: (element, int=0)->
+  setRangeAt: (element, pos=0)->
     range = document.createRange()
     sel = window.getSelection()
     #node = element.firstChild;
-    range.setStart(element, int); #DANGER this is supported by IE 9
+    range.setStart(element, pos); #DANGER this is supported by IE 9
     #range.setStartAfter(element)
     #range.setEnd(element, int);
     range.collapse(true)
@@ -261,7 +261,7 @@
     element.focus()

   #set focus and caret position on element
-  setRangeAtText: (element, int=0)->
+  setRangeAtText: (element, pos=0)->
     range = document.createRange()
     sel = window.getSelection()
     node = element.firstChild;


CR comments

CR comments

--- apps/user/client/email_prefrences_form.coffee
+++ apps/user/client/email_prefrences_form.coffee
@@ -1,7 +1,4 @@
 _ = require 'underscore'
-Backbone = require 'backbone'
-FlashMessage = require '../../../components/flash/index.coffee'
-Form = require '../../../components/mixins/form.coffee'
 SubForm = require './sub_form.coffee'

 module.exports = class EmailPreferencesForm extends SubForm
@@ -16,8 +13,8 @@
     @configureEmailSubscriptions(@model.get('receive_emails'))

   changeEmailSubscription: (event) ->
-    receive_emails = if $(event.target).is ':checked' then true else false
-    @configureEmailSubscriptions(receive_emails)
+    receiveEmails = if $(event.target).is ':checked' then true else false
+    @configureEmailSubscriptions(receiveEmails)

   configureEmailSubscriptions: (enable) ->
     @$('.email_subscriptions input').prop('disabled', !enable)


Testing utility functions

Testing utility functions

--- test/utils.test.coffee
+++ test/utils.test.coffee
@@ -28,4 +28,8 @@
     formatResults = streamTweets._private.formatResults
     expect(formatResults(longResult1)).to.be.a('object')

-
+  it 'Should handle errors in the input of make request', ()->
+    makeRequest = streamTweets._private.makeRequest
+    # todo
+
+


Simplify jade task

Simplify jade task

--- root/Gruntfile.coffee
+++ root/Gruntfile.coffee
@@ -1,14 +1,4 @@
 glob = require('glob')
-
-getJadeFiles = ->
-  ret = {}
-  files = glob.sync("src/**/*.jade").filter((file) ->
-    not file.match(/_layout\.jade$/)
-  ).forEach((file) ->
-    dest = file.replace(/^src/, "dest").replace(/jade$/, "html")
-    ret[dest] = file
-  )
-  ret

 getDeplyUploadFiles = ->
   glob.sync("dest/**/*").map (path) ->
@@ -37,7 +27,13 @@
         ]
     jade:
       compile:
-        files: getJadeFiles()
+        files: [
+          expand: true
+          cwd: 'src/'
+          src: ['**/!(_)*.jade']
+          dest: 'dest/'
+          ext: '.html'
+        ]
     connect:
       server:
         options:


Physics: Old attempt to fix native Bullet, without effect. Code is more correct at least.

--- engine/physics.coffee
+++ engine/physics.coffee
@@ -129,7 +129,7 @@
             verts.set vertices.subarray(v*vstride,v*vstride+3), offset
             offset += 3
         mesh = new Ammo.btTriangleIndexVertexArray(indices.length/3, inds.buffer, 3*4,
-                                                vertices.length/3, verts.buffer, 3*4)
+                                                vlen, verts.buffer, 3*4)
         mesh.things = [inds, verts] # avoid deleting those
         # crashes because wrapbtBvhTriangleMeshShape::calculateLocalInertia
         # is being called for some reason
@@ -154,22 +154,22 @@
             HEAPF32.set vertices.subarray(v*vstride,v*vstride+3), offset
             offset += 3
         mesh = new Ammo.btTriangleIndexVertexArray(indices.length/3, inds, 3*4,
-                                                vertices.length/3, verts, 3*4)
+                                                vlen, verts, 3*4)
     shape =  new Ammo.btBvhTriangleMeshShape mesh, true, true
     shape.name = name
     _tmp_Vector3.setValue scale[0], scale[1], scale[2]
     shape.setLocalScaling _tmp_Vector3
     shape.setMargin margin
+    shape.calculateLocalInertia = ->
     return shape

 CompoundShape = ->
     return new Ammo.btCompoundShape

 RigidBody = (mass, shape, position, rotation, friction, elasticity, form_factor)->
-    # For some reason this is the formula in BGE for inertia
-    inertia = form_factor * mass/3
-    localInertia =  new Ammo.btVector3 inertia, inertia, inertia
-    shape.calculateLocalInertia mass, localInertia
+    localInertia =  new Ammo.btVector3 0, 0, 0
+    if mass
+        shape.calculateLocalInertia mass, localInertia
     startTransform = new Ammo.btTransform
     _tmp_Vector3.setValue position[0], position[1], position[2]
     startTransform.setOrigin _tmp_Vector3


skip xsolla token length check

skip xsolla token length check

--- resources/assets/lib/store-xsolla.coffee
+++ resources/assets/lib/store-xsolla.coffee
@@ -22,8 +22,6 @@
     new Promise (resolve, reject) ->
       $.post route('payments.xsolla.token'), { orderNumber }
       .done (data) ->
-        # Make sure laroute hasn't trolled us.
-        return reject(message: 'wrong token length') unless data.access_token?.length == 32
         resolve(data)
       .fail (xhr) ->
         reject(xhr: xhr)


reload page to show error if xsolla paystation is dismissed without completing

--- resources/assets/lib/store-xsolla.coffee
+++ resources/assets/lib/store-xsolla.coffee
@@ -52,7 +52,10 @@
       done = true

     XPayStationWidget.on XPayStationWidget.eventTypes.CLOSE, ->
+      LoadingOverlay.show()
+      LoadingOverlay.show.flush()
+
       if done
-        LoadingOverlay.show()
-        LoadingOverlay.show.flush()
         window.location = laroute.route('payments.xsolla.completed', 'foreignInvoice': orderNumber)
+      else
+        window.location.reload()


check that track is a function

check that track is a function

--- components/split_test/split_test.coffee
+++ components/split_test/split_test.coffee
@@ -19,11 +19,12 @@
   set: (outcome) ->
     @cookies().set @_key(), outcome

-    analytics?.track 'Experiment Viewed',
-      experiment_id: @key
-      experiment_name: @key
-      variation_id: outcome
-      variation_name: outcome
+    if _.isFunction analytics?.track
+      analytics?.track 'Experiment Viewed',
+        experiment_id: @key
+        experiment_name: @key
+        variation_id: outcome
+        variation_name: outcome

     # Set for Google Analytics
     setDimension @dimension, outcome if @dimension?


Updates memberEmailAddress and contactEmailAddress when invite is sent

--- src/persistence.coffee
+++ src/persistence.coffee
@@ -658,6 +658,18 @@
         err
       ).callback callback

+  # Reload memberEmailAddresses when invite is sent
+  wrapMethod sdk, 'invite', (invite) ->
+    (options, callback) ->
+      invite.call(this, options).then((result) ->
+        if options.hasOwnProperty('memberId')
+          memberId = options.memberId
+          sdk.loadMemberEmailAddresses({memberId: memberId}).then -> result
+        else if options.hasOwnProperty('contactId')
+          contactId = options.contactId
+          sdk.loadContactEmailAddresses({contactId: contactId}).then -> result
+    ).callback callback
+

 revertSDK = (sdk) ->
   revertWrapMethod sdk, 'saveMember'


FileUpload: initDropZone: allow dropZone to be CUI.DOM.

--- src/elements/FileUpload/FileUpload.coffee
+++ src/elements/FileUpload/FileUpload.coffee
@@ -229,7 +229,7 @@
 			dropZone:
 				mandatory: true
 				check: (v) ->
-					isElement(v)
+					isElement(v) or isElement(v.DOM)
 			multiple:
 				mandatory: true
 				default: true


* Escape inputs when they are provided directly by the user.

--- vendor/assets/javascripts/control.js.coffee
+++ vendor/assets/javascripts/control.js.coffee
@@ -96,7 +96,7 @@
     return if @itemExists(name)
     if @options.onCreateItem
       return unless name = @options.onCreateItem(name)
-    @addItemRow(name)
+    @addItemRow(_.escape(name))
     @clearInput()
     @updateQuery()


Selection basics.

Selection basics.

--- vendor/assets/javascripts/lazy/admin.coffee
+++ vendor/assets/javascripts/lazy/admin.coffee
@@ -21,7 +21,7 @@

 class LazyAdmin
   constructor: ->
-    @_selection_actions()
+    @_init_selection()
     @_custom_file_input()
     @_locales_tabs()
     @_fileupload()
@@ -58,12 +58,21 @@
       $(event.currentTarget).parent().toggleClass('opened')

     $(document).on 'append-next-page', (event, content) =>
+      all_selected = @all_selected()
       element = $(event.target)
       element.find('.show-more').replaceWith $(content)
       element.foundation()
       @set_main_content_height()
+      if all_selected
+        $('.items-list input[type=checkbox]').each (idx, el) ->
+          $(el)
+            .prop('checked', true)
+            .trigger('change', [true])
+        @selection_changed()
+

     @_sortable_list()
+

   set_main_content_height: ->
     _height = $(window).innerHeight() - $('header.panel-header').outerHeight()
@@ -116,17 +125,47 @@
     $.datepicker.setDefaults(dateFormat: 'dd/mm/yy');
     _init_each_datepicker()

-
-  _selection_actions: ->
-    $('form select#selection_action').on 'change', (event) ->
-      ids = []
-      for obj in $(this.form).serializeArray()
-        if obj.name == 'selection[ids][]'
-          ids.push obj.value
-      if ids.length > 0 and $(this).val().length > 0
-        $(this.form).submit()
+  selection_changed: (el) ->
+    select_all = $('#selection_all_ids').closest('.custom-check-box')
+    if @all_selected()
+      select_all.removeClass('unknown').addClass('checked')
+    else if @any_selected()
+      select_all.removeClass('checked').addClass('unknown')
+    else
+      select_all.removeClass('checked').removeClass('unknown')
+
+  selected: ->
+    $('.items-list input[type=checkbox]:checked').map (idx, el) ->
+      el.value
+
+  unselected: ->
+    $('.items-list input[type=checkbox]:not(:checked)').map (idx, el) ->
+      el.value
+
+  all_selected: ->
+    @selected().length > 0 and @unselected().length == 0
+
+  any_selected: ->
+    @selected().length > 0
+
+  _init_selection: ->
+    $(document).on 'change', '.items-list input[type=checkbox]', (event, mute) =>
+      el = $(event.currentTarget)
+      @selection_changed(el) unless mute == true
+
+    $(document).on 'click', '#selection_all_ids', (event) =>
+      event.preventDefault();
+      if @any_selected()
+        $('.items-list input[type=checkbox]:checked').each (idx, el) ->
+          $(el)
+            .prop('checked', false)
+            .trigger('change', [true])
       else
-        $(this).val('')
+        $('.items-list input[type=checkbox]').each (idx, el) ->
+          $(el)
+            .prop('checked', true)
+            .trigger('change', [true])
+      @selection_changed()

   _sortable_list: ->
     group = $('.items-list[data-sortable-update]:not(.filtered) .jspPane')


Fix multiple root path problem in vm root path selector.

--- client/ide/finder/idevmitem.coffee
+++ client/ide/finder/idevmitem.coffee
@@ -46,9 +46,8 @@
       path  = nodes.splice(1,x).join '/'
       parents.push "/#{path}"

-    parents.reverse()
-
-    items = []
+    parents = _.unique parents.reverse()
+    items   = []

     for path in parents when path
       items.push title: path, value: path


Refactor suggestion completer class for readability and design

--- client/suggestion_completer.coffee
+++ client/suggestion_completer.coffee
@@ -8,57 +8,60 @@
     @suggestionField = @options.suggestionField || "suggest"
     @suggestionName = @options.suggestionName || "placesuggest"
     @indexName = @options.indexName || "places"
-    @term = null
-    @inputEl.on('input', (e) =>
-      q = $("#q").val()
-      if q.length >= 2 and q != @term
-        @term = q
-        @getAutoCompleteResults(q).then (result) =>
-          @emit('result', result)
-          completions = result[@suggestionName][0].options
-          @listEl.empty()
-          $.map(completions, (completion) =>
-            li = $("
"+completion.text+"
")
-            li.on('click', (e) =>
-              $("##{@listEl.attr('id')} li").removeClass('selected')
-              li.addClass('selected')
-              @submit(e)
-            )
-            @listEl.append(li)
-          )
-    )
+    @nrOfResults = @options.nrOfResults || 6
+    @q = null
     @index = 0
     @payload = null
-    @inputEl.on('keydown', (e) =>
-      suggestionEls = $("##{@listEl.attr('id')} li")
-      keycode = event.which || event.keyCode
-      @index = suggestionEls.index($("##{@listEl.attr('id')} li.selected"))
-      switch keycode
-        when 40 # Down
-          if @index == -1 or @index > suggestionEls.length-2
-            @index = 0
-          else
-            @index++
-          suggestionEls.removeClass('selected')
-          $(suggestionEls[@index]).addClass('selected')
-          @emit('arrowdown',
-            $("##{@listEl.attr('id')} li.selected").first()
+    @suggestionEls = $()
+    @inputEl.on('input', (e) => @onInput(e))
+    @inputEl.on('keydown', (e) => @onKeyDown(e))
+
+  onInput: (e) ->
+    q = $("#q").val()
+    if q.length >= 2 and q != @q
+      @q = q
+      @getResults(q).then (result) =>
+        @emit('result', result)
+        completions = result[@suggestionName][0].options
+        @listEl.empty()
+        $.map(completions, (completion) =>
+          li = $("
"+completion.text+"
")
+          li.on('click', (e) =>
+            $("##{@listEl.attr('id')} li").removeClass('selected')
+            li.addClass('selected')
+            @submit(e)
           )
-        when 38 # Up
-          if @index < 0
-            @index = suggestionEls.length-1
-          else
-            @index--
-          suggestionEls.removeClass('selected')
-          $(suggestionEls[@index]).addClass('selected')
-          @emit('arrowup',
-            $("##{@listEl.attr('id')} li.selected").first()
-          )
-        when 13, 9 # Enter, tab
-          @submit(e)
-    )
+          @listEl.append(li)
+        )
+  onKeyDown: (e) ->
+    keycode = event.which || event.keyCode
+    @suggestionEls = $("##{@listEl.attr('id')} li")
+    @index = @suggestionEls.index($("##{@listEl.attr('id')} li.selected"))
+    switch keycode
+      when 40 # Down
+        if @index == -1 or @index > @suggestionEls.length-2
+          @index = 0
+        else
+          @index++
+        @suggestionEls.removeClass('selected')
+        $(@suggestionEls[@index]).addClass('selected')
+        @emit('arrowdown',
+          $("##{@listEl.attr('id')} li.selected").first()
+        )
+      when 38 # Up
+        if @index < 0
+          @index = @suggestionEls.length-1
+        else
+          @index--
+        @suggestionEls.removeClass('selected')
+        $(@suggestionEls[@index]).addClass('selected')
+        @emit('arrowup',
+          $("##{@listEl.attr('id')} li.selected").first()
+        )
+      when 13, 9 # Enter, tab
+        @submit(e)

   submit: (e) ->
     $("##{@listEl.attr('id')} li").removeClass("current")
@@ -69,15 +72,20 @@
     $("##{@listEl.attr('id')} li.selected").first().addClass("current")
     e.preventDefault()

-  getAutoCompleteResults: (q) ->
-    params = '{"' +
-      @suggestionName + '": {"text": "' +
-        q + '", "completion": {"field": "' +
-          @suggestionField +
-            '", "size": 6}}}'
+  getResults: (q) ->
+    @q = q
+    params = {
+      "placesuggest": {
+        "text": q,
+        "completion": {
+          "field": @suggestionField,
+          "size": @nrOfResults
+        }
+      }
+    }
     $.post('http://'+@host + ':' +
         @port +
-          '/'+@indexName+'/_suggest', params
+          '/'+@indexName+'/_suggest', JSON.stringify(params)
     )


better default background for map editor

--- app/assets/javascripts/map_editor.js.coffee
+++ app/assets/javascripts/map_editor.js.coffee
@@ -6,7 +6,7 @@
       box:
         height: 400
         width: null
-      back: "OpenStreetMap.HOT"
+      back: "Esri.WorldImagery"
       show: null
       edit: null
       change: null


fix default tabbing

fix default tabbing

--- app/assets/javascripts/views/tabs.js.coffee
+++ app/assets/javascripts/views/tabs.js.coffee
@@ -10,6 +10,7 @@
       s = window.location.hash.replace('#','')
       $("section.#{s}, ul.tabs a[data-section='#{s}']").addClass('active')
     else
+      $("section.tabbed").first().addClass('active')
       $("ul.tabs a").first().addClass('active')

   tabToggle: (e) ->


Fixed visual percentages bar

Fixed visual percentages bar

--- app/coffee/modules/common/directives.coffee
+++ app/coffee/modules/common/directives.coffee
@@ -39,11 +39,11 @@

 SprintProgressBarDirective = ->
-    renderProgress = ($el, percentage) ->
+    renderProgress = ($el, percentage, visual_percentage) ->
         if $el.is(".current-progress")
             $el.css("width", "#{percentage}%")
         else
-            $el.find(".current-progress").css("width", "#{percentage}%")
+            $el.find(".current-progress").css("width", "#{visual_percentage}%")
             $el.find(".number").html("#{percentage} %")

     link = ($scope, $el, $attrs) ->
@@ -51,7 +51,8 @@
             closedPoints = sprint.closed_points
             totalPoints = sprint.total_points
             percentage = Math.round(100 * (closedPoints/totalPoints))
-            renderProgress($el, percentage)
+            visual_percentage = Math.round(98 * (closedPoints/totalPoints)) #Visual hack for .current-progress bar
+            renderProgress($el, percentage, visual_percentage)

     return {link: link}


fix lightbox transitions

fix lightbox transitions

--- app/coffee/modules/common/lightboxes.coffee
+++ app/coffee/modules/common/lightboxes.coffee
@@ -39,13 +39,14 @@
         lightboxContent = $el.children().not(".close")
         lightboxContent.hide()

-        $el.css('display', 'flex')
-
-        @animationFrame.add =>
+        @animationFrame.add ->
+            $el.css('display', 'flex')
+
+        @animationFrame.add ->
             $el.addClass("open")

-            @animationFrame.add ->
-                $el.find('input,textarea').first().focus()
+        @animationFrame.add ->
+            $el.find('input,textarea').first().focus()

         @animationFrame.add =>
             lightboxContent.show()


fix add watchers lightbox

fix add watchers lightbox

--- app/coffee/modules/common/lightboxes.coffee
+++ app/coffee/modules/common/lightboxes.coffee
@@ -618,7 +618,7 @@
         render = (users) ->
             ctx = {
                 selected: false
-                users: _.head(users, 5)
+                users: _.slice(users, 0, 5)
                 showMore: users.length > 5
             }


don't fetch mail of a non imported mailbox

--- app/controllers/mailboxes_controller.coffee
+++ app/controllers/mailboxes_controller.coffee
@@ -103,11 +103,14 @@
     fetchBoxes = (boxes, callback) ->
         if boxes.length > 0
             box = boxes.pop()
-            box.getNewMails 200, (err) ->
-                if err
-                    callback err
-                else
-                    fetchBoxes box, callback
+            if box.imported
+                box.getNewMails 200, (err) ->
+                    if err
+                        callback err
+                    else
+                        fetchBoxes box, callback
+            else
+                fetchBoxes box, callback
         else
             callback()


Add test for finding user by search route.

--- test/app_test.coffee
+++ test/app_test.coffee
@@ -595,6 +595,18 @@
               json.arguments[0].should.eql argData
               done()

+        it 'should find a user by username', (done) ->
+          session (user, get, post) ->
+            query = encodeURIComponent(user.username)
+            get '/search/' + query + '.json', (err, res) ->
+              should.not.exist err
+              res.status.should.equal 200
+              json = res.body
+              json.users.should.be.an.instanceOf Array
+              json.users.length.should.equal 1
+              json.users[0].username.should.equal user.username
+              done()
+
     describe '/logout', ->
       describe 'GET /logout', ->
         it 'should clear session cookie and redirect to index', (done) ->


don't load list if already in scope or parent scope

--- lib/assets/javascripts/directives/ngrepeatlist.js.coffee
+++ lib/assets/javascripts/directives/ngrepeatlist.js.coffee
@@ -9,10 +9,23 @@
       element.parent().addClass('loading')
       list = element.injector().get(factory)
       context = {}
-      context[attributes.ngContext + '_id'] = scope[attributes.ngContext].id if attributes.ngContext
-      list.index context, (data) ->
-        scope[factory] = data
-        element.parent().removeClass('loading')
+      if attributes.ngContext
+        name = attributes.ngContext
+        if name.match(/\_id$/)
+          watch = name
+        else
+          watch = name + '.id'
+          name += "_id"
+        scope.$watch watch, (newVal) ->
+          if newVal
+            context[name] = if scope[name].id then scope[name].id else scope[name]
+            list.index context, (data) ->
+              scope[factory] = data
+              element.parent().removeClass('loading')
+      else
+        list.index context, (data) ->
+          scope[factory] = data
+          element.parent().removeClass('loading')
     template: (element, attributes) ->
       element[0].setAttribute('ng-repeat', attributes.ngRepeatList)
       element[0].removeAttribute('ng-repeat-list')


synced with readme

synced with readme

--- script.coffee
+++ script.coffee
@@ -2,13 +2,14 @@
 API
 ===
 ```javascript
-var worker = communist({sum:function(a,b,cb){cb(a+b);},square:function(a){return a*a;});
-worker.sum(2,5).then(function(a){console.log(a);})//prints 7
+var worker = communist({sum:function(a,cb){cb(a[0]+a[1]);},square:function(a){return a*a;});
+worker.sum([2,5]).then(function(a){console.log(a);})//prints 7
 worker.square(5).then(function(a){console.log(a);})//prints 25
-worker.close()//closes the worker, can be overwritten, worker._close() is a backup
+worker.close()//closes the worker, can be overwritten, worker._close() can't be closed.
 ```

-Give it an object of functions, and you can call them by name, your functions can either return a value or call a callback function which is passed after all your arguments.
+Give it an object of functions, and you can call them by name, your functions can either return a value or call a callback function which is passed as the second argument.
+Call the function with the data as the first argument and a transfer list for the second.
 It takes two arguments, data and an optional list of any arrayBuffers to transfer ownership of.
 If you want to do things once when the worker is created pass a function called `initialize` this gets called once with no arguments.  All workers are called in the same context so
 `this` can be used to store things, functions can also use `this` to call each other.
@@ -32,7 +33,7 @@

 ###Want it fancy? MAP REDUCE!!!

-```lang-javascript
+```javascript
 var worker = communist(4);
 //pass it the number of map workers
 worker.data([1,2,3]);
@@ -97,4 +98,4 @@
 	color: true

 $ ()->
-	$('#cont').html(marked(md))+	$('#cont').html(marked(md))


Last Reviews: Only reload if was added a new review

--- app/scripts/controllers/last-reviews.coffee
+++ app/scripts/controllers/last-reviews.coffee
@@ -7,7 +7,7 @@
     @getLastReviews()

     @WebSocketFcty.on((message) =>
-      @getLastReviews()
+      @getLastReviews() if message.type == 'new-review'
     )

   _fillReviews: (reviews) =>


setup the nav bar background to white to see the border changes

--- testApp/app/controllers/navigationbar.coffee
+++ testApp/app/controllers/navigationbar.coffee
@@ -3,12 +3,9 @@

     # always put everything inside PhoneGap deviceready
     document.addEventListener "deviceready", ->
-
       # Make Navigation Bar to appear with a custom title text
       steroids.view.navigationBar.show { title: "navbar" }

-      steroids.view.setBackgroundColor("#FFF")
-
   @testHideAnimated: ->
     steroids.view.navigationBar.hide {
       animated: true


Stub out remaining Redis methods

Stub out remaining Redis methods

--- test/models/station.coffee
+++ test/models/station.coffee
@@ -31,6 +31,8 @@
     @stub1 = sinon.stub redis, "get"
     @stub2 = sinon.stub citybikes, "networks"
     @stub3 = sinon.stub citybikes, "stations"
+    @stub4 = sinon.stub redis, "set"
+    @stub5 = sinon.stub redis, "expire"
     @stub1.yields null, null

   it 'constructor should instatinate object', (done) ->
@@ -99,4 +101,6 @@
     @stub1.restore()
     @stub2.restore()
     @stub3.restore()
+    @stub4.restore()
+    @stub5.restore()
     done()


Catch decodeURIComponent() errors

Catch decodeURIComponent() errors

--- server.coffee
+++ server.coffee
@@ -530,6 +530,9 @@
     console.log()
     return
   req = parseRequest decodedRequest
+  if not req?
+    console.warn "error: request can't be parsed: #{decodedRequest}"
+    return
   console.log "===request (decoded)==="
   process.stdout.write decodedRequest
   console.log "============="
@@ -594,6 +597,10 @@
     if bufString.indexOf('\r\n\r\n') is -1
       return
     req = parseRequest bufString
+    if not req?
+      console.warn "error: request can't be parsed: #{bufString}"
+      c.buf = null
+      return
     req.rawbody = c.buf[req.headerBytes+4..]
     req.socket = c
     if req.headers['content-length']?
@@ -1504,9 +1511,18 @@
     continue if /^\s*$/.test line
     params = line.split ": "
     headers[params[0].toLowerCase()] = params[1]
-  method: method
-  uri: decodeURIComponent uri
-  protocol: protocol
-  headers: headers
-  body: body
-  headerBytes: headerPart.length  # TODO
+
+  try
+    decodedURI = decodeURIComponent uri
+  catch e
+    console.log "error: failed to decode URI: #{uri}"
+    return null
+
+  return {
+    method: method
+    uri: decodedURI
+    protocol: protocol
+    headers: headers
+    body: body
+    headerBytes: headerPart.length  # TODO
+  }


Get rid of rekuire from where I missed it

note to self, use ack more and rm -rf node_modules when testing this
shit

--- server.coffee
+++ server.coffee
@@ -1,5 +1,4 @@
 path = require 'path'
-rek = require 'rekuire'

 ## Set the env so that we don't have to ask again
 APP_ROOT = process.env.APP_ROOT = path.resolve(__dirname)
@@ -9,8 +8,8 @@
 GA_ID      = process.env.GA_ID

 ## Load and configure the app
-app = rek 'lib/app'
-Comic = rek 'lib/comic'
+app = require './lib/app'
+Comic = require './lib/comic'

 app.locals.GA_ID = GA_ID
 app.set 'port', (process.env.PORT or 5000)


Fix general settings tab checkboxes

Fix general settings tab checkboxes

--- source/scripts/views/tab/general.coffee
+++ source/scripts/views/tab/general.coffee
@@ -49,7 +49,7 @@
   # Handle settings being changed
   do (setting, element) ->
     options[setting] = ->
-      el = @ui[element]
+      el = @ui[setting]
       if el.attr('type') is 'checkbox'
         val = el.is ':checked'
       else


Better error messages.

Better error messages.

--- server.coffee
+++ server.coffee
@@ -255,7 +255,7 @@
       return if _.isNull(value) and not @required

       # TODO: This is not triggered if required field simply do not exist or is set to undefined (does MongoDB support undefined value?)
-      Log.error "Document's '#{ id }' field '#{ @sourcePath }' was updated with an invalid value: #{ util.inspect value }"
+      Log.error "Document '#{ @sourceDocument.Meta._name }' '#{ id }' field '#{ @sourcePath }' was updated with an invalid value: #{ util.inspect value }"
       return

     # Only _id is requested, we do not have to do anything
@@ -266,7 +266,7 @@
         transform: null

       unless target
-        Log.error "Document's '#{ id }' field '#{ @sourcePath }' is referencing a nonexistent document '#{ value._id }'"
+        Log.error "Document '#{ @sourceDocument.Meta._name }' '#{ id }' field '#{ @sourcePath }' is referencing a nonexistent document '#{ value._id }'"
         # TODO: Should we call reference.removeSource here? And remove from reverse fields?
         return

@@ -415,7 +415,7 @@
     if field instanceof globals.Document._ObservingField
       if field.ancestorArray and name is field.ancestorArray
         unless _.isArray value
-          Log.error "Document's '#{ id }' field '#{ name }' was updated with a non-array value: #{ util.inspect value }"
+          Log.error "Document '#{ @sourceDocument.Meta._name }' '#{ id }' field '#{ name }' was updated with a non-array value: #{ util.inspect value }"
           return
       else
         value = [value]


Refactoring: randomSequence

Refactoring: randomSequence

--- text-fade-in.js.coffee
+++ text-fade-in.js.coffee
@@ -24,10 +24,7 @@

     a

-  randomSequence = (length) ->
-    sequence = []
-    sequence.push(i) for i in [0..length]
-    shuffle sequence
+  randomSequence = (length) -> shuffle [0..length]

   replace = (element, text, sequence) ->
     index     = sequence.shift()


Operators now respect new lines as being spaced.

--- lexer.coffee
+++ lexer.coffee
@@ -289,6 +289,8 @@

   # Generate a newline token. Consecutive newlines get merged together.
   newlineToken: (newlines) ->
+    prev = last @tokens
+    prev.newLine = true if prev
     @token 'TERMINATOR', '\n' unless @tag() is 'TERMINATOR'
     true


unterminated -> missing.

unterminated -> missing.

--- lexer.coffee
+++ lexer.coffee
@@ -407,7 +407,7 @@
       else if end is '"' and prev is '#' and letter is '{'
         stack.push end = '}'
       prev = letter
-    throw new Error "missing #{ stack.pop()[0] }, starting on line #{ @line + 1 }"
+    throw new Error "missing #{ stack.pop() }, starting on line #{ @line + 1 }"

   # Expand variables and expressions inside double-quoted strings using


compute: sometimes clear cache of state

compute: sometimes clear cache of state

--- salvus/compute.coffee
+++ salvus/compute.coffee
@@ -577,6 +577,9 @@
         delete @_state_time
         delete @_state_error
         delete @_state_set_by
+        if @_state_cache_timeout?
+             clearTimeout(@_state_cache_timeout)
+             delete @_state_cache_timeout

     update_host: (opts) =>
         opts = defaults opts,
@@ -703,9 +706,14 @@
                         opts.cb(err)
                     else
                         dbg("got state='#{@_state}'")
+                        @clear_state()
                         @_state       = resp.state
                         @_state_time  = resp.time
                         @_state_error = resp.state_error
+                        f = () =>
+                             dbg("clearing cache due to timeout")
+                             @clear_state()
+                        @_state_cache_timeout = setTimeout(f, 30000)
                         opts.cb(undefined, resp)
         else
             dbg("getting state='#{@_state}' from cache")


Removed stupid .js extension

Removed stupid .js extension

This forces an extension, but tbh I don't care which extension the file
has

--- js-library/Gruntfile.coffee
+++ js-library/Gruntfile.coffee
@@ -36,7 +36,6 @@
           'wrap/last.js'
         ]
         dest: 'tmp/factlink.core.js'
-        ext: '.js'
       core:
         options:
           banner: banner_template


put the program email atop the mobile home screen.

--- wrapper/index.coffee
+++ wrapper/index.coffee
@@ -233,15 +233,23 @@
 				htmlHomeTop: ''
 				htmlHomeBottom: ''
 			}
+
 			for p in self.doc.programs.map( (p)-> p.publicObject )
 				p.studentEnrolled = true
 				retObj.programs.unshift(p)
 			retObj.programs = _.uniq(retObj.programs, 'id')
 			retObj.htmlHomeTop = "#{etabits.stats.activeUsers} users online
"
-			retObj.htmlHomeTop += 'Got any question? Send us a message to our Facebook page. Your feedback is highly appreciated!'
-
-			if self.doc.actionsCounter > 10
-				retObj.htmlHomeBottom = 'fb.com/SVUHelper: App Facebook page'
+			retObj.htmlHomeTop += '''<big>
+تعميم من الجامعة:
+

+حل مشكلة برنامج مشغل الجلسات

+الطلاب الأعزاء

+تم حل مشكلة برنامج مشغل الجلسات المتزامنة التي حصلت ظهر اليوم الاثنين 16/2/2015، بإمكانكم تحميل النسخة المعدلة من البرنامج
+من هنا
+</big>'''
+			##retObj.htmlHomeTop += 'Got any question? Send us a message to our Facebook page. Your feedback is highly appreciated!'
+
+			retObj.htmlHomeBottom = 'fb.com/SVUHelper: App Facebook page'

 			if self.doc.passwordExpired
 				retObj.htmlHomeTop = 'PASSWORD EXPIRED!

Please login to your account at svuonline.org and change it NOW!
'


Fix tabbing.

Fix tabbing.

--- js/flight.coffee
+++ js/flight.coffee
@@ -47,10 +47,10 @@
             @targetPanel = document.querySelector options.targetPanel
          else
            @targetPanel = options.targetPanel
-
-        @pageHistory.push(window.location.hash)
+
+         @pageHistory.push(window.location.hash)

-        transitionType = @targetPanel.getAttribute("data-transition")
+         transitionType = @targetPanel.getAttribute("data-transition")

       else
         if options.back


Fixed fuzzy match logging

Fixed fuzzy match logging

--- src/trivia-game.coffee
+++ src/trivia-game.coffee
@@ -136,9 +136,8 @@
             adjustedValue = value

         if fuzzyMatchCheck.value? and not indexOfCheck
-            fuzzyMatchInfo = "Fuzzy Match found, fuzzy_min_match = #{FUZZY_MIN_MATCH}, distance = #{fuzzyMatchCheck.distance}, value = #{fuzzyMatchCheck.value}"
-            resp.reply fuzzyMatchInfo
-            @robot.logger.info fuzzyMatchInfo
+            resp.reply "Fuzzy Match found"
+            @robot.logger.info "Fuzzy Match found, fuzzy_min_match = #{FUZZY_MIN_MATCH}, distance = #{fuzzyMatchCheck.distance}, value = #{fuzzyMatchCheck.value}"

         resp.reply "YOU ARE CORRECT!!! The answer is #{@currentQ.answer}, you scored #{adjustedValue} points."


graphs render again. yay!

graphs render again. yay!

--- src/ubret/Graph.coffee
+++ src/ubret/Graph.coffee
@@ -36,6 +36,7 @@
     @drawBrush()  # Implemented by subclasses

   start: =>
+    @el.html _.template @template, {selector: @selector}
     @setupGraph()

   clearFilters: =>


adjusted Sof Zman KS by 2 minutes for added safety

--- js/mincha.coffee
+++ js/mincha.coffee
@@ -34,7 +34,7 @@
     @zmanim = SunCalc.getTimes(moment(day_iterator).toDate().setHours(12), window.config.latitude, window.config.longitude)
     @sunset = moment(@zmanim.sunset).subtract(30, 'second')
   today: -> moment(@sunset)
-  last_time_for_shema: -> @_last_time_for_shema ?= moment(@zmanim.magen_abraham_dawn).add((@zmanim.magen_abraham_dusk - @zmanim.magen_abraham_dawn) / 4000, 'seconds')
+  last_time_for_shema: -> @_last_time_for_shema ?= moment(@zmanim.magen_abraham_dawn).add((@zmanim.magen_abraham_dusk - @zmanim.magen_abraham_dawn) / 4000 - 120, 'seconds')
   shema_is_before_9_am: -> @last_time_for_shema().isBefore(@today().hour(9).minute(0))
   mincha_minutes_before_sunset_on_shabbat: -> if @hebrew_date.isEreb9Ab() then 100 else 45
   mincha_minutes_before_sunset_on_rosh_hashana: -> if @hebrew_date.is1stDayOfYomTob() || @hebrew_date.isErebShabbat() then 60 else 40


Workspace: Fixed opening file in client tabbed editor.

--- client/app/CommonViews/workspace/panes/collaborativetabbededitorpane.coffee
+++ client/app/CommonViews/workspace/panes/collaborativetabbededitorpane.coffee
@@ -17,7 +17,13 @@

     @createEditorTabs()
     return @createEditorInstance()  unless @isJoinedASession
-    @recoverOldSessionTabs()
+
+    @workspaceRef.on "value", (snapshot) =>
+      tabs = snapshot.val() and snapshot.val().tabs # not same with {tabs} = snapshot.val()?
+      return unless tabs
+      for key, value of tabs
+        file = FSHelper.createFileFromPath value.path
+        @createEditorInstance file, null, value.sessionKey

   createEditorTabs: ->
     @tabHandleContainer = new ApplicationTabHandleHolder
@@ -64,17 +70,9 @@
       workspaceRefData.path = file.path
       @openedFiles.push file.path

-    @workspaceRef.child("tabs").push workspaceRefData
+    @workspaceRef.child("tabs").push workspaceRefData  unless sessionKey

     return yes # return something instead of workspaceRef.child
-
-  recoverOldSessionTabs: ->
-    @workspaceRef.once "value", (snapshot) =>
-      tabs = snapshot.val() and snapshot.val().tabs # not same with {tabs} = snapshot.val()?
-      return unless tabs
-      for key, value of tabs
-        file = FSHelper.createFileFromPath value.path
-        @createEditorInstance file, null, value.sessionKey

   openFile: CollaborativeTabbedEditorPane::createEditorInstance


SourceIn: Clean up a leftover debugging console.log

--- src/streammachine/master/source_in.coffee
+++ src/streammachine/master/source_in.coffee
@@ -192,7 +192,6 @@

             return if !line?

-            console.log "ICy line is !#{line}!"
             if line
                 match = @headerExp.exec line
                 @info.headers[match[1].toLowerCase()] = match[2]


Support robots.txt

Support robots.txt

--- scripts/detail.coffee
+++ scripts/detail.coffee
@@ -10,6 +10,11 @@

   uri_server = process.env.TRAVELQABOT_URI_SERVER ? "localhost"
   uri_dir = process.env.TRAVELQABOT_URI_DIR ? "/d"
+
+  robot.router.get "/robots.txt", (req, res) ->
+    res.type 'text'
+    res.send "User-Agent: *\nDisallow: /\n"
+    res.end

   robot.router.get "#{uri_dir}/:id", (req, res) ->
     id = req.params.id


Get flattened, not flatten.

Get flattened, not flatten.

--- scripts/points.coffee
+++ scripts/points.coffee
@@ -97,7 +97,7 @@
         robot.http("https://tmi.twitch.tv/group/user/masonest/chatters")
             .get() (err, res, body) ->
                 chat = JSON.parse(body)
-                people = flatten([chat.chatters.moderators, chat.chatters.staff, chat.chatters.admins, chat.chatters.global_mods, chat.chatters.viewers])
+                people = flattened([chat.chatters.moderators, chat.chatters.staff, chat.chatters.admins, chat.chatters.global_mods, chat.chatters.viewers])

                 for username in people
                     points[username] += 5


convert tabs to spaces

convert tabs to spaces

--- scripts/taytay.coffee
+++ scripts/taytay.coffee
@@ -6,40 +6,40 @@
 #   taylor swift|swifty|tails|tayter tot|taytay|t-swift|t-swizzle|tay - Reply with Taylor Swift

 tay = [
-	"https://media0.giphy.com/media/l0K4c8bZHJzDCmg6Y/200.gif",
-	"https://media2.giphy.com/media/3o7qDPw0ut34r5Tala/200.gif",
-	"https://media0.giphy.com/media/g8CJl0epg71MQ/200.gif",
-	"https://media2.giphy.com/media/rvDtLCABDMaqY/200.gif",
-	"https://media3.giphy.com/media/HDQO74ekA0Hp6/200.gif",
-	"https://media1.giphy.com/media/3oEduYNRQXNYfGZ7LG/200.gif",
-	"https://media1.giphy.com/media/qvWCTAG5MxOmc/200.gif",
-	"https://media1.giphy.com/media/KDHkHNpM3Eht6/200.gif",
-	"https://media0.giphy.com/media/vk8zbMJQcfaIE/200.gif",
-	"https://media.giphy.com/media/nXiel1G5ToSgU/giphy.gif",
-	"http://media1.giphy.com/media/nXOds9I8K8gUg/giphy.gif",
-	"https://media.giphy.com/media/RMonqmwIZagbm/giphy.gif",
-	"http://www.reactiongifs.com/r/tw.gif",
-	"http://www.reactiongifs.com/r/tssmd.gif",
-	"http://www.reactiongifs.com/wp-content/uploads/2013/09/thug.gif",
-	"http://www.reactiongifs.com/wp-content/uploads/2013/10/taylor-swift-mind-blown.gif",
-	"http://cdn.playbuzz.com/cdn/d6edbba5-ddfb-4438-8b5d-17458f98ab88/30b228c6-db5d-4105-aa38-bbe0533f3cc5.gif",
-	"https://66.media.tumblr.com/5e0096d02ca16c3382c1006e9f6c338d/tumblr_neabpn72Cw1tm4tz8o1_400.gif",
-	"https://media.giphy.com/media/895uU9amktvgc/giphy.gif",
-	"http://danglingmouse.com/wp-content/uploads/2014/10/taylor-laughing.gif",
-	"http://ak-hdl.buzzfed.com/static/2015-04/13/0/imagebuzz/webdr01/anigif_optimized-17556-1428901157-16.gif",
-	"http://likegif.com/wp-content/uploads/2013/03/Taylor-Swift-gif-114.gif",
-	"http://static.buzznet.com/uploads/2016/03/unnamed10.gif",
-	"http://2.bp.blogspot.com/-CqCYGJUibOM/VVsn0WPVzzI/AAAAAAAAHek/AGefXk0S8kI/s1600/Taylor-swift-Bad-Blood-Neve.gif",
-	"https://media.giphy.com/media/26BkLBA32cX3AfM0o/giphy.gif",
-	"http://www.eonline.com/eol_images/Entire_Site/20141010//rs_540x225-141110045936-tumblr_neti2yNK1i1tarer7o1_1280.gif",
+  "https://media0.giphy.com/media/l0K4c8bZHJzDCmg6Y/200.gif",
+  "https://media2.giphy.com/media/3o7qDPw0ut34r5Tala/200.gif",
+  "https://media0.giphy.com/media/g8CJl0epg71MQ/200.gif",
+  "https://media2.giphy.com/media/rvDtLCABDMaqY/200.gif",
+  "https://media3.giphy.com/media/HDQO74ekA0Hp6/200.gif",
+  "https://media1.giphy.com/media/3oEduYNRQXNYfGZ7LG/200.gif",
+  "https://media1.giphy.com/media/qvWCTAG5MxOmc/200.gif",
+  "https://media1.giphy.com/media/KDHkHNpM3Eht6/200.gif",
+  "https://media0.giphy.com/media/vk8zbMJQcfaIE/200.gif",
+  "https://media.giphy.com/media/nXiel1G5ToSgU/giphy.gif",
+  "http://media1.giphy.com/media/nXOds9I8K8gUg/giphy.gif",
+  "https://media.giphy.com/media/RMonqmwIZagbm/giphy.gif",
+  "http://www.reactiongifs.com/r/tw.gif",
+  "http://www.reactiongifs.com/r/tssmd.gif",
+  "http://www.reactiongifs.com/wp-content/uploads/2013/09/thug.gif",
+  "http://www.reactiongifs.com/wp-content/uploads/2013/10/taylor-swift-mind-blown.gif",
+  "http://cdn.playbuzz.com/cdn/d6edbba5-ddfb-4438-8b5d-17458f98ab88/30b228c6-db5d-4105-aa38-bbe0533f3cc5.gif",
+  "https://66.media.tumblr.com/5e0096d02ca16c3382c1006e9f6c338d/tumblr_neabpn72Cw1tm4tz8o1_400.gif",
+  "https://media.giphy.com/media/895uU9amktvgc/giphy.gif",
+  "http://danglingmouse.com/wp-content/uploads/2014/10/taylor-laughing.gif",
+  "http://ak-hdl.buzzfed.com/static/2015-04/13/0/imagebuzz/webdr01/anigif_optimized-17556-1428901157-16.gif",
+  "http://likegif.com/wp-content/uploads/2013/03/Taylor-Swift-gif-114.gif",
+  "http://static.buzznet.com/uploads/2016/03/unnamed10.gif",
+  "http://2.bp.blogspot.com/-CqCYGJUibOM/VVsn0WPVzzI/AAAAAAAAHek/AGefXk0S8kI/s1600/Taylor-swift-Bad-Blood-Neve.gif",
+  "https://media.giphy.com/media/26BkLBA32cX3AfM0o/giphy.gif",
+  "http://www.eonline.com/eol_images/Entire_Site/20141010//rs_540x225-141110045936-tumblr_neti2yNK1i1tarer7o1_1280.gif",
 ]

 module.exports = (robot) ->
-
-	robot.respond /tay me/i, (msg) ->
-		msg.send msg.random tay
-
-	tayExpression = /\b(taylor swift|swifty|tails|tayter tot|taytay|t-swift|t-swizzle|tay)\b/i
-
-	robot.hear tayExpression, (msg) ->
-		msg.send msg.random tay
+
+  robot.respond /tay me/i, (msg) ->
+    msg.send msg.random tay
+
+  tayExpression = /\b(taylor swift|swifty|tails|tayter tot|taytay|t-swift|t-swizzle|tay)\b/i
+
+  robot.hear tayExpression, (msg) ->
+    msg.send msg.random tay


Added 'empty' call for proper old nodes removal

IF just "" html element nodes is not removed from DOM and cause a memory leak

--- scripts/themis.coffee
+++ scripts/themis.coffee
@@ -210,7 +210,7 @@
                     navigationBar.present()

         dismiss: ->
-            $('#main').html ''
+            $('#main').empty()
             navigationBar.dismiss()

     new SignupView()
@@ -268,7 +268,7 @@
                     navigationBar.present()

         dismiss: ->
-            $('#main').html ''
+            $('#main').empty()
             navigationBar.dismiss()

     new SigninView()
@@ -324,7 +324,7 @@
                     navigationBar.present()

         dismiss: ->
-            $('#main').html ''
+            $('#main').empty()
             navigationBar.dismiss()

     new LoginView()
@@ -352,7 +352,7 @@
                         identity: identity

         dismiss: ->
-            $('#main').html ''
+            $('#main').empty()
             navigationBar.dismiss()

     new IndexView()
@@ -576,7 +576,7 @@
                                         $changePasswordModal.modal 'show'

         dismiss: ->
-            $('#main').html ''
+            $('#main').empty()
             navigationBar.dismiss()

     new ProfileView()
@@ -614,7 +614,7 @@
                             $result.addClass('text-success').text 'Email verified! Thank you!'

         dismiss: ->
-            $('#main').html ''
+            $('#main').empty()
             navigationBar.dismiss()

     new VerifyEmailView()
@@ -643,7 +643,7 @@
                         active: 'news'

         dismiss: ->
-            $('#main').html ''
+            $('#main').empty()
             navigationBar.dismiss()

     new NewsView()
@@ -661,7 +661,7 @@
             $('#main').html renderTemplate 'not-found-view', urlPath: window.location.pathname

         dismiss: ->
-            $('#main').html ''
+            $('#main').empty()

     new NotFoundView()

@@ -747,7 +747,7 @@
                             console.log errorThrown

         dismiss: ->
-            $('#themis-navbar').html ''
+            $('#themis-navbar').empty()

     new NavigationBar()


50hertz fetcher

50hertz fetcher

--- server/fetcher.coffee
+++ server/fetcher.coffee
@@ -9,7 +9,6 @@
 csv = require "csv"

 class Import
-  days: []

 class Import50Hertz extends Import
   url: (day, type, part) ->
@@ -24,29 +23,31 @@
       else
         "http://ws.50hertz.com/web01/api/PhotovoltaicActual/ListRecords?filterDateTime=#{day}&callback=jQuery"

-  fetchPart: (day, type, part) ->
+  fetch: (day, type, part, cb) ->
+    @type = type
+    @day = day
+    @part = part
+
     request @url(day, type, part),
       (error, response, body) =>
-        body = body.replace /^jQuery\(/, ""
-        body = body.replace /\);$/, ""
-        @parse JSON.parse(body)
+        #console.log "resonse: #{body}"

-  fetch: (type, day, cb) ->
-    @fetchPart day, "forecast"
-    @fetchPart day, "produced"
+        @parse JSON.parse(body.replace(/^jQuery\(/, "").replace(/\);$/, "")), cb

   parse: (data, cb) ->
     i = 0
-
-    for row in data
+    days = []
+    for row in data
       day = utils.YYYYMMDD new Date row.Date
       time = new Date row.FromTime

-      @days[day] = [] unless days[day]
-      @days[day][i++] =
-        time: time
-        estimated: row.Value
+      entry = time: time
+      entry[@part] = row.Value
+
+      days[day] = [] unless days[day]
+      days[day][i++] = entry
+
+    cb days

 importer = new Import50Hertz
-importer.fetch "2014-07-01", (days) ->
-  console.log days+importer.fetch "2014-07-01", "wind", "actual", (days) -> console.log days


fix bugs

fix bugs

--- server/methods.coffee
+++ server/methods.coffee
@@ -52,7 +52,10 @@
   unassocaitedIncidentIds = _.difference(priorIncidentIds, associatedIncidentIds)
   Incidents.remove(_id: $in: unassocaitedIncidentIds)

-  priorIncidentOffsets = Incidents.find(articleId: source._id).map (incident) ->
+  priorIncidentOffsets = Incidents.find(
+    articleId: source._id
+    annotations: $exists: true
+  ).map (incident) ->
     incident.annotations.case[0].textOffsets
   incidents.forEach (incident) ->
     # Do not create new incidents that overlap old ones
@@ -151,6 +154,9 @@
           # in the geoname lookup service's database.
           return
         geonamesById[geoname.id] = GeonameSchema.clean(geoname)
+        # Do not store alternate names because they aren't needed and take up
+        # a lot of space.
+        geoname.alternateNames = []
       locationAnnotations.forEach (loc) ->
         geoname = geonamesById[loc.geoname.geonameid]
         if geoname


event: fix x-forwarded-for ip header

event: fix x-forwarded-for ip header

--- services/event.coffee
+++ services/event.coffee
@@ -29,7 +29,7 @@
     _.defaults {
       userId: user.id
       sessionId: user.sessionId
-      ip: req.headers['x-forwards-for'] or req.connection.remoteAddress
+      ip: req.headers['x-forwarded-for'] or req.connection.remoteAddress
     }, userFields

 module.exports = new EventService()


Fix: Popup menu doesn't close when option selected.

--- lib/Popup.coffee
+++ lib/Popup.coffee
@@ -67,12 +67,12 @@
         if (item == '-')
           ul.append '

'
         else
-          do (item) ->
+          do (item) =>
             li = $ '

'
             li.addClass item.className if item.className
             a = $('' + (item.title || item[0]) + '')
-            a.click (e) ->
-              closePopup()
+            a.click (e) =>
+              @close()
               item.action() if item.action
               item[1]() if item[1]
             li.append a


added support for build of subfolder components

--- lib/build.coffee
+++ lib/build.coffee
@@ -28,7 +28,11 @@
     return cmd

   buildSingleFileCommand:(target,params) ->
-    cmd = 'ant '+target+' -Ddeploy.single.folderName='+params[2]+' -Ddeploy.single.fileName="'+params[0]+'" -Ddeploy.single.metadataType='+params[1]+' -f '+@root + '/build/build.xml'
+    cmd = 'ant '+target
+    if params.length > 3
+        cmd += ' -Ddeploy.single.subFolderName='+params[3]
+    cmd += ' -Ddeploy.single.folderName='+params[2]+' -Ddeploy.single.fileName="'+params[0]+'" -Ddeploy.single.metadataType='+params[1]+' -f '
+    cmd += @root + '/build/build.xml'
     return cmd

   startNewBuild:(buildTarget,params) ->
@@ -158,11 +162,15 @@
               metaDataType = 'Flow'
             when 'aura'
               metaDataType = 'AuraDefinitionBundle'
+            when 'documents'
+              metaDataType = 'Document'
             else
               metaDataType = null

           if(metaDataType != null)
             params = [fileNameParsed,metaDataType,folderName[0]]
+            if metaDataType == 'AuraDefinitionBundle' || metaDataType == 'Document'
+                params.push(folderName[1])
             if @child then @abort(=> @startNewBuild('deploy-single-file',params)) else @startNewBuild('deploy-single-file',params)
           else
             @buildView.buildUnsupported()


require coffee-reactify and browserify-css explicitly

--- lib/carte.coffee
+++ lib/carte.coffee
@@ -8,6 +8,8 @@
 watchify = require 'watchify'
 uglify = require 'gulp-uglify'
 streamify = require 'gulp-streamify'
+coffeeReactify = require 'coffee-reactify'
+browserifyCss = require 'browserify-css'
 _ = require 'lodash'
 jade = require 'gulp-jade'
 rename = require 'gulp-rename'
@@ -45,8 +47,8 @@
       entries: [__dirname + '/carte/client.coffee']
       extensions: ['.coffee', '.js', '.cjsx', '.css']
     browserify
-      .transform 'coffee-reactify'
-      .transform 'browserify-css',
+      .transform coffeeReactify
+      .transform browserifyCss,
         rootDir: 'public'
         processRelativeUrl: (relativeUrl)->
           stripQueryStringAndHashFromPath = (url)-> url.split('?')[0].split('#')[0]


Set the url on the QueryModel

Set the url on the QueryModel

--- src/coffee/cilantro/models/query.coffee
+++ src/coffee/cilantro/models/query.coffee
@@ -4,7 +4,8 @@
 ], (c, base) ->

     class QueryModel extends base.Model
-
+        url: ->
+            c.session.url('queries')

     class QueryCollection extends base.Collection
         model: QueryModel


Fixed shadowing bug in bulkGetOpsSince

Fixed shadowing bug in bulkGetOpsSince

--- lib/index.coffee
+++ lib/index.coffee
@@ -453,30 +453,32 @@
       for result, i in redisResults
         results[cNames[i]] ?= {}

+        cName = cNames[i]
+        docName = docNames[i]
+        from = froms[i]
+
         if result is 0 # sentinal value to mean we should go to the oplog.
           pending++
-          do (i) ->
-            cName = cNames[i]
-            docName = docNames[i]
-            from = froms[i]
-
+          do (cName, docName, from) ->
             # We could have a bulkGetOps in the oplog too, but because we cache
             # anything missing in redis, I'm not super worried about the extra
             # calls here.
             oplogGetOps cName, docName, from, null, (err, ops) ->
               return callback err if err
+              #console.log "ops strategy 1 for #{cName}.#{docName}", ops
               results[cName][docName] = ops

               version = from + ops.length
               redisCacheVersion cName, docName, version, done
         else
-          v = froms[i]
+          v = from
           ops = for value in result
             op = JSON.parse value
             op.v = v++
             op

-          results[cNames[i]][docNames[i]] = ops
+          #console.log "ops strategy 2 for #{cName}.#{docName}", ops
+          results[cName][docName] = ops

       done()


Stricter number parsing when unpacking query

--- lib/query.coffee
+++ lib/query.coffee
@@ -70,9 +70,8 @@
     if (m = /^\/(.*?)\/(g?i?m?y?)$/.exec(input))
       vals.push({'$regex' : new RegExp(m[1], m[2])})

-    f = parseFloat(input)
-    if not isNaN(f)
-      vals.push(f)
+    if +input == parseFloat(input)
+      vals.push(+input)

     d = new Date(input)
     if input.length >= 8 and d.getFullYear() > 1983


Added check for existence of aSingleLine regex in lib/utils.coffee to fix #139

--- lib/utils.coffee
+++ lib/utils.coffee
@@ -392,7 +392,7 @@
       # However, we treat all comments beginning with } as inline code commentary
       # and comments starting with ^ cause that comment and the following code
       # block to start folded.
-      else if (match = line.match aSingleLine)?
+      else if aSingleLine? and (match = line.match aSingleLine)?

         # Uses `match` as a placeholder.
         [match, comment] = match


handle multi-folder projects properly

handle multi-folder projects properly

--- lib/utils.coffee
+++ lib/utils.coffee
@@ -42,10 +42,14 @@
     comm.msg 'cd', {path: path.dirname(file)}

   cdProject: ->
-    # TODO: multiple folders
-    dir = atom.project.getPaths()?[0]
-    dir? or atom.notifications.addError 'This project has no folders.'
-    comm.msg 'cd', {path: dir}
+    dirs = atom.project.getPaths()
+    if dirs.length < 1
+      atom.notifications.addError 'This project has no folders.'
+    else if dirs.length == 1
+      comm.msg 'cd', {path: dirs[0]}
+    else
+      selector.show dirs, (dir) =>
+        comm.msg 'cd', {path: dir}

   cdHome: ->
     comm.msg 'cd', {path: @home()}


Revert "Fix perf issue"

Revert "Fix perf issue"

This reverts commit 9ca69d11957a1a998bc7e177ecfb348da6c158dd.

--- framer/Context.coffee
+++ framer/Context.coffee
@@ -1,598 +1,122 @@
-"""
+Utils = require "./Utils"

-VRComponent class
+{_} = require "./Underscore"
+{BaseClass} = require "./BaseClass"
+{Config} = require "./Config"
+{EventManager} = require "./EventManager"

-properties
-- front (set: imagePath <string>, get: layer)
-- right
-- back
-- left
-- top
-- bottom
-- heading <number>
-- elevation <number>
-- tilt <number> readonly
+Counter = 1

-- orientationLayer <bool>
-- arrowKeys <bool>
-- lookAtLatestProjectedLayer <bool>
+class exports.Context extends BaseClass
+
+	constructor: (options={}) ->
+
+		super

-methods
-- projectLayer(layer) # heading and elevation are set as properties on the layer
-- hideEnviroment()
+		Counter++

-events
-- Events.OrientationDidChange, (data {heading, elevation, tilt})
+		options = _.defaults options,
+			contextName: null
+			parentLayer: null
+			name: null

---------------------------------------------------------------------------------
+		if not options.name
+			throw Error("Contexts need a name")

-VRLayer class
+		@_parentLayer = options.parentLayer
+		@_name = options.name
+
+		@reset()

-properties
-- heading <number> (from 0 up to 360)
-- elevation <number> (from -90 down to 90 up)
+	reset: ->

-"""
+		@eventManager?.reset()
+		@eventManager = new EventManager

-SIDES = [
-	"north",
-	"front",
-	"east",
-	"right",
-	"south",
-	"back",
-	"west",
-	"left",
-	"top",
-	"bottom",
-]
+		if @_rootElement
+			# Clean up the current root element:
+			if @_rootElement.parentNode
+				# Already attached to the DOM - remove it:
+				@_rootElement.parentNode.removeChild(@_rootElement)
+			else
+				# Not on the DOM yet. Prevent it from being added (for this happens
+				# async):
+				@_rootElement.__cancelAppendChild = true

-KEYS = {
-	LeftArrow: 37
-	UpArrow: 38
-	RightArrow: 39
-	DownArrow: 40
-}
+		# Create a fresh root element:
+		@_createRootElement()

-KEYSDOWN = {
-	left: false
-	up: false
-	right: false
-	down: false
-}
+		@_delayTimers?.map (timer) -> window.clearTimeout(timer)
+		@_delayIntervals?.map (timer) -> window.clearInterval(timer)

-Events.OrientationDidChange = "orientationdidchange"
+		if @_animationList
+			for animation in @_animationList
+				animation.stop(false)

-class VRAnchorLayer extends Layer
+		@_layerList = []
+		@_animationList = []
+		@_delayTimers = []
+		@_delayIntervals = []
+		@_layerIdCounter = 1

-	constructor: (layer, cubeSide) ->
-		super undefined
-		@width = 0
-		@height = 0
-		@clip = false
-		@name = "anchor"
-		@cubeSide = cubeSide
+		@emit("reset", @)

-		@layer = layer
-		layer.superLayer = @
-		layer.center()
+	destroy: ->
+		@reset()
+		if @_rootElement.parentNode
+			@_rootElement.parentNode.removeChild(@_rootElement)
+		Utils.domCompleteCancel(@_appendRootElement)

-		layer.on "change:orientation", (newValue, layer) =>
-			@updatePosition(layer)
-		@updatePosition(layer)
+	getRootElement: ->
+		@_rootElement

-		layer._context.on "layer:destroy", (layer) =>
-			if layer == @layer
-				@destroy()
+	getLayers: ->
+		_.clone(@_layerList)

-	updatePosition: (layer) ->
-		halfCubSide = @cubeSide/2
-		@style["webkitTransform"] = "translateX(#{(@cubeSide - @width)/2}px) translateY(#{(@cubeSide - @height)/2}px) rotateZ(#{layer.heading}deg) rotateX(#{90-layer.elevation}deg) translateZ(#{halfCubSide*.9}px) rotateX(180deg)"
+	addLayer: (layer) ->
+		return if layer in @_layerList
+		@_layerList.push(layer)
+		return null

-class exports.VRLayer extends Layer
+	removeLayer: (layer) ->
+		@_layerList = _.without(@_layerList, layer)
+		return null

-	constructor: (options = {}) ->
-		options = _.defaults options,
-			heading: 0
-			elevation: 0
-		super options
+	layerCount: ->
+		return @_layerList.length

-	@define "heading",
-		get: -> @_heading
-		set: (value) ->
-			if value >= 360
-				value = value % 360
-			else if value < 0
-				rest = Math.abs(value) % 360
-				value = 360 - rest
-			if @_heading != value
-				@_heading = value
-				@emit("change:heading", value)
-				@emit("change:orientation", value)
+	nextLayerId: ->
+		@_layerIdCounter++

-	@define "elevation",
-		get: -> @_elevation
-		set: (value) ->
-			value = Utils.clamp(value, -90, 90)
-			if value != @_elevation
-				@_elevation = value
-				@emit("change:elevation", value)
-				@emit("change:orientation", value)
+	_createRootElement: ->

-class exports.VRComponent extends Layer
+		@_rootElement = document.createElement("div")
+		@_rootElement.id = "FramerContextRoot-#{@_name}"
+		@_rootElement.classList.add("framerContext")

-	constructor: (options = {}) ->
-		options = _.defaults options,
-			cubeSide: 3000
-			perspective: 1200
-			lookAtLatestProjectedLayer: false
-			width: Screen.width
-			height: Screen.height
-			orientationLayer: true
-			arrowKeys: true
-		super options
-		@perspective = options.perspective
-		@backgroundColor = null
-		@createCube(options.cubeSide)
-		@degToRad = Math.PI / 180
-		@layersToKeepLevel = []
-		@lookAtLatestProjectedLayer = options.lookAtLatestProjectedLayer
-		@arrowKeys = options.arrowKeys
-		@_keys()
+		if @_parentLayer
+			@_appendRootElement()
+		else
+			Utils.domComplete(@_appendRootElement)

-		@_heading = 0
-		@_elevation = 0
-		@_tilt = 0
+	_appendRootElement: =>
+		parentElement = @_parentLayer?._element
+		parentElement ?= document.body
+		parentElement.appendChild(@_rootElement)

-		@_headingOffset = 0
-		@_elevationOffset = 0
-		@_deviceHeading = 0
-		@_deviceElevation = 0
+	run: (f) ->
+		previousContext = Framer.CurrentContext
+		Framer.CurrentContext = @
+		f()
+		Framer.CurrentContext = previousContext

-		if options.heading
-			@heading = options.heading
-		if options.elevation
-			@elevation = options.elevation
+	@define "width",
+		get: ->
+			return @_parentLayer.width if @_parentLayer
+			return window.innerWidth

-		@orientationLayer = options.orientationLayer
+	@define "height",
+		get: ->
+			return @_parentLayer.height if @_parentLayer
+			return window.innerHeight

-		@desktopPan(0, 0)
-
-		# tilting and panning
-		if Utils.isMobile()
-			window.addEventListener "deviceorientation", (event) =>
-				@orientationData = event
-
-		Framer.Loop.on("update", @deviceOrientationUpdate)
-
-		# Make sure we remove the update from the loop when we destroy the context
-		Framer.CurrentContext.on "reset", ->
-			Framer.Loop.off("update", @deviceOrientationUpdate)
-
-		@on "change:frame", ->
-			@desktopPan(0,0)
-
-
-	_keys: ->
-		document.addEventListener "keydown", (event) =>
-			if @arrowKeys
-				switch event.which
-					when KEYS.UpArrow
-						KEYSDOWN.up = true
-						event.preventDefault()
-					when KEYS.DownArrow
-						KEYSDOWN.down = true
-						event.preventDefault()
-					when KEYS.LeftArrow
-						KEYSDOWN.left = true
-						event.preventDefault()
-					when KEYS.RightArrow
-						KEYSDOWN.right = true
-						event.preventDefault()
-
-		document.addEventListener "keyup", (event) =>
-			if @arrowKeys
-				switch event.which
-					when KEYS.UpArrow
-						KEYSDOWN.up = false
-						event.preventDefault()
-					when KEYS.DownArrow
-						KEYSDOWN.down = false
-						event.preventDefault()
-					when KEYS.LeftArrow
-						KEYSDOWN.left = false
-						event.preventDefault()
-					when KEYS.RightArrow
-						KEYSDOWN.right = false
-						event.preventDefault()
-
-		window.onblur = ->
-			KEYSDOWN.up = false
-			KEYSDOWN.down = false
-			KEYSDOWN.left = false
-			KEYSDOWN.right = false
-
-	@define "orientationLayer",
-		get: -> return @desktopOrientationLayer != null && @desktopOrientationLayer != undefined
-		set: (value) ->
-			if @world != undefined
-				if Utils.isDesktop()
-					if value == true
-						@addDesktopPanLayer()
-					else if value == false
-						@removeDesktopPanLayer()
-
-	@define "heading",
-		get: ->
-			heading = @_heading + @_headingOffset
-			if heading > 360
-				heading = heading % 360
-			else if heading < 0
-				rest = Math.abs(heading) % 360
-				heading = 360 - rest
-			return heading
-		set: (value) ->
-			@lookAt(value, @_elevation)
-
-	@define "elevation",
-		get: -> @_elevation
-		set: (value) -> @lookAt(@_heading, value)
-
-	@define "tilt",
-		get: -> @_tilt
-		set: (value) -> throw "Tilt is readonly"
-
-	SIDES.map (face) =>
-		@define face,
-			get: -> @layerFromFace(face) # @getImage(face)
-			set: (value) -> @setImage(face, value)
-
-	createCube: (cubeSide = @cubeSide) =>
-		@cubeSide = cubeSide
-
-		@world?.destroy()
-		@world = new Layer
-			name: "world"
-			superLayer: @
-			width: cubeSide, height: cubeSide
-			backgroundColor: null
-			clip: false
-		@world.style.webkitTransformStyle = "preserve-3d"
-		@world.center()
-
-		halfCubSide = @cubeSide/2
-
-		@side0 = new Layer
-		@side0.style["webkitTransform"] = "rotateX(-90deg) translateZ(-#{halfCubSide}px)"
-		@side1 = new Layer
-		@side1.style["webkitTransform"] = "rotateY(-90deg) translateZ(-#{halfCubSide}px) rotateZ(90deg)"
-		@side2 = new Layer
-		@side2.style["webkitTransform"] = "rotateX(90deg) translateZ(-#{halfCubSide}px) rotateZ(180deg)"
-		@side3 = new Layer
-		@side3.style["webkitTransform"] = "rotateY(90deg) translateZ(-#{halfCubSide}px) rotateZ(-90deg)"
-		@side4 = new Layer
-		@side4.style["webkitTransform"] = "rotateY(-180deg) translateZ(-#{halfCubSide}px) rotateZ(180deg)"
-		@side5 = new Layer
-		@side5.style["webkitTransform"] = "translateZ(-#{halfCubSide}px)"
-
-		@sides = [@side0, @side1, @side2, @side3, @side4, @side5]
-		colors = ["#866ccc", "#28affa", "#2dd7aa", "#ffc22c", "#7ddd11", "#f95faa"]
-		sideNames = ["front", "right", "back", "left", "top", "bottom"]
-
-		index = 0
-		for side in @sides
-			side.name = sideNames[index]
-			side.width = side.height = cubeSide
-			side.superLayer = @world
-			side.html = sideNames[index]
-			side.color = "white"
-			side._backgroundColor = colors[index]
-			side.backgroundColor = colors[index]
-			side.style =
-				lineHeight: "#{cubeSide}px"
-				textAlign: "center"
-				fontSize: "#{cubeSide / 10}px"
-				fontWeight: "100"
-				fontFamily: "Helvetica Neue"
-			index++
-
-		if @sideImages
-			for key of @sideImages
-				@setImage key, @sideImages[key]
-
-	hideEnviroment: ->
-		for side in @sides
-			side.destroy()
-
-	layerFromFace: (face) ->
-		map =
-			north: @side0
-			front: @side0
-			east:  @side1
-			right: @side1
-			south: @side2
-			back:  @side2
-			west:  @side3
-			left:  @side3
-			top:   @side4
-			bottom:@side5
-		return map[face]
-
-	setImage: (face, imagePath) ->
-
-		if not face in SIDES
-			throw Error "VRComponent setImage, wrong name for face: " + face + ", valid options: front, right, back, left, top, bottom, north, east, south, west"
-
-		if not @sideImages
-			@sideImages = {}
-		@sideImages[face] = imagePath
-
-		layer = @layerFromFace(face)
-
-		if imagePath
-			layer?.html = ""
-			layer?.image = imagePath
-		else
-			layer?.html = layer?.name
-			layer?.backgroundColor = layer?._backgroundColor
-
-	getImage: (face) ->
-
-		if not face in SIDES
-			throw Error "VRComponent getImage, wrong name for face: " + face + ", valid options: front, right, back, left, top, bottom, north, east, south, west"
-
-		layer = @layerFromFace(face)
-		if layer
-			layer.image
-
-	projectLayer: (insertLayer) ->
-
-		heading = insertLayer.heading
-		if heading == undefined
-			heading = 0
-		elevation = insertLayer.elevation
-		if elevation == undefined
-			elevation = 0
-
-		if heading >= 360
-			heading = value % 360
-		else if heading < 0
-			rest = Math.abs(heading) % 360
-			heading = 360 - rest
-
-		elevation = Utils.clamp(elevation, -90, 90)
-
-		insertLayer.heading = heading
-		insertLayer.elevation = elevation
-
-		anchor = new VRAnchorLayer(insertLayer, @cubeSide)
-		anchor.superLayer = @world
-
-		if @lookAtLatestProjectedLayer
-			@lookAt(heading, elevation)
-
-	# Mobile device orientation
-
-	deviceOrientationUpdate: =>
-
-		if Utils.isDesktop()
-			if @arrowKeys
-				if @_lastCallHorizontal == undefined
-					@_lastCallHorizontal = 0
-					@_lastCallVertical = 0
-					@_accelerationHorizontal = 1
-					@_accelerationVertical = 1
-					@_goingUp = false
-					@_goingLeft = false
-
-				date = new Date()
-				x = .1
-				if KEYSDOWN.up || KEYSDOWN.down
-					diff = date - @_lastCallVertical
-					if diff < 30
-						if @_accelerationVertical < 30
-							@_accelerationVertical += 0.18
-					if KEYSDOWN.up
-						if @_goingUp == false
-							@_accelerationVertical = 1
-							@_goingUp = true
-						@desktopPan(0, 1 * @_accelerationVertical * x)
-					else
-						if @_goingUp == true
-							@_accelerationVertical = 1
-							@_goingUp = false
-
-						@desktopPan(0, -1 * @_accelerationVertical * x)
-					@_lastCallVertical = date
-
-				else
-					@_accelerationVertical = 1
-
-				if KEYSDOWN.left || KEYSDOWN.right
-					diff = date - @_lastCallHorizontal
-					if diff < 30
-						if @_accelerationHorizontal < 25
-							@_accelerationHorizontal += 0.18
-					if KEYSDOWN.left
-						if @_goingLeft == false
-							@_accelerationHorizontal = 1
-							@_goingLeft = true
-						@desktopPan(1 * @_accelerationHorizontal * x, 0)
-					else
-						if @_goingLeft == true
-							@_accelerationHorizontal = 1
-							@_goingLeft = false
-						@desktopPan(-1 * @_accelerationHorizontal * x, 0)
-					@_lastCallHorizontal = date
-				else
-					@_accelerationHorizontal = 1
-
-		else if @orientationData
-
-			alpha = @orientationData.alpha
-			beta = @orientationData.beta
-			gamma = @orientationData.gamma
-
-			if alpha != 0 && beta != 0 && gamma != 0
-				@directionParams(alpha, beta, gamma)
-
-			xAngle = beta
-			yAngle = -gamma
-			zAngle = alpha
-
-			halfCubSide = @cubeSide/2
-			orientation = "rotate(#{window.orientation * -1}deg) "
-			translationX = "translateX(#{(@width / 2) - halfCubSide}px)"
-			translationY = " translateY(#{(@height / 2) - halfCubSide}px)"
-			translationZ = " translateZ(#{@perspective}px)"
-			rotation = translationZ + translationX + translationY + orientation + " rotateY(#{yAngle}deg) rotateX(#{xAngle}deg) rotateZ(#{zAngle}deg)" + " rotateZ(#{-@_headingOffset}deg)"
-			@world.style["webkitTransform"] = rotation
-
-	directionParams: (alpha, beta, gamma) ->
-
-		alphaRad = alpha * @degToRad
-		betaRad = beta * @degToRad
-		gammaRad = gamma * @degToRad
-
-		# Calculate equation components
-		cA = Math.cos(alphaRad)
-		sA = Math.sin(alphaRad)
-		cB = Math.cos(betaRad)
-		sB = Math.sin(betaRad)
-		cG = Math.cos(gammaRad)
-		sG = Math.sin(gammaRad)
-
-		# x unitvector
-		xrA = -sA * sB * sG + cA * cG
-		xrB = cA * sB * sG + sA * cG
-		xrC = cB * sG
-
-		# y unitvector
-		yrA = -sA * cB
-		yrB = cA * cB
-		yrC = -sB
-
-		# -z unitvector
-		zrA = -sA * sB * cG - cA * sG
-		zrB = cA * sB * cG - sA * sG
-		zrC = cB * cG
-
-		# Calculate heading
-		heading = Math.atan(zrA / zrB)
-
-		# Convert from half unit circle to whole unit circle
-		if zrB < 0
-			heading += Math.PI
-		else if zrA < 0
-			heading += 2 * Math.PI
-
-		# # Calculate Altitude (in degrees)
-		elevation = Math.PI / 2 - Math.acos(-zrC)
-
-		cH = Math.sqrt(1 - (zrC * zrC))
-		tilt = Math.acos(-xrC / cH) * Math.sign(yrC)
-
-		# Convert radians to degrees
-		heading *= 180 / Math.PI
-		elevation *= 180 / Math.PI
-		tilt *= 180 / Math.PI
-
-		@_heading = Math.round(heading * 1000) / 1000
-		@_elevation = Math.round(elevation * 1000) / 1000
-
-		tilt = Math.round(tilt * 1000) / 1000
-		orientationTiltOffset = (window.orientation * -1) + 90
-		tilt += orientationTiltOffset
-		if tilt > 180
-			diff = tilt - 180
-			tilt = -180 + diff
-		@_tilt = tilt
-
-		@_deviceHeading = @_heading
-		@_deviceElevation = @_elevation
-
-		@_emitOrientationDidChangeEvent()
-
-	# Desktop tilt
-
-	removeDesktopPanLayer: =>
-		@desktopOrientationLayer?.destroy()
-
-	addDesktopPanLayer: =>
-		@desktopOrientationLayer?.destroy()
-		@desktopOrientationLayer = new Layer
-			width: 100000, height: 10000
-			backgroundColor: null
-			superLayer:@
-			name: "desktopOrientationLayer"
-		@desktopOrientationLayer.center()
-		@desktopOrientationLayer.draggable.enabled = true
-
-		@prevDesktopDir = @desktopOrientationLayer.x
-		@prevDesktopHeight = @desktopOrientationLayer.y
-
-		@desktopOrientationLayer.on Events.DragStart, =>
-			@prevDesktopDir = @desktopOrientationLayer.x
-			@prevDesktopHeight = @desktopOrientationLayer.y
-			@desktopDraggableActive = true
-
-		@desktopOrientationLayer.on Events.Move, =>
-			if @desktopDraggableActive
-				strength = Utils.modulate(@perspective, [1200, 900], [22, 17.5])
-				deltaDir = (@desktopOrientationLayer.x - @prevDesktopDir) / strength
-				deltaHeight = (@desktopOrientationLayer.y - @prevDesktopHeight) / strength
-				@desktopPan(deltaDir, deltaHeight)
-				@prevDesktopDir = @desktopOrientationLayer.x
-				@prevDesktopHeight = @desktopOrientationLayer.y
-
-		@desktopOrientationLayer.on Events.AnimationEnd, =>
-			@desktopDraggableActive = false
-			@desktopOrientationLayer?.center()
-
-	desktopPan: (deltaDir, deltaHeight) ->
-		halfCubSide = @cubeSide/2
-		translationX = "translateX(#{(@width / 2) - halfCubSide}px)"
-		translationY = " translateY(#{(@height / 2) - halfCubSide}px)"
-		translationZ = " translateZ(#{@perspective}px)"
-		@_heading -= deltaDir
-
-		if @_heading > 360
-			@_heading -= 360
-		else if @_heading < 0
-			@_heading += 360
-
-		@_elevation += deltaHeight
-		@_elevation = Utils.clamp(@_elevation, -90, 90)
-
-		rotation = translationZ + translationX + translationY + " rotateX(#{@_elevation + 90}deg) rotateZ(#{360 - @_heading}deg)" + " rotateZ(#{-@_headingOffset}deg)"
-		@world.style["webkitTransform"] = rotation
-
-		@_heading = Math.round(@_heading * 1000) / 1000
-		@_tilt = 0
-		@_emitOrientationDidChangeEvent()
-
-	lookAt: (heading, elevation) ->
-		halfCubSide = @cubeSide/2
-		translationX = "translateX(#{(@width / 2) - halfCubSide}px)"
-		translationY = " translateY(#{(@height / 2) - halfCubSide}px)"
-		translationZ = " translateZ(#{@perspective}px)"
-		rotation = translationZ + translationX + translationY + " rotateZ(#{@_tilt}deg) rotateX(#{elevation + 90}deg) rotateZ(#{-heading}deg)"
-
-		@world?.style["webkitTransform"] = rotation
-		@_heading = heading
-		@_elevation = elevation
-		if Utils.isMobile()
-			@_headingOffset = @_heading - @_deviceHeading
-
-		@_elevationOffset = @_elevation - @_deviceElevation
-
-		heading = @_heading
-		if heading < 0
-			heading += 360
-		else if heading > 360
-			heading -= 360
-
-		@emit(Events.OrientationDidChange, {heading: heading, elevation: @_elevation, tilt: @_tilt})
-
-	_emitOrientationDidChangeEvent: ->
-		@emit(Events.OrientationDidChange, {heading: @heading, elevation: @_elevation, tilt: @_tilt})


Fixing SVG "use" tag from producing an error; we need to use querySelector instead of getElementById because seemingly the latter fails when the queried for element is not staged yet.

--- framer/SVGPath.coffee
+++ framer/SVGPath.coffee
@@ -16,6 +16,7 @@

 class exports.SVGPath extends SVGBaseLayer
 	constructor: (path, options) ->
+
 		if path instanceof SVGPath
 			path = path.element

@@ -25,8 +26,8 @@
 		if path instanceof SVGPathElement
 			@_path = path
 		else if path instanceof SVGUseElement
-			link = path.getAttribute("xlink:href").replace("#", '')
-			@_path = @_svg.getElementById(link)
+			link = path.getAttribute("xlink:href")
+			@_path = @_svg.querySelector(link)
 		@_length = @_path.getTotalLength()

 	# Custom properties


Parsing floats instead of ints

Parsing floats instead of ints

--- framer/SVGPath.coffee
+++ framer/SVGPath.coffee
@@ -8,7 +8,7 @@
 			values = value.split(',')
 		else
 			values = value.split(" ")
-		values = values.map((v) -> parseInt(v.trim()))
+		values = values.map((v) -> parseFloat(v.trim()))
 		return values
 	return value


tween with arbitrary easing function

tween with arbitrary easing function

--- test/drawable.coffee
+++ test/drawable.coffee
@@ -31,7 +31,8 @@

   equal object2.I.alpha, 0.5

-test "scale"
+test "scale", ->
+  tr

 module()


one more debugging fix

one more debugging fix

--- evolution.coffee
+++ evolution.coffee
@@ -280,8 +280,7 @@
         continue if t.id == tid || seen[id]
         data = thing.others[id]
         continue unless data
-        d = @geoData[data] # FIXME
-        if d <= distance
+        if @geoData[data] <= distance
           seen[id] = true
           nearOnes.push t
     else


Bugfix for invalid port

Bugfix for invalid port

--- start.coffee
+++ start.coffee
@@ -3,9 +3,8 @@
 join = require("path").join
 Vein = require "vein"

-try
-  if process.argv[2] is "-p" then port = parseInt(process.argv[3]) else port = process.env.PORT or 8080
-catch e
+if process.argv[2] is "-p" then port = parseInt(process.argv[3]) else port = process.env.PORT or 8080
+if port is NaN
   console.error "Listening port was invalid"
   port = process.env.PORT or 8080


just add header in start.coffee

just add header in start.coffee

--- start.coffee
+++ start.coffee
@@ -1,3 +1,20 @@
+###
+
+Copyright (c) 2011-2012  Voicious
+
+This program is free software: you can redistribute it and/or modify it under the terms of the
+GNU Affero General Public License as published by the Free Software Foundation, either version
+3 of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+See the GNU Affero General Public License for more details.
+
+You should have received a copy of the GNU Affero General Public License along with this
+program. If not, see <http://www.gnu.org/licenses/>.
+
+###
+
 fs = require('fs')

 server = require('./server')
@@ -17,4 +34,4 @@
         initLog()
         server.start(config.SERVER_PORT)

-start()+start()


add py as name for python codefence

add py as name for python codefence

--- lib/extension-helper.coffee
+++ lib/extension-helper.coffee
@@ -18,6 +18,7 @@
   'objc': 'm'
   'objective-c': 'm'
   'php': 'php'
+  'py': 'py'
   'python': 'py'
   'rb': 'rb'
   'ruby': 'rb'


code cleaning: add space

code cleaning: add space

--- config/initializers/logging.coffee
+++ config/initializers/logging.coffee
@@ -6,7 +6,7 @@
         logging.write text

     console.info = (text) =>
-       logging.write text
+        logging.write text

     console.error = (text) =>
         logging.write text


image gets replace with new file on delete/reupload

--- assets/js/views/image-upload/preview.coffee
+++ assets/js/views/image-upload/preview.coffee
@@ -45,10 +45,11 @@
       @$imagePreview.attr 'src', ''
       @$el.removeClass 'has-image'
       @$file.removeClass 'hidden'
-      @$file.val ''
+      @$file.replaceWith @$file = @$file.clone(true)
       @trigger 'deleteImage'

       return
+
   )

   Preview


Bug fix

Bug fix

--- lib/model_extensions.coffee
+++ lib/model_extensions.coffee
@@ -263,7 +263,7 @@
     attributes = {}

     Utils.presaveBelongsToRelationships @, (err) =>
-      return options.error?(@, resp, options) if err
+      return options.error?(@, err) if err

       return _original_save.call(@, attributes, Utils.wrapOptions(options, (err, model, resp, options) =>
         --@_orm.save


Removed reference to work-in-progress base View class.

--- app/assets/javascripts/cartilage.js.coffee
+++ app/assets/javascripts/cartilage.js.coffee
@@ -2,7 +2,6 @@
 #= require "extensions"
 #
 #= require "cartilage/core"
-#= require "cartilage/view"
 #
 #= require_tree "./cartilage/models"
 #= require_tree "./cartilage/collections"


remove spinner

remove spinner

--- app/assets/javascripts/dashboard.js.coffee
+++ app/assets/javascripts/dashboard.js.coffee
@@ -17,15 +17,6 @@
   $(document).on "page:change", ->
     $("form[data-validate]").validate()

-  $(document).on("page:fetch", ->
-    AlertMessage.show
-      type: 'loading',
-      text: I18n.t('loading')
-      keep: true
-      scope: 'ajax-loading'
-  ).on "page:change", ->
-    AlertMessage.remove('ajax-loading')
-
   $(document).on("ajax:before", ->
     AlertMessage.show
       type: 'loading'


BF-195

--- app/assets/javascripts/model/binary.coffee
+++ app/assets/javascripts/model/binary.coffee
@@ -105,7 +105,7 @@

   planes : [
     { layer : null, zoomStep : null, view : { u : 0, v : 1, w : 2 } }
-    { layer : null, zoomStep : null, view : { u : 1, v : 2, w : 0 } }
+    { layer : null, zoomStep : null, view : { u : 2, v : 1, w : 0 } }
     { layer : null, zoomStep : null, view : { u : 0, v : 2, w : 1 } }
   ]

@@ -263,6 +263,20 @@
   # A synchronized implementation of `get`. Cuz its faster.
   getSync : (position, zoomStep, area, plane) ->

+    #buffer = new Uint8Array(@TEXTURE_SIZE * @TEXTURE_SIZE)
+
+#    for i in [0...512] by 1
+ #     for j in [0..512] by 1
+  #      color = 0
+   #     if i >= 255
+    #      color += 128
+     #   if j >= 255
+      #    color += 64
+       # buffer[i*512+j] = color
+
+    #return buffer
+
+
     topLeftPosition = position.slice(0)
     topLeftPosition[plane.view.u] -= (@TEXTURE_SIZE >> 1) << zoomStep
     topLeftPosition[plane.view.v] -= (@TEXTURE_SIZE >> 1) << zoomStep


Fixed entity check

Fixed entity check

--- lib/service/dataLink.coffee
+++ lib/service/dataLink.coffee
@@ -151,7 +151,8 @@
           result.statusCode = 200

         if result.statusCode < 400 and entityParser.isKinveyEntity(entity) is false
-          entity = entityParser.entity entity
+          if entity.constructor isnt Array
+            entity = entityParser.entity entity

         result.continue = false
         task.request = result
@@ -162,7 +163,8 @@
           result.statusCode = 200

         if result.statusCode < 400 and entityParser.isKinveyEntity(entity) is false
-          entity = entityParser.entity entity
+          if entity.constructor isnt Array
+            entity = entityParser.entity entity

         result.continue = true


Moved function.

Moved function.

--- app/assets/javascripts/web/_site.js.coffee
+++ app/assets/javascripts/web/_site.js.coffee
@@ -1452,6 +1452,25 @@
     feedbin.measureEntryColumn()
     feedbin.setNativeBorders()

+  baseFontSize: ->
+    element = document.createElement('div')
+    content = document.createTextNode('content')
+    element.appendChild content
+    element.style.display = 'none'
+    element.style.font = '-apple-system-body'
+
+    if element.style.font != "" && "ontouchend" of document
+      document.body.appendChild element
+      style = window.getComputedStyle(element, null)
+      size = style.getPropertyValue 'font-size'
+      base = parseInt(size) - 1
+      element.parentNode.removeChild(element)
+    else
+      base = "16"
+
+    $("html").css
+      "font-size": "#{base}px"
+
   embeds: {}

   entries: {}
@@ -1521,24 +1540,8 @@
       $(window).on('window:throttledResize', feedbin.panelCount);

     baseFontSize: ->
-      element = document.createElement('div')
-      content = document.createTextNode('content')
-      element.appendChild content
-      element.style.display = 'none'
-      element.style.font = '-apple-system-body'
-
-      if element.style.font != "" && "ontouchend" of document
-        document.body.appendChild element
-        style = window.getComputedStyle(element, null)
-        size = style.getPropertyValue 'font-size'
-        base = parseInt(size) - 1
-        element.parentNode.removeChild(element)
-      else
-        base = "16"
-
-      $("html").css
-        "font-size": "#{base}px"
-
+      feedbin.baseFontSize()
+
     faviconColors: ->
       feedbin.faviconColors($("body"))


toggle verb row selection

toggle verb row selection

--- app/assets/javascripts/zvg/verbatim.coffee
+++ app/assets/javascripts/zvg/verbatim.coffee
@@ -17,6 +17,11 @@
     rows.enter()
       .append('tr')
       .attr('class', 'response')
+      .on('click', ->
+        selection = d3.select(@)
+        isSelected = selection.classed('selected')
+        selection.classed('selected', not isSelected)
+      )

     rows.attr('id', (d) -> "respondent_#{d.id}")


fix #1745 prevent firefox click after drag

--- app/coffee/modules/backlog/sortable.coffee
+++ app/coffee/modules/backlog/sortable.coffee
@@ -104,7 +104,11 @@
                     item = $(item)
                     itemUs = item.scope().us

-                    item.find('a').removeClass('noclick')
+                    # HACK: setTimeout prevents that firefox click
+                    # event fires just after drag ends
+                    setTimeout ( =>
+                        item.find('a').removeClass('noclick')
+                    ), 300

                     return itemUs

@@ -137,6 +141,7 @@

                     deleteElement(ui.item)
                     $scope.$emit("sprint:us:move", [itemUs], itemIndex, null)
+
                     ui.item.find('a').removeClass('noclick')

         $scope.$on "$destroy", ->
@@ -168,7 +173,6 @@
                         itemUs = item.scope().us

                         deleteElement(item)
-                        item.find('a').removeClass('noclick')

                         return itemUs

@@ -181,8 +185,17 @@

                     itemUs = ui.item.scope().us
                     itemIndex = ui.item.index()
-                    ui.item.find('a').removeClass('noclick')
+
+                    # HACK: setTimeout prevents that firefox click
+                    # event fires just after drag ends
+                    setTimeout ( =>
+                        ui.item.find('a').removeClass('noclick')
+                    ), 300
+
                     $scope.$emit("sprint:us:move", [itemUs], itemIndex, $scope.sprint.id)
+
+                $el.on "sortstart", (event, ui) ->
+                    ui.item.find('a').addClass('noclick')

     return {link:link}


close click

close click

--- public/dreamcatcher/controllers/entries/books_controller.coffee
+++ public/dreamcatcher/controllers/entries/books_controller.coffee
@@ -5,6 +5,9 @@

   init: ->
     @model.get {}, @callback('populate')
+
+  getBookElement: (el) ->
+    return el.closest '.book'

   populate: (html) ->
     $('#entryField .matrix').prepend html
@@ -73,12 +76,14 @@
     $('.open', bookEl).show()
     $('.closed', bookEl).hide()

-    $('#bodyClick').show()
-    bookEl.css 'z-index', '1200' #todo: should be a style
+    $('.closeClick').show()
+
+  '.closeClick click': (el) ->
+    @closeBook @getBookElement el

   changeBookColor: (el) ->
     color = el.attr 'class'
-    bookEl = el.closest '.book'
+
     bookEl.attr 'class', 'book'
     bookEl.addClass color
     bookEl.data 'color', color


FF: Use HUD.copyToClipboard for copyCurrentUrl

--- content_scripts/mode_normal.coffee
+++ content_scripts/mode_normal.coffee
@@ -91,7 +91,7 @@

   copyCurrentUrl: ->
     chrome.runtime.sendMessage { handler: "getCurrentTabUrl" }, (url) ->
-      chrome.runtime.sendMessage { handler: "copyToClipboard", data: url }
+      HUD.copyToClipboard url
       url = url[0..25] + "...." if 28 < url.length
       HUD.showForDuration("Yanked #{url}", 2000)


feat: log render times for views in development

--- app/config/app.coffee
+++ app/config/app.coffee
@@ -14,6 +14,9 @@
     Ember.Logger.error(e.message)
     Ember.Logger.error(e.stack)

+  # Log view render times to the console
+  Ember.STRUCTURED_PROFILE = true
+
   Ember.Logger.debug(
     "Running in the %c#{env.get('name')}%c environment", 'color: red;', ''
   )


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/engine_selector.coffee
+++ test/engine_selector.coffee
@@ -26,6 +26,6 @@
     y: 20

   equals engine.first('type=testObj').length, 1
-  equals engine.find('')
+  equals engine.find('type')

 module()


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/engine_selector.coffee
+++ test/engine_selector.coffee
@@ -19,4 +19,5 @@

 test "#first", ->

+
 module()


sneak in fix to remove warning about plan footer button

--- tutor/src/helpers/plan.coffee
+++ tutor/src/helpers/plan.coffee
@@ -15,7 +15,7 @@

   subscribeToPublishing: (plan, callback) ->
     {jobId, id} = PlanPublishStore._getIds(plan)
-    isPublishing = PlanHelper.isPublishing(plan)
+    isPublishing = PlanHelper.isPublishing(plan) or false

     publishStatus = PlanPublishStore.getAsyncStatus(id)
     isPublishingInStore = PlanPublishStore.isPublishing(id)


expanded default network container healthcheck. initially check often, when successful write a file. Next iteration, if the file exists we first sleep before running the check. this decreases the cpu load drastically.

--- index.coffee
+++ index.coffee
@@ -25,10 +25,10 @@
     scriptBaseDir: env.assert 'SCRIPT_BASE_DIR'
   net_container:
     healthcheck:
-      test: env.get 'NETWORK_HEALTHCHECK_TEST', "ifconfig eth0 | grep 'inet addr:'"
-      interval:  env.get 'NETWORK_HEALTHCHECK_INTERVAL', '15s'
-      timeout: env.get 'NETWORK_HEALTHCHECK_TIMEOUT', '5s'
-      retries: env.get 'NETWORK_HEALTHCHECK_RETRIES', 60
+      test: env.get 'NETWORK_HEALTHCHECK_TEST', "if [ ! -f /tmp/healthcheck ]; then ifconfig eth0 | grep 'inet addr:'; [ $$? -eq 0 ] && touch /tmp/healthcheck; else sleep 30; ifconfig eth0 | grep 'inet addr:'; fi"
+      interval:  env.get 'NETWORK_HEALTHCHECK_INTERVAL', '1s'
+      timeout: env.get 'NETWORK_HEALTHCHECK_TIMEOUT', '35s'
+      retries: env.get 'NETWORK_HEALTHCHECK_RETRIES', 5

 console.log 'Config \n\n', config, '\n\n'


only install connectors once per gateblu starting.

--- index.coffee
+++ index.coffee
@@ -13,6 +13,7 @@
   constructor: (@config) ->
     @deviceProcesses = {}
     @runningDevices = []
+    @connectorsInstalled = {}

   refreshDevices: (devices, callback) =>
     debug 'refreshDevices', _.pluck(devices, 'uuid')
@@ -137,6 +138,8 @@

   installConnector : (connector, callback=->) =>
     debug 'installConnector', connector
+    return if @connectorsInstalled[connector]
+
     nodeModulesDir = path.join @config.tmpPath, 'node_modules'
     fs.mkdirpSync nodeModulesDir
     connectorPath = path.join nodeModulesDir, connector
@@ -152,10 +155,11 @@
           @emit 'stderr', error
           return callback()

-        @emit 'npm:stderr', stderr.toString()
-        @emit 'npm:stdout', stdout.toString()
-        debug 'forever stdout', stdout.toString()
-        debug 'forever stderr', stderr.toString()
+        @emit 'npm:stderr', stderr
+        @emit 'npm:stdout', stdout
+        debug 'npm:stdout', stdout
+        debug 'npm:stderr', stderr
+        @connectorsInstalled[connector] = true
         callback()
     )


Disable voxel-reach and dependants, require interact

--- index.coffee
+++ index.coffee
@@ -114,7 +114,7 @@
     'voxel-registry': {}
     'voxel-artpacks': {}
     'voxel-recipes': {}
-    'voxel-quarry': {}
+    #'voxel-quarry': {} # requires voxel-harvest
     'voxel-measure': {}
     'voxel-webview': {onDemand: true}  # disabled by default until https://github.com/deathcap/voxel-webview/issues/3
     'voxel-carry': {inventoryWidth:10, inventoryRows:5}
@@ -128,7 +128,7 @@
     'voxel-furnace': {}
     'voxel-pickaxe': {}
     'voxel-wool': {}
-    'voxel-pumpkin': {}
+    #'voxel-pumpkin': {} # requires voxel-use
     #'voxel-daylight': {ambientColor: 0x888888, directionalColor: 0xffffff} # adds three.js lights

     'voxel-land': {populateTrees: true}
@@ -139,32 +139,32 @@
     'voxel-console': {}
     'voxel-commands': {}
     #'voxel-drop': {} # requires voxel-texture-shader
-    'voxel-start': {}
+    #'voxel-start': {} # requires interact
     'voxel-zen': {}

-    'voxel-player': {image: 'player.png', homePosition: [2,14,4], homeRotation: [0,0,0]}
+    #'voxel-player': {image: 'player.png', homePosition: [2,14,4], homeRotation: [0,0,0]} # three.js
     'voxel-health': {}
     'voxel-health-bar': {}
-    'voxel-health-fall': {}
+    #'voxel-health-fall': {} # requires voxel-player
     'voxel-food': {}
     #'voxel-sfx': {} # requires voxel-texture-shader, game.materials artpacks
     'voxel-fly': {flySpeed: 0.8, onDemand: true}
     'voxel-gamemode': {}
-    'voxel-walk': {}
+    #'voxel-walk': {} # requires controls target
     'voxel-sprint': {}
     'voxel-inventory-hotbar': {inventorySize:10}
     'voxel-inventory-crafting': {}
-    'voxel-reach': { reachDistance: 8 }
+    #'voxel-reach': { reachDistance: 8 } # requires interact
     # left-click hold to mine
-    'voxel-mine':
-      instaMine: false
-      progressTexturesPrefix: 'destroy_stage_'
-      progressTexturesCount: 9
+    #'voxel-mine': # requires voxel-reach, and currently three.js mesh
+    #  instaMine: false
+    #  progressTexturesPrefix: 'destroy_stage_'
+    #  progressTexturesCount: 9
     # right-click to place block (etc.)
-    'voxel-use': {}
+    #'voxel-use': {} # requires voxel-reach
     # handles 'break' event from voxel-mine (left-click hold breaks blocks), collects block and adds to inventory
-    'voxel-harvest': {}
+    #'voxel-harvest': {} # requires voxel-mine
     # highlight blocks when you look at them
     #'voxel-highlight': # creates three.js mesh
     #  color:  0xff0000
@@ -174,7 +174,7 @@
     'voxel-keys': {}

     # the GUI window (built-in toggle with 'H')
-    'voxel-debug': {}
+    #'voxel-debug': {} # heavily three.js dependent
     'voxel-plugins-ui': {}
     'kb-bindings-ui': {}
   }


processes ids where not being trimmed

processes ids where not being trimmed

--- index.coffee
+++ index.coffee
@@ -15,7 +15,7 @@
 	console.log "args : ", leargs if debug
 	process.on 'exit', (code) ->
 		if fs.existsSync(pidfile) and delpid
-			console.log 'Removing pid file..'
+			console.log 'Removing pid file..' if debug
 			fs.unlinkSync pidfile
 			return

@@ -24,7 +24,7 @@
 			if error
 				#console.log cmdline, error, stdout, stderr
 				throw new Error (error)
-			callback (stdout.split("\n").indexOf(pid) isnt -1)
+			callback (stdout.split("\n").map((el)->return el.replace(/ /g,"")).indexOf(pid) isnt -1)

 	check = (cback)->
 		if fs.existsSync(pidfile)


Use the `USER` env var instead of `whoami`

Calling `whoami` will always return the user with which the server was
started, which might or might not be the current user.

For example if we change the user in the middle of a session the prompt
will continue showing the original username.

--- index.coffee
+++ index.coffee
@@ -11,8 +11,8 @@
       cwd

   register 'user',
-    update: (done) ->
-      Impromptu.exec 'whoami', done
+    update: ->
+      process.env.USER

   register 'host',
     update: (done) ->


use the propa bot

use the propa bot

--- index.coffee
+++ index.coffee
@@ -126,7 +126,7 @@
       query = message.text[matchedCommand.name.length..].trim()
       message.text.toLowerCase().indexOf(matchedCommand.name)

-      context = new ReplyContext(matchedCommand.looker, bot, message)
+      context = new ReplyContext(matchedCommand.looker, spawnedBot, message)
       filters = {}
       for filter in matchedCommand.dashboard.filters
         filters[filter.name] = query
@@ -141,9 +141,9 @@
           help += " — _#{command.description}_"
         help += "\n"
       if _.values(customCommands).length > 0
-        bot.reply(message, help)
+        spawnedBot.reply(message, help)
       else
-        bot.reply(message, "No custom commands are configured.")
+        spawnedBot.reply(message, "No custom commands are configured.")
       refreshCommands()

 runCLI = (bot, message) ->


startPolling no longer needs s3 url

startPolling no longer needs s3 url

--- index.coffee
+++ index.coffee
@@ -34,7 +34,7 @@
     return false

   api.start(dom.readBootstrapData())
-  NotificationActions.startPolling('https://s3.amazonaws.com/ox-notification-testing/notifications.json')
+  NotificationActions.startPolling()

   startMathJax()
   TransitionAssistant.startMonitoring()


Show correct directory in symlinked dirs

process.cwd() reads "through" a symlink and ends up showing you the wrong directory

--- index.coffee
+++ index.coffee
@@ -1,9 +1,9 @@
 module.exports = (Impromptu) ->
   @register 'pwd', ->
-    process.cwd()
+    process.env.PWD

   @register 'prettyPwd', ->
-    cwd = process.cwd()
+    cwd = process.env.PWD
     if cwd.indexOf process.env.HOME == 0
       cwd = '~' + cwd.slice process.env.HOME.length
     cwd


useless command

useless command

--- index.coffee
+++ index.coffee
@@ -7,10 +7,9 @@
   if isDev
     exec "jade -w ."
     exec "sass -w ."
-    exec "rm -rf ./public/js/app", ->
-      exec "coffeescript-concat -I ./public/js -o ./public/js/app", ->
-        exec "coffee -cb ./public/js/app", ->
-          startServer()
+    exec "coffeescript-concat -I ./public/js -o ./public/js/app", ->
+      exec "coffee -cb ./public/js/app", ->
+        startServer()
   else
     startServer()

@@ -25,7 +24,6 @@
       handler:
         file: "./public/templates/index.html"

-
   server.route
     method: "GET"
     path: "/{path*}"


Change pickaxe recipes from amorphous to positional

Now that PositionalRecipe is functional

Requires https://github.com/deathcap/craftingrecipes/commit/70d1631e41b9747439badbf75e58aef47a79e16a

--- index.coffee
+++ index.coffee
@@ -94,8 +94,18 @@
   RecipeLocator.register new AmorphousRecipe(['wood.log'], new ItemPile('plankOak', 2))
   RecipeLocator.register new AmorphousRecipe(['wood.plank', 'wood.plank'], new ItemPile('stick', 4))
   RecipeLocator.register new AmorphousRecipe(['wood.plank', 'wood.plank', 'wood.plank', 'wood.plank'], new ItemPile('workbench', 1))
-  RecipeLocator.register new AmorphousRecipe(['stick', 'stick', 'wood.plank', 'wood.plank', 'wood.plank'], new ItemPile('pickaxeWood', 1)) # TODO: changed to positional recipe once available
-  RecipeLocator.register new AmorphousRecipe(['stick', 'stick', 'tree.leaves', 'tree.leaves', 'tree.leaves'], new ItemPile('pickaxeDiamond', 1)) # temporary recipe
+
+  RecipeLocator.register new PositionalRecipe([
+    ['wood.plank', 'wood.plank', 'wood.plank'],
+    [undefined, 'stick', undefined],
+    [undefined, 'stick', undefined]], new ItemPile('pickaxeWood', 1))
+
+  # temporary recipe
+  RecipeLocator.register new PositionalRecipe([
+    ['tree.leaves', 'tree.leaves', 'tree.leaves'],
+    [undefined, 'stick', undefined],
+    [undefined, 'stick', undefined]], new ItemPile('pickaxeDiamond', 1))
+

   game.materials.load registry.getBlockPropsAll 'texture'

@@ -146,7 +156,9 @@
   game.mode = 'survival'

   playerInventory = new Inventory(10, 5)
+  #playerInventory.give new ItemPile('stick', 32)
   #playerInventory.give new ItemPile('logOak', 10)
+  #playerInventory.give new ItemPile('plankOak', 10)
   #playerInventory.give new ItemPile('logBirch', 5)
   #playerInventory.give new ItemPile('workbench', 1)
   #toolbar = createToolbar {el: '#tools'}


Remove single and double quotes from highlighted pairs

--- index.coffee
+++ index.coffee
@@ -5,19 +5,22 @@

 module.exports =
 class BracketMatcher extends AtomPackage
-  startPairMatches:
+  pairedCharacters:
     '(': ')'
     '[': ']'
     '{': '}'
     '"': '"'
     "'": "'"

+  startPairMatches:
+    '(': ')'
+    '[': ']'
+    '{': '}'
+
   endPairMatches:
     ')': '('
     ']': '['
     '}': '{'
-    '"': '"'
-    "'": "'"

   pairHighlighted: false

@@ -153,7 +156,7 @@
         editSession.moveCursorRight()
         false
       else if autoCompleteOpeningBracket
-        editSession.insertText(text + @startPairMatches[text])
+        editSession.insertText(text + @pairedCharacters[text])
         editSession.moveCursorLeft()
         range = [cursorBufferPosition, cursorBufferPosition.add([0, text.length])]
         @bracketMarkers.push editSession.markBufferRange(range)
@@ -166,7 +169,7 @@
       cursorBufferPosition = editSession.getCursorBufferPosition()
       previousCharacter = editSession.getTextInBufferRange([cursorBufferPosition.add([0, -1]), cursorBufferPosition])
       nextCharacter = editSession.getTextInBufferRange([cursorBufferPosition, cursorBufferPosition.add([0,1])])
-      if @startPairMatches[previousCharacter] is nextCharacter
+      if @pairedCharacters[previousCharacter] is nextCharacter
         editSession.transact =>
           editSession.moveCursorLeft()
           editSession.delete()
@@ -174,7 +177,7 @@
         false

   wrapSelectionInBrackets: (editSession, bracket) ->
-    pair = @startPairMatches[bracket]
+    pair = @pairedCharacters[bracket]
     editSession.mutateSelectedText (selection) =>
       return if selection.isEmpty()

@@ -191,8 +194,16 @@
   isQuote: (string) ->
     /'|"/.test(string)

+  getInvertedPairedCharacters: ->
+    return @invertedPairedCharacters if @invertedPairedCharacters
+
+    @invertedPairedCharacters = {}
+    for open, close of @pairedCharacters
+      @invertedPairedCharacters[close] = open
+    @invertedPairedCharacters
+
   isOpeningBracket: (string) ->
-    @startPairMatches[string]?
+    @pairedCharacters[string]?

   isClosingBracket: (string) ->
-    @endPairMatches[string]?
+    @getInvertedPairedCharacters()[string]?