Initialize scroller to *largest* visible scrollable element.

--- content_scripts/scroller.coffee
+++ content_scripts/scroller.coffee
@@ -84,14 +84,17 @@
       element = element.parentElement || document.body
   element

-# On some pages, document.body is not scrollable.  Here, we search the document for the first visible element
-# which does scroll vertically. This is used to initialize activatedElement. See #1358.
+# On some pages, document.body is not scrollable.  Here, we search the document for the largest visible
+# element which does scroll vertically. This is used to initialize activatedElement. See #1358.
 firstScrollableElement = (element=document.body) ->
   if doesScroll element, "y", 1, 1
     element
   else
-    for child in element.children
-      return ele if DomUtils.getVisibleClientRect(child) and ele = firstScrollableElement child
+    children = ({element: child, rect: DomUtils.getVisibleClientRect(child)} for child in element.children)
+    children = children.filter (child) -> child.rect # Filter out non-visible elements.
+    children.map (child) -> child.area = child.rect.width * child.rect.height
+    for child in children.sort((a,b) -> b.area - a.area) # Largest to smallest by visible area.
+      return ele if ele = firstScrollableElement child.element
     null

 checkVisibility = (element) ->


'overpass' helper that does json decoding too

--- src/data.coffee
+++ src/data.coffee
@@ -71,6 +71,13 @@
     else
       deferred.resolve(body)
   return deferred.promise
+
+
+overpass = (q) ->
+  url = "http://overpass-api.de/api/interpreter?data=#{encodeURIComponent(q)}"
+  return httpGet(url)
+    .then (resp) ->
+      return JSON.parse(resp)

 ensureDir = (path) ->
@@ -179,22 +186,21 @@
   .then (rv) ->
     dem = rv
     q = query(bbox)
-    url = "http://overpass-api.de/api/interpreter?data=#{encodeURIComponent(q)}"
     console.log("overpass:", q)
-    retry(RETRY, httpGet, url)
-
-  .then (resp) ->
+    retry(RETRY, overpass, q)
+
+  .then (result) ->
     if SAVERAW?
-      return exec('python -m json.tool', resp).then (prettyResp) ->
-        fs.writeFileSync("#{SAVERAW}/#{region}.json", prettyResp)
-        return resp
+      return exec('python -m json.tool', JSON.stringify(result))
+        .then (prettyResp) ->
+          fs.writeFileSync("#{SAVERAW}/#{region}.json", prettyResp)
+          return result

     else
-      return resp
-
-  .then (resp) ->
-    p = JSON.parse(resp)
-    compileOsm(bbox, p, dem)
+      return result
+
+  .then (result) ->
+    compileOsm(bbox, result, dem)

   .then (db) ->
     ensureDir("build/#{region}")


include roads and rivers in data file

include roads and rivers in data file

--- src/data.coffee
+++ src/data.coffee
@@ -34,11 +34,13 @@

 query = (bbox) ->
   filters = [
-    {t: 'relation', k: 'route',   v: 'hiking'}
-    {t: 'node',     k: 'natural', v: 'saddle'}
-    {t: 'node',     k: 'natural', v: 'peak'}
-    {t: 'node',     k: 'tourism', v: '', op: '~'}
-    {t: 'way',      k: 'tourism', v: '', op: '~'}
+    {t: 'relation', k: 'route',    v: 'hiking'}
+    {t: 'node',     k: 'natural',  v: 'saddle'}
+    {t: 'node',     k: 'natural',  v: 'peak'}
+    {t: 'way',      k: 'highway',  v: '', op: '~'}
+    {t: 'way',      k: 'waterway', v: '', op: '~'}
+    {t: 'node',     k: 'tourism',  v: '', op: '~'}
+    {t: 'way',      k: 'tourism',  v: '', op: '~'}
   ]
   overpassBbox = [bbox[1], bbox[0], bbox[3], bbox[2]]
   item = (f) -> "#{f.t}[\"#{f.k}\"#{f.op or '='}\"#{f.v}\"](#{overpassBbox});"
@@ -86,6 +88,8 @@
   routeIds = d3.set()
   segmentIds = d3.set()
   poi = []
+  roads = []
+  rivers = []

   pos = (id) ->
     node = obj[id]
@@ -118,6 +122,12 @@
     }
     return f

+  road = (obj) ->
+    turf.linestring(pos(n) for n in obj.nodes)
+
+  river = (obj) ->
+    turf.linestring(pos(n) for n in obj.nodes)
+
   route = (relation) ->
     segments = []
     for m in relation.members
@@ -148,9 +158,17 @@
       if o.type == 'node' or o.type == 'way'
         poi.push(tourism(o))

