VICS82 tutorial text

VICS82 tutorial text

--- app/translations/en_us.coffee
+++ app/translations/en_us.coffee
@@ -116,20 +116,20 @@
       details: "Check out The Spotter's Guide  for detailed descriptions and reference images of Real Lenses and False Positives.  You can use the Spotter's Guide as a reference throughout your time on Space Warps."
     identify:
       header: "Identifying gravitational lenses"
-      details: "The yellow-ish looking galaxy is a gravitational lens. It is bending and magnifying the light from a faint blue galaxy, lying far behind, into a blue arc that surrounds the lensing galaxy."
+      details: "The group of orange galaxies is a gravitational lens. It is bending and magnifying the light from a faint galaxy, lying far behind, into a blue arc that surrounds the lensing galaxy."
     mark:
       header: "Marking lensed features"
-      details: "If you see something that is being lensed, mark it. In this case, click on the blue arc."
-      details_stage2: "If you see something that is being lensed, and you have checked the Spotter's Guide to make sure it is not a False Positive, mark it. In this case, click on the blue arc!"
+      details: "If you see something that is being lensed, mark it. In this case, click on the arc."
+      details_stage2: "If you see something that is being lensed, and you have checked the Spotter's Guide to make sure it is not a False Positive, mark it. In this case, click on the arc!"
     good_job:
       header: "Great job!"
-      details: "You’ve correctly identified a gravitational lens!

To remove a marker, just click it and you will see the remove button."
+      details: "You've correctly identified a gravitational lens!

To remove a marker, just click it and you will see the remove button."
     try_again:
       header: "Whoops, try again."
-      details: "Drag the marker over the blue arc to identify the lens."
+      details: "Drag the marker over the arc to identify the lens."
     training:
       header: "Training images"
-      details: "From time to time we'll throw in a training image like this one, that contains a simulated or previously known gravitational lens. We'll let you know if you spot the lenses in training images."
+      details: "From time to time we'll throw in a training image like this one, that contains a simulated gravitational lens. We'll let you know if you spot the lenses in training images."
       details_stage2: "From time to time we'll throw in a training image like this one, that either contains a simulated gravitational lens, or an object that could be confused with a lens (a 'False Positive'). We'll let you know how you got on!"
     thanks:
       header: "Thanks!"


Default date-paginator's current date to the latest date.

--- src/main/coffeescript/components/date-paginator.coffee
+++ src/main/coffeescript/components/date-paginator.coffee
@@ -10,7 +10,7 @@
 		earliestMonth: null
 		latestMonth: null

-		currentMonth: Dates.asMonth(Dates.now())
+		currentMonth: Ember.computed.oneWay 'latestMonth'

 		previousYear: ( -> @get('currentMonth').minus(1, 'years').get('year') ).property('currentMonth')
 		nextYear: ( -> @get('currentMonth').plus(1, 'years').get('year') ).property('currentMonth')


fix https://github.com/sagemathinc/smc/issues/1881

--- src/smc-webapp/editor_history.coffee
+++ src/smc-webapp/editor_history.coffee
@@ -232,6 +232,9 @@
             return false

     diff_mode: (enabled) =>
+        if not @syncstring?
+            # document already closed or not yet initialized
+            return
         @_diff_mode = enabled
         if enabled
             if @view_doc != @diff_doc


Stringify the error

Stringify the error

--- src/smc-webapp/project_miniterm.cjsx
+++ src/smc-webapp/project_miniterm.cjsx
@@ -101,7 +101,7 @@
                     # computation was cancelled -- ignore result.
                     return
                 if err
-                    @setState(error:err, state:'edit')
+                    @setState(error:JSON.stringify(err), state:'edit')
                 else if output.exit_code != 0 and new Date() - start_time >= .98*EXEC_TIMEOUT
                     # we get no other error except it takes a long time and the exit_code isn't 0.
                     @setState


webapp: fixing project id when creating ssh authorized keys file -- fixes #1231

--- src/smc-webapp/project_settings.cjsx
+++ src/smc-webapp/project_settings.cjsx
@@ -660,13 +660,14 @@

     open_authorized_keys: (e) ->
         e.preventDefault()
+        project_id = @props.project.get('project_id')
         async.series([
             (cb) =>
-                project_tasks(@project_id).ensure_directory_exists
+                project_tasks(project_id).ensure_directory_exists
                     path : '.ssh'
                     cb   : cb
             (cb) =>
-                @actions(project_id: @props.project.get('project_id')).open_file
+                @actions(project_id: project_id).open_file
                     path       : '.ssh/authorized_keys'
                     foreground : true
                 cb()


cleanup, lineMarker is automatically cleared on refresh if empty

--- lib/ui.coffee
+++ lib/ui.coffee
@@ -52,7 +52,7 @@
   autoPreview: null
   autoPreviewOnQueryChange: null

-  preventSyncToEditor: false
+  inPreview: false
   ignoreChange: false
   ignoreCursorMove: false
   destroyed: false
@@ -510,7 +510,7 @@

   # Return success or fail
   syncToEditor: (editor) ->
-    return false if @preventSyncToEditor
+    return false if @inPreview

     point = editor.getCursorBufferPosition()
     if @provider.boundToSingleFile
@@ -546,16 +546,12 @@
       moveAndScroll()

   preview: ->
-    @preventSyncToEditor = true
-    item = @items.getSelectedItem()
-    unless item?
-      @preventSyncToEditor = false
-      @highlighter.clearLineMarker()
-      return
-
+    return unless item = @items.getSelectedItem()
+
+    @inPreview = true
     @provider.openFileForItem(item).then (editor) =>
       editor.scrollToBufferPosition(item.point, center: true)
-      @preventSyncToEditor = false
+      @inPreview = false
       @emitDidPreview({editor, item})

   confirm: ({keepOpen, flash}={}) ->


Improving feedback when registering users

--- app/coffee/modules/auth.coffee
+++ app/coffee/modules/auth.coffee
@@ -157,7 +157,6 @@

     templateFn = ->
         publicRegisterEnabled = $config.get("publicRegisterEnabled")
-        console.log publicRegisterEnabled
         if not publicRegisterEnabled
             return ""
         return template({url:$navUrls.resolve("register")})
@@ -226,8 +225,7 @@
             $location.path($navUrls.resolve("home"))

         onErrorSubmit = (response) ->
-            $confirm.notify("light-error", "According to our Oompa Loompas, the username or email is
-                                            already in use.") #TODO: i18n
+            $confirm.notify("light-error", "According to our Oompa Loompas there was an error. #{response.data._error_message}") #TODO: i18n

         submit = ->
             if not form.validate()


Add spine require to toolbox to make it pass tests on linux

--- app/controllers/Toolbox.coffee
+++ app/controllers/Toolbox.coffee
@@ -1,3 +1,4 @@
+Spine = require 'spine'

 class Toolbox extends Spine.Controller


Adds tests for expression component id capabilities

--- math/expression_components_spec.coffee
+++ math/expression_components_spec.coffee
@@ -7,6 +7,8 @@
     it "returns the correct repsonse for isOperator", ->
       expect(@comp.isOperator()).toEqual opts.is_operator

+    it "responds to ID", ->
+      expect(@comp.id()).toEqual opts.id

 describe "Expression Components", ->
   beforeEach ->
@@ -18,8 +20,9 @@
   describe "Expression", ->
     it_adheres_to_the_expression_component_interface {
       instance_fn: ->
-        @comps.build_expression()
+        @comps.build_expression(id: 9876)
       is_operator: false
+      id: 9876
     }

     it "assigns components from its construtor", ->
@@ -79,16 +82,18 @@
   describe "division", ->
     it_adheres_to_the_expression_component_interface {
       instance_fn: ->
-        @comps.classes.division.build()
+        @comps.classes.division.build(id: 12345)
       is_operator: true
-      }
+      id: 12345
+    }

   describe "variables", ->
     it_adheres_to_the_expression_component_interface {
       instance_fn: ->
-        @comps.classes.variable.build(name: "example")
+        @comps.classes.variable.build(name: "example", id: 678)
       is_operator: false
-      }
+      id: 678
+    }

     it "will tell you its name", ->
       @variable = @comps.classes.variable.build(name: "doot")


Serve static files from the right directory.

--- server.coffee
+++ server.coffee
@@ -20,7 +20,7 @@

     @webServer = express()
     @webServer.use(cors())
-    @webServer.use(express.static("./scenes/"))
+    @webServer.use(express.static(@folder))
     @webServerHandle = @webServer.listen(8090)

     @restart = _.throttle(@restartServer, 1000)


colors and graph

colors and graph

--- flyover-00.coffee
+++ flyover-00.coffee
@@ -1,13 +1,17 @@
-# http://jsbin.com/nuhozo/7/edit?js,output
+# http://jsbin.com/nuhozo/8/edit?js,output

-WIDTH = 500
-HEIGHT = 500
+WIDTH = 720
+HEIGHT = 720
 MID = [WIDTH/2, HEIGHT/2]
 COUNT = 7
 SPACING = WIDTH / (COUNT + 1)
-SIZE = 20
+SIZE = SPACING / 4
 SIZE_HALF = SIZE / 2
-Z_OFFSET = -0.333333
+Z_OFFSET = -1/8
+
+BG = 'hsl(60, 10%, 95%)'
+GRID = 'hsl(60, 10%, 70%)'
+FG = 'hsl(60, 10%, 20%)'

 scrollX = 0
 scrollY = 0
@@ -19,11 +23,11 @@
 canvas.width = WIDTH
 canvas.height = HEIGHT

+context.fillStyle = BG
+context.strokeStyle = FG

 draw = () ->
-  context.fillStyle = 'white'
   context.fillRect(0, 0, WIDTH, HEIGHT)
-  context.fillStyle = 'rgba(255, 255, 255, 0.5)'

   for x in [-1...COUNT+2]
     for y in [-1...COUNT+2]
@@ -31,8 +35,21 @@
       posY = y * SPACING + scrollY
       topX = posX + (MID[0] - posX) * Z_OFFSET
       topY = posY + (MID[1] - posY) * Z_OFFSET
+
+      # grid
+      context.strokeStyle = GRID
+      for gx in [0...8]
+        for gy in [0...8]
+          gxc = posX + (gx * SIZE_HALF)
+          gyc = posY + (gy * SIZE_HALF)
+          context.beginPath()
+          context.moveTo(gxc + SIZE_HALF, gyc)
+          context.lineTo(gxc, gyc)
+          context.lineTo(gxc, gyc + SIZE_HALF)
+          context.stroke()

       # bottom
+      context.strokeStyle = FG
       context.strokeRect(posX - SIZE_HALF, posY - SIZE_HALF, SIZE, SIZE)

       # sides
@@ -49,13 +66,13 @@
       context.stroke()

       # top
-      #context.fillRect(topX - SIZE_HALF, topY - SIZE_HALF, SIZE, SIZE)
       context.strokeRect(topX - SIZE_HALF, topY - SIZE_HALF, SIZE, SIZE)

 animate = () ->
   requestAnimationFrame(animate)
-  scrollX = (Date.now() / 50) % SPACING
-  scrollY = (Date.now() / 50) % SPACING
+  scrollX = ((Date.now() / 2000) * 50) % SPACING
+  scrollY = scrollX
   draw()

 animate()
+#draw()


Let initialize, set and add* return 'this' so one can chain if he likes to

--- src/bounded_context.coffee
+++ src/bounded_context.coffee
@@ -31,10 +31,12 @@
       aggregate: @_aggregateService
       repository: => @getRepository.apply @, arguments
       adapter: => @getAdapter.apply @, arguments
+    @

   set: (key, value) ->
     @_params[key] = value
+    @

   addApplicationService: (serviceObj) ->
@@ -42,46 +44,57 @@
       switch type
         when 'commands' then @addCommands serviceObj[type]
         when 'queries' then @addQueries serviceObj[type]
+    @

   addCommand: (commandName, fn) ->
     @_applicationServiceCommands[commandName] = => fn.apply @_di, arguments
+    @

   addCommands: (commandObj) ->
     @addCommand commandName, commandFunction for commandName, commandFunction of commandObj
+    @

   addQuery: (queryName, fn) ->
     @_applicationServiceQueries[queryName] = => fn.apply @_di, arguments
+    @

   addQueries: (queryObj) ->
     @addQuery queryName, queryFunction for queryName, queryFunction of queryObj
+    @

   addAggregate: (aggregateName, aggregateDefinitionObj) ->
     @_aggregateService.registerAggregateDefinition aggregateName, aggregateDefinitionObj
+    @

   addReadAggregate: (aggregateName, ReadAggregate) ->
     @_readAggregateDefinitions[aggregateName] = ReadAggregate
+    @

   addRepository: (aggregateName, repository) ->
     @_repositories[aggregateName] = repository
+    @

   addDomainEventHandler: (eventName, handlerFn) ->
     @_domainEventService.on eventName, handlerFn
+    @

   addAdapter: (adapterName, adapterClass) ->
     @_adapters[adapterName] = adapterClass
+    @

   addAdapters: (adapterObj) ->
     @addAdapter adapterName, fn for adapterName, fn of adapterObj
+    @

   getRepository: (aggregateName) ->


Add limited support for embedded APL sequences

By "limited", I mean the start/end delimiters of embedded APL blocks are
*not* set dynamically based on a dyadic ⎕INP's left-hand arguments list.
While I thought it'd be possible with some clever nesting or lookaheads,
there's no way to interpolate nested patterns using delimiters set by an
earlier match's capturing groups.

So I'm settling for a more limited solution: using hard-coded tag types.
These are currently limited to the following (case insensitive):

    <?apl ?>   <?= ?>   <? ?>   # PHP-style
    <%apl %>   <%= %>   <% %>   # ASP.net-style

These are arguably the least likely to trigger inadvertent highlighting,
and are the most logical choice, given their semblance to PHP.

--- grammars/apl.cson
+++ grammars/apl.cson
@@ -58,7 +58,10 @@
 		endCaptures:
 			0: {name: "constant.other.apl"}
 		contentName: "text.embedded.html.basic"
-		patterns: [{ include: "text.html.basic" }]
+		patterns: [
+			{ include: "text.html.basic" }
+			{ include: "#embedded-apl"   }
+		]
 	}
 ]

