allowed any Unicode letters in names

allowed any Unicode letters in names

--- src/lexer.coffee
+++ src/lexer.coffee
@@ -8,6 +8,8 @@
 #   'J' JS literal
 #   'X' symbol
 #   '$' end of file
+
+rLetters = '_A-Za-zªµºÀ-ÖØ-öø-ˁˆ-ˑˠ-ˤˬˮͰ-ʹͶ-ͷͺ-ͽΆΈ-ΊΌΎ-ΡΣ-ϵϷ-ҁҊ-ԧԱ-Ֆՙա-ևא-תװ-ײؠ-يٮ-ٯٱ-ۓەۥ-ۦۮ-ۯۺ-ۼۿܐܒ-ܯݍ-ޥޱߊ-ߪߴ-ߵߺࠀ-ࠕࠚࠤࠨࡀ-ࡘࢠࢢ-ࢬऄ-हऽॐक़-ॡॱ-ॷॹ-ॿঅ-ঌএ-ঐও-নপ-রলশ-হঽৎড়-ঢ়য়-ৡৰ-ৱਅ-ਊਏ-ਐਓ-ਨਪ-ਰਲ-ਲ਼ਵ-ਸ਼ਸ-ਹਖ਼-ੜਫ਼ੲ-ੴઅ-ઍએ-ઑઓ-નપ-રલ-ળવ-હઽૐૠ-ૡଅ-ଌଏ-ଐଓ-ନପ-ରଲ-ଳଵ-ହଽଡ଼-ଢ଼ୟ-ୡୱஃஅ-ஊஎ-ஐஒ-கங-சஜஞ-டண-தந-பம-ஹௐఅ-ఌఎ-ఐఒ-నప-ళవ-హఽౘ-ౙౠ-ౡಅ-ಌಎ-ಐಒ-ನಪ-ಳವ-ಹಽೞೠ-ೡೱ-ೲഅ-ഌഎ-ഐഒ-ഺഽൎൠ-ൡൺ-ൿඅ-ඖක-නඳ-රලව-ෆก-ะา-ำเ-ๆກ-ຂຄງ-ຈຊຍດ-ທນ-ຟມ-ຣລວສ-ຫອ-ະາ-ຳຽເ-ໄໆໜ-ໟༀཀ-ཇཉ-ཬྈ-ྌက-ဪဿၐ-ၕၚ-ၝၡၥ-ၦၮ-ၰၵ-ႁႎႠ-ჅჇჍა-ჺჼ-ቈቊ-ቍቐ-ቖቘቚ-ቝበ-ኈኊ-ኍነ-ኰኲ-ኵኸ-ኾዀዂ-ዅወ-ዖዘ-ጐጒ-ጕጘ-ፚᎀ-ᎏᎠ-Ᏼᐁ-ᙬᙯ-ᙿᚁ-ᚚᚠ-ᛪᜀ-ᜌᜎ-ᜑᜠ-ᜱᝀ-ᝑᝠ-ᝬᝮ-ᝰក-ឳៗៜᠠ-ᡷᢀ-ᢨᢪᢰ-ᣵᤀ-ᤜᥐ-ᥭᥰ-ᥴᦀ-ᦫᧁ-ᧇᨀ-ᨖᨠ-ᩔᪧᬅ-ᬳᭅ-ᭋᮃ-ᮠᮮ-ᮯᮺ-ᯥᰀ-ᰣᱍ-ᱏᱚ-ᱽᳩ-ᳬᳮ-ᳱᳵ-ᳶᴀ-ᶿḀ-ἕἘ-Ἕἠ-ὅὈ-Ὅὐ-ὗὙὛὝὟ-ώᾀ-ᾴᾶ-ᾼιῂ-ῄῆ-ῌῐ-ΐῖ-Ίῠ-Ῥῲ-ῴῶ-ῼⁱⁿₐ-ₜℂℇℊ-ℓℕℙ-ℝℤΩℨK-ℭℯ-ℹℼ-ℿⅅ-ⅉⅎↃ-ↄⰀ-Ⱞⰰ-ⱞⱠ-ⳤⳫ-ⳮⳲ-ⳳⴀ-ⴥⴧⴭⴰ-ⵧⵯⶀ-ⶖⶠ-ⶦⶨ-ⶮⶰ-ⶶⶸ-ⶾⷀ-ⷆⷈ-ⷎⷐ-ⷖⷘ-ⷞⸯ々-〆〱-〵〻-〼ぁ-ゖゝ-ゟァ-ヺー-ヿㄅ-ㄭㄱ-ㆎㆠ-ㆺㇰ-ㇿ㐀-䶵一-鿌ꀀ-ꒌꓐ-ꓽꔀ-ꘌꘐ-ꘟꘪ-ꘫꙀ-ꙮꙿ-ꚗꚠ-ꛥꜗ-ꜟꜢ-ꞈꞋ-ꞎꞐ-ꞓꞠ-Ɦꟸ-ꠁꠃ-ꠅꠇ-ꠊꠌ-ꠢꡀ-ꡳꢂ-ꢳꣲ-ꣷꣻꤊ-ꤥꤰ-ꥆꥠ-ꥼꦄ-ꦲꧏꨀ-ꨨꩀ-ꩂꩄ-ꩋꩠ-ꩶꩺꪀ-ꪯꪱꪵ-ꪶꪹ-ꪽꫀꫂꫛ-ꫝꫠ-ꫪꫲ-ꫴꬁ-ꬆꬉ-ꬎꬑ-ꬖꬠ-ꬦꬨ-ꬮꯀ-ꯢ가-힣ힰ-ퟆퟋ-ퟻ豈-舘並-龎ﬀ-ﬆﬓ-ﬗיִײַ-ﬨשׁ-זּטּ-לּמּנּ-סּףּ-פּצּ-ﮱﯓ-ﴽﵐ-ﶏﶒ-ﷇﷰ-ﷻﹰ-ﹴﹶ-ﻼＡ-Ｚａ-ｚｦ-ﾾￂ-ￇￊ-ￏￒ-ￗￚ-ￜ'

 tokenDefs = [
   ['-', /^(?:[ \t]+|[⍝\#].*)+/]
@@ -20,7 +22,7 @@
   ['S', /^(?:'[^']*')+|^(?:"[^"]*")+/]
   ['.', /^[\(\)\[\]\{\}:;←]/]
   ['J', /^«[^»]*»/]
-  ['X', /^(?:⎕?[a-z_]\w*|⍺⍺|⍵⍵|∇∇|[^¯'":«»])/i]
+  ['X', ///^(?:⎕?[#{rLetters}][#{rLetters}0-9]*|⍺⍺|⍵⍵|∇∇|[^¯'":«»])///i]
 ]

 # `stack` keeps track of bracket nesting and causes 'L' tokens to be dropped


refactor(lorax): Refactored lorax to make API easier to use

--- src/lorax.coffee
+++ src/lorax.coffee
@@ -14,6 +14,7 @@

 util   = require "util"
 fs     = require "fs"
+Q      = require "q"
 config = require "./lib/config"
 git    = require "./lib/git"

@@ -145,15 +146,39 @@

 ###
 @function
+@name get
+@description
+Get all commits or commits since last tag
+@param {String} grep  String regex to match
+@param {Function} log Function to output log messages
+@returns {Promise} Promise with an array of commits
+###
+get = (grep, log = console.log) ->
+  deferred = Q.defer()
+
+  getLog = (tag) ->
+    msg = "Reading commits"
+    msg += " since #{tag}" if tag
+    log msg
+
+    git.getLog(grep, tag).then deferred.resolve, deferred.reject
+
+  git.getLastTag().then getLog, -> getLog()
+
+  return deferred.promise
+
+###
+@function
 @name generate
 @description
 A shortcut function to get the latest tag, parse all the commits and generate the changelog
 @param {String} toTag The latest tag
 @param {String} file Filename to write to
-@param {String} fromTag A specific tag to start from
 ###
-generate = (toTag, file, fromTag) ->
-  parseAndWrite = (commits) ->
+generate = (toTag, file) ->
+  grep = Config.get("type").join "|"
+
+  get(grep).then (commits) ->
     parsedCommits = (parseCommit commit for commit in commits when commit)

     console.log "Parsed #{parsedCommits.length} commit(s)"
@@ -163,19 +188,8 @@

     console.log "Generated changelog to #{file} (#{toTag})"

-  parseFromLastTag = (lastTag) ->
-    tag = fromTag or lastTag
-    if tag
-      console.log "Reading commits since #{tag}"
-    else
-      console.log "Reading all commits"
-
-    grep = Config.get("type").join "|"
-    git.getLog(grep, tag).then parseAndWrite
-
-  git.getLastTag().then parseFromLastTag, -> parseFromLastTag()
-
 lorax = module.exports = {
+  get
   parseCommit
   write
   generate


Small code style change

Small code style change

--- src/mimic.coffee
+++ src/mimic.coffee
@@ -50,7 +50,7 @@
     return all_model if user_name == 'all'
     user = robot.brain.userForName(user_name)
     return null if !user
-    models[user.id] = models[user.id] or makeNewModel(user.id)
+    models[user.id] ||= makeNewModel(user.id)
     return models[user.id]

   # NSAbot
@@ -59,7 +59,7 @@
     return if !msg.message.text

     user = msg.message.user
-    models[user.id] = models[user.id] or makeNewModel(user.id)
+    models[user.id] ||= makeNewModel(user.id)
     model = models[user.id]

     model.learn msg.message.text


Simplify models.

Simplify models.

--- src/model.coffee
+++ src/model.coffee
@@ -24,13 +24,9 @@

     value ?= @state[prop]

-    validators = @validators[prop]
-    unless Array.isArray validators
-      validators = [validators]
-
-    for validator in validators
-      unless validator.call @, value, prop
-        return false
+    validator = @validators[prop]
+    unless validator.call @, value, prop
+      return false
     true

   validateAll: ->
@@ -43,18 +39,12 @@
     unless prop?
       return @transformAll()

-    transforms = @transforms[prop]
-    unless Array.isArray transforms
-      transforms = [transforms]
+    tranform = @transforms[prop]

     if value?
-      for transform in transforms
-        value = transform.call @, value, prop
-      value
+      transform.call @, value, prop
     else
-      for transform in transforms
-        @state[prop] = transform.call @, value, prop
-      @state[prop]
+      @state[prop] = transform.call @, @state[prop], prop

   transformAll: ->
     for prop of @transforms


Modify 'core' test to not warn on config override 'require's

--- test/core.coffee
+++ test/core.coffee
@@ -16,7 +16,11 @@
       warnOnReplace: false,
       warnOnUnregistered: false

-    mockery.registerAllowables [join(__dirname, '..', 'package')]
+    mockery.registerAllowables [
+      join(__dirname, '..', 'package')
+      join(__dirname, '..', 'Axiom')
+      join(__dirname, '..', 'axiom', 'base')
+    ]

     mockery.registerMock "#{process.cwd()}/node_modules/axiom-base", {
       services:


Coffee-script redux friendly syntax

Coffee-script redux friendly syntax

--- test/core.coffee
+++ test/core.coffee
@@ -24,10 +24,10 @@

   divider = (a, b, callback) ->
     throw new Error("Division by zero") if b == 0
-    setTimeout =>
+    setTimeout (=>
       extra = this?.extraValue || 0
       callback(null, extra + a / b)
-    , 10
+    ), 10

   it 'wraps a function to return a promise', ->
     pDivider = @Z.bindAsync(divider)


Add more unit tests

Add more unit tests

--- test/test.coffee
+++ test/test.coffee
@@ -1,9 +1,19 @@

 { expect } = require 'chai'
 sinon      = require 'sinon'
+nock       = require 'nock'
+isJSON     = require 'is-json'
 request    = require 'request'

 SL = require '../lib/'
+
+###
+* Test promises
+* Test chaning promises (using mocks? nock.js?)
+* Test specifying format 'JSON' and 'XML'
+* Test att url:en är rätt
+* Test att request kastar en error
+###

 keys =
   realtimeInformation: 'xxx'
@@ -12,51 +22,107 @@
   trafficSituation: 'xxx'
   disturbanceInformation: 'xxx'

-describe 'SL (Storstockholms Lokaltrafik) API Wrapper', ()->
+describe 'SL (Storstockholms Lokaltrafik) API Wrapper\n', ()->

-  describe 'new SL()', ()->
-    it "should throw NoKeySuppliedError", ->
-      noKeySupplied = -> new SL()
-      expect( noKeySupplied ).to.throw /One of the following API keys have to be supplied/
+  it "should be able to instantiate if valid key name(s) is supplied", ->
+    expect( -> new SL {realtimeInformation: "xxx"} ).to.not.throw Error
+    expect( -> new SL {tripPlanner: "xxx", locationLookup: 'yyy'} ).to.not.throw Error
+    expect( -> new SL keys ).to.not.throw Error

-  describe 'new SL("string")', ()->
-    it "should throw InvalidKeyFormatSuppliedError", ->
-      invalidKeyFormatSupplied = -> new SL("string")
-      expect( invalidKeyFormatSupplied ).to.throw /The supplied API key\(s\) has to be an object/
+  it "should be able to specify json or xml response format", ->
+    expect( -> new SL keys, 'xml' ).to.not.throw Error
+    expect( -> new SL keys, 'XML' ).to.not.throw Error
+    expect( -> new SL keys, 'json' ).to.not.throw Error
+    expect( -> new SL keys, 'JSON' ).to.not.throw Error

-  describe 'new SL({realtimeInformation: "xxx", wrongKeyName: "xxx"})', ()->
-    it "should throw InvalidKeyNameSuppliedError", ->
-      invalidKeyNameSupplied = -> new SL({realtimeInformation: "xxx", wrongKeyName: "xxx"})
-      expect( invalidKeyNameSupplied ).to.throw /The supplied key (.*) should be one of the following/
+  it "should throw error if no key is supplied", ->
+    expect( -> new SL() ).to.throw /One of the following API keys have to be supplied/

-  describe 'new SL({realtimeInformation: "xxx"}, "txt")', ->
-    it "should throw InvalidResponseFormatSuppliedError", ->
-      invalidResponseFormatSupplied = -> new SL({realtimeInformation: "xxx"}, "txt")
-      expect( invalidResponseFormatSupplied ).to.throw /(.*) which you supplied is not supported./
+  it "should throw error if supplied keys is not an object", ->
+    expect( -> new SL("string") ).to.throw /The supplied API key\(s\) has to be an object/
+    expect( -> new SL(123) ).to.throw /The supplied API key\(s\) has to be an object/

-  describe '...', ->
-    it 'should throw NoKeySuppliedForServiceError', ->
+  it "should throw error if invalid keyname is supplied", ->
+    invalidKeyNameSupplied = -> new SL({realtimeInformation: "xxx", wrongKeyName: "xxx"})
+    expect( invalidKeyNameSupplied ).to.throw /The supplied key (.*) should be one of the following/

-  describe '#realtimeInformation', ->
+  it "should throw error if invalid response format is supplied", ->
+    expect( -> new SL({realtimeInformation: "xxx"}, "txt") ).to.throw /(.*) which you supplied is not supported./
+    expect( -> new SL({realtimeInformation: "xxx"}, 123) ).to.throw /(.*) which you supplied is not supported./
+    expect( -> new SL({realtimeInformation: "xxx"}, {}) ).to.throw /(.*) which you supplied is not supported./

-    before (done) ->
+  it 'should throw error if calling a service without corresponding api key\n', ->
+    sl = new SL {realtimeInformation: "xxx"}
+    expect( -> sl.trafficSituation() ).to.throw /You have not supplied an API key for the (.*) service/
+    expect( -> sl.locationLookup {} ).to.throw /You have not supplied an API key for the (.*) service/
+
+  describe '.realtimeInformation', ->
+
+    beforeEach (done) ->
+      response = JSON.stringify require('./fixtures/success/realtimeInformation')
       sinon
         .stub(request, 'get')
-        .yields(null, null, JSON.stringify({login: "bulkan"}))
+        .yields(null, null, response)
       done()
+
+    afterEach (done) ->
+      request.get.restore()
+      done()
+
+    it 'should make request to realtimeinformation endpoint', (done) ->
+      sl = new SL {realtimeInformation: "xxx"}
+      sl.realtimeInformation (err, data) ->
+        url = request.get.args[0][0].url
+        expect(url).to.eql 'http://api.sl.se/api2/realtimedepartures.json?key=xxx'
+        done()
+
+    it 'should return response body as second argument in callback', (done) ->
+      sl = new SL {realtimeInformation: "xxx"}
+      sl.realtimeInformation (err, data) ->
+        expect(err).to.be.null
+        expect(data).to.not.be.empty
+        expect(data).to.be.an "object"
+        done()
+
+    it 'should call the callback only once', (done) ->
+      sl = new SL {realtimeInformation: "xxx"}
+      sl.realtimeInformation (err, data) ->
+        expect(request.get.calledOnce).to.eql true
+        done()
+
+    it 'options object should modify query string for request', (done) ->
+      sl = new SL {realtimeInformation: "xxx"}
+      sl.realtimeInformation {siteid: 9507}, (err, data) ->
+        url = request.get.args[0][0].url
+        expect(url).to.eql 'http://api.sl.se/api2/realtimedepartures.json?siteid=9507&key=xxx'
+        done()
+
+    it 'should return JSON if format is supplied at instantiation', (done) ->
+      sl = new SL {realtimeInformation: "xxx"}, 'json'
+      sl.realtimeInformation {}, (err, data) ->
+        expect( isJSON(data) ).to.eql true
+        done()
+
+  describe 'error', ->

     after (done) ->
       request.get.restore()
       done()

-    it 'can get user profile', (done) ->
-      sl = new SL realtimeInformation: 'xxx'
-      sl.realtimeInformation {siteid: 9507}, (err, data) ->
-        console.log request.get.args[0]
-        expect(err).to.be.null
-        expect(request.get.calledOnce).to.eql true
-        expect(data).to.not.be.empty
+    it 'should throw error if response has StatusCode other than 0', (done) ->
+
+      response = JSON.stringify require('./fixtures/error/realtimeInformation_4001')
+      sinon
+        .stub(request, 'get')
+        .yields(null, null, response)
+
+      sl = new SL {realtimeInformation: "xxx"}
+      sl.realtimeInformation (err, data) ->
+        expect(err).not.to.be.null
         done()
+
+    it 'should throw error if response contains errorCode', (done) ->
+      done()

   # describe '#locationLookup', ->


added multiple results test

added multiple results test

--- test/test.coffee
+++ test/test.coffee
@@ -58,7 +58,6 @@
 it 'should perform a simple select (zip)', connect (db, done)->
   selected = []
   db ->
-    # Get all zip codes
     selected = @zipcodes.pluck.zip()
   , (err)->
     throw err if err
@@ -67,3 +66,21 @@
     selected.should.be.instanceOf Array
     selected.slice(0, 1)[0].should.equal 501
     done()
+
+it 'should perform a simple select (multiple results)', connect (db, done)->
+  zips = []
+  cities = []
+  db ->
+    zips = @zipcodes.pluck.zip()
+    cities = @zipcodes.pluck.city()
+  , (err)->
+    throw err if err
+    zips.length.should.equal 4999
+    zips[0].should.equal 501
+    zips.should.be.instanceOf Array
+    zips.slice(0, 1)[0].should.equal 501
+    cities.length.should.equal 4999
+    cities[0].should.equal 'Holtsville'
+    cities.should.be.instanceOf Array
+    cities.slice(0, 1)[0].should.equal 'Holtsville'
+    done()


test: Change wording

test: Change wording

--- test/test.coffee
+++ test/test.coffee
@@ -34,12 +34,12 @@

 describe 'method', ->
   describe 'escapeTags', ->
-    it 'should return an expected result', ->
+    it 'should return expected result', ->
       assert.strictEqual bitterServer.escapeTags('
Night & "Day"
'),
         '<h1>Night & "Day"</h1>'

   describe 'formatDate', ->
-    it 'should return an expected result', ->
+    it 'should return expected result', ->
       assert.strictEqual bitterServer.formatDate('2013', '06', '09'),
         'June 9, 2013'
       assert.strictEqual bitterServer.formatDate('2020', '12', '31'),


virtualization data

virtualization data

--- src/devtools/panel/app/event.coffee
+++ src/devtools/panel/app/event.coffee
@@ -0,0 +1,42 @@
+# 事件控制器
+((exports)->
+  # 用来模拟数据的恢复
+  exports.tohken.event.handle = null
+  # 启动
+  exports.tohken.event.start = (environment)->
+    # 按分钟更新的定时器
+    exports.tohken.event.handle = setInterval(function() {
+      exports.tohken.event.action.call(environment)
+    }, 180000)
+  # 终止
+  exports.tohken.event.stop = ->
+    return if exports.tohken.event.handle == null
+    clearInterval(exports.tohken.event.handle)
+  # 进程
+  exports.tohken.event.action = ->
+    # 资源每三分钟增加三点
+    max = @data['resource']['max_resource']
+    @data['resource']['vcharcoal'] += 3 if @data['resource']['charcoal'] + @data['resource']['vcharcoal'] + 3 <= max
+    @data['resource']['vsteel']    += 3 if @data['resource']['steel']    + @data['resource']['vsteel']    + 3 <= max
+    @data['resource']['vcoolant']  += 3 if @data['resource']['coolant']  + @data['resource']['vcoolant']  + 3 <= max
+    @data['resource']['vfile']     += 3 if @data['resource']['file']     + @data['resource']['vfile']     + 3 <= max
+    # 自然恢复，每分钟+3，上限为49，在出阵和远征时不进行回复
+    # 因为战斗后会返回新的数据所以跳过
+    # 出阵时 -10
+    # 开始战斗 -3
+    # MVP +10
+    # 胜利时队长+3，失败不增加
+    # S时+4(不含MVP)  A时+3  B时+2  C时+1，败北不增加
+    # 待验证
+    return if @data['status']['in_battle']
+    conquest_list = []
+    # 检定远征
+    _.forEach @data['party']['data'], (v, k)=>
+      if v['finished_at'] != null
+        _.forEach v['slot'], (vi, ki)=>
+          conquest_list.push vi['serial_id'] if vi['serial_id'] != null
+    # 开始循环增加
+    _.forEach @data['sword']['data'], (v, k)=>
+      if _.findIndex v['serial_id'] == -1
+        @data['sword']['data'][k]['vfatigue'] +3 if @data['sword']['data'][k]['fatigue'] + @data['sword']['data'][k]['vfatigue'] <= 49
+)(window)


Simple pane: Added 'footer_center' attribute.

--- src/elements/Pane/SimplePane.coffee
+++ src/elements/Pane/SimplePane.coffee
@@ -22,6 +22,7 @@
 			"header_center"
 			"header_right"
 			"footer_left"
+			"footer_center"
 			"footer_right"
 		]
 			value = @["_#{k}"]
@@ -44,6 +45,7 @@
 			header_center: {}
 			header_left: {}
 			footer_left: {}
+			footer_center: {}
 			footer_right: {}
 			content: {}
 			force_header:
@@ -76,7 +78,7 @@
 				@_top =
 					content: @__pane_header

-		if @forceFooter() or not (CUI.util.isUndef(@_footer_left) and CUI.util.isUndef(@_footer_right))
+		if @forceFooter() or not (CUI.util.isUndef(@_footer_left) and CUI.util.isUndef(@_footer_center) and CUI.util.isUndef(@_footer_right))
 			@__pane_footer = new CUI.PaneFooter()

 			@_bottom =


delayed test

delayed test

--- src/test/builder.coffee
+++ src/test/builder.coffee
@@ -97,3 +97,27 @@

         xxx.end()

+    delayed: (æ) ->
+        xml = new Builder
+        xml.on 'end', æ.done
+        xml.on 'data', (tag) -> æ.equal results.shift(), tag
+        results = [
+            '<grass>'
+            '<dog/>'
+            '<cat/>'
+            '</grass>'
+        ]
+        dog = null
+        grass = xml.tag('grass')()
+        setTimeout ( ->
+            dog = grass.tag('dog')()
+        ), 2
+        setTimeout ( ->
+            grass.tag('cat').end()
+            dog.end()
+        ), 4
+        setTimeout ( ->
+            grass.end()
+        ),3
+
+


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/timed_events.coffee
+++ src/timed_events.coffee
@@ -24,7 +24,7 @@
   @name every
   @methodOf TimedEvents#
   @param {Number} n The number of frames to wait before executing the callback
-  @returns {Number} Distance between the two objects
+  @param
   ###
   every: (n, fn) ->
     if I.age.mod(n) is 0


refactored tree rendering

refactored tree rendering

--- app/assets/javascripts/oxalis/view/skeletontracing/abstract_tree_renderer.coffee
+++ app/assets/javascripts/oxalis/view/skeletontracing/abstract_tree_renderer.coffee
@@ -55,6 +55,7 @@
     # List of {x : ..., y : ..., id: ...} objects
     @nodeList = []

+    # set global mode
     mode = @MODE_NOCHAIN

     # TODO: Actually, I though that buildTree() is pretty heavy, but
@@ -90,71 +91,94 @@
     @recordWidths(root)
     @drawTreeWithWidths(root, @NODE_RADIUS, @canvas.width() - @NODE_RADIUS, @nodeDistance, mode)

-  drawTreeWithWidths : (tree, left, right, top, mode = @MODE_NORMAL) ->
+  drawTreeWithWidths : (tree, left, right, top, mode) ->

     # get the decision point
     decisionPoint = @getNextDecisionPoint(tree)
-    # calculate m (middle that divides the left and right tree, if any)
-    if decisionPoint.children.length == 0
-      m = (left + right) / 2
+
+    # TODO: algorithm expects all decision points to be branch points
+    # though a normal point with comment only has one child --> refactor this
+
+    middle = @calculateTreeMiddle(decisionPoint, left, right)
+    chainCount = @calculateChainCount(tree)
+    rootX = middle # if decisionPoint is leaf, there's not much to do
+
+    # if the decision point has 2 children, draw them and remember their position
+    if decisionPoint.children.length > 1
+
+      topChildren = @calculateChildTreeTop(mode, chainCount, top)
+      c1 = @drawTreeWithWidths(decisionPoint.children[0], left,  middle, topChildren, mode)
+      c2 = @drawTreeWithWidths(decisionPoint.children[1], middle, right, topChildren, mode)
+
+      # set the root's x coordinate to be in between the decisionPoint's children
+      rootX = (c1.rootX + c2.rootX) / 2
+
+      # draw edges from last node in 'chain' (or root, if chain empty)
+      # and the decisionPoint's children
+      @drawEdge(rootX, topChildren - @nodeDistance, c1.rootX, c1.top)
+      @drawEdge(rootX, topChildren - @nodeDistance, c2.rootX, c2.top)
+
+    if mode == @MODE_NORMAL or chainCount < 3
+      # Draw the chain and the root, connect them.
+      node = tree
+      for i in [0..chainCount]
+        @drawNode(rootX, top + i * @nodeDistance, node.id)
+        node = node.children[0]
+        if i != 0
+          @drawEdge(rootX, top + (i - 1) * @nodeDistance, rootX, top + i * @nodeDistance)
+
+    else if mode == @MODE_NOCHAIN
+
+      # Find out, if the chain contains an active node
+      node = tree.children[0]; hasActiveNode = false
+      for i in [0...(chainCount - 1)]
+        hasActiveNode |= node.id == @activeNodeId
+        node = node.children[0]
+
+      # Draw root, chain indicator and decision point
+      @drawNode(rootX, top, tree.id)
+      @drawEdge(rootX, top, rootX, top + 0.5 * @nodeDistance)
+      @drawChainIndicator(rootX, top + 0.5 * @nodeDistance, top + 1.5 * @nodeDistance, hasActiveNode)
+      @drawEdge(rootX, top + 1.5 * @nodeDistance, rootX, top + 2 * @nodeDistance)
+      @drawNode(rootX, top + 2 * @nodeDistance, decisionPoint.id)
+
+    return { rootX, top }
+
+  ###*
+   * calculate m (middle that divides the left and right tree, if any)
+   * @param  {Object} decisionPoint  a point which definitely has to be drawn
+   * @param  {Number} left           left border in pixels
+   * @param  {Number} right          right border in pixels
+   * @return {Number}                middle in pixels
+  ###
+  calculateTreeMiddle : (decisionPoint, left, right) ->
+
+    if decisionPoint.children.length < 2
+      return (left + right) / 2
     else
-      c1 = decisionPoint.children[0]
-      c2 = decisionPoint.children[1]
-      m = (right - left) * c1.width / (c1.width + c2.width) + left
-
-    # Calculate the length of the 'chain' of nodes with one child,
-    # because they all share the root's x coordinate
+      child1 = decisionPoint.children[0]
+      child2 = decisionPoint.children[1]
+      return (right - left) * child1.width / (child1.width + child2.width) + left
+
+  # Calculate the length of the 'chain' of nodes with one child,
+  # because they all share the root's x coordinate
+  calculateChainCount : (tree) ->
+
     chainCount = 0
     subTree = tree
     while subTree.children.length == 1
       subTree = subTree.children[0]
       chainCount++

-    # if the decision point has (2) children, draw them and remember their position
-    if decisionPoint.children.length > 0
-      # Calculate the top of the children
+    return chainCount
+
+  # Calculate the top of the children
+  calculateChildTreeTop : (mode, chainCount, top) ->
+
       if mode == @MODE_NORMAL or chainCount < 3
-        topC = top + (chainCount + 1) * @nodeDistance
+        return top + (chainCount + 1) * @nodeDistance
       else if mode == @MODE_NOCHAIN
-        topC = top + 3 * @nodeDistance
-
-      c1 = @drawTreeWithWidths(decisionPoint.children[0], left,  m, topC, mode)
-      c2 = @drawTreeWithWidths(decisionPoint.children[1], m, right, topC, mode)
-      # set the root's x coordinate to be in between the decisionPoint's childs
-      xr = (c1[0] + c2[0]) / 2
-      # draw edges from last node in 'chain' (or root, if chain empty)
-      # and the decisionPoint's children
-      @drawEdge(xr, topC - @nodeDistance, c1[0], c1[1])
-      @drawEdge(xr, topC - @nodeDistance, c2[0], c2[1])
-    else
-      # if decisionPoint is leaf, there's not much to do
-      xr = m
-
-    if mode == @MODE_NORMAL or chainCount < 3
-      # Draw the chain and the root, connect them.
-      node = tree
-      for i in [0..chainCount]
-        @drawNode(xr, top + i * @nodeDistance, node.id)
-        node = node.children[0]
-        if i != 0
-          @drawEdge(xr, top + (i - 1) * @nodeDistance, xr, top + i * @nodeDistance)
-
-    else if mode == @MODE_NOCHAIN
-
-      # Find out, if the chain contains an active node
-      node = tree.children[0]; hasActiveNode = false
-      for i in [0...(chainCount - 1)]
-        hasActiveNode |= node.id == @activeNodeId
-        node = node.children[0]
-
-      # Draw root, chain indicator and decision point
-      @drawNode(xr, top, tree.id)
-      @drawEdge(xr, top, xr, top + 0.5 * @nodeDistance)
-      @drawChainIndicator(xr, top + 0.5 * @nodeDistance, top + 1.5 * @nodeDistance, hasActiveNode)
-      @drawEdge(xr, top + 1.5 * @nodeDistance, xr, top + 2 * @nodeDistance)
-      @drawNode(xr, top + 2 * @nodeDistance, decisionPoint.id)
-
-    return [xr, top]
+        return top + 3 * @nodeDistance

   drawNode : (x, y, id) ->


return if response.body is null

return if response.body is null

- Destructuring assignment does not work the same as in ES6
- typeof null === 'object'

--- src/remote/services/hull.coffee
+++ src/remote/services/hull.coffee
@@ -84,10 +84,9 @@
     @trackMatcher = new TrackEventMatcher(config.track)

   ensureIsHuman: (response) =>
-    return response if (typeof response.body != 'object')
+    return response if (!response.body?)

     { sitekey, stoken } = response.body;
-
     return response if (response.status != 429 || !sitekey? || !stoken?)

     banner = @_getCaptchaBanner()


fix typo

fix typo

--- src/js/player.coffee
+++ src/js/player.coffee
@@ -51,7 +51,7 @@
          *

          *

          *    engines
-         *    初始化Engine，根据传入的engines来指定具体使用FlashMP3Core还是AudioCore来接管播放，当然也可以传入内核列表，Engine会内核所支持的音频格式做自适应。这里只看一下engines参数的可能值（其他参数一般无需配置，如有需要请查看engine.coffee的源码）：
+         *    初始化Engine，根据传入的engines来指定具体使用FlashMP3Core还是AudioCore来接管播放，当然也可以传入内核列表，Engine会根据内核所支持的音频格式做自适应。这里只看一下engines参数的可能值（其他参数一般无需配置，如有需要请查看engine.coffee的源码）：
          *

          *    [{

          *    constructor: 'FlashMP3Core',


enh: limit field size by height

enh: limit field size by height

--- coffee/application.coffee
+++ coffee/application.coffee
@@ -27,6 +27,10 @@
   window.innerWidth \
     || document.documentElement.clientWidth\
     || document.body.clientWidth
+windowHeight = ->
+  window.innerHeight \
+    || document.documentElement.clientHeight\
+    || document.body.clientHeight

 documentWidth = ->
     document.documentElement.scrollWidth\
@@ -45,6 +49,13 @@
     containerAvail=E('canvas-container').clientWidth
     #console.log "awail width: #{containerAvail}"
     w = containerAvail
+
+  #now calculae available height
+  canvasRect = canvas.getBoundingClientRect()
+  h = windowHeight() - canvasRect.top
+
+  #get the smaller of both
+  w = Math.min(w,h)
   #reduce it a bit
   w -= 16


go side still expects 'authentication' for now

--- src/kite/kite.coffee
+++ src/kite/kite.coffee
@@ -92,7 +92,7 @@
     return

   wrapMessage: (method, params, callback) ->
-    auth              : @options.auth
+    authentication    : @options.auth
     withArgs          : params
     responseCallback  : (response) ->
       { withArgs:[{ error: err, result }]} = response


Add comments

Add comments

--- templates/coffee.coffee
+++ templates/coffee.coffee
@@ -84,7 +84,9 @@

   s options.indent + options.contents
   unless options.global
-    s options.indent + 'return'
+    s options.indent + ""
+    s options.indent + "# No global variable export"
+    s options.indent + "return"
   s ")"

   buff.join('\n')


add modes python3,anaconda;update jupyter bridge

--- src/smc-webapp/buttonbar.coffee
+++ src/smc-webapp/buttonbar.coffee
@@ -1116,9 +1116,9 @@
             insert : "%javascript\n/* Use print(...) for output */"
         mode_jupyter_bridge:
             insert : """
-                     a3 = jupyter("anaconda3")
-                     # start new cells with %a3
-                     # or set %default_mode a3
+                     a5 = jupyter("anaconda5")
+                     # start new cells with %a5
+                     # or set %default_mode a5
                      """
         mode_md:
             insert : "%md\n"
@@ -1126,6 +1126,10 @@
             insert : "%octave\n"
         mode_python:
             insert : "%python\n"
+        mode_python3:
+            insert : "%python3\n"
+        mode_anaconda:
+            insert : "%anaconda\n"
         mode_r:
             insert : "%r\n"
         mode_scilab:
@@ -1596,6 +1600,7 @@
             ["Benchmark code repeatedly", "#mode_timeit"],
             ["Time code once", "#mode_time"],
             ["Language modes"],
+            ["Anaconda", "#mode_anaconda"],
             ["Cython", "#mode_cython"],
             ["Gap", "#mode_gap"],
             ["PARI/GP", "#mode_gp"],
@@ -1606,6 +1611,7 @@
             ["Markdown", "#mode_md"],
             ["Octave", "#mode_octave"],
             ["Python", "#mode_python"],
+            ["Python 3", "#mode_python3"],
             ["R", "#mode_r"],
             ["Shell", "#mode_sh"],
         ]]


cocalc: merging reverted smc_base_url → app_base_url

--- src/smc-webapp/misc_page.coffee
+++ src/smc-webapp/misc_page.coffee
@@ -44,7 +44,7 @@
 exports.APP_LOGO_NAME_WHITE    = require('webapp-lib/cocalc-font-white.svg')

 {join} = require('path')
-exports.BASE_URL = if window? then "#{window.location.protocol}//#{join(window.location.hostname, window.smc_base_url ? '')}" else theme.DOMAIN_NAME
+exports.BASE_URL = if window? then "#{window.location.protocol}//#{join(window.location.hostname, window.app_base_url ? '')}" else theme.DOMAIN_NAME

 local_diff = exports.local_diff = (before, after) ->
     # Return object
@@ -184,7 +184,7 @@
                         # j-i should be 36, unless we ever start to have different (vanity) project_ids
                         path = src.slice(j + '/files/'.length)
                         project_id = src.slice(i + '/projects/'.length, j)
-                        new_src = join('/', window.smc_base_url, project_id, 'raw', path)
+                        new_src = join('/', window.app_base_url, project_id, 'raw', path)
                         y.attr(attr, new_src)
                         continue

@@ -193,7 +193,7 @@
                         continue

                     # we do not have an absolute url, hence we assume it is a relative URL to a file in a project
-                    new_src = join('/', window.smc_base_url, opts.project_id, 'raw', opts.file_path, src)
+                    new_src = join('/', window.app_base_url, opts.project_id, 'raw', opts.file_path, src)
                     y.attr(attr, new_src)

         return e


webapp/project new: not null returned gives react error, or somewhere else?

--- src/smc-webapp/project_new.cjsx
+++ src/smc-webapp/project_new.cjsx
@@ -171,7 +171,8 @@
         show_jupyterlab_server_panel : false

     render: ->
-        return if not @props.create_file or not @props.create_file or not @props.project_id
+        if not @props.create_file or not @props.create_file or not @props.project_id
+            return null

         row_style =
             marginBottom:'8px'


Added CINR and RSSI paths for Seowon CPEs

--- sanitize.coffee
+++ sanitize.coffee
@@ -15,5 +15,9 @@
       extractFloat(value)
     when 'InternetGatewayDevice.WiMAX.Status.CINR2'
       extractFloat(value)
+    when 'InternetGatewayDevice.X_MTK_WiMAX_Param.CINR_level' # Seowon devices
+      extractFloat(value)
+    when 'InternetGatewayDevice.X_MTK_WiMAX_Param.RSSI_level' # Seowon devices
+      extractFloat(value)
     else
-      undefined+      undefined


fix(load): fix window load timing

fix(load): fix window load timing

Summary:
The app regressed to launching a blank white page for a while before
showing the UI. This was because I forgot to await for the now async
package loading

Test Plan: Manually boot app with compile cache disabled

Reviewers: mark, spang, juan

Reviewed By: juan

Differential Revision: https://phab.nylas.com/D3883

--- src/nylas-env.coffee
+++ src/nylas-env.coffee
@@ -683,12 +683,12 @@
           window.requestAnimationFrame =>
             window.requestAnimationFrame =>
               @displayWindow() unless initializeInBackground
-              @startWindow()
-              # These don't need to wait for the window's stores and such
-              # to fully activate:
-              @requireUserInitScript() unless safeMode
-              @showMainWindow()
-              ipcRenderer.send('window-command', 'window:loaded')
+              @startWindow().then =>
+                # These don't need to wait for the window's stores and
+                # such to fully activate:
+                @requireUserInitScript() unless safeMode
+                @showMainWindow()
+                ipcRenderer.send('window-command', 'window:loaded')

   # Initializes a secondary window.
   # NOTE: If the `packageLoadingDeferred` option is set (which is true for


remove some log messages

remove some log messages

--- src/smc-webapp/jupyter/store.coffee
+++ src/smc-webapp/jupyter/store.coffee
@@ -116,7 +116,6 @@
     get_cm_mode: =>
         metadata = @get('backend_kernel_info') ? @get('metadata')
         metadata = metadata?.toJS()
-        console.log('metadata =', metadata)

         if metadata?.language_info?.codemirror_mode?
             mode = metadata?.language_info?.codemirror_mode
@@ -130,7 +129,6 @@
         if typeof(mode) == 'string'
             mode = {name:mode}  # some kernels send a string back for the mode; others an object

-        console.log 'mode=', mode
         return mode

     get_more_output: (id) =>


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/point.coffee
+++ src/point.coffee
@@ -50,9 +50,8 @@
       @copy().clamp()

     clamp$: (n) ->
-      if @magnitude() > n
-        @norm$(n)
-
+      @norm$(n) if @magnitude() > n
+
     ###*
     Creates a copy of this point.


remove debug flag

remove debug flag

--- src/qqapi.coffee
+++ src/qqapi.coffee
@@ -43,7 +43,7 @@
         psessionid: psessionid
         r: jsons r

-    client.post {url:url,debug:true} , params , (ret,e)->
+    client.post {url:url} , params , (ret,e)->
         long_poll( auth_opts , callback )
         callback(ret,e)


add pretty pring

add pretty pring

--- src/quest.coffee
+++ src/quest.coffee
@@ -228,7 +228,7 @@
       console.log "Nothing to output.".gray

   jsonify: (sqlResult) ->
-    console.log JSON.stringify sqlResult.rows
+    console.log JSON.stringify(sqlResult.rows, undefined, 2)

   outputRows: (sqlResult) ->
     switch @output


Remove unused class variable

Remove unused class variable

--- src/robot.coffee
+++ src/robot.coffee
@@ -43,7 +43,6 @@
     @Response     = Response
     @commands     = []
     @listeners    = []
-    @loadPaths    = []
     @enableSlash  = false
     @logger       = new Log process.env.HUBOT_LOG_LEVEL or 'info'

@@ -152,7 +151,6 @@

     Fs.exists path, (exists) =>
       if exists
-        @loadPaths.push path
         for file in Fs.readdirSync(path)
           @loadFile path, file


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/score.coffee
+++ src/score.coffee
@@ -1,5 +1,5 @@
 ###*
-The score module overlays a
+The score module overlays a score value

 @name Oscillator
 @constructor


Add "room" and "reply_to" after .userForId

--- src/slack.coffee
+++ src/slack.coffee
@@ -117,8 +117,6 @@
     # Return an author object
     id       : req.param 'user_id'
     name     : req.param 'user_name'
-    reply_to : req.param 'channel_id'
-    room     : req.param 'channel_name'

   userFromParams: (params) ->
     # hubot < 2.4.2: params = user
@@ -155,6 +153,7 @@
       hubotMsg = self.getMessageFromRequest req
       author = self.getAuthorFromRequest req
       author = self.robot.brain.userForId author.id, author
+      author.reply_to = req.param 'channel_id'
       author.room = req.param 'channel_name'
       self.channelMapping[req.param 'channel_name'] = req.param 'channel_id'


Changes and cleanup to sound.coffee file

--- src/sound.coffee
+++ src/sound.coffee
@@ -1,10 +1,10 @@
-# OGG - Firefox, Chrome, Opera
-# MP3 - IE
-
-
-# Interface for defining and playing sound assets
-# Uses the Buzz HTML5 audio library by Jay Salvat
-# http://buzz.jaysalvat.com/
+#### OGG - Firefox, Chrome, Opera
+#### MP3 - IE
+
+
+#### Interface for defining and playing sound assets
+#### Uses the Buzz HTML5 audio library by Jay Salvat
+#### http://buzz.jaysalvat.com/

 Kona.Sound =
     defaults:
@@ -33,6 +33,7 @@
       eventsOnce = {}
       supported  = buzz.isSupported()

+
       # publics
       @load = ->
         return (if supported then @sound.load() else @)
@@ -44,7 +45,6 @@

       @togglePlay = ->
         return @ if !supported
-
         if @sound.paused
           @sound.play()
         else
@@ -61,11 +61,10 @@

       @stop = ->
-          return @ if !supported
-
-          @setTime( @getDuration() )
-          @sound.pause()
-          return @
+        return @ if !supported
+        @setTime( @getDuration() )
+        @sound.pause()
+        return @

       @isEnded = ->
@@ -74,17 +73,15 @@

       @loop = ->
         return @ if !supported
-
         @sound.loop = 'loop'
         @bind( 'ended.buzzloop', ->
-            @currentTime = 0
-            @play()
+          @currentTime = 0
+          @play()
         return @

       @unloop = ->
         return @ if !supported
-
         @sound.removeAttribute 'loop'
         @unbind 'ended.buzzloop'
         return @
@@ -92,364 +89,268 @@

       @mute = ->
         return @ if !supported
-
         @sound.muted = true
         return @

-        @unmute = ->
-          return (if supported then @sound.muted = false else null)
-
-
-        @toggleMute = ->
-          return (if supported then @sound.muted = !@sound.muted else null)
-
-
-        @isMuted = ->
-          return (if supported then @sound.muted else null)
+      @unmute = ->
+        return (if supported then @sound.muted = false else null)
+
+
+      @toggleMute = ->
+        return (if supported then @sound.muted = !@sound.muted else null)
+
+      @isMuted = ->
+        return (if supported then @sound.muted else null)

       @setVolume = (volume) ->
-          return @ if !supported
-          volume = 0 if volume < 0
-          volume = 100 if volume > 100
-
-          @volume = volume
-          @sound.volume = volume / 100
-          return @
+        return @ if !supported
+        volume = 0 if volume < 0
+        volume = 100 if volume > 100
+        @volume = volume
+        @sound.volume = volume / 100
+        return @

       @getVolume = ->
         return (if supported then @volume = false else null)

-        @increaseVolume = (value=1) ->
-          return @setVolume(@volume + value)
-
-
-        @decreaseVolume = (value=1)
-          return @setVolume(this.volume - value)
-
-
-        @setTime = (time) ->
-            return @ if !supported
-
-            @whenReady ->
-              @sound.currentTime = time
-            return @
-
-
-        @getTime = ->
-          return null if !supported
-
-          time = Math.round( @sound.currentTime * 100 ) / 100
-          return (if isNaN(time) then buzz.defaults.placeholder else time)
-
-
-        @setPercent = (percent) ->
-          return @ if !supported
-
-          return @setTime( buzz.fromPercent( percent, @sound.duration ) )
-
-
-        @getPercent = ->
-          return @ if !supported
-
-          percent = Math.round( buzz.toPercent( @sound.currentTime, @sound.duration ) )
-          return (if isNaN(percent) then buzz.defaults.placeholder else percent)
-
-
-        @setSpeed = (duration) ->
-          return (if supported then @sound.playbackRate = duration else null)
-
-
-        @getSpeed = ->
-          return (if supported then @sound.playbackRate else null)
-
-
-        @getDuration = ->
-          return null if !supported
-
-          duration = Math.round( this.sound.duration * 100 ) / 100
-          return (if isNaN(duration) then buzz.defaults.placeholder else duration)
-
-
-        @getPlayed = ->
-          return (if supported then timerangeToArray(@sound.played) else null)
-
-
-        @getBuffered = ->
-          return (if supported then timerangeToArray(@sound.buffered) else null)
-
-
-        @getSeekable = ->
-          return (if supported then timerangeToArray(@sound.seekable) else null)
-
-
-
-
-
-
-
-
-
-        this.getErrorCode = function() {
-            if ( supported && this.sound.error ) {
-                return this.sound.error.code
-            }
-            return 0
-        }
-
-        this.getErrorMessage = function() {
-          return null if !supported
-
-            switch( this.getErrorCode() ) {
-                case 1:
-                    return 'MEDIA_ERR_ABORTED'
-                case 2:
-                    return 'MEDIA_ERR_NETWORK'
-                case 3:
-                    return 'MEDIA_ERR_DECODE'
-                case 4:
-                    return 'MEDIA_ERR_SRC_NOT_SUPPORTED'
-                default:
-                    return null
-            }
-        }
-
-        this.getStateCode = function() {
-          return null if !supported
-
-            return this.sound.readyState
-        }
-
-
-
-
-
-
-
-
-
-
-
-        this.getStateMessage = function() {
-          return null if !supported
-
-            switch( this.getStateCode() ) {
-                case 0:
-                    return 'HAVE_NOTHING'
-                case 1:
-                    return 'HAVE_METADATA'
-                case 2:
-                    return 'HAVE_CURRENT_DATA'
-                case 3:
-                    return 'HAVE_FUTURE_DATA'
-                case 4:
-                    return 'HAVE_ENOUGH_DATA'
-                default:
-                    return null
-            }
-        }
-
-        this.getNetworkStateCode = function() {
-          return null if !supported
-
-            return this.sound.networkState
-        }
-
-
-
-
-
-
-
-
-        this.getNetworkStateMessage = function() {
-          return null if !supported
-
-            switch( this.getNetworkStateCode() ) {
-                case 0:
-                    return 'NETWORK_EMPTY'
-                case 1:
-                    return 'NETWORK_IDLE'
-                case 2:
-                    return 'NETWORK_LOADING'
-                case 3:
-                    return 'NETWORK_NO_SOURCE'
-                default:
-                    return null
-            }
-        }
-
-        this.set = function( key, value ) {
-            return this if !supported
-
-            this.sound[ key ] = value
-            return this
-        }
-
-
-
-
-
-
-
-
-
-
-
-
-        this.get = function( key ) {
-          return null if !supported
-
-          return key ? this.sound[ key ] : this.sound
-        }
-
-        this.bind = function( types, func ) {
-            return this if !supported
-
-            types = types.split( ' ' )
-
-            var that = this,
-        efunc = function( e ) { func.call( that, e ) }
-
-            for( var t = 0 t < types.length t++ ) {
-                var type = types[ t ],
-                    idx = type
-                    type = idx.split( '.' )[ 0 ]
-
-                    events.push( { idx: idx, func: efunc } )
-                    this.sound.addEventListener( type, efunc, true )
-            }
-            return this
-        }
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-        this.unbind = function( types ) {
-            return this if !supported
-
-            types = types.split( ' ' )
-
-            for( var t = 0 t < types.length t++ ) {
-                var idx = types[ t ],
-                    type = idx.split( '.' )[ 0 ]
-
-                for( var i = 0 i < events.length i++ ) {
-                    var namespace = events[ i ].idx.split( '.' )
-                    if ( events[ i ].idx == idx || ( namespace[ 1 ] && namespace[ 1 ] == idx.replace( '.', '' ) ) ) {
-                        this.sound.removeEventListener( type, events[ i ].func, true )
-                        // remove event
-                        events.splice(i, 1)
-                    }
+      @increaseVolume = (value = 1) ->
+        return @setVolume(@volume + value)
+
+
+      @decreaseVolume = (value = 1)
+        return @setVolume(@volume - value)
+
+
+      @setTime = (time) ->
+        return @ if !supported
+        @whenReady ->
+          @sound.currentTime = time
+          return @
+
+
+      @getTime = ->
+        return null if !supported
+        time = Math.round(@sound.currentTime * 100) / 100
+        return (if isNaN(time) then buzz.defaults.placeholder else time)
+
+
+      @setPercent = (percent) ->
+        return @ if !supported
+        return @setTime(buzz.fromPercent(percent, @sound.duration))
+
+
+      @getPercent = ->
+        return @ if !supported
+        percent = Math.round(buzz.toPercent(@sound.currentTime, @sound.duration))
+        return (if isNaN(percent) then buzz.defaults.placeholder else percent)
+
+
+      @setSpeed = (duration) ->
+        return (if supported then @sound.playbackRate = duration else null)
+
+
+      @getSpeed = ->
+        return (if supported then @sound.playbackRate else null)
+
+
+      @getDuration = ->
+        return null if !supported
+        duration = Math.round(@sound.duration * 100) / 100
+        return (if isNaN(duration) then buzz.defaults.placeholder else duration)
+
+
+      @getPlayed = ->
+        return (if supported then timerangeToArray(@sound.played) else null)
+
+
+      @getBuffered = ->
+        return (if supported then timerangeToArray(@sound.buffered) else null)
+
+
+      @getSeekable = ->
+        return (if supported then timerangeToArray(@sound.seekable) else null)
+
+
+      @getErrorCode = ->
+        if supported and @sound.error
+          return @sound.error.code
+        return 0
+
+
+      @getErrorMessage = ->
+        return null if !supported
+          switch @getErrorCode
+            case 1:
+              return 'MEDIA_ERR_ABORTED'
+            case 2:
+              return 'MEDIA_ERR_NETWORK'
+            case 3:
+              return 'MEDIA_ERR_DECODE'
+            case 4:
+              return 'MEDIA_ERR_SRC_NOT_SUPPORTED'
+            default:
+              return null
+
+
+      @getStateCode = ->
+        return null if !supported
+        return @sound.readyState
+
+
+      @getStateMessage = ->
+        return null if !supported
+          switch @getStateCode
+            case 0:
+              return 'HAVE_NOTHING'
+            case 1:
+              return 'HAVE_METADATA'
+            case 2:
+              return 'HAVE_CURRENT_DATA'
+            case 3:
+              return 'HAVE_FUTURE_DATA'
+            case 4:
+              return 'HAVE_ENOUGH_DATA'
+            default:
+              return null
+
+
+      @getNetworkStateCode = ->
+        return null if !supported
+        return @sound.networkState
+
+
+      @getNetworkStateMessage = ->
+        return null if !supported
+          switch @getNetworkStateCode
+            case 0:
+              return 'NETWORK_EMPTY'
+            case 1:
+              return 'NETWORK_IDLE'
+            case 2:
+              return 'NETWORK_LOADING'
+            case 3:
+              return 'NETWORK_NO_SOURCE'
+            default:
+              return null
+
+
+      @set = (key, value) ->
+        return @ if !supported
+
+
+      @sound[key] = value
+        return @
+
+
+      @get = (key) ->
+        return null if !supported
+        return if key then @sound[key] else @sound
+
+
+#### I left this kind of untouched because I wasn't sure of how to deal with line 259
+      @bind = (types, func) ->
+        return @ if !supported
+        types = types.split( ' ' )
+        that = @,
+        efunc = function( e ) { func.call(that, e) }
+          for( var t = 0 t < types.length t++ ) {
+              type = types[t],
+              idx = type
+              type = idx.split('.')[0]
+              events.push({idx: idx, func: efunc})
+              @sound.addEventListener(type, efunc, true)
+          }
+          return @
+      }
+
+
+#### I need to go over this one too
+      @unbind = (types) ->
+        return @ if !supported
+        types = types.split(' ')
+          #for( var t = 0 t < types.length t++ ) {
+           #   var idx = types[ t ],
+            #      type = idx.split( '.' )[ 0 ]
+          for t in types
+            idx = t
+            type = idx.split('.')[0]
+              for( var i = 0 i < events.length i++ ) {
+                #for i in events
+                temp = i
+                  #var namespace = events[ i ].idx.split( '.' )
+                  namespace = temp.idx.split('.')
+                  if (events[i].idx == idx || (namespace[1] && namespace[1] == idx.replace( '.', '' ) ) ) {
+                      @sound.removeEventListener( type, events[ i ].func, true )
+                      // remove event
+                      events.splice(i, 1)
+                  }
+              }
+          }
+          return @
+      }
+
+
+#### Do you remove the ( and { from lines 302 and 307?
+      @bindOnce = (type, func) ->
+        return @ if !supported
+        that = @
+        eventsOnce[pid++] = false
+        @bind(pid + type, function() {
+          if !eventsOnce[pid]
+            eventsOnce[pid] = true
+            func.call(that)
+             @unbind(pid + type)
+          })
+
+
+      @trigger = (types) ->
+        return @ if !supported
+        types = types.split( ' ' )
+        for( var t = 0 t < types.length t++ ) {
+            var idx = types[t]
+            for( var i = 0 i < events.length i++ ) {
+                var eventType = events[ i ].idx.split( '.' )
+                if ( events[ i ].idx == idx || ( eventType[ 0 ] && eventType[ 0 ] == idx.replace( '.', '' ) ) ) {
+                  var evt = document.createEvent('HTMLEvents')
+                  evt.initEvent( eventType[ 0 ], false, true )
+                  @sound.dispatchEvent(evt)
+                  }
+              }
+          }
+        return @
+
+
+#### Something with fadeTo and doFade
+      @fadeTo = (to, duration, callback) ->
+        return @ if !supported
+        if duration instanceof Function
+          callback = duration
+          duration = buzz.defaults.duration
+        else
+          duration = duration || buzz.defaults.duration
+        from = @volume,
+        delay = duration / Math.abs(from - to),
+        that = @
+        @play()
+
+
+        function doFade() {
+          setTimeout( function() {
+          if ( from < to and that.volume < to ) {
+            that.setVolume( that.volume += 1 )
+            doFade()
+          } else if ( from > to and that.volume > to ) {
+              that.setVolume( that.volume -= 1 )
+                doFade()
+          } else if ( callback instanceof Function ) {
+                callback.apply( that )
                 }
-            }
-            return this
-        }
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-        this.bindOnce = function( type, func ) {
-            return this if !supported
-
-            var that = this
-
-            eventsOnce[ pid++ ] = false
-            this.bind( pid + type, function() {
-               if ( !eventsOnce[ pid ] ) {
-                   eventsOnce[ pid ] = true
-                   func.call( that )
-               }
-               that.unbind( pid + type )
-            })
-        }
-
-        this.trigger = function( types ) {
-            return this if !supported
-
-            types = types.split( ' ' )
-
-            for( var t = 0 t < types.length t++ ) {
-                var idx = types[ t ]
-
-                for( var i = 0 i < events.length i++ ) {
-                    var eventType = events[ i ].idx.split( '.' )
-                    if ( events[ i ].idx == idx || ( eventType[ 0 ] && eventType[ 0 ] == idx.replace( '.', '' ) ) ) {
-                        var evt = document.createEvent('HTMLEvents')
-                        evt.initEvent( eventType[ 0 ], false, true )
-                        this.sound.dispatchEvent( evt )
-                    }
-                }
-            }
-            return this
-        }
-
-
-
-
-
-
-
-
-
-
-
-
-
-        this.fadeTo = function( to, duration, callback ) {
-           return this if !supported
-
-            if ( duration instanceof Function ) {
-                callback = duration
-                duration = buzz.defaults.duration
-            } else {
-                duration = duration || buzz.defaults.duration
-            }
-
-            var from = this.volume,
-        delay = duration / Math.abs( from - to ),
-                that = this
-            this.play()
-
-            function doFade() {
-                setTimeout( function() {
-                    if ( from < to && that.volume < to ) {
-                        that.setVolume( that.volume += 1 )
-                        doFade()
-                    } else if ( from > to && that.volume > to ) {
-                        that.setVolume( that.volume -= 1 )
-                        doFade()
-                    } else if ( callback instanceof Function ) {
-                        callback.apply( that )
-                    }
-                }, delay )
-            }
+          }, delay )
+          }
             this.whenReady( function() {
                 doFade()
             })
@@ -458,466 +359,302 @@
         }

-
-
-
-
-
-
-
-
-
-
-
-
-
-        this.fadeIn = function( duration, callback ) {
-            return this if !supported
-
-            return this.setVolume(0).fadeTo( 100, duration, callback )
-        }
-
-        this.fadeOut = function( duration, callback ) {
-           return this if !supported
-
-            return this.fadeTo( 0, duration, callback )
-        }
-
-        this.fadeWith = function( sound, duration ) {
-            return this if !supported
-
-            this.fadeOut( duration, function() {
-                this.stop()
-            })
-
-            sound.play().fadeIn( duration )
-
-            return this
-        }
-
-
-
-
-
-
-
-
-
-
-
-
-        this.whenReady = function( func ) {
-            return null if !supported
-
-            var that = this
-            if ( this.sound.readyState === 0 ) {
-                this.bind( 'canplay.buzzwhenready', function() {
-                    func.call( that )
-                })
-            } else {
-                func.call( that )
-            }
-        }
-
-        // privates
-        function timerangeToArray( timeRange ) {
-            var array = [],
-                length = timeRange.length - 1
-
-            for( var i = 0 i <= length i++ ) {
-                array.push({
-                    start: timeRange.start( length ),
-                    end: timeRange.end( length )
-                })
-            }
-            return array
-        }
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-        function getExt( filename ) {
-            return filename.split('.').pop()
-        }
-
-        function addSource( sound, src ) {
-            var source = document.createElement( 'source' )
-            source.src = src
-            if ( buzz.types[ getExt( src ) ] ) {
-                source.type = buzz.types[ getExt( src ) ]
-            }
-            sound.appendChild( source )
-        }
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-        // init
-        if ( supported && src ) {
-
-            for(var i in buzz.defaults ) {
-              if(buzz.defaults.hasOwnProperty(i)) {
-                options[ i ] = options[ i ] || buzz.defaults[ i ]
-              }
-            }
-
-            this.sound = document.createElement( 'audio' )
-
-            if ( src instanceof Array ) {
-                for( var j in src ) {
-                  if(src.hasOwnProperty(j)) {
-                    addSource( this.sound, src[ j ] )
-                  }
-                }
-            } else if ( options.formats.length ) {
-                for( var k in options.formats ) {
-                  if(options.formats.hasOwnProperty(k)) {
-                    addSource( this.sound, src + '.' + options.formats[ k ] )
-                  }
-                }
-            } else {
-                addSource( this.sound, src )
-            }
-
-            if ( options.loop ) {
-                this.loop()
-            }
-
-            if ( options.autoplay ) {
-                this.sound.autoplay = 'autoplay'
-            }
-
-            if ( options.preload === true ) {
-                this.sound.preload = 'auto'
-            } else if ( options.preload === false ) {
-                this.sound.preload = 'none'
-            } else {
-                this.sound.preload = options.preload
-            }
-
-            this.setVolume( options.volume )
-
-            buzz.sounds.push( this )
+      @fadeIn = (duration, callback) ->
+        return @ if !supported
+        return @setVolume(0).fadeTo(100, duration, callback)
+
+
+      @fadeOut = (duration, callback) ->
+        return @ if !supported
+        return @fadeTo(0, duration, callback)
+
+
+#### Check here too
+      @fadeWith = (sound, duration) ->
+        return @ if !supported
+        @fadeOut (duration), ->
+          @stop()
+        sound.play().fadeIn(duration)
+        return @
+
+
+#### Check here too
+      @whenReady = (func) ->
+        return null if !supported
+        that = @
+        if @sound.readyState === 0
+          @bind ('canplay.buzzwhenready'), ->
+            func.call(that)
+        else
+          func.call(that)
+
+        #### privates
+#### Another check
+#### It was an object with a collection of key value pairs
+      timeRangeToArray = (timeRange) ->
+        array = []
+        length = timeRange.length - 1
+        for(var i = 0 i <= length i++)
+          array.push(
+          start: timeRange.start(length)
+          end: timeRange.end(length)
+          )
+        return array
+
+
+      getExt = (filename) ->
+        return filename.split('.').pop()
+
+
+      addSource = (sound, src) ->
+        source = document.createElement( 'source' )
+        source.src = src
+        if buzz.types[getExt(src)]
+          source.type = buzz.types[getExt(src)]
+        sound.appendChild(source)
+
+        ####// init
+        #### So is this whole block of code belong to addSource?
+        #### There was already a closed bracket above this to close out the function
+        if supported and src {
+            for var i in buzz.defaults
+              if buzz.defaults.hasOwnProperty(i)
+                options[i] = options[i] || buzz.defaults[i]
+
+            @sound = document.createElement('audio')
+
+            if src instanceof Array
+                for var j in src
+                  if src.hasOwnProperty(j)
+                    addSource(@sound, src[j])
+            else if options.formats.length
+                for var k in options.formats
+                  if(options.formats.hasOwnProperty(k))
+                    addSource(@sound, src + '.' + options.formats[k])
+            else
+              addSource(@sound, src)
+
+            if options.loop
+              @loop()
+
+            if options.autoplay
+              @sound.autoplay = 'autoplay'
+
+            if options.preload === true
+              @sound.preload = 'auto'
+            else if options.preload === false
+              @sound.preload = 'none'
+            else
+              @sound.preload = options.preload
+
+            @setVolume(options.volume)
+
+            buzz.sounds.push(@)
         }
     },

-
-
-
-
-
-
-
-
-
-
-
-
-    group: function( sounds ) {
-        sounds = argsToArray( sounds, arguments )
-
-        // publics
-        this.getSounds = function() {
-            return sounds
-        }
-
-        this.add = function( soundArray ) {
-            soundArray = argsToArray( soundArray, arguments )
-            for( var a = 0 a < soundArray.length a++ ) {
-                sounds.push( soundArray[ a ] )
-            }
-        }
-
-        this.remove = function( soundArray ) {
-            soundArray = argsToArray( soundArray, arguments )
-            for( var a = 0 a < soundArray.length a++ ) {
-                for( var i = 0 i < sounds.length i++ ) {
-                    if ( sounds[ i ] == soundArray[ a ] ) {
-                        delete sounds[ i ]
-                        break
-                    }
-                }
-            }
-        }
-
-
-
-
-
-
-
-
-
-
-
-
-        this.load = function() {
-            fn( 'load' )
-            return this
-        }
-
-        this.play = function() {
-            fn( 'play' )
-            return this
-        }
-
-        this.togglePlay = function( ) {
-            fn( 'togglePlay' )
-            return this
-        }
-
-        this.pause = function( time ) {
-            fn( 'pause', time )
-            return this
-        }
-
-        this.stop = function() {
-            fn( 'stop' )
-            return this
-        }
-
-        this.mute = function() {
-            fn( 'mute' )
-            return this
-        }
-
-        this.unmute = function() {
-            fn( 'unmute' )
-            return this
-        }
-
-        this.toggleMute = function() {
-            fn( 'toggleMute' )
-            return this
-        }
-
-
-
-
-
-
-
-
-
-        this.setVolume = function( volume ) {
-            fn( 'setVolume', volume )
-            return this
-        }
-
-        this.increaseVolume = function( value ) {
-            fn( 'increaseVolume', value )
-            return this
-        }
-
-        this.decreaseVolume = function( value ) {
-            fn( 'decreaseVolume', value )
-            return this
-        }
-
-        this.loop = function() {
-            fn( 'loop' )
-            return this
-        }
-
-        this.unloop = function() {
-            fn( 'unloop' )
-            return this
-        }
-
-        this.setTime = function( time ) {
-            fn( 'setTime', time )
-            return this
-        }
-
-        this.setduration = function( duration ) {
-            fn( 'setduration', duration )
-            return this
-        }
-
-
-
-
-
-
-
-
-        this.set = function( key, value ) {
-            fn( 'set', key, value )
-            return this
-        }
-
-        this.bind = function( type, func ) {
-            fn( 'bind', type, func )
-            return this
-        }
-
-        this.unbind = function( type ) {
-            fn( 'unbind', type )
-            return this
-        }
-
-        this.bindOnce = function( type, func ) {
-            fn( 'bindOnce', type, func )
-            return this
-        }
-
-        this.trigger = function( type ) {
-            fn( 'trigger', type )
-            return this
-        }
-
-        this.fade = function( from, to, duration, callback ) {
-            fn( 'fade', from, to, duration, callback )
-            return this
-        }
-
-        this.fadeIn = function( duration, callback ) {
-            fn( 'fadeIn', duration, callback )
-            return this
-        }
-
-        this.fadeOut = function( duration, callback ) {
-            fn( 'fadeOut', duration, callback )
-            return this
-        }
-
-
-
-
-
-
-
-
-
-        // privates
-        function fn() {
-            var args = argsToArray( null, arguments ),
-                func = args.shift()
-
-            for( var i = 0 i < sounds.length i++ ) {
-                sounds[ i ][ func ].apply( sounds[ i ], args )
-            }
-        }
-
-        function argsToArray( array, args ) {
-            return ( array instanceof Array ) ? array : Array.prototype.slice.call( args )
-        }
-    },
-
-
-    all: function() {
-      return new buzz.group( buzz.sounds )
-    },
-
-    isSupported: function() {
-        return !!buzz.el.canPlayType
-    },
-
-    isOGGSupported: function() {
-        return !!buzz.el.canPlayType && buzz.el.canPlayType( 'audio/ogg codecs="vorbis"' )
-    },
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-    isWAVSupported: function() {
-        return !!buzz.el.canPlayType && buzz.el.canPlayType( 'audio/wav codecs="1"' )
-    },
-
-    isMP3Supported: function() {
-        return !!buzz.el.canPlayType && buzz.el.canPlayType( 'audio/mpeg' )
-    },
-
-    isAACSupported: function() {
-        return !!buzz.el.canPlayType && ( buzz.el.canPlayType( 'audio/x-m4a' ) || buzz.el.canPlayType( 'audio/aac' ) )
-    },
-
-    toTimer: function( time, withHours ) {
-        var h, m, s
-        h = Math.floor( time / 3600 )
-        h = isNaN( h ) ? '--' : ( h >= 10 ) ? h : '0' + h
-        m = withHours ? Math.floor( time / 60 % 60 ) : Math.floor( time / 60 )
-        m = isNaN( m ) ? '--' : ( m >= 10 ) ? m : '0' + m
-        s = Math.floor( time % 60 )
-        s = isNaN( s ) ? '--' : ( s >= 10 ) ? s : '0' + s
-        return withHours ? h + ':' + m + ':' + s : m + ':' + s
-    },
-
-    fromTimer: function( time ) {
-        var splits = time.toString().split( ':' )
-        if ( splits && splits.length == 3 ) {
-            time = ( parseInt( splits[ 0 ], 10 ) * 3600 ) + ( parseInt(splits[ 1 ], 10 ) * 60 ) + parseInt( splits[ 2 ], 10 )
-        }
-        if ( splits && splits.length == 2 ) {
-            time = ( parseInt( splits[ 0 ], 10 ) * 60 ) + parseInt( splits[ 1 ], 10 )
-        }
-        return time
-    },
-
-
-
-
-
-
-
-
-
-
-
-
-    toPercent: function( value, total, decimal ) {
-    var r = Math.pow( 10, decimal || 0 )
-
-    return Math.round( ( ( value * 100 ) / total ) * r ) / r
-    },
-
-    fromPercent: function( percent, total, decimal ) {
-    var r = Math.pow( 10, decimal || 0 )
-
-        return  Math.round( ( ( total / 100 ) * percent ) * r ) / r
-    }
-}
+    group: (sounds) ->
+      sounds = argsToArray(sounds, arguments)
+
+      // publics
+      @getSounds = ->
+        return sounds
+
+      @add = (soundArray) ->
+        soundArray = argsToArray(soundArray, arguments)
+        for var a = 0 a < soundArray.length a++
+          sounds.push(soundArray[a])
+      #    for sound in soundArray
+      #      sounds.push(push)
+
+#### Can be redone
+      @remove = (soundArray) ->
+        soundArray = argsToArray(soundArray, arguments)
+          for( var a = 0 a < soundArray.length a++ )
+            for( var i = 0 i < sounds.length i++ )
+              if (sounds[ i ] == soundArray[ a ])
+                delete sounds[i]
+                break
+
+
+      @load = ->
+        fn( 'load' )
+        return @
+
+
+      @play = ->
+        fn( 'play' )
+        return @
+
+
+      @togglePlay = ->
+        fn('togglePlay')
+        return @
+
+
+      @pause = (time)
+        fn('pause', time)
+        return @
+
+
+      @stop = ->
+        fn('stop')
+        return @
+
+
+      @mute = ->
+        fn('mute')
+        return @
+
+
+      @unmute = ->
+        fn('unmute')
+        return @
+
+
+      @toggleMute = ->
+        fn('toggleMute')
+        return @
+
+
+      @setVolume = (volume) ->
+        fn('setVolume', volume)
+        return @
+
+
+      @increaseVolume = (value) ->
+        fn('increaseVolume', value)
+        return @
+
+
+      @decreaseVolume = (value) ->
+        fn('decreaseVolume', value)
+        return @
+
+
+      @loop = ->
+        fn('loop')
+        return @
+
+
+      @unloop = ->
+        fn('unloop')
+        return @
+
+
+      @setTime = (time) ->
+        fn('setTime', time)
+        return @
+
+
+      @setDuration = (duration) ->
+        fn('setDuration', duration)
+        return @
+
+
+      @set = (key, value) ->
+        fn('set', key, value)
+        return @
+
+
+      @bind = (type, func) ->
+        fn('bind', type, func)
+        return @
+
+
+      @unbind = (type) ->
+        fn('unbind', type)
+        return @
+
+
+      @bindOnce = (type, func) ->
+        fn('bindOnce', type, func)
+        return @
+
+
+      @trigger = (type) ->
+        fn('trigger', type)
+        return @
+
+      @fade = (from, to, duration, callback) ->
+        fn('fade', from, to, duration, callback)
+        return @
+
+
+      @fadeIn = (duration, callback) ->
+        fn('fadeIn', duration, callback)
+        return @
+
+
+      @fadeOut = (duration, callback) ->
+        fn('fadeOut', duration, callback)
+        return @
+
+
+      // privates
+      fn = ->
+        args = argsToArray(null, arguments)
+        func = args.shift()
+        for i in sounds
+        #for( var i = 0 i < sounds.length i++ ) {
+          sounds[i][func].apply(sounds[i], args)
+        argsToArray = (array, args) ->
+        return (array instanceof Array) ? array : Array.prototype.slice.call(args)
+    #},
+
+
+    #all: function()
+    all: ->
+      return new buzz.group(buzz.sounds)
+
+
+    isSupported: ->
+      return !!buzz.el.canPlayType
+
+
+    isOGGSupported: ->
+      return !!buzz.el.canPlayType and buzz.el.canPlayType('audio/ogg codecs="vorbis"')
+
+
+    isWAVSupported: ->
+      return !!buzz.el.canPlayType and buzz.el.canPlayType('audio/wav codecs="1"')
+
+
+    isMP3Supported: ->
+      return !!buzz.el.canPlayType and buzz.el.canPlayType('audio/mpeg')
+
+
+    isAACSupported: ->
+      return !!buzz.el.canPlayType and (buzz.el.canPlayType('audio/x-m4a') || buzz.el.canPlayType('audio/aac'))
+
+
+    toTimer: (time, withHours) ->
+      h = Math.floor(time / 3600)
+      h = isNaN(h) ? '--' : ( h >= 10 ) ? h : '0' + h
+      m = withHours ? Math.floor(time / 60 % 60) : Math.floor(time / 60)
+      m = isNaN(m) ? '--' : (m >= 10) ? m : '0' + m
+      s = Math.floor(time % 60)
+      s = isNaN(s) ? '--' : (s >= 10) ? s : '0' + s
+      return withHours ? h + ':' + m + ':' + s : m + ':' + s
+
+
+    fromTimer: (time) ->
+      splits = time.toString().split(':')
+      if splits and splits.length == 3
+        time = (parseInt(splits[0], 10) * 3600) + (parseInt(splits[1], 10) * 60) + parseInt(splits[2], 10)
+      if splits and splits.length == 2
+        time = (parseInt(splits[0], 10) * 60) + parseInt(splits[1], 10)
+      return time
+
+
+    toPercent: (value, total, decimal) ->
+      r = Math.pow(10, decimal || 0)
+      return Math.round(((value * 100) / total) * r) / r
+
+
+    fromPercent: (percent, total, decimal) ->
+      r = Math.pow(10, decimal || 0)
+      return Math.round(((total / 100) * percent) * r) / r


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/state.coffee
+++ src/state.coffee
@@ -2,8 +2,7 @@
   currentState = null
   initialState = null

-  self = GameObject().extend
-
+  self = GameObject().extend
     defaultState: () ->
       return initialState


remove extra parenthesis for debug calls

--- src/stomp.coffee
+++ src/stomp.coffee
@@ -145,7 +145,7 @@

   _transmit: (command, headers, body) ->
     out = Frame.marshall(command, headers, body)
-    @debug?(">>> " + out)
+    @debug? ">>> " + out
     @ws.send(out)

   _setupHeartbeat: (headers) ->
@@ -157,19 +157,21 @@

       unless @heartbeat.outgoing == 0 or serverIncoming == 0
         ttl = Math.max(@heartbeat.outgoing, serverIncoming)
-        @debug("send PING every #{ttl}ms")
+        @debug? "send PING every #{ttl}ms"
         @pinger = window?.setInterval(=>
           @ws.send Byte.LF
-          @debug?(">>> PING")
+          @debug? ">>> PING"
         , ttl)

       unless @heartbeat.incoming == 0 or serverOutgoing == 0
         ttl = Math.max(@heartbeat.incoming, serverOutgoing)
-        @debug("check PONG every #{ttl}ms")
+        @debug? "check PONG every #{ttl}ms"
         @ponger = window?.setInterval(=>
           delta = Date.now() - @serverActivity
           # We wait twice the TTL to be flexible on window's setInterval calls
-          @_cleanUp() if delta > ttl * 2
+          if delta > ttl * 2
+            @debug? "did not receive server activity for the last #{delta}ms"
+            @_cleanUp()
         , ttl)

   # [CONNECT Frame](http://stomp.github.com/stomp-specification-1.1.html#CONNECT_or_STOMP_Frame)
@@ -179,11 +181,11 @@
       errorCallback,
       vhost_,
       heartbeat="10000,10000") ->
-    @debug?("Opening Web Socket...")
+    @debug? "Opening Web Socket..."
     @ws.onmessage = (evt) =>
       data = if typeof(ArrayBuffer) != 'undefined' and evt.data instanceof ArrayBuffer
         view = new Uint8Array( evt.data )
-        @debug?('--- got data length: ' + view.length)
+        @debug? "--- got data length: #{view.length}"
         data = ""
         for i in view
           data += String.fromCharCode(i)
@@ -192,9 +194,9 @@
         evt.data
       @serverActivity = Date.now()
       if data == Byte.LF # heartbeat
-        @debug?('<<< PONG')
+        @debug? "<<< PONG"
         return
-      @debug?('<<< ' + data)
+      @debug? "<<< #{data}"
       # Handle STOMP frames received from the server
       for frame in Frame.unmarshall(data)
         # [CONNECTED Frame](http://stomp.github.com/stomp-specification-1.1.html#CONNECTED_Frame)
@@ -223,9 +225,9 @@
         else if frame.command is "ERROR"
           errorCallback?(frame)
         else
-          @debug?("Unhandled frame: " + JSON.stringify(frame))
+          @debug? "Unhandled frame: #{frame}"
     @ws.onclose   = =>
-      msg = "Whoops! Lost connection to " + @ws.url
+      msg = "Whoops! Lost connection to #{@ws.url}"
       @debug?(msg)
       errorCallback?(msg)
     @ws.onopen    = =>
@@ -237,11 +239,11 @@
       @heartbeat.outgoing = parseInt(cx)
       @heartbeat.incoming = parseInt(cy)
       headers['accept-version'] = Stomp.VERSIONS.supportedVersions()
-      @_transmit("CONNECT", headers)
+      @_transmit "CONNECT", headers

   # [DISCONNECT Frame](http://stomp.github.com/stomp-specification-1.1.html#DISCONNECT)
   disconnect: (disconnectCallback) ->
-    @_transmit("DISCONNECT")
+    @_transmit "DISCONNECT"
     # Discard the onclose callback to avoid calling the errorCallback when
     # the client is properly disconnected.
     @ws.onclose = null
@@ -259,7 +261,7 @@
   # [SEND Frame](http://stomp.github.com/stomp-specification-1.1.html#SEND)
   send: (destination, headers={}, body='') ->
     headers.destination = destination
-    @_transmit("SEND", headers, body)
+    @_transmit "SEND", headers, body

   # [SUBSCRIBE Frame](http://stomp.github.com/stomp-specification-1.1.html#SUBSCRIBE)
   subscribe: (destination, callback, headers={}) ->
@@ -270,34 +272,34 @@
       id = headers.id
     headers.destination = destination
     @subscriptions[id] = callback
-    @_transmit("SUBSCRIBE", headers)
+    @_transmit "SUBSCRIBE", headers
     return id

   # [UNSUBSCRIBE Frame](http://stomp.github.com/stomp-specification-1.1.html#UNSUBSCRIBE)
   unsubscribe: (id, headers={}) ->
     headers.id = id
     delete @subscriptions[id]
-    @_transmit("UNSUBSCRIBE", headers)
+    @_transmit "UNSUBSCRIBE", headers

   # [BEGIN Frame](http://stomp.github.com/stomp-specification-1.1.html#BEGIN)
   begin: (transaction, headers={}) ->
     headers.transaction = transaction
-    @_transmit("BEGIN", headers)
+    @_transmit "BEGIN", headers

   # [COMMIT Frame](http://stomp.github.com/stomp-specification-1.1.html#COMMIT)
   commit: (transaction, headers={}) ->
     headers.transaction = transaction
-    @_transmit("COMMIT", headers)
+    @_transmit "COMMIT", headers

   # [ABORT Frame](http://stomp.github.com/stomp-specification-1.1.html#ABORT)
   abort: (transaction, headers={}) ->
     headers.transaction = transaction
-    @_transmit("ABORT", headers)
+    @_transmit "ABORT", headers

   # [ACK Frame](http://stomp.github.com/stomp-specification-1.1.html#ACK)
   ack: (message_id, headers={}) ->
     headers["message-id"] = message_id
-    @_transmit("ACK", headers)
+    @_transmit "ACK", headers

 if window?
   window.Stomp = Stomp


Improved see more feature.

Improved see more feature.

--- app/assets/javascripts/app/controllers/agent_ticket_zoom.js.coffee
+++ app/assets/javascripts/app/controllers/agent_ticket_zoom.js.coffee
@@ -82,23 +82,26 @@
         if article_lines.length > preview
           preview_mode = true
           if article_lines[preview] is ''
-            article_lines.splice( preview, 0, "----SEEMORE----" )
+            article_lines.splice( preview, 0, '----SEEMORE----' )
           else
-            article_lines.splice( preview + 1, 0, "----SEEMORE----" )
+            article_lines.splice( preview + 1, 0, '----SEEMORE----' )
           article['html'] = article_lines.join("\n")
         article['html'] = window.linkify( article['html'] )
-        notify = "" + T('See more') + ""
+        notify = '' + T('See more') + ''

         # preview mode
         if preview_mode
-          article['html'] = article['html'].replace /^\n----SEEMORE----\n/m, (match) =>
+          @article_changed = false
+          article['html'] = article['html'].replace /^\n{0,10}----SEEMORE----\n/m, (match) =>
+            @article_changed = true
             notify + '
'
-          article['html'] = article['html'] + '
'
+          if @article_changed
+            article['html'] = article['html'] + '
'

         # hide signatures and so on
         else
           @article_changed = false
-          article['html'] = article['html'].replace /^\n(--|__)/m, (match) =>
+          article['html'] = article['html'].replace /^\n{0,10}(--|__)/m, (match) =>
             @article_changed = true
             notify + '
' + match
           if @article_changed


[skip] Improved code for parsing and normalizing tags (25m)

--- app/assets/javascripts/houston/feedback/views/comments_view.coffee
+++ app/assets/javascripts/houston/feedback/views/comments_view.coffee
@@ -228,8 +228,7 @@

   addTag: ->
     $input = $('.feedback-new-tag')
-    tags = $input.val().split(/[,;]/).map (tag)->
-      tag.compact().toLowerCase().replace(/\s+/, '-')
+    tags = @parseTags($input.val())
     ids = @selectedIds()
     $.post '/feedback/comments/tags', comment_ids: ids, tags: tags
       .success =>
@@ -237,6 +236,11 @@
         @editSelected()
       .error ->
         console.log 'error', arguments
+
+  parseTags: (text)->
+    tags = _.map text.split(/[,;]/), (tag)->
+      tag.compact().toLowerCase().replace(/\W+/g, '-')
+    _.reject tags, (tag)-> !tag

   promptToImportCsv: (data)->
     $modal = $(@renderImportModal(data)).modal()
@@ -287,8 +291,7 @@
     $newTag = $modal.find('.feedback-new-tag')

     addTags = =>
-      tags = $newTag.val().split(/[,;]/).map (tag)->
-        tag.compact().toLowerCase().replace(/\s+/, '-')
+      tags = @parseTags($newTag.val())
       $tags = $modal.find('.feedback-tag-list')
       for tag in tags
         $tags.append """


remove console in search artists view

remove console in search artists view

--- apps/home_2/components/followed_artists/search_artists_view.coffee
+++ apps/home_2/components/followed_artists/search_artists_view.coffee
@@ -63,9 +63,7 @@
     replacementDeferred = Q.defer()
     waitDeferred = Q.defer()

-    @findReplacement id, (artist) ->
-      console.log 'artist', artist.get('name')
-      replacementDeferred.resolve(artist)
+    @findReplacement id, (artist) -> replacementDeferred.resolve(artist)
     setTimeout ( -> waitDeferred.resolve() ), 500
     Q.all [replacementDeferred.promise, waitDeferred.promise]


And actually remove the now-unused delay function.

--- src/utils.coffee
+++ src/utils.coffee
@@ -16,5 +16,3 @@
 			.trim() or os.hostname()

 		return crypto.createHash('sha1').update(serial, 'utf8').digest('hex')
-
-exports.delay = (ms) -> new Promise (v) -> setTimeout(v, ms)


Minimize service cart on init.

Minimize service cart on init.

--- src/views.coffee
+++ src/views.coffee
@@ -1365,6 +1365,8 @@
             @listenTo @collection, 'minmax', @render
             if @collection.length
                 @minimized = false
+            else
+                @minimized = true
         maximize: ->
             @minimized = false
             @collection.trigger 'minmax'


Move overlay to left of the marker

Move overlay to left of the marker

--- lib/bubble.coffee
+++ lib/bubble.coffee
@@ -18,7 +18,11 @@
       errorRange = new Range([p[0][0] - 1, p[0][1] - 1], [p[1][0] - 1, p[1][1]])
       return unless errorRange.containsPoint point
       marker = textEditor.markBufferRange errorRange, {invalidate: 'never'}
-      @bubble = textEditor.decorateMarker marker, type: 'overlay', item: BubbleView(@linter, message)
+      @bubble = textEditor.decorateMarker(marker, {
+        type: 'overlay',
+        position: 'tail',
+        item: BubbleView(@linter, message)
+      })
       found = true

   remove: ->


resolve and callback with body.data or body

--- lib/clever.coffee
+++ lib/clever.coffee
@@ -32,9 +32,9 @@
     quest opts, (err, resp, body) ->
       handle_errors resp, body, (err, resp, body) ->
         reject err if err
-        resolve body?.data
+        resolve body?.data or body
         return cb?(err) if err
-        cb?(err, body?.data)
+        cb?(err, body?.data or body)
   return cb if _.isFunction(cb)
   promise


Fix priority working.

Fix priority working.

--- lib/docpad.coffee
+++ lib/docpad.coffee
@@ -550,7 +550,7 @@

 			# Next
 			(err) =>
-				@pluginsArray.sort (a,b) -> a.priority < b.priority
+				@pluginsArray.sort (a,b) -> a.priority - b.priority
 				logger.log 'debug', "Plugins loaded for #{pluginsPath}"
 				next(err)
 		)


staging has different imagemagick behavior than dev

--- lib/imager.coffee
+++ lib/imager.coffee
@@ -25,8 +25,8 @@
       .trim()
       .gravity('Center')
       .resize(size.width, size.height, "^")
+      .background(face.color)
       .extent(@maxSize, @maxSize)
-      .background(face.color)
       .stream('png', callback)

   parseCrop: (size) ->


use spawn instead of exec for python

use spawn instead of exec for python

--- lib/kernel.coffee
+++ lib/kernel.coffee
@@ -22,18 +22,22 @@

         @connect()
         if @language == 'python' and not @kernelInfo.argv?
-            commandString = "ipython kernel --no-secure
-                    --hb=#{@config.hb_port}
-                    --control=#{@config.control_port}
-                    --shell=#{@config.shell_port}
-                    --stdin=#{@config.stdin_port}
-                    --iopub=#{@config.iopub_port}
-                    --colors=NoColor"
-
-            console.log "launching kernel:", commandString
-            @kernelProcess = child_process.exec(commandString, {
-                    cwd: projectPath
-                })
+            commandString = "ipython"
+            args = [
+                "kernel",
+                "--no-secure",
+                "--hb=#{@config.hb_port}",
+                "--control=#{@config.control_port}",
+                "--shell=#{@config.shell_port}",
+                "--stdin=#{@config.stdin_port}",
+                "--iopub=#{@config.iopub_port}",
+                "--colors=NoColor"
+                ]
+
+            # console.log "launching kernel:", commandString
+            # @kernelProcess = child_process.exec(commandString, {
+            #         cwd: projectPath
+            #     })
         else
             commandString = _.first(@kernelInfo.argv)
             args = _.rest(@kernelInfo.argv)
@@ -43,10 +47,10 @@
                 else
                     return arg

-            console.log "launching kernel:", commandString, args
-            @kernelProcess = child_process.spawn(commandString, args, {
-                    cwd: projectPath
-                })
+        console.log "launching kernel:", commandString, args
+        @kernelProcess = child_process.spawn(commandString, args, {
+                cwd: projectPath
+            })

             # commandString = 'unbuffer ' + commandString + ' ' + args.join(' ')
             # console.log "launching:", commandString


Code Cleanup

Code Cleanup

--- lib/linter.coffee
+++ lib/linter.coffee
@@ -64,9 +64,6 @@
     self = this
     atom.workspace.onDidChangeActivePaneItem ->
       self.msgPanel.render()
-#    atom.workspace.observePaneItems (pane)->
-#      return if typeof panel.onDidDestroy is 'undefined'
-#      pane.onDidDestroy -> self.msgPanel.render()
     atom.workspace.observeTextEditors (editor)->
       editor.buffer.onDidSave (info)->
         if self.config.type is 'remote' then self.H.upload(info.path).then(-> self.Lint(info.path)) else self.Lint(info.path)
@@ -76,12 +73,10 @@
       dir = path.dirname(localPath).split(path.sep).join('/').replace(' ', '\\ ') #escaping spaces
       exec "hh_client --json --from atom", {"cwd": dir}, (_,__,errors)->
         self.setErrors(errors)
-        self.msgPanel.render()
     else
       dir = @H.remotePath(path.dirname(localPath))
       @ssh.exec("hh_client --json --from atom", {"cwd": dir}).then (result)->
         self.setErrors(result.stderr)
-        self.msgPanel.render()
   setErrors:(output)->
     self = this
     if output.substr(0,1) isnt '{'
@@ -98,3 +93,4 @@
       for trace in error.message when typeof trace isnt 'undefined'
         deError.trace.push line: trace.line,start: trace.start, end: trace.end, message:trace.descr,file:trace.path.replace(@config.remoteDir,@config.localDir).split('/').join(path.sep)
       @errors.push deError
+    self.msgPanel.render()


logger changes (will externalize it into own module soon)

--- lib/logger.coffee
+++ lib/logger.coffee
@@ -1,5 +1,7 @@
 # Logger
 # Adapted from socket.io-node's logger
+c = require('colors')
+

 # arguments helper
 toArray = (enu) ->
@@ -15,10 +17,10 @@

 # Colors for log levels
 colors = [
-  31
-  33
-  36
-  90
+  c.red
+  c.yellow
+  c.green
+  c.cyan
 ]

 # Count from levels
@@ -26,26 +28,30 @@
 num = levels.length

 # Pads the nice output to the longest log level
-pad = (str) ->
-  return str + new Array(max - str.length + 1).join(' ') if str.length < max
-  str
+pad = (str, len) ->
+  if str.length < len then str + new Array(len - str.length + 1).join(' ') else str

 # Public API

-# Logger Class (used to be exports)
-module.exports = Logger = (@colors=true) ->
+# Logger Class
+Logger = (@prefix, @size = 10) ->

 # Log method
 Logger::log = (type) ->
   index = levels.indexOf(type)
   return @ if index >= num
+  type = pad(type, max).toUpperCase()
+
+  @delim = colors[index]('-')
+  end = if @prefix then [c.blue(c.bold(pad(@prefix, @size))), @delim] else []
+  #console.log @size, @prefix.length, @prefix

   console.log.apply console, [
-      if @colors
-        '   \033[' + colors[index] + 'm' + pad(type) + ' -\033[39m'
-      else
-        type + ':'
-    ].concat(toArray(arguments)[1...])
+    c.grey new Date().toLocaleTimeString()
+    @delim
+    if type is 'error' then c.bold type else type
+    @delim
+  ].concat(end).concat(toArray(arguments)[1...])
   @

 # Generate methods
@@ -53,7 +59,14 @@
   Logger::[name] = ->
     @log.apply(@, [name].concat(toArray(arguments)))

+module.exports = Logger
+
 # Quick test
 if module is require.main
-  log = new Logger({colors:true})
-  log.warn('this could be bad').info('wee').error('this bad.').debug('irrelephant')
+  size = 10
+  log = new Logger('logger', size)
+  log.error('this is very bad').warn('this could be bad').info('standard message').debug('irrelephant message')
+  log = new Logger('deathmatch', size)
+  log.error('this is very bad').warn('this could be bad').info('standard message').debug('irrelephant message')
+  log = new Logger()
+  log.error('this is very bad').warn('this could be bad').info('standard message').debug('irrelephant message')


Test travis

Test travis

--- lib/mecano.coffee
+++ lib/mecano.coffee
@@ -335,11 +335,6 @@
                     cmd: "git clone #{options.source} #{path.basename options.destination}"
                     cwd: path.dirname options.destination
                 , (err, executed, stdout, stderr) ->
-                    console.log '================='
-                    console.log stdout
-                    console.log '-----------------'
-                    console.log stderr
-                    console.log '================='
                     return next err if err
                     checkout()
             log = ->


Fixes #45

Fixes #45

--- config/application.coffee
+++ config/application.coffee
@@ -206,4 +206,4 @@

     lint:
       files: "<%= files.glob.js.app %>"
-      tasks: ["configure","lint"]
+      tasks: ["configure","jshint"]


:gift: incSearch works on rangeMarker, selectionOnly  determine automatically

--- lib/motion.coffee
+++ lib/motion.coffee
@@ -878,11 +878,18 @@
     else
       cursor.getBufferPosition()

+  isSearchInSelection: ->
+    @isIncrementalSearch?() and @editorElement.classList.contains('selection-only-mode')
+
   getMatchList: (cursor, input) ->
-    if @isMode('visual') and @isIncrementalSearch?() and @editorElement.classList.contains('selection-only-mode')
+    scanRanges = []
+    if @isSearchInSelection()
       scanRanges = @editor.getSelectedBufferRanges()
-    else
-      scanRanges = []
+    else if @vimState.hasRangeMarkers()
+      ranges = @vimState.getRangeMarkerBufferRanges()
+      cursorPoint = @getFromPoint(cursor)
+      if ranges.some((range) -> range.containsPoint(cursorPoint, true))
+        scanRanges = ranges

     MatchList.fromScan @editor,
       fromPoint: @getFromPoint(cursor)


fix oauth1

fix oauth1

Signed-off-by: Arnaud Richard <791baf8974538c563f287130527dd623cb0e836d@webshell.io>

--- lib/oauth1.coffee
+++ lib/oauth1.coffee
@@ -307,7 +307,7 @@

 	# build body
 	if req.method == "PATCH" || req.method == "POST" || req.method == "PUT"
-		options.body = req._body
+		options.body = req._body || req.body

 	# do request
 	callback null, request(options)


Remove some unused code

Remove some unused code

--- lib/routes.coffee
+++ lib/routes.coffee
@@ -1,9 +1,3 @@
-filters =
-
-  resetScroll: ->
-    scrollTo = window.currentScroll || 0;
-    $('body').scrollTop(scrollTo);
-    $('body').css("min-height", 0);

 Router.configure
   layoutTemplate: 'layout'
@@ -13,11 +7,7 @@
   routeControllerNameConverter: 'upperCamelCase'
   # trackPageView: true

-# Router.onAfterAction(filters.resetScroll,
-#   {except:['wishes', 'proposals', 'speakers', 'vote']});
-
 Router.map ->
-  # @route 'home', path: '/', action: -> @redirect('agenda')
   @route 'home',
     path: '/'
     fastRender: true


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/game_object.coffee
+++ test/game_object.coffee
@@ -37,6 +37,8 @@
   o = GameObject()

   p = o.sendOrApply "position"
+
+  equals p.x

 test "[event] create", 2, ->
   GameObject


remove step from game object default events

--- test/game_object.coffee
+++ test/game_object.coffee
@@ -43,7 +43,7 @@
     create: 'ok(true, "created event is fired on create")'
   .create()

-test "[event] update", 2, ->
+test "[event] update", 1, ->
   gameObject = GameObject()

   gameObject.bind "update", ->


refactoring

refactoring

--- lib/server.coffee
+++ lib/server.coffee
@@ -3,17 +3,14 @@
 http = require('http')
 #https = require('https')

-self = {}
 Session = require('./session').Session
 Request = require('./request')
+
 api = require('./api')
+static = new($SS.libs.static.Server)('./public')

 class exports.Server
-
-  constructor: () ->
-    self = @
-    @static_server = new($SS.libs.static.Server)('./public')
-
+
   start: ->
     @server = http.createServer(@_processHttpRequest)
     @socket = $SS.libs.io.listen(@server, {transports: ['websocket', 'flashsocket']})
@@ -28,7 +25,8 @@
       key:  fs.readFileSync("#{$SS.root}/config/ssl/key.pem"),
       cert: fs.readFileSync("#{$SS.root}/config/ssl/cert.pem")
     }
-
+
+
   # HTTP

   _processHttpRequest: (request, response) ->
@@ -38,7 +36,7 @@
       $SS.sys.asset.request.serve(request, response)
     else
       request.addListener 'end', ->
-        self.static_server.serve(request, response)
+        static.serve(request, response)
         $SS.sys.log.staticFile(request)


cors realz

cors realz

--- lib/server.coffee
+++ lib/server.coffee
@@ -13,13 +13,11 @@
 kublai.use express.logger('dev')
 #kublai.use(require('less-middleware')({ src: './public/style' }))
 kublai.use express.static('./public')
-kublai.all '/', (req, res, next)->
+
+#kublai.set 'json spaces',0
+kublai.get '/data/:year/:set/counties/:tables', (req,res)->
 	res.header "Access-Control-Allow-Origin", "*"
 	res.header "Access-Control-Allow-Headers", "X-Requested-With"
-	next()
-
-#kublai.set 'json spaces',0
-kublai.get '/data/:year/:set/counties/:tables', (req,res,next)->
 	if docs[req.params.year].counties[req.params.tables]
 		res.jsonp docs[req.params.year].counties[req.params.tables]
 		return true


improve indexation tests

improve indexation tests

--- test/index_tests.coffee
+++ test/index_tests.coffee
@@ -76,6 +76,9 @@
                     should.exist body.fields
                     should.exist body.doc
                     should.exist body.doc.docType
+                if url is '/search'
+                    should.exist body.query
+                    body.query.should.equal "dragons"

             indexer.listen 9102


Update sunset.coffee

Update sunset.coffee

--- lib/sunset.coffee
+++ lib/sunset.coffee
@@ -3,7 +3,7 @@
 cached_themes =
   ui: []
   syntax: []
-
+
 module.exports = Sunset =
   config:
     daytime_syntax_theme:


start adding better documentation

start adding better documentation

--- src/guts.coffee
+++ src/guts.coffee
@@ -1,14 +1,6 @@
-#####
-###
 #    Guts Framework for Backbone views
-###
-#
-# Handling subviews:
-# http://stackoverflow.com/questions/9337927/how-to-handle-initializing-and-rendering-subviews-in-backbone-js
-# Basic render strategy:
-# http://ianstormtaylor.com/rendering-views-in-backbonejs-isnt-always-simple/
-verbose = false
-
+
+# A helper function used in sanity testing the child view reassignment strategy
 isDescendant = (parent, child) ->
   node = child.parentNode
   while node isnt null
@@ -17,6 +9,8 @@
     node = node.parentNode
   false

+# A helper function used to transfer child nodes from within an orphaned DOM
+# element to its new home in its parent's DOM tree.
 moveChildren = (elFrom, elTo) ->
   if elTo.childNodes.length
     throw 'moveChildren : error : destination tag should not have any children'
@@ -25,11 +19,22 @@
     elTo.appendChild elFrom.childNodes[0]

-# BasicModelView always updates and is therefore only OK for basic templates
-# that are not nested or involving forms.
+# BasicModelView is a simple, always-up-to-date view that knows how to
+# render itself given a template or className. All Model based Views in Guts
+# will attempt to render using their prescribed template.
+# @example
+#   view = new BasicModelView
+#     model: myModel
+#     template: 'my-template'
+#     el: $('#my-model')
+# It should not be necessary to call any methods on BasicModelView, except the
+# constructor.
 class BasicModelView extends Backbone.View
+  # returns the name of the template to pass to
+  # Guts.render at render stime. Template path resolution checks the following
+  # locations in order: @options.template, @template, @className.
   get_template: =>
-    @options.template or @template or @className
+      @options.template or @template or @className

   render: =>
     if @options.models
@@ -50,6 +55,7 @@
     @$el.html(template_result)
     @

+  # @param [options] options can contain 'model', or 'models', as well as 'template'. See Backbone.View for more options.
   initialize: (options) =>
     @options = options
     @render()
@@ -174,9 +180,6 @@

   save: =>
     @_stop_listening()
-    @listenToOnce @model, 'sync', =>
-      if verbose
-        console.log 'save succeeded'
     @model.save()

   submitted: (e) =>
@@ -388,8 +391,6 @@
   @render: (template_name, context, helpers) ->
     template = App.Handlebars[template_name]
     if template
-      if verbose
-        console.log "Rendering template '#{template_name}'"
       output = template context,
         helpers: _.defaults {}, helpers, Handlebars.helpers
       return output


added FunctionBlock which replaces cls.FuncVal

--- test/select.test.coffee
+++ test/select.test.coffee
@@ -62,6 +62,20 @@
   'build query':
     'no need to call from() first': ->
       @inst.toString()
+
+    '>> function(1)':
+      beforeEach: -> @inst.function('1')
+      toString: ->
+        assert.same @inst.toString(), 'SELECT 1'
+      toParam: ->
+        assert.same @inst.toParam(), { text: 'SELECT 1', values: [] }
+
+    '>> function(MAX(?,?), 3, 5)':
+      beforeEach: -> @inst.function('MAX(?, ?)', 3, 5)
+      toString: ->
+        assert.same @inst.toString(), 'SELECT MAX(3, 5)'
+      toParam: ->
+        assert.same @inst.toParam(), { text: 'SELECT MAX(?, ?)', values: [3, 5] }

     '>> from(table).from(table2, alias2)':
       beforeEach: -> @inst.from('table').from('table2', 'alias2')


Oh my, this domain exists now

Oh my, this domain exists now

--- test/test-airbud.coffee
+++ test/test-airbud.coffee
@@ -155,7 +155,7 @@

     it "should not throw exception for unresolvable domain", (done) ->
       opts =
-        url: "http://asd.asdasdasd.asdfsadf.com/non-existing.json"
+        url: "http://asd.asdasdasd.asdfnon-existingsadf.com/non-existing.json"

       Airbud.json opts, (err, data, meta) ->
         meta.should.have.property("attempts").that.equals 2


Refactor given helper to use an accessor and allow lazy init. Add waiting helper implementation.

--- test/test_helper.coffee
+++ test/test_helper.coffee
@@ -32,10 +32,20 @@
     @[name] = @subject if name?

 global.given = (name, block) ->
-  beforeEach -> @[name] = block.call this
-
-global.waiting = ->
-
+  beforeEach ->
+    self = this
+    Object.defineProperty this, name,
+                          configurable: true,
+                          enumerable: true,
+                          get: -> self["__#{name}"] ?= block.call self
+  afterEach ->
+    delete @[name]
+
+global.waiting = (block) ->
+  beforeEach ->
+    ended = false
+    runs -> block.call(this).then -> ended = true
+    waitsFor progress(-> ended), 'Timed out during promise', 2000

 cursor = 0
 global.progress = (f) ->


Fix xmodule jasmine tests so they stop trying to reload the page

--- common/lib/xmodule/xmodule/js/src/combinedopenended/display.coffee
+++ common/lib/xmodule/xmodule/js/src/combinedopenended/display.coffee
@@ -404,7 +404,7 @@
     $.postWithPrefix "#{@ajax_url}/check_for_score", (response) =>
       if response.state == "done" or response.state=="post_assessment"
         delete window.queuePollerID
-        location.reload()
+        @reload()
       else
         window.queuePollerID = window.setTimeout(@poll, 10000)


Updated error ellipse winding order

Updated error ellipse winding order

--- attitude/display/assets/functions.coffee
+++ attitude/display/assets/functions.coffee
@@ -50,9 +50,14 @@
   (p)->
     e = math.normalErrors p.covariance, p.axes, opts
     f = createFeature("Polygon", [e])
+
+    # Check winding (note: only an issue with non-traditional
+    # stereonet axes)
     a = d3.geoArea(f)
     if a > 2*Math.PI
       f = createFeature("Polygon",[e.reverse()])
+    f.properties ?= {}
+    f.properties.area = a
     f

 module.exports =


Should the paramenter of JSON.parse be string??

--- tests/integrated.coffee
+++ tests/integrated.coffee
@@ -24,7 +24,7 @@
           "./tests/data/single/source.css.map"
         ).then(
           (map) ->
-            right.sourcemap = JSON.parse(map)
+            right.sourcemap = JSON.parse(map.toString "utf-8")
             right.sourcemap.sources = right.sourcemap.sources.map(
               (file) -> path.relative(
                 process.cwd(), path.resolve "./tests/data/single", file