fix express example

fix express example

--- examples/express/app.coffee
+++ examples/express/app.coffee
@@ -1,7 +1,9 @@
-app = require('express').createServer()
+express = require 'express'
+app = express()

 coffeecup = require '../../src/coffeecup'

+app.set('view engine', 'coffee')
 app.engine 'coffee', require('coffeecup').__express

 app.get '/', (req, res) ->


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/oscillator.coffee
+++ src/oscillator.coffee
@@ -5,7 +5,7 @@
 @constructor
 @param {Number} amplitude How much to scale the oscillator function value
 @param {Number} period How fast the osciallator function repeats
-@param {Number} offset How much to offset the cr
+@param {Number} offset How much to offset the created osciallator function
 ###
 Oscillator = (options={}) ->
   {amplitude, period, offset} = options


clean up github example test

clean up github example test

--- examples/github/test/client.coffee
+++ examples/github/test/client.coffee
@@ -3,12 +3,15 @@

 fs = require("fs")

+# read login:password from a file
 string = fs.readFileSync("auth")
 string = string.slice(0, string.length - 1)
+# pack it up, yo.
 basic_auth = new Buffer(string).toString("base64")

 Client.discover "http://localhost:1338/", (err, client) ->

+  # imbue the client with an authorization function
   client.authorizer = (type, action) ->
     resource = @
     if type == "Basic"
@@ -30,39 +33,9 @@
               console.log repo.owner.url

       response: (response) ->
-        console.log "unexpected responses status"
+        console.log "unexpected response status"
         console.log response

   return

-  configuration = client.resources.configuration
-
-  testify "configuration.update responds with 204", (test) ->
-    #time = Date.now()
-    rand = Math.random().toString()
-    configuration.update
-      content:
-        origins: [rand]
-        names: ["known", "event", "names"]
-      on:
-        204: (response) ->
-          test.done()
-          testify "configuration.get", (test) ->
-            configuration.get
-              on:
-                200: (response, config) ->
-                  origins = config.origins
-                  test.assert.equal(origins.constructor, Array)
-                  test.assert.ok(origins.length > 0)
-                  test.assert.ok origins.some (item) ->
-                    item == rand
-                  test.assert.equal(config.names.constructor, Array)
-                  test.assert.ok(config.names.length > 0)
-                  test.done()
-                response: (response) ->
-                  test.assert.fail null, null, "Unexpected response status: #{response.status}"
-
-
-
-


extend ng-create to allow for attributes

--- lib/assets/javascripts/directives/ngcreate.js.coffee
+++ lib/assets/javascripts/directives/ngcreate.js.coffee
@@ -5,13 +5,17 @@
     link: (scope, element, attributes) ->
       element.bind 'click', (event) ->
         [parentName, listName] = attributes.ngContext.split('.') if attributes.ngContext
-        scope.create(attributes.ngCreate,parentName,listName)
+        attr = eval('(' + attributes.ngAttributes + ')') || {}
+        scope.create(attributes.ngCreate,parentName,listName,attr)
     controller: ($scope, $injector, factoryName) ->
-      $scope.create = (modelName,parentName,listName) ->
+      $scope.create = (modelName,parentName,listName,attributes) ->
         factory = factoryName(modelName)
         list = $injector.get(factory)
         object = {}
-        object[parentName + '_id'] = $scope[parentName].id if parentName
+        object[modelName] = attributes
+        if parentName
+          object[parentName]         = $scope[parentName]    unless parentName.indexOf('_id') < 0
+          object[parentName + '_id'] = $scope[parentName].id     if parentName.indexOf('_id') < 0
         list.create object, (returnData) ->
           if listName
             scope = if $scope[parentName] then $scope else $scope.$parent


YEAH! We saved the children

YEAH! We saved the children

--- test/spec/test_reactive.coffee
+++ test/spec/test_reactive.coffee
@@ -100,8 +100,7 @@
       expect(elt.getAttribute('x')).toBe('10')
     it 'should have the given child contents', ->
       console.log("!!!elt: #{elt}")
-      kids = elt.children
-      console.log("!!!elt.children: #{elt.children}")
+      kids = elt.childNodes
       expect(kids.length).toBe(1)
       expect(kids[0] instanceof SVGElement).toBe(true)


ensures dropdown hides on click

ensures dropdown hides on click

--- components/filter2/dropdown/view.coffee
+++ components/filter2/dropdown/view.coffee
@@ -67,7 +67,7 @@
       @params.set @facet, val

     @renderActive e
-    @hideMenu e
+    _.defer => @hideMenu e

     false

@@ -77,8 +77,8 @@
       $(e.currentTarget).addClass('is-active')

   hideMenu: (e) ->
-    $(e.currentTarget).parent().hidehover() unless navigator.userAgent.match('iPad')
+    unless navigator.userAgent.match('iPad')
+      $(e.currentTarget).closest('nav').hidehover()

-


Handle windows asset names containing spaces in publish-build task

--- build/tasks/publish-build-task.coffee
+++ build/tasks/publish-build-task.coffee
@@ -119,7 +119,7 @@
 zipAssets = (buildDir, assets, callback) ->
   zip = (directory, sourcePath, assetName, callback) ->
     if process.platform is 'win32'
-      zipCommand = "C:/psmodules/7z.exe a -r #{assetName} #{sourcePath}"
+      zipCommand = "C:/psmodules/7z.exe a -r #{assetName} \"#{sourcePath}\""
     else
       zipCommand = "zip -r --symlinks '#{assetName}' '#{sourcePath}'"
     options = {cwd: directory, maxBuffer: Infinity}


Separate the debugging contexts

Separate the debugging contexts

--- src/lib/Component.coffee
+++ src/lib/Component.coffee
@@ -10,6 +10,8 @@
 IP = require './IP'

 debug = require('debug') 'noflo:component'
+debugBrackets = require('debug') 'noflo:component:brackets'
+debugSend = require('debug') 'noflo:component:send'

 class Component extends EventEmitter
   description: ''
@@ -193,7 +195,7 @@
           port.get ip.scope, ip.index
           context = @getBracketContext(port.name, ip.scope, ip.index).pop()
           context.closeIp = ip
-          debug "#{@nodeId} closeBracket-C from '#{context.source}' to #{context.ports}: '#{ip.data}'"
+          debugBrackets "#{@nodeId} closeBracket-C from '#{context.source}' to #{context.ports}: '#{ip.data}'"
           result =
             __resolved: true
             __bracketClosingAfter: [context]
@@ -250,7 +252,7 @@
   addBracketForwards: (result) ->
     if result.__bracketClosingBefore?.length
       for context in result.__bracketClosingBefore
-        debug "#{@nodeId} closeBracket-A from '#{context.source}' to #{context.ports}: '#{context.closeIp.data}'"
+        debugBrackets "#{@nodeId} closeBracket-A from '#{context.source}' to #{context.ports}: '#{context.closeIp.data}'"
         continue unless context.ports.length
         for port in context.ports
           ipClone = context.closeIp.clone()
@@ -279,7 +281,7 @@
                 ipClone = ctx.ip.clone()
                 ipClone.index = parseInt idx
                 idxIps.unshift ipClone
-                debug "#{@nodeId} register from '#{inport}' to '#{portIdentifier}' < '#{ctx.ip.data}'"
+                debugBrackets "#{@nodeId} register from '#{inport}' to '#{portIdentifier}' < '#{ctx.ip.data}'"
                 ctx.ports.push portIdentifier
             continue
           # Don't register ports we're only sending brackets to
@@ -292,12 +294,12 @@
           unforwarded.reverse()
           for ctx in unforwarded
             ips.unshift ctx.ip.clone()
-            debug "#{@nodeId} register from '#{inport}' to '#{outport}' < '#{ctx.ip.data}'"
+            debugBrackets "#{@nodeId} register from '#{inport}' to '#{outport}' < '#{ctx.ip.data}'"
             ctx.ports.push outport

     if result.__bracketClosingAfter?.length
       for context in result.__bracketClosingAfter
-        debug "#{@nodeId} closeBracket-B from '#{context.source}' to #{context.ports}: '#{context.closeIp.data}'"
+        debugBrackets "#{@nodeId} closeBracket-B from '#{context.source}' to #{context.ports}: '#{context.closeIp.data}'"
         continue unless context.ports.length
         for port in context.ports
           ipClone = context.closeIp.clone()
@@ -321,22 +323,22 @@
             for ip in idxIps
               portIdentifier = "#{port}[#{ip.index}]"
               if ip.type is 'openBracket'
-                debug "#{@nodeId} sending #{portIdentifier} < '#{ip.data}'"
+                debugSend "#{@nodeId} sending #{portIdentifier} < '#{ip.data}'"
               else if ip.type is 'closeBracket'
-                debug "#{@nodeId} sending #{portIdentifier} > '#{ip.data}'"
+                debugSend "#{@nodeId} sending #{portIdentifier} > '#{ip.data}'"
               else
-                debug "#{@nodeId} sending #{portIdentifier} DATA"
+                debugSend "#{@nodeId} sending #{portIdentifier} DATA"
               @outPorts[port].sendIP ip
           continue
         continue unless @outPorts.ports[port].isAttached()
         for ip in ips
           portIdentifier = port
           if ip.type is 'openBracket'
-            debug "#{@nodeId} sending #{portIdentifier} < '#{ip.data}'"
+            debugSend "#{@nodeId} sending #{portIdentifier} < '#{ip.data}'"
           else if ip.type is 'closeBracket'
-            debug "#{@nodeId} sending #{portIdentifier} > '#{ip.data}'"
+            debugSend "#{@nodeId} sending #{portIdentifier} > '#{ip.data}'"
           else
-            debug "#{@nodeId} sending #{portIdentifier} DATA"
+            debugSend "#{@nodeId} sending #{portIdentifier} DATA"
           @outPorts[port].sendIP ip
       @outputQ.shift()


Ajax: Allow posting data.

Ajax: Allow posting data.

