Remove unneeeded calls to config.update()

--- src/packages/whitespace/spec/whitespace-spec.coffee
+++ src/packages/whitespace/spec/whitespace-spec.coffee
@@ -22,7 +22,6 @@
     spyOn(fsUtils, 'write')

     config.set("whitespace.ensureSingleTrailingNewline", false)
-    config.update()

     # works for buffers that are already open when extension is initialized
     editor.insertText("foo   \nbar\t   \n\nbaz")
@@ -45,7 +44,6 @@

     afterEach ->
       config.set("whitespace.ensureSingleTrailingNewline", originalConfigValue)
-      config.update()

     it "adds a trailing newline when there is no trailing newline", ->
       editor.insertText "foo"
@@ -74,7 +72,6 @@

     it "does not add trailing newline if ensureSingleTrailingNewline is false", ->
       config.set("whitespace.ensureSingleTrailingNewline", false)
-      config.update()

       editor.insertText "no trailing newline"
       editor.getBuffer().save()


add preview

add preview

--- lib/fonts.coffee
+++ lib/fonts.coffee
@@ -1,6 +1,9 @@
 module.exports =

   config:
+    preview:
+      type: 'string'
+      default: 'The Quick brown fox { 0 !== "O" }'
     fontFamily:
       description: 'Use one of the fonts available in this package.'
       type: 'string'


clean up index.coffee to pass coffelint

clean up index.coffee to pass coffelint

--- lib/index.coffee
+++ lib/index.coffee
@@ -12,13 +12,17 @@
     constructor: (@roots) ->
       @files     = []
       util      = new RootsUtil(@roots)
-      @filesnames = if opts.files then util.files(opts.files).map((f) -> path.join(roots.root,f.relative)) else []
+
+      @filesnames = if opts.files
+        util.files(opts.files).map (f) ->
+          path.join(roots.root, f.relative)
+      else []

     compile_hooks: ->
       category = 'inline-css'

       write: (ctx) =>
-        if path.extname(ctx.file_options._path) == '.html' and opts.files == undefined
+        if path.extname(ctx.file_options._path) == '.html' and !opts.files
           @files.push(ctx)
           return false
         else if ctx.file_options.filename in @filesnames


Don't check for identical values in "Set"

--- lib/pages.coffee
+++ lib/pages.coffee
@@ -69,7 +69,8 @@
       check sub, Match.Where (sub) ->
         sub.connection?.id?

-      return 0 if @valuesEqual(@get(k, sub.connection.id), v)
+      #More expensive than setting to identical values, causes problems with regular expressions.
+      #return 0 if @valuesEqual(@get(k, sub.connection.id), v)

       if !@availableSettings[k] or (_.isFunction(@availableSettings[k]) and !@availableSettings[k] v, sub)
         @error 4002, "Changing #{k} not allowed."
@@ -836,4 +837,4 @@
     @countPages()
     @checkQueue()

-Meteor.Pagination = Pages+Meteor.Pagination = Pages


adding x-forwarded-* headers

adding x-forwarded-* headers

--- lib/proxy.coffee
+++ lib/proxy.coffee
@@ -96,7 +96,9 @@
       dest = splitHostPort(randomUpstream(req.conci_endpoint.upstream))
       @http_proxy.web(req, res, {
         target: "http://#{dest.host}:#{dest.port}",
-        host: req.conci_domain
+        headers: { 'x-forwarded-user': req.session.passport.user },
+        host: req.conci_domain,
+        xfwd: true
         })

 module.exports = new Proxy


Replaced block of functions with helper functions.

--- lib/setup.coffee
+++ lib/setup.coffee
@@ -17,43 +17,15 @@
 	config.network.hostonly ?= {}
 	config.network.internal ?= {}

-	configure_dhcp = (nn, nc, add_dhcp, modify_dhcp) ->
-		(callback) ->
-			return callback new Error "no ip specified for network #{nn}" if not nc.ip?
-			return callback new Error "no netmask specified for network #{nn}" if not nc.netmask?
-			return callback new Error "no dhcp lower_ip specified for network #{nn}" if not nc.dhcp.lower_ip?
-			return callback new Error "no dhcp upper_ip specified for network #{nn}" if not nc.dhcp.upper_ip?
-
-			dhcp.list_servers (err, servers) ->
-				return callback err if err
-
-				s = servers.narrow (previous, current) ->
-					return previous if previous and (previous.NetworkName == nn or previous.NetworkName == "HostInterfaceNetworking-#{nn}")
-					return current if current and (current.NetworkName == nn or current.NetworkName == "HostInterfaceNetworking-#{nn}")
-
-				h = () ->
-					s.IP != nc.ip or
-					s.NetworkMask != nc.netmask or
-					s.lowerIPAddress != nc.dhcp.lower_ip or
-					s.upperIPAddress != nc.dhcp.upper_ip
-
-				if s
-					if h()
-						modify_dhcp nn, nc.ip, nc.netmask, nc.dhcp.lower_ip, nc.dhcp.upper_ip, callback
-					else
-						return do callback if callback
-				else
-					 add_dhcp nn, nc.ip, nc.netmask, nc.dhcp.lower_ip, nc.dhcp.upper_ip, callback
-
-	for netname, netconfig of config.network.hostonly
-		actions.push do (netname, netconfig) ->
+	for n, c of config.network.hostonly
+		actions.push do (n, c) ->
 			(callback) ->
 				network.list_hostonly_ifs (err, ifs) ->
 					return callback err if err

 					i = ifs.narrow (previous, current) ->
-						return previous if previous and previous.Name == netname
-						return current if current and current.Name == netname
+						return previous if previous and previous.Name == n
+						return current if current and current.Name == n

 					if not i
 						callee = arguments.callee
@@ -63,27 +35,29 @@

 							network.list_hostonly_ifs callee
 					else
-						if i.IP != netconfig.ip or s.i.NetworkMask != netconfig.netmask
-							network.configure_hostonly_if netname, netconfig.ip, netconfig.netmask, callback
+						if i.IP != c.ip or s.i.NetworkMask != c.netmask
+							network.configure_hostonly_if n, c.ip, c.netmask, callback
 						else
 							return do callback if callback

-		if netconfig.dhcp?
-			actions.push do (netname, netconfig) ->
-				configure_dhcp netname, netconfig, dhcp.add_hostonly_server, dhcp.modify_hostonly_server
+		if c.dhcp?
+			actions.push do (n, c) ->
+				(callback) ->
+					dhcp.ensure_hostonly_server n, c.ip, c.netmask, c.dhcp.lower_ip, c.dhcp.upper_ip, callback

-			actions.push do (netname, netconfig) ->
+			actions.push do (n, c) ->
 				(callback) ->
-					dhcp.enable_hostonly_server netname, callback
+					dhcp.enable_hostonly_server n, callback

-	for netname, netconfig of config.network.internal
-		if netconfig.dhcp?
-			actions.push do (netname, netconfig) ->
-				configure_dhcp netname, netconfig, dhcp.add_internal_server, dhcp.modify_internal_server
+	for n, c of config.network.internal
+		if c.dhcp?
+			actions.push do (n, c) ->
+				(callback) ->
+					dhcp.ensure_internal_server n, c.ip, c.netmask, c.dhcp.lower_ip, c.dhcp.upper_ip, callback

-			actions.push do (netname, netconfig) ->
+			actions.push do (n, c) ->
 				(callback) ->
-					dhcp.enable_internal_server netname, callback
+					dhcp.enable_internal_server n, callback

 	if actions.length == 0
 		return do callback if callback


Users fix

Users fix

