support checking window active

support checking window active

--- lib/ui/blink.coffee
+++ lib/ui/blink.coffee
@@ -21,6 +21,9 @@
 client.handle 'withWin', (id, code) ->
   evalwith windows[id], code

+client.handle 'winActive', (id) ->
+  windows.hasOwnProperty id
+
 client.onDisconnected ->
   for id, win of windows
     delete windows[id]


:new: Add a new validator rule for duplicate responses

--- lib/validate.coffee
+++ lib/validate.coffee
@@ -30,8 +30,11 @@
         throw new Error 'Missing type field on Linter Response'
       if result.html
         throw new Error 'Invalid html field on Linter Response' if typeof result.html isnt 'string'
+        throw new Error 'Got both html and text fields on Linter Response, expecting only one' if typeof result.text is 'string'
+        result.text = null
       else if result.text
         throw new Error 'Invalid text field on Linter Response' if typeof result.text isnt 'string'
+        result.html = null
       else
         throw new Error 'Missing html/text field on Linter Response'
       if result.trace
@@ -49,4 +52,4 @@
       result.key = JSON.stringify(result)
       result.linter = linter.name
       Validate.messages(result.trace, linter) if result.trace and result.trace.length
-    return undefined
+    return


added strictSSL option to getLatestCliVersion

--- lib/wakatime.coffee
+++ lib/wakatime.coffee
@@ -362,8 +362,11 @@
   )

 getLatestCliVersion = (callback) ->
