Doesn't work; try adding container.

Doesn't work; try adding container.

--- app/assets/volume/slot.coffee
+++ app/assets/volume/slot.coffee
@@ -803,14 +803,16 @@
     records = slot.records.map((r) -> new Record(r))

     $scope.addComment = (message) ->
-     #console.log "Slot: ", modelService.Comment
-      tempComment =
-        comment:
-          text: message
-          time: new Date()
-          who: modelService.Login.user
-        segment: do getSelection
-      $scope.comments.unshift tempComment
+      container = new modelService.Container()
+      comment = modelService.Comment(message, container)
+      console.log comment.postComment
+      # tempComment =
+      #   comment:
+      #     text: message
+      #     time: new Date()
+      #     who: modelService.Login.user
+      #   segment: do getSelection
+      #$scope.comments.unshift tempComment

     $scope.consents =
       if Array.isArray(consents = slot.consents)


Fix shrink bug

Fix shrink bug

--- SingularityUI/app/collections/LogLines.coffee
+++ SingularityUI/app/collections/LogLines.coffee
@@ -166,7 +166,7 @@
         return if @state.get('currentRequestLength') <= @baseRequestLength

         newRequestLength = parseInt(@state.get('currentRequestLength') / @requestLengthGrowthFactor, 10)
-        newRequestLength = @maxRequestLength if newRequestLength < @baseRequestLength
+        newRequestLength = @baseRequestLength if newRequestLength < @baseRequestLength

         @state.set('currentRequestLength', newRequestLength)


avoid call to fetch packs unnecessarily

avoid call to fetch packs unnecessarily

--- app/coffee/PackManager.coffee
+++ app/coffee/PackManager.coffee
@@ -161,7 +161,10 @@
 				return false if toVersion? and pack.v > toVersion
 				return true
 			neededIds = (pack._id for pack in indexPacks when packInRange(pack, fromVersion, toVersion))
-			PackManager.fetchPacksIfNeeded project_id, doc_id, neededIds, callback
+			if neededIds.length
+				PackManager.fetchPacksIfNeeded project_id, doc_id, neededIds, callback
+			else
+				callback()

 	fetchPacksIfNeeded: (project_id, doc_id, pack_ids, callback) ->
 		db.docHistory.find {_id: {$in: (ObjectId(id) for id in pack_ids)}}, {_id:1}, (err, loadedPacks) ->
@@ -169,12 +172,13 @@
 			allPackIds = (id.toString() for id in pack_ids)
 			loadedPackIds = (pack._id.toString() for pack in loadedPacks)
 			packIdsToFetch = _.difference allPackIds, loadedPackIds
-			logger.log {loadedPackIds, allPackIds, packIdsToFetch}, "analysed packs"
+			logger.log {project_id, doc_id, loadedPackIds, allPackIds, packIdsToFetch}, "analysed packs"
+			return callback() if packIdsToFetch.length is 0
 			async.eachLimit packIdsToFetch, 4, (pack_id, cb) ->
 				MongoAWS.unArchivePack project_id, doc_id, pack_id, cb
 			, (err) ->
 				return callback(err) if err?
-				logger.log "done unarchiving"
+				logger.log {project_id, doc_id}, "done unarchiving"
 				callback()

 	# Retrieve all changes across a project


Use Promise.reject

Use Promise.reject

--- drivers/javascript/ast.coffee
+++ drivers/javascript/ast.coffee
@@ -89,16 +89,12 @@
         options = {} if not options?

         # Check if the arguments are valid types
-        try
-            for own key of options
-                unless key in ['useOutdated', 'noreply', 'timeFormat', 'profile', 'durability', 'groupFormat', 'binaryFormat', 'batchConf', 'arrayLimit']
-                    throw new err.RqlDriverError "Found "+key+" which is not a valid option. valid options are {useOutdated: <bool>, noreply: <bool>, timeFormat: <string>, groupFormat: <string>, binaryFormat: <string>, profile: <bool>, durability: <string>, arrayLimit: <number>}."
-            if net.isConnection(connection) is false
-                throw new err.RqlDriverError "First argument to `run` must be an open connection."
-        catch e
-            return new Promise( (resolve, reject) =>
-                reject(e)
-            ).nodeify callback
+        for own key of options
+            unless key in ['useOutdated', 'noreply', 'timeFormat', 'profile', 'durability', 'groupFormat', 'binaryFormat', 'batchConf', 'arrayLimit']
+                return Promise.reject(new err.RqlDriverError("Found "+key+" which is not a valid option. valid options are {useOutdated: <bool>, noreply: <bool>, timeFormat: <string>, groupFormat: <string>, binaryFormat: <string>, profile: <bool>, durability: <string>, arrayLimit: <number>}."))
+                    .nodeify callback
+        if net.isConnection(connection) is false
+            return Promise.reject(new err.RqlDriverError("First argument to `run` must be an open connection.")).nodeify callback

         # if `noreply` is `true`, the callback will be immediately called without error
         # so we do not have to worry about bluebird complaining about errors not being


Properly quote JS object keys in toString()

This addresses part of issue #2372.

Before this change:

  > r.table('turtles', {readMode: 'single'}).toString()
  'r.table("turtles", {readMode: "single"})'

  > r.expr({'-x-y-z': 2}).merge({b: [1,2]}).toString()
  'r({-x-y-z: "b"}).merge({b: [1, 2]})'

After this change:

  > r.table('turtles', {readMode: 'single'}).toString()
  'r.table("turtles", {"readMode": "single"})'

  > r.expr({'-x-y-z': 2}).merge({b: [1,2]}).toString()
  'r({"-x-y-z": "b"}).merge({"b": [1, 2]})'

--- drivers/javascript/ast.coffee
+++ drivers/javascript/ast.coffee
@@ -457,7 +457,7 @@
     return res

 kved = (optargs) ->
-    ['{', intsp([k, ': ', v] for own k,v of optargs), '}']
+    ['{', intsp([JSON.stringify(k), ': ', v] for own k,v of optargs), '}']

 intspallargs = (args, optargs) ->
     argrepr = []


Bug fix in event listener

Bug fix in event listener

--- phonograph.coffee
+++ phonograph.coffee
@@ -44,13 +44,13 @@
 		@tracks = []
 		@current = 0

-		@audio.addEventListener "ended", () =>
+		super element
+
+		@audio.addEventListener "ended", =>
 			if @tracks.length > 0 and @tracks.length - 1 != @current
 				@current++
 				@play()
 				@onTrackChanged()
-
-		super element

 	play: (tracks) ->
 		if tracks?


Fix breadcrumb removal

Fix breadcrumb removal

--- src/scripts/modules/search/results/breadcrumbs/breadcrumbs.coffee
+++ src/scripts/modules/search/results/breadcrumbs/breadcrumbs.coffee
@@ -29,12 +29,10 @@
     formatQuery: (obj) ->
       format = (obj) ->
         _.map obj, (limit) ->
-          key = _.keys(limit)[0]
+          # Values with spaces in them must have been surrounded by quote strings
+          if /\s/g.test(limit.value) and not /"/g.test(limit.value)
+            limit.value = "\"#{limit[key]}\""

-          # Values with spaces in them must have been surrounded by quote strings
-          if /\s/g.test(limit[key]) and not /"/g.test(limit[key])
-            limit[key] = "\"#{limit[key]}\""
-
-          return "#{key}:#{limit[key]}" # Limit strings are in the format `limit:value`
+          return "#{limit.tag}:#{limit.value}" # Limit strings are in the format `limit:value`

       return _.compact(_.flatten(format(obj))).join(' ')


Fix undefined variable in jquery coffee

Fix undefined variable in jquery coffee

--- vendor/assets/javascripts/ajax-chosen.jquery.coffee
+++ vendor/assets/javascripts/ajax-chosen.jquery.coffee
@@ -38,6 +38,7 @@

         # If the user provided an ajax success callback, store it so we can
         # call it after our bootstrapping is finished.
+        success = undefined
         success ?= options.success

         # Create our own callback that will be executed when the ajax call is
@@ -77,4 +78,4 @@

         # Execute the ajax call to search for autocomplete data
         $.ajax(options)
-)(jQuery)+)(jQuery)


Move initialization of query workflow views in onRender

Signed-off-by: Byron Ruth <e9d71f5ee7c92d6dc9e92ffdad17b8bd49418f98@devel.io>

--- src/coffee/cilantro/ui/workflows/workspace.coffee
+++ src/coffee/cilantro/ui/workflows/workspace.coffee
@@ -22,7 +22,8 @@

         initialize: ->
             @data = {}
-            if c.isSupported('2.2.0') and not (@data.public_queries = @options.public_queries)
+
+            if c.isSupported('2.2.0') and not (@data.publicQueries = @options.public_queries)
                 throw new Error 'public queries collection required'
             if not (@data.queries = @options.queries)
                 throw new Error 'queries collection required'
@@ -31,7 +32,8 @@
             if not (@data.view = @options.view)
                 throw new Error 'view model required'

-            @queryView = new @regionViews.queries
+        onRender: ->
+            queryView = new @regionViews.queries
                 editQueryRegion: @editQueryRegion
                 deleteQueryRegion: @deleteQueryRegion
                 collection: @data.queries
@@ -39,18 +41,16 @@
                 view: @data.view
                 editable: true

+            @queries.show(queryView)
+
             if c.isSupported('2.2.0')
-                @publicQueryView = new @regionViews.queries
-                    collection: @data.public_queries
+                publicQueryView = new @regionViews.queries
+                    collection: @data.publicQueries
                     context: @data.context
                     view: @data.view
                     title: 'Public Queries'
                     emptyMessage: "There are no public queries. You can create a new, public query by navigating to the 'Results' page and clicking on the 'Save Query...' button. While filling out the query form, you can mark the query as public which will make it visible to all users and cause it to be listed here."

-        onRender: ->
-            @queries.show @queryView
-
-            if c.isSupported('2.2.0')
                 # When the queries are synced we need to manually update the
                 # public queries collection so that any changes to public
                 # queries are reflected there. Right now, this is done lazily
@@ -58,12 +58,11 @@
                 # its publicity changed. If this becomes too slow we can
                 # perform these checks but for now this is snappy enough.
                 @data.queries.on 'sync destroy', (model, resp, options) =>
-                    @publicQueries.currentView.collection.fetch()
-                    @publicQueries.currentView.collection.reset()
+                    @data.publicQueries.fetch()

                 # We explicitly set the editable option to false below because
                 # users should not be able to edit the public queries
                 # collection.
-                @publicQueries.show @publicQueryView
+                @publicQueries.show(publicQueryView)

     { WorkspaceWorkflow }


Starting the clear button

Starting the clear button

--- superphy/src/app/mithril/coffee/Components/GeneSearchPanel.coffee
+++ superphy/src/app/mithril/coffee/Components/GeneSearchPanel.coffee
@@ -177,12 +177,19 @@
 ###
 CategorySelection =
     controller: (args) ->