--- lib/users.coffee
+++ lib/users.coffee
@@ -1,22 +1,7 @@
-Users= {}
-
-Users.GetTestUser = ->
-  Meteor.users.findOne({name:"Juan"})
-  },
-  {
-    name: "Christopher"
-    image: "johnny-liftoff.jpg"
-    age: 37
-    interests: ["users", "projects", "tags"]
+@Users = {}

 Users.findOne = (name)->
   Meteor.users.findOne({name:name})

 Users.find = ->
   Meteor.users.find()
-
-
-
-
-
-


Fixed join table code

Fixed join table code

--- lib/utils.coffee
+++ lib/utils.coffee
@@ -64,10 +64,12 @@
       return data.id
     return data

-  @joinTableName: (relation) ->
+  @joinTableURL: (relation) ->
     model_name1 = inflection.pluralize(inflection.underscore(relation.model_type.model_name))
     model_name2 = inflection.pluralize(inflection.underscore(relation.reverse_relation.model_type.model_name))
     return if model_name1.localeCompare(model_name2) < 0 then "#{model_name1}_#{model_name2}" else "#{model_name2}_#{model_name1}"
+
+  @joinTableModelName: (relation) -> inflection.classify(inflection.singularize(Utils.joinTableURL(relation)))

   # @private
   @createJoinTableModel: (relation) ->
@@ -75,11 +77,16 @@
     schema[relation.foreign_key] = ['Integer', indexed: true]
     schema[relation.reverse_relation.foreign_key] = ['Integer', indexed: true]

-    # @private
-    class JoinTable extends Backbone.Model
-      urlRoot: "#{Utils.parseUrl(_.result(relation.model_type.prototype, 'url')).database_path}/#{Utils.joinTableName(relation)}"
-      @schema: schema
-      sync: relation.model_type.createSync(JoinTable)
+    try
+      class JoinTable extends Backbone.Model
+        urlRoot: "#{Utils.parseUrl(_.result(relation.model_type.prototype, 'url')).database_path}/#{Utils.joinTableURL(relation)}"
+        @schema: schema
+        sync: relation.model_type.createSync(JoinTable)
+    catch
+      class JoinTable extends Backbone.Model
+        @model_name: Utils.joinTableModelName(relation)
+        @schema: schema
+        sync: relation.model_type.createSync(JoinTable)

     return JoinTable


fixed a bug with how the sanitization code wasn't stripping out attributes (and removed a console.debug)

--- vendor/assets/javascripts/mercury/regions/editable.js.coffee
+++ vendor/assets/javascripts/mercury/regions/editable.js.coffee
@@ -331,7 +331,6 @@
       event.preventDefault()
       return
     else
-      console.debug('testing')
       # get current selection & range
       selection = @selection()
       selection.placeMarker()
@@ -369,8 +368,8 @@
             for allowedTag, allowedAttributes of Mercury.config.pasting.whitelist
               if element.tagName.toLowerCase() == allowedTag.toLowerCase()
                 allowed = true
-                for attr, index in element.attributes
-                  jQuery(element).attr(attr.name, null) if attr && allowedAttributes.indexOf(attr.name) == -1
+                for attr in jQuery(element.attributes)
+                  jQuery(element).removeAttr(attr.name) unless attr.name in allowedAttributes
                 break
             jQuery(element).replaceWith(jQuery(element).contents()) unless allowed
           content = sanitizer.html()


social-api: define reply list get request function

--- workers/social/lib/social/models/socialapi/requests.coffee
+++ workers/social/lib/social/models/socialapi/requests.coffee
@@ -102,6 +102,13 @@

   url = "#{SOCIAL_API_URL}/message/#{data.messageId}/reply"
   post url, data, callback
+
+listReplies = (data, callback)->
+  unless data.messageId
+    return callback { message: "Request is not valid for adding a reply"}
+
+  url = "#{SOCIAL_API_URL}/message/#{data.messageId}/reply"
+  get url, data, callback

 fetchPopularTopics = (data, callback)->
   if not data.groupName or not data.type
@@ -248,6 +255,7 @@
   fetchPopularPosts
   fetchPopularTopics
   addReply
+  listReplies
   unlikeMessage
   likeMessage
   listLikers


proper order [#67023622]

proper order [#67023622]

--- app/assets/javascripts/controllers/project_commits_controller.js.coffee
+++ app/assets/javascripts/controllers/project_commits_controller.js.coffee
@@ -1,3 +1,3 @@
 Review.ProjectCommitsController = Ember.ArrayController.extend
-  sortProperties: ['message']
+  sortProperties: ['created_at']
   sortAscending:  false


Trigger inview after opening modal

Trigger inview after opening modal

--- angular/core/services/modal_service.coffee
+++ angular/core/services/modal_service.coffee
@@ -31,6 +31,7 @@
       $timeout(->
         elementToFocus = document.querySelector('md-dialog [md-autofocus]') || document.querySelector('md-dialog h1')
         elementToFocus.focus()
+        angular.element(window).triggerHandler('checkInView')
       , 400)

     ariaLabel = (modal) ->


remove readymade-site-background

remove readymade-site-background

--- app/assets/javascripts/components/app.cjsx
+++ app/assets/javascripts/components/app.cjsx
@@ -58,9 +58,6 @@

-

-

-

           <MainHeader


adding common vars to gramgoods.js

adding common vars to gramgoods.js

--- app/assets/javascripts/gramgoods.js.coffee
+++ app/assets/javascripts/gramgoods.js.coffee
@@ -6,6 +6,10 @@
         timeout: 10000
         error: ->
             alert 'Something went wrong. Please refresh the page and try again.'
+
+    window.hide = 'hide'
+    window.$window = ($ window)
+    window.$product_form_wrapper = ($ '#product_form_wrapper')

     window.spinner = new Spinner {color: '#fff'}
     $.extend window.spinner,


OX-721 testing

OX-721 testing

--- app/assets/javascripts/oxalis/model.coffee
+++ app/assets/javascripts/oxalis/model.coffee
@@ -45,7 +45,7 @@

             @user = new User(user)
             @scaleInfo = new ScaleInfo(tracing.content.dataSet.scale)
-            @binary = new Binary(@user, tracing.content.dataSet, TEXTURE_SIZE_P, "color", 8)
+            @binary = new Binary(@user, tracing.content.dataSet, TEXTURE_SIZE_P, "volume", 24)
             @binaryVolume = new Binary(@user, tracing.content.dataSet, TEXTURE_SIZE_P, "volume", 24, true)
             @flycam = new Flycam2d(VIEWPORT_SIZE, @scaleInfo, @binary.cube.ZOOM_STEP_COUNT - 1, @user)
             @flycam3d = new Flycam3d(DISTANCE_3D, tracing.content.dataSet.scale)


Fixes #654

Fixes #654

--- app/assets/javascripts/oxalis/model.coffee
+++ app/assets/javascripts/oxalis/model.coffee
@@ -56,9 +56,11 @@
         @user.fetch().pipe( =>

           @set("dataset", new Backbone.Model(tracing.content.dataSet))
+          colorLayers = _.filter( @get("dataset").get("dataLayers"),
+                                  (layer) -> layer.category == "color")
           @set("datasetConfiguration", new DatasetConfiguration({
             @datasetName
-            dataLayerNames : _.pluck(@get("dataset").get("dataLayers"), "name")
+            dataLayerNames : _.pluck(colorLayers, "name")
           }))
           @get("datasetConfiguration").fetch().pipe( =>


code prettify

code prettify

--- files.coffee
+++ files.coffee
@@ -288,7 +288,7 @@
     extension     = fileName.split('.').pop()
     path          = "#{@storagePath}/#{randFileName}.#{extension}"

-    result        =
+    result =
       name:       fileName
       extension:  extension
       path:       path
@@ -301,16 +301,15 @@
           type: if opts.type then opts.type else 'application/*'
           size: if opts.size then opts.size else buffer.length
           extension: extension
-      isVideo:    if opts.type then opts.type.toLowerCase().indexOf("video") > -1 else false
-      isAudio:    if opts.type then opts.type.toLowerCase().indexOf("audio") > -1 else false
-      isImage:    if opts.type then opts.type.toLowerCase().indexOf("image") > -1 else false
-      _prefix:    @_prefix
+      isVideo: if opts.type then opts.type.toLowerCase().indexOf("video") > -1 else false
+      isAudio: if opts.type then opts.type.toLowerCase().indexOf("audio") > -1 else false
+      isImage: if opts.type then opts.type.toLowerCase().indexOf("image") > -1 else false
+      _prefix: @_prefix
       _collectionName: @collectionName
       _storagePath:    @storagePath
       _downloadRoute:  @downloadRoute

     console.info "Meteor.Files Debugger: The file #{fileName} (binary) was added to #{@collectionName}" if @debug
-

     if callback
       fs.outputFile path, buffer, 'binary', callback
@@ -352,7 +351,7 @@
       throw new Meteor.Error 500, "Error on [load(#{url}, #{opts})]; Error:" + JSON.stringify error
     ).on('response', (response) ->
       bound ->
-        result        =
+        result =
           name:       fileName
           extension:  extension
           path:       path
@@ -365,10 +364,10 @@
               type: response.headers['content-type']
               size: response.headers['content-length']
               extension: extension
-          isVideo:    response.headers['content-type'].toLowerCase().indexOf("video") > -1
-          isAudio:    response.headers['content-type'].toLowerCase().indexOf("audio") > -1
-          isImage:    response.headers['content-type'].toLowerCase().indexOf("image") > -1
-          _prefix:    self._prefix
+          isVideo: response.headers['content-type'].toLowerCase().indexOf("video") > -1
+          isAudio: response.headers['content-type'].toLowerCase().indexOf("audio") > -1
+          isImage: response.headers['content-type'].toLowerCase().indexOf("image") > -1
+          _prefix: self._prefix
           _collectionName: self.collectionName
           _storagePath:    self.storagePath
           _downloadRoute:  self.downloadRoute
@@ -623,8 +622,8 @@
             Meteor.call self.methodNames.MeteorFileWrite, unitArray, fileData, meta, first, chunksQtyInPart, currentChunk, totalSentChunks, randFileName, part, streams, file.size, (error, data)->
               if not result.onPause.get()
                 if data.chunk + 1 <= chunksQtyInPart
-                  from         = currentChunk * self.chunkSize
-                  to           = from + self.chunkSize
+                  from = currentChunk * self.chunkSize
+                  to   = from + self.chunkSize

                   fileReader.readAsArrayBuffer filePart.slice from, to
                   currentChunk = ++data.chunk
@@ -633,8 +632,8 @@
               else
                 result.continueFrom.push () ->
                   if data.chunk + 1 <= chunksQtyInPart
-                    from         = currentChunk * self.chunkSize
-                    to           = from + self.chunkSize
+                    from = currentChunk * self.chunkSize
+                    to   = from + self.chunkSize

                     fileReader.readAsArrayBuffer filePart.slice from, to
                     currentChunk = ++data.chunk


remove log

remove log

--- SingularityUI/app/views/deploy.coffee
+++ SingularityUI/app/views/deploy.coffee
@@ -27,8 +27,6 @@
         id = $target.data 'id'
         collectionName = $target.data 'collection'

-        console.log collectionName
-
         # Need to reach into subviews to get the necessary data
         collection = @subviews[collectionName].collection
         utils.viewJSON collection.get id


stripping whitespace from labels when building csvs and inserting into input

--- app/assets/javascripts/admin/stores.js.coffee
+++ app/assets/javascripts/admin/stores.js.coffee
@@ -75,8 +75,9 @@

     # build csv of label data
     # i.e. size: small,medium,large
-    map_label_values = (name) -> $.map(($ ".label-#{name}"), (e) ->
-        ($ e).data('value')).join(',')
+    map_label_values = (name) ->
+        $.map(($ ".label-#{name}"), (e) ->
+            $.trim(($ e).data('value'))).join(',')

     trigger_process_csv = ($input) ->
         if $input.val()?


playing with onscreen directives

playing with onscreen directives

--- app/assets/javascripts/angular/main.js.coffee
+++ app/assets/javascripts/angular/main.js.coffee
@@ -47,6 +47,22 @@

     return

+@gradecraft.directive "ngOnscreen", ->
+  require: "ngModel"
+  link: (scope, elm, attr, ctrl) ->
+    ctrl.$parsers.unshift (viewValue) ->
+      value = viewValue
+      max = scope.$eval(attr.ngMax)
+      if value and value != "" and value > max
+        ctrl.$setValidity "ngMax", false
+        'undefined'
+      else
+        ctrl.$setValidity "ngMax", true
+        value
+
+    return
+
+
 # @gradecraft.directive "ngMin", ->
 #   restrict: "A"
 #   require: "ngModel"


Fix naming

Fix naming

--- app/assets/javascripts/plugins/borg.js.coffee
+++ app/assets/javascripts/plugins/borg.js.coffee
@@ -10,7 +10,7 @@
   getTask: (host) ->
     @tasks[host] ||= @createTask(host)

-  updateTasksDOM: ->
+  refresh: ->
     for _, task of @tasks
       task.updateDOM()
     null
@@ -47,7 +47,7 @@
       @activate()

     @parse(parser)
-    @updateTasksDOM()
+    @refresh()

     if parser.findTaskEnd(@capistranoTask)
       @finish()


Fix border color calculation

Fix border color calculation

--- app/scripts/Civilization/Entity/Player.coffee
+++ app/scripts/Civilization/Entity/Player.coffee
@@ -4,4 +4,5 @@
     @fillColor = @mainColor

     # Convert the long int color to a hex string for TinyColor
-    @borderColor = tinycolor.darken(@mainColor.toString(16), 1)
+    borderColor = tinycolor.darken(@mainColor.toString(16), 30)
+    @borderColor = parseInt(borderColor.toHex(), 16)


Fix leak for graphs. Related to #622

Fix leak for graphs. Related to #622

--- admin/static/coffee/vis.coffee
+++ admin/static/coffee/vis.coffee
@@ -150,16 +150,16 @@
                 table:      @type is 'table'

             # Set up the plot
-            sensible_plot = @context.sensible()
+            @sensible_plot = @context.sensible()
                 .height(@HEIGHT_IN_PIXELS)
                 .colors(["#983434","#729E51"])
                 .extent([0, @HEIGHT_IN_UNITS])
             d3.select(@.$('.plot')[0]).call (div) =>
                 div.data([[@read_stats, @write_stats]])
                 # Chart itself
-                div.append('div')
+                @selection = div.append('div')
                     .attr('class', 'chart')
-                    .call(sensible_plot)
+                @selection.call(@sensible_plot)
                 # Horizontal axis
                 div.append('div')
                     .attr('class', 'haxis')
@@ -180,7 +180,7 @@
                 # Vertical axis
                 div.append('div')
                     .attr('class', 'vaxis')
-                    .call(@context.axis(@HEIGHT_IN_PIXELS, [@read_stats, @write_stats], sensible_plot.scale())
+                    .call(@context.axis(@HEIGHT_IN_PIXELS, [@read_stats, @write_stats], @sensible_plot.scale())
                         .orient('left')
                         .ticks(@VAXIS_TICK_SUBDIVISION_COUNT)
                         .tickSubdivide(@VAXIS_MINOR_SUBDIVISION_COUNT - 1)
@@ -189,7 +189,7 @@
                 # Vertical axis grid
                 div.append('div')
                     .attr('class', 'vgrid')
-                    .call(@context.axis(@HEIGHT_IN_PIXELS, [@read_stats, @write_stats], sensible_plot.scale())
+                    .call(@context.axis(@HEIGHT_IN_PIXELS, [@read_stats, @write_stats], @sensible_plot.scale())
                         .orient('left')
                         .ticks(@VAXIS_TICK_SUBDIVISION_COUNT)
                         .tickSize(-(@WIDTH_IN_PIXELS + 35), 0, 0)
@@ -200,6 +200,7 @@
             return @

         destroy: =>
+            @sensible_plot.remove(@selection)
             @context.on 'focus'
             @legend.destroy()


do run mismatch report but do not yet fail on results

--- bokehjs/test/test_common/test_defaults.coffee
+++ bokehjs/test/test_common/test_defaults.coffee
@@ -100,8 +100,7 @@
       console.log("'base.locations[\"#{m}\"]' not found but there's a Python model '#{m}'")
     expect(missing.length).to.equal 0

-  # this is skipped until it passes
-  it.skip "match between Python and CoffeeScript", ->
+  it "match between Python and CoffeeScript", ->
     fail_count = 0
     for name in all_view_model_names
       coll = Collections(name)
@@ -172,4 +171,6 @@

       if not check_matching_defaults(name, get_defaults(name), attrs)
         fail_count = fail_count + 1
-    expect(fail_count).to.equal 0
+
+    # TODO this is skipped until it passes
+    #expect(fail_count).to.equal 0


remove TODO

remove TODO

--- app/scripts/result_controllers.coffee
+++ app/scripts/result_controllers.coffee
@@ -216,7 +216,6 @@
                     URL.revokeObjectURL s.download.blobName
                 if value == ""
                     return
-                totalHits = 0 # TODO
                 requestData = s.instance.getProxy().prevParams
                 [fileName, blobName] = @kwicDownload.makeDownload value.split("/")..., s.kwic, requestData, hitsDisplay
                 s.download.fileName = fileName


timeout 30sec

timeout 30sec

--- angular/test/protractor.coffee
+++ angular/test/protractor.coffee
@@ -9,7 +9,7 @@
 exports.config =
   # seleniumServerJar: '../node_modules/webdriver-manager/selenium/selenium-server-standalone-3.3.0.jar'
   # seleniumAddress: 'http://localhost:4444/wd/hub'
-  allScriptsTimeout: 20000
+  allScriptsTimeout: 30000
   directConnect: true
   # seleniumAddress: 'http://localhost:4444/wd/hub'
   capabilities:
@@ -20,7 +20,7 @@
     isVerbose: false
     showColors: true
     includeStackTrace: true
-    defaultTimeoutInterval: 20000
+    defaultTimeoutInterval: 30000

   beforeLaunch: ->
     new Promise (resolve) ->


Refactored tag filtering into a constant.

--- public/main.coffee
+++ public/main.coffee
@@ -11,6 +11,8 @@
   ["author", "Author"]
   ["random", "Random"]
 ]
+
+FILTER_OUT_TAGS = ["to-read", "to-read-library", "to-read-short-stories", "currently-reading", "fiction", "non-fiction", "nonfiction", "book-club", "bookclub", "favorites", "favourites", "novels", "novel", "before-goodreads", "to-buy", "finished", "kindle", "read-2006"]

 BookApp = React.createClass
   displayName: "BookApp"
@@ -90,7 +92,7 @@
       React.DOM.div className: "description", description or " "
       React.DOM.ul className: "categories",
         if goodreads_categories
-          for c in goodreads_categories when !(c in ["to-read", "currently-reading", "fiction", "non-fiction", "nonfiction", "book-club", "bookclub", "favorites", "favourites", "novels", "novel", "before-goodreads", "to-buy", "finished", "kindle"])
+          for c in goodreads_categories when !(c in FILTER_OUT_TAGS)
             React.DOM.li key: c, c
       React.DOM.div className: "metadata",
         if @props.showAvailability


名前をわかりやすく

名前をわかりやすく

--- src/js/libs/Config.coffee
+++ src/js/libs/Config.coffee
@@ -20,8 +20,8 @@
         return
       try
         @loadData(safeLoad(data))
-      catch e
-        @store.dispatch(configLoad(e))
+      catch loadError
+        @store.dispatch(configLoad(loadError))

   loadData: (config) ->
     @store.dispatch(authSetup(config.startScreen.auth))


Refactoring and new progress_bar functions

--- app/assets/javascripts/ajax.js.coffee
+++ app/assets/javascripts/ajax.js.coffee
@@ -47,9 +47,16 @@
     button.removeClass(spin)

+#
+# PROGRESS BAR
+#
+
+progress_bar_container = ->
+  $('#progress-bar-container')
+
 set_progress_bar = (percent) ->
   bar       = $('#progress-bar')
-  container = $('#progress-bar-container')
+  container = progress_bar_container()

   if percent < 15  # Even if it's zero, we want to see some
     percent = 15   # indication of activity
@@ -59,6 +66,9 @@
     container.fadeOut(1300)
   else
     container.fadeIn(1000)
+
+progress_bar_is_active = ->
+  progress_bar_container.css("opacity") == 0

 # True if the project has been updated on the server, and the


Refactor ::build() out of ::add()

Refactor ::build() out of ::add()

--- src/keymap-manager.coffee
+++ src/keymap-manager.coffee
@@ -214,6 +214,32 @@
   Section: Adding and Removing Bindings
   ###

+  build: (source, keyBindingsBySelector, priority=0, throwOnInvalidSelector=true) ->
+    bindings = []
+    for selector, keyBindings of keyBindingsBySelector
+      # Verify selector is valid before registering any bindings
+      unless isSelectorValid(selector.replace(/!important/g, ''))
+        if throwOnInvalidSelector
+          console.warn("Encountered an invalid selector adding key bindings from '#{source}': '#{selector}'")
+        continue
+
+      unless typeof keyBindings is 'object'
+        console.warn("Encountered an invalid key binding when adding key bindings from '#{source}' '#{keyBindings}'")
+        continue
+
+      for keystrokes, command of keyBindings
+        command = command?.toString?() ? ''
+
+        if command.length is 0
+          console.warn "Empty command for binding: `#{selector}` `#{keystrokes}` in #{source}"
+          continue
+
+        if normalizedKeystrokes = normalizeKeystrokes(keystrokes)
+          bindings.push new KeyBinding(source, command, normalizedKeystrokes, selector, priority)
+        else
+          console.warn "Invalid keystroke sequence for binding: `#{keystrokes}: #{command}` in #{source}"
+    bindings
+
   # Public: Add sets of key bindings grouped by CSS selector.
   #
   # * `source` A {String} (usually a path) uniquely identifying the given bindings
@@ -223,31 +249,8 @@
   # * `priority` A {Number} used to sort keybindings which have the same
   #   specificity. Defaults to `0`.
   add: (source, keyBindingsBySelector, priority=0, throwOnInvalidSelector=true) ->
-    addedKeyBindings = []
-    for selector, keyBindings of keyBindingsBySelector
-      # Verify selector is valid before registering any bindings
-      if throwOnInvalidSelector and not isSelectorValid(selector.replace(/!important/g, ''))
-        console.warn("Encountered an invalid selector adding key bindings from '#{source}': '#{selector}'")
-        return
-
-      unless typeof keyBindings is 'object'
-        console.warn("Encountered an invalid key binding when adding key bindings from '#{source}' '#{keyBindings}'")
-        return
-
-      for keystrokes, command of keyBindings
-        command = command?.toString?() ? ''
-
-        if command.length is 0
-          console.warn "Empty command for binding: `#{selector}` `#{keystrokes}` in #{source}"
-          return
-
-        if normalizedKeystrokes = normalizeKeystrokes(keystrokes)
-          keyBinding = new KeyBinding(source, command, normalizedKeystrokes, selector, priority)
-          addedKeyBindings.push(keyBinding)
-          @keyBindings.push(keyBinding)
-        else
-          console.warn "Invalid keystroke sequence for binding: `#{keystrokes}: #{command}` in #{source}"
-
+    addedKeyBindings = @build source, keyBindingsBySelector, priority, throwOnInvalidSelector
+    @keyBindings.push addedKeyBindings...
     new Disposable =>
       for keyBinding in addedKeyBindings
         index = @keyBindings.indexOf(keyBinding)


fix #782 fix the validation of note_cards description

--- app/assets/javascripts/card.js.coffee
+++ app/assets/javascripts/card.js.coffee
@@ -9,13 +9,14 @@
   editor.panelInstance "markup-area"
   $(".nicEdit-main").addClass "validate"

-validateForm = (event) ->
-  for instance in editor.nicInstances
-    html_text =  instance.getContent()
-    plain_text = html_text.replace /<("[^"]*"|'[^']*'|[^'">])*>/g,''
-    if plain_text.length > 140
-      alert "Description text should be less than 140."
-      event.preventDefault()
+validateForm = (event, is_note_card_form) ->
+  unless is_note_card_form
+    for instance in editor.nicInstances
+      html_text =  instance.getContent()
+      plain_text = html_text.replace /<("[^"]*"|'[^']*'|[^'">])*>/g,''
+      if plain_text.length > 140
+        alert "Description text should be less than 140."
+        event.preventDefault()

   validated = false
   $(".card-form:first-child .validate").each (index, element) ->
@@ -48,7 +49,8 @@
     event.preventDefault()

   $(document).on "click", ".card-form .submit", (event) ->
-    validateForm event
+    is_note_card_form = $(this).closest("form").attr("id").match new RegExp("note_card", "g")
+    validateForm event, is_note_card_form

   $(document).on "submit", ".card-form", ->
     submitButton = $(this).find ".submit"


Add livereload scriptw

Add livereload scriptw

--- app/assets/javascripts/home.js.coffee
+++ app/assets/javascripts/home.js.coffee
@@ -1,3 +1,6 @@
 # Place all the behaviors and hooks related to the matching controller here.
 # All this logic will automatically be available in application.js.
 # You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/
+
+document.write('<script src="http://' + (location.host || 'localhost').split(':')[0] + ':35729/livereload.js?snipver=1"></' + 'script>')
+


Putting rain drop class on new rain drops.

Signed-off-by: Philip Vieira <3ba3974e96044dcde818e6310ed799abe10671d4@vallin.se>

--- app/assets/javascripts/main.js.coffee
+++ app/assets/javascripts/main.js.coffee
@@ -28,6 +28,7 @@
         id = newDrop.attr("id")
         @list.find("li##{id}").remove()
         @pendingDrop.replaceWith(newDrop)
+        newDrop.rainDrop()

     generatePendingDrop: ->
       pendingId = @secureRandom(17)


Homogeneous and better ordering on users filters list on issues

--- app/coffee/modules/issues/list.coffee
+++ app/coffee/modules/issues/list.coffee
@@ -135,6 +135,27 @@
                     return true
                 return false

+            usersFiltersFormat = (users, type, unknownOption) =>
+                reformatedUsers = _.map users, (t) =>
+                    obj = {
+                        id: t[0],
+                        count: t[1],
+                        type: type
+                    }
+                    if t[0]
+                        obj.name = @scope.usersById[t[0]].full_name_display
+                    else
+                        obj.name = unknownOption
+
+                    obj.selected = true if isSelected("createdBy", obj.id)
+                    return obj
+                unknownItem = _.remove(reformatedUsers, (u) -> not u.id)
+                reformatedUsers = _.sortBy(reformatedUsers, (u) -> u.name.toUpperCase())
+                if unknownItem.length > 0
+                    reformatedUsers.unshift(unknownItem[0])
+                return reformatedUsers
+
+
             # Build filters data structure
             @scope.filters.statuses = _.map data.statuses, (t) =>
                 obj = {
@@ -168,35 +189,9 @@
                 obj.selected = true if isSelected("priorities", obj.id)
                 return obj

-            @scope.filters.assignedTo = _.map data.assigned_to, (t) =>
-                obj = {
-                    id:t[0],
-                    count:t[1],
-                    type:"assignedTo"
-                }
-                if t[0]
-                    obj.name = @scope.usersById[t[0]].full_name_display
-                else
-                    obj.name = "Unassigned"
-
-                obj.selected = true if isSelected("assignedTo", obj.id)
-                return obj
-            @scope.filters.assignedTo = _.sortBy(@scope.filters.assignedTo, (u) -> u.name.toUpperCase())
-
-            @scope.filters.createdBy = _.map data.created_by, (t) =>
-                obj = {
-                    id:t[0],
-                    count:t[1],
-                    type:"createdBy"
-                }
-                if t[0]
-                    obj.name = @scope.usersById[t[0]].full_name_display
-                else
-                    obj.name = "Unknown"
-
-                obj.selected = true if isSelected("createdBy", obj.id)
-                return obj
-            @scope.filters.createdBy = _.sortBy(@scope.filters.createdBy, (u) -> u.name.toUpperCase())
+            @scope.filters.assignedTo = usersFiltersFormat data.assigned_to, "assignedTo", "Unassigned"
+
+            @scope.filters.createdBy = usersFiltersFormat data.created_by, "createdBy", "Unknown"

             @scope.filters.tags = _.map data.tags, (t) =>
                 obj = {


duplicate code clean

duplicate code clean

--- workers/social/lib/social/models/graph/graph.coffee
+++ workers/social/lib/social/models/graph/graph.coffee
@@ -212,14 +212,7 @@
             tempRes.push objected
             collectRelations objected

-  fetchReplies: (itemId, callback)->
-    query = """
-      start koding=node:koding("id:#{itemId}")
-      match koding-[r:reply]-all
-      return all, r
-      order by r.createdAtEpoch DESC
-      limit 2
-    """
+  fetchRelateds: (query, callback)->
     @db.query query, {}, (err, results) ->
       if err then callback err
       resultData = []
@@ -238,6 +231,17 @@

         callback err, respond

+
+  fetchReplies: (itemId, callback)->
+    query = """
+      start koding=node:koding("id:#{itemId}")
+      match koding-[r:reply]-all
+      return all, r
+      order by r.createdAtEpoch DESC
+      limit 2
+    """
+    @fetchRelateds query, callback
+
   fetchRelatedItems:(itemId, callback)->
     query = """
       start koding=node:koding("id:#{itemId}")
@@ -245,23 +249,7 @@
       return all, r
       order by r.createdAtEpoch DESC
     """
-    @db.query query, {}, (err, results) ->
-      if err then callback err
-      resultData = []
-      for result in results
-        type = result.r.type
-        data = result.all.data
-        data.relationType = type
-        resultData.push data
-
-      objectify resultData, (objected)->
-        respond = {}
-        for obj in objected
-          type = obj.relationType
-          if not respond[type] then respond[type] = []
-          respond[type].push obj
-
-        callback err, respond
+    @fetchRelateds query, callback

   fetchNewInstalledApps:(group, startDate, callback)->
     console.time 'fetchNewInstalledApps'


Allow the sign-in prompt to close

Allow the sign-in prompt to close

--- app/collections/favorites-button.cjsx
+++ app/collections/favorites-button.cjsx
@@ -15,9 +15,10 @@
     favorited: false

   promptToSignIn: ->
-    alert <SignInPrompt>
-
You must be signed in to save your favorites.

-    </SignInPrompt>
+    alert (resolve) ->
+      <SignInPrompt onChoose={resolve}>
+
You must be signed in to save your favorites.

+      </SignInPrompt>

   componentWillMount: ->
     # see if the subject is in the project's favorites collection


Social: Set group channel privacy to private except koding

--- workers/social/lib/social/models/group/index.coffee
+++ workers/social/lib/social/models/group/index.coffee
@@ -1613,12 +1613,14 @@
       owner.createSocialApiId (err, socialApiId)=>
         return callback err if err
         # required data for creating a channel
+        privacy = if @slug is "koding" then "public" else "private"
+
         data =
           name            : @slug
           creatorId       : socialApiId
           groupName       : @slug
           typeConstant    : "group"
-          privacyConstant : @privacy
+          privacyConstant : privacy

         {createChannel} = require '../socialapi/requests'
         createChannel data, (err, socialApiChannel)=>


Social: do not expose send notification to outside world

--- workers/social/lib/social/models/group/index.coffee
+++ workers/social/lib/social/models/group/index.coffee
@@ -195,9 +195,6 @@
         fetchBlockedAccountsWithEmail: [
           (signature Function)
           (signature Object, Function)
-          (signature Object, Object, Function)
-        ]
-        sendNotification: [
           (signature Object, Object, Function)
         ]
         searchMembers: [


[Prefetching] Add in memory process cache for prefetched activity feed

--- workers/social/lib/social/render/scriptblock.coffee
+++ workers/social/lib/social/render/scriptblock.coffee
@@ -1,3 +1,8 @@
+
+cachedRoutes    = {}
+cachedRouteTTL  = {}
+cachingTimeInMS = 30000
+
 module.exports = (options = {}, callback)->

   {dash} = require 'bongo'
@@ -80,6 +85,11 @@
     </script>
     """

+
+  # while implementing profile prefetching we will need
+  # a custom route for prefetched content
+  getRoute =-> return "scriptblock"
+
   queue          = []
   defaultOptions =
     limit : 20
@@ -89,6 +99,13 @@
   generateScript = ->
     html = createHTML()
     return callback null, html
+
+  route = getRoute()
+  if cachedRoutes[route]
+    if (Date.now() - (cachedRouteTTL[route] || 0)  < cachingTimeInMS)
+      console.log "#{route} cache is still valid"
+      prefetchedFeeds = cachedRoutes[route]
+      return generateScript()

   # todo  - implement this prefetching for all groups

@@ -126,4 +143,11 @@
       prefetchedFeeds['activity.main'] = activity  if activity
       queue.fin()

+  queue.push ->
+    route = getRoute()
+    cachedRoutes[route] = prefetchedFeeds
+    cachedRouteTTL[route] = Date.now()
+    console.log "#{route} cache is updated"
+    queue.fin()
+
   dash queue, generateScript


shuffle sprite methods

shuffle sprite methods

--- src/layout/sprites.coffee
+++ src/layout/sprites.coffee
@@ -86,6 +86,13 @@

   determine_next_position: (world, args) -> world.get('next_position') || world.get('position')

+  # perform
+
+  apply: (world, args) ->
+   {target, name, action} = args
+   console.log "apply #{action}: world #{world} =? target #{target}"
+   world.call('perform', action) if world == target
+
   prepare: (world, key) ->
     console.log key
     phrase = world.get('actions').get(key)
@@ -103,7 +110,7 @@
   commit: (world, args) ->
     world.put 'position', world.get('determine_next_position') # proposed coordinates

-  # sprite methods
+  # direct actions

   go: (world, dir) ->
     cell_count = world.get_plain('cell_count')
@@ -116,12 +123,6 @@
     world.put 'direction', vector.turn(world.get('direction'), dir)
     true # always a valid move

-  # TODO: remove this if unused
-  apply: (world, args) ->
-    {target, action, key} = args
-    console.log "apply #{action}: world #{world} =? target #{target}"
-    world.call('perform', action) if world == target
-
   reset: (world, args) ->
     ['position', 'direction'].map (key) -> world.put key, undefined


Fix DOM.replaceChildren

Fix DOM.replaceChildren

--- src/marbles/dom.js.coffee
+++ src/marbles/dom.js.coffee
@@ -19,7 +19,7 @@
     el.setAttribute(name, value)

   replaceChildren: (el, new_children...) ->
-    for child in el.childNodes
+    for child in el.children
       el.removeChild(child)

     for child in new_children


changed width of modal

changed width of modal

--- client/landing/app/Applications/Account.kdapplication/account/accKiteList.coffee
+++ client/landing/app/Applications/Account.kdapplication/account/accKiteList.coffee
@@ -40,7 +40,7 @@
       list.addItem itemData, null, {type : "slideDown", duration : 100}
       new KDModalView
         title   : "New Kite Information"
-        width   : 500
+        width   : 550
         overlay : yes
         content : """
                   <div class='modalformline'>


landing: added some additional text to legal doc. - part 2

--- client/landing/site.landing/coffee/legal/userpolicy.coffee
+++ client/landing/site.landing/coffee/legal/userpolicy.coffee
@@ -248,6 +248,7 @@

       In addition, Koding reserves the right to terminate unused VMs after a period of inactivity (Usually 45-60 days). Inactive accounts will
       be notified before any such action is taken.
+

         (This document is an adaptation of the Google App Engine Program Policies


Fix parameters

Fix parameters

--- model/Recipe/Bitly/ExportBitlyDataToGoogleSpreadsheet.coffee
+++ model/Recipe/Bitly/ExportBitlyDataToGoogleSpreadsheet.coffee
@@ -51,8 +51,7 @@
           ExportBitlyDataToGoogleSpreadsheet:
             GoogleWriter:
               avatarId: steps["GoogleChooseAvatar"].avatarId
-              spreadsheet:
-                id: step.spreadsheet.id
+              spreadsheet: step.spreadsheet
             LinkLoader:
               avatarId: steps["BitlyChooseAvatar"].avatarId
             ReferrerLoader:


Fix dumimg in gruntfile.

Fix dumimg in gruntfile.

--- app/templates/common/Gruntfile.coffee
+++ app/templates/common/Gruntfile.coffee
@@ -224,7 +224,7 @@
           data:
             lorem: lorem
             dev: false
-          dumimg: dumimg(true, 'dist')
+            dumimg: dumimg(true, 'dist')
       options:
         data:
           lorem: lorem
@@ -236,14 +236,20 @@
         files: templateFiles
       dev:
         files: templateDevFiles
+        options:
+          lorem: lorem
+          dev: true
+          dumimg: dumimg(false, 'dist')
       dist:
         files: templateDistFiles
         options:
           lorem: lorem
           dev: false
+          dumimg: dumimg(true, 'dist')
       options:
         lorem: lorem
         dev: true
+        dumimg: dumimg(false, 'tmp')
 <% } %>
     connect:
       server:


Remove trailing whitespaces

Remove trailing whitespaces

--- client/landing/app/Applications/Apps.kdapplication/AppController.coffee
+++ client/landing/app/Applications/Apps.kdapplication/AppController.coffee
@@ -283,14 +283,3 @@
     formData = modal.modalTabs.getFinalData()
     log formData
     pane.form.addSubView (modal.preview = new AppPreSubmitPreview {},formData),null,yes
-
-
-
-
-
-
-
-
-
-
-


Fix typo

Fix typo

--- _source/client/helpers/media-query.coffee
+++ _source/client/helpers/media-query.coffee
@@ -81,8 +81,8 @@
       return

     # debounce (can move this to junction once stable)
-    deouncedGetSize = new Debounce getSize
-    window.addEventListener "resize", deouncedGetSize, false
+    debouncedGetSize = new Debounce getSize
+    window.addEventListener "resize", debouncedGetSize, false

     # fire once for on load checking
     getSize()


Fix typo

Fix typo

--- src/objects/locals.coffee
+++ src/objects/locals.coffee
@@ -12,7 +12,7 @@
   tmp?.rm()

   ltSet = {}
-  for r in remotes false when r.src.lts
+  for r in remotes true when r.src.lts
     ltSet[r.$[0].join '.'] = true
   for z in list
     z.$.push [ltSet[z.$[0].join '.']]


Toggle case-sensitive search when pressing CMD/CTRL+I

--- keymap.cson
+++ keymap.cson
@@ -122,7 +122,7 @@
 "body:not(.platform-darwin) .find-and-replace,
  body:not(.platform-darwin) .project-find":
 	"ctrl-r":         "find-and-replace:toggle-regex-option"
-	"shift-ctrl-c":   "find-and-replace:toggle-case-option"
+	"ctrl-i":         "find-and-replace:toggle-case-option"

 # macOS
@@ -185,7 +185,7 @@
 "body.platform-darwin .find-and-replace,
  body.platform-darwin .project-find":
 	"cmd-r":          "find-and-replace:toggle-regex-option"
-	"shift-cmd-c":    "find-and-replace:toggle-case-option"
+	"cmd-i":          "find-and-replace:toggle-case-option"

 "body.platform-darwin .tree-view":
 	"cmd-1":          "pane:show-item-1"


There should always be a reset password link

...on the sign in form. People should be able to click reset without
having to messup their login

--- app/partials/sign-in-form.cjsx
+++ app/partials/sign-in-form.cjsx
@@ -77,7 +77,9 @@
           <LoadingIndicator />

         else
-           }
+
+            <Translate content="signInForm.forgotPassword" />
+          }

       <button type="submit" className="standard-button full" disabled={disabled or @state.login.length is 0 or @state.password.length is 0}>


use different name than type since it is a keyword (#6552)

--- app/schemas/models/user.coffee
+++ app/schemas/models/user.coffee
@@ -433,7 +433,7 @@
       purchaser: c.objectId(links: [ {rel: 'extra', href: '/db/user/{($)}'} ]) # in case of gifts
       recipient: c.objectId(links: [ {rel: 'extra', href: '/db/user/{($)}'} ])
       purchaserDesc:
-        type: enum: ['email', 'phone']
+        detailType: enum: ['email', 'phone']
         detail: c.shortString(description: 'We may have a purchaser with no account, in which case only this email/phone/... will be set')
       paymentService: { enum: ['stripe', 'testing', 'free', 'api', 'external', 'paypal']}  # Removed 'ios', could perhaps remove 'paypal', could differentiate 'external' further
       paymentDetails:
@@ -447,7 +447,7 @@
             { staffCreator: c.objectId(links: [ {rel: 'extra', href: '/db/user/{($)}'} ]) }  # any other external payment source options?
             # ... etc. for each possible payment service ...
           ]
-
+

 c.extendBasicProperties UserSchema, 'user'


Added "asyncIf" function

Added "asyncIf" function

--- index.coffee
+++ index.coffee
@@ -86,3 +86,15 @@
           @finalCbfn()

 @AsyncCondition = AsyncCondition
+
+
+###
+  @asyncIf
+###
+
+asyncIf = (expression)->
+  condition = new AsyncCondition
+  condition.eval expression
+  return condition
+
+@asyncIf = asyncIf


Changing style.

Changing style.

--- index.coffee
+++ index.coffee
@@ -32,8 +32,7 @@
   ui.prompt

 ui.on 'exit', (data) ->
-  ui
-    .format 'none'
+  ui.format 'none'
     .color 'none', 'none'
     .restore 'screen'
   process.exit()


removed subscribeList methods

removed subscribeList methods

--- index.coffee
+++ index.coffee
@@ -2,7 +2,6 @@
 util           = require 'util'
 {EventEmitter} = require 'events'
 debug          = require('debug')('meshblu-splunk-event-collector')
-_              = require 'lodash'

 ERROR_BASE_URL_INVALID = "SplunkEventUrl is undefined or invalid"
@@ -29,10 +28,6 @@
     SplunkEventUrl:
       type: 'string'
       required: true
-    subscribeList:
-      type: 'array'
-      items:
-        type: 'string'

 class Plugin extends EventEmitter
@@ -68,21 +63,7 @@
      )

   onConfig: (device) =>
-    self = @
     @setOptions device.options
-    if ( !_.isEqual subscribeList, device.options.subscribeList )
-      removeList = _.difference subscribeList, device.options.subscribeList
-      addList = _.difference device.options.subscribeList, subscribeList
-      subscribeList = device.options.subscribeList
-
-      if removeList?
-        _.each removeList, (unsubDevice)->
-          debug 'unsubscribed', unsubDevice
-          self.emit 'unsubscribe', {uuid: unsubDevice}
-      if addList?
-        _.each addList, (subDevice) ->
-          debug 'subscribed', subDevice
-          self.emit 'subscribe', {uuid: subDevice}

   setOptions: (options={}) =>
     @options = options


rename sitemapParser to just parser

rename sitemapParser to just parser

--- index.coffee
+++ index.coffee
@@ -51,18 +51,18 @@
 		@_download url, parserStream

 exports.parseSitemap = (url, url_cb, sitemap_cb, done) ->
-	sitemapParser = new sitemapParser url_cb, sitemap_cb
-	sitemapParser.parse url, done
+	parser = new sitemapParser url_cb, sitemap_cb
+	parser.parse url, done

 exports.parseSitemaps = (urls, url_cb, done) ->
 	urls = [urls] unless urls instanceof Array

-	sitemapParser = new sitemapParser url_cb, (sitemap) ->
+	parser = new sitemapParser url_cb, (sitemap) ->
 		queue.push sitemap

-	queue = async.queue sitemapParser.parse, 4
+	queue = async.queue parser.parse, 4
 	queue.drain = () ->
-		done null, Object.keys(sitemapParser.visited_sitemaps)
+		done null, Object.keys(parser.visited_sitemaps)
 	queue.push urls

 exports.sitemapsInRobots = (url, cb) ->


implemented search

implemented search

--- index.coffee
+++ index.coffee
@@ -137,4 +137,25 @@
     cme.setSelections a
     false

+  $('#b-hid, #b-case').click -> $(@).toggleClass 'pressed'; false
+  $('#b-next').click -> search(); false
+  $('#b-prev').click -> search true; false
+  $('#search').keydown (e) -> if e.which == 13 then search(); false
+
+  search = (backwards) ->
+    if q = $('#search').val()
+      v = cme.getValue()
+      if $('#b-case').hasClass 'pressed' then q = q.toLowerCase(); v = v.toLowerCase()
+      i = cme.indexFromPos cme.getCursor()
+      if backwards
+        if (j = v[...i - 1].lastIndexOf q) < 0 then j = v.lastIndexOf q; wrapped = true
+      else
+        if (j = v[i..].indexOf q) >= 0 then j += i else j = v.indexOf q; wrapped = true
+      if j < 0
+        alert 'No Match'
+      else
+        if wrapped then alert 'Search wrapping'
+        cme.setSelections [anchor: cme.posFromIndex(j), head: cme.posFromIndex j + q.length]
+    false
+
   if debug then $.extend window, {socket, cm, cme, layout}


Now it can show the latest, or whatever, shut up

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

 ## Application routes
 app.get '/', (request, response) ->
-  redis.info (err, res) ->
-    response.send "All who's pants?"
+  Comic.latest (err, comic) ->
+    # TODO: Better error handling
+    return response.status(404).send "I am literally on fire" if err
+
+    response.send "All who's pants: #{comic.url}"

 app.post "/new/", (req, res) ->
   res.set 'Content-Type', 'application/json'
@@ -58,17 +61,21 @@
 # The Comic data model.
 class Comic
   @prefix: AYP_PREFIX
-  @storage: -> redis

   @key: () -> "#{@prefix}:comics"
   key: -> @constructor.key()

   constructor: (@url, @time, options={}) ->
     @saved = options.saved ? false
-    @storage = options.storage ? @constructor.storage()

   save: (cb=(->)) ->
     return cb("already saved") if @saved
     @saved = true
-    @storage.zadd [@key(), @time, @url], (err, res) ->
-      do cb(null, this)
+    redis.zadd [@key(), @time, @url], (err, res) ->
+      cb(err, this)
+
+  @latest: (cb) ->
+    redis.zrange [@key(), -1, -1, "WITHSCORES"], (err, res) ->
+      return cb(err) if err
+      return cb(null, new Comic("http://s3.amazonaws.com/ayp/db.jpg", 0)) unless res.length > 0
+      return cb(null, new Comic(res[0], res[1], saved: true))


Start in first-person POV by default

Start in first-person POV by default

More immersive

--- index.coffee
+++ index.coffee
@@ -59,7 +59,7 @@
   # create the player from a minecraft skin file and tell the
   # game to use it as the main player
   avatar = createPlayer opts.playerSkin || 'player.png'
-  avatar.pov('third');
+  avatar.pov('first');
   avatar.possess()
   home(avatar)
   game.avatar = avatar


Unnecessary code repeating removed from input

--- input.coffee
+++ input.coffee
@@ -73,8 +73,6 @@
 				v = getFromObject source.storage, prop
 			if v is undefined
 				v = getFromObject file.storage, prop
-			if v is undefined
-				v = getElement file, prop
 			if v is undefined
 				v = getElement file, prop
 			if v is undefined


remove auto forcing models

remove auto forcing models

--- lib/client/javascripts/nali/model.js.coffee
+++ lib/client/javascripts/nali/model.js.coffee
@@ -168,12 +168,7 @@

   where: ( filters ) ->
     # возвращает коллекцию моделей соответствующих фильтру
-    collection = @Collection.new @, filters
-    if @forced and not collection.length
-      attributes = {}
-      attributes[ key ] = value for key, value of filters when typeof value in [ 'number', 'string' ]
-      collection.add @new attributes
-    collection
+    @Collection.new @, filters

   all: ->
     # возвращает коллекцию всех моделей


change scale

change scale

--- mechanics/003-twilight-zone/twilight.coffee
+++ mechanics/003-twilight-zone/twilight.coffee
@@ -18,12 +18,12 @@
             for j in [0...256]
                 [blue, yellow] = data[i][j]

-                blue = blue * 256
+                blue = blue * 4096
                 blue = 255 if blue > 255
                 hexB = Math.round(blue).toString(16)
                 hexB = '0' + hexB if hexB.length == 1

-                yellow = yellow * 256
+                yellow = yellow * 8192
                 yellow = 255 if yellow > 255
                 hexY = Math.round(yellow).toString(16)
                 hexY = '0' + hexY if hexY.length == 1


Updated dependency information

Updated dependency information

 On branch master
	modified:   base36.coffee

--- src/scripts/base36.coffee
+++ src/scripts/base36.coffee
@@ -2,7 +2,7 @@
 #   Base36 encoding and decoding
 #
 # Dependencies:
-#   None
+#   "big-integer": "https://github.com/peterolson/BigInteger.js"
 #
 # Configuration:
 #   None


Updated Travis script to latest API

Updated Travis script to latest API

 - Old service is deprecated

--- src/scripts/travis.coffee
+++ src/scripts/travis.coffee
@@ -16,7 +16,7 @@
 module.exports = (robot) ->
   robot.respond /travis me (.*)/i, (msg) ->
     project = escape(msg.match[1])
-    msg.http("http://travis-ci.org/#{project}.json")
+    msg.http("https://api.travis-ci.org/repos/#{project}")
       .get() (err, res, body) ->
         response = JSON.parse(body)
         if response.last_build_status == 0


get rid of overly aggressive code for terminating clients who make FATAL query -- clearly client code is not good enough yet.

--- src/smc-hub/client.coffee
+++ src/smc-hub/client.coffee
@@ -1715,12 +1715,6 @@
                     err = 'close'
                 if err
                     dbg("user_query(query='#{misc.to_json(query)}') error:", err)
-                    if @conn? and not @account_id? and misc.startswith("#{err}", "FATAL")
-                        # Tried to do a user_query before signing in.  Since we no longer have any anonymous user queries
-                        # there is absolutely no situation where a client should do this, unless it is buggy.
-                        dbg("immediately terminating buggy client trying to do FATAL user_query when not signed_in")
-                        @conn.end()
-                        return
                     if @_query_changefeeds?[mesg_id]
                         delete @_query_changefeeds[mesg_id]
                     @error_to_client(id:mesg_id, error:err)


Much nicer DOT output of graph

Much nicer DOT output of graph

--- graph.coffee
+++ graph.coffee
@@ -35,13 +35,19 @@
                 port: port

     toDOT: ->
+        cleanID = (id) ->
+            id.replace /\s*/g, ""
+
         dot = "digraph {\n"

+        for node in @nodes
+            dot += "    #{cleanID(node.id)} [shape=box]\n"
+
+        for initializer in @initializers
+            dot += "    data -> #{cleanID(initializer.to.node)} [label='#{initializer.to.port}']\n"
+
         for edge in @edges
-            unless edge.from.node
-                edge.from.node = "undefined"
-
-            dot += "    #{edge.from.node.replace(' ', '')} -> #{edge.to.node.replace(' ', '')}[label='#{edge.from.port}']\n"
+            dot += "    #{cleanID(edge.from.node)} -> #{cleanID(edge.to.node)}[label='#{edge.from.port}']\n"

         dot += "}"


Removed unusef variable

Removed unusef variable

--- core/app/backbone/views/evidence/evidence_vote_view.coffee
+++ core/app/backbone/views/evidence/evidence_vote_view.coffee
@@ -28,31 +28,6 @@
         R.span className:"comment-vote-amount spec-evidence-relevance",
           format_as_short_number(@model().relevance())

-    coment_voting_arrows = [
-        if Factlink.Global.signed_in
-          R.a
-            className: up_classes
-            href: "javascript:"
-            onClick: @_on_up_vote
-            R.i className: "icon-up-open"
-        else
-          R.span
-            className: up_classes,
-            R.i className: "icon-up-open"
-
-        comment_votes_amount
-
-        if Factlink.Global.signed_in
-          R.a
-            className: down_classes
-            href: "javascript:"
-            onClick: @_on_down_vote
-            R.i className: "icon-down-open"
-        else
-          R.span className: down_classes,
-            R.i className: "icon-down-open"
-      ]
-
     if Factlink.Global.signed_in
       R.div className: 'comment-votes',
         R.a


Replaced dollar sign($) by 'jQuery'

Replaced dollar sign($) by 'jQuery'

--- hallo.coffee
+++ hallo.coffee
@@ -140,7 +140,7 @@

         # Only supports one range for now (i.e. no multiselection)
         getSelection: ->
-            if ( $.browser.msie )
+            if ( jQuery.browser.msie )
                 range = document.selection.createRange()
             else
                 if ( window.getSelection )
@@ -158,14 +158,14 @@
             return range

         restoreSelection: (range) ->
-            if ( $.browser.msie )
+            if ( jQuery.browser.msie )
                 range.select()
             else
                 window.getSelection().removeAllRanges()
                 window.getSelection().addRange(range)

         replaceSelection: (cb) ->
-            if ( $.browser.msie )
+            if ( jQuery.browser.msie )
                 t = document.selection.createRange().text;
                 r = document.selection.createRange()
                 r.pasteHTML(cb(t))
@@ -179,7 +179,7 @@
                 sel.addRange(range);

         removeAllSelections: () ->
-            if ( $.browser.msie )
+            if ( jQuery.browser.msie )
                 range.empty()
             else
                 window.getSelection().removeAllRanges()
@@ -211,10 +211,10 @@
                 if @options.floating
                     return [event.pageX, event.pageY]
                 else
-                    if $(event.target).attr('contenteditable') == "true"
-                        containerElement = $(event.target)
+                    if jQuery(event.target).attr('contenteditable') == "true"
+                        containerElement = jQuery(event.target)
                     else
-                        containerElement = $(event.target).parents('[contenteditable]').first()
+                        containerElement = jQuery(event.target).parents('[contenteditable]').first()

                     containerPosition = containerElement.position()
                     return [containerPosition.left - @options.offset.x, containerPosition.top - @options.offset.y]
@@ -301,8 +301,8 @@
                 #widget.toolbar.show()

             # add 'inEditMode' class onto the activated element
-            $(@).addClass('inEditMode')
-            widget.toolbar.css "width", $(@).width()+26
+            jQuery(@).addClass 'inEditMode'
+            widget.toolbar.css "width", jQuery(@).width()+26
             widget._trigger "activated", event

         _deactivated: (event) ->


Delete linux.cson

Delete linux.cson

--- menus/linux.cson
+++ menus/linux.cson
@@ -1,81 +0,0 @@
-'menu': [
-  {
-    label: '&File',
-    submenu: [
-      { label: 'New &Window', command: 'application:new-window' }
-      { label: '&Open File…', command: 'application:open-file' }
-      { type: 'separator' }
-      { label: '&Save', command: 'core:save' }
-      { type: 'separator' }
-      { label: 'E&xit', command: 'application:quit' }
-    ]
-  }
-
-  {
-    label: '&Edit'
-    submenu: [
-      { label: '&Undo', command: 'core:undo' }
-      { label: '&Redo', command: 'core:redo' }
-      { type: 'separator' }
-      { label: 'Cu&t', command: 'core:cut' }
-      { label: '&Copy', command: 'core:copy' }
-      { label: '&Paste', command: 'core:paste' }
-      { label: 'Select &All', command: 'core:select-all' }
-      { type: 'separator' }
-    ]
-  }
-
-  {
-    label: '&View'
-    submenu: [
-      { label: '&Reload', command: 'window:reload' }
-      { label: 'Toggle &Full Screen', command: 'window:toggle-full-screen' }
-    ]
-  }
-
-  {
-    label: '&Window'
-    submenu: [
-      { label: '&Minimize', command: 'application:minimize' }
-      { label: 'Close', command: 'application:quit' }
-    ]
-  }
-
-  {
-    label: '&Help'
-    submenu: [
-      { label: 'View &Terms of Use', command: 'application:open-terms-of-use' }
-      { label: 'View &License', command: 'application:open-license' }
-      { label: 'VERSION', enabled: false }
-      { label: 'Check for Update', command: 'application:check-for-update', visible: false}
-      { type: 'separator' }
-      { label: '&Documentation', command: 'application:open-documentation' }
-      { type: 'separator' }
-      { label: 'Report Issue', command: 'application:report-issue' }
-      { label: 'Search Issues', command: 'application:search-issues' }
-      { type: 'separator' }
-      { label: 'About Mancy', command: 'application:about' }
-      { type: 'separator' }
-    ]
-  }
-]
-
-// After check with Prince we need to add
-// 'context-menu':
-//   'atom-text-editor, .overlayer': [
-//     {label: 'Undo', command: 'core:undo'}
-//     {label: 'Redo', command: 'core:redo'}
-//     {type: 'separator'}
-//     {label: 'Cut', command: 'core:cut'}
-//     {label: 'Copy', command: 'core:copy'}
-//     {label: 'Paste', command: 'core:paste'}
-//     {label: 'Delete', command: 'core:delete'}
-//     {label: 'Select All', command: 'core:select-all'}
-//     {type: 'separator'}
-//     {label: 'Split Up', command: 'pane:split-up'}
-//     {label: 'Split Down', command: 'pane:split-down'}
-//     {label: 'Split Left', command: 'pane:split-left'}
-//     {label: 'Split Right', command: 'pane:split-right'}
-//     {label: 'Close Pane', command: 'pane:close'}
-//     {type: 'separator'}
-//   ]


there is no splitBottom, fixed splitDown instead.

--- menus/shell.cson
+++ menus/shell.cson
@@ -10,7 +10,7 @@
       'label': 'term'
       'submenu': [
         { 'label': 'Open New Terminal in Right Pane', 'command': 'term:open-split-right' },
-        { 'label': 'Open New Terminal in Bottom Pane', 'command': 'term:open-split-bottom' }
+        { 'label': 'Open New Terminal in Bottom Pane', 'command': 'term:open-split-down' }
       ]
     ]
   }


test(tabbar): ARIA and active class

test(tabbar): ARIA and active class

--- modules/directives/tabbar/test/index.coffee
+++ modules/directives/tabbar/test/index.coffee
@@ -1,43 +1,61 @@
-describe 'navbarDirective', ->
+describe 'tabbarDirective', ->

+  element = null
   scope   = null
-  element = null
+  state   = null
+
+  beforeEach module 'ui.state'

   beforeEach module 'bp'

-  beforeEach inject ($rootScope, $compile) ->
+  beforeEach module ($stateProvider, $urlRouterProvider) ->
+    $urlRouterProvider.otherwise '/first'
+    $stateProvider
+      .state 'first',
+        url: '/first'
+      .state 'second',
+        url: '/second'
+    null
+
+  beforeEach inject ($rootScope, $compile, $state) ->
     scope = $rootScope.$new()
-    template = "<bp-navbar>
-        <bp-button class='one'>Title</bp-button>
-        {{ label }}
-        <bp-button class='two'>Other</bp-button>
-        Bar
-        <bp-button class='three'>Three</bp-button>
-      </bp-navbar>"
+    state = $state
+    template = '''
+    <bp-tabbar>
+      <bp-tab class="bp-icon-search" bp-tap='to(tabState)' bp-state="first">
+        First
+      </bp-tab>
+      <bp-tab class="bp-icon-search" bp-tap='to(tabState)' bp-state="second">
+        Second
+      </bp-tab>
+    </bp-tabbar>'''
     element = $compile(template) scope
     scope.$apply()

-  describe 'element', ->
+  describe 'tabbar element', ->
     it 'should have ARIA role', ->
-      expect(element.attr 'role' ).toBe 'navigation'
+      expect(element.attr 'role' ).toBe 'tablist'

-  describe 'text', ->
+  describe 'tab elements', ->
     it 'should have ARIA role', ->
-      $text = element.find '.bp-navbar-text'
-      expect($text.attr 'role' ).toBe 'heading'
+      element.children().each (i,element) ->
+        expect($(element).attr 'role' ).toBe 'tab'

-    it 'should be compiled', ->
-      $text = element.find '.bp-navbar-text'
-      expect($text.text()).toBe ' Bar'
-      scope.label = 'Foo'
-      scope.$apply()
-      $text = element.find '.bp-navbar-text'
-      expect($text.text()).toBe 'Foo Bar'
+    it 'should have `bp-tab-active` class', ->
+      $first  = element.find ':first-child'
+      $second = element.find ':nth-child(2)'

-  describe 'buttons', ->
-    it 'should have correct order', ->
-      $buttons = element.children('bp-button')
-      expect($buttons.length).toBe 3
-      expect($buttons.filter('.one').hasClass 'before' ).toBe true
-      expect($buttons.filter('.two').hasClass 'before' ).toBe true
-      expect($buttons.filter('.three').hasClass 'after' ).toBe true
+      expect($first.hasClass 'bp-tab-active').toBe true
+      expect($second.hasClass 'bp-tab-active').toBe false
+
+      # done = no
+      # runs ->
+      #   scope.$on '$stateChangeError', ->
+      #     done = yes
+      #   state.transitionTo 'second'
+
+      # waitsFor (-> done), 1000
+
+      # runs ->
+      #   expect($first.hasClass 'bp-tab-active').toBe false
+      #   expect($second.hasClass 'bp-tab-active').toBe true


Add plugin title string

Add plugin title string

--- src/code/utils/lang/zh-TW.coffee
+++ src/code/utils/lang/zh-TW.coffee
@@ -171,6 +171,7 @@

   "~RELATIONSHIP.NO_RELATION": "沒有關係被定義"

+  "~CODAP.INTERACTIVE_FRAME.TITLE": "SageModeler"
   "~CODAP.SIMULATION.RUN": "執行"
   "~CODAP.SIMULATION.EXPERIMENT": "實驗.#"
   "~CODAP.SIMULATION.STEPS": "步驟"


update nav course name when courses are loaded and route has courseId param

--- src/components/navbar/index.cjsx
+++ src/components/navbar/index.cjsx
@@ -17,9 +17,6 @@
 module.exports = React.createClass
   displayName: 'Navigation'

-  mixins: [BindStoreMixin]
-  bindStore: CourseStore
-
   contextTypes:
     router: React.PropTypes.func

@@ -27,32 +24,27 @@
     CourseListingStore.ensureLoaded()

   getInitialState: ->
-    course: undefined
+    course = @getCourseFromParams()
+    {course}
+
+  getCourseFromParams: ->
+    {courseId} = @context.router.getCurrentParams()
+    CourseStore.get(courseId) if courseId?

   handleCourseChanges: ->
     if @isMounted()
-      {courseId} = @context.router.getCurrentParams()
-
-      unless @state.course?.id is courseId
-        course = CourseStore.get(courseId)
+      course = @getCourseFromParams()
+      unless _.isEqual(course, @state.course)
         @setState({course})

-  # Also need to listen to when location finally updates.
-  # This is especially crucial for redirect from dashboard because component mounts
-  # before location gets fully updated.  If it doesn't listen for when location
-  # changes, this component never update it's state with the course in that case.
-  addBindListener: ->
-    @context.router.getLocation().addChangeListener(@handleCourseChanges)
-    CourseListingStore.addChangeListener(@updateAll)
+  componentDidUpdate: ->
+    @handleCourseChanges()

-  removeBindListener: ->
-    @context.router.getLocation().removeChangeListener(@handleCourseChanges)
-    CourseListingStore.removeChangeListener(@updateAll)
+  componentDidMount: ->
+    CourseStore.on('course.loaded', @handleCourseChanges)

-  updateAll: -> @setState({})
-
-  bindUpdate: ->
-    @handleCourseChanges()
+  componentWillUnmount: ->
+    CourseStore.off('course.loaded', @handleCourseChanges)

   transitionToMenuItem: (routeName, params) ->
     @context.router.transitionTo(routeName, params)


Undo the cancelled header-title structure change

--- resources/assets/coffee/react/changelog-index/main.coffee
+++ resources/assets/coffee/react/changelog-index/main.coffee
@@ -85,10 +85,10 @@
         div className: 'osu-page-header-v3__title-icon',
           div className: 'osu-page-header-v3__icon'
         h1
-          className: 'header-title'
+          className: 'osu-page-header-v3__title-text'
           dangerouslySetInnerHTML:
             __html: osu.trans 'changelog.index.title._',
-              info: "#{osu.trans('changelog.index.title.info')}"
+              info: "#{osu.trans('changelog.index.title.info')}"

   showMore: =>


fixed autoresizing of the canvas

fixed autoresizing of the canvas

--- dramagrama.coffee
+++ dramagrama.coffee
@@ -229,8 +229,10 @@
 		nextRectangleEndX = point.x + @rectangleWidth + @childrenHorizontalPadding
 		nextRectangleEndY = point.y + @rectangleHeight + @childrenVerticalPadding

-		# resize to be the perfect width and height
-		@paper.setSize(Math.max(@paperWidth, nextRectangleEndX), Math.max(@paperHeight,nextRectangleEndY) )
+		# resize to be the optimal width and height
+		@paperWidth = Math.max(@paperWidth, nextRectangleEndX)
+		@paperHeight = Math.max(@paperHeight,nextRectangleEndY)
+		@paper.setSize(@paperWidth, @paperHeight)

 		fillColour = block.colour || "white"


Deserialize Workspace in atom.loadStateSync

--- src/atom.coffee
+++ src/atom.coffee
@@ -174,6 +174,7 @@
       notificationManager: @notifications, setRepresentedFilename: @setRepresentedFilename.bind(this),
       setDocumentEdited: @setDocumentEdited.bind(this), atomVersion: @getVersion()
     })
