stop using jQuery for removing card

stop using jQuery for removing card

--- app/assets/javascripts/manuscript_manager.js.coffee
+++ app/assets/javascripts/manuscript_manager.js.coffee
@@ -73,6 +73,17 @@
             {id: flow.id, position: flow.position}
         success: (data)=>
           @setState flows: @state.flows
+
+    removeCard: (taskId, phaseId) ->
+      $.ajax
+        url: 'tasks/' + taskId
+        method: 'DELETE'
+        success: =>
+          newFlows = @state.flows.slice(0)
+          flow = _.findWhere(newFlows, {id: phaseId})
+          flow.tasks = _.reject flow.tasks, (task) ->
+            task.taskId == taskId
+          @setState flows: newFlows

     addColumn: (index) ->
       column = {
@@ -113,6 +124,7 @@
               tasks: flow.tasks,
               phase_id: flow.id,
               paper: @state.paper
+              removeCard: @removeCard
             }
       ))

@@ -120,18 +132,17 @@
     displayName: "Column"
     manuscriptCards: ->
       {li} = React.DOM
-      cards = for task in @props.tasks
-        (li {}, Tahi.manuscriptManager.Card {task: task})
+      cards = _.map @props.tasks, (task) =>
+        (li {}, Tahi.manuscriptManager.Card {task: task, removeCard: => @props.removeCard(task.taskId, @props.phase_id)})
       cards.concat((li {},
         NewCardButton {
           paper: @props.paper,
           phase_id: @props.phase_id
       }))

+
     render: ->
       {h2, div, ul, li} = React.DOM
-
-
       (li {className: 'column'},
         Tahi.manuscriptManager.ColumnAppender {
           addFunction: @props.addFunction,
@@ -159,7 +170,7 @@
         (i {className: 'glyphicon glyphicon-plus'}))

     componentDidMount: ->
-      $(this.getDOMNode()).tooltip()
+      $(@getDOMNode()).tooltip()

   Card: React.createClass
     displayName: "Card"
@@ -190,16 +201,8 @@
           "data-toggle": "tooltip",
           "data-placement": "right",
           "title": "Delete Card",
-          onClick: @destroyCard })
+          onClick: @props.removeCard })
       )

     displayCard: (event) ->
       Tahi.overlay.display event, @props.task.cardName
-
-    destroyCard: ->
-      $.ajax
-        url: 'tasks/' + @props.task.taskId
-        method: 'DELETE'
-        success: =>
-          $(@getDOMNode()).remove()
-


Skips rendering if there are no hotkey bindings in the current view


Added failing test for a case compared to WordNet::QueryData->validForms

--- test/wordnet_test.coffee
+++ test/wordnet_test.coffee
@@ -141,6 +141,12 @@
         results.should.eql(['farther#r', 'far#r'])
         done()

+    xit 'should succeed for find#v', (done) ->
+      wordnet.validForms 'find#v', (results) ->
+        should.exist(results)
+        results.should.eql(['find#v'])
+        done()
+

   describe 'validFormsAsync', () ->


Convert date to string, so first track is not an object

--- app/assets/javascripts/mixingpanel/source.js.coffee
+++ app/assets/javascripts/mixingpanel/source.js.coffee
@@ -106,7 +106,7 @@
   propertiesFor: (source, base_name = "last_touch")->
     props = {}
     props[base_name+"_source"]       = source
-    props[base_name+"_timestamp"]    = new Date()
+    props[base_name+"_timestamp"]    = (new Date()).toISOString()
     props[base_name+"_referrer_url"] = @properties.uri.href || null
     props[base_name+"_location_url"] = @properties.location.href || null
     props[base_name+"_utm_source"]   = @utm.source || null


OX-553 Remove empty line

OX-553 Remove empty line

--- app/assets/javascripts/oxalis/model/flycam2d.coffee
+++ app/assets/javascripts/oxalis/model/flycam2d.coffee
@@ -194,7 +194,6 @@

   setPosition : (position) ->

-
     @setPositionSilent(position)
     @trigger("positionChanged", position)


minor fixes to make the v1 endpoints work again with the new data model


clear pictograms

clear pictograms

--- app/assets/javascripts/store/variant_options.coffee
+++ app/assets/javascripts/store/variant_options.coffee
@@ -27,9 +27,9 @@
       id = ids[0]
       $('#variant_id').val(variants[id].id)
       if $(@).find("option:selected").hasClass("in-stock")