@@ -285,3 +288,17 @@
 				3: {name: "punctuation.assignment.switch.apl"}
 			patterns: [{include: "#main"}]
 		}]
+
+
+	# Embedded APL sequences (for GNU APL heredocs)
+	"embedded-apl":
+		patterns: [{
+			name: "meta.embedded.block.apl"
+			begin: "(?i)(<(\\?|%)(?:apl(?=\\s+)|=))"
+			end:   "(?<=\\s)(\\2>)"
+			patterns: [{include: "#main"}]
+			beginCaptures:
+				1: {name: "punctuation.section.embedded.begin.apl"}
+			endCaptures:
+				1: {name: "punctuation.section.embedded.end.apl"}
+		}]


fixed reload browser if change css file.

Signed-off-by: Bo-Yi Wu <f80eec1bf98b3feebacc247f35f2b506218004cb@gmail.com>

--- templates/gulpfile.coffee
+++ templates/gulpfile.coffee
@@ -113,8 +113,9 @@
   # run tasks automatically when files change
   gulp.watch paths.coffee + '/**/*.coffee', ['coffee']
   gulp.watch paths.test + '/**/*.coffee', ['test_coffee']
-  gulp.watch paths.src + '/*.html', ['html']
-  gulp.watch paths.sass + '/**/*.scss', ['styles']
+  gulp.watch paths.src + '/*.html', ['html']<% if (!includeCss) { %>
+  gulp.watch paths.sass + '/**/*.scss', ['styles']<% } else { %>
+  gulp.watch paths.css + '/**/*.css', ['styles']<% } %>
   gulp.watch paths.images + '/**/*.{jpg,jpeg,png,gif}', ['images']

 gulp.task 'copy', ->


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/active_bounds.coffee
+++ test/active_bounds.coffee
@@ -14,6 +14,6 @@
   obj.bind 'destroy', ->
     destroySpy = true

-  obj.I.x
+  obj.I.x =

 module()


dont use urlencoded() middleware

dont use urlencoded() middleware

bodyParser does these three things:

app.use(express.json());
app.use(express.urlencoded());
app.use(express.multipart());

I haven't tracked down the *exact* cause, but I'm pretty close. In any
case the urlencoded() middleware breaks POSTs going through the proxy.

See https://github.com/kbaribeau/node-proxy-timeout-test

--- tasks/server.coffee
+++ tasks/server.coffee
@@ -26,7 +26,8 @@
     app = express()
     userConfig.drawRoutes app  if userConfig.drawRoutes
     app.configure ->
-      app.use express.bodyParser()
+      app.use express.json()
+      app.use express.multipart()
       app.use express.static(process.cwd() + "/" + webRoot)
       app.use apiProxy(apiProxyHost, apiPort, new httpProxy.RoutingProxy())  if apiProxyEnabled
       app.use express.errorHandler()


use fake window helper

use fake window helper

--- test/model/notifications.spec.coffee
+++ test/model/notifications.spec.coffee
@@ -3,18 +3,11 @@
 URLs = require 'model/urls'
 Notifications = require 'model/notifications'
 Poller = require 'model/notifications/pollers'
+FakeWindow = require 'test/helpers/fake-window'

 describe 'Notifications', ->
   beforeEach ->
-    @windowImpl =
-      clearInterval: sinon.spy()
-      setInterval: sinon.spy -> Math.random()
-      localStorage:
-        getItem: sinon.stub().returns('[]')
-        setItem: sinon.stub()
-      document:
-        hidden: false
-
+    @windowImpl = new FakeWindow
     sinon.stub(Poller.prototype, 'poll')
     sinon.spy(Poller.prototype, 'setUrl')


geometry: added getEnvelope function

geometry: added getEnvelope function

--- test/geometry-test.coffee
+++ test/geometry-test.coffee
@@ -87,4 +87,13 @@
       assert.isFunction geom.isWithinDistance
       assert.ok geom.isWithinDistance geom, 10

+    "should have a getEnvelope function": (geom) ->
+      assert.isFunction geom.getEnvelope
+      assert.equal geom.getEnvelope().toString(), "POINT (1.0000000000000000 1.0000000000000000)"
+
+    "should return a vaild envelope for a linestring": (geom) ->
+      geom = (new WKTReader()).read "LINESTRING (0 0, 1 1)"
+      #TODO remove this ugly wkt
+      assert.equal geom.getEnvelope().toString(), "POLYGON ((0.0000000000000000 0.0000000000000000, 1.0000000000000000 0.0000000000000000, 1.0000000000000000 1.0000000000000000, 0.0000000000000000 1.0000000000000000, 0.0000000000000000 0.0000000000000000))"
+
 tests.export module


ugg, something broke

ugg, something broke

--- test/iced_advanced.coffee
+++ test/iced_advanced.coffee
@@ -1,6 +1,5 @@
 if require?
-  iced = require("../lib/coffee-script/iced").runtime
-  icedlib = require('../lib/coffee-script/icedlib')
+  iced = icedlib = require 'iced-runtime'

 ##----------------------------------------------------------------------


Add behaviour of expectAbstract

Add behaviour of expectAbstract

--- test/src/specs/app/Behaviours.coffee
+++ test/src/specs/app/Behaviours.coffee
@@ -47,4 +47,17 @@
         params = _.clone(paramList) if _.toArray(paramList).length > 0
         doTest(name, params)
         paramList[name] = name
+
+  ###
+  Ensure that this class/type is abstract
+  ###
+  expectAbstract: (type) ->
+    describe "Abstract", ->
+      it "should be an abstract class/type", ->
+        if(type.isAbstract && type.isAbstract())
+          return
+
+        expect("Should have failed").toEqual("because class/type should be abstract")
+
+
 }


Fix search for adopted hounds

Fix search for adopted hounds

--- scripts/adopt.coffee
+++ scripts/adopt.coffee
@@ -6,7 +6,7 @@
   fs.readFile file, 'utf8', (err, data) ->
     return callback err if err

-    if data.search ///^#{greyhound}:/// is -1
+    if data.search ///#{greyhound}:///g is -1
       return callback "Sorry, couldn't find #{greyhound} 😕"

     err = null
@@ -31,7 +31,7 @@
       name: res.message.user.real_name,
       email: res.message.user.profile.email

-    res.reply "Moving #{greyhound} to Happy Tails 💗\nHang on a sec..."
+    res.reply "Moving #{greyhound} to Happy Tails! 💗\nHang on a sec..."
     git.pull (repo) ->
       adopt repo, greyhound, (err) ->
         return res.reply err if err


Added RU language in supported languages (#541)

- We want to allow using the Russian language translation

--- app/views/core/RootView.coffee
+++ app/views/core/RootView.coffee
@@ -148,7 +148,7 @@

   addLanguagesToSelect: ($select, initialVal) ->
     # For now, we only want to support a few languages for Ozaria when launching for HoC 2019:
-    supportedLanguages = ['en-US', 'es-419', 'zh-HANS']
+    supportedLanguages = ['en-US', 'es-419', 'zh-HANS', 'ru']
     filteredLocale = _.pick(locale, supportedLanguages)
     codes = _.keys(filteredLocale)


Fix viewer issues with Atom 1.39/Electron 3.x

--- lib/server.coffee
+++ lib/server.coffee
@@ -17,7 +17,9 @@
           e(err)
         else if @latex.server.openTab
           @latex.viewer.openViewerNewWindow()
-
+    # Don't allow sockets to timeout (which closes the viewer)
+    @http.timeout = 0
+
     @ws = new ws.Server server: @http
     @ws.on "connection", (ws) =>
       ws.on "message", (msg) => @latex.viewer.wsHandler(ws, msg)


Extend UpsertThroughTemporaryTable, parametrize ID field name.

--- lib/Task/ActivityTask/Save/UpsertThroughTemporaryTable.coffee
+++ lib/Task/ActivityTask/Save/UpsertThroughTemporaryTable.coffee
@@ -7,8 +7,10 @@
   constructor: (input, options, dependencies) ->
     _.defaults input,
       bufferTableName: "UpsertData"
+      idFieldName: "_uid"
     Match.check input, Match.ObjectIncluding
       bufferTableName: String
+      idFieldName: String
     super
     @temporaryModel = @createModel()
     @temporaryModel::tableName = @bufferTableName
@@ -70,15 +72,15 @@
               UPDATE "#{@model::tableName}" AS storage
               SET #{@getUpdateColumns("buffer")}
               FROM "#{@bufferTableName}" AS buffer
-              WHERE storage."_uid" = buffer."_uid" AND storage."_avatarId" = buffer."_avatarId"
+              WHERE storage."#{@idFieldName}" = buffer."#{@idFieldName}" AND storage."_avatarId" = buffer."_avatarId"
             """)
       .then ->
         trx.raw("""
               INSERT INTO "#{@model::tableName}"
               SELECT buffer.*
               FROM "#{@bufferTableName}" AS buffer
-              LEFT OUTER JOIN "#{@model::tableName}" as storage ON (buffer."_uid" = storage."_uid" AND buffer."_avatarId" = storage."_avatarId")
-              WHERE storage."_uid" IS NULL
+              LEFT OUTER JOIN "#{@model::tableName}" as storage ON (buffer."#{@idFieldName}" = storage."#{@idFieldName}" AND buffer."_avatarId" = storage."_avatarId")
+              WHERE storage."#{@idFieldName}" IS NULL
             """)
 #      .then ->
 #        trx.raw("""


Whitespace

Whitespace

--- apps/show2/client/index.coffee
+++ apps/show2/client/index.coffee
@@ -40,8 +40,8 @@
       attachRelatedShows 'featured', bootstrappedShow
     else
       attachRelatedShows 'gallery', bootstrappedShow
-
-  relatedArticlesEl = $('.js-related-articles')
+
+  relatedArticlesEl = $('.js-related-articles')
   relatedArticlesView = new RelatedArticlesView showId: bootstrappedShow._id, numToShow: 3, el: relatedArticlesEl
   $('.artwork-column').first().prepend relatedArticlesView.$el


handle failed compile due to validation error

--- app/coffee/CompileManager.coffee
+++ app/coffee/CompileManager.coffee
@@ -68,11 +68,16 @@
 					image:     request.imageName
 					environment: env
 				}, (error, output, stats, timings) ->
+					# request was for validation only
 					if request.check is "validate"
 						result = if error?.code then "fail" else "pass"
 						error = new Error("validation")
 						error.validate = result
-					# compile was killed by user
+					# request was for compile, and failed on validation
+					if request.check is "error" and error?.message is 'exited'
+						error = new Error("compilation")
+						error.validate = "fail"
+					# compile was killed by user, was a validation, or a compile which failed validation
 					if error?.terminated or error?.validate
 						OutputFileFinder.findOutputFiles request.resources, compileDir, (err, outputFiles) ->
 							return callback(err) if err?


Swap start_ts and end_ts comparison to correctly break chunks after 5 minutes

--- app/coffee/UpdatesManager.coffee
+++ app/coffee/UpdatesManager.coffee
@@ -235,7 +235,9 @@
 			# a new summarized update next timge, hence we monitor the previous update.
 			if previousUpdateWasBigDelete
 				shouldConcat = false
-			else if earliestUpdate and earliestUpdate.meta.start_ts - update.meta.end_ts < @TIME_BETWEEN_DISTINCT_UPDATES
+			else if earliestUpdate and earliestUpdate.meta.end_ts - update.meta.start_ts < @TIME_BETWEEN_DISTINCT_UPDATES
+				# We're going backwards in time through the updates, so only combine if this update starts less than 5 minutes before
+				# the end of current summarized block, so no block spans more than 5 minutes.
 				shouldConcat = true

 			isBigDelete = false


make sure to clearTimeout before continous zoom

--- app/components/frame-viewer.cjsx
+++ app/components/frame-viewer.cjsx
@@ -133,6 +133,7 @@
     return @state.frameDimensions.width == @state.viewBoxDimensions.width && @state.frameDimensions.height == @state.viewBoxDimensions.height

   continuousZoom: (change) ->
