Add a couple more test cases for Sync.

Add a couple more test cases for Sync.

--- tests/unit_tests/settings_test.coffee
+++ tests/unit_tests/settings_test.coffee
@@ -78,3 +78,16 @@
     chrome.storage.sync.set { scrollStepSize: JSON.stringify(40) }
     chrome.storage.sync.remove 'scrollStepSize'
     assert.isFalse Settings.has 'scrollStepSize'
+
+  should "trigger a postUpdateHook", ->
+    message = "Hello World"
+    # Install a bogus update hook for an existing setting.
+    Settings.postUpdateHooks['scrollStepSize'] = (value) -> Sync.message = value
+    chrome.storage.sync.set { scrollStepSize: JSON.stringify(message) }
+    # Was the update hook triggered?
+    assert.equal message, Sync.message
+
+  should "sync a key which is not a setting", ->
+    # There is nothing to test, here.  It's purpose is just to ensure that, should additional settings be
+    # added in future, then the Sync won't cause unexpected crashes.
+    chrome.storage.sync.set { notASetting: JSON.stringify("notAUsefullValue") }


Fix mocha complaining about returning Promise-like object

--- services/web/test/acceptance/coffee/ProjectStructureMongoLockTest.coffee
+++ services/web/test/acceptance/coffee/ProjectStructureMongoLockTest.coffee
@@ -34,6 +34,7 @@
 					namespace = ProjectEntityMongoUpdateHandler.LOCK_NAMESPACE
 					@lock_key = "lock:web:#{namespace}:#{project._id}"
 					LockManager._getLock @lock_key, namespace, done
+			return

 		after (done) ->
 			LockManager._releaseLock @lock_key, done


Fix metrics to match API changes.

Fix metrics to match API changes.

--- src/ext/components/model-output.coffee
+++ src/ext/components/model-output.coffee
@@ -370,24 +370,24 @@
                   )
                   g.from table
                 )