-  url = 'https://raw.githubusercontent.com/wakatime/wakatime/master/wakatime/__about__.py'
-  request.get(url, (error, response, body) ->
+  options = {
+      strictSSL: atom.config.get('wakatime.strictSSL')
+      url: 'https://raw.githubusercontent.com/wakatime/wakatime/master/wakatime/__about__.py'
+  }
+  request.get(options, (error, response, body) ->
     version = null
     if !error and response.statusCode == 200
       re = new RegExp(/__version_info__ = \('([0-9]+)', '([0-9]+)', '([0-9]+)'\)/g)


naming things ( >__< )

naming things ( >__< )

--- api/apps/verticals/model.coffee
+++ api/apps/verticals/model.coffee
@@ -23,7 +23,7 @@
   title: @string().allow('', null)
   description: @string().allow('', null)
   slug: @string().allow('', null)
-  partner_logo: @string().allow('', null)
+  partner_logo_url: @string().allow('', null)
   featured_article_ids: @array().items(@objectId()).allow(null)
   start_at: @date().allow(null)
   end_at: @date().allow(null)


Click modal layer to close

Click modal layer to close

--- core/app/backbone/modules/modal.coffee
+++ core/app/backbone/modules/modal.coffee
@@ -16,6 +16,7 @@

     events:
       "click .close-popup": "close"
+      "click .js-layer": "close"
       "click": "stopPropagation"

     regions:


cherry-pick https://github.com/Shopify/dashing/pull/521

--- vendor/assets/javascripts/dashing/dashing-src.coffee
+++ vendor/assets/javascripts/dashing/dashing-src.coffee
@@ -37,7 +37,6 @@
     @mixin($(@node).data())
     Dashing.widgets[@id] ||= []
     Dashing.widgets[@id].push(@)
-    @mixin(Dashing.lastEvents[@id]) # in case the events from the server came before the widget was rendered

     type = Batman.Filters.dashize(@view)
     $(@node).addClass("widget widget-#{type} #{@id}")
@@ -51,6 +50,12 @@

   @::on 'ready', ->
     Dashing.Widget.fire 'ready'
+
+    # In case the events from the server came before the widget was rendered
+    lastData = Dashing.lastEvents[@id]
+    if lastData
+      @mixin(lastData)
+      @onData(lastData)

   receiveData: (data) =>
     @mixin(data)
@@ -106,8 +111,8 @@
   if lastEvents[data.id]?.updatedAt != data.updatedAt
     if Dashing.debugMode
       console.log("Received data for #{data.id}", data)
+    lastEvents[data.id] = data
     if widgets[data.id]?.length > 0
-      lastEvents[data.id] = data
       for widget in widgets[data.id]
         widget.receiveData(data)


Tests: Fix ReferenceError

Tests: Fix ReferenceError

--- spec/heterogenous.coffee
+++ spec/heterogenous.coffee
@@ -103,7 +103,7 @@
   afterEach (done) ->
     g.broker.disconnect done

-  names = Object.keys state.commands
+  names = Object.keys g.commands
   names.forEach (name) ->
     testParticipant g, name


Reverse the order of tooltip charts

Reverse the order of tooltip charts

--- elaboratecharts/coffee/index.coffee
+++ elaboratecharts/coffee/index.coffee
@@ -17,7 +17,7 @@
   return s

-prepareChart = ->
+prepareChart = (numberOfPositions) ->
   $chart = $('#chart')
   $chart.highcharts 'StockChart',
     chart:
@@ -47,8 +47,8 @@
                #{ Highcharts.dateFormat('%A, %b %e, %Y', @x) }
              "
         points = _.sortBy(@points, (point) -> point.y)
-        for point, index in points.reverse()
-          strIndex = zfill(index + 1, 2)
+        for point, index in points[-numberOfPositions..]
+          strIndex = zfill(numberOfPositions - index, 2)
           if point.y > 0
             s += "
#{ strIndex }
                    ● "
@@ -147,11 +147,11 @@
     for item, count of topitems
       items[item][timestamp] = count

-  chart = prepareChart()
   # Limit timeframe to last half.
   if timeframe != 'overall'
     timestamps = timestamps[-timestamps.length // 2 .. -1]

+  chart = prepareChart(numberOfPositions)
   for item, weeks of items
     series =
       name: item


Forgot to remove debug code.

Forgot to remove debug code.

--- src/Ecentinela/ComiconvBundle/Resources/public/js/contact.coffee
+++ src/Ecentinela/ComiconvBundle/Resources/public/js/contact.coffee
@@ -5,7 +5,7 @@
         e.preventDefault()

         @button.prop 'disabled', 'disabled'
-        alert @form.prop('action')
+
         $.post @form.prop('action'), email: @email.val(), text: @text.val(), =>
             @button.prop 'disabled', null
             @email.val ''


Added new tests to validate changes with %.

Now there are tests of  when it finds the nearest symbol to match. For examples
(abcd) and you hit % while on b.

--- spec/motions-spec.coffee
+++ spec/motions-spec.coffee
@@ -1083,8 +1083,14 @@
       keydown('%')
       expect(editor.getCursorScreenPosition()).toEqual [0, 0]

-    it 'no-ops when no matching action is possible', ->
+    it 'it moves appropriately to find the nearest matching action', ->
       editor.setCursorScreenPosition([0, 3])
       keydown('%')
-      expect(editor.getCursorScreenPosition()).toEqual [0, 3]
+      expect(editor.getCursorScreenPosition()).toEqual [0, 2]
       expect(editor.getText()).toEqual  "( ( ) )--{ text in here; and a function call(with parameters) }\n"
+
+    it 'it moves appropriately to find the nearest matching action', ->
+      editor.setCursorScreenPosition([0, 26])
+      keydown('%')
+      expect(editor.getCursorScreenPosition()).toEqual [0, 60]
+      expect(editor.getText()).toEqual  "( ( ) )--{ text in here; and a function call(with parameters) }\n"


Re-enable setSource tests for browser

Re-enable setSource tests for browser

--- spec/ComponentLoader.coffee
+++ spec/ComponentLoader.coffee
@@ -260,9 +260,6 @@

   describe 'writing sources', ->
     describe 'with working code', ->
-      before ->
-        # setSource not implemented in webpack loader yet
-        return @skip() if noflo.isBrowser()
       workingSource = """
       var noflo = require('noflo');


removed debug output from spec.

removed debug output from spec.

--- spec/IntegrationSpec.coffee
+++ spec/IntegrationSpec.coffee
@@ -59,26 +59,17 @@
     widgetLoaded = ->
       widget.hasCategoryList() && widget.hasProductList()

-    console.log "waiting for widget to load"
     waitFor widgetLoaded, ->
-      console.log "widget loaded"
-
       expect(widget.hasCategoryOption("Shoes")).toBeTruthy()
       widget.selectCategory("Shoes")

       categoryLoaded = ->
         widget.hasProduct("Meindl RFS Tibet")

-      console.log "waiting for category to load"
       waitFor categoryLoaded, ->
-        console.log "category loaded"
-
         widget.productLink("Meindl RFS Tibet").click()

-        console.log "waiting for variations to load"
         waitFor widget.variationsLoaded, ->
-          console.log "variations loaded"
-
           expect("label[for='epages-shop-variation-USSize']").toBeVisible()
           done()


wrong variable name

wrong variable name

--- app/assets/javascripts/init.coffee
+++ app/assets/javascripts/init.coffee
@@ -11,9 +11,9 @@
   ClientSideValidations.selectors.validate_inputs += ', .select2-container:visible :input:enabled[data-validate]'
   $(document).foundation()
   Facebook.load()
-  if Airesis.env == 'production'
+  if Airesis.environment == 'production'
     GoogleAnalytics.load()
-  if Airesis.env == 'test'
+  if Airesis.environment == 'test'
     $.fx.off = true
   #polling alerts
   if Airesis.signed_in


Use port 8888 for ease of typing

Use port 8888 for ease of typing

--- cli/Gruntfile.coffee
+++ cli/Gruntfile.coffee
@@ -214,7 +214,7 @@
     connect:
       server:
         options:
-          port: 8000
+          port: 8888
           hostname: '*'
           base: 'public'


Tweak spec description

Tweak spec description

--- spec/wrap-guide-spec.coffee
+++ spec/wrap-guide-spec.coffee
@@ -88,7 +88,7 @@
       wrapGuide.updateGuide()
       expect(wrapGuide).toBeHidden()

-  it "only attaches to editorViews that are part of a pane", ->
+  it "only attaches to editor views that are part of a pane", ->
     editorView2 = new EditorView(mini: true)
     editorView.overlayer.append(editorView2)
     expect(editorView2.find('.wrap-guide').length).toBe 0


images: remove global variables

images: remove global variables

--- client/images.coffee
+++ client/images.coffee
@@ -1,5 +1,7 @@
 Session.setDefault "trigger_update", null
-Session.setDefault "uploader_changed", false
+
+my_uploader = null # variable local to this script
+uploader_state_changed = new Deps.Dependency

 # ---- Template.binary_data_from_id_block -------------

@@ -84,10 +86,10 @@

 Template.show_upload_progress.helpers
   percent_uploaded: ->
-    Session.get "uploader_changed"
-    if !window.uploader?
+    uploader_state_changed.depend()
+    if !my_uploader?
       return
-    Math.round(window.uploader.progress() * 100);
+    Math.round(my_uploader.progress() * 100);

 get_id_from_key = (key) ->
   arr = key.split("/")
@@ -108,23 +110,23 @@
   ctx =
     lastModifiedDate: upload_file.lastModifiedDate

-  window.uploader = new Slingshot.Upload("myFileUploads",ctx)
-  Session.set("uploader_changed", !Session.get("uploader_changed"))
-
-  window.uploader.send upload_file, (error, downloadUrl) ->
+  my_uploader = new Slingshot.Upload("myFileUploads",ctx)
+  uploader_state_changed.changed()
+
+  my_uploader.send upload_file, (error, downloadUrl) ->
     # This callback is called when the upload is complete (or on error).
     $("#show_upload_progress_id").modal("hide")

     if error?
-      window.uploader = null
-      Session.set("uploader_changed", !Session.get("uploader_changed"))
+      my_uploader = null
+      uploader_state_changed.changed()
       console.error(error)
       bootbox.alert("There was an error uploading the file")
       return

-    s3_key = window.uploader.param('key')
-    window.uploader = null
-    Session.set("uploader_changed", !Session.get("uploader_changed"))
+    s3_key = my_uploader.param('key')
+    my_uploader = null
+    uploader_state_changed.changed()

     _id = get_id_from_key( s3_key )
     updater_doc =


add ability to drag and drop file for upload

--- client/images.coffee
+++ client/images.coffee
@@ -210,9 +210,31 @@
             dialog_template = window.dialog_template
             insert_image_save_func(dialog_template, send_coll, my_id, "images")

+handle_files = (fileList, template) ->
+  # template is template instance of InsertImageDialog
+  if fileList.length == 0
+    return
+  if fileList.length > 1
+    console.error "More than one file selected"
+    return
+  chosen_file = fileList[0]
+
 Template.InsertImageDialog.created = ->
   @selected_files = new ReactiveVar()
   @selected_files.set([])
+
+  # prevent dropping files onto page creating navigation
+  # http://stackoverflow.com/a/6756680/1633026
+  window.addEventListener 'dragover', ((e) ->
+    e = e or event
+    e.preventDefault()
+    return
+  ), false
+  window.addEventListener 'drop', ((e) ->
+    e = e or event
+    e.preventDefault()
+    return
+  ), false

 Template.InsertImageDialog.helpers
   selected_files: ->
@@ -227,12 +249,26 @@
     if !file_dom_element
       return
     template.selected_files.set(file_dom_element.files)
+    handle_files(file_dom_element.files,template)

   "click #fileSelect": (event, template) ->
     file_dom_element = template.find("#insert_image")
     if file_dom_element
       file_dom_element.click()
     event.preventDefault()
+
+  "dragenter #file_form_div": (event, template) ->
+     event.stopPropagation()
+     event.preventDefault()
+  "dragover #file_form_div": (event, template) ->
+     event.stopPropagation()
+     event.preventDefault()
+  "drop #file_form_div": (event, template) ->
+     event.stopPropagation()
+     event.preventDefault()
+     dt = event.originalEvent.dataTransfer
+     template.selected_files.set(dt.files)
+     handle_files(dt.files, template)

 Template.binary_data_table.rendered = ->
   $('.flex-images').flexImages({rowHeight: 200});


Add Channel#unfollow function

Add Channel#unfollow function

--- core/app/backbone/models/channel.coffee
+++ core/app/backbone/models/channel.coffee
@@ -98,3 +98,11 @@
       success: => console.info 'Hey, we just followed a channel!', this
       error: => @set('followed?', false)

+  unfollow: ->
+    unfollowUrl = "#{@normal_url()}/unfollow"
+    @set('followed?', false)
+    $.ajax
+      url: unfollowUrl
+      type: 'post'
+      success: => console.info 'we unfollowed channel!', this
+      error: => @set('followed?', true)


removed unused method

removed unused method

--- core/app/backbone/models/channel.coffee
+++ core/app/backbone/models/channel.coffee
@@ -89,12 +89,6 @@
     _.extend super(),
       is_mine: @is_mine()

-  toggleFollow: ->
-    if @get('followed?')
-      @unfollow()
-    else
-      @follow()
-
   follow: ->
     followUrl = "#{@normal_url()}/follow"
     @set('followed?', true)


Updated simulate('change') snippet

Updated simulate('change') snippet

--- snippets/simulateChange.cson
+++ snippets/simulateChange.cson
@@ -3,6 +3,6 @@
     "prefix": "simChange"
     "body": """
       ${1:input}.simulate("change", {
-        ${target: { value: value },}
-        });
+        ${2:target: { value: value },}
+      });
       """


o Added a path_prefix default

o Added a path_prefix default

--- simple_tiles.coffee
+++ simple_tiles.coffee
@@ -16,6 +16,7 @@
     'port': 3000,
     'hostname': '127.0.0.1',
     'logfile': 'console',
+    'path_prefix': '',
     'layers': []
 })


Changing router onBeforeAction handers to waitOn

--- client/router.coffee
+++ client/router.coffee
@@ -5,9 +5,8 @@
 	    onRun: ->
 	    	$('html, body').scrollTop(0)
 	    	@next()
-	    onBeforeAction: ->
+	    waitOn: ->
 	    	@subscribe('exploreStoriesPub', '', '', '').wait()
-	    	@next()
 	    action: -> if @ready() then @render()
 	    data: ->
 	    	Session.set "page", "explore"
@@ -22,9 +21,8 @@
 	@route "read",
 	    path: "read/:storyDashTitle"
 	    template: "read"
-	    onBeforeAction: ->
-	    	@subscribe('readStoryPub', @.params.storyDashTitle).wait()
-	    	@next()
+	    waitOn: ->
+	    	Meteor.subscribe('readStoryPub', @.params.storyDashTitle)
 	    action: -> if @ready() then @render()
 	    data: ->
 	    	# Get rid of these
@@ -32,9 +30,6 @@
 	    	Session.set "newStory", false
 	    	Session.set "read", true
 	    	Session.set "page", "read"
-
-	    	# Session.set("currentY", 0)
-	    	# Session.set("currentX", 0)

 	    	if story
 	    		Session.set "verticalSections", story.verticalSections
@@ -45,9 +40,8 @@
 	@route "readWithCoordinates",
 	    path: "read/:storyDashTitle/:currentX/:currentY"
 	    template: "read"
-	    onBeforeAction: ->
+	    waitOn: ->
 	    	@subscribe('readStoryPub', @.params.storyDashTitle).wait()
-	    	@next()
 	    action: -> if @ready() then @render()
 	    onRun: ->
 	    	# Scroll to current section
@@ -96,9 +90,8 @@
 	    onRun: ->
 	    	$('html, body').scrollTop(0)
 	    	@next()
-	    onBeforeAction: ->
+	    waitOn: ->
 	    	@subscribe('createStoryPub', @.params.storyDashTitle).wait()
-	    	@next()
 	    action: -> if @ready() then @render()
 	    data: ->
 	    	story = Stories.findOne()


payment: `JPaymentPlan#fetchQuota` should only take usage into account

--- workers/social/lib/social/models/payment/plan.coffee
+++ workers/social/lib/social/models/payment/plan.coffee
@@ -258,16 +258,13 @@
         callback err, unless err then group.slug

   checkQuota: (options, callback) ->
-    {usage, spend, couponCode, multiplyFactor} = options
+    {usage, couponCode, multiplyFactor} = options
     multiplyFactor ?= 1

-    usages = for own planCode, quantity of spend
+    usages = for own planCode, quantity of usage
       planSize    = @quantities[planCode] ? 0
       usageAmount = usage[planCode] ? 0
-      spendAmount = (spend[planCode] ? 0) * multiplyFactor
-
-      total = planSize - usageAmount - spendAmount
-
+      total       = planSize - usageAmount
       { planCode, total }

     [ok, over] = partition usages, ({ total }) -> total >= 0


log doclines on hash mismatch

log doclines on hash mismatch

--- services/document-updater/app/coffee/RedisManager.coffee
+++ services/document-updater/app/coffee/RedisManager.coffee
@@ -52,7 +52,7 @@
 			# check the hash computed on the redis server
 			writeHash = result?[0]
 			if logHashWriteErrors and writeHash? and writeHash isnt docHash
-				logger.error project_id: project_id, doc_id: doc_id, writeHash: writeHash, origHash: docHash, "hash mismatch on putDocInMemory"
+				logger.error project_id: project_id, doc_id: doc_id, writeHash: writeHash, origHash: docHash, docLines:docLines, "hash mismatch on putDocInMemory"
 			# update docsInProject set
 			rclient.sadd keys.docsInProject(project_id:project_id), doc_id, callback

@@ -92,7 +92,7 @@
 			if docLines? and storedHash?
 				computedHash = RedisManager._computeHash(docLines)
 				if logHashReadErrors and computedHash isnt storedHash
-					logger.error project_id: project_id, doc_id: doc_id, doc_project_id: doc_project_id, computedHash: computedHash, storedHash: storedHash, "hash mismatch on retrieved document"
+					logger.error project_id: project_id, doc_id: doc_id, doc_project_id: doc_project_id, computedHash: computedHash, storedHash: storedHash, docLines:docLines, "hash mismatch on retrieved document"

 			try
 				docLines = JSON.parse docLines
@@ -181,7 +181,7 @@
 					# check the hash computed on the redis server
 					writeHash = result?[0]
 					if logHashWriteErrors and writeHash? and writeHash isnt newHash
-						logger.error doc_id: doc_id, writeHash: writeHash, origHash: newHash, "hash mismatch on updateDocument"
+						logger.error doc_id: doc_id, writeHash: writeHash, origHash: newHash, docLines:newDocLines, "hash mismatch on updateDocument"
 					return callback()

 	getDocIdsInProject: (project_id, callback = (error, doc_ids) ->) ->


Remove `jquery.guillotine` import in legacy CoffeeScript

--- app/javascript/packs/legacy.coffee
+++ app/javascript/packs/legacy.coffee
@@ -5,7 +5,6 @@
 import {} from 'jquery-ujs'
 import 'popper.js'
 import 'bootstrap'
-import 'jquery.guillotine'
 import 'particleground/jquery.particleground.min'
 import 'jquery.growl'
 import 'sweetalert'


Deduplicate code

Deduplicate code

--- _includes/coffeescript/main.coffee
+++ _includes/coffeescript/main.coffee
@@ -76,13 +76,10 @@
           when true then Slide.setStateCss(s.element, "active")
           when false then Slide.setStateCss(s.element, "inactive")

-        $(s.element).css({
-          "-webkit-transform": "translate(#{(s.x - @currentChapter().x) * (@originalWidth + 20)}px, #{(s.y - c.currentSlide().y) * (@originalHeight + 20)}px)"
-          "-moz-transform": "translate(#{(s.x - @currentChapter().x) * (@originalWidth + 20)}px, #{(s.y - c.currentSlide().y) * (@originalHeight + 20)}px)"
-          "-ms-transform": "translate(#{(s.x - @currentChapter().x) * (@originalWidth + 20)}px, #{(s.y - c.currentSlide().y) * (@originalHeight + 20)}px)"
-          "-o-transform": "translate(#{(s.x - @currentChapter().x) * (@originalWidth + 20)}px, #{(s.y - c.currentSlide().y) * (@originalHeight + 20)}px)"
-          "transform": "translate(#{(s.x - @currentChapter().x) * (@originalWidth + 20)}px, #{(s.y - c.currentSlide().y) * (@originalHeight + 20)}px)"
-        })
+        Presentation.translate(
+          s.element,
+          (c.x - @currentChapter().x) * (@originalWidth + 20),
+          (s.y - c.currentSlide().y) * (@originalHeight + 20))
       )
     )

@@ -122,4 +119,13 @@
         when 27 # escape
           $(document.activeElement).blur()

+  @translate: (element, x, y) ->
+    $(element).css({
+      "-webkit-transform": "translate(#{x}px, #{y}px)"
+      "-moz-transform": "translate(#{x}px, #{y}px)"
+      "-ms-transform": "translate(#{x}px, #{y}px)"
+      "-o-transform": "translate(#{x}px, #{y}px)"
+      "transform": "translate(#{x}px, #{y}px)"
+    })
+
 window.Presentation = Presentation


Dont set accept header

Dont set accept header

--- plugins/bugify/index.coffee
+++ plugins/bugify/index.coffee
@@ -30,7 +30,6 @@
       .post("#{config.url}/api/issues.json")
       .auth(config.apiKey, "")
       .type("form")
-      .set('Accept', 'application/json')
       .send(payload)
       .on "error", (err) ->
         callback(err)


Removed new lines

Removed new lines

--- app/js/util/factlink_button.coffee
+++ app/js/util/factlink_button.coffee
@@ -8,5 +8,3 @@
     # prevent mousedown from deselecting the text that
     # the button is intended to use:
     e.preventDefault()
-
-


log number of updates for future debugging

--- app/coffee/UpdateManager.coffee
+++ app/coffee/UpdateManager.coffee
@@ -40,6 +40,7 @@
 	fetchAndApplyUpdates: (project_id, doc_id, callback = (error) ->) ->
 		RealTimeRedisManager.getPendingUpdatesForDoc doc_id, (error, updates) =>
 			return callback(error) if error?
+			logger.log {project_id: project_id, doc_id: doc_id, count: updates.length}, "processing updates"
 			if updates.length == 0
 				return callback()
 			async.eachSeries updates,


saving some work

saving some work

--- etc/gulpfile.coffee
+++ etc/gulpfile.coffee
@@ -166,35 +166,32 @@
   gulp.task name, ->
     gulp.watch glob, (evt) ->
       logFile evt
-      evt._eventStart = process.hrtime()
-      task = handler.call @, evt
-      task.pipe each (f) ->
-        console.log 'pppppp'
-#       f._startTime = process.hrtime()
-#       st = each()
-#       st.pipe pl.coffee
-#       st.write f
+      handler.call @, evt
+
+

 JNT = pln()

-
-
-pln JNT, [
+logTime = each (f) ->
+  console.log f
+  @push f
+
+
+pl =
+  coffee: pln JNT, [
     coffee {bare: yes}
     gulp.dest GLOBS.coffee.tmp
     logStream
   ]
-
-pln JNT, [
-    each (f) ->
-      console.log 'llll'
-  ]
+  # spec: pln JNT, [
+  #   each (f) ->
+  # ]

 watcher "watch:src", GLOBS.coffee.src, (evt) ->
   gulp.src evt.path
+    .pipe logTime
     .pipe JNT
-

 watcher "watch:etc", GLOBS.etc.src, (evt) ->
     if match evt.path, '**/*gulp*.coffee'


Remove Site from exports

Remove Site from exports

--- exports/atom.coffee
+++ exports/atom.coffee
@@ -1,4 +1,4 @@
-{Document, Point, Range, Site} = require 'telepath'
+{Document, Point, Range} = require 'telepath'

 module.exports =
   _: require 'underscore-plus'
@@ -11,7 +11,6 @@
   Git: require '../src/git'
   Point: Point
   Range: Range
-  Site: Site

 # The following classes can't be used from a Task handler and should therefore
 # only be exported when not running as a child node process


close menu when picking a website

close menu when picking a website

--- app/assets/javascripts/epiclogger.coffee
+++ app/assets/javascripts/epiclogger.coffee
@@ -12,7 +12,7 @@
     $(window).trigger 'resize'

   sidebarToggle: ->
-    $('#sidebar .selectbox > p').on 'click', (e) ->
+    $('#sidebar .selectbox > p, .title').on 'click', (e) ->
       select = $('#sidebar .selectbox');
       if select.hasClass 'open'
         select.removeClass 'open'


Inserts newest flashes on top

Inserts newest flashes on top

--- app/assets/javascripts/flashes.js.coffee
+++ app/assets/javascripts/flashes.js.coffee
@@ -3,5 +3,5 @@
 $(document).ajaxComplete (event, request) ->
   flash_data = request.getResponseHeader('X-Flash-Data')
   return unless flash_data
-  $('#flashes').append(flash_data)
+  $('#flashes').prepend(flash_data)
   $('#flashes').activateUnobtrusiveJavascript()


A test for #252

A test for #252

--- spec/Helpers.coffee
+++ spec/Helpers.coffee
@@ -1349,7 +1349,7 @@
           in: ['d1', 'd2']
           params: 'p1'
           out: 'out'
-          arrayPolicy: # default values
+          arrayPolicy: # inversed
             in: 'all'
             params: 'any'
         , (input, groups, out) ->
@@ -1375,6 +1375,44 @@
         p12.disconnect()
         p13.send 3
         p13.disconnect()
+
+      it 'should behave normally with string output from another component', (done) ->
+        c = new component.Component
+        c.inPorts.add 'd1',
+          datatype: 'string'
+          addressable: true
+        c.outPorts.add 'out',
+          datatype: 'object'
+        d11 = socket.createSocket()
+        d12 = socket.createSocket()
+        d13 = socket.createSocket()
+        out = socket.createSocket()
+        c.inPorts.d1.attach d11
+        c.inPorts.d1.attach d12
+        c.inPorts.d1.attach d13
+        c.outPorts.out.attach out
+        c2 = new component.Component
+        c2.inPorts.add 'in', datatype: 'string'
+        c2.outPorts.add 'out', datatype: 'string'
+        helpers.WirePattern c2,
+          in: 'in'
+          out: 'out'
+          forwardGroups: true
+        , (input, groups, out) ->
+          out.send input
+        d3 = socket.createSocket()
+        c2.inPorts.in.attach d3
+        c2.outPorts.out.attach d11
+
+        helpers.WirePattern c,
+          in: 'd1'
+          out: 'out'
+        , (input, groups, out) ->
+          chai.expect(input).to.deep.equal {0: 'My string'}
+          done()
+
+        d3.send 'My string'
+        d3.disconnect()

   describe 'MultiError', ->
     describe 'with simple sync processes', ->


removing debug log

removing debug log

--- server/lib/konnector_poller.coffee
+++ server/lib/konnector_poller.coffee
@@ -29,7 +29,6 @@
                         interval = 23 * day
                     else
                         interval = periods[konnector.importInterval]
-                    console.log konnector
                     @prepareNextCheck konnector, interval
                 callback()


Rename editSession to editor

Rename editSession to editor

--- spec/github-file-spec.coffee
+++ spec/github-file-spec.coffee
@@ -6,7 +6,7 @@
 describe "GitHubFile", ->
   describe "commands", ->
     githubFile = null
-    editSession = null
+    editor = null
     workingDirPath = path.join(os.tmpdir(), 'to-the-hubs-working-dir')
     filePathRelativeToWorkingDir = 'some-dir/some-file.md'

@@ -25,8 +25,8 @@

     setupGithubFile = ->
       atom.project.setPath(workingDirPath)
-      editSession = atom.project.openSync(filePathRelativeToWorkingDir)
-      githubFile = GitHubFile.fromPath(editSession.getPath())
+      editor = atom.project.openSync(filePathRelativeToWorkingDir)
+      githubFile = GitHubFile.fromPath(editor.getPath())

     teardownWorkingDirAndRestoreFixture = (fixtureName) ->
       success = null


Remove empty space

Remove empty space

--- spec/Subtree.coffee
+++ spec/Subtree.coffee
@@ -186,8 +186,3 @@
       testChild child,
         child:'yep 5'
         state:'w-child'
-
-
-
-
-


fix bump coffee script compiler on parameter variable

--- app/assets/javascripts/hub_client.coffee
+++ app/assets/javascripts/hub_client.coffee
@@ -1,7 +1,7 @@
 class HubApi
   # url: hub host url (example: 'http://hub.instedd.org')
   # proxy_pi: url to use for calling hub api on behalf current user: (example: '/hub' if that redirects to 'http://hub.instedd.org/api')
-  constructor: (@url, @proxyApi = "#{url}/api") ->
+  constructor: (@url, @proxyApi = "#{@url}/api") ->
     @pickers = {}

     if window.addEventListener


Improve date picker UX by defaulting to year first

--- app/assets/javascripts/members.js.coffee
+++ app/assets/javascripts/members.js.coffee
@@ -7,6 +7,7 @@

   $('.datepicker').datepicker
     format: 'dd/mm/yyyy',
+    viewMode: 2

   $('select#member_university_name').on 'change', (e) =>
     parentElement = $('#member_university_name_other').parents('.control-group')


Fix replace method to replace all occurrences

I have used the JavaScript string method replace in a wrong way. It only
replaced the first occurrence of the character I wanted to replace. I
have fixed that by using now a regular expression instead.

Signed-off-by: Konrad Reiche <4ff0213f56e2082fcd8c882d04c546881c579ce4@gmail.com>

--- app/assets/javascripts/metrics.js.coffee
+++ app/assets/javascripts/metrics.js.coffee
@@ -96,7 +96,7 @@
     for metric in ['completeness', 'weighted-completeness',
       'richness-of-information', 'accuracy']

-      attribute = metric.replace('-', '_')
+      attribute = metric.replace(/-/g, '_')
       if gon.repository[attribute]?
         score = gon.repository[attribute].average
         metricMeter[metric].updateScore(score)


Modify the typo of the element's id in surveys js file

--- app/assets/javascripts/surveys.js.coffee
+++ app/assets/javascripts/surveys.js.coffee
@@ -31,7 +31,7 @@
     $("#checkBoxes").append(markup)
     return false

-  $("#questions").sortable
+  $("#edit-questions").sortable
     placeholder: "sortable-placeholder"
     start: (event, ui) ->
       $(ui.item).parent().find('.sortable-placeholder').height($(ui.item).height())


another fix

another fix

--- app/assets/javascripts/bookings/application.js.coffee
+++ app/assets/javascripts/bookings/application.js.coffee
@@ -18,6 +18,7 @@
     event.end.setHours(data.to_time.split(':')[0])
     event.end.setMinutes(data.to_time.split(':')[1])
     event.recurring = data.recurring
+    event.id = data.id if data.id
     event

   displayErros: (errors)->
@@ -31,7 +32,7 @@
     templateSlotCalendar.fullCalendar "unselect"
     $('#template_form').css('display', 'block')

-  setFormInputsValues: (event)->
+  setFormFieldsValue: (event)->
     $('#template_slot_day').val(event.start.getDayName())
     $('#from_time_hour').val(event.start.getHours())
     $('#from_time_minute').val(event.start.getMinutes())
@@ -39,9 +40,28 @@
     $('#to_time_minute').val(event.end.getMinutes())
     $('#template_slot_recurring').val(event.recurring || 0)

+  successHandler: (event, data)->
+    $('#template_form').html('')
+    $('#template_form').html('
Saved Successfully
')
+    event = FormHandler.setEvent event, data
+    unless event.isNew # refresh events if update
+      templateSlotCalendar.fullCalendar 'rerenderEvents'
+    else
+      event.isNew = false
+      templateSlotCalendar.fullCalendar "renderEvent",
+        title: data.id.toString()
+        start: event.start
+        recurring: data.recurring
+        end: event.end
+        id: data.id
+        allDay: false
+      , true # make the event "stick"
+    $('#template_form').css('display', 'block')
+    event
+
   showForm: (event)->
       $('#template_form').html(templateSlotForm)
-      FormHandler.setFormInputsValues(event)
+      FormHandler.setFormFieldsValue(event)

       $('#template_form form').on 'submit', (e) ->
         e.preventDefault()
@@ -60,20 +80,8 @@
           dataType: 'json'

           success: (data)->
-            $('#template_form').html('')
-            $('#template_form').html('
Saved Successfully
')
-            event = FormHandler.setEvent event, data
-            if event.id # refresh events if update
-              templateSlotCalendar.fullCalendar 'rerenderEvents'
-            else
-              templateSlotCalendar.fullCalendar "renderEvent",
-                title: data.id.toString()
-                start: event.start
-                recurring: data.recurring
-                end: event.end
-                allDay: false
-              , true # make the event "stick"
-            $('#template_form').css('display', 'block')
+            FormHandler.successHandler event, data
+            console.log event
           error: (xhr, textStatus, errorThrown) ->
             FormHandler.displayErros xhr.responseJSON.errors

@@ -88,7 +96,7 @@
   window.templateSlotCalendar = $("#calendar").fullCalendar(
     eventClick: (event, element) ->
       FormHandler.showForm(event)
-      $('#template_form form').attr('action', $('#template_form form').attr('action') + "/#{event.title}")
+      $('#template_form form').attr('action', $('#template_form form').attr('action') + "/#{event.id}")
       $('#template_form form').attr('method', 'patch')

     theme: false
@@ -105,7 +113,7 @@
     selectHelper: true

     select: (start, end, allDay) ->
-      FormHandler.showForm( {start: start, end: end} )
+      FormHandler.showForm( {start: start, end: end, isNew: true} )

     editable: true

@@ -129,14 +137,14 @@
     eventResize: (event, dayDelta, minuteDelta, revertFunc, jsEvent, ui, view)->
       FormHandler.showForm(event)
       $('#template_form').css('display', 'none')
-      $('#template_form form').attr('action', $('#template_form form').attr('action') + "/#{event.title}")
+      $('#template_form form').attr('action', $('#template_form form').attr('action') + "/#{event.id}")
       $('#template_form form').attr('method', 'patch')
       $('#template_form form').submit()

     eventDrop: (event, dayDelta, minuteDelta, allDay, revertFunc, jsEvent, ui, viw)->
       FormHandler.showForm(event)
       $('#template_form').css('display', 'none')
-      $('#template_form form').attr('action', $('#template_form form').attr('action') + "/#{event.title}")
+      $('#template_form form').attr('action', $('#template_form form').attr('action') + "/#{event.id}")
       $('#template_form form').attr('method', 'patch')
       $('#template_form form').submit()


Improve empty series rendering in demand charts

Sets the opacity of any series whose values are all zero to be zero.
Prevents rendering multiple empty series on top of each other which
creates aliasing artifacts in Chrome.

--- app/assets/javascripts/d3/dynamic_demand_curve.coffee
+++ app/assets/javascripts/d3/dynamic_demand_curve.coffee
@@ -102,8 +102,10 @@
     refresh: (xScale, yScale) ->
       super

+      legendSeries = @getLegendSeries()
+
       @setStackedData()
-      @drawLegend(@getLegendSeries())
+      @drawLegend(legendSeries)

       xScale = @createTimeScale(@dateSelect.getCurrentRange())
       yScale = @createLinearScale()
@@ -131,3 +133,14 @@

       else
         @initialDraw(xScale, yScale, area, line)
+
+      serie = @svg.selectAll('g.serie')
+        .style('opacity', (data) ->
+          # Sets the opacity of invisible series (particularly those whose
+          # values are all zeroes) to avoid rendering artifacts when multiple
+          # such series overlap.
+          if _.find(legendSeries, (s) -> s.get('gquery_key') == data.key)
+            1.0
+          else
+            0.0
+        )


Fix escaping of HTML entities like &, < and >

--- app/assets/javascripts/player/renderers/pre.js.coffee
+++ app/assets/javascripts/player/renderers/pre.js.coffee
@@ -58,7 +58,7 @@
     @$el.find(".line:eq(" + n + ")")[0].innerHTML = '<span>' + html.join('') + '</span>'

   escape: (text) ->
-    text.replace('&', '&').replace('<', '<')
+    text.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>')

   spanFromBrush: (brush) ->
     key = brush.hash()


fix filename from url

fix filename from url

--- app/assets/javascripts/uploadcare/files/url.js.coffee
+++ app/assets/javascripts/uploadcare/files/url.js.coffee
@@ -19,7 +19,9 @@
       # while server preview servise doesn't work with URL-files
       @__tmpFinalPreviewUrl = @__url

-      @fileName = utils.parseUrl(@__url).pathname.split('/').pop() or null
+      filename = utils.parseUrl(@__url).pathname.split('/').pop()
+      @fileName = if filename then decodeURIComponent(filename) else null
+
       @__notifyApi()

     __startUpload: ->


Minor copy edits

Minor copy edits

--- app/pages/about/about-home.cjsx
+++ app/pages/about/about-home.cjsx
@@ -8,57 +8,31 @@
    pageContent: '''
     ## What is the Zooniverse?

-    The Zooniverse is the world's largest and most popular online platform for
-    people-powered research projects. This research is being conducted in part by
-    volunteers—hundreds of thousands of people around the world who come together to
-    assist professional researchers with their data. Our goal is to enable research
-    that would not be possible (or practical) otherwise. People-powered research results
-    in new discoveries, datasets useful to the wider research community, and [many publications](./#/about/publications).
+    The Zooniverse is the world’s largest and most popular platform for people-powered research. This research is      made possible by volunteers—hundreds of thousands of people around the world who come together to assist           professional researchers. Our goal is to enable research that would not be possible, or practical, otherwise.      Zooniverse research results in new discoveries, datasets useful to the wider research community, and [many         publications](./#/about/publications).

-    ### At the Zooniverse, anyone can be a researcher online.
+    ### At the Zooniverse, anyone can be a researcher

-    You don't need any specialised background, training, or expertise to participate in any
-    Zooniverse projects. We make it easy for anyone to contribute to real academic research,
-    on their own computer, at their own convenience.
+    You don’t need any specialised background, training, or expertise to participate in any Zooniverse projects. We make it easy for anyone to contribute to real academic research, on their own computer, at their own convenience.

-    You'll be able to study authentic objects of interest gathered by researchers,
-    like images of faraway galaxies, historical records and diaries, or videos of animals
-    in their natural habitats. By answering simple questions about them, you'll help contribute
-    to our understanding of our world, our history, our universe, and more.
+You’ll be able to study authentic objects of interest gathered by researchers, like images of faraway galaxies, historical records and diaries, or videos of animals in their natural habitats. By answering simple questions about them, you’ll help contribute to our understanding of our world, our history, our Universe, and more.

-    And with our wide-ranging and ever-expanding suite of projects, covering many disciplines
-    and topics across the sciences and humanities, there's a place for anyone and everyone to
-    explore, learn and have fun in the Zooniverse!
+With our wide-ranging and ever-expanding suite of projects, covering many disciplines and topics across the sciences and humanities, there's a place for anyone and everyone to explore, learn and have fun in the Zooniverse.

-    ### We accelerate important research by working together.
+    ### We accelerate important research by working together

-    The major challenge of 21st century research is dealing with the flood of information we can
-    now collect about the world around us. Computers can help, but in many fields the human ability
-    for pattern recognition—and our ability to be surprised—is still superior. With the help of
-    Zooniverse volunteers, researchers can analyze that information more quickly and accurately
-    than would otherwise be possible, saving time and resources, advancing the ability of computers
-    to do the same tasks, and leading to faster research output.
+    The major challenge of 21st century research is dealing with the flood of information we can now collect about the world around us. Computers can help, but in many fields the human ability for pattern recognition—and our ability to be surprised—makes us superior. With the help of Zooniverse volunteers, researchers can analyze their information more quickly and accurately than would otherwise be possible, saving time and resources, advancing the ability of computers to do the same tasks, and leading to faster progress and understanding of the world, getting to exciting results more quickly.

-    Our projects apply the philosophy of the "wisdom of crowds." Combining the classifications of
-    many individual users produces accurate and usable data with a measurable estimate of
-    error—exactly what's needed to make progress in these fields of research.
+Our projects apply the philosophy of the “wisdom of crowds.” Combining the classifications of many individual users produces accurate and usable data with a measurable estimate of error—exactly what’s needed to make progress in these fields of research.

-    ### Volunteers and professionals make real discoveries together.
+    ### Volunteers and professionals make real discoveries together

     Zooniverse projects are constructed with the aim of converting volunteers' efforts into
     measurable results. These projects have produced a large number of [published research papers](./#/about/publications),
-    as well as several open-source sets of analyzed data. In some cases, Zooniverse volunteers have
-    even made completely new and scientifically significant discoveries!
+    as well as several open-source sets of analyzed data. In some cases, Zooniverse volunteers have even made completely unexpected and scientifically significant discoveries.

-    A significant amount of this research takes place on the Zooniverse discussion boards,
-    where volunteers can work together with each other and with the research teams. These boards
-    are integrated with each project's data to allow for everything from quick hashtagging to in-depth
-    collaborative analysis. There is also a central Zooniverse board for general chat and discussion
-    about Zooniverse-wide matters.
+A significant amount of this research takes place on the Zooniverse discussion boards, where volunteers can work together with each other and with the research teams. These boards are integrated with each project to allow for everything from quick hashtagging to in-depth collaborative analysis. There is also a central Zooniverse board for general chat and discussion about Zooniverse-wide matters.

-    Many of the most interesting discoveries from Zooniverse projects
-    have come from discussion between volunteers and researchers.
-    We encourage all users to join the conversation on the discussion boards for more in-depth participation.'''
+Many of the most interesting discoveries from Zooniverse projects have come from discussion between volunteers and researchers. We encourage all users to join the conversation on the discussion boards for more in-depth participation.'''

 module.exports = React.createClass
   displayName: 'AboutHome'
@@ -70,4 +44,4 @@
     document.documentElement.classList.remove 'on-secondary-page'

   render: ->
-    <Markdown>{counterpart "aboutHome.pageContent"}</Markdown>+    <Markdown>{counterpart "aboutHome.pageContent"}</Markdown>


removed debug output.

removed debug output.

--- public/app.coffee
+++ public/app.coffee
@@ -29,9 +29,6 @@

     if @env is "production"
       @apiUrl = matches[1]
-
-    console.log shopUrl
-    console.log matches

     scriptTag.after new StylesView().render().el
     scriptTag.after new LoadingView().render().el


refactor new module method

refactor new module method

--- lib/appHelper.coffee
+++ lib/appHelper.coffee
@@ -21,9 +21,9 @@
   APP    : null

 OPTIONS =
-  NAME         : 'testApp'
-  ORGANIZATION : 'sailorjs'
-  REPOSITORY   : 'testApp'
+  name         : 'testApp'
+  organization : 'sailorjs'
+  repository   : 'testApp'

 TEMPLATE =
   BASE  : path.join(__dirname, '../template/base')
@@ -75,7 +75,7 @@
    *   - name
    *   - organization
    *   - repository
-   * @param  {Function} cb      Optional Callback
+   * @param  {Function} cb    Optional Callback
   ###
   @newBase: (dir, options, cb) =>
     args = Args([
@@ -95,40 +95,31 @@
     SCOPE.SAILS  = @_resolvePath 'sails'
     SCOPE.SAILOR = @_resolvePath 'sailor'

-    console.log SCOPE
-
     # search the depndencies in the SCOPE's or download
     @_copyDependencies(appJSON, args.cb)

   ###
-   * Generate a new module
+   * Generate a new module for a project
    * @param  {String} dir     Optional path
    * @param  {Object} options Optional options, like:
    *   - name
    *   - organization
    *   - repository
-   * @param  {Function} cb      Optional Callback
+   * @param  {Function} cb    Optional Callback
   ###
   @newModule: (dir, options, cb) =>
     args = Args([
-      {name         : Args.STRING   | Args.Optional, _default: OPTIONS.NAME}
-      {organization : Args.STRING   | Args.Optional, _default: OPTIONS.ORGANIZATION}
-      {repository   : Args.STRING   | Args.Optional, _default: name}
-      {dir          : Args.STRING   | Args.Optional, _default: "#{process.cwd()}"}
-      {done         : Args.FUNCTION | Args.Optional, _default: undefined}
-    ], arguments)
-
-    SCOPE.APP    = "#{args.dir}/#{args.name}"
+      {dir          : Args.STRING   | Args.Optional, _default: "#{process.cwd()}" }
+      {options      : Args.OBJECT   | Args.Optional, _default: OPTIONS            }
+      {cb           : Args.FUNCTION | Args.Optional, _default: undefined          }
+    ], arguments)
+
+    SCOPE.APP    = "#{args.dir}/#{args.options.name}"
     wrench.copyDirSyncRecursive TEMPLATE.MODULE, SCOPE.APP, forceDelete: true

-    options =
-      name         : args.name
-      organization : args.organization
-      repository   : args.repository
-
-    @_copyTemplate template, options for template in TEMPLATE.FILES
+    @_copyTemplate template, args.options for template in TEMPLATE.FILES

     appJSON  = require("#{SCOPE.APP}/package.json")
     delete appJSON.dependencies?.sailorjs
@@ -136,8 +127,8 @@
     SCOPE.SAILS  = @_resolvePath 'sails'
     SCOPE.SAILOR = @_resolvePath 'sailor'

-    # search the dependency in sails or sailor and linkin in the folder of the project
-    @_copyDependencies(appJSON, args.done)
+    # search the depndencies in the SCOPE's or download
+    @_copyDependencies(appJSON, args.cb)

@@ -166,7 +157,7 @@
   @writePluginFile: (source, dir, done) ->
     args = Args([
       {src:  Args.STRING   | Args.Required}
-      {dir : Args.STRING   | Args.Optional, _default: path.join(process.cwd(), "/#{OPTIONS.NAME}/config/plugins.coffee")}
+      {dir : Args.STRING   | Args.Optional, _default: path.join(process.cwd(), "/#{OPTIONS.name}/config/plugins.coffee")}
       {done: Args.FUNCTION | Args.Optional, _default: undefined}
     ], arguments)

@@ -190,6 +181,7 @@

     wrench.rmdirSyncRecursive args.dir  if fs.existsSync(args.dir)
     args.done?()
+

   ###
@@ -242,6 +234,7 @@
     path.join fs.realpathSync(result.stdout), '../..' if fileType.isSymbolicLink()

+
   ###
   Localize a dependency and return the path
   ###
@@ -281,6 +274,8 @@
     sailorLocal = path.resolve(SCOPE.APP, 'node_modules', 'sailorjs')
     fs.symlink SCOPE.SAILOR, sailorLocal, "junction", (symLinkErr) ->
       cb?()
+
+

   ###
   Process a template file and copy it on the destinity


Disable pushing camera forward

Disable pushing camera forward

And trap the players within the screen.
At the end of level, the right side 'opens'

--- app/scripts/components/ScrollWall.coffee
+++ app/scripts/components/ScrollWall.coffee
@@ -11,7 +11,7 @@
         y: 0

     @_speed = { x: 0, y: 0 }
-    @wallEnd = Crafty.e('2D, Canvas, ScrollFront')
+    @wallEnd = Crafty.e('2D, Canvas, ScrollFront, Edge')
       .attr(x: - (Crafty.viewport.x - Crafty.viewport.width) - 3, y: 0, h: Crafty.viewport.height, w: 12)
     @attach @wallEnd

@@ -27,20 +27,21 @@
       speedX = @_speed.x
       speedY = @_speed.y

-      # When the ships are in the first 30% of the screen,
-      # speed up the camera.
-      Crafty('PlayerControlledShip').each ->
-        threshold = Crafty.viewport.width * (2.0 / 3.0)
-        relOffset = @x + Crafty.viewport.x
-        if relOffset > threshold
-          percentageOutOfBounds = (relOffset - threshold) / (Crafty.viewport.width - threshold)
-          increase = (5 + @_forcedSpeed.x) * percentageOutOfBounds
+      if @allowPushing
+        # When the ships are in the first 30% of the screen,
+        # speed up the camera.
+        Crafty('PlayerControlledShip').each ->
+          threshold = Crafty.viewport.width * (2.0 / 3.0)
+          relOffset = @x + Crafty.viewport.x
+          if relOffset > threshold
+            percentageOutOfBounds = (relOffset - threshold) / (Crafty.viewport.width - threshold)
+            increase = (5 + @_forcedSpeed.x) * percentageOutOfBounds

-          if speedX > 0 # Prevend division by zero
-            percentage = (speedX + increase) / speedX
-            speedY *= percentage
+            if speedX > 0 # Prevend division by zero
+              percentage = (speedX + increase) / speedX
+              speedY *= percentage

-          speedX += increase
+            speedX += increase

       @x += speedX
       @y += speedY
@@ -76,6 +77,9 @@
       @_speed.y = 0
     this

+  setAllowPushing: (@allowPushing) ->
+
   off: ->
+    @wallEnd.removeComponent('Edge')
     @unbind('EnterFrame')


Terminal: Pass options to constructor and return default char sizes.

--- client/Terminal/src/Terminal.coffee
+++ client/Terminal/src/Terminal.coffee
@@ -14,7 +14,7 @@

     { containerView, @readOnly } = options

-    super()
+    super options

     if @readOnly
       for keyHandler in ['keyDown', 'keyPress', 'keyUp', 'paste']
@@ -162,8 +162,8 @@

   getCharSizes:->
     sizes =
-      width  : @measurebox.getWidth()  or @_mbWidth
-      height : @measurebox.getHeight() or @_mbHeight
+      width  : @measurebox.getWidth()  or @_mbWidth  or 7
+      height : @measurebox.getHeight() or @_mbHeight or 14

     [@_mbWidth, @_mbHeight] = [@measurebox.getWidth(), @measurebox.getHeight()]


Remove unused map.coffee

Remove unused map.coffee

--- src/adventure/map.coffee
+++ src/adventure/map.coffee
@@ -1,80 +0,0 @@
-# -----------------------------------------------------------------------------
-#
-# Copyright (C) 2013 by John Watkinson
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-# -----------------------------------------------------------------------------
-
-# This class is deprecated-- just hanging around for some demo code
-class Map extends TileView
-
-  constructor: (gurk, @mapName) ->
-    super(gurk, @mapName)
-    @setButton(1, "NEW")
-    @setButton(3, "HIT")
-    @setButton(7, "WOW")
-    @setButton(9, "SHOOT")
-    @setBanner("Sir Rugnar (HP: 8)")
-
-  command: (text) =>
-    switch text
-      when View.LEFT then @move(-1, 0)
-      when View.UP then @move(0, -1)
-      when View.RIGHT then @move(1, 0)
-      when View.DOWN then @move(0, 1)
-      when "NEW" then @gurk.showConfirm(Data.icons.party, "New Game", "Erase your old saved game and make a new one? You won't be able to recover your old game!", {}, {})
-      # when "LOAD" then @gurk.showAlert("party.png", "Test Alert", "Thanks for doing this, it has been a great test of functionality. All the best.", {})
-      when "HIT"
-        @animate(Data.icons.animation, 4, 4, 200, @animDone)
-      when "WOW"
-        view = new SelectView(@gurk, "SELECT")
-        view.addIcon(Data.icons.party, 3, 3)
-        view.addLabel("Blammo!", "#FFF", 22, 12)
-        y = 3 + 19
-        view.addIcon("grass.png", 3, y)
-        view.addOption("Grass", "#880", 22, y + 9)
-        y += 19
-        view.addIcon("tree.png", 3, y)
-        view.addOption("Forest", "#080", 22, y + 9)
-        y += 19
-        view.addIcon("water.png", 3, y)
-        view.addOption("Water", "#44F", 22, y + 9)
-        @gurk.pushView(view)
-      when "SHOOT"
-        # no-op
-        abc = 0
-  animDone: =>
-
-  doDraw: =>
-    for y in [0..Screen.WIN_SIZE]
-      yy = y + @posY - Screen.CENTER_OFFSET
-      if (yy >= 0 and yy < @height)
-        for x in [0..Screen.WIN_SIZE]
-          xx = x + @posX - Screen.CENTER_OFFSET
-          if (xx >= 0 and xx < @width)
-            tile = @getTerrainIcon(xx, yy)
-            @drawTile(tile, x, y)
-    @drawTile(Data.icons.party, Screen.CENTER_OFFSET, Screen.CENTER_OFFSET)
-    @drawBanner()
-    # Test drawing
-    # @screen.clearColor(Screen.GURK_BLUE)
-    # @screen.drawText("(\"Oopla!\" 'Quoi?')", "#FF0000", 20, 20)
-    # @screen.wrapText("Here is a long paragraph that is going to have to be wrapped, y'all! It's super long and now two line breaks approach.\n\nMore stuff here, because why not? And we'll get it to break too, haha. 123456789012345678901234567890123456789012345678901234567890", "#FFFFFF", 0, 0, 128)
-
-  move: (x, y) =>
-    @posX += x
-    @posY += y
-    if (@posX < 0) then @posX = 0 else if (@posX >= @width) then @posX = @width - 1
-    if (@posY < 0) then @posY = 0 else if (@posY >= @height) then @posY = @height - 1
-    @draw();


board: chat.coffee fix norlogize onChat for large boards

--- app/assets/javascripts/chat.coffee
+++ app/assets/javascripts/chat.coffee
@@ -30,12 +30,14 @@
     existing = $("#board_" + msg.id)
     return  if existing.length > 0
     if @isInboxLarge
-      @inbox.append(msg.large).find(".board-left:first .norloge").click @norloge
+      @inbox.append(msg.large).find(".board-left:last .norloge").click @norloge
       @inboxContainer.scrollTop(@inbox.height())
+      @norlogize      right for right in @inbox.find(".board-right:last")
+      @norlogize_left left  for left  in @inbox.find(".board-left time:last")
     else
       @inbox.prepend(msg.message).find(".board-left:first .norloge").click @norloge
-    @norlogize      right for right in @inbox.find(".board-right:first")
-    @norlogize_left left  for left  in @inbox.find(".board-left time:first")
+      @norlogize      right for right in @inbox.find(".board-right:first")
+      @norlogize_left left  for left  in @inbox.find(".board-left time:first")

   postMessage: (event) =>
     form = $(event.target)


change: let turbolinks handle custom links

--- app/assets/javascripts/init.coffee
+++ app/assets/javascripts/init.coffee
@@ -112,4 +112,5 @@
         #       here"
         window.open($(this).data('href'), '_blank');
       else
-        document.location = $(this).data('href')
+        # DOC: https://github.com/turbolinks/turbolinks#turbolinksvisit
+        Turbolinks.visit($(this).data('href'))


Replace compiled code with source

Replace compiled code with source

--- lib/collector.coffee
+++ lib/collector.coffee
@@ -31,15 +31,23 @@

     # Private
     getBrowserResolution: ->
-      takenFromOctolytics = `function() {
-        var e, t, n, r;
-        try {
-            return t = 0, e = 0, typeof window.innerWidth == "number" ? (t = window.innerWidth, e = window.innerHeight) : ((n = document.documentElement) != null ? n.clientWidth : void 0) != null ? (t = document.documentElement.clientWidth, e = document.documentElement.clientHeight) : ((r = document.body) != null ? r.clientWidth : void 0) != null && (t = document.body.clientWidth, e = document.body.clientHeight), t + "x" + e
-        } catch (i) {
-            return "unknown"
-        }
-      }`
-      takenFromOctolytics()
+      try
+        width = 0
+        height = 0
+
+        if typeof window.innerWidth is 'number'
+          width  = window.innerWidth
+          height = window.innerHeight
+        else if document.documentElement?.clientWidth?
+          width  = document.documentElement.clientWidth
+          height = document.documentElement.clientHeight
+        else if document.body?.clientWidth?
+          width  = document.body.clientWidth
+          height = document.body.clientHeight
+
+        width + 'x' + height
+      catch e
+        'unknown'

     # Private
     getPackages: ->


Fix making Problems from AetherProblems with no range

--- app/views/play/level/tome/Problem.coffee
+++ app/views/play/level/tome/Problem.coffee
@@ -14,7 +14,7 @@
       { @lineMarkerRange, @textMarkerRange } = @buildMarkerRangesFromAetherProblem(@aetherProblem) if isCast

       { @level, @range, @message, @hint, @userInfo } = @aetherProblem
-      { @row, @column: col } = @aetherProblem.range?[0]
+      { @row, @column: col } = @aetherProblem.range?[0] or {}
       @createdBy = 'aether'
     else
       unless userCodeHasChangedSinceLastCast


Add untracked changes.

Add untracked changes.

--- client/core/app.coffee
+++ client/core/app.coffee
@@ -16,5 +16,3 @@
     controller: 'PaginationCtrl'
     resolve:
       organizations: (organization) -> organization.get()
-
-


mouse rotation

mouse rotation

--- app/assets/javascripts/view.coffee
+++ app/assets/javascripts/view.coffee
@@ -204,9 +204,12 @@
 		curCoords[0] = mouseX
 		curCoords[1] = mouseY
 		buttonDown = true
+		engine.startAnimationLoop()

 	mouseReleased = ->
 		buttonDown = false
+		engine.stopAnimationLoop()
+		window.setTimeout writeFramerate, 500

 # #####################
 # KEYBOARD
@@ -239,18 +242,34 @@
 			cam.move [0,0,-moveValueStrafe]

 		#Rotate up
+		if buttonDown
+			cam.pitch -(curCoords[1]-mouseY)/500
+			curCoords[1] = mouseY
+
 		if keyboard.isKeyDown(KEY_UP)
 			cam.pitch moveValueRotate

 		#Rotate down
+		if buttonDown
+			cam.pitch (curCoords[1]-mouseY)/500
+			curCoords[1] = mouseY
+
 		if keyboard.isKeyDown(KEY_DOWN)
 			cam.pitch -moveValueRotate

 		#Rotate right
+		if buttonDown
+			cam.yaw -(curCoords[0]-mouseX)/500
+			curCoords[0] = mouseX
+
 		if keyboard.isKeyDown(KEY_RIGHT)
 			cam.yaw -moveValueRotate

 		#Rotate left
+		if buttonDown
+			cam.yaw (curCoords[0]-mouseX)/500
+			curCoords[0] = mouseX
+
 		if keyboard.isKeyDown(KEY_LEFT)
 			cam.yaw moveValueRotate


Fix camelCase typo

Fix camelCase typo

--- lib/constants.coffee
+++ lib/constants.coffee
@@ -9,7 +9,7 @@
   }

   linters: {
-    pyLint: 'pylint'
+    pylint: 'pylint'
     mccabe: 'mccabe'
     pep8: 'pep8'
     pep257: 'pep257'


adding new classes to includes

adding new classes to includes

--- client/includes.coffee
+++ client/includes.coffee
@@ -110,6 +110,7 @@
   "Framework/components/modals/KDModalViewLoad.coffee",
   "Framework/components/modals/KDBlockingModalView.coffee",
   "Framework/components/modals/KDModalViewWithForms.coffee",
+  "Framework/components/modals/KDModalViewStack.coffee",

   # notification
   "Framework/components/notifications/KDNotificationView.coffee",
@@ -239,6 +240,7 @@
   "app/CommonViews/introductiontooltip/introductiontooltipcontroller.coffee",

   "app/CommonViews/ModalViewWithTerminal.coffee",
+  "app/CommonViews/DNDUploader.coffee",

   "app/MainApp/kodingappcontroller.coffee",
   "app/MainApp/sidebar/sidebarcontroller.coffee",


styles: predefined styles removed for now

--- client/includes.coffee
+++ client/includes.coffee
@@ -691,7 +691,7 @@
   "stylus/app.bottom.styl",
   "stylus/app.about.styl",
   "stylus/app.commons.styl",
-  "stylus/app.predefined.styl",
+  # "stylus/app.predefined.styl",
   "stylus/app.editor.styl",
   "stylus/app.finder.styl",
   "stylus/app.aceeditor.styl",


update openInVim in my new environment, now open exactly same cursor pos

--- lib/developer.coffee
+++ lib/developer.coffee
@@ -207,10 +207,11 @@

   openInVim: ->
     editor = atom.workspace.getActiveTextEditor()
-    {row} = editor.getCursorBufferPosition()
+    {row, column} = editor.getCursorBufferPosition()
+    # e.g. /Applications/MacVim.app/Contents/MacOS/Vim -g /etc/hosts "+call cursor(4, 3)"
     new BufferedProcess
-      command: "/Applications/MacVim.app/Contents/MacOS/mvim"
-      args: [editor.getPath(), "+#{row+1}"]
+      command: "/Applications/MacVim.app/Contents/MacOS/Vim"
+      args: ['-g', editor.getPath(), "+call cursor(#{row+1}, #{column+1})"]

   generateIntrospectionReport: ->
     generateIntrospectionReport _.values(Base.getRegistries()),


Destroy model when delete event occurs

Destroy model when delete event occurs

--- lib/directory.coffee
+++ lib/directory.coffee
@@ -90,7 +90,9 @@
   # The changes will be emitted as 'entries-added' and 'entries-removed' events.
   watch: ->
     @watchSubscription ?= PathWatcher.watch @path, (eventType) =>
-      @reload() if eventType is 'change'
+      switch eventType
+        when 'change' then @reload()
+        when 'delete' then @destroy()

   getEntries: ->
     names = fs.readdirSync(@path).sort (name1, name2) ->


now sorts items properly

now sorts items properly

--- client/listview.coffee
+++ client/listview.coffee
@@ -1,5 +1,5 @@
 Template.ListView.onCreated ->
-  @items = Item.find()
+  @items = Item.find({}, {sort: {name: 1}})
   @edit = new ReactiveVar(false)

 Template.ListView.onRendered ->


Ctrl-Enter to confirm Prefs dialog

Ctrl-Enter to confirm Prefs dialog

(instead of just Enter)

--- client/prefs-ui.coffee
+++ client/prefs-ui.coffee
@@ -47,7 +47,7 @@
       </div>
     """
       .tabs(activate: (e, ui) -> tabs[$(ui.newTab).index()].resize?(); return)
-      .keydown (e) -> if e.which == 13 && !e.shiftKey && !e.ctrlKey && !e.altKey then ok(); false
+      .keydown (e) -> if e.which == 13 && !e.shiftKey && e.ctrlKey && !e.altKey then ok(); false
       .on 'dragstart', -> false
       .dialog
         autoOpen: 0, title: 'Preferences', width: 600, minWidth: 600, height: 450, minHeight: 450


Model#serialize: Removed unused parameter. Closes #97.

--- src/chaplin/models/model.coffee
+++ src/chaplin/models/model.coffee
@@ -67,7 +67,7 @@
     # (i.e. an object which has the attributes as prototype)
     # so primitive values might be added and altered safely.
     # Map models to their attributes, recursively.
-    serialize: (model) ->
+    serialize: ->
       serializeAttributes this, @getAttributes()

     # Disposal


Treat area elements as being at the point of their img element

--- lib/dom_utils.coffee
+++ lib/dom_utils.coffee
@@ -64,8 +64,13 @@
           true
         else if tagName == "a"
           true
-        else if tagName == "area"
-          element.hasAttribute "href"
+        else if tagName == "img"
+          mapName = element.getAttribute "usemap"
+          if mapName
+            map = document.querySelector(mapName.replace /^#/, "")
+            areas = Array::slice.call(map.getElementsByTagName "area")
+            elements.concat areas
+          false
         else if (tagName == "input" and DomUtils.isSelectable element) or tagName == "textarea"
           not (element.disabled or element.hasAttribute "readonly")
         else if (tagName == "input" and element.getAttribute("type")?.toLowerCase() != "hidden") or


Fix item drops on inspector panel [#93637418]

--- src/code/views/link-view.coffee
+++ src/code/views/link-view.coffee
@@ -25,7 +25,20 @@
     $container.droppable
       accept: '.proto-node'
       hoverClass: "ui-state-highlight"
-      drop: @addNode
+      drop: (e, ui) =>
+        # this seems crazy but we can't get the real drop target from the event so we have to calculate it
+        # we also can't just make the inspector panel eat the drops because the container handler is called first
+        $panel = $ '.inspector-panel-content'
+        panel =
+          width: $panel.width()
+          height: $panel.height()
+          offset: $panel.offset()
+        inPanel = ui.offset.left >= panel.offset.left and
+                  ui.offset.top >= panel.offset.top and
+                  ui.offset.left <= panel.offset.left + panel.width and
+                  ui.offset.top <= panel.offset.top + panel.height
+        if not inPanel
+          @addNode e, ui

     @dropImageHandler = new DropImageHandler
       maxWidth: 100


fix(base-service): handle graphql errors with status code 400 correctly

this behavior changed recently, so this update is needed

--- src/coffee/services/base.coffee
+++ src/coffee/services/base.coffee
@@ -615,12 +615,6 @@

       # TODO: check other possible acceptable codes (304, ...)
       if 200 <= response.statusCode < 300
-        # FIXME: find a better solution
-        if @constructor.name is 'GraphQLService' and not body.data
-          graphqlError = new GraphQLError 'GraphQL error', _.extend responseJson, body,
-            statusCode: 400
-            originalRequest: originalRequest
-          return reject graphqlError

         return resolve _.extend responseJson,
           statusCode: response.statusCode
@@ -644,6 +638,12 @@
             statusCode: response.statusCode
             message: errMsg
             originalRequest: originalRequest
+      # FIXME: find a better solution
+      else if response.statusCode is 400 and @constructor.name is 'GraphQLService' and not body.data
+        graphqlError = new GraphQLError 'GraphQL error', _.extend responseJson, body,
+        statusCode: 400
+        originalRequest: originalRequest
+        return reject graphqlError
       else
         # a SphereError response e.g.: {statusCode: 400, message: 'Oops, something went wrong'}
         errorMessage = body.message or body.error_description or body.error or 'Undefined SPHERE.IO error message'


Refactor whenPresent to use promises.

Refactor whenPresent to use promises.

--- src/bootstrap-elm.coffee
+++ src/bootstrap-elm.coffee
@@ -95,16 +95,17 @@
     reader.readAsText file

-whenPresent = (getElem, onSuccess, onError, attemptsRemaining = 10000) ->
-  elem = getElem()
-
-  if elem?
-    onSuccess elem
-  else if attemptsRemaining
-    requestAnimationFrame ->
-      whenPresent getElem, onSuccess, onError, attemptsRemaining - 1
-  else
-    onError()
+whenPresent = (getElem, attemptsRemaining = 10000) ->
+  new Promise (resolve, reject) ->
+    elem = getElem()
+
+    if elem?
+      resolve elem
+    else if attemptsRemaining
+      requestAnimationFrame ->
+        whenPresent(getElem, attemptsRemaining - 1).then resolve, reject
+    else
+      reject()

 editors = new WeakMap()
 mutationObserverOptions =
@@ -119,10 +120,9 @@
     editor

 setUpEditor = (id, html, onMutate) ->
-  whenPresent (-> document.getElementById id),
-    ((elem) -> getEditorFor(elem, onMutate).writeHtml html, true),
-    ((err) -> console.error "Could not write after 10,000 attempts:", id, html),
-    10000
+  whenPresent((-> document.getElementById id), 10000)
+    .then ((elem) -> getEditorFor(elem, onMutate).writeHtml html, true),
+          ((err)  -> console.error "Could not write after 10,000 attempts:", id, html)

 refreshDocList = -> sync.listDocs().then (docs) ->
   app.ports.listDocs.send docs
@@ -140,8 +140,8 @@
   refreshDocList()

 app.ports.downloadDoc.subscribe ({filename, contentType}) ->
-  whenPresent (-> document.getElementById "document-page"),
-    ((elem) ->
+  whenPresent((-> document.getElementById "document-page"), 10000)
+    .then ((elem) ->
       clone = document.getElementById("document-page").cloneNode true

       for editable in clone.querySelectorAll("[contentEditable=true]")


Bail out early on unexpected errors

Bail out early on unexpected errors

--- src/WebSocket.coffee
+++ src/WebSocket.coffee
@@ -90,6 +90,12 @@
         # Don't ever expect payloads to return a secret
         chai.expect(data.secret, 'Message should not contain secret').to.be.a 'undefined'
         chai.expect(data.payload.secret, 'Payload should not contain secret').to.be.a 'undefined'
+
+        if expects[0].command isnt 'error' and data.command is 'error'
+          # We received an unexpected error, bail out
+          done new Error data.payload
+          return
+
         if allowExtraPackets and not messageMatches data, expects[0]
           # Ignore messages we don't care about in context of the test
           connection.once 'message', listener


finished start and pause methods for drmSimpleSlider

--- app/coffee/drm-simple-slider/drm-simple-slider.coffee
+++ app/coffee/drm-simple-slider/drm-simple-slider.coffee
@@ -21,6 +21,8 @@
             slides = @.slideHolder.find '.drm-simple-slide'
             current = 0
             sliderControls = $('.drm-simple-slider-nav').find 'button'
+            begin = @.startShow()
+            pause = -> drmSimpleSlider.pauseShow(begin)

             ## Initialize

@@ -33,13 +35,14 @@
             else
                 sliderControls.hide()
                 @.slideList.hide()
-                slides.first().show()
+                slides.first().show()

             if drmSimpleSlider.config.animate == 'yes'
-                $(window).on 'load', @.startShow
+                $(window).on 'load', $.proxy(begin)

             sliderControls.on 'click', @.advanceImage
-            @.slideList.on 'click', 'button', @.goToImage
+            @.slideList.on 'click', 'button', @.goToImage
+            @.slideHolder.on 'mouseenter', pause

         getCurrent: ->
             slides = drmSimpleSlider.slideHolder.find '.drm-simple-slide'
@@ -95,8 +98,11 @@
                 start = setInterval ->
                     nextControl.trigger 'click'
                 , drmSimpleSlider.config.play
+            console.log "Starting: #{start}"
+            return start

-            return start
+        pauseShow: (start) ->
+            clearInterval(start, drmSimpleSlider.config,pause)
     }

     drmSimpleSlider.init()


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/array_extensions.coffee
+++ src/array_extensions.coffee
@@ -1,3 +1,5 @@
+
+
 ###*
 Returns a copy of the array without null and undefined values.


Add color functions

Add color functions

grey and alpha

--- framer/Color.coffee
+++ framer/Color.coffee
@@ -153,15 +153,21 @@
 	toString: ->
 		return @toRgbString()

-	transparent: ->
+	alpha: (alpha = 1) ->
 		result = new Color
 			r: @r
 			g: @g
 			b: @b
-			a: 0
+			a: alpha
+
+	transparent: ->
+		@alpha(0)

 	mix: (colorB, fraction, limit = false, model) ->
 		return Color.mix(@, colorB, fraction, limit, model)
+
+	copy: ->
+		return new Color(@)

 	isEqual: (colorB) ->
 		return Color.equal(@, colorB)
@@ -253,6 +259,12 @@
 	@random: (alpha = 1.0) ->
 		c = -> parseInt(Math.random() * 255)
 		return new Color "rgba(#{c()}, #{c()}, #{c()}, #{alpha})"
+
+	@grey: (g = 0.5, alpha = 1) ->
+		g = parseInt(g * 255)
+		return new Color "rgba(#{g}, #{g}, #{g}, #{alpha})"
+
+	@gray: (args...) -> @grey(args...)

 	@toColor: (color) -> return new Color(color)
 	@validColorValue: (color) -> return color instanceof Color or color == null


Layer.on error on missing events

Layer.on error on missing events

--- framer/Layer.coffee
+++ framer/Layer.coffee
@@ -956,10 +956,13 @@
 		@_addListener(eventName, listener)

 	addListener: (eventName, listener) =>
+		throw Error("Layer.on needs an event name") unless eventName
+		throw Error("Layer.on needs an event listener") unless listener
 		super(eventName, listener)
 		@_addListener(eventName, listener)

 	removeListener: (eventName, listener) ->
+		throw Error("Layer.off needs an event name") unless eventName
 		super(eventName, listener)
 		@_removeListener(eventName, listener)


Cleanup

Cleanup

--- framer/Utils.coffee
+++ framer/Utils.coffee
@@ -76,26 +76,18 @@
 # Note: in Framer 3 we try to keep all times in seconds

 # Used by animation engine, needs to be very performant
-
-Utils.getTime = -> Date.now() / 1000
 if window.performance
 	Utils.getTime = -> window.performance.now() / 1000
-
-console.log Utils.getTime()
-
-# Utils.getTime = -> Date.now() / 1000
-
-# This works only in chrome, but we only use it for testing
-# if window.performance
-# 	Utils.getTime = -> performance.now() / 1000
+else
+	Utils.getTime = -> Date.now() / 1000

 Utils.delay = (time, f) ->
-	timer = setTimeout f, time * 1000
+	timer = setTimeout(f, time * 1000)
 	Framer.CurrentContext._delayTimers.push(timer)
 	return timer

 Utils.interval = (time, f) ->
-	timer = setInterval f, time * 1000
+	timer = setInterval(f, time * 1000)
 	Framer.CurrentContext._delayIntervals.push(timer)
 	return timer


Slightly smarter labelLayer

Slightly smarter labelLayer

--- framer/Utils.coffee
+++ framer/Utils.coffee
@@ -184,8 +184,10 @@

 Utils.labelLayer = (layer, text, style={}) ->

+	fontSize = Math.max(Math.min(48, parseInt(layer.height / 3.2)), 14)
+
 	style = _.extend({
-		font: "10px/1em Menlo"
+		font: "#{fontSize}px/1em Menlo"
 		lineHeight: "#{layer.height}px"
 		textAlign: "center"
 		color: "#fff"


should use `compose`

should use `compose`

--- lib/operators.coffee
+++ lib/operators.coffee
@@ -557,7 +557,7 @@

   constructor: ->
     super
-    @target = new Motions.MoveToRelativeLine(@vimState)
+    @compose(new Motions.MoveToRelativeLine(@vimState))

 class ChangeToLastCharacterOfLine extends Change
   @extend()


:art:

:art:

--- lib/operators.coffee
+++ lib/operators.coffee
@@ -125,8 +125,7 @@

   execute: ->
     if _.any @target.select()
-      @editor.replaceSelectedText {}, (text) =>
-        @getNewText(text)
+      @editor.replaceSelectedText {}, @getNewText.bind(this)
     @vimState.activateNormalMode()

 # [TODO] Rename to ToggleCaseAndMoveRight
@@ -206,15 +205,10 @@
     @vimState.mark.set(@input, @editor.getCursorBufferPosition())
     @vimState.activateNormalMode()

-# Increase/Decrease
-# -------------------------
-#
-# It increases or decreases the next number on the line
-#
 class Increase extends Operator
   @extend()
+  complete: true
   step: 1
-  complete: true

   constructor: ->
     super
@@ -244,16 +238,14 @@
   @extend()
   step: -1

-# AdjustIndentation
-# -------------------------
 class Indent extends Operator
   @extend()
   lineWiseAlias: true
   execute: ->
-    @target.select() # FIXME how to respect count of default 1 without passing count
-    {start} = @editor.getSelectedBufferRange()
+    @target.select()
+    startRow = @editor.getSelectedBufferRange().start.row
     @indent()
-    @editor.setCursorBufferPosition([start.row, 0])
+    @editor.setCursorBufferPosition([startRow, 0])
     @editor.moveToFirstCharacterOfLine()
     @vimState.activateNormalMode()


fix scoping bug

fix scoping bug

--- src/beautifiers/jscs.coffee
+++ src/beautifiers/jscs.coffee
@@ -14,7 +14,7 @@

   beautify: (text, language, options) ->
     @verbose("JSCS Fixer language #{language}")
-    return new @Promise((resolve, reject) ->
+    return new @Promise((resolve, reject) =>
       try
         if !checker?
           cliConfig = require 'jscs/lib/cli-config'


Add missing categories group to whitelist

--- src/coffee/import.coffee
+++ src/coffee/import.coffee
@@ -239,6 +239,7 @@
       { type: 'metaTitle', group: 'white' }
       { type: 'metaDescription', group: 'white' }
       { type: 'metaKeywords', group: 'white' }
+      { type: 'categories', group: 'white' }
     ]
     if header.has(CONS.HEADER_PRICES)
       config.push { type: 'prices', group: 'white' }


Make sure showing success dialogs works correctly

--- spec/command-installer-spec.coffee
+++ spec/command-installer-spec.coffee
@@ -41,6 +41,19 @@
     expect(appDelegate.confirm).toHaveBeenCalledWith({
       message: "Failed to install shell commands"
       detailedMessage: "another error"
+    })
+
+  it "shows a success dialog when installing commands interactively succeeds", ->
+    appDelegate = jasmine.createSpyObj("appDelegate", ["confirm"])
+    installer = new CommandInstaller("2.0.2", appDelegate)
+    spyOn(installer, "installAtomCommand").andCallFake (__, callback) -> callback()
+    spyOn(installer, "installApmCommand").andCallFake (__, callback) -> callback()
+
+    installer.installShellCommandsInteractively()
+
+    expect(appDelegate.confirm).toHaveBeenCalledWith({
+      message: "Commands installed."
+      detailedMessage: "The shell commands `atom` and `apm` are installed."
     })

   describe "when using a stable version of atom", ->


Add tests for ignoredRanges in findMatchingOpening/ClosingBracket

--- spec/text-manipulation-spec.coffee
+++ spec/text-manipulation-spec.coffee
@@ -28,7 +28,7 @@
       .toBe('')

   describe 'findMatchingOpeningBracket', ->
-    #             012345678901
+    #             0123456789012
     bufferText = '[1,[1,2,[]]]'
     it 'handles index at end of nested list', ->
       expect(TextManipulation.findMatchingOpeningBracket bufferText, [], 11, false)
@@ -45,6 +45,27 @@
     it 'handles index at start of list contents', ->
       expect(TextManipulation.findMatchingOpeningBracket bufferText, [], 1, false)
        .toEqual({bracketIx: 0, ranges: [  ]})
+
+    #                 01234567890123
+    nestedListText = '[xxx[xxx]xxx]'
+    it 'handles ignoredRange preceding nested list', ->
+      expect(TextManipulation.findMatchingOpeningBracket nestedListText, [[2,3]], 11, false)
+       .toEqual({bracketIx: 0, ranges: [ [1, 2], [3, 4], [9, 11] ]})
+    it 'handles ignoredRange immediately preceding nested list', ->
+      expect(TextManipulation.findMatchingOpeningBracket nestedListText, [[2,4]], 11, false)
+       .toEqual({bracketIx: 0, ranges: [ [1, 2], [9, 11] ]})
+    it 'handles ignoredRange spanning start of nested list', ->
+      expect(TextManipulation.findMatchingOpeningBracket nestedListText, [[2,5]], 11, false)
+       .toEqual(null)
+    it 'handles ignoredRange following nested list', ->
+      expect(TextManipulation.findMatchingOpeningBracket nestedListText, [[10, 11]], 11, false)
+       .toEqual({bracketIx: 0, ranges: [ [1, 4], [9, 10] ]})
+    it 'handles ignoredRange immediately following nested list', ->
+      expect(TextManipulation.findMatchingOpeningBracket nestedListText, [[9,11]], 11, false)
+       .toEqual({bracketIx: 0, ranges: [ [1, 4] ]})
+    it 'handles index inside ignoredRange', ->
+      expect(TextManipulation.findMatchingOpeningBracket nestedListText, [[3, 11]], 10, false)
+       .toEqual({bracketIx: 0, ranges: [ [1, 3] ]})

   describe 'findMatchingClosingBracket', ->
     #             01234567890123
@@ -65,6 +86,27 @@
       expect(TextManipulation.findMatchingClosingBracket bufferText, [], 0, false)
        .toEqual(null)

+    #                 01234567890123
+    nestedListText = '[xxx[xxx]xxx]'
+    it 'handles ignoredRange following nested list', ->
+      expect(TextManipulation.findMatchingClosingBracket nestedListText, [[9, 11]], 2, false)
+       .toEqual({bracketIx: 12, ranges: [ [2, 4], [11, 12] ]})
+    it 'handles ignoredRange immediately following nested list', ->
+      expect(TextManipulation.findMatchingClosingBracket nestedListText, [[10, 11]], 2, false)
+       .toEqual({bracketIx: 12, ranges: [ [2, 4], [9, 10], [11, 12] ]})
+    it 'handles ignoredRange spanning end of nested list', ->
+      expect(TextManipulation.findMatchingClosingBracket nestedListText, [[8, 11]], 2, false)
+       .toEqual(null)
+    it 'handles ignoredRange preceding nested list', ->
+      expect(TextManipulation.findMatchingClosingBracket nestedListText, [[2, 4]], 2, false)
+       .toEqual({bracketIx: 12, ranges: [ [9, 12] ]})
+    it 'handles ignoredRange immediately preceding nested list', ->
+      expect(TextManipulation.findMatchingClosingBracket nestedListText, [[2, 3]], 2, false)
+       .toEqual({bracketIx: 12, ranges: [ [3, 4], [9, 12] ]})
+    it 'handles index inside ignoredRange', ->
+      expect(TextManipulation.findMatchingClosingBracket nestedListText, [[2, 10]], 3, false)
+       .toEqual({bracketIx: 12, ranges: [ [10, 12] ]})
+
   describe 'getEnclosingList', ->
     #             01234567890123
     bufferText = '[1,(a,b),2]'
@@ -197,7 +239,7 @@
         .toEqual(7)
     it 'handles index immediately after ignore', ->
       expect(TextManipulation.backwardSkipIgnored ignoreRanges, 6)
-        .toEqual(6)
+        .toEqual(4)
     it 'handles index inside ignore', ->
       expect(TextManipulation.backwardSkipIgnored ignoreRanges, 5)
         .toEqual(4)


Warn Electron users about OTA update

Warn Electron users about OTA update

--- lib/spark-dev.coffee
+++ lib/spark-dev.coffee
@@ -623,29 +623,51 @@
       if !firmware
         firmware = files[0]

-      process.chdir rootPath
-      firmware = path.relative rootPath, firmware
-
-      @statusView.setStatus 'Flashing via the cloud...'
-
-      @flashCorePromise = @spark.flashCore @SettingsHelper.getLocal('current_core'), [firmware]
-      @flashCorePromise.done (e) =>
-        @statusView.setStatus e.status + '...'
-        @statusView.clearAfter 5000
-
-        if atom.config.get 'spark-dev.deleteFirmwareAfterFlash'
-          if fs.existsSync firmware
-            fs.unlink firmware
-
-        @flashCorePromise = null
-      , (e) =>
-        @requestErrorHandler e
-        if e.code == 'ECONNRESET'
-          @statusView.setStatus 'Device seems to be offline', 'error'
-        else
-          @statusView.setStatus e.message, 'error'
-        @statusView.clearAfter 5000
-        @flashCorePromise = null
+      flashDevice = =>
+        process.chdir rootPath
+        firmware = path.relative rootPath, firmware
+
+        @statusView.setStatus 'Flashing via the cloud...'
+
+        @flashCorePromise = @spark.flashCore @SettingsHelper.getLocal('current_core'), [firmware]
+        @flashCorePromise.done (e) =>
+          if e.ok
+            @statusView.setStatus e.status + '...'
+            @statusView.clearAfter 5000
+
+            if atom.config.get 'spark-dev.deleteFirmwareAfterFlash'
+              if fs.existsSync firmware
+                fs.unlink firmware
+          else
+            error = e.errors?[0]?.error
+            @statusView.setStatus error, 'error'
+            @statusView.clearAfter 5000
+
+          @flashCorePromise = null
+        , (e) =>
+          @requestErrorHandler e
+          if e.code == 'ECONNRESET'
+            @statusView.setStatus 'Device seems to be offline', 'error'
+          else
+            @statusView.setStatus e.message, 'error'
+          @statusView.clearAfter 5000
+          @flashCorePromise = null
+
+      if @SettingsHelper.getLocal('current_core_platform') == 10
+        atom.confirm
+          message: 'Flashing over cellular'
+          detailedMessage: 'You\'re trying to flash your app to ' +
+            @SettingsHelper.getLocal('current_core_name') +
+            ' over cellular. This will use at least a few KB from your
+            data plan. ' +
+            'Instead it\'s recommended to flash
+            it via USB.'
+          buttons:
+            'Cancel': ->
+            'Flash OTA anyway': =>
+              flashDevice()
+      else
+        flashDevice()
     else
       # If multiple firmware files, show select
       @initView 'select-firmware'


Fixed donation text

Fixed donation text

--- coffee/_sidebar.coffee
+++ coffee/_sidebar.coffee
@@ -41,7 +41,7 @@
     "

-        Dontae Us
+        Donate Us

     "
   )


Fix really bad degradation for hover preciseness #406

--- lib/vim-state.coffee
+++ lib/vim-state.coffee
@@ -73,7 +73,9 @@
       @highlightSearch.refresh()
     @subscriptions.add @editor.onDidStopChanging(refreshHighlightSearch)

+    # [FIXME] #406 find better way to fix, this is very imperative, result of just worked through try&error.
     @subscriptions.add @editor.observeSelections (selection) =>
+      return if selection.isEmpty()
       return if @operationStack.isProcessing()
       unless swrap(selection).hasProperties()
         swrap(selection).saveProperties()


cosmetic change

cosmetic change

--- lib/vim-state.coffee
+++ lib/vim-state.coffee
@@ -283,13 +283,13 @@

   resetNormalMode: ({userInvocation}={}) ->
     if userInvocation ? false
-      if @editor.hasMultipleCursors()
-        @clearSelections()
-
-      else if @hasPersistentSelections() and @getConfig('clearPersistentSelectionOnResetNormalMode')
-        @clearPersistentSelections()
-      else if @occurrenceManager.hasPatterns()
-        @occurrenceManager.resetPatterns()
+      switch
+        when @editor.hasMultipleCursors()
+          @clearSelections()
+        when @hasPersistentSelections() and @getConfig('clearPersistentSelectionOnResetNormalMode')
+          @clearPersistentSelections()
+        when @occurrenceManager.hasPatterns()
+          @occurrenceManager.resetPatterns()

       if @getConfig('clearHighlightSearchOnResetNormalMode')
         @globalState.set('highlightSearchPattern', null)


:art: addClass and removeClass take multiple cssClass

--- lib/vim-state.coffee
+++ lib/vim-state.coffee
@@ -97,8 +97,7 @@
     if @editor.isAlive()
       @activate('normal') # reset to base mdoe.
       @editorElement.component?.setInputEnabled(true)
-      @removeClass 'vim-mode'
-      @removeClass 'normal-mode'
+      @removeClass 'vim-mode', 'normal-mode'
     # @editorElement.removeEventListener 'mouseup', @checkSelections
     @editor = null
     @editorElement = null
@@ -278,7 +277,6 @@
     @hover.reset()
     @operationStack.clear()

-
   # Miscellaneous commands
   # -------------------------
   undo: ->
@@ -331,11 +329,11 @@
       cursor.setVisible(true) unless cursor.isVisible()
       @updateClassCond cursor.selection.isReversed(), 'reversed'

-  addClass:    (klass) ->
-    @editorElement.classList.add(klass)
-
-  removeClass: (klass) ->
-    @editorElement.classListr.remove(klass)
+  addClass: (klass...) ->
+    @editorElement.classList.add(klass...)
+
+  removeClass: (klass...) ->
+    @editorElement.classList.remove(klass...)

   updateClassCond: (condition, klass) ->
     action = (if condition then 'add' else 'remove')


:lipstick:

:lipstick:

--- lib/vim-state.coffee
+++ lib/vim-state.coffee
@@ -86,35 +86,35 @@

     @registerOperationCommands
       # Operator
-      'activate-insert-mode': => new Operators.Insert(this)
-      'activate-replace-mode': => new Operators.ReplaceMode(this)
-      'substitute': => [new Operators.Change(this), new Motions.MoveRight(@editor, this)]
-      'substitute-line': => new Operators.SubstituteLine(this)
-      'insert-after': => new Operators.InsertAfter(this)
-      'insert-after-end-of-line': => new Operators.InsertAfterEndOfLine(this)
-      'insert-at-beginning-of-line': => new Operators.InsertAtBeginningOfLine(this)
-      'insert-above-with-newline': => new Operators.InsertAboveWithNewline(this)
-      'insert-below-with-newline': => new Operators.InsertBelowWithNewline(this)
-      'delete': => @linewiseAliasedOperator(Operators.Delete)
-      'change': => @linewiseAliasedOperator(Operators.Change)
+      'activate-insert-mode':             => new Operators.Insert(this)
+      'activate-replace-mode':            => new Operators.ReplaceMode(this)
+      'substitute':                       => [new Operators.Change(this), new Motions.MoveRight(@editor, this)]
+      'substitute-line':                  => new Operators.SubstituteLine(this)
+      'insert-after':                     => new Operators.InsertAfter(this)
+      'insert-after-end-of-line':         => new Operators.InsertAfterEndOfLine(this)
+      'insert-at-beginning-of-line':      => new Operators.InsertAtBeginningOfLine(this)
+      'insert-above-with-newline':        => new Operators.InsertAboveWithNewline(this)
+      'insert-below-with-newline':        => new Operators.InsertBelowWithNewline(this)
+      'delete':                           => @linewiseAliasedOperator(Operators.Delete)
+      'change':                           => @linewiseAliasedOperator(Operators.Change)
       'change-to-last-character-of-line': => [new Operators.Change(this), new Motions.MoveToLastCharacterOfLine(@editor, this)]
-      'delete-right': => [new Operators.Delete(this), new Motions.MoveRight(@editor, this)]
-      'delete-left': => [new Operators.Delete(this), new Motions.MoveLeft(@editor, this)]
+      'delete-right':                     => [new Operators.Delete(this), new Motions.MoveRight(@editor, this)]
+      'delete-left':                      => [new Operators.Delete(this), new Motions.MoveLeft(@editor, this)]
       'delete-to-last-character-of-line': => [new Operators.Delete(this), new Motions.MoveToLastCharacterOfLine(@editor, this)]
-      'toggle-case': => new Operators.ToggleCase(this)
-      'upper-case': => new Operators.UpperCase(this)
-      'lower-case': => new Operators.LowerCase(this)
-      'toggle-case-now': => new Operators.ToggleCase(this, complete: true)
-      'yank': => @linewiseAliasedOperator(Operators.Yank)
-      'yank-line': => [new Operators.Yank(this), new Motions.MoveToRelativeLine(@editor, this)]
-      'put-before': => new Operators.Put(this, location: 'before')
-      'put-after': => new Operators.Put(this, location: 'after')
-      'join': => new Operators.Join(this)
-      'indent': => @linewiseAliasedOperator(Operators.Indent)
-      'outdent': => @linewiseAliasedOperator(Operators.Outdent)
-      'auto-indent': => @linewiseAliasedOperator(Operators.Autoindent)
-      'increase': => new Operators.Increase(this)
-      'decrease': => new Operators.Decrease(this)
+      'toggle-case':                      => new Operators.ToggleCase(this)
+      'upper-case':                       => new Operators.UpperCase(this)
+      'lower-case':                       => new Operators.LowerCase(this)
+      'toggle-case-now':                  => new Operators.ToggleCase(this, complete: true)
+      'yank':                             => @linewiseAliasedOperator(Operators.Yank)
+      'yank-line':                        => [new Operators.Yank(this), new Motions.MoveToRelativeLine(@editor, this)]
+      'put-before':                       => new Operators.Put(this, location: 'before')
+      'put-after':                        => new Operators.Put(this, location: 'after')
+      'join':                             => new Operators.Join(this)
+      'indent':                           => @linewiseAliasedOperator(Operators.Indent)
+      'outdent':                          => @linewiseAliasedOperator(Operators.Outdent)
+      'auto-indent':                      => @linewiseAliasedOperator(Operators.Autoindent)
+      'increase':                         => new Operators.Increase(this)
+      'decrease':                         => new Operators.Decrease(this)

       'move-left': => new Motions.MoveLeft(@editor, this)
       'move-up': => new Motions.MoveUp(@editor, this)


Insert backward-compatibility check for Atom versions < 0.200

--- lib/vim-state.coffee
+++ lib/vim-state.coffee
@@ -547,4 +547,9 @@
 # Package authors - copy and paste this code at your own risk.
 getChangesSinceCheckpoint = (buffer, checkpoint) ->
   {history} = buffer
-  history.undoStack.slice(history.getCheckpointIndex(checkpoint))
+
+  # TODO: remove this conditional once Atom 0.200 has been out for a while.
+  if index = history.getCheckpointIndex?(checkpoint)
+    history.undoStack.slice(index)
+  else
+    []


social: set default limits for sidebar items

--- workers/social/lib/social/cache/socialapi.coffee
+++ workers/social/lib/social/cache/socialapi.coffee
@@ -18,14 +18,17 @@

     SocialChannel.fetchPopularTopics client, options, cb

+  defaultOptions =
+    limit: 5
+
   fetchFollowedChannels = (bongoModels, client, cb)->
-    SocialChannel.fetchFollowedChannels client, { limit: 5 }, cb
+    SocialChannel.fetchFollowedChannels client, defaultOptions, cb

   fetchPinnedMessages = (bongoModels, client, cb)->
-    SocialChannel.fetchPinnedMessages client, {}, cb
+    SocialChannel.fetchPinnedMessages client, defaultOptions, cb

   fetchPrivateMessages = (bongoModels, client, cb)->
-    SocialMessage.fetchPrivateMessages client, {}, cb
+    SocialMessage.fetchPrivateMessages client, defaultOptions, cb

   fetchGroupActivities = (bongoModels, client, cb)->
     SocialChannel.fetchGroupActivities client, {}, cb


Disable setting page title for now, not working as expected

--- assets/javascripts/application.js.coffee
+++ assets/javascripts/application.js.coffee
@@ -48,6 +48,7 @@
   }

   setPageTitle: (title) =>
+    return
     title = @base_title + ' - ' + title if title
     title ?= @base_title
     document.title = title


fix escaping bug on windows when opening REPL

--- lib/connection/terminal.coffee
+++ lib/connection/terminal.coffee
@@ -16,6 +16,8 @@
       when "darwin"
         @exec "osascript -e 'tell application \"Terminal\" to activate'"
         @exec "osascript -e 'tell application \"Terminal\" to do script \"#{@escape(sh)}\"'"
+      when "win32"
+        @exec "#{@terminal()} \"#{sh}\""
       else
         @exec "#{@terminal()} \"#{@escape(sh)}\""


limit rate of content fetching a bit more

--- gist-content.coffee
+++ gist-content.coffee
@@ -106,7 +106,9 @@
         #console.log "writing body", body
         fs.writeFile filePath, body, ->
           console.log gist.id, fileName
-          fileCb()
+          setTimeout ->
+            fileCb()
+          , Math.random() * 150 + 150
     else
       fileCb()
   , () ->
@@ -142,5 +144,5 @@
     async.each gistMeta, gistFetcher, done
     #async.each gistMeta, gistCloner, done
   else
-    async.eachLimit gistMeta, 50, gistFetcher, done
+    async.eachLimit gistMeta, 10, gistFetcher, done
     #async.eachLimit gistMeta, 10, gistCloner, done


Remove unknown grammar key for support by Linguist

--- grammars/fstar.cson
+++ grammars/fstar.cson
@@ -3,10 +3,6 @@
 'name': 'F*'

 'fileTypes': ['fst', 'fsi', 'fs', 'fs7', 'fsti']
-
-'editor':
-  'commentStart' : '\\(\\*'
-  'commentEnd'   : '\\*\\)'

 'patterns': [
   {


Config: remove dump after importing the data

--- config/main.dev.coffee
+++ config/main.dev.coffee
@@ -389,7 +389,7 @@
         echo '#---> CREATING VANILLA KODING DB @gokmen <---#'
         tar jxvf #{projectRoot}/install/docker-mongo/default-db-dump.tar.bz2
         mongorestore -h#{boot2dockerbox} -dkoding dump/koding
-        rm -rf #{projectRoot}/dump
+        rm -rf ./dump

         echo '#---> UPDATING MONGO DATABASE ACCORDING TO LATEST CHANGES IN CODE (UPDATE PERMISSIONS @chris) <---#'
         cd #{projectRoot}


JSAlertView update

JSAlertView update

--- JSKit/03_JSAlertView.coffee
+++ JSKit/03_JSAlertView.coffee
@@ -37,12 +37,15 @@
 		if ($(@_viewSelector+"_form").length)
 			$(@_viewSelector+"_form").remove()
 		$(@_viewSelector).append(@_tag)
+		when "JSAlertViewStylePlainTextInput"
+			@cancel = true
+		else
+			@cancel = false
 		buttonhash =
 			OK:=>
 				if (@delegate? && typeof @delegate.clickedButtonAtIndex == "function")
 					switch @_style
 						when "JSAlertViewStylePlainTextInput"
-							@cancel = true
 							arr = []
 							for i in [0...@_param.length]
 								t = $(@_viewSelector+"_textfield_"+i).val()
@@ -50,7 +53,6 @@
 							text = JSON.stringify(arr)
 							@delegate.clickedButtonAtIndex(text, @_self)
 						when "JSAlertViewStyleDefault"
-							@cancel = false
 							@delegate.clickedButtonAtIndex(1, @_self)
 				$(@_viewSelector+"_form").dialog("close")
 				@_self.removeFromSuperview()


mixpanel: use nickname to track, not firstname

--- client/app/MainApp/mixpanel.coffee
+++ client/app/MainApp/mixpanel.coffee
@@ -99,9 +99,9 @@
     if KD.isLoggedIn()
       user = KD.whoami()
       email = user.fetchEmail (err, email)->
-        mixpanel.identify user.profile.firstName
+        mixpanel.identify user.profile.nickname
         mixpanel.people.set
-          "$username"   : user.profile.firstName
+          "$username"   : user.profile.nickname
           "name"        : "#{user.profile.firstName} #{user.profile.lastName}"
           "$joinDate"   : user.meta.createdAt
           "$email"      : email


client: modify environment and machine settings routes

--- client/app/lib/routehandler.coffee
+++ client/app/lib/routehandler.coffee
@@ -107,13 +107,12 @@
       global.location.href = path

     when 'stacks'
-      { stack } = info.params
-      # TODO: fetch/get stack by slug and send it to modal
-      # new EnvironmentsModal selected: @getOption 'stack'
-      new EnvironmentsModal()
+      { stackId } = info.params
+      new EnvironmentsModal selected: stackId

     when 'machine-settings'
-      { slug } = info.params
+      { slug, state } = info.params
       environmentDataProvider.fetchMachineBySlug slug, (machine) ->
+        modal = new MachineSettingsModal {}, new Machine { machine: remote.revive machine }
+        modal.showPaneByName state  if state

-        new MachineSettingsModal {}, new Machine { machine: remote.revive machine }


Clean up geyser rendering KO binding.

Clean up geyser rendering KO binding.

--- client/src/scripts/knockout.coffee
+++ client/src/scripts/knockout.coffee
@@ -50,11 +50,13 @@
   update: (element, valueAccessor, allBindings, viewModel, bindingContext) ->
     if data = ko.unwrap valueAccessor()
       $element = $ element
-      markup = data.markup or data
-      $element.html geyser.render markup
-      if data.behaviors
-        for behavior in data.behaviors
-          behavior $element
+      if data.markup
+        $element.html geyser.render data.markup
+        if data.behaviors
+          for behavior in data.behaviors
+            behavior $element
+      else
+        $element.html geyser.render data
     else
       $(element).text 'Loading. Please wait..'
     return