+    @clearZoomingTimeout()
     return if change == 0
     @setState zooming: true, =>
       zoomNow = =>


Slightly better wording for help command

--- scripts/sensu.coffee
+++ scripts/sensu.coffee
@@ -3,7 +3,7 @@
 #
 # Commands:
 #   hubot sensu info - Returns some status information around Sensu.
-#   hubot sensu clients info <host> - Returns some information around the Sensu client.
+#   hubot sensu clients info <host> - Returns Sensu related client specific information.
 #   hubot sensu clients remove <host> - Removes the node from Sensu.
 #
 # Configuration:


add tests for multiple dotted paths, and deeper dotted paths

--- test/encrypt.coffee
+++ test/encrypt.coffee
@@ -1687,21 +1687,48 @@
     NestedModelSchema = mongoose.Schema
       nest:
         secretBird: type: String
-
-    NestedModelSchema.plugin encrypt, encryptionKey: encryptionKey, signingKey: signingKey, collectionId: 'EncryptedFields', encryptedFields: ['nest.secretBird']
+        secretBird2: type: String
+
+    NestedModelSchema.plugin encrypt, encryptionKey: encryptionKey, signingKey: signingKey, collectionId: 'EncryptedFields', encryptedFields: ['nest.secretBird', 'nest.secretBird2']

     NestedModel = mongoose.model 'Nested', NestedModelSchema

     nestedDoc = new NestedModel
-      nest: secretBird: 'Unencrypted text'
+      nest:
+        secretBird: 'Unencrypted text'
+        secretBird2: 'Unencrypted text 2'

     nestedDoc.encrypt (err) ->
       assert.equal err, null
       assert.equal nestedDoc.nest.secretBird, undefined
+      assert.equal nestedDoc.nest.secretBird2, undefined

       nestedDoc.decrypt (err) ->
         assert.equal err, null
         assert.equal nestedDoc.nest.secretBird, 'Unencrypted text'
+        assert.equal nestedDoc.nest.secretBird2, 'Unencrypted text 2'
+        done()
+
+  it 'should encrypt nested fields with dot notation two layers deep', (done) ->
+    NestedModelSchema = mongoose.Schema
+      nest:
+        secretBird:
+          topSecretEgg: type: String
+
+    NestedModelSchema.plugin encrypt, encryptionKey: encryptionKey, signingKey: signingKey, collectionId: 'EncryptedFields', encryptedFields: ['nest.secretBird.topSecretEgg']
+
+    NestedModel = mongoose.model 'NestedNest', NestedModelSchema
+
+    nestedDoc = new NestedModel
+      nest: secretBird: topSecretEgg: 'Unencrypted text'
+
+    nestedDoc.encrypt (err) ->
+      assert.equal err, null
+      assert.equal nestedDoc.nest.secretBird.topSecretEgg, undefined
+
+      nestedDoc.decrypt (err) ->
+        assert.equal err, null
+        assert.equal nestedDoc.nest.secretBird.topSecretEgg, 'Unencrypted text'
         done()

 describe 'saving same authenticated document twice asynchronously', ->


Fix remove mount

Fix remove mount

--- kites/sharedHosting/mounter.coffee
+++ kites/sharedHosting/mounter.coffee
@@ -239,7 +239,7 @@
     fs.readFile cfg,(err,data)->
       if err?
         log.error error = "[ERROR] Couldn't read config file #{cfg}: #{err.message}"
-        callback error
+        callback null, []
       else
         try
           currentConf = JSON.parse data
@@ -337,13 +337,8 @@
         callback err
       else
         index = 0
-        for elem in res
-          if elem.remotehost is not remotehost
-            index+=1
-          else
-            res.splice(index,1)
-            break
-        jsonConf = JSON.stringify res
+        filteredMounts = [mount for mount in res when mount.remotehost isnt remotehost][0]
+        jsonConf = JSON.stringify filteredMounts or ''
         fs.writeFile cfg, jsonConf, 'utf-8',(err)->
           if err?
             log.error error = "Couldn't update config #{cfg}: #{err.message}"


Correct number of Expertise cards in U-Wing.

fixes #388

--- coffeescripts/manifest.coffee
+++ coffeescripts/manifest.coffee
@@ -3604,7 +3604,7 @@
         {
             name: 'Expertise'
             type: 'upgrade'
-            count: 1
+            count: 2
         }
         {
             name: 'Pivot Wing'


Use oauth2orize error handler

Use oauth2orize error handler

If the request body is wrong but still good enough to identify, then the
spec says you "should" tell the app dev about it. That's what the
indirect error handler middleware does.

--- src/server/common/AuthService.coffee
+++ src/server/common/AuthService.coffee
@@ -53,6 +53,7 @@
 			# The user can then accept or deny.
 			redirect = "/authorize?transactionID=#{id}&user=#{user}&client=#{client}"
 			res.redirect redirect
+		server.errorHandler { mode: "indirect" }
 	]

 service.decision = [


Integrated Astromech is a modification.

Integrated Astromech is a modification.

fixes #270

--- coffeescripts/manifest.coffee
+++ coffeescripts/manifest.coffee
@@ -2683,7 +2683,7 @@
         }
         {
             name: 'Integrated Astromech'
-            type: 'upgrade'
+            type: 'modification'
             count: 1
         }
         {


Modal: Documentation and JSDocs.

Modal: Documentation and JSDocs.

--- src/lib/components/modal/Modal.coffee
+++ src/lib/components/modal/Modal.coffee
@@ -8,7 +8,11 @@

   ###*
-    Modal view with overlay option for Spark Framework.
+    Modal view with overlay option for Spark Framework. By default modal will be
+    appended to document body and it will have an overlay. When overlay clicked
+    the modal will be destroyed. You should pass title and content params of the
+    modal in your options. You can also customize buttons. See `@createButtons_`
+    for more information.

     @constructor
     @export
@@ -31,28 +35,53 @@
     @buttons = {}

     if options.overlay
-      @setOverlay()
+      @setOverlay_()

-    @createTitle   options.title
-    @createContent options.content
-    @createButtons options.buttons
+    @createTitle_   options.title
+    @createContent_ options.content
+    @createButtons_ options.buttons

-  createTitle: (title) ->
+  ###*
+    Creates modal content.
+
+    @private
+    @param {string} title Modal title.
+  ###
+  createTitle_: (title) ->
     @title = new spark.core.View
       template : title
       cssClass : 'modal-title'
       renderTo : this

-  createContent: (content) ->
+  ###*
+    Creates modal content.
+
+    @private
+    @param {string} content Modal content.
+  ###
+  createContent_: (content) ->
     @content   = new spark.core.View
       template : content
       cssClass : 'modal-content'
       renderTo : this

-  createButtons: (buttons) ->
+  ###*
+    Creates modal buttons. You can use preset buttons like YES, YES_NO or you
+    can pass custom array which contains button options in it. Preset buttons
+    will emit `ModalButtonClicked` with button title. You can programatically
+    listen that event and look for button title to understand which button is
+    clicked. Custom buttons should contain its callback. No other events will be
+    fired for custom buttons. All buttons will be stored in `buttons` object
+    with its names.
+
+    @private
+    @param {spark.components.Modal.Buttons|Array.<Object>} buttons Preset button
+    value in Buttons enum or custom buttons array.
+  ###
+  createButtons_: (buttons) ->
     @buttonsContainer = new spark.core.View
       cssClass : 'buttons-container'
       renderTo : this
@@ -61,7 +90,7 @@

     if presetButtons.indexOf(buttons) > -1
       buttons.forEach (title) =>
-        new spark.components.Button
+        @buttons[title] = new spark.components.Button
           cssClass : spark.components.Modal.ButtonCssClasses[title]
           title    : title
           renderTo : @buttonsContainer
@@ -69,10 +98,16 @@
     else
       buttons.forEach (options) =>
         options.renderTo = @buttonsContainer
-        new spark.components.Button options
+        @buttons[options.title] = new spark.components.Button options

-  setOverlay: ->
+  ###*
+    Sets overlay for modal. Overlay and modal will be destroyed when overlay
+    clicked if `removeOnOverlayClick` options is passed true in modal options.
+
+    @private
+  ###
+  setOverlay_: ->
     @overlay = new spark.components.Overlay
       removeOnClick: @getOptions().removeOnOverlayClick

@@ -80,14 +115,26 @@
       @removeFromDocument()

-  @Buttons        =
-    YES           : [ 'Yes' ]
-    NO            : [ 'No'  ]
-    YES_NO        : [ 'Yes', 'No' ]
-    YES_NO_CANCEL : [ 'Yes', 'No', 'Cancel' ]
+  ###*
+    Preset buttons enum. Created buttons will emit 'ModalButtonClicked' event
+    width button value.
+
+    @export
+    @enum {Array.<string>}
+  ###
+  @Buttons =
+    'YES'           : [ 'Yes' ]
+    'NO'            : [ 'No'  ]
+    'YES_NO'        : [ 'Yes', 'No' ]
+    'YES_NO_CANCEL' : [ 'Yes', 'No', 'Cancel' ]

+  ###*
+    Css class map for preset buttons.
+
+    @private
+  ###
   @ButtonCssClasses =
-    Yes    : 'green'
-    No     : 'red'
-    Cancel : 'grey'
+    'Yes'    : 'green'
+    'No'     : 'red'
+    'Cancel' : 'grey'


- Referee doesn't save compiled coffee script in the editor, so needs the coffee script compiler in the game runtime.

--- config.coffee
+++ config.coffee
@@ -25,6 +25,7 @@
   'css'
   'firepad'
   'jquery-ui-custom'
+  'coffeescript'
 ].join '|'

 #- Build the config


Corrects atom snippet spacing

Corrects atom snippet spacing