-        $('#cart-form button[type=submit]').attr('disabled', false).html('

카트에 담기')
+        $('#cart-form button[type=submit]').attr('disabled', false).html('카트에 담기')
       else
-        $('#cart-form button[type=submit]').attr('disabled', true).html('

사이즈를 골라주세요.')
+        $('#cart-form button[type=submit]').attr('disabled', true).html('사이즈를 골라주세요.')
       $("#cart-form s.original.krw").html variants[id].old_price
       $("#cart-form p.current.krw").html variants[id].price
       $("#cart-form s.original.usd").html variants[id].old_price_usd
@@ -42,7 +42,7 @@
     otherOptionSelector = $("#option_type_" + otherOptionId)

     # disable the add to cart button – we'll reenable it later
-    $('#cart-form button[type=submit]').attr('disabled', true).html('

사이즈를 골라주세요.')
+    $('#cart-form button[type=submit]').attr('disabled', true).html('사이즈를 골라주세요.')

     # if this is the "select an option" option, reset availability on the other option and return
     if $(@)[0].selectedIndex == 0
@@ -70,10 +70,10 @@
           if $("#option-#{key}").val() == $("#option-#{key}").parent().val()
             $('#variant_id').val(variant[id].id)
             if $(@).find("option:selected").hasClass("in-stock")
-              $('#cart-form button[type=submit]').attr('disabled', false).html('

카트에 담기')
+              $('#cart-form button[type=submit]').attr('disabled', false).html('카트에 담기')
               parent.addClass('active')
             else
-              $('#cart-form button[type=submit]').attr('disabled', true).html('

사이즈를 골라주세요.')
+              $('#cart-form button[type=submit]').attr('disabled', true).html('사이즈를 골라주세요.')
               parent.removeClass('active')
             $("#cart-form s.original.krw").html variant[id].old_price
             $("#cart-form p.current.krw").html variant[id].price


Fixed: parsing tests

Fixed: parsing tests

--- test/Parse.coffee
+++ test/Parse.coffee
@@ -9,12 +9,11 @@
 num = helpers.num

 parse = (code) ->
-  array = []
   p = new Parser(code)
   p.tokens

 to_verb = (str) ->
-  { 'raw?' : true, tokens: [str], 'function call?' : true, 'kind' : 'function call', value : str }
+  { 'raw?' : true, tokens: [str], ':)~?' : true, 'kind' : ':)~', value : str }
 to_run_now_func = (o) ->
   Parse.To_Run_Now_Function(o)
 to_func = (o) ->


Disable mark selection for drawing in progress (#4787)

Disable drawing mark selection, except for the selected tool, when the selected tool is in progress.

--- app/classifier/tasks/drawing/markings-renderer.cjsx
+++ app/classifier/tasks/drawing/markings-renderer.cjsx
@@ -37,6 +37,7 @@

   render: ->
     skippedMarks = 0
+    drawingInProgress = false
     <g>
       {for annotation in @props.annotations
         annotation._key ?= Math.random()
@@ -55,6 +56,7 @@

               mark._key ?= Math.random()
               currentAnnotation = @props.annotations[@props.annotations.length - 1]
+              drawingInProgress = @state.selection?._inProgress and mark isnt @state.selection

               if skippedMarks < currentAnnotation._hideMarksBefore
                 skippedMarks += 1
@@ -82,7 +84,7 @@
               toolEnv =
                 containerRect: @props.containerRect
                 scale: scale
-                disabled: isPriorAnnotation
+                disabled: isPriorAnnotation || drawingInProgress
                 selected: mark is @state.selection and not isPriorAnnotation
                 getEventOffset: @props.getEventOffset
                 preferences: @props.preferences


API Wiki now saved into 'api/' folder

API Wiki now saved into 'api/' folder

--- scripts/api-wiki/files.coffee
+++ scripts/api-wiki/files.coffee
@@ -35,7 +35,7 @@
     unless utils.has(pathUtils.basename(path), 'index')
         filename += '-' + fileHeading
     filename += '-API.md'
-    wikiPath = pathUtils.join repo, filename
+    wikiPath = pathUtils.join repo, 'API/', filename

 exports.parseAll = (path, repo, callback) ->
     parsedFiles = []


Caches and requests need authentication

Caches and requests need authentication

(closes #299)

--- assets/scripts/app/routes.coffee
+++ assets/scripts/app/routes.coffee
@@ -80,6 +80,7 @@
   @route 'notFound', path: "/*path"

 Travis.RequestsRoute = Travis.Route.extend
+  needsAuth: true
   setupController: ->
     @_super.apply this, arguments
     @controllerFor('repo').activate('requests')
@@ -88,6 +89,7 @@
     Travis.Request.fetch repository_id: @modelFor('repo').get('id')

 Travis.CachesRoute = Travis.Route.extend
+  needsAuth: true
   setupController: ->
     @_super.apply this, arguments
     @controllerFor('repo').activate('caches')


fixed memory leak

fixed memory leak

--- core/app/backbone/views/channels_view.coffee
+++ core/app/backbone/views/channels_view.coffee
@@ -47,7 +47,7 @@
   initialize: ->
     @model = if @model? then @model.clone() else new User
     @setUserFromChannels()
-    @collection.on 'reset', @setUserFromChannels, this
+    @bindTo @collection, 'reset', @setUserFromChannels, this

   setUserFromChannels: ->
     channel = window.Channels.first()


Add Linux keybinding for `editor:log-cursor-scope`

--- keymap.cson
+++ keymap.cson
@@ -68,6 +68,7 @@
 	"shift-ctrl-c":   "file-icons:toggle-colours"
 	"alt-ctrl-t":     "user:toggle-trendy-faggot-mode"
 	"ctrl-p":         "user:temp-1"
+	"alt-p":          "editor:log-cursor-scope"

 "body:not(.platform-darwin) atom-text-editor":
 	"ctrl-g":         "go-to-line:toggle"


reorganizing color tests into module

reorganizing color tests into module

--- test/color.coffee
+++ test/color.coffee
@@ -1,109 +1,111 @@
-test "Color default should be black", ->
+module "Color"
+
+test "default should be black", ->
   color = Color()
   equals color.r(), 0, "default red channel is 0"
   equals color.g(), 0, "default green channel is 0"
   equals color.b(), 0, "default blue channel is 0"
   equals color.a(), 0, "default alpha channel is 0"

-test "Color should parse rgb", ->
+test "should parse rgb", ->
   color = Color('rgb(3, 56, 174)')
   equals color.r(), 3, 'red channel should equal input red value'
   equals color.g(), 56, 'green channel should equal input green value'
   equals color.b(), 174, 'blue channel should equal input blue value'

-test "Color should parse rgba", ->
+test "should parse rgba", ->
   color = Color('rgb(4, 66, 134, 0.45)')
   equals color.r(), 4
   equals color.g(), 66
   equals color.b(), 134
   equals color.a(), 0.45

-test "Color should parse rgba without leading 0 on alpha value", ->
+test "should parse rgba without leading 0 on alpha value", ->
   color = Color('rgb(4, 66, 134, .33)')
   equals color.r(), 4
   equals color.g(), 66
   equals color.b(), 134
   equals color.a(), 0.33

-test "Color should parse length 8 hex string", ->
+test "should parse length 8 hex string", ->
   color = Color('ae56f03a')
   equals color.r(), 174
   equals color.g(), 86
   equals color.b(), 240
   equals color.a(), 58 / 255.0

-test "Color should parse length 8 hex string with leading #", ->
+test "should parse length 8 hex string with leading #", ->
   color = Color('#001f34bb')
   equals color.r(), 0
   equals color.g(), 31
   equals color.b(), 52
   equals color.a(), 187 / 255.0

-test "Color should parse length 6 hex string", ->
+test "should parse length 6 hex string", ->
   color = Color('1084ce')
   equals color.r(), 16
   equals color.g(), 132
   equals color.b(), 206

-test "Color should parse length 6 hex string with leading #", ->
+test "should parse length 6 hex string with leading #", ->
   color = Color('#dfef12')
   equals color.r(), 223
   equals color.g(), 239
   equals color.b(), 18

-test "Color should parse length 4 hex string", ->
+test "should parse length 4 hex string", ->
   color = Color('b8f0')
   equals color.r(), 187
   equals color.g(), 136
   equals color.b(), 255
   equals color.a(), 0.0

-test "Color should parse length 4 hex string with leading #", ->
+test "should parse length 4 hex string with leading #", ->
   color = Color('#ab87')
   equals color.r(), 170
   equals color.g(), 187
   equals color.b(), 136
   equals color.a(), 119 / 255.0

-test "Color should parse length 3 hex string", ->
+test "should parse length 3 hex string", ->
   color = Color('a94')
   equals color.r(), 170
   equals color.g(), 153
   equals color.b(), 68

-test "Color should parse length 3 hex string with leading #", ->
+test "should parse length 3 hex string with leading #", ->
   color = Color('#3ef')
   equals color.r(), 51
   equals color.g(), 238
   equals color.b(), 255

-test "Color should know what Fuzzy Wuzzy Brown is", ->
+test "should know what Fuzzy Wuzzy Brown is", ->
   color = Color("Fuzzy Wuzzy Brown")
   equals color.r(), 196
   equals color.g(), 86
   equals color.b(), 85

-test "Color should parse arrays", ->
+test "should parse arrays", ->
   color = Color([1, 24, 101])
   equals color.r(), 1
   equals color.g(), 24
   equals color.b(), 101

-test "Color parse 3 or 4 number arguments", ->
+test "parse 3 or 4 number arguments", ->
   color = Color(4, 34, 102, 0.4)
   equals color.r(), 4
   equals color.g(), 34
   equals color.b(), 102
   equals color.a(), 0.4

-test "Color should parse first argument array, second argument alpha", ->
+test "should parse first argument array, second argument alpha", ->
   color = Color([4, 200, 43], 0.4)
   equals color.r(), 4
   equals color.g(), 200
   equals color.b(), 43
   equals color.a(), 0.4

-test "Color should parse first argument string, second argument alpha", ->
+test "should parse first argument string, second argument alpha", ->
   hexColor = Color("#1084ce", 0.5)
   rgbColor = Color("rgb(3, 90, 210)", 0.3)
   equals hexColor.r(), 16
@@ -116,7 +118,7 @@
   equals rgbColor.b(), 210
   equals rgbColor.a(), 0.3

-test "Color should equal colors with the same rbga values", ->
+test "should equal colors with the same rbga values", ->
   color1 = Color(4, 20, 100)
   color2 = Color('rgb(4, 20, 100)')
   color3 = Color('#041464')
@@ -136,11 +138,11 @@
   ok(alpha1.equals(alpha4))
   ok(alpha1.equals(alpha5))

-test "Color should output proper toString", ->
+test "should output proper toString", ->
   color = Color(5, 25, 125, 0.73)
   equals color.toString(), "rgba(5, 25, 125, 0.73)"

-test "Color should output proper toHex", ->
+test "should output proper toHex", ->
   white = Color(255, 255, 255)
   black = Color(0, 0, 0)
   equals white.toHex(), "#ffffff"


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/color.coffee
+++ test/color.coffee
@@ -40,10 +40,10 @@
   equals color.a(), 187 / 255.0

 test "Color should parse length 6 hex string", ->
-  color = Color('ffffff')
-  equals color.r(), 255
-  equals color.g(), 255
-  equals color.b(), 255
+  color = Color('1084ce')
+  equals color.r(), 16
+  equals color.g(), 132
+  equals color.b(), 206

 test "Color should parse length 6 hex string with leading #", ->
   color = Color('#ffffff')


removed legacy test file

removed legacy test file

--- tests.coffee
+++ tests.coffee
@@ -1,168 +0,0 @@
-defaultOptions = {
-  pretty:           false
-  stream:           false
-  tableDelimiter:   '|'
-  firstLineMarker:  '-*'
-  cellPadding:      ' '
-  delimiterOnBegin: '|'
-  delimiterOnEnd:   '|'
-  csvComment:       '#'
-  csvDelimiter:     ','
-  csvQuote:         '"'
-  csvEscape:        '"'
-  prettyCellSpace:  ' '
-}
-
-csvOptions = {
-  comment:          defaultOptions.csvComment
-  delimiter:        defaultOptions.csvDelimiter
-  quote:            defaultOptions.csvQuote
-  escape:           defaultOptions.csvEscape
-}
-
-chai    = require('chai')
-parse   = require('csv-parse')
-csv2md  = require('../src/csv2md')
-expect  = chai.expect
-
-csvString = require('fs').readFileSync(__dirname+'/example1.csv').toString()
-csvData   = []
-
-
-describe 'markdown table output', ->
-
-  before (done) ->
-    parse csvString, csvOptions, (err, data) ->
-      csvData = data
-      expect(err).to.be.equal null
-      done()
-
-  it 'expect to have a parsable, multiline csv file', ->
-    # properly we should not test the parser here
-    # but we must ensure that we have a valid csv file
-    expect(csvString.match(/\n/).length).to.be.above 0
-    expect(csvData).to.have.length.above 0
-    expect(csvData[0]).to.have.length.above 0
-
-
-  it 'expect to transform to a simple markdown table with default values', ->
-    csv2md.setOptions(defaultOptions)
-    md = csv2md.rowsToString(csvData)
-    expect(md.trim()).to.be.equal """
-    | a | b | c_1 | c_2 |
-    |---|---|---|---|
-    | -122.1430195 | 124.3 | true | false |
-    | null | a | a very long string | ~ |
-    | a | b | c_1 | c_2 |
-    """
-
-  it 'expect to transform to a simple markdown table in pretty', ->
-    csv2md.setOptions(defaultOptions)
-    csv2md.setOptions({ pretty: true })
-    md = csv2md.rowsToString(csvData)
-    expect(md.trim()).to.be.equal """
-    | a            | b     | c_1                | c_2   |
-    |--------------|-------|--------------------|-------|
-    | -122.1430195 | 124.3 | true               | false |
-    | null         | a     | a very long string | ~     |
-    | a            | b     | c_1                | c_2   |
-    """
-
-  it 'expect to transform to a simple markdown table with various options', ->
-    csv2md.setOptions(defaultOptions)
-    csv2md.setOptions
-      tableDelimiter:   '$'
-      firstLineMarker:  '+*'
-      cellPadding:      '_'
-      delimiterOnBegin: '/'
-      delimiterOnEnd:   '\\'
-    md = csv2md.rowsToString(csvData)
-    expect(md.trim()).to.be.equal """
-      /_a_$_b_$_c_1_$_c_2_\\
-      /+++$+++$+++$+++\\
-      /_-122.1430195_$_124.3_$_true_$_false_\\
-      /_null_$_a_$_a very long string_$_~_\\
-      /_a_$_b_$_c_1_$_c_2_\\
-    """
-    csv2md.setOptions({ pretty: true, prettyCellSpace: '…' })
-    md = csv2md.rowsToString(csvData)
-    expect(md.trim()).to.be.equal """
-      /_a……………………………_$_b…………_$_c_1………………………………………_$_c_2……_\\
-      /++++++++++++++$+++++++$++++++++++++++++++++$+++++++\\
-      /_-122.1430195_$_124.3_$_true……………………………………_$_false_\\
-      /_null……………………_$_a…………_$_a very long string_$_~…………_\\
-      /_a……………………………_$_b…………_$_c_1………………………………………_$_c_2……_\\
-    """
-
-  it 'expect to transform also empty cells', (done) ->
-    csvData = """
-      foo,bar,baz
-      a#,"b",c
-      d,e,
-      ,f,
-      ,,
-      g,h,i
-    """
-    parse csvData.trim(), {}, (err, rows) ->
-      expect(err).to.be.equal null
-      csv2md.setOptions(defaultOptions)
-      csv2md.setOptions(pretty: true)
-      expect(csv2md.rowsToString(rows).trim()).to.be.equal """
-        | foo | bar | baz |
-        |-----|-----|-----|
-        | a#  | b   | c   |
-        | d   | e   |     |
-        |     | f   |     |
-        |     |     |     |
-        | g   | h   | i   |
-      """.trim()
-      done()
-
-  it 'expect to transforms a csv string synchronously to markdown via require', ->
-    csv = """
-    foo,bar,baz
-    a#,"b",c
-    d,e,
-    ,f,
-    ,,
-    g,h,i
-    """
-    expectedOutcome = """
-    | foo | bar | baz |
-    |---|---|---|
-    | a# | b | c |
-    | d | e |  |
-    |  | f |  |
-    |  |  |  |
-    | g | h | i |
-    """.trim()
-    expect(
-      require('../src/csv2md').csv2md(csv).trim()
-    ).to.be.equal expectedOutcome
-    expect(
-      require('../src/csv2md').csv2md(csv, {}).trim()
-    ).to.be.equal expectedOutcome
-    expect(
-      require('../src/csv2md').csv2md(csv, {
-        tableDelimiter: 'ǁ'
-        pretty: true
-      }).trim()
-    ).to.be.equal """
-    ǁ foo ǁ bar ǁ baz ǁ
-    ǁ-----ǁ-----ǁ-----ǁ
-    ǁ a#  ǁ b   ǁ c   ǁ
-    ǁ d   ǁ e   ǁ     ǁ
-    ǁ     ǁ f   ǁ     ǁ
-    ǁ     ǁ     ǁ     ǁ
-    ǁ g   ǁ h   ǁ i   ǁ
-    """.trim()
-
-  it 'expect to execute bin/csv2md', ->
-    md = require('child_process').execSync("#{__dirname}/../bin/csv2md --pretty #{__dirname}/example1.csv")
-    expect(md.toString().trim()).to.be.equal """
-    | a            | b     | c_1                | c_2   |
-    |--------------|-------|--------------------|-------|
-    | -122.1430195 | 124.3 | true               | false |
-    | null         | a     | a very long string | ~     |
-    | a            | b     | c_1                | c_2   |
-    """.trim()


enhance tests

enhance tests

--- tests.coffee
+++ tests.coffee
@@ -2108,13 +2108,13 @@
   r = n1.to n2, "KNOWS", {testRels: true}
   _id = r.get().id

-  r.updateProperties {newProp1: 'newPropValue1', newProp2: 'newPropValue2'}
-
-  __relationCRC__ test, r, n1.get().id, n2.get().id, "KNOWS", {newProp1: 'newPropValue1', newProp2: 'newPropValue2'}
+  r.updateProperties {newProp1: 'newPropValue1', newProp2: 'newPropValue2', testRels: 'asd'}
+
+  __relationCRC__ test, r, n1.get().id, n2.get().id, "KNOWS", {newProp1: 'newPropValue1', newProp2: 'newPropValue2', testRels: 'asd'}

   cursor = db.query "MATCH ()-[r]-() WHERE id(r) = {id} RETURN DISTINCT r", {id: _id}
   cursor.each (relation) ->
-    __relationCRC__ test, relation.r, n1.get().id, n2.get().id, "KNOWS", {newProp1: 'newPropValue1', newProp2: 'newPropValue2'}
+    __relationCRC__ test, relation.r, n1.get().id, n2.get().id, "KNOWS", {newProp1: 'newPropValue1', newProp2: 'newPropValue2', testRels: 'asd'}

   test.equal r.delete(), undefined
   test.equal n1.delete(), undefined


`ast#run` must not throw errors, must call cb or reject promise

--- drivers/javascript/ast.coffee
+++ drivers/javascript/ast.coffee
@@ -73,7 +73,8 @@
                     callback = options
                     options = {}
                 else
-                    throw new err.RqlDriverError "Second argument to `run` cannot be a function is a third argument is provided."
+                    options new err.RqlDriverError("Second argument to `run` cannot be a function is a third argument is provided.")
+                    return
             # else we suppose that we have run(connection[, options][, callback])
         else if connection?.constructor is Object
             if @showRunWarning is true
@@ -88,11 +89,18 @@
         options = {} if not options?

         # Check if the arguments are valid types
-        for own key of options
-            unless key in ['useOutdated', 'noreply', 'timeFormat', 'profile', 'durability', 'groupFormat', 'binaryFormat', 'batchConf', 'arrayLimit']
-                throw new err.RqlDriverError "Found "+key+" which is not a valid option. valid options are {useOutdated: <bool>, noreply: <bool>, timeFormat: <string>, groupFormat: <string>, binaryFormat: <string>, profile: <bool>, durability: <string>, arrayLimit: <number>}."
-        if net.isConnection(connection) is false
-            throw new err.RqlDriverError "First argument to `run` must be an open connection."
+        try
+            for own key of options
+                unless key in ['useOutdated', 'noreply', 'timeFormat', 'profile', 'durability', 'groupFormat', 'binaryFormat', 'batchConf', 'arrayLimit']
+                    throw new err.RqlDriverError "Found "+key+" which is not a valid option. valid options are {useOutdated: <bool>, noreply: <bool>, timeFormat: <string>, groupFormat: <string>, binaryFormat: <string>, profile: <bool>, durability: <string>, arrayLimit: <number>}."
+            if net.isConnection(connection) is false
+                throw new err.RqlDriverError "First argument to `run` must be an open connection."
+        catch e
+            if typeof callback is 'function'
+                return callback(e)
+            else
+                return new Promise (resolve, reject) =>
+                    reject(e)

         if options.noreply is true or typeof callback is 'function'
             try


fixing tests

fixing tests

--- test/epsom.coffee
+++ test/epsom.coffee
@@ -19,8 +19,13 @@
     rm '-fr', temp_path

   it 'should create the expected files', ->
+    expected = [ 'assets','config','Gruntfile.coffee','package.json','server.js','src' ]
     res =  ls temp_path
-    res.join('').should.equal [ 'assets','config','Gruntfile.coffee','package.json','server.js','src' ].join('')
+    res.length.should.equal expected.length
+    for x in res
+      expected.indexOf(x).should.not.equal -1
+
+#    res.join('').should.equal [ 'assets','config','Gruntfile.coffee','package.json','server.js','src' ].join('')

   it 'should have no empty package.json', ->
     file_st = fs.readFileSync "#{temp_path}/package.json", 'utf8'


force refresh on settings save

force refresh on settings save

--- src/options.coffee
+++ src/options.coffee
@@ -1,6 +1,7 @@
 saveOptions = ->
   chrome.storage.sync.set
-    token: document.getElementById('token').value
+    token: document.getElementById('token').value,
+    lastChecked: 0 # force a refresh on next load
   , ->
     status = document.getElementById('status')
     status.textContent = 'Options saved.'
@@ -10,7 +11,7 @@

 restoreOptions = ->
   chrome.storage.sync.get
-    token: ""
+    token: "",
   , (items) ->
     document.getElementById('token').value = items.token


Test connect to front server and stub API server behind it

--- test/front.coffee
+++ test/front.coffee
@@ -4,6 +4,8 @@
 assert = chai.assert

 Front = require('../lib/front')
+WebSocket = require('ws')
+WebSocketServer = require('ws').Server

 describe 'Front', ->
   beforeEach ->
@@ -13,8 +15,10 @@
     @front.start

   it 'should start a WSS', ->
-    @front.start
-    # test for WSS existence
+    stub = new WebSocketServer({port: 3021, host: "0.0.0.0"});
+    @front.start {port: 3020, host: "0.0.0.0", apiEndpoint: "ws://localhost:3021"}
+    client = new WebSocket("ws://localhost:3020")
+

   it 'should connect to the API', ->
     @front.start


test Graph.children and Graph.parents and fix missing edge in test graph

--- test/graph.coffee
+++ test/graph.coffee
@@ -36,6 +36,12 @@

       test.deepEqual ['a'], graph.parentless()
       test.deepEqual ['b'], graph.childless()
+
+      test.deepEqual ['a'], graph.parents('b')
+      test.deepEqual [], graph.parents('a')
+
+      test.deepEqual ['b'], graph.children('a')
+      test.deepEqual [], graph.children('b')

       test.deepEqual [], graph.whereAllParentsIn []
       test.deepEqual [], graph.whereAllParentsIn new Set()
@@ -77,6 +83,14 @@
       test.deepEqual ['a'], graph.parentless()
       test.deepEqual ['c'], graph.childless()

+      test.deepEqual ['b'], graph.parents('c')
+      test.deepEqual ['a'], graph.parents('b')
+      test.deepEqual [], graph.parents('a')
+
+      test.deepEqual ['b'], graph.children('a')
+      test.deepEqual ['c'], graph.children('b')
+      test.deepEqual [], graph.children('c')
+
       test.deepEqual [], graph.whereAllParentsIn []
       test.deepEqual ['b'], graph.whereAllParentsIn ['a']
       test.deepEqual ['c'], graph.whereAllParentsIn ['b']
@@ -113,6 +127,14 @@
       test.deepEqual ['a'], graph.parentless()
       test.deepEqual ['c'], graph.childless()

+      test.ok new Set('b', 'a').equals graph.parents('c')
+      test.deepEqual ['a'], graph.parents('b')
+      test.deepEqual [], graph.parents('a')
+
+      test.ok new Set('b', 'c').equals graph.children('a')
+      test.deepEqual ['c'], graph.children('b')
+      test.deepEqual [], graph.children('c')
+
       test.deepEqual [], graph.whereAllParentsIn []
       test.deepEqual ['b'], graph.whereAllParentsIn ['a']
       test.deepEqual [], graph.whereAllParentsIn ['b']
@@ -135,6 +157,7 @@
         .add(0, 7)
         .add(0, 10)
         .add(0, 13)
+        .add(0, 14)
         .add(1, 2)
         .add(1, 9)
         .add(1, 13)
@@ -170,6 +193,12 @@

       test.ok new Set(12, 13, 14, 11, 15).equals graph.childless()

+      test.ok new Set(0, 1, 2).equals graph.parents(13)
+      test.ok new Set(2, 0, 10, 7, 9).equals graph.parents(14)
+
+      test.ok new Set(10, 12, 13, 14).equals graph.children(2)
+      test.ok new Set(10, 7, 9, 6).equals graph.children(5)
+
       test.ok new Set().equals graph.whereAllParentsIn []
       test.ok new Set(2).equals graph.whereAllParentsIn [1]
       test.ok new Set(6, 8).equals graph.whereAllParentsIn [5, 3]
@@ -199,6 +228,7 @@
         [0, 7]
         [0, 10]
         [0, 13]
+        [0, 14]
         [1, 2]
         [1, 9]
         [1, 13]


Multidocument retrieval.

Multidocument retrieval.

--- test/index.coffee
+++ test/index.coffee
@@ -815,16 +815,56 @@
           done()

     it "should work with deep events", (done) ->
-      Report.record 'deep_events', 'this.is.a.event': 1, (err, doc) ->
+      Report.record 'deep_events', 'here.is.a.event': 1, (err, doc) ->
         throw err if err
         Report.get 'deep_events', (err, doc) ->
           throw err if err
-          console.log doc
           expect(doc).to.not.be.null
           doc.total.should.equal 1
           doc.all[0].should.equal 1
-          doc.events.this.is.a.event.should.equal 1
+          doc.events.here.is.a.event.should.equal 1
           done()

+    it "should work with deep events and multiple documents", (done) ->
+      timestamp = moment()
+      Report.record 'mult', 'a.b.c': 1, timestamp.toDate(), (err, doc) ->
+        throw err if err
+        timestamp.add -1, 'minute'
+        Report.record 'mult', 'a.b.c': 1, timestamp.toDate(), (err, doc) ->
+          throw err if err
+          timestamp.add -1, 'minute'
+          Report.record 'mult', 'a.b.c': 1, timestamp.toDate(), (err, doc) ->
+            throw err if err
+            timestamp.add -1, 'minute'
+            Report.get 'mult', timestamp.toDate(), (err, doc) ->
+              throw err if err
+              expect(doc).to.not.be.null
+              doc.total.should.equal 3
+              doc.all.slice(0).should.eql [1, 1, 1]
+              doc.events.a.b.c.should.equal 3
+              done()
+
+    it "should work with some silly amount of stuff", (done) ->
+      count = 30
+      timestamp = moment()
+      increment = ->
+        Report.record 'silly', 'a.b.c': 1, timestamp.toDate(), (err, doc) ->
+          throw err if err
+          count -= 1
+          timestamp.add -10, 'seconds'
+          return retrieve() if not count
+          increment()
+
+      retrieve = ->
+        Report.get 'silly', timestamp.toDate(), (err, doc) ->
+          throw err if err
+          expect(doc).to.not.be.null
+          doc.all.length.should.equal 6
+          doc.events.a.b.c.should.equal 30
+          done()
+
+      increment()
+
+
     # TODO: Test multiple document queries


Delete toolbar.cson

Delete toolbar.cson

--- toolbar.cson
+++ toolbar.cson
@@ -1,76 +0,0 @@
-# For Atom Editor Flex-toolbar package
-[
-  {
-    type: "button"
-    icon: "terminal"
-    callback: "run-in-terminal:editor-start-terminal-here"
-    tooltip: "Open Terminal Here"
-    iconset: "fa"
-  }
-  {
-    type: "button"
-    icon: "object-group"
-    callback: "tree-view:show-current-file-in-file-manager"
-    tooltip: "Open Explorer Here"
-    iconset: "fa"
-  }
-  {
-    type: "spacer"
-  }
-  {
-    type: "button"
-    icon: "check-square"
-    callback: "build:compile"
-    tooltip: "Compile"
-    iconset: "fa"
-    show: "Arduino"
-  }
-  {
-    type: "button"
-    icon: "arrow-circle-up"
-    callback: "build:upload_then_terminal"
-    tooltip: "Build/Upload/Terminal"
-    iconset: "fa"
-    show: "Arduino"
-  }
-  {
-    type: "button"
-    icon: "cloud-upload"
-    callback: "build:upload_ota"
-    tooltip: "Build/Upload OTA"
-    iconset: "fa"
-    show: "Arduino"
-  }
-  {
-    type: "button"
-    icon: "upload"
-    callback: "build:spiffs"
-    tooltip: "Upload spiffs"
-    iconset: "fa"
-    show: "Arduino"
-  }
-  {
-    type: "button"
-    icon: "toggle-up"
-    callback: "build:spiffs_ota"
-    tooltip: "Upload spiffs OTA"
-    iconset: "fa"
-    show: "Arduino"
-  }
-  {
-    type: "button"
-    icon: "question-circle"
-    callback: "build:select-active-target"
-    tooltip: "Build Choose Target"
-    iconset: "fa"
-    show: "Arduino"
-  }
-  {
-    type: "button"
-    icon: "play-circle"
-    callback: "run-in-terminal:editor-start-terminal-here-and-run"
-    tooltip: "Run"
-    iconset: "fa"
-    show: "Python"
-  }
-]


`Item` binded properties code refactored

--- types/item.coffee
+++ types/item.coffee
@@ -59,6 +59,15 @@

 		Object.seal @

+	createBindedSetter = (propName, setFunc) ->
+		(val) ->
+			if typeof val is 'string'
+				setImmediate =>
+					binding = new Binding @, val
+					Impl.setItemBinding @_uid, propName, binding
+			else
+				setFunc.call @, val
+
 	utils.defProp @::, 'id', 'e', ->
 		@_uid
 	, (val) ->
@@ -100,49 +109,29 @@

 	utils.defProp @::, 'width', 'e', ->
 		Impl.getItemWidth @_uid
-	, (val) ->
-		if typeof val is 'number'
-			expect(val).toBe.float()
-			expect(val).not().toBe.lessThan 0
-			Impl.setItemWidth @_uid, val
-		else
-			setImmediate =>
-				binding = new Binding @, val
-				Impl.setItemBinding @_uid, 'width', binding
+	, createBindedSetter 'width', (val) ->
+		expect(val).toBe.float()
+		expect(val).not().toBe.lessThan 0
+		Impl.setItemWidth @_uid, val

 	utils.defProp @::, 'height', 'e', ->
 		Impl.getItemHeight @_uid
-	, (val) ->
-		if typeof val is 'number'
-			expect(val).toBe.float()
-			expect(val).not().toBe.lessThan 0
-			Impl.setItemHeight @_uid, val
-		else
-			setImmediate =>
-				binding = new Binding @, val
-				Impl.setItemBinding @_uid, 'height', binding
+	, createBindedSetter 'height', (val) ->
+		expect(val).toBe.float()
+		expect(val).not().toBe.lessThan 0
+		Impl.setItemHeight @_uid, val

 	utils.defProp @::, 'x', 'e', ->
 		Impl.getItemX @_uid
-	, (val) ->
-		if typeof val is 'number'
-			expect(val).toBe.float()
-			Impl.setItemX @_uid, val
-		else
-			setImmediate =>
-				binding = new Binding @, val
-				Impl.setItemBinding @_uid, 'x', binding
+	, createBindedSetter 'x', (val) ->
+		expect(val).toBe.float()
+		Impl.setItemX @_uid, val

 	utils.defProp @::, 'y', 'e', ->
 		Impl.getItemY @_uid
-	, (val) ->
-		if typeof val is 'number'
-			expect(val).toBe.float()
-			Impl.setItemY @_uid, val
-		else
-			setImmediate =>
-				binding = new Binding @, val
-				Impl.setItemBinding @_uid, 'y', binding
+	, createBindedSetter 'y', (val) ->
+		expect(val).toBe.float()
+		Impl.setItemY @_uid, val

 	utils.defProp @::, 'z', 'e', ->
 		Impl.getItemZ @_uid
@@ -176,6 +165,7 @@
 	utils.defProp @::, 'children', 'e', ->
 		children = Impl.getItemChildren @_uid

+		# TODO: cache array for the optimization
 		for child, i in children
 			children[i] = items[child]


abort splitting by clicking on any node

abort splitting by clicking on any node

--- tree2.coffee
+++ tree2.coffee
@@ -623,22 +623,14 @@
   collection_dropdown_menu: shannon_fano_dropdown_menu

   mousedown: (pos, idx, t) ->
+    if super(pos, idx, t)
+      return true
+
     if @splitting?
-      hit_another = false
-      for n in @nodes
-        if n.isHit(pos)
-          hit_another = true
-          break
-
-      if not hit_another
-        @splitNode(@splitting.node, @splitting.pos0, @splitting.pos1)
-        @splitting = null
-        return true
-
+      @splitNode(@splitting.node, @splitting.pos0, @splitting.pos1)
       @splitting = null
-
-    super pos, idx, t
-
+      return true
+
   mousemove: (pos) ->
     super pos

@@ -669,7 +661,9 @@
     return return_value

   clickend: (pos, t) ->
-    if @selected.node.contains.length > 1
+    if @splitting
+      @splitting = null
+    else if @selected.node.contains.length > 1
       @splitting = node: @selected.node, pos0: pos, pos1: pos

   splitNode: (node, pos0, pos1) ->


spec: Remove "restore" event test

spec: Remove "restore" event test

It is too slow and unstable.

--- spec/api-browser-window-spec.coffee
+++ spec/api-browser-window-spec.coffee
@@ -208,12 +208,3 @@
       w.once 'minimize', -> done()
       w.show()
       w.minimize()
-
-  describe 'restore event', ->
-    return if isCI and process.platform is 'linux'
-    it 'emits when window is restored', (done) ->
-      @timeout 10000
-      w.once 'restore', -> done()
-      w.show()
-      w.minimize()
-      w.restore()


--- spec/api-crash-reporter-spec.coffee
+++ spec/api-crash-reporter-spec.coffee
@@ -17,6 +17,10 @@

   # It is not working on 64bit Windows.
   return if process.platform is 'win32' and process.arch is 'x64'
+
+  # The crash-reporter test is not reliable on CI machine.
+  isCI = remote.process.argv[2] == '--ci'
+  return if isCI

   it 'should send minidump when renderer crashes', (done) ->
     @timeout 120000


Fix IE11 animation support.

Fix IE11 animation support.

--- engine/animation.coffee
+++ engine/animation.coffee
@@ -240,7 +240,10 @@
                         type = orig_chan[0]
                         name = orig_chan[1]
                         prop = orig_chan[2]
-                        blend = v[...].fill(prop=='scale')
+                        blend = v[...]
+                        initial_value = +(prop=='scale') # 1 if scale, 0 otherwise
+                        for _,i in blend
+                            blend[i] = initial_value
                         blend_factor = 1
                     switch strip.blend_type
                         when 'REPLACE'


Updates article fixture

Updates article fixture

--- node/fabricate.coffee
+++ node/fabricate.coffee
@@ -1060,6 +1060,8 @@
         "auction_id": null,
         "show_ids": null,
         "primary_featured_artist_ids": null,
+        "contributing_authors": [],
+        "is_super_article": false,
         "id": _.uniqueId()
       }


add partner_location

add partner_location

--- node/fabricate.coffee
+++ node/fabricate.coffee
@@ -604,6 +604,22 @@
       suggested_next_bid_cents: 325000
       updated_at: "2013-11-14T00:03:27Z"

+    when 'partner_location'
+      id: _.uniqueId()
+      name: 'Eli Ridgway'
+      address: '172 Minna Street'
+      address_2: ''
+      city: 'San Francisco'
+      country: 'United States'
+      state: 'CA'
+      postal_code: '94105'
+      phone: '415-777-1366'
+      coordinates: null
+      position: 1
+      email: 'info@eliridgway.com'
+      fax: ''
+      publicly_viewable: true
+
     when 'order'
       id: _.uniqueId()
       code: '484A30'
@@ -631,21 +647,7 @@
         artwork: fabricate('artwork')
         edition_set: fabricate('edition_set', acquireable: true, sold: false, forsale: true, price: '$1,800' )
         partner: fabricate('partner')
-        partner_location: {
-          name: 'Eli Ridgway'
-          address: '172 Minna Street'
-          address_2: ''
-          city: 'San Francisco'
-          country: 'United States'
-          state: 'CA'
-          postal_code: '94105'
-          phone: '415-777-1366'
-          coordinates: null
-          position: 1
-          email: 'info@eliridgway.com'
-          fax: ''
-          publicly_viewable: true
-        }
+        partner_location: fabricate('partner_location')
       }]

   , extObj


Removed old syncro init code from main coffee file


Slight changes regarding duration of transitions in example of plotframe

--- plotframe/example.coffee
+++ plotframe/example.coffee
@@ -10,8 +10,8 @@
    .attr("cy", (d) -> svgscale.y d.y)
    .attr("r", 5)
    .attr("fill", "slateblue")
-   .on("mouseover", -> d3.select(this).transition().duration(250).attr("fill", "hotpink"))
+   .on("mouseover", -> d3.select(this).transition().attr("fill", "hotpink"))
    .on("mouseover.tooltip", (d,i) -> svgscale.svg.append("text").text(i+1).attr("id", "tooltip")
       .attr("x", svgscale.x(d.x)+10).attr("y", svgscale.y(d.y)).attr("fill","hotpink")
-      .attr("opacity",0).style("font-family", "sans-serif").transition().duration(250).attr("opacity", 1))
-   .on("mouseout", -> d3.select(this).transition().duration(250).attr("fill", "slateblue"); d3.selectAll("#tooltip").transition().duration(250).remove())
+      .attr("opacity",0).style("font-family", "sans-serif").transition().attr("opacity", 1))
+   .on("mouseout", -> d3.select(this).transition().duration(500).attr("fill", "slateblue"); d3.selectAll("#tooltip").transition().duration(500).attr("opacity",0).remove())


add 'own' to be explicit

add 'own' to be explicit

--- plugin/browserify.coffee
+++ plugin/browserify.coffee
@@ -59,7 +59,7 @@
   # and options object for debug and the basedir
   browserify = Browserify [getReadable(step)], browserifyOptions

-  for transformName, transformOptions of browserifyOptions.transforms
+  for own transformName, transformOptions of browserifyOptions.transforms
     browserify.transform transformName, transformOptions

   # have browserify process the file and include all required modules.


Amélioration du code

Amélioration du code

--- js/game/Game.coffee
+++ js/game/Game.coffee
@@ -21,8 +21,8 @@

             # Scenes
             @scenes =
-                loading: new LoadingScene(this)
-                play: new PlayScene(this)
+                loading: new LoadingScene @
+                play: new PlayScene @

             # Scene actuelle
             @scene = @scenes.loading


Sim: Fix Metronome's inconsistent targeting.

--- server/bw/data/moves.coffee
+++ server/bw/data/moves.coffee
@@ -1397,11 +1397,7 @@
     battle.message "Waggling a finger let it use #{move.name}!"

     # Determine new targets
-    if move.target == 'selected-pokemon'
-      pokemon = battle.getOpponents(user)
-      targets = [ battle.rng.choice(pokemon) ]
-    else
-      targets = battle.getTargets(move, user)
+    targets = battle.getTargets(move, user)
     battle.executeMove(move, user, targets)

 extendMove 'Natural Gift', ->


Index Courses by releasePhase

Index Courses by releasePhase

--- server/models/Course.coffee
+++ server/models/Course.coffee
@@ -5,6 +5,8 @@
 {sortCourses} = require '../../app/core/utils'

 CourseSchema = new mongoose.Schema {}, {strict: false, minimize: false, read:config.mongo.readpref}
+
+CourseSchema.index({releasePhase: 1}, {name: 'releasePhase index'})

 CourseSchema.plugin plugins.NamedPlugin
 CourseSchema.plugin plugins.SearchablePlugin, {searchable: ['name', 'description']}


Less time-sensitive test.

Less time-sensitive test.

--- tests.coffee
+++ tests.coffee
@@ -794,7 +794,7 @@
       removedDelta /= removedTimestamps.length - 1

       # Each removed is approximately 91 ms apart. So the average of deltas should be somewhere there.
-      @assertTrue removedDelta > 80
+      @assertTrue removedDelta > 60, removedDelta
   ]

   testClientMultipleCursors: ->


Remove dead variables.

Remove dead variables.

--- test/helpers.coffee
+++ test/helpers.coffee
@@ -2,7 +2,7 @@
 # -------

 # pull the helpers from `CoffeeScript.helpers` into local variables
-{starts, ends, compact, count, merge, extend, flatten, del, last, normalizePath, relativePath} = CoffeeScript.helpers
+{starts, ends, compact, count, merge, extend, flatten, del, last} = CoffeeScript.helpers

 # `starts`


Use .build() in tests.

Use .build() in tests.

--- test/specs.coffee
+++ test/specs.coffee
@@ -10,7 +10,7 @@
     group.specs.forEach (spec) ->
       run = if spec.meta?.pending then xit else if spec.meta?.only then it.only else it
       run spec.name, do (spec) -> ->
-        result = js2coffee(spec.input)
+        result = js2coffee.build(spec.input).code
         try
           expect(result).eql(spec.output)
         catch e


screwing around

screwing around

--- slurp.coffee
+++ slurp.coffee
@@ -1,87 +1,47 @@
-class DirEnt
-  constructor: (@name, @stats) ->
-
-class Dir extends DirEnt
-  constructor: (args...) ->
-    @data: {}
-    super args...
-
-  addEnt: (ent) ->
-    if @data[ent.name]
-      throw new Error 'name already exists'
-
-    @data[ent.name] = ent
-
-  dirList: ->
-    Object.getOwnPropertyNames @data
-
-class File extends DirEnt
-  constructor: (args...) ->
-    @data: ""
-    super args...
-
-  setData: (@data) ->
-
-class Link extends DirEnt
-  constructor: (args...) ->
-    @target: ""
-    super args...
-
-  setTarget: (@target) ->
-
 fs = require 'fs'
 path = require 'path'

-slurpAny = (fullPath, failure, success) ->
-  fs.lstat fullPath, (err, stats) ->
-    if err
-      failure err
+UNSUPPORTED_DIRENT_TYPE =
+  new Error "Unsupported file type for '#{path.join @path, name}'"

-    else if stats.isFile()
-      slurpFile fullPath, failure, (f) ->
-        f.stats = stats
-        success f
+class DirEnt
+  constructor: ({@path, @stat}) ->
+    if not @stat
+      @stat = new Promise (resolve, reject) ->
+        fs.lstat @path, (err, stat) ->
+          reject err if err

-    else if stats.isDirectory()
-      slurpDir fullPath, failure, (d) ->
-        d.stats = stats
-        success d
+          @stat = stat

-    else if stats.isSymbolicLink()
-      slurpLink fullPath, failure, (l) ->
-        l.stats = stats
-        success l
+class Dir extends DirEnt
+  constructor: ({@path}) ->
+    super

-slurpDir = (fullPath, success, failure) ->
-  fs.readdir fullPath, (err, entries) ->
-    if err
-      failure err
-    else
-      slurpEntries dir, entries, success, failure
+    @_scan()

-slurpEntries = (dir, entry, entries..., success, failure) ->
-  slurpAny dir, entry, failure, (e) ->
-    dir.addEnt e
+  _scan: ->
+    new Promise (resolve, reject) ->
+      fs.readdir @path, (err, entries) ->
+        reject err if err

-    if entries
-      slurpEntries dir, entries, success, failure
-    else
-      success dir
+        @_addEntry name for name in entries

-slurpFile = (fullPath, failure, success) ->
-  fs.readFile fullPath, (err, data) ->
-    if err
-      failure err
-    else
-      ent = new File path.basename fullPath
-      ent.setData data
-      success ent
+  @_addEntry: (name) ->
+    self = this
+    new Promise (resolve, reject) ->
+      fs.lstat path.join(@path, name), (err, stat) ->
+        reject err if err

-slurpLink = (fullPath, failure, success) ->
-  fs.readLink fullPath, (err, target) ->
-    if err
-      failure err
-    else
-      link = new Link path.basename fullPath
-      link.setTarget target
-      success link
+        klass = if stat.isDirectory() then Dir
+          else if stat.isFile() then File
+          else if stat.isSymbolicLink() then SymLink
+          else throw UNSUPPORTED_DIRENT_TYPE
+
+        o = new klass name: name, stat: stat
+        Object.defineProperty self, name, value: o
+        resolve o
+
+class File extends DirEnt
+
+class Link extends DirEnt
+


geoip: shareLocation button is added to AccountSetting in koding

--- client/account/lib/views/accounteditusername.coffee
+++ client/account/lib/views/accounteditusername.coffee
@@ -15,6 +15,7 @@
 VerifyPINModal = require 'app/commonviews/verifypinmodal'
 VerifyPasswordModal = require 'app/commonviews/verifypasswordmodal'
 sinkrow = require 'sinkrow'
+KodingSwitch = require 'app/commonviews/kodingswitch'

 module.exports = class AccountEditUsername extends JView
@@ -56,6 +57,9 @@
       title          : 'Use Gravatar'
       callback       : =>
         @account.modify "profile.avatar": ""
+
+    #@switch = new KodingSwitch
+

     @emailForm = new KDFormViewWithFields
       cssClass             : 'AppModal-form'
@@ -100,6 +104,12 @@
           name             : "confirmPassword"
           type             : "password"
           label            : 'Password (again)'
+        shareLocation      :
+          label            : 'Share my location while posting'
+          defaultValue     : whoami().shareLocation
+          itemClass        : KodingSwitch
+          cssClass         : 'tiny'
+          name             : 'shareLocation'
       buttons              :
         Save               :
           title            : 'Save Changes'
@@ -153,21 +163,22 @@
   update: (formData) ->

     {JUser} = remote.api
-    {email, password, confirmPassword, firstName, lastName, username} = formData
-
+    {email, password, confirmPassword, firstName, lastName, username, shareLocation} = formData
+
+    console.log shareLocation
+
     profileUpdated = yes
     skipPasswordConfirmation = no
     queue = [
       =>
         # update firstname and lastname
         me = whoami()
-        {profile:{firstName:oldFirstName, lastName:oldLastName}} = me
-        # do not do anything if current firstname and lastname is same
-        return queue.next() if oldFirstName is firstName and oldLastName is lastName
+        console.log me

         me.modify {
           "profile.firstName": firstName,
           "profile.lastName" : lastName
+          "shareLocation"    : formData.shareLocation
         }, (err)->
           return notify err.message  if err
           queue.next()
@@ -278,12 +289,14 @@

     {email} = @userInfo
     {nickname, firstName, lastName} = @account.profile
-
+
+    console.log @emailForm
     @emailForm.inputs.email.setDefaultValue Encoder.htmlDecode email
     @emailForm.inputs.username.setDefaultValue Encoder.htmlDecode nickname
     @emailForm.inputs.firstName.setDefaultValue Encoder.htmlDecode firstName
     @emailForm.inputs.lastName.setDefaultValue Encoder.htmlDecode lastName
-
+    @emailForm.inputs.shareLocation.setDefaultValue whoami().shareLocation
+    console.log whoami()
     {focus} = kd.utils.parseQuery()
     @emailForm.inputs[focus]?.setFocus()  if focus


reactivity: change to suggestions max query length

--- client/activity/lib/flux/actions/suggestions.coffee
+++ client/activity/lib/flux/actions/suggestions.coffee
@@ -2,7 +2,7 @@
 actionTypes = require '../actions/actiontypes'
 getGroup    = require 'app/util/getGroup'

-MAX_QUERY_LENGTH      = 50
+MAX_QUERY_LENGTH      = 10
 NUMBER_OF_SUGGESTIONS = 5

 ###*


contentModal: add testpath to buttons but cancel or no

--- client/app/lib/components/contentModal/index.coffee
+++ client/app/lib/components/contentModal/index.coffee
@@ -60,4 +60,6 @@
       button = new kd.ButtonView buttons[key]
       if buttons[key].title is 'Cancel' or buttons[key].title is 'No'
         button.setClass 'cancel'
+      else
+        button.setAttribute 'testpath', 'proceed'
       @footer.addSubView button


test: reorganize and add granularity to TRIV tests

clean up TimeRangeInputView tests for values

--- coffee/test/views/time_range_input_view.spec.coffee
+++ coffee/test/views/time_range_input_view.spec.coffee
@@ -42,43 +42,76 @@
       expect(widgets.endInputView instanceof TimeInputView).toBe true
       expect(widgets.totalTime.length).toBe 1

-    it 'should accept initialization values and provide read/write access ' +
-        'through a `values` property', ->
-      [triv, widgets] = setupTRIV
-        start: 0
-        end: 50
-
-      expect(triv.values().start).toBe 0
-      expect(triv.values().end).toBe 50
-
-      triv.values start: 25
-      expect(triv.values().start).toBe 25
-      expect(triv.values().end).toBe 50
-
-      triv.values end: 35
-      expect(triv.values().start).toBe 25
-      expect(triv.values().end).toBe 35
-
-      triv.values {start: 10, end: 40}
-      expect(triv.values().start).toBe 10
-      expect(triv.values().end).toBe 40
-
-    it 'should propagate values to its range slider and time inputs', ->
-      [triv, widgets] = setupTRIV
-        start: 0
-        end: 50
-        max: 100
-
-      expect(widgets.rangeSliderView.values()[0]).toBe 0
-      expect(widgets.rangeSliderView.values()[1]).toBe 50
-      expect(widgets.startInputView.value()).toBe 0
-      expect(widgets.endInputView.value()).toBe 50
-
-      triv.values {start: 10, end: 40}
-      expect(widgets.rangeSliderView.values()[0]).toBe 10
-      expect(widgets.rangeSliderView.values()[1]).toBe 40
-      expect(widgets.startInputView.value()).toBe 10
-      expect(widgets.endInputView.value()).toBe 40
+
+    describe 'TimeRangeInputView::values', ->
+
+      it 'should provide read access to start and end through a `values`
+          property', ->
+        [triv, widgets] = setupTRIV
+          start: 0
+          end: 50
+
+        expect(triv.values().start).toBe 0
+        expect(triv.values().end).toBe 50
+
+        triv._start = 25
+        expect(triv.values().start).toBe 25
+        expect(triv.values().end).toBe 50
+
+        triv._end= 35
+        expect(triv.values().start).toBe 25
+        expect(triv.values().end).toBe 35
+
+        triv._start = 10
+        triv._end = 40
+        expect(triv.values().start).toBe 10
+        expect(triv.values().end).toBe 40
+
+      it 'should provide write access to start and end through a `values`
+          property', ->
+        [triv, widgets] = setupTRIV
+          start: 0
+          end: 50
+
+        expect(triv.values().start).toBe 0
+        expect(triv.values().end).toBe 50
+        expect(triv._start).toBe 0
+        expect(triv._end).toBe 50
+
+        triv.values start: 25
+        expect(triv.values().start).toBe 25
+        expect(triv.values().end).toBe 50
+        expect(triv._start).toBe 25
+        expect(triv._end).toBe 50
+
+        triv.values end: 35
+        expect(triv.values().start).toBe 25
+        expect(triv.values().end).toBe 35
+        expect(triv._start).toBe 25
+        expect(triv._end).toBe 35
+
+        triv.values {start: 10, end: 40}
+        expect(triv.values().start).toBe 10
+        expect(triv.values().end).toBe 40
+        expect(triv._start).toBe 10
+        expect(triv._end).toBe 40
+
+      it 'should propagate values to its range slider and time inputs', ->
+        [triv, widgets] = setupTRIV
+          start: 0
+          end: 50
+          max: 100
+
+        expect(widgets.rangeSliderView.values()[0]).toBe 0
+        expect(widgets.rangeSliderView.values()[1]).toBe 50
+        expect(widgets.startInputView.value()).toBe 0
+        expect(widgets.endInputView.value()).toBe 50
+
+        triv.values {start: 10, end: 40}
+        expect(widgets.rangeSliderView.values()[0]).toBe 10
+        expect(widgets.rangeSliderView.values()[1]).toBe 40
+        expect(widgets.startInputView.value()).toBe 10
+        expect(widgets.endInputView.value()).toBe 40

     describe 'TimeRangeInputView: min and max', ->


don't print out db on loading c

don't print out db on loading c

--- src/c.coffee
+++ src/c.coffee
@@ -32,7 +32,8 @@

 # get a connection to the db
 global.db = (cb) ->
-    return global.db = get_db(cb)
+    global.db = get_db(cb)
+    return
 console.log("db() -- sets global variable db to a database")

 global.gcloud = ->


Added laravel ___ translate directive snippet

--- snippets/laravel_blade.cson
+++ snippets/laravel_blade.cson
@@ -1,4 +1,4 @@
-'.text.html.php.blade, .text.html.php':
+'.php.blade, .html.php':

   'translate blade directive':
     'prefix': 'lang'
@@ -6,11 +6,11 @@
     'rightLabelHTML': 'Blade @lang(\'key.val\')'
     'body': "@lang('${1:file_name}.${2:key_name})"

-  'translate blade directive':
+  'translate blade directive trans':
     'prefix': '__'
     'leftLabelHTML': 'Ⓢ'
     'rightLabelHTML': 'Blade {{ __(\'key.val\') }}'
-    'body': "{{ __('${1:file_name}.${2:key_name}) }}"
+    'body': "{{ __('${1:file_name}.${2:key_name}') }}"

   'translate blade directive':
     'prefix': 'lara:translation-file-declaration'


Updated simulate('click') snippet

Updated simulate('click') snippet

--- snippets/simulateClick.cson
+++ snippets/simulateClick.cson
@@ -1,5 +1,5 @@
 ".source.js, .source.jsx, .source.ts":
-  "simulate("click")":
+  "simulate('click')":
     "prefix": "simClick"
     "body": """
       ${1:btn}.simulate("click", ${2:{}});


Fix bug in failing editor spec

Fix bug in failing editor spec

--- spec/app/editor-spec.coffee
+++ spec/app/editor-spec.coffee
@@ -252,20 +252,20 @@
         path = "/tmp/atom-changed-file.txt"
         fs.write(path, "")
         editSession = project.buildEditSessionForPath(path)
+        editor.edit editSession
         editSession.insertText("a buffer change")

-        fileChangeHandler = jasmine.createSpy('fileChange')
-        editSession.buffer.file.on 'contents-change', fileChangeHandler
+        bufferContentsChangeHandler = jasmine.createSpy('fileChange')
+        editSession.on 'buffer-contents-change-on-disk', bufferContentsChangeHandler

         spyOn($native, "alert")

         fs.write(path, "a file change")

         waitsFor "file to trigger contents-change event", ->
-          fileChangeHandler.callCount > 0
+          bufferContentsChangeHandler.callCount > 0

         runs ->
-          editor.edit(editSession)
           expect($native.alert).toHaveBeenCalled()

     describe ".loadNextEditSession()", ->


Convert failing specs to linewise code folding

--- spec/app/editor-spec.coffee
+++ spec/app/editor-spec.coffee
@@ -584,17 +584,9 @@

     describe "when there are folds", ->
       it "skips line numbers", ->
-        editor.createFold([[3, 10], [5, 1]])
+        editor.createFold(3, 5)
         expect(editor.gutter.find('.line-number:eq(3)').text()).toBe '4'
         expect(editor.gutter.find('.line-number:eq(4)').text()).toBe '7'
-
-    describe "when there is a fold on the last screen line of a wrapped line", ->
-      it "renders line numbers correctly when the fold is destroyed (regression)", ->
-        setEditorHeightInLines(editor, 20)
-        editor.setMaxLineLength(50)
-        fold = editor.createFold([[3, 52], [3, 56]])
-        fold.destroy()
-        expect(editor.gutter.find('.line-number:last').text()).toBe '13'

     describe "when the scrollView is scrolled to the right", ->
       it "adds a drop shadow to the gutter", ->
@@ -905,46 +897,50 @@
         beforeEach ->
           setEditorWidthInChars(editor, 50)
           editor.setSoftWrap(true)
-          editor.createFold(new Range([3, 3], [3, 7]))
+          editor.createFold(2, 3)

         describe "when it is a single click", ->
           it "re-positions the cursor from the clicked screen position to the corresponding buffer position", ->
             expect(editor.getCursorScreenPosition()).toEqual(row: 0, column: 0)
-            editor.visibleLines.trigger mousedownEvent(editor: editor, point: [4, 7])
-            expect(editor.getCursorBufferPosition()).toEqual(row: 3, column: 58)
+            editor.visibleLines.trigger mousedownEvent(editor: editor, point: [9, 0])
+            expect(editor.getCursorBufferPosition()).toEqual(row: 8, column: 11)

         describe "when it is a double click", ->
           it "selects the word under the cursor", ->
             expect(editor.getCursorScreenPosition()).toEqual(row: 0, column: 0)
-            editor.visibleLines.trigger mousedownEvent(editor: editor, point: [4, 3], originalEvent: {detail: 1})
+            editor.visibleLines.trigger mousedownEvent(editor: editor, point: [9, 0], originalEvent: {detail: 1})
             editor.visibleLines.trigger 'mouseup'
-            editor.visibleLines.trigger mousedownEvent(editor: editor, point: [4, 3], originalEvent: {detail: 2})
-            expect(editor.getSelectedText()).toBe "right"
+            editor.visibleLines.trigger mousedownEvent(editor: editor, point: [9, 0], originalEvent: {detail: 2})
+            expect(editor.getSelectedText()).toBe "sort"

         describe "when it is clicked more then twice (triple, quadruple, etc...)", ->
           it "selects the line under the cursor", ->
             expect(editor.getCursorScreenPosition()).toEqual(row: 0, column: 0)

             # Triple click
-            editor.visibleLines.trigger mousedownEvent(editor: editor, point: [4, 3], originalEvent: {detail: 1})
+            point = [9, 3]
+            editor.visibleLines.trigger mousedownEvent(editor: editor, point: point, originalEvent: {detail: 1})
             editor.visibleLines.trigger 'mouseup'
-            editor.visibleLines.trigger mousedownEvent(editor: editor, point: [4, 3], originalEvent: {detail: 2})
+            editor.visibleLines.trigger mousedownEvent(editor: editor, point: point, originalEvent: {detail: 2})
             editor.visibleLines.trigger 'mouseup'
-            editor.visibleLines.trigger mousedownEvent(editor: editor, point: [4, 3], originalEvent: {detail: 3})
+            editor.visibleLines.trigger mousedownEvent(editor: editor, point: point, originalEvent: {detail: 3})
             editor.visibleLines.trigger 'mouseup'
-            expect(editor.getSelectedText()).toBe "    var pivot = items.shift(), current, left = [], right = [];"
+            expect(editor.getSelectedText()).toBe "    return sort(left).concat(pivot).concat(sort(right));"
+
+            editor.logLines()

             # Quad click
-            editor.visibleLines.trigger mousedownEvent(editor: editor, point: [8, 3], originalEvent: {detail: 1})
+            point = [12, 3]
+            editor.visibleLines.trigger mousedownEvent(editor: editor, point: point, originalEvent: {detail: 1})
             editor.visibleLines.trigger 'mouseup'
-            editor.visibleLines.trigger mousedownEvent(editor: editor, point: [8, 3], originalEvent: {detail: 2})
+            editor.visibleLines.trigger mousedownEvent(editor: editor, point: point, originalEvent: {detail: 2})
             editor.visibleLines.trigger 'mouseup'
-            editor.visibleLines.trigger mousedownEvent(editor: editor, point: [8, 3], originalEvent: {detail: 3})
+            editor.visibleLines.trigger mousedownEvent(editor: editor, point: point, originalEvent: {detail: 3})
             editor.visibleLines.trigger 'mouseup'
-            editor.visibleLines.trigger mousedownEvent(editor: editor, point: [8, 3], originalEvent: {detail: 4})
+            editor.visibleLines.trigger mousedownEvent(editor: editor, point: point, originalEvent: {detail: 4})
             editor.visibleLines.trigger 'mouseup'

-            expect(editor.getSelectedText()).toBe "      current < pivot ? left.push(current) : right.push(current);"
+            expect(editor.getSelectedText()).toBe "  return sort(Array.apply(this, arguments));"

       describe "when soft-wrap is disabled", ->
         describe "when it is a single click", ->
@@ -1184,13 +1180,6 @@
           editor.insertText("\n")
           expect(editor.buffer.lineForRow(2)).toEqual("    ")

-      describe "when a newline is inserted following a fold placeholder", ->
-        it "indents cursor based on the indentation of previous buffer line", ->
-          editor.createFold([[1, 10], [1, 30]])
-          editor.setCursorBufferPosition([1, 30])
-          editor.insertText("\n")
-          expect(editor.buffer.lineForRow(2)).toEqual("    ")
-
       describe "when text beginning with a newline is inserted", ->
         it "indents cursor based on the indentation of previous buffer line", ->
           editor.setCursorBufferPosition([4, 29])


:green_heart: Fix TravisCI failures

:green_heart: Fix TravisCI failures

--- spec/csv-editor-spec.coffee
+++ spec/csv-editor-spec.coffee
@@ -39,8 +39,10 @@
         csvEditor = t
         csvEditorElement = atom.views.getView(csvEditor)

+    runs -> expect(csvEditor.file).toBeDefined()
+
   modifyAndSave = (block) ->
-    waitsFor ->
+    waitsFor 'tablr editor attached to DOM', ->
       csvEditorElement.querySelector('tablr-editor')

     runs block
@@ -55,7 +57,7 @@

       tableEditor.save()

-    waitsFor -> fs.writeFile.callCount > 0
+    waitsFor 'file written on disk', -> fs.writeFile.callCount > 0

   beforeEach ->
     [csvEditor, csvEditorElement, csvDest, projectPath, tableEditor, tableEditorElement, openSpy, destroySpy, savedContent, spy] = []
@@ -119,7 +121,7 @@
           1,2
           """)

-          waitsFor -> changeSpy.callCount > 0
+          waitsFor 'change callback called', -> changeSpy.callCount > 0

         it 'updates the preview', ->
           expect(csvEditorElement.updatePreview).toHaveBeenCalled()
@@ -132,7 +134,7 @@
           tableEditorButton = csvEditorElement.form.openTableEditorButton
           click(tableEditorButton)

-          waitsFor -> tableEditor?
+          waitsFor 'table editor created', -> tableEditor?

         describe 'and the new content can be parsed with the current settings', ->
           describe 'when the table is in an unmodified state', ->
@@ -142,7 +144,7 @@
               1,2
               """)

-              waitsFor -> changeSpy.callCount > 1
+              waitsFor 'change callback called', -> changeSpy.callCount > 0

             it 'replaces the table content with the new one', ->
               nextAnimationFrame()
@@ -168,8 +170,8 @@
               1,2
               """)

-              waitsFor -> changeSpy.callCount > 1
-              waitsFor -> conflictSpy.callCount > 0
+              waitsFor 'change callback called', -> changeSpy.callCount > 0
+              waitsFor 'conflict callback called', -> conflictSpy.callCount > 0

             it 'leaves the table in a modified state', ->
               expect(csvEditor.editor).toBe(tableEditor)
@@ -183,7 +185,7 @@
               "1";"2"
               """)

-              waitsFor -> changeSpy.callCount > 1
+              waitsFor 'change callback called', -> changeSpy.callCount > 0
               waitsFor -> csvEditor.editor is undefined

             it 'replaces the table content with a form', ->
@@ -202,7 +204,7 @@
                 tableEditorButton = csvEditorElement.form.openTableEditorButton
                 click(tableEditorButton)

-                waitsFor -> tableEditor?
+                waitsFor 'table editor created', -> tableEditor?

               it 'now opens the new version of the file', ->
                 expect(csvEditor.editor.getColumns()).toEqual([undefined, undefined])
@@ -228,7 +230,7 @@
           fsp.removeSync(newPath)
           fsp.moveSync(csvEditor.getPath(), newPath)

-        waitsFor -> spy.callCount > 0
+        waitsFor 'change path callback called', -> spy.callCount > 0

       it 'detects the change in path', ->
         expect(spy).toHaveBeenCalledWith(newPath)
@@ -258,14 +260,14 @@
           tableEditorButton = csvEditorElement.form.openTableEditorButton
           click(tableEditorButton)

-        waitsFor -> tableEditor?
+        waitsFor 'table editor created', -> tableEditor?

       describe 'when the table is modified', ->
         beforeEach ->
           csvEditor.editor.addRow()

           fsp.removeSync(csvDest)
-          waitsFor -> deleteSpy.callCount > 0
+          waitsFor 'delete callback called', -> deleteSpy.callCount > 0

         it "retains its path and reports the buffer as modified", ->
           expect(csvEditor.getPath()).toBe(csvDest)
@@ -274,7 +276,7 @@
       describe 'when the table is not modified', ->
         beforeEach ->
           fsp.removeSync(csvDest)
-          waitsFor -> deleteSpy.callCount > 0
+          waitsFor 'delete callback called', -> deleteSpy.callCount > 0

         it "retains its path and reports the buffer as not modified", ->
           expect(csvEditor.getPath()).toBe(csvDest)
@@ -283,7 +285,7 @@
       describe 'when resaved', ->
         it 'recreates the file on disk', ->
           fsp.removeSync(csvDest)
-          waitsFor -> deleteSpy.callCount > 0
+          waitsFor 'delete callback called', -> deleteSpy.callCount > 0
           runs -> expect(fs.existsSync(csvDest)).toBeFalsy()
           waitsForPromise -> csvEditor.save()
           runs -> expect(fs.existsSync(csvDest)).toBeTruthy()
@@ -299,8 +301,7 @@
           textEditorButton = csvEditorElement.form.openTextEditorButton
           click(textEditorButton)

-        waitsFor ->
-          destroySpy.callCount > 0
+        waitsFor 'destroy callback called', -> destroySpy.callCount > 0

       it 'opens a new text editor for that file', ->
         expect(atom.workspace.getActiveTextEditor()).toBeDefined()
@@ -329,13 +330,13 @@
           destroySpy = jasmine.createSpy('did-destroy')
           csvEditor.onDidDestroy(destroySpy)

-        waitsFor sleep CHANGE_TIMEOUT
+        waitsFor "for #{CHANGE_TIMEOUT}ms", sleep CHANGE_TIMEOUT

         runs ->
           textEditorButton = csvEditorElement.form.openTextEditorButton
           click(textEditorButton)

-        waitsFor -> destroySpy.callCount > 0
+        waitsFor 'destroy callback called', -> destroySpy.callCount > 0

         runs ->
           csvEditor.destroy()
@@ -360,7 +361,7 @@
             tableEditorButton = csvEditorElement.form.openTableEditorButton
             click(tableEditorButton)

-          waitsFor -> tableEditor?
+          waitsFor 'table editor created', -> tableEditor?

         it 'has a table filled with the file content', ->
           expect(tableEditor.getScreenColumnCount()).toEqual(3)
@@ -385,7 +386,7 @@
           expect(tableElement.focus).toHaveBeenCalled()

         it 'clears the csv editor content and replace it with a table element', ->
-          waitsFor ->
+          waitsFor 'tablr editor attached to DOM', ->
             tableEditorElement = csvEditorElement.querySelector('tablr-editor')

           runs ->
@@ -442,7 +443,7 @@
                 tableEditorButton = secondCSVEditorElement.form.openTableEditorButton
                 click(tableEditorButton)

-            waitsFor ->
+            waitsFor 'second editor choice applied', ->
               secondCSVEditor.editor

           it 'reuses the same table for two different table editors', ->
@@ -479,7 +480,7 @@
                     tableEditorButton = csvEditorElement.form.openTableEditorButton
                     click(tableEditorButton)

-                waitsFor -> tableEditor?
+                waitsFor 'table editor created', -> tableEditor?

               it 'returns a living editor', ->
                 expect(tableEditor.table.getColumnCount()).toEqual(3)
@@ -551,10 +552,12 @@
           click(tableEditorButton)

       it 'displays the error in the csv preview', ->
-        waitsFor -> csvEditorElement.querySelector('atom-csv-preview .alert')
+        waitsFor 'csv alert displayed in the DOM', ->
+          csvEditorElement.querySelector('atom-csv-preview .alert')

       it 'disables the open table action', ->
-        waitsFor -> csvEditorElement.form.openTableEditorButton.disabled
+        waitsFor 'open button disabled', ->
+          csvEditorElement.form.openTableEditorButton.disabled

     describe 'changing the delimiter settings', ->
       beforeEach ->
@@ -565,13 +568,13 @@
           csvEditor.onDidOpen ({editor}) ->
             tableEditor = editor

-        waitsFor sleep CHANGE_TIMEOUT
+        waitsFor "for #{CHANGE_TIMEOUT}ms", sleep CHANGE_TIMEOUT

         runs ->
           tableEditorButton = csvEditorElement.form.openTableEditorButton
           click(tableEditorButton)

-        waitsFor -> tableEditor?
+        waitsFor 'table editor created', -> tableEditor?

       it 'now parses the table data properly', ->
         expect(tableEditor).toBeDefined()
@@ -604,13 +607,13 @@
           csvEditor.onDidOpen ({editor}) ->
             tableEditor = editor

-        waitsFor sleep CHANGE_TIMEOUT
+        waitsFor "for #{CHANGE_TIMEOUT}ms", sleep CHANGE_TIMEOUT

         runs ->
           tableEditorButton = csvEditorElement.form.openTableEditorButton
           click(tableEditorButton)

-        waitsFor -> tableEditor?
+        waitsFor 'table editor created', -> tableEditor?

       it 'now parses the table data properly', ->
         expect(tableEditor).toBeDefined()
@@ -647,13 +650,13 @@
           csvEditor.onDidOpen ({editor}) ->
             tableEditor = editor

-        waitsFor sleep CHANGE_TIMEOUT
+        waitsFor "for #{CHANGE_TIMEOUT}ms", sleep CHANGE_TIMEOUT

         runs ->
           tableEditorButton = csvEditorElement.form.openTableEditorButton
           click(tableEditorButton)

-        waitsFor -> tableEditor?
+        waitsFor 'table editor created', -> tableEditor?

       it 'now parses the table data properly', ->
         expect(tableEditor).toBeDefined()
@@ -696,7 +699,7 @@
           tableEditorButton = csvEditorElement.form.openTableEditorButton
           click(tableEditorButton)

-        waitsFor -> tableEditor?
+        waitsFor 'table editor created', -> tableEditor?

       it 'uses this layout to setup the display table', ->
         expect(tableEditor.getScreenRowHeightAt(1)).toEqual(100)
@@ -747,7 +750,7 @@
       atom.packages.observeDisabledPackages()
       atom.packages.disablePackage('tablr')

-      waitsFor -> tableEditPackage.deactivate.callCount > 0
+      waitsFor "package deactivated", -> tableEditPackage.deactivate.callCount > 0

     it 'disposes the csv opener', ->
       editor = null
@@ -881,7 +884,7 @@
               ]
           })

-          waitsFor -> tableEditor = csvEditor.editor
+          waitsFor 'table editor restored', -> tableEditor = csvEditor.editor
           runs ->
             expect(tableEditor.getScreenRowHeightAt(1)).toEqual(100)
             expect(tableEditor.getScreenRowHeightAt(2)).toEqual(200)
@@ -921,7 +924,7 @@
               selections: [[[0,0],[1,1]]]
           })

-          waitsFor -> tableEditor = restored.editor
+          waitsFor 'table editor restored', -> tableEditor = restored.editor
           runs ->
             expect(restored.isModified()).toBeTruthy()
             expect(restored.editor.getColumns()).toEqual([null, null, null])


UI/core adjusting the modal max-size offsets

--- ui/core/src/modal.coffee
+++ ui/core/src/modal.coffee
@@ -6,8 +6,8 @@
 class Modal extends Element
   extend:
     current: null
-    offsetX: 50
-    offsetY: 50
+    offsetX: 40
+    offsetY: 40

   #
   # Basic constructor


Updating visibility fixed

Updating visibility fixed

--- style.coffee
+++ style.coffee
@@ -31,10 +31,6 @@
 				listenTextRec style, child

 		return
-
-	visibilityChangeListener = ->
-		if @file.isRendered
-			@updateVisibility()

 	textChangeListener = ->
 		if @file.isRendered
@@ -667,14 +663,26 @@
 		# changes
 		clone.node.onAttrsChange attrsChangeListener, clone

-		# visibility changes
-		tmpNode = clone.node
-		loop
-			if tmpNode.attrs.has('neft:if') or tmpNode.attrs.has('neft:else')
-				tmpNode.onVisibleChange visibilityChangeListener, clone
-
-			tmpNode = tmpNode.parent
-			if not tmpNode or tmpNode.attrs.has('neft:style')
-				break
-
 		clone
+
+	# synchronize visibility
+	{Tag} = File.Element
+	opts = utils.CONFIGURABLE
+	getter = utils.lookupGetter Tag::, 'visible'
+	setter = utils.lookupSetter Tag::, 'visible'
+	utils.defineProperty Tag::, 'visible', opts, getter, do (_super = setter) ->
+		updateVisibility = (node) ->
+			if node._style
+				node._documentStyle.updateVisibility()
+			else if node instanceof Tag
+				for child in node.children
+					updateVisibility child
+			return
+
+		(val) ->
+			if _super.call @, val
+				updateVisibility @
+				true
+			false
+
+	Style


Trigger event on select2 modal open

Trigger event on select2 modal open

--- app/assets/javascripts/ab_admin/components/select2_bridge.js.coffee
+++ app/assets/javascripts/ab_admin/components/select2_bridge.js.coffee
@@ -15,7 +15,7 @@
         e.preventDefault()
         @modal.data('target', this)
         fn = =>
-          @modal.modal()
+          @runModal()
         $.get $el.attr('href'), {modal: true}, fn, 'script'

   buildOptions: ->
@@ -33,6 +33,10 @@
       @initCreateChoiceOnce()
       @initCreateChoice()
     @options
+
+  runModal: ->
+    @el.trigger('select2.modal_open')
+    @modal.modal()

   initSortable: ->
     @el.select2('container').find('ul.select2-choices').sortable
@@ -52,7 +56,7 @@
         @modal.data('target', this)
         fn = =>
           $('#modal_form form input[name$="[name]"]').val(term)
-          @modal.modal()
+          @runModal()
         $.get @el.data('add'), {modal: true}, fn, 'script'
       null

@@ -92,7 +96,7 @@

   initAjaxInput: ->
     if @el.data('image')
-      @buildimageOptions()
+      @buildImageOptions()
     if @el.data('result')
       @options.formatResult = (item) => fetchTemplate(@el.data('result'))(item)
     if @el.data('selection')
@@ -112,7 +116,7 @@
         results: data
         more: data.length > 0

-  buildimageOptions: ->
+  buildImageOptions: ->
     @options.formatResult = (item) ->
       html = '
'
       html += "" if item.image


DatasetEditView added

DatasetEditView added

--- app/assets/javascripts/admin/views/dataset/dataset_edit_view.coffee
+++ app/assets/javascripts/admin/views/dataset/dataset_edit_view.coffee
@@ -30,7 +30,7 @@

                 <input type="checkbox" id="isPublic" name="isPublic" <%- isChecked(isPublic) %>>
-                isPublic
+                  publicly accessible


clean up cloudinary-uploads.js.coffee spacing

--- app/assets/javascripts/hanuman/hanuman-cloudinary-uploads.js.coffee
+++ app/assets/javascripts/hanuman/hanuman-cloudinary-uploads.js.coffee
@@ -53,8 +53,6 @@
     if name.match(regex) != null && className !=  file+"-hidden-input"
       $(obj).remove()

-
-
 # ***** PHOTOS *****
 @bindPhotoUploads = ->
   $('.survey-photo-upload').on 'click', (e, data) ->
@@ -72,15 +70,12 @@
     else
       photoIdx = imgCount + 1

-    console.log photoIdx + " photo"
-
     $(e.target).siblings('.progress').find('.photo-progress-bar').removeAttr("style")
     $(e.target).siblings('.progress').addClass('hidden')

     $photoPreviewContainer = $(e.target).siblings('.photo-preview-container')
     $photoPreviewContainer.append "
" + $.cloudinary.image(data.result.public_id, format: data.result.format, version: data.result.version, crop: 'fill', width: 350).prop('outerHTML') + "
"
     addTexareaForUpload("photo", data, photoIdx, $photoPreviewContainer)
-

   # handle errors
   $('.cloudinary-fileupload.survey-photo-upload').bind 'fileuploadfail', (e, data) ->
@@ -106,7 +101,6 @@
     else
       videoIdx = vidCount + 1

-    console.log videoIdx + " video"
     $(e.target).siblings('.progress').find('.video-progress-bar').removeAttr("style")
     $(e.target).siblings('.progress').addClass('hidden')

@@ -121,13 +115,11 @@
       $(".survey-video-upload").on 'click', (e, data) ->
         $(e.target).siblings('.video-upload-error').find('p').remove()

-
   # handle errors
   $('.cloudinary-fileupload.survey-video-upload').bind 'fileuploadfail', (e, data) ->
     $(e.target).siblings('.video-upload-error').append "
 Failed to upload video, please try again
"
     $(".survey-video-upload").on 'click', ->
       $(e.target).siblings('.video-upload-error').find('p').remove()
-

 # ***** DOCUMENTS *****
 @bindDocumentUploads = ->
@@ -146,8 +138,6 @@
     else
       docIdx = docCount + 1

-    console.log docIdx + " document"
-
     $(e.target).siblings('.progress').find('.document-progress-bar').removeAttr("style")
     $(e.target).siblings('.progress').addClass('hidden')

@@ -163,8 +153,6 @@
     addTexareaForUpload("document", data, docIdx, $documentPreviewContainer)
     if data.result.format != "pdf" && data.result.format != "png" && data.result.format != "jpg"
       $('.document-preview img:last').attr('src', '/assets/file-icon.png')
-
-    docIdx += 1

   # validating upload format. If fomat to supported then handle error
     if data.result.format != undefined
@@ -221,7 +209,6 @@
     $(fileToDelete).find("."+file+"-preview, .upload-view-mode:visible").each (idx, element) ->
       $(element).find('.upload-sort-order').val(idx+1)

-
   if $('.edit-mode-file').length > 0
     removeFileHiddenInput()
     $('input[type=submit]').on 'click',(e) ->
@@ -266,9 +253,6 @@
         if !emptyPhotoContainer || $($('.photo-upload')[i]).closest('.photo-column').find('.photo-preview-container').html() != ""
           $($('.photo-upload')[i]).removeAttr('data-parsley-required')

-
-  # instantiate cloudinary file upload (direct upload)
-
   # unbind
   $.cleanData( $('input.cloudinary-fileupload[type=file]') )


Don't scroll for links to document fragments

--- app/main.cjsx
+++ app/main.cjsx
@@ -15,7 +15,9 @@
 # make sure project stats page does not scroll back to the top when the URL changes
 shouldUpdateScroll = (prevRouterProps, routerProps) ->
   pathname = routerProps.location.pathname.split('/')
-  if ('stats' in pathname) and ('projects' in pathname)
+  isStats = ('stats' in pathname) and ('projects' in pathname)
+  hashChange = routerProps.location.hash.length
+  if isStats or hashChange
     false
   else
     true


Moved rendering to logical place

Moved rendering to logical place

--- core/app/backbone/views/facts/fact_base_view.coffee
+++ core/app/backbone/views/facts/fact_base_view.coffee
@@ -12,10 +12,6 @@
   onRender: ->
     @factWheelRegion.show @wheelView()
     @factBodyRegion.show @bodyView()
-
-  initialize: ->
-    @bindTo @model, 'change', =>
-      @factBodyRegion.currentView?.render()

   wheelView: ->
     wheel = @model.getFactWheel()
@@ -56,6 +52,7 @@

   initialize: ->
     @trunk8Init 3, '.js-displaystring', '.less'
+    @bindTo @model, 'change', @render, @

   triggerViewClick: (e) ->
     @trigger 'click:body', e


Removed comments from 'play' phase.

Removed comments from 'play' phase.

--- client.coffee
+++ client.coffee
@@ -58,7 +58,7 @@
 		# 		Dom.onTap !->
 		# 			showWinnerListModal()

-		if phase in ['play', 'vote']
+		if phase is 'vote'
 			Event.markRead 'hist', commentsRound
 			renderComments(commentsRound)


Docs - Allow for 4.x

Docs - Allow for 4.x

Google had indexed docs version 4.0-4.2 and master(5).

--- scripts/docs.coffee
+++ scripts/docs.coffee
@@ -35,7 +35,7 @@
     else
       if version == 3
         "site:laravel3.veliovgroup.com/docs #{query}"
-      else if version == 'master'
+      else if version?
         "site:laravel.com/docs/#{version} #{query}"
       else
         "site:laravel.com/docs #{query}"


Add marker.

Add marker.

--- main.coffee
+++ main.coffee
@@ -46,6 +46,31 @@
         @draw(@x, @y)
         #@circ([200,100])
         #@circ([100,200])
+
+        @marker0 = @marker('black')
+
+    marker: (color) ->
+        m = @plot.append("circle")
+            .attr("r",10)
+            .style("fill", color)
+            .style("stroke", color)
+            .style("stroke-width","1")
+            .call(
+                d3.behavior
+                .drag()
+                .origin(=>
+                    x:m.attr("cx")
+                    y:m.attr("cy")
+                )
+                .on("drag", => @dragMarker(m, d3.event.x, d3.event.y)) #, guide))
+            )
+
+    dragMarker: (marker, u, v) -> #, guide) ->
+        marker.attr("cx", u)
+        marker.attr("cy", v)
+        #phi = Math.atan2(@yScale.invert(v), @xScale.invert(u))
+        #guide.attr("x2", @xScale $blab.Figure.xMax*cos(phi))
+        #guide.attr("y2", @yScale $blab.Figure.xMax*sin(phi))

     draw: (X, Y)->
         lineData = ({ x: x, y:Y[i] } for x,i in X)
@@ -121,6 +146,18 @@
             .x( (d) => @xScale d.x )
             .y( (d) => @yScale d.y )
             .interpolate("linear");
+
+    radialLine: (color) ->
+        @plot.append('line')
+            .attr("x1", @xScale 0)
+            .attr("y1", @yScale 0)
+            .style("stroke", color)
+            .style("stroke-width","1")
+
+    moveMarker: (marker, u, v) ->
+        marker.attr("cx", u)
+        marker.attr("cy", v)
+

 class Histo extends d3Object


added comments

added comments

--- main.coffee
+++ main.coffee
@@ -37,36 +37,51 @@

 class IsoBlocks
+	# Starting x cordinate on the screen
 	origin_x: 0
+	# Starting y cordinate on the screen
 	origin_y: 0
-	offset_x: 0
-	offset_y: 0
-	character_spacing: 0
+
+	# Spacing between 2 characters in terms of number of blocks
+	character_spacing: 1
+
+	# Array of pre-generated cubes which are used throughout.
+	cubes: []
+
+	# Current cube index in the @cubes array to be used for drawing
 	current_cube_index: 0

-	# Pre-generated array of cubes which are used throughout.
-	cubes: []
-
-	# A HTML string template for a block
+	# An HTML string template for a block
 	cube_template: '

'

-	constructor: ->
-		@preCreateCubes 450
+	###
+	Constructor
+	@param	num_cubes	Number 	Number of cubes to pre-generate
+	###
+	constructor: (num_cubes = 450) ->
+		@preGenerateCubes num_cubes

+	###
+	@params	s 	string 	String to draw
+	@params	start_x	Number 	Starting x cordinate of the string
+	@params	start_y	Number 	Starting y cordinate of the string
+	###
 	generate: (s, start_x, start_y) ->
-		#cos = Math.cos(-30 * Math.PI /180)
-		#sin = Math.sin(-30 * Math.PI /180)
+		@origin_x = start_x
+		@origin_y = start_y
+
+		# Initialize the @current_cube_index to start using the cubes from end for current string
 		@current_cube_index = @cubes.length - 1

-		# Hide all cubes and reset color
+		# Hide all cubes
 		for cube in @cubes
 			$(cube).addClass('unused')#.removeClass('iso_color_yellow iso_color_green')
 			#$(cube).css 'top', parseInt($(cube).css('top'), 10) - 400
-		@origin_x = start_x
-		@origin_y = start_y
+
 		current_row = 0
 		current_col = 0

+		# Loop throught the characters and keep drawing them
 		for ch, index in s
 			current_col += @drawCharacter ch, current_row, current_col

@@ -76,6 +91,9 @@
 	###
 	It draws a character at given row and column
 	Returns the width of the character in number of blocks
+	@param 	ch 	String 	A character to draw
+	@param 	row Number 	Current character's row
+	@param 	col Number 	Current character's column
 	###
 	drawCharacter: (ch, row, col) ->
 		# Lowercase the character
@@ -122,7 +140,10 @@
 		# Return the character width
 		current_ch.length

-	preCreateCubes: (cube_count) ->
+	###
+	@param 	cube_count	Number 	Number of cubes to pre-generate
+	###
+	preGenerateCubes: (cube_count) ->
 		html_string = ''
 		for i in [1...cube_count]
 			current_cube = @cube_template


revert

revert

--- main.coffee
+++ main.coffee
@@ -2,10 +2,6 @@
 Path = require 'path'

 module.exports = (robot) ->
-  path = Path.resolve __dirname, 'module'
-  Fs.exists path, (exists) ->
-    robot.loadFile path, file for file in Fs.readdirSync(path) if exists
-
   path = Path.resolve __dirname, 'scripts'
   Fs.exists path, (exists) ->
     robot.loadFile path, file for file in Fs.readdirSync(path) if exists


Minor tweaks.

Minor tweaks.

--- main.coffee
+++ main.coffee
@@ -475,7 +475,7 @@
 				request.body.character_race
 				request.body.character_gender
 			)
-			request.uonline.characterCreated = true
+			request.uonline._characterCreated = true
 		else
 			options = request.uonline
 			options.error = true
@@ -483,12 +483,12 @@
 			options.nameIsBusy = alreadyExists
 			options.character_name = request.body.character_name
 			request.uonline.options = options
-			request.uonline.characterCreated = false
+			request.uonline._characterCreated = false
 		next()
 	,
 	commit,
 	(request, response) ->
-		if request.uonline.characterCreated
+		if request.uonline._characterCreated
 			response.redirect 303, '/character/'
 		else
 			response.render 'new_character', request.uonline.options
@@ -629,15 +629,6 @@
 		next()
 	,
 	setInstance('state'), render('state')
-
-
-app.get '/test/', (req, res, next) ->
-	console.log('test route. it exists, but 404 will appear')
-	next()
-
-app.use (request, response, next) ->
-	console.log('after route')
-	next()

 # 404 handling


groups: set the width of invitation share modal

--- client/Dashboard/views/groupsinvitationcodelistitemview.coffee
+++ client/Dashboard/views/groupsinvitationcodelistitemview.coffee
@@ -31,7 +31,7 @@
       cssClass                : 'invitation-share-modal'
       title                   : 'Share Invitation Code'
       overlay                 : yes
-      width                   : 400
+      width                   : 640
       height                  : 'auto'
       tabs                    :
         forms                 :
@@ -83,7 +83,7 @@
       cssClass               : 'invitation-share-modal'
       title                  : 'Share Invitation Code'
       overlay                : yes
-      width                  : 400
+      width                  : 640
       height                 : 'auto'
       tabs                   :
         forms                :


Firewall: Create rule widgets for update view.

--- client/Environments/views/firewall/addfirewallrulemodal.coffee
+++ client/Environments/views/firewall/addfirewallrulemodal.coffee
@@ -58,7 +58,7 @@
     super options, data

     @filterWidgets = []
-    @createRuleWidget no
+    if data then @createExistingRules() else @createRuleWidget no

     button      = new KDButtonView
       title     : "Add more rules"
@@ -76,6 +76,10 @@

     @modalTabs.forms.Rules.fields.container.addSubView widget
     @filterWidgets.push widget
+
+  createExistingRules: ->
+    @getData().rules.forEach (rule, index) =>
+      @createRuleWidget index isnt 0, rule

   handleFormSubmit: ->
     isValid          = yes


ace: Added fileTypes array to ace config.

--- client/app/Applications/Ace.kdapplication/AppController.coffee
+++ client/app/Applications/Ace.kdapplication/AppController.coffee
@@ -61,6 +61,11 @@
         eventName : "exit"
       }
     ]
+    "fileTypes": [
+      "php", "pl", "py", "jsp", "asp", "aspx", "htm", "html", "phtml","shtml",
+      "sh", "cgi", "htaccess","fcgi","wsgi","mvc","xml","sql","rhtml", "diff",
+      "js","json", "coffee", "css","styl","sass", "scss", "less", "txt"
+    ]
     # mimeTypes    : "text"

   constructor: (options = {}, data)->


liveupdates: now only active when scrolled to top. every scroll will set it to off

--- client/app/Applications/Activity.kdapplication/AppController.coffee
+++ client/app/Applications/Activity.kdapplication/AppController.coffee
@@ -460,9 +460,13 @@

     @scrollView.on 'scroll', (event) =>
       if event.delegateTarget.scrollTop > 10
+        # scrollshadow and switch off liveupdates
         @activityHeader.setClass "scrolling-up-outset"
+        @activityHeader.liveUpdateButton.setValue off
+
       else
         @activityHeader.unsetClass "scrolling-up-outset"
+        @activityHeader.liveUpdateButton.setValue on

     @scrollView.addSubView @noActivityItem = new KDCustomHTMLView
       cssClass : "lazy-loader"


myteam/teamsettings: upload aws right after user pick team logo

--- client/home/lib/myteam/components/hometeamsettings/container.coffee
+++ client/home/lib/myteam/components/hometeamsettings/container.coffee
@@ -33,21 +33,27 @@
     teamName = if @state.team? then Encoder.htmlDecode @state.team.get 'title' else ''
     @setState
       teamName: teamName
-      file: null
-      fileType: null
-      fileName: null

   onUploadInput: ->

+
     [file] = @refs.view.input.files
-
+    return  unless file
     reader = new FileReader
     reader.onload = (reader, event) =>
-      team = @state.team.setIn ['customize', 'logo'], reader.target.result
-      fileName = "#{@state.team.get 'slug'}-logo-#{Date.now()}.png"
-      fileType = file.type
-      @setState { team, file, fileType, fileName }
+
+      name = "#{@state.team.get 'slug'}-logo-#{Date.now()}.png"
+      mimeType = file.type
+      content = file
+      TeamFlux.actions.uploads3({ name, content, mimeType }).then ({ url }) =>
+        dataToUpdate =
+          customize:
+            logo: url
+        @updateTeam { dataToUpdate }
+
+      .catch ({ err }) ->
+        showError err  if err

     reader.readAsDataURL file

@@ -78,7 +84,6 @@
     content = @state.file
     mimeType = @state.fileType

-    if _.isEmpty dataToUpdate.title
       if not (name or content or mimeType)
         return


Fix contex/bounds naming in the closure.

--- CGDraw.coffee
+++ CGDraw.coffee
@@ -104,7 +104,6 @@
 		indent:     ""

 		constructor: ( options ) ->
-			{ ctx: @context, bnd: @bounds } = options

 			switch options.type
 				when 'SVG'
@@ -128,6 +127,8 @@
 						@result.join "\n"

 				when 'CoreGraphics'
+					{ ctx: @context, bnd: @bounds } = options
+
 					if "swift" is options.lang
 						@initClosure = @initClosureSwift
 					else
@@ -190,7 +191,7 @@
 				\tlet verticalRatio: CGFloat = #{@bounds}.size.height/#{doc.height}
 				\tlet horizontalRatio: CGFloat = #{@bounds}.size.width/#{doc.width}
 				\tlet scale: CGFloat = verticalRatio < horizontalRatio ? verticalRatio : horizontalRatio
-				\tCGContextTranslateCTM(context, (bounds.size.width-#{doc.width}*scale)*0.5, (bounds.size.height-#{doc.height}*scale)*0.5)
+				\tCGContextTranslateCTM(#{@context}, (#{@bounds}.size.width-#{doc.width}*scale)*0.5, (#{@bounds}.size.height-#{doc.height}*scale)*0.5)
 				\tCGContextScaleCTM(#{@context}, scale, scale)
 			"""

@@ -200,7 +201,7 @@
 				\tconst CGFloat verticalRatio   = #{@bounds}.size.height/#{doc.height};
 				\tconst CGFloat horizontalRatio = #{@bounds}.size.width/#{doc.width};
 				\tconst CGFloat scale = verticalRatio < horizontalRatio ? verticalRatio : horizontalRatio;
-				\tCGContextTranslateCTM(context, (bounds.size.width-#{doc.width}*scale)*0.5, (bounds.size.height-#{doc.height}*scale)*0.5);
+				\tCGContextTranslateCTM(#{@context}, (#{@bounds}.size.width-#{doc.width}*scale)*0.5, (#{@bounds}.size.height-#{doc.height}*scale)*0.5);
 				\tCGContextScaleCTM(#{@context}, scale, scale);
 			"""


Fix incorrect checkbox state.

Fix incorrect checkbox state.

--- app/assets/javascripts/main.coffee
+++ app/assets/javascripts/main.coffee
@@ -34,11 +34,13 @@
                     checked = ""
                     if i.pin_enabled == "true"
                         checked = "checked"
+                    if i.contactless_enabled == "true"
+                        cl_checked = "checked"
                     windowContents = '
' +
                         '
' + i.name + '
' +
                         '
<address>' + i.address + '</address>
' +
                         '
<input type="checkbox" id="pinEnabled" value="true" ' + checked  + ' disabled />business has PIN pad
' +
-                        '
<input type="checkbox" id="contactlessEnabled" value="true" ' + checked  + ' disabled />business supports contactless cards
' +
+                        '
<input type="checkbox" id="contactlessEnabled" value="true" ' + cl_checked  + ' disabled />business supports contactless cards
' +
                         '
report errors

'

                     newMarker = {
@@ -141,11 +143,13 @@
                 checked = ""
                 if data.pin_enabled == "true"
                     checked = "checked"
+                if data.contactless_enabled == "true"
+                    cl_checked = "checked"
                 windowContents = '
' +
                     '
' + data.name + '
' +
                     '
<address>' + data.address + '</address>
' +
                     '
<input type="checkbox" id="pinEnabled" value="true" ' + checked + ' disabled />business has PIN pad
' +
-                    '
<input type="checkbox" id="contactlessEnabled" value="true" ' + checked + ' disabled />business supports contactless cards
' +
+                    '
<input type="checkbox" id="contactlessEnabled" value="true" ' + cl_checked + ' disabled />business supports contactless cards
' +
                     '
report errors

'

                 newMarker = {


analytics for channel visibility

analytics for channel visibility

--- components/channel_visibility/client/channel_visibility_view.coffee
+++ components/channel_visibility/client/channel_visibility_view.coffee
@@ -1,5 +1,6 @@
 Backbone = require 'backbone'
 Backbone.$ = $
+analytics = require '../../../lib/analytics.coffee'

 template = -> require('../templates/channel_visibility.jade') arguments...

@@ -28,6 +29,9 @@
     @$('.metadata--selector__option.is-active').removeClass 'is-active'
     $selection.addClass 'is-active'

+    analytics.track.click "Channel visibility changed to #{$selection.data('value')}",
+      label: analytics.modelNameAndIdToLabel 'Channel', @model.id
+
     @model.set 'status', $selection.data('value')
     @model.url = "#{sd.API_URL}/channels/#{@model.id}"
     @model.save() if @autoSync


Add tests for create-load-delete cycle

Add tests for create-load-delete cycle

--- spec/StorageMiddleware.coffee
+++ spec/StorageMiddleware.coffee
@@ -10,6 +10,7 @@
 describe 'Storage Middleware', ->
   mw = null
   idb = null
+  component = null
   before (done) ->
     @timeout 4000
     mw = window.middleware 'ui/StorageMiddleware', baseDir
@@ -34,3 +35,34 @@
         idb = data
       mw.receiveAction 'storage:db', check, done
       mw.send action, payload, {}
+  describe 'receiving a storage:save:component action', ->
+    it 'should send a storage:stored:component action', (done) ->
+      action = 'storage:save:component'
+      comp =
+        name: 'Foo'
+        language: 'python'
+        project: 'baz'
+        code: ''
+        tests: ''
+      check = (data) ->
+        chai.expect(data).to.eql comp
+        component = data
+      mw.receiveAction 'storage:stored:component', check, done
+      mw.send action, comp,
+        db: idb
+  describe 'receiving a storage:load:components action', ->
+    it 'should send a storage:stored:component action', (done) ->
+      action = 'storage:load:components'
+      check = (data) ->
+        chai.expect(data).to.eql component
+      mw.receiveAction 'storage:stored:component', check, done
+      mw.send action, {},
+        db: idb
+  describe 'receiving a storage:delete:component action', ->
+    it 'should send a storage:removed:component action', (done) ->
+      action = 'storage:delete:component'
+      check = (data) ->
+        chai.expect(data).to.equal component.id
+      mw.receiveAction 'storage:removed:component', check, done
+      mw.send action, component,
+        db: idb


Do not restart the push when the window is closing

--- app/assets/javascripts/push.coffee
+++ app/assets/javascripts/push.coffee
@@ -27,9 +27,7 @@
         console.log err  if window.console

     onError: (e) =>
-      if e.eventPhase == EventSource.CLOSED
-        @started = false
-        @start()
+      console.log "onError", e  if window.console

   pushs  = []
   $.push = (chan) ->


sort by doc_id to ensure consistent order for all changes with the same timestamp

--- app/coffee/MongoPackManager.coffee
+++ app/coffee/MongoPackManager.coffee
@@ -158,7 +158,12 @@
 				return true

 			timeOrder = (a, b) ->
-				b.meta.end_ts - a.meta.end_ts
+				(b.meta.end_ts - a.meta.end_ts) || documentOrder(a, b)
+
+			documentOrder = (a, b) ->
+				x = a.doc_id.valueOf()
+				y = b.doc_id.valueOf()
+				if x > y then 1 else if x < y then -1 else 0

 			updates = MongoPackManager._filterAndLimit(updates, unpackedSet, filterFn, limit)
 			#console.log 'initial updates are', updates


comments

comments

--- app/helper/executableHelper.coffee
+++ app/helper/executableHelper.coffee
@@ -256,10 +256,9 @@
         requestCallback null, collection.result

   preprocessCollection = (collection, req, serviceInfo, parameterHelper,executionType, callback) ->
-    #process a collection
-
-    #check if collection exists
+    #handle images with/without images differently
     if(collection.hasImages)
+      #check if the collection is available
       if(not ImageHelper.checkCollectionAvailable(req.body.images[0].value))
         err =
           statusCode: 500
@@ -267,7 +266,6 @@
           statusText: 'The collection ' + req.body.images[0].value + ' does not exist on the server'
         callback err, null
         return
-
       folder = nconf.get('paths:imageRootPath') + path.sep + collection.name
       collection.inputParameters = _.clone(req.body.inputs)
       setCollectionHighlighter(collection, req)
@@ -286,6 +284,7 @@
           process.hasImages = true
           collection.processes.push(process)
         callback null, collection
+
     process = new Process()
     if (collection.hasFiles)
       process.hasFiles = true