-          else if table.schema['training_MSE']
-            if table.schema['validation_MSE']
+          else if table.schema['training_mse']
+            if table.schema['validation_mse']
               renderPlot 'Scoring History - MSE', no, _.plot (g) ->
                 g(
                   g.path(
-                    g.position 'epochs', 'training_MSE'
-                    g.strokeColor g.value '#1f77b4'
-                  )
-                  g.path(
-                    g.position 'epochs', 'validation_MSE'
+                    g.position 'epochs', 'training_mse'
+                    g.strokeColor g.value '#1f77b4'
+                  )
+                  g.path(
+                    g.position 'epochs', 'validation_mse'
                     g.strokeColor g.value '#ff7f0e'
                   )
                   g.point(
-                    g.position 'epochs', 'training_MSE'
-                    g.strokeColor g.value '#1f77b4'
-                  )
-                  g.point(
-                    g.position 'epochs', 'validation_MSE'
+                    g.position 'epochs', 'training_mse'
+                    g.strokeColor g.value '#1f77b4'
+                  )
+                  g.point(
+                    g.position 'epochs', 'validation_mse'
                     g.strokeColor g.value '#ff7f0e'
                   )
                   g.from table
@@ -396,11 +396,11 @@
               renderPlot 'Scoring History - MSE', no, _.plot (g) ->
                 g(
                   g.path(
-                    g.position 'epochs', 'training_MSE'
-                    g.strokeColor g.value '#1f77b4'
-                  )
-                  g.point(
-                    g.position 'epochs', 'training_MSE'
+                    g.position 'epochs', 'training_mse'
+                    g.strokeColor g.value '#1f77b4'
+                  )
+                  g.point(
+                    g.position 'epochs', 'training_mse'
                     g.strokeColor g.value '#1f77b4'
                   )
                   g.from table


Attempt At Fixing #50

Attempt At Fixing #50

--- lib/gobuild.coffee
+++ lib/gobuild.coffee
@@ -50,6 +50,7 @@
       @emit @name + '-complete', editorView, saving
       callback(null)
       return
+    splitgopath = go.splitgopath()
     env = @dispatch.env()
     env['GOPATH'] = gopath
     cwd = path.dirname(buffer.getPath())
@@ -75,7 +76,7 @@
     cmd = go.executable
     done = (exitcode, stdout, stderr, messages) =>
       console.log @name + ' - stdout: ' + stdout if stdout? and stdout.trim() isnt ''
-      messages = @mapMessages(editorView, stderr, cwd) if stderr? and stderr isnt ''
+      messages = @mapMessages(editorView, stderr, cwd, splitgopath) if stderr? and stderr isnt ''
       pattern = cwd + '/*' + output
       glob pattern, {mark: false, sync:true}, (er, files) ->
         for file in files
@@ -95,37 +96,50 @@
       callback(null, messages)
     @dispatch.executor.exec(cmd, cwd, env, done, args)

-  mapMessages: (editorView, data, cwd) ->
-    pattern = /^(.*?):(\d*?):((\d*?):)?\s((.*)?((\n\t.*)+)?)/img
+  mapMessages: (editorView, data, cwd, splitgopath) =>
+    pattern = /^((#)\s(.*)?)|((.*?):(\d*?):((\d*?):)?\s((.*)?((\n\t.*)+)?))/img
     messages = []
-    extract = (matchLine) ->
+    pkg = ''
+    extract = (matchLine) =>
       return unless matchLine?
-      file = null
-      if matchLine[1]? and matchLine[1] isnt ''
-        if matchLine[1].substring(0, 1) is '/' or matchLine[1].substring(1, 2) is ':\\'
-          file = matchLine[1]
-        else
-          file = path.join(cwd, matchLine[1])
+      if matchLine[2]? and matchLine[2] is '#'
+        # Found A Package Indicator
+        pkg = @absolutePathForPackage(matchLine[3], splitgopath)
+      else
+        file = null
+        if matchLine[5]? and matchLine[5] isnt ''
+          if matchLine[5].substring(0, 1) is '/' or matchLine[5].substring(1, 2) is ':\\'
+            file = matchLine[5]
+          else if pkg? and pkg isnt ''
+            file = path.join(pkg, matchLine[5])
+          else
+            file = path.join(cwd, matchLine[5])

-      message = switch
-        when matchLine[4]?
-          file: file
-          line: matchLine[2]
-          column: matchLine[4]
-          msg: matchLine[5]
-          type: 'error'
-          source: 'syntaxcheck'
-        else
-          file: file
-          line: matchLine[2]
-          column: false
-          msg: matchLine[5]
-          type: 'error'
-          source: 'syntaxcheck'
-      messages.push message
+        message = switch
+          when matchLine[4]?
+            file: file
+            line: matchLine[6]
+            column: matchLine[8]
+            msg: matchLine[9]
+            type: 'error'
+            source: 'syntaxcheck'
+          else
+            file: file
+            line: matchLine[6]
+            column: false
+            msg: matchLine[9]
+            type: 'error'
+            source: 'syntaxcheck'
+        messages.push message
     loop
       match = pattern.exec(data)
       extract(match)
       break unless match?
     @emit @name + '-messages', editorView, messages
     return messages
+
+  absolutePathForPackage: (pkg, splitgopath) =>
+    for gopath in splitgopath
+      combinedpath = path.join(gopath, 'src', pkg)
+      return combinedpath if fs.existsSync(combinedpath)
+    null


Slight refactoring and slight optmization

--- lib/one-tab.coffee
+++ lib/one-tab.coffee
@@ -12,26 +12,28 @@

     initPane: (pane) ->
         subscription = new CompositeDisposable
-
         subscription.add pane.onDidDestroy ->
             subscription.dispose()

         subscription.add pane.onDidAddItem =>
-            @handlePaneItemEvent pane
+            @updateTabBarVisibility pane

         subscription.add pane.onDidRemoveItem =>
-            @handlePaneItemEvent pane
+            @updateTabBarVisibility pane

-        @handlePaneItemEvent pane
+        @updateTabBarVisibility pane

-    handlePaneItemEvent: (pane) ->
+    updateTabBarVisibility: (pane) ->
         paneView = atom.views.getView pane
         tabView = paneView.querySelector '.tab-bar'

-        if pane.getItems().length == 1
-            tabView.setAttribute 'one-tab', tabView.style.height
-            tabView.style.height = "1px"
+        if pane.getItems().length is 1
+            tabView.setAttribute 'one-tab-height', tabView.style.height
+            tabView.style.height = '1px'
         else
-            tabView.style.height = tabView.getAttribute 'one-tab'
+            oldValue = tabView.getAttribute 'one-tab-height'
+            if oldValue isnt null
+                tabView.style.height = oldValue
+                tabView.removeAttribute 'one-tab-height'

 module.exports = new OneTab()


fixes showing public address or name when adding a new address

--- app/assets/javascripts/backbone/apps/addresses/index/index_view.js.coffee
+++ app/assets/javascripts/backbone/apps/addresses/index/index_view.js.coffee
@@ -398,9 +398,11 @@
     _clickSave: (event) ->
       event.preventDefault()
       balance = @ui.balance.text()
+      name = _.str.trim @ui.inputName.val()
       @model.set
         balance: balance.slice(0, _.indexOf(balance, ' ')).replace(/,/g, '')
-        name: _.str.trim(@ui.inputName.val())
+        name: name
+        display_name: name or @model.get('public_address')

       @collection.create @model.attributes,
         success: (model, response, options) =>


Refactoring

Refactoring

--- app/assets/javascripts/mixingpanel/source.js.coffee
+++ app/assets/javascripts/mixingpanel/source.js.coffee
@@ -61,7 +61,7 @@
   append: ->
     value = @getValue()
     @writeReferenceTouch(value)
-    if @registerSource() && value != undefined
+    if @registerSource() and value?
       @writeFirstTouch(value)
       @writeLastTouch(value)
       @writeSource(value)


add missing =

add missing =

--- app/assets/javascripts/socializer/activities.coffee
+++ app/assets/javascripts/socializer/activities.coffee
@@ -74,5 +74,5 @@
     $("[data-behavior~=tooltip-on-click]").each ->
       addTooltipSupport $(@)

-  if controllerName == "activities" || controllerName = "shares"
+  if controllerName == "activities" || controllerName == "shares"
     addTimeAgoSupport()


Added more paragraph selectors

Added more paragraph selectors

--- app/js/jail_iframe/classes/paragraph_buttons.coffee
+++ app/js/jail_iframe/classes/paragraph_buttons.coffee
@@ -25,7 +25,8 @@
     setTimeout (=> @_addParagraphButtonsBatch(elementsLeft)), 200

   _paragraphSelectors: ->
-    ['p', 'h2', 'h3', 'h4', 'h5', 'h6', 'li', 'dd', 'dt']
+    ['p', 'h2', 'h3', 'h4', 'h5', 'h6', 'li', 'dd', 'dt',
+     '.paragraph', '.para', '.par']

   _prefixedParagraphSelectors: (prefix) ->
     (prefix + ' ' + selector for selector in @_paragraphSelectors())


hide image view if no image is in embedly

--- client/app/Applications/Activity.kdapplication/views/embedboxviews.coffee
+++ client/app/Applications/Activity.kdapplication/views/embedboxviews.coffee
@@ -87,10 +87,13 @@
   constructor:(options={}, data)->
     super options, data

-    @embedImage = new EmbedBoxLinkViewImage
-      cssClass : 'preview_image'
-      delegate : this
-    ,data
+    if data.link_embed?.images?[0]?
+      @embedImage = new EmbedBoxLinkViewImage
+        cssClass : 'preview_image'
+        delegate : this
+      ,data
+    else
+      @embedImage = new KDCustomHTMLView 'hidden'

     @embedText = new EmbedBoxLinkViewText
       cssClass  : 'preview_text'


Added html escaping to the chart tooltip.

--- community/server/src/main/coffeescript/neo4j/webadmin/ui/LineChart.coffee
+++ community/server/src/main/coffeescript/neo4j/webadmin/ui/LineChart.coffee
@@ -21,8 +21,10 @@
 define(
   ['lib/DateFormat'
    'neo4j/webadmin/ui/Tooltip'
+   'ribcage/security/HtmlEscaper'
    'lib/jquery.flot','lib/backbone']
-  (DateFormat, Tooltip) ->
+  (DateFormat, Tooltip,HtmlEscaper) ->
+
     class LineChart

       timezoneOffset = (new Date()).getTimezoneOffset()  * 60 * 1000
@@ -49,6 +51,8 @@
       constructor : (el) ->
         @el = $(el)
         @settings = @defaultSettings
+
+        @htmlEscaper = new HtmlEscaper

         @tooltip = new Tooltip({ closeButton : false })

@@ -62,7 +66,7 @@
             x = @settings.tooltipXFormatter(item.datapoint[0])
             y = @settings.tooltipYFormatter(item.datapoint[1])

-            @tooltip.show("" + item.series.label + "" + y + "" + x + "", [item.pageX, item.pageY])
+            @tooltip.show("" + @htmlEscaper.escape(item.series.label) + "" + @htmlEscaper.escape(y) + "" + @htmlEscaper.escape(x) + "", [item.pageX, item.pageY])

         else
           @tooltip.hide()


Flux: Environment: Handle accept invitation state and update related machine store.

--- client/app/lib/flux/environment/stores/machinesstore.coffee
+++ client/app/lib/flux/environment/stores/machinesstore.coffee
@@ -13,6 +13,7 @@

     @on actions.LOAD_USER_ENVIRONMENT_SUCCESS, @load
     @on actions.MACHINE_UPDATED, @updateMachine
+    @on actions.INVITATION_ACCEPTED, @acceptInvitation

   load: (machines, { own, shared, collaboration }) ->
@@ -37,4 +38,9 @@
       if machine
         machine_ = toImmutable machine

-      machines.set id, machine_+      machines.set id, machine_
+
+
+  acceptInvitation: (machines, id ) ->
+
+    machines.setIn [id, 'isApproved'], yes


environment/flux: make sure sharedUsers are persisted

--- client/app/lib/flux/environment/stores/machinesstore.coffee
+++ client/app/lib/flux/environment/stores/machinesstore.coffee
@@ -18,6 +18,8 @@
     @on actions.SET_MACHINE_ALWAYS_ON_SUCCESS, @setAlwaysOnSuccess
     @on actions.SET_MACHINE_ALWAYS_ON_FAIL, @setAlwaysOnFail
     @on actions.LOAD_MACHINE_SHARED_USERS, @loadSharedUsers
+    @on actions.SHARED_VM_INVITATION_REJECTED, @removeSharedUser
+

   load: (machines, { own, shared, collaboration }) ->
@@ -27,7 +29,14 @@
     machines.withMutations (machines) ->
       envData.forEach ({ machine, workspaces }) ->
         machine.hasOldOwner = machine.meta?.oldOwner?
-        machines.set machine._id, toImmutable machine
+
+        _machine = toImmutable machine
+
+        if m = machines.get(machine._id)
+          if sharedUsers = m.get 'sharedUsers'
+            _machine = _machine.set 'sharedUsers', sharedUsers
+
+        machines.set machine._id, _machine

   updateMachine: (machines, { id, event, machine }) ->


Comment out test for now

Comment out test for now

--- client/apps/edit/components/content2/test/index.test.coffee
+++ client/apps/edit/components/content2/test/index.test.coffee
@@ -1,89 +1,93 @@
-_ = require 'underscore'
-benv = require 'benv'
-sinon = require 'sinon'
-{ resolve } = require 'path'
-moment = require 'moment'
-fixtures = require '../../../../../../test/helpers/fixtures.coffee'
-Article = require '../../../../../models/article.coffee'
-React = require 'react'
-ReactDOM = require 'react-dom'
-ReactTestUtils = require 'react-addons-test-utils'
-r =
-  find: ReactTestUtils.findRenderedDOMComponentWithClass
-  simulate: ReactTestUtils.Simulate
+# _ = require 'underscore'
+# benv = require 'benv'
+# sinon = require 'sinon'
+# { resolve } = require 'path'
+# moment = require 'moment'
+# fixtures = require '../../../../../../test/helpers/fixtures.coffee'
+# Article = require '../../../../../models/article.coffee'
+# React = require 'react'
+# ReactDOM = require 'react-dom'
+# ReactTestUtils = require 'react-addons-test-utils'
+# { reducers, initialState } = require '../../../../../reducers'
+# { createReduxStore } = require '../../../../../lib/createReduxStore'
+# r =
+#   find: ReactTestUtils.findRenderedDOMComponentWithClass
+#   simulate: ReactTestUtils.Simulate

-describe 'EditContent', ->
+# describe 'EditContent', ->

-  beforeEach (done) ->
-    benv.setup =>
-      benv.expose $: benv.require 'jquery'
-      global.HTMLElement = () => {}
-      window.matchMedia = sinon.stub().returns(
-        {
-          matches: false
-          addListener: sinon.stub()
-          removeListener: sinon.stub()
-        }
-      )
-      @EditContent = benv.require resolve(__dirname, '../index.coffee')
-      HeroSection = benv.require resolve(__dirname, '../sections/hero/index.jsx')
-      SectionList = @SectionList = sinon.stub()
-      Header = benv.require resolve(__dirname, '../sections/header')
-      @EditContent.__set__ 'HeroSection', HeroSection
-      @EditContent.__set__ 'Header', Header
-      @EditContent.__set__ 'SectionList', SectionList
-      @article = new Article _.extend fixtures().articles,
-        layout: 'classic'
-        author:
-          name: 'Artsy Editorialz'
-          id: '123'
-        published: false
-        published_at: null
-      @props = {
-        article: @article
-        channel: {hasFeature: @hasFeature = sinon.stub().returns(true)}
-      }
-      @component = ReactDOM.render React.createElement(@EditContent, @props), (@$el = $ "

")[0], =>
-      @component.debouncedSave = sinon.stub()
-      done()
+#   beforeEach (done) ->
+#     benv.setup =>
+#       benv.expose $: benv.require 'jquery'
+#       global.HTMLElement = () => {}
+#       window.matchMedia = sinon.stub().returns(
+#         {
+#           matches: false
+#           addListener: sinon.stub()
+#           removeListener: sinon.stub()
+#         }
+#       )
+#       @store = createReduxStore(reducers, initialState)
+#       @EditContent = benv.require resolve(__dirname, '../index.coffee')
+#       HeroSection = benv.require resolve(__dirname, '../sections/hero/index.jsx')
+#       SectionList = @SectionList = sinon.stub()
+#       Header = benv.require resolve(__dirname, '../sections/header')
+#       @EditContent.__set__ 'HeroSection', HeroSection
+#       @EditContent.__set__ 'Header', Header
+#       @EditContent.__set__ 'SectionList', SectionList
+#       @article = new Article _.extend fixtures().articles,
+#         layout: 'classic'
+#         author:
+#           name: 'Artsy Editorialz'
+#           id: '123'
+#         published: false
+#         published_at: null
+#       @props = {
+#         article: @article
+#         channel: {hasFeature: @hasFeature = sinon.stub().returns(true)}
+#         store: @store
+#       }
+#       @component = ReactDOM.render React.createElement(@EditContent, @props), (@$el = $ "

")[0], =>
+#       @component.debouncedSave = sinon.stub()
+#       done()

-  afterEach ->
-    benv.teardown()
+#   afterEach ->
+#     benv.teardown()

-  describe 'Render', ->
+#   describe 'Render', ->

-    it 'renders the hero section if channel hasFeature and layout is classic', ->
-      $(ReactDOM.findDOMNode(@component)).html().should.containEql '
'
+#     it 'renders the hero section if channel hasFeature and layout is classic', ->
+#       $(ReactDOM.findDOMNode(@component)).html().should.containEql '
'

-    it 'does not render the hero section if hasFeature is false', ->
-      @props.channel.hasFeature = sinon.stub().returns(false)
-      @props.article.set 'hero_section', null
-      component = ReactDOM.render React.createElement(@EditContent, @props), (@$el = $ "

")[0], =>
-      $(ReactDOM.findDOMNode(component)).html().should.not.containEql '
'
+#     it 'does not render the hero section if hasFeature is false', ->
+#       @props.channel.hasFeature = sinon.stub().returns(false)
+#       @props.article.set 'hero_section', null
+#       component = ReactDOM.render React.createElement(@EditContent, @props), (@$el = $ "

")[0], =>
+#       $(ReactDOM.findDOMNode(component)).html().should.not.containEql '
'

-    it 'does not render the hero section if layout is standard', ->
-      @props.article.set 'layout', 'standard'
-      component = ReactDOM.render React.createElement(@EditContent, @props), (@$el = $ "

")[0], =>
-      $(ReactDOM.findDOMNode(component)).html().should.not.containEql '
'
+#     it 'does not render the hero section if layout is standard', ->
+#       @props.article.set 'layout', 'standard'
+#       component = ReactDOM.render React.createElement(@EditContent, @props), (@$el = $ "

")[0], =>
+#       $(ReactDOM.findDOMNode(component)).html().should.not.containEql '
'

-    it 'renders the header section', ->
-      $(ReactDOM.findDOMNode(@component)).html().should.containEql '
'
+#     it 'renders the header section', ->
+#       $(ReactDOM.findDOMNode(@component)).html().should.containEql '
'

-    it 'renders the section list', ->
-      @SectionList.called.should.eql true
+#     it 'renders the section list', ->
+#       @SectionList.called.should.eql true

-  describe '#saveArticle', ->
+#   describe '#saveArticle', ->

-    it 'calls saveArticle when a section is changed', ->
-      @component.saveArticle = sinon.stub()
-      @component.props.article.sections.models[1].set 'caption', 'This is a new caption'
-      @component.saveArticle.called.should.eql true
+#     it 'calls saveArticle when a section is changed', ->
+#       @component.saveArticle = sinon.stub()
+#       @component.props.article.sections.models[1].set 'caption', 'This is a new caption'
+#       @component.saveArticle.called.should.eql true

-    it '#saveArticle calls debouncedSave if unpublished', ->
-      @component.saveArticle()
-      @component.debouncedSave.called.should.eql true
+#     it '#saveArticle calls debouncedSave if unpublished', ->
+#       @component.saveArticle()
+#       @component.debouncedSave.called.should.eql true

-    it '#saveArticle does not autosave if published', ->
-      @component.props.article.set 'published', true
-      @component.saveArticle()
-      @component.debouncedSave.called.should.eql false
+#     it '#saveArticle does not autosave if published', ->
+#       @component.props.article.set 'published', true
+#       @component.saveArticle()
+#       @component.debouncedSave.called.should.eql false


don't timeout (for a long time) getting zfs list of local snapshots

--- salvus/storage.coffee
+++ salvus/storage.coffee
@@ -2532,7 +2532,7 @@
                 misc_node.execute_code
                     command     : "sudo"
                     args        : ['zfs', 'list', '-r', '-t', 'snapshot', '-o', 'name', '-s', 'creation', f]
-                    timeout     : 60
+                    timeout     : 3600
                     err_on_exit : true
                     cb          : (err, output) ->
                         if err


minor fix

minor fix

--- src/api/vm.coffee
+++ src/api/vm.coffee
@@ -692,7 +692,7 @@
 exports.export = ({vm, compress}) ->
   compress ?= true
   try
-    VM = @getObject id, ['VM', 'VM-snapshot']
+    VM = @getObject vm, ['VM', 'VM-snapshot']
   catch
     @throw 'NO_SUCH_OBJECT'

@@ -721,7 +721,7 @@
   }
 exports.export.permission = 'admin'
 exports.export.params = {
-  id: { type: 'string' }
+  vm: { type: 'string' }
   compress: { type: 'boolean', optional: true }
 }


don't need checkers anymore

don't need checkers anymore


Fix parser error with socket precedences

--- src/coffee.coffee
+++ src/coffee.coffee
@@ -138,6 +138,12 @@

       if wrappingParen?
         block.currentlyParenWrapped = true
+
+    addSocket = (node, precedence = 0, wrappingParen = null) ->
+      socket = new model.Socket null, precedence
+      addMarkup socket, node, wrappingParen
+
+      mark node

     # ## mark ##
     # The core recursive function for adding the markup associated
@@ -162,8 +168,8 @@
         when 'Op'
           addBlock node, operatorPrecedences[node.operator], colors.VALUE, wrappingParen

-          mark node.first, operatorPrecedences[node.operator]
-          if node.second? then mark node.second, operatorPrecedences[node.operator]
+          addSocket node.first, operatorPrecedences[node.operator]
+          if node.second? then addSocket node.second, operatorPrecedences[node.operator]

         # ### Existence ###
         # The Existence operator works
@@ -171,7 +177,7 @@
         # it has highest precedence.
         when 'Existence'
           addBlock node, 7, colors.VALUE, wrappingParen
-          mark node.expression, 7
+          addSocket node.expression, 7

         # ### In ###
         # The In operator has precedence
@@ -179,8 +185,8 @@
         when 'In'
           addBlock node, 0, colors.VALUE, wrappingParen

-          mark node.object, 0
-          mark node.array, 0
+          addSocket node.object, 0
+          addSocket node.array, 0

         # ### Value ####
         # A Value is not of much use to the ICE
@@ -190,12 +196,9 @@
           mark node.base, precedence

         # ### Literal ###
-        # A Literal is a value
-        # that should be in a editable
-        # textarea.
+        # Pass for literals.
         when 'Literal', 'Bool', 'Undefined', 'Null'
-          socket = new model.Socket null, precedence
-          addMarkup socket, node, wrappingParen
+          0

         # ### Call ###
         # Call blocks are blue and
@@ -207,9 +210,9 @@
           # just some text, parse it. Otherwise,
           # don't even put a text socket in.
           unless node.variable.base?.constructor.name is 'Literal'
-            mark node.variable
-
-          for arg in node.args then mark arg
+            addSocket node.variable
+
+          for arg in node.args then addSocket arg

         # ### Code ###
         # Code is a function definition.
@@ -217,12 +220,12 @@
         # functions can be one-line or indented.
         when 'Code'
           addBlock node, precedence, colors.VALUE, wrappingParen
-          for param in node.params then mark param
+          for param in node.params then addSocket param

           # If it is a one-line function,
           # unwrap the body.
           if getBounds(node.body).end.line is getBounds(node).start.line
-            mark node.body.unwrap()
+            addSocket node.body.unwrap()

           # Otherwise, do not.
           else
@@ -246,21 +249,21 @@
         # but for now both are blue and have equal precedence.
         when 'Assign'
           addBlock node, precedence, colors.COMMAND, wrappingParen
-          mark node.variable; mark node.value
+          addSocket node.variable; addSocket node.value

         # ### For ###
         # A for block has a lot of optional arguments.
         when 'For'
           addBlock node, precedence, colors.CONTROL, wrappingParen
-          if node.index? then mark node.index
-          if node.source? then mark node.source
-          if node.name? then mark node.name
-          if node.from? then mark node.from
+          if node.index? then addSocket node.index
+          if node.source? then addSocket node.source
+          if node.name? then addSocket node.name
+          if node.from? then addSocket node.from

           # If it is a one-line "for",
           # unwrap the body.
           if getBounds(node.body).end.line is getBounds(node).start.line
-            mark node.body.unwrap()
+            addSocket node.body.unwrap()

           # Otherwise, do not.
           else
@@ -271,7 +274,7 @@
         # Nothing particularly interesting.
         when 'Range'
           addBlock node, precedence, colors.VALUE, wrappingParen
-          mark node.from; mark node.to
+          addSocket node.from; addSocket node.to

         # ### If ###
         # An If consists of two separate
@@ -279,12 +282,12 @@
         # an indent or a one-line.
         when 'If'
           addBlock node, precedence, colors.CONTROL, wrappingParen
-          mark node.condition
+          addSocket node.condition

           # If it is a one-line "if",
           # unwrap the body.
           if getBounds(node.body).end.line is getBounds(node).start.line
-            mark node.body.unwrap()
+            addSocket node.body.unwrap()

           # Otherwise, do not.
           else
@@ -294,7 +297,7 @@
             # If it is a one-line "else",
             # unwrap the body.
             if getBounds(node.elseBody).end.line is getBounds(node).start.line
-              mark node.elseBody.unwrap()
+              addSocket node.elseBody.unwrap()

             # Otherwise, do not.
             else
@@ -314,12 +317,12 @@
         # A return is the only block other than 'break' rendered RETURN.
         when 'Return'
           addBlock node, precedence, colors.RETURN, wrappingParen
-          if node.expression? then mark node.expression
+          if node.expression? then addSocket node.expression

         # ### While ###
         when 'While'
           addBlock node, precedence, colors.CONTROL, wrappingParen
-          mark node.condition
+          addSocket node.condition

           # If it is a one-line "while",
           # unwrap the body.
@@ -336,7 +339,7 @@
         # we will simply signify to the child node that it must wrap
         # itself in these parentheses.
         when 'Parens'
-          if node.body? then mark node.body.unwrap(), 0, node
+          if node.body? then addSocket node.body.unwrap(), 0, node

         # ### Obj ###
         # Objects can be one-line or multiline,
@@ -383,8 +386,10 @@
               start: false

             id += 1
-
-          for property in node.properties then mark property
+
+            for property in node.properties then mark property
+          else
+            for property in node.properties then addSocket property

     # We do not mark the root expression,
     # but rather every child of it.


new intrinsic cloudformation fns, `Fn::ImportValue`, `Fn::Sub`; include ability for Outputs to be mapped, while also including support for previous method

--- src/coffin.coffee
+++ src/coffin.coffee
@@ -265,6 +265,9 @@

   Output: (name, args...) =>
     result = {}
+    if args.length is 1 and (args[1] instanceof Object)
+      map = args[1]
+      result[name] = map
     if args.length is 1
       result[name] =
         Value: args[0]
@@ -309,11 +312,20 @@
     'Fn::Base64': arg
   GetAZs: (arg) ->
     'Fn::GetAZs': arg
+  ImportValue: (arg) ->
+    'Fn::ImportValue': arg
   Select: (index, args...) ->
     if args.length is 1 and (args[0] instanceof Array)
       'Fn::Select': [index, args[0]]
     else
       'Fn::Select': [index, args]
+  Sub: (args...) ->
+    if args.length is 2 and (args[1] instanceof Object)
+      subString = args[0]
+      vars = args[1]
+      'Fn::Sub': [subString, vars]
+    else
+      'Fn::Sub': args[0]
   And: (conditions...) ->
     'Fn::And': conditions
   Equals: (value_1, value_2) ->


console.log upload progress

console.log upload progress

--- src/app.coffee
+++ src/app.coffee
@@ -86,8 +86,10 @@
     r.lpush "item_tokens", item_token

     req.on 'data', (data) ->
+      console.log "[UPLOAD] got #{data.length} more bytes"
       r.append "file_data:#{file_token}", data
     req.on 'end', () ->
+      console.log "[UPLOAD] DONE"
       timeoutSet 500, () -> #TODO: respond only after all data saved
         r.hset "item_info:#{item_token}", 'completed_at', JSON.stringify(new Date().getTime()), (e, v) ->
           respond res, {


Pretty print json from api

Pretty print json from api

--- src/app.coffee
+++ src/app.coffee
@@ -64,6 +64,7 @@
       reportUrl: (id) -> "/crashreports/#{id}"
       titleCase: titleCase

+  breakpad.set 'json spaces', 2
   breakpad.set 'views', path.resolve(__dirname, '..', 'views')
   breakpad.engine('handlebars', hbs.engine)
   breakpad.set 'view engine', 'handlebars'


Add route to download crashreport files

Add route to download crashreport files

--- src/app.coffee
+++ src/app.coffee
@@ -88,6 +88,19 @@
       Crashreport.getStackTrace record, (err, report) ->
         res.render 'view', { title: 'Crash Report', report: report, fields: record.toJSON() }

+  breakpad.get '/crashreports/:id/:filefield', (req, res, next) ->
+    # download the file for the given id
+    Crashreport.findById(req.params.id).then (crashreport) ->
+      if not crashreport?
+        return res.status(404).send 'Crash report not found'
+
+      contents = crashreport.get(req.params.filefield)
+
+      if not Buffer.isBuffer(contents)
+        return res.status(404).send 'Crash report field is not a file'
+
+      res.send(contents)
+
   breakpad.post '/symfiles', (req, res, next) ->
     Symfile.createFromRequest req, (err, symfile) ->
       return next(err) if err?


Fix empty alternative displaying

Fix empty alternative displaying

--- MediaAdminBundle/Resources/public/coffee/view/AlternativeSelectView.coffee
+++ MediaAdminBundle/Resources/public/coffee/view/AlternativeSelectView.coffee
@@ -37,11 +37,7 @@
   changeCropFormat: (event) ->
     format = $(event.currentTarget).val()
     image = @options.alternatives[format] || @options.original
-    $('#preview_thumbnail', @$el).hide()
     $('#preview_thumbnail', @$el).attr 'src', image
-    $('#preview_thumbnail', @$el).load ->
-      $('#preview_thumbnail', @$el).show()
-      return

   sendMedia: (event) ->
     event.preventDefault()


hubot: fix infinite loop because of SSE connection closed

Closing the SSE connection is expected. We shouldn't just restart it
without needing another run.

--- hubot/fleet.coffee
+++ hubot/fleet.coffee
@@ -68,5 +68,4 @@
     es.onmessage = (e) ->
       fleet.process JSON.parse(e.data)
     es.onerror = ->
-      robot.logger.warning "#{scenario}: unable to parse SSE events"
-      msg.send "#{scenario}: unable to parse SSE events"
+      es.close()


remove unused code

remove unused code

--- app/lib/experiments.coffee
+++ app/lib/experiments.coffee
@@ -10,4 +10,8 @@
     dataType: 'json'
   }).promise()

+getCohort = (data) ->
+  data.cohort
+
+exports.getCohort = getCohort
 exports.getExperiment = getExperiment


GameObject.Effect

GameObject.Effect

--- test/game_object_effect.coffee
+++ test/game_object_effect.coffee
@@ -8,7 +8,8 @@
   player.fadeOut 1, ->
     fadedOut = true

-  player.update(2)
+  player.update(1)
+  player.update(1)

   ok fadedOut


slight tweaks for the wiki

slight tweaks for the wiki

--- test/google_images_test.coffee
+++ test/google_images_test.coffee
@@ -1,7 +1,6 @@
 Tests  = require('./tests')
 assert = require 'assert'
 helper = Tests.helper()
-mu     = "http://mustachify.me/?src="

 require('../src/hubot/scripts/google-images') helper

@@ -15,6 +14,7 @@
   )

 # callbacks for when hubot sends messages
+mu    = "http://mustachify.me/?src="
 tests = [
   (msg) -> assert.equal "#{mu}(foo)#.png", msg
   (msg) -> assert.equal "#{mu}(foo)#.png", msg
@@ -36,3 +36,4 @@
   helper.receive 'helper: image me foo'
   helper.receive 'helper: image foo'
   helper.receive 'helper: animate me foo'
+


fix colaz

fix colaz


Update CLI options.

Update CLI options.

--- src/cli.coffee
+++ src/cli.coffee
@@ -13,12 +13,12 @@
     -b, --browser                   open browser automatically
     -h, --host                      hostname to bind to
     -p, --port                      port to listen on
-    -r, --require <name>            require the given module
     -t, --timeout <ms>              set test-case timeout in milliseconds [2000]
     --check-leaks                   check for global variable leaks
     --compilers <ext>:<module>,...  specify compiler to use for a given extension
-    --globals <names>,...           allow the given comma-delimited global [names]
+    --globals <names>,...           allow the given global [names]
     --recursive                     include sub directories
+    --require <module>,...          require the given modules
   """
   process.exit 0

@@ -51,11 +51,15 @@
       opts.host = args.shift()
     when '-p', '--port'
       opts.port = parseInt args.shift(), 10
+    when '-t', '--timeout'
+      opts.timeout = parseInt args.shift(), 10
     when '--check-leaks'
       opts.checkLeaks = true
     when '--compilers'
       opts.compilers = args.shift().split ','
     when '--globals'
+      opts.globals = args.shift().split ','
+    when '--require'
       opts.globals = args.shift().split ','
     when '--recursive'
       opts.recursive = true


Update cli module.

Update cli module.

--- src/cli.coffee
+++ src/cli.coffee
@@ -3,22 +3,18 @@
 fs = require 'fs'
 requisite = require('../lib')

-writeBundle = (bundle, options) ->
-  _options =
-    minify: options.minify
-    minifer: options.minifer
-
-  if options.output?
-    fs.writeFileSync options.output, bundle.toString _options, 'utf8'
+writeBundle = (bundle, opts) ->
+  if opts.output?
+    fs.writeFileSync opts.output, bundle.toString opts, 'utf8'
   else
-    console.log bundle.toString _options
+    console.log bundle.toString opts

 help = (code, message) ->
   console.log """

   Usage: requisite path/to/entry-module [options]

-  Options:
+  options:
     -b, --bare                   Compile without a top-level function wrapper
     -e, --export <name>          Export module as <name>
     -i, --include [module, ...]  Additional modules to include, in <require as>:<path to module> format
@@ -35,15 +31,15 @@
   console.log '\n' + message if message
   process.exit code

-options =
-  bare: false
+opts =
+  bare:    false
+  include: []
   exclude: null
-  export: null
-  include: []
-  minify: false
-  output: null
+  export:  null
+  minify:  false
+  output:  null
   prelude: null
-  watch: false
+  watch:   false

 args = process.argv.slice 2

@@ -59,38 +55,38 @@
 while opt = args.shift()
   switch opt
     when '-b', '--bare'
-      options.bare = true
+      opts.bare = true
     when '-i', '--include'
       while (module = args.shift())? and module.charAt(0) != '-'
         try
           [requireAs, absolutePath] = module.split ':'
-          options.include[requireAs] = absolutePath
+          opts.include[requireAs] = absolutePath
         catch err
           help 1, 'Invalid argument to include'
     when '-x', '--exclude'
-      options.exclude = new RegExp args.shift()
+      opts.exclude = new RegExp args.shift()
     when '-e', '--export'
-      options.export = args.shift()
+      opts.export = args.shift()
     when '-m', '--minify'
-      options.minify = true
+      opts.minify = true
     when '-o', '--output'
-      options.output = args.shift()
+      opts.output = args.shift()
     when '-p', '--prelude'
-      options.prelude = args.shift()
+      opts.prelude = args.shift()
     when '-w', '--watch'
-      options.watch = true
+      opts.watch = true
     when '-h', '--help'
       help 0
     else
       help 1

-if options.watch and not options.output?
+if opts.watch and not opts.output?
   help 1, 'Output must be specified when using watch'

-requisite.bundle entry, options, (err, bundle) ->
-  writeBundle bundle, options
+requisite.bundle entry, opts, (err, bundle) ->
+  writeBundle bundle, opts

-  if options.watch
+  if opts.watch
     requisite.watch bundle, (event, filename, mod) ->
       console.log "#{/\d{2}:\d{2}:\d{2}/.exec(new Date())[0]} - recompiling, #{filename} changed"
-      writeBundle bundle, options
+      writeBundle bundle, opts


Remove vim toggle key combo

Remove vim toggle key combo

--- keymaps/vim-mode.cson
+++ keymaps/vim-mode.cson
@@ -1,6 +1,3 @@
-'body':
-  'meta-alt-ctrl-o': 'vim-mode:toggle'
-
 '.vim-mode.insert-mode:not(.mini)':
   'escape': 'vim-mode:activate-command-mode'
   'ctrl-c': 'vim-mode:activate-command-mode'


remove all server side sorts from support publish


:bug:Add null check to sub_sponsor endpoint

Causes exception when sponsor has deleted their account.

--- server/handlers/user_handler.coffee
+++ server/handlers/user_handler.coffee
@@ -401,7 +401,7 @@
         name: sponsor.get('name')

       # Get recipient subscription info
-      findStripeSubscription sponsor.get('stripe').customerID, userID: req.user.id, (subscription) =>
+      findStripeSubscription sponsor.get('stripe')?.customerID, userID: req.user.id, (subscription) =>
         info.subscription = subscription
         @sendDatabaseError(res, 'No sponsored subscription found') unless info.subscription?
         @sendSuccess(res, info)


Fix online.net link bank operation

Fix online.net link bank operation

--- server/konnectors/online_net.coffee
+++ server/konnectors/online_net.coffee
@@ -52,8 +52,8 @@
             .use(linkBankOperation
                 log: log
                 model: Bill
-                identifier: 'online_net'
-                dateDelta: 4
+                identifier: 'online'
+                dateDelta: 14
                 amountDelta: 0.1
             )
             .args(requiredFields, {}, {})


pass sprite prop test

pass sprite prop test


print errors to console

print errors to console

should work; haven't tested it yet though

--- lib/connection.coffee
+++ lib/connection.coffee
@@ -11,7 +11,7 @@

     @client.handle 'error', (options) =>
       if atom.config.get("julia-client.errorsToConsole")
-        console.log('TODO: send this error to the console!!')
+        process.emitter.emit 'stderr', options.msg
       else
         atom.notifications.addError options.msg, options


Add ability to override idle handler

Add ability to override idle handler

--- lib/dispatcher.coffee
+++ lib/dispatcher.coffee
@@ -106,6 +106,9 @@
     @doc.diagnose()
     @doc.report()

+  # Set the idle handler
+  when_idle: (@idle_handler) ->
+
   # Clear the timeout
   clear_timeout: ->
     clearTimeout @timeout


use instanceof instead, in case it changes keys at some point

--- src/slack.coffee
+++ src/slack.coffee
@@ -79,9 +79,9 @@
       email_address: user.profile.email
       slack: {}
     for key, value of user
-      # _client is the slack-client instance and contains references to the all the data types (users, channels) plus things like the token, s
+      # user contains an of the SlackClient, which and contains references to the all the data types (users, channels) plus things like the token, s
       # so, don't bother storing it
-      continue if key is '_client'
+      continue if value instanceof SlackClient
       newUser.slack[key] = value

     if user.id of @robot.brain.data.users


replaceSingleQuotes and dontQuote fix

replaceSingleQuotes and dontQuote fix

Fix for #140

--- src/squel.coffee
+++ src/squel.coffee
@@ -908,7 +908,7 @@
         if typeof value is 'undefined'  # e.g. if field is an expression such as: count = count + 1
           str += field
         else
-          str += "#{field} = #{@_formatValue(value, fieldOptions)}"
+          str += "#{field} = #{if fieldOptions.dontQuote then value else @_formatValue(value, fieldOptions)}"

       "SET #{str}"


ide: small refactoring for readme text

ide: small refactoring for readme text

--- client/ide/lib/idehelpers.coffee
+++ client/ide/lib/idehelpers.coffee
@@ -5,8 +5,8 @@
 FSHelper = require 'app/util/fs/fshelper'
 FilePermissionsModal = require './views/modals/filepermissionsmodal'

-WORKSPACE_WELCOME_TXT = '''
-  # Awesome, you\'ve just made a new workspace!
+WORKSPACE_WELCOME_TXT = """
+  # Awesome, you've just made a new workspace!
   Workspaces are fantastic for organizing your work. This new workspace, which lives inside the 'Workspaces' folder of your home
   directory, is a place where you can store all relevant and related files to a particular project. For example, if you
   are working on multiple projects, its nice to have them separated as follows:
@@ -24,7 +24,7 @@
   like IDE settings, files open, Terminals open, etc.

   Enjoy and Happy Koding!
-'''
+"""

 module.exports = helpers =


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/tween.coffee
+++ src/tween.coffee
@@ -23,7 +23,7 @@
         I[property] = f(t)

   tween: (duration, properties) ->
-
+    properties = O
     # TODO Extract special properties
     easing = properties.easing || "linear"


Refactor parse pattern.

Refactor parse pattern.

--- src/utils.coffee
+++ src/utils.coffee
@@ -69,15 +69,26 @@

   new RegExp re

-parsePattern = (pattern) ->
-  return unless pattern?
+parsePattern = (patterns) ->
+  return unless patterns?

-  if Array.isArray pattern or typeof pattern == 'string'
-    globToRegex pattern
-  else if pattern instanceof RegExp
-    pattern
-  else
-    throw new Error 'Expected RegExp or glob pattern(s)'
+  if patterns instanceof RegExp
+    return patterns
+
+  if typeof patterns == 'string'
+    return globToRegex patterns
+
+  unless Array.isArray
+    throw new Error 'Expected Array, RegExp or glob pattern'
+
+  # convert every pattern to RegExp
+  for pat, i in patterns
+    if typeof pat == 'string'
+      patterns[i] = globToRegex pattern
+    else if not pat instanceof RegExp
+      throw new Error 'Expected RegExp or glob pattern'
+
+  new RegExp (re.source for re in patterns).join '|'

 # utility function to setup args for walk/watch
 exports.parseArgs = (fn) ->


Update error message

Update error message

--- lib/app/twitter_last_step.coffee
+++ lib/app/twitter_last_step.coffee
@@ -48,7 +48,6 @@
     href = "#{opts.logoutPath}?redirect-to=#{opts.loginPagePath}"
     msg = "An account with this email address already exists. If this is " +
           "your account please " +
-          "delete this account, " +
-          "log in to Artsy via email and password, and link Twitter " +
+          "log in to Artsy with your email and password, and link your Twitter account" +
           "in your settings instead."
   res.redirect opts.twitterLastStepPath + '?error=' + msg


remove debug stuff

remove debug stuff

--- lib/autocomplete-provider.coffee
+++ lib/autocomplete-provider.coffee
@@ -33,10 +33,8 @@
                     return
                 retries = retries + 1
                 if (window.requesting == 1)
-                    #console.log('retrying', lastrun_myself)
                     return lolz(time, retries)
                 if window.lastrun == lastrun_myself
-                    console.log('match! doing another request', lastrun_myself)
                     window.requesting = 0
                     return dis.getSuggestions(options)
                 return lolz(time, retries)


core/windowctrl: use debug instead of KD.log :rabbit:

--- lib/core/windowcontroller.coffee
+++ lib/core/windowcontroller.coffee
@@ -1,6 +1,7 @@
-$                  = require 'jquery'
-KD                 = require './kd'
-KDController       = require './controller'
+debug        = require('debug') 'kd:windowcontroller'
+$            = require 'jquery'
+KD           = require './kd'
+KDController = require './controller'

 ###
 todo:
@@ -175,7 +176,7 @@
   revertKeyView:(view)->

     unless view
-      KD.warn 'view, which shouldn\'t be the keyview anymore, must be passed as a parameter!'
+      debug 'missing view'
       return

     if view is @keyView and @keyView isnt @oldKeyView


Improve file path display

Improve file path display

Remove the project path prefix from the file path in the Ensime panel.
This makes the names shorter and easier to read.

--- lib/features/typechecking.coffee
+++ lib/features/typechecking.coffee
@@ -18,7 +18,7 @@
     @notesByFile = _.groupBy(notes, (note) -> note.file)

     addNoteToMessageView = (note) =>
-      file = note.file
+      file = atom.project.relativizePath(note.file)[1]
       @messages.add new LineMessageView
         file: file
         line: note.line


Use promises for sequential ghc-modi communication

--- lib/ghc-modi-process-base.coffee
+++ lib/ghc-modi-process-base.coffee
@@ -109,54 +109,73 @@
       return @runModCmd {options, command, text, uri, args, callback}
     savedLines = []
     process.stdout.pause()
-    callbackAndResume = (res) ->
+    Promise.prototype.finally = (callback) -> @then(callback, callback)
+    Promise.resolve().then ->
+      if text?
+        debug "Loading file text for ghc-modi"
+        new Promise (resolve, reject) ->
+          process.stdout.once 'readable', ->
+            data = process.stdout.read().toString()
+            if data is "OK#{EOL}"
+              debug "Successfully loaded file text for ghc-modi"
+              return resolve()
+            else
+              debug "Failed to load file text for ghc-modi: #{data}"
+              return reject()
+          process.stdin.write "map-file #{uri}#{EOL}#{text}#{EOT}"
+      else
+        Promise.resolve()
+    .then ->
+      new Promise (resolve, reject) ->
+        parseData = ->
+          data = process.stdout.read()
+          unless data?
+            atom.notifications.addError "Haskell-ghc-mod: ghc-modi crashed
+                on #{command} with message #{savedLines.join(EOL)}",
+              detail: dir.getPath()
+              dismissable: true
+            console.error savedLines
+            reject()
+            return
+          data = data.toString()
+          debug "Got response from ghc-modi:#{EOL}#{data}"
+          lines = data.split(EOL)
+          savedLines = savedLines.concat lines
+          result = lines[lines.length - 2]
+          if result.match(/^OK/)
+            lines = savedLines.slice(0, -2)
+            resolve lines.map (line) ->
+              line.replace /\0/g, EOL
+          else
+            process.stdout.once 'readable', parseData
+        process.stdout.once 'readable', parseData
+        if uri?
+          cmd = [command, uri].concat args
+        else
+          cmd = [command].concat args
+        debug "Running ghc-modi command #{cmd}"
+        process.stdin.write cmd.join(' ').replace(EOL, ' ') + EOL
+    .finally (res) ->
+      if text?
+        new Promise (resolve, reject) ->
+          debug "Unloading file text from ghc-modi"
+          process.stdout.once 'readable', ->
+            data = process.stdout.read().toString()
+            if data is "OK#{EOL}"
+              debug "Successfully unloaded file text for ghc-modi"
+              return resolve(res)
+            else
+              debug "Failed to unload file text for ghc-modi: #{data}"
+              return reject()
+          process.stdin.write "unmap-file #{uri}#{EOL}"
+      else
+        Promise.resolve()
+    .finally (res) ->
       process.stdout.resume()
-      callback res
-    parseData = ->
-      data = process.stdout.read()
-      unless data?
-        atom.notifications.addError "Haskell-ghc-mod: ghc-modi crashed
-            on #{command} with message #{savedLines.join(EOL)}",
-          detail: dir.getPath()
-          dismissable: true
-        console.error savedLines
-        callbackAndResume []
-        return
-      data = data.toString()
-      debug "Got response from ghc-modi:#{EOL}#{data}"
-      lines = data.split(EOL)
-      savedLines = savedLines.concat lines
-      result = lines[lines.length - 2]
-      if result.match(/^OK/)
-        lines = savedLines.slice(0, -2)
-        callbackAndResume lines.map (line) ->
-          line.replace /\0/g, EOL
-      else
-        process.stdout.once 'readable', parseData
-    if text?
-      debug "Loading file text for ghc-modi"
-      process.stdin.write "map-file #{uri}#{EOL}#{text}#{EOT}"
-      process.stdout.once 'readable', ->
-        data = process.stdout.read().toString()
-        if data isnt "OK#{EOL}"
-          debug "Failed to load file text for ghc-modi"
-          callbackAndResume []
-          return
-        debug "Successfully loaded file text for ghc-modi"
-        process.stdout.once 'readable', parseData
-    else
-      process.stdout.once 'readable', parseData
-
-    if uri?
-      cmd = [command, uri].concat args
-    else
-      cmd = [command].concat args
-    debug "Running ghc-modi command #{cmd}"
-    process.stdin.write cmd.join(' ').replace(EOL, ' ') + EOL
-
-    if text?
-      debug "Unloading file text from ghc-modi"
-      process.stdin.write "unmap-file #{uri}#{EOL}"
+      callback res ? []
+    .catch (err) ->
+      console.error 'Looks like ghc-modi crahsed', err
+      callback []

   killProcess: =>
     return unless @processMap?


Add more langauges to mapping

Add more langauges to mapping

--- lib/markdown-preview-view.coffee
+++ lib/markdown-preview-view.coffee
@@ -6,7 +6,12 @@
 Editor = require 'editor'

 fenceNameToExtension =
+  "coffeescript": "coffee"
+  "toml": "toml"
   "ruby": "rb"
+  "go": "go"
+  "mustache": "mustache"
+  "java": "java"

 module.exports =
 class MarkdownPreviewView extends ScrollView


Show GitHub API error message when markdown preview fails

--- lib/markdown-preview-view.coffee
+++ lib/markdown-preview-view.coffee
@@ -32,15 +32,20 @@
   getPath: ->
     @buffer.getPath()

-  setErrorHtml: ->
+  setErrorHtml: (result)->
+    try failureMessage = JSON.parse(result.responseText).message
+
     @html $$$ ->
       @h2 'Previewing Markdown Failed'
-      @h3 'Possible Reasons'
-      @ul =>
-        @li =>
-          @span 'You aren\'t online or are unable to reach '
-          @a 'github.com', href: 'https://github.com'
-          @span '.'
+      if failureMessage?
+        @h3 failureMessage
+      else
+        @h3 'Possible Reasons'
+        @ul =>
+          @li =>
+            @span 'You aren\'t online or are unable to reach '
+            @a 'github.com', href: 'https://github.com'
+            @span '.'

   setLoading: ->
     @html($$$ -> @div class: 'markdown-spinner', 'Loading Markdown...')
@@ -56,4 +61,4 @@
         mode: 'markdown'
         text: @buffer.getText()
       success: (html) => @html(html)
-      error: => @setErrorHtml()
+      error: (result) => @setErrorHtml(result)


Storing the full range in search

Storing the full range in search

instead of just the start locations, to be able to compare the ranges for
bracker matching

--- lib/motions/search-motion.coffee
+++ lib/motions/search-motion.coffee
@@ -26,13 +26,13 @@
   execute: (count=1) ->
     @scan()
     @match count, (pos) =>
-      @editor.setCursorBufferPosition(pos)
+      @editor.setCursorBufferPosition(pos.range.start)

   select: (count=1) ->
     @scan()
     cur = @editor.getCursorBufferPosition()
     @match count, (pos) =>
-      @editor.setSelectedBufferRange([cur, pos])
+      @editor.setSelectedBufferRange([cur, pos.range.start])
     [true]

   match: (count, callback) ->
@@ -53,15 +53,17 @@
     cur = @editor.getCursorBufferPosition()
     matchPoints = []
     iterator = (item) =>
-      matchPoints.push(item.range.start)
+      matchPointItem =
+        range: item.range
+      matchPoints.push(matchPointItem)

     @editor.scan(regexp, iterator)

     previous = _.filter matchPoints, (point) =>
       if @reverse
-        point.compare(cur) < 0
-      else
-        point.compare(cur) <= 0
+        point.range.start.compare(cur) < 0
+      else
+        point.range.start.compare(cur) <= 0

     after = _.difference(matchPoints, previous)
     after.push(previous...)
@@ -163,7 +165,7 @@
       @reverse=false
     else
       @character = ''
-
+
     @character

   getCurrentWordMatch: ->


Difficulty index

Difficulty index

--- app/assets/javascripts/langtrainer_frontend_backbone/models/step.js.coffee
+++ app/assets/javascripts/langtrainer_frontend_backbone/models/step.js.coffee
@@ -1,4 +1,18 @@
 class Langtrainer.LangtrainerApp.Models.Step extends Backbone.Model
+  initialize: ->
+    @resetState()
+
+  resetState: ->
+    @wordsHelped = 0
+    @stepsHelped = 0
+
+  difficultyIndex: (answer) ->
+    wordsNumber = answer.split(' ').length
+    if wordsNumber == 0
+      return 1
+
+    return @stepsHelped + @wordsHelped/wordsNumber
+
   baseParams: ->
     result = '?token=' + Langtrainer.LangtrainerApp.currentUser.readAttribute('token')
     result += '&unit=' + Langtrainer.LangtrainerApp.world.get('unit').get('id')
@@ -14,6 +28,7 @@
     result = Langtrainer.LangtrainerApp.apiEndpoint + '/verify_answer'
     result += @baseParams()
     result += '&answer=' + answer
+    result += '&difficulty_index=' + @difficultyIndex(answer)

   nextStepUrl: ->
     result = Langtrainer.LangtrainerApp.apiEndpoint + '/next_step'
@@ -91,14 +106,22 @@
       result = that.nextWordMatches(answer, rightAnswer)
       !!result

+    if result? && result[2].length > 0
+      @wordsHelped += 1
+
     result

   verifyAnswerOnServer: (answer, language) ->
+    if answer.length is 0
+      @trigger('verify:wrong')
+      return
+
     that = @
     $.ajax
       url: @verifyAnswerUrl(@sanitizeText(answer))
       dataType: 'json'
       success: (response) ->
+        that.resetState()
         if response
           that.set response
           that.trigger('change', that)
@@ -114,6 +137,7 @@
       url: @nextStepUrl()
       dataType: 'json'
       success: (response) ->
+        that.resetState()
         that.set response
         Langtrainer.LangtrainerApp.world.get('step').set response
         that.trigger('change', that)
@@ -124,3 +148,5 @@
     $.ajax
       url: @showRightAnswerUrl()
       dataType: 'json'
+
+    @stepsHelped += 1


Colour markers according to their services

--- src/views.coffee
+++ src/views.coffee
@@ -73,14 +73,9 @@
         draw_units: (unit_list, opts) ->
             @clear_all_markers()
             markers = []
-            if opts.service?
-                color = colors.service_color(opts.service)
-            else
-                color = null

             unit_list.each (unit) =>
-                if !color?
-                    color = colors.unit_color(unit)
+                color = colors.unit_color(unit) or 'rgb(255, 255, 255)'
                 icon = new widgets.CanvasIcon 50, color
                 location = unit.get('location')
                 if location?


mocks/jobs: style (reduce -> map)

mocks/jobs: style (reduce -> map)

--- lib/mocks/jobs.coffee
+++ lib/mocks/jobs.coffee
@@ -19,9 +19,8 @@
       else
         events = _.sortBy @events, (e) -> e.timeout
         event_pairs = _.zip events, _.rest(events).concat [_.last events]
-        _.reduce event_pairs, (acc, [event, next_event]) ->
-          acc.concat _.extend event, timeout: next_event.timeout - event.timeout
-        , []
+        _.map event_pairs, ([event, next_event]) ->
+          _.extend event, timeout: next_event.timeout - event.timeout
     async.forEachSeries events, ({event, timeout, args}, cb_fe) =>
       setTimeout =>
         @emit event, @handle, args...


Use _.is functions

Use _.is functions

--- src/world.coffee
+++ src/world.coffee
@@ -11,24 +11,15 @@
 AUTHORITY = "_AUTHORITY"
 INDEX = "_INDEX"

-isFunction = (value) ->
-  typeof(value) == "function"
-
-isObject = (value) ->
-  typeof(value) == "object"
-
-isString = (value) ->
-  typeof(value) == "string"
-
 class World
   constructor: (up, label, rx) ->
     assert up, "up always exists"
-    assert isObject(up), "up is an object"
+    assert _.isObject(up), "up is an object"
     @up = up
     cache_rx = rx or @up.get_raw(RX)
     assert cache_rx, "cache_rx"
     @doc = cache_rx.map()
-    assert isString(label), "label is string"
+    assert _.isString(label), "label is string"
     @doc.put(LABEL, label)
     @doc.put(CHILDREN, cache_rx.array())
     @doc.put(RX, rx) if rx?
@@ -52,7 +43,7 @@

   get: (key) ->
     value = @get_raw(key)
-    return value(@,{}) if isFunction(value)
+    return value(@,{}) if _.isFunction(value)
     value

   owner: (key) ->
@@ -66,12 +57,13 @@

   call: (key, args) ->
     closure = @get_raw(key)
-    assert isFunction(closure), "#{key}: #{closure} is not a function"
+    assert _.isFunction(closure), "#{key}: #{closure} is not a function"
     closure(@, args)

   # TODO: refactor import_dict methods somewhere
   _import_children: (array) ->
     result = @rx().array()
+    assert _.isArray(array), "_import_children: #{array} is not an array"
     for value in array
       assert value, 'import child'
       result.push @make_world(value)
@@ -91,7 +83,7 @@
     new World(authority or @, label)

   _from_value: (value) ->
-    assert isString(value), "_from_value requires string"
+    assert _.isString(value), "_from_value requires string"
     world = @_spawn_world "#{value}"
     world.put("value", value)
     world
@@ -106,7 +98,7 @@

   make_world: (value) ->
     return value if @is_world(value)
-    return @_from_dict(value) if isObject(value)
+    return @_from_dict(value) if _.isObject(value)
     @_from_value value

   _children: -> @get(CHILDREN)


Removing old coffeescript code

Removing old coffeescript code

--- lib/old/panels.coffee
+++ lib/old/panels.coffee
@@ -1,85 +0,0 @@
-{CompositeDisposable} = require 'atom'
-module.exports = Splitter =
-
-  # Register commands
-  # TODO refactor this to be nicer maybe?
-  activate: (state) ->
-    atom.commands.add "atom-workspace", "atom-panels:move-right": => @moveRight()
-    atom.commands.add "atom-workspace", "atom-panels:move-left", => @moveLeft()
-    atom.commands.add "atom-workspace", "atom-panels:move-down", => @moveDown()
-    atom.commands.add "atom-workspace", "atom-panels:move-up", => @moveUp()
-    atom.commands.add "atom-workspace", "atom-panels:split-right", => @splitRight()
-    atom.commands.add "atom-workspace", "atom-panels:split-left", => @splitLeft()
-    atom.commands.add "atom-workspace", "atom-panels:split-down", => @splitDown()
-    atom.commands.add "atom-workspace", "atom-panels:split-up", => @splitUp()
-
-  # TODO refactor this to be nicer
-  moveRight: -> @move 'horizontal', +1
-  moveLeft: -> @move 'horizontal', -1
-  moveUp: -> @move 'vertical', -1
-  moveDown: -> @move 'vertical', +1
-
-  splitRight: -> @move 'horizontal', +1, true
-  splitLeft: -> @move 'horizontal', -1, true
-  splitUp: -> @move 'vertical', -1, true
-  splitDown: -> @move 'vertical', +1, true
-
-  # Moves to the associated pane
-  # @param direction - horizontal or vertical
-  # @param distance  - +1 for increase distance, -1 for decrease
-  # @param split     - should we be splitting if there is no pane present
-  move: (direction, distance, split) ->
-    swapped = false
-    pane = atom.workspace.getActivePane()
-    if atom.workspace.getPanes().length > 1
-      target = @getTarget(pane, direction, distance)
-      if target?
-        @swapEditor pane, target
-        swapped = true
-
-    # If we haven't already swapped and we want to split
-    if !swapped && split
-      buffer = pane.getActiveItem().buffer
-      buffer.load()
-      keys = {copyActiveItem: true}
-      switch [direction, distance].join(' ')
-        when 'horizontal 1'
-          blankPane = pane.splitRight(keys)
-          break
-        when 'horizontal -1'
-          blankPane = pane.splitLeft(keys)
-          break
-        when 'vertical 1'
-          blankPane = pane.splitDown(keys)
-          break
-        when 'vertical -1'
-          blankPane = pane.splitUp(keys)
-          break
-
-  getTarget: (pane, direction, distance) ->
-    [axis, child] = @getAxis(pane, direction)
-    if axis?
-      return @getRelativePane axis, child, distance
-
-  swapEditor: (source, target) ->
-    target.activate()
-
-  getAxis: (pane, direction) ->
-    axis = pane.parent
-    child = pane
-    while true
-      return unless axis.constructor.name == 'PaneAxis'
-      break if axis.orientation == direction
-      child = axis
-      axis = axis.parent
-      break
-    return [axis,child]
-
-  getRelativePane: (axis, source, delta) ->
-    if axis.children?
-      position = axis.children.indexOf source
-      target = position + delta
-      return unless target < axis.children.length
-      if axis.children[target]?
-        return axis.children[target].getPanes()[0]
-    return null


align comments to the indentation level they proceed.

--- src/fmt.coffee
+++ src/fmt.coffee
@@ -12,6 +12,7 @@
 	CURR_INDENT	= ""
 	CURR_LINE	= 0
 	INDENT_SIZE	= options.tab.length
+	comments	= []

 	tokens.unshift [
 		'PROGRAM',
@@ -37,6 +38,22 @@
 			console.log("CURR_INDENT IS:" + CURR_INDENT.length);
 			if token[0] is INDENT or token[0] is OUTDENT
 					return
+			if token[0] is HERECOMMENT
+#				formatted_code += "###" + token[1] + "###"
+				comments.push {
+					type: HERECOMMENT
+					text: token[1]
+				}
+				CURR_LINE = token[2].first_line
+				return
+			if token[0] is COMMENT
+#				formatted_code += "# " + token[1]
+				comments.push {
+					type: COMMENT
+					text: token[1]
+				}
+				CURR_LINE = token[2].first_line
+				return
 			if token[2].first_line > CURR_LINE
 				formatted_code += "\n" + CURR_INDENT
 				CURR_LINE = token[2].first_line
@@ -44,12 +61,12 @@
 				return
 			if token[0] is TERMINATOR
 				return
-			if token[0] is HERECOMMENT
-				formatted_code += "###" + token[1] + "###"
-				return
-			if token[0] is COMMENT
-				formatted_code += "# " + token[1]
-				return
+			for j in [0..comments.length - 1] by 1
+				do (j) ->
+					if comments[j].type is COMMENT
+						formatted_code += "# " + comments[j].text
+						formatted_code += "\n" + CURR_INDENT
+			comments = []
 			formatted_code += token[1]

 	if true and


Revert "add serialization"

Revert "add serialization"

This reverts commit 915181a9568e61968c792087e971a39f5f5a78d3.

--- lib/ui/console.coffee
+++ lib/ui/console.coffee
@@ -6,16 +6,13 @@
 views =         require './views'

 module.exports =
-
-  uri: 'atom://julia-client/console'
-
   activate: (@client) ->
     @create()

     @subs = new CompositeDisposable

     @subs.add atom.workspace.addOpener (uri) =>
-      if uri is @uri
+      if uri is 'atom://julia-client/console'
         @c

     @client.handle 'info', (msg) =>
@@ -39,10 +36,7 @@
     @c.onEval (ed) => @eval ed
     @client.onWorking => @c.loading true
     @client.onDone => @c.loading false
-    @c.getURI = => @uri
     atom.views.getView(@c).classList.add 'julia'
-    @c.serialize = ->
-      deserializer: 'JuliaConsole'
     history.read().then (entries) =>
       @c.history.set entries


Handle PMs once, rather than twice.

Handle PMs once, rather than twice.

Currently new PMs are handled first by the 'message' handler and then by the
'pm' handler.  This change makes the 'message' handler take a back seat to the
'pm' handler.

--- src/irc.coffee
+++ src/irc.coffee
@@ -165,6 +165,10 @@
         self.createUser channel, nick

     bot.addListener 'message', (from, to, message) ->
+      if options.nick.toLowerCase() == to.toLowerCase()
+        # this is a private message, let the 'pm' listener handle it
+        return
+
       console.log "From #{from} to #{to}: #{message}"

       user = self.createUser to, from


implemented mountpoint and template support

--- modulware.coffee
+++ modulware.coffee
@@ -9,11 +9,16 @@
   routesFile: 'routes.yml'
   codeFilePattern: 'code/**/*.@(coffee|js)'
   i18nFilePattern: 'i18n/**/*.@(yml|yaml)'
+  viewFolderPattern: 'views/*.@(jade)'
   defaultHTTPMethod: '*' # can be get|put|post|delete|patch|all|*(is for all)
   debug: true
   encoding: 'utf8'
   moduleNamePattern: '^[a-zA-Z\\_\\-0-9]+$'
-  basedir: null
+  basedir: null # with `null` => process.cwd()
+  configOnLocals: true
+  defaultMountpoint: '/' # is able to contain a $module variable, e.g. '/$module'
+  expressjsSettings:
+    'view engine': 'jade'
 }

 _options = {}
@@ -30,14 +35,15 @@
 exports.setOptions = (options) ->
   # apply options over default options
   _.extendOwn(_options, defaultConfig, options)
-  unless _options.basedir
-    _options.basedir = path.dirname(process.argv[1])
+  _options.basedir ?= process.cwd()
   _options

-exports.applyMethods = (app) ->
-  return @log.error('app argument needs to be a function') if typeof app isnt 'function'
+exports.applyOnExpress = (instanceMethod) ->
+  return @log.error('instanceMethod argument needs to be a function') if typeof instanceMethod isnt 'function'
   exports.buildModuleIndex()
-  exports.applyOnApp(app)
+  app = exports._applyOnInstanceMethod(instanceMethod)
+  app.locals.config = @getConfig() if _options.configOnLocals
+  return app

 exports.log = {
   error:      ->
@@ -145,7 +151,7 @@

   sortedRoutes = before.concat(sortedRoutes.concat(after))

-exports.getConfig = (byReference = false) ->
+exports.getConfig = (byReference = true) ->
   if byReference then _config else _.extend({},_config)

 exports.getI18N = (byReference = false) ->
@@ -153,41 +159,75 @@

 exports.getTranslation = exports.getI18N

-exports.applyOnApp = (app) ->
+exports._applyOnInstanceMethod = (instanceMethod) ->
   modules = _modules
   options  = _options
-  for route in _sortedRoutes
-    routes = modules[route].routes.routes
+
+  mountToApp = (Boolean) typeof instanceMethod is 'function' and instanceMethod?.name is 'createApplication'
+
+  if mountToApp
+    mainApp = instanceMethod()
+  else
+    app = instanceMethod
+
+  # now all routes will be applied to the app
+  # in order of `before:` and `after:`
+
+  for moduleName in _sortedRoutes
+
+    # if we deal with express instance method
+    # we create a new express / app instance for each module
+    if mountToApp
+      app = instanceMethod()
+      for settingKey of options.expressjsSettings
+        app.set(settingKey, options.expressjsSettings[settingKey])
+
+    routes = modules[moduleName].routes.routes
     codeFiles = {}
-    for file in modules[route].codeFiles
+    for file in modules[moduleName].codeFiles
       #codeFiles[]
       key = file.replace(/^.*\/([^\\]+?)\.[^\.]+?$/, '$1')
       if codeFiles[key]
         throw Error("'#{file}' is an ambigious code file to '#{codeFiles[key]}'; files with identical name are not allowed")
       codeFiles[key] = file

-    routes = modules[route].routes.routes
-
-
-
-    for url, action of routes
-      urlParts = url.match(/^((post|put|get|delete|patch|all|\*)\:)*(.+)$/i)
-      if urlParts
-        # e.g. POST:/about
-        httpMethod = urlParts[2]?.toLowerCase() or 'all'
-        httpMethod = 'all' if httpMethod is options.defaultHTTPMethod
-        urlRule = urlParts[3]
-        # e.g. index.about (index -> code file, about -> method)
-        actionParts = action.match(/^([^\.]+)\.([^\.]+)/)
-        codeFile = actionParts[1]
-        method = actionParts[2]
-        if actionParts and codeFile and method
-          # abort if code file doesnt exists
-          throw Error("Cannot find file '#{codeFile}' for route '#{url}' in module '#{route}'") unless codeFiles[codeFile]
-          if method
-            try
-              app[httpMethod](urlRule, require(options.basedir+'/'+codeFiles[codeFile])[method])
-            catch e
-              throw Error("Cannot find method '#{method}' in file '#{codeFile}' for route '#{url}' in module '#{route}'")
-
-  return app
+    routes = modules[moduleName].routes.routes
+
+    for urlString, action of routes
+      # you can have multiple urls (comma seperated), like
+      # GET:/home,POST:/home
+      urls = urlString.split(',')
+      for url in urls
+        urlParts = url.match(/^((post|put|get|delete|patch|all|\*)\:)*(.+)$/i)
+        if urlParts
+          # e.g. POST:/about
+          httpMethod = urlParts[2]?.toLowerCase() or 'all'
+          httpMethod = 'all' if httpMethod is options.defaultHTTPMethod
+          urlRule = urlParts[3]
+          # e.g. index.about (index -> code file, about -> method)
+          actionParts = action.match(/^([^\.]+)\.([^\.]+)/)
+          codeFile = actionParts[1]
+          method = actionParts[2]
+          if actionParts and codeFile and method
+            # abort if code file doesnt exists
+            throw Error("Cannot find file '#{codeFile}' for route '#{url}' in module '#{moduleName}'") unless codeFiles[codeFile]
+            if method
+              try
+                app[httpMethod](urlRule, require(options.basedir+'/'+codeFiles[codeFile])[method])
+              catch e
+                throw Error("Cannot find method '#{method}' in file '#{codeFile}' for route '#{url}' in module '#{moduleName}'")
+
+    # apply views
+    viewFiles = glob.sync("#{moduleName}/#{options.viewFolderPattern}")
+    app.set('views',path.dirname(viewFiles[0])) if viewFiles?.length > 0
+
+    # mount to (parent/main) app
+    if mountToApp
+      mountpoint = if @getConfig()[moduleName]?.mountpoint then @getConfig()[moduleName]?.mountpoint else options.defaultMountpoint
+      mountpoint = mountpoint.replace(/\$module([^\w]+.*|)$/g, moduleName+'$1')
+      mainApp.use(mountpoint, app)
+
+  return if mountToApp
+    mainApp
+  else
+    app


ttest_power: code to create SVG with population and sampling distributions

--- ttest_power/ttest_power.coffee
+++ ttest_power/ttest_power.coffee
@@ -1,4 +1,4 @@
-# code to create
+# code to create interactive graph demonstrating power in a two-sample t-test

 # function that prints slider value next to slider
 printSliderValue = (sliderID) ->
@@ -12,6 +12,18 @@
         textbox.value = Math.round(10**slider.value * 10**digits)/10**digits
     else
         textbox.value = slider.value
+
+getSliderValue = (sliderID) ->
+    textboxID = "#{sliderID}value"
+
+    textbox = document.getElementById(textboxID)
+    slider = document.getElementById(sliderID)
+
+    if sliderID=="alpha"
+        digits=Math.floor(-slider.value)+2
+        return(Math.round(10**slider.value * 10**digits)/10**digits)
+    else
+        return(slider.value)

 # stuff for form
 param =
@@ -62,7 +74,126 @@
 d3.select("form#sliders")
   .on("change", () ->
                    for par in ["n", "alpha", "delta", "sigma"]
-                       printSliderValue(par))
+                       printSliderValue(par)
+                   update_plots() )
+
+# normal distribution
+dnorm = (x, mu=100, sd=5) ->
+    Math.exp(-0.5*Math.pow((x-mu)/sd, 2))/(sd * Math.sqrt(2*Math.PI))
+
+# create svg
+bgcolor = "#ccc"
+colors = ["slateblue", "violetred"]
+npts = 600
+figwidth = 600
+figheight= 300
+margin = {left:50,right:10, bottom:50, top:30, inner: 5}
+figtotw = figwidth + margin.left + margin.right
+figtoth = figheight + margin.top + margin.bottom
+height = figtoth*3
+width=   figtotw
+svg = d3.select("div#chart")
+        .append("svg")
+        .attr("width", width)
+        .attr("height", height)
+
+figs = [null, null, null]
+titles = ["Population distribution",
+    "Sampling distribution",
+    "Distribution of test statistic"]
+short = ["pop", "samp", "stat"]
+xscale = [null, null, null]
+yscale = [null, null, null]
+xrange = [[0,1], [0,1], [0,1]]
+yrange = [[0,1], [0,1], [0,1]]
+
+draw_plot = (index) ->
+    figs[index] = svg.append("g")
+                 .attr("id", short[index])
+                 .attr("transform", "translate(0,#{figtoth*index})")
+                 .append("svg")
+                 .attr("width", figtotw)
+                 .attr("height", figtoth)
+
+    figs[index].append("rect")
+               .attr("x", margin.left)
+               .attr("y", margin.top)
+               .attr("height", figheight)
+               .attr("width", figwidth)
+               .attr("fill", bgcolor)
+               .attr("stroke", "black")
+               .attr("stroke-width", 2)
+    figs[index].append("text")
+               .text(titles[index])
+               .attr("x", margin.left + figwidth/2)
+               .attr("y", margin.top/2)
+               .attr("dominant-baseline", "middle")
+               .attr("text-anchor", "middle")
+
+    if index==0 or index==1 # population distribution
+        xrange[index] = [100-param.sigma.max*3, 100+param.delta.max+param.sigma.max*3]
+        yrange[index] = [0, dnorm(0, 0, param.sigma.min)]
+    else
+        xrange[2] = [-5, 20]
+        yrange[2] = [0, 1]
+
+    xscale[index] = d3.scale.linear().clamp(true)
+                            .range([margin.left, margin.left+figwidth])
+                            .domain(xrange[index])
+    yscale[index] = d3.scale.linear().clamp(true)
+                            .range([margin.top+figheight, margin.top+margin.inner])
+                            .domain(yrange[index])
+
+
+for i in [0..2]
+    draw_plot(i)
+
+
+curve = (index) ->
+    d3.svg.line()
+      .x((d) -> xscale[index](d.x))
+      .y((d) -> yscale[index](d.y))
+
+update_plots = () ->
+    svg.selectAll("path").remove()
+
+    sigma = +getSliderValue("sigma")
+    delta = +getSliderValue("delta")
+    n = +getSliderValue("n")
+
+    x = d3.range(npts).map (i) ->
+        xrange[0][0] + i/npts*(xrange[0][1] - xrange[0][0])
+
+    yscale[0].domain([0, dnorm(0, 0, sigma)])
+    yscale[1].domain([0, dnorm(0, 0, sigma/Math.sqrt(n))])
+
+    mu = [100, 100+delta]
+    for i of mu
+        data = []
+        for j of x
+            data.push({x:x[j], y:dnorm(x[j], mu[i], sigma)})
+
+        figs[0].append("path")
+               .datum(data)
+               .attr("d", curve(0))
+               .attr("fill", "none")
+               .attr("stroke", colors[i])
+               .attr("stroke-width", 2)
+
+    mu = [100, 100+delta]
+    for i of mu
+        data = []
+        for j of x
+            data.push({x:x[j], y:dnorm(x[j], mu[i], sigma/Math.sqrt(n))})
+
+        figs[1].append("path")
+               .datum(data)
+               .attr("d", curve(1))
+               .attr("fill", "none")
+               .attr("stroke", colors[i])
+               .attr("stroke-width", 2)
+
+update_plots()

 # population distributions
 # sampling distributions


tweaks to ugh and you got it

tweaks to ugh and you got it

--- scripts/im-out.coffee
+++ scripts/im-out.coffee
@@ -9,13 +9,11 @@
     msg.send "http://mlkshk.com/r/BM7L.gif"
   robot.hear /cat falling off a table/i, (msg) ->
     msg.send "http://mlkshk.com/r/BJNO.gif"
-  robot.hear /ugh/i, (msg) ->
+  robot.hear /\bugh/i, (msg) ->
     msg.send "http://i.imgur.com/5qOPW.gif"
   robot.hear /what.*going on/i, (msg) ->
     msg.send "http://i.imgur.com/pgG2z.gif"
   robot.hear /you got it/i, (msg) ->
     msg.send "http://28.media.tumblr.com/tumblr_ltp4xqYG2b1qzosj0o1_400.gif"
-  robot.hear /you got it/i, (msg) ->
-    msg.send "http://28.media.tumblr.com/tumblr_ltp4xqYG2b1qzosj0o1_400.gif"


lolwut

lolwut

--- components/form/utilities.coffee
+++ components/form/utilities.coffee
@@ -2,7 +2,7 @@

 module.exports =
   firstVisibleInput: ($el) ->
-    $el.find('input:hidden, textarea:hidden').first()
+    $el.find('input:visible, textarea:visible').first()

   moveCursorToEnd: ($input) ->
     val = $input.val()


Updated resize event of KDTabView. Now it uses percent values to adjust tab width.

--- components/tabs/KDTabView.coffee
+++ components/tabs/KDTabView.coffee
@@ -228,32 +228,25 @@
     handle.getDomElement().addClass "hide-close-icon"

   resizeTabHandles: KD.utils.throttle ->
-    return if not @getOptions().resizeTabHandles or @_tabHandleContainerHidden
-
-    @tabHandleContainer.$().addClass "resize-in-action"
-    clearTimeout @resizeTimer
-
-    visibleHandles   = []
-    visibleTotalSize = 0
-    options          = @getOptions()
-    containerSize    = @tabHandleContainer.$().outerWidth no
-
+    return if not @getOptions().resizeTabHandles or @_tabHandleContainerHidden or @blockTabHandleResize
+
+    visibleHandles           = []
+    visibleTotalSize         = 0
+    options                  = @getOptions()
+    containerSize            = @tabHandleContainer.$().outerWidth(no) - options.lastTabHandleMargin
+    containerMarginInPercent = 100 * options.lastTabHandleMargin / containerSize

     for handle in @handles
       unless handle.isHidden()
         visibleHandles.push handle
         visibleTotalSize += handle.$().outerWidth no

-    if containerSize < visibleTotalSize + 40
-      for handle in visibleHandles
-        possibleWidth = containerSize / visibleHandles.length - 10
-        handle.$().css width: if possibleWidth > options.maxHandleWidth then options.maxHandleWidth else possibleWidth
+    sizeWhenUsedMaxHandleWidth = visibleHandles.length * options.maxHandleWidth;
+    if visibleTotalSize > containerSize or sizeWhenUsedMaxHandleWidth > containerSize
+      possiblePercent = (100 - containerMarginInPercent) / visibleHandles.length
+      handle.setWidth(possiblePercent, "%") for handle in visibleHandles
     else
-      for handle in visibleHandles
-        handle.$().css width: options.maxHandleWidth
-
-    @resizeTimer = @utils.wait 300, =>
-      @tabHandleContainer.$().removeClass "resize-in-action"
-  , 100

   _windowDidResize:(event)=> @resizeTabHandles event
+      handle.setWidth options.maxHandleWidth for handle in visibleHandles
+  , 300


log file renamed to log.json

log file renamed to log.json

--- src/log.coffee
+++ src/log.coffee
@@ -13,12 +13,15 @@
         @save()

     save: ->
-        str_entries = JSON.stringify(@entries)
+        str_entries = JSON.stringify(@entries, null, 4)
         #saving backup
         localStorage.myoulog_backup = str_entries
-        fs.writeFile 'log', str_entries, (err)->
-            if err then console.log err
-            console.log 'The file was saved!'
+        console.log 'Saving log file...'
+        fs.writeFile 'log.json', str_entries, (err)->
+            if err
+                console.log err
+            else
+                console.log 'Log file saved!'

     add_multiple_entries: (entries=[], save=true)->
         for e in entries
@@ -67,7 +70,11 @@
 # This promise is resolved only after read the log file.
 log.get_load_promise = ->
     new Promise (resolve, reject) ->
-        fs.readFile 'log', 'utf8', (err, data)->
+        if fs.existsSync 'log'
+            fs.rename('log', 'log.json')
+            console.log 'Old log file found. Renamed to log.json'
+
+        fs.readFile 'log.json', 'utf8', (err, data)->
             old_log = []
             if err
                 console.log err


Implemented getHostByAddr

Implemented getHostByAddr

--- src/natives/classes/java.net.Inet6AddressImpl.coffee
+++ src/natives/classes/java.net.Inet6AddressImpl.coffee
@@ -1,4 +1,5 @@
 host_lookup = {}
+host_reverse_lookup = {}

 # 240.0.0.0 .. 250.0.0.0 is currently unused address space
 next_host_address = 4026531840
@@ -9,10 +10,18 @@
     error 'Out of addresses'
     next_host_address = 4026531840

+pack_address = (address) ->
+  ret = 0
+  for i in [3 .. 0] by -1
+    ret |= (address[i] & 0xFF)
+    ret <<= 8
+  ret
+
 host_allocate_address = (address) ->
   host_address_inc()
   ret = next_host_address
   host_lookup[ret] = address
+  host_reverse_lookup[address] = ret
   ret

 native_methods.java.net.Inet6AddressImpl = [
@@ -31,7 +40,10 @@
     rs.init_string 'localhost'

   o 'getHostByAddr([B)Ljava/lang/String;', (rs, _this, addr) ->
-    rs.init_string ''
+    ret = host_reverse_lookup[pack_address(addr.array)]
+    if ret is undefined
+      return null
+    rs.init_string ret

   o 'isReachable0([BII[BII)Z', (rs, _this, addr, scope, timeout, inf, ttl, if_scope) ->
     false


Grant application no longer uploads duplicate image in Firefox. Closes #82

--- app/assets/javascripts/multifile.js.coffee
+++ app/assets/javascripts/multifile.js.coffee
@@ -2,6 +2,7 @@
   $('.first input.multi').live 'change', ->
     clone = $(@parentNode).clone()
     clone.val('')
+    clone.children('input.multi').val('')
     $(this).removeAttr('id')
     $(this).hide()
     filename = $(this).val().split("\\").pop()


200 is a number

200 is a number

--- scripts/trello.coffee
+++ scripts/trello.coffee
@@ -69,7 +69,7 @@
       return
     msg.http(url)
       .get() (err, res, body) ->
-        if res.statusCode isnt '200'
+        if res.statusCode isnt 200
           return
     jsdom = require 'jsdom'
     jsdom.env(


Return column = 0 when mouse to the left of editor

Fixes #966

--- src/editor.coffee
+++ src/editor.coffee
@@ -1652,12 +1652,13 @@

   screenPositionFromMouseEvent: (e) ->
     { pageX, pageY } = e
-
-    editorRelativeTop = pageY - @scrollView.offset().top + @scrollTop()
+    offset = @scrollView.offset()
+
+    editorRelativeTop = pageY - offset.top + @scrollTop()
     row = Math.floor(editorRelativeTop / @lineHeight)
     column = 0

-    if lineElement = @lineElementForScreenRow(row)[0]
+    if pageX > offset.left and lineElement = @lineElementForScreenRow(row)[0]
       range = document.createRange()
       iterator = document.createNodeIterator(lineElement, NodeFilter.SHOW_TEXT, acceptNode: -> NodeFilter.FILTER_ACCEPT)
       while node = iterator.nextNode()


fix custom filters load

fix custom filters load

--- app/coffee/modules/controllerMixins.coffee
+++ app/coffee/modules/controllerMixins.coffee
@@ -83,9 +83,8 @@
             location.search(name, value)

     replaceAllFilters: (filters, load=false) ->
-        if !@location.isInCurrentRouteParams(name, value)
-            location = if load then @location else @location.noreload(@scope)
-            location.search(filters)
+        location = if load then @location else @location.noreload(@scope)
+        location.search(filters)

     unselectFilter: (name, value, load=false) ->
         params = @location.search()


remove scheduler tab if new scheduler is on

fixes CNVS-31856

Test Plan:
- Turn on scheduler feature flag
- Go to calendar
- Notice scheduler tab is off
- Turn off feature flag
- Notice tab is back

Change-Id: I2d854f858952d3533c240d63667df8742e0cda85
Reviewed-on: https://gerrit.instructure.com/90222
Tested-by: Jenkins
QA-Review: Heath Hales <8ca81b68c7e2592ac614bd07f22fcc735ca55f93@instructure.com>
Reviewed-by: Jeremy Stanley <b3f594e10a9edcf5413cf1190121d45078c62290@instructure.com>
Product-Review: Steven Burnett <a0b450117c5d20a22d5c4d52bb8ae13adee003d8@instructure.com>

--- app/coffeescripts/bundles/calendar2.coffee
+++ app/coffeescripts/bundles/calendar2.coffee
@@ -21,7 +21,7 @@
   @header = new CalendarHeader(
     el: "#calendar_header"
     calendar2Only: ENV.CALENDAR.CAL2_ONLY
-    showScheduler: ENV.CALENDAR.SHOW_SCHEDULER
+    showScheduler: ENV.CALENDAR.SHOW_SCHEDULER and !ENV.CALENDAR.BETTER_SCHEDULER
     )
   @calendar = new Calendar(
     "#calendar-app", ENV.CALENDAR.CONTEXTS, ENV.CALENDAR.MANAGE_CONTEXTS, @eventDataSource,


Use UI Hash instead of selectors

Use UI Hash instead of selectors

For my_list, all_list and new_list

--- core/app/backbone/views/channels/auto_complete_search_channels_view.coffee
+++ core/app/backbone/views/channels/auto_complete_search_channels_view.coffee
@@ -19,16 +19,21 @@

   template: "channels/auto_complete_search_channels"

+  ui:
+    my_list:  '.js-list-my'
+    all_list: '.js-list-all'
+    new_list: '.js-list-new'
+
   onRender: -> @updateRows()

   appendHtml: (collectionView, itemView, index) ->
     model = itemView.model
     if not model.get('new') and model.existingChannelFor(currentUser)
-      @$('.js-list-my').append itemView.el
+      @ui.my_list.append itemView.el
     else if not model.get('new')
-      @$('.js-list-all').append itemView.el
+      @ui.all_list.append itemView.el
     else
-      @$('.js-list-new').append itemView.el
+      @ui.new_list.append itemView.el

     @updateRows()


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/follow.coffee
+++ src/follow.coffee
@@ -5,6 +5,6 @@
   self = GameObject(I)

   self.bind "update", ->
-    ; # Add update method behavior
+

   return self


follow docs

follow docs

--- src/follow.coffee
+++ src/follow.coffee
@@ -15,7 +15,7 @@
   y: 50
   width: 10
   height: 10
-  velocity:
+  velocity: Point(0, 0)

 enemy.


guv-heroku-workerstats: Support --role, allows analyze only certain roles/dynos

--- src/heroku.coffee
+++ src/heroku.coffee
@@ -228,7 +228,39 @@
     # FIXME: handle. Maybe outside/before

-analyzeStartups = (filename, started, callback) ->
+calculateStats = (state) ->
+  starts = state.startups.map (s) -> s.duration/1000
+  stops = state.shutdowns.map (s) -> s.duration/1000
+  scaleups = state.scaleups.map (s) -> s.duration/1000
+  uptimes = state.uptimes.map (s) -> s.duration/1000
+  results =
+    scaleup: statistics.median scaleups
+    scaleup_stddev: statistics.standard_deviation scaleups
+    scaleup_length: scaleups.length
+    uptime: statistics.median uptimes
+    uptime_stddev: statistics.standard_deviation uptimes
+    uptime_length: uptimes.length
+    startup: statistics.mean starts
+    startup_stddev: statistics.standard_deviation starts
+    startup_length: starts.length
+    shutdown: statistics.mean stops
+    shutdown_stddev: statistics.standard_deviation stops
+    shutdown_length: stops.length
+  wasted = results.scaleup + results.startup + results.shutdown # assumes Heroku charges for all these steps
+  results.utilization = results.uptime / (results.uptime + wasted)
+
+  return results
+
+hasDynos = (roles) ->
+  return (s) ->
+    hasRoles = roles? and roles.length # empty array means include everything
+    return true if not hasRoles
+    [role, number] = s.dyno.split '.'
+    include = role in roles
+
+    return include
+
+analyzeStartups = (filename, started, dynos, callback) ->
   fs = require 'fs'

   state = {}
@@ -239,27 +271,18 @@
     for e in events
       applyEvent state, e

-    starts = state.startups.map (s) -> s.duration/1000
-    stops = state.shutdowns.map (s) -> s.duration/1000
-    scaleups = state.scaleups.map (s) -> s.duration/1000
-    uptimes = state.uptimes.map (s) -> s.duration/1000
-    results =
-      scaleup: statistics.median scaleups
-      scaleup_stddev: statistics.standard_deviation scaleups
-      scaleup_length: scaleups.length
-      uptime: statistics.median uptimes
-      uptime_stddev: statistics.standard_deviation uptimes
-      uptime_length: uptimes.length
-      startup: statistics.mean starts
-      startup_stddev: statistics.standard_deviation starts
-      startup_length: starts.length
-      shutdown: statistics.mean stops
-      shutdown_stddev: statistics.standard_deviation stops
-      shutdown_length: stops.length
-    wasted = results.scaleup + results.startup + results.shutdown # assumes Heroku charges for all these steps
-    results.utilization = results.uptime / (results.uptime + wasted)
+    state.startups = state.startups.filter hasDynos(dynos)
+    state.shutdowns = state.shutdowns.filter hasDynos(dynos)
+    state.scaleups = state.scaleups.filter hasDynos(dynos)
+    state.uptimes = state.uptimes.filter hasDynos(dynos)
+
+    results = calculateStats state

     return callback null, results
+
+collectOption = (value, array) ->
+  array.push value
+  return array

 # TODO: calculate whole delay from scaling to up by default, and scaling down to down
 # TODO: allow to separate between (module) loading time, and startup time
@@ -275,6 +298,8 @@
     .arguments('<heroku.log>')
     .option('--started <regexp>', 'Regular expression matching output sent by process when started',
             String, 'noflo-runtime-msgflo started')
+    .option('--role <rolename>', 'Calculate stats for a subset of roles. Can be specified multiple times',
+            collectOption, [])
     .action (f, env) ->
       filename = f
     .parse(process.argv)
@@ -282,7 +307,8 @@

   started = (info) ->
     return program.started.test info
-  analyzeStartups filename, started, (err, res) ->
+  roles = program.role
+  analyzeStartups filename, started, roles, (err, res) ->
     throw err if err
     console.log res


Use png if no svg icon exists

Use png if no svg icon exists

--- views/components/etc/icon.cjsx
+++ views/components/etc/icon.cjsx
@@ -1,6 +1,7 @@
 {ROOT, layout, _, $, $$, React, ReactBootstrap, useSVGIcon} = window
 {$slotitems} = window
 path = require 'path-extra'
+fs = require 'fs-extra'

 SlotitemIcon = React.createClass
   name: 'SlotitemIcon'
@@ -13,7 +14,7 @@
 MaterialIcon = React.createClass
   name: 'MaterialIcon'
   render: ->
-    if useSVGIcon
+    if useSVGIcon && fs.accessSync("file://#{ROOT}/assets/svg/material/#{@props.materialId}.svg")

     else


User-Updater: based on JReferral and relationships

--- scripts/user-updater/main.coffee
+++ scripts/user-updater/main.coffee
@@ -20,14 +20,15 @@

 koding.once 'dbClientReady', ->

-  JAccount = rekuire 'account.coffee'
-  JReward  = rekuire 'rewards/index.coffee'
-  JUser    = rekuire 'user/index.coffee'
+  JAccount  = rekuire 'account.coffee'
+  JReward   = rekuire 'rewards/index.coffee'
+  JReferral = rekuire 'referral/index.coffee'
+  JUser     = rekuire 'user/index.coffee'

   # Config
   # ------

-  query = type: 'registered'
+  query = { type: "disk", unit: "MB" }
   skip  = 0

   # Helpers
@@ -45,90 +46,120 @@
         callback null, index

-  fetchUser  = (acc, callback)->
-    selector = { targetId: acc._id, as: 'owner', sourceName: 'JUser' }
-    Relationship.one selector, (err, rel) ->
-      return callback err   if err
-      return callback null  unless rel
-      JUser.one {_id: rel.sourceId}, callback
+  fetchAccount = (_id, cb)->
+
+    JAccount.one {_id}, (err, account)->
+      return cb err  if err
+      return cb {message: "no account found"}  unless account
+
+      JUser.one {username: account.profile.nickname}, (err, user)->
+        return cb err  if err
+        return cb {message: "no user found"}  unless user
+
+        cb null, {user, account}

-  addMissingRewards = (account, index, callback)->
+  fetchMembers = (referral, as, cb)->

-    referrerUsername = account.profile.nickname
+    selector = {targetId: referral._id, as}

-    console.log "##{index} working on", referrerUsername
+    Relationship.one selector, (err, rel)->
+      return cb err  if err
+      return cb {message: "no relationship found"}  unless rel

-    query = { referrerUsername }
+      fetchAccount rel.sourceId, (err, member)->
+        return cb err  if err
+        cb null, member

-    originId         = account._id
-    type             = "disk"
-    unit             = "MB"
-    amount           = 500
-    sourceCampaign   = "oldkoding"

-    JAccount.someData query, {_id:1, profile:1}, {skip}, (err, cursor)->
+  fetchDamn = (referral, cb)->

-      return console.log "ERROR: ", err  if err?
+    fetchMembers referral, "referrer", (err, referrer)->
+      return cb err  if err

-      iterate cursor, (referrer, index, callback)->
+      fetchMembers referral, "referred", (err, referred)->
+        return cb err  if err

-        fetchUser referrer, (err, user)->
+        cb null, {referrer, referred}

-          if err?
-            {nickname} = referrer.profile
-            console.log "Failed to fetch JUser for #{nickname}, skipping."
-            console.log err
-            return callback null

-          unless user.status is 'confirmed'
-            console.log "Referral #{user.username} is not confirmed, skipping."
-            return callback null
-          else
-            console.log "Referral #{user.username} valid, creating reward..."
+  createMissingReward = (referral, {referrer, referred}, callback)->

-          providedBy = referrer._id
+    type           = "disk"
+    unit           = "MB"
+    amount         = referral.amount
+    sourceCampaign = "oldkoding"

-          reward = new JReward {
-            type, unit, amount, sourceCampaign, providedBy, originId
-          }
+    console.log "Referrer #{referrer.user.username} valid, creating reward..."

-          reward.save (err)->
+    providedBy = referrer.account._id
+    originId   = referred.account._id

-            if err?.code is 11000
-              console.log "
-                Reward for #{referrerUsername} from #{user.username}
-                was already exists, skipping
-              "
-            else
-              console.warn err  if err?
+    reward = new JReward {
+      type, unit, amount, sourceCampaign, providedBy, originId
+    }

-            options = { unit, type, originId }
+    reward.save (err)->

-            JReward.calculateAndUpdateEarnedAmount options, (err)->
-              console.warn err  if err?
-              callback null
+      if err?.code is 11000
+        console.log "
+          Reward for #{referrer.user.username} from #{referred.user.username}
+          was already exists, skipping
+        "
+      else if err
+        console.warn err
+        return callback null

-      , 0, (err, total)->
+      options = { unit, type, originId }

-        if total > 0
-          console.log "Processed #{total} referral for #{referrerUsername}"
+      JReward.calculateAndUpdateEarnedAmount options, (err)->
+        console.warn err  if err?
+        callback null

-        callback null
+
+  addMissingRewards = (referral, index, callback)->
+
+    console.log "Working on #{index}. referral"
+
+    fetchDamn referral, (err, res) ->
+
+      if err
+        console.log "ERROR>", err
+        return callback null
+
+      {referrer, referred} = res
+
+      if 'confirmed' not in [referrer.user.status, referred.user.status]
+        console.log "
+          #{referrer.user.username} or #{referred.user.username}
+          is not confirmed, skipping.
+        "
+        return callback null
+
+      createMissingReward referral, {referrer, referred}, (err)->
+        console.log "ERROR>", err  if err
+
+        [referrer, referred] = [referred, referrer]
+
+        createMissingReward referral, {referrer, referred}, (err)->
+          console.log "ERROR>", err  if err
+
+          callback null

   # Main updater
   # ------------

-  JAccount.count query, (err, userCount)->
+  JReferral.count query, (err, referralCount)->

-    console.log "Total #{userCount - skip} accounts found, starting..."
+    console.log "Total #{referralCount - skip} referrals found, starting..."

-    JAccount.someData query, {_id:1, profile:1}, {skip}, (err, cursor)->
+    JReferral.someData query, {_id: 1, amount: 1}, {skip}, (err, cursor)->

       return console.log "ERROR: ", err  if err?

       iterate cursor, addMissingRewards, skip, (err, total)->

+        console.log "ERROR >>", err  if err?
         console.log "FINAL #{total}"
         process.exit 0


Find data key via grep.

Find data key via grep.

--- app/assets/javascripts/s3_direct_upload.js.coffee
+++ app/assets/javascripts/s3_direct_upload.js.coffee
@@ -104,7 +104,11 @@
         key = $uploadForm.data("key").replace('{timestamp}', new Date().getTime()).replace('{unique_id}', @files[0].unique_id)

         # substitute upload timestamp and unique_id into key
-        data[1].value = settings.path + key
+        key_field = $.grep data, (n) ->
+          n if n.name == "key"
+
+        if key_field.length > 0
+          key_field[0].value = settings.path + key

         # IE <= 9 doesn't have XHR2 hence it can't use formData
         # replace 'key' field to submit form


Add DropZone target to settings

Add DropZone target to settings

--- app/assets/javascripts/s3_direct_upload.js.coffee
+++ app/assets/javascripts/s3_direct_upload.js.coffee
@@ -21,6 +21,7 @@
     remove_completed_progress_bar: true
     remove_failed_progress_bar: false
     progress_bar_target: null
+    dropzone_target: null
     click_submit_target: null
     allow_multiple_files: true

@@ -35,6 +36,8 @@

   setUploadForm = ->
     $uploadForm.fileupload
+
+      dropZone: settings.dropzone_target

       add: (e, data) ->
         file = data.files[0]