--- ide-snippets/atom/StructureJS.Atom.cson
+++ ide-snippets/atom/StructureJS.Atom.cson
@@ -19,240 +19,243 @@
   "View":
    prefix: "viewClass"
    body: """
-        define(function(require, exports, module) { // jshint ignore:line
-            'use strict';
-
-            // Imports
-            var Extend = require('structurejs/util/Extend');
-            var DOMElement = require('structurejs/display/DOMElement');
-
-            /**
-             * TODO: YUIDoc_comment
-             *
-             * @class ${1:Class}
-             * @extends DOMElement
-             * @constructor
-             **/
-            var ${1:Class} = (function () {
-
-                var _super = Extend (${1:Class}, DOMElement); // jshint ignore:line
-
-                function ${1:Class} () { // jshint ignore:line
-                    _super.call(this);
-
-         			$2
-                }
-
-                /**
-                 * @overridden DOMElement.create
-                 */
-                ${1:Class}.prototype.create = function () {
-                    _super.prototype.create.call(this);
-
-                    // Create or setup objects in this parent class.
-                };
-
-                /**
-                 * @overridden DOMElement.enable
-                 */
-                ${1:Class}.prototype.enable = function () {
-                    if (this.isEnabled === true) { return this; }
-
-                    // Enable the child objects and/or add any event listeners.
-
-                    return _super.prototype.enable.call(this);
-                };
-
-                /**
-                 * @overridden DOMElement.disable
-                 */
-                ${1:Class}.prototype.disable = function () {
-                    if (this.isEnabled === false) { return this; }
-
-                    // Disable the child objects and/or remove any event listeners.
-
-                    return _super.prototype.disable.call(this);
-                };
-
-                /**
-                 * @overridden DOMElement.layout
-                 */
-                ${1:Class}.prototype.layout = function () {
-                    // Layout or update the child objects in this parent class.
-
-                    return this;
-                };
-
-                /**
-                 * @overridden DOMElement.destroy
-                 */
-                ${1:Class}.prototype.destroy = function () {
-                    // Call destroy on any child objects.
-                    // This super method will also null out your properties for garbage collection.
-
-                    _super.prototype.destroy.call(this);
-                };
-
-                return ${1:Class};
-            })();
-
-            module.exports = ${1:Class};
-
-        });
+define(function(require, exports, module) { // jshint ignore:line
+    'use strict';
+
+    // Imports
+    var Extend = require('structurejs/util/Extend');
+    var DOMElement = require('structurejs/display/DOMElement');
+
+    /**
+     * TODO: YUIDoc_comment
+     *
+     * @class ${1:Class}
+     * @extends DOMElement
+     * @constructor
+     **/
+    var ${1:Class} = (function () {
+
+        var _super = Extend (${1:Class}, DOMElement); // jshint ignore:line
+
+        function ${1:Class} () { // jshint ignore:line
+            _super.call(this);
+
+ 			$2
+        }
+
+        /**
+         * @overridden DOMElement.create
+         */
+        ${1:Class}.prototype.create = function () {
+            _super.prototype.create.call(this);
+
+            // Create or setup objects in this parent class.
+        };
+
+        /**
+         * @overridden DOMElement.enable
+         */
+        ${1:Class}.prototype.enable = function () {
+            if (this.isEnabled === true) { return this; }
+
+            // Enable the child objects and/or add any event listeners.
+
+            return _super.prototype.enable.call(this);
+        };
+
+        /**
+         * @overridden DOMElement.disable
+         */
+        ${1:Class}.prototype.disable = function () {
+            if (this.isEnabled === false) { return this; }
+
+            // Disable the child objects and/or remove any event listeners.
+
+            return _super.prototype.disable.call(this);
+        };
+
+        /**
+         * @overridden DOMElement.layout
+         */
+        ${1:Class}.prototype.layout = function () {
+            // Layout or update the child objects in this parent class.
+
+            return this;
+        };
+
+        /**
+         * @overridden DOMElement.destroy
+         */
+        ${1:Class}.prototype.destroy = function () {
+            // Call destroy on any child objects.
+            // This super method will also null out your properties for garbage collection.
+
+            _super.prototype.destroy.call(this);
+        };
+
+        return ${1:Class};
+
+    })();
+
+    module.exports = ${1:Class};
+
+});
    """
   "Value Object":
    prefix: "valueObject"
    body: """
-        define(function(require, exports, module) { // jshint ignore:line
-            'use strict';
-
-            // Imports
-            var Extend = require('structurejs/util/Extend');
-            var ValueObject = require('structurejs/model/ValueObject');
-
-            /**
-             * TODO: YUIDoc_comment
-             *
-             * @class ${1:Class}
-             * @extends ValueObject
-             * @constructor
-             **/
-            var ${1:Class} = (function () {
-
-                var _super = Extend (${1:Class}, ValueObject); // jshint ignore:line
-
-                function ${1:Class} () { // jshint ignore:line
-                    _super.call(this);
-
-                    if (data) {
-                        this.update(data);
-                    }
-                }
-
-                /**
-                * @overridden ValueObject.update
-                */
-                ${1:Class}.prototype.update = function (data) {
-                    _super.prototype.update.call(this, data);
-
-                    // Override any values after the default super update method has set the values.
-                };
-
-                return ${1:Class};
-            })();
-
-            module.exports = ${1:Class};
-
-        });
+define(function(require, exports, module) { // jshint ignore:line
+    'use strict';
+
+    // Imports
+    var Extend = require('structurejs/util/Extend');
+    var ValueObject = require('structurejs/model/ValueObject');
+
+    /**
+     * TODO: YUIDoc_comment
+     *
+     * @class ${1:Class}
+     * @extends ValueObject
+     * @constructor
+     **/
+    var ${1:Class} = (function () {
+
+        var _super = Extend (${1:Class}, ValueObject); // jshint ignore:line
+
+        function ${1:Class} () { // jshint ignore:line
+            _super.call(this);
+
+            if (data) {
+                this.update(data);
+            }
+        }
+
+        /**
+        * @overridden ValueObject.update
+        */
+        ${1:Class}.prototype.update = function (data) {
+            _super.prototype.update.call(this, data);
+
+            // Override any values after the default super update method has set the values.
+        };
+
+        return ${1:Class};
+
+    })();
+
+    module.exports = ${1:Class};
+
+});
    """
   "Stage":
    prefix: "stageClass"
    body: """
-        define(function(require, exports, module) { // jshint ignore:line
-            'use strict';
-
-            // Imports
-            var Extend = require('structurejs/util/Extend');
-            var Stage = require('structurejs/display/Stage');
-
-            /**
-             * TODO: YUIDoc_comment
-             *
-             * @class ${1:Class}
-             * @extends Stage
-             * @constructor
-             **/
-            var ${1:Class} = (function () {
-
-                var _super = Extend (${1:Class}, Stage); // jshint ignore:line
-
-                function ${1:Class} () { // jshint ignore:line
-                    _super.call(this);
-
-         			$2
-                }
-
-                /**
-                 * @overridden Stage.create
-                 */
-                ${1:Class}.prototype.create = function () {
-                    _super.prototype.create.call(this);
-
-                    // Create or setup objects in this parent class.
-                };
-
-                /**
-                 * @overridden Stage.enable
-                 */
-                ${1:Class}.prototype.enable = function () {
-                    if (this.isEnabled === true) { return this; }
-
-                    // Enable the child objects and/or add any event listeners.
-
-                    return _super.prototype.enable.call(this);
-                };
-
-                /**
-                 * @overridden Stage.disable
-                 */
-                ${1:Class}.prototype.disable = function () {
-                    if (this.isEnabled === false) { return this; }
-
-                    // Disable the child objects and/or remove any event listeners.
-
-                    return _super.prototype.disable.call(this);
-                };
-
-                /**
-                 * @overridden Stage.layout
-                 */
-                ${1:Class}.prototype.layout = function () {
-                    // Layout or update the objects in this parent class.
-
-                    return this;
-                };
-
-                /**
-                 * @overridden Stage.destroy
-                 */
-                ${1:Class}.prototype.destroy = function () {
-                    // Call destroy on any child objects.
-                    // This super method will also null out your properties for garbage collection.
-
-                    _super.prototype.destroy.call(this);
-                };
-
-                return ${1:Class};
-            })();
-
-            module.exports = ${1:Class};
-
-        });
-   """
+define(function(require, exports, module) { // jshint ignore:line
+    'use strict';
+
+    // Imports
+    var Extend = require('structurejs/util/Extend');
+    var Stage = require('structurejs/display/Stage');
+
+    /**
+     * TODO: YUIDoc_comment
+     *
+     * @class ${1:Class}
+     * @extends Stage
+     * @constructor
+     **/
+    var ${1:Class} = (function () {
+
+        var _super = Extend (${1:Class}, Stage); // jshint ignore:line
+
+        function ${1:Class} () { // jshint ignore:line
+            _super.call(this);
+
+ 			$2
+        }
+
+        /**
+         * @overridden Stage.create
+         */
+        ${1:Class}.prototype.create = function () {
+            _super.prototype.create.call(this);
+
+            // Create or setup objects in this parent class.
+        };
+
+        /**
+         * @overridden Stage.enable
+         */
+        ${1:Class}.prototype.enable = function () {
+            if (this.isEnabled === true) { return this; }
+
+            // Enable the child objects and/or add any event listeners.
+
+            return _super.prototype.enable.call(this);
+        };
+
+        /**
+         * @overridden Stage.disable
+         */
+        ${1:Class}.prototype.disable = function () {
+            if (this.isEnabled === false) { return this; }
+
+            // Disable the child objects and/or remove any event listeners.
+
+            return _super.prototype.disable.call(this);
+        };
+
+        /**
+         * @overridden Stage.layout
+         */
+        ${1:Class}.prototype.layout = function () {
+            // Layout or update the objects in this parent class.
+
+            return this;
+        };
+
+        /**
+         * @overridden Stage.destroy
+         */
+        ${1:Class}.prototype.destroy = function () {
+            // Call destroy on any child objects.
+            // This super method will also null out your properties for garbage collection.
+
+            _super.prototype.destroy.call(this);
+        };
+
+        return ${1:Class};
+
+    })();
+
+    module.exports = ${1:Class};
+
+});
+"""
   "Property":
     prefix: "property"
     body: """
-        /**
-         * TODO: YUIDoc_comment
-         *
-         * @property ${1:propertyName}
-         * @type {${2:propertyType}}
-         * @${3:accessType}
-         */
-        this.${1} = ${4:null};
+/**
+ * TODO: YUIDoc_comment
+ *
+ * @property ${1:propertyName}
+ * @type {${2:propertyType}}
+ * @${3:accessType}
+ */
+this.${1} = ${4:null};
     """
   "Method":
     prefix: "method"
     body: """
-        /**
-         * @TODO: YUIDoc_comment
-         *
-         * @method ${1:methodName}
-         * @${2:accessType}
-         */
-        ${3:ClassName}.prototype.${1} = function ($4) {
-            $5
-        };
+/**
+ * @TODO: YUIDoc_comment
+ *
+ * @method ${1:methodName}
+ * @${2:accessType}
+ */
+${3:ClassName}.prototype.${1} = function ($4) {
+    $5
+};
     """
   "Import":
     prefix: "import"
@@ -260,78 +263,80 @@
   "Extend":
     prefix: "extendClass"
     body: """
-       /**
-        * @fileOverview
-        * ${1:Class}
-        */
-        define(function(require, exports, module) { // jshint ignore:line
-            'use strict';
-
-            // Imports
-            var Extend = require('structurejs/util/Extend');
-            var ${2} = require('path/${2}');
-
-            /**
-             * TODO: YUIDoc_comment
-             *
-             * @class ${1:Class}
-             * @extends ${2}
-             * @constructor
-             **/
-            var ${1:Class} = (function () {
-
-                var _super = Extend (${1:Class}); // jshint ignore:line
-
-                function ${1:Class} () { // jshint ignore:line
-                    _super.call(this);
-                }
-
-                return ${1:Class};
-            })();
-
-            module.exports = ${1:Class};
-
-        });
+/**
+* @fileOverview
+* ${1:Class}
+*/
+define(function(require, exports, module) { // jshint ignore:line
+    'use strict';
+
+    // Imports
+    var Extend = require('structurejs/util/Extend');
+    var ${2} = require('path/${2}');
+
+    /**
+     * TODO: YUIDoc_comment
+     *
+     * @class ${1:Class}
+     * @extends ${2}
+     * @constructor
+     **/
+    var ${1:Class} = (function () {
+
+        var _super = Extend (${1:Class}); // jshint ignore:line
+
+        function ${1:Class} () { // jshint ignore:line
+            _super.call(this);
+        }
+
+        return ${1:Class};
+
+    })();
+
+    module.exports = ${1:Class};
+
+});
     """
-      "Event":
-        prefix: "eventClass"
-        body: """
-            define(function (require, exports, module) { // jshint ignore:line
-                'use strict';
-
-                // Imports
-                var Extend = require('structurejs/util/Extend');
-                var BaseEvent = require('structurejs/event/BaseEvent');
-
-                /**
-                 * TODO: YUIDoc_comment
-                 *
-                 * @class ${1:Class}
-                 * @extends ${1}
-                 * @constructor
-                 **/
-                var ${1:Class} = (function () {
-
-                    /**
-                     * This is an example of an event type.
-                     * Go ahead and rename or remove this.
-                     *
-                     * @event EXAMPLE
-                     * @type {string}
-                     * @static
-                     */
-                    ${1:Class}.EXAMPLE = '${1:Class}.example';
-
-                  var _super = Extend(${1:Class}, ${1});
-
-                      function ${1:Class}() {
-                          _super.call(this);
-                      }
-
-                      return ${1:Class};
-                })();
-
-                module.exports = ${1:Class};
-
-            });
+  "Event":
+    prefix: "eventClass"
+    body: """
+define(function (require, exports, module) { // jshint ignore:line
+    'use strict';
+
+    // Imports
+    var Extend = require('structurejs/util/Extend');
+    var BaseEvent = require('structurejs/event/BaseEvent');
+
+    /**
+     * TODO: YUIDoc_comment
+     *
+     * @class ${1:Class}
+     * @extends ${1}
+     * @constructor
+     **/
+    var ${1:Class} = (function () {
+
+        /**
+         * This is an example of an event type.
+         * Go ahead and rename or remove this.
+         *
+         * @event EXAMPLE
+         * @type {string}
+         * @static
+         */
+        ${1:Class}.EXAMPLE = '${1:Class}.example';
+
+      var _super = Extend(${1:Class}, ${1});
+
+          function ${1:Class}() {
+              _super.call(this);
+          }
+
+          return ${1:Class};
+
+    })();
+
+    module.exports = ${1:Class};
+
+});
         """


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/metered.coffee
+++ test/metered.coffee
@@ -7,7 +7,7 @@

   ok obj.meter

-test "should set max<Attribute> if it isn't present in the in", ->
+test "should set max<Attribute> if it isn't present in the including object", ->
   obj = GameObject
     health: 150


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/metered.coffee
+++ test/metered.coffee
@@ -7,7 +7,7 @@

   ok obj.meter

-test "should respect "
+test "should respect 0 being set as the m"

 test "should set max<Attribute> if it isn't present in the including object", ->
   obj = GameObject


Still redirect to getting started, but render it in 'main' outlet

Rendering views outside of routes cycle seems problematic at the moment,
so redirection is our best bet. However, the way I initially did it in
cc90200 causes problems for people who don't have any own repositories
set up for Travis CI, but still want to log in and browse around -
rendering getting started page as a full page hides left sidebar with a
list of repositories.

This commit changes getting started page to render in the main outlet,
just as before redirection changes.

--- assets/scripts/app/routes.coffee
+++ assets/scripts/app/routes.coffee
@@ -79,6 +79,7 @@

 Travis.Router.map ->
   @resource 'index', path: '/', ->
+    @resource 'getting_started'
     @route 'current', path: '/'
     @resource 'repo', path: '/:owner/:name', ->
       @route 'index', path: '/'
@@ -93,7 +94,6 @@
     @resource 'repo.settings', path: '/:owner/:name/settings', ->
       @route 'tab', path: ':tab'