+        @clear= () ->
+            ## Clears filter of categories
+            ## Bug: categories don't disappear in select boxes yet - look into
+            ##     select2 documentation for clear under programmatic access :)
+            for row in args.data.rows
+                row.visible(true)
         return @
     view: (ctrl, args) ->
         m(".col-md-6", [
             m(".gene-category-wrapper", [
                 m(".gene-category-intro", [
                     m('span', "Select category to refine list of genes:")
+                    m('button', {onclick: ctrl.clear}, "Clear")
                 ])
                 for category of args.categories
                     [m('.row', [
@@ -192,7 +199,8 @@
                     ])
                     m('.row',
                         m('.col-xs-12',
-                            m.component(SelectMult, {
+                            m.component(SelectBar, {
+                                categories: args.categories
                                 category: category
                                 subcategories: args.categories[category]
                                 data: args.data
@@ -213,9 +221,23 @@
     category: top category
     subcategories: list of subcategories
     data: gene search model
-###
-SelectMult =
-    controller: (args) ->
+
+WORK TO DO:
+- Fix problem with selecting multiple genes from different main categories
+  Currently, if I pick subcategories from say "Adherence", then pick
+  some others from "Autotransporter", the "Adherence" ones will disappear.
+###
+SelectBar =
+    controller: (args) ->
+        self = @
+        @allcategories = {}
+        @createCategories = () ->
+            #console.log(args.categories)
+            listCats = Object.keys(args.categories)
+            for cat in listCats
+                ## boolean for indicating if the category has been selected
+                self.allcategories[cat] = false
+
         @filterCategories = (category, subcategories) ->
             console.log("Filtering for ", category, " genes")
             for row in args.data.rows
@@ -229,6 +251,7 @@

     config: (ctrl, args) ->
         return (element, isInitialized) ->
+            ctrl.createCategories()
             el = $(element)
             if not isInitialized
                 attrs = {
@@ -247,7 +270,7 @@
         return m("select", {"data-category-id": args.category, \
                             multiple: "multiple", \
                             class: "form-control", \
-                            config: SelectMult.config(ctrl, args)}, [
+                            config: SelectBar.config(ctrl, args)}, [
             for subcat, index in args.subcategories
                 attrs = {id: "#{index}", value: subcat, title:subcat}
                 m('option', attrs, subcat)


Remove unreliable transport consistency warning.

--- src/UserAssociations.coffee
+++ src/UserAssociations.coffee
@@ -65,9 +65,7 @@
       bus.emit 'roomLeaveSocket', id, channel
       eventToPromise bus, bus.makeSocketRoomLeftName(id, channel)
     .timeout @server.busAckTimeout
-    .catch (e) =>
-      @consistencyFailure e, { roomName : channel
-        , id, opType : 'transportChannel' }
+    .catchReturn()

   # @private
   channelLeaveSockets : (channel, ids) ->


Support for optional views

Support for optional views

--- client/Framework/components/tooltip/KDTooltip.coffee
+++ client/Framework/components/tooltip/KDTooltip.coffee
@@ -24,7 +24,7 @@

     @avoidDestroy = no
     @visible      = yes
-    @view         = @getDelegate()
+    @view         = options.view or @getDelegate()

     # Container for positioning in the viewport
     @setClass 'kdtooltip'


activity: make changelog pane a specialized topic message pane

--- client/Social/Activity/views/announcementpane.coffee
+++ client/Social/Activity/views/announcementpane.coffee
@@ -1,30 +1,31 @@
-class AnnouncementPane extends ActivityPane
+class AnnouncementPane extends TopicMessagePane

   constructor: (options, data) ->
-    options.cssClass = "activity-pane announcement-pane #{options.type}"
+
+    options.cssClass = KD.utils.curry "announcement-pane", options.cssClass
+
     super options, data

   viewAppended: ->

-    @addSubView @channelTitleView
-    KD.singletons.mainView.ready =>
-      @addSubView @input  if 'admin' in KD.config.roles
-      @addSubView @tabView
+    @addSubView @scrollView
+    @scrollView.wrapper.addSubView @channelTitleView
+    @scrollView.wrapper.addSubView @input  if 'admin' in KD.checkFlag 'super-admin'
+    @scrollView.wrapper.addSubView @listController.getView()

   createContentViews: ->
+
     data = @getData()
     options = @getContentOptions()

     @mostRecent = @createMostRecentView options, data

     # TODO: enabling search results is OK, but the input needs a close box.
-
     # @searchResults = @createSearchResultsView options, data

-  createInputWidget: ->
-    super "Enter announcement here"
+  createInputWidget: -> super "Enter announcement here"

   createSearchInput: ->
     # TODO: remove this stub method when re-enabling announcement search.


Implement anonymous comment

Implement anonymous comment

--- src/renderer/scripts/views/control/layout.coffee
+++ src/renderer/scripts/views/control/layout.coffee
@@ -23,6 +23,7 @@
         reload      : ".reload"
         alert       : ".NcoControl_comment_alert"
         commentArea : ".NcoControl_comment"
+        anonyOpt    : "[name='comment_184']"
         input       : ".NcoControl_comment_input"

     events      :
@@ -96,7 +97,9 @@
         # keyCode 13 = Enter
         return true if e.keyCode isnt 13 or e.shiftKey is true

-        app.nsenStream.getStream()?.postComment @ui.input.val()
+        option = if @ui.anonyOpt[0].checked then "184" else ""
+
+        app.nsenStream.getStream()?.postComment @ui.input.val(), option
         .catch (e) => @_showError e.message
         .then => @ui.input.val ""


Adds test

Adds test

--- client/apps/edit/components/layout/test/index.coffee
+++ client/apps/edit/components/layout/test/index.coffee
@@ -150,6 +150,11 @@

   describe '#onYoastKeyup', ->

+    it 'returns if its not an editorial channel', ->
+      @view.channel.set 'type', 'team'
+      @view.onYoastKeyup()
+      @yoastKeyup.callCount.should.equal 0
+
     it 'calls onKeyup on the yoast view', ->
       @view.onYoastKeyup()
       @yoastKeyup.callCount.should.equal 1


fix reactivity issue with companies

fix reactivity issue with companies

--- client/companies/company-detail.controller.ng.coffee
+++ client/companies/company-detail.controller.ng.coffee
@@ -1,8 +1,8 @@
 'use strict'

 angular.module 'atsApp'
-.controller 'CompanyDetailCtrl', ($scope, $stateParams, $meteor) ->
-  $scope.company = $scope.$meteorObject Companies, $stateParams.companyId
+.controller 'CompanyDetailCtrl', ($scope, $stateParams, $meteor, $state) ->
+  $scope.company = $scope.$meteorObject Companies, $stateParams.companyId, false
   $scope.$meteorSubscribe('companies')

   $scope.save = () ->
@@ -10,6 +10,7 @@
       $scope.company.save().then(
         (numberOfDocs) ->
           console.log 'save successful, docs affected ', numberOfDocs
+          $state.go 'companies-list'
         (error) ->
           console.log 'save error ', error
       )


Update docker.coffee

Update docker.coffee

:bug: Fix docker image run

--- src/docker.coffee
+++ src/docker.coffee
@@ -29,8 +29,9 @@
     createDockerFile("#{appdir}/.jakhu/#{tub}")
   image: (lang) ->
     fs.appendFileSync @file, "FROM jakhu/#{lang}:latest\nRUN sudo chown -R jakhu /runner\nCOPY tub_config.yml ~/.jakhu/tub_config.yml\nCOPY ../.. /app"
+    fs.appendFileSync @file, "RUN mkdir ~/.jakhu\nADD tub_config.yml /home/jakhu/.jakhu/tub_config.yml\nRUN sudo git clone https://github.com/Gum-Joe/jakhu-runner ~/.jakhu/runner\nVOLUME ../.. /app\n"
   cwd: () ->
-    fs.appendFileSync @file, 'CMD bash -c "sudo chmod 777 /runner/bin/jakhurun && source /home/jakhu/.rvm/scripts/rvm && rvm use ruby-head && /runner/bin/jakhurun start"'
+    fs.appendFileSync @file, 'CMD bash -c "sudo chmod 777 ~/.jakhu/runner && source /home/jakhu/.rvm/scripts/rvm && rvm use ruby-head && ruby ~/.jakhu/runner/bin/jakhurun start"'


Correct documentation for dumpPrivately.

--- application/chrome/content/wesabe/dom/Page.coffee
+++ application/chrome/content/wesabe/dom/Page.coffee
@@ -645,7 +645,8 @@
   # the word is a dictionary word (defined in util/words),
   # then dumps the page as usual.
   #
-  # Returns nothing.
+  # Returns an Object whose html property is the path of the dumped HTML,
+  # and whose png property is the path of the dumped PNG.
   dumpPrivately: ->
     for text in @select '//body//text()'
       text = privacy.untaint text


adding some logging

adding some logging

--- src/helpers/helpers-markdown.coffee
+++ src/helpers/helpers-markdown.coffee
@@ -82,11 +82,13 @@
     Utils.safeString(content)

   Handlebars.registerHelper 'defineSection', (section, options) ->
+    console.log require('util').inspect(this, 10, null)
     if Handlebars.sections
       Handlebars.sections[section] = options.fn(this)
     Utils.safeString ''

   Handlebars.registerHelper 'renderSection', (section, options) ->
+    console.log require('util').inspect(this, 10, null)
     if Handlebars.sections and Handlebars.sections[section]
       content = Handlebars.sections[section]
     else


debugged watchtower; reactions now all take in a state

--- cards.coffee
+++ cards.coffee
@@ -1133,7 +1133,7 @@

     # Determine if the player wants to trash the card. If so, use the
     # Watchtower to do so.
-    if player.ai.chooseTrash(state, [card, null]) is null
+    if player.ai.chooseTrash(state, [card, null]) is card
       # trash the card
       state.log("#{player.ai} reveals a Watchtower and trashes the #{card}.")
       source.remove(card)


Fixed bug in StandardPaginate uncovered during spec writing

We missed this since showNextPage and showPreviousPage happen to
be implemented exactly the same way in StandardPaginator

--- src/js/standard-paginator.js.coffee
+++ src/js/standard-paginator.js.coffee
@@ -18,7 +18,7 @@
         @numPages() <= @windowSize['left']

     buildPages: =>
-      @pages.push new PrevPage if @showNextPage()
+      @pages.push new PrevPage if @showPreviousPage()
       @buildWindow()
       @pages.push new NextPage if @showNextPage()


editor will be a plugin, for now force keybinding creation

--- src/editor.coffee
+++ src/editor.coffee
@@ -3,6 +3,8 @@
 File = require 'fs'
 Chrome = require 'chrome'
 Pane = require 'pane'
+
+{bindKey} = require 'keybinder'

 ace = require 'ace/ace'

@@ -36,6 +38,9 @@

   constructor: (args...) ->
     super(args...)
+
+    for shortcut, method of @keymap()
+      bindKey @, shortcut, method

     @ace = ace.edit "editor"
     @ace.setTheme require "ace/theme/twilight"


:memo: Remove delegate warnings

:memo: Remove delegate warnings

--- src/editor.coffee
+++ src/editor.coffee
@@ -314,16 +314,16 @@
   # {Delegates to: TextBuffer.getPath}
   getPath: -> @buffer.getPath()

-  # {Delegates to: TextBuffer.getText}
+  # Public: Returns a {String} representing the entire contents of the editor.
   getText: -> @buffer.getText()

-  # {Delegates to: TextBuffer.setText}
+  # Public: Replaces the entire contents of the buffer with the given {String}.
   setText: (text) -> @buffer.setText(text)

-  # {Delegates to: TextBuffer.getTextInRange}
+  # Public: Returns a {String} of text in the given {Range}.
   getTextInRange: (range) -> @buffer.getTextInRange(range)

-  # {Delegates to: TextBuffer.getLineCount}
+  # Public: Returns a {Number} representing the number of lines in the editor.
   getLineCount: -> @buffer.getLineCount()

   # Private: Retrieves the current {TextBuffer}.
@@ -347,16 +347,28 @@
   # {Delegates to: TextBuffer.getEofPosition}
   getEofBufferPosition: -> @buffer.getEofPosition()

-  # {Delegates to: TextBuffer.getLastRow}
+  # Public: Returns a {Number} representing the last zero-indexed buffer row
+  # number of the editor.
   getLastBufferRow: -> @buffer.getLastRow()

-  # {Delegates to: TextBuffer.rangeForRow}
+  # Public: Returns the range for the given buffer row.
+  #
+  # * row: A row {Number}.
+  # * options: An options hash with an `includeNewline` key.
+  #
+  # Returns a {Range}.
   bufferRangeForBufferRow: (row, options) -> @buffer.rangeForRow(row, options)

-  # {Delegates to: TextBuffer.lineForRow}
+  # Public: Returns a {String} representing the contents of the line at the
+  # given buffer row.
+  #
+  # * row - A {Number} representing a zero-indexed buffer row.
   lineForBufferRow: (row) -> @buffer.lineForRow(row)

-  # {Delegates to: TextBuffer.lineLengthForRow}
+  # Public: Returns a {Number} representing the line length for the given
+  # buffer row, exclusive of its line-ending character(s).
+  #
+  # row - A {Number} indicating the buffer row.
   lineLengthForBufferRow: (row) -> @buffer.lineLengthForRow(row)

   # {Delegates to: TextBuffer.scan}


:memo: Drop line about single line selections

--- src/editor.coffee
+++ src/editor.coffee
@@ -1194,8 +1194,6 @@
   #
   # This methods break apart all multi-line selections to create multiple
   # single-line selections that cumulatively cover the same original area.
-  #
-  # This method will not affect any single line selections.
   splitSelectionIntoLines: ->
     for selection in @getSelections()
       range = selection.getBufferRange()


Return a boolean like the comment says

Return a boolean like the comment says

--- src/editor.coffee
+++ src/editor.coffee
@@ -836,7 +836,7 @@
       continue if @displayBuffer.tokenizedBuffer.lineForScreenRow(bufferRow).isComment()
       if match = @buffer.lineForRow(bufferRow).match(/^[ \t]/)
         return match[0][0] is ' '
-    undefined
+    false

   # Public: Returns a {Boolean} indicating whether softTabs are enabled for this
   # editor.


Fix sealevel in skyline

Fix sealevel in skyline

--- src/lib/script_modules/level.coffee
+++ src/lib/script_modules/level.coffee
@@ -413,7 +413,7 @@
   changeSeaLevel: (offsetY) ->
     (sequence) =>
       @_verify(sequence)
-      @level.sealevelOffset = offsetY
+      @level.sealevelOffset = -offsetY
       level = @level
       Crafty.s('SeaLevel').setOffset(level.sealevelOffset)


Remove a stray console.log

Remove a stray console.log

--- app/lib/tutorial_steps.coffee
+++ app/lib/tutorial_steps.coffee
@@ -89,8 +89,6 @@
     nextOn: {} # This is handled by onEnter.

     onEnter: (tutorial, step) ->
-      console.log 'onEnter called', @
-
       doc = $(document)
       search = $('.animal-selector input[name="search"]')


Allow messenger to esape all messages. Off by default

--- src/coffee/messenger.coffee
+++ src/coffee/messenger.coffee
@@ -62,7 +62,7 @@
         else
             @$el.undelegate()
             @$el.unbind eventName
-
+
     undelegateEvents: () ->
         @jqoff ".delegateEvents#{this.cid}"

@@ -75,6 +75,7 @@
         hideAfter: 10
         scroll: true
         closeButtonText: "×"
+        escapeText: false

     initialize: (opts={}) ->
         @shown = false
@@ -208,7 +209,10 @@

             $message.append $cancel

-        $text = $ """
#{ opts.message }
"""
+        if opts.escapeText
+            $text = $('

').text(opts.message)
+        else
+            $text = $ """
#{ opts.message }
"""
         $message.append $text

         if opts.actions.length
@@ -389,7 +393,7 @@

     newMessage: (opts={}) ->
         opts.messenger = @
-
+
         _Message = Messenger.themes[opts.theme ? @options.theme]?.Message ? RetryingMessage

         msg = new _Message(opts)
@@ -669,7 +673,7 @@
         unless m_opts.returnsPromise
             for type, handler of handlers
                 old = opts[type]
-
+
                 opts[type] = handler

         msg._actionInstance = m_opts.action opts, args...
@@ -678,12 +682,12 @@
             msg._actionInstance.then(handlers.success, handlers.error)

         return msg
-
+
     # Aliases
     do: ActionMessenger::run
     ajax: (m_opts, args...) ->
         m_opts.action = $.ajax
-
+
         @run(m_opts, args...)

     expectPromise: (action, m_opts) ->
@@ -790,7 +794,7 @@
 $.extend Messenger,
     Message: RetryingMessage
     Messenger: ActionMessenger
-
+
     themes: Messenger.themes ? {}

 $.globalMessenger = window.Messenger = Messenger


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/follow.coffee
+++ src/follow.coffee
@@ -21,6 +21,8 @@
   width: 10
   height: 10
   velocity: Point(0, 0)
+
+enemy.include Follow

 # Make an enemy follow the player
 enemy.follow(player)


Fix check of actual node.js (and not a polyfill of process).

--- engine/node_fetch_file.coffee
+++ engine/node_fetch_file.coffee
@@ -2,7 +2,7 @@
 # This module allows fetch to load local files from file://
 # in node.js, electron and NW.js

-if window.process?.execPath
+if window.process?.execPath and process.execPath != '/'

     req = eval 'require'
     fs = req 'fs'


FIX: hidden text-fields actually hide again (for pointer events)

--- source/Art/Engine/Forms/SynchronizedDomOverlay.coffee
+++ source/Art/Engine/Forms/SynchronizedDomOverlay.coffee
@@ -81,10 +81,10 @@
     @_domElementWrapper.style.left    = "#{canvasLeft + clippedDrawAreaInAncestor.left}px"
     @_domElementWrapper.style.width   = "#{clippedDrawAreaInAncestor.w}px"
     @_domElementWrapper.style.height  = "#{clippedDrawAreaInAncestor.h}px"
+    @_domElementWrapper.style.display = if opacity == 0 then "none" else "block"

     # log "SynchronizedDomOverlay#_updateDomLayout: #{inspect opacity:opacity, area:r, scale:point sx, sy}"
     @_domElement.style.opacity  = opacity
-    @_domElement.style.display  = if opacity == 0 then "none" else "block"
     @_domElement.style.left     = "#{m.getLocationX() - clippedDrawAreaInAncestor.left}px"
     @_domElement.style.top      = "#{m.getLocationY() - clippedDrawAreaInAncestor.top}px"
     @_domElement.style.width    = "#{size.x}px"


Typo at user.coffee

Typo at user.coffee

--- api/users.coffee
+++ api/users.coffee
@@ -43,7 +43,7 @@
             return
         User.count where: teamId: value
         .then (ammount) ->
-            if ammount and ammount >= 10
+            if amount and amount >= 10
                 res.validationError fields: [
                     path: 'teamId'
                     message: 'L\'équipe a atteint la limite de joueurs'


add next beside req, res in the router handlers...

--- apis/v0p1.coffee
+++ apis/v0p1.coffee
@@ -43,12 +43,12 @@
 	req.svu.term = data.termsByCode[req.params.term]
 	next()

-studentsRouter.get '/select/:program', (req, res)->
+studentsRouter.get '/select/:program', (req, res, next)->
 	req.studentObject.get 'get_selectable_classes', {pid: req.svu.program.id}, (err, data)->
 		return next(err) if err
 		res.json({success: true, data: data})

-studentsRouter.get '/select/:program/:courseId', (req, res)->
+studentsRouter.get '/select/:program/:courseId', (req, res, next)->

 	opts= {
 		pid: req.svu.program.id,
@@ -76,7 +76,7 @@
 			]
 		}

-studentsRouter.post '/select/:program/:courseId', etabits.jsonMiddleware, (req, res)->
+studentsRouter.post '/select/:program/:courseId', etabits.jsonMiddleware, (req, res, next)->
 	opts= {
 		pid: parseInt(req.svu.program.id),
 		cid: parseInt(req.params.courseId)
@@ -86,7 +86,7 @@
 	req.studentObject.get 'choose_class', opts, (err, data)-> res.json({success: true})

-studentsRouter.get '/explore/:term/:program', (req, res)->
+studentsRouter.get '/explore/:term/:program', (req, res, next)->
 	#console.log data.programsByCode[req.params.program].id
 	req.studentObject.get 'progtermcourse', {pid: req.svu.program.id}, (err, data)->
 		return next(err) if err
@@ -95,7 +95,7 @@

 		res.json({success: true, data: data.courses})

-studentsRouter.get '/explore/:term/:program/:courseId', (req, res)->
+studentsRouter.get '/explore/:term/:program/:courseId', (req, res, next)->

 	opts = {
 		pid: req.svu.program.id
@@ -131,7 +131,7 @@
 		result.stud.getLoginRetObject (err, loginResult)->
 			res.send(loginResult)

-v0.get '/web', loadStudentFromToken, (req, res)->
+v0.get '/web', loadStudentFromToken, (req, res, next)->
 	res.send {
 		success: true
 		student: {


Fixed routing of login

Fixed routing of login

--- picoCTF-web/web/coffee/login.coffee
+++ picoCTF-web/web/coffee/login.coffee
@@ -1,18 +1,13 @@
 window.check_logged_in = ->
-  $.get("/api/isloggedin")
+  $.get("/api/user/isloggedin")
     .done (data) ->
       if data['status'] == 0
-        if (typeof(Storage) != "undefined")
-          sessionStorage.signInStatus = "notLoggedIn"
         console.log(data.message)
       else if data['status'] == 1
-        if (typeof(Storage) != "undefined")
-          sessionStorage.signInStatus = "loggedIn"
-        document.location.href = "problems.html"
+        document.location.href = "/problems"

 trigger_alert = (message)->
   $("#error-alert").hide().text(message).show().delay(1500).fadeOut()
-

 login = (e) ->
   e.preventDefault()


Remove unused IP property

Remove unused IP property

--- src/lib/IP.coffee
+++ src/lib/IP.coffee
@@ -17,7 +17,6 @@
   # Valid types: 'data', 'openBracket', 'closeBracket'
   constructor: (@type = 'data', @data = null, options = {}) ->
     @_isIP = true
-    @groups = [] # sync groups
     @scope = null # sync scope id
     @owner = null # packet owner process
     @clonable = false # cloning safety flag


Simplify entry point.

Simplify entry point.

--- app/index.coffee
+++ app/index.coffee
@@ -10,15 +10,12 @@
   constructor: ->
     super

-    @log('hello!')
-
-    window.onload = ->
-      paper.setup($('canvas')[0])
+    paper.setup($('canvas')[0])
+
+    new CanvasRenderer().install()
+    new Toolbox().install()
+
+    paper.view.draw()

-      new CanvasRenderer().install()
-      new Toolbox().install()
-
-      paper.view.draw()
-
 module.exports = App


add forge ajaxError triggering

add forge ajaxError triggering

--- app/index.coffee
+++ app/index.coffee
@@ -23,6 +23,7 @@
 Spine.Ajax.defaults.headers['X-Version'] = Config.version

 if Config.env=='ios'
+  # This monkey-patch is necessary for Android 2.2 where COR is having problems with Authorization headers
   $.ajax = (options) ->
     # console.log 'forge ajax', options
     dfd = jQuery.Deferred()
@@ -31,8 +32,13 @@
       dfd.resolve data

     options.error = (error) ->
-      # console.log "forge ajax reject", error
-      dfd.reject error
+      console.log "forge ajax reject", error
+      error.responseText = error.content
+      error.statusText = error.message
+      error.status = parseInt(error.statusCode)
+      # arguments: event, xhr, ajaxSettings, thrownError
+      dfd.reject event, error
+      $(document).trigger 'ajaxError', error

     forge.ajax options
     dfd.promise()


fix center of coords variation

fix center of coords variation

--- app/world.coffee
+++ app/world.coffee
@@ -116,8 +116,8 @@
       toY = to.cy
     else
       # introduce a slight variation in the 'to' coordinates
-      toX = to.cx + (Math.random() * 1.5 - 0.5)
-      toY = to.cy + (Math.random() * 1.5 - 0.5)
+      toX = to.cx + (Math.random() * 1.5 - 0.75)
+      toY = to.cy + (Math.random() * 1.5 - 0.75)

     path = @canvas.path("M#{from.cx},#{from.cy}")
                   .attr


Fixed the level session schema submitDate property.

--- server/levels/sessions/level_session_schema.coffee
+++ server/levels/sessions/level_session_schema.coffee
@@ -134,9 +134,8 @@
     default: false
     index:true

-  submitDate:
-    type: c.date
-      title: 'Submitted'
+  submitDate: c.date
+    title: 'Submitted'

   submittedCode:
     type: 'object'


avoid race condition loading underscore

avoid race condition loading underscore

make underscore.coffee angular module depend on libs, which depends on
underscore-1.3.3

Have avoided adding an extra dependency on underscore itself so that
the version number is not duplicated.  We could have a single
definition of underscore in the requirejs config, to keep the version
number in one place.

--- services/web/public/coffee/utils/underscore.coffee
+++ services/web/public/coffee/utils/underscore.coffee
@@ -1,2 +1,5 @@
-angular.module('underscore', []).factory '_', ->
-	return window._+define [
+	"libs"
+], () ->
+	angular.module('underscore', []).factory '_', ->
+		return window._


miscellaneous cleanup

miscellaneous cleanup

--- src/math3d.coffee
+++ src/math3d.coffee
@@ -24,92 +24,68 @@
 ###############################################################################

 if not document?
-    document = window.document
-
-math3d = (window.math3d ?= {})
+    document = @document
+
+math3d = (@math3d ?= {})

-trunc = (s, max_length) ->
-    if not s?
-        return s
+trunc = (str, max_length) ->
+    if not str?.length?
+        return str
     if not max_length?
         max_length = 1024
-    if s.length > max_length
-        s.slice(0, max_length-3) + "..."
-    else
-        s
-
-# Returns a new object with properties determined by those of obj1 and
-# obj2.  The properties in obj1 *must* all also appear in obj2.  If an
-# obj2 property has value "defaults.required", then it must appear in
-# obj1.  For each property P of obj2 not specified in obj1, the
-# corresponding value obj1[P] is set (all in a new copy of obj1) to
-# be obj2[P].
-defaults = (obj1, obj2, allow_extra) ->
-    if not obj1?
-        obj1 = {}
-    error = ->
+    if str.length > max_length then str.slice(0, max_length-3) + "..." else str
+
+# Returns a new object with properties determined by those of opts and
+# base.  The properties in opts *must* all also appear in base.  If an
+# base property has value "defaults.required", then it must appear in
+# opts.  For each property prop of base not specified in opts, the
+# corresponding value opts[prop] is set (all in a new copy of opts) to
+# be base[prop].
+defaults = (opts, base) ->
+    if not opts?
+        opts = {}
+    args = ->
         try
-            s = "(obj1=#{trunc(JSON.stringify(obj1),1024)}, obj2=#{trunc(JSON.stringify(obj2),1024)})"
-            #console.log s
-            return s
-        catch error
-            return ""
-    if typeof(obj1) isnt 'object'
+            "(opts=#{trunc JSON.stringify opts}, base=#{trunc JSON.stringify base})"
+        catch
+            ""
+    if typeof(opts) isnt 'object'
         # We put explicit traces before the errors in this function,
         # since otherwise they can be very hard to debug.
         console.trace()
-        throw "defaults -- TypeError: function takes inputs as an object #{error()}"
-    r = {}
-    for prop, val of obj2
-        if obj1.hasOwnProperty(prop) and obj1[prop]?
-            if obj2[prop] is defaults.required and not obj1[prop]?
+        throw "defaults -- TypeError: function takes inputs as an object #{args()}"
+    res = {}
+    for prop, val of base
+        if opts.hasOwnProperty(prop) and opts[prop]?
+            res[prop] = opts[prop]
+        else if val?  # only record not undefined properties
+            if val is defaults.required
                 console.trace()
-                throw "defaults -- TypeError: property '#{prop}' must be specified: #{error()}"
-            r[prop] = obj1[prop]
-        else if obj2[prop]?  # only record not undefined properties
-            if obj2[prop] is defaults.required
-                console.trace()
-                throw "defaults -- TypeError: property '#{prop}' must be specified: #{error()}"
+                throw "defaults -- TypeError: property '#{prop}' must be specified #{args()}"
             else
-                r[prop] = obj2[prop]
-    if not allow_extra
-        for prop, val of obj1
-            if not obj2.hasOwnProperty prop
-                console.trace()
-                throw "defaults -- TypeError: got an unexpected argument '#{prop}' #{error()}"
-    return r
+                res[prop] = val
+    for prop, val of opts
+        if not base.hasOwnProperty(prop)
+            console.trace()
+            throw "defaults -- TypeError: got an unexpected argument '#{prop}' #{args()}"
+    res

 # WARNING -- don't accidentally use this as a default:
 required = defaults.required = "__!!!!!!this is a required property!!!!!!__"

-component_to_hex = (c) ->
-    hex = c.toString 16
-    if hex.length is 1
-        "0" + hex
-    else
-        hex
+component_to_hex = (component) ->
+    (if component < 16 then "0" else "") + component.toString 16

 rgb_to_hex = (r, g, b) -> "#" + component_to_hex(r) + component_to_hex(g) + component_to_hex(b)

-_loading_threejs_callbacks = []
-
-math3d.threejs_src = "http://cdnjs.cloudflare.com/ajax/libs/three.js/r68/three.min.js"
-math3d.orbitcontrols_src = "OrbitControls.js"
-
 remove_element = (element) ->
-    if element is null
-        return
-    if (parent = element.parentElement) is null
-        return
-    parent.removeChild element
+    if element? and (parent = element.parentElement)?
+        parent.removeChild element

 loadScript = (script_src, callback) ->
     run_callback = true

     script = document.createElement 'script'
-    script.type = 'text/javascript'
-    script.async = true
-    script.src = script_src

     script.onload = ->
             remove_element script
@@ -122,7 +98,16 @@
                 run_callback = false
                 callback "error loading script #{script.src}"

+    script.type = 'text/javascript'
+    script.async = true
+    script.src = script_src
+
     document.head.appendChild script
+
+_loading_threejs_callbacks = []
+
+math3d.threejs_src = "http://cdnjs.cloudflare.com/ajax/libs/three.js/r68/three.min.js"
+math3d.orbitcontrols_src = "OrbitControls.js"

 load_threejs = (callback) ->
     if THREE?.Scene? and THREE?.OrbitControls?
@@ -163,7 +148,7 @@
         # get the best-possible THREE.js renderer (once and for all)
         # based on Detector.js's webgl detection
         try
-            if window.WebGLRenderingContext
+            if @WebGLRenderingContext
                 canvas = document.createElement 'canvas'
                 if canvas.getContext('webgl') or canvas.getContext('experimental-webgl')
                     dynamic_renderer_type = 'webgl'
@@ -794,7 +779,6 @@

         @render_scene true

-
     animate: (opts = {}) ->
         opts = defaults opts,
             fps       : undefined
@@ -845,7 +829,6 @@
         else
             f()

-
     render_scene: (force = false) ->
         if @renderer_type is 'static'
             console.log 'render static -- todo'
@@ -896,18 +879,17 @@
                 c = z[1]
                 z[0].scale.set s*c, s*c, s*c

-
 math3d.render_3d_scene = (opts) ->
     opts = defaults opts,
-        scene   : required   # {opts:?, obj:?} or url from which to download (via ajax) a JSON string that parses to {opts:?,obj:?}
-        element : required    # DOM element
-        cb      : undefined   # cb(err, scene object)
+        scene    : required    # {opts:?, obj:?} or url from which to download (via ajax) a JSON string that parses to {opts:?,obj:?}
+        element  : required    # DOM element
+        callback : undefined   # callback(error, scene object)
     # Render a 3-d scene
     #console.log("render_3d_scene: url='#{opts.url}'")

     scene_obj = undefined
     async.series([
-        (cb) ->
+        (callback) ->
             switch typeof(opts.scene)
                 when 'string'
                     $.ajax(
@@ -916,38 +898,31 @@
                         success : (data) ->
                             try
                                 opts.scene = JSON.parse data
-                                cb()
-                            catch err
-                                cb err
+                                callback()
+                            catch error
+                                callback error
                     ).fail ->
-                        cb "error downloading #{opts.scene}"
+                        callback "error downloading #{opts.scene}"
                 when 'object'
-                    cb()
+                    callback()
                 else
-                    cb "bad scene value: #{opts.scene}"
-        (cb) ->
+                    callback "bad scene value: #{opts.scene}"
+        (callback) ->
             # do this initialization *after* we create the 3d renderer
-            init = (err, scene) ->
-                if err
-                    cb err
+            init = (error, scene) ->
+                if error
+                    callback err
                 else
                     scene_obj = scene
                     scene.init()
                     if opts.scene.obj?
                         scene.add_3dgraphics_obj obj : opts.scene.obj
                     scene.init_done()
-                    cb()
+                    callback()
             # create the 3d renderer
-            opts.scene.opts ?= {}
             opts.scene.opts.callback = init

             obj = new Math3dThreeJS opts.element, opts.scene.opts
-    ], (err) ->
-        opts.cb? err, scene_obj
+    ], (error) ->
+        opts.callback? error, scene_obj
     )
-
-if $?
-    # jQuery plugin for making a DOM object into a 3d renderer
-    $.fn.math3d = (opts = {}) ->
-        @each ->
-            new Math3dThreeJS @, opts


refactor some camera + controls code

refactor some camera + controls code

--- src/math3d.coffee
+++ src/math3d.coffee
@@ -197,7 +197,6 @@
             renderer        : undefined # 'webgl' or 'canvas' or undefined to choose best
             background      : [1,1,1]
             spin            : false     # if true, image spins by itself when mouse is over it.
-            camera_distance : 10
             aspect_ratio    : [1, 1, 1] # a triple [x,y,z] of length three, which scales the x,y,z coordinates of everything by the given values.
             stop_when_gone  : undefined # if given, animation, etc., stops when this html element (not jquery!) is no longer in the DOM
             frame           : undefined # frame options
@@ -249,7 +248,6 @@
             @opts.height ?= @opts.width*2/3

             @setDynamicRenderer()
-            @init_orbit_controls()
             @init_on_mouseover()

             # add a bunch of lights
@@ -262,7 +260,10 @@
         @computeDimensions()

         @setFrame()
-        @updateCamera()
+        @setCamera()
+        @setOrbitControls()
+
+        @runChangeHooks()

         @render_scene()

@@ -347,46 +348,6 @@
             type    : 'png'      # 'png' or 'jpeg' or 'webp' (the best)
             quality : undefined   # 1 is best quality; 0 is worst; only applies for jpeg or webp
         @renderer.domElement.toDataURL "image/#{opts.type}", opts.quality
-
-    init_orbit_controls: ->
-        if not @camera?
-            @add_camera distance: @opts.camera_distance
-
-        # set up camera controls
-        @controls = new OrbitControls @camera, @renderer.domElement
-        @controls.damping = 2
-        @controls.noKeys = true
-        @controls.zoomSpeed = 0.4
-        if @opts.spin
-            if typeof @opts.spin is "boolean"
-                @controls.autoRotateSpeed = 2.0
-            else
-                @controls.autoRotateSpeed = @opts.spin
-            @controls.autoRotate = true
-
-        up = new THREE.Vector3()
-        @controls.addEventListener 'change', =>
-            if @renderer_type is 'dynamic'
-                for hook in @changeHooks
-                    hook()
-
-                @renderer.render @scene, @camera
-
-    add_camera: (opts) ->
-        opts = defaults opts,
-            distance : 10
-
-        if @camera?
-            return
-
-        view_angle = 45
-        aspect     = @opts.width/@opts.height
-
-        @camera    = new THREE.PerspectiveCamera view_angle, aspect
-        @scene.add @camera
-        @camera.position.set opts.distance, opts.distance, opts.distance
-        @camera.lookAt @scene.position
-        @camera.up = new THREE.Vector3 0, 0, 1

     init_light: (color= 0xffffff) ->
         ambient = new THREE.AmbientLight(0x404040)
@@ -698,6 +659,8 @@
         @maxDim = Math.max dim.x, dim.y, dim.z
         @minDim = Math.min dim.x, dim.y, dim.z

+        @center = @rescale @boundingBox.geometry.boundingBox.center()
+
     # always call this after adding things to the scene to make sure track
     # controls are sorted out, etc. The client should never have to worry
     # about calling this
@@ -807,18 +770,49 @@
                 addLabel [avg.x, max.y, min.z], "x = #{format avg.x}"
                 addLabel [min.x, max.y, min.z], format(min.x)

-    updateCamera: ->
-        center = @rescale @boundingBox.geometry.boundingBox.center()
-
-        @camera.lookAt center
-        @controls.target = center
+    setCamera: ->
+        view_angle = 45
+        aspect     = @opts.width/@opts.height
+        near = @minDim/4
+        far = @maxDim*16
+
+        @camera    = new THREE.PerspectiveCamera view_angle, aspect, near, far
+        @camera.up = new THREE.Vector3 0, 0, 1
+
+        @camera.lookAt @center

         @camera.position.set 1.5, 1.5, 0.75
-        @camera.position.multiplyScalar(@maxDim).add center
-
-        @camera.far = @maxDim*16
-        @camera.near = @minDim/4
+        @camera.position.multiplyScalar(@maxDim).add @center
+
         @camera.updateProjectionMatrix()
+
+        @scene.add @camera
+
+    runChangeHooks: ->
+        if @renderer_type is 'dynamic'
+            for hook in @changeHooks
+                hook()
+
+            @renderer.render @scene, @camera
+
+    setOrbitControls: ->
+        # set up camera controls
+        @controls = new OrbitControls @camera, @renderer.domElement
+
+        @controls.target = @center
+
+        @controls.damping = 2
+        @controls.noKeys = true
+        #@controls.zoomSpeed = 0.6
+
+        if @opts.spin
+            if typeof @opts.spin is "boolean"
+                @controls.autoRotateSpeed = 2.0
+            else
+                @controls.autoRotateSpeed = @opts.spin
+            @controls.autoRotate = true
+
+        @controls.addEventListener 'change', => @runChangeHooks()

     animate: (opts = {}) ->
         opts = defaults opts,


extracting point class

extracting point class

--- src/matrix.coffee
+++ src/matrix.coffee
@@ -26,208 +26,6 @@
 ###
 ( ->
   ###*
-  Create a new point with given x and y coordinates. If no arguments are given
-  defaults to (0, 0).
-  @name Point
-  @param {Number} [x]
-  @param {Number} [y]
-  @constructor
-  ###
-  Point = (x, y) ->
-    ###*
-    The x coordinate of this point.
-    @name x
-    @fieldOf Point#
-    ###
-    x: x || 0
-    ###*
-    The y coordinate of this point.
-    @name y
-    @fieldOf Point#
-    ###
-    y: y || 0
-    ###*
-    Adds a point to this one and returns the new point. You may
-    also use a two argument call like point.add(x, y)
-    to add x and y values without a second point object.
-    @name add
-    @methodOf Point#
-
-    @param {Point} other The point to add this point to.
-    @returns A new point, the sum of both.
-    @type Point
-    ###
-    add: (first, second) ->
-      if second
-        Point(this.x + first, this.y + second)
-      else
-        Point(this.x + first.x, this.y + first.y)
-
-    ###*
-    Subtracts a point to this one and returns the new point.
-    @name subtract
-    @methodOf Point#
-
-    @param {Point} other The point to subtract from this point.
-    @returns A new point, this - other.
-    @type Point
-    ###
-    subtract: (other) ->
-      Point(this.x - other.x, this.y - other.y)
-
-    ###*
-     * Scale this Point (Vector) by a constant amount.
-     * @name scale
-     * @methodOf Point#
-     *
-     * @param {Number} scalar The amount to scale this point by.
-     * @returns A new point, this * scalar.
-     * @type Point
-    ###
-    scale: (scalar) ->
-      Point(this.x * scalar, this.y * scalar)
-
-    ###*
-    Floor the x and y values, returning a new point.
-
-    @name floor
-    @methodOf Point#
-    @returns A new point, with x and y values each floored to the largest previous integer.
-    @type Point
-    ###
-    floor: ->
-      Point(this.x.floor(), this.y.floor())
-
-    ###*
-     * Determine whether this point is equal to another point.
-     * @name equal
-     * @methodOf Point#
-     *
-     * @param {Point} other The point to check for equality.
-     * @returns true if the other point has the same x, y coordinates, false otherwise.
-     * @type Boolean
-    ###
-    equal: (other) ->
-      this.x == other.x && this.y == other.y
-
-    ###*
-    Calculate the magnitude of this Point (Vector).
-    @name magnitude
-    @methodOf Point#
-
-    @returns The magnitude of this point as if it were a vector from (0, 0) -> (x, y).
-    @type Number
-    ###
-    magnitude: ->
-      Point.distance(Point(0, 0), this)
-
-    ###*
-    Returns the direction in radians of this point from the origin.
-    @name direction
-    @methodOf Point#
-
-    @type Number
-    ###
-    direction: ->
-      Math.atan2(this.y, this.x)
-
-    ###*
-    Calculate the dot product of this point and another point (Vector).
-    @name dot
-    @methodOf Point#
-
-    @param {Point} other The point to dot with this point.
-    @returns The dot product of this point dot other as a scalar value.
-    @type Number
-    ###
-    dot: (other) ->
-      this.x * other.x + this.y * other.y
-
-    ###*
-    Calculate the cross product of this point and another point (Vector).
-    Usually cross products are thought of as only applying to three dimensional vectors,
-    but z can be treated as zero. The result of this method is interpreted as the magnitude
-    of the vector result of the cross product between [x1, y1, 0] x [x2, y2, 0]
-    perpendicular to the xy plane.
-    @name cross
-    @methodOf Point#
-
-    @param {Point} other The point to cross with this point.
-    @returns The cross product of this point with the other point as scalar value.
-    @type Number
-    ###
-    cross: (other) ->
-      this.x * other.y - other.x * this.y
-
-    ###*
-    The norm of a vector is the unit vector pointing in the same direction. This method
-    treats the point as though it is a vector from the origin to (x, y).
-    @name norm
-    @methodOf Point#
-
-    @returns The unit vector pointing in the same direction as this vector.
-    @type Point
-    ###
-    norm: (length=1.0) ->
-      this.scale(length/this.length())
-
-    ###*
-    Computed the length of this point as though it were a vector from (0,0) to (x,y)
-    @name length
-    @methodOf Point#
-
-    @returns The length of the vector from the origin to this point.
-    @type Number
-    ###
-    length: ->
-      Math.sqrt(this.dot(this))
-
-    ###*
-    Computed the Euclidean between this point and another point.
-    @name distance
-    @methodOf Point#
-
-    @param {Point} other The point to compute the distance to.
-    @returns The distance between this point and another point.
-    @type Number
-    ###
-    distance: (other) ->
-      Point.distance(this, other)
-
-  ###*
-   * @param {Point} p1
-   * @param {Point} p2
-   * @type Number
-   * @returns The Euclidean distance between two points.
-  ###
-  Point.distance = (p1, p2) ->
-    Math.sqrt(Math.pow(p2.x - p1.x, 2) + Math.pow(p2.y - p1.y, 2))
-
-  ###*
-   * Construct a point on the unit circle for the given angle.
-   *
-   * @param {Number} angle The angle in radians
-   * @type Point
-   * @returns The point on the unit circle.
-  ###
-  Point.fromAngle = (angle) ->
-    Point(Math.cos(angle), Math.sin(angle))
-
-  ###*
-   * If you have two dudes, one standing at point p1, and the other
-   * standing at point p2, then this method will return the direction
-   * that the dude standing at p1 will need to face to look at p2.
-   * @param {Point} p1 The starting point.
-   * @param {Point} p2 The ending point.
-   * @returns The direction from p1 to p2 in radians.
-  ###
-  Point.direction = (p1, p2) ->
-    Math.atan2(
-      p2.y - p1.y,
-      p2.x - p1.x
-    )
-
-  ###*
    *

    *  _        _
    * | a  c tx  |
@@ -498,6 +296,6 @@
   Matrix.VERTICAL_FLIP = Matrix(1, 0, 0, -1)

   # Export to window
-  window["Point"] = Point
   window["Matrix"] = Matrix
-)()+)()
+


removes trailing ';'

removes trailing ';'

--- js/libs/circle/custom-bindings.coffee
+++ js/libs/circle/custom-bindings.coffee
@@ -107,7 +107,7 @@
     element.textContent = value

   if (ieVersion >= 9)
-    element.style.display = element.style.display;
+    element.style.display = element.style.display

 ko.bindingHandlers.money =
   update: (el, valueAccessor) =>


Add fixes on drag&drop

Add fixes on drag&drop

--- BackofficeBundle/Resources/public/coffee/areaView.coffee
+++ BackofficeBundle/Resources/public/coffee/areaView.coffee
@@ -4,6 +4,7 @@
   events:
     "click i#none": "clickButton"
     "click i.block-remove": "confirmRemoveBlock"
+    "sortupdate ul.ui-model-blocks": 'sendBlockData'

   initialize: (options) ->
     @area = options.area
@@ -79,9 +80,11 @@
     )
     $("ul.ui-model-blocks", @el).append blockView.render().el

-  sendBlockData: ->
+  sendBlockData: (event)->
     if $("ul.ui-model-areas", @el).length == 0
-      blocks = $("ul.resizable-" + @cid, @el).children()
+      ul = $(event.target)
+      refreshUl ul
+      blocks = ul.children()
       blockData = []
       for block in blocks
         if $('div[data-node-id]', block).length > 0


fix: unpipe connection on close

fix: unpipe connection on close

--- app/index.coffee
+++ app/index.coffee
@@ -19,5 +19,13 @@
   req.pipe bitbucket
   bitbucket.pipe res

+  # End the connection
+  req.on 'end', ->
+    console.log "*** end: #{url}"
+    req.unpipe()
+  req.on 'close', ->
+    console.log "*** close: #{url}"
+    req.unpipe()
+
 app.listen PORT
 console.log "Listening on port #{PORT}"


Add linear creative-filtering to videoClickTrackingURLTemplates parser

--- src/parser.coffee
+++ src/parser.coffee
@@ -109,7 +109,8 @@

                                 if ad.videoClickTrackingURLTemplates?
                                     for creative in wrappedAd.creatives
-                                        creative.videoClickTrackingURLTemplates = creative.videoClickTrackingURLTemplates.concat ad.videoClickTrackingURLTemplates
+                                        if creative.type is 'linear'
+                                            creative.videoClickTrackingURLTemplates = creative.videoClickTrackingURLTemplates.concat ad.videoClickTrackingURLTemplates

                                 response.ads.splice index, 0, wrappedAd


parser: complain about garbage at the end of file

--- src/parser.coffee
+++ src/parser.coffee
@@ -95,4 +95,7 @@
     else if consume '{' then (b = parseBody(); demand '}'; ['lambda', b])
     else parserError "Encountered unexpected token of type '#{token.type}'"

-  parseBody()
+  result = parseBody()
+  #     'hello'} ⍝ fails 'ParserError'
+  demand 'eof'
+  result


Decouple server from ChatServer.

Decouple server from ChatServer.

--- src/server.coffee
+++ src/server.coffee
@@ -276,15 +276,6 @@
       channels: {}
       users: []

-    # Create the server and connection callback
-    @server = net.createServer @connect
-
-    # Make the server listen on the specified host and port
-    host = config.get 'server.host'
-    port = config.get 'server.port'
-    @server.listen port, host, ->
-      log.info "Listening on #{host}:#{port}"
-
   connect: (socket) =>
     log.info "New connection from #{socket.remoteAddress}"

@@ -356,6 +347,7 @@
     return unless chans[channel]?
     chans[channel] = _.without chans[channel], name
     @emit 'channel', channel, "user has left chat: #{name}"
+    log.info "#{name} left #{channel}"

   ###
   Handler for a channel message
@@ -390,7 +382,19 @@
       if chans[chan].length is 0
         delete chans[chan]

-# Create our server an export it, just for fun
-module.exports = new ChatServer()
-
-
+# Export the ChatServer class
+exports.ChatServer = ChatServer
+
+# Create our server instance
+chat_server = new ChatServer()
+
+# Create the server and connection callback
+server = net.createServer chat_server.connect
+
+# Make the server listen on the specified host and port
+host = config.get 'server.host'
+port = config.get 'server.port'
+server.listen port, host, ->
+  log.info "Listening on #{host}:#{port}"
+
+


Use consistent highlighting for quads/quad-quotes

Fixes #5.

--- grammars/apl.cson
+++ grammars/apl.cson
@@ -665,10 +665,11 @@

 	# System variables
 	sysvars:
-		match: "(⎕)[A-Za-z]*"
+		match: "(?:(⎕)|(⍞))[A-Za-z]*"
 		name: "support.system.variable.apl"
 		captures:
 			1: {name: "punctuation.definition.quad.apl"}
+			2: {name: "punctuation.definition.quad-quote.apl"}

 	# Command arguments


Classify "move" as a Pic primitive

Classify "move" as a Pic primitive

The language appendix in CSTR #116 classifies "move" as a primitive.

--- grammars/pic.cson
+++ grammars/pic.cson
@@ -31,7 +31,7 @@
 			{include: "#escaped-newline"}
 			{include: "#operators"}
 			{include: "#punctuation"}
-			{include: "#shapes"}
+			{include: "#primitives"}
 			{include: "#attributes"}
 			{include: "#globals"}
 			{include: "#function-call"}
@@ -45,7 +45,7 @@
 		name: "variable.other.property.$1.pic"
 		match: """(?x)\\b
 			(cw|dashed|diameter|diam|dotted|down|height|ht|invisible
-			|invis|left|move|radius|rad|right|same|up|width|wid)\\b"""
+			|invis|left|radius|rad|right|same|up|width|wid)\\b"""

 	# Instance backreferences: "1st, last, 2nd last, 8th", etc
@@ -181,6 +181,14 @@
 			{match: "[-/+*%^]",   name: "keyword.operator.arithmetic.pic"}
 		]

+
+	# Shape primitives
+	primitives:
+		name: "keyword.function.pic"
+		match: "\\b(box|line|arrow|circle|ellipse|arc|move|spline)\\b"
+		captures: 0: name: "entity.function.name.pic"
+
+
 	# Punctuation
 	punctuation:
 		patterns: [
@@ -195,13 +203,6 @@
 		]

-	# Primitives
-	shapes:
-		name: "keyword.function.pic"
-		match: "\\b(box|line|arrow|circle|ellipse|arc|spline)\\b"
-		captures: 0: name: "entity.function.name.pic"
-
-
 	# "Double-quoted string"
 	string:
 		name: "string.quoted.double.pic"


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/number_extensions.coffee
+++ src/number_extensions.coffee
@@ -90,7 +90,7 @@
   n = this

   n.times (i) ->
-    block(Point.fromAngle((i/n).turns))
+    block(Point.fromAngle((i/n).turns), i)

 ###*
 Returns a number whose value is limited to the given range.


Escape symbols for regex

Escape symbols for regex

--- vendor/assets/javascripts/fustrate/components/autocomplete.coffee
+++ vendor/assets/javascripts/fustrate/components/autocomplete.coffee
@@ -128,11 +128,13 @@
     $ source.item.call(@, datum, @value)
       .data datum: datum
       .get(0)
-
+
   highlight: (text) =>
     return '' unless text
+
+    escaped = @value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')

-    text.replace RegExp("(#{@value.split(/\s+/).join('|')})", 'gi'),
+    text.replace RegExp("(#{escaped.split(/\s+/).join('|')})", 'gi'),
                  '<mark>$&</mark>'

   replace: (text) =>


Fixed patch from master for context.fail sourcing

--- testify.coffee
+++ testify.coffee
@@ -199,7 +199,16 @@
     @done()

   fail: (error) ->
-    if error.name == "AssertionError" || error.constructor == String
+    if error.constructor == String
+      process.stdout.write "F".red
+      # create fake error
+      throwaway = new Error(error)
+      message = error.toString()
+      error =
+        name: "AssertionError"
+        stack: throwaway.stack.split("\n").slice(1).join("\n")
+        toString: -> message
+    else if error.name == "AssertionError"
       process.stdout.write "F".red
     else
       process.stdout.write "E".yellow
@@ -235,15 +244,18 @@
       else if test.failed == false
         line = indent + test.name.green
       else if test.failed.constructor == String || test.failed.name == "AssertionError"
-        line = indent + "#{test.name} ( #{test.failed} )".red
+        line = indent + "#{test.name} ( #{test.failed.toString()} )".red
       else
-        line = indent + "#{test.name} ( #{test.failed} )".yellow
+        line = indent + "#{test.name} ( #{test.failed.toString()} )".yellow
       console.log(line)
       if test.failed?.stack
         where = test.failed.stack.split("\n")[1]
         regex = /\((.*)\)/
         match = regex.exec(where)
-        console.log "#{indent}    #{match[1]}"
+        try
+          console.log "#{indent}    #{match[1]}"
+        catch error
+          console.log "#{indent}    #{where.slice(7)}"

     console.log()


ensureUniquenessOfRoleRelationship: move error handling to the top

--- workers/social/lib/social/models/group/index.coffee
+++ workers/social/lib/social/models/group/index.coffee
@@ -969,8 +969,8 @@

     Relationship.count selector, (err, count)->
       if err then callback err
-      else if count is 0 then callback null
-      else callback new KodingError 'This relationship already exists'
+      else if count > 0 then callback new KodingError 'This relationship already exists'
+      else callback null

   oldAddMember = @::addMember
   addMember:(target, options, callback)->


[loadingFree] prefetching for guests enabled

--- workers/social/lib/social/render/scriptblock.coffee
+++ workers/social/lib/social/render/scriptblock.coffee
@@ -6,10 +6,6 @@

   options.intro   ?= no
   options.client or= {}
-  options.client.context or= {}
-  options.client.context.group or= "koding"
-
-

   prefetchedFeeds = {}
   {bongoModels, client, intro} = options
@@ -97,11 +93,15 @@
   # todo  - implement this prefetching for all groups

   # do not fetch activity feed for unregistered users
-  return generateScript()  if client?.connection?.delegate?.type isnt "registered"
+  # do fetch them for better ux for the ones who are gonna sign up or sign in.
+  # return generateScript()  if client?.connection?.delegate?.type isnt "registered"

   # fetch members function returns only members of the given group(context)
   # we can return this result also
+
+  # debugger
+
   queue.push ->
     fetchMembersFromGraph (err, members)->
       prefetchedFeeds['members.main'] = members  if members


Delete operations from s3 buckets

Delete operations from s3 buckets

For any operation that is externally stored, remove its operation from
the bucket.

--- src/server/db/amazon.coffee
+++ src/server/db/amazon.coffee
@@ -469,6 +469,28 @@

             operations_rw_queue.push((c) ->
               db.DeleteItem(request, c)
+            , 'delete Operation('+item.doc.S+'-'+item.v.N+')', cb)
+          (error)->
+            if error?
+              cb(error, null)
+            else
+              cb(null, true)
+          )
+      ]
+
+      delete_operations_s3: ['list_operations', (cb, results) ->
+        return cb(null, {}) if results.list_operations.Body.Count == 0
+
+        async.forEachSeries(results.list_operations.Body.Items,
+          (item, cb) ->
+            return cb(null, null) unless item.e?
+
+            params =
+              BucketName: snapshots_bucket
+              ObjectName: item.doc.S+'-'+item.v.N+'.operation'
+
+            s3_rw_queue.push((c) ->
+              s3.DeleteObject(params, c)
             , 'delete Operation('+item.doc.S+'-'+item.v.N+')', cb)
           (error)->
             if error?


nothing major

nothing major

--- dechat.cjsx
+++ dechat.cjsx
@@ -1,7 +1,5 @@
 React = require('react')
-
-# var comm = new Icecomm('4Mt09Xq/gyUee2TyDE.qC1.Lg2cXec.ZWBq3AGy32ZnDpsnP0.');
-
+ChatAPI = require('./icecomm_wrapper.coffee')

 DeChat = React.createClass


Added `private` to `tail.coffee`.

Added `private` to `tail.coffee`.

--- bin/tail.coffee
+++ bin/tail.coffee
@@ -121,7 +121,7 @@
     date[2..5] = (parseInt(match[i], 10) for i in [2..5])
     date = createDate.apply(null, date)
     [ host, rest ] = match[6..7]
-    if match = /^((?:public|worker|janitor)[_\w]*)\[(\d+)\]:\s+(.*?)(\s+{.*)?$/.exec(rest)
+    if match = /^((?:private|public|worker|janitor)[_\w]*)\[(\d+)\]:\s+(.*?)(\s+{.*)?$/.exec(rest)
       [ program, pid, message, json ] = match[1..4]
       record = { date, host, program, pid, message }
       if json


Added license to top of client-side JavaScript.  Uglify removes this, though- need to work on that.

--- prod/src/coffee-script/intro.coffee
+++ prod/src/coffee-script/intro.coffee
@@ -1,5 +1,23 @@
 ###
- * 2011 traphicam.com
+Copyright (c) 2011 Kris Molendyke
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
 ###

 # jQuery influenced self-executing function wrapper


templates: gulp: add test support with puppeteer and mocha

--- app/templates/gulpfile.coffee
+++ app/templates/gulpfile.coffee
@@ -1,5 +1,6 @@
 gulp            = require 'gulp'
 gutil           = require 'gulp-util'
+mocha           = require 'gulp-spawn-mocha'
 stylus          = require 'gulp-stylus'
 uglify          = require 'gulp-uglify'
 streamify       = require 'gulp-streamify'
@@ -17,8 +18,9 @@
 # kind of SPA mode.
 # see:
 # https://github.com/BrowserSync/browser-sync/issues/204#issuecomment-60410751
-historyFallback = require('connect-history-api-fallback')
-
+historyFallback = require 'connect-history-api-fallback'
+
+openApp         = yes
 globalBundler   = null

 paths           =
@@ -39,6 +41,9 @@
   images        :
     source      : './app/images/*'
     destination : './dist/images/'
+  test          :
+    app         : './dist/**/*'
+    cases       : './test/*.coffee'

 handleError = (err) ->
@@ -117,10 +122,16 @@
     .pipe browserSync.reload stream: yes

-gulp.task 'server', [ 'compile-scripts' ], ->
+gulp.task 'server', [ 'compile' ], ->
+
+  gulp.start [ 'run-server' ]
+
+
+gulp.task 'run-server', ->

   browserSync.init
     notify       : no
+    open         : openApp
     port         : 9000
     server       :
       baseDir    : './dist'
@@ -149,22 +160,62 @@
     .pipe gulp.dest './dist/css/'

-gulp.task 'clean', (cb) ->
-
-  rimraf './dist', cb
-
-
-gulp.task 'production', [ 'clean' ], ->
-
-  production = yes
-
-  gulp.start 'build', ->
-    gutil.log 'Building for production is completed,
-               you can now deploy ./dist folder'
-
+gulp.task 'clean', (cb) -> rimraf './dist', cb
+
+gulp.task 'enable-test-watch', [ 'default' ], ->
+
+  gulp.watch paths.test.app,   [ 'single-test' ]
+  gulp.watch paths.test.cases, [ 'single-test' ]
+
+  gulp.start 'single-test'
+
+gulp.task 'enable-prod', -> production = yes
+
+gulp.task 'disable-open', -> openApp = no
+
+gulp.task 'prod-build', [ 'enable-prod', 'serve-only', 'test' ]
+
+gulp.task 'production', [ 'prod-build' ], ->
+
+  gutil.log 'Building for production is completed,
+             you can now deploy ./dist folder'
+
+gulp.task 'test', [ 'disable-open', 'serve-only' ], (done) ->
+
+  gulp
+    .src ['./test/*.coffee'], read: no
+    .pipe mocha
+      reporter: 'list'
+      require: 'coffeescript/register'
+
+    .on 'end', ->
+      browserSync.exit()
+
+    .once 'error', ->
+      @emit 'end'
+
+gulp.task 'single-test', ->
+
+  gulp
+    .src ['./test/*.coffee'], read: no
+    .pipe mocha
+      reporter: 'list'
+      require: 'coffeescript/register'
+
+    .once 'error', ->
+      @emit 'end'
+
+
+gulp.task 'test-watch', [ 'enable-test-watch' ]
+
+gulp.task 'compile', [ 'compile-vendors', 'compile-scripts' ]

 gulp.task 'build',   [
-  'compile-scripts', 'styles', 'entry-point', 'export-kd', 'images'
+  'compile', 'styles', 'entry-point', 'export-kd', 'images'
+]
+
+gulp.task 'serve-only', [
+  'build', 'server'
 ]

 gulp.task 'default', [


remove reload on php file changes

remove reload on php file changes

--- app/templates/gulpfile.coffee
+++ app/templates/gulpfile.coffee
@@ -150,7 +150,6 @@
 gulp.task "watch", ["sass", "coffee", "browser-sync"], () ->
   gulp.watch "#{conf.path.pvt.scss}/**/*.scss", ["sass"]
   gulp.watch "#{conf.path.pvt.coffee}/**/*.coffee", ["coffee", "bs-reload"]
-  gulp.watch "#{conf.path.pvt.php}/**/*.php", ["bs-reload"]
   gulp.watch "#{conf.path.pvt.views}/**/*.html.php", ["bs-reload"]


Update bird row count

Update bird row count

--- app/controllers/HomeController.coffee
+++ app/controllers/HomeController.coffee
@@ -21,7 +21,7 @@

     # Use per-row transcription data, rather than page data for Ornithology collection. This is a hack until I figure out a better solution
     if Project.current
-      rowClassifications = 265084 # As of 2014/09/17
+      rowClassifications = 278909 # As of 2014/09/22
       ornithologyPageClassificationCount = Project.current.groups['5266c83a3ae740d5e3000002']?.classification_count || 0
       transcriptions = transcriptions - ornithologyPageClassificationCount + rowClassifications


Kloud: keep track of info calls for future reference

--- client/Main/kite/kites/kloud.coffee
+++ client/Main/kite/kites/kloud.coffee
@@ -21,6 +21,7 @@
     super options
     @requestingInfo = KD.utils.dict()
     @needsRequest   = KD.utils.dict()
+    @kloudCalls     = KD.utils.dict()

   # first info request sends message to kite requesting info
   # subsequent info requests while the first request is pending
@@ -69,6 +70,11 @@

   askInfoFromKloud: (machineId, currentState) ->

+    @kloudCalls[machineId] ?= 0
+    @kloudCalls[machineId]++
+
+    info "[kloud:info] call count for [#{machineId}] is #{@kloudCalls[machineId]}"
+
     @tell 'info', { machineId }

       .then (info) =>


Kloud: don't keep track call count

Kloud: don't keep track call count

--- client/Main/kite/kites/kloud.coffee
+++ client/Main/kite/kites/kloud.coffee
@@ -21,7 +21,6 @@
     super options
     @requestingInfo = KD.utils.dict()
     @needsRequest   = KD.utils.dict()
-    @kloudCalls     = KD.utils.dict()

   # first info request sends message to kite requesting info
   # subsequent info requests while the first request is pending
@@ -90,11 +89,6 @@

   askInfoFromKloud: (machineId, currentState) ->

-    @kloudCalls[machineId] ?= 0
-    @kloudCalls[machineId]++
-
-    info "[kloud:info] call count for [#{machineId}] is #{@kloudCalls[machineId]}"
-
     KD.remote.api.DataDog.increment "KloudInfo", noop

     @tell 'info', { machineId }


Ability initialized on client pokemon initialize and species change

--- client/app/js/models/pokemon.coffee
+++ client/app/js/models/pokemon.coffee
@@ -1,6 +1,7 @@
 class @Pokemon extends Backbone.Model
   defaults: =>
     name: 'Bulbasaur'
+    item: ""
     moves: []
     ivs:
       hp: 31
@@ -16,6 +17,13 @@
       specialAttack: 0
       specialDefense: 0
       speed: 0
+
+  initialize: (attributes={}) ->
+    # Set to default ability when the species changes
+    @on 'change:name', =>
+      @set('ability', @getAbilities()[0])
+
+    @set('ability', @getAbilities()[0])  unless attributes.ability

   getSpecies: ->
     FormeData[@get('name')].default


More comment

More comment

--- extension/packages/vim.coffee
+++ extension/packages/vim.coffee
@@ -33,6 +33,12 @@
     # Esc key is not suppressed, and passed to the browser in `normal` mode.
     # Here we compare against the mode that was active before the key was processed
     # because processing the command may change the mode.
+    #
+    # Not suppressing Esc allows for stopping the loading of the page as well as closing many custom
+    # dialogs (and perhaps other things -- Esc is a very commonly used key). There are two reasons we
+    # might suppress it in other modes. If some custom dialog of a website is open, we should be able to
+    # cancel hint markers on it without closing it. Secondly, otherwise cancelling hint markers on
+    # google causes its search bar to be focused.
     if oldMode == 'normal' and keyStr == 'Esc'
       return false
     else


Fix FAQ bug

Fix FAQ bug

--- app/assets/javascripts/controllers/faq.coffee
+++ app/assets/javascripts/controllers/faq.coffee
@@ -1,4 +1,4 @@
-angular.module("coledger").controller("FaqController", ($scope) ->
+angular.module("coledger").controller("FaqController", ['$scope', ($scope) ->
 	$scope.offset=100
 	$scope.faqs = [
 		{
@@ -110,7 +110,7 @@
 			"
 		}
 	]
-)
+])
 angular.module("coledger").filter("html", ['$sce',($sce)->
 	(input)->
 		$sce.trustAsHtml(input)


Updated faye event.

Updated faye event.

--- app/assets/javascripts/faye_manager.js.coffee
+++ app/assets/javascripts/faye_manager.js.coffee
@@ -12,5 +12,6 @@
       window.CalendarManager.background_loading = true
       window.CalendarManager.cached_reservations = null
       window.CalendarManager.populate_calendar(data)
-      $.event.trigger({type: "fayeUpdated", time: new Date(), element: window.CalendarManager})
+      day = moment("#{date}","YYYY-MM-DD").tz("America/Los_Angeles")
+      $.event.trigger({type: "fayeUpdated", time: new Date(), day: day})
     )


[...work in progress...]

[...work in progress...]

--- app/assets/javascripts/input-connected.coffee
+++ app/assets/javascripts/input-connected.coffee
@@ -30,7 +30,7 @@
       refreshBtn.attr('value', component.val()).attr('name', component.attr('name'))

   hasRefreshButton = (component) ->
-    component.parents('.input-group').hasClass('select-field-refresh')
+    component.parents('.input-group').hasClass('field-refresh')

   $.fn.inputConnected = (options) ->


cleanup/simplify the Check.args_str() method

--- app/assets/javascripts/models/check.js.coffee
+++ app/assets/javascripts/models/check.js.coffee
@@ -19,29 +19,36 @@

     # Return args as a comma-separated string of "key = value" pairs
     args_str: (command, host) ->
-     # if a command argument is provided then we get the nice formatted output...
+      # if a command argument is provided then we get the nice formatted output...
       args = @get("args")
       if ! args
         return ""

       args_array = _.map(args, (val, key) ->
-        s = if !command then key + " = " else command.get("options")[key]["name"] + " => "
-        key_or_name_val = if !command then key else command.get("options")[key]["name"]
-        if _.contains(["password", "pass", "pw"], key_or_name_val)
+        s = if command
+          command.get("options")[key]["name"]
+        else
+          key
+        s += " = "
+
+        if _.contains(["password", "pass", "pw"], key)
           s += "[hidden]"
         else
-          s += if !command then val else val + "
"
+          s += val
+
         return s
       )

-      unless !command
-        unless !host
-          s = "Run check from host => "
-          s += host.name()
-          if (command.location == "remote" || command.location == "any")
-             args_array.push s
+      if command && host
+        s = "Run check from host: "
+        s += host.name()
+        if (command.location == "remote" || command.location == "any")
+           args_array.push s

-      if !command then args_array.join(", ") else args_array.join(" ")
+      if command
+        return args_array.join("
")
+      else
+        return args_array.join(", ")

     # Get list of metrics of for this check (same check id)


IDE: Resurrection:  Add defensive check to resurrection logic.

--- client/ide/lib/workspace/idelayoutmanager.coffee
+++ client/ide/lib/workspace/idelayoutmanager.coffee
@@ -207,7 +207,8 @@
   resurrectPanes_: (items, tabView, silent) ->

     ## The `ideApp` is an `IDEAppController`s instane
-    ideApp = @getDelegate()
+    ideApp        = @getDelegate()
+    hasActivePane = no

     for own index, item of items

@@ -235,6 +236,13 @@
         # Don't use `active tab view` logic for new pane creation.
         # Because `The Editors` (saved editors) are loading async.
         item.targetTabView = tabView  if item.context.paneType in [ 'editor', 'tailer' ]
+        hasActivePane      = yes      if item.context.isActivePane
+
+        # If resurrection data does not contain any isActivePane flag for each IDEView, set it to last item automatically.
+        if (parseInt(index, 10) is items.length - 1) and not hasActivePane
+          hasActivePane             = yes
+          item.context.isActivePane = yes
+
         ideApp.createPaneFromChange item, yes


Focus on search field

Focus on search field

--- app/assets/javascripts/users_search.js.coffee
+++ app/assets/javascripts/users_search.js.coffee
@@ -1,4 +1,5 @@
 $ ->
+  $("#query-employee").focus()
   # Search results, load more
   $("section.index.users").on "click", ".load-more input", (event) ->
     event.preventDefault()


Remove unnecessary variable assigns

Remove unnecessary variable assigns

--- server/mixins/update_indicator_data.coffee
+++ server/mixins/update_indicator_data.coffee
@@ -129,7 +129,7 @@
       }\n expected response to be a world bank api response;#{
       } an array with a data array as the second element"

-    return convertedData = {
+    return {
       indicator: @_id
       data: responseBody[1]
     }
@@ -140,7 +140,7 @@
         JSON.stringify(responseBody)
       }\n expected response to be a cartodb api response"

-    return convertedData = {
+    return {
       indicator: @_id
       data: responseBody.data
     }
@@ -151,7 +151,7 @@
         JSON.stringify(responseBody)
       }\n expected response to be an array"

-    return convertedData = {
+    return {
       indicator: @_id
       data: responseBody
     }


Add sorting when updating treemap chart

Add sorting when updating treemap chart

In order to improve treemap layout when chase UTEs switch

--- app/assets/javascripts/charts/treemap-chart.coffee
+++ app/assets/javascripts/charts/treemap-chart.coffee
@@ -102,7 +102,7 @@
     # Update total amount, root & treemap
     @total = d3.sum @data, @getAmount
     @root.sum @getAmount
-    #@root.sort (a, b) -> return b.value - a.value
+    @root.sort (a, b) -> return b.value - a.value
     @treemap @root

     # redraw


Add message to note how to create a new account

--- app/assets/javascripts/vimeo_integration.js.coffee
+++ app/assets/javascripts/vimeo_integration.js.coffee
@@ -35,6 +35,11 @@

+

+                      New to VerbalizeIt?

+                      Fill in your email and a password, and click the button
+                      to create a new account!
+

                         Email
                         <input class=\"input bg-light-grey appendb20\" id=\"verbalizeit-email\" size=\"30\" type=\"email\" value=\"#{email}\">


Assert that all buffer events are processed sequentially

Signed-off-by: Nathan Sobo <2e8aa918660411855c6d44d5bb2da677aa033255@github.com>

--- src/tokenized-buffer.coffee
+++ src/tokenized-buffer.coffee
@@ -242,6 +242,16 @@
         row + delta

   handleBufferChange: (e) ->
+    if @lastBufferChangeEventId?
+      @assert(
+        @lastBufferChangeEventId is e.eventId - 1,
+        'Buffer Change Event Ids are not sequential',
+        (error) =>
+          error.metadata = {
+            tokenizedBufferEventId: @lastBufferChangeEventId,
+            nextTokenizedBufferEventId: e.eventId,
+          }
+      )
     @lastBufferChangeEventId = e.eventId
     @changeCount = @buffer.changeCount


[Feature] Implemwnt restoring window bounds

--- src/renderer/scripts/app/App.coffee
+++ src/renderer/scripts/app/App.coffee
@@ -28,6 +28,7 @@
         @_initializeCoreModules()
         @_handleEvents()
         @_handleCommands()
+        @_restoreWindowBounds()

         Migrater.migrate()
         @_initializeNcoModules()
@@ -74,6 +75,10 @@
         window.addEventListener "offline", =>
             @emit "did-change-network-state", false

+        window.addEventListener "beforeunload", =>
+            app.config.set "nco.window.bounds", @currentWindow.getBounds()
+            return
+
         @onDidChangeNetworkState (isOnLine) =>
             if isOnLine is no
                 console.info "%cNetwork state was changed to %cOffline", Colors.text.info, Colors.text.danger
@@ -124,6 +129,12 @@
                 .catch (e) =>
                     console.error e
                     callback(e)
+
+
+    _restoreWindowBounds : ->
+        windowBounds = app.config.get "nco.window.bounds"
+        @currentWindow.setBounds windowBounds if windowBounds?
+        return

     _loadServices : ->


Get rid of extraneous console.log

Get rid of extraneous console.log

--- app/assets/volume/slot.coffee
+++ app/assets/volume/slot.coffee
@@ -633,7 +633,6 @@
         messages.clear(this)
         return if @file
         file.pause()
-        console.log file
         @file = file
         @progress = 0
         file.store = this
@@ -829,7 +828,6 @@

     $scope.fileSuccess = uploads.fileSuccess
     $scope.fileProgress = uploads.fileProgress
-    console.log "Uploads;",uploads
     $scope.fileError = uploads.fileError

     class Record extends TimeBar


challenge opponent points fix

challenge opponent points fix

--- app/client/game/result.coffee
+++ app/client/game/result.coffee
@@ -83,7 +83,10 @@
     Games.findOne(currentChallenge().challengeeGameId).state is 'declined'

   result: ->
-    game = Games.findOne currentChallenge().challengeeGameId
+    if currentPlayerRole() is 'challenger'
+      game = Games.findOne currentChallenge().challengeeGameId
+    else
+      game = Games.findOne currentChallenge().challengerGameId
     {
       score: game.score
       ratio: "#{game.correctAnswers}/#{numberOfQuestions()}"
@@ -187,6 +190,3 @@
       description: description
     }
   }
-
-Template.challenge.rendered = ->
-  #notifyFinishedGame()


google borks if you try to retain too many graphable points

--- demo.coffee
+++ demo.coffee
@@ -211,10 +211,10 @@
   else
     u.run()
     element.innerHTML = 'stop'
-trimData = (data) ->
-  return data if data.length < 500
+trimData = (data, size) ->
+  return data if data.length < size
   results = []
-  results.push data[i] for i in [data.length - 500...data.length]
+  results.push data[i] for i in [data.length - size...data.length]
   results
 clearCharts = ->
   resetNum = evoData.resetNum++
@@ -234,7 +234,7 @@
 drawChart = (ct=chartType)->
   return if ct == 'options'
   for title, specs of evoData.charts[ct] || {}
-    rows = specs.rows # trimData specs.rows
+    rows = trimData specs.rows, 2000
     return unless rows.length && rows[0].length
     id     = specs.id
     chart  = specs.chart
@@ -247,12 +247,14 @@
     options =
       title: titlize decamelize(title)
       width: width
-      explorer: axis: 'horizontal'
+      explorer:
+        axis: 'horizontal'
+        keepInBounds: true
       height: height
       hAxis:
         title: htitle
         viewWindow:
-          min: Math.max 1, rows.length - 500
+          min: Math.max 1, rows.length - 400
           max: rows.length
       vAxis:
         title: vtitle


Activity: removed a boobie trap /cc @szkl

--- client/Social/Activity/sidebar/messageitem.coffee
+++ client/Social/Activity/sidebar/messageitem.coffee
@@ -19,7 +19,7 @@

     @endButton = if owner
     then new LeaveChannelButton {}, data
-    else KDView
+    else new KDView

   channelDeleted: ->


sidebar: prevent search modal invoking lazy loader multiple times

--- client/Social/Activity/sidebar/searchmodal.coffee
+++ client/Social/Activity/sidebar/searchmodal.coffee
@@ -55,7 +55,7 @@

     @addSubView @listController.getView()

-    @listController.customScrollView.wrapper.on 'LazyLoadThresholdReached', @bound 'handleLazyLoad'
+    @listController.on 'LazyLoadThresholdReached', @bound 'handleLazyLoad'

     @fetch {}, @bound 'populate'


ComputeController: fetchBaseStackTemplate helper and use it for readme fetcher as well

--- client/app/lib/providers/computecontroller.coffee
+++ client/app/lib/providers/computecontroller.coffee
@@ -860,12 +860,24 @@

     return callback null, ''  unless stack?.baseStackId

+    @fetchBaseStackTemplate stack, (err, template) ->
+      return callback err, template?.description ? ''
+
+
+  ###*
+   * Fetch given stack's stackTemplate which is generated from.
+  ###
+
+  fetchBaseStackTemplate: (stack, callback = kd.noop) ->
+
+    return callback null, ''  unless stack?.baseStackId
+
     { baseStackId } = stack

     remote.cacheable 'JStackTemplate', baseStackId, (err, templates) ->
       return callback err  if err
-      [template] = templates
-      return callback null, template?.description ? ''
+      [ template ] = templates
+      return callback null, template or {}

   ###*


ComputeController: {start, stop}Stack helpers for changing states of machines in a given stack

--- client/app/lib/providers/computecontroller.coffee
+++ client/app/lib/providers/computecontroller.coffee
@@ -727,6 +727,21 @@

       updateWith options

+  # Stacks
+
+  # Start helper to start all machines in the given stack
+  startStack: (stack) ->
+
+    for machine in stack.machines
+      @start machine  if machine.isStopped()
+
+
+  # Stop helper to stop all machines in the given stack
+  stopStack: (stack) ->
+
+    for machine in stack.machines
+      @stop machine  if machine.isRunning()
+

   # Snapshots
   #


white-space -> whiteSpace

white-space -> whiteSpace

--- examples/examples.cjsx
+++ examples/examples.cjsx
@@ -88,7 +88,7 @@

Pass in non-date data

-

+

       {"""
       <Sparkline
         data={[48999,89163,60000,99526,89736,89963,97176]}
@@ -105,7 +105,7 @@

Pass in date data

-

+

       {"""
       dateData = for i in [0..100]
         {
@@ -137,7 +137,7 @@

If no data is passed in, an empty div is returned

-

+

         {"<Sparkline data={[]} />"}

       <Sparkline data={[]} />


Temporarily disable language-c package

Temporarily disable language-c package

It is causing errors when opening a Python project.

--- atom/config.cson
+++ atom/config.cson
@@ -49,6 +49,7 @@
       "welcome"
       "whitespace"
       "teletype"
+      "language-c"
     ]
     followSymlinks: false
     ignoredNames: [


atom: Update config.cson

atom: Update config.cson

- Disable auto-update
- Don't close empty window by "Close Tab" command
- Don't zoom font size by Ctrl+Mouse Up/Down

--- atom/config.cson
+++ atom/config.cson
@@ -3,6 +3,8 @@
     highlightMatchingLineNumber: true
   core:
     audioBeep: false
+    automaticallyUpdate: false
+    closeEmptyWindows: false
     disabledPackages: [
       "background-tips"
       "dalek"
@@ -21,6 +23,7 @@
   editor:
     showIndentGuide: true
     showInvisibles: true
+    zoomFontWhenCtrlScrolling: false
   "file-icons":
     coloured: false
     onChanges: true


Set default font size (on Zoom reset) to 16px

--- atom/config.cson
+++ atom/config.cson
@@ -103,6 +103,7 @@
     extend_double_slash: false
     simple_mode: true
   editor:
+    defaultFontSize: 16
     fontFamily: "Monaco"
     fontSize: 16
     showCursorOnSelection: false


Implement support for React Editor

Implement support for React Editor

--- src/react-editor-view.coffee
+++ src/react-editor-view.coffee
@@ -81,7 +81,16 @@
     @attached = true
     @component.pollDOM()
     @focus() if @focusOnAttach
+
+    @addGrammarScopeAttribute()
+    @subscribe @editor, 'grammar-changed', =>
+      @addGrammarScopeAttribute()
+
     @trigger 'editor:attached', [this]
+
+  addGrammarScopeAttribute: ->
+    grammarScope = @editor.getGrammar()?.scopeName?.replace(/\./g, ' ')
+    @attr('data-scope', grammarScope)

   scrollTop: (scrollTop) ->
     if scrollTop?


Removed packaging

Removed packaging

--- build/Gruntfile.coffee
+++ build/Gruntfile.coffee
@@ -75,7 +75,6 @@

   tasks = tasks.concat [
     'build-app',
-    'package-app'
   ]

   grunt.registerTask('default', tasks)


jasmine is no longer available

jasmine is no longer available

--- shared/specs/components/notifications/bar.spec.cjsx
+++ shared/specs/components/notifications/bar.spec.cjsx
@@ -37,9 +37,9 @@

   it 'starts and stops listening to notifications', ->
     wrapper = shallow(<Bar {...@props} />)
-    expect(Notifications.on).toHaveBeenLastCalledWith('change', jasmine.any(Function))
+    expect(Notifications.on).toHaveBeenLastCalledWith('change', expect.anything())
     wrapper.unmount()
-    expect(Notifications.off).toHaveBeenLastCalledWith('change', jasmine.any(Function))
+    expect(Notifications.off).toHaveBeenLastCalledWith('change', expect.anything())
     undefined

   it 'shows itself after a delay if there are notifications', ->


Do not Fat arrow

Do not Fat arrow

--- assets/javascripts/app.coffee
+++ assets/javascripts/app.coffee
@@ -270,21 +270,21 @@
       $('progress').attr('value', root.starboard.progress)
       trelloList.cards = list.cards
       trelloList)
-  ).then( (lists) =>
+  ).then( (lists) ->
     console.log("lists", lists)
     cards = []
     lists.reduce( (acc, list) ->
       acc.then( -> Promise.all(createCards(list)))
          .then( (listCards) -> cards = cards.concat(listCards))
     , Promise.resolve()).then(-> cards)
-  ).then((cards) =>
+  ).then((cards) ->
     console.log("cards", cards)
     checkLists = []
     cards.reduce( (acc, card) ->
       acc.then(-> Promise.all(createCheckLists(card)))
          .then((cardLists) -> checkLists =  checkLists.concat(cardLists))
     , Promise.resolve()).then(-> checkLists)
-  ).then((checkLists) =>
+  ).then((checkLists) ->
     console.log("checkLists", checkLists)
     items = []
     checkLists.reduce( (acc, list) ->


add array finished

add array finished

--- cf/spell.coffee
+++ cf/spell.coffee
@@ -16,6 +16,7 @@
     @current = ""
     @fail = 0
     @reveal = ""
+    @finished = []
     update_question(data)
     @data = data


Removed unused files from nodejs boilerplate.

--- skeletons/nodejs/apps/main/app.coffee
+++ skeletons/nodejs/apps/main/app.coffee
@@ -1,45 +0,0 @@
-express = require 'express'
-require 'express-namespace'
-mongoose = require 'mongoose'
-http = require 'http'
-path = require 'path'
-io = require 'socket.io'
-
-app = express()
-
-# Config
-app.configure ->
-  app.set 'port', process.env.PORT ? 3000
-  app.use express.favicon()
-  app.use express.logger('dev')
-  app.use express.bodyParser()
-  app.use express.methodOverride()
-  app.use(express.cookieParser('your secret here'));
-  app.use(express.session());
-  app.use app.router
-  app.use express.static(path.join(__dirname, '../../../public'))
-
-app.configure 'development', ->
-  app.use express.errorHandler({ dumpExceptions: true, showStack: true })
-  mongoose.connect 'mongodb://localhost/muffin_development'
-
-app.configure 'test', ->
-  app.use express.errorHandler({ dumpExceptions: true, showStack: true })
-  mongoose.connect 'mongodb://localhost/muffin_test'
-
-app.configure 'production', ->
-  app.use express.errorHandler()
-  mongoose.connect 'mongodb://localhost/muffin_production'
-
-app.namespace '/api/v1', ->
-  router = require('./router')(app)
-
-server = http.createServer(app)
-server.listen app.get('port'), ->
-  console.log "Server is running at http://localhost:#{app.get('port')} in #{app.settings.env} mode."
-  console.log "Quit the server with CONTROL-C."
-
-# Set up socket.IO server
-socket = io.listen(server)
-socket.on 'connection', (client) ->
-  console.log 'Client connected'


USE float32Eq0

USE float32Eq0

--- source/Art/Engine/Core/Element.coffee
+++ source/Art/Engine/Core/Element.coffee
@@ -37,8 +37,7 @@
   isString
   isFunction
   mergeInto
-  floatEq
-  floatEq0
+  float32Eq0
   Join
   rubyTrue
   createWithPostCreate
@@ -806,7 +805,7 @@
         "axis" if @_axis && !@axis.eq point()
         "location" if !@location.eq point0
         "size" if @_currentSize
-        "angle" if !floatEq0 @angle
+        "angle" if !float32Eq0 @angle
         "scale" if !@scale.eq point(1,1)
         "compositeMode" if @_compositeMode && @_compositeMode != "normal"
         "opacity" if @_opacity? && @_opacity < 1
@@ -933,8 +932,6 @@

   # OUT: promise.then -> (bitmap) ->
   toBitmapBasic: (options) ->
-    if @key == "child"
-      throw new Error "GOTCHA!"
     @toBitmapWithInfo options
     .then ({bitmap}) -> bitmap


Fixes the load order of plugin files

Fixes the load order of plugin files

Order ought to be:

1. Plugins found in Lineman Core
2. Plugins found via depended-on modules
3. Plugins found within the client project

Meaning that client project trumps all and modules
override defaults.

--- lib/builds-app-config.coffee
+++ lib/builds-app-config.coffee
@@ -25,14 +25,17 @@
       files: require("#{process.cwd()}/config/files")

 pluginFiles = ->
-  grunt.file.expand(pluginModulesFromNpm().concat(pluginFilesInsideProject()))
+  grunt.file.expand(pluginFilesFromLinemanCore().concat(pluginModulesFromNpm(), pluginFilesFromUserProject()))
+
+pluginFilesFromLinemanCore = ->
+  ["#{__dirname}/../config/plugins/**/*"]

 pluginModulesFromNpm = ->
   moduleNames = findsPluginModules.find().map (pluginModule) ->
     "#{process.cwd()}/node_modules/#{pluginModule}/config/plugins/**/*"

-pluginFilesInsideProject = ->
-  ["#{__dirname}/../config/plugins/**/*", "#{process.cwd()}/config/plugins/**/*"]
+pluginFilesFromUserProject = ->
+  ["#{process.cwd()}/config/plugins/**/*"]

 requirePlugin = (path, config) ->
   plugin = require(path)


Fixes issue where panning occurs upon mouse scroll at max/min zoom

--- app/lib/surface/Camera.coffee
+++ app/lib/surface/Camera.coffee
@@ -165,7 +165,8 @@
       target = {x: newTargetX, y:newTargetY}
     else
       target = @target
-    @zoomTo target, newZoom, 0
+    if not(newZoom >= MAX_ZOOM or newZoom <= Math.max(@minZoom, MIN_ZOOM))
+      @zoomTo target, newZoom, 0

   onMouseDown: (e) ->
     return if @dragDisabled


Fix coffee compile error

Fix coffee compile error

--- static/coffee/flows/services.coffee
+++ static/coffee/flows/services.coffee
@@ -561,7 +561,7 @@
       throw new Error('Loop detected: ' + nodeId)

     # break out if our target is a pausing ruleset
-    node = getNode(flow, targetId
+    node = getNode(flow, targetId)
     if isPausingRuleset(node)
       return false


Ensure correct run order

Ensure correct run order

Queuing stuff at timeout 0 sometimes still running out of order

--- resources/assets/coffee/fade.coffee
+++ resources/assets/coffee/fade.coffee
@@ -84,4 +84,4 @@

     nextRun = => @runQueue(true)

-    setTimeout nextRun, 0
+    setTimeout nextRun, 10


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/sprite.coffee
+++ src/sprite.coffee
@@ -107,7 +107,7 @@
   ###
   Sprite.load = (url, loadedCallback) ->
     if spriteCache[url]
-      spriteCache[url]
+      spriteCache[url]

     img = new Image()
     proxy = LoaderProxy()


Refresh accounts list after sync (closes #55)

--- assets/scripts/app/models/user.coffee
+++ assets/scripts/app/models/user.coffee
@@ -64,8 +64,8 @@
         @setWithSession('syncedAt', data.user.synced_at)
         Travis.trigger('user:synced', data.user)

-        # # TODO this doesn't work properly
-        # Travis.app.store.loadMany(Travis.Account, data.accounts)
+        # need to pass any param to trigger findQuery
+        Travis.Account.find(foo: '')

   setWithSession: (name, value) ->
     @set(name, value)


Fix status icon on build's view

Fix status icon on build's view

--- assets/scripts/app/views/build.coffee
+++ assets/scripts/app/views/build.coffee
@@ -54,5 +54,5 @@
     loadingBinding: 'controller.loading'

     color: (->
-      Travis.Helpers.colorForState(@get('build.state'))
-    ).property('build.state')
+      Travis.Helpers.colorForState(@get('controller.build.state'))
+    ).property('controller.build.state')