Add dynamic name attribute to iframe

Add dynamic name attribute to iframe

We observed duplicate reports without our JavaScript code to have run.
Judging that this is probably related to content caching of our iframe
we generate a timestamp-based name attribute for our iframe for
the purpose of cache busting.

Ref: https://stackoverflow.com/questions/2648053/preventing-iframe-caching-in-browser/26191196#26191196

--- src/loader.coffee
+++ src/loader.coffee
@@ -22,6 +22,7 @@
   iframe = document.createElement('iframe')
   iframe.src = "javascript:false"
   iframe.title = ""
+  iframe.name = "frame-#{new Date().getTime()}" # https://stackoverflow.com/a/26191196/4375736
   # iframe.role = "presentation" # a11y

   (iframe.frameElement or iframe).style.cssText =


csv.js: if no columns are passed, it will get columns from the keys of the rowObjects

--- source/javascripts/lib/csv.coffee
+++ source/javascripts/lib/csv.coffee
@@ -64,9 +64,14 @@
     else if param
       @columns  = if _isArray param.columns then param.columns else []
       if param.rowObjects
-        @rowArray = do ->
+        if @columns.length is 0
+          # this may be slow, but it should get all the possible columns
           for row in param.rowObjects
-            row[c] for c in param.columns
+            for key, val of row when key not of columns
+              @columns.push key
+        @rowArray = do =>
+          for row in param.rowObjects
+            row[c] for c in @columns
       else
         @rowArray = if _isArray param.rows then param.rows else []
       @kind     = param.kind if param.kind?


SocialWorker: pass options to expressify

--- workers/social/lib/social/main.coffee
+++ workers/social/lib/social/main.coffee
@@ -156,7 +156,12 @@
   helmet.defaults app
   app.use cors()

-  app.post '/xhr', koding.expressify()
+  options =
+    rateLimitEnabled    : KONFIG.nodejsRateLimiter.enabled
+    userRateLimitRules  : KONFIG.nodejsRateLimiter.userRules
+    guestRateLimitRules : KONFIG.nodejsRateLimiter.guestRules
+
+  app.post '/xhr', koding.expressify options
   app.get '/xhr', (req, res) ->
     res.send 'Socialworker is OK'


Add regression coverage for remeasuring characters w/ requested update

The problem here is that the lines in the editor no longer reflect the
lines on screen, but we're looking for line nodes corresponding to the
editor's current contents.

--- spec/editor-component-spec.coffee
+++ spec/editor-component-spec.coffee
@@ -1953,6 +1953,7 @@
         wrapperView.hide()

         component.setFontSize(22)
+        editor.getBuffer().insert([0, 0], 'a') # regression test against atom/atom#3318

         wrapperView.show()
         editor.setCursorBufferPosition([0, Infinity])


Fix Test

Fix Test

--- spec/provider-manager-spec.coffee
+++ spec/provider-manager-spec.coffee
@@ -45,25 +45,25 @@

     it 'adds providers', ->
       expect(providerManager.isProviderRegistered(testProvider)).toEqual(false)
-      expect(providerManager.subscriptions?.disposables?.has(testProvider)).toEqual(false)
+      expect(providerManager.subscriptions?.disposables?.indexOf(testProvider)).toBe(-1)

       providerManager.addProvider(testProvider, '2.0.0')
       expect(providerManager.isProviderRegistered(testProvider)).toEqual(true)
       apiVersion = providerManager.apiVersionForProvider(testProvider)
       expect(apiVersion).toEqual('2.0.0')
-      expect(providerManager.subscriptions?.disposables?.has(testProvider)).toEqual(true)
+      expect(providerManager.subscriptions?.disposables?.indexOf(testProvider)).not.toBe(-1)

     it 'removes providers', ->
       expect(providerManager.metadataForProvider(testProvider)).toBeFalsy()
-      expect(providerManager.subscriptions?.disposables?.has(testProvider)).toEqual(false)
+      expect(providerManager.subscriptions?.disposables?.indexOf(testProvider)).toBe(-1)

       providerManager.addProvider(testProvider)
       expect(providerManager.metadataForProvider(testProvider)).toBeTruthy()
-      expect(providerManager.subscriptions?.disposables?.has(testProvider)).toEqual(true)
+      expect(providerManager.subscriptions?.disposables?.indexOf(testProvider)).not.toBe(-1)

       providerManager.removeProvider(testProvider)
       expect(providerManager.metadataForProvider(testProvider)).toBeFalsy()
-      expect(providerManager.subscriptions?.disposables?.has(testProvider)).toEqual(false)
+      expect(providerManager.subscriptions?.disposables?.indexOf(testProvider)).toBe(-1)

     it 'can identify a provider with a missing getSuggestions', ->
       bogusProvider =


Correctly set initial stock levels in HQ

--- src/mapgen.coffee
+++ src/mapgen.coffee
@@ -4,14 +4,14 @@
   x.map((x) -> x / 100)

 mapParameters = Bacon.combineTemplate
-  #initialFood: K('initial_food')
-  #initialLumber: K('initial_lumber')
-  #initialStone: K('initial_stone')
-  #initialOre: K('initial_ore')
-  #initialIron: K('initial_iron')
+  initialFood: K('initial_food')
+  initialLumber: K('initial_lumber')
+  initialStone: K('initial_stone')
+  initialOre: K('initial_ore')
+  initialIron: K('initial_iron')
   #initialShips: K('initial_ships')
-  #initialPlanks: K('initial_planks')
-  #initialFlax: K('initial_flax')
+  initialPlanks: K('initial_planks')
+  initialFlax: K('initial_flax')
   treeChance: percentage K('tree_chance')
   ironChance: percentage K('iron_chance')
   rockLevel: percentage K('rock_level')
@@ -125,13 +125,13 @@
     sprites: [["fg_hq_00", "fg_hq_10", "fg_hq_20"],
               ["fg_hq_01", "fg_hq_11", "fg_hq_21"]]
     stock:
-      food: 300
-      flax: 0
-      lumber: 0
-      planks: 0
-      stone: 0
-      iron: 0
-      ore: 0
+      food: params.initialFood
+      flax: params.initialFlax
+      lumber: params.initialLumber
+      planks: params.initialPlanks
+      stone: params.initialStone
+      iron: params.initialIron
+      ore: params.initialOre
   GameState.start
     camera: [hqLocation[0] - ((params.camWidth - 1) / 2),
              hqLocation[1] - ((params.camHeight - 1) / 2)]


Stage: Use default port

Stage: Use default port

--- config/main.stage.coffee
+++ config/main.stage.coffee
@@ -7,7 +7,7 @@

 domainName = "stage.aws.koding.com"

-mongo = 'PROD-koding:34W4BXx595ib3J72k5Mh@db-m0.stage.aws.koding.com:17017/beta_koding'
+mongo = 'PROD-koding:34W4BXx595ib3J72k5Mh@db-m0.stage.aws.koding.com:27017/beta_koding'

 projectRoot = nodePath.join __dirname, '..'


utils/spy: Make available as public interface

--- src/msgflo.coffee
+++ src/msgflo.coffee
@@ -31,3 +31,5 @@
 exports.manager = require '../src/manager'
 exports.library = require '../src/library'
 exports.procfile = require '../src/procfile'
+exports.utils
+  spy: require '../src/utils/spy'


フォローしてるタグの情報の判定処理おかしかったのでqiita.coffee修正

フォローしてるタグの情報の判定処理おかしかったのでqiita.coffee修正

--- coffee/model/qiita.coffee
+++ coffee/model/qiita.coffee
@@ -128,25 +128,18 @@
   _request:(parameter,storedTo,callback) ->

     self = @
-    # if self.isConnected() is false
-    #   # controller.errorHandle()
-    #   logData =
-    #     source  : "qiita._request()"
-    #     time    : moment().format("YYYY-MM-DD hh:mm:ss")
-    #     message : "fail"
-    #   Ti.API.info logData
-
-    #   controller.logging(logData)

     xhr = Ti.Network.createHTTPClient()

     xhr.ondatastream = (e) ->
-      if storedTo isnt "followingTags"
+      if storedTo is "followingTags"
+        Ti.API.debug "フォローしてるタグの情報取得する際には何も行わない"
+      else
         if Math.round(e.progress * 100) <= 100

           Ti.API.info "xhr.ondatastream start progress is #{Math.round(e.progress * 100)}"
           progressBar.value = e.progress
-          # commandController.countUp()
+

     Ti.API.info parameter.method + ":" + parameter.url
     xhr.open(parameter.method,parameter.url)
@@ -157,8 +150,10 @@

       # アプリ起動中にキャッシュしたい情報かどうかをこのstoredToパラメータ
       # にて行う。
-
-      if storedTo isnt false
+      if storedTo is "followingTags"
+        Ti.API.debug "フォローしてるタグの情報取得する際には何も行わない"
+      else
+
         Ti.API.info "start _storedStocks #{storedTo}"
         # QiitaAPIから取得した投稿情報をTi.App.Propertiesに都度突っ込み
         # これをローカルDB的に活用する
@@ -178,10 +173,8 @@
       callback(json,relLink)

     xhr.onerror = (e) ->
-      # Ti.API.info "status code: #{@.status}"
       error = JSON.parse(@.responseText)
-      Ti.App.Properties.setBool "#{storedTo}Error", true
-      # Ti.API.info "_request method error.#{error.error}"
+      Ti.API.debug "_request method error.#{error.error}"


index script

index script

--- coffeescript/index.coffee
+++ coffeescript/index.coffee
@@ -7,14 +7,14 @@
 # winston.level = 'debug' # most
 # winston.level = 'verbose' # a little more than info

-# keimola, germany, usa
+# keimola, germany, usa, france
 if process.env.TESTRACE?
   testRace =
-    trackName: 'keimola'
-    carCount: 4
+    trackName: 'usa'
+    carCount: 3
     joinOnly: off

-winston.info "I'm #{name} and connect to #{serverHost}:#{serverPort}"
+winston.verbose "I'm #{name} and connect to #{serverHost}:#{serverPort}"

 botData = {name, key}


whitespace and small source tweaks

whitespace and small source tweaks

--- jqTabs.coffee
+++ jqTabs.coffee
@@ -20,7 +20,7 @@
 		# if the `hasher` library isn't loaded, set useHistory to false regardles of previous setting
 		if @settings.useHistory and not hasher?
 			@settings.useHistory = false
-
+
 		if not @settings.tabsClickable
 			@settings.useHistory = false

@@ -38,9 +38,9 @@

 		$('ul.tab-headers', $tabsContainer).on 'click', 'li', (e) =>
 			e.preventDefault()
-
+
 			if @settings.tabsClickable
-
+
 				target = $ e.currentTarget

 				unless target.hasClass @settings.activeClass
@@ -64,7 +64,7 @@

 		@$tabContent.addClass @settings.hiddenClass
 		$(@$tabContent[whereTo]).removeClass @settings.hiddenClass
-
+
 	seek : (whereTo) =>
 		#only proceed, if the tab you want to seek to exists
 		if 0 > whereTo or whereTo >= @numTabs
@@ -81,12 +81,12 @@
 				hasher.changed.active = false
 				hasher.setHash hash
 				hasher.changed.active = true
-
+
 			@changeTab whereTo
 			if @settings.callbacksAfter[whereTo]?
 				@settings.callbacksAfter[whereTo]()
 		return
-
+
 	next : ->
 		@seek (@activeTab + 1)
 		return
@@ -94,7 +94,7 @@
 	previous : ->
 		@seek (@activeTab - 1)
 		return
-
+
 	on : (index, position, callback) ->
 		switch position
 			when 'before'
@@ -141,7 +141,7 @@
 			$newTabHeader = @makeHeader tabHeader
 			$newTabHeader.addClass @settings.activeClass
 			headerContainer.append($newTabHeader)
-
+
 			$newTabContent = @makeContent tabContent
 			$newTabContent.removeClass @settings.hiddenClass
 			@$tabsContainer.append $newTabContent
@@ -161,10 +161,10 @@

 		@updateElements()

-	removeLast: () ->
+	removeLast: ->

-	updateElements: () ->
 		@$tabs = $('ul.tab-headers li:not(.ignore-tab)', @$tabsContainer)
+	updateElements: ->
 		@$tabContent = @$tabsContainer.children('div')

 	makeHeader: (header) ->
@@ -173,8 +173,8 @@
 	makeContent: (content) =>
 		$('<div/>', { 'class': 'tabcontent ' + @settings.hiddenClass}).append(content)

-	setHashChange : () ->
-
+	setHashChange : ->
+
 		historyChangeTab = (newHash) =>

 				changeTo = -1
@@ -185,7 +185,7 @@
 					if href == newHash
 						changeTo = index
 						return false
-
+
 				if changeTo isnt -1
 					@seek changeTo

@@ -194,6 +194,6 @@
 			hasher.changed.add historyChangeTab

 			hasher.init()
-
+

 window.jqTabs = jqTabs


servicesEndpoint changed to koding.com for access control allow origin protection

--- config/main.kontrol-prod.coffee
+++ config/main.kontrol-prod.coffee
@@ -110,7 +110,7 @@
       version   : version
       mainUri   : "http://koding.com"
       broker    :