-  @route 'getting_started'
   @route 'first_sync'
   @route 'stats', path: '/stats'
   @route 'auth', path: '/auth'
@@ -119,15 +119,8 @@
         @render('builds/not_found', into: 'repo', outlet: 'pane')

 Travis.GettingStartedRoute = Travis.Route.extend
-  setupController: ->
-    $('body').attr('id', 'home')
-    @container.lookup('controller:repos').activate()
-    @container.lookup('controller:application').connectLayout 'simple'
-    @_super.apply(this, arguments)
-
-  renderTemplate: ->
-    @render 'top', outlet: 'top'
-    @_super.apply(this, arguments)
+  renderTemplate: ->
+    @render('no_owned_repos')

 Travis.FirstSyncRoute = Travis.Route.extend
   actions:


Let's go ahead and increase the latency timers too.

--- tests/youtube-info.coffee
+++ tests/youtube-info.coffee
@@ -17,7 +17,7 @@
   context 'youtube.com url', ->
     beforeEach ->
       room.user.say 'alice', 'https://www.youtube.com/watch?v=ePoi0_zSnYk'
-      this.clock.tick(3000)
+      this.clock.tick(10000)

     it 'should be able to find title', ->
       assert room.messages.length > 1
@@ -26,7 +26,7 @@
   context 'youtube.com url with additional values', ->
     beforeEach ->
       room.user.say 'alice', 'https://www.youtube.com/watch?v=iq9DLJfpHd0&feature=youtu.be&ab_channel=CatRe-Tailer'
-      this.clock.tick(3000)
+      this.clock.tick(10000)

     it 'should be able to find title', ->
       assert room.messages.length > 1
@@ -35,7 +35,7 @@
   context 'youtu.be url', ->
     beforeEach ->
       room.user.say 'alice', 'https://youtu.be/HP66dH1yEZo'
-      this.clock.tick(3000)
+      this.clock.tick(10000)

     it 'should be able to find title', ->
       assert room.messages.length > 1
@@ -44,7 +44,7 @@
   context 'youtu.be url with additional values', ->
     beforeEach ->
       room.user.say 'alice', 'https://youtu.be/oVnAVcbMoSM?t=39s'
-      this.clock.tick(3000)
+      this.clock.tick(10000)

     it 'should be able to find title', ->
       assert room.messages.length > 1


Don't automatically sign in if there is no token available

--- assets/scripts/app/auth.coffee
+++ assets/scripts/app/auth.coffee
@@ -19,7 +19,7 @@
     global  = localStorage.getItem('travis.user')
     session = sessionStorage.getItem('travis.user')
     user    = session || global
-    if user
+    if user && localStorage.getItem('travis.token')
       localStorage.setItem('travis.user', user) unless global
       data = JSON.parse(user)
       data = { user: data } unless data.user?


add time profiling

add time profiling

--- server/router.coffee
+++ server/router.coffee
@@ -18,6 +18,8 @@

 fs = Npm.require('fs')
 path = Npm.require('path')
+
+ENABLE_PROFILING = true

 Router.configureBodyParsers = ->
   # The resolve-incidents endpoint may have files larger than the default limit
@@ -318,21 +320,23 @@
         locations: $not: $size: 0
     else
       query = utils.eventToIncidentQuery(event)
-    if (@request.query.activeCases + "").toLowerCase() == "true"
-      # TODO: look up disease specific stat
-      event.caseLengthDays = 14
-      extendedStartDate = new Date(dateRange.start)
-      # Incidents from an extended date range are included to determine the
-      # initial number of active cases at the beginning of the intended date range.
-      # Assuming the half life of all cases is the case length, less than 10%
-      # of the cases from before the extended date range would still be active
-      # in the original date range.
-      extendedStartDate.setDate(extendedStartDate.getDate() - (4 * event.caseLengthDays))
-      dateRange.start = extendedStartDate
     if dateRange
+      if (@request.query.activeCases + "").toLowerCase() == "true"
+        # TODO: look up disease specific stat
+        event.caseLengthDays = 14
+        extendedStartDate = new Date(dateRange.start)
+        # Incidents from an extended date range are included to determine the
+        # initial number of active cases at the beginning of the intended date range.
+        # Assuming the half life of all cases is the case length, less than 10%
+        # of the cases from before the extended date range would still be active
+        # in the original date range.
+        extendedStartDate.setDate(extendedStartDate.getDate() - (4 * event.caseLengthDays))
+        dateRange.start = extendedStartDate
       query['dateRange.start'] = $lte: dateRange.end
       query['dateRange.end'] = $gte: dateRange.start
+    console.time('fetch incidents') if ENABLE_PROFILING
     event.incidents = Incidents.find(query).fetch()
+    console.timeEnd('fetch incidents') if ENABLE_PROFILING
   @response.setHeader('Access-Control-Allow-Origin', '*')
   @response.setHeader('Content-Type', 'application/json')
   @response.statusCode = 200
@@ -343,6 +347,7 @@
       dailyDecayRate = 1.0
       if (@request.query.activeCases + "").toLowerCase() == "true"
         dailyDecayRate = Math.pow(.5, (1 / event.caseLengthDays))
+      console.time('create differentials') if ENABLE_PROFILING
       baseIncidents = []
       constrainingIncidents = []
       event.incidents.map (incident) ->
@@ -363,7 +368,10 @@
       ).concat(supplementalIncidents)
       differentials = _.where(allDifferentials, type: 'cases')
       subIntervals = differentialIncidentsToSubIntervals(differentials)
+      console.timeEnd('create differentials') if ENABLE_PROFILING
+      console.time('resolve') if ENABLE_PROFILING
       extendSubIntervalsWithValues(differentials, subIntervals)
+      console.timeEnd('resolve') if ENABLE_PROFILING
       locationTree = LocationTree.from(subIntervals.map (x) -> x.location)
       topLocations = locationTree.children.map (x) -> x.value
       locToSubintervals = {}
@@ -393,7 +401,9 @@
         if location in topLocations
           maxSubintervalsPerTopLocation = maxSubintervalsPerTopLocation.concat(maxSubintervals)
         maxSubintervals = _.sortBy(maxSubintervals, (x) -> x.start)
+        console.time('compute active cases') if ENABLE_PROFILING
         total = subIntervalsToActiveCases(maxSubintervals, dailyDecayRate).slice(-1)[0][1]
+        console.timeEnd('compute active cases') if ENABLE_PROFILING
         return [key, total]
       .object()
       .value()


Not necessary now using mocha as promised.

--- test/runWith.coffee
+++ test/runWith.coffee
@@ -21,11 +21,5 @@
     throw new Error "Function should have failed."
   it "works when providing good options", (next) ->
     runWith(goodOptions)
-      .then (memo) ->
-        console.log "success"
-        console.log memo
-        next null
-      .then null, (err) ->
-        next err
   after (next) ->
     rimraf path.resolve(goodOptions.cwd, goodOptions.inputs[0]), next


DiaScene: updateScene improvements with autoUpdate and other helpers

--- lib/components/dia/diascene.coffee
+++ lib/components/dia/diascene.coffee
@@ -20,6 +20,7 @@
     options.fakeLineColor    or= "green"
     options.fakeLineDashes    ?= []
     options.curveDistance     ?= 50
+    options.updateEvery       ?= 10

     super

@@ -28,6 +29,10 @@
     @activeDias      = []
     @activeJoints    = []
     @fakeConnections = []
+
+    @_autoUpdate = null
+    @updateEvery = @getOption 'updateEvery'
+

   diaAdded: (container, diaObj) ->

@@ -258,9 +263,60 @@
     @fakeConnections = []
     @updateScene()

-  updateScene: _.throttle ->
-
+
+  updateScene: do (queued = no) -> ->
+
+    return  if @_paused
+
+    return unless @parentIsInDom
+      if not queued
+        @once 'viewAppended', @bound 'updateScene'
+        queued = yes
+
+      return
+
+    needsUpdate = !!@_forceUpdate
     @cleanup @realCanvas
+
+    for connection in @connections
+
+      if connection.options.transfers.length > 0
+        needsUpdate = yes
+        KD.utils.defer =>
+          @_autoUpdate ?= KD.utils.repeat @updateEvery, @bound 'updateScene'
+
+      @drawConnectionLine connection
+
+    for connection in @fakeConnections
+      @drawConnectionLine connection
+
+    unless needsUpdate
+      @_autoUpdate = KD.utils.killRepeat @_autoUpdate
+
+
+  pause: ->
+    @_paused = yes
+
+
+  play: ->
+    @_paused = no
+    @updateScene()
+
+
+  startAutoUpdate: do (autoKill = null) -> (duration) ->
+
+    @_forceUpdate = yes
+    @_autoUpdate ?= KD.utils.repeat @updateEvery, @bound 'updateScene'
+
+    return  unless duration
+    KD.utils.killWait autoKill
+    autoKill = KD.utils.wait duration, @bound 'stopAutoUpdate'
+
+
+  stopAutoUpdate: ->
+    @_forceUpdate = no
+    @_autoUpdate = KD.utils.killRepeat @_autoUpdate
+

   getBezierCurves = (t, pos) ->


fix(tests): add guards tests

fix(tests): add guards tests

--- tests/guards.coffee
+++ tests/guards.coffee
@@ -0,0 +1,28 @@
+describe "Guards", ->
+
+  f_context ->
+
+    test_guards_1(I) where(I > 10) -> true
+    test_guards_1(I) where(I < 10) -> false
+    test_guards_1(I) -> I
+
+    test_guards_2(I, K) where(I > 10 and K < 20) -> true
+    test_guards_2(I, K) where(I < 10 and K > 20) -> false
+    test_guards_2(I, K) -> {I, K}
+
+
+
+  it('Handle single condition in guard', ->
+    expect(test_guards_1(20)).toBe(true)
+    expect(test_guards_1(5)).toBe(false)
+    expect(test_guards_1(10)).toBe(10)
+  )
+
+  it('Handle multiple conditions in guard', ->
+    expect(test_guards_2(20, 10)).toBe(true)
+    expect(test_guards_2(5, 30)).toBe(false)
+    expect(test_guards_2(10, 20)).toEqual(
+      I: 10
+      K: 20
+    )
+  )


remove unused method

remove unused method

--- lib/panoptes-subject-uploader.iced
+++ lib/panoptes-subject-uploader.iced
@@ -25,7 +25,7 @@
     imageFileName = value.match?(/([^\/]+\.(?:jpg|jpeg|gif|png|svg|mp4|txt))/i)?[1]
     if imageFileName?
       existingImageFile = glob.sync(path.resolve searchDir, imageFileName.replace /\W/g, '?')[0]
-      if existingImageFile? and  existingImageFile not in imageFiles
+      if existingImageFile? and existingImageFile not in imageFiles
         imageFiles.push existingImageFile
   imageFiles

@@ -42,13 +42,6 @@
   location = {}
   location[mimeType] = url
   location
-
-createSubject = (subjectData) ->
-  return if ( !subjectData or !subjectData instanceof Object )
-  subject = apiClient.type('subjects').create(subjectData)
-  await subject.save().then(defer _).catch(console.error.bind console)
-  log "Saved subject #{subject.id}"
-  subject

 argOpts =
   alias:


misc updates with tag-list .live bug

misc updates with tag-list .live bug

--- public/dreamcatcher/controllers/common/tags_controller.coffee
+++ public/dreamcatcher/controllers/common/tags_controller.coffee
@@ -5,34 +5,12 @@
   model: {
     tag: Dreamcatcher.Models.Tag
   }
-
+
   init: (el, mode='edit') ->
     @element = el
     @mode = mode
-
+    @clickEvent = if (navigator.userAgent.match(/iPad/i)) then "touchstart" else "click"

-  '#newTag keyup': (el, ev) ->
-    if ev.keyCode is 188 or ev.keyCode is 13 or ev.keyCode is 191 # ',', 'enter' and '/'
-      $('.tagAdd').click()
-
-  getTag: -> $('#newTag').val().replace('/','').replace(',','').trim()
-
-  '.tagAdd click': (el) ->
-    tagName = @getTag()
-
-    switch @mode
-      when 'new'
-        @appendTag tagName unless @alreadyExists tagName
-
-      when 'edit'
-        log 'editMode'
-        ###
-        entryId = $('#showEntry').data 'id'
-        @model.tag.create {
-          entry_id: entryId
-          what_name: tagName
-        }, @callback('appendTag', tagName)
-        ###

   alreadyExists: (tagName) ->
     exists = false
@@ -46,5 +24,39 @@
     html = $.View('//dreamcatcher/views/common/tags/show.ejs', {tagName: tagName})
     $('#tag-list').append html
     $('#newTag').val ''
+
+  removeTagFromDom: (tagId) ->
+    $("##{tagId}").remove()
+
+  getTag: -> $('#newTag').val().replace('/','').replace(',','').trim()
+
+  # DOM Listeners
+  '#newTag keyup': (el, ev) ->
+    if ev.keyCode is 188 or ev.keyCode is 13 or ev.keyCode is 191 # ',', 'enter' and '/'
+      $('.tagAdd').click()
+      return false
+
+  '.tagAdd click': (el) ->
+    tagName = @getTag()
+    log 'clickEvent: ' + @clickEvent
+    switch @mode
+      when 'new'
+        @appendTag tagName unless @alreadyExists tagName
+      when 'edit'
+        log 'edit mode'
+        # entryId = $('#showEntry').data 'id'
+        # @model.tag.create {
+        #   entry_id: entryId
+        #   what_name: tagName
+        # }, @callback('appendTag', tagName)