--- src/lib/ajax/ajax.coffee
+++ src/lib/ajax/ajax.coffee
@@ -21,7 +21,9 @@

   ```coffee
     spark.ajax.request
+      type     : 'POST'
       url      : 'http://reqr.es/api/users/12'
+      data     : name: 'fatih'
       dataType : 'json' # optional if the JSON response header set.
       success  : (response) -> console.log response.data.first_name
       error    : -> console.log 'error'
@@ -34,6 +36,12 @@
   options = spark.ajax.getOptions_ options

   { type, url, data, dataType, success, error } = options
+
+  if data and spark.validation.isObject data
+    try
+      data    = JSON.stringify data
+      headers =
+        'Content-Type': 'application/json; charset=UTF-8'

   callbackWrapper = (e) =>
     target   = e.target
@@ -48,7 +56,7 @@

     success.call spark.ajax, response, e

-  goog.net.XhrIo.send url, callbackWrapper, type
+  goog.net.XhrIo.send url, callbackWrapper, type, data, headers

 ###*


fix to change attack/decay back to log scale

--- src/remcoaudio.coffee
+++ src/remcoaudio.coffee
@@ -93,11 +93,14 @@
     amp.gain.cancelScheduledValues audio.currentTime
     amp.gain.setValueAtTime 1 - oneShot, audio.currentTime

+  attack = value: 1
+  decay = value: 1
+
   triggerOneShot = ->
     if preset.get 'oneshotstate'
+      a = attack.vaue
+      d = decay.value
       amp.gain.setValueAtTime 0, audio.currentTime
-      a = preset.get 'attack'
-      d = preset.get 'decay'
       amp.gain.linearRampToValueAtTime 1, audio.currentTime + a
       amp.gain.linearRampToValueAtTime 0,
         audio.currentTime + a + d
@@ -105,6 +108,8 @@
   b = new bindings preset
   b.bindParam 'volume', output.gain
   b.bindParam 'noise', noise.frequency, bindings.logScale 50, 10000
+  b.bindParam 'attack', attack, bindings.logScale .1, 3
+  b.bindParam 'decay', decay, bindings.logScale .1, 3
   b.bindRange 'slfFreq', 'slfRange', slf.frequency,
     [1,10,100,5000], bindings.logScale .1, 1
   b.bindRange 'vcoFreq', 'vcoRange', vcooffset,
@@ -115,4 +120,7 @@
   # return a collection of exposed parameters
   {
     triggerOneShot: triggerOneShot
+    getLength: ->
+      console.log attack.value + decay.value
+      attack.value + decay.value
   }


Handles GET forms correctly now.  I was only handling GET button_to's before.

--- src/runtime.js.coffee
+++ src/runtime.js.coffee
@@ -354,7 +354,16 @@
         ,
         (element, callback) ->
           if element.method is "get" or element.method is "GET"
-            self.visit element.action
+            # If this is a get form, like look up a order number, we need to pass
+            # the form fields on the URL.
+            params = jQuery.params(jQuery(element).serialize())
+            url = element.action
+            if /\?/.test url
+              url += '+' + params
+            else
+              url += '?' + params
+
+            self.visit url
           else
             self.submit element, callback


always show create new option if exists

always show create new option if exists

--- app/assets/javascripts/active_admin/lib/jquery.autocomplete_collection.js.coffee
+++ app/assets/javascripts/active_admin/lib/jquery.autocomplete_collection.js.coffee
@@ -180,7 +180,7 @@
         return item

     # items = @sorter(items)
-    if !items.length
+    if !items.length and !@allowNew
       return if @shown then @hide() else @

     @render(items.slice(0, @options.items)).show()


Made resetPlaceholders global

Made resetPlaceholders global

--- core/app/assets/javascripts/base/placeholder_fix_ie.coffee
+++ core/app/assets/javascripts/base/placeholder_fix_ie.coffee
@@ -2,7 +2,7 @@
 $ ->
   resetPlaceholders()

-resetPlaceholders = ->
+window.resetPlaceholders = ->
   $('.placeholder')
     .not(':password')
     .trigger('blur.placeholder')


Do not animate line chart

Do not animate line chart

--- src/client/game.coffee
+++ src/client/game.coffee
@@ -15,6 +15,7 @@
     last_stat = stat[stat.length - 1]
     name      = last_stat.count + ' ' + last_stat.name
     return new Plottable.Plot.Line(xScale, yScale)
+                      .animate(false)
                       .addDataset(stat)
                       .project("x", "iteration", xScale)
                       .project("y", "count", yScale)


disable import- combine fleets

disable import- combine fleets

--- views/importTab.cjsx
+++ views/importTab.cjsx
@@ -37,7 +37,7 @@
     continue if ship[0] is null
     totalLv += ship[1][0]
   tyku = getTyku(deck)
-
+
   [totalLv,
    tyku.basic,
    tyku.alv]


Endpoints: added outbox endpoints

Endpoints: added outbox endpoints

--- smsgw/static/src/api/endpoints.coffee
+++ smsgw/static/src/api/endpoints.coffee
@@ -40,3 +40,8 @@
         regenerate: (uuid, user = "@me") -> "#{BASE}/users/#{user}/applications/#{uuid}/regenerate/"
         update: (uuid, user = "@me") -> @get uuid, user
         delete: (uuid, user = "@me") -> @get uuid, user
+    outbox:
+        index: (user = "@me") -> "#{BASE}/users/#{user}/outbox/"
+        get: (uuid, user = "@me") -> "#{BASE}/users/#{user}/outbox/#{uuid}/"
+        update: (uuid, user = "@me") -> @get uuid, user
+        delete: (uuid, user = "@me") -> @get uuid, user


Fixed the bug of the message when more than one results exist.

--- src/conomi-sake.coffee
+++ src/conomi-sake.coffee
@@ -29,7 +29,7 @@
     "群馬県":10,   "群馬":10,    "ぐんま":10,
     "埼玉県":11,   "埼玉":11,    "さいたま":11,
     "千葉県":12,   "千葉":12,    "ちば":12,
-    "東京都":13,   "東京":13,
+    "東京都":13,   "東京":13,    "とうきょう":13,
     "神奈川県":14, "神奈川":14,
     "新潟県":15,   "新潟":15,
     "富山県":16,   "富山":16,
@@ -79,7 +79,7 @@
             p = p + '&prefecture_code=' + table[keyword]
         else
             p = p + '&sake_name=' + encodeURIComponent(keyword)
-        console.log "http://http://www.sakenote.com" + p
+        console.log "http://www.sakenote.com" + p
         req = http.get { host:'www.sakenote.com', path:p }, (res) ->
             contents = ""
             res.on 'data', (chunk) ->
@@ -100,7 +100,7 @@
                         rep += "\n"
                         if value['maker_url'] != null
                             rep += "　URL: " + value['maker_url'] + "\n"
-                        msg.send rep
+                msg.send rep

         req.on "error", (e) ->
             msg.send "(fu) ひでぶっ!!  ... {e.message}"


Workspace: Added tab margin to the editor.

--- client/landing/CollaborativeWorkspace/panes/collaborativetabbededitorpane.coffee
+++ client/landing/CollaborativeWorkspace/panes/collaborativetabbededitorpane.coffee
@@ -51,6 +51,7 @@
       delegate                  : @
       sortable                  : no
       closeAppWhenAllTabsClosed : no
+      lastTabHandleMargin       : 200
       tabHandleContainer        : @tabHandleContainer

     @tabView.on "PaneAdded", (pane) =>


Fix sortie free slot check

Fix sortie free slot check

--- views/services/sortieFreeSlotCheck.coffee
+++ views/services/sortieFreeSlotCheck.coffee
@@ -1,3 +1,5 @@
+__ = i18n.main.__.bind(i18n.main)
+
 window.addEventListener 'game.response',
   ({detail: {path, body}}) ->
     if path == '/kcsapi/api_get_member/mapinfo'
@@ -7,15 +9,15 @@
         shipSlots = basic.api_max_chara - Object.keys(getStore('info.ships')).length
         if shipSlots < minShipSlots
           setTimeout =>
-            error __ "Attention! Ship Slot has only %s left.", "#{@state.maxChara - @state.shipCount}"
+            error __ "Attention! Ship Slot has only %s left.", shipSlots
           , 1000
       if config.get 'poi.mapStartCheck.item.enable', false
         minEquipSlots = config.get 'poi.mapStartCheck.item.minFreeSlots', 10
         equipSlots = basic.api_max_slotitem - Object.keys(getStore('info.equips')).length
-        if slotsLeft < minFreeItemSlots
+        if equipSlots < minEquipSlots
           errMsg = __ "Attention! Item Slot is full."
-          if slotsLeft > 0
-            errMsg = __ "Attention! Only %d free item slot(s) left!", slotsLeft
+          if equipSlots > 0
+            errMsg = __ "Attention! Only %d free item slot(s) left!", equipSlots
           setTimeout =>
             error errMsg
           , 1000


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/core_object.coffee
+++ src/core_object.coffee
@@ -156,7 +156,7 @@
             # Attempt to look up module in global namespace
             for key, value of root
               if value is Module
-                moduleName = key
+                Module._name = moduleName = key # Cache

           if moduleName
             unless I.includedModules.include moduleName


ApplicationManager: Pass requested label as app params so app can figure out which label is used to call it (completely my idea, I'm gokmen)

--- client/Main/ApplicationManager.coffee
+++ client/Main/ApplicationManager.coffee
@@ -68,6 +68,7 @@
       options            or= {}
       appOptions           = KD.getAppOptions name
       appParams            = options.params or {}
+      appParams.label      = name
       defaultCallback      = createOrShow.bind this, appOptions, appParams, callback
       kodingAppsController = KD.getSingleton("kodingAppsController")


listen using on

listen using on

--- client/lanes/react/mixins/Data.coffee
+++ client/lanes/react/mixins/Data.coffee
@@ -3,26 +3,34 @@
     constructor: (component) -> @component = component

     bindEvents: (events, model) ->
-        events.listenTo(model, 'error',     @onError)
-            .listenTo(model,   'request',   @onRequest)
-            .listenTo(model,   'load sync', @onSync)
+        model.on('error', @onError, @)
+            .on('request', @onRequest, @)
+            .on('load sync', @)
+
+    unbindEvents: (events, model) ->
+        model.off(null, null, @)
+
+    setState: (state, options) ->
+        return if options?.silent
+        fn = @component.setNetworkActivity or @component.setState
+        fn.call(@component, state)

     onError: (modelOrCollection, options) ->
-        @component.setState(
+        @setState(
             isRequesting: false
             hasError: modelOrCollection?.errorMessage or true
             errors: modelOrCollection.errors
-        ) unless options?.silent
+        )

     onRequest: (modelOrCollection, type, options) ->
-        @component.setState(
+        @setState(
             isRequesting: true
             hasError: false
             errors: []
-        ) unless options?.silent
+        )

     onSync: (modelOrCollection, res, options = {}) ->
-        @component.setState(isRequesting: false) unless options?.silent
+        @setState(isRequesting: false)

 getNetworkListener = (component) ->
     return unless _.result(component, 'listenNetworkEvents') is true
@@ -32,6 +40,7 @@

     onModelUnbind: (model, name) ->
         Lanes.Models.PubSub.remove(model)
+        getNetworkListener(@)?.unBindEvents(@modelBindings, @[name])

     onModelBind: (model, name) ->
         model = this[name]


Fix #299 - Force list.permanent to a boolean value when toggling the class

--- source/scripts/views/list_menu.coffee
+++ source/scripts/views/list_menu.coffee
@@ -28,7 +28,7 @@

   # Show/Hide buttons depending on the list attributes
   update: (list) =>
-    @ui.delete.toggleClass 'hidden', list.permanent
+    @ui.delete.toggleClass 'hidden', !! list.permanent

   delete: ->
     ListModal.run()


fix unnecessary call count check

fix unnecessary call count check

--- spec/persistent-selection-spec.coffee
+++ spec/persistent-selection-spec.coffee
@@ -138,19 +138,16 @@
             # cursor: [[3, 12], [4, 12], [0, 12], [1, 12]]

     describe "select-occurrence-in-a-persistent-selection", ->
-      [update] = []
-      beforeEach ->
-        vimState.persistentSelection.markerLayer.onDidUpdate(update = jasmine.createSpy())
-
       it "select all instance of cursor word only within marked range", ->
         runs ->
-          paragraphText = "ooo xxx ooo\nxxx ooo xxx\n"
+          paragraphText = """
+            ooo xxx ooo
+            xxx ooo xxx\n
+            """
           ensurePersistentSelection 'g m i p } } j .', # Mark 2 inner-word and 1 inner-paragraph
             length: 2
             text: [paragraphText, paragraphText]

-        waitsFor ->
-          update.callCount is 1
         runs ->
           ensure 'g cmd-d',
             selectedText: ['ooo', 'ooo', 'ooo', 'ooo', 'ooo', 'ooo' ]


socialcontroller: remove getScreenMap function

--- client/Main/socialapicontroller.coffee
+++ client/Main/socialapicontroller.coffee
@@ -296,12 +296,6 @@
     return "socialapi.#{groupName}-#{typeConstant}-#{name}"

-  getScreenMapToken = (body, type) ->
-
-    {_id} = KD.whoami()
-    md5.digest "#{type}-#{body}-#{_id}"
-
-
   addToScreenMap = (options) ->
     {messageId, clientRequestId} = options
     {_inScreenMap} = KD.singletons.socialapi


localstroage controller version bug fixed

--- client/app/MainApp/localstorage.coffee
+++ client/app/MainApp/localstorage.coffee
@@ -55,7 +55,7 @@
     @localStorages = {}

   storage:(appName, version = "1.0")->
-    key = "#{appName}-{version}"
+    key = "#{appName}-#{version}"
     return @localStorages[key] or= new LocalStorage appName, version

 # Let people can use AppStorage


Providers: Config: add softlayer credential support with fields

--- client/app/lib/providers/config.coffee
+++ client/app/lib/providers/config.coffee
@@ -137,6 +137,20 @@
         label              : "API Key"
         placeholder        : "rackspace api key"

+  softlayer                :
+    name                   : "Softlayer"
+    link                   : "http://www.softlayer.com"
+    title                  : "Softlayer"
+    color                  : "#B52025"
+    description            : "Softlayer resources"
+    credentialFields       :
+      username             :
+        label              : "Username"
+        placeholder        : "username for softlayer"
+      api_key              :
+        label              : "API Key"
+        placeholder        : "softlayer api key"
+
   userInput                :
     name                   : "User Input"
     title                  : "User Input"


Making Nitin dangerous

Making Nitin dangerous

--- client/landing/Business/AppView.coffee
+++ client/landing/Business/AppView.coffee
@@ -64,7 +64,7 @@

Scale as you grow

-              Fully scalable environments with customizable stacks gives you
+              Fully scalable environments with customizable stacks give you
               the ability to scale as you get bigger in size. Size matters.


DateTime parse fail fix

DateTime parse fail fix

--- coffee/sap_bo/sap_bo_connector.coffee
+++ coffee/sap_bo/sap_bo_connector.coffee
@@ -30,8 +30,8 @@
     converters = {}
     Object.keys(schema).map (k)->
         converters[schema[k].id] = switch schema[k].dataType
-            when tableau.dataTypeEnum.datetime then (x) -> dateFormat(new Date(x), "yyyy-mm-dd HH:MM:ss")
-            when tableau.dataTypeEnum.date then (x) -> dateFormat(new Date(x), "yyyy-mm-dd")
+            when tableau.dataTypeEnum.datetime then (x) -> convertToDateString(x, "yyyy-mm-dd HH:MM:ss")
+            when tableau.dataTypeEnum.date then (x) -> convertToDateString(x, "yyyy-mm-dd")
             else (x)-> x
     (row) ->
         Object.keys(row).map (k)->
@@ -40,6 +40,12 @@
 #  Replaces any non-id characters with an underscore
 sanitizeId = (name)->
   name.replace(/[^a-zA-Z0-9_]/g, '_')
+
+# Parse date to a given format
+convertToDateString = (dateString, format) ->
+    date = new Date(dateString)
+    return dateFormat(date, format) if not isNaN(date) and isFinite(date.getTime())
+    dateString

 wdc_base.make_tableau_connector
     steps:


components: DbusIfaceProperties: remove useless log()

--- components/DbusIfaceProperties.coffee
+++ components/DbusIfaceProperties.coffee
@@ -124,8 +124,6 @@
     # Send property set through dbus
     c.setProperty = (propName, signature, value) ->
       return unless @bus and @path
-
-      log "set #{propName} = #{value}"
       variant = new GLib.Variant(signature, value)
       wrappedVariant = new GLib.Variant('(ssv)',  [iface.name, propName, variant])
       @getProxy().call('org.freedesktop.DBus.Properties.Set', wrappedVariant, Gio.DBusCallFlags.NONE, -1, null, Lang.bind(@, @propertySet))


Add JSDoc for class constructor

Add JSDoc for class constructor

--- src/dnt-connect.coffee
+++ src/dnt-connect.coffee
@@ -3,6 +3,14 @@
 CONNECT_URL = 'https://www.turistforeningen.no/connect'

 ###
+#
+# Create DNT Connect client
+#
+# @param <string-utf8> client - DNT Connect client name
+# @param <string-base64> key - DNT Connect API key
+# @param <object> opts - options
+#
+# @return <Connect> class instance
 #
 ###
 Connect = (client, key, opts) ->


Draw rebound turtle

Draw rebound turtle

--- src/draw_turtle.coffee
+++ src/draw_turtle.coffee
@@ -1,23 +1,21 @@
-draw_arc = (width, x_sign, y_sign) ->
+arc_path = (width, x_sign, y_sign) ->
   "a#{width},#{width} 0 0,1 #{x_sign*width},#{y_sign*width} "
-draw_ellipse = (width, x_sign, y_sign) ->
-  draw_arc(width, x_sign, y_sign) + draw_arc(width, -1*x_sign, -1*y_sign)
-draw_turtle_path = (cell_size) ->
+ellipse_path = (width, x_sign, y_sign) ->
+  arc_path(width, x_sign, y_sign) + arc_path(width, -1*x_sign, -1*y_sign)
+turtle_path = (cell_size) ->
+  path = ""
   width = cell_size / 2
   for offset in [[1,1], [1,-1], [-1,1], [-1,-1]]
-    path += draw_ellipse(width, offset[0], offset[1])
+    path += ellipse_path(width, offset[0], offset[1])
   head = cell_size / 4
-  path += "m#{width / 3},0 " + draw_ellipse(head, 2,0)
+  path += "m#{width / 3},0 " + ellipse_path(head, 2,0)
   shell = cell_size * 2 / 3
-  path += "m#{width / 2},0 " + draw_ellipse(shell , -3 / 2,0)
-
-draw_turtle = (me, cell_size) ->
-  x = me.bind -> me.get("i") * cell_size
-  y = me.bind -> me.get("j") * cell_size
-  path = me.bind -> "M#{x},#{y} " + draw_turtle_path(cell_size)
-  SVG.path {class: "path-turtle", d: path, fill: "#88ff88"}
+  path += "m#{width / 2},0 " + ellipse_path(shell , -3 / 2,0)

 exports.draw_turtle = (SVG, world) ->
   cell_size = world.get('scale')
   me = world.get("ME")
-  draw_turtle(me)
+  x = me.bind -> me.get("i") * cell_size
+  y = me.bind -> me.get("j") * cell_size
+  path = me.bind -> "M#{x.get()},#{y.get()} " + turtle_path(cell_size)
+  SVG.path {class: "path-turtle", d: path, fill: "#88ff88"}


Fix page up/page down keybindings

Fix page up/page down keybindings

--- src/editor-view.coffee
+++ src/editor-view.coffee
@@ -168,8 +168,8 @@
         'core:move-down': => @editor.moveCursorDown()
         'core:move-to-top': => @editor.moveCursorToTop()
         'core:move-to-bottom': => @editor.moveCursorToBottom()
-        'core:page-down': => @editor.pageDown()
-        'core:page-up': => @editor.pageUp()
+        'core:page-down': => @pageDown()
+        'core:page-up': => @pageUp()
         'core:select-up': => @editor.selectUp()
         'core:select-down': => @editor.selectDown()
         'core:select-to-top': => @editor.selectToTop()


Remove growl-alert.coffee

Remove growl-alert.coffee

--- src/growl-alert.coffee
+++ src/growl-alert.coffee
@@ -1,152 +0,0 @@
-
-do (window) ->
-    'use strict';
-
-    htmlTemplate = """
-

-

-

-        """
-
-    extend = ->
-        i = 1
-        while i < arguments.length
-            for key of arguments[i]
-                if arguments[i].hasOwnProperty(key)
-                    arguments[0][key] = arguments[i][key]
-            i++
-        arguments[0]
-
-    animationProp = do ->
-        el = document.createElement('fake')
-        animations =
-            'animation': 'animationend'
-            'OAnimation': 'oAnimationEnd'
-            'MozAnimation': 'animationend'
-            'WebkitAnimation': 'webkitAnimationEnd'
-        for a of animations
-            if el.style[a] != undefined
-                return animations[a]
-
-    setMessage = (param, type) ->
-        if typeof param == 'string'
-            opts =
-                text: param
-                type: type
-            return opts
-        else if param != null && typeof param == 'object'
-            return extend(param, {
-                type: type
-            })
-        else
-            return { type: type }
-
-    growl = (opts) ->
-
-        $el = undefined
-        $container = undefined
-
-        config = extend(growl.defaults, opts)
-
-        typeClass =
-            success: 'alert-message--success'
-            info: 'alert-message--info'
-            warning: 'alert-message--warning'
-            error: 'alert-message--error'
-
-        closeClass = 'alert-message__close'
-        containerClass = 'container-alert-message'
-        fadeAwayTimeout = undefined
-
-        bootstrap = ->
-            $container = document.querySelector('#' + config.containerId)
-
-            if $container == null
-                $container = document.createElement('div')
-                $container.setAttribute('id', config.containerId)
-                $container.setAttribute('class', containerClass)
-                document.querySelector('body').appendChild($container)
-
-            $el = document.createElement('div')
-            $el.innerHTML = htmlTemplate
-            $el.setAttribute('class', config.class)
-            $container.insertBefore($el, $container.firstChild)
-            $el.classList.add(typeClass[config.type])
-            $el.querySelector('.alert-message__text').innerHTML = config.text
-            openMessage()
-
-            if config.closeOnClick
-                $el.addEventListener('click touchstart', ->
-                    closeMessage()
-                )
-
-            if config.fadeAway and !isNaN(config.fadeAwayTimeout)
-                fadeAwayTimeout = setTimeout((->
-                    closeMessage()
-                    return
-                ), config.fadeAwayTimeout)
-            return
-
-        openMessage = ->
-            $el.classList.add(config.activeClass)
-            clearTimeout fadeAwayTimeout
-
-            if config.alertBoxLoadedCB instanceof Function
-                config.alertBoxLoadedCB($el)
-            return
-
-        closeMessage = ->
-            $el.classList.add(config.closingClass)
-
-            $el.addEventListener(animationProp, ->
-                $el.parentNode.removeChild($el)
-            )
-
-            clearTimeout fadeAwayTimeout
-
-            if config.alertBoxOnCloseCB instanceof Function
-                config.alertBoxOnCloseCB($el)
-            return
-
-        bootstrap()
-
-        $el.querySelector('.' + closeClass).addEventListener 'click', ->
-            closeMessage()
-            return
-
-        # return
-        return $el
-
-    growl.defaults =
-        class: 'alert-message'
-        activeClass: 'alert-message--active'
-        closingClass: 'alert-message--closing'
-        containerId: 'growl-container'
-        type: 'success'
-        text: 'Simple notification'
-        closeOnClick: false
-        fadeAway: false
-        fadeAwayTimeout: 5000
-        alertBoxLoadedCB: undefined
-        alertBoxOnCloseCB: undefined
-
-    growl.success = (opts) ->
-        growl(setMessage(opts, 'success'))
-
-    growl.error = (opts) ->
-        growl(setMessage(opts, 'error'))
-
-    growl.warning = (opts) ->
-        growl(setMessage(opts, 'warning'))
-
-    growl.info = (opts) ->
-        growl(setMessage(opts, 'info'))
-
-    if typeof module == "object" && module.exports
-        module.exports = growl
-    else if typeof define == "function" && define.amd
-      define([], growl)
-    else
-        window.growl = growl
-
-    return growl


subprocess: Don't require child_process at toplevel

--- src/subprocess.coffee
+++ src/subprocess.coffee
@@ -1,14 +1,13 @@
-
-# For starting the runtime used in test
-try
-  child_process = require 'child_process'
-catch err
-  return

 common = require './common'
 debug = require('debug')('fbp-spec:subprocess')

 exports.start = (command, options, callback) ->
+  try
+    child_process = require 'child_process'
+  catch err
+    return callback err
+
   options.timeout = 4000 if not options.timeout?

   started = false


Add node-statsd client package

Add node-statsd client package

--- CakeNodeModules.coffee
+++ CakeNodeModules.coffee
@@ -43,3 +43,5 @@
     cron            : '0.3.4'
     csv             : '0.0.17'
     embedly         : '0.6.0'
+
+    'node-statsd'   : '0.0.3'


cleaning up and extending upon test cases

--- src/test/basic.coffee
+++ src/test/basic.coffee
@@ -54,7 +54,7 @@
 			assert.throws (-> topic.create { "CON ST1" : 0 }), TypeError
 			assert.throws (-> topic.create { "53CONST1" : 0 }), TypeError

-	'When an enum is create()d properly using only integers':
+	'Inheritance and prevention of instantiation':
 		topic: () ->
 			Enum = require('../lib/enumjs.js').Enum
 			Enum.create { A : 1, B : 0, C : 0 }
@@ -63,6 +63,25 @@
 		'values() must not be an instance method' : (topic) ->
 			assert.isUndefined topic.A.values
 			assert.typeOf topic.values, 'function'
+		'enum is a subclass of Enum' : (topic) ->
+			Enum = require('../lib/enumjs.js').Enum
+			assert.equal topic.super_, Enum
+		'enum constants are instances of enum' : (topic) ->
+			Enum = require('../lib/enumjs.js').Enum
+			assert.instanceOf topic.A, topic
+			assert.instanceOf topic.A, Enum
+			assert.instanceOf topic.B, topic
+			assert.instanceOf topic.B, Enum
+		'valueOf() class method is different from valueOf() instance method' : (topic) ->
+			assert.notStrictEqual topic.valueOf, topic.A.valueOf
+			assert.notStrictEqual topic.valueOf, topic.B.valueOf
+		'subclasses of Enum have no create() factory method' : (topic) ->
+			assert.isUndefined topic.create
+
+	'When an enum is create()d properly using only integers':
+		topic: () ->
+			Enum = require('../lib/enumjs.js').Enum
+			Enum.create { A : 1, B : 0, C : 0 }
 		'instance method valueOf() must return correct enum constant' : (topic) ->
 			assert.doesNotThrow (-> topic.A.valueOf()), TypeError
 			assert.doesNotThrow (-> topic.A.valueOf('0')), TypeError
@@ -84,22 +103,11 @@
 			assert.isTrue topic.A.ordinal() == 1
 			assert.isTrue topic.B.ordinal() == 2
 			assert.isTrue topic.C.ordinal() == 3
-		'enum is a subclass of Enum' : (topic) ->
-			Enum = require('../lib/enumjs.js').Enum
-			assert.equal topic.super_, Enum
-		'enum constants are instances of enum' : (topic) ->
-			Enum = require('../lib/enumjs.js').Enum
-			assert.instanceOf topic.A, topic
-			assert.instanceOf topic.A, Enum
-			assert.instanceOf topic.B, topic
-			assert.instanceOf topic.B, Enum

 	'When an enum is create()d properly using object notation only':
 		topic: () ->
 			Enum = require('../lib/enumjs.js').Enum
 			Enum.create { A : { ordinal : 0 }, B : { ordinal : 0 }, C : {} }
-		'The enum class cannot be instantiated' : (topic) ->
-			assert.throws (-> topic()), TypeError
 		'The minimum ordinal is one (1)' : (topic) ->
 			assert.isTrue topic.A.ordinal() == 1
 		'The ordinals are all in sequence' : (topic) ->


implement `<fncr>` tag (action)

implement `<fncr>` tag (action)


uses notification instead of alert

uses notification instead of alert

--- src/ui/version.coffee
+++ src/ui/version.coffee
@@ -22,8 +22,10 @@
             higherVersionAvailable = versionToInt(releasedVersion) > versionToInt(localVersion)
             if higherVersionAvailable and (releasedVersion isnt versionAdvertised)
                 window.localStorage.versionAdvertised = releasedVersion
-                alert "A new yakyak version is available, please upgrade #{localVersion} to #{releasedVersion}"
-            else
-                console.log "YakYak local version is #{localVersion}, released version is #{releasedVersion}"
+                notr {
+                    html: "A new YakYak version is available
Please upgrade #{localVersion} to #{releasedVersion}
(click to dismiss)",
+                    stay: 0
+                }
+            console.log "YakYak local version is #{localVersion}, released version is #{releasedVersion}"

 module.exports = {check, versionToInt}


Rename isMemberOfCourse to just getCourse

--- src/user/model.coffee
+++ src/user/model.coffee
@@ -24,9 +24,8 @@
     _.extend(this, BLANK_USER)
     @channel.emit('change')

-  isMemberOfCourse: (collectionUUID) ->
-    course = _.findWhere @courses, ecosystem_book_uuid: collectionUUID
-    return !!course
+  getCourse: (collectionUUID) ->
+    _.findWhere @courses, ecosystem_book_uuid: collectionUUID

   ensureStatusLoaded: ->
     api.channel.emit("user.send.statusUpdate") unless @isLoggedIn()


fix back to for oauth sign in

fix back to for oauth sign in

--- angular/core/components/auth/provider_form/auth_provider_form.coffee
+++ angular/core/components/auth/provider_form/auth_provider_form.coffee
@@ -6,4 +6,4 @@

     $scope.select = (provider) ->
       $scope.$emit 'processing'
-      $window.location = provider.href
+      $window.location = provider.href = + "?back_to=#{$window.location.href}"


corrects an indent

corrects an indent

--- app/assets/javascripts/backbone/config/marionette/renderer.js.coffee
+++ app/assets/javascripts/backbone/config/marionette/renderer.js.coffee
@@ -8,7 +8,7 @@
       template = HandlebarsTemplates["backbone/apps/#{path}"]
       unless template
         throw "Template #{templatePath} not found!"
-       template(data)
+      template(data)

     getTemplate: (template) ->
       array = template.split('/')


Simplify guarding in address-selection watch

--- app/assets/javascripts/sprangular/directives/addressSelection.coffee
+++ app/assets/javascripts/sprangular/directives/addressSelection.coffee
@@ -10,13 +10,12 @@
     $scope.existingAddress = false

     $scope.$watch 'addresses', (addresses) ->
-      return unless addresses
+      return unless addresses && addresses.length > 0

-      if addresses.length > 0
-        found = _.find addresses, (existing) ->
-          existing.same($scope.address)
+      found = _.find addresses, (existing) ->
+        existing.same($scope.address)

-        $scope.toggleExistingAddress() if found
+      $scope.toggleExistingAddress() if found

     $scope.toggleExistingAddress = ->
       $scope.existingAddress = !$scope.existingAddress


popup: trying out Samfundets feed

popup: trying out Samfundets feed

--- cs/popup.coffee
+++ cs/popup.coffee
@@ -131,7 +131,7 @@
   # else
   #   $('#news').html '

Nyheter

Frakoblet fra online.ntnu.no

'
   newsLimit = 4
-
+
   News.get 'samfundet', newsLimit, (items) ->

     # Find most recent post, return if we've already seen it


Add envelope defaults and warning

Add envelope defaults and warning

--- src/instruments.coffee
+++ src/instruments.coffee
@@ -129,8 +129,8 @@
     options.type          ?= Envelope.AD

     if options.type == Envelope.AD
-      unless options.attackTime? && options.decayTime?
-        logWarning "Options must specify 'attackTime' and 'decayTime' values for AD envelope type."
+      options.attackTime  ?= 0.03
+      options.decayTime   ?= 1
       options.sustainTime = 0
       options.releaseTime = 0
       options.sustainLevel = 0
@@ -184,6 +184,8 @@
     inputSample * @getMultiplier(localTime)

   process: (child) ->
+    unless arguments.length == 1
+      throw new Error "#{@constructor.name}.process() only accepts a single argument."
     unless Function.isFunction(child)
       throw new Error "#{@constructor.name}.process() requires a sound generator but did not receive any."
     (time) => @realProcess(time, child(time))


Emitted SwitchStateChange event in KDOnOffSwitch.

--- components/inputs/KDOnOffSwitch.coffee
+++ components/inputs/KDOnOffSwitch.coffee
@@ -62,6 +62,7 @@

   switchStateChanged:()->
     # log "new state of #{@getName()} is #{@getValue()}",@getCallback()?
+    @emit 'SwitchStateChanged', @getValue()
     @getCallback().call @,@getValue() if @getCallback()?

   mouseDown:(event)->


Re-implementing "show closed" functionality for get competitions

--- resources/competitions.coffee
+++ resources/competitions.coffee
@@ -1,6 +1,4 @@
 {models, Resource, filters} = require '../resource_helper'
-FromJson = filters.FromJson
-FromUrlParams = filters.FromUrlParams

 class CompetitionResource extends Resource
   resourceName: 'competitions'
@@ -8,7 +6,7 @@

   getEndpoints: -> [
     getByOwner
-    'index'
+    index
     'show'
     'update'
     'destroy'
@@ -22,9 +20,29 @@
 getByOwner =
   route: '/by-owner/:ownerId'
   method: 'GET'
-  filters: [FromUrlParams]
+  filters: [filters.FromUrlParams, filters.FromQueryParams]
   emptyResult: []
   handler: ->
-    @api.find(owner_id: @params.ownerId)
+    ownerId = @params.ownerId
+    showClosed = @params.showClosed
+    query =
+      owner_id: ownerId
+    unless showClosed == 'true' or showClosed == true
+      query.open = true
+    console.log "[HTTP] request to list competitions by owner (ownerId=#{ownerId}, showClosed=#{showClosed})"
+    @api.find(query)
+
+index =
+  route: '/'
+  method: 'GET'
+  filters: [filters.FromUrlParams, filters.FromQueryParams]
+  emptyResult: []
+  handler: ->
+    showClosed = @params.showClosed
+    query = {}
+    unless showClosed == 'true' or showClosed == true
+      query.open = true
+    console.log "[HTTP] request to list competitions (showClosed=#{showClosed})"
+    @api.find(query)

 module.exports = new CompetitionResource


move 'Color Scheme' menu into 'View' menu for Linux/Windows

--- src/ui/views/menu.coffee
+++ src/ui/views/menu.coffee
@@ -112,7 +112,7 @@
                     click: -> action 'changefontsize', 'x-large'
                 }
             ]
-
+
         }, {
             label: 'Toggle Full Screen',
             accelerator: 'Command+Control+F',
@@ -232,6 +232,31 @@
             enabled: viewstate.loggedin
             click: (it) -> action 'showconvlast', it.checked
         }, {
+            label: 'Color Scheme'
+            submenu: [
+                {
+                    label: 'Default'
+                    type: 'radio'
+                    checked: viewstate.colorScheme == 'default'
+                    click: -> action 'changetheme', 'default'
+                }, {
+                    label: 'Blue'
+                    type: 'radio'
+                    checked: viewstate.colorScheme == 'blue'
+                    click: -> action 'changetheme', 'blue'
+                }, {
+                    label: 'Dark'
+                    type: 'radio'
+                    checked: viewstate.colorScheme == 'dark'
+                    click: -> action 'changetheme', 'dark'
+                }, {
+                    label: 'Material'
+                    type: 'radio'
+                    checked: viewstate.colorScheme == 'material'
+                    click: -> action 'changetheme', 'material'
+                }
+            ]
+        }, {
             label: 'Font Size'
             submenu: [
                 {
@@ -261,7 +286,6 @@
                     click: -> action 'changefontsize', 'x-large'
                 }
             ]
-
         }, {
             label: 'Toggle Full Screen',
             accelerator: 'Control+Alt+F',
@@ -300,33 +324,7 @@
             checked:  viewstate.showtray
             click: -> action 'toggleshowtray'
         }
-    ]},
-    {
-            label: 'Color Scheme'
-            submenu: [
-                {
-                    label: 'Default'
-                    type: 'radio'
-                    checked: viewstate.colorScheme == 'default'
-                    click: -> action 'changetheme', 'default'
-                }, {
-                    label: 'Blue'
-                    type: 'radio'
-                    checked: viewstate.colorScheme == 'blue'
-                    click: -> action 'changetheme', 'blue'
-                }, {
-                    label: 'Dark'
-                    type: 'radio'
-                    checked: viewstate.colorScheme == 'dark'
-                    click: -> action 'changetheme', 'dark'
-                }, {
-                    label: 'Material'
-                    type: 'radio'
-                    checked: viewstate.colorScheme == 'material'
-                    click: -> action 'changetheme', 'material'
-                }
-            ]
-        }
+    ]}
 ]

 module.exports = (viewstate) ->


contain sortable in parent

contain sortable in parent

--- lib/assets/javascripts/simple_form_attachments/simple_form_attachments.js.coffee
+++ lib/assets/javascripts/simple_form_attachments/simple_form_attachments.js.coffee
@@ -14,6 +14,7 @@
       items: '> tr'
       forcePlaceholderSize: true
       placeholder: 'attachment-placeholder'
+      containment: 'parent'
       start: (event, ui) ->
         cellCount = 0
         $('td, th', ui.helper).each ->


level states

level states

--- src/level_state.coffee
+++ src/level_state.coffee
@@ -6,7 +6,8 @@

   self.bind "enter", ->
     # TODO: Adjustable Fade In
-    engine.fadeIn()
+    engine.fadeIn
+      dur

     engine.loadMap I.level, ->
       engine.I.transitioning = false


GLray: Use invisible objects too.

GLray: Use invisible objects too.

--- engine/glray.coffee
+++ engine/glray.coffee
@@ -205,9 +205,9 @@
             # Assuming perspective projection and no shifting
             @inv_proj_x = camera.projection_matrix_inv[0]
             @inv_proj_y = camera.projection_matrix_inv[5]
-            @meshes = for m in scene.mesh_passes[0] when m.visible and m.render and m.physics_type != 'NO_COLLISION'
+            @meshes = for m in scene.mesh_passes[0] when m.physics_type != 'NO_COLLISION'
                 m
-            for m in scene.mesh_passes[1] when m.visible and m.render and m.alpha >= @alpha_treshold and m.physics_type != 'NO_COLLISION'
+            for m in scene.mesh_passes[1] when m.alpha >= @alpha_treshold and m.physics_type != 'NO_COLLISION'
                 @meshes.push (m)

         gl.uniformMatrix4fv(mat.u_projection_matrix, false, camera.projection_matrix)


Better line parsing (add tests).

Better line parsing (add tests).

--- tests/unit_tests/commands_test.coffee
+++ tests/unit_tests/commands_test.coffee
@@ -98,6 +98,22 @@
     for command in Commands.advancedCommands
       assert.isTrue 0 <= @allCommands.indexOf command

+context "Parse commands",
+  should "omit whitespace", ->
+    assert.equal 0, BgUtils.parseLines("    \n    \n   ").length
+
+  should "omit comments", ->
+    assert.equal 0, BgUtils.parseLines(" # comment   \n \" comment   \n   ").length
+
+  should "join lines", ->
+    assert.equal 1, BgUtils.parseLines("a\\\nb").length
+    assert.equal "ab", BgUtils.parseLines("a\\\nb")[0]
+
+  should "trim lines", ->
+    assert.equal 2, BgUtils.parseLines("  a  \n  b").length
+    assert.equal "a", BgUtils.parseLines("  a  \n  b")[0]
+    assert.equal "b", BgUtils.parseLines("  a  \n  b")[1]
+
 # TODO (smblott) More tests:
 # - Ensure each background command has an implmentation in BackgroundCommands
 # - Ensure each foreground command has an implmentation in vimium_frontent.coffee


Add us TODOs

Add us TODOs

--- dev/us.cson
+++ dev/us.cson
@@ -811,3 +811,488 @@
     { type: 'content-4', color: 'black',  content: 'TRUCKS', transform: 'translate(0,-100%) scale(.7,.9)' }
     { type: 'us_speed_value', color: 'black',  transform: 'translate(0,35%) scale(1.1)' }
   ]
+
+'pass-on-either-side':
+  category: 'mandatory'
+  name: 'Pass on either side'
+  elements: [
+    { type: 'circle-bg', color: 'black' }
+  ]
+
+'yield':
+  category: 'regulatory'
+  name: 'Yield'
+  elements: [
+    { type: 'circle-bg', color: 'black' }
+  ]
+
+'no-bicycles':
+  category: 'Regulatory'
+  name: 'No bicycles'
+  elements: [
+    { type: 'circle-bg', color: 'black' }
+  ]
+
+'no-entry':
+  category: 'regulatory'
+  name: 'No entry'
+  elements: [
+    { type: 'circle-bg', color: 'black' }
+  ]
+
+'no-left-or-u-turn':
+  category: 'regulatory'
+  name: 'No left or U turn'
+  elements: [
+    { type: 'circle-bg', color: 'black' }
+  ]
+
+'no-left-turn':
+  category: 'regulatory'
+  name: 'No left turn'
+  elements: [
+    { type: 'circle-bg', color: 'black' }
+  ]
+
+'no-parking':
+  category: 'regulatory'
+  name: 'No parking'
+  elements: [
+    { type: 'circle-bg', color: 'black' }
+  ]
+
+'no-pedestrians':
+  category: 'regulatory'
+  name: 'No pedestrians'
+  elements: [
+    { type: 'circle-bg', color: 'black' }
+  ]
+
+'no-straight-through':
+  category: 'regulatory'
+  name: 'No straight through'
+  elements: [
+    { type: 'circle-bg', color: 'black' }
+  ]
+
+'no-heavy-goods-vehicles':
+  category: 'regulatory'
+  name: 'No heaby goods vehicles'
+  elements: [
+    { type: 'circle-bg', color: 'black' }
+  ]
+
+'no-right-turn':
+  category: 'regulatory'
+  name: 'No right turn'
+  elements: [
+    { type: 'circle-bg', color: 'black' }
+  ]
+
+'no-u-turn':
+  category: 'regulatory'
+  name: 'No U turn'
+  elements: [
+    { type: 'circle-bg', color: 'black' }
+  ]
+
+'maximum-speed-limit':
+  category: 'regulatory'
+  name: 'Maximum speed limit'
+  elements: [
+    { type: 'circle-bg', color: 'black' }
+  ]
+
+'stop':
+  category: 'regulatory'
+  name: 'Stop'
+  elements: [
+    { type: 'circle-bg', color: 'black' }
+  ]
+
+'t-roads':
+  category: 'warning'
+  name: 'T-roads'
+  elements: [
+    { type: 'circle-bg', color: 'black' }
+  ]
+
+'y-roads':
+  category: 'warning'
+  name: 'Y-roads'
+  elements: [
+    { type: 'circle-bg', color: 'black' }
+  ]
+
+'added-lane-from-entering-roadway':
+  category: 'warning'
+  name: 'Added lane from entering roadway'
+  elements: [
+    { type: 'circle-bg', color: 'black' }
+  ]
+
+'added-lane-right':
+  category: 'warning'
+  name: 'Added lane right'
+  elements: [
+    { type: 'circle-bg', color: 'black' }
+  ]
+
+'slippery-bicycles':
+  category: 'warning'
+  name: 'Slippery bicycles'
+  elements: [
+    { type: 'circle-bg', color: 'black' }
+  ]
+
+'crossroads-ahead':
+  category: 'warning'
+  name: 'Crossroads ahead'
+  elements: [
+    { type: 'circle-bg', color: 'black' }
+  ]
+
+'double-curve-first-left-ahead':
+  category: 'warning'
+  name: 'Double curve first left ahead'
+  elements: [
+    { type: 'circle-bg', color: 'black' }
+  ]
+
+'double-curve-first-right-ahead':
+  category: 'warning'
+  name: 'Double curve first right ahead'
+  elements: [
+    { type: 'circle-bg', color: 'black' }
+  ]
+
+'cyclists-crossing':
+  category: 'warning'
+  name: 'Cyclists crossing'
+  elements: [
+    { type: 'circle-bg', color: 'black' }
+  ]
+
+'double-reverse-curve':
+  category: 'warning'
+  name: 'Reverse curve'
+  elements: [
+    { type: 'circle-bg', color: 'black' }
+  ]
+
+'falling-rocks-or-debris-right':
+  category: 'warning'
+  name: 'Falling rocks or debris right'
+  elements: [
+    { type: 'circle-bg', color: 'black' }
+  ]
+
+'emergency-vehicles':
+  category: 'warning'
+  name: 'Emergency vehicles'
+  elements: [
+    { type: 'circle-bg', color: 'black' }
+  ]
+
+'flaggers-in-road-ahead':
+  category: 'warning'
+  name: 'Flaggers in road ahead'
+  elements: [
+    { type: 'circle-bg', color: 'black' }
+  ]
+
+'hairpin-curve-right-ahead':
+  category: 'warning'
+  name: 'Curve right ahead'
+  elements: [
+    { type: 'circle-bg', color: 'black' }
+  ]
+
+'divided-highway-ahead':
+  category: 'warning'
+  name: 'Highway ahead'
+  elements: [
+    { type: 'circle-bg', color: 'black' }
+  ]
+
+'horizontal-alignment-left':
+  category: 'warning'
+  name: 'Horizontal alignment left'
+  elements: [
+    { type: 'circle-bg', color: 'black' }
+  ]
+
+'horizontal-alignment-right':
+  category: 'warning'
+  name: 'Horizontal alignment right'
+  elements: [
+    { type: 'circle-bg', color: 'black' }
+  ]
+
+'roundabout-ahead':
+  category: 'warning'
+  name: 'Roundabout ahead'
+  elements: [
+    { type: 'circle-bg', color: 'black' }
+  ]
+
+'tram-crossing':
+  category: 'warning'
+  name: 'Tram crossing'
+  elements: [
+    { type: 'circle-bg', color: 'black' }
+  ]
+
+'loop-270-degree':
+  category: 'warning'
+  name: 'Loop 270 degree'
+  elements: [
+    { type: 'circle-bg', color: 'black' }
+  ]
+
+'loop-pretzel':
+  category: 'warning'
+  name: 'Loop pretzel'
+  elements: [
+    { type: 'circle-bg', color: 'black' }
+  ]
+
+'height-restriction-ahead':
+  category: 'warning'
+  name: 'Height restriction ahead'
+  variation: 'feet'
+  elements: [
+    { type: 'circle-bg', color: 'black' }
+  ]
+
+'traffic-merges-right-ahead':
+  category: 'warning'
+  name: 'Traffic merges right ahead'
+  elements: [
+    { type: 'circle-bg', color: 'black' }
+  ]
+
+'height-restriction-ahead--meters':
+  category: 'warning'
+  name: 'Height restriction ahead'
+  variation: 'meters'
+  elements: [
+    { type: 'circle-bg', color: 'black' }
+  ]
+
+'motorcycle-crossing':
+  category: 'warning'
+  name: 'Motorcycle crossing'
+  elements: [
+    { type: 'circle-bg', color: 'black' }
+  ]
+
+'narrow-bridge-ahead':
+  category: 'warning'
+  name: 'Bridge ahead'
+  elements: [
+    { type: 'circle-bg', color: 'black' }
+  ]
+
+'offset-roads':
+  category: 'warning'
+  name: 'Offset roads'
+  elements: [
+    { type: 'circle-bg', color: 'black' }
+  ]
+
+'pass-left-or-right':
+  category: 'warning'
+  name: 'Pass left or right'
+  elements: [
+    { type: 'circle-bg', color: 'black' }
+  ]
+
+'pedestrian-crossing':
+  category: 'warning'
+  name: 'Pedestrian crossing'
+  elements: [
+    { type: 'circle-bg', color: 'black' }
+  ]
+
+'playground-ahead':
+  category: 'warning'
+  name: 'Playground ahead'
+  elements: [
+    { type: 'circle-bg', color: 'black' }
+  ]
+
+'uncontrolled-railroad-crossing-ahead':
+  category: 'warning'
+  name: 'Uncrontroller railroad crossing ahead'
+  elements: [
+    { type: 'circle-bg', color: 'black' }
+  ]
+
+'road-narrows-ahead':
+  category: 'warning'
+  name: 'Road narrows ahead'
+  elements: [
+    { type: 'circle-bg', color: 'black' }
+  ]
+
+'road-narrows-left-ahead':
+  category: 'warning'
+  name: 'Road narrows left ahead'
+  elements: [
+    { type: 'circle-bg', color: 'black' }
+  ]
+
+'road-narrows-right-ahead':
+  category: 'warning'
+  name: 'Road narrows right ahead'
+  elements: [
+    { type: 'circle-bg', color: 'black' }
+  ]
+
+'junction-with-a-side-road-acute-left-ahead':
+  category: 'warning'
+  name: 'Junction with a side road acute left ahead'
+  elements: [
+    { type: 'circle-bg', color: 'black' }
+  ]
+
+'junction-with-a-side-road-acute-right-ahead':
+  category: 'warning'
+  name: 'Junction with a side road acute right ahead'
+  elements: [
+    { type: 'circle-bg', color: 'black' }
+  ]
+
+'junction-with-a-side-road-perpendicular-left-ahead':
+  category: 'warning'
+  name: 'Junction with a side road perpendicular left ahead'
+  elements: [
+    { type: 'circle-bg', color: 'black' }
+  ]
+
+'junction-with-a-side-road-perpendicular-right-ahead':
+  category: 'warning'
+  name: 'Junction with a side road perpendicular right ahead'
+  elements: [
+    { type: 'circle-bg', color: 'black' }
+  ]
+
+'slippery-road-surface':
+  category: 'warning'
+  name: 'Slippery road surface'
+  elements: [
+    { type: 'circle-bg', color: 'black' }
+  ]
+
+'steep-hill-downwards':
+  category: 'warning'
+  name: 'Steep hill downwards'
+  elements: [
+    { type: 'circle-bg', color: 'black' }
+  ]
+
+'stop-ahead':
+  category: 'warning'
+  name: 'Stop ahead'
+  elements: [
+    { type: 'circle-bg', color: 'black' }
+  ]
+
+'traffic-signals-ahead':
+  category: 'warning'
+  name: 'Traffic signals ahead'
+  elements: [
+    { type: 'circle-bg', color: 'black' }
+  ]
+
+'triple-reverse-curve':
+  category: 'warning'
+  name: 'Triple reverse curve'
+  elements: [
+    { type: 'circle-bg', color: 'black' }
+  ]
+
+'trucks-rollover':
+  category: 'warning'
+  name: 'Trucks rollover'
+  elements: [
+    { type: 'circle-bg', color: 'black' }
+  ]
+
+'turn-curve-left':
+  category: 'warning'
+  name: 'Turn curve left'
+  elements: [
+    { type: 'circle-bg', color: 'black' }
+  ]
+
+'turn-curve-right':
+  category: 'warning'
+  name: 'Turn curve right'
+  elements: [
+    { type: 'circle-bg', color: 'black' }
+  ]
+
+'turn-left':
+  category: 'warning'
+  name: 'Turn left'
+  elements: [
+    { type: 'circle-bg', color: 'black' }
+  ]
+
+'double-turn-first-left-ahead':
+  category: 'warning'
+  name: 'Double turn first left ahead'
+  elements: [
+    { type: 'circle-bg', color: 'black' }
+  ]
+
+'double-turn-first-right-ahead':
+  category: 'warning'
+  name: 'Double turn first right ahead'
+  elements: [
+    { type: 'circle-bg', color: 'black' }
+  ]
+
+'turn-right':
+  category: 'warning'
+  name: 'Turn right'
+  elements: [
+    { type: 'circle-bg', color: 'black' }
+  ]
+
+'two-way-traffic-ahead':
+  category: 'warning'
+  name: 'Two way traffic ahead'
+  elements: [
+    { type: 'circle-bg', color: 'black' }
+  ]
+
+'winding-road-first-left-ahead':
+  category: 'warning'
+  name: 'Winding road first left ahead'
+  elements: [
+    { type: 'circle-bg', color: 'black' }
+  ]
+
+'winding-road-first-right-ahead':
+  category: 'warning'
+  name: 'Winding road first right ahead'
+  elements: [
+    { type: 'circle-bg', color: 'black' }
+  ]
+
+'workers-in-road-ahead':
+  category: 'warning'
+  name: 'Workers in road ahead'
+  elements: [
+    { type: 'circle-bg', color: 'black' }
+  ]
+
+'yield-ahead':
+  category: 'warning'
+  name: 'Yield ahead'
+  elements: [
+    { type: 'circle-bg', color: 'black' }
+  ]


Display proper message when you have no networks selected

--- assets/js/views/html_code_area.coffee
+++ assets/js/views/html_code_area.coffee
@@ -8,6 +8,7 @@
           styleType: App.request('supportedStyles').findWhere({selected: true}).get('name')
         ).render().$el.html()

+      code = "<!-- Oh no! Better select some networks! -->" unless code.length > 0
       @$el.text(html_beautify(code))

     modelEvents: ->


fixing syntax error

fixing syntax error

--- src/models/base.coffee
+++ src/models/base.coffee
@@ -38,7 +38,7 @@

   sync: () ->
     # attempt to detect pagination
-    if _.isObject data \
+    if _.isObject(data) \
     and "results" of data and "prev" of data and "next" of data
       @count = data.count
       @prev = data.previous


tambah router untuk index

tambah router untuk index

--- Server/routes/UserRoute.coffee
+++ Server/routes/UserRoute.coffee
@@ -96,4 +96,7 @@
                 success: true
     ) req, res

+router.get '/', (req, res) ->
+    res.render 'index'
+
 module.exports = router


code cleanup up-  deleted appendMetadata()

--- app/controllers/classifier.coffee
+++ app/controllers/classifier.coffee
@@ -671,8 +671,7 @@
       processedKnown = @averageKnownPoints(processedKnown)
       radius = 10
       #process marking surface
-      for surface in [@markingSurfaceList...]
-
+      for surface in [@markingSurfaceList...]
         surface.addShape 'ellipse', class: "known-asteroid", opacity: 0.75, cx: processedKnown.x, cy: processedKnown.y, rx: radius, ry: radius, fill: "none", stroke: "rgb(20,200,20)", 'stroke-width': 2
       #and evaluate annotations for users marking known objects
       @evaluateAnnotations(processedKnown.P_ref)
@@ -705,14 +704,6 @@
     processedKnown.x =  processedKnown.x * (SCALING_FACTOR / HALF_WINDOW_HEIGHT )
     processedKnown.y =  processedKnown.y * (SCALING_FACTOR / HALF_WINDOW_HEIGHT )
     processedKnown
-
-  appendMetadata: ->
-    # allKnowns = ""
-    # knownObjects = @Subject.current?.metadata?.known_objects["0001"]
-    # if knownObjects
-    #   for metadata in knownObjects
-    #     allKnowns += metadata.object if metadata.good_known is true
-    # @el.find("#metadata-knowns").html allKnowns

   evaluateAnnotations: (P_ref) ->
     # console.log 'GROUND TRUTH: (',P_ref.x,',',P_ref.y,')'


critical fix: marks destroyed before being sent (oops); remove console logs

--- app/controllers/classifier.coffee
+++ app/controllers/classifier.coffee
@@ -303,7 +303,7 @@
   loadSubjectData: () ->
     $('#graph-container').addClass 'loading-lightcurve'
     jsonFile = @subject.selected_light_curve.location
-    console.log 'jsonFile: ', jsonFile # DEBUG CODE
+    # console.log 'jsonFile: ', jsonFile # DEBUG CODE

     # handle ui elements
     @el.find('#loading-screen').fadeIn()
@@ -574,7 +574,7 @@
     # @noTransitsButton.show()
     @classifySummary.fadeOut(150)
     @nextSubjectButton.hide()
-    @canvasGraph.marks.destroyAll() #clear old marks
+    # @canvasGraph.marks.destroyAll() #clear old marks
     # @canvas.outerHTML = ""
     @resetTalkComment @talkComment
     @resetTalkComment @altTalkComment
@@ -626,12 +626,13 @@

     # SEND CLASSIFICATION
     @course.incrementCount()
-    console.log 'YOU\'VE MARKED ', @course.count, ' LIGHT CURVES!'
+    # console.log 'YOU\'VE MARKED ', @course.count, ' LIGHT CURVES!'

     @classification.annotate
       classification_type: 'light_curve'
       selected_id:          @subject.selected_light_curve._id
       location:             @subject.selected_light_curve.location
+
     for mark in [@canvasGraph.marks.all...]
       @classification.annotate
         timestamp: mark.timestamp
@@ -644,14 +645,15 @@
     # dump all recorded click events to classification
     @classification.set 'recordedClickEvents', [@recordedClickEvents...]

-    # DEBUG CODE
-    console.log JSON.stringify( @classification )
-    console.log '********************************************'
+    # # DEBUG CODE
+    # console.log JSON.stringify( @classification )
+    # console.log '********************************************'

     # send classification (except for tutorial subject)
     unless @classification.subject.id is 'TUTORIAL_SUBJECT'
       @classification.send()

+    @canvasGraph.marks.destroyAll() #clear old marks
     @recordedClickEvents = []
     @Subject.next()


Tags are implemented.

Tags are implemented.

--- app/controllers/tasks.item.coffee
+++ app/controllers/tasks.item.coffee
@@ -88,6 +88,8 @@
     @el.draggable({ disabled: false })
     val = @name.text()
     if val then @task.updateAttribute("name", val) else @task.destroy()
+    # Makes tags clickable because shit.
+    @name.html(val.replace(new RegExp("\\s#([^ ]*)", "ig"), ' #$1'))

   endEditOnEnter: (e) =>
     if e.which is ENTER_KEY
@@ -109,7 +111,12 @@

   tagClick: (e) =>
     e.stopPropagation()
-    @log $(e.currentTarget).text()
+    List.trigger "changeList",
+      name: "Tagged with " + $(e.currentTarget).text()
+      id: "search"
+      tasks: Task.tag($(e.currentTarget).text().substr(1))
+      disabled: yes
+      permanent: yes

 module.exports = TaskItem


Added possibility to access plugin by type if type exists

--- src/core/pluginsEngine/plugins.coffee
+++ src/core/pluginsEngine/plugins.coffee
@@ -59,6 +59,8 @@
 		if not plugin_data.name? or plugin_data.name isnt plugin_name
 			plugin_data.name = plugin_name

+
+
 		if plugin_data.type isnt 'global-interface'
 			loadPlugin(plugin_data)
 		else
@@ -68,8 +70,12 @@
 		env.debug "Loading " + plugin_data.name.blue
 		try
 			plugin = require(env.pluginsEngine.cwd + '/plugins/' + plugin_data.name + plugin_data.main)(env)
-			pluginsEngine.plugin[plugin_data.name] = plugin
-			pluginsEngine.plugin[plugin_data.name]?.plugin_config = plugin_data
+			if plugin_data.type?
+				pluginsEngine.plugin[plugin_data.type] = plugin
+				pluginsEngine.plugin[plugin_data.type]?.plugin_config = plugin_data
+			else
+				pluginsEngine.plugin[plugin_data.name] = plugin
+				pluginsEngine.plugin[plugin_data.name]?.plugin_config = plugin_data
 		catch e
 			env.debug "Error while loading plugin " + plugin_data.name
 			env.debug e.message.yellow + ' at line ' + e.lineNumber?.red


ListView

ListView

- Fix for "ng" layout row measuring when ListViews are nested.

--- src/elements/ListView/ListView.coffee
+++ src/elements/ListView/ListView.coffee
@@ -384,7 +384,7 @@
 				col = parseInt(cell.getAttribute("col"))

 				if @_autoLayout == 2
-					if DOM.getAttribute(cell.parentNode,"cui-lv-tr-unmeasured")
+					if DOM.getAttribute(cell.parentNode, "cui-lv-tr-unmeasured")
 						# row has not been measured
 						return
 					@__resetRowDim(row)
@@ -949,7 +949,7 @@
 				if opts.resetRows
 					sel = ".cui-lv-tr-outer"
 				else
-					sel = "[cui-lv-tr-unmeasured]"
+					sel = "[cui-lv-tr-unmeasured=\""+@listViewCounter+"\"]"

 				for row in DOM.matchSelector(@grid[0], "[cui-lv-quadrant='#{qi}'] "+sel)
 					rows[parseInt(DOM.getAttribute(row, "row"))] = row
@@ -1194,7 +1194,7 @@
 					when true
 						html[qi].push("
")
 					when 2
-						html[qi].push("

")
+						html[qi].push("

")
 					else
 						html[qi].push("
")

@@ -1396,7 +1396,7 @@

 		if @__rows[row_i]
 			for row in @__rows[row_i]
-				DOM.setAttribute(row, "cui-lv-tr-unmeasured", 1)
+				DOM.setAttribute(row, "cui-lv-tr-unmeasured", @listViewCounter)

 		for display_col_i in [0..@colsCount-1]
 			col_i = @getColIdx(display_col_i)


Activity: removed log

Activity: removed log

--- client/Social/Activity/AppController.coffee
+++ client/Social/Activity/AppController.coffee
@@ -88,7 +88,6 @@
       messages   = socialapi.getPrefetchedData 'navigated'
       KD.utils.defer ->  callback null, messages
     else
-      log id, firstFetch, 'hello'
       socialapi.channel.fetchActivities {id, from, limit}, callback

     firstFetch = yes


markerlist() ok

markerlist() ok

--- app/views/CNeditor/CNeditor.coffee
+++ app/views/CNeditor/CNeditor.coffee
@@ -121,6 +121,11 @@
     setDepthAbs : (absDepth) ->
         @lineDepthAbs = absDepth
         @line$.prop('class',"#{@lineType}-#{absDepth}")
+
+    setTypeDepth : (type, absDepth) ->
+        @lineType = type
+        @lineDepthAbs = absDepth
+        @line$.prop('class',"#{type}-#{absDepth}")

 Line.clone = (line) ->
     clone = new Line()
@@ -682,14 +687,14 @@
     #
     #  Turn selected lines in a Marker List
     ###
+
     markerList : (l) ->
-        # 1- Variables
+        # 1- find first and last div of the lines to turn into markers
         if l?
             startDivID = l.lineID
             endLineID  = startDivID
         else
             range = @getEditorSelection().getRangeAt(0)
-
             startDiv = selection.getLineDiv(
                     range.startContainer,
                     range.startOffset
@@ -698,59 +703,99 @@
                     range.endContainer,
                     range.endOffset
                 )
-
-            # 2- find first and last div corresponding to the 1rst and
-            #    last selected lines
             startDivID =  startDiv.id
             endLineID = endDiv.id

-        # 3- loop on each line between the first and last line selected
-        # TODO : deal the case of a multi range (multi selections).
-        #        Currently only the first range is taken into account.
+        # 2- loop on each line between the first and last line selected
+        # TODO : deal the case of a multi range (multi selections).
         line = @_lines[startDivID]
         loop
             switch line.lineType
-                when 'Th'
-                    lineTypeTarget = 'Tu'
-                    # transform all next Th & Lh siblings in Tu & Lu
-                    l = line.lineNext
-                    while l!=null and l.lineDepthAbs >= line.lineDepthAbs
-                        switch l.lineType
-                            when 'Th'
-                                l.line$.prop("class","Tu-#{l.lineDepthAbs}")
-                                l.lineType = 'Tu'
-                                l.lineDepthRel = @_findDepthRel(l)
-                            when 'Lh'
-                                l.line$.prop("class","Lu-#{l.lineDepthAbs}")
-                                l.lineType = 'Lu'
-                                l.lineDepthRel = @_findDepthRel(l)
-                        l=l.lineNext
-                    # transform all previous Th &vLh siblings in Tu & Lu
-                    l = line.linePrev
-                    while l!=null and l.lineDepthAbs >= line.lineDepthAbs
-                        switch l.lineType
-                            when 'Th'
-                                l.line$.prop("class","Tu-#{l.lineDepthAbs}")
-                                l.lineType = 'Tu'
-                                l.lineDepthRel = @_findDepthRel(l)
-                            when 'Lh'
-                                l.line$.prop("class","Lu-#{l.lineDepthAbs}")
-                                l.lineType = 'Lu'
-                                l.lineDepthRel = @_findDepthRel(l)
-                        l=l.linePrev
-                when 'Lh', 'Lu'
-                    # remember : the default indentation action is to make
-                    # a marker list, that's why it works here.
-                    @tab(line)
+                when 'Th','To'
+                    @_toggleLineType(line)
+                when 'Lh', 'Lo'
+                    line.setTypeDepth('Tu',line.lineDepthAbs+1)
+                when 'Lu'
+                    line.setTypeDepth('Tu',line.lineDepthAbs)
                 else
                     lineTypeTarget = false
-
-            if lineTypeTarget
-                line.setType(lineTypeTarget)
             if line.lineID == endLineID
                 break
-            else
-                line = line.lineNext
+            line = line.lineNext
+
+
+    ###
+        old version
+    ###
+    # markerList : (l) ->
+    #     # 1- Variables
+    #     if l?
+    #         startDivID = l.lineID
+    #         endLineID  = startDivID
+    #     else
+    #         range = @getEditorSelection().getRangeAt(0)
+
+    #         startDiv = selection.getLineDiv(
+    #                 range.startContainer,
+    #                 range.startOffset
+    #             )
+    #         endDiv = selection.getLineDiv(
+    #                 range.endContainer,
+    #                 range.endOffset
+    #             )
+
+    #         # 2- find first and last div corresponding to the 1rst and
+    #         #    last selected lines
+    #         startDivID =  startDiv.id
+    #         endLineID = endDiv.id
+
+    #     # 3- loop on each line between the first and last line selected
+    #     # TODO : deal the case of a multi range (multi selections).
+    #     #        Currently only the first range is taken into account.
+    #     line = @_lines[startDivID]
+    #     loop
+    #         switch line.lineType
+    #             when 'Th'
+    #                 lineTypeTarget = 'Tu'
+    #                 # transform all next Th & Lh siblings in Tu & Lu
+    #                 l = line.lineNext
+    #                 while l!=null and l.lineDepthAbs >= line.lineDepthAbs
+    #                     switch l.lineType
+    #                         when 'Th'
+    #                             l.line$.prop("class","Tu-#{l.lineDepthAbs}")
+    #                             l.lineType = 'Tu'
+    #                             l.lineDepthRel = @_findDepthRel(l)
+    #                         when 'Lh'
+    #                             l.line$.prop("class","Lu-#{l.lineDepthAbs}")
+    #                             l.lineType = 'Lu'
+    #                             l.lineDepthRel = @_findDepthRel(l)
+    #                     l=l.lineNext
+    #                 # transform all previous Th &vLh siblings in Tu & Lu
+    #                 l = line.linePrev
+    #                 while l!=null and l.lineDepthAbs >= line.lineDepthAbs
+    #                     switch l.lineType
+    #                         when 'Th'
+    #                             l.line$.prop("class","Tu-#{l.lineDepthAbs}")
+    #                             l.lineType = 'Tu'
+    #                             l.lineDepthRel = @_findDepthRel(l)
+    #                         when 'Lh'
+    #                             l.line$.prop("class","Lu-#{l.lineDepthAbs}")
+    #                             l.lineType = 'Lu'
+    #                             l.lineDepthRel = @_findDepthRel(l)
+    #                     l=l.linePrev
+    #             when 'Lh', 'Lu'
+    #                 # remember : the default indentation action is to make
+    #                 # a marker list, that's why it works here.
+    #                 @tab(line)
+    #             else
+    #                 lineTypeTarget = false
+
+    #         if lineTypeTarget
+    #             line.setType(lineTypeTarget)
+    #         if line.lineID == endLineID
+    #             break
+    #         else
+    #             line = line.lineNext

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


Update CampaignView.coffee (#6383)

Update CampaignView.coffee (#6383)

`->` override the `this`, so change to `=>`

--- app/views/play/CampaignView.coffee
+++ app/views/play/CampaignView.coffee
@@ -519,7 +519,7 @@
     if me.level() < 12 and @terrain is 'dungeon' and not @editorMode
       context.levels = _.reject context.levels, slug: 'signs-and-portents'
     if me.freeOnly()
-      context.levels = _.reject context.levels, (level) ->
+      context.levels = _.reject context.levels, (level) =>
         return true if (level.type in ['course', 'course-ladder']) and me.isStudent() and not @courseInstance  # Too much hassle to get Wakka Maul working for CS1 with no classroom
         return level.requiresSubscription
     if features.brainPop


Remove unnecessary nested selector with id.

--- apps/following/client/index.coffee
+++ apps/following/client/index.coffee
@@ -68,7 +68,7 @@
   showItemContent: (followItem) =>
     item = followItem.getItem() # Actual item model, e.g. an Artist
     item.fetchArtworks success: (artworks) =>
-      $container = @$("##{sd.TYPE} ##{item.get('id')}")
+      $container = @$("##{item.get('id')}")
       $followButton = $container.find(".follow-button")
       $artworks = $container.find('.artworks')
       view = new FillwidthView


Add the FileSystem.exists() method.

Add the FileSystem.exists() method.

--- system/FileSystem.coffee
+++ system/FileSystem.coffee
@@ -76,6 +76,13 @@
       contents.push {name: dir.name, meta: {type: "dir"}} for dir in files when dir.loc.match(@dirExp)?
       return contents

+    exists: (path) ->
+      files = @store.query {
+        loc: path
+      }
+      return true if files[0]?
+      return false
+
     open: (path) ->
       handle = makeUuid @node
       file = @store.query {


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/clamp_bounds.coffee
+++ test/clamp_bounds.coffee
@@ -1,7 +1,7 @@
 module "ClampBounds"

 test "should clamp the x-axis", ->
-  play
+  player =

   # Test for equality of two objects
   equals one, 1


:lipstick:

:lipstick:

--- d3-meets-chart.coffee
+++ d3-meets-chart.coffee
@@ -50,15 +50,15 @@
     easeInCirc: 'circle-in'
     easeOutCirc: 'circle-out'
     easeInOutCirc: 'circle-in-out'
-    easeInElastic: 'elastic-out'        # invert? but based on Chart.js
-    easeOutElastic: 'elastic-in'        # invert? but based on Chart.js
-    easeInOutElastic: 'elastic-in-out'  # differs from Chart.js
+    easeInElastic: 'elastic-out'        # Invert? but based on Chart.js
+    easeOutElastic: 'elastic-in'        # Invert? but based on Chart.js
+    easeInOutElastic: 'elastic-in-out'  # Differs from Chart.js
     easeInBack: 'back-in'
     easeOutBack: 'back-out'
     easeInOutBack: 'back-in-out'
-    easeInBounce: 'bounce-out'          # invert? but based on Chart.js
-    easeOutBounce: 'bounce-in'          # invert? but based on Chart.js
-    easeInOutBounce: 'bounce-in-out'    # differs from Chart.js
+    easeInBounce: 'bounce-out'          # Invert? but based on Chart.js
+    easeOutBounce: 'bounce-in'          # Invert? but based on Chart.js
+    easeInOutBounce: 'bounce-in-out'    # Differs from Chart.js
     # TODO: Implement custom easing type
     # easeInQuart: ''
     # easeOutQuart: ''
@@ -68,7 +68,6 @@
     # easeInOutQuint: ''

   getEasingType: (easingType) ->
-    # TODO: not string
     easingTypeName = @easingTypes[easingType]
     unless easingTypeName?
       throw new ReferenceError "'#{easingType}' is not a easing type name"
@@ -167,6 +166,6 @@
         if !--n and (--@transitionEndAllCount == 0)
           options.onAnimationComplete.apply(@, arguments)

-  # FIXME: resopnsive and unit bug
+  # FIXME: resopnsive and unit support
   translateToCenter: =>
     "translate(#{@rootSvgWidth() / 2}, #{@rootSvgHeight() / 2})"


activity: change input message

activity: change input message

--- client/Social/Activity/views/inputview.coffee
+++ client/Social/Activity/views/inputview.coffee
@@ -5,7 +5,7 @@
     options.cssClass              = KD.utils.curry "input-view", options.cssClass
     options.autogrow             ?= yes
     options.minHeight            ?= 54
-    options.placeholder          ?= "What's new #{Encoder.htmlDecode KD.whoami().profile.firstName}?"
+    options.placeholder          ?= "Hey #{Encoder.htmlDecode KD.whoami().profile.firstName}, share something interesting or ask a question."
     options.attributes          or= {}
     options.attributes.testpath or= "ActivityInputView"
     options.attributes.rows     or= 1


timeRangeInputView: only set values with @values

pipe value changes on start and end input changes through @values method

--- coffee/src/views/time_range_input_view.coffee
+++ coffee/src/views/time_range_input_view.coffee
@@ -181,29 +181,29 @@
   _onStartInputChanged: (start) =>
     return if start == @_start

-    @_start = start
-    changed = start: true
+    values =
+      start: start
+      end: @_end

     # if start has crossed above end, bump end higher
-    if @_end < @_start
-      @_end = @_bound(@_start + @_bounceOffset)
-      changed.end = true
-
-    @_change changed
+    if @_end < start
+      values.end = @_bound start + @_bounceOffset
+
+    @values values

   _onEndInputChanged: (end) =>
     return if end == @_end

-    @_end = end
-    changed = end: true
-
-    # if end has crossed below start, bump start lower
-    if @_end < @_start
-      @_start = @_bound(@_end - @_bounceOffset)
-      changed.start = true
-
-    @_change changed
+    values =
+      start: @_start
+      end: end
+
+    # if end has crossed below end, bump start lower
+    if end < @_start
+      values.start = @_bound end - @_bounceOffset
+
+    @values values

   _onSliderChanged: (vals) =>


checking

checking

--- components/auction_reminder/test/index.coffee
+++ components/auction_reminder/test/index.coffee
@@ -17,12 +17,13 @@
       Backbone.$ = $
       sinon.stub Backbone, 'sync'
       @reminder = new AuctionReminder
-      # AuctionReminder.__set__ 'Cookies', sinon.stub()
+      AuctionReminder.__set__ 'Cookies', sinon.stub()
       done()

   afterEach ->
+    benv.teardown()
     Backbone.sync.restore()
-    benv.teardown()
+    AuctionReminder.__set__ 'Cookies', sinon.restore()

   describe '#module.exports', ->
     it 'fetches auction and auction image if there is one', ->
@@ -41,13 +42,17 @@
       sinon.stub Backbone, 'sync'
       @AuctionReminderModal = AuctionReminder.__get__ 'AuctionReminderModal'
       @AuctionReminderModal::open = sinon.stub()
-      # AuctionReminder.__set__ 'Cookies', sinon.stub()
+      @cookies = sinon.stub(AuctionReminder.__get__ 'Cookies', 'initialize')
+      # console.log @cookies
+      # sinon.stub(@cookies)
       @auctionImage = "foo.jpg"
       done()

   afterEach ->
+    benv.teardown()
     Backbone.sync.restore()
-    benv.teardown()
+    @cookies.restore()
+

   describe '#initialize', ->

@@ -68,7 +73,7 @@
       _.isUndefined(view.$container).should.equal false

     it 'does not display if there are more than 24 hours until the end', ->
-      auction = new Sale fabricate 'sale', { end_at: moment().add(24,'hours') }
+      auction = new Sale fabricate 'sale', { end_at: moment().add(25,'hours') }
       view = new @AuctionReminderModal(
         auction: auction
         auctionImage: @auctionImage


Suppression d'une classe inutile

Suppression d'une classe inutile

--- js/game/SuperClass.coffee
+++ js/game/SuperClass.coffee
@@ -1,17 +0,0 @@
-###
-Permet l'héritage multiple
-https://stackoverflow.com/questions/9064935/extending-multiple-classes-in-coffee-script
-###
-
-moduleKeywords = ['included', 'extended']
-
-define ->
-    class SuperClass
-        @include: (obj) ->
-            throw('include(obj) requires obj') unless obj
-            for key, value of obj.prototype when key not in moduleKeywords
-                @::[key] = value
-
-            included = obj.included
-            included.apply(this) if included
-            @


Print # of skipped tests too.

Print # of skipped tests too.

--- ConsoleReporter.coffee
+++ ConsoleReporter.coffee
@@ -5,7 +5,7 @@

 class ConsoleReporter extends  practical.mocha.BaseReporter

-  VERSION: "0.2.0-rc.1"
+  VERSION: "0.2.0"

   constructor: (@clientRunner, @serverRunner, @options)->
     try
@@ -107,6 +107,7 @@
       console.log("---------------------RESULTS----------------------")
       console.log("PASSED:", @serverStats.passes + @clientStats.passes)
       console.log("FAILED:", @serverStats.failures + @clientStats.failures)
+      console.log("SKIPPED:", @serverStats.pending + @clientStats.pending)
       console.log("TOTAL:", @serverStats.total + @clientStats.total)
       console.log("--------------------------------------------------")
       console.log("--------------------------------------------------\n")


(- fix tests) Omission of .coffee in a require was breaking browserify

--- Paws/Test/data.tests.coffee
+++ Paws/Test/data.tests.coffee
@@ -1,5 +1,5 @@
 `                                                                                /*|*/ require = require('../Library/cov_require.js')(require)`
-support = require './support'
+support = require './support.coffee'

 assert  = require 'assert'
 expect  = require 'expect.js'


making 'while' and 'until' have the same associativity and precedence as 'for', #356

--- grammar.coffee
+++ grammar.coffee
@@ -602,10 +602,10 @@
   ["right",     'INDENT']
   ["left",      'OUTDENT']
   ["right",     'WHEN', 'LEADING_WHEN', 'IN', 'OF', 'BY', 'THROW']
-  ["right",     'FOR', 'NEW', 'SUPER', 'CLASS']
+  ["right",     'FOR', 'WHILE', 'UNTIL', 'NEW', 'SUPER', 'CLASS']
   ["left",      'EXTENDS']
   ["right",     'ASSIGN', 'RETURN']
-  ["right",     '->', '=>', '<-', 'UNLESS', 'IF', 'ELSE', 'WHILE', 'UNTIL']
+  ["right",     '->', '=>', '<-', 'UNLESS', 'IF', 'ELSE']
 ]

 # Wrapping Up


Coco 5622aef: grammar: POST_IF now has the same precedence as FOR/WHILE, making  work as expected

--- grammar.coffee
+++ grammar.coffee
@@ -566,8 +566,8 @@
   ['nonassoc',  'INDENT', 'OUTDENT']
   ['right',     '=', ':', 'COMPOUND_ASSIGN', 'RETURN', 'THROW', 'EXTENDS']
   ['right',     'FORIN', 'FOROF', 'BY', 'WHEN']
-  ['right',     'IF', 'UNLESS', 'ELSE', 'FOR', 'WHILE', 'UNTIL', 'LOOP', 'SUPER', 'CLASS']
-  ['right',     'POST_IF', 'POST_UNLESS']
+  ['right',     'IF', 'UNLESS', 'ELSE', 'UNTIL', 'LOOP', 'SUPER', 'CLASS']
+  ['left',      'POST_IF', 'POST_UNLESS', 'FOR', 'WHILE']
 ]

 # Wrapping Up


Give focusInput a memory (refactor).

Give focusInput a memory (refactor).

This makes the diff look big, but that's mainly due to a change in
the indentation.

--- content_scripts/vimium_frontend.coffee
+++ content_scripts/vimium_frontend.coffee
@@ -332,21 +332,23 @@
     new VisualMode()

   focusInput: do ->
-    # Track the most-recently focused input element.
-    focusedElement = null
+    # Track the most recently focused input element.
+    recentlyFocusedElement = null
     handlerStack.push
+      _name: "focus-input-tracker"
       focus: (event) ->
-        focusedElement = event.target if DomUtils.isEditable event.target
+        recentlyFocusedElement = event.target if DomUtils.isEditable event.target
+        true

     (count) ->
       # Focus the first input element on the page, and create overlays to highlight all the input elements, with
       # the currently-focused element highlighted specially. Tabbing will shift focus to the next input element.
       # Pressing any other key will remove the overlays and the special tab behavior.
-      resultSet = DomUtils.evaluateXPath(textInputXPath, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE)
+      resultSet = DomUtils.evaluateXPath textInputXPath, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE
       visibleInputs =
         for i in [0...resultSet.snapshotLength] by 1
-          element = resultSet.snapshotItem(i)
-          rect = DomUtils.getVisibleClientRect(element)
+          element = resultSet.snapshotItem i
+          rect = DomUtils.getVisibleClientRect element
           continue if rect == null
           { element: element, rect: rect }

@@ -354,13 +356,14 @@

       selectedInputIndex =
         if count == 1
+          # As the starting index, we pick that of the most recently focused input element (or 0).
           elements = visibleInputs.map (visibleInput) -> visibleInput.element
-          Math.max 0, elements.indexOf focusedElement
+          Math.max 0, elements.indexOf recentlyFocusedElement
         else
           Math.min(count, visibleInputs.length) - 1

       hints = for tuple in visibleInputs
-        hint = document.createElement("div")
+        hint = document.createElement "div"
         hint.className = "vimiumReset internalVimiumInputHint vimiumInputHint"

         # minus 1 for the border


Minor refactoring of CursorHider.

Minor refactoring of CursorHider.

--- content_scripts/vimium_frontend.coffee
+++ content_scripts/vimium_frontend.coffee
@@ -1089,23 +1089,20 @@
 CursorHider =
   #
   # Hides the cursor when the browser scrolls, and prevent mouse from hovering while invisible
+  # NOTE(smblott) onScroll and onMouseMove events come in pairs.
   #
   cursorHideStyle: null
   isScrolling: false

-  showCursor: ->
-    @cursorHideStyle.remove() if @cursorHideStyle.parentElement
-  hideCursor: ->
-    document.head.appendChild @cursorHideStyle unless @cursorHideStyle.parentElement
-
-  onMouseMove: (event) ->
-    if CursorHider.isScrolling # This event was caused by scrolling, don't show the cursor.
-      CursorHider.isScrolling = false
-    else
-      CursorHider.showCursor()
   onScroll: (event) ->
     CursorHider.isScrolling = true
-    CursorHider.hideCursor()
+    unless CursorHider.cursorHideStyle.parentElement
+      document.head.appendChild CursorHider.cursorHideStyle
+
+  onMouseMove: (event) ->
+    if CursorHider.cursorHideStyle.parentElement and not CursorHider.isScrolling
+      CursorHider.cursorHideStyle.remove()
+    CursorHider.isScrolling = false

   init: ->
     @cursorHideStyle = document.createElement("style")


[api] return counts as array instead of map to be more REST-y

--- src/redis-model.coffee
+++ src/redis-model.coffee
@@ -291,12 +291,12 @@

   queues: (callback) ->
     ideally   = errify callback
-    allCounts = {}
+    allCounts = []

     await @redis.keys "bull:*:id", ideally defer queues
-    for queue in queues
+    for queue, i in queues
       name = queue[5..-4]
-      await @stateCounts name, ideally defer allCounts[name]
+      await @stateCounts name, ideally defer allCounts[i]

     callback null, allCounts


return undefined for jest

return undefined for jest

--- coach/specs/exercise/collection.spec.coffee
+++ coach/specs/exercise/collection.spec.coffee
@@ -31,7 +31,7 @@
       @step.content.questions[0].formats = ['true-false']
       Collection.quickLoad(@stepId, @step)
       expect(Collection.getCurrentPanel(@stepId)).equal('multiple-choice')
-
+      undefined

   describe 'free response caching', ->
     it 'get returns cached free response if free response has been cached', ->


Fix missing modal backdrop.

Fix missing modal backdrop.

--- app/assets/javascripts/dot_ledger/views/sorted_transactions/form.js.coffee
+++ app/assets/javascripts/dot_ledger/views/sorted_transactions/form.js.coffee
@@ -3,8 +3,6 @@
     template: 'sorted_transactions/form'
     className: 'modal'
     initialize: ->
-      @options.categories.on 'all', =>
-        @render()

     ui:
       name: 'input[name=name]'
@@ -14,6 +12,14 @@
     onRender: ->
       new DotLedger.Helpers.FormErrors(@model, @$el)

+      @options.categories.on 'all', =>
+        @renderCategories()
+
+      @ui.name.val(@model.get('name') || @options.transaction.get('search'))
+      @ui.tags.val(@model.get('tag_list'))
+
+    renderCategories: ->
+      @ui.category.empty()
       _.each @options.categories.groupBy('type'), (categories, label) =>
         $optgroup = $("<optgroup label='#{label}'></optgroup>")
         _.each categories, (category) ->
@@ -21,9 +27,7 @@
           $optgroup.append($option)
         @ui.category.append($optgroup)

-      @ui.name.val(@model.get('name') || @options.transaction.get('search'))
       @ui.category.val(@model.get('category_id'))
-      @ui.tags.val(@model.get('tag_list'))

     events:
       'click button.save': 'save'


Fix missing reply buttons

Fix missing reply buttons

--- client/message.coffee
+++ client/message.coffee
@@ -510,6 +510,7 @@
   Router.current().params?.message == id

 Template.submessage.helpers
+  canReply: -> canPost @group, @_id
   tabindex: tabindex
   tabindex5: -> tabindex 5
   tabindex7: -> tabindex 7


autorun message drag support to properly detect files

--- client/message.coffee
+++ client/message.coffee
@@ -349,7 +349,7 @@
   image = imagesInternal[id].image
   checkImage image if image?

-messageDrag = (target, bodyToo = true) ->
+messageDrag = (target, bodyToo = true, old) ->
   return unless target
   onDragStart = (e) =>
     #url = "coauthor:#{@data._id}"
@@ -369,8 +369,11 @@
     #  url = formatted
     if bodyToo
       $(@find '.message-file')?.find('img, video, a')?.each (i, elt) =>
+        elt.removeEventListener 'dragstart', old if old?
         elt.addEventListener 'dragstart', onDragStart
+  target.removeEventListener 'dragstart', old if old?
   target.addEventListener 'dragstart', onDragStart
+  onDragStart

 ## A message is "naturally" folded if it is flagged as minimized or deleted.
 ## It still will be default-folded if it's an image referenced in another
@@ -386,7 +389,8 @@

   ## Drag/drop support.
   focusButton = $(@find '.message-left-buttons').find('.focusButton')[0]
-  messageDrag.call @, focusButton
+  @autorun =>
+    listener = messageDrag.call @, focusButton, true, listener

   ## Fold naturally folded (minimized and deleted) messages
   ## by default on initial load.
@@ -1205,7 +1209,8 @@
   canPrivateReply: -> 'private' in (@threadPrivacy ? ['public'])

 Template.tableOfContentsMessage.onRendered ->
-  messageDrag.call @, @find('a'), false
+  @autorun =>
+    listener = messageDrag.call @, @find('a'), false, listener

 addDragOver = (e) ->
   e.preventDefault()


Use https:// for default search engine.

Use https:// for default search engine.

Google bounces all searches to https:// anyway.

--- background_scripts/settings.coffee
+++ background_scripts/settings.coffee
@@ -88,7 +88,7 @@
     # "\bnext\b,\bmore\b,>,→,»,≫,>>"
     nextPatterns: "next,more,>,\u2192,\xbb,\u226b,>>"
     # default/fall back search engine
-    searchUrl: "http://www.google.com/search?q="
+    searchUrl: "https://www.google.com/search?q="
     # put in an example search engine
     searchEngines: "w: http://www.wikipedia.org/w/index.php?title=Special:Search&search=%s wikipedia"
     newTabUrl: "chrome://newtab"


Debounce updating the location with search query.

--- client/results.coffee
+++ client/results.coffee
@@ -206,6 +206,12 @@
     structuredQueryChange(sidebarIntoQuery template)
     return # Make sure CoffeeScript does not return anything

+# We do not want location to be updated for every key press, because this really makes browser history hard to navigate
+# TODO: This might make currentSearchQuery be overriden with old value if it happens that exactly after 500 ms user again presses a key, but location is changed to old value which sets currentSearchQuery and thus input field back to old value
+updateLoction = _.debounce (query) ->
+  Meteor.Router.toNew Meteor.Router.searchPath query
+, 500
+
 Deps.autorun ->
   if Session.get 'searchActive'
-    Meteor.Router.toNew Meteor.Router.searchPath Session.get 'currentSearchQuery'
+    updateLoction Session.get 'currentSearchQuery'


discussions: code cleanup

discussions: code cleanup

--- client/app/CommonViews/opinions/discussionactivityopinionview.coffee
+++ client/app/CommonViews/opinions/discussionactivityopinionview.coffee
@@ -15,7 +15,7 @@

     @opinionController = new OpinionListViewController view: @opinionList

-    @getData().on 'update', (updatedData)=>
+    @getData().on 'update', =>
       if @opinionList.items.length < 2 and @getData().opinions
         @opinionList.addItem @getData().opinions[@getData().opinions.length-1]


track refunds/returned refunds for customers

--- common/collections/hooks/sales-hooks.coffee
+++ common/collections/hooks/sales-hooks.coffee
@@ -20,11 +20,32 @@
     if sale.status != @previous.status and sale.status == 'refunded'
       if product && !product.paidUpfront
         Meteor.users.update(sale.producerId, { $inc: 'profile.balance': -1 * sale.price * sale.qty })
+
       Meteor.users.update(sale.customerId, { $inc: 'profile.balance': Markup(sale).saleTotal() })
+
+      Orders.insert {
+        user: sale.customerId,
+        recipient: sale.customerId,
+        balanceAmount: Markup(sale).saleTotal()
+        orderTotal: Markup(sale).saleTotal()
+        status: 'credited',
+        note: "refund for x#{sale.qty} #{sale.productName}"
+      }
       # should it go back into inventory?
       return
     if sale.status != @previous.status and @previous.status == 'refunded'
       if product && !product.paidUpfront
-        Meteor.users.update(sale.producerId, {$inc: 'profile.balance': sale.price*sale.qty})
+        Meteor.users.update(sale.producerId, { $inc: 'profile.balance': sale.price*sale.qty })
+
       Meteor.users.update(sale.customerId, {$inc: 'profile.balance': -1 * Markup(sale).saleTotal() })
+
+      Orders.insert {
+        user: sale.customerId,
+        recipient: sale.customerId,
+        balanceAmount: Markup(sale).saleTotal()
+        orderTotal: Markup(sale).saleTotal()
+        status: 'debited',
+        note: "returned refund of x#{sale.qty} #{sale.productName}"
+      }
+
       return


fixed followers/following/liked subroutes

--- client/landing/app/Applications/Members.kdapplication/AppController.coffee
+++ client/landing/app/Applications/Members.kdapplication/AppController.coffee
@@ -233,7 +233,7 @@
         controller.ready -> controller.handleQuery? query
       callback contentDisplay

-    switch route.split('/')[1]
+    switch route.split('/')[2]
       when 'Followers'
         @createFolloweeContentDisplay model, 'followers', kallback
       when 'Following'


Reset layout feature implemented.

Reset layout feature implemented.

--- components/sliderbar/KDSliderBarView.coffee
+++ components/sliderbar/KDSliderBarView.coffee
@@ -80,6 +80,7 @@
   setValue:(value, handle)->
     handle ?= @handles.first
     handle.setValue value
+    @emit "ValueChanged", handle

   setLimits:->
     {maxValue, minValue, interval}      = @getOptions()


Fix component caching, fixes #427

Fix component caching, fixes #427

--- src/lib/nodejs/ComponentLoader.coffee
+++ src/lib/nodejs/ComponentLoader.coffee
@@ -21,9 +21,9 @@
 babel = require 'babel-core'

 class ComponentLoader extends loader.ComponentLoader
-  writeCache: (options, modules, callback) ->
+  writeCache: (options, manifest, callback) ->
     filePath = path.resolve @baseDir, options.manifest
-    fs.writeFile filePath, JSON.stringify(modules, null, 2),
+    fs.writeFile filePath, JSON.stringify(manifest, null, 2),
       encoding: 'utf-8'
     , callback

@@ -78,14 +78,14 @@
     manifestOptions = @prepareManifestOptions()

     if @options.cache and not @failedCache
-      @readCache manifestOptions, (err, modules) =>
+      @readCache manifestOptions, (err, manifest) =>
         if err
           @failedCache = true
           @processing = false
           return callback err unless @options.discover
           return @listComponents callback
         @components = {}
-        @readModules modules.modules, (err) =>
+        @readModules manifest.modules, (err) =>
           @ready = true
           @processing = false
           @emit 'ready', true
@@ -101,7 +101,10 @@
         @ready = true
         @emit 'ready', true
         return callback null, @components unless @options.cache
-        @writeCache manifestOptions, modules, (err) =>
+        @writeCache manifestOptions,
+          version: 1
+          modules: modules
+        , (err) =>
           return callback err if err
           callback null, @components