+    @themes.workspace = @workspace

     @registerDefaultOpeners()

@@ -539,7 +540,10 @@
     @windowEventHandler = new WindowEventHandler(this)

     @packages.loadPackages()
-    @deserializeEditorWindow()
+
+    workspaceElement = @views.getView(@workspace)
+    @keymaps.defaultTarget = workspaceElement
+    document.querySelector(@workspaceParentSelectorctor).appendChild(workspaceElement)

     @watchProjectPath()

@@ -688,19 +692,7 @@
     false

   deserializeWorkspace: ->
-    Workspace = require './workspace'
-
-    startTime = Date.now()
-    @workspace.deserialize(@state.workspace, @deserializers)
-    @themes.workspace = @workspace
-    @deserializeTimings.workspace = Date.now() - startTime
-
-    workspaceElement = @views.getView(@workspace)
-    @keymaps.defaultTarget = workspaceElement
-    document.querySelector(@workspaceParentSelectorctor).appendChild(workspaceElement)
-
-  deserializeEditorWindow: ->
-    @deserializeWorkspace()
+

   loadThemes: ->
     @themes.load()
@@ -772,6 +764,10 @@
     @project.deserialize(@state.project, @deserializers) if @state.project?
     @deserializeTimings.project = Date.now() - startTime