-        servicesEndpoint: "https://server-#{version}.x.koding.com/-/services/broker"
+        servicesEndpoint: "https://koding.com/-/services/broker"
         sockJS   : "https://broker-#{version}.koding.com/subscribe"
       apiUri    : 'https://www.koding.com'
       # Is this correct?


Remove extra indent in eventBinding().

Remove extra indent in eventBinding().

--- src/rivets.coffee
+++ src/rivets.coffee
@@ -139,8 +139,8 @@

 # Returns an element binding routine for the specified attribute.
 eventBinding = (event) -> (el, bind, unbind) ->
-    bindEvent el, event, bind if bind
-    unbindEvent el, event, unbind if unbind
+  bindEvent el, event, bind if bind
+  unbindEvent el, event, unbind if unbind

 # Returns an attribute binding routine for the specified attribute. This is what
 # is used when there are no matching routines for an identifier.


Added support for AMD, CommonJS strict.

Added support for AMD, CommonJS strict.

Based on [UMD example](https://github.com/umdjs/umd/blob/master/commonjsStrictGlobal.js).

--- src/rivets.coffee
+++ src/rivets.coffee
@@ -375,32 +375,34 @@
 Rivets.formatters = {}

 # The rivets module. This is the public interface that gets exported.
-rivets =
+factory = (exports) ->
   # Exposes the core binding routines that can be extended or stripped down.
-  binders: Rivets.binders
+  exports.binders = Rivets.binders

   # Exposes the formatters object to be extended.
-  formatters: Rivets.formatters
+  exports.formatters = Rivets.formatters

   # Exposes the rivets configuration options. These can be set manually or from
   # rivets.configure with an object literal.
-  config: Rivets.config
+  exports.config = Rivets.config

   # Sets configuration options by merging an object literal.
-  configure: (options={}) ->
+  exports.configure = (options={}) ->
     for property, value of options
       Rivets.config[property] = value
     return

   # Binds a set of model objects to a parent DOM element. Returns a Rivets.View
   # instance.
-  bind: (el, models = {}) ->
+  exports.bind = (el, models = {}) ->
     view = new Rivets.View(el, models)
     view.bind()
     view

-# Exports rivets for both CommonJS and the browser.
-if module?
-  module.exports = rivets
+# Exports rivets for CommonJS, AMD and the browser.
+if typeof exports == 'object'
+  factory(exports)
+else if typeof define == 'function' && define.amd
+  define ['exports'], factory
 else
-  @rivets = rivets
+  factory(@rivets = {})


Move output listener out of debug func

Move output listener out of debug func

--- src/runner.coffee
+++ src/runner.coffee
@@ -14,7 +14,6 @@
     debug 'recv graph', command, payload
   client.on 'network', ({command, payload}) ->
     debug 'recv network', command, payload
-    console.log payload.message if command is 'output' and payload.message
   client.on 'runtime', ({command, payload}) ->
     debug 'recv runtime', command, payload
   client.on 'component', ({command, payload}) ->
@@ -43,7 +42,11 @@
     @client.on 'status', onStatus
     @client.connect()

+    @client.on 'network', ({command, payload}) ->
+      console.log payload.message if command is 'output' and payload.message
+
     debugReceivedMessages @client
+

   disconnect: (callback) ->
     debug 'disconnect'


fixes #1380: `super` with reserved names

--- nodes.coffee
+++ nodes.coffee
@@ -469,7 +469,7 @@
     {name} = method
     throw SyntaxError 'cannot call super on an anonymous function.' unless name
     if method.klass
-      "#{method.klass}.__super__.#{name}"
+      (new Value (new Literal method.klass), [new Access(new Literal "__super__"), new Access new Literal name]).compile o
     else
       "#{name}.__super__.constructor"

@@ -933,8 +933,8 @@
       else
         o.scope.find name
     if @value instanceof Code and match = METHOD_DEF.exec name
-      @value.name  = match[2]
       @value.klass = match[1] if match[1]
+      @value.name  = match[2] ? match[3] ? match[4]
     val = @value.compile o, LEVEL_LIST
     return "#{name}: #{val}" if @context is 'object'
     val = name + " #{ @context or '=' } " + val
@@ -1798,9 +1798,24 @@
 # Tabs are two spaces for pretty printing.
 TAB = '  '

-IDENTIFIER = /^[$A-Za-z_\x7f-\uffff][$\w\x7f-\uffff]*$/
+IDENTIFIER_STR = "[$A-Za-z_\\x7f-\\uffff][$\\w\\x7f-\\uffff]*"
+IDENTIFIER = /// ^ #{IDENTIFIER_STR} $ ///
 SIMPLENUM  = /^[+-]?\d+$/
-METHOD_DEF = /^(?:([$A-Za-z_][$\w\x7f-\uffff]*)\.prototype\.)?([$A-Za-z_][$\w\x7f-\uffff]*)$/
+METHOD_DEF = ///
+	^
+		(?:
+			(#{IDENTIFIER_STR})
+			\.prototype
+				(?:
+					\.(#{IDENTIFIER_STR})
+				|
+					\[("(?:[^"\r\n]|\\")*"|'(?:[^'\r\n]|\\')*')\]
+				)
+		)
+	|
+		(#{IDENTIFIER_STR})
+	$
+///

 # Is a literal value a string?
 IS_STRING = /^['"]/


coco e8563db: tiny optimizations

coco e8563db: tiny optimizations

--- nodes.coffee
+++ nodes.coffee
@@ -14,6 +14,10 @@
 YES  = -> yes
 NO   = -> no
 THIS = -> this
+
+# Default implementations of common functions.
+RETURN = -> @returns = yes; this
+NEGATE = -> @negated = not @negated; this

 #### Base

@@ -571,9 +575,12 @@
     @objects = @properties = props or []

   compileNode: (o) ->
-    for prop, i in @properties when (prop.variable or prop).base instanceof Parens
-      rest = @properties.splice i
-      break
+    props = @properties
+    return (if @front then '({})' else '{}') unless props.length
+    for prop, i in props
+      if prop instanceof Splat or (prop.variable or prop).base instanceof Parens
+        rest = @properties.splice i
+        break
     idt         = o.indent += TAB
     nonComments = (prop for prop in @properties when prop not instanceof Comment)
     lastNoncom  = last nonComments
@@ -632,6 +639,7 @@
     @objects = objs or []

   compileNode: (o) ->
+    return '[]' unless @objects.length
     o.indent += TAB
     return code if code = Splat.compileSplattedArray o, @objects
     code = (obj.compile o, LEVEL_LIST for obj in @objects).join ', '
@@ -652,14 +660,11 @@
   children: ['variable', 'parent', 'properties']

   isStatement: YES
+  makeReturn:  RETURN

   # Initialize a **Class** with its name, an optional superclass, and a
   # list of prototype property assignments.
   constructor: (@variable, @parent, @properties = []) ->
-
-  makeReturn: ->
-    @returns = true
-    this

   # Instead of generating the JavaScript string directly, we build up the
   # equivalent syntax tree and compile that, in pieces. You can see the
@@ -983,16 +988,13 @@
   children: ['condition', 'guard', 'body']

   isStatement: YES
+  makeReturn:  RETURN

   constructor: (condition, options) ->
     @condition = if options?.invert then condition.invert() else condition
     @guard     = options?.guard

   addBody: (@body) ->
-    this
-
-  makeReturn: ->
-    @returns = true
     this

   containsPureStatement: ->
@@ -1119,11 +1121,9 @@

   children: ['object', 'array']

+  invert: NEGATE
+
   constructor: (@object, @array) ->
-
-  invert: ->
-    @negated = not @negated
-    this

   compileNode: (o) ->
     if @array instanceof Value and @array.isArray()
@@ -1207,11 +1207,9 @@

   children: ['expression']

+  invert: NEGATE
+
   constructor: (@expression) ->
-
-  invert: ->
-    @negated = not @negated
-    this

   compileNode: (o) ->
     code = @expression.compile o
@@ -1265,6 +1263,7 @@
   children: ['body', 'source', 'guard', 'step', 'from', 'to']

   isStatement: YES
+  makeReturn:  RETURN

   constructor: (body, head) ->
     if head.index instanceof Value
@@ -1273,10 +1272,6 @@
     @body    = Expressions.wrap [body]
     @pattern = @name instanceof Value
     @returns = false
-
-  makeReturn: ->
-    @returns = true
-    this

   containsPureStatement: While::containsPureStatement


Fixing Issue #589. Compound assignment to an operation should have lower precedence.

--- nodes.coffee
+++ nodes.coffee
@@ -1110,6 +1110,7 @@
   compileAssignment: (o) ->
     [first, firstVar] = @first.compileReference o, precompile: yes, assignment: yes
     second = @second.compile o
+    second = "(#{second})" if @second instanceof OpNode
     o.scope.find(first) if first.match(IDENTIFIER)
     return "#{first} = #{ ExistenceNode.compileTest(o, literal(firstVar)) } ? #{firstVar} : #{second}" if @operator is '?='
     "#{first} = #{firstVar} #{ @operator.substr(0, 2) } #{second}"


Revert "fixed condext during construction of bound functions; now using native"

This reverts commit 8d6e33c2cf6aedd3a1243b2113d660180b0c647d.

--- nodes.coffee
+++ nodes.coffee
@@ -1769,17 +1769,7 @@

   # Create a function bound to the current value of "this".
   bind: '''
-    (Function.prototype.bind ? Function.prototype.call.bind(Function.prototype.bind) : function(fn, me){
-		return function bound(){
-			if (!(this instanceof bound))
-				return fn.apply(me, arguments);
-			var ctor = function(){};
-			ctor.prototype = fn.prototype;
-			var obj = new ctor();
-			fn.apply(obj, arguments);
-			return obj;
-		};
-	})
+    function(fn, me){ return function(){ return fn.apply(me, arguments); }; }
   '''

   # Discover if an item is in an array.


whitespace adjustments

whitespace adjustments

--- nodes.coffee
+++ nodes.coffee
@@ -541,8 +541,7 @@
     parts: []
     parts.push @from.compile o if @from isnt @from_var
     parts.push @to.compile o if @to isnt @to_var
-    tail: "\n$o.indent"
-    if parts.length then "${parts.join('; ')};$tail" else tail
+    if parts.length then "${parts.join('; ')};\n$o.indent" else ''

   # When compiled normally, the range returns the contents of the *for loop*
   # needed to iterate over the values in the range. Used by comprehensions.
@@ -1226,7 +1225,7 @@
       for_part:     source.compile merge o, {index: ivar, step: @step}
     else
       svar:         scope.free_variable()
-      source_part:  "$svar = ${ @source.compile(o) };\n$@tab"
+      source_part:  "$svar = ${ @source.compile(o) };"
       if @pattern
         var_part:   new AssignNode(@name, literal("$svar[$ivar]")).compile(merge o, {indent: @idt(1), top: true}) + "\n"
       else
@@ -1235,7 +1234,8 @@
         lvar:       scope.free_variable()
         step_part:  if @step then "$ivar += ${ @step.compile(o) }" else "$ivar++"
         for_part:   "$ivar = 0, $lvar = ${svar}.length; $ivar < $lvar; $step_part"
-    set_result:     if rvar then @idt() + rvar + ' = []; ' else @idt()
+    source_part:    (if rvar then "$rvar = []; " else '') + source_part
+    source_part:    if source_part then "$@tab$source_part\n$@tab" else @tab
     return_result:  @compile_return_value(rvar, o)

     body:           ClosureNode.wrap(body, true) if top_level and body.contains (n) -> n instanceof CodeNode
@@ -1247,7 +1247,7 @@
     body:           body.compile(merge(o, {indent: body_dent, top: true}))
     vars:           if range then name else "$name, $ivar"
     close:          if @object then '}}' else '}'
-    "$set_result${source_part}for ($for_part) {\n$var_part$body\n$@tab$close$return_result"
+    "${source_part}for ($for_part) {\n$var_part$body\n$@tab$close$return_result"

 #### IfNode


Issue #1071, parenthesizing undefined literals when accessed.

--- nodes.coffee
+++ nodes.coffee
@@ -291,7 +291,7 @@

   compileNode: (o) ->
     code = if @isUndefined
-      'void 0'
+      if o.level >= LEVEL_ACCESS then '(void 0)' else 'void 0'
     else if @value.reserved
       "\"#{@value}\""
     else


complete remove evaluator example

complete remove evaluator example

--- src/server.coffee
+++ src/server.coffee
@@ -15,9 +15,6 @@
 app.get '/dst/parser.js', (req, res) ->
   res.sendfile './dst/parser.js'

-app.get '/dst/evaluator.js', (req, res) ->
-  res.sendfile './dst/evaluator.js'
-
 app.get '/test', (req, res) ->
   formula = req.query.formula
   data = req.query.data


small typo

small typo

--- src/sirius.coffee
+++ src/sirius.coffee
@@ -141,8 +141,6 @@
   constructor: (params, wrapper = (x) -> x) ->
     controller = params['controller'] || throw new Error("Params must contain a Controller")

-    `var c = function(m){console.log(m);};`
-
     act = params['action']

     action = if Sirius.Utils.is_string(act)
@@ -197,7 +195,7 @@
       data   = if Sirius.Utils.is_array(@data) then @data else if @data then [@data] else []
       data   = Sirius.Application.adapter.get_property(e, data)
       merge  = [].concat([], [e], data)
-      # fix bug#4 when event is a custom event we should get a args for this event
+      # fix bug#4 when event is a custom event we should get an args for this event
       merge  = [].concat([], merge, args...)
       if @guard
         if @guard.apply(null, merge)
@@ -251,7 +249,6 @@

     # context should be null for functions without controller
     wrapper = (fn) ->
-      #console.log Sirius.Application.controller_wrapper
       for key, value of Sirius.Application.controller_wrapper
         @[key] = value


:lipstick:

:lipstick:

--- src/app/config.coffee
+++ src/app/config.coffee
@@ -45,7 +45,6 @@
     return if fsUtils.exists(@configDirPath)

     fsUtils.makeDirectory(@configDirPath)
-

     queue = async.queue ({sourcePath, destinationPath}, callback) =>
       fsUtils.copy(sourcePath, destinationPath, callback)


Set/get text using edit session instead of buffer

--- src/app/editor.coffee
+++ src/app/editor.coffee
@@ -621,11 +621,11 @@
   # Public: Replaces the current buffer contents.
   #
   # text - A {String} containing the new buffer contents.
-  setText: (text) -> @getBuffer().setText(text)
+  setText: (text) -> @activeEditSession.setText(text)
   # Public: Retrieves the current buffer contents.
   #
   # Returns a {String}.
-  getText: -> @getBuffer().getText()
+  getText: -> @activeEditSession.getText()
   # Public: Retrieves the current buffer's file path.
   #
   # Returns a {String}.


appId & orgUrl in sandbox config

appId & orgUrl in sandbox config

--- src/client/api.coffee
+++ src/client/api.coffee
@@ -281,7 +281,8 @@
         env.config.assetsUrl            = remoteConfig.assetsUrl
         env.config.services             = remoteConfig.services
         env.config.widgets.sources.hull = remoteConfig.baseUrl + '/widgets'
-        env.sandbox.config.assetsUrl    = remoteConfig.assetsUrl
+        env.sandbox.config.appId        = env.config.appId
+        env.sandbox.config.orgUrl       = env.config.orgUrl
         env.sandbox.config.services     = remoteConfig.services
         for m in ['me', 'app', 'org']
           attrs = data[m]


clearTimeout instead of timeout.clear

clearTimeout instead of timeout.clear

--- src/client/api.coffee
+++ src/client/api.coffee
@@ -278,10 +278,10 @@
       timeout = setTimeout(
         ()->
           initialized.reject('Remote loading has failed. Please check "orgUrl" and "appId" in your configuration. This may also be about connectivity.')
-        , 30000)
+        , 30000)

       onRemoteReady = (remoteConfig)->
-        timeout.clear()
+        window.clearTimeout(timeout)
         data = remoteConfig.data
         app.config.assetsUrl            = remoteConfig.assetsUrl
         app.config.services             = remoteConfig.services


fix https://github.com/sagemathinc/smc/issues/1750 -- "enter doesn't submit in duplicate dialog"

--- src/smc-webapp/project_files.cjsx
+++ src/smc-webapp/project_files.cjsx
@@ -1150,11 +1150,11 @@

     render_rename_or_duplicate: () ->
         single_item = @props.checked_files.first()
-        action_title = switch @props.file_action
-                            when 'rename'
-                                'Rename'
-                            when 'duplicate'
-                                'Duplicate'
+        switch @props.file_action
+            when 'rename'
+                action_title = 'Rename'
+            when 'duplicate'
+                action_title = 'Duplicate'
         <div>
             <Row>
                 <Col sm=5 style={color:'#666'}>
@@ -1198,10 +1198,15 @@
     render_duplicate: ->
         @render_rename_or_duplicate()

-    submit_action_rename: () ->
+    submit_action_rename: ->
         single_item = @props.checked_files.first()
         if @valid_rename_input(single_item)
             @rename_or_duplicate_click()
+
+    # Make submit_action_duplicate an alias for submit_action_rename, due to how our
+    # dynamically generated function calls work.
+    submit_action_duplicate: ->
+        @submit_action_rename()

     move_click: ->
         @props.actions.move_files


Determinate export type dependent on other parameters. Refs #24.

--- src/coffee/run.coffee
+++ src/coffee/run.coffee
@@ -15,7 +15,6 @@
   .describe('sphereHost', 'SPHERE.IO API host to connect to')
   .describe('fetchHours', 'Number of hours to fetch modified orders')
   .describe('standardShippingMethod', 'Allows to define the fallback shipping method name if order has none')
-  .describe('exportType', 'CSV or XML')
   .describe('exportUnsyncedOnly', 'whether only unsynced orders will be exported or not')
   .describe('targetDir', 'the folder where exported files are saved')
   .describe('useExportTmpDir', 'whether to use a system tmp folder to store exported files')
@@ -33,7 +32,6 @@
   .describe('timeout', 'Set timeout for requests')
   .default('fetchHours', 48) # let's keep it limited to 48h
   .default('standardShippingMethod', 'None')
-  .default('exportType', 'xml')
   .default('exportUnsyncedOnly', true)
   .default('targetDir', path.join(__dirname,'../exports'))
   .default('useExportTmpDir', false)
@@ -104,12 +102,14 @@
     user_agent: "#{package_json.name} - #{package_json.version}"
   clientOptions.host = argv.sphereHost if argv.sphereHost

+  exportType = if argv.csvTemplate then 'csv' else 'xml'
+
   orderExport = new OrderExport
     client: clientOptions
     export:
       fetchHours: argv.fetchHours
       standardShippingMethod: argv.standardShippingMethod
-      exportType: argv.exportType
+      exportType: exportType
       exportUnsyncedOnly: argv.exportUnsyncedOnly
       csvTemplate: argv.csvTemplate


Use fat arrows

Use fat arrows

--- src/coffee/run.coffee
+++ src/coffee/run.coffee
@@ -39,7 +39,7 @@
 process.on 'exit', => process.exit(@exitCode)

 credentialsConfig = ProjectCredentialsConfig.create()
-.then (credentials) ->
+.then (credentials) =>
   options =
     baseConfig:
       fetchHours: argv.fetchHours
@@ -60,15 +60,15 @@

   updater = new PriceSync options
   updater.run()
-  .then (message) ->
+  .then (message) =>
     logger.info message
     @exitCode = 0
-  .fail (error) ->
+  .fail (error) =>
     logger.error error, 'Oops, something went wrong!'
     # process.exit(1)
     @exitCode 1
   .done()
-.fail (err) ->
+.fail (err) =>
   logger.error err, "Problems on getting client credentials from config files."
   # process.exit(1)
   @exitCode = 1


Ensure cache is invalidated when preload operation completes

--- src/trix/models/attachment.coffee
+++ src/trix/models/attachment.coffee
@@ -35,9 +35,9 @@
   getAttributes: ->
     @attributes.toObject()

-  setAttributes: (attributes = {}) ->
+  setAttributes: (attributes = {}, options = {}) ->
     newAttributes = @attributes.merge(attributes)
-    unless @attributes.isEqualTo(newAttributes)
+    if options.touch or not @attributes.isEqualTo(newAttributes)
       @attributes = newAttributes
       @didChangeAttributes()
       @delegate?.attachmentDidChangeAttributes?(this)
@@ -117,7 +117,7 @@
       @preloadOperation ?= operation
       operation.then ({width, height}) =>
         @preloadOperation = operation
-        @setAttributes({width, height})
+        @setAttributes({width, height}, touch: true)
         @releaseFile()

   createPreviewPreloadOperationForFile: (file) ->


Defer GC'ing view caches

Defer GC'ing view caches

--- src/trix/views/object_view.coffee
+++ src/trix/views/object_view.coffee
@@ -1,3 +1,7 @@
+#= require trix/utilities/helpers
+
+{defer} = Trix.Helpers
+
 class Trix.ObjectView
   constructor: (@object, @options = {}) ->
     @childViews = []
@@ -43,7 +47,7 @@
   refreshCache: ->
     views = @getAllChildViews().concat(this)
     @refreshLocationCacheWithViews(views)
-    @refreshViewCacheWithViews(views)
+    defer => @refreshViewCacheWithViews(views)

   refreshLocationCacheWithViews: (views) ->
     @cache.locations = {}


limit query

limit query

--- test/cases/controller/paramsTest.coffee
+++ test/cases/controller/paramsTest.coffee
@@ -47,13 +47,13 @@
         assert.deepEqual ['Second Post', 'First Post'], _.map posts, (i) -> i.get('title')
         done()

-    # test 'limit: 1', (done) ->
-    #   params = limit: 1
-    #
-    #   _.post '/posts', params: params, (response) ->
-    #     posts = response.controller.get('posts')
-    #     assert.equal 1, posts.length
-    #     done()
+    test 'limit: 1', (done) ->
+      params = limit: 1
+
+      _.get '/posts', params: params, (response) ->
+        posts = response.controller.get('posts')
+        assert.equal 1, posts.length
+        done()

   test 'date string is serialized to database'
     # params = user: birthdate: _(26).years().ago().toDate()


Load covered file

Load covered file

--- test/dummy-project/test/coverage.coffee
+++ test/dummy-project/test/coverage.coffee
@@ -1,6 +1,6 @@
 all = require('../../../src')

 # relative to package root
-all.config.globals.dummyProject = '../../build/test/library/dist/dummy-project.coffee'
+all.config.globals.dummyProject = '../../build/test/library/dist/dummy-project.coverage.node.js'

 all.test.coverage({ base: __dirname })


Correct test function name.

Correct test function name.

--- test/integration/connection-test.coffee
+++ test/integration/connection-test.coffee
@@ -234,7 +234,7 @@
     #console.log(text)
   )

-exports.execSqlMultipleTime = (test) ->
+exports.execSqlMultipleTimes = (test) ->
   timesToExec = 5
   sqlExecCount = 0


Remove redundant test

Remove redundant test

--- test/microservice-stop-stop-test.coffee
+++ test/microservice-stop-stop-test.coffee
@@ -1,66 +0,0 @@
-# Copyright 2016 Fuzzy.io
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#    http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-debug = require('debug')('microservice-start-stop-test')
-
-async = require 'async'
-_ = require 'lodash'
-vows = require 'vows'
-assert = require 'assert'
-request = require 'request'
-
-Widget = require './widget'
-WidgetService = require './widgetservice'
-
-APP_KEY = "soothlesseecovezqislam"
-
-process.on 'uncaughtException', (err) ->
-  process.stderr.write require('util').inspect(err) + "\n"
-
-vows
-  .describe('start-stop-start-stop')
-  .addBatch
-    'When we instantiate a microservice':
-      topic: ->
-        callback = @callback
-        try
-          env =
-            PORT: "2342"
-            HOSTNAME: "localhost"
-            DRIVER: "memory"
-            LOG_FILE: "/dev/null"
-            APP_KEY_UNIT_TEST: APP_KEY
-          service = new WidgetService env
-          callback null, service
-        catch err
-          callback err
-        undefined
-      'it works': (err, service) ->
-        assert.ifError err
-      'and we start and stop the service':
-        topic: (service) ->
-          callback = @callback
-          async.waterfall [
-            (callback) ->
-              service.start callback
-            (callback) ->
-              service.stop callback
-            (callback) ->
-              service.stop callback
-          ], callback
-          undefined
-        'it works': (err) ->
-          assert.ifError err
-
-  .export(module)


chore(tests): add specs for Products#priceWithDiscount

--- test/unit/modules/resources_spec.coffee
+++ test/unit/modules/resources_spec.coffee
@@ -34,12 +34,29 @@

         expect(products).to.have.length 2

-        # TODO write specs for `product.priceWithDiscount`
-        product = products[0]
-        expect(product).to.be.an.instanceof(Products)
+    describe "#priceWithDiscount", ->
+      product = null
+      beforeEach inject (Products) ->
+        product = new Products(price: @price, discount: @discount)

-        expect(product.hasDiscount).to.not.be.undefined
-        expect(product.hasDiscount()).to.be.false
+      it "is defined", ->
+        expect(product.priceWithDiscount).to.not.be.undefined
+
+      context "when discount is not defined", ->
+        before ->
+          @discount = undefined
+          @price = 9.99
+
+        it "returns the base price", ->
+          expect(product.priceWithDiscount()).to.equal 9.99
+
+      context "when discount is defined", ->
+        before ->
+          @discount = 22
+          @price = 100
+
+        it "returns price with discount", ->
+          expect(product.priceWithDiscount()).to.equal 78

     describe "#hasDiscount", ->
       product = null


Small thing with the top nubby for puzzle-piece fit

--- src/controller.coffee
+++ src/controller.coffee
@@ -563,7 +563,9 @@
       @session.view.opts.tabHeight + height
   points.push new @draw.Point @session.view.opts.tabOffset, height

-  points.push new @draw.Point -5, height
+  points.push new @draw.Point @session.view.opts.bevelClip, height
+  points.push new @draw.Point 0, height + @session.view.opts.bevelClip
+  points.push new @draw.Point -5, height + @session.view.opts.bevelClip
   points.push new @draw.Point -5, -5

   @topNubbyPath.setPoints points


renamed all tests from mirror -> repository

Signed-off-by: Dominik Richter <b7323add517e187efe0283b76511b7ed762bfe13@gmail.com>

--- test/spec/controllers/main.coffee
+++ test/spec/controllers/main.coffee
@@ -62,10 +62,10 @@
     }
   ]

