fix whitespace

fix whitespace

--- app/assets/javascripts/forms.js.coffee
+++ app/assets/javascripts/forms.js.coffee
@@ -7,9 +7,9 @@
 $ ->
   $(".people-list.select-person li").click ->
     togglePeopleSelect this
-
+
   if $('div.actions.scroll_this').length
-
+
     responsiveLimit = ''
     didScroll = false
     scrollElem = $('div.actions.scroll_this')
@@ -21,7 +21,7 @@
       responsiveLimit = true
     else
       responsiveLimit = false
-
+
     $(window).scroll ->
       didScroll = true

@@ -33,30 +33,30 @@

             #catch the windows scrolled point
             scrollPoint = $(window).scrollTop()
-
+
             #position checks, class additions
             if scrollPoint > startTop
               $(scrollElem).addClass "inmotion"
             else
               $(scrollElem).removeClass "inmotion"
-
+
             bottomStop = $('section.question').offset().top + $('section.question').height()
             scrollElemBoundary = scrollPoint + $(scrollElem).outerHeight()
-
+
             if scrollElemBoundary >= bottomStop
               $(scrollElem).addClass "magnetize"
             else
               $(scrollElem).removeClass "magnetize"
-
+
           didScroll = false
     ), 10

   #@todo JM delete/modify this when ajax replaces official lookup on Q&A flows
   fieldValid = {}
-
+
   if $('section.question ol.people-list.select-person').length
     $("article form input").blur ->
-
+
       $("article form fieldset:eq(0) :text, article form fieldset:eq(0) select").each (int) ->
         unless $(this).val() is ""
           fieldValid['field_' + int] = true
@@ -65,20 +65,20 @@

       for elem of fieldValid
         return false if fieldValid[elem] is false
-
+
       #animation for loading in officials
       officialCount = $('section.question ol.people-list.select-person li').length
       iterater = 0
-
+
       #load in header and slide to it first
       $('section.question label.select-person').fadeTo 100, 1
-
+
       selectRecipient = $("section.question label.select-person").offset().top
-
+
       $('body').animate
         scrollTop: selectRecipient
       , 300
-
+
       #then load in officials
       showOfficials = setInterval(->
         if iterater < officialCount
@@ -86,4 +86,4 @@
           iterater++
         else
           window.clearInterval showOfficials
-      , 10)+      , 10)


rename feedparser to FeedParser

rename feedparser to FeedParser

--- scripts/feed_checker.coffee
+++ scripts/feed_checker.coffee
@@ -16,7 +16,7 @@
 # Author:
 #   Takeshi Watanabe

-feedparser = require 'feedparser'
+FeedParser = require 'feedparser'
 request = require 'request'

 module.exports = (robot) ->
@@ -41,7 +41,7 @@
         robot.logger.debug "checking feed: #{url}"

         next = []
-        request(url).pipe(new feedparser())
+        request(url).pipe(new FeedParser())
           .on 'error', (err) ->
             robot.logger.warning "feed parsing error in #{url}: #{err}"
           .on 'meta', (meta) ->


updated tests to use a helper function tryParse

--- test/peg-coffee-test.coffee
+++ test/peg-coffee-test.coffee
@@ -15,6 +15,15 @@
   expect = global.expect
   PEG = global.PEG
   PEGCoffee = global.PEGCoffee
+
+
+# Helper functions
+tryParse = (parser, text) ->
+  try
+    result = parser.parse(text)
+  catch e
+    result = e
+  return result

 # Test suite for the plugin
@@ -63,7 +72,7 @@
     suite 'simple CoffeeScript', ->
       test 'action', ->
         parser = PEG.buildParser 'start = "a" { return "#{1+1}" }'
-        expect(parser.parse "a").to.equal "2"
+        expect(tryParse parser, "a").to.equal "2"

       test 'initializer', ->
         parser = PEG.buildParser '''
@@ -73,22 +82,36 @@
           start
             = "a" { return @val }
         '''
-        expect(parser.parse "a").to.equal "2"
+        expect(tryParse parser, "a").to.equal "2"

       suite 'predicates', ->
-        test 'semantic not code', ->
-          parser = PEG.buildParser '''
-            start
-              = !{return typeof Array is "undefined"}
-          '''
-          expect(parser.parse "").to.equal ""
+        suite 'semantic not code', ->
+          test 'success on |false| return', ->
+            parser = PEG.buildParser '''
+              start
+                = !{return typeof Array is "undefined"}
+            '''
+            expect(tryParse parser, "").to.equal ""
+          test 'failure on |true| return', ->
+            parser = PEG.buildParser '''
+              start
+                = !{return typeof Array isnt "undefined"}
+            '''
+            expect(tryParse parser, "").to.be.a Error

-        test 'semantic and code', ->
-          parser = PEG.buildParser '''
-            start
-              = &{return typeof Array isnt "undefined"}
-          '''
-          expect(parser.parse "").to.equal ""
+        suite 'semantic and code', ->
+          test 'success on |true| return', ->
+            parser = PEG.buildParser '''
+              start
+                = &{return typeof Array isnt "undefined"}
+            '''
+            expect(tryParse parser, "").to.equal ""
+          test 'failure on |false| return', ->
+            parser = PEG.buildParser '''
+              start
+                = &{return typeof Array is "undefined"}
+            '''
+            expect(tryParse parser, "").to.be.a Error

       suite 'variable use', ->
@@ -97,14 +120,14 @@
             start
               = a:"a" &{return a is "a"}
           '''
-          expect(parser.parse "a").to.eql ["a", ""]
+          expect(tryParse parser, "a").to.eql ["a", ""]

         test 'can use the |offset| variable to get the current parse position', ->
           parser = PEG.buildParser '''
             start
               = "a" &{return offset is 1}
           '''
-          expect(parser.parse "a").to.eql ["a", ""]
+          expect(tryParse parser, "a").to.eql ["a", ""]

         test 'can use the |line| and |column| variables to get the current line and column', ->
           parser = PEG.buildParser '''