+    startTime = Date.now()
+    @workspace.deserialize(@state.workspace, @deserializers) if @state.workspace?
+    @deserializeTimings.workspace = Date.now() - startTime
+
   getStateKey: (paths) ->
     if paths?.length > 0
       sha1 = crypto.createHash('sha1').update(paths.slice().sort().join("\n")).digest('hex')
@@ -800,7 +796,7 @@
       try
         require(userInitScriptPath) if fs.isFileSync(userInitScriptPath)
       catch error
-        atom.notifications.addError "Failed to load `#{userInitScriptPath}`",
+        @notifications.addError "Failed to load `#{userInitScriptPath}`",
           detail: error.message
           dismissable: true


fix #76, we now can use cmd+q to exit application

--- coffee/app.coffee
+++ coffee/app.coffee
@@ -31,6 +31,12 @@
 window.addEventListener "dragover", preventDefault, false
 window.addEventListener "drop", preventDefault, false
 window.addEventListener "dragstart", preventDefault, false
+
+# For mac, add basic menu back
+if process.platform is 'darwin'
+  defaultMenu = new gui.Menu({ type: 'menubar' })
+  defaultMenu.createMacBuiltin(gui.App.manifest.name)
+  win.menu = defaultMenu

 # Spinner options (should go in config file).
 # Should replace spinner by rotating svg later