-  allMirrors1 = [
+  allRepos1 = [
     {
       'id': '44e5f422-62db-42dc-b1ce-37ca3393710f',
-      'name': 'Magus Mirror',
+      'name': 'Magus Repository',
       'label': 'live',
       'url': 'http://archive.canonical.com/ubuntu/dists/precise/partner/binary-amd64/Packages.gz',
       'provider': 'apt'
@@ -250,13 +250,13 @@
         expect(p.versions).toBe(undefined)

   it 'should list /packages compared to upstream repositories', () ->
-    # get mirrors
-    paginateResponse @httpBackend, '/v1/mirrors', allMirrors1, () -> scope.fetchMirrors()
-    expect(scope.mirrors.length).toBe(allMirrors1.length)
-    # set packages list when selecting a mirror
+    # get repos
+    paginateResponse @httpBackend, '/v1/repositories', allRepos1, () -> scope.fetchRepos()
+    expect(scope.repos.length).toBe(allRepos1.length)
+    # set packages list when selecting a repository
     ps = allPackages2
-    scope.selectMirror(allMirrors1[0])
-    opts = {'query': [['mirror',allMirrors1[0].id],['outdated','true']]}
+    scope.selectRepo(allRepos1[0])
+    opts = {'query': [['repo',allRepos1[0].id],['outdated','true']]}
     dontPaginateResponse @httpBackend, '/v1/packages', ps, (() -> scope.fetchPackages()), opts
     # results
     expect(scope.allPackages.length).toBe(ps.length)
@@ -337,52 +337,52 @@
     expect( scope.packages[0].name ).toBe( allPackages1[0].name )

-  ## Querying Mirrors
-
-  it 'should be able to [C]reate a new mirror', () ->
-    # when you create a new mirror it should add a new position to the list to the front
-    expect( scope.newMirrors.length ).toBe(0)
-    scope.newMirror()
-    expect( scope.newMirrors.length ).toBe(1)
-    expect( scope.newMirrors[0].id ).toBe(undefined)
+  ## Querying Repositories
+
+  it 'should be able to [C]reate a new repository', () ->
+    # when you create a new repository it should add a new position to the list to the front
+    expect( scope.newRepos.length ).toBe(0)
+    scope.newRepo()
+    expect( scope.newRepos.length ).toBe(1)
+    expect( scope.newRepos[0].id ).toBe(undefined)
     # when you click on save, it should issue an post request to the server to indicate a new element
-    callResponse @httpBackend, '/v1/mirrors', 'POST', 201, allMirrors1[0], () -> scope.saveMirror(scope.newMirrors[0])
+    callResponse @httpBackend, '/v1/repositories', 'POST', 201, allRepos1[0], () -> scope.saveRepo(scope.newRepos[0])
     # make sure nothing is changed if the call was successful
-    expect( scope.newMirrors.length ).toBe(0)
-    expect( scope.mirrors[0].id ).not.toBe(undefined)
-
-  it 'should [R]ead all mirrors the server has available', () ->
-    paginateResponse @httpBackend, '/v1/mirrors', allMirrors1, () -> scope.fetchMirrors()
-    expect(scope.mirrors.length).toBe(allMirrors1.length)
-
-  it 'should be able to [U]pdate an existing mirror', () ->
+    expect( scope.newRepos.length ).toBe(0)
+    expect( scope.repos[0].id ).not.toBe(undefined)
+
+  it 'should [R]ead all repositories the server has available', () ->
+    paginateResponse @httpBackend, '/v1/repositories', allRepos1, () -> scope.fetchRepos()
+    expect(scope.repos.length).toBe(allRepos1.length)
+
+  it 'should be able to [U]pdate an existing repository', () ->
     # TODO: maybe remove the creation and expect it to exist already
-    scope.newMirror()
-    callResponse @httpBackend, '/v1/mirrors', 'POST', 201, allMirrors1[0], () -> scope.saveMirror(scope.newMirrors[0])
-
-    # initiate editing of a mirror
-    obj = scope.mirrors[0]
-    expect(scope.editingMirror[obj.id]).toBe(undefined)
-    scope.editMirror(obj)
-    expect(scope.editingMirror[obj.id]).not.toBe(undefined)
-
-    # change some property of this mirror
-    scope.editingMirror[obj.id].name = 'Minus Monor'
+    scope.newRepo()
+    callResponse @httpBackend, '/v1/repositories', 'POST', 201, allRepos1[0], () -> scope.saveRepo(scope.newRepos[0])
+
+    # initiate editing of a repository
+    obj = scope.repos[0]
+    expect(scope.editingRepo[obj.id]).toBe(undefined)
+    scope.editRepo(obj)
+    expect(scope.editingRepo[obj.id]).not.toBe(undefined)
+
+    # change some property of this repository
+    scope.editingRepo[obj.id].name = 'Minus Monor'

     # save the result
-    res = allMirrors1[0]
+    res = allRepos1[0]
     res.name = 'Minus Monor'
-    callResponse @httpBackend, '/v1/mirrors/'+obj.id, 'PATCH', 200, res, () -> scope.saveMirror(scope.editingMirror[obj.id])
+    callResponse @httpBackend, '/v1/repositories/'+obj.id, 'PATCH', 200, res, () -> scope.saveRepo(scope.editingRepo[obj.id])

     # check if the results are correct
-    expect(scope.editingMirror[obj.id]).toBe(undefined)
-    obj = scope.mirrors[0]
+    expect(scope.editingRepo[obj.id]).toBe(undefined)
+    obj = scope.repos[0]
     expect(obj.name).toBe('Minus Monor')

-  it 'should be able to [D]elete an existing mirror', () ->
+  it 'should be able to [D]elete an existing repository', () ->
     # TODO: maybe remove the creation and expect it to exist already
-    scope.newMirror()
-    callResponse @httpBackend, '/v1/mirrors', 'POST', 201, allMirrors1[0], () -> scope.saveMirror(scope.newMirrors[0])
-    # update the name of an existing mirror
-    callResponse @httpBackend, '/v1/mirrors/'+scope.mirrors[0].id, 'DELETE', 204, null, () -> scope.deleteMirror(scope.mirrors[0])
-    expect(scope.mirrors.length).toBe(0)+    scope.newRepo()
+    callResponse @httpBackend, '/v1/repositories', 'POST', 201, allRepos1[0], () -> scope.saveRepo(scope.newRepos[0])
+    # update the name of an existing repository
+    callResponse @httpBackend, '/v1/repositories/'+scope.repos[0].id, 'DELETE', 204, null, () -> scope.deleteRepo(scope.repos[0])
+    expect(scope.repos.length).toBe(0)


KodingClient: added RegisterHostNameAndKey router

--- client/app/MainApp/kodingrouter.coffee
+++ client/app/MainApp/kodingrouter.coffee
@@ -260,6 +260,26 @@
     createContentHandler       = @bound 'createContentDisplayHandler'
     createStaticContentHandler = @bound 'createStaticContentDisplayHandler'

+    registerHostNameAndKey = ->
+      kite =
+        name: "kodingclient"
+        publicIP: "127.0.0.1"
+        port: "5555"
+
+      k = new NewKite(kite)
+      k.connect()
+
+      k.tell "info", (err, result) =>
+        KD.remote.api.JKodingKey.registerHostnameAndKey {
+            key:result.key
+            hostname:result.hostID
+        }, (err, res)=>
+          KD.notify_ err.message if err
+          KD.notify_ res if res
+          result.cb true
+          @clear()
+
+
     routes =

       '/'      : handleRoot
@@ -353,6 +373,8 @@
             new ReferrerModal
         else
           @handleRoute '/Login'
+
+      '/:name?/RegisterHostKey': registerHostNameAndKey

       '/member/:username': ({params:{username}})->
         @handleRoute "/#{username}", replaceState: yes


Reset PubSub after each spec

Reset PubSub after each spec

--- client/lanes/testing/BeforeEach.coffee
+++ client/lanes/testing/BeforeEach.coffee
@@ -10,6 +10,8 @@

 afterEach ->
     Lanes.current_user._events = @__user_events
+    Lanes.Models.PubSub.types.reset()
+    Lanes.Models.PubSub.mb = @prevMB

 beforeEach ->
     @__user_events = Lanes.current_user._events
@@ -22,6 +24,11 @@
         message: ''
         data: []
     }
+
+    @prevMB = Lanes.Models.PubSub.mb
+    Lanes.Models.PubSub.mb = jasmine.createSpyObj('MessageBus', [
+        'subscribe', 'unsubscribe'
+    ])

     originalFn = Lanes.Models.Sync.perform
     spyOn(Lanes.Models.Sync, 'perform').and.callFake( (method, options) ->


Exempt Roff files from custom keystroke commands

--- keymap.cson
+++ keymap.cson
@@ -15,6 +15,8 @@
 	"f5":             "lines:sort-basic"
 	"alt-up":         "editor:move-line-up"
 	"alt-down":       "editor:move-line-down"
+
+"atom-text-editor:not([data-grammar='text roff'])":
 	"[":              "user:square-bracket"
 	"{":              "user:curly-bracket"


mapping for ctrl-shift-a, ctrl-shift-e

mapping for ctrl-shift-a, ctrl-shift-e

--- keymap.cson
+++ keymap.cson
@@ -1,14 +1,15 @@
 '.platform-win32 atom-text-editor, .platform-linux atom-text-editor':
   'ctrl-e': 'editor:move-to-end-of-screen-line'
-  'ctrl-shift-e': 'find-and-replace:use-selection-as-find-pattern'  # was ctrl-e
+  'ctrl-shift-e': 'editor:select-to-end-of-line'
+  'alt-shift-e': 'find-and-replace:use-selection-as-find-pattern'  # was ctrl-e

 'atom-workspace atom-text-editor':
-  'ctrl-shift-a': 'core:select-all'  # was ctrl-a
+  'alt-shift-a': 'core:select-all'  # was ctrl-a
   'ctrl-a': 'editor:move-to-first-character-of-line'
+  'ctrl-shift-a': 'editor:select-to-first-character-of-line'

 # great. ctrl-k is delete-to-end-of-line in emacs
 # in atom it's a prefix key, so we'd have to remap a lot of keys
-# plus there seems to be no command that acts like ctrl-k does in emacs

 # deconflict flex-toolbar and term-02
 'atom-workspace':


Add destroy()

Add destroy()

--- layout.coffee
+++ layout.coffee
@@ -327,6 +327,30 @@
     set_name: (@name) ->
       # console.log 'set_name', name, this
       @parent?[name] = @
+
+    _removeFromParent: (name, skipevents) ->
+      return if not @parent
+      index = @parent[name].indexOf(@)
+      if (index != -1)
+        @parent[name].splice(index, 1)
+        @parent.trigger(name) if (not skipevents) and @parent.events?[name]
+      return
+
+    destroy: (skipevents) ->
+      # console.log 'destroy node', @
+      @trigger('destroy', @) if events?['destroy']
+
+      if @listeningTo
+        @stopListening()
+      @unbind()
+
+      if (@parent?[@name] == @)
+        delete @parent[@name]
+
+      for subnode, i in @subnodes
+        subnode.destroy(true)
+
+      @_removeFromParent('subnodes', skipevents)

   class Sprite
@@ -390,6 +414,10 @@
       # $(document.elementFromPoint(event.clientX,event.clientY)).trigger(type);
       # el.show();

+    destroy: ->
+      @el.parentNode.removeChild(@el);
+      @el = @jqel = null
+

   ignoredAttributes = {parent: true, id: true, name: true, extends: true, type: true}
   class View extends Node
@@ -435,6 +463,15 @@

     set_clickable: (clickable) ->
       @sprite.set_clickable(clickable)
+
+    destroy: ->
+      # console.log 'destroy view', @
+      super()
+      @_removeFromParent('subviews')
+
+      @sprite.destroy()
+      @sprite = null
+

   # flatten element.attributes to a hash
   flattenattributes = (namednodemap)  ->
@@ -608,7 +645,7 @@
     constructor: (el, attributes = {}) ->
       super(el, attributes)
       # listen for new subviews
-      @parent.bind('subviews', @added)
+      @listenTo(@parent, 'subviews', @added)
       @parent.layouts ?= []
       @parent.layouts.push(this)

@@ -621,20 +658,27 @@
       @update()
       # console.log('layout', attributes, @)

-    # called when a new subview is added to the parent view
+    # called when a new subview is added or removed from the parent view
     added: (child) =>
       # console.log 'added layout', child, @
-      @trigger('subview', child) if @events?['subview']
+      if child
+        @trigger('subview', child) if @events?['subview']
       @update(null, child)

     # override to update the position of the parent view's children
-    update: (ignore, sender) =>
+    update: (value, sender) =>
       # console.log 'update layout', sender
       return if @skip()

     # returns true if the layout should't update
     skip: () ->
-      true if locked or (not @parent?.subviews)
+      true if locked or (not @parent?.subviews) or (@parent.subviews.length == 0)
+
+    destroy: ->
+      locked = true
+      # console.log 'destroy layout', @
+      super()
+      @_removeFromParent('layouts')

   class SimpleLayout extends Layout
@@ -674,7 +718,7 @@

     added: (child) ->
       # console.log 'added', child
-      child.bind(@axis, @update)
+      @listenTo(child, @axis, @update)
       super(child)

     update: (value, sender) ->


Cleanup

Cleanup

--- layout.coffee
+++ layout.coffee
@@ -1,9 +1,9 @@
 hackstyle = do ->
 	# hack jQuery to send a style event when CSS changes
 	stylemap= {left: 'x', top: 'y', 'background-color': 'bgcolor'}
-	origstyle = $.style;
+	origstyle = $.style
 	newstyle = (elem, name, value) ->
-	  returnval = origstyle.apply(this, arguments);
+	  returnval = origstyle.apply(this, arguments)
 	  name = stylemap[name] or name
 	  # we are setting and aren't disabled
 	  sendstyle = elem.$view?.events?[name]
@@ -11,12 +11,12 @@
 	  if sendstyle
 	    elem.$view.setAttribute(name, value, true)

-	  returnval;
+	  returnval
 	return (active) ->
 		if active
-			$.style = newstyle;
+			$.style = newstyle
 		else
-			$.style = oldstyle;
+			$.style = oldstyle

 window.lz = do ->
 	# from https://github.com/spine/spine/tree/dev/src
@@ -109,7 +109,7 @@
 	class Node extends Module
 		@include Events

-		constructor: (el, options) ->
+		constructor: (el, options = {}) ->
 #			console.log 'new node', @, options
 			@init(options)

@@ -125,7 +125,7 @@
         name = n.property.name

         # remove the property so we can compute the rest of the expression
-        n = n.object;
+        n = n.object

         scopes.push({binding: acorn.stringify(n), property: name})
 #       console.log 'MemberExpression', name, acorn.stringify n
@@ -141,13 +141,13 @@
 			scopes = propertyBindings.find(expression)
 #				console.log 'found scopes', scopes

-			constraintBinding = @constraints[name];
+			constraintBinding = @constraints[name]
 			bindings = constraintBinding.bindings or= {}

 			for scope in scopes
 				{binding, property} = scope
 				bindingfn = (new Function([], 'return ' + binding)).bind(@)
-				bindings[property] = bindingfn;
+				bindings[property] = bindingfn
 #					console.log('bound', name, expression, property, binding)

 #				console.log 'matched constraint', name, @, expression
@@ -262,7 +262,7 @@

 	types = {x: 'number', y: 'number', width: 'number', height: 'number'}
 	class View extends Node
-		skipStyle= {parent: true, id: true, name: true};
+		skipStyle= {parent: true, id: true, name: true}

 		constructor: (el, options = {}) ->
 			if (el instanceof HTMLElement and el.$view)
@@ -331,7 +331,7 @@

 	class Class
-		constructor: (el, options) ->
+		constructor: (el, options = {}) ->
 			name = options.name
 			delete options.name
 			body = el.innerHTML


Don't crash if methods or handlers aren't present

--- layout.coffee
+++ layout.coffee
@@ -214,17 +214,19 @@
       @types = attributes.$types ? {}
       delete attributes.$types

-      # Install methods
-      for methodname, method of attributes.$methods
-        # console.log 'installing method', methodname, method, @
-        installMethod(@, methodname, compileScript.apply(null, method))
-      delete attributes.$methods
-
-      for {name, script, args, type} in attributes.$handlers?
-        name = name.substr(2)
-        # console.log 'installing handler', name, args, type, script, @
-        @bind(name, eventCallback(name, script, @, args))
-      delete attributes.$handlers
+      if attributes.$methods
+        # Install methods
+        for methodname, method of attributes.$methods
+          # console.log 'installing method', methodname, method, @
+          installMethod(@, methodname, compileScript.apply(null, method))
+        delete attributes.$methods
+
+      if attributes.$handlers
+        for {name, script, args, type} in attributes.$handlers
+          name = name.substr(2)
+          # console.log 'installing handler', name, args, type, script, @
+          @bind(name, eventCallback(name, script, @, args))
+        delete attributes.$handlers

       # Bind to event expressions and set attributes
       for name, value of attributes


gamestate

gamestate

--- src/game_state.coffee
+++ src/game_state.coffee
@@ -64,6 +64,9 @@
     I.objects = I.objects.concat(queuedObjects)
     queuedObjects = []

+    #TODO Include GameState modules like cameras,
+    # save states, etc.
+
   # We must always return self as the last line
   return self


MirrorInput tests use jsdom #99

MirrorInput tests use jsdom #99

--- wzk/ui/form/MirrorInput_test.coffee
+++ wzk/ui/form/MirrorInput_test.coffee
@@ -3,42 +3,28 @@

   input = null
   target = null
-  elements = null
-
-  mockDom = (els) ->
-    createDom: (tagName) ->
-      els[tagName]
-
-  mockEl = ->
-    el =
-      events: {}
-      addEventListener: (type, listener) ->
-        el.events[type] = listener
-      value: ''
-      insertBefore: ->
-    el
-
-  mockInput = ->
-    el = mockEl()
-    el.type = 'input'
-    el
-
-  renderInput = (input) ->
-    input.createDom()
-    input.enterDocument()
+  doc = null
+  dom = null

   type = (el, val) ->
     el.value += val
-    el.events['keyup']?()
+    el._listeners['keyup']?.false[0]?.listener.listener({})

   setup ->
-    elements =
-      input: mockInput()
+    doc = jsdom """
+    <html><head></head>
+    <body>
+    <input type="text" name="target" id="target">
+    </body>
+    </html>
+    """
+    dom = new wzk.dom.Dom doc
+    target = doc.getElementById 'target'
+    target.value = ''

   test 'Should watch an element', ->
-    target = mockInput()
-    input = new MirrorInput dom: mockDom(elements), target: target
-    renderInput input
+    input = new MirrorInput dom: dom, target: target
+    input.render doc.body

     type target, 'a'
     assert.equal input.getValue(), target.value
@@ -47,9 +33,8 @@
     assert.equal input.getValue(), target.value

   test 'Should stop watching after its value has been changed', ->
-    target = mockInput()
-    input = new MirrorInput dom: mockDom(elements), target: target
-    renderInput input
+    input = new MirrorInput dom: dom, target: target
+    input.render doc.body

     type target, 'a'
     assert.equal input.getValue(), target.value
@@ -60,10 +45,11 @@
     assert.equal input.getValue(), 'aA'

   test 'Should watch wzk.ui.form.Input', ->
-    target = new wzk.ui.form.Input dom: mockDom(elements)
-    renderInput target
-    input = new MirrorInput dom: mockDom(elements), target: target
-    renderInput input
+    target = new wzk.ui.form.Input dom: dom
+    target.render doc.body
+
+    input = new MirrorInput dom: dom, target: target
+    input.render doc.body

     type target.getElement(), 'a'
     assert.equal input.getValue(), target.getValue()
@@ -72,13 +58,12 @@
     assert.equal input.getValue(), target.getValue()

   test 'Should apply a filter on a value', ->
-    target = mockInput()
-    input = new MirrorInput dom: mockDom(elements), target: target
+    input = new MirrorInput dom: dom, target: target
     input.addFilter (val) ->
       val.toUpperCase()
     input.addFilter (val) ->
       val.replace /\ /g, '_'
-    renderInput input
+    input.render doc.body

     type target, 'a'
     assert.equal input.getValue(), 'A'


include .quark as supercollider syntax

include .quark as supercollider syntax

--- grammars/supercollider.cson
+++ grammars/supercollider.cson
@@ -1,6 +1,7 @@
 'fileTypes': [
   'sc',
-  'scd'
+  'scd',
+  'quark'
 ]
 'foldingStartMarker': '\\{|\\/\\*'
 'foldingStopMarker': '\\}|\\*\\/'


Correct issue ordering (fixes #3)

Correct issue ordering (fixes #3)

--- src/status.coffee
+++ src/status.coffee
@@ -3,7 +3,7 @@
 window.Status or= {}

 class window.Status.Widget
-  _version = "2.3.2-compat"
+  _version = "2.3.4-compat"

   constructor: (@options = {}) ->
     requiredOptions = ["hostname", "selector"]
@@ -191,9 +191,11 @@
     data = @parseEventWithState e

     if "issues" of data
+      @issues = {}
+      @visibleIssues = []
+
       for issue in data["issues"]
-        @issues[issue["id"]] = issue
-        @visibleIssues.push issue["id"] if Object.keys(@issues).length < 5
+        @insertIssue issue

     @updateIssues()

@@ -205,11 +207,7 @@

     if "issue" of data
       data["issue"]["updates"] = []
-      @issues[data["issue"]["id"]] = data["issue"]
-
-      unless data["issue"]["id"] in @visibleIssues
-        @visibleIssues.pop() if @visibleIssues.length >= 4
-        @visibleIssues.unshift data["issue"]["id"]
+      @insertIssue data["issue"]

     @updateIssues()

@@ -233,8 +231,7 @@
       issueId = data["issue"]["id"]

     return unless issueId of @issues
-    delete @issues[issueId]
-    @visibleIssues.unshift issueId
+    @removeIssue issueId
     @updateIssues()

   parse: (event) ->
@@ -282,6 +279,17 @@
   updateIssuePaneText: ->
     return if @issuesPresent()
     setElText @elements.paneText, @i18n["issue"]["empty"][@state]
+
+  insertIssue: (issue) ->
+    id = issue["id"]
+    @issues[id] = issue
+    return if id in @visibleIssues
+    @visibleIssues.splice @issuePosition(id), 0, id
+
+  removeIssue: (id) ->
+    delete @issues[id]
+    return unless id in @visibleIssues
+    @visibleIssues.splice @visibleIssues.indexOf(id), 1

   createIssue: (issue) ->
     container = @createEl "div", @elements.paneIssues, "issue"
@@ -335,6 +343,23 @@

     { body: body, label: label, date: date }

+  issuePosition: (id) ->
+    now = Math.floor(Date.now() / 1000)
+    timestamps = "past": [], "future": []
+    issueTimestamp = 0
+
+    for _, issue of @issues
+      scheduled = !!issue["scheduled"]
+      timestamp = if scheduled then issue["starts_at"] else issue["created_at"]
+      timestamps[if timestamp > now then "future" else "past"].push timestamp
+      issueTimestamp = timestamp if issue["id"] == id
+
+    if issueTimestamp in timestamps["future"]
+      timestamps["future"].sort().indexOf(issueTimestamp) +
+        timestamps["past"].length
+    else
+      timestamps["past"].sort().reverse().indexOf(issueTimestamp)
+
   createEl: (type, parent, className = undefined) ->
     cssClass = "status-widget"
     cssClass += "__#{className}" if className?


Better detection for CSS transitions, now works on IE.

--- controls/SlidingPanels.Sequence.coffee
+++ controls/SlidingPanels.Sequence.coffee
@@ -43,11 +43,8 @@
       # HACK for IE8, in which jQuery 1.7.2 will throw an exception if we try to
       # get the css("transition") property.
       return false
-    for transitionProperty in [ "-webkit-transition", "transition" ]
-      value = $element.css transitionProperty
-      if value isnt undefined and value != ""
-        return true
-    false
+    value = $element.css "transition-duration"
+    return value? and value != ""

   initialize: ->
     @inDocument -> @_adjustWidths()


seems to work (not sure), but this isnt streamed any more :(

--- src/stream.coffee
+++ src/stream.coffee
@@ -36,101 +36,94 @@
 removed = (el) ->
     el.closed is "removed"

+# delay or invoke job immediately
+delay = (job) ->
+    return if removed this
+    # only when tag is ready
+    if @_streamed? and @closed
+        do job
+    else
+        @_stream_buffer?= []
+        @_stream_buffer.push(job)
+
+
+# invoke all delayed stream work
+release = () ->
+#     console.log "release", @name, attrStr(@attrs), @_stream_buffer
+    if @_stream_buffer?
+        for job in @_stream_buffer
+            do job
+        delete @_stream_buffer
+

 #return true if @hidden # dont emit data when this tag is hidden # FIXME
 streamify = (tpl) ->
+#     buffer = null
     stream = new Stream
     stream.readable = on

-    emit = (data) ->
-        stream.emit('data', data)
+    write = (data) ->
+        stream.emit('data', data) if data

-    _write = (data) ->
-        return if @hidden
-        if @_stream.before is null
-#             console.log "write", @name, attrStr(@attrs), data
-            emit data
-        else
-#             console.log "buffer", @name, attrStr(@attrs), data
-            @_stream.buffer.push data
+    tpl.stream = stream
+    builder = tpl.xml ? tpl
+    builder._streamed = yes

-    write_head = ->
-        if @isempty and not @closed
-#             console.log "head", @name, attrStr(@attrs)
-            _write.call this, prettify this, "<#{@name}#{attrStr @attrs}>"
-
-    write = (data) ->
-        write_head.call this
-        _write.call this, data
-
-    builder = tpl.xml ? tpl
-    tpl.stream = stream
-    builder._stream =
-        buffer:[]
-        before:null
-        after:null
-        first:null
-        last:null
+#     tpl.on 'add', (parent, el) ->
+#         console.log "add", el.name, attrStr(el.attrs)
+#         write buffer
+#         buffer = prettify el, "<#{el.name}#{attrStr el.attrs}>"

     tpl.on 'add', (parent, el) ->
-        console.log "add", el.name, attrStr(el.attrs)
-        write_head.call parent
-        parstream = parent._stream
-        el._stream =
-            buffer:[]
-            before:parstream.last
-            after:null
-            first:null
-            last:null
-        parstream.last?.after = el
-        parstream.first ?= el
-        parstream.last = el
+        # insert into parent
+#         unless parent.closed is 'pending'
+#         release.call parent if parent._streamed?
+        if parent is builder
+            target = el
+        else
+            target = parent
+        delay.call target, ->
+            console.log "write", el.name, el.closed, el._streamed
+            if el.closed is 'self'
+                write prettify el, "<#{el.name}#{attrStr el.attrs}/>"
+            else
+                write prettify el, "<#{el.name}#{attrStr el.attrs}>"
+                release.call el unless parent is builder
+            el._streamed = yes
+
+        console.log "add", el.name, attrStr(el.attrs), "(#{parent.name})", el._stream_buffer?.length, (el is parent.pending[0]), parent._streamed
+#         release.call parent if el is parent.pending[0]

     tpl.on 'close', (el) ->
-
-        console.log "close", el.name, attrStr(el.attrs), el.closed, el.isempty
-        if el._stream.before
-            cur = el._stream
-            after  = cur.after?._stream
-            before = cur.before._stream
-            before.buffer = before.buffer?.concat(cur.buffer)
-            after?.before = cur.before
-            before.after  = cur.after
-            if el is el.parent._stream.last
-                el.parent._stream.last = cur.before
-            el._stream = "done"
-            return
-        while el?.closed
-            break unless el.parent._stream.first is el
-            # empty buffer
-            emit data for data in el._stream.buffer
-            if el.closed is 'self'
-                data = "<#{el.name}#{attrStr el.attrs}/>"
-            else
-                data = "</#{el.name}>"
-#             console.log el.name, attrStr(el.attrs), el.isempty, el.closed, data
-            write.call el, prettify el, data
-            # update linked list
-            el._stream.after?.before = null
-            if el is el.parent._stream.first
-                el.parent._stream.first = el._stream.after
-            if el is el.parent._stream.last
-                el.parent._stream.last = null
-            el._stream = "done"
-            el = el._stream.after
-        0
+        console.log "close", el.name, attrStr(el.attrs), el.closed, el.isempty, (el is el.parent.pending[0])
+#         buffer = null
+#         delay.call el.parent, ->
+#         if el is el.parent.pending[0]
+#             release.call el.parent
+#             target = el.parent
+#             t = el
+#         else
+#             target = el
+#             t = el.parent
+        delay.call el, ->
+#             release.call el
+            unless el.closed is 'self'
+                write prettify el, "</#{el.name}>"
+#             el._streamed = yes
+        release.call el if el.parent is builder#el.parent._streamed?
+#         release.call el.parent if el is el.parent.pending[0]

     tpl.on 'data', (el, data) ->
-        write.call el, data
+        delay.call el, ->
+            write data

     tpl.on 'text', (el, text) ->
-        write.call el, text
+        delay.call el, ->
+            write text

     tpl.on 'raw', (el, html) ->
-        write.call el, html
-
-    tpl.on 'data', (el, data) ->
-        write.call el, data
+        delay.call el, ->
+            write html

     tpl.on 'attr', (el, key, value) ->
         return unless el.isempty
@@ -138,6 +131,7 @@

     tpl.on 'end', ->
         console.log "tpl end"
+#         release.call builder
         stream.emit 'end'

 # exports


+ find method to Subkey.

+ find method to Subkey.

--- src/subkey.coffee
+++ src/subkey.coffee
@@ -23,7 +23,7 @@
 LoadingError        = errors.LoadingError
 setImmediate        = global.setImmediate or process.nextTick

-deprecate = require("depd")("level-subkey")
+deprecate = require("depd")("nosql-subkey")
 deprecate.assignProperty = (object, deprecatedProp, currentProp) ->
   if object[deprecatedProp]
     this deprecatedProp + " property, use `" + currentProp + "` instead."
@@ -385,6 +385,54 @@
     post: (opType, key, hook) ->
       @_addHook(key, hook, dbCore.post.bind(dbCore, opType))

+    prepareFindOptions: (aOptions)->
+      aOptions = @mergeOpts(aOptions)
+      assignDeprecatedPrefixOption aOptions
+      makeData = aOptions.makeData
+
+      if not aOptions.makeData
+        aOptions.makeData = if aOptions.keys isnt false and aOptions.values isnt false then (key, value) ->
+            key: key
+            value: value
+        else if aOptions.values is false then (key) -> key
+        else if aOptions.keys   is false then (_, value) -> value
+        else ->
+
+      #the aOptions.path could be relative
+      aOptions.path = getPathArray(aOptions.path, @_pathArray) or @_pathArray
+      aOptions
+    findSync: (aOptions)->
+      aOptions = @prepareFindOptions aOptions
+      it = dbCore.iterator(aOptions)
+      result = []
+      item = it.nextSync()
+      while item isnt false
+        result.push aMakeData(item.key, item.value)
+        item = it.nextSync()
+      result
+    findAsync: (aOptions, callback)->
+      if isFunction aOptions
+        callback = aOptions
+        aOptions = undefined
+      throw new InvalidArgumentError('callback argument required.') unless callback
+      aOptions = @prepareFindOptions aOptions
+      it = dbCore.iterator(aOptions)
+      result = []
+      nextOne = ->
+        it.next (err, key, value)->
+          if err
+            err = null if err.notFound()
+            return callback err, result
+          return callback err, result if err is undefined and key is undefined and value is undefined
+          result.push makeData(key, value)
+          nextOne()
+      nextOne()
+    find: (aOptions, callback)->
+      if isFunction aOptions
+        callback = aOptions
+        aOptions = undefined
+      if callback then @findAsync aOptions, callback else @findSync aOptions
+
     readStream: (opts) ->
       throw new NotImplementedError("please `npm install nosql-stream` to use streamable feature") unless DefaultReadStream
       opts = @mergeOpts(opts)


doubleplus logging for #707

doubleplus logging for #707

--- content/keymanager.coffee
+++ content/keymanager.coffee
@@ -211,11 +211,17 @@
     if citekey = @keys.findOne({ itemID: item.id })
       # item already has proposed citekey ?
       debug("KeyManager.propose: testing whether #{item.id} can keep #{citekey.citekey}")
-      if citekey.citekey.slice(0, proposed.citekey.length) == proposed.citekey                                # key begins with proposed sitekey
+      if citekey.citekey.startsWith(proposed.citekey)                                                         # key begins with proposed sitekey
         re = (proposed.postfix == '0' && @postfixRE.numeric) || @postfixRE.alphabetic
         if citekey.citekey.slice(proposed.citekey.length).match(re)                                           # rest matches proposed postfix
-          if @keys.findOne({ libraryID: item.libraryID, citekey: citekey.citekey, itemID: { $ne: item.id } }) # noone else is using it
+          if other = @keys.findOne({ libraryID: item.libraryID, citekey: citekey.citekey, itemID: { $ne: item.id } }) # noone else is using it
             return citekey
+          else
+            debug('KeyManager.propose: no, because', other, 'is using it')
+        else
+          debug('KeyManager.propose: no, because', citekey.citekey.slice(proposed.citekey.length), 'does not match', '' + re)
+      else
+        debug('KeyManager.propose: no, because', citekey.citekey, 'does not start with', citekey.citekey)

     debug("KeyManager.propose: testing whether #{item.id} can use proposed #{proposed.citekey}")
     # unpostfixed citekey is available


Added fabric.js boilerplate for require.js to player.coffee

--- player.coffee
+++ player.coffee
@@ -14,7 +14,8 @@
 #    You should have received a copy of the GNU Affero General Public License
 #    along with this program.  If not, see http://www.gnu.org/licenses/.

-define(['fabric'], ->
+define(['fabric'], (fabric) ->
+  fabric = fabric.fabric if fabric.fabric?

   class Player


Add close icon to popover

Add close icon to popover

--- elaboratecharts/coffee/index.coffee
+++ elaboratecharts/coffee/index.coffee
@@ -325,6 +325,21 @@
       '<input type="text" class="form-control input-sm"
          value="' + History.getLocationHref() + '" onclick="this.select();" />'
     placement: 'top'
+    title: 'Copy and share with friends'
+    template: '

+

+                <button type="button" class="close"
+                  onclick=$(".btn-xxs").popover("hide");>
+                  ×
+                  Close
+                </button>
+

+

+
'

   $('.btn-xxs').on 'shown.bs.popover', ->
     $('.popover input').focus().select()
+
+  $(window).click (e) ->
+    if !$(e.target).parents('.popover').length && !$(e.target).is('.btn-xxs')
+      $('.btn-xxs').popover('hide')


debug

debug

--- JSKit/04_JSMenuView.coffee
+++ JSKit/04_JSMenuView.coffee
@@ -46,7 +46,7 @@
                 if (@_userInteractionEnabled == false)
                     return
                 obj = ui.item
-                JSLog(event)
+                JSLog(obj)
                 @selectMenuItem(item)
                 @closeMenu()
             classes:


debug

debug

--- JSKit/04_JSMenuView.coffee
+++ JSKit/04_JSMenuView.coffee
@@ -7,7 +7,7 @@
     constructor:(frame = JSRectMake(0, 0, 200, 0))->
         super(frame)
         @_textSize = 10
-        @_backgroundColor = JSColor("white")
+        @_backgroundColor = JSColor("clearColor")
         @_clipToBounds = false
         @_containment = false

@@ -26,7 +26,7 @@
         @_div = "

"
         menustr = ""
         for disp in @_menuitem
-            menustr += "
"+disp+"
"
+            menustr += "
"+disp+"
"
         @_div = @_div.replace(//, menustr)

         if ($(@_viewSelector+"_menu").length)


wrap requirejs; add height(), width()

wrap requirejs; add height(), width()

--- matrix.coffee
+++ matrix.coffee
@@ -1,10 +1,38 @@
-define ->
+define ['util/log'], (log) ->

-  map: (f, A, B = A) ->
+  fail = log.fail
+
+  map = (f, A, Buf = A) ->
     for col, j in A
       for el, i in col
-        B[j][i] = f(el)
-    B
+        Buf[j][i] = f(el)
+    Buf

-  times: (s, A, B) ->
-    this.map(((x) -> s*x), A, B)
+  times = (s, A, Buf) ->
+    map(((x) -> s*x), A, Buf)
+
+  isEmpty = (A) ->
+    if A.length == 0
+      true
+    else if A[0].length == 0
+      fail 'invalid matrix'
+    else
+      false
+
+  height = (A) ->  # assume A is rectangular
+    if (isEmpty A)
+      0
+    else
+      A[0].length
+
+  width = (A) -> # assume A is rectangular
+    if (isEmpty A)
+      0
+    else
+      A.length
+
+  # public api
+  map: map
+  times: times
+  width: width
+  height: height


actually allow the votes to go through

actually allow the votes to go through

--- nodeko.coffee
+++ nodeko.coffee
@@ -273,7 +273,7 @@

 # create vote
 post '/teams/:teamId/votes', ->
-  return @redirect '/teams/' + @req.param('teamId') unless @isAdmin
+  return @redirect '/teams/' + @req.param('teamId') unless @isJudge

   Team.fromParam @req.param('teamId'), (error, team) =>
     # TODO: handle error


remove unnecessary shim

remove unnecessary shim

--- scripts/app.coffee
+++ scripts/app.coffee
@@ -7,8 +7,6 @@
     jquery: 'https://code.jquery.com/jquery-2.1.1.min'
     jsonp: '../bower_components/jsonproxy/jsonp'
     moment: 'https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.0.0/moment.min'
-  shim:
-    jsonp: ['jquery']
 )

 require ['jquery'], ($) ->


Update abu image.

Update abu image.

--- scripts/nom.coffee
+++ scripts/nom.coffee
@@ -4,4 +4,4 @@

 module.exports = (robot) ->
   robot.hear /\bnom\b|food/i, (msg) ->
-    msg.send "http://i.imgur.com/sUsqL.gif" # was "http://i.imgur.com/Ytq3S.gif"
+    msg.send "http://replygif.net/i/152"


atom: don't show welcome on startup

atom: don't show welcome on startup

--- home/atom/.atom/config.cson
+++ home/atom/.atom/config.cson
@@ -44,3 +44,5 @@
   tabs:
     displayMruTabList: false
     enableMruTabSwitching: false
+  welcome:
+    showOnStartup: false


Add support for meteor-roles groups

Add support for meteor-roles groups

- Taken from meteor-voltage

--- server/boot.coffee
+++ server/boot.coffee
@@ -5,6 +5,7 @@
 Blog =
   settings:
     adminRole: null
+    adminGroup: null
     rss:
       title: ''
       description: ''
@@ -60,6 +61,11 @@
       if not Meteor.user()
         return false

+      # If role AND group is passed
+      if Blog.settings.adminRole and Blog.settings.adminGroup and not Roles.userIsInRole(Meteor.user(), Blog.settings.adminRole, Blog.settings.adminGroup)
+        return false
+
+      # If only role is passed
       if Blog.settings.adminRole and not Roles.userIsInRole(Meteor.user(), Blog.settings.adminRole)
         return false


IE9 support added

IE9 support added

--- impl/css/level0/item.coffee
+++ impl/css/level0/item.coffee
@@ -13,6 +13,11 @@

 	if prefix
 		"#{prefix}ransform"
+
+transform3dSupported = do ->
+	tmp = document.createElement 'div'
+	tmp.style[transformProp] = 'translate3d(1px,1px,0)'
+	tmp.style[transformProp].indexOf('translate3d') isnt -1

 isFirefox = navigator.userAgent.indexOf('Firefox') isnt -1

@@ -166,7 +171,7 @@
 			target = item._impl

 			# position
-			if item._impl.isHot
+			if item._impl.isHot and transform3dSupported
 				transform = "translate3d(#{data.x}px, #{data.y}px, 0) "
 			else
 				transform = "translate(#{data.x}px, #{data.y}px) "


Call menu on menu button click, this should be a class.

--- js/application.coffee
+++ js/application.coffee
@@ -14,6 +14,9 @@
     Backbone.history.start()

     @Collections.Activities.fetch()
+
+    $('#menu-button').click ->
+      flight.slideOutMenu()

 window.app = new App


Set cache expiration

Set cache expiration

--- server.coffee
+++ server.coffee
@@ -18,6 +18,7 @@
     @pathComponents.push(path.basename(lastPathComponent, ext))
     @extension = ext.substring(1, ext.length)

+
 class Processor
   constructor: (@ns, @request, @response) ->

@@ -36,6 +37,9 @@
           else
             headers = coffee.helpers.extend({'Cache-Control': 'public'}, imageResponse.headers)
             delete headers['content-length']
+            if @ns.cacheExpiration?
+              headers['Expires'] = new Date(new Date().getTime() + (@ns.cacheExpiration * 1000)).toUTCString()
+
             @response.writeHead(imageResponse.statusCode, headers)
             stdout.pipe(@response)
         )


Found another q import

Found another q import

--- server.coffee
+++ server.coffee
@@ -74,7 +74,6 @@
   if options.debug
     app.use express.logger(format: 'dev')
     loggerOptions.level = 'verbose'
-    require('q').longStackSupport = true
   else
     loggerOptions.level = 'info'
   logger.add winston.transports.Console, loggerOptions


do not forward cookies

do not forward cookies

--- server.coffee
+++ server.coffee
@@ -16,6 +16,8 @@
     resp.end 'ok'
   else
     url = Url.parse req.url
+
+    delete(req.headers.cookie)

     query_digest = url.pathname.replace(/^\//, '')
     query_params = QueryString.parse(url.query)
@@ -86,7 +88,7 @@
 console.log "SSL-Proxy running on #{port} with pid:#{process.pid}."
 console.log "Using the secret key #{shared_key}"

-Fs.open "tmp/camouflage.pid", "w", 0666, (err, fd) ->
+Fs.open "tmp/camo.pid", "w", 0666, (err, fd) ->
   Fs.writeSync fd, process.pid

 server.listen port


Deleted notification when user is close to beacon

--- server.coffee
+++ server.coffee
@@ -71,18 +71,6 @@
 						include: userId
 						text: 'You are in range of an enemy beacon, capture it now!'
 					found=true;
-		#Check if user is nearby an enemy beacon.
-		if !found
-			beacons.iterate (beacon) ->
-				if beacon.peek('owner') isnt getTeamOfUser(userId) and !found
-					dist = distance(geoloc.latitude, geoloc.longitude, beacon.peek('location','lat'), beacon.peek('location','lng'))
-					if dist < beaconRadius *2.5
-						#send notifcation
-						Event.create
-							unit: 'nearby'
-							include: userId
-							text: 'You are ' + parseInt(dist,10)+ ' m away from an enemy beacon, capture it now!'
-						found=true;
 		#Last notification send, so that the user will not be spammed with notifications
 		if found
 			Db.shared.set('lastNotification', userId, recieved)
@@ -348,7 +336,7 @@
 	# The game will end in 1 hour if all the beacons are captured by one team
 	capOwner = Db.shared.peek('game', 'beacons', '0', 'owner')
 	log 'capOwner', capOwner
-	allBeaconsCaptured = true
+	allBeaconsCaptured = true;
 	Db.shared.iterate 'game', 'beacons', (beacon) ->
 		if capOwner isnt beacon.peek('owner')
 			log 'capOwner2', beacon.peek('owner')


If the game hasn't started in an hour, kill it

--- server.coffee
+++ server.coffee
@@ -53,7 +53,7 @@
     socket.gameid = false
     refreshLobby()
   for k, v of games
-    delete games[k] if v.players.length < 2 and (new Date() - v.date) > 3600000
+    delete games[k] if (not v.started or v.players.length < 2) and (new Date() - v.date) > 3600000

 joinGame = (socket, gameid) ->
   game = games[gameid]


Better logging

Better logging

--- server.coffee
+++ server.coffee
@@ -202,7 +202,7 @@
             if rank
                 if rank in [1..3]
                     if results[self - 1][1] is results[rank - 1][1]
-                        log 'Position ' + i + ' has the same score as guessed position ' + self
+                        log 'Position ' + rank + ' has the same score as guessed position ' + self + '. ' + results[self - 1][1] + '==' + results[rank - 1][1]
                         diff = 0

                 if not diff?


/status

/status

--- server.coffee
+++ server.coffee
@@ -46,9 +46,13 @@

    @get '/olive', ->  @render 'olive.blade'

-   # @use @wrap ->
-   #    @res.header 'Content-Type', Conf.contentType
-   #    @next()
+   @use @wrap ->
+      @res.header 'Content-Type', Conf.contentType
+      @next()
+
+   @get '/status', ->
+      @jsonp status: 'ok'
+

    @client '/main.js': ->
       @connect()


Removed old pusher keys

Removed old pusher keys

--- server.coffee
+++ server.coffee
@@ -2,15 +2,13 @@
 games = {}
 gameIDs = []

-require('nko')('LqNOy7Hy1JHYB4yU')
-
-pusher_key = (process.env.PUSHER_KEY || 'af77425a09a90cbee51c')
+pusher_key = process.env.PUSHER_KEY

 Pipe = require 'pusher-pipe'
 pipe = Pipe.createClient
   key:    pusher_key,
-  secret: (process.env.PUSHER_SECRET  || 'fb21cbeef5a569dd6b46'),
-  app_id: (process.env.PUSHER_APP_ID  || 12)
+  secret: process.env.PUSHER_SECRET,
+  app_id: process.env.PUSHER_APP_ID
 pipe.connect()
 # pipe.debug = true


add poc react snippets

add poc react snippets

--- snippets.cson
+++ snippets.cson
@@ -33,6 +33,41 @@
   'Mocha afterEach':
     'prefix': 'aft'
     'body': 'afterEach((${1:done}) => {\n  $2\n});'
+  'React Component: POC':
+    'prefix': 'rcom'
+    'body': """
+    const React = require(\'react\');
+    const { Component, PropTypes } = React;
+
+    class $1 extends Component {
+      static propTypes = {
+        $2
+      }
+
+      render () {
+        return null;
+      }
+    }
+
+    module.exports = $1;
+    """
+  'React Component Spec: POC':
+    'prefix': 'rcoms'
+    'body': """
+    const React = require(\'react\');
+
+    const $1 = require(\'app/components/$2\');
+
+    describe(\'$1\', () => {
+      function render(props = {}) {
+        return shallowRender(<$1 {...props} />);
+      }
+
+      it('renders', () => {
+        expect(render()).to.not.be.undefined;
+      });
+    });
+    """

 #
 # Unity / C-Sharp snippets


Add HTML snippets for working with HTML manpages

--- snippets.cson
+++ snippets.cson
@@ -529,6 +529,18 @@
 		prefix: "link"
 		body: '<link rel="stylesheet" type="text/css" href="${1:main}.css" />$2'

+	Manpage:
+		prefix: "man"
+		body: '${1:name}(${2:1})$3'
+
+	kbd:
+		prefix: "k"
+		body: "$1$2"
+
+	var:
+		prefix: "v"
+		body: "<var>$1</var>$2"
+
 	InlineDiv:
 		prefix: "divi"
 		body: '
$2
'


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/color.coffee
+++ test/color.coffee
@@ -11,9 +11,10 @@
   equals color.g(), 56, 'green channel should equal input green value'
   equals color.b(), 74, 'blue channel should equal input blue value'

+###
 test "Color should parse hex starting with #", ->
   color = Color('#ffffff')
   equals color.r(), 255, 'red channel should equal numeric equivalent of ff'
   equals color.g(), 255, 'green channel should equal numeric equivalent of ff'
   equals color.b(), 255, 'blue channel should equal numeric equivalent of ff'
-
+###


Move error tests over to assert from Chai.should

--- test/error.coffee
+++ test/error.coffee
@@ -1,21 +1,21 @@
 humanizeDuration = require '..'
-require('chai').should()
+assert = require 'assert'

 describe 'error handling', ->

   it 'throws an error when passed a bad language in the function', ->
     humanizingWith = (options) ->
       return -> humanizeDuration(10000, options)
-    humanizingWith({ language: 'bad language' }).should.throw Error
-    humanizingWith({ language: '' }).should.throw Error
-    humanizingWith({ language: null }).should.throw Error
+    assert.throws humanizingWith(language: 'bad language'), Error
+    assert.throws humanizingWith(language: ''), Error
+    assert.throws humanizingWith(language: null), Error

   it 'throws an error when passed a bad language in a humanizer', ->
     h = humanizeDuration.humanizer({ language: 'bad language' })
     humanizing = (options) ->
       return -> h(10000, options)
-    humanizing().should.throw Error
-    humanizing({ language: 'bad language' }).should.throw Error
-    humanizing({ language: '' }).should.throw Error
-    humanizing({ language: null }).should.throw Error
-    humanizing({ language: 'es' }).should.not.throw Error
+    assert.throws humanizing(), Error
+    assert.throws humanizing(language: 'bad language'), Error
+    assert.throws humanizing(language: ''), Error
+    assert.throws humanizing(language: null), Error
+    assert.doesNotThrow humanizing(language: 'es'), Error


Implemented continuable lines in the REPL. Use a trailing slash. Multiline functions are now possible.

--- repl.coffee
+++ repl.coffee
@@ -6,7 +6,6 @@

 # Require the **coffee-script** module to get access to the compiler.
 CoffeeScript = require './coffee-script'
-helpers      = require './helpers'
 readline     = require 'readline'
 Script       = process.binding('evals').Script

@@ -19,12 +18,19 @@
 error = (err) ->
   stdio.write (err.stack or err.toString()) + '\n\n'

+# The current backlog of multi-line code.
+backlog = ''
+
 # The main REPL function. **run** is called every time a line of code is entered.
 # Attempt to evaluate the command. If there's an exception, print it out instead
 # of exiting.
 run = (buffer) ->
+  code = backlog += '\n' + buffer.toString()
+  if code[code.length - 1] is '\\'
+    return backlog = backlog[0...backlog.length - 1]
+  backlog = ''
   try
-    val = CoffeeScript.eval buffer.toString(), bare: on, globals: on, filename: 'repl'
+    val = CoffeeScript.eval code, bare: on, globals: on, filename: 'repl'
     console.log val if val isnt undefined
   catch err
     error err


Client: Activity: Updated setSettings method. it sets default settings when setting item values null

--- client/activity/lib/flux/channelnotificationsettings/stores/channelnotificationsettingsstore.coffee
+++ client/activity/lib/flux/channelnotificationsettings/stores/channelnotificationsettingsstore.coffee
@@ -2,6 +2,7 @@
 immutable                      = require 'immutable'
 toImmutable                    = require 'app/util/toImmutable'
 KodingFluxStore                = require 'app/flux/base/store'
+getDefaultNotificationSettings = require 'activity/util/getDefaultNotificationSettings'

 ###*
  * Store to handle channel notification settings
@@ -23,6 +24,13 @@

   setSettings: (notificationSettings, { channelId, channelNotificationSettings }) ->
+
+    _oldSettings    = notificationSettings.get channelNotificationSettings.channelId
+    defaultSettings = if _oldSettings then _oldSettings.toJS() else getDefaultNotificationSettings()
+
+    for key of channelNotificationSettings
+      if channelNotificationSettings[key] is null
+        channelNotificationSettings[key] = defaultSettings[key]

     notificationSettings.set channelId, toImmutable channelNotificationSettings


Move findDeps to the upper level

Move findDeps to the upper level

--- src/Bacon.coffee
+++ src/Bacon.coffee
@@ -1071,6 +1071,16 @@
   else
     new Desc(context, method, args)

+findDeps = (x) ->
+  if isArray(x)
+    _.flatMap findDeps, x
+  else if isObservable(x)
+    [x]
+  else if x instanceof Source
+    [x.obs]
+  else
+    []
+
 class Desc
   constructor: (context, method, args) ->
     @context = context
@@ -1085,7 +1095,7 @@
     return @flatDeps[b.id]

   apply: (obs) ->
-    deps = _.cached (=> @findDeps([@context].concat(@args)))
+    deps = _.cached (=> findDeps([@context].concat(@args)))
     obs.internalDeps = obs.internalDeps || deps
     obs.dependsOn = dependsOn.bind(@, obs)
     obs.deps = deps
@@ -1101,15 +1111,7 @@
       @flatDeps[dep.id] = true
       @collectDeps(dep)

-  findDeps: (x) ->
-    if isArray(x)
-      _.flatMap @findDeps.bind(@), x
-    else if isObservable(x)
-      [x]
-    else if x instanceof Source
-      [x.obs]
-    else
-      []
+

 withDescription = (desc..., obs) ->
   describe(desc...).apply(obs)


Format code

Format code

--- app/assets/javascripts/uploads.js.coffee
+++ app/assets/javascripts/uploads.js.coffee
@@ -36,20 +36,20 @@
       src = data.result.source
       extension = src.url.substr(src.url.lastIndexOf('.'), src.url.length)
       if $.inArray(extension, image_exts) > -1
-        addToFileList(data.orig_name, src.url, image_thumbnail_markdown(remove_ext(data.orig_name), src.url, src.thumb.url))
+        add_to_file_list(data.orig_name, src.url, image_thumbnail_markdown(remove_ext(data.orig_name), src.url, src.thumb.url))
       else
-        addToFileList(data.orig_name, src.url, link_markdown(remove_ext(data.orig_name), src.url))
+        add_to_file_list(data.orig_name, src.url, link_markdown(remove_ext(data.orig_name), src.url))

   $(post_body).on 'paste', (event) ->
-    postBody = event.currentTarget
-    pastedText = event.originalEvent.clipboardData.getData('text')
-    caretPosition = postBody.selectionStart
-    if pastedText.indexOf("![") == -1 #Ignore images with thumbnails
+    post_body = event.currentTarget
+    pasted_text = event.originalEvent.clipboardData.getData('text')
+    caret_position = post_body.selectionStart
+    if pasted_text.indexOf("![") == -1 #Ignore images with thumbnails
       #Paste event is fired before data is pasted, wait 100ms to make sure that the text is there.
       setTimeout ( ->
-          firstPos = postBody.value.indexOf(pastedText, caretPosition)
-          lastPos = firstPos + pastedText.indexOf("]")
-          postBody.setSelectionRange(firstPos + 1, lastPos - 1)
+          first_pos = post_body.value.indexOf(pasted_text, caret_position)
+          last_pos = first_pos + pasted_text.indexOf("]")
+          post_body.setSelectionRange(first_pos + 1, last_pos)

       ), 100

@@ -64,9 +64,11 @@
   extension = filename.substr(filename.lastIndexOf('.'), filename.length)
   $.inArray(extension, image_exts) || $.inArray(extension, doc_exts)

-addToFileList = (fileName, url, markdown_url) ->
-  content = "
" + fileName + ""
-  content += "<button class='button tiny copy-file' data-clipboard-text='" + markdown_url + "'>" + I18n.t('copy_link') + "
"
+add_to_file_list = (filename, url, markdown_url) ->
+  content = "

+               #{filename}
+              <button class='button tiny copy-file' data-clipboard-text='#{markdown_url}'> #{I18n.t('copy_link')} </button>
+
"
   $("#file-list").append(content)

 image_markdown = (title, url) ->


convert another Session variable to a ReactiveVar

--- client/general.coffee
+++ client/general.coffee
@@ -20,7 +20,7 @@

 # --------------------------------------------
 # session variables
-Session.setDefault "editing_name", null
+editing_name = new ReactiveVar(null)
 Session.setDefault "editing_add_synonym", null
 Session.setDefault "editing_add_tag", null
 Session.setDefault "comment_preview_mode", false
@@ -186,13 +186,13 @@

 Template.top_content_row2.helpers
   editing_name: ->
-    d = Session.get("editing_name")
+    d = editing_name.get()
     return false unless d?
     return true if @_id is d._id & @collection is d.collection
     false

 Template.top_content_row2.events "click .edit-name": (e, tmpl) ->
-  Session.set "editing_name", tmpl.data
+  editing_name.set(tmpl.data)
   Deps.flush() # update DOM before focus
   ni = tmpl.find("#name-input")
   ni.value = @name
@@ -201,7 +201,7 @@

 Template.top_content_row2.events window.okCancelEvents("#name-input",
   ok: (value,evt) ->
-    if Session.get("editing_name") == null
+    if editing_name.get() == null
       # Hmm, why do we get here? Cancel was clicked.
       return
     coll = window.get_collection_from_name(@collection)
@@ -209,11 +209,11 @@
       $set:
         name: value

-    Session.set "editing_name", null
+    editing_name.set(null)
     return

   cancel: (evt) ->
-    Session.set "editing_name", null
+    editing_name.set(null)
     return
 )


route.getName() -> route.name

route.getName() -> route.name

--- client/helpers.coffee
+++ client/helpers.coffee
@@ -36,7 +36,7 @@
     pattern = route.path()

   else
-    pattern = route.getName()
+    pattern = route.name

   re = new RegExp exp, 'i'


Fix self-drag detection

Fix self-drag detection

--- client/message.coffee
+++ client/message.coffee
@@ -760,12 +760,17 @@
   childMsg = Messages.findOne child
   parentMsg = Messages.findOne parent
   oldParent = findMessageParent child
-  return if parentMsg?._id == oldParent?._id
+  oldIndex = oldParent?.children.indexOf child
+  ## Ignore drag to same place
+  if parentMsg?._id == oldParent?._id
+    return if index == oldIndex or
+      (not index? and oldIndex == oldParent.children.length - 1)
   Modal.show 'messageParentConfirm',
     child: childMsg
     parent: parentMsg
     oldParent: oldParent
     index: index
+    oldIndex: oldIndex

 Template.messageParentConfirm.events
   "click .messageParentButton": (e, t) ->


Bugfix in children ordering

Bugfix in children ordering

--- client/message.coffee
+++ client/message.coffee
@@ -40,11 +40,11 @@
 #Template.registerHelper 'titleOrUntitled', ->
 #  titleOrUntitled @

-@messageChildren = (children) ->
-  return children unless children
-  children = Messages.find _id: $in: children
+@messageChildren = (childrenIds) ->
+  return childrenIds unless childrenIds
+  children = Messages.find _id: $in: childrenIds
                .fetch()
-  children = _.sortBy children, (child) => children.indexOf child._id
+  children = _.sortBy children, (child) => childrenIds.indexOf child._id
   ## Use canSee to properly fake non-superuser mode.
   children = (child for child in children when canSee child)
   for child, index in children


Add tests to verify chai is actually working, not just exported

--- tests/chai.coffee
+++ tests/chai.coffee
@@ -1,16 +1,17 @@
-Tinytest.add "chai - exists", (test) ->
-  test.isNotNull chai, "library chai is not defined"
-  test.notEqual chai, `undefined`, "library chai is not defined"
+foo = 'bar'

-Tinytest.add "chai - exists - assert", (test) ->
-  test.isNotNull assert, "assert is not defined"
-  test.notEqual assert, `undefined`, "assert is not defined"
+Tinytest.add "chai - chai exported", (test) ->
+  test.isTrue chai instanceof Object, "chai is not defined"

-Tinytest.add "chai - exists - expect", (test) ->
-  test.isNotNull expect, "expect is not defined"
-  test.notEqual expect, `undefined`, "expect is not defined"
+Tinytest.add "chai - assert exported", (test) ->
+  test.isTrue assert instanceof Object, "assert is not defined"
+  assert.equal(foo, 'bar', 'foo equal `bar`')

-Tinytest.add "chai - exists - should", (test) ->
-  test.isNotNull should, "library chai is not defined"
-  test.notEqual should, `undefined`, "library chai is not defined"
+Tinytest.add "chai - expect exported", (test) ->
+  test.isTrue expect instanceof Object, "expect is not defined"
+  expect(foo).to.equal('bar')

+Tinytest.add "chai - should exported", (test) ->
+  test.isTrue should instanceof Object, "should is not defined"
+  should()
+  foo.should.equal('bar')


Add `select*Match` convenience functions.

--- tests/test.coffee
+++ tests/test.coffee
@@ -2,6 +2,17 @@
 casper.options.onWaitTimeout = ->
     casper.capture "casperjs.png"
     _dfl_onWaitTimeout()
+
+selectFirstMatch = (select2_selector, search_text) =>
+    selectNthMatch select2_selector, 1, search_text
+
+selectNthMatch = (select2_selector, n, search_text) =>
+    casper.then ->
+        @mouseEvent 'mousedown', "#{select2_selector} .select2-choice"
+        @waitUntilVisible 'input.select2-input'
+    .then ->
+        @sendKeys 'input.select2-input', search_text
+        @mouseEvent 'mouseup', ".select2-match:nth-of-type(#{n})"

 casper.test.begin "Page comes up", (test) ->
     casper.start "index.html", ->
@@ -22,13 +33,11 @@

 casper.test.begin "Basic functionality", (test) ->
     casper.start "index.html", ->
+        # Wait for pilot selector to become visible
         @waitUntilVisible '#rebel-builder .pilot-selector-container .select2-container .select2-choice:first-child'
+
+    selectFirstMatch('#rebel-builder .pilot-selector-container .select2-container:first-of-type', 'Rookie Pilot')
     .then ->
-        @mouseEvent 'mousedown', '#rebel-builder .pilot-selector-container .select2-container .select2-choice:first-child'
-        @waitUntilVisible 'input.select2-input'
-    .then ->
-        @sendKeys 'input.select2-input', 'Rookie Pilot'
-        @mouseEvent 'mouseup', '.select2-match'
         @waitUntilVisible '#rebel-builder .ship-xwing0 .points-display-container'
     .then ->
         test.assertSelectorHasText '#rebel-builder .ship-xwing0 .points-display-container', 21
@@ -38,5 +47,10 @@
         test.assertSelectorHasText '.ship-xwing0 .addon-container .select2-container:nth-of-type(3) .select2-choice', 'No Modification'
         test.assertDoesntExist '.ship-xwing0 .addon-container .select2-container:nth-of-type(4)'

+    selectFirstMatch('.ship-xwing0 .addon-container .select2-container:first-of-type', 'Proton Torpedoes')
+    .then ->
+        test.assertSelectorHasText '#rebel-builder .ship-xwing0 .points-display-container', 25
+        test.assertSelectorHasText '#rebel-builder .total-points', 25
+
     .run ->
         test.done()


Fix a bug

Fix a bug

--- src/6.timer.coffee
+++ src/6.timer.coffee
@@ -67,6 +67,7 @@
         super()
 class Date.Observer extends Date.IntervalTimer
     constructor: (fun) ->
+        super(200)
         @_fun = fun
         @onChange = eventField()
         @onArrive.bind(=>
@@ -77,7 +78,6 @@
             )
             @oldValue = newValue
         )
-        super(200)
     run: ->
         if @getRunning() then return
         @oldValue = @_fun()


Make use of SDK ProgressState enhancement to simplify os download

--- lib/actions/os.coffee
+++ lib/actions/os.coffee
@@ -79,23 +79,9 @@
 				console.info("Destination file: #{outputFile}\n")

 				bar = new visuals.widgets.Progress('Downloading Device OS')
-				time = new Date().getTime()
-				received = 0

 				resin.models.os.download osParams, outputFile, callback, (state) ->
-					return if options.quiet or not state?
-
-					newTime = new Date().getTime()
-					timeDelta = newTime - time
-					receivedDelta = state.received - received
-
-					remaining = state.total - state.received
-					remainingTicks = remaining / receivedDelta
-
-					eta = Math.floor(remainingTicks * timeDelta)
-
-					console.log(bar.tick(state.percent, eta))
-					time = newTime
+					console.log(bar.tick(state.percentage, state.eta))

 		], (error) ->
 			return done(error) if error?


Search completion; do not highlight search terms.

Highlighting the search terms suggests they are in some way contributing
to the match.  They are not, so don't highlight them.

This gets particularly ugly when you have short, single-letter costom
search engines (eg. w), and have all of the "w"s highlighted -- for not
useful reason.

--- background_scripts/completion.coffee
+++ background_scripts/completion.coffee
@@ -29,15 +29,17 @@
   generateHtml: ->
     return @html if @html
     relevancyHtml = if @showRelevancy then "#{@computeRelevancy()}" else ""
+    highlightTerms =
+      if @noHighlightTerms then ((s) -> Utils.escapeHtml s) else ((s) => @highlightTerms Utils.escapeHtml s)
     # NOTE(philc): We're using these vimium-specific class names so we don't collide with the page's CSS.
     @html =
       """

          #{@type}
-         #{@highlightTerms(Utils.escapeHtml(@title))}
+         #{highlightTerms @title}

-        #{@shortenUrl(@highlightTerms(Utils.escapeHtml(@url)))}
+        #{@shortenUrl highlightTerms @url}
         #{relevancyHtml}

       """
@@ -402,8 +404,7 @@

   mkSuggestion: (insertText, args...) ->
     suggestion = new Suggestion args...
-    suggestion.insertText = insertText
-    suggestion
+    extend suggestion, insertText: insertText, noHighlightTerms: true

   # The score is computed in filter() and provided here via suggestion.extraRelevancyData.
   computeRelevancy: (suggestion) -> suggestion.extraRelevancyData