+    if o.type == 'way' and o.tags.highway?
+      roads.push(road(o))
+
+    if o.type == 'way' and o.tags.waterway?
+      rivers.push(river(o))
+
   layers = {
     segments: turf.featurecollection(segment(id) for id in segmentIds.values())
     poi: turf.featurecollection(poi)
+    roads: turf.featurecollection(roads)
+    rivers: turf.featurecollection(rivers)
   }

   return {


Changes on License

Changes on License

--- src/quo.coffee
+++ src/quo.coffee
@@ -1,7 +1,26 @@
 ###
-  QuoJS 2.0
-  (c) 2011, 2012 Javi Jiménez Villar (@soyjavi)
-  http://quojs.tapquo.com
+    QuoJS 2.0
+    http://quojs.tapquo.com
+
+    Copyright (C) 2011,2012 Javi Jiménez Villar (@soyjavi)
+
+    Permission is hereby granted, free of charge, to any person obtaining a
+    copy of this software and associated documentation files (the "Software"),
+    to deal in the Software without restriction, including without limitation
+    the rights to use, copy, modify, merge, publish, distribute, sublicense,
+    and/or sell copies of the Software, and to permit persons to whom the
+    Software is furnished to do so, subject to the following conditions:
+
+    The above copyright notice and this permission notice shall be included in
+    all copies or substantial portions of the Software.
+
+    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+    DEALINGS IN THE SOFTWARE.
 ###

 Quo = (->


#0 #done @功能 增加tag

#0 #done @功能 增加tag

--- common.coffee
+++ common.coffee
@@ -38,7 +38,7 @@

 #检查tag
 exports.checkTag = (tag)->
-  if tag in ['bug', '需求', '支持', '功能'] then tag else '需求'
+  if tag in ['bug', '需求', '支持', '功能', 'project'] then tag else '需求'

 #状态只能是这几种
 exports.checkStatus = (status)->


Reword specs

Reword specs

--- spec/tokenized-buffer-spec.coffee
+++ spec/tokenized-buffer-spec.coffee
@@ -385,7 +385,7 @@
       expect(tokens[1].value).toBeTruthy()
       expect(tokens[2].value).toBe 'xyz'

-  describe "when the grammar tokenized", ->
+  describe "when the grammar is tokenized", ->
     it "emits the `tokenized` event", ->
       editor = null
       tokenizedHandler = jasmine.createSpy("tokenized handler")
@@ -399,7 +399,7 @@
         fullyTokenize(tokenizedBuffer)
         expect(tokenizedHandler.callCount).toBe(1)

-    it "doesn't re-emit the `tokenized` event when a line is edited", ->
+    it "doesn't re-emit the `tokenized` event when it is re-tokenized", ->
       editor = null
       tokenizedHandler = jasmine.createSpy("tokenized handler")


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/collision.coffee
+++ src/collision.coffee
@@ -217,7 +217,7 @@
     rayRectangle: (source, direction, target) ->
       unless target.xw? and target.yw?
         return Collision.rayRectangle source, direction,
-          xw:
+          xw: target.width

       xw = target.xw
       yw = target.yw


collaboration: fix grammatical mistakes in modal

--- client/Main/main.coffee
+++ client/Main/main.coffee
@@ -19,9 +19,9 @@
       cssClass         : "new-kdmodal"
       content          : """

-
Thanks for trying out bleeding 'edge' of Koding features. Most of the new features here are experimental and hence prone to breaking.

+
Thanks for trying out the bleeding 'edge' of Koding features. Most of the new features here are experimental and hence prone to breaking.

-
If you run into problems, we'd love to hear what we wrong and how to reproduce the error. Please send an email to edge@koding.com. Note even though this environment is experimental, your vms are the same production vms as on koding.com.

+
If you run into problems, we'd love to hear what went wrong and how to reproduce the error. Please send an email to edge@koding.com. Note that even though this environment is experimental, your vms are the same production vms as on koding.com.

By continuing you acknowledge the experimental nature of this environment.


Docs for up.feedback

Docs for up.feedback

--- lib/assets/javascripts/unpoly/feedback.coffee
+++ lib/assets/javascripts/unpoly/feedback.coffee
@@ -155,8 +155,9 @@
     findActivatableArea(element).classList.add(CLASS_ACTIVE)

   ###**
-  Links that are currently [loading through Unpoly](/form-up-target)
+  Links that are currently [loading through Unpoly](/a-up-follow)
   are assigned the `up-active` class automatically.
+
   Style `.up-active` in your CSS to improve the perceived responsiveness
   of your user interface.


updated bitbucket api to sand-bucket

updated bitbucket api to sand-bucket

--- src/libs/bitbucket.coffee
+++ src/libs/bitbucket.coffee
@@ -6,7 +6,7 @@

 class BitBucket
   constructor: (credentials, repos = "") ->
-    bitBucket = require 'bitbucket-api'
+    bitBucket = require 'sand-bucket'
     @client = bitBucket.createClient(credentials)
     @repositories = repos


Precompute sequence of triangles for Delaunay.

--- examples/delaunay.coffee
+++ examples/delaunay.coffee
@@ -227,12 +227,14 @@
       @sites__         = args[1] || new HashSet()
       @children__      = args[2] || new HashMap()

+      @triangles__  = seq.select @triangulation__, (t) ->
+        seq.forall t, (p) -> not p.isInfinite()
+
     # The method `toSeq` returns the proper (non-virtual) triangles contained
     # in this triangulation as a lazy sequence. It does so by removing any
     # triangles from the underlying triangulation object which contain a
     # virtual vertex.
-    toSeq: ->
-      seq.select @triangulation__, (t) -> seq.forall t, (p) -> not p.isInfinite()
+    toSeq: -> @triangles__

     # The method `third` finds the unique third vertex forming a triangle with
     # the two given ones in the given orientation, if any.


raspberrypi.coffee: Updating links to getting started guide

Signed-off-by: Gareth Davies <2f9181a725196cd0a5e07c6a6e161d83fcc71157@balena.io>

--- raspberrypi.coffee
+++ raspberrypi.coffee
@@ -18,9 +18,9 @@

 	instructions: commonImg.instructions
 	gettingStartedLink:
-		windows: 'https://docs.resin.io/raspberrypi/nodejs/getting-started/#adding-your-first-device'
-		osx: 'https://docs.resin.io/raspberrypi/nodejs/getting-started/#adding-your-first-device'
-		linux: 'https://docs.resin.io/raspberrypi/nodejs/getting-started/#adding-your-first-device'
+		windows: 'https://www.balena.io/docs/learn/getting-started/raspberry-pi/nodejs/'
+		osx: 'https://www.balena.io/docs/learn/getting-started/raspberry-pi/nodejs/'
+		linux: 'https://www.balena.io/docs/learn/getting-started/raspberry-pi/nodejs/'
 	supportsBlink: true

 	options: [ networkOptions.group ]


profile :indentation fix

profile :indentation fix

--- server/lib/crawler/staticpages/profile.coffee
+++ server/lib/crawler/staticpages/profile.coffee
@@ -152,12 +152,12 @@
 putContent = (account, sUpdates)->
   getGraphMeta  = require './graphmeta'
   {profile:{nickname, firstName, lastName, about}} = account if account
-  nickname or= "A koding nickname"
+  nickname  or= "A koding nickname"
   firstName or= "a koding "
-  lastName or= "user"
-  about    or= ""
-
-  numberOfLikes = if account.counts.likes then account.counts.likes else "0"
+  lastName  or= "user"
+  about     or= ""
+
+  numberOfLikes     = if account.counts.likes     then account.counts.likes     else "0"
   numberOfFollowers = if account.counts.followers then account.counts.followers else "0"
   numberOfFollowing = if account.counts.following then account.counts.following else "0"
   imgURL = "https://gravatar.com/avatar/#{account.profile.hash}?size=90&d=https%3A%2F%2Fapi.koding.com%2Fimages%2Fdefaultavatar%2Fdefault.avatar.90.png"


throw error when connect returns error

throw error when connect returns error

--- src/sdk.coffee
+++ src/sdk.coffee
@@ -54,13 +54,10 @@
     popupLocation = popupWindow.location.toString()
     uri = new SC.URI(popupLocation, {decodeQuery: true, decodeFragment: true})
     error = uri.query.error || uri.fragment.error
-
-    if error == "redirect_uri_mismatch"
-      popupWindow.document.body.innerHTML = "
The redirect URI '"+ popupLocation +"' you specified does not match the one that is configured in your SoundCloud app.
 You can fix this in your app settings on SoundCloud"
-      return false
-
     popupWindow.close()
+
     if error
+      throw new Error("SC OAuth2 Error: " + uri.query.error_description)
       SC._trigger("error", error)
     else
       SC.accessToken(uri.fragment.access_token);


support denormal numbers

support denormal numbers

--- src/constant_pool.coffee
+++ src/constant_pool.coffee
@@ -93,7 +93,10 @@
     sign = (uint32 &       0x80000000)>>>31
     exponent = (uint32 &   0x7F800000)>>>23
     significand = uint32 & 0x007FFFFF
-    value = Math.pow(-1,sign)*(1+significand*Math.pow(2,-23))*Math.pow(2,exponent-127)
+    if exponent is 0  # we must denormalize!
+      value = Math.pow(-1,sign)*significand*Math.pow(2,-149)
+    else
+      value = Math.pow(-1,sign)*(1+significand*Math.pow(2,-23))*Math.pow(2,exponent-127)
     float = new @ value
     return [float, 1, bytes_array]

@@ -117,7 +120,10 @@
     sign     = (uint32_a & 0x80000000)>>>31
     exponent = (uint32_a & 0x7FF00000)>>>20
     significand = util.lshift(uint32_a & 0x000FFFFF, 32) + uint32_b
-    value = Math.pow(-1,sign)*(1+significand*Math.pow(2,-52))*Math.pow(2,exponent-1023)
+    if exponent is 0  # we must denormalize!
+      value = Math.pow(-1,sign)*significand*Math.pow(2,-1074)
+    else
+      value = Math.pow(-1,sign)*(1+significand*Math.pow(2,-52))*Math.pow(2,exponent-1023)
     double = new @ value
     return [double, 2, bytes_array]


fixing float parsing

fixing float parsing

--- src/constant_pool.coffee
+++ src/constant_pool.coffee
@@ -37,7 +37,7 @@
   sign = (uint32 &       0x80000000)>>>31
   exponent = (uint32 &   0x7F800000)>>>23
   significand = uint32 & 0x007FFFFF
-  single = Math.pow(-1,sign)*significand*Math.pow(2,-23)*Math.pow(2,exponent-127)
+  single = Math.pow(-1,sign)*(1+significand*Math.pow(2,-23))*Math.pow(2,exponent-127)
   return [single,bytes_array]

 class root.ConstantPool


atom prefs update

atom prefs update

--- dotfiles/atom/config.cson
+++ dotfiles/atom/config.cson
@@ -13,6 +13,7 @@
     'softTabs': false
     'fontFamily': 'Menlo'
     'fontSize': 12
+    'invisibles': {}
   'core':
     'projectHome': '/Users/MoOx/Sync/Development'
     'disabledPackages': [
@@ -42,3 +43,5 @@
     'microBreakIntervalInSeconds': 1200
   'linter':
     'showStatusBarWhenCursorIsInErrorRange': true
+  'atom-beautify':
+    '_analyticsUserId': 'bcb0826f-cb01-4339-bb49-4f4de6116860'


Server: fix create team flow

Server: fix create team flow

--- servers/lib/server/handlers/createteam.coffee
+++ servers/lib/server/handlers/createteam.coffee
@@ -25,9 +25,10 @@
     alreadyMember
   } = body

-  redirect ?= '/'
-  context   = { group: slug }
-  clientId  = getClientId req, res
+  redirect     ?= '/'
+  context       = { group: slug }
+  clientId      = getClientId req, res
+  alreadyMember = alreadyMember is 'true'

   return handleClientIdNotFound res, req  unless clientId

@@ -95,7 +96,7 @@
           # handle the request with an HTTP redirect:
           res.redirect 301, redirect

-    if alreadyMember is 'true'
+    if alreadyMember
     then JUser.login client.sessionToken, body, createGroup
     else JUser.convert client, body, createGroup


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/expirable.coffee
+++ src/expirable.coffee
@@ -3,8 +3,6 @@
     duration: -1

   self.bind "afterUpdate", ->
-    # this check occurs before I.age is updated
-    # TODO: fix off by 1 error
     if I.duration != -1 && I.age >= I.duration
       I.active = false


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/expirable.coffee
+++ src/expirable.coffee
@@ -8,7 +8,7 @@

   self.bind "update", ->
     if I.fadeOut
-      log I.alpha
+      log I.age
       I.alpha = 1 - (I.age + 1 / I.duration)

     if I.duration != -1 && I.age + 1 >= I.duration


Have default role for clients.

Have default role for clients.

--- app/schemas.coffee
+++ app/schemas.coffee
@@ -32,6 +32,7 @@
     type:   "selection"
     opts:   ["User", "Admin", "Superadmin"]
     desc:   "Rolle"
+    value:  "User"
   ]

   group: [


see how much memory absurd history uses up

--- src/models/channel.coffee
+++ src/models/channel.coffee
@@ -21,7 +21,7 @@

   addMessage: (msg) ->
     message = new Message(@, msg)
-    @messages.shift() if @messages.length > 50
+    @messages.shift() if @messages.length > 250
     @messages.push(message)

 module.exports = Channel


fix using attribution to follow cursors

fix using attribution to follow cursors

--- src/modules/cursor.coffee
+++ src/modules/cursor.coffee
@@ -103,7 +103,8 @@
     this.initListeners()

   initListeners: ->
-    @editor.on(Scribe.Editor.events.TEXT_CHANGE, (delta) =>
+    @editor.applyDelta = _.wrap(@editor.applyDelta, (applyDelta, delta) =>
+      applyDelta.call(@editor, delta)
       _applyDelta.call(this, delta)
     )


Start autopager on mount

Start autopager on mount

--- resources/assets/coffee/react/beatmaps/paginator.coffee
+++ resources/assets/coffee/react/beatmaps/paginator.coffee
@@ -23,6 +23,7 @@
   componentDidMount: ->
     @throttledAutoPagerOnScroll = _.throttle(@autoPagerOnScroll, 500)
     $(window).on 'scroll.paginator', @throttledAutoPagerOnScroll
+    Timeout.set 0, @throttledAutoPagerOnScroll

   componentWillUnmount: ->


Only change sticky state if actually changed

--- resources/assets/coffee/react/profile-page/extra.coffee
+++ resources/assets/coffee/react/profile-page/extra.coffee
@@ -109,7 +109,8 @@

   _tabsStick: (_e, target) =>
-    @setState tabsSticky: (target == 'profile-extra-tabs')
+    newState = (target == 'profile-extra-tabs')
+    @setState(tabsSticky: newState) if newState != @state.tabsSticky

   updateOrder: (element) =>
     oldOrder = @state.profileOrder


Fix an error where title details sometimes fail to show cover art thumbnail

--- src/opac/edit_hold.coffee
+++ src/opac/edit_hold.coffee
@@ -110,7 +110,7 @@
 				# > FIXME: empty divs may be left behind

 			# We add the given thumbnail image.
-			if $img.get(0).naturalHeight > 0
+			if $img.get(0)?.naturalHeight > 0
 				$('li', @).prepend $img.prop('title', '')

 			@listview 'refresh'


Debug room for full stack

Debug room for full stack

--- src/hubot-cah.coffee
+++ src/hubot-cah.coffee
@@ -28,6 +28,7 @@
   robot.error (err, res) ->
     if res?
       res.reply "Someone broke me again: #{err.message}"
+      robot.messageRoom "#debug", "CAH Error:\n\n#{err.message}\n\n===\n\n#{err.stack}"
     robot.logger.error err.message
     robot.logger.error err.stack
     robot.logger.error JSON.stringify(robot.brain.data.cah, null, '\t')
@@ -50,6 +51,7 @@
     delete robot.brain.data.cah
     global.game = new Game(robot)
     start(robot, game)
+    res.reply "💥 the game has been reset 💥"

   robot.hearspond /cah help$/i, (res) ->
     res.send helpSummary


Updated docs

Updated docs

--- src/hubot-cah.coffee
+++ src/hubot-cah.coffee
@@ -29,14 +29,16 @@
 helpSummary += "\ncah help - List cah commands"
 helpSummary += "\ncah join - Add yourself to the game"
 helpSummary += "\ncah leave - Remove yourself as an active player"
+helpSummary += "\ncah kick - Remove another active player (czar only)"
 helpSummary += "\ncah czar - Display name of the current Card Czar"
 helpSummary += "\ncah players - List active players"
 helpSummary += "\ncah leaders - Top five score leaders"
 helpSummary += "\ncah score - Display your score"
 helpSummary += "\ncah hand - List cards in your hand"
-helpSummary += "\ncah play <#> <#> ... - Indicate white cards to be submitted as an answer, where # indicates card index in hand and amount of white cards submitted corresponds to the amount required by the current black card"
-helpSummary += "\ncah answers - List the current white card submissions for the current black card (Card Czar only)"
-helpSummary += "\ncah choose <#> - Choose a winning answer (Card Czar only)"
+helpSummary += "\ncah play <#> <#> ... - Indicate white cards to be submitted as an answer, applied to black card blanks in order"
+helpSummary += "\ncah answers - List the submitted white cards once all have been submitted"
+helpSummary += "\ncah answers! - List the submitted white cards anytime (czar only)"
+helpSummary += "\ncah choose <#> - Choose a winning answer (czar only)"
 helpSummary += "\ncah status - Display summary of current game"
 helpSummary += "\ncah skip - Discard current black card and assign a new Card Czar"


robot can accept invite command in irc

robot can accept invite command in irc

--- src/hubot/irc.coffee
+++ src/hubot/irc.coffee
@@ -94,6 +94,10 @@
     bot.addListener 'kick', (channel, who, _by, reason) ->
         console.log('%s was kicked from %s by %s: %s', who, channel, _by, reason)

+    bot.addListener 'invite', (channel, from) ->
+      console.log('%s invite you to join %s', from, channel)
+      bot.join channel
+
     @bot = bot

 module.exports = IrcBot


Fix the web ui to know about the new location of the server.

--- admin/static/coffee/dataexplorer.coffee
+++ admin/static/coffee/dataexplorer.coffee
@@ -538,11 +538,7 @@
             log_initial '(initializing) dataexplorer view:'

             host = window.location.hostname
-            if port is ''
-                port = 13457
-            else
-                port = parseInt(window.location.port)-1000+13457
-            window.conn = new rethinkdb.net.HttpConnection 'http://'+host+':'+port , ''
+            window.conn = new rethinkdb.net.HttpConnection 'http://'+host+':'+window.location.port+'/ajax/reql'
             window.r = rethinkdb.query
             window.R = r.R


Fix behavior when no suggestions are found

--- admin/static/coffee/dataexplorer.coffee
+++ admin/static/coffee/dataexplorer.coffee
@@ -634,10 +634,9 @@
                                 else
                                     cached_query = @query_first_part+@written_suggestion+@query_last_part
                                 if cached_query isnt @codemirror.getValue() # We fired a keydown tab before a keyup, so our suggestions are not up to date
-                                    new_element = @codemirror.getValue().slice @query_first_part.length, @codemirror.getValue().length-@query_last_part.length
-                                    regex = @create_safe_regex new_element
+                                    @current_element = @codemirror.getValue().slice @query_first_part.length, @codemirror.getValue().length-@query_last_part.length
+                                    regex = @create_safe_regex @current_element
                                     new_suggestions = []
-                                    console.log '+++++'
                                     new_highlighted_suggestion = -1
                                     for suggestion, index in @current_suggestions
                                         if index < @current_highlighted_suggestion
@@ -646,13 +645,15 @@
                                             new_suggestions.push suggestion
                                     @current_suggestions = new_suggestions
                                     @current_highlighted_suggestion = new_highlighted_suggestion
-                                    @.$('.suggestion_name_list').empty()
-                                    for suggestion, i in @current_suggestions
-                                        @.$('.suggestion_name_list').append @template_suggestion_name
-                                            id: i
-                                            suggestion: suggestion
-                                    @ignored_next_keyup = true
-                                    @current_element = new_element
+                                    if @current_suggestions.length > 0
+                                        @.$('.suggestion_name_list').empty()
+                                        for suggestion, i in @current_suggestions
+                                            @.$('.suggestion_name_list').append @template_suggestion_name
+                                                id: i
+                                                suggestion: suggestion
+                                        @ignored_next_keyup = true
+                                    else
+                                        @hide_suggestion_and_description()

                                 # Switch throught the suggestions


[Artist] Adds artist_page to running ab tests

--- src/desktop/components/split_test/running_tests.coffee
+++ src/desktop/components/split_test/running_tests.coffee
@@ -15,10 +15,18 @@
 # module.exports = {}

 module.exports = {
- onboarding_test:
-   key: 'onboarding_test'
-   outcomes:
-     control: 50
-     experiment: 50
-   edge: 'experiment'
+  onboarding_test:
+    key: 'onboarding_test'
+    outcomes:
+      control: 50
+      experiment: 50
+    edge: 'experiment'
+  artist_page_variants:
+    key: 'artist_page_variants'
+    outcomes:
+      control: 25
+      no_info: 25
+      no_header: 25
+      no_info_header: 25
+    edge: 'no_info_header'
 }


:lipstick:

:lipstick:

--- spec/find-view-spec.coffee
+++ spec/find-view-spec.coffee
@@ -90,8 +90,8 @@
       editor.setCursorBufferPosition([2,0])
       findView.findEditor.setText 'notinthefilebro'
       findView.findEditor.focus()
+
       findView.trigger 'core:confirm'
-
       expect(editor.getCursorBufferPosition()).toEqual [2,0]
       expect(shell.beep).toHaveBeenCalled()
       expect(findView.find(':focus')).toExist()


:white_check_mark: for SurroundWord, DeleteSurroundAnyPair, ChangeSurroundAnyPair

--- spec/operators-spec.coffee
+++ spec/operators-spec.coffee
@@ -1294,11 +1294,6 @@
             line )
           """
         cursorBuffer: [0, 0]
-
-    # [TODO]
-    describe 'surround-word', ->
-    describe 'delete surround-any-pair', ->
-    describe 'change surround-any-pair', ->

     describe 'surround', ->
       it "surround text object with ( and repeatable", ->
@@ -1368,6 +1363,79 @@
             !orange!
             """

+    describe 'surround-word', ->
+      it "surround a word with ( and repeatable", ->
+        ensure ['gsw', char: '('],
+          text: "(apple)\npairs: [brackets]\npairs: [brackets]\n( multi\n  line )"
+          cursor: [0, 0]
+        ensure 'j.',
+          text: "(apple)\n(pairs): [brackets]\npairs: [brackets]\n( multi\n  line )"
+      it "surround a word with { and repeatable", ->
+        ensure ['gsw', char: '{'],
+          text: "{apple}\npairs: [brackets]\npairs: [brackets]\n( multi\n  line )"
+          cursor: [0, 0]
+        ensure 'j.',
+          text: "{apple}\n{pairs}: [brackets]\npairs: [brackets]\n( multi\n  line )"
+
+    describe 'delete surround-any-pair', ->
+      beforeEach ->
+        set
+          text: """
+            apple
+            (pairs: [brackets])
+            {pairs "s" [brackets]}
+            ( multi
+              line )
+            """
+          cursor: [1, 9]
+
+        atom.keymaps.add "test",
+          'atom-text-editor.vim-mode:not(.insert-mode)':
+            'd s': 'vim-mode:delete-surround-any-pair'
+
+      it "delete surrounded any pair found and repeatable", ->
+        ensure 'ds',
+          text: 'apple\n(pairs: brackets)\n{pairs "s" [brackets]}\n( multi\n  line )'
+        ensure '.',
+          text: 'apple\npairs: brackets\n{pairs "s" [brackets]}\n( multi\n  line )'
+
+      it "delete surrounded any pair found with skip pair out of cursor and repeatable", ->
+        set cursor: [2, 14]
+        ensure 'ds',
+          text: 'apple\n(pairs: [brackets])\n{pairs "s" brackets}\n( multi\n  line )'
+        ensure '.',
+          text: 'apple\n(pairs: [brackets])\npairs "s" brackets\n( multi\n  line )'
+        ensure '.', # do nothing any more
+          text: 'apple\n(pairs: [brackets])\npairs "s" brackets\n( multi\n  line )'
+
+      it "delete surrounded chars expanded to multi-line", ->
+        set cursor: [3, 1]
+        ensure 'ds',
+          text: 'apple\n(pairs: [brackets])\n{pairs "s" [brackets]}\n multi\n  line '
+
+    describe 'change surround-any-pair', ->
+      beforeEach ->
+        set
+          text: """
+            (apple)
+            (grape)
+            <lemmon>
+            {orange}
+            """
+          cursor: [0, 1]
+
+        atom.keymaps.add "test",
+          'atom-text-editor.vim-mode:not(.insert-mode)':
+            'c s': 'vim-mode:change-surround-any-pair'
+
+      it "change any surrounded pair found and repeatable", ->
+        ensure ['cs', char: '<'],
+          text: "<apple>\n(grape)\n<lemmon>\n{orange}"
+        ensure 'j.',
+          text: "<apple>\n<grape>\n<lemmon>\n{orange}"
+        ensure 'jj.',
+          text: "<apple>\n<grape>\n<lemmon>\n<orange>"
+
   describe "the i keybinding", ->
     beforeEach ->
       set


Add tests for goto (G) line motions with delete, yank, and change

--- spec/operators-spec.coffee
+++ spec/operators-spec.coffee
@@ -328,6 +328,45 @@
             keydown('k')
             expect(editor.getText()).toBe("12345")

+    describe "when followed by a G", ->
+      beforeEach ->
+        originalText = "12345\nabcde\nABCDE"
+        editor.setText(originalText)
+
+      describe "on the beginning of the second line", ->
+        it "deletes the bottom two lines", ->
+          editor.setCursorScreenPosition([1,0])
+          keydown('d')
+          keydown('G', shift: true)
+          expect(editor.getText()).toBe("12345\n")
+
+      describe "on the middle of the second line", ->
+        it "deletes the bottom two lines", ->
+          editor.setCursorScreenPosition([1,2])
+          keydown('d')
+          keydown('G', shift: true)
+          expect(editor.getText()).toBe("12345\n")
+
+    describe "when followed by a goto line G", ->
+      beforeEach ->
+        originalText = "12345\nabcde\nABCDE"
+        editor.setText(originalText)
+
+      describe "on the beginning of the second line", ->
+        it "deletes the bottom two lines", ->
+          editor.setCursorScreenPosition([1,0])
+          keydown('d')
+          keydown('2')
+          keydown('G', shift: true)
+          expect(editor.getText()).toBe("12345\nABCDE")
+
+      describe "on the middle of the second line", ->
+        it "deletes the bottom two lines", ->
+          editor.setCursorScreenPosition([1,2])
+          keydown('d')
+          keydown('2')
+          keydown('G', shift: true)
+          expect(editor.getText()).toBe("12345\nABCDE")

   describe "the D keybinding", ->
     beforeEach ->
@@ -401,6 +440,50 @@
         keydown('r', ctrl: true)
         expect(editor.getText()).toBe "12345\nfg\nABCDE"

+    describe "when followed by a G", ->
+      beforeEach ->
+        originalText = "12345\nabcde\nABCDE"
+        editor.setText(originalText)
+
+      describe "on the beginning of the second line", ->
+        it "deletes the bottom two lines", ->
+          editor.setCursorScreenPosition([1,0])
+          keydown('c')
+          keydown('G', shift: true)
+          keydown('escape');
+          expect(editor.getText()).toBe("12345\n");
+
+      describe "on the middle of the second line", ->
+        it "deletes the bottom two lines", ->
+          editor.setCursorScreenPosition([1,2])
+          keydown('c')
+          keydown('G', shift: true)
+          keydown('escape');
+          expect(editor.getText()).toBe("12345\n");
+
+    describe "when followed by a goto line G", ->
+      beforeEach ->
+        originalText = "12345\nabcde\nABCDE"
+        editor.setText(originalText)
+
+      describe "on the beginning of the second line", ->
+        it "deletes the bottom two lines", ->
+          editor.setCursorScreenPosition([1,0])
+          keydown('c')
+          keydown('2')
+          keydown('G', shift: true)
+          keydown('escape');
+          expect(editor.getText()).toBe("12345\n\nABCDE")
+
+      describe "on the middle of the second line", ->
+        it "deletes the bottom two lines", ->
+          editor.setCursorScreenPosition([1,2])
+          keydown('c')
+          keydown('2')
+          keydown('G', shift: true)
+          keydown('escape');
+          expect(editor.getText()).toBe("12345\n\nABCDE")
+
   describe "the C keybinding", ->
     beforeEach ->
       editor.getBuffer().setText("012\n")
@@ -495,6 +578,50 @@

       it "leaves the cursor at the starting position", ->
         expect(editor.getCursorScreenPosition()).toEqual [0, 4]
+
+    describe "when followed by a G", ->
+      beforeEach ->
+        originalText = "12345\nabcde\nABCDE"
+        editor.setText(originalText)
+
+      describe "on the beginning of the second line", ->
+        it "deletes the bottom two lines", ->
+          editor.setCursorScreenPosition([1,0])
+          keydown('y')
+          keydown('G', shift: true)
+          keydown('P', shift: true)
+          expect(editor.getText()).toBe("12345\nabcde\nABCDE\nabcde\nABCDE")
+
+      describe "on the middle of the second line", ->
+        it "deletes the bottom two lines", ->
+          editor.setCursorScreenPosition([1,2])
+          keydown('y')
+          keydown('G', shift: true)
+          keydown('P', shift: true)
+          expect(editor.getText()).toBe("12345\nabcde\nABCDE\nabcde\nABCDE")
+
+    describe "when followed by a goto line G", ->
+      beforeEach ->
+        originalText = "12345\nabcde\nABCDE"
+        editor.setText(originalText)
+
+      describe "on the beginning of the second line", ->
+        it "deletes the bottom two lines", ->
+          editor.setCursorScreenPosition([1,0])
+          keydown('y')
+          keydown('2')
+          keydown('G', shift: true)
+          keydown('P', shift: true)
+          expect(editor.getText()).toBe("12345\nabcde\nabcde\nABCDE")
+
+      describe "on the middle of the second line", ->
+        it "deletes the bottom two lines", ->
+          editor.setCursorScreenPosition([1,2])
+          keydown('y')
+          keydown('2')
+          keydown('G', shift: true)
+          keydown('P', shift: true)
+          expect(editor.getText()).toBe("12345\nabcde\nabcde\nABCDE")

   describe "the yy keybinding", ->
     describe "on a single line file", ->


Added coffee script for date picker to export based on dates and added container property to tooltip.

--- app/assets/javascripts/deal_redemptions/admin/import.js.coffee
+++ app/assets/javascripts/deal_redemptions/admin/import.js.coffee
@@ -1,8 +1,37 @@
 $(document).ready ->
   $('i.csv-tip').tooltip({
+    container: 'body'
     html: true
     placement: 'right'
     trigger: 'hover'
     template: '<img src="/assets/admin/csv-demo.jpg" alt="" />'
     viewport: 'col-md-4'
-  })+  })
+
+  nowTemp = new Date()
+  now = new Date(nowTemp.getFullYear(), nowTemp.getMonth(), nowTemp.getDate(), 0, 0, 0, 0)
+
+  checkin = $('input#exportStartDate').datepicker({
+    format: 'mm-dd-yyyy'
+    onRender: (date) ->
+      return date.valueOf() < now.valueOf() ? 'disabled' : ''
+
+  }).on('changeDate', (ev) ->
+    if ev.date.valueOf() > checkout.date.valueOf()
+      newDate = new Date(ev.date)
+      newDate.setDate(newDate.getDate() + 1)
+      checkout.setValue(newDate)
+
+    checkin.hide()
+
+    $('input#exportEndDate').focus()
+  ).data('datepicker')
+
+  checkout = $('#exportEndDate').datepicker({
+    format: 'mm-dd-yyyy'
+    onRender: (date) ->
+      return date.valueOf() <= checkin.date.valueOf() ? 'disabled' : ''
+
+  }).on('changeDate', (ev) ->
+    checkout.hide()
+  ).data('datepicker')


Corrected stupid method name typo :expressionless:

--- src/models.js.coffee
+++ src/models.js.coffee
@@ -125,13 +125,13 @@
     @up_voted = false
     @metadata = {}
     this.humanize_tags()
-    this.injest_metadata(api_response)
+    this.ingest_metadata(api_response)

   # Checks for the presence of metadata in a given object and caches it
   #
   # @param [Object] data the data, usually from an API response
   #
-  injest_metadata: (data) ->
+  ingest_metadata: (data) ->
     reserved_fields = ['id', 'title', 'tags', 'community_tags']
     for field, value of data
       if reserved_fields.indexOf(field) == -1
@@ -225,7 +225,7 @@
     else
       _fish = this
       @pond.fishpond.connection.request ['ponds', @pond.id, 'fish', this.id], (response) ->
-        _fish.injest_metadata(response)
+        _fish.ingest_metadata(response)
         callback(_fish)

 class Fishpond::Result


Unit tests refactor

Unit tests refactor

--- specs/somewhereSpec.coffee
+++ specs/somewhereSpec.coffee
@@ -1,88 +1,85 @@
 'use strict'

 expect = require('chai').expect
-db = require '../index'
+DB = require '../index'

 describe "JSONdb module", ->
-  beforeEach ->
-    db.connect('./mochaTest.json')
-
-  it "should save and write an item", ->
+  describe "Check findOne, find, update and remove methods", ->
+    db = new DB './mochaTest.json'
     user =
       username: 'dreyacosta'
       name: 'David'
       blog: 'dreyacosta.com'
       source: 'twitter'
     item = db.save 'users', user
-    expect(item.username).to.equal 'dreyacosta'

-  it "should find one", ->
-    item = db.findOne 'users', { username: 'dreyacosta' }
-    expect(item.name).to.equal 'David'
+    it "should find one", ->
+      item = db.findOne 'users', { username: 'dreyacosta' }
+      expect(item.name).to.equal 'David'

-  it "should find all", ->
-    user =
-      username: 'drey'
-      name: 'David'
-      blog: 'drey.com'
-      source: 'twitter'
-    item = db.save 'users', user
-    items = db.find 'users', { name: 'David' }
-    expect(items.length).to.equal 2
+    it "should find all", ->
+      user =
+        username: 'drey'
+        name: 'David'
+        blog: 'drey.com'
+        source: 'twitter'
+      item = db.save 'users', user
+      items = db.find 'users', { name: 'David' }
+      expect(items.length).to.equal 2

-  it "should return an empty object if no match on find one", ->
-    item = db.findOne 'users', { name: 'Paul' }
-    expect(typeof item).to.equal 'object'
-    expect(Object.keys(item).length).to.equal 0
+    it "should return an empty object if no match on find one", ->
+      item = db.findOne 'users', { name: 'Paul' }
+      expect(typeof item).to.equal 'object'
+      expect(Object.keys(item).length).to.equal 0

-  it "should return an empty array if no match on find all", ->
-    items = db.find 'users', { name: 'Paul' }
-    expect(items.push).to.be.a 'function'
-    expect(items.length).to.equal 0
+    it "should return an empty array if no match on find all", ->
+      items = db.find 'users', { name: 'Paul' }
+      expect(items.push).to.be.a 'function'
+      expect(items.length).to.equal 0

-  it "should return a pure object when find one", ->
-    item = db.findOne 'users', { username: 'dreyacosta' }
-    expect(item.name).to.equal 'David'
-    item.name = 'Mike'
-    item = db.findOne 'users', { username: 'dreyacosta' }
-    expect(item.name).to.equal 'David'
+    it "should return a pure object when find one", ->
+      item = db.findOne 'users', { username: 'dreyacosta' }
+      expect(item.name).to.equal 'David'
+      item.name = 'Mike'
+      item = db.findOne 'users', { username: 'dreyacosta' }
+      expect(item.name).to.equal 'David'

-  it "should return a pure array when find all", ->
-    user =
-      username: 'pirish'
-      name: 'Paul'
-      blog: 'pirish.com'
-      source: 'twitter'
-    item = db.save 'users', user
-    items = db.find 'users', { source: 'twitter' }
-    expect(items.length).to.equal 3
-    items[0].source = 'facebook'
-    items = db.find 'users', { source: 'twitter' }
-    expect(items.length).to.equal 3
+    it "should return a pure array when find all", ->
+      user =
+        username: 'pirish'
+        name: 'Paul'
+        blog: 'pirish.com'
+        source: 'twitter'
+      item = db.save 'users', user
+      items = db.find 'users', { source: 'twitter' }
+      expect(items.length).to.equal 3
+      items[0].source = 'facebook'
+      items = db.find 'users', { source: 'twitter' }
+      expect(items.length).to.equal 3

-  it "should return a pure object when update", ->
-    item = db.findOne 'users', { username: 'dreyacosta' }
-    item = db.update 'users', item.id, { name: 'David Rey' }
-    expect(item.name).to.equal 'David Rey'
-    item.name = 'Mike'
-    item = db.findOne 'users', { username: 'dreyacosta' }
-    expect(item.name).to.equal 'David Rey'
+    it "should return a pure object when update", ->
+      item = db.findOne 'users', { username: 'dreyacosta' }
+      item = db.update 'users', item.id, { name: 'David Rey' }
+      expect(item.name).to.equal 'David Rey'
+      item.name = 'Mike'
+      item = db.findOne 'users', { username: 'dreyacosta' }
+      expect(item.name).to.equal 'David Rey'

-  it "should update item", ->
-    data =
-      country: 'Spain'
-    item = db.findOne 'users', { username: 'drey' }
-    item = db.update 'users', item.id, data
-    expect(item.country).to.equal 'Spain'
+    it "should update item", ->
+      data =
+        country: 'Spain'
+      item = db.findOne 'users', { username: 'drey' }
+      item = db.update 'users', item.id, data
+      expect(item.country).to.equal 'Spain'

-  it "should remove item from a collection", ->
-    item = db.findOne 'users', { username: 'drey' }
-    expect(item.username).to.equal 'drey'
-    result = db.remove 'users', item.id
-    expect(result).to.equal true
-    item = db.findOne 'users', { id: item.id }
-    expect(typeof item).to.equal 'object'
-    expect(Object.keys(item).length).to.equal 0
+    it "should remove item from a collection", ->
+      item = db.findOne 'users', { username: 'drey' }
+      expect(item.username).to.equal 'drey'
+      result = db.remove 'users', item.id
+      expect(result).to.equal true
+      item = db.findOne 'users', { id: item.id }
+      expect(typeof item).to.equal 'object'
+      expect(Object.keys(item).length).to.equal 0

-  it "should clear the database file", ->
-    db.clear()+    it "should clear the database file", ->
+      do db.clear


Added unit test for remove item that not exist

--- specs/somewhereSpec.coffee
+++ specs/somewhereSpec.coffee
@@ -144,6 +144,8 @@
       expect(Object.keys(item).length).to.equal 0

     it "should not remove an item that not exist", ->
+      item = db.remove 'users', '12345'
+      expect(item).to.equal false

     it "should clear the database file", ->
       do db.clear


Adjusted loader delegate file name.

Adjusted loader delegate file name.

--- src/moka/moka.coffee
+++ src/moka/moka.coffee
@@ -205,7 +205,7 @@
       throw "Can't compile '#{name}',\n#{e}"

   loaderFileContent: (options = {compiled:yes}) ->
-    mainMokaPath = path.join(@root, 'src/loader.moka')
+    mainMokaPath = path.join(@root, 'src/loaderDelegate.moka')
     if path.existsSync mainMokaPath
       code = "namespace = '#{@config().namespace}'\nprivateNamespace = '#{@privateNamespace()}'" + \
       fs.readFileSync(mainMokaPath, 'utf-8') + '\n' + \
@@ -213,7 +213,7 @@
       code = @compile 'loader', code if options.compiled
       code
     else
-      throw "Can't find loader.moka."
+      throw "Can't find loaderDelegate.moka."

   fileNameForPath: (filePath) ->
     filePath.match(/([^\/]+)\.moka$/)[1]


removing requires; plugin path isn't needed (was prepending the path twice)

--- app/assets/javascripts/avalon_player.js.coffee
+++ app/assets/javascripts/avalon_player.js.coffee
@@ -12,11 +12,7 @@
 #   specific language governing permissions and limitations under the License.
 # ---  END LICENSE_HEADER BLOCK  ---

-//= require mediaelement_rails/mediaelement-and-player
-//= require mediaelement-qualityselector/mep-feature-qualities
-//= require mediaelement-skin-avalon/mep-feature-responsive
-//= require keyboard_access
-//= require android_pre_play
+

 class AvalonPlayer
   constructor: (container, stream_info, opts) ->
@@ -34,7 +30,6 @@
     features = (feature for feature in features when feature?)
     player_options =
       mode: 'auto_plugin'
-      pluginPath: '/assets/mediaelement_rails/'
       usePluginFullScreen: false
       thumbnailSelectorUpdateURL: '/update-url'
       thumbnailSelectorEnabled: true
@@ -94,7 +89,7 @@
           clip_span.css 'width', end_percent-start_percent+'%'
           $('.mejs-time-total').append clip_span
         if _this.player.options.displayMarkers
-          duration = _this.stream_info.duration
+          duration = _this.stream_info.duration
           scrubber = $('.mejs-time-rail')
           scrubber.css('position', 'relative')
           $('.row.marker').each (i,value) ->


update ref book test with new page content selector

--- tutor/test/components/reference-book.spec.coffee
+++ tutor/test/components/reference-book.spec.coffee
@@ -73,7 +73,7 @@
       .to.match(/1\.1/)

   it 'renders page html', ->
-    expect(@state.div.querySelector('.page').textContent)
+    expect(@state.div.querySelector('.book-content').textContent)
       .to.equal('A bunch of html')

   it 'toggles menu when navbar control is clicked', ->


Fix unescaped strings in the labels dropdown template

--- app/assets/javascripts/labels_select.js.coffee
+++ app/assets/javascripts/labels_select.js.coffee
@@ -32,9 +32,9 @@
       if issueUpdateURL
         labelHTMLTemplate = _.template(
             '<% _.each(labels, function(label){ %>
-            <a href="<%- ["",issueURLSplit[1], issueURLSplit[2],""].join("/") %>issues?label_name[]=<%= encodeURIComponent(label.title) %>">
+            <a href="<%- ["",issueURLSplit[1], issueURLSplit[2],""].join("/") %>issues?label_name[]=<%- encodeURIComponent(label.title) %>">
             <span class="label has-tooltip color-label" title="<%- label.description %>" style="background-color: <%- label.color %>; color: <%- label.text_color %>;">
-            <%= label.title %>
+            <%- label.title %>

             <% }); %>'


Force json format in file upload

Force json format in file upload

--- app/assets/javascripts/redcursor_app.js.coffee
+++ app/assets/javascripts/redcursor_app.js.coffee
@@ -12,7 +12,7 @@
     else
       $('.comment_box').append "![](#{url})"
   $('.comment_box').filedrop
-    url: '/images'
+    url: '/images.json'
     paramname: 'image[image_file]'
     allowedfiletypes: ['image/jpeg','image/png','image/gif']
     headers:


fix typo in error message

fix typo in error message

Change-Id: I9a6990de831310a0532890168cd04f2bc8ed5260
Reviewed-on: https://gerrit.instructure.com/9651
Tested-by: Hudson <dfb64870173313a9a7b56f814c8e3b33e268497a@instructure.com>
Reviewed-by: Ryan Shaw <ea3cd978650417470535f3a4725b6b5042a6ab59@instructure.com>

--- app/coffeescripts/calendar/TimeBlockRow.coffee
+++ app/coffeescripts/calendar/TimeBlockRow.coffee
@@ -67,7 +67,7 @@
           valid = false
           @inputs.end_time.$el
             .addClass('error')
-            .errorBox(I18n.t 'ends_in_past_error', 'You cannot create an appontment slot that ends in the past')
+            .errorBox(I18n.t 'ends_in_past_error', 'You cannot create an appointment slot that ends in the past')
         # make sure start is before end
         if @inputs.start_time.val && @inputs.end_time.val
           testDate = @validDate() || 'Today'


we no longer need the minor tick size

we no longer need the minor tick size

--- src/packages/editor-stats/src/editor-stats-view.coffee
+++ src/packages/editor-stats/src/editor-stats-view.coffee
@@ -26,7 +26,7 @@
     @y.range [h - pt - pb, 0]

     @xaxis ?= d3.svg.axis().scale(@x).orient('top')
-      .tickSize(-h + pt + pb, 50)
+      .tickSize(-h + pt + pb)
       .tickFormat (d) =>
         d = new Date(@stats.startDate.getTime() + (d * 6e4))
         mins = d.getMinutes()


CleanUp

CleanUp

--- src/main.coffee
+++ src/main.coffee
@@ -1,27 +1,3 @@
 testRunner = require './TestRunner'

-testRunner.run()
-
-#log.debug(rc)
-#
-##sleep = spawn("sleep",['3'])
-##sleep.on "exit", (code)->
-##  log.info("sleep exited with code: ",code)
-#
-#ls    = spawn('ls', ['-lh', '/usr']);
-#buffer = ""
-#
-#ls.stdout.on "data", (data) ->
-#  buffer += data
-##  console.log "stdout: " + data
-#
-#ls.stderr.on "data", (data) ->
-#  console.log "stderr: " + data
-#
-#ls.on "close", (code) ->
-#  console.log(buffer)
-#  console.log "child process exited with code " + code
-#
-
-#process.on 'exit', ->
-#  console.log('Process terminated')
+testRunner.run()


cleanup

cleanup


format

format

--- src/main.coffee
+++ src/main.coffee
@@ -115,11 +115,11 @@
 		return

 	genTrigger = (self, state) -> (value) ->
-		if self._state != $pending
-			return
+		return if self._state != $pending

 		self._state = state
 		self._value = value
+
 		i = 0
 		len = self._thenIndex

@@ -130,10 +130,7 @@
 			handler = self._handlers[k]
 			thenHandler = self._handlers[k + 2]

-			out = if handler
-				handler value
-			else
-				value
+			out = if handler then handler value else value

 			if thenHandler
 				chainHandler out, thenHandler


cleanup todos

cleanup todos

--- src/xml.coffee
+++ src/xml.coffee
@@ -5,7 +5,7 @@

 # could be boosted up by using a c lib → https://github.com/wezm/node-genx
 # TODO add clone method to tag so tags can be cloned (param: deep for deep copy)
-# TODO partials?
+

 parse_args = (name, attrs, children, opts) ->
     unless typeof attrs is 'object'


:speak_no_evil:

--- src/app/theme-config-panel.coffee
+++ src/app/theme-config-panel.coffee
@@ -54,14 +54,11 @@
       li

   enabledThemeReceived: (helper) ->
-    console.log "RECEIVE", helper
     name = helper.attr('name')
     @enabledThemes.find("[name='#{name}']:not('.ui-draggable')").remove()
     @enabledThemes.find(".ui-draggable").removeClass('ui-draggable')

   enabledThemesUpdated: ->
-    console.log "enabledThemesUpdated"
-    console.log @getEnabledThemeNames()
     config.set('core.themes', @getEnabledThemeNames())

   getEnabledThemeNames: ->


Improved documentation

Improved documentation

--- src/admin/backbone.admin.appcontroller.coffee
+++ src/admin/backbone.admin.appcontroller.coffee
@@ -29,16 +29,16 @@
   ###
   Constructor

+  @param {Object} options The options to configure the application controller
+
+  Recognized options:
   ```
-  # Available options:
   options:
-    router:
+		router: Boolean | Router class | Router instance
   ```

-  *`router`: Could be a boolean to enable or disable the router. Could be a class to instanciate a new router or
+  - `router`: Could be a boolean to enable or disable the router. Could be a class to instanciate a new router or
   could be an instanciated router.
-
-  @param {Object} options The options to configure the application controller
   ###
   constructor: (options) ->
     _.extend @, Backbone.Events


(re style cli) Huge, fragile, horrible, monkey-patching hacks

Squashes: e91b5, 71dc6

--- Paws/Source/interactive.coffee
+++ Paws/Source/interactive.coffee
@@ -1,6 +1,7 @@
 `                                                                                /*|*/ require = require('../Library/cov_require.js')(require)`
 Paws = require './Paws.coffee'
 Paws.infect global
+T = Paws.debugging.tput

 PrettyError = require('pretty-error')

@@ -17,8 +18,10 @@
       #      be moved to start().
       @readline = readline.createInterface
          input: @_?.input ? process.stdin, output: @_?.output ? process.stdout
-
       @readline.setPrompt @_?.prompt ? ':: '
+      @readline.line_style = T.sgr 7
+      @readline.clear_style = T.sgr 27
+      @hackReadline()

       @error_renderer = @_?.error_renderer
       unless @error_renderer?
@@ -36,15 +39,15 @@

    prompt: -> @readline.prompt()

-
    start: ->
-      #process.title = 'paws.js: interact'
+      process.title = 'paws.js (interact)'
       @here.start()

       shortcircuit = undefined # ???
       @readline.on 'line', (line)=>
          return shortcircuit = false if shortcircuit # ???
          return @readline.prompt() unless line.length
+         @readline.write @readline.clear_style

          # FIXME: Input during the processing is currently all processed immediately after a prompt
          #        is next shown. This is rather icky when the user ^C's a ton, and then externally
@@ -71,14 +74,28 @@
       SIGTERM = =>
          @here.stop()
          @readline.write "\x1b[2K\x1b[0G" # Zero cursor.
+         @readline.write @readline.clear_style
+         @readline.close()
          process.stdin.destroy()
       @readline.on 'close', SIGTERM
       process.on 'SIGTERM', SIGTERM

-      t = Paws.debugging.tput
+      SIGTSTP = =>
+         process.once 'SIGCONT', =>
+            @readline.input.pause()
+            @readline.input.resume()
+            @readline._setRawMode true
+            @readline._refreshLine()
+
+         @readline.write @readline.clear_style
+         @readline._setRawMode false
+         process.kill process.pid, 'SIGTSTP'
+
+      @readline.on 'SIGTSTP', SIGTSTP
+
       Paws.alert "Successive lines will be evaluated as executions, with shared `locals`."
-      Paws.alert "   (#{t.bold '⌃d'} to close the input-stream; "+
-                     "#{t.bold '⌃c'} to synchronously force new prompt)"
+      Paws.alert "   (#{T.bold '⌃d'} to close the input-stream; "+
+                     "#{T.bold '⌃c'} to synchronously force new prompt)"
       @prompt()

@@ -118,5 +135,50 @@
       @prompt()
    .rename '<interact: resume prompt>'

-   # Generates an `Execution` that will print information about the `Thing` passed to it
-   # (presumably, the final result of a line of code typed into the `Interactive`.)
+
+   # --- ---- --- /!\ --- ---- --- #
+
+   # This is all a huge, fragile, horrible, monkey-patching hack.
+   hackReadline: ->
+      exportz = readline
+
+      _refreshLine = @readline._refreshLine
+      @readline._refreshLine = =>
+         [clearScreenDown, exportz.clearScreenDown] = [exportz.clearScreenDown, haxClearScreenDown]
+         _refreshLine.apply @readline
+         exportz.clearScreenDown = clearScreenDown
+
+      # This ensures our custom line-styles get applied every time the line is refreshed
+      haxClearScreenDown = (stream)=>
+         stream.write '\x1b[0J'
+         stream.write T.column_address(0)
+         stream.write T.sgr(7)+(new Array(T.columns+1).join ' ')
+         stream.write T.column_address(0)
+         stream.write @readline.line_style
+
+      _ttyWrite = @readline._ttyWrite
+      # These replace the usual behavior of pausing the input-stream (which means all input while
+      # paused is buffered, and will eventually be dumped back out), and simply *ignores* all input
+      # until unpaused. No buffering.
+      @readline.pause = ->
+         return if @paused
+         @_ttyWrite = haxTtyWrite
+         @paused = true
+         @emit 'pause'
+         return this
+
+      @readline.resume = ->
+         return unless @paused
+         @_ttyWrite = _ttyWrite
+         @paused = false
+         @emit 'resume'
+         return this
+
+      haxTtyWrite = (s, key)->
+         if key.ctrl
+            switch key.name
+               when 'c' then @emit 'SIGINT'     # ^c (interrupt)
+               when 'd' then @close             # ^d (EOF)
+               when 'z'                         # ^z (process backgrounding)
+                  return if process.platform == 'win32'
+                  @emit 'SIGTSTP'


FIX: check I18n existance

FIX: check I18n existance

--- lib/assets/javascripts/tao/application.coffee
+++ lib/assets/javascripts/tao/application.coffee
@@ -21,7 +21,7 @@
       $(@).removeClass 'disabled'

   _initI18n: ->
-    I18n.locale = @locale
+    I18n?.locale = @locale

   _initIcons: ($page) ->
     $icons = $page.siblings('#tao-icons')


Fix link to PHP protocol implementation

Fix link to PHP protocol implementation

--- lib/assets/javascripts/unpoly/protocol.coffee
+++ lib/assets/javascripts/unpoly/protocol.coffee
@@ -24,7 +24,7 @@
 - [Roda](https://github.com/adam12/roda-unpoly)
 - [Rack](https://github.com/adam12/rack-unpoly) (Sinatra, Padrino, Hanami, Cuba, ...)
 - [Phoenix](https://elixirforum.com/t/unpoly-a-framework-like-turbolinks/3614/15) (Elixir)
-- [PHP](https://github.com/adam12/rack-unpoly) (Symfony, Laravel, Stack)
+- [PHP](https://github.com/webstronauts/php-unpoly) (Symfony, Laravel, Stack)

 @module up.protocol
 ###


Stalkers can more easily be killed now

Stalkers can more easily be killed now

--- app/scripts/lazerscripts/stage1/stalker.coffee
+++ app/scripts/lazerscripts/stage1/stalker.coffee
@@ -5,13 +5,14 @@

   spawn: ->
     Crafty.e('Drone').drone(
-      health: 200
+      health: 100
       x: 680
       y: 400
       speed: 300
     )

   execute: ->
+    @bindSequence 'Destroyed', @onKilled
     @sequence(
       @pickTarget('PlayerControlledShip')
       @moveTo(x: 680, y: 450)
@@ -22,4 +23,5 @@
       @moveTo(y: -50)
     )

-
+  onKilled: ->
+    @explosion(@location())


added correct/attempted/total

added correct/attempted/total

--- src/components/scores/concept-coach-cell.cjsx
+++ src/components/scores/concept-coach-cell.cjsx
@@ -15,9 +15,27 @@
       Math.round((@props.task.correct_exercise_count / @props.task.exercise_count) * 100)
     tooltip =
       <BS.Tooltip>
-

-          Date Last Worked: <Time format='MMM. D' date={@props.task.last_worked_at} />
-

+        <BS.Table>
+

+

+              Correct
+              Attempted
+              Total possible
+

+

+

+

+              {@props.task.correct_exercise_count}
+              {@props.task.completed_exercise_count}
+              {@props.task.exercise_count}
+

+

+
+                Date Last Worked: <Time format='MMM. D' date={@props.task.last_worked_at} />
+
+

+

+        </BS.Table>
       </BS.Tooltip>

@@ -32,7 +50,7 @@
           }
       </Router.Link>
       <BS.OverlayTrigger
-        placement='top'
+        placement='right'
         delayShow={1000}
         delayHide={0}
         overlay={tooltip}>


Fix RedisManagerTests

Fix RedisManagerTests

--- test/unit/coffee/RedisManager/RedisManagerTests.coffee
+++ test/unit/coffee/RedisManager/RedisManagerTests.coffee
@@ -22,6 +22,8 @@
 				projectKey: ({doc_id}) -> "ProjectId:#{doc_id}"
 				pendingUpdates: ({doc_id}) -> "PendingUpdates:#{doc_id}"
 				docsInProject: ({project_id}) -> "DocsIn:#{project_id}"
+				trackChangesEnabled: ({doc_id}) -> "TrackChangesEnabled:#{doc_id}"
+				trackChangesEntries: ({doc_id}) -> "TrackChangesEntries:#{doc_id}"
 			"logger-sharelatex": @logger = { error: sinon.stub(), log: sinon.stub(), warn: sinon.stub() }
 			"./Metrics": @metrics =
 				inc: sinon.stub()
@@ -37,39 +39,52 @@
 			@lines = ["one", "two", "three"]
 			@jsonlines = JSON.stringify @lines
 			@version = 42
+			@track_changes_on = true
+			@redis_track_changes_on = "1"
+			@track_changes_entries = { comments: "mock", entries: "mock" }
+			@json_track_changes_entries = JSON.stringify @track_changes_entries
 			@rclient.get = sinon.stub()
-			@rclient.exec = sinon.stub().callsArgWith(0, null, [@jsonlines, @version, @project_id])
-			@RedisManager.getDoc @project_id, @doc_id, @callback
-
-		it "should get the lines from redis", ->
-			@rclient.get
-				.calledWith("doclines:#{@doc_id}")
-				.should.equal true
-
-		it "should get the version from", ->
-			@rclient.get
-				.calledWith("DocVersion:#{@doc_id}")
-				.should.equal true
-
-		it 'should return the document', ->
-			@callback
-				.calledWith(null, @lines, @version)
-				.should.equal true
-
-	describe "getDoc with an invalid project id", ->
-		beforeEach ->
-			@lines = ["one", "two", "three"]
-			@jsonlines = JSON.stringify @lines
-			@version = 42
-			@another_project_id = "project-id-456"
-			@rclient.get = sinon.stub()
-			@rclient.exec = sinon.stub().callsArgWith(0, null, [@jsonlines, @version, @another_project_id])
-			@RedisManager.getDoc @project_id, @doc_id, @callback
-
-		it 'should return an error', ->
-			@callback
-				.calledWith(new Errors.NotFoundError("not found"))
-				.should.equal true
+			@rclient.exec = sinon.stub().callsArgWith(0, null, [@jsonlines, @version, @project_id, @redis_track_changes_on, @json_track_changes_entries])
+
+		describe "successfully", ->
+			beforeEach ->
+				@RedisManager.getDoc @project_id, @doc_id, @callback
+
+			it "should get the lines from redis", ->
+				@rclient.get
+					.calledWith("doclines:#{@doc_id}")
+					.should.equal true
+
+			it "should get the version from", ->
+				@rclient.get
+					.calledWith("DocVersion:#{@doc_id}")
+					.should.equal true
+
+			it "should get the track changes state", ->
+				@rclient.get
+					.calledWith("TrackChangesEnabled:#{@doc_id}")
+					.should.equal true
+
+			it "should get the track changes entries", ->
+				@rclient.get
+					.calledWith("TrackChangesEntries:#{@doc_id}")
+					.should.equal true
+
+			it 'should return the document', ->
+				@callback
+					.calledWith(null, @lines, @version, @track_changes_on, @track_changes_entries)
+					.should.equal true
+
+		describe "getDoc with an invalid project id", ->
+			beforeEach ->
+				@another_project_id = "project-id-456"
+				@rclient.exec = sinon.stub().callsArgWith(0, null, [@jsonlines, @version, @another_project_id, @redis_track_changes_on, @json_track_changes_entries])
+				@RedisManager.getDoc @project_id, @doc_id, @callback
+
+			it 'should return an error', ->
+				@callback
+					.calledWith(new Errors.NotFoundError("not found"))
+					.should.equal true

 	describe "getPreviousDocOpsTests", ->
 		describe "with a start and an end value", ->
@@ -166,13 +181,14 @@
 			@lines = ["one", "two", "three"]
 			@ops = [{ op: [{ i: "foo", p: 4 }] },{ op: [{ i: "bar", p: 8 }] }]
 			@version = 42
+			@track_changes_entries = { comments: "mock", entries: "mock" }

 			@rclient.exec = sinon.stub().callsArg(0)

 		describe "with a consistent version", ->
 			beforeEach ->
 				@RedisManager.getDocVersion.withArgs(@doc_id).yields(null, @version - @ops.length)
-				@RedisManager.updateDocument @doc_id, @lines, @version, @ops, @callback
+				@RedisManager.updateDocument @doc_id, @lines, @version, @ops, @track_changes_entries, @callback

 			it "should get the current doc version to check for consistency", ->
 				@RedisManager.getDocVersion
@@ -188,6 +204,11 @@
 				@rclient.set
 					.calledWith("DocVersion:#{@doc_id}", @version)
 					.should.equal true
+
+			it "should set the track changes entries", ->
+				@rclient.set
+					.calledWith("TrackChangesEntries:#{@doc_id}", JSON.stringify(@track_changes_entries))
+					.should.equal true

 			it "should push the doc op into the doc ops list", ->
 				@rclient.rpush
@@ -210,7 +231,7 @@
 		describe "with an inconsistent version", ->
 			beforeEach ->
 				@RedisManager.getDocVersion.withArgs(@doc_id).yields(null, @version - @ops.length - 1)
-				@RedisManager.updateDocument @doc_id, @lines, @version, @ops, @callback
+				@RedisManager.updateDocument @doc_id, @lines, @version, @ops, @track_changes_entries, @callback

 			it "should not call multi.exec", ->
 				@rclient.exec.called.should.equal false
@@ -223,7 +244,7 @@
 		describe "with no updates", ->
 			beforeEach ->
 				@RedisManager.getDocVersion.withArgs(@doc_id).yields(null, @version)
-				@RedisManager.updateDocument @doc_id, @lines, @version, [], @callback
+				@RedisManager.updateDocument @doc_id, @lines, @version, [], @track_changes_entries, @callback

 			it "should not do an rpush", ->
 				@rclient.rpush
@@ -242,7 +263,9 @@
 			@rclient.exec.yields()
 			@lines = ["one", "two", "three"]
 			@version = 42
-			@RedisManager.putDocInMemory @project_id, @doc_id, @lines, @version, done
+			@track_changes_on = true
+			@track_changes_entries = { comments: "mock", entries: "mock" }
+			@RedisManager.putDocInMemory @project_id, @doc_id, @lines, @version, @track_changes_on, @track_changes_entries, done

 		it "should set the lines", ->
 			@rclient.set
@@ -252,6 +275,16 @@
 		it "should set the version", ->
 			@rclient.set
 				.calledWith("DocVersion:#{@doc_id}", @version)
+				.should.equal true
+
+		it "should set the track changes entries", ->
+			@rclient.set
+				.calledWith("TrackChangesEntries:#{@doc_id}", JSON.stringify(@track_changes_entries))
+				.should.equal true
+
+		it "should set the track changes state", ->
+			@rclient.set
+				.calledWith("TrackChangesEnabled:#{@doc_id}", "1")
 				.should.equal true

 		it "should set the project_id for the doc", ->


use set_dims

use set_dims

--- bokehjs/src/coffee/models/canvas/canvas.coffee
+++ bokehjs/src/coffee/models/canvas/canvas.coffee
@@ -87,7 +87,7 @@
     solver = @document.solver()
     solver.add_constraint(EQ(@_left))
     solver.add_constraint(EQ(@_bottom))
-    @_set_dims([@get('canvas_width'), @get('canvas_height')])
+    @set_dims([@get('canvas_width'), @get('canvas_height')])
     logger.debug("Canvas attached to document")

   # transform view coordinates to underlying screen coordinates


Fixed add/remove links for Rails 4 using turbolinks

Bound to on("page:change") and also prevented the double action when
document.load happens as well by first removing the onClick action
from the links.

--- vendor/assets/javascripts/nested_form_fields.js.coffee
+++ vendor/assets/javascripts/nested_form_fields.js.coffee
@@ -1,5 +1,5 @@
-jQuery ->
-
+bind_nested_forms_links = () ->
+  $('body').off("click", '.add_nested_fields_link')
   $('body').on 'click', '.add_nested_fields_link', ->
     $link = $(this)
     association_path = $link.data('association-path')
@@ -21,11 +21,17 @@
     $template.before( $parsed_template )
     false

-
+  $('body').off("click", '.remove_nested_fields_link')
   $('body').on 'click', '.remove_nested_fields_link', ->
     $link = $(this)
     delete_association_field_name = $link.data('delete-association-field-name')
     $nested_fields_container = $link.parents(".nested_fields").first()
     $nested_fields_container.before "<input type='hidden' name='#{delete_association_field_name}' value='1' />"
     $nested_fields_container.hide()
-    false+    false
+
+$(document).on "page:change", ->
+    bind_nested_forms_links()
+
+jQuery ->
+    bind_nested_forms_links()


mark textarea style as htmlSafe

mark textarea style as htmlSafe

--- addon/components/eui-textarea.coffee
+++ addon/components/eui-textarea.coffee
@@ -26,6 +26,6 @@

     width = @get('width') or widths[@get('size')] or widths['medium']
     height = @get('height') or heights[@get('size')] or heights['medium']
-    return "width: #{width}; height: #{height};"
+    return ("width: #{width}; height: #{height};").htmlSafe()

 `export default textarea`


cache icon type and improve readability

cache icon type and improve readability

--- views/components/etc/icon.cjsx
+++ views/components/etc/icon.cjsx
@@ -1,5 +1,4 @@
-{ROOT, layout, _, $, $$, React, ReactBootstrap, useSVGIcon} = window
-{$slotitems} = window
+{ROOT, React, useSVGIcon} = window
 path = require 'path-extra'
 fs = require 'fs-extra'
 classnames = require 'classnames'
@@ -8,27 +7,35 @@
   type = if isSVG then 'svg' else 'png'
   classnames type, props.className

+ICON_TYPES =
+  UNAVAILABLE: 0
+  PNG: 1
+  SVG: 2
+iconCache = []
+
 SlotitemIcon = React.createClass
   name: 'SlotitemIcon'
-  render: ->
-    reallyUseSVG = useSVGIcon
-    iconPath = null
+  svgPath: ->
+    "#{ROOT}/assets/svg/slotitem/#{@props.slotitemId}.svg"
+  pngPath: ->
+    "#{ROOT}/assets/img/slotitem/#{@props.slotitemId + 100}.png"
+  determineIconType: ->
     if useSVGIcon
-      iconPath = "#{ROOT}/assets/svg/slotitem/#{@props.slotitemId}.svg"
       try
         # accessSync can not read asar properly
-        fs.statSync iconPath
-      catch
-        reallyUseSVG = false
-    if reallyUseSVG
-
-    else
-      iconPath = "#{ROOT}/assets/img/slotitem/#{@props.slotitemId + 100}.png"
-      try
-        fs.statSync iconPath
-
-      catch
-        # both png and svg not found
+        fs.statSync @svgPath()
+        return ICON_TYPES.SVG
+    try
+      fs.statSync @pngPath()
+      return ICON_TYPES.PNG
+    ICON_TYPES.UNAVAILABLE
+  render: ->
+    switch iconCache[@props.slotitemId] ?= @determineIconType()
+      when ICON_TYPES.PNG
+
+      when ICON_TYPES.SVG
+
+      else


Update isEmptyLine after folds, which could start on empty lines

--- src/display-layer.coffee
+++ src/display-layer.coffee
@@ -411,6 +411,7 @@
           bufferColumn = foldEndBufferPosition.column
           bufferLine = @buffer.lineForRow(bufferRow)
           bufferLineLength = bufferLine.length
+          isEmptyLine &&= (bufferLineLength is 0)
           screenColumn += 1
           screenLineWidth += @ratioForCharacter('⋯')
           tokensScreenExtent = screenColumn


Reordena funções updateValues/Item para refletir a ordem de chamada

--- src/coffee/portal-minicart.coffee
+++ src/coffee/portal-minicart.coffee
@@ -76,6 +76,15 @@
 		@updateValues data
 		@updateItems data

+	updateValues: (data) =>
+		return unless data
+
+		total = 0
+		for subtotal in data.totalizers when subtotal.id is 'Items'
+			total += subtotal.value
+
+		$(".vtexsc-text", @context).text "R$ " + formatCurrency total
+
 	updateItems: (data) =>
 		return unless data

@@ -92,15 +101,6 @@

 		$(".vtexsc-productList .cartSkuRemove", @context).on 'click', =>
 			@deleteItem(this)
-
-	updateValues: (data) =>
-		return unless data
-
-		total = 0
-		for subtotal in data.totalizers when subtotal.id is 'Items'
-			total += subtotal.value
-
-		$(".vtexsc-text", @context).text "R$ " + formatCurrency total

 	deleteItem: (item) =>
 		$(item).parent().find('.vtexsc-overlay').show()


+ inside call intendation in debug log

+ inside call intendation in debug log

--- test/debug.coffee
+++ test/debug.coffee
@@ -1,20 +1,33 @@
 Function.prototype.toJSON = -> @name

+stack = ""
+indent = "      "
+
+log = (arg) -> console.log "#{stack}#{indent}#{arg}"
+pass = (arg) -> arg
+
 debug = {
-  call: (instance, key)->
+  call: (instance, key, prefix = '#', suffix = '')->
     method = instance[key]
     instance[key] = (args...) ->
-      console.log "call #{key}(#{args.map(JSON.stringify).join(',')})"
-      method.call instance, args...
+      log "#{prefix}#{key}#{suffix}(#{args.map(JSON.stringify).join(',')})"
+      indent += "  "
+      retVal = method.call instance, args...
+      indent = indent.substr 0, indent.length - 2
+      if retVal is instance
+        log "return this"
+      else
+        log "return #{JSON.stringify(retVal)}"
+      retVal

-  property: (instance, key)->
+  property: (instance, key, onSet = pass)->
     value = instance[key]
     Object.defineProperty instance, key,
       get: ->
         value
       set: (arg)->
-        console.log "set #{key}: #{JSON.stringify arg}"
-        value = arg
+        log "set #{key}: #{JSON.stringify arg}"
+        value = onSet arg
       enumerable: true,
       configurable: true,

@@ -24,16 +37,25 @@
   check: (testedCheck) ->
     debug.property testedCheck, 'finish'
     debug.property testedCheck, 'state'
-    debug.property testedCheck.state, 'strategy'
+    debug.property testedCheck.state, 'strategy', (value)->
+      debug.call { strategy: value }, 'strategy'
     debug.property testedCheck.state, 'result'
     debug.property testedCheck.state, 'current'
-    debug.call testedCheck.state.done, 'push'
+    debug.call testedCheck.state.done, 'push', 'done.'
     testedCheck.push = debug.decorate testedCheck.push, (originalPush)->
-      originalPush.call testedCheck
-      debug.property testedCheck.state, 'strategy'
+      stack += "|"
+      indent = indent.substr 1
+      retVal = originalPush.call testedCheck
+      debug.property testedCheck.state, 'strategy', (value)->
+        debug.call { strategy: value }, 'strategy', '#', "[#{value.name}]"
       debug.property testedCheck.state, 'result'
       debug.property testedCheck.state, 'current'
-      debug.call testedCheck.state.done, 'push'
+      debug.call testedCheck.state.done, 'push', 'done.'
+      retVal
+    testedCheck.pop = debug.decorate testedCheck.pop, (originalPop)->
+      indent += " "
+      stack = stack.substr 1
+      originalPop.call testedCheck
     testedCheck.add = debug.decorate testedCheck.add, (originalAdd, args...)->
       try
         originalAdd.call testedCheck, args...
@@ -42,8 +64,6 @@
         throw e

     debug.call testedCheck, 'add'
-    debug.call testedCheck, 'push'
-    debug.call testedCheck, 'pop'
     debug.call testedCheck, 'assert'
     testedCheck
 }


Deleting load_worker.coffee

Deleting load_worker.coffee

--- engine/load_worker.coffee
+++ engine/load_worker.coffee
@@ -1,282 +0,0 @@
-CRUNCH_MEM = 64
-MAX_ACTIVE_TASKS = 6
-worker = @
-
-remove_from_array = (array, i) ->
-        i = array.indexOf i
-        if i != -1
-            array.splice i,1
-
-
-# Crunch is imported in the head of the load_worker (in loader.coffee)
-# JS("importScripts('crunch.js')")
-
-# TODO: test: failing files, stuck files
-
-#decode functions:
-load_crunch = (task_id, queue_id, data, extra_data, uri) ->
-    data = new Uint8Array data
-    data_view = new DataView data.buffer
-    src_size = data.length
-    src = Crunch._malloc src_size
-    Crunch.HEAPU8.set data, src
-    width = data_view.getUint16 12
-    height = data_view.getUint16 14
-    levels = data[16]
-    faces = data[17]
-    format = data[18]
-    additional_levels = data[25]
-    level_buffers = []
-    # TODO: copy levels in a single buffer instead of several?
-    # good because there are less gc objects? or bad idea because it's contiguous?
-    if format == 0
-        block_bytes = 8
-    else
-        block_bytes = 16
-    context = Crunch._crn_unpack_begin src, src_size
-    for i in [0...levels]
-        data_length = (Math.max( 4, width>>i )>>2) * (Math.max( 4, height>>i )>>2) * block_bytes
-        #max_size = Math.max(max_size, src_size+dataLength)
-        #console.log max_size
-        data_offset = Crunch._crn_unpack_level context, src, src_size, i
-        # This makes a copy, to be used as a transferable buffer
-        buffer = Crunch.HEAPU8.buffer[data_offset ... data_offset+data_length]
-        if not COMPRESSED_TEXTURE_SUPPORT
-            buffer = dxtToRgb565(new Uint16Array(buffer), 0, width>>i, height>>i).buffer
-        level_buffers.push buffer
-        Crunch._free data_offset
-    common_data = null
-    transfer = level_buffers
-    if additional_levels
-        # Transfer the common data too, to be used for higher levels and in case of lost context
-        common_data_size = data_view.getUint32 70
-        common_data = data.buffer[ ... common_data_size]
-        transfer = level_buffers.concat [data.buffer]
-    post_message [task_id, queue_id, [additional_levels, width, height, format, level_buffers, common_data, uri]], transfer
-    Crunch._crn_unpack_end context
-    Crunch._free src
-
-load_crunch_extra = (task_id, queue_id, data, original_common_data, uri)->
-    global max_size
-    data = new Uint8Array data
-    original_common_data = new Uint8Array original_common_data
-    data_view = new DataView data.buffer
-    # The data contains a modified header (16 bytes)
-    # and then the new level. We thus calculate the size:
-    src_size = original_common_data.length +  data.length - 16
-    src = Crunch._malloc src_size
-    # First we set the original common data
-    Crunch.HEAPU8.set original_common_data, src
-    # Then we overwrite the modified header (first 16 bytes)
-    Crunch.HEAPU8.set data.subarray(0,16), src
-    # Also we have to write the total length to the nextlevel offset
-    # because crunch uses that for calculating the size
-    Crunch.HEAPU8.set data.subarray(6, 10), src + 74
-    # Finally we copy the new level to the first level offset
-    # which is just after the common data
-    Crunch.HEAPU8.set data.subarray(16), src + original_common_data.length
-
-    width = data_view.getUint16 12
-    height = data_view.getUint16 14
-    faces = original_common_data[17]
-    format = original_common_data[18]
-    level_buffers = []
-    # TODO: copy levels in a single buffer instead of several?
-    # good because there are less gc objects? or bad idea because it's contiguous?
-    if format == 0
-        block_bytes = 8
-    else
-        block_bytes = 16
-    context = Crunch._crn_unpack_begin src, src_size
-    data_length = (Math.max( 4, width )>>2) * (Math.max( 4, height )>>2) * block_bytes
-    #max_size = Math.max(max_size, src_size+dataLength)
-    #console.log max_size
-    data_offset = Crunch._crn_unpack_level context, src, src_size, 0
-    # This makes a copy, to be used as a transferable buffer
-    buffer = Crunch.HEAPU8.buffer[data_offset ... data_offset+(data_length)]
-    Crunch._free data_offset
-
-    post_message [task_id, queue_id, [width, height, format, buffer, uri]], [buffer]
-    Crunch._crn_unpack_end context
-    Crunch._free src
-
-if typeof window isnt 'undefined'
-    window.load_crunch = load_crunch
-    window.load_crunch_extra = load_crunch_extra
-
-dxtToRgb565 = (src, src16Offset, width, height) ->
-    c = new Uint16Array 4
-    dst = new Uint16Array width * height
-    nWords = (width * height) / 4
-    m = dstI = i = r0 = g0 = b0 = r1 = g1 = b1 = 0
-
-    blockWidth = width / 4
-    blockHeight = height / 4
-
-    for blockY in [0...blockHeight]
-        for blockX in [0...blockWidth]
-            i = src16Offset + 4 * (blockY * blockWidth + blockX)
-            c[0] = src[i]
-            c[1] = src[i + 1]
-            r0 = c[0] & 0x1f
-            g0 = c[0] & 0x7e0
-            b0 = c[0] & 0xf800
-            r1 = c[1] & 0x1f
-            g1 = c[1] & 0x7e0
-            b1 = c[1] & 0xf800
-
-            # Interpolate between c0 and c1 to get c2 and c3.
-            # Note that we approximate 1/3 as 3/8 and 2/3 as 5/8 for
-            # speed.  This also appears to be what the hardware DXT
-            # decoder in many GPUs does :)
-
-            c[2] = ((5 * r0 + 3 * r1) >> 3) | (((5 * g0 + 3 * g1) >> 3) & 0x7e0) | (((5 * b0 + 3 * b1) >> 3) & 0xf800)
-            c[3] = ((5 * r1 + 3 * r0) >> 3) | (((5 * g1 + 3 * g0) >> 3) & 0x7e0) | (((5 * b1 + 3 * b0) >> 3) & 0xf800)
-            m = src[i + 2]
-            dstI = (blockY * 4) * width + blockX * 4
-            dst[dstI] = c[m & 0x3]
-            dst[dstI + 1] = c[(m >> 2) & 0x3]
-            dst[dstI + 2] = c[(m >> 4) & 0x3]
-            dst[dstI + 3] = c[(m >> 6) & 0x3]
-            dstI += width
-            dst[dstI] = c[(m >> 8) & 0x3]
-            dst[dstI + 1] = c[(m >> 10) & 0x3]
-            dst[dstI + 2] = c[(m >> 12) & 0x3]
-            dst[dstI + 3] = c[(m >> 14)]
-            m = src[i + 3]
-            dstI += width
-            dst[dstI] = c[m & 0x3]
-            dst[dstI + 1] = c[(m >> 2) & 0x3]
-            dst[dstI + 2] = c[(m >> 4) & 0x3]
-            dst[dstI + 3] = c[(m >> 6) & 0x3]
-            dstI += width
-            dst[dstI] = c[(m >> 8) & 0x3]
-            dst[dstI + 1] = c[(m >> 10) & 0x3]
-            dst[dstI + 2] = c[(m >> 12) & 0x3]
-            dst[dstI + 3] = c[(m >> 14)]
-    return dst
-
-post_message = @postMessage.bind @
-console = {
-    log : (msg)->
-        post_message ['log', msg]
-}
-
-class Queue
-    constructor: (id)->
-        @tasks = []
-        @loaded = 0
-        @active_tasks = 0
-        @id = id
-
-    add_progress: (progress)->
-        @loaded += progress
-        post_message ['progress', @id, @loaded]
-
-    clear: ()->
-        @loaded = 0
-        @active_tasks = 0
-        for xhr in @tasks
-            xhr.abort()
-        return
-
-    init_tasks: ()->
-        while @active_tasks < Math.min MAX_ACTIVE_TASKS, @tasks.length
-            @tasks[@active_tasks].send()
-            @active_tasks += 1
-
-    finish_task: (xhr)->
-        remove_from_array @tasks, xhr
-        @active_tasks -= 1
-        @init_tasks()
-        if @active_tasks == 0
-            post_message ['done', @id]
-
-    add_task: (task_id, uri, decode_function, extra_data, tries=6, retry_time=1)->
-        xhr = new XMLHttpRequest
-        xhr.open 'GET', uri, true
-        do_json = false
-        if decode_function=='text'
-            xhr.responseType = 'text'
-            decode_function = null
-        else if decode_function == 'json'
-            # some browsers still don't implement json type
-            xhr.responseType = 'text'
-            decode_function = null
-            do_json = true
-        else
-            xhr.responseType = 'arraybuffer'
-        @tasks.push xhr
-        total = 0
-        loaded = 0
-
-        queue = @
-        retry = ->
-            queue.add_task task_id, uri, decode_function, extra_data, tries - 1, retry_time * 2
-        xhr.onload = (evt) ->
-            if xhr.status == 200 or xhr.status == 0
-                data = xhr.response
-                if do_json
-                    data = JSON.parse data
-                if decode_function
-                    worker[decode_function](task_id, queue.id, data, extra_data, uri)
-                else
-                    if data.byteLength?
-                        total = data.byteLength
-                        #total = (new Uint8Array(data)).length
-                        post_message [task_id, queue.id, [xhr.response], [xhr.response]]
-                        #console.log xhr.response.byteLength  # it should be 0
-                    else
-                        post_message [task_id, queue.id, [xhr.response]]
-                        total = data.length or total
-            else
-                # TODO: is this necessary for onload?
-                if tries
-                    post_message [
-                        task_id, queue.id, 'error', xhr.status, xhr.response,
-                        'retrying '+uri.split('/').pop()+' in '+retry_time+' seconds'
-                    ]
-                    setTimeout retry, retry_time*1000
-                    return
-                else
-                    post_message [task_id, queue.id, 'error', xhr.status, xhr.response, 1]
-            queue.add_progress total - loaded
-            queue.finish_task xhr
-
-        xhr.onerror = (evt)->
-            if tries
-                post_message [task_id, queue.id, 'error', xhr.status, xhr.response, 'retrying '+uri.split('/').pop()+' in '+retry_time+' seconds']
-                setTimeout retry, retry_time*1000
-            else
-                post_message [task_id, queue.id, 'error', xhr.status, xhr.response, 2]
-                queue.add_progress total - loaded
-                queue.finish_task xhr
-
-        xhr.onprogress = (evt)->
-            if evt.lengthComputable
-                queue.add_progress evt.loaded - loaded
-                total = evt.total
-                loaded = evt.loaded
-
-        @init_tasks()
-
-
-queues = []
-
-# clear 1 queue_id
-# get   1 queue_id  2 task_id  3 url  4 decode_function  5 [extra data]
-
-# decode_function can be "text", "json" or any function defined here for decoding
-
-
-onmessage = (e)->
-    d = e.data
-    if d[0]=='clear'
-        queues[d[1]].clear()
-    else if d[0]=='get'
-        q = queues[d[1]]
-        if not q
-            q = queues[d[1]] = new Queue d[1]
-        q.add_task d[2], d[3], d[4], d[5]
-    else
-        console.log 'error ' + d[0]


add colors to charts

add colors to charts

--- app/assets/javascripts/application.coffee
+++ app/assets/javascripts/application.coffee
@@ -11,8 +11,18 @@
   bloodPressureData = {
     labels: gon.chart_data.blood_pressure_out.labels
     datasets: [
-      { data: gon.chart_data.blood_pressure_in.values }
-      { data: gon.chart_data.blood_pressure_out.values }
+      {
+        fillColor : "rgba(220,220,220,0.5)"
+        strokeColor : "rgba(220,220,220,1)"
+        pointColor : "rgba(220,220,220,1)"
+        pointStrokeColor : "#fff"
+        data: gon.chart_data.blood_pressure_in.values }
+      {
+        fillColor : "rgba(151,187,205,0.5)"
+        strokeColor : "rgba(151,187,205,1)"
+        pointColor : "rgba(151,187,205,1)"
+        pointStrokeColor : "#fff"
+        data: gon.chart_data.blood_pressure_out.values }
     ]
   }


Reformatting only

Reformatting only

--- app/assets/javascripts/scrapers.js.coffee
+++ app/assets/javascripts/scrapers.js.coffee
@@ -7,12 +7,10 @@
   objDiv.scrollTop = objDiv.scrollHeight

 # Always start with the console scrolled to the end
-$ ->
-  console_scroll_to_bottom()
+$ -> console_scroll_to_bottom()

 class Sync.LogLineLogLine extends Sync.View
   afterInsert: -> console_scroll_to_bottom()

 class Sync.ScraperConsole extends Sync.View
-  afterUpdate: ->
-    console_scroll_to_bottom()
+  afterUpdate: -> console_scroll_to_bottom()


set Leaflet image path

set Leaflet image path

--- app/assets/javascripts/subjects.js.coffee
+++ app/assets/javascripts/subjects.js.coffee
@@ -6,6 +6,7 @@
 loadData = (json_representation) ->
   jQuery () ->
     if $("body.subjects").length > 0
+      L.Icon.Default.imagePath = "/assets"
       window.map = L.map('map').setView([41.6702, -86.2457], 14)
       L.tileLayer('http://{s}.tile.cloudmade.com/540fa91132c54177ac2cce3a415e5ff0/997/256/{z}/{x}/{y}.png', {
         attribution: 'Map data © OpenStreetMap contributors, CC-BY-SA, Imagery © CloudMade',


Removed debug messages

Removed debug messages

--- app/assets/javascripts/the_role.js.coffee
+++ app/assets/javascripts/the_role.js.coffee
@@ -1,5 +1,4 @@
 showForm = (e) ->
-  console.log('show form for ' + e.className)
   $(e).children('span.a').hide()
   $(e).children('span.b').css('visibility', 'visible')
   $(e).children('span.a').off('click')
@@ -10,7 +9,6 @@
     e.parentNode.submit()

 hideForm = (e) ->
-  console.log('hide form for ' + e.className)
   $(e).children('span.a').show()
   $(e).children('span.b').css('visibility', 'hidden')
   $(e).find('.btn').off('click')


Fixing importing when project memberships is just the same value than the user max

--- app/coffee/modules/common/importer.coffee
+++ app/coffee/modules/common/importer.coffee
@@ -38,7 +38,7 @@
                     privateError = !currentUserService.canCreatePrivateProjects().valid
                     maxMemberships = null

-                    if user.get('max_memberships_private_projects') != null && result.headers.memberships > user.get('max_memberships_private_projects')
+                    if user.get('max_memberships_private_projects') != null && result.headers.memberships >= user.get('max_memberships_private_projects')
                         membersError = true
                     else
                         membersError = false
@@ -55,7 +55,7 @@
                 else
                     publicError = !currentUserService.canCreatePublicProjects().valid

-                    if user.get('max_memberships_public_projects') != null && result.headers.memberships > user.get('max_memberships_public_projects')
+                    if user.get('max_memberships_public_projects') != null && result.headers.memberships >= user.get('max_memberships_public_projects')
                         membersError = true
                     else
                         membersError = false


Fixed "added first factlink" activity.

Fixed "added first factlink" activity.

--- core/app/backbone/views/activities/user_activities_view.coffee
+++ core/app/backbone/views/activities/user_activities_view.coffee
@@ -19,7 +19,7 @@
   itemView: Backbone.View
   itemViewContainer: ".js-region-activities"

-  @actions: ["added_first_factlink", "created_channel", "added_subchannel"]
+  @actions: ["created_channel", "added_subchannel"]
   actions: -> UserActivitiesGroupView.actions

   @new: (options)->
@@ -50,7 +50,7 @@
     @lastView = super(item, NewItemView, options)

 class UserFactActivitiesGroupView extends UserActivitiesGroupView
-  @actions: ["added_fact_to_channel", "created_comment", "created_sub_comment", "added_supporting_evidence", "added_weakening_evidence", "believes", "doubts", "disbelieves"]
+  @actions: ["added_first_factlink", "added_fact_to_channel", "created_comment", "created_sub_comment", "added_supporting_evidence", "added_weakening_evidence", "believes", "doubts", "disbelieves"]
   actions: -> UserFactActivitiesGroupView.actions

   template:


Fully remove merge-conflicts package

Fully remove merge-conflicts package

--- atom/packages.cson
+++ atom/packages.cson
@@ -33,7 +33,6 @@
   "list-edit"
   "local-history"
   "markdown-scroll-sync"
-  "merge-conflicts"
   "package-sync"
   "pigments"
   "plantuml-preview"


For run on Returns statements, insert a `\n`

--- src/nodes/doc.coffee
+++ src/nodes/doc.coffee
@@ -181,7 +181,7 @@
         if in_hash
           _.last(_.last(returns).options).desc += " #{line}"
         else
-          _.last(returns).desc += " #{line}"
+          _.last(returns).desc += "\n#{line}"

     returns


Add larger example test

Add larger example test

--- test/index.coffee
+++ test/index.coffee
@@ -31,3 +31,123 @@

     '''
+
+  it 'should be able to build a standard example', ->
+    eq '''
+      // http://getbootstrap.com/examples/navbar-static-top/
+
+      title "Static Top Navbar Example for Bootstrap"
+
+      navbar static
+        brand "Project name"              (to "#")
+
+        item "Home"                       (to "#")  active
+        item "About"                      (to "#about")
+        item "Contact"                    (to "#contact")
+        item "Dropdown"
+          item "Action"                   (to "#")
+          item "Another action"           (to "#")
+          item "Something else here"      (to "#")
+          divider
+          header "Nav header"
+          item "Separated link"           (to "#")
+          item "One more separated link"  (to "#")
+
+        space
+        item "Default"     (to "http://getbootstrap.com/examples/navbar/")
+        item "Static top"  (to "http://getbootstrap.com/examples/navbar-static-top/")  active
+        item "Fixed top"   (to "http://getbootstrap.com/examples/navbar-fixed-top/")
+
+      jumbotron
+        header "Navbar example"
+        p "This example is a quick exercise to illustrate how the default, static and fixed to top navbar work. It includes the responsive CSS and HTML, so it also adapts to your viewport and device."
+        p "To see the difference between static and fixed top navbars, just scroll."
+        button primary "View navbar docs »" (to "http://getbootstrap.com/components/#navbar")
+
+    ''', '''
+      <!DOCTYPE html>
+
+
+          <meta charset="utf-8">
+          <meta http-equiv="X-UA-Compatible" content="IE=edge">
+          <meta name="viewport" content="width=device-width, initial-scale=1">
+          <meta name="description" content="">
+          <meta name="author" content="">
+          <link rel="shortcut icon" href="../../assets/ico/favicon.ico">
+
+          <title>Static Top Navbar Example for Bootstrap</title>
+
+
+          <link href="../../dist/css/bootstrap.min.css" rel="stylesheet">
+
+
+          <link href="navbar-static-top.css" rel="stylesheet">
+
+
+
+
+
+
+
+
+

+

+

+                <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
+                  Toggle navigation
+
+
+
+                </button>
+                Project name
+

+

+

+
Home

+
About

+
Contact

+

+                    Dropdown
+

+
Action

+
Another action

+
Something else here

+

+
Nav header

+
Separated link

+
One more separated link

+

+

+

+

+
Default

+
Static top

+
Fixed top

+

+

+

+

+

+
+

+
Navbar example

+
This example is a quick exercise to illustrate how the default, static and fixed to top navbar work. It includes the responsive CSS and HTML, so it also adapts to your viewport and device.

+
To see the difference between static and fixed top navbars, just scroll.

+

+                View navbar docs »
+

+

+

+
+
+
+
+
+
+
+
+    '''


Remove some "super"s

Remove some "super"s

--- app/js/jail_iframe/classes/buttons.coffee
+++ app/js/jail_iframe/classes/buttons.coffee
@@ -11,10 +11,12 @@
   _addClass: (classes) =>
     @$el.addClass classes
     @frame.sizeFrameToFitContent()
+    @_updatePosition()

   _removeClass: (classes) =>
     @$el.removeClass classes
     @frame.sizeFrameToFitContent()
+    @_updatePosition()

   _fadeIn: ->
     return if @_visible
@@ -63,14 +65,6 @@
   destroy: ->
     super
     $(document).off 'mousemove', @_onMouseLeave
-
-  _addClass: ->
-    super
-    @_updatePosition()
-
-  _removeClass: ->
-    super
-    @_updatePosition()

   _onMouseLeave: =>
     @_removeClass 'fl-button-state-hovered'
@@ -128,6 +122,8 @@
     current_user_opinion = $(event.target).data('opinion')
     FactlinkJailRoot.createFactFromSelection(current_user_opinion)

+  _updatePosition: ->
+
   placeNearSelection: (mouseX=null) ->
     return if @_visible


Improve error reporting and retry ops in stress test script

--- services/document-updater/test/stress/coffee/run.coffee
+++ services/document-updater/test/stress/coffee/run.coffee
@@ -34,38 +34,57 @@

 		DocUpdaterClient.subscribeToAppliedOps (channel, update) =>
 			update = JSON.parse(update)
+			if update.error?
+				console.error new Error("Error from server: '#{update.error}'")
 			if update.doc_id == @doc_id
 				@processReply(update)

 	sendUpdate: () ->
 		data = String.fromCharCode(65 + @charCode++ % 26)
 		@content = insert(@content, @pos, data)
+		@inflight_op = {
+			i: data
+			p: @pos++
+		}
+		@resendUpdate()
+		@inflight_op_sent = Date.now()
+
+	resendUpdate: () ->
+		assert(@inflight_op?)
 		DocUpdaterClient.sendUpdate(
 			@project_id, @doc_id
 			{
 				doc: @doc_id
-				op: [@inflight_op = {
-					i: data
-					p: @pos++
-				}]
+				op: [@inflight_op]
 				v: @version
 				meta:
 					source: @client_id
+				dupIfSource: [@client_id]
 			}
 		)
-		@inflight_op_sent = Date.now()
+		@update_timer = setTimeout () =>
+			console.log "[#{new Date()}] \t[#{@client_id.slice(0,4)}] WARN: Resending update after 5 seconds"
+			@resendUpdate()
+		, 5000

 	processReply: (update) ->
-		if update.error?
-			throw new Error("Error from server: '#{update.error}'")
-		assert(update.op.v == @version, "Op version from server is not increasing by 1 each time")
+		if update.op.v != @version
+			if update.op.v < @version
+				console.log "[#{new Date()}] \t[#{@client_id.slice(0,4)}] WARN: Duplicate ack (already seen version)"
+				return
+			else
+				throw new Error("version jumped ahead")
 		@version++
 		if update.op.meta.source == @client_id
-			@counts.local_updates++
-			@inflight_op = null
-			delay = Date.now() - @inflight_op_sent
-			@counts.max_delay = Math.max(@counts.max_delay, delay)
-			@continue()
+			if @inflight_op?
+				@counts.local_updates++
+				@inflight_op = null
+				clearTimeout @update_timer
+				delay = Date.now() - @inflight_op_sent
+				@counts.max_delay = Math.max(@counts.max_delay, delay)
+				@continue()
+			else
+				console.log "[#{new Date()}] \t[#{@client_id.slice(0,4)}] WARN: Duplicate ack"
 		else
 			assert(update.op.op.length == 1)
 			@counts.remote_updates++
@@ -136,7 +155,7 @@
 			version = body.version

 			clients = []
-			for pos in [1, 2, 3, 4, 5]
+			for pos in [1, 2]
 				do (pos) ->
 					client = new StressTestClient({doc_id, project_id, content, pos: pos, version: version, updateDelay: UPDATE_DELAY})
 					clients.push client


use kinds for example

use kinds for example

--- src/games/example.coffee
+++ src/games/example.coffee
@@ -3,13 +3,11 @@

 me = {
   name: 'me'
-  shape: 'turtle'
   kind: 'turtle'
   position: [1,1]
   stroke: 'black'
   fill: 'blue'
   actions: { first: ['left', 'forward'], repeat: ['forward', 'forward', 'right'], interrupt: ['reverse', 'right'] }
-  editable: true
 }

 yu = my.dup me, {
@@ -21,13 +19,11 @@

 exit = {
   name: 'exit'
-  shape: 'diamond'
   kind: 'gate'
   position: [5,1]
   stroke: 'maroon'
   fill: 'red'
   actions: {interrupt: ['_victory']}
-  obstruction: false
 }

 exports.game = {


Added debug logging for config variables

--- pimatic-smartmeter.coffee
+++ pimatic-smartmeter.coffee
@@ -53,10 +53,11 @@

       super()

-
-      if @config.debug
-        env.logger.debug("Smartmeter Config : \"#{config}\"")
-        # env.logger.debug("Smartmeter Config : \"#{config}\"")
+      env.logger.debug ("Smartmeter portName : \"#{@portName}\"")
+      env.logger.debug ("Smartmeter baudRate : \"#{@baudRate}\"")
+      env.logger.debug ("Smartmeter dataBits : \"#{@dataBits}\"")
+      env.logger.debug ("Smartmeter parity : \"#{@parity}\"")
+      env.logger.debug ("Smartmeter stopBits : \"#{@stopBits}\"")

       P1DataStream = require "./p1meterdata"


Optional BeanWrapper Support

Optional BeanWrapper Support

--- web/scripts/referenceUI.coffee
+++ web/scripts/referenceUI.coffee
@@ -173,7 +173,7 @@
             msg = ''
             finished_cb = ->
                 jQuery('#abortCode').hide(500 , -> jQuery('#runCode').show())
-            codeland.doppioAPI.run(sandBoxEditor.getStudentCode(),'', finished_cb)
+            codeland.doppioAPI.run(sandBoxEditor.getStudentCode(),null, finished_cb)

             e.preventDefault()
             return


LOG removed

LOG removed

--- source/Art/Engine/Elements/Widgets/ScrollElement.coffee
+++ source/Art/Engine/Elements/Widgets/ScrollElement.coffee
@@ -342,7 +342,6 @@
   animateToValidScrollPosition: (desiredOffset = 0)->
     {scrollPosition} = @
     boundedScrollPosition = @boundSp scrollPosition + desiredOffset
-    log {boundedScrollPosition}
     global.scrollElement = @
     if boundedScrollPosition != scrollPosition
       @animators = merge originialAnimators = @animators,


Attempt to make code editor background image preloading more reliable

https://github.com/codecombat/codecombat/commit/624e4f8b791162ad62d029b1bf4b846ac859bb11
  wasn't fully effective, so this adds some redundancy.

--- app/views/play/level/PlayLevelView.coffee
+++ app/views/play/level/PlayLevelView.coffee
@@ -160,6 +160,9 @@

     if features.china
       @checkTournamentEndInterval = setInterval @checkTournamentEnd.bind(@), 3000
+
+    preloadImages = ['/images/level/code_palette_wood_background.png', '/images/level/code_editor_background_border.png']
+    _.delay (-> $('<img/>')[0].src = img for img in preloadImages), 1000

   getMeta: ->
     link: [


both work, shared code dir also gets scanned => confired, might have to adjust output to signal it is shared code though...

--- src/organizer.coffee
+++ src/organizer.coffee
@@ -140,7 +140,7 @@
       lines.push(if level <= 0 then key else indents.join('')+turnChar+"──"+forkChar+key)
       arguments.callee(branch[key], level+1, parentAry.concat(isLast)) #recurse into key's tree (NB: parentAry.length === level)
     return
-  )(@sanitizedTree, 0, [])
+  )(@sanitizedTree(), 0, [])
   lines.join('\n')


Adding super() in Processor

Adding super() in Processor

--- src/processor.coffee
+++ src/processor.coffee
@@ -6,7 +6,6 @@
   class Processor extends EventEmitter

     constructor: (@runner) ->
-      super()

     process: (context, message) =>
       @emit "started", { context, message }