fix for error message

fix for error message

--- src/mailButler.appsscript.coffee
+++ src/mailButler.appsscript.coffee
@@ -51,8 +51,8 @@
     loc = @messages.en if !loc
     str = loc[key]
     if map and str
-      for k,v in map
-        rgx = new RegExp "\\$#{key}\\$", 'g'
+      for k,v of map
+        rgx = new RegExp "\\$#{k}\\$", 'g'
         str = str.replace rgx, v
     str


Add new files to defaults detection.

Add new files to defaults detection.

--- src/codo.coffee
+++ src/codo.coffee
@@ -222,8 +222,10 @@
   #
   @detectReadme: (done) ->
     Async.filter [
+      'README.markdown'
       'README.md'
       'README'
+      'readme.markdown'
       'readme.md'
       'readme'
     ], fs.exists, (results) -> done null, _.first(results) || ''
@@ -232,12 +234,14 @@
   #
   @detectExtras: (done) ->
     Async.filter [
-      'CHANGELOG'
+      'CHANGELOG.markdown'
       'CHANGELOG.md'
       'AUTHORS'
       'AUTHORS.md'
+      'AUTHORS.markdown'
       'LICENSE'
       'LICENSE.md'
+      'LICENSE.markdown'
       'LICENSE.MIT'
       'LICENSE.GPL'
     ], fs.exists, (results) -> done null, results