@@ -119,4 +142,36 @@
             nl    = ("\\r" / "\\n" / "\\u2028" / "\\u2029")
           ''', trackLineAndColumn: true

-          expect(parser.parse "1\n2\n\n3\n\n\n4 5 x").to.eql [7, 5]
+          expect(tryParse parser, "1\n2\n\n3\n\n\n4 5 x").to.eql [7, 5]
+
+      suite 'variable use in semantic not code', ->
+        test 'can use label variables', ->
+          parser = PEG.buildParser '''
+            start
+              = a:"a" !{return a isnt "a"}
+          '''
+          expect(tryParse parser, "a").to.eql ["a", ""]
+
+        test 'can use the |offset| variable to get the current parse position', ->
+          parser = PEG.buildParser '''
+            start
+              = "a" !{return offset isnt 1}
+          '''
+          expect(tryParse parser, "a").to.eql ["a", ""]
+
+        test 'can use the |line| and |column| variables to get the current line and column', ->
+          parser = PEG.buildParser '''
+            {
+              global.result = "test"
+            }
+            start = line (nl+ line)* {return @result }
+            line  = thing (" "+ thing)*
+            thing = digit / mark
+            digit = [0-9]
+            mark  = !{ @result = [line, column]; return false } "x"
+            nl    = ("\\r" / "\\n" / "\\u2028" / "\\u2029")
+          ''', trackLineAndColumn: true
+
+          expect(tryParse parser, "1\n2\n\n3\n\n\n4 5 x").to.eql [7, 5]
+
+


chore: change browser for webrider

chore: change browser for webrider

--- test/protractor.conf.coffee
+++ test/protractor.conf.coffee
@@ -4,7 +4,7 @@

   # capabilities to be passed to the webdriver instance
   capabilities:
-    browserName: "chrome"
+    browserName: "firefox"

   # spec patterns
   specs: [


documentation

documentation

--- src/lib/missing-support.coffee
+++ src/lib/missing-support.coffee
@@ -17,6 +17,25 @@
       resultStats[browser] = versionsWithoutSupport

 ###
+Get data on CSS features not supported by the given autoprefixer-like browser
+selection.
+
+Returns:
+```
+{
+  'feature-name': {
+    missing: {
+      'ie': '8', // etc.
+    }
+    caniuseData: {
+      // caniuse-db json data for this feature
+    }
+  },
+  'feature-name-2': {} // etc.
+}
+```
+
+`feature-name` is a caniuse-db slug.

 ###
 missing = (browserRequest) ->


Flunk test without explicitly exiting

Flunk test without explicitly exiting

--- spec/parser_spec.coffee
+++ spec/parser_spec.coffee
@@ -81,4 +81,3 @@
               if diff.removed
                 console.error "Removed: \n#{_.str.strip(diff.value)}"
             console.error delta
-            process.exit(1)


Limits ID3 tag reading to only artist and title

--- app/assets/javascripts/mixes.js.coffee
+++ app/assets/javascripts/mixes.js.coffee
@@ -20,13 +20,16 @@
   # read id3 tag data when file is chosen
   handleFileChange = (i,event) ->
     file = event.target.files[0]
-    jsmediatags.read(file, {
+    new (jsmediatags.Reader)(file).setTagsToRead([
+      'title'
+      'artist'
+    ]).read
       onSuccess: (tag) ->
         populateName i,tag
-      ,
+        return
       onError: (error) ->
-        console.log error
-    })
+        console.log ':(', error.type, error.info
+        return

   # add listeners for id3 tag reader
   setupFileChangeListeners = () ->


Edit spec_helper.coffee

Edit spec_helper.coffee

--- spec/spec_helper.coffee
+++ spec/spec_helper.coffee
@@ -16,16 +16,16 @@
   baseUrl: "./"
   paths:
     # app
-    "app/main": "tmp/lib/app/js/main"
-    "app/application": "tmp/lib/app/js/application"
-    "app/config": "tmp/lib/app/js/config"
-    "app/common": "tmp/lib/app/js/common"
-    "app/repository": "tmp/lib/app/js/repository"
-    "app/repositories": "tmp/lib/app/js/repositories"
-    "app/page": "tmp/lib/app/js/page"
-    "app/labels": "tmp/lib/app/js/labels"
-    "app/label": "tmp/lib/app/js/label"
-    "app/label_category": "tmp/lib/app/js/label_category"
+    "app/main"           : "tmp/lib/app/js/main"
+    "app/application"    : "tmp/lib/app/js/application"
+    "app/config"         : "tmp/lib/app/js/config"
+    "app/common"         : "tmp/lib/app/js/common"
+    "app/repository"     : "tmp/lib/app/js/repository"
+    "app/repositories"   : "tmp/lib/app/js/repositories"
+    "app/page"           : "tmp/lib/app/js/page"
+    "app/labels"         : "tmp/lib/app/js/labels"
+    "app/label"          : "tmp/lib/app/js/label"
+    "app/label_category" : "tmp/lib/app/js/label_category"
     # components
     "com/jquery/jquery.pjax":             "lib/server/static/lib/com/jquery-pjax/js/jquery.pjax"
     "com/bootstrap/bootstrap":            "lib/server/static/lib/com/bootstrap/js/bootstrap"


Focus map on the world instead of just the USA

--- app/assets/javascripts/pages.js.coffee
+++ app/assets/javascripts/pages.js.coffee
@@ -3,9 +3,9 @@
   handler.buildMap
     provider:
       center:
-        lat: 39
-        lng: -101
-      zoom: 4
+        lat: 32
+        lng: 0
+      zoom: 2
       scrollwheel: false
     internal:
       id: 'home-map-jumbotron'


using namespace api/

using namespace api/

--- app/assets/javascripts/store.js.coffee
+++ app/assets/javascripts/store.js.coffee
@@ -1,4 +1,6 @@
 # http://emberjs.com/guides/models/using-the-store/
+DS.RESTAdapter.reopen
+  namespace: "api"

 Bandango.Store = DS.Store.extend
   # Override the default adapter with the `DS.ActiveModelAdapter` which


nest user info in user_info in JSON

nest user info in user_info in JSON

--- app/assets/javascripts/users.js.coffee
+++ app/assets/javascripts/users.js.coffee
@@ -26,15 +26,15 @@
     $.ajax '/update',
       method: 'POST'
       data:
-        user_id: user_id
-        screen_resolution: "#{screen.width} x #{screen.height}"
-        window_size: "#{$window.width()} x #{$window.height()}"
-        html5_support: "#{Modernizr.canvas}"
-        css3_support: "#{Modernizr.borderradius}"
-        flash_version: flash_version
-        audio_formats: audio_formats
-        video_formats: video_formats
-        cookies: cookies
-
+        user_info:
+          id: user_id
+          screen_resolution: "#{screen.width} x #{screen.height}"
+          window_size: "#{$window.width()} x #{$window.height()}"
+          html5_support: "#{Modernizr.canvas}"
+          css3_support: "#{Modernizr.borderradius}"
+          flash_version: flash_version
+          audio_formats: audio_formats
+          video_formats: video_formats
+          cookies: cookies

   update_info()


Client: Activity: Moved onLoadMore functionality to chatPane onThresholdReached method and removed unnecessary messages props

--- client/activity/lib/components/publicchatpane/index.coffee
+++ client/activity/lib/components/publicchatpane/index.coffee
@@ -10,7 +10,6 @@

   @defaultProps =
     thread   : immutable.Map()
-    messages : immutable.List()
     padded   : no

   constructor: (props) ->
@@ -30,15 +29,6 @@
     return  unless body = value

     ActivityFlux.actions.message.createMessage @channel('id'), body
-
-
-  onLoadMore: ->
-
-    return  unless @props.messages.size
-    return  if @props.thread.getIn ['flags', 'isMessagesLoading']
-
-    from = @props.messages.first().get('createdAt')
-    kd.utils.defer => ActivityFlux.actions.message.loadMessages @channel('id'), { from, loadedWithScroll: yes }

   onFollowChannel: ->
@@ -68,7 +58,7 @@

   renderFooter: ->

-    return null  unless @props.messages
+    return null  unless @props.thread?.get 'messages'

     { thread } = @props

@@ -91,10 +81,8 @@
     <ChatPane
       thread     = { @props.thread }
       className  = "PublicChatPane"
-      messages   = { @props.messages }
       onSubmit   = { @bound 'onSubmit' }
-      afterInviteOthers = {@bound 'afterInviteOthers'}
-      onLoadMore = { @bound 'onLoadMore' }>
+      afterInviteOthers = {@bound 'afterInviteOthers'}>
       {@renderFooter()}
     </ChatPane>


Client: Activity: Added tooltipStates to show/hide tooltips by clicking links and document

--- client/activity/lib/components/publicchatpane/index.coffee
+++ client/activity/lib/components/publicchatpane/index.coffee
@@ -13,6 +13,33 @@
     thread   : immutable.Map()
     messages : immutable.List()
     padded   : no
+
+  constructor: (props) ->
+
+    super props
+
+    @state =
+      showIntegrationTooltip   : no
+      showCollaborationTooltip : no
+
+
+  componentDidMount: ->
+
+    document.addEventListener 'click', @bound 'setTooltipStates'
+
+
+  setTooltipStates: (event) ->
+
+    className = event.target.parentNode.className
+
+    return @setState showCollaborationTooltip: no  if className is 'ChatPane-addIntegrationAction'
+    return @setState showIntegrationTooltip: no  if className is 'ChatPane-startCollaborationAction'
+
+    kd.utils.stopDOMEvent event
+
+    @setState
+      showIntegrationTooltip   : no
+      showCollaborationTooltip : no

   channel: (key) -> @props.thread?.getIn ['channel', key]
@@ -46,7 +73,8 @@
   startCollaboration: (event) ->

     kd.utils.stopDOMEvent event
-    console.log 'startCollaboration clicked!'
+    event.stopPropagation()
+    @setState showCollaborationTooltip: not @state.showCollaborationTooltip

   inviteOthers: (event) ->
@@ -59,9 +87,12 @@

     ChatInputFlux.actions.value.setValue @props.thread.get('channelId'), '/invite @'

+
   addIntegration: (event) ->

     kd.utils.stopDOMEvent event
+    event.stopPropagation()
+    @setState showIntegrationTooltip: not @state.showIntegrationTooltip

   renderFollowChannel: ->
@@ -103,6 +134,8 @@
       inviteOthers       = { @bound 'inviteOthers' }
       addIntegration     = { @bound 'addIntegration' }
       startCollaboration = { @bound 'startCollaboration' }
+      showIntegrationTooltip = { @state.showIntegrationTooltip }
+      showCollaborationTooltip = { @state.showCollaborationTooltip }
     >
       {@renderFooter()}
     </ChatPane>


API.read async function

API.read async function

--- src/api.coffee
+++ src/api.coffee
@@ -1,4 +1,5 @@
 {async, read, merge } = require "fairmont"
+{yaml} = require "panda-serialize"
 JSCK = require "jsck"

 Schemas = require "./schemas"
@@ -7,6 +8,9 @@

 module.exports = class API

+  @read: async (apiFile) ->
+    new @ yaml yield read apiFile
+
   constructor: (description) ->
     {valid, errors} = validator.validate description
     if not valid


Use json file to keep connections

Use json file to keep connections

--- src/app.coffee
+++ src/app.coffee
@@ -5,6 +5,7 @@
 bodyParser = require('body-parser')
 iptables = Promise.promisifyAll(require('./iptables'))
 spawn = require('child_process').spawn
+exec = require('child_process').exec
 os = require('os')
 async = require('async')

@@ -16,6 +17,11 @@
 server = null
 ssidList = null
 dnsServer = null
+
+connectionFile = '/data/connections.json'
+connectionsFromFile = null
+if fs.existsSync(connectionFile)
+	connectionsFromFile = require(connectionFile)

 ignore = ->

@@ -58,6 +64,32 @@
 					throw err if err?
 					console.log('Server listening')

+connectOrStartServer = (wifi, retryCallback) ->
+	if connectionsFromFile?
+		console.log('Trying to join previously known networks')
+		Promise.each connectionsFromFile, (conn) ->
+			wifi.joinAsync(conn.ssid, conn.passphrase)
+			.then ->
+				console.log('Joined! Exiting.')
+				retryCallback()
+			.catch(ignore)
+		.then ->
+			startServer(wifi)
+	else
+		startServer(wifi)
+
+saveToFile = (ssid, passphrase) ->
+	if connectionsFromFile?
+		connectionsFromFile.push({ ssid, passphrase })
+	else
+		connectionsFromFile = [ { ssid, passphrase } ]
+	return new Promise (resolve, reject) ->
+		fs.writeFile connectionFile, JSON.stringify(connectionsFromFile), (err) ->
+			return reject(err) if err?
+			exec 'sync', (err) ->
+				reject(err) if err?
+				resolve()
+
 console.log('Starting node connman app')
 manageConnection = (retryCallback) ->
 	connman.initAsync()
@@ -88,6 +120,8 @@
 				console.log('Server closed and captive portal disabled')
 				wifi.joinAsync(req.body.ssid, req.body.passphrase)
 			.then ->
+				saveToFile(req.body.ssid, req.body.passphrase)
+			.then ->
 				console.log('Joined! Exiting.')
 				retryCallback()
 			.catch (err) ->
@@ -113,8 +147,7 @@
 					console.log('Joined! Exiting.')
 					retryCallback()
 				.catch (err) ->
-					startServer(wifi)
-
+					connectOrStartServer(wifi, retryCallback)
 			else
 				console.log('Already connected')
 				retryCallback()


The signup state should not be inherited from root scope.

--- src/app.coffee
+++ src/app.coffee
@@ -51,7 +51,6 @@
       controller: "PagesSettingsCtrl as settings"
     )
     .state("signup",
-      parent: "root"
       url: "/signup/:invite/"
       templateUrl: "app/pages/signup/pages.signup.template.html"
       controller: "PagesSignupCtrl as signup"


Don't wait for server to close

Don't wait for server to close

--- src/app.coffee
+++ src/app.coffee
@@ -71,16 +71,16 @@
 			if req.body.ssid and req.body.passphrase
 				console.log("Selected " + req.body.ssid)
 				res.send('OK')
-				server.close ->
-					iptables.delete { table: 'nat', rule: "PREROUTING -i tether -j TETHER"}, ->
-						iptables.flush 'nat', 'TETHER', ->
-							dnsServer.kill()
-							console.log("Server closed and captive portal disabled")
-							wifi.joinWithAgent req.body.ssid, req.body.passphrase, (err) ->
-								console.log(err) if err
-								return startServer(wifi) if err
-								console.log("Joined! Exiting.")
-								process.exit()
+				server.close()
+				iptables.delete { table: 'nat', rule: "PREROUTING -i tether -j TETHER"}, ->
+					iptables.flush 'nat', 'TETHER', ->
+						dnsServer.kill()
+						console.log("Server closed and captive portal disabled")
+						wifi.joinWithAgent req.body.ssid, req.body.passphrase, (err) ->
+							console.log(err) if err
+							return startServer(wifi) if err
+							console.log("Joined! Exiting.")
+							process.exit()

 		# Create TETHER iptables chain (will silently fail if it already exists)
 		iptables.createChain 'nat', 'TETHER', ->


Add proper support for getting original bounds for Leaflet to app.coffee

--- src/app.coffee
+++ src/app.coffee
@@ -1,8 +1,4 @@
-#requirejs ['leaflet'], (L) ->
-#    # Allow calling original getBounds when needed.
-#    # (leaflet.activearea overrides getBounds)
-#    L.Map.prototype._originalGetBounds = L.Map.prototype.getBounds
-#
+# TODO: Rewrite this to work with r.js
 #if appSettings.sentry_url
 #    config = {}
 #    if appSettings.sentry_disable
@@ -44,7 +40,8 @@
     'cs!app/base',
     'cs!app/widgets',
     'cs!app/control',
-    'cs!app/router'
+    'cs!app/router',
+    'leaflet'
 ],
 (
     Models,
@@ -74,8 +71,13 @@
     sm,
     widgets,
     BaseControl,
-    BaseRouter
+    BaseRouter,
+    L
 ) ->
+
+    # Allow calling original getBounds when needed.
+    # (leaflet.activearea overrides getBounds)
+    L.Map.prototype._originalGetBounds = L.Map.prototype.getBounds

     DEBUG_STATE = appSettings.debug_state
     VERIFY_INVARIANTS = appSettings.verify_invariants


Remove stray Continue

Remove stray Continue


[fix] Stopped showing the ticket also when removing it from a milestone (4m)

--- app/assets/javascripts/houston/roadmap/views/edit_milestone_view.coffee
+++ app/assets/javascripts/houston/roadmap/views/edit_milestone_view.coffee
@@ -76,9 +76,11 @@

   removeTicket: (e)->
     e.preventDefault()
+    e.stopImmediatePropagation()
     $button = $(e.target)
     $button.prop 'disabled', true
     $ticket = $button.closest('.ticket')
+    $ticket.addClass('deleting')
     id = +$ticket.attr('data-id')
     $.destroy("/roadmap/milestones/#{@id}/tickets/#{id}")
       .error =>


Correct failing test

Correct failing test

--- test/signers/v4.spec.coffee
+++ test/signers/v4.spec.coffee
@@ -117,7 +117,10 @@

   describe 'canonicalString', ->
     it 'sorts the search string', ->
-      req = new AWS.CloudSearchDomain({endpoint: 'host.domain.com'}).search({query: 'foo', cursor: 'initial', queryOptions: '{}'}).build()
+      req = new AWS.CloudSearchDomain({endpoint: 'host.domain.com'})
+        .search({query: 'foo', cursor: 'initial', queryOptions: '{}'})
+        .removeListener('build', AWS.CloudSearchDomain.prototype.convertGetToPost)
+        .build()
       signer = new AWS.Signers.V4(req.httpRequest, 'cloudsearchdomain')
       expect(signer.canonicalString().split('\n')[2]).to.equal('cursor=initial&format=sdk&pretty=true&q=foo&q.options=%7B%7D')


DE16815 added warning message to Kanban App when workspace DnD ranking is disabled

--- kanban/KanbanAppSpec.coffee
+++ kanban/KanbanAppSpec.coffee
@@ -224,6 +224,15 @@
         @click(className: 'scroll-backwards').then =>
           expect(columnToShow.hidden).toBe false

+  it 'should show warning message when workspace DnD ranking disabled', ->
+    @createApp({},
+      DragDropRankingEnabled: false
+      renderTo: @createSmallContainer()
+    ).then =>
+      notification = @app.down('.rallynotification')
+      @waitForVisible(notification.getEl().dom).then =>
+        expect(@app.down('.rallynotification').message).toEqual 'Drag and drop re-ranking is disabled for Manual Rank Workspaces.'
+
   helpers
     createApp: (settings = {}, options = {}) ->
       @app = Ext.create 'Rally.apps.kanban.KanbanApp',
@@ -232,6 +241,9 @@
             project:
               _ref: @projectRef
               Name: 'Project 1'
+            workspace:
+              WorkspaceConfiguration:
+                DragDropRankingEnabled: if Ext.isDefined(options.DragDropRankingEnabled) then options.DragDropRankingEnabled else true
         )
         settings: settings
         renderTo: options.renderTo || 'testDiv'


fix: test case

fix: test case

--- tests/integration/components/settings-split-test.coffee
+++ tests/integration/components/settings-split-test.coffee
@@ -13,4 +13,4 @@

   @render hbs """{{settings-split}}"""

-  assert.equal @$().text().trim(), 'GeneralSecurityDeveloper SettingsNamespace  no namespace+ Add NamespaceAdd NamespaceAdd NamespaceLanguageSelect the preferred language belowEnglish日本語JIRA IntegrationIntegrate JIRAAre you sure you want to revoke JIRA Integration?CancelOkGitHub IntegrationIntegrate GitHubAre you sure you want to revoke Github Integration?CancelOk'
+  assert.equal @$().text().trim(), 'GeneralSecurityDeveloper SettingsNamespaceYou can add a new namespace below  no namespace+ Add NamespaceAdd NamespaceAdd NamespaceLanguageSelect the preferred language belowEnglish日本語JIRA IntegrationIntegrate JIRAAre you sure you want to revoke JIRA Integration?CancelOkGitHub IntegrationIntegrate GitHubAre you sure you want to revoke Github Integration?CancelOk'


<model> Driver - Distinction Driver for sensors and/or actuators

--- server/models/driver.coffee
+++ server/models/driver.coffee
@@ -14,12 +14,17 @@

 const DRIVERS_DIR = '../drivers/'

+actuatorsDrivers = null
 sensorsDrivers = null # List of drivers supported by the system - Must be set when server starts.
-module.exports.setDrivers = (ref) -> sensorsDrivers = ref
+module.exports.setDrivers = (refS, refA) ->
+	sensorsDrivers = refS
+	actuatorsDrivers = refA

 module.exports = class Driver extends cozydb.CozyModel
 	@schema:
 		name: 		type : String		# not Empty
+		isActuator:	type : Boolean		# not Empty
+		isSensor:	type : Boolean		# not Empty

 	###
@@ -125,7 +130,10 @@
 						# Initialize the driver:
 						unless driverModule = require dirName + file.name
 							callback 'Couldn\'t find module', null
-						unless driverModule.add and driverModule.remove and driver.update and driver.init and driver.apply
+						unless isActuator = driverModule.isActuator or isSensor = driverModule.isSensor # or (!isActuator and !isSensor)
+							callback 'Driver not defining if for Sensors and/or Actuators', null
+
+						unless driverModule.add and driverModule.remove and driver.update and driver.apply and (!isActuator or driver.init)
 							callback 'Driver not implementing the interface', null

 						driverModule.init (err) ->
@@ -135,4 +143,13 @@
 								# If everything worked, save the Driver instance in DB:
 								data =
 									name: file.name
-								superCreate data, callback+									isSensor: isSensor
+									isActuator: isActuator
+								superCreate data, (err, driver) ->
+									if err
+										callback 'Error saving the driver in DB', null
+									else
+										sensorsDrivers[driver.name] = driverModule if driver.isSensor
+										actuatorsDrivers[driver.name] = driverModule if driver.isActuator
+										callback null, driver
+


Strip source of \r before comparison

Strip source of \r before comparison

--- server/models/submit.coffee
+++ server/models/submit.coffee
@@ -66,7 +66,7 @@
     return @user == other.user \
         and @problem == other.problem \
         and outcomeType(@outcome) == outcomeType(other.outcome) \
-        and @source == other.source \
+        and @source.replace("\r", "") == other.source.replace("\r", "") \
         and (@language == other.language \
              or Math.abs(@time - other.time) < 1500 \
              or Math.abs(Math.abs(@time - other.time) - 3 * 60 * 60 * 1000) < 1500)


Bug fix for IE8-9: don't rely on the strict mode

--- src/chaplin/controllers/controller.coffee
+++ src/chaplin/controllers/controller.coffee
@@ -39,9 +39,8 @@
   # Convenience method to publish the `!composer:compose` event. See the
   # composer for information on parameters, etc.
   compose: (name) ->
-    retrieve = (arguments.length is 1)
-    name = if retrieve then 'retrieve' else 'compose'
-    mediator.execute "composer:#{name}", arguments...
+    method = if arguments.length is 1 then 'retrieve' else 'compose'
+    mediator.execute "composer:#{method}", arguments...

   # Redirection
   # -----------


activity: assign active class when a sidebar link is clicked

--- client/app/lib/components/sidebarlist/sidebarlistitem.coffee
+++ client/app/lib/components/sidebarlist/sidebarlistitem.coffee
@@ -7,10 +7,16 @@

 module.exports = class SidebarListItem extends React.Component

+  constructor: (props) ->
+    super props
+
+    @state = { clicked: no }
+
+
   getClassName: ->
     classnames
       SidebarListItem        : yes
-      active                 : @props.active
+      active                 : @props.active ? @state.clicked

   renderUnreadCount: ->
@@ -22,9 +28,14 @@

+  onClick: (args...) ->
+
+    @setState { clicked: yes }, => @props.onClick? args...
+
+
   render: ->

-    <Link className={@getClassName()} href={@props.href} onClick={@props.onClick}>
+    <Link className={@getClassName()} href={@props.href} onClick={@bound 'onClick'}>

       {@props.title}
       {@renderUnreadCount()}


Removes an extra 't'

Removes an extra 't'

--- app/data/locations/bentonparkview.cson
+++ app/data/locations/bentonparkview.cson
@@ -1,6 +1,6 @@
 name: "Benton Park View"
 address: "Benton Park Road, Newcastle, NE7 7NF"
-contact: "Chris Harriott - 0191 216 2861"
+contact: "Chris Harriot - 0191 216 2861"
 map:
   lon: 55.003361
   lat: -1.580851


Improve image and chart display login

Improve image and chart display login

Improve image and chart display logic by always having one logo image
or chart and not resdisplaying that chart or image in the results
section.

--- _includes/simulation.coffee
+++ _includes/simulation.coffee
@@ -387,31 +387,11 @@
   )

-card_bind = (type, tag, take_func = id) ->
-  ### Bind data to a card selection
-
-  Args:
-    type: the type of the data either image or youtube
-    tag: the tag element to append to
-    take_func: how much of the data to append
-
-  Returns:
-    a function to bind the data
-  ###
-  sequence(
-    (x) -> x.data,
-    filter((x) -> x.type is type),
-    take_func,
-    select_tag(tag),
-  )
-
-
-add_card = (addf, type, tag, with_div = id, take_func = take(1)) ->
+add_card = (addf, tag, with_div = id) ->
   ### Constuct a data card

   Args:
     addf: the content of the card
-    type: the type of the data either image or youtube
     tag: the tag element to append to
     with_div: function to add an extra div for columns
     take_func: data preprocessing function before bind
@@ -420,8 +400,8 @@
     a function to build the card
   ###
   sequence(
-    card_bind(type, tag, take_func = take_func),
-    with_div,
+    select_tag(tag)
+    with_div
     build_card(addf)
   )

@@ -481,7 +461,7 @@
     .attr('style', 'background-color: white;'))

-take_data = (chart_data) ->
+vegarize = (chart_data) ->
   ### Generate function to combine a Vega plot and its data

   Args:
@@ -494,18 +474,6 @@
     map(combine_data(chart_data)),
     map(update_data)
   )
-
-
-add_card_col = (addf, type, take_func) ->
-  ### Construct a data card in columns and rows for #images
-
-  Args:
-    addf: the content of the card
-    type: the type of the data either image, line or youtube
-    take_func: the data preprocessing function
-  ###
-  with_div = (x) -> x.append('div').attr('class', 'col s4')
-  add_card(addf, type, '#images', with_div = with_div, take_func = take_func)

 build = (data, sim_name, codes_data, chart_data) ->
@@ -527,10 +495,21 @@
   date(data)
   software(data, codes_data)
   results_table(data)
-  add_card(add_image, 'image', '#logo_image')(data)
-  add_card(add_youtube, 'youtube', '#youtube')(data)
-  add_card_col(add_image, 'image', id)(data)
-  add_card_col(add_chart, 'line', take_data(chart_data))(data)
-
+
+  result_data = groupBy(((x) -> x.type), data.data)
+  vega_data = vegarize(chart_data)(result_data.line)
+
+  if result_data.image?
+    add_card(add_image, '#logo_image', with_div = id)(result_data.image[0..0])
+    result_data.image = result_data.image[1..]
+  else if vega_data.length > 0
+    add_card(add_chart, '#logo_image', with_div = id)(vega_data[0..0])
+    vega_data = vega_data[1..]
+
+  with_div = (x) -> x.append('div').attr('class', 'col s4')
+  add_card(add_image, '#images', with_div = with_div)(result_data.image)
+  # with_div = (x) -> x.append('div').attr('class', 'col s4')
+  add_card(add_chart, '#images', with_div = with_div)(vega_data)
+  add_card(add_youtube, '#youtube', with_div = id)(result_data.youtube[0..0])

 build(DATA, SIM_NAME, CODES_DATA, CHART_DATA)


More approachable description

More approachable description

--- scripts/yubikey-vrbo.coffee
+++ scripts/yubikey-vrbo.coffee
@@ -1,5 +1,5 @@
 # Description:
-#   Load a random rental property from VRBO based on emorrise yubikey emissions
+#   Load a random rental property from VRBO based on accidental yubikey emissions, probably from emorisse
 #
 # Dependencies:
 #


resurrection: comment out view all notifications link

--- client/Main/avatararea/avatarareanotificationspopup.coffee
+++ client/Main/avatararea/avatarareanotificationspopup.coffee
@@ -23,15 +23,15 @@

     @avatarPopupContent.addSubView @listController.getView()

-    @avatarPopupContent.addSubView new KDView
-      height   : "auto"
-      cssClass : "sublink"
-      partial  : "View all of your activity notifications..."
-      click    : =>
-        appManager = KD.getSingleton "appManager"
-        appManager.open('Inbox')
-        appManager.tell 'Inbox', "goToNotifications"
-        @hide()
+    # @avatarPopupContent.addSubView new KDView
+    #   height   : "auto"
+    #   cssClass : "sublink"
+    #   partial  : "View all of your activity notifications..."
+    #   click    : =>
+    #     appManager = KD.getSingleton "appManager"
+    #     appManager.open('Inbox')
+    #     appManager.tell 'Inbox', "goToNotifications"
+    #     @hide()

   hide:->
     if KD.isLoggedIn()


account/shortcuts: cancel out current recording session when facade is disposed

--- client/account/lib/views/accounteditshortcutsfacade.coffee
+++ client/account/lib/views/accounteditshortcutsfacade.coffee
@@ -69,6 +69,7 @@
 #
 exports.dispose = ->

+  recorder.cancel()
   getShortcuts().removeListener 'change', changeHandler
   entities = {}
   running = no


Constrain comments to viewport.

Constrain comments to viewport.

--- common/lib/xmodule/xmodule/js/src/annotatable/display.coffee
+++ common/lib/xmodule/xmodule/js/src/annotatable/display.coffee
@@ -52,7 +52,9 @@
             at: 'top center' # of target
             target: 'mouse'
             container: @$(@wrapperSelector)
+            viewport: true,
             adjust:
+                method: 'none shift'
                 mouse: false # dont follow the mouse
         show:
             event: 'click'


Set the default refreshAfter time to 200ms, because 1s was too laggy. 200ms feels as fast as it should, and if you have some highly interactive dependencies on text control input, just set it to something even lower.

--- js/controls/textcontrol/textcontrol.coffee
+++ js/controls/textcontrol/textcontrol.coffee
@@ -24,7 +24,7 @@
       textIndent: 0
       fontSize: '10px'
       color: '#666'
-    refreshAfter:   1
+    refreshAfter:   0.2 # 200ms
     refreshOnBlur:  true
     refreshOnInput: true
     refreshOnIdle:  true


workers/social: add permit check

workers/social: add permit check

--- workers/social/lib/social/models/group/groupdata.coffee
+++ workers/social/lib/social/models/group/groupdata.coffee
@@ -54,15 +54,16 @@
   #   Provide the path you would like to fetch.
   #
   # @return {Object} persisted value.
-  @fetchByKey = secure (client, path, callback) ->
-    # for more granular control, specify each sub key as well.
-    availableKeys = [ 'countly' ]
+  @fetchByKey = permit 'open group',
+    success: (client, path, callback) ->
+      # for more granular control, specify each sub key as well.
+      availableKeys = [ 'countly' ]

-    unless path in availableKeys
-      return callback new Error 'path is forbidden'
+      unless path in availableKeys
+        return callback new Error 'path is forbidden'

-    slug = client?.context?.group
-    JGroupData.fetchDataAt slug, path, callback
+      slug = client?.context?.group
+      JGroupData.fetchDataAt slug, path, callback

   @fetchDataAt = (slug, path, callback) ->


Tools now have displayName in their manifest; fixed test.

--- test/unit/model/view.coffee
+++ test/unit/model/view.coffee
@@ -10,7 +10,8 @@
     beforeEach ->
       @tool = Cu.Model.Tool.findOrCreate
         name: 'test-plugin'
-        displayName: 'Test Plugin'
+        manifest:
+          displayName: 'Test Plugin'

       @view = Cu.Model.View.findOrCreate
         user: 'test'
@@ -19,7 +20,7 @@

     it 'has a related tool', ->
       tool = @view.get('tool')
-      tool.get('displayName').should.equal 'Test Plugin'
+      tool.get('manifest').displayName.should.equal 'Test Plugin'

 class TestDb
   class Model


added tests

added tests

--- services/web/test/UnitTests/coffee/Compile/ClsiManagerTests.coffee
+++ services/web/test/UnitTests/coffee/Compile/ClsiManagerTests.coffee
@@ -346,7 +346,49 @@

 			it "should set to main.tex", ->
 				@request.compile.rootResourcePath.should.equal "main.tex"
-
+
+		describe "when there is no valid root document and no main.tex document", ->
+			beforeEach () ->
+				@project.rootDoc_id = "not-valid"
+				@docs = {
+					"/other.tex": @doc_1 = {
+						name: "other.tex"
+						_id: "mock-doc-id-1"
+						lines: ["Hello", "world"]
+					},
+					"/chapters/chapter1.tex": @doc_2 = {
+						name: "chapter1.tex"
+						_id: "mock-doc-id-2"
+						lines: [
+							"Chapter 1"
+						]
+					}
+				}
+				@ProjectEntityHandler.getAllDocs = sinon.stub().callsArgWith(1, null, @docs)
+				@ClsiManager._buildRequest @project, null, @callback
+
+			it "should report an error", ->
+				@callback.calledWith(new Error("no main file specified")).should.equal true
+
+
+		describe "when there is no valid root document and a single document which is not main.tex", ->
+			beforeEach (done) ->
+				@project.rootDoc_id = "not-valid"
+				@docs = {
+					"/other.tex": @doc_1 = {
+						name: "other.tex"
+						_id: "mock-doc-id-1"
+						lines: ["Hello", "world"]
+					}
+				}
+				@ProjectEntityHandler.getAllDocs = sinon.stub().callsArgWith(1, null, @docs)
+				@ClsiManager._buildRequest @project, null, (@error, @request) =>
+					done()
+
+			it "should set io to the only file", ->
+				@request.compile.rootResourcePath.should.equal "other.tex"
+
+
 		describe "with the draft option", ->
 			it "should add the draft option into the request", (done) ->
 				@ClsiManager._buildRequest @project_id, {timeout:100, draft: true}, (error, request) =>


revise to optimization friendly version

revise to optimization friendly version

--- lib/index.coffee
+++ lib/index.coffee
@@ -1,10 +1,13 @@
-
 getOptions = require './get-options'
 Chain = require './chain'

-module.exports = (args...) ->
+module.exports = ->

-  options = getOptions args...
+  # optimization friendly method
+  args = new Array arguments.length
+  args[i] = arguments[i] for i in [0...arguments.length]
+
+  options = getOptions.apply null, args

   new Chain options


Remove debugger

Remove debugger

--- drivers/javascript/net.coffee
+++ drivers/javascript/net.coffee
@@ -299,7 +299,6 @@
             while i < data.length
                 totalBuf.set(i+4, data.get(i))
                 i++
-            debugger

         @write totalBuf


Changed default camera elevation.

Changed default camera elevation.

--- src/ProjectUtils.coffee
+++ src/ProjectUtils.coffee
@@ -117,7 +117,7 @@
     location = Projects.getLocationCoords(projectId)
     return unless location
     if location.latitude? and location.longitude?
-      location.elevation ?= 5000
+      location.elevation ?= 3000
       Logger.debug 'Loading project location', location
       AtlasManager.zoomTo
         position: location


Add a few more standard ports

Add a few more standard ports

--- lib/models/localhost-proxy.coffee
+++ lib/models/localhost-proxy.coffee
@@ -5,7 +5,7 @@
   constructor: (port) ->
     @port = port
     @remoteHost = 'http://ile.learn.co'
-    @desiredPorts = ['3000', '4000', '8000', '9393']
+    @desiredPorts = ['3000', '4000', '4567', '8000', '9292', '9393']

   start: ->
     @withAvailablePort((ports) =>


Refactor proxy specs a bit

Refactor proxy specs a bit

--- test/unit/proxy.spec.coffee
+++ test/unit/proxy.spec.coffee
@@ -4,32 +4,30 @@
   fsMock = require('mocks').fs
   httpMock = require('mocks').http
   loadFile = require('mocks').loadFile
-  actualOptions = requestedUrl = null
+
+  actualOptions = requestedUrl = response = null
+
   # async helper
   waitForFinishingResponse = ->
     waitsFor (-> response._isFinished()), 'Finish response', 100
-  mocks = {}
+
+  m = loadFile __dirname + '/../../lib/proxy.js', {'http-proxy': {}}
+
+  mockProxy =
+    on: ->
+    proxyRequest: (req, res, opt) ->
+      actualOptions = opt
+      requestedUrl = req.url
+      res.writeHead 200
+      res.end 'DONE'

   beforeEach util.disableLogger
-
-  mocks['http-proxy'] = {}
-  globals = process: {}
-
-  m = loadFile __dirname + '/../../lib/proxy.js', mocks, globals
-
-  mockProxy = {}
-  mockProxy.on = ->
-  mockProxy.proxyRequest = (req, res, opt) ->
-    actualOptions = opt
-    requestedUrl = req.url
-    res.writeHead 200
-    res.end 'DONE'
-  response = null

   beforeEach ->
     actualOptions = {}
     requestedUrl = ''
     response = new httpMock.ServerResponse
+

   it 'should proxy requests', ->
     proxy = m.createProxyHandler mockProxy, {'/proxy': 'http://localhost:9000'}
@@ -41,6 +39,7 @@
       expect(requestedUrl).toEqual '/test.html'
       expect(actualOptions).toEqual {host: 'localhost', port: '9000'}

+
   it 'should support multiple proxies', ->
     proxy = m.createProxyHandler mockProxy, {'/proxy': 'http://localhost:9000', '/static': 'http://gstatic.com'}
     expect(proxy new httpMock.ServerRequest('/static/test.html'), response).toBeTruthy()
@@ -49,6 +48,7 @@
     runs ->
       expect(requestedUrl).toEqual '/test.html'
       expect(actualOptions).toEqual {host: 'gstatic.com', port: '80'}
+

   it 'should handle nested proxies', ->
     proxy = m.createProxyHandler mockProxy, {'/sub': 'http://localhost:9000', '/sub/some': 'http://gstatic.com/something'}
@@ -59,15 +59,18 @@
       expect(requestedUrl).toEqual '/something/Test.html'
       expect(actualOptions).toEqual {host: 'gstatic.com', port: '80'}

+
   it 'should parse a simple proxy config', ->
     proxy = {'/base/': 'http://localhost:8000/'}
     parsedProxyConfig = m.parseProxyConfig proxy
     expect(parsedProxyConfig).toEqual({'/base/': {host: 'localhost', port: '8000', baseProxyUrl: '/'}})
+

   it 'should handle proxy configs with paths', ->
     proxy = {'/base': 'http://localhost:8000/proxy'}
     parsedProxyConfig = m.parseProxyConfig proxy
     expect(parsedProxyConfig).toEqual({'/base': {host: 'localhost', port: '8000', baseProxyUrl: '/proxy'}})

+
   it 'should handle empty proxy config', ->
     expect(m.parseProxyConfig {}).toEqual({})


fixed a bug in the native extensions regex.

--- app/assets/javascripts/habberdash/dependencies/native_extensions.js.coffee
+++ app/assets/javascripts/habberdash/dependencies/native_extensions.js.coffee
@@ -2,4 +2,4 @@
 String::toCamel = -> @replace(/([\-|_][a-z])/g, ($1) -> $1.toUpperCase().replace(/[\-|_]/, ''))
 String::toDash = -> @replace(/([A-Z])/g, ($1) -> "-#{$1.toLowerCase()}").replace(/\s+/, '-').replace(/^-|-$/, '').replace(/-+/, '-')
 String::toUnderscore = -> @replace(/([A-Z])/g, ($1) -> "_#{$1.toLowerCase()}")
-String::toSlug = -> @replace(/[\\\/-\s\?#\.]+/g, '-').toLowerCase()
+String::toSlug = -> @replace(/[\/\\\-\s\?\#\.]+/g, '-').toLowerCase()


fix layout constants for slightly better layout

--- app/scripts/alchemy/core/layout.coffee
+++ app/scripts/alchemy/core/layout.coffee
@@ -17,36 +17,38 @@
 class alchemy.Layout
     constructor: ->
         conf = alchemy.conf
-        @k = Math.sqrt(_.size(alchemy._nodes) / (conf.graphWidth() * conf.graphHeight()))
+        nodes = alchemy._nodes
+        @k = Math.sqrt(Math.log(_.size(alchemy._nodes)) / (conf.graphWidth() * conf.graphHeight() ) )
         @_clustering = new alchemy.clustering

         if conf.cluster
             @_charge = () -> @_clustering.layout.charge
+            @_linkStrength = (edge) -> @_clustering.layout.linkStrength(edge)
+
         else
             @_charge = () -> -10 / @k
+            @_linkStrength = (edge) ->
+                if nodes[edge.source.id].properties.root or nodes[edge.target.id].properties.root
+                    1
+                else
+                    0.9

         if conf.cluster
             @_linkDistancefn = (edge) -> @_clustering.layout.linkDistancefn(edge)
-        else if typeof conf.linkDistancefn is ('number' or 'string')
+        else if conf.linkDistancefn is 'default'
+            @_linkDistancefn = (edge) ->
+                1 / (@k * 50)
+        else if typeof conf.linkDistancefn is 'number'
             @_linkDistancefn = (edge) -> conf.linkDistancefn
         else if typeof conf.linkDistancefn is 'function'
-            conf.linkDistancefn(edge)
+            @_linkDistancefn = (edge) -> conf.linkDistancefn(edge)

-        if alchemy.conf.cluster
-            @_linkStrength = (edge) -> @_clustering.layout.linkStrength(edge)
-        else
-            @_linkStrength = (edge) ->
-                nodes = alchemy._nodes
-                if nodes[edge.source.id].properties.root or nodes[edge.target.id].properties.root
-                    0.5
-                else
-                    0.8

     gravity: () =>
         if alchemy.conf.cluster
             @_clustering.layout.gravity(@k)
         else
-            100 * @k
+            50 * @k

     linkStrength: (edge) =>
         @_linkStrength(edge)


Can find neighbor ids from node

Can find neighbor ids from node

Uses a crazy ugly regex.  Should find another way to do it but leaving
as is for the time being.

--- app/scripts/alchemy/models/node.coffee
+++ app/scripts/alchemy/models/node.coffee
@@ -5,4 +5,13 @@
         # Merge undefined nodeStyle keys from conf.
         # Works with undefined @nodeStyle
         conf = alchemy.conf
-        @nodeStyle = _.merge(conf.nodeStyle, @nodeStyle)+        @nodeStyle = _.merge(conf.nodeStyle, @nodeStyle)
+
+    # Stores edge.id for easy edge lookup
+    edges: []
+    addEdge: (edge)-> @edges.push(edge)
+
+    # Neighbors
+    neighbors: ()->
+        regex = new RegExp("[(#{@id}#{'\\'}-)(#{'\\'}-#{@id})]","g")
+        _.map @edges, (edgeID)->  edgeID.replace(regex, "")


Add uglify task to grunt

Add uglify task to grunt

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -6,10 +6,14 @@
       compile:
         files:
           'chrome-app/lib/pomodoro.js': ['src/*.coffee']
+    uglify:
+      build:
+        src: 'chrome-app/lib/pomodoro.js',
+        dest: 'chrome-app/lib/pomodoro.min.js'

   grunt.loadNpmTasks "grunt-contrib-concat"
   grunt.loadNpmTasks('grunt-contrib-uglify');
   grunt.loadNpmTasks('grunt-contrib-coffee');

-  grunt.registerTask "default", ["coffee"]+  grunt.registerTask "default", ["coffee","uglify"]


also reload on change to index.html

also reload on change to index.html

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -29,7 +29,7 @@
         files: mainProcessScripts
         tasks: ['coffeelint', 'restart-electron']
       rendererProcess:
-        files: mainProcessScripts
+        files: mainProcessScripts.concat('index.html')
         tasks: ['compile-render', 'reload-electron']
       auxScripts:
         files: auxScripts


use spec reporter

use spec reporter

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -273,6 +273,8 @@

     mocha:
       src: ['test/index.html']
+      options:
+        reporter: 'Spec'

     browserify:
       compile:


Gruntfile: Don't need to run templates task right now.

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -100,10 +100,10 @@
           '--output_wrapper="(function(){%output%})();"'
           '--compilation_level="ADVANCED_OPTIMIZATIONS"'
           '--warning_level="VERBOSE"'
-          '--externs=src/externs/externs.js'
+          # '--externs=src/externs/externs.js'
           '--jscomp_error=accessControls'
           '--jscomp_error=checkRegExp'
-          # '--formatting=PRETTY_PRINT'
+          '--formatting=PRETTY_PRINT'
           '--jscomp_error=checkTypes'
           '--jscomp_error=checkVars'
           '--jscomp_error=invalidCasts'
@@ -130,7 +130,7 @@
       'coffee'
       'coffee2closure'
       'stylus'
-      'templates'
+      # 'templates'
       'deps'
       'karma'
       'builder'
@@ -144,7 +144,7 @@
       'coffee'
       'coffee2closure'
       'stylus'
-      'templates'
+      # 'templates'
       'deps'
       'builder'
     ]
@@ -157,7 +157,7 @@
       'coffee'
       'coffee2closure'
       'stylus'
-      'templates'
+      # 'templates'
       'deps'
       'builder'
       'karma'


Update uglify task options

Update uglify task options

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -9,14 +9,27 @@
           'dist/jquery-textfade.js': 'src/jquery-textfade.litcoffee'

     # -> uglifyjs dist/jquery-textfade.js -o dist/jquery-textfade.min.js -m -c --comments '/^!/'
+    # uglify:
+    #   options:
+    #     compress:         {}
+    #     mangle:           true
+    #     preserveComments: (_, comment) -> /^!/.test comment.value
+    #   minify:
+    #     files:
+    #       'dist/jquery-textfade.min.js': 'dist/jquery-textfade.js'
+
     uglify:
       options:
-        compress:         {}
-        mangle:           true
-        preserveComments: (_, comment) -> /^!/.test comment.value
-      minify:
+        sourceMap:
+          filename: 'jquery-textfade.js'
+          url: 'jquery-textfade.js.map'
+        output:
+          comments: (_, comment) -> /^!/.test comment.value
+      dist:
         files:
           'dist/jquery-textfade.min.js': 'dist/jquery-textfade.js'
+
+

     # -> coffee --watch --output dist src
     watch:


temp: testing with a Uno

temp: testing with a Uno

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -135,7 +135,7 @@
       tablelights_run: './spec/microflo-linux.sh TableLights 4444 & sleep 5'
       portallights_arduino_gen: microflo_gen 'PortalLights', { target: 'arduino', ext: 'ino.tmpl' }
       portallights_arduino_build: arduino_build 'PortalLights',
-        board: 'arduino:avr:leonardo'
+        board: 'arduino:avr:uno'
         ext: 'ino'
       portallights_linux_gen: microflo_gen 'PortalLights'
       portallights_linux_comp: microflo_compile 'PortalLights'


Copies lib jquery to test directory for tests.

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -155,6 +155,15 @@
           {
             src:
               [
+                'jquery-2.0.2.min.js'
+              ]
+            dest: 'test/lib'
+            expand: true
+            cwd: 'lib'
+          }
+          {
+            src:
+              [
                 '**'
               ]
             dest: 'test/fonts'


Add a Grunt task

Add a Grunt task

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -151,7 +151,7 @@
         overwrite: true
         replacements: [
           from: /(['"])0\.0\.X(['"])/
-          to: '$10.0.2$2'
+          to: '$10.0.X$2'
         ]

   grunt.registerTask 'build', [
@@ -180,5 +180,6 @@
   # Aliases
   grunt.registerTask 'default', ['build']
   grunt.registerTask 'test', ['testem:main']
+  grunt.registerTask 'testall', ['testem:all_launchers']
   grunt.registerTask 'node', ['build:node']
   grunt.registerTask 'node', ['build:node']


Gruntfile: Added live reload feature to watcher.

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -90,10 +90,14 @@
         configFile   : 'karma.conf.js'

     watch            :
+      options            :
+        livereload       : yes
       configFiles    :
         files        : [ 'Gruntfile.coffee', 'karma.conf.js' ]
         options      :
           reload     : yes
+      html               :
+        files            : [ '*.html' ]
       src            :
         files        : [ '**/*.coffee' ]
         tasks        : [ 'clean', 'mkdir', 'coffee', 'coffee2closure', 'deps', 'karma' ]


Updated Gruntfile to update component.json based on package information.

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -194,6 +194,8 @@

       newArray = JSON.stringify fileList
       data     = data.replace /"main": \[[^\]]+]/, "\"main\": #{newArray}"
+      data     = data.replace /"name": [^,]+/ , "\"name\": \"#{grunt.config.get("pkg").name}\""
+      data     = data.replace /"version": [^,]+/, "\"version\": \"#{grunt.config.get("pkg").version}\""

       fs.writeFile componentFile, data, "utf8", (err, data) ->
         grunt.log.writeln "Updated component.json"


feat(grunt): cleanup syntax

feat(grunt): cleanup syntax

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -188,7 +188,7 @@
           expand: true
           cwd: "<%= config.dist %>"
           src: "**/*.html"
-          dest: "<%= config.dist %>/"
+          dest: "<%= config.dist %>"
         ]

     xmlmin:
@@ -197,7 +197,7 @@
           expand: true
           cwd: "<%= config.dist %>"
           src: "**/*.xml"
-          dest: "<%= config.dist %>/"
+          dest: "<%= config.dist %>"
         ]

     minjson:
@@ -206,7 +206,7 @@
           expand: true
           cwd: "<%= config.dist %>"
           src: "**/*.json"
-          dest: "<%= config.dist %>/"
+          dest: "<%= config.dist %>"
         ]

     cssmin:
@@ -218,7 +218,7 @@
           expand: true
           cwd: "<%= config.dist %>"
           src: ["**/*.css", "!*.min.css"]
-          dest: "<%= config.dist %>/"
+          dest: "<%= config.dist %>"
         ]

       # html:
@@ -243,7 +243,7 @@
           expand: true
           cwd: "<%= config.dist %>"
           src: "**/*.html"
-          dest: "<%= config.dist %>/"
+          dest: "<%= config.dist %>"
         ]

     cacheBust:
@@ -259,7 +259,7 @@
           baseDir: "<%= config.dist %>"
           cwd: "<%= config.dist %>"
           src: "**/*.html"
-          dest: "<%= config.dist %>/"
+          dest: "<%= config.dist %>"
         ]

     usebanner:
@@ -342,7 +342,7 @@
           {
             expand: true
             dot: true
-            cwd: "<%= amsf.core %>/"
+            cwd: "<%= amsf.core %>"
             src: [
               ".*"
               "*.json"
@@ -366,7 +366,7 @@
               "*.txt"
               "*.xml"
             ]
-            dest: "<%= config.app %>/"
+            dest: "<%= config.app %>"
           }
           {
             expand: true


Fix 404 on our js on windows.

Fix 404 on our js on windows.

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -31,7 +31,7 @@
                 rename : (dest, matchedSrcPath, options) ->
                     r = /^(frontend)\/.*(.js)$/
                     if r.test matchedSrcPath
-                        fileName = matchedSrcPath.split path.sep
+                        fileName = matchedSrcPath.split '/'
                         fileName = fileName[fileName.length - 1]
                         path.join 'www', 'public', 'js', fileName
                     else


Add task to run after deploying to production

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -97,6 +97,10 @@
     express:
       dev:
         spawn: false
+      prod:
+        options:
+          background: false
+          livereload: false
       server:
         options:
           background: false
@@ -226,3 +230,5 @@
   grunt.registerTask 'test:server', ['build', 'shell:mocha']
   grunt.registerTask 'test:client', ['browserify:tests', 'testem:ci:basic']
   grunt.registerTask 'test', ['clean:all', 'test:server', 'test:client']
+
+  grunt.registerTask 'heroku:production', ['minify']


dont't use source map.

dont't use source map.

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -40,7 +40,6 @@
         ]
       develop:
         options:
-          sourceMap: true
           bare: true
         files: [
           expand: true


Scope vulcanization to current directory

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -55,6 +55,7 @@
     exec:
       vulcanize:
         command: 'node_modules/.bin/polymer-bundler --strip-comments index.dist.html > index.html'
+        cwd: __dirname

     # CoffeeScript compilation of tests
     coffee:


Revert to using CoffeeScript plugin options: bare: true

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -16,6 +16,8 @@
       controls:
         files:
           "controls/controls.js": sortDependencies.sortFiles "controls/*.coffee"
+        options:
+          bare: true
       test:
         files:
           "test/unittests.js": "test/*.coffee"


fixing Gruntfile

fixing Gruntfile

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -410,8 +410,8 @@
       generic_core:
         src: FILES.jasmine_helpers
             .concat [
+              'build/compile-src/mocks/freedom-mocks.js'
               'node_modules/uproxy-lib/dist/logging/logging.js'
-              'build/compile-src/mocks/freedom-mocks.js'
               'build/compile-src/socks-to-rtc/socks-to-rtc.js'
               'build/compile-src/rtc-to-net/rtc-to-net.js'
               'build/compile-src/uproxy.js'


Added timestamp variable for more accurate testing

--- test/unit/routerTest.coffee
+++ test/unit/routerTest.coffee
@@ -6,6 +6,8 @@

 describe "HTTP Router", ->

+	requestTimestamp = (new Date()).toString()
+
 	describe ".route", ->
 		it "should route an incomming request to the endpoints specific by the channel config", (done) ->
 			testUtils.createMockServer 201, "Mock response body\n", 9876, ->
@@ -25,7 +27,7 @@
 				ctx.response = new Object()
 				ctx.path = ctx.request.url = "/test"
 				ctx.request.method = "GET"
-				ctx.requestTimestamp = (new Date()).toString()
+				ctx.requestTimestamp = requestTimestamp

 				router.route ctx, (err) ->
 					if err
@@ -58,7 +60,7 @@
 			ctx.response = new Object()
 			ctx.path = ctx.request.url = "/test/multicasting"
 			ctx.request.method = "GET"
-			ctx.requestTimestamp = (new Date()).toString()
+			ctx.requestTimestamp = requestTimestamp
 			return ctx

 		it "should route an incomming https request to the endpoints specific by the channel config", (done) ->
@@ -117,12 +119,12 @@
 							ctx.routes[0].response.body.toString().should.be.eql "Mock response body 1\n"
 							ctx.routes[0].response.headers.should.be.ok
 							ctx.routes[0].request.path.should.be.exactly "/test/multicasting"
-							ctx.routes[0].request.timestamp.should.be.exactly (new Date()).toString()
+							ctx.routes[0].request.timestamp.should.be.exactly requestTimestamp
 							ctx.routes[1].response.status.should.be.exactly 400
 							ctx.routes[1].response.body.toString().should.be.eql "Mock response body 3\n"
 							ctx.routes[1].response.headers.should.be.ok
 							ctx.routes[1].request.path.should.be.exactly "/test/multicasting"
-							ctx.routes[1].request.timestamp.should.be.exactly (new Date()).toString()
+							ctx.routes[1].request.timestamp.should.be.exactly requestTimestamp
 							done()

@@ -152,7 +154,7 @@
 						ctx.response = new Object()
 						ctx.request.url = "/test/multi-primary"
 						ctx.request.method = "GET"
-						ctx.requestTimestamp = (new Date()).toString()
+						ctx.requestTimestamp = requestTimestamp

 						router.route ctx, (err) ->
 							if err
@@ -180,7 +182,7 @@
 				ctx.response = new Object()
 				ctx.request.url = "/test"
 				ctx.request.method = "POST"
-				ctx.requestTimestamp = (new Date()).toString()
+				ctx.requestTimestamp = requestTimestamp
 				ctx.body = "TestBody"

 				router.route ctx, (err) ->
@@ -211,7 +213,7 @@
 				ctx.request.url = "/test?parma1=val1&parma2=val2"
 				ctx.request.method = "GET"
 				ctx.request.querystring = "parma1=val1&parma2=val2"
-				ctx.requestTimestamp = (new Date()).toString()
+				ctx.requestTimestamp = requestTimestamp

 				router.route ctx, (err) ->
 					if err
@@ -258,7 +260,7 @@
 				ctx.response = new Object()
 				ctx.path = ctx.request.url = "/test"
 				ctx.request.method = "GET"
-				ctx.requestTimestamp = (new Date()).toString()
+				ctx.requestTimestamp = requestTimestamp

 				router.route ctx, (err) ->
 					if err
@@ -310,7 +312,7 @@
 				ctx.response = new Object()
 				ctx.path = ctx.request.url = "/test"
 				ctx.request.method = "GET"
-				ctx.requestTimestamp = (new Date()).toString()
+				ctx.requestTimestamp = requestTimestamp

 				router.route ctx, (err) ->
 					if err
@@ -365,7 +367,7 @@
 					ctx.response = new Object()
 					ctx.path = ctx.request.url = "/test"
 					ctx.request.method = "GET"
-					ctx.requestTimestamp = (new Date()).toString()
+					ctx.requestTimestamp = requestTimestamp

 					router.route ctx, (err) ->
 						if err
@@ -400,7 +402,7 @@
 				ctx.response = new Object()
 				ctx.request.url = "/test"
 				ctx.request.method = "GET"
-				ctx.requestTimestamp = (new Date()).toString()
+				ctx.requestTimestamp = requestTimestamp

 				router.route ctx, (err) ->
 					if err
@@ -427,7 +429,7 @@
 				ctx.response = new Object()
 				ctx.request.url = "/test"
 				ctx.request.method = "GET"
-				ctx.requestTimestamp = (new Date()).toString()
+				ctx.requestTimestamp = requestTimestamp

 				router.route ctx, (err) ->
 					if err
@@ -454,7 +456,7 @@
 				ctx.request.url = "/test"
 				ctx.request.method = "GET"
 				ctx.request.header = { authorization: "Basic bWU6bWU=" }
-				ctx.requestTimestamp = (new Date()).toString()
+				ctx.requestTimestamp = requestTimestamp

 				router.route ctx, (err) ->
 					if err
@@ -484,7 +486,7 @@
 				ctx.request.url = "/test"
 				ctx.request.method = "GET"
 				ctx.request.header = { authorization: "Basic bWU6bWU=" }
-				ctx.requestTimestamp = (new Date()).toString()
+				ctx.requestTimestamp = requestTimestamp

 				router.route ctx, (err) ->
 					if err
@@ -518,7 +520,7 @@
 				ctx.response = new Object()
 				ctx.path = ctx.request.url = "/test"
 				ctx.request.method = "GET"
-				ctx.requestTimestamp = (new Date()).toString()
+				ctx.requestTimestamp = requestTimestamp

 				router.route ctx, (err) ->
 					if err


allow extending controller to overwrite other options

--- lib/assets/javascripts/super_classes/angular_scoped_model.coffee
+++ lib/assets/javascripts/super_classes/angular_scoped_model.coffee
@@ -3,15 +3,18 @@
   @inject: (args...) ->
     args.push('$scope','$rootScope','$routeParams','$controller')
     @$inject = args
-  @extendController: (args...) ->
-    @$extendedControllers = args
+  @extendController:  ->
+    @$extendedControllers = [].slice.call(arguments,0)
   constructor: (args...) ->
     # Bind injected dependencies on scope ie @$scope
     for key, index in @constructor.$inject || []
       @[key] = args[index]
     # extend controllers
-    for name in @constructor.$extendedControllers || []
-      angular.extend @, @$controller name, {$scope: @$scope}
+    for controller in @constructor.$extendedControllers || []
+      name = controller.name || controller
+      opts = {$scope: @$scope}
+      opts[key] = @[val] for key,val of controller.scope || {}
+      angular.extend @, @$controller name, opts
     # Bind all functions not begining with _ to scope
     for key, val of @constructor.prototype
       continue if key in ['constructor', 'initialize'] or key[0] is '_'


Fix message of one test

Fix message of one test

--- test/unit/utils-test.coffee
+++ test/unit/utils-test.coffee
@@ -129,7 +129,7 @@
       it 'shows ~ 4 warnings', ->
         assert.lengthOf warnings, 4

-      it 'prints lines for those three warnings', ->
+      it 'prints lines for those warnings', ->
         expectedLines = [
           'lines 5-6'
           'line 12'


Workers: Added websocket

Workers: Added websocket

--- app/scripts/controllers/workers.coffee
+++ app/scripts/controllers/workers.coffee
@@ -1,11 +1,15 @@
 'use strict'

 class WorkersCtrl
-  constructor: (@scope, @WorkersFcty) ->
+  constructor: (@scope, @WorkersFcty, @WebSocketFcty) ->
     @workers = []
     @activeWorkersPercentage = 0

     @getWorkers()
+
+    @WebSocketFcty.on((message) =>
+      @getWorkers() if message.type == 'worker-status'
+    )

   getWorkers: ->
@@ -16,14 +20,7 @@
       @activeWorkersPercentage = @activeWorkers / @workerCount
     )

+
 angular.module('holmesApp')
-  .controller 'WorkersCtrl',  ($scope, WorkersFcty) ->
-    $scope.model = new WorkersCtrl($scope, WorkersFcty)
-
-    #randomizePercentage = ->
-      #percentage = Math.random()
-      #console.log percentage
-      #$scope.model.activeWorkersPercentage = percentage
-      #$timeout(randomizePercentage, 1000)
-
-    #randomizePercentage()
+  .controller 'WorkersCtrl',  ($scope, WorkersFcty, WebSocketFcty) ->
+    $scope.model = new WorkersCtrl($scope, WorkersFcty, WebSocketFcty)


Revert "Revert "run ff tests as part of ci""

This reverts commit de340c78046d31cc063bd4e797a4419303cfb256.

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -25,13 +25,13 @@
   grunt.registerTask 'nexus:deploy', 'Deploys to nexus', ['build', 'nexus:__createartifact__']

   grunt.registerTask 'check', 'Run convention tests on all files', ['regex-check']
-  grunt.registerTask 'ci', 'Does a full build an deploys to nexus', ['build', 'test', 'nexus:__createartifact__']
+  grunt.registerTask 'ci', 'Does a full build, runs tests and deploys to nexus', ['build', 'test:chrome', 'test:firefox', 'nexus:__createartifact__']

   grunt.registerTask 'test:__buildjasmineconf__', 'Internal task to build and alter the jasmine conf', ['jasmine:apps:build', 'replace:jasmine']
   grunt.registerTask 'test:conf', 'Fetches the deps, compiles coffee and css files, runs jshint and builds the jasmine test config', ['nexus:deps', 'clean:test', 'coffee', 'css', 'test:__buildjasmineconf__']
   grunt.registerTask 'test', 'Sets up and runs the tests in the default browser. Use --browser=<other> to run in a different browser.', ['test:conf', 'express:inline', 'webdriver_jasmine_runner:apps']
-  grunt.registerTask 'test:chrome', 'Sets up and runs the tests in Chrome', ['test:conf', 'webdriver_jasmine_runner:chrome']
-  grunt.registerTask 'test:firefox', 'Sets up and runs the tests in Firefox', ['test:conf', 'webdriver_jasmine_runner:firefox']
+  grunt.registerTask 'test:chrome', 'Sets up and runs the tests in Chrome', ['test:conf', 'express:inline', 'webdriver_jasmine_runner:chrome']
+  grunt.registerTask 'test:firefox', 'Sets up and runs the tests in Firefox', ['test:conf', 'express:inline', 'webdriver_jasmine_runner:firefox']
   grunt.registerTask 'test:server', 'Starts a Jasmine server at localhost:8890', ['express:server', 'express-keepalive']

   spec = (grunt.option('spec') || grunt.option('jsspec') || '*').replace(/(Spec|Test)$/, '')


MISC: remove some redundancy in Gruntfile.

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -3,33 +3,27 @@
     pkg: grunt.file.readJSON 'package.json'

     shell:
+      options:
+        failOnError: true
+
       jison:
         command: 'mkdir -p lib/; jison src/grammar.y src/lexer.l -o lib/parser.js'
-        options:
-          failOnError: true

       browserify:
         command: 'browserify -t coffeeify --extension=".coffee" -s <%= pkg.name %> src/<%= pkg.name %>.coffee > dist/<%= pkg.name %>.js;
           browserify -t coffeeify --extension=".coffee" -s <%= pkg.name %>-core src/<%= pkg.name %>-core.coffee > dist/<%= pkg.name %>-core.js;'
-        options:
-          failOnError: true

       browserify_spec:
         command: 'browserify -t coffeeify --extension=".coffee" -s <%= pkg.name %>-spec spec/<%= pkg.name %>-spec.coffee > demo/js/<%= pkg.name %>-spec.js;
           browserify -t coffeeify --extension=".coffee" -s <%= pkg.name %>-core-spec spec/<%= pkg.name %>-core-spec.coffee > demo/js/<%= pkg.name %>-core-spec.js;
           browserify -t coffeeify --extension=".coffee" -s functional-spec spec/functional-spec.coffee > demo/js/functional-spec.js;'
-        options:
-          failOnError: true

       copy_uglified:
         command: 'cp dist/<%= pkg.name %>-core.js demo/js/<%= pkg.name %>-core.js;
                   cp dist/<%= pkg.name %>.js demo/js/<%= pkg.name %>.js;'
-        failOnError: true

       push_ghpages:
         command: 'git subtree push --prefix demo origin gh-pages'
-        options:
-          failOnError: true

     jasmine_node:
       all: ['spec/']


fixed server task to not terminate

fixed server task to not terminate

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -12,6 +12,7 @@
     grunt.log.writeln "Express server listening on port 8000"
     require("./lib/app-server.js").listen 8000
     require("child_process").exec "open \"http://localhost:8000\""
+    grunt.task.run ["watch"]

   # Print a timestamp (useful for when watching)
   grunt.registerTask "timestamp", ->


Added the new `dist` folder to the `clean` task.

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -70,7 +70,7 @@
       test:
         files: ['src/test/**/*.coffee']
         tasks: ['coffeelint:test', 'coffee:test', 'simplemocha']
-    clean: ['out/']
+    clean: ['out/', 'dist/']

   grunt.event.on 'watch', (action, files, target)->
     grunt.log.writeln "#{target}: #{files} has #{action}"


removing bogus task from gruntfile

removing bogus task from gruntfile

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -13,10 +13,6 @@
             ext: '.js'
           ]

-    exec:
-      release:
-        'browserify lib/doofinder.js --standalone doofinder > dist/doofinder.js'
-
     browserify:
       default:
         src: ['lib/doofinder.js']


Fixed issue where running tests before they have been compiled doesn't run any tests.

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -46,7 +46,7 @@
   version = grunt.option('version') || '0.1.0'

   srcFiles = "src/**/*.js"
-  specFiles = grunt.file.expand ['test/**/*Spec.js']
+  specFiles = -> grunt.file.expand ['test/**/*Spec.js']

   buildDir = "builds"

@@ -169,7 +169,7 @@

     "regex-check":
       consolelogs:
-        src: [srcFiles, specFiles]
+        src: [srcFiles, specFiles()]
         options:
           pattern: /console\.log/g

@@ -177,14 +177,16 @@
       testPage:
         replacements: [{
           from: '__helperFiles__'
-          to: _.map(grunt.file.expand(["test/javascripts/helpers/**/*.js"]), (file) ->
-            "<script src=\"#{file}\"></script>"
-          ).join('\n  ')
+          to: ->
+            _.map(grunt.file.expand(["test/javascripts/helpers/**/*.js"]), (file) ->
+              "<script src=\"#{file}\"></script>"
+            ).join('\n  ')
         }, {
           from: '__specFiles__'
-          to: _.map(specFiles, (file) ->
-            "<script src=\"#{file}\"></script>"
-          ).join('\n  ')
+          to: ->
+            _.map(specFiles(), (file) ->
+              "<script src=\"#{file}\"></script>"
+            ).join('\n  ')
         }]
         src: ['test/testpage.tpl']
         dest: 'test/testpage.html'


do not load missing grunt-contrib-copy

do not load missing grunt-contrib-copy

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -57,7 +57,6 @@

   grunt.loadNpmTasks "grunt-contrib-watch"
   grunt.loadNpmTasks "grunt-contrib-clean"
-  grunt.loadNpmTasks "grunt-contrib-copy"
   grunt.loadNpmTasks "grunt-contrib-coffee"
   grunt.loadNpmTasks "grunt-mocha-cov"


Attempting to speed up grunt build

Attempting to speed up grunt build

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -14,7 +14,7 @@

 			scripts:
 				files: [ 'frontend/coffeescript/**/*.coffee' ]
-				tasks: [ 'bower', 'copy:components', 'coffee:requirejs', 'requirejs' ]
+				tasks: [ 'copy:components', 'coffee:requirejs', 'requirejs' ]

 			styles:
 				files: [ 'frontend/sass/**/*.scss' ]
@@ -93,6 +93,7 @@
 					dir: '<%= pkg.static_dir %>'
 					keepBuildDir: true
 					optimize: 'none'
+					optimizeCss: 'none'
 					modules: [ {
 						name: '../common'
 						include: [ 'jquery',


use base stack id as a template id

use base stack id as a template id

--- client/app/lib/components/sidebarstacksection/stackupdatedwidget.coffee
+++ client/app/lib/components/sidebarstacksection/stackupdatedwidget.coffee
@@ -23,7 +23,7 @@

   handleOnClick : ->
     { appManager, router } = kd.singletons
-    { templateId } =  @props.stack.get('baseTemplate').toJS()
+    templateId =  @props.stack.get 'baseStackId'
     actions.reinitStackFromWidget(@props.stack).then ->
       appManager.tell 'Stackeditor', 'reloadEditor', templateId


improved the thingsAt function to get overlapping things centered in neighboring cells

--- evolution.coffee
+++ evolution.coffee
@@ -139,9 +139,17 @@
     createCreatures Carnivore, 'carnivores'
     # keep an inert instance of each type in its initial state
     @seeds =
+      stone:     new Stone [0,0], paramsForType( 'stones', true )
       plant:     new Plant [0,0], paramsForType( 'plants', true )
       herbivore: new Herbivore [0,0], paramsForType( 'herbivores', true )
       carnivore: new Carnivore [0,0], paramsForType( 'carnivores', true )
+  # obtains the maximum radius of any thing in the universe
+  maxRadius: ->
+    return @maxRadius unless @maxRadius.nil?
+    mx = 0
+    for t, o of @seeds
+      mx = o.radius if o.radius > mx
+    @maxRadius = mx
   # iterate over all things in the universe
   visitThings: ( f, returns, safe ) ->
     ret = [] if returns
@@ -153,8 +161,15 @@
             v = f(t)
             ret.push v if returns
     ret
+  # find all things overlapping (minus appendages) overlapping a given point
   thingsAt: ( x, y ) ->
-    t for t in @cellAt( x, y ).inhabitants when t.radius > Math.sqrt( (t.x - x)**2 + (t.y - y )**2 )
+    c = @cellAt( x, y )
+    test = (t) -> t.radius > Math.sqrt( (t.x - x)**2 + (t.y - y )**2 )
+    ret = []
+    ret.push t for t in c.inhabitants when test t
+    for other in c.neighbors when other[1] <= @maxRadius
+      ret.push t for t in other[0].inhabitants when test t
+    ret
   cellAt: ( x, y ) -> @cells[ x // @cellWidth ][ y // @cellWidth ]
   # place a thing in the appropriate cell
   place: (thing, onlyMovingIn) ->


app-updates: Fix for update button.

app-updates: Fix for update button.

--- client/landing/app/Applications/Apps.kdapplication/Views/appview.coffee
+++ client/landing/app/Applications/Apps.kdapplication/Views/appview.coffee
@@ -170,9 +170,10 @@
         @openButton.show()

       appName = app.manifest.name
-      if appsController.isAppUpdateAvailable appName, manifests[appName]?.version
+      version = manifests?[appName]?.version # strange, but it's not working with { ... }
+
+      if version and appsController.isAppUpdateAvailable appName, version
         @updateButton.show()
-

     {icns, name, version, authorNick} = app.manifest
     thumb = if icns and (icns['256'] or icns['512'] or icns['128'] or icns['160'] or icns['64'])


Error names fix.

Error names fix.

--- lib/ChatServiceError.coffee
+++ lib/ChatServiceError.coffee
@@ -37,7 +37,7 @@
   # @nodoc
   toString : () ->
     str = @errorStrings[@name] || @name.unknownError
-    util.format "Error: #{str}", @args...
+    util.format "ChatServiceError: #{str}", @args...

 module.exports = ChatServiceError