-}+  $('#tag-list').find('.tag .close').live @clickEvent, (ev) =>
+    # @removeTagFromDom($(ev.currentTarget).parent().data('id'))
+
+}
+
+# $(document).ready ->
+#   clickEvent = if (navigator.userAgent.match(/iPad/i)) then "touchstart" else "click"
+#   $('#tag-list').find('.tag .close').live 'click', (event)=>
+#     log 'delete'
+#     # @removeTagFromDom($(event.currentTarget).parent().data('id'))


Frame Ids must be integers.

Frame Ids must be integers.

--- background_scripts/main.coffee
+++ background_scripts/main.coffee
@@ -361,7 +361,7 @@
       try
         port.postMessage request
       catch
-        @unregisterFrame tabId, frameId
+        @unregisterFrame tabId, parseInt frameId

   prepareToActivateMode: (tabId, originatingFrameId, {modeIndex}) ->
     @tabState[tabId] = {frameIds: frameIdsForTab[tabId][..], hintDescriptors: [], originatingFrameId, modeIndex}


cleanup

cleanup

--- coffee/microbox.coffee
+++ coffee/microbox.coffee
@@ -1,48 +1,3 @@
-
-# contains = (haystack, needle) ->
-
-# 	haystack.indexOf (needle) > -1
-
-# parent = (element, filter) ->
-
-# 	# {Function} filter
-# 	if izzy['function'] filter
-
-# 		while _continue element
-
-# 			if filter element
-# 				return element
-
-# 			element = element.parentNode
-
-# 	# {Object} filter
-# 	else
-
-# 		while _continue element
-
-# 			good = 0
-# 			count = 0
-
-# 			for property of filter
-
-# 				e = element[property]
-# 				f = filter[property]
-
-# 				if contains element[property], filter[property]
-# 					++good
-
-# 				++count
-
-# 			if count is good
-# 				return element
-
-# 			element = element.parentNode
-
-# 	_continue = (element) ->
-# 		not izzy.defined(element.documentElement) and element.tagName isnt 'HTML'
-
-# 	false
-
 template =

 	caption: (data) ->


Fix to wait_for_email_verification for newer iron router

--- shared/router.coffee
+++ shared/router.coffee
@@ -77,8 +77,9 @@

   @route 'entryVerificationPending',
     path: '/verification-pending'
-    onBeforeAction: (pause)->
+    onBeforeAction: ->
       Session.set('entryError', undefined)
+      @next()

   @route 'entryResetPassword',
     path: 'reset-password/:resetToken'


#992 - Can't delete a child group after deleting its parent - also fixes #986

--- jsapp/xlform_model_view/view.row.coffee
+++ jsapp/xlform_model_view/view.row.coffee
@@ -92,14 +92,12 @@

   class GroupView extends BaseRowView
     className: "survey__row survey__row--group  xlf-row-view xlf-row-view--depr"
-    events:
-      "click .js-delete-group": "deleteGroup"
     initialize: (opts)->
       @options = opts
       @_shrunk = !!opts.shrunk
       @$el.attr("data-row-id", @model.cid)
       @surveyView = @options.surveyView
-    deleteGroup: (evt)->
+    deleteGroup: (evt)=>
       skipConfirm = $(evt.currentTarget).hasClass('js-force-delete-group')
       if skipConfirm or confirm('Are you sure you want to split apart this group?')
         @model.splitApart()
@@ -108,6 +106,7 @@

     render: ->
       @$el.html $viewTemplates.row.groupView(@model)
+      @$('.js-delete-group').click @deleteGroup
       @$label = @$('.group__label').eq(0)
       @$rows = @$('.group__rows').eq(0)


add website visit :P

add website visit :P

--- browser/src/coffee/search.coffee
+++ browser/src/coffee/search.coffee
@@ -36,6 +36,9 @@
             return e.preventDefault() if [40, 38].indexOf(e.which) >= 0
             keyword = @keyword
             return if keyword.length < 2
+            if keyword.match /^https?:\/\//
+                @webContentSrc = keyword
+                return
             handle = new S.ds keyword
             loadedItems = {}
             processResult =  (result)->


Fix deleted aliases persistence, and blank users

--- src/cc.coffee
+++ src/cc.coffee
@@ -36,7 +36,9 @@
             _channels[channel] or= {}
             ctx = _channels[channel]
         robot.logger.debug "hubot-cc: creating / updating an alias (\"#{alias}\")"
-        ctx[alias] = users
+        ctx[alias] = users.filter((u) ->
+            u.trim().length > 1
+        )
         _saveAliases()

     _getMentions = (alias, channel = false) ->
@@ -73,6 +75,7 @@
         alias = res.match[1]
         delete _global[alias] if _global[alias]?
         delete _channels[channel][alias] if _channels[channel]?[alias]?
+        _saveAliases()
         res.reply "Removed @#{alias} from \"#{channel}\", and the global context."

     robot.respond /cc list/i, id: "cc.list", (res) ->


social: whine loudly if the delegate isn't instance of account

I saw this in prod on once, and that's weird/scary

--- bongo-api-builder/build.coffee
+++ bongo-api-builder/build.coffee
@@ -2,6 +2,8 @@
 Broker = require 'broker'
 {argv} = require 'optimist'
 KONFIG = require('koding-config-manager').load("main.#{argv.c}")
+
+require 'colors'

 broker = new Broker KONFIG.mq

@@ -11,12 +13,15 @@
   models      : '../workers/social/lib/social/models'
   resourceName: 'bongo-api-builder'
   mq          : broker
-  fetchClient :(sessionToken, context, callback)->
+  fetchClient : (sessionToken, context, callback) ->
     # console.log {'fetchClient', sessionToken, context, callback}
     [callback, context] = [context, callback] unless callback
     context             ?= group: 'koding'
     callback            ?= ->
-    koding.models.JUser.authenticateClient sessionToken, context, (err, account)->
+    koding.models.JUser.authenticateClient sessionToken, context, (err, account) ->
+      unless account instanceof koding.models.JAccount
+        console.log "this is not a proper account".red, { account }
+        koding.emit 'error', message: 'this is not a proper account'
       if err
         koding.emit 'error', err
       else


Increase Atom font

Increase Atom font

--- atom.cson
+++ atom.cson
@@ -43,7 +43,7 @@
     ]
   editor:
     fontFamily: "Fira Code Retina"
-    fontSize: 16
+    fontSize: 17
     invisibles: {}
     nonWordCharacters: "^*\"`'/|\\?!;:,.%#@(){}[]<>=+-~_"
     showIndentGuide: true


[Uebersicht] - Separate percentage, status, and time remaining into their own containers

--- uebersicht/battery.coffee
+++ uebersicht/battery.coffee
@@ -4,18 +4,27 @@

 render: (output) ->
   info = output.split('@')
-  status = info[0]
-  percentage = info[1]
+  status = info[0].trim()
+  percentage = info[1].trim()
   remaining =
     if info[2].indexOf('no') == -1
-      info[2]
+      "#{info[2].trim()} remaining"
     else
       ''
-  info = [status, percentage, remaining].join(' ')
+  keyDiv = "
 battery
"
+  percentageDiv = "
 #{percentage}
"
+  statusDiv = "
 #{status}
"
+  remainingDiv =
+    if remaining != ''
+      "
 #{remaining}
"
+    else
+      ''
   """

-
 battery

-
 #{info.trim()}

+    #{keyDiv}
+    #{percentageDiv}
+    #{statusDiv}
+    #{remainingDiv}

   """

@@ -25,11 +34,12 @@
   justify-content: flex-end;
   width: 100%;
   top: 10px;
+  right: 10px;

   .container
-    margin: 0 10px 0 0;
     display: flex;
     height: 30px;
+    padding: 0 5px 0 0;
     background-color: #2b2b2b;

   .text
@@ -44,6 +54,6 @@

   .value
     align-self: center;