datatableize only first table

datatableize only first table

--- main.coffee
+++ main.coffee
@@ -8,4 +8,4 @@
 # TODO: pick table
 # TODO: run datable on table

-$('table').DataTable();
+$('table')[0].DataTable();


use version in package.json

use version in package.json

--- main.coffee
+++ main.coffee
@@ -252,7 +252,7 @@
     logErr 'Device listing failed: ' + message

-cli.version '0.0.4'
+cli.version pkgJson.version

 cli.command 'init <name>'
   .description 'Create a new ClojureScript React Native project'


order and title non-nulls instead of true

--- gulp-nav.coffee
+++ gulp-nav.coffee
@@ -103,8 +103,8 @@
         .replace /^$/, '/'                             # root needs a title too
       order: orderGen++
   current.exists = yes             # if we're here, this resource *does* exist!
-  current.title = title if title   # overwrite defaults
-  current.order = order if order
+  current.title = title ? current.title     # overwrite defaults with non-nulls
+  current.order = order ? current.order
   navInContext current, [_path.join '']

 # Create the actual nav object that will be exposed to user code. This object


Update gulpfile for app externs.

Update gulpfile for app externs.

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -32,6 +32,7 @@
   ]
   compiler: 'bower_components/closure-compiler/compiler.jar'
   externs: [
+    'app/client/js/externs.js'
     'bower_components/react-externs/externs.js'
   ]
   thirdParty:
@@ -80,14 +81,14 @@
     compilerPath: paths.compiler
     compilerFlags:
       closure_entry_point: 'app.main'
-      externs: paths.externs
       define: [
         'goog.array.ASSUME_NATIVE_FUNCTIONS=true'
         'goog.dom.ASSUME_STANDARDS_MODE=true'
-        # 'goog.json.USE_NATIVE_JSON=true'
+        'goog.json.USE_NATIVE_JSON=true'
         # 'goog.net.XmlHttpDefines.ASSUME_NATIVE_XHR=true'
         'goog.style.GET_BOUNDING_CLIENT_RECT_ALWAYS_EXISTS=true'
       ]
+      externs: paths.externs

 gulp.task 'compile-serverapp', ->
   este.compile paths.js, 'app/server/build',


Better dev reload

Better dev reload

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -12,7 +12,7 @@
 cssmin      = require 'gulp-cssmin'
 addsrc      = require 'gulp-add-src'
 changed     = require 'gulp-changed'
-shorthand    = require 'gulp-shorthand'
+shorthand   = require 'gulp-shorthand'
 pkg         = require './package.json'
 _s          = require 'underscore.string'
 prefix      = require 'gulp-autoprefixer'
@@ -38,8 +38,7 @@
   js       :
     main   : ['assets/js/src/__init.coffee'
               'assets/js/src/main.coffee'
-              'assets/js/src/cover.coffee'
-              'assets/js/src/search.coffee']
+              'assets/js/src/cover.coffee']
     vendor : ['assets/vendor/fastclick/lib/fastclick.js'
               'assets/vendor/ghostHunter/jquery.ghostHunter.min.js'
               'assets/vendor/pace/pace.min.js'
@@ -88,17 +87,16 @@
   return

 gulp.task 'server', ->
-  browserSync.init null,
+  browserSync.init
     proxy: "http://127.0.0.1:#{PORT.GHOST}"
+    port: PORT.BROWSERSYNC
     files: ['assets/**/*.*']
-    reloadDelay: 300
-    port: PORT.BROWSERSYNC
   return

 gulp.task 'build', ['css', 'js']

 gulp.task 'default', ->
   gulp.start ['build', 'server']
-  gulp.watch src.sass.files, ['css']
-  gulp.watch src.js.main, ['js']
-  gulp.watch src.js.vendor, ['js']
+  gulp.watch src.sass.files, ['css', reload]
+  gulp.watch src.js.main, ['js', reload]
+  gulp.watch src.js.vendor, ['js', reload]


Increased timeout

Increased timeout

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -47,7 +47,7 @@
   ).pipe(
     mocha(
       "reporter": "dot"
-      "timeout": 5000
+      "timeout": 10000
     )
   )


updating the gulpfile to compile the coffeescript & js together, no need to break up the function

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -49,13 +49,11 @@
   gulp.src paths.php
     .pipe gulp.dest(paths.build)

-gulp.task "coffee", (cb) ->
+gulp.task "js", (cb) ->
   gulp.src paths.js.coffee
     .pipe coffee(bare: true)
     .pipe gulp.dest(paths.js.base)
-  cb()

-gulp.task "js", ->
   gulp.src require(paths.js.app)
     .pipe plumber()
     .pipe concat("index.js")
@@ -87,7 +85,7 @@

 gulp.task "default", ->
   gulp.start "clean"
-  gulp.start "fonts", "images", "php", "coffee", "js", "css", "wordpress"
+  gulp.start "fonts", "images", "php", "js", "css", "wordpress"

 gulp.task "watch", ->
   gulp.start "default"
@@ -95,6 +93,6 @@
   watch paths.fonts,     -> gulp.start ["fonts"]
   watch paths.images,    -> gulp.start ["images"]
   watch paths.php,       -> gulp.start ["php"]
-  watch paths.js.app,    -> gulp.start ["coffee", "js"]
-  watch paths.js.coffee, -> gulp.start ["coffee", "js"]
+  watch paths.js.app,    -> gulp.start ["js"]
+  watch paths.js.coffee, -> gulp.start ["js"]
   watch paths.css.less,  -> gulp.start ["css"]


made tasks that run all performance / test tasks

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -1,19 +1,15 @@
 gulp = require 'gulp'
-gutil = require 'gulp-util'
-espruino = require './gulp-espruino/src/gulp-espruino'
-requireDir = require 'require-dir'
-gulps = requireDir './src/tasks'
-
-fake = connection: { fakePath: '../Espruino/espruino' }
+gulps = (require 'require-dir')('./src/tasks')

 gulp.task 'default', ['test']
-
 gulp.task 'clean', -> gulps['clean']

+gulp.task 'test', ['test-module', 'test-deferred', 'test-unit']
 gulp.task 'test-unit', ['clean'], -> gulps['test-unit']()
 gulp.task 'test-deferred', ['clean'], -> gulps['deferred']()
 gulp.task 'test-module', ['clean'], -> gulps['modules']()

+gulp.task 'performance', ['performance-defined', 'performance-running']
 gulp.task 'performance-defined', ['clean'], -> gulps['defined-memory']()
 gulp.task 'performance-running', ['clean'], -> gulps['running-memory']()


updated gulpfile.coffee to remove watchDelay and add watchOptions/aggregateTimeout/poll

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -101,7 +101,9 @@
   devServer = new WebpackDevServer(webpack(webpackConfig),
     contentBase: './public/'
     hot: true
-    watchDelay: 100
+    watchOptions:
+        aggregateTimeout: 100
+        poll: 300
     noInfo: true
   )
   devServer.listen 8080, "0.0.0.0", (err) ->


Fix path

Fix path

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -22,7 +22,7 @@

 gulp.task 'build-web-res', ->
 	gulp.src paths.webRes
-		.pipe gulp.dest './bin/web/resources'
+		.pipe gulp.dest './bin/web/statics/resources'

 gulp.task 'build-web-views', ->
 	gulp.src paths.webViews


ComputePlansModal: Push newly created machine to KD.userMachines array.

--- client/Main/providers/computeplansmodalpaid.coffee
+++ client/Main/providers/computeplansmodalpaid.coffee
@@ -81,9 +81,11 @@

     computeController.create {
       provider : "koding", stack, storage
-    }, (err)=>
+    }, (err, machine)=>

       KD.showError err

+      KD.userMachines.push machine
+
       @createVMButton.hideLoader()
       @destroy()


client: move helper function in scope

client: move helper function in scope

--- client/app/lib/util/transformReactivityTags.coffee
+++ client/app/lib/util/transformReactivityTags.coffee
@@ -6,6 +6,11 @@
 module.exports = (text = '', callback) ->

   skipRanges = getBlockquoteRanges text
+
+  inSkipRange = (position) ->
+    for [start, end] in skipRanges
+      return yes  if start <= position <= end
+    return no

   hashtagsWithIndices = twitter.extractHashtagsWithIndices(text).filter (h) ->
     [start, end] = h.indices
@@ -43,9 +48,3 @@
     return text

-inSkipRange = (position) ->
-  for [start, end] in skipRanges
-    return yes  if start <= position <= end
-  return no
-
-


helper: social add sendActivationMessage

--- client/ide/lib/collaboration/helpers/social.coffee
+++ client/ide/lib/collaboration/helpers/social.coffee
@@ -124,6 +124,27 @@

 ###*
+ * Sends an activation message to given channel.
+ *
+ * @param {SocialChannel} channel
+ * @param {function(err:object, result: object)} callback
+###
+sendActivationMessage = (channel, callback) ->
+
+  {message} = kd.singletons.socialapi
+  nickname  = getNick()
+
+  options =
+    body       : "@#{nickname} activated collaboration."
+    channelId  : channel.id
+    payload    :
+      'system-message' : 'start'
+      collaboration    : yes
+
+  message.sendPrivateMessage options, callback
+
+
+###*
  * Wrapper function around `JAccount#cacheable`
  * It adds a little bit of intelligence around first
  * argument so that, it will fetch the account depending
@@ -151,5 +172,6 @@
   destroyChannel
   leaveChannel
   initChannel
+  sendActivationMessage
   fetchAccount
 }


Perhaps this will fix joining bugginess

Perhaps this will fix joining bugginess

--- server/lobbysystem/lobbies.coffee
+++ server/lobbysystem/lobbies.coffee
@@ -31,6 +31,7 @@
     team = "dire"
   return if tteam is team
   if index?
+    index = lobby[team].indexOf(index)
     lobby[tteam].push(lobby[team].splice(index, 1)[0])
     lobbies.update {_id: lobby._id},
       $set:


Small performance improvements

Small performance improvements

--- src/vtree_src/node.coffee
+++ src/vtree_src/node.coffee
@@ -20,13 +20,11 @@
     @parent = node

   setChildren: (nodes) ->
-    @children = _.filter(nodes, (node) =>
-      node.parent and node.parent.el is @el
-    )
+    @children = _.filter(nodes, (node) => node.parent is @)

   removeChild: (node) ->
-    return if _.indexOf(@children, node) is -1
-    @children = _.reject(@children, (childNode) -> childNode is node)
+    return if (nodeIndex = _.indexOf(@children, node)) is -1
+    @children.splice(nodeIndex, 1)

   init: ->
     @hooks.init(@)


webpack: some documentation for the webpack setup

--- src/webpack.config.coffee
+++ src/webpack.config.coffee
@@ -1,5 +1,5 @@
 ###
-Webpack configuration file
+# Webpack configuration file

 Run dev server with source maps:

@@ -22,7 +22,47 @@
     - https://github.com/petehunt/webpack-howto
     - http://webpack.github.io/docs/tutorials/getting-started/

-###
+---
+
+## Information for developers
+
+This webpack config file might look scary, but it only consists of a few moving parts.
+
+1. There is the "main" SMC application, which is split into "css", "lib" and "smc":
+   1. css: a collection of all static styles from various locations. it might be possible
+      to use the text extraction plugin to make this a .css file, but that didn't work out.
+      Some css is inserted, but it doesn't work and no styles are applied. in the end,
+      it doesn't matter to load it one way or the other. as .js is even better, because
+      the initial page load is instant and doesn't require to get the compiled css styles.
+   2. lib: this is a compilation of the essential js files in webapp-lib (via webapp-lib.coffee)
+   3. smc: the core smc library. besides this, there are also chunks (number-hash.js) that are
+      loaded later on deman (see "require.ensure"). for example, that does contain latex completions,
+      the data for the wizard, etc.
+2. There are static html files for the policies. They originate in webapp-lib/policies, whereas
+   at least one file is generated by update_react_static. That script runs part of the smc application
+   in node.js to render to html. Then, this is included into the html page and compiled. it's not
+   possible to automate this fully, because during the processing of these templates, the "css" chunk
+   from 1.1 is injected, too. In the future, also other elements from the website (e.g. <Footer/>) will
+   be rendered as static elements and included there.
+3. there are auxiliary files for the "video chat" functionality. That might be redone differently, but
+   for now rendering to html only works via the html webpack plugin in such a way, that it rewrites
+   paths and post processes the files correctly.
+
+The remaining configuration deals with setting up variables (misc_page contains the centralized
+information about where the page is getting rendered to, because also the hub.coffee needs to know
+about certain file locations)
+
+MathJax: It lifes in its own isolated world. This means, don't mess with the MathJax.js ...
+it needs to know from where it is loaded (the path), to retrieve many additional files on demand.
+That's also the main reason why it is slow, because for each file a new SSL connection has to be setup!
+(even, if the sever just says 302 not modified). Still, we help it a little bit by caching it, too.
+The trick is to add the MathJax version number to the path, such that it is unique and will definitely
+trigger a reload after an update. The MathjaxVersionedSymlink in combination with misc_node.MATHJAX_LIB
+does extract the MathJax version number, computes the path, and does symlink to it's location.
+Why in misc_node? The problem is, that also the jupyter server (in its isolated iframe),
+needs to know about the URL. That way, the hub can send down the URL to the server (there is no webapp client in between).
+###
+
 'use strict'

 _             = require('lodash')