-    margin: 5px 5px 5px 5px;
+    margin: 5px 0px 5px 5px;
     background-color: #d2e9dd;
   """


config: rabbitPrefix.get() works

config: rabbitPrefix.get() works

--- utils/rabbitPrefix.coffee
+++ utils/rabbitPrefix.coffee
@@ -1,18 +1,14 @@
 fs       = require 'fs'
 nodePath = require 'path'

-projectRoot =->
-  nodePath.join __dirname, '..'
-
-hostname =->
-  require("os").hostname()
-
 get =->
+  projectRoot = nodePath.join(__dirname, '..')
   rabbitPrefix = ((
-    try fs.readFileSync nodePath.join(projectRoot(), '.rabbitvhost'), 'utf8'
+    try fs.readFileSync nodePath.join(projectRoot, '.rabbitvhost'), 'utf8'
     catch e
-      console.log "You're missing the '.rabbitvhost' file in #{projectRoot()}. I'll use your hostname #{hostname()} instead."
-      return hostname()
+      hostname = require("os").hostname()
+      console.log "You're missing the '.rabbitvhost' file in #{projectRoot}\nUsing your hostname: '#{hostname}' as identifier for RabbitMQ instead.\n"
+      hostname
   ).trim())+"-dev"
   rabbitPrefix = rabbitPrefix.split('.').join('-')


FShelpter: added getGroupRelated internal method for getting group related files

--- client/finder/fs/fshelper.coffee
+++ client/finder/fs/fshelper.coffee
@@ -212,9 +212,8 @@
     get    : (name)->
       "#{KD.config.uploadsUri}/#{KD.whoami().getId()}/#{name}"

-    upload : (name, content, type, slug, callback)->
-      # groups
-      args = {name, bucket : "groups", path : "buckets", content}
+    getGroupRelated : (group, name)->
+      "#{KD.config.uploadsUriForGroup}/#{group}/#{name}"

       #user
       # args = {name,  content}


Add comparator to fields

Add comparator to fields

--- client/lanes/models/Query.coffee
+++ client/lanes/models/Query.coffee
@@ -14,6 +14,7 @@
         format:   type: 'function'
         flex:     type: 'number',  default: 1
         textAlign: type: 'string', default: 'left'
+        comparator: type: 'function', default: -> Lanes.u.makeComparatorUsing('id')

     derived:
         default_value: deps: ['id'], fn: ->


adds space object class type api for better debugging

--- source/object.coffee
+++ source/object.coffee
@@ -104,6 +104,8 @@

     return Child

+  @type: (name) -> @toString = this::toString = -> name
+
   # Create and instance of the class that this method is called on
   # e.g.: Space.Object.create() would return an instance of Space.Object
   @create: ->


export the ID machinery

export the ID machinery

--- src/main.iced
+++ src/main.iced
@@ -7,3 +7,4 @@
 exports.status    = require './status'
 exports.checkers  = require './checkers'
 exports.app       = require './app'
+exports.id        = require './id'


Simple argument passing example

Simple argument passing example

--- spec/Thenable.coffee
+++ spec/Thenable.coffee
@@ -90,6 +90,24 @@
       chai.expect(err).to.be.an.instanceOf Error
       chai.expect(err.message).to.equal 'Failed here foo'
       done()
+
+  describe 'passing arguments', ->
+    it 'should produce the expected result', (done) ->
+      t = Root()
+      t.then (c, d) ->
+        # c.source.get('data') is 1, what we delivered to 'root'
+        d+1
+      .then (c, d) ->
+        # c.source.get('data') is 2
+        d+1
+      .then (c, d) ->
+        # c.source.get('data') is 3
+        d+1
+      .finally (c, d) ->
+        # c.source.get('data') is 4
+        chai.expect(d).to.equal 4
+        done()
+      t.deliver 1

   describe 'with a branching thenable', ->
     it 'should run two rounds of execution', (done) ->
@@ -414,7 +432,7 @@
         paths = results.map (r) -> r.path
         idx = paths.indexOf 'root-3-then'
         results[idx]
-      .then (c, res) ->
+      .finally (c, res) ->
         chai.expect(res).to.eql [15]
         done()


Fix stringify helper params.

Fix stringify helper params.

--- spec/compiler.coffee
+++ spec/compiler.coffee
@@ -6,7 +6,8 @@

 stringify = (o) ->
-  JSON.stringify o, 1, 1
+  JSON.stringify o, null, 1
+
 assert = chai.assert
 expect = chai.expect

@@ -278,4 +279,4 @@
       expect(ast).to.eql results
       #assert results[0]._uuid? is true, 'has uuid'
       #for key, val of ast
-      #  expect(results[0][key]).to.eql val+      #  expect(results[0][key]).to.eql val


Use "putOverlay" instead of old school

Use "putOverlay" instead of old school

--- client/finder/lib/index.coffee
+++ client/finder/lib/index.coffee
@@ -19,9 +19,6 @@

     super options, data

-    { frontApp } = kd.singletons.appManager
-    frontApp.on 'IDETabDropped', @bound 'hideFakeHolder'
-

   create: (options = {}) ->

@@ -37,9 +34,14 @@
     finderView.addSubView @getAppTitleView()  if options.addAppTitle
     finderView.addSubView @createUploader controller

-    @fakeHolder = new KDCustomHTMLView cssClass : 'hidden fake-holder'
+    finderView.putOverlay
+      container   : finderView
+      cssClass    : 'hidden'
+      opacity     : 1
+      transparent : yes

-    finderView.addSubView @fakeHolder
+    { frontApp } = kd.singletons.appManager
+    frontApp.on 'IDETabDropped', -> finderView.overlay?.hide()

     return controller

@@ -51,7 +53,7 @@
       partial  : "Ace Editor"

-  createUploader: (controller)->
+  createUploader: (controller) ->

     uploaderPlaceholder = new KDView
       domId       : 'finder-dnduploader'
@@ -69,7 +71,8 @@
       if mouseEvent.length
         { items }  = mouseEvent[0].originalEvent.dataTransfer

-        return @fakeHolder.show()  if items?[0].kind is 'string' and not internalDragging
+        if items?[0].kind is 'string' and not internalDragging
+          return finderView.overlay?.show()

       unless internalDragging
         uploaderPlaceholder.show()
@@ -87,7 +90,7 @@
     finderView = controller.getView()
     finderView.on 'dragenter', onDrag
     finderView.on 'dragover' , onDrag
-    finderView.on 'drop', @bound 'hideFakeHolder'
+    finderView.on 'drop', -> finderView.overlay?.hide()

     uploader
       .on 'dragleave', ->
@@ -108,8 +111,3 @@
         uploaderPlaceholder.hide()

     return uploaderPlaceholder
-
-
-  hideFakeHolder: ->
-
-    @fakeHolder.hide()


Don't pause to show changes to partial args if they aren't even visible.

--- coffee/DebuggerController.coffee
+++ coffee/DebuggerController.coffee
@@ -68,11 +68,12 @@
       delta = (if currentLine > @currentScrollTop then 0.125 else -0.125)
       @currentScrollTop += delta
       window.setTimeout (=> @render()), MILLIS_FOR_SCROLL_INSTRUCTIONS
-    else if props.numPartialCallExecuting != null &&
+    else if @features.showPartialCalls && props.numPartialCallExecuting != null &&
        props.numPartialCallExecuting != @prevProps.numPartialCallExecuting
       @prevProps = props
       window.setTimeout (=> @render()), MILLIS_TO_HIGHLIGHT_CALL
-    else if calls1 && calls0 && calls1.length == calls0.length &&
+    else if @features.showPartialCalls &&
+         calls1 && calls0 && calls1.length == calls0.length &&
          calls1[calls1.length - 1]?.length > calls0[calls0.length - 1]?.length
       @prevProps = props
       window.setTimeout (=> @render()), MILLIS_TO_HIGHLIGHT_ADDED_ARG


regular dom click trigger does not work, but click from react test utils does\!  pretty cool: https://facebook.github.io/react/docs/test-utils.html

--- test/components/practice.spec.coffee
+++ test/components/practice.spec.coffee
@@ -1,6 +1,6 @@
 {expect} = require 'chai'
 _ = require 'underscore'
-React = require 'react'
+React = require 'react/addons'
 Router = require 'react-router'

 # Temporary until we update to Router 0.13 where this is exposed
@@ -41,9 +41,31 @@

   it 'should load allow students to continue exercises', (done) ->
-    tests = (node, component, state, router) ->
-      expect(node.querySelector('button.btn-primary')).to.not.be.null
-      expect(node.querySelector('button.btn-primary').innerText).to.equal('Continue')
+    tests = (node) ->
+      continueButton = node.querySelector('button.btn-primary')
+      introScreenText = node.innerText
+
+      expect(continueButton).to.not.be.null
+      expect(continueButton.innerText).to.equal('Continue')
+
+      React.addons.TestUtils.Simulate.click(continueButton);
+
+      expect(node.innerText).to.not.be.equal(introScreenText)
+
       done()

     helper(VALID_MODEL, 1, tests)
+
+
+  it 'should render next screen when to continue is clicked', (done) ->
+    tests = (node) ->
+      continueButton = node.querySelector('button.btn-primary')
+      introScreenText = node.innerText
+
+      React.addons.TestUtils.Simulate.click(continueButton);
+      expect(node.innerText).to.not.be.equal(introScreenText)
+
+      done()
+
+    helper(VALID_MODEL, 1, tests)
+


day night cycle

day night cycle

--- src/Landscape.coffee
+++ src/Landscape.coffee
@@ -16,7 +16,7 @@
     @night.anchor.setTo(0.5, 0.5)

     fade = @game.add.tween(@night)
-    fade.to({ alpha: 1 }, 300000 / SPEED, Phaser.Easing.Linear.None, true, 200000 / SPEED)
+    fade.to({ alpha: 1 }, 300000 / SPEED, Phaser.Easing.Linear.None, true, 150000 / SPEED, 999, true)
     fade.start

     @sky = @game.add.sprite(@game.world.centerX, @game.world.centerY, 'sky')
@@ -24,7 +24,7 @@
     @sky.anchor.setTo(0.5, 0.5)

     fade = @game.add.tween(@sky)
-    fade.to({ alpha: 0 }, 500000 / SPEED, Phaser.Easing.Linear.None, true)
+    fade.to({ alpha: 0 }, 450000 / SPEED, Phaser.Easing.Linear.None, true, 0, 999, true)
     fade.start

     @sun = @game.add.sprite(0, 0, 'sun')
@@ -34,21 +34,21 @@
     @sun.y = @game.world.centerY * 0.5 - 100

     grow = @game.add.tween(@sun.scale)
-    grow.to({ x: 5, y: 3 }, 1200000 / SPEED, Phaser.Easing.Linear.None, true)
+    grow.to({ x: 1.5, y: 1.5 }, 450000 / SPEED, Phaser.Easing.Cubic.InOut, true, 0, 999, true)
     grow.start

     set = @game.add.tween(@sun)
-    set.to({ y: 450 }, 300000 / SPEED, Phaser.Easing.Quintic.None, true)
+    set.to({ y: 675 }, 450000 / SPEED, Phaser.Easing.Sinusoidal.InOut, true, 0, 999, true)
     set.start

     fade = @game.add.tween(@sun)
-    fade.to({ alpha: 0 }, 400000 / SPEED, Phaser.Easing.Linear.None, true)
+    fade.to({ alpha: 0 }, 450000 / SPEED, Phaser.Easing.Linear.None, true, 0, true, true)
     fade.start

     @cloud = @game.add.sprite(1000, 70, 'cloud')
     @cloud.alpha = 0.4
     move = @game.add.tween(@cloud)
-    move.to({x: -800}, 300000 / SPEED, Phaser.Easing.Linear.None, true, 0, 1)
+    move.to({x: -800}, 300000 / SPEED, Phaser.Easing.Linear.None, true, 0, 999)
     move.start

     @mountain0a = @game.add.sprite(0, 180, 'mountain0')


Ignore unrelated args and warn invalid args

--- lib/cl.coffee
+++ lib/cl.coffee
@@ -5,22 +5,43 @@
 # If one day we need to process more command line arguments,
 # it's better to find a 3rd party command line library to do this job.

+# Pre-process Arguments
+ignore2ndArg = false
+reElectron = /electron$/i
+preprocessArg = (arg, idx) ->
+  switch idx
+    when 0
+      ignore2ndArg = true if reElectron.test arg
+      return true
+    when 1
+      return true if ignore2ndArg
+    else
+      return false
+  false
+
 # Parse Debug Options
 reDebug = /^-(-debug|d)$/i
 reDebugEx = /^--debug-extra=\w[\w-]*(,\w[\w-]*)*$/i
 parseDebugOptions = (arg) ->
-  if reDebug.test(arg)
+  if reDebug.test arg
     process.env.DEBUG = 1
-  else if reDebugEx.test(arg)
+    true
+  else if reDebugEx.test arg
     if process.env.DEBUG_EXTRA?
       process.env.DEBUG_EXTRA += ','
     else
       process.env.DEBUG_EXTRA = ''
     process.env.DEBUG_EXTRA += arg.split("=")[1]
+    true
+  else
+    false

-process.argv.forEach (arg) ->
-  parseDebugOptions(arg)
+process.argv.forEach (arg, idx) ->
+  switch
+    when preprocessArg(arg, idx) then return
+    when parseDebugOptions(arg) then return
+    else console.warn "Invalid argument (ignored): #{arg}"

 if process.env.DEBUG?


Clampable#clampToBounds

Clampable#clampToBounds

--- src/clampable.coffee
+++ src/clampable.coffee
@@ -10,4 +10,4 @@
     Object.extend(I.clampData, data)

   clampToBounds: (bounds) ->
-    bounds ||= Rectangle x: 0, y: 0, App.width, App.height+    bounds ||= Rectangle x: 0, y: 0, widthApp.width, App.height


Commented out /contact route as it's not used

--- src/code/routes.cjsx
+++ src/code/routes.cjsx
@@ -27,11 +27,11 @@
 						cb null, require './views/randomizer-smash-up'
 			]
 		,
-			path: 'contact'
-			getComponent: (location, cb) =>
-				require.ensure [], (require) =>
-					cb null, require './views/contact'
-		,
+		# 	path: 'contact'
+		# 	getComponent: (location, cb) =>
+		# 		require.ensure [], (require) =>
+		# 			cb null, require './views/contact'
+		# ,
 			path: '*'
 			getComponent: (location, cb) =>
 				require.ensure [], (require) =>


Updated TwoLineTitle BigTitle to bold

Updated TwoLineTitle BigTitle to bold

--- Framework/Views/Controls/TwoLineTitle_Framework_Android.coffee
+++ Framework/Views/Controls/TwoLineTitle_Framework_Android.coffee
@@ -25,7 +25,7 @@
     bigTitle = super
     bigTitle.updateLayout {
       height: Ti.UI.SIZE
-      font: { fontSize: 28 }
+      font: { fontSize: 28, fontWeight: 'bold' }
     }

     bigTitle


deleted useless fields for user

deleted useless fields for user

--- src/core/user.coffee
+++ src/core/user.coffee
@@ -126,8 +126,6 @@
         param = req.body
         if param.name? and param.name isnt ""
             param.mail = param.name + do Date.now
-            param.id_acl = 0 #TO DO : put the right value
-            param.id_role = 0 #TO DO : put the right value
             @newUser req, res, param, ((req, res) =>
                 {Room}  = require './room'
                 Room.newRoom req, res, { }
@@ -145,8 +143,6 @@
                 rid             = param.room
                 delete param.room
                 param.mail      = param.name + do Date.now
-                param.id_acl    = 0
-                param.id_role   = 0
                 @newUser req, res, param, ((req, res) =>
                     res.redirect "/room/#{rid}"
                 ), @errorOnQuickLogin


fix issue # 60

fix issue # 60

--- lib/md.coffee
+++ lib/md.coffee
@@ -352,19 +352,24 @@
     $(preElement).replaceWith(highlightedBlock)

   $('pre').each (i, preElement)->
-    codeBlock = $(preElement).children().first()
-    lang = 'text'
-    if codeBlock.attr('class')
-      lang = codeBlock.attr('class').replace(/^language-/, '') or 'text'
+    if preElement.children[0].name == 'code'
+      codeBlock = $(preElement).children().first()
+      lang = 'text'
+      if codeBlock.attr('class')
+        lang = codeBlock.attr('class').replace(/^language-/, '') or 'text'
+      text = codeBlock.text()
+    else
+      lang = 'text'
+      text = preElement.children[0].data

     if lang == 'mermaid'
       mermaid.parseError = (err, hash)->
         renderCodeBlock(preElement, err, 'text')

-      if mermaidAPI.parse(codeBlock.text().trim())
-        $(preElement).replaceWith("
 #{codeBlock.text()}
")
-    else
-      renderCodeBlock(preElement, codeBlock.text(), lang)
+      if mermaidAPI.parse(text.trim())
+        $(preElement).replaceWith("
 #{text}
")
+    else
+      renderCodeBlock(preElement, text, lang)

   return $.html()

@@ -480,7 +485,6 @@
         html = md.render(editor.getText())

   markdownPreview.headings = headings
-
   return resolveImagePathAndCodeBlock(html, markdownPreview, option)

 module.exports = {


skip <!-- slide --> inside code block

skip <!-- slide --> inside code block

--- lib/md.coffee
+++ lib/md.coffee
@@ -910,6 +910,15 @@
   outputString = text.replace /(^|\n)\<\!\-\-\s+slide\s+([\w\W]*?)\-\-\>/g, (whole, prefix, args, offset)->
     match = args.match(/(?:[^\s\n:"']+|"[^"]*"|'[^']*')+/g) # split by space and \newline and : (not in single and double quotezz)

+    # skip <!-- slide --> within code block
+    # eg:
+    # ```html
+    # <!-- slide -->
+    # ```
+    str = text.slice(0, offset)
+    n = str.match(/^\`\`\`/gm)?.length or 0
+    return whole if n % 2 != 0
+
     if match and match.length % 2 == 0
       option = {}
       i = 0
@@ -925,7 +934,7 @@
       option = {}

     if prefix == '\n'
-      line = text.slice(0, offset).match(/^/gm).length
+      line = str.match(/^/gm).length
     else
       line = 0


framerate docs

framerate docs

--- src/framerate.coffee
+++ src/framerate.coffee
@@ -1,11 +1,11 @@
 ###*
-@name Framerate
-@constructor
-
 This object keeps track of framerate and displays it by creating and appending an
 html element to the DOM.

 Once created you call snapshot at the end of every rendering cycle.
+
+@name Framerate
+@constructor
 ###
 Framerate = (options) ->
   options ||= {}
@@ -41,10 +41,10 @@
   setInterval(updateFramerate, framerateUpdateInterval)

   ###*
+  Call this method everytime you render.
+
   @name rendered
   @methodOf Framerate#
-
-  Call this method everytime you render.
   ###
   self =
     rendered: ->


Bugfix: wrong typeahead displaykey for streets.

The bug was introduced in faa12735786705537cca0b7d1eda7e4dfd962e24.

--- src/geocoding.coffee
+++ src/geocoding.coffee
@@ -155,7 +155,12 @@
         getDatasetOptions: =>
             name: 'address'
             displayKey: (c) ->
-                c.humanAddress()
+                if c instanceof models.Position
+                    c.humanAddress()
+                else if c instanceof models.Street
+                    c.getText 'name'
+                else
+                    c
             source: @getSource()
             templates:
                 suggestion: (c) =>


cosmetic

cosmetic

--- lib/ui.coffee
+++ lib/ui.coffee
@@ -684,6 +684,7 @@
     return if @suppressPreview
     return unless @isActive()
     return unless item = @items.getSelectedItem()
+
     @inPreview = true
     @provider.openFileForItem(item, activatePane: false).then (editor) =>
       editor.scrollToBufferPosition(item.point, center: true)


Update docs info

Update docs info

--- src/instagram.coffee
+++ src/instagram.coffee
@@ -3,10 +3,12 @@
 #
 # Dependencies:
 #   "instagram-node-lib": "*"
+#   https://github.com/mckelvey/instagram-node-lib
 #
 # Configuration:
 #   HUBOT_INSTAGRAM_CLIENT_KEY
 #   HUBOT_INSTAGRAM_ACCESS_KEY
+#   HUBOT_INSTAGRAM_ACCESS_TOKEN
 #
 # Commands:
 #   hubot insta tag <tag> <count>- Show recent instagram tags


Graph: Use setProperties() in loadJSON()

--- src/lib/Graph.coffee
+++ src/lib/Graph.coffee
@@ -646,10 +646,11 @@
   graph = new Graph definition.properties.name

   graph.startTransaction 'loadJSON', metadata
-  # TODO: capture properties in journal
+  properties = {}
   for property, value of definition.properties
     continue if property is 'name'
-    graph.properties[property] = value
+    properties[property] = value
+  graph.setProperties properties

   for id, def of definition.processes
     def.metadata = {} unless def.metadata


Reset command input on successful commands

--- app/assets/javascripts/angular/controllers/form_ctrl.js.coffee
+++ app/assets/javascripts/angular/controllers/form_ctrl.js.coffee
@@ -13,8 +13,9 @@
       path += "/#{other_entity}" if other_entity?

       $http.post(path)
-        .success (data, status, headers, config) ->
+        .success (data, status, headers, config) =>
           Output.add data
+          @command = ""

         .error (data, status, headers, config) ->
           console.error "Everything sucks ...", data


fix missing HTML tag attributes

fix missing HTML tag attributes

--- src/md-reader.coffee
+++ src/md-reader.coffee
@@ -576,7 +576,7 @@
                 if name is 'p' then send [ '.', name, null, meta, ]
                 else                send [ ')', name, null, meta, ]
               when 'lone'
-                send [ '.', name, null, meta, ]
+                send [ '.', name, extra, meta, ]
               else throw new Error "unknown HTML tag position #{rpr position}"
           #.................................................................................................
           else


remove Debugging

remove Debugging

--- mailer.coffee
+++ mailer.coffee
@@ -4,8 +4,6 @@
   auth:
     user: 'booksharingcucc@gmail.com'
     pass: process.env.BOOKSHARE_PASSWORD
-
-console.log process.env.OPENSHIFT_NODEJS_IP

 module.exports =
   sendListingRequestMessage: (email, takedowncode, information, address) ->


Bump latency.

Bump latency.

--- player.coffee
+++ player.coffee
@@ -12,7 +12,7 @@
   update: false
   overscan: false
   can_dupe: true
-  latency: 90
+  latency: 180
   bufferSize: 248

   constructor: (@gl, @audio, @inputs, @core, @game, @save) ->


Use unverified_positions instead of current_line

--- player.coffee
+++ player.coffee
@@ -46,14 +46,14 @@
           @unverified_positions.splice(@unverified_positions.indexOf(pos), 1)

     currentLine: ->
-      @current_line.points.slice() # Copy array
+      @unverified_positions.slice() # Copy array

     currentLinePos: ->
-      return [] unless @current_line.points.length > 0
-      @current_line.points[@current_line.points.length - 1]
+      return [] unless @unverified_positions.length > 0
+      @unverified_positions[@unverified_positions.length - 1]

     addToLine: (pos) ->
-      @current_line.points.push(pos)
+      @unverified_positions.push(pos)

   Player
 )


FinderController:moveFiles remove async.parallel dependency, uses promises

--- client/finder/filetree/controllers/findertreecontroller.coffee
+++ client/finder/filetree/controllers/findertreecontroller.coffee
@@ -286,21 +286,21 @@
       targetNodeView = @nodes[targetNodeView.getData().parentPath]
       targetItem = targetNodeView.getData()

-    stack = []
-    nodesToBeMoved.forEach (node)=>
-      stack.push (cb) =>
-        sourceItem = node.getData()
-        FSItem.move sourceItem, targetItem, (err, response)=>
-          if err then @notify null, null, err
-          else
-            cb err, node
-
-    callback or= (error, result) =>
-      @notify "#{result.length} item#{if result.length > 1 then 's' else ''} moved!", "success"
-      @removeNodeView node for node in result
+    movedNodes = []
+    results = nodesToBeMoved.map (node) ->
+      sourceItem = node.getData()
+      FSItem.move(sourceItem, targetItem).then ->
+        movedNodes.push node
+
+    Promise.all(results).then =>
+      @notify "#{movedNodes.length} item#{if movedNodes.length > 1 then 's' else ''} moved!", "success"
+      @removeNodeView node for node in movedNodes
       @refreshFolder targetNodeView

-    async.parallel stack, callback
+    .catch (err) =>
+      @notify null, null, err
+
+    .nodeify callback

   copyFiles:(nodesToBeCopied, targetNodeView, callback)->


fix broken tz selector

fix broken tz selector

--- web/js/utils.coffee
+++ web/js/utils.coffee
@@ -23,7 +23,7 @@
       39: '#193695' # Kerava internal bus lines

     @utcOffset: () ->
-      @_utcOffset ?= parseInt($('[meta[name=x-timezone-offset]').attr('content'), 10)
+      @_utcOffset ?= parseInt($('meta[name=x-timezone-offset]').attr('content'), 10)

     @now: () ->
       now = moment()


Update all.coffee

Update all.coffee

--- src/query/all.coffee
+++ src/query/all.coffee
@@ -82,7 +82,7 @@
           ret.push element

       return ret
-    return [ qs ] if qs instanceof Element
+    return if single == true then qs else [ qs ] if qs instanceof Element
     if waff.__isarray qs
       arr = waff.__toarray qs
       _arr = []


basics of drag and drop support

basics of drag and drop support

--- src/tabs-view.coffee
+++ src/tabs-view.coffee
@@ -4,6 +4,8 @@

 module.exports =
 class Tabs extends View
+  dndType: 'text/x-atom-tab'
+
   @activate: (rootView) ->
     rootView.eachEditor (editor) =>
       @prependToEditorPane(rootView, editor) if editor.attached
@@ -33,8 +35,25 @@
       @editor.destroyEditSessionIndex(index)
       false

-    @on 'dragstart', '.tab', (e) ->
-      console.log 'TAB DRAGGED', e
+    @on 'dragstart', '.tab', @onDragStart
+    @on 'dragend',   '.tab', @onDragEnd
+    @on 'dragenter', '.tab', @onDragEnter
+    @on 'dragleave', '.tab', @onDragLeave
+
+  onDragStart: (event) =>
+    $(event.target).addClass 'is-dragging'
+
+  onDragEnd: (event) =>
+    $(event.target).removeClass 'is-dragging'
+
+  onDragEnter: (event) =>
+    el = $(event.target)
+    el = el.closest('.tab') if !el.hasClass('tab')
+    el.addClass 'is-drop-target'
+
+  onDragLeave: (event) =>
+    el = $(event.target)
+    el.removeClass 'is-drop-target' if el.hasClass 'tab'

   addTabForEditSession: (editSession) ->
     @append(new Tab(editSession, @editor))


Refactor destroyBindings a bit.

Refactor destroyBindings a bit.

--- src/view/view.coffee
+++ src/view/view.coffee
@@ -144,10 +144,10 @@
       @html = @constructor.store.get(source)

     set: (key, html) ->
-      @html = html
-      @isBound = false
       @destroyBindings()
       @destroySubviews()
+
+      @html = html

       @loadView(@node) if @node and html?
       @initializeBindings() if @bindImmediately
@@ -165,8 +165,10 @@
       Batman.removeData(oldNode, 'view', true) if oldNode
       return if node == @node

+      @destroyBindings()
+      @destroySubviews()
+
       @node = node
-      @isBound = false
       return if not node

       Batman._data(node, 'view', this)
@@ -193,6 +195,7 @@
   destroyBindings: ->
     binding.die() for binding in @bindings
     bindings = []
+    @isBound = false

   destroySubviews: ->
     subview.die() for subview in @subviews.toArray()


We need Directory now.

We need Directory now.

--- src/workspace.coffee
+++ src/workspace.coffee
@@ -4,6 +4,7 @@
 {join} = path
 {Emitter, Disposable, CompositeDisposable} = require 'event-kit'
 fs = require 'fs-plus'
+{Directory} = require 'pathwatcher'
 DefaultDirectorySearcher = require './default-directory-searcher'
 Model = require './model'
 TextEditor = require './text-editor'


fsdafads

fsdafads

--- vendor/assets/javascripts/log.coffee
+++ vendor/assets/javascripts/log.coffee
@@ -460,7 +460,7 @@
 #### Start deansi.coffee
 Log.Deansi =
   CLEAR_ANSI: ///
-(?:\033) # leader
+(?:\x33) # leader
 (?:
     \[0?c                 # query device code
   | \[[0356]n             # device-related


Remove stray console.log

Remove stray console.log

--- services/web/app/coffee/Features/History/RestoreManager.coffee
+++ services/web/app/coffee/Features/History/RestoreManager.coffee
@@ -33,7 +33,6 @@
 	_addEntityWithUniqueName: (user_id, project_id, parent_folder_id, basename, fsPath, callback = (error) ->) ->
 		FileSystemImportManager.addEntity user_id, project_id, parent_folder_id, basename, fsPath, false, (error, entity) ->
 			if error?
-				console.log "ERROR", error, error instanceof Errors.InvalidNameError
 				if error instanceof Errors.InvalidNameError
 					# likely a duplicate name, so try with a prefix
 					date = moment(new Date()).format('Do MMM YY H:mm:ss')


Remove blank options argument from renderMarkdown

--- src/packages/markdown-preview/lib/markdown-preview-view.coffee
+++ src/packages/markdown-preview/lib/markdown-preview-view.coffee
@@ -95,7 +95,7 @@

   renderMarkdown: ->
     @setLoading()
-    roaster @buffer.getText(), {}, (err, html) =>
+    roaster @buffer.getText(), (err, html) =>
       if err
         @setErrorHtml(err)
       else


adding test for #2037; should've included it in the first place

--- test/comments.coffee
+++ test/comments.coffee
@@ -202,3 +202,6 @@
     b: ->

   ok B.prototype.a instanceof Function
+
+test "#2037: herecomments shouldn't imply line terminators", ->
+  do (-> ### ###; fail)


include Cooldown as default module

include Cooldown as default module

--- test/cooldown.coffee
+++ test/cooldown.coffee
@@ -52,7 +52,6 @@
         approachBy: 1
         target: 1000

-  obj.include(Cooldown)

   4.times ->
     obj.update()


cooldown finilazation

cooldown finilazation

--- test/cooldown.coffee
+++ test/cooldown.coffee
@@ -7,7 +7,6 @@
       bullet:
         target: 3
         approachBy: 1
-        value: 100

   obj.include(Cooldown)