@@ -78,13 +118,12 @@

 mathjaxVersionedSymlink = new MathjaxVersionedSymlink()

-# plugins
-
 # deterministic hashing for assets
 WebpackSHAHash = require('webpack-sha-hash')
 webpackSHAHash = new WebpackSHAHash()

 # cleanup like "make distclean"
+# otherwise, compiles create an evergrowing pile of files
 CleanWebpackPlugin = require('clean-webpack-plugin')
 cleanWebpackPlugin = new CleanWebpackPlugin [OUTPUT],
                                             verbose: true
@@ -102,7 +141,8 @@

 # https://www.npmjs.com/package/html-webpack-plugin
 HtmlWebpackPlugin = require('html-webpack-plugin')
-# we need our own chunk sorter, because dependency doesn't work
+# we need our own chunk sorter, because just by dependency doesn't work
+# this way, we can be 100% sure
 smcChunkSorter = (a, b) ->
     order = ['css', 'lib', 'smc']
     if order.indexOf(a.names[0]) < order.indexOf(b.names[0])
@@ -119,6 +159,7 @@
     collapseWhitespace : true
     conservativeCollapse : true

+# this is the main indes.html file, which should be served without any caching
 jade2html = new HtmlWebpackPlugin
                         date     : dateISO
                         title    : TITLE
@@ -158,18 +199,28 @@
                         chunks   : []
                         minify   : htmlMinifyOpts

+# global css loader configuration
+cssConfig = JSON.stringify(minimize: true, discardComments: {removeAll: true}, mergeLonghand: true, sourceMap: true)
+
+###
+# ExtractText for CSS should work, but doesn't.
+# Configuration left as a comment for future endeavours.
+
 # https://webpack.github.io/docs/stylesheets.html
 ExtractTextPlugin = require("extract-text-webpack-plugin")

 # merge + minify of included CSS files
-cssConfig = JSON.stringify(minimize: true, discardComments: {removeAll: true}, mergeLonghand: true, sourceMap: true)
 extractCSS = new ExtractTextPlugin("styles-[hash].css")
 extractTextCss  = ExtractTextPlugin.extract("style", "css?sourceMap&#{cssConfig}")
 extractTextSass = ExtractTextPlugin.extract("style", "css?#{cssConfig}!sass?sourceMap&indentedSyntax")
 extractTextScss = ExtractTextPlugin.extract("style", "css?#{cssConfig}!sass?sourceMap")
 extractTextLess = ExtractTextPlugin.extract("style", "css?#{cssConfig}!less?sourceMap")
+###

 # custom plugin, to handle the quirky situation of extra *.html files
+# it was originally used to copy auxiliary .html files, but since there is
+# no processing of the included style/js files (hashing them), it's useless for now.
+# in the future, it might become handy for any additional files.
 class LinkFilesIntoTargetPlugin
     constructor: (@files, @target) ->

@@ -199,6 +250,9 @@
 copyWebpackPlugin = new CopyWebpackPlugin []
 ###

+# this is like #ifdef in the source code. It is particularly useful in the
+# source code of SMC, such that it knows about itself's version and where
+# mathjax is. The version&date is shown in the hover-title in the footer (year)
 setNODE_ENV          = new webpack.DefinePlugin
                                 'process.env' :
                                    'NODE_ENV' : JSON.stringify(NODE_ENV)
@@ -209,7 +263,9 @@
 {StatsWriterPlugin} = require("webpack-stats-plugin")
 statsWriterPlugin   = new StatsWriterPlugin(filename: "webpack-stats.json")

-
+# this is for debugging: adding it prints out a long long json of everything
+# that ends up inside the chunks. that way, one knows where which part did end up.
+# (i.e. if require.ensure works, etc.)
 class PrintChunksPlugin

 PrintChunksPlugin.prototype.apply = (compiler) ->
@@ -221,11 +277,6 @@
                     includes: c.modules.map (m) ->  m.request
             )

-
-
-#provideGlobals = new webpack.ProvidePlugin
-#                                $: "jquery"
-#                                jQuery: "jquery"

 plugins = [
     cleanWebpackPlugin,
@@ -237,7 +288,7 @@
     videoChatCell,
     #commonsChunkPlugin,
     assetsPlugin,
-    extractCSS,
+    #extractCSS,
     #copyWebpackPlugin
     webpackSHAHash,
     statsWriterPlugin,
@@ -256,6 +307,8 @@
     plugins.push new webpack.optimize.OccurenceOrderPlugin()
     plugins.push new webpack.optimize.LimitChunkCountPlugin(maxChunks: 10)
     plugins.push new webpack.optimize.MinChunkSizePlugin(minChunkSize: 32768)
+    # to get source maps working in production mode, one has to figure out how
+    # to get inSourceMap/outSourceMap working here.
     plugins.push new webpack.optimize.UglifyJsPlugin
                                 sourceMap: false
                                 minimize: true
@@ -287,8 +340,8 @@
                                     side_effects: true

-# don't use base64!
-hashname    = '[sha256:hash:base62:33].[ext]'
+# tuning generated filenames and the configs for the aux files loader.
+hashname    = '[sha256:hash:base62:33].[ext]' # don't use base64, it's not recommended for some reason.
 pngconfig   = "name=#{hashname}&limit=16000&mimetype=image/png"
 svgconfig   = "name=#{hashname}&limit=16000&mimetype=image/svg+xml"
 icoconfig   = "name=#{hashname}&mimetype=image/x-icon"
@@ -331,7 +384,7 @@
             { test: /\.woff(2)?(\?v=[0-9].[0-9].[0-9])?$/, loader: "url-loader?#{woffconfig}" },
             { test: /\.(ttf|eot)(\?v=[0-9].[0-9].[0-9])?$/, loader: "file-loader?name=#{hashname}" },
             # { test: /\.css$/,    loader: 'style!css' },
-            { test: /\.css$/, loaders: ["style-loader", "css-loader?#{cssConfig}"]}, # loader: extractTextCss }, #
+            { test: /\.css$/, loaders: ["style-loader", "css-loader?#{cssConfig}"]}, # loader: extractTextCss }, #
             { test: /\.jade$/, loader: 'jade' },
         ]


Use PaneContainerViews::getPaneViews

Use PaneContainerViews::getPaneViews

--- src/workspace-view.coffee
+++ src/workspace-view.coffee
@@ -316,7 +316,7 @@
   #
   # Returns an Array of all open {PaneView}s.
   getPaneViews: ->
-    @panes.getPanes()
+    @panes.getPaneViews()

   # Public: Register a function to be called for every current and future
   # editor view in the workspace.


Default the react mini editors to on

Default the react mini editors to on

--- src/workspace-view.coffee
+++ src/workspace-view.coffee
@@ -72,6 +72,7 @@
     audioBeep: true
     destroyEmptyPanes: true
     useReactEditor: true
+    useReactMiniEditors: true

   @content: ->
     @div class: 'workspace', tabindex: -1, =>


Improved autosave.

Improved autosave.

--- app/assets/javascripts/app/controllers/agent_ticket_create.js.coffee
+++ app/assets/javascripts/app/controllers/agent_ticket_create.js.coffee
@@ -49,12 +49,6 @@
       @log 'notice', 'error', defaults
       @render(defaults)

-    # start auto save
-    @delay(
-      => @autosave(),
-      5000
-    )
-
   meta: =>
     text = App.i18n.translateInline( @article_attributes['title'] )
     subject = @el.find('[name=subject]').val()
@@ -91,7 +85,7 @@
         @autosaveLast = data
         @log 'notice', 'form hash changed', diff, data
         App.TaskManager.update( @task_key, { 'state': data })
-    @interval( update, 10000, @id,  @auto_save_key )
+    @interval( update, 3000, @id,  @auto_save_key )

   # get data / in case also ticket data for split
   fetch: (params) ->
@@ -210,6 +204,9 @@
     @textModule = new App.TextModuleUI(
       el: @el.find('.ticket-create').find('textarea')
     )
+
+    # start auto save
+    @autosave()

   localUserInfo: (params) =>


[Aphid.Views.ImageView] Added support for specifying the image address during initialization.

--- app/assets/javascripts/cartilage/views/image_view.js.coffee
+++ app/assets/javascripts/cartilage/views/image_view.js.coffee
@@ -33,8 +33,9 @@
   #
   initialize: (options = {}) ->
     @isLoaded     = false
+    @imageElement = ($ "<img />").hide()
     @imageAddress = options["imageAddress"]
-    @imageElement = ($ "<img />").hide()
+    ($ @imageElement).attr("src", @imageAddress) if @imageAddress?

   render: ->


fix dragging unselected mark

fix dragging unselected mark

--- app/assets/javascripts/components/image-subject-viewer.cjsx
+++ app/assets/javascripts/components/image-subject-viewer.cjsx
@@ -177,15 +177,15 @@

   handleMarkClick: (mark, e) ->
     {x,y} = @getEventOffset e
-    @selectMark mark

     # save click offset from mark center
     @setState
+      selectedMark: mark
       markOffset: {
-        x: @state.selectedMark.x - x,
-        y: @state.selectedMark.y - y
+        x: mark.x - x,
+        y: mark.y - y
       }
-
+
   handleDragMark: (e) ->
     console.log 'handleDragMark()'
     console.log 'SELECTED MARK: ', @state.selectedMark


adding logging to longpolling

adding logging to longpolling

--- lib/assets/javascripts/firehose/long_poll.js.coffee
+++ lib/assets/javascripts/firehose/long_poll.js.coffee
@@ -36,6 +36,7 @@
     super(delay)

   _request: =>
+    console?.log "_request", arguments
     # Set the Last Message Sequence in a query string.
     # Ideally we'd use an HTTP header, but android devices don't let us
     # set any HTTP headers for CORS requests.
@@ -43,7 +44,6 @@
     data.last_message_sequence = @_lastMessageSequence
     # TODO: Some of these options will be deprecated in jQurey 1.8
     #       See: http://api.jquery.com/jQuery.ajax/#jqXHR
-
     $.ajax @config.longPoll.url,
       crossDomain: true
       data: data
@@ -51,18 +51,20 @@
       success: @_success
       error: @_error
       xhr: hackedXHR
-
       complete: (jqXhr) =>
+        console?.log "XHR complete", arguments
         # Get the last sequence from the server if specified.
         if jqXhr.status == 200
           @_lastMessageSequence = jqXhr.getResponseHeader(@messageSequenceHeader)
           if @_lastMessageSequence == null
             console?.log 'ERROR: Unable to get last message sequnce from header'
+    console?.log "_request END"

   stop: =>
     @_stopRequestLoop = true

   _success: (data, status, jqXhr) =>
+    console?.log "_success", arguments
     # TODO we actually want to do this when the thing calls out... mmm right now it takes
     # up to 30s before we can call this thing.
     # Call the 'connected' callback if the connection succeeds.
@@ -81,6 +83,7 @@
       @connect(@_okInterval)

   _ping: =>
+    console?.log "_ping", arguments
     # Ping long poll server to verify internet connectivity
     # jQuery CORS doesn't support timeouts and there is no way to access xhr2 object
     # directly so we can't manually set a timeout.
@@ -93,6 +96,7 @@
   # We need this custom handler to have the connection status
   # properly displayed
   _error: (jqXhr, status, error) =>
+    console?.log "_error", arguments
     @_isConnected = false
     @config.disconnected()


Circles are colored now, and kill the transition on resize events.

--- app/assets/javascripts/drops_calculator/chartView.js.coffee
+++ app/assets/javascripts/drops_calculator/chartView.js.coffee
@@ -8,7 +8,7 @@
   renderTemplate: (@context) ->
     @el.html(@template(@context))

-  render: (@context) ->
+  render: (@context, doTransition = true) ->
     @el.html(@template(@context))

     data = @context.probs.map (x, i) ->
@@ -22,6 +22,11 @@
     x.domain([0, data.length-1])
     y = d3.scale.linear().range([height - margin.top, margin.bottom])
     y.domain([0, 1])
+
+    if doTransition
+      transitionDuration = 1000
+    else
+      transitionDuration = 0

     xAxis = d3.svg.axis()
       .scale(x)
@@ -74,14 +79,14 @@
       .style('stroke', 'none')
       .style('fill', '#9fa8da')
       .style('opacity', 0.5)
-      .transition().duration(1000)
+      .transition().duration(transitionDuration)
         .attrTween('d', tweener(area))
     entering.append('path')
       .attr('class', 'line')
       .style('fill', 'none')
       .style('stroke-width', 2)
       .style('stroke', '#1a237e')
-      .transition().duration(1000)
+      .transition().duration(transitionDuration)
         .attrTween('d', tweener(line))
     entering.append('g')
       .attr('class', 'y axis')
@@ -137,12 +142,13 @@
           .attr('cx', (d) -> x(d.x))
           .attr('cy', (d) -> y(d.y))
           .attr('r', 3)
+          .attr('fill', '#1a237e')

     @bind()

   bind: =>
     $(window).on('resize',  _.debounce( =>
-      @render(@context)
+      @render(@context, 0)
     , 300))