Fix affiliation in /subscriptions notifications

Outgoing notifications for /subscriptions items always had the
'pubsub:affiliation' set to 'none'. Now it will be set to the correct
value.

--- src/local/operations.coffee
+++ src/local/operations.coffee
@@ -1322,7 +1322,7 @@
                                 subscription.node.indexOf("/user/#{id}/") is 0
                             affiliations = {}
                             async.forEach userSubscriptions, (subscription, cb5) =>
-                                t.getAffiliation subscriber, subscription.node, (err, affiliation) =>
+                                t.getAffiliation subscription.node, subscriber, (err, affiliation) =>
                                     affiliations[subscription.node] = affiliation
                                     cb5(err)
                             , (err) =>


operations: Unsubscribe needs PrivilegedOperation methods

--- src/local/operations.coffee
+++ src/local/operations.coffee
@@ -514,7 +514,7 @@

 ##
 # Not privileged as anybody should be able to unsubscribe him/herself
-class Unsubscribe extends ModelOperation
+class Unsubscribe extends PrivilegedOperation
     transaction: (t, cb) ->
         if @req.node.indexOf("/user/#{@req.actor}/") == 0
             return cb new errors.Forbidden("You may not unsubscribe from your own nodes")
@@ -522,10 +522,12 @@
         async.waterfall [ (cb2) =>
             t.setSubscription @req.node, @req.actor, @req.sender, 'none', cb2
         , (cb2) =>
-            t.getAffiliation @req.node, @req.actor, cb2
-        , (affiliation, cb2) =>
+            @fetchActorAffiliation t, cb2
+        , (cb2) =>
+            @fetchNodeConfig t, cb2
+        , (cb2) =>
             # only decrease if <= defaultAffiliation
-            if isAffiliationAtLeast(@nodeConfig.defaultAffiliation, affiliation) and
+            if isAffiliationAtLeast(@nodeConfig.defaultAffiliation, @actorAffiliation) and
                @actorAffiliation isnt 'outcast'
                 @actorAffiliation = 'none'
                 t.setAffiliation @req.node, @req.actor, 'none', cb2


links including remote sites, and cleanup

--- server/express/lib/server.coffee
+++ server/express/lib/server.coffee
@@ -32,13 +32,36 @@

   app.redirect('index', (req, res) ->
     '/view/welcome-visitors'
-    #res.sendfile("#{argv.r}/server/sinatra/views/static.html")
+  )
+
+  app.redirect('remotefav', (req, res) ->
+    console.log req.params
+    "http://#{req.params[0]}"
   )

   # Get routes

   app.get('/', (req, res) ->
     res.redirect('index')
+  )
+
+  app.get(///^/remote/([a-zA-Z0-9:\.-]+)/([a-z0-9-]+)\.json$///, (req, res) ->
+    getopts = {
+      host: req.params[0]
+      port: 80
+      path: "/#{req.params[1]}.json"
+    }
+    console.log getopts
+    http.get(getopts, (resp) ->
+      responsedata = ''
+      resp.on('data', (chunk) ->
+        responsedata += chunk
+      )
+      resp.on('end', ->
+        console.log responsedata
+        res.json(JSON.parse(responsedata))
+      )
+    )
   )

   app.get('*.json', (req, res) ->
@@ -60,12 +83,6 @@
   )$ ///

   app.get(viewdomain, (req, res) ->
-    elements = req.params[0].split('/')
-    #pages = while ((site = elements.shift()) and (id = elements.shift()))
-    #  if site is 'view' or site is 'my'
-    #    {id}
-    #  else
-    #    {id, site}
     res.sendfile("#{argv.r}/server/sinatra/views/static.html")
   )

@@ -97,17 +114,8 @@
     next()
   )

-  app.get(///^/remote/([a-zA-Z0-9:\.-]+)/([a-z0-9-]+)\.json$///, (req, res)->
-    console.log req.params
-    getopts = {
-      host: req.params[0]
-      port: 80
-      path: req.params[1]
-    }
-    console.log getopts
-    http.get(getopts, (resp) ->
-      res.sendfile(resp.body)
-    )
+  app.get(///^/remote/([a-zA-Z0-9:\.-]+/favicon.png)$///, (req, res) ->
+        res.redirect('remotefav')
   )

   # Put routes
@@ -130,7 +138,8 @@
           for item, index in page.story
             if item.id is action.after
               before = action.after
-          page.story.splice(index, 0, action.item)
+          before += 1
+          page.story.splice(before, 0, action.item)

         when 'remove'
           page.story = (item for item in page.story when item?.id isnt action.id)


Remove transition code from value prefixer

--- lib/value.coffee
+++ lib/value.coffee
@@ -12,12 +12,8 @@
     for prefix, value of decl._autoprefixerValues
       continue if value == decl.value

-      isTransition = prop == 'transition' or prop == 'transition-property'
-      propPrefix   = vendor.prefix(prop)
-
+      propPrefix = vendor.prefix(prop)
       if propPrefix == prefix
-        decl.value = value
-      else if isTransition and value.indexOf('-webkit-filter') != -1
         decl.value = value
       else if propPrefix == '-pie-'
         continue


Fix storing token expiration date

Fix storing token expiration date

--- server/lib/npd.coffee
+++ server/lib/npd.coffee
@@ -22,7 +22,7 @@
     res = await callApiRaw('auth/token', data)
     await Config.set("npd.refreshToken", res.refreshToken)
     await Config.set("npd.token", res.token)
-    await Config.set("npd.tokenExpireIn", res.tokenExpireIn)
+    await Config.set("npd.tokenExpireIn", new Date(res.tokenExpireIn))

 getToken = () ->
     tokenExpireIn = await Config.get("npd.tokenExpireIn")


Add Copyright Header

Add Copyright Header

--- src/model.coffee
+++ src/model.coffee
@@ -1,9 +1,9 @@
-###
+###!
 # # Epic Model
 #
-# Represent data like a boss.
-#
+# @author [Pascal Hertleif](https://github.com/killercup)
 # @version 0.3.3
+# @license MIT
 ###
 angular.module('EpicModel', [
 ])


OPCE6: position using directly pointers to word + offset and not position in Rte._rte.words array + offset

--- lib/y-rte.coffee
+++ lib/y-rte.coffee
@@ -41,6 +41,11 @@
       if array[index].equals selection
         array.pop(index)
         break
+
+  # Get index of word in rte list
+  index: (rte) ->
+    return rte._rte.words.indexOf(@)
+

 # A class describing a selection with a style (bold, italic, …)
 class Selection
@@ -64,12 +69,17 @@

       @rte = rte

-      @startPos = @_relativeFromAbsolute start
-      @endPos = @_relativeFromAbsolute end
+      retStart = @_relativeFromAbsolute start
+      retEnd = @_relativeFromAbsolute end
+
+      @startPos = retStart
+      @endPos = retEnd
       @setStyle style

-      @left = @rte.getWord @startPos.word
-      @right = @rte.getWord @endPos.word
+      @left = @rte.getWord retStart.word
+      @leftPos = retStart.pos
+      @right = @rte.getWord retEnd.word
+      @rightPos = retEnd.pos

       @left.left.push @
       @right.right.push @
@@ -107,10 +117,10 @@
   # @param [Selection] s the selection to compare to this
   #
   equals: (selection)->
-    @startPos.word == selection.startPos.word and
-    @startPos.pos == selection.startPos.pos and
-    @endPos.word == selection.endPos.word and
-    @endPos.pos == selection.endPos.pos
+    @left == selection.left and
+    @leftPos == selection.leftPos and
+    @right == selection.right and
+    @rightPos == selection.rightPos

   # Compares *the bounds* of two selections
   #
@@ -139,8 +149,8 @@
   #
   # @param [Selection] selection the selection to compare to this
   atLeftOf: (selection) ->
-    (@endPos.word == selection.startPos.word and
-     @endPos.pos == selection.startPos.pos)
+    (@right == selection.left and
+     @rightPos == selection.leftPos)

   #TODO
   setStyle: (@style) ->
@@ -203,8 +213,11 @@
     newSel.unbind()
     newSel.startPos = @startPos
     newSel.endPos = @endPos
+
     newSel.left = @left
+    newSel.leftPos = @leftPos
     newSel.right = @right
+    newSel.rightPos = @rightPos
     newSel.bind()

     newSel
@@ -355,30 +368,29 @@
   deleteSel: (selection) ->
     if not selection.isValid()
       throw new Error "Invalid selection"
-    if not 0 <= selection.startPos.loc <= @getWord(selection.startPos.word).word.length
-      throw new Error "Invalid selection"
-    if not 0 <= selection.endPos.loc <= @getWord(selection.endPos.word).word.length
-      throw new Error "Invalid selection"
-
-    start = selection.startPos.word
-    end = selection.endPos.word
-
-    newLeft = @getWord(start).word.substring(0, selection.startPos.pos)
-    newRight = @getWord(end).word.substring(selection.endPos.pos)
-
-    if start == end
-      @setWord(start, newLeft + newRight)
+
+    left = selection.left
+    right = selection.right
+
+    leftIndex = left.index @
+    rightIndex = right.index @
+
+    newLeft = left.word.substring(0, selection.leftPos)
+    newRight = right.word.substring(selection.rightPos)
+
+    if left == right
+      @setWord(leftIndex, newLeft + newRight)

       # delete the words in between
-      @deleteWords(start+1, end)
-    else
-      @setWord(start, newLeft)
-      @setWord(end, newRight)
+      @deleteWords(leftIndex+1, rightIndex)
+    else
+      @setWord(leftIndex, newLeft)
+      @setWord(rightIndex, newRight)

       # delete the words in between
-      @deleteWords(start+1, end)
+      @deleteWords(leftIndex+1, rightIndex)
       # merge
-      @merge(start)
+      @merge(leftIndex)

   # Insert text at position
   #
@@ -468,6 +480,7 @@
     # Link the boundary words to selection
     leftWord = selection.left
     rightWord = selection.right
+
     # Merge left…
     for tmpSelection in leftWord.right when tmpSelection
       tmpSelection.merge selection
@@ -510,11 +523,10 @@
   removeSel: (selection) ->
     index = 0
     array = @_rte.selections
-    for element in array
-      if (element.equals(selection) and (element.style == selection.style))
+    for index in [0..array.length-1]
+      if (array[index].equals(selection) and (array[index].style == selection.style))
         array.splice index, 1
         break
-      index += 1

     # unbind selection
     selection.unbind


Webserver: Handle null parameter

Webserver: Handle null parameter

--- servers/lib/server/client.coffee
+++ servers/lib/server/client.coffee
@@ -9,6 +9,7 @@

 isInAppRoute = (name)->
+  return no  unless name
   [firstLetter] = name
   if /^\d/.test firstLetter then no
   else if firstLetter.toUpperCase() is firstLetter then yes


zoom/pan disables tracking

zoom/pan disables tracking

--- src/ui.coffee
+++ src/ui.coffee
@@ -314,6 +314,7 @@
     updateSymbols()
     showLocation()
     renderScale()
+    positionDisableTracking()

   zoom.on('zoomend', renderSymbols)
   d3.select(window).on('resize', resize)
@@ -334,6 +335,11 @@
       if inside(location.pos, db.bbox)
         centerAt(location.pos)

+  positionDisableTracking = ->
+    if locationMode == LOCATION_TRACK
+      locationMode = LOCATION_SHOW
+      locationbuttong.classed('tracking', false)
+
   positionHide = ->
     location = null
     showLocation()


fixed code style on servers.lib.server.google_callback.coffee

--- servers/lib/server/google_callback.coffee
+++ servers/lib/server/google_callback.coffee
@@ -1,35 +1,35 @@
 {
   redirectOauth
   saveOauthToSession
-}                  = require './helpers'
-{google}           = KONFIG
-http               = require "https"
-querystring        = require 'querystring'
-provider           = "google"
+}           = require './helpers'
+{ google }  = KONFIG
+http        = require 'https'
+querystring = require 'querystring'
+provider    = 'google'

 module.exports = (req, res) ->
   access_token  = null
   refresh_token = null
-  {code}        = req.query
-  {clientId}    = req.cookies
+  { code }      = req.query
+  { clientId }  = req.cookies
   {
     client_id
     client_secret
     redirect_uri
   }            = google

-  return redirectOauth "No code in query", req, res, {provider}  unless code
+  return redirectOauth 'No code in query', req, res, { provider }  unless code

   # Get user info with access token
-  fetchUserInfo = (userInfoResp)->
-    rawResp = ""
-    userInfoResp.on "data", (chunk) -> rawResp += chunk
-    userInfoResp.on "end", ->
+  fetchUserInfo = (userInfoResp) ->
+    rawResp = ''
+    userInfoResp.on 'data', (chunk) -> rawResp += chunk
+    userInfoResp.on 'end', ->
       try
         response = JSON.parse rawResp
-        {id, email, given_name, family_name} = response
+        { id, email, given_name, family_name } = response
       catch e
-        redirectOauth "Error getting id", req, res, {provider}
+        redirectOauth 'Error getting id', req, res, { provider }

       if id
         googleResp = {
@@ -43,45 +43,45 @@
           profile      : response
         }

-        saveOauthToSession googleResp, clientId, provider, (err)->
+        saveOauthToSession googleResp, clientId, provider, (err) ->
           if err
-            return redirectOauth "Error saving oauth info", req, res, {provider}
+            return redirectOauth 'Error saving oauth info', req, res, { provider }

-          redirectOauth null, req, res, {provider}
+          redirectOauth null, req, res, { provider }

-  authorizeUser = (authUserResp)->
-    rawResp = ""
+  authorizeUser = (authUserResp) ->
+    rawResp = ''
     authUserResp.setEncoding('utf8')
-    authUserResp.on "data", (chunk) -> rawResp += chunk
-    authUserResp.on "end", ->
+    authUserResp.on 'data', (chunk) -> rawResp += chunk
+    authUserResp.on 'end', ->
       try
         tokenInfo = JSON.parse rawResp
       catch e
-        redirectOauth "Error getting access token", req, res, {provider}
+        redirectOauth 'Error getting access token', req, res, { provider }

-      {access_token, refresh_token} = tokenInfo
+      { access_token, refresh_token } = tokenInfo
       if access_token
         options =
-          host   : "www.googleapis.com"
+          host   : 'www.googleapis.com'
           path   : "/oauth2/v2/userinfo?alt=json&access_token=#{access_token}"
-          method : "GET"
+          method : 'GET'
         r = http.request options, fetchUserInfo
         r.end()
       else
-        redirectOauth "No access token", req, res, {provider}
+        redirectOauth 'No access token', req, res, { provider }

   postData   = querystring.stringify {
     code,
     client_id,
     client_secret,
     redirect_uri,
-    grant_type : "authorization_code"
+    grant_type : 'authorization_code'
   }

   options   =
-    host    : "accounts.google.com"
-    path    : "/o/oauth2/token"
-    method  : "POST"
+    host    : 'accounts.google.com'
+    path    : '/o/oauth2/token'
+    method  : 'POST'
     headers : {
       'Content-Type'  : 'application/x-www-form-urlencoded'
       'Content-Length': postData.length,
@@ -92,4 +92,4 @@
   r.write postData
   r.end()

-  r.on 'error', (e)-> console.error 'problem with request: ' + e.message
+  r.on 'error', (e) -> console.error 'problem with request: ' + e.message


Always remove category.

Always remove category.

--- app/core/Tracker.coffee
+++ app/core/Tracker.coffee
@@ -166,14 +166,15 @@
     # Trimming properties we don't use internally
     # TODO: delete properites.level for 'Saw Victory' after 2/8/15.  Should be using levelID instead.
     if event in ['Clicked Start Level', 'Inventory Play', 'Heard Sprite', 'Started Level', 'Saw Victory', 'Click Play', 'Choose Inventory', 'Homepage Loaded', 'Change Hero']
-      delete properties.category
       delete properties.label
     else if event in ['Loaded World Map', 'Started Signup', 'Finished Signup', 'Login', 'Facebook Login', 'Google Login', 'Show subscription modal']
-      delete properties.category
+
+

     # SnowPlow
     snowplowAction = event.toLowerCase().replace(/[^a-z0-9]+/ig, '_')
     properties.user = me.id
+    delete properties.category
     #console.log "SnowPlow", snowplowAction, properties
     window.snowplow 'trackUnstructEvent',
       schema: 'iglu:com.codecombat/' + snowplowAction + '/jsonschema/1-0-0'


Use dynamic for `in`

Use dynamic for `in`

--- src/nodes.coffee
+++ src/nodes.coffee
@@ -79,6 +79,18 @@

   @collection: (ast, model, controller) ->
     collection = get(model, ast.arguments[0])
+    new Dynamic(ast, collection, model, controller)
+
+  @in: (ast, model, controller) ->
+    collection = new Serenade.Collection([])
+    update = ->
+      subModel = get(model, ast.arguments[0])
+      if subModel
+        collection.update([subModel])
+      else
+        collection.update([])
+    update()
+    model.bind? "change:#{ast.arguments[0]}", update
     new Dynamic(ast, collection, model, controller)

   constructor: (@ast, @collection, @model, @controller) ->
@@ -244,36 +256,6 @@
     else
       @anchor

-class In
-  constructor: (@ast, @model, @controller) ->
-    @anchor = Serenade.document.createTextNode('')
-    @model.bind? "change:#{@ast.arguments[0]}", @build
-
-  build: =>
-    @removeNodes()
-    subModel = get(@model, @ast.arguments[0])
-    @nodes = (Nodes.compile(child, subModel, @controller) for child in @ast.children)
-    node.insertAfter(@nodes[i-1]?.lastElement() or @anchor) for node, i in @nodes
-
-  append: (inside) ->
-    inside.appendChild(@anchor)
-    @build()
-
-  insertAfter: (after) ->
-    after.parentNode.insertBefore(@anchor, after.nextSibling)
-    @build()
-
-  remove: ->
-    @removeNodes()
-    @anchor.parentNode.removeChild(@anchor)
-
-  removeNodes: ->
-    node.remove() for node in @nodes if @nodes
-    @nodes = undefined
-
-  lastElement: ->
-    @nodes[@nodes.length - 1].lastElement()
-
 Nodes =
   compile: (ast, model, controller) ->
     switch ast.type
@@ -284,7 +266,7 @@
           when "view" then new Node.view(ast, model, controller)
           when "collection" then new Dynamic.collection(ast, model, controller)
           when "if" then new If(ast, model, controller)
-          when "in" then new In(ast, model, controller)
+          when "in" then new Dynamic.in(ast, model, controller)
           else new Node.helper(ast, model, controller)
       else throw SyntaxError "unknown type '#{ast.type}'"


show the cancel button for not_running builds

--- js/inner/build.coffee
+++ js/inner/build.coffee
@@ -138,6 +138,8 @@
         false
       else
         switch @lifecycle()
+          when "not_running"
+            true
           when "running"
             true
           when "queued"


working on tab bar selection.

working on tab bar selection.

--- jmbo/view.coffee
+++ jmbo/view.coffee
@@ -119,6 +119,9 @@

 class TabBarView extends Backbone.View
   className: 'jmbo-view-tab-bar-view'
+
+  events:
+    'click li': setSelected

   initialize: ->
     @template = _.template """
@@ -142,6 +145,9 @@
     @$el.html @template(collection: @collection)
     return @el

+  setSelected: =>
+    l 'pew'
+

 class TabBarControllerView extends Backbone.View


Desbloqueia chamadas caso operador esteja offline


allow for no scopes in the github auth url

--- js/libs/github.coffee
+++ js/libs/github.coffee
@@ -15,10 +15,15 @@

     path = "https://github.com/login/oauth/authorize"
     client_id = window.renderContext.githubClientId
-    scope = scope.join ","
-    scope = encodeURIComponent(scope)

     l = window.location
     redirect = "#{l.protocol}//#{l.host}/auth/github?return-to=#{destination}"
     redirect = encodeURIComponent(redirect)
-    "#{path}?client_id=#{client_id}&redirect_uri=#{redirect}&scope=#{scope}"
+
+    url =  "#{path}?client_id=#{client_id}&redirect_uri=#{redirect}"
+    if scope
+      scope = scope.join ","
+      scope = encodeURIComponent(scope)
+      url += "&scope=#{scope}"
+
+    url


style($tests): change running specs helper description

The specs message are now more explicit with the context enclosed
between commas.

--- specs/support/helpers/spec_helpers.coffee
+++ specs/support/helpers/spec_helpers.coffee
@@ -2,7 +2,6 @@
 exports.createEnv = (block, context) ->
   env = spectacular.env.clone()
   env.options.noColors = true
-  env.options.showSource = false
   context.results = ''

   spyOn(env, 'load').andCallThrough ->
@@ -62,33 +61,33 @@
 exports.runningSpecs = (desc) ->

   shouldFailWith: (re, block) ->
-    describe "running specs with #{desc}", ->
+    describe "running specs, with #{desc}", ->
       before (async) ->
         @env = createEnv block, this
         @reporter = createReporter @env, this, async
         runEnvExpectingNormalTermination @env, this, async

-      it 'should fail, status', -> @status.should be 1
-      it 'should fail, results', -> @results.should match re
+      it ', should fail, status', -> @status.should be 1
+      it ', should fail, results', -> @results.should match re

   shouldSucceedWith: (re, block) ->
-    describe "running specs with #{desc}", ->
+    describe "running specs, with #{desc}", ->
       before (async) ->
         @env = createEnv block, this
         @reporter = createReporter @env, this, async
         runEnvExpectingNormalTermination @env, this, async

-      it 'should succeed, status', -> @status.should be 0
-      it 'should succeed, results', -> @results.should match re
+      it ', should succeed, status', -> @status.should be 0
+      it ', should succeed, results', -> @results.should match re

   shouldStopWith: (re, block) ->
-    describe "running specs with #{desc}", ->
+    describe "running specs, with #{desc}", ->
       before (async) ->
         @env = createEnv block, this
         @reporter = createReporter @env, this, async
         runEnvExpectingInterruption @env, this, async

-      it 'should stop, error message', ->
+      it ', should stop, error message', ->
         @reason.message.should match re

 exports.environmentMethod = (method) ->


rename class as PreferEnglishOperators

rename class as PreferEnglishOperators

--- src/prefer_english_operators.coffee
+++ src/prefer_english_operators.coffee
@@ -1,4 +1,4 @@
-module.exports = class EnglishOperators
+module.exports = class PreferEnglishOperators
   rule:
     name: 'english_operators'
     description: 'This rule encourages to use english style operators.'


Spec file now uses internal 'unit' module


Fix named IIFE.

Fix named IIFE.

--- js2coffee.coffee
+++ js2coffee.coffee
@@ -94,17 +94,21 @@
     @estraverse().replace root,
       enter: (node, parent) ->
         self.controller = this
+        self.node = node
         depth += 1
         fn = self["#{node.type}"]
-        res = (fn.apply(self, [ node, parent, (=> @skip()), (=> @break()) ]) if fn)
+        self.onBeforeEnter?(node, depth, fn, res)
+        res = (fn.apply(self, [ node, parent ]) if fn)
         self.onEnter?(node, depth, fn, res)
         res
       leave: (node, parent) ->
         self.controller = this
+        self.node = node
+        depth -= 1
         fn = self["#{node.type}Exit"]
-        res = (fn.apply(self, [ node, parent, (=> @skip()), (=> @break()) ]) if fn)
+        self.onBeforeExit?(node, depth, fn, res)
+        res = (fn.apply(self, [ node, parent ]) if fn)
         self.onExit?(node, depth, fn, res)
-        depth -= 1
         res
     root

@@ -133,16 +137,18 @@
   ###

   pushStack: (node) ->
-    @parent = @scope
+    [ oldScope, oldCtx ] = [ @scope, @ctx ]
+    # TODO remove @parent = @scope
     @scopes.push [ node, @ctx ]
     @ctx = clone(@ctx)
     @scope = node
-    @onScopeEnter?(node, @ctx)
+    @onScopeEnter?(@scope, @ctx, oldScope, oldCtx)

   popStack: (node) ->
-    @onScopeExit?(node, @ctx)
+    [ oldScope, oldCtx ] = [ @scope, @ctx ]
     [ @scope, @ctx ] = @scopes.pop()
-    @parent = if @scopes.length > 1 then @scopes[@scopes.length-2]
+    # TODO remove @parent = if @scopes.length > 1 then @scopes[@scopes.length-2]
+    @onScopeExit?(@scope, @ctx, oldScope, oldCtx)
     node

   ###*
@@ -171,45 +177,59 @@
 class OtherTransformer extends TransformerBase
   Program: (node) ->
     @pushStack node
+
   ProgramExit: (node) ->
-    @popStack()
+    @popStack node
+
   BlockStatementExit: (node) ->
     @removeEmptyStatementsFromBody node
+
   FunctionDeclaration: (node, parent) ->
     throw new Error("FnDecl: Not supposed to happen") # it's supposed to be eaten by fixScope
-  FunctionDeclarationExit: (node) ->
-    @popStack()
+
   FunctionExpression: (node, parent) ->
     throw new Error("NamedFnExpr: Not supposed to happen") if node.id # fixScope should've gotten this case
     @pushStack node.body
     @removeUndefinedParameter node
+
   FunctionExpressionExit: (node) ->
     @popStack()
+
   SwitchStatement: (node) ->
     @consolidateCases node
+
   SwitchCase: (node) ->
-    @removeBreaksFromConsequents node
+    @removeBreaksFromConsequents(node)
+
   CallExpression: (node) ->
-    @parenthesizeCallee node
+    @parenthesizeCallee(node)
+
   MemberExpression: (node) ->
     @transformThisToAtSign(node)
     @replaceWithPrototype(node) or
-    @parenthesizeObjectIfFunction node
+    @parenthesizeObjectIfFunction(node)
+
   CoffeePrototypeExpression: (node) ->
     @transformThisToAtSign(node)
+
   Identifier: (node) ->
-    @escapeUndefined node
+    @escapeUndefined(node)
+
   BinaryExpression: (node) ->
     @updateBinaryExpression node
+
   UnaryExpression: (node) ->
     @updateVoidToUndefined node
+
   LabeledStatement: (node, parent) ->
     @warnAboutLabeledStatements node, parent
+
   WithStatement: (node) ->
     @syntaxError node, "'with' is not supported in CoffeeScript"
+
   VariableDeclarator: (node) ->
     @addShadowingIfNeeded(node)
-    @addExplicitUndefinedInitializer node
+    @addExplicitUndefinedInitializer(node)

   ###
   # Remove `{type: 'EmptyStatement'}` from the body.
@@ -414,37 +434,40 @@
 ###

 class FunctionTransformer extends TransformerBase
-  onScopeEnter: (node) ->
-    @prebody = []
-
-  onScopeExit: (node) ->
-    if @prebody.length
-      @scope.body = @prebody.concat(@scope.body)
+  onScopeEnter: (scope, ctx, oldScope, oldCtx) ->
+    ctx.prebody = []
+
+  onScopeExit: (scope, ctx, oldScope, oldCtx) ->
+    if oldCtx.prebody.length
+      scope.body = oldCtx.prebody.concat(scope.body)

   Program: (node) ->
     @pushStack node
+    node

   ProgramExit: (node) ->
     @popStack()
+    node

   FunctionDeclaration: (node) ->
-    @pushStack node.body
-    @skip()
-    @prebody.push @buildFunctionDeclaration(node)
+    @ctx.prebody.push @buildFunctionDeclaration(node)
+    @pushStack(node)
+    return
+
+  FunctionDeclarationExit: (node) ->
+    @popStack(node)
     { type: 'EmptyStatement' }

   FunctionExpression: (node) ->
-    @pushStack node.body
     return unless node.id?
-    @skip()
-    @prebody.push @buildFunctionDeclaration(node)
+    @ctx.prebody.push @buildFunctionDeclaration(node)
+    @pushStack(node)
+    return
+
+  FunctionExpressionExit: (node) ->
+    return unless node.id?
+    @popStack(node)
     { type: 'Identifier', name: node.id.name }
-
-  FunctionDeclarationExit: (node) ->
-    @popStack()
-
-  FunctionExpressionExit: (node) ->
-    @popStack()

   ###
   # Returns a `a = -> ...` statement out of a FunctionDeclaration node.
@@ -1051,10 +1074,10 @@
     else
       ""

-  TransformerBase::onEnter = (node, depth, fn) ->
+  TransformerBase::onBeforeEnter = (node, depth, fn) ->
     console.log Array(depth+1).join("  "), node.type, (if fn then "*" else ""), isDead(@ast)

-  TransformerBase::onExit = (node, depth, fn) ->
+  TransformerBase::onBeforeExit = (node, depth, fn) ->
     console.log Array(depth+1).join("  "), ""+node.type+"Exit", (if fn then "*" else ""), isDead(@ast)

-js2coffee.debug()
+# js2coffee.debug()


style(request): remove unneeded parentheses

--- src/vk.coffee
+++ src/vk.coffee
@@ -23,7 +23,7 @@
     xhr.onload = ->
       callback JSON.parse @responseText

-    serializedParams = ( "#{encodeURIComponent(key)}=#{encodeURIComponent(val)}" for own key, val of params ).join "&"
+    serializedParams = ( "#{encodeURIComponent key}=#{encodeURIComponent val}" for own key, val of params ).join "&"
     xhr.open "POST", url
     xhr.setRequestHeader "Content-Type", "application/x-www-form-urlencoded"
     xhr.send serializedParams


Observing straight away so it will work in FF, TODO: Fix leaks later

--- source/runtime.coffee
+++ source/runtime.coffee
@@ -127,7 +127,7 @@

 bindObservable = (element, value, context, update) ->
   observable = Observable(value, context)
-  update observable()
+  # update observable()

   observe = ->
     observable.observe update
@@ -136,10 +136,14 @@
   unobserve = ->
     observable.stopObserving update

+  observe()
+
+  # TODO: This may have trouble in some browsers...
+  # TODO: Find a better way to clean up
   # We want to keep our binding active whenever the element is in the DOM
   # but clean up when the element leaves the DOM
-  element.addEventListener("DOMNodeInsertedIntoDocument", observe)
-  element.addEventListener("DOMNodeRemovedFromDocument", unobserve)
+  # element.addEventListener("DOMNodeInsertedIntoDocument", observe)
+  # element.addEventListener("DOMNodeRemovedFromDocument", unobserve)

   return element


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/point.coffee
+++ src/point.coffee
@@ -49,7 +49,8 @@
     clamp: (n) ->
       @copy().clamp()

-    clamp$: () ->
+    clamp$: (n) ->
+      i

     ###*
     Creates a copy of this point.


Added tests for hasMany

Added tests for hasMany

--- test/generators/relational/has_many.coffee
+++ test/generators/relational/has_many.coffee
@@ -229,6 +229,194 @@
                 assert.ok(_.isEqual(_.omit(updated_moved_reverse.toJSON(), 'owner_id'), _.omit(moved_reverse_json, 'owner_id')), "Set the id:. Expected: #{util.inspect(_.omit(updated_moved_reverse.toJSON(), 'owner_id'))}. Actual: #{util.inspect(_.omit(moved_reverse_json, 'owner_id'))}")
                 done()

+    it 'Can manually add a relationship by related_id (hasOne)', (done) ->
+      # TODO: implement embedded find
+      return done() if embed
+
+      Owner.cursor().include('reverses').toModel (err, owner) ->
+        assert.ok(!err, "No errors: #{err}")
+        assert.ok(owner, 'found owners')
+        reverses = owner.get('reverses').models
+        assert.equal(reverses.length, 2, "loaded correct models.")
+        reverse_ids = (reverse.id for reverse in reverses)
+
+        Owner.cursor({id: {$ne: owner.id}}).include('reverses').toModel (err, another_owner) ->
+          assert.ok(!err, "No errors: #{err}")
+          assert.ok(another_owner, "loaded another model.")
+          assert.ok(owner.id isnt another_owner.id, "loaded a model with a different id.")
+
+          another_reverses = another_owner.get('reverses').models
+          assert.equal(another_reverses.length, 2, "loaded correct models.")
+          another_reverse_ids = (reverse.id for reverse in another_reverses)
+          moved_reverse_id = another_reverse_ids[0]
+          moved_reverse_json = another_reverses[0].toJSON()
+
+          owner.patchAdd 'reverses', moved_reverse_json, (err) ->
+            assert.ok(!err, "No errors: #{err}")
+            updated_reverses = owner.get('reverses').models
+            updated_reverse_ids = (reverse.id for reverse in updated_reverses)
+
+            assert.equal(updated_reverse_ids.length, 3, "Moved the reverse. Expected: #{3}. Actual: #{updated_reverse_ids.length}")
+            assert.ok(_.contains(updated_reverse_ids, moved_reverse_id), "Moved the reverse_id")
+
+            Owner.cursor({id: another_owner.id}).include('reverses').toModel (err, another_owner) ->
+              assert.ok(!err, "No errors: #{err}")
+              updated_another_reverses = another_owner.get('reverses').models
+              updated_another_reverse_ids = (reverse.id for reverse in updated_another_reverses)
+              assert.equal(updated_another_reverse_ids.length, 1, "Moved the reverse from previous. Expected: #{1}. Actual: #{updated_another_reverse_ids.length}")
+              assert.ok(!_.contains(updated_another_reverse_ids, moved_reverse_id), "Moved the reverse_id from previous")
+
+              owner.get 'reverses', (err, updated_reverses) ->
+                assert.ok(!err, "No errors: #{err}")
+                assert.equal(updated_reverses.length, 3, "loaded correct models.")
+                updated_reverse_ids = (reverse.id for reverse in updated_reverses)
+
+                assert.equal(updated_reverse_ids.length, 3, "Moved the reverse")
+                assert.ok(_.contains(updated_reverse_ids, moved_reverse_id), "Moved the reverse_id")
+                updated_moved_reverse = updated_reverses[_.indexOf(updated_reverse_ids, moved_reverse_id)]
+
+                assert.ok(_.isEqual(_.omit(updated_moved_reverse.toJSON(), 'owner_id'), _.omit(moved_reverse_json, 'owner_id')), "Set the id:. Expected: #{util.inspect(_.omit(updated_moved_reverse.toJSON(), 'owner_id'))}. Actual: #{util.inspect(_.omit(moved_reverse_json, 'owner_id'))}")
+                done()
+
+    it 'Can manually add a relationship by related_id (hasOne)', (done) ->
+      # TODO: implement embedded find
+      return done() if embed
+
+      Owner.cursor().include('reverses').toModel (err, owner) ->
+        assert.ok(!err, "No errors: #{err}")
+        assert.ok(owner, 'found owners')
+        reverses = owner.get('reverses').models
+        assert.equal(reverses.length, 2, "loaded correct models.")
+        reverse_ids = (reverse.id for reverse in reverses)
+
+        Owner.cursor({id: {$ne: owner.id}}).include('reverses').toModel (err, another_owner) ->
+          assert.ok(!err, "No errors: #{err}")
+          assert.ok(another_owner, "loaded another model.")
+          assert.ok(owner.id isnt another_owner.id, "loaded a model with a different id.")
+
+          another_reverses = another_owner.get('reverses').models
+          assert.equal(another_reverses.length, 2, "loaded correct models.")
+          another_reverse_ids = (reverse.id for reverse in another_reverses)
+          moved_reverse_id = another_reverse_ids[0]
+          moved_reverse_json = another_reverses[0].toJSON()
+
+          owner.patchAdd 'reverses', another_reverses[0], (err) ->
+            assert.ok(!err, "No errors: #{err}")
+            updated_reverses = owner.get('reverses').models
+            updated_reverse_ids = (reverse.id for reverse in updated_reverses)
+
+            assert.equal(updated_reverse_ids.length, 3, "Moved the reverse. Expected: #{3}. Actual: #{updated_reverse_ids.length}")
+            assert.ok(_.contains(updated_reverse_ids, moved_reverse_id), "Moved the reverse_id")
+
+            Owner.cursor({id: another_owner.id}).include('reverses').toModel (err, another_owner) ->
+              assert.ok(!err, "No errors: #{err}")
+              updated_another_reverses = another_owner.get('reverses').models
+              updated_another_reverse_ids = (reverse.id for reverse in updated_another_reverses)
+              assert.equal(updated_another_reverse_ids.length, 1, "Moved the reverse from previous. Expected: #{1}. Actual: #{updated_another_reverse_ids.length}")
+              assert.ok(!_.contains(updated_another_reverse_ids, moved_reverse_id), "Moved the reverse_id from previous")
+
+              owner.get 'reverses', (err, updated_reverses) ->
+                assert.ok(!err, "No errors: #{err}")
+                assert.equal(updated_reverses.length, 3, "loaded correct models.")
+                updated_reverse_ids = (reverse.id for reverse in updated_reverses)
+
+                assert.equal(updated_reverse_ids.length, 3, "Moved the reverse")
+                assert.ok(_.contains(updated_reverse_ids, moved_reverse_id), "Moved the reverse_id")
+                updated_moved_reverse = updated_reverses[_.indexOf(updated_reverse_ids, moved_reverse_id)]
+
+                assert.ok(_.isEqual(_.omit(updated_moved_reverse.toJSON(), 'owner_id'), _.omit(moved_reverse_json, 'owner_id')), "Set the id:. Expected: #{util.inspect(_.omit(updated_moved_reverse.toJSON(), 'owner_id'))}. Actual: #{util.inspect(_.omit(moved_reverse_json, 'owner_id'))}")
+                done()
+
+    it 'Can manually add a relationship by related_id (belongsTo)', (done) ->
+      # TODO: implement embedded find
+      return done() if embed
+
+      Reverse.findOne (err, reverse) ->
+        assert.ok(!err, "No errors: #{err}")
+        assert.ok(reverse, 'found reverse')
+
+        reverse.get 'owner', (err, owner) ->
+          assert.ok(!err, "No errors: #{err}")
+          assert.ok(owner, "loaded correct model.")
+
+          Owner.cursor({id: {$ne: owner.id}, $one: true}).toJSON (err, another_owner_json) ->
+            assert.ok(!err, "No errors: #{err}")
+            assert.ok(another_owner_json, "loaded another model.")
+            assert.ok(owner.id isnt another_owner_json.id, "loaded a model with a different id.")
+
+            reverse.patchAdd 'owner', another_owner_json.id, (err) ->
+              assert.ok(!err, "No errors: #{err}")
+              updated_owner = reverse.get('owner')
+              assert.ok(updated_owner.id is another_owner_json.id, "Set the id: #{err}. Expected: #{another_owner_json.id}. Actual: #{updated_owner.id}")
+
+              reverse.get 'owner', (err, updated_owner) ->
+                assert.ok(!err, "No errors: #{err}")
+                assert.ok(updated_owner, "loaded another model.")
+
+                assert.ok(_.contains(updated_owner.get('reverse_ids'), reverse.id), "reverse_id is correct.")
+                assert.ok(_.isEqual(updated_owner.toJSON(), another_owner_json), "Set the id: #{err}. Expected: #{util.inspect(another_owner_json)}. Actual: #{util.inspect(updated_owner.toJSON())}")
+                done()
+
+    it 'Can manually add a relationship by related json (belongsTo)', (done) ->
+      # TODO: implement embedded find
+      return done() if embed
+
+      Reverse.findOne (err, reverse) ->
+        assert.ok(!err, "No errors: #{err}")
+        assert.ok(reverse, 'found reverse')
+
+        reverse.get 'owner', (err, owner) ->
+          assert.ok(!err, "No errors: #{err}")
+          assert.ok(owner, "loaded correct model.")
+
+          Owner.cursor({id: {$ne: owner.id}, $one: true}).toJSON (err, another_owner_json) ->
+            assert.ok(!err, "No errors: #{err}")
+            assert.ok(another_owner_json, "loaded another model.")
+            assert.ok(owner.id isnt another_owner_json.id, "loaded a model with a different id.")
+
+            reverse.patchAdd 'owner', another_owner_json, (err) ->
+              assert.ok(!err, "No errors: #{err}")
+              updated_owner = reverse.get('owner')
+              assert.ok(updated_owner.id is another_owner_json.id, "Set the id: #{err}. Expected: #{another_owner_json.id}. Actual: #{updated_owner.id}")
+
+              reverse.get 'owner', (err, updated_owner) ->
+                assert.ok(!err, "No errors: #{err}")
+                assert.ok(updated_owner, "loaded another model.")
+
+                assert.ok(_.contains(updated_owner.get('reverse_ids'), reverse.id), "reverse_id is correct.")
+                assert.ok(_.isEqual(updated_owner.toJSON(), another_owner_json), "Set the id: #{err}. Expected: #{util.inspect(another_owner_json)}. Actual: #{util.inspect(updated_owner.toJSON())}")
+                done()
+
+    it 'Can manually add a relationship by related model (belongsTo)', (done) ->
+      # TODO: implement embedded find
+      return done() if embed
+
+      Reverse.findOne (err, reverse) ->
+        assert.ok(!err, "No errors: #{err}")
+        assert.ok(reverse, 'found reverse')
+
+        reverse.get 'owner', (err, owner) ->
+          assert.ok(!err, "No errors: #{err}")
+          assert.ok(owner, "loaded correct model.")
+
+          Owner.cursor({id: {$ne: owner.id}}).toModel (err, another_owner) ->
+            assert.ok(!err, "No errors: #{err}")
+            assert.ok(another_owner, "loaded another model.")
+            assert.ok(owner.id isnt another_owner.id, "loaded a model with a different id.")
+
+            reverse.patchAdd 'owner', another_owner, (err) ->
+              assert.ok(!err, "No errors: #{err}")
+              updated_owner = reverse.get('owner')
+              assert.ok(updated_owner.id is another_owner.id, "Set the id: #{err}. Expected: #{another_owner.id}. Actual: #{updated_owner.id}")
+
+              reverse.get 'owner', (err, updated_owner) ->
+                assert.ok(!err, "No errors: #{err}")
+                assert.ok(updated_owner, "loaded another model.")
+
+                assert.ok(_.contains(updated_owner.get('reverse_ids'), reverse.id), "reverse_id is correct.")
+                assert.ok(_.isEqual(updated_owner.toJSON(), another_owner_json), "Set the id: #{err}. Expected: #{util.inspect(another_owner_json)}. Actual: #{util.inspect(updated_owner.toJSON())}")
+                done()
+
     it 'Can manually delete a relationship by related_id (hasMany)', (done) ->
       Owner.findOne (err, owner) ->
         assert.ok(!err, "No errors: #{err}")


whitespace changes only

whitespace changes only

--- source/javascripts/mincha.coffee
+++ source/javascripts/mincha.coffee
@@ -62,7 +62,7 @@
       when @hebrew_date.isErebSukkot() || (@hebrew_date.isSukkot() && !@hebrew_date.isHoshanaRaba() && !@hebrew_date.isSheminiAseret()) then 'sukkot-first-days'
       when @hebrew_date.isHoshanaRaba() || @hebrew_date.isSheminiAseret() then 'sukkot-last-days'
       when @hebrew_date.isErebYomKippur() || @hebrew_date.isYomKippur() then 'yom-kippur'
-      when (@hebrew_date.isErebShabbat() && !@hebrew_date.isPurim())|| @hebrew_date.isShabbat() then 'shabbat'
+      when (@hebrew_date.isErebShabbat() && !@hebrew_date.isPurim()) || @hebrew_date.isShabbat() then 'shabbat'
       when @hebrew_date.isTaanit() && !@hebrew_date.is9Ab() then 'taanit'
       when @hebrew_date.isPurim() then 'purim-table'
       when (@hebrew_date.is9Ab() || @hebrew_date.isEreb9Ab()) && !@hebrew_date.isShabbat() then 'chabob'


remove questionable test

remove questionable test

--- spec/atom-spec.coffee
+++ spec/atom-spec.coffee
@@ -11,8 +11,8 @@
       it 'sets the position of the window, and can retrieve the position just set', ->
         atom.setPosition(22, 45)
         expect(atom.getPosition()).toEqual x: 22, y: 45
-        atom.setPosition(-22, -45) # negative coordinates are fine on external displays under OS X
-        expect(atom.getPosition()).toEqual x: -22, y: -45
+        # atom.setPosition(-22, -45) # negative coordinates are fine on external displays under OS X
+        # expect(atom.getPosition()).toEqual x: -22, y: -45

     describe '::getSize and ::setSize', ->
       originalSize = null


Render all PDF files when printing (fix #331)

--- imports/disappear.coffee
+++ imports/disappear.coffee
@@ -5,9 +5,13 @@
 ## This code is written from scratch in CoffeeScript
 ## and with a completely different API designed for `client/messagePDF.coffee`.

+## Whether user is in print dialog, which forces everything visible
+export printing = false
+
 ## appear's `viewable` function adapted from
 ## https://raw.githubusercontent.com/creativelive/appear/master/dist/appear.js
 export visible = (node) ->
+  return true if printing
   rect = node.getBoundingClientRect()
   windowHeight = window.innerHeight || document.documentElement.clientHeight
   windowWidth = window.innerWidth || document.documentElement.clientWidth
@@ -17,7 +21,7 @@
   (rect.right - rect.width) <= 2 * windowWidth
   ## `2 *` and `-` means view anything within one screenful of visible

-tracking = []
+export tracking = []

 checkOne = (tracked) ->
   now = visible tracked.node
@@ -38,9 +42,16 @@
 , 50

 ## Called automatically when needed by `track`
-enable = ->
+enable = (printEverything = true) ->
   window.addEventListener 'resize', check, false
   window.addEventListener 'scroll', check, false
+  if printEverything
+    window.addEventListener 'beforeprint', ->
+      printing = true
+      check()
+    window.addEventListener 'afterprint', ->
+      printing = false
+      check()

 ## Called automatically when needed by `untrack`
 disable = ->


Remove white spaces

Remove white spaces

--- spec/phoneSpec.coffee
+++ spec/phoneSpec.coffee
@@ -11,7 +11,7 @@
 		countryCodeBrazil = '55'
 		countryCodeUSA = '1'
 		# Act
-		# Assert
+		# Assert
 		expect(vtex.phone.countries[countryCodeBrazil]).toBeDefined()
 		expect(vtex.phone.countries[countryCodeUSA]).toBeDefined()

@@ -23,7 +23,7 @@
 			# Act
 			result = vtex.phone.validateInternational(number)

-			# Assert
+			# Assert
 			expect(result.valid).toBe(true)

 		it 'standard formatted number', ->
@@ -50,7 +50,7 @@
 			# Act
 			result = vtex.phone.validateInternational(number)

-			# Assert
+			# Assert
 			expect(result.valid).toBe(true)

 		it 'an average number from Buenos Aires', ->
@@ -73,7 +73,7 @@
 			# Act
 			result = vtex.phone.validateInternational(number)

-			# Assert
+			# Assert
 			expect(result.valid).toBe(true)

 		it 'number with national destination code with 1 digit', ->
@@ -83,13 +83,45 @@
 		it 'number with national destination code with 3 digits', ->
 			# Arrange
 			number = "+598 (447) 87256"
-
+
+	describe 'chilean phone should validate a', ->
+
+		number = ''
+
+		afterEach ->
+			# Act
+			result = vtex.phone.validateInternational(number)
+
+			# Assert
+			expect(result.valid).toBe(true)
+
+		it 'number with destination national code 2', ->
+			# Arrange
+			number = "+56 (2) 98986565"
+
+		it 'number with 7 digits', ->
+			# Arrange
+			number = "+56 (35) 9898656"

-
-
-
-
-
-
-
-
+		it 'number with 6 digits', ->
+			# Arrange
+			number = "+56 (35) 989865"
+
+		it 'number with destination national code 58', ->
+			# Arrange
+			number = "+56 (58) 9898656"
+
+	describe 'american phone should validate a', ->
+
+		number = ''
+
+		afterEach ->
+			# Act
+			result = vtex.phone.validateInternational(number)
+
+			# Assert
+			expect(result.valid).toBe(true)
+
+		it 'number', ->
+			# Arrange
+			number = "+1 (201) 9898656"


:white_check_mark: Keep pending pane upon losing focus

--- spec/tabs-spec.coffee
+++ spec/tabs-spec.coffee
@@ -887,25 +887,17 @@
             expect($(tabBar.tabForItem(editor2)).find('.title')).not.toHaveClass 'temp'

       describe 'when opening views that do not have file paths', ->
-        editor2 = null
-        settingsView = null
-
-        beforeEach ->
-          waitsForPromise ->
-            atom.workspace.open('sample.txt', pending: true).then (o) ->
-              editor2 = o
-
+
+        it 'creates a permanent tab', ->
+          settingsView = null
           waitsForPromise ->
             atom.packages.activatePackage('settings-view').then ->
               atom.workspace.open('atom://config').then (o) ->
                 settingsView = o

-        it 'creates a permanent tab', ->
-          expect(tabBar.tabForItem(settingsView)).toExist()
-          expect($(tabBar.tabForItem(settingsView)).find('.title')).not.toHaveClass 'temp'
-
-        it 'destroys an existing temp tab', ->
-          expect(tabBar.tabForItem(editor2)).not.toExist()
+          runs ->
+            expect(tabBar.tabForItem(settingsView)).toExist()
+            expect($(tabBar.tabForItem(settingsView)).find('.title')).not.toHaveClass 'temp'

       describe 'when editing a file', ->
         it 'makes the tab permanent', ->
@@ -933,7 +925,7 @@
             expect($(tabBar.tabForItem(editor1)).find('.title')).not.toHaveClass 'temp'

       describe 'when switching from a pending tab to a permanent tab', ->
-        it "closes the pending tab", ->
+        it "keeps the pending tab open", ->
           editor1 = null
           editor2 = null

@@ -947,23 +939,28 @@

           runs ->
             pane.activateItem(editor1)
-            expect(pane.getItems().length).toBe 1
-            expect(pane.getItems()).toEqual [editor1]
-            expect($(tabBar.tabForItem(editor1)).find('.title')).not.toHaveClass 'temp'
+            expect(pane.getItems().length).toBe 2
+            expect(pane.getItems()).toEqual [editor1, editor2]
+            expect($(tabBar.tabForItem(editor2)).find('.title')).toHaveClass 'temp'

       describe "when splitting a pending tab", ->
+        editor1 = null
+        beforeEach ->
+          waitsForPromise ->
+            atom.workspace.open('sample.txt', pending: true).then (o) -> editor1 = o
+
         it "makes the tab permanent in the new pane", ->
-          editor1 = null
-          waitsForPromise ->
-            atom.workspace.open('sample.txt').then (o) -> editor1 = o
-
-          runs ->
-            pane.activateItem(editor1)
-            pane2 = pane.splitRight(copyActiveItem: true)
-            tabBar2 = new TabBarView
-            tabBar2.initialize(pane2)
-
-            expect($(tabBar2.tabForItem(pane2.getActiveItem())).find('.title')).not.toHaveClass 'temp'
+          pane.activateItem(editor1)
+          pane2 = pane.splitRight(copyActiveItem: true)
+          tabBar2 = new TabBarView
+          tabBar2.initialize(pane2)
+          newEditor = pane2.getActiveItem()
+          expect(newEditor.isPending()).toBe false
+          expect($(tabBar2.tabForItem(newEditor)).find('.title')).not.toHaveClass 'temp'
+
+        it "keeps the pending tab in the old pane", ->
+          expect(editor1.isPending()).toBe true
+          expect($(tabBar.tabForItem(editor1)).find('.title')).toHaveClass 'temp'

       describe "when dragging a pending tab to a different pane", ->
         it "makes the tab permanent in the other pane", ->


Fix stack traces for RangeError

Fix stack traces for RangeError

--- src/range.coffee
+++ src/range.coffee
@@ -27,9 +27,14 @@
   else
     false

-class Range.RangeError extends Error
+class Range.RangeError
   constructor: (@type, @message, @parent = null) ->
-    super(@message)
+    @stack = (new Error()).stack
+
+  # The Error built-in cannot be invoked with .apply() so we can't use super.
+  @:: = new Error
+  @::constructor = @
+

 # Public: Creates a wrapper around a range object obtained from a DOMSelection.
 class Range.BrowserRange


:keyboard: hey look some keybindings

:keyboard: hey look some keybindings

--- keymaps/git.cson
+++ keymaps/git.cson
@@ -3,6 +3,19 @@
   'ctrl-shift-8': 'github:toggle-github-tab'
   'ctrl-9': 'github:toggle-git-tab-focus'
   'ctrl-shift-9': 'github:toggle-git-tab'
+  'alt-g enter': 'github:commit'
+  'alt-g f': 'github:fetch'
+  'alt-g F': 'github:pull'
+  'alt-g p': 'github:push'
+  'alt-g P': 'github:force-push'
+  'alt-g =': 'github:clone'
+
+'atom-text-editor':
+  'alt-m 1': 'github:resolve-as-ours'
+  'alt-m 2': 'github:resolve-as-theirs'
+  'alt-m 3': 'github:resolve-as-base'
+  'alt-m enter': 'github:resolve-as-current'
+  'alt-m r': 'github:revert-current'

 '.github-Panel':
   'cmd-enter': 'github:commit'


get the lowest common ancestor instead of highest

Fix #166 as reported by @jujug42

--- src/range.coffee
+++ src/range.coffee
@@ -387,7 +387,7 @@
                  # Everyone else
                  (a, b) -> a.compareDocumentPosition(b) & 16

-    $(range.startContainer).parents().reverse().each ->
+    $(range.startContainer).parents().each ->
       if contains(this, range.endContainer)
         range.commonAncestorContainer = this
         return false


Simpler concat

Simpler concat

--- src/robot.coffee
+++ src/robot.coffee
@@ -194,7 +194,7 @@
     @connect.listen process.env.PORT || 8080

     hostname = process.env.HEROKU_URL
-    hostname = "#{hostname}/" unless /\/$/.test hostname
+    hostname += '/' unless /\/$/.test hostname

     if hostname
       setInterval =>


Rename variable to something more fitting

--- src/robot.coffee
+++ src/robot.coffee
@@ -193,12 +193,12 @@

     @connect.listen process.env.PORT || 8080

-    hostname = process.env.HEROKU_URL
-
-    if hostname
-      hostname += '/' unless /\/$/.test hostname
+    herokuUrl = process.env.HEROKU_URL
+
+    if herokuUrl
+      herokuUrl += '/' unless /\/$/.test herokuUrl
       setInterval =>
-        HttpClient.create("#{hostname}hubot/ping")
+        HttpClient.create("#{herokuUrl}hubot/ping")
           .post() (err, res, body) =>
             @logger.info "keep alive ping!"
       , 1200000


Prevent keep-alive from pinging `undefined/` when HEROKU_URL is not set

--- src/robot.coffee
+++ src/robot.coffee
@@ -194,9 +194,9 @@
     @connect.listen process.env.PORT || 8080

     hostname = process.env.HEROKU_URL
-    hostname += '/' unless /\/$/.test hostname

     if hostname
+      hostname += '/' unless /\/$/.test hostname
       setInterval =>
         HttpClient.create("#{hostname}hubot/ping")
           .post() (err, res, body) =>


Tools feels left out

Tools feels left out

--- src/scripts/jenkins-notifier.coffee
+++ src/scripts/jenkins-notifier.coffee
@@ -61,6 +61,18 @@
             console.log "JEEEEEEEEEEEEEENKIIIIIIIIINS"
             console.log res.statusCode
           robot.send envelope, "I hope you know what you're doing..."
+        if data.name == 'Admin-v2 Deploy (PROD) (RACKSPACE)'
+          console.log "LEEEEEEEEEEEROOOOOOOOOOOOOY"
+          http.get 'http://wall:5051/shipit-v2', (res) ->
+            console.log "JEEEEEEEEEEEEEENKIIIIIIIIINS"
+            console.log res.statusCode
+          robot.send envelope, "I hope you know what you're doing..."
+        if data.name == 'Admin-v1 Deploy (PROD) (RACKSPACE)'
+          console.log "LEEEEEEEEEEEROOOOOOOOOOOOOY"
+          http.get 'http://wall:5051/shipit-v1', (res) ->
+            console.log "JEEEEEEEEEEEEEENKIIIIIIIIINS"
+            console.log res.statusCode
+          robot.send envelope, "I hope you know what you're doing..."

     catch error
       console.log "jenkins-notify error: #{error}. Data: #{req.body}"


Clear previous file state

Clear previous file state

--- src/smc-webapp/project_store.coffee
+++ src/smc-webapp/project_store.coffee
@@ -354,6 +354,11 @@
                         # Only generate the editor component if we don't have it already
                         # Also regenerate if view type (public/not-public) changes
                         if not open_files.has(opts.path) or open_files.getIn([opts.path, 'component']).is_public != is_public
+                            was_public = open_files.getIn([opts.path, 'component'])?.is_public
+                            if was_public? and was_public != is_public
+                                @setState(open_files : store.open_files.delete(opts.path))
+                                project_file.remove(opts.path, @redux, @project_id, was_public)
+
                             open_files_order = store.open_files_order

                             # Initialize the file's store and actions


Changed window title updates to place filename in front [#109914028]

--- src/code/client.coffee
+++ src/code/client.coffee
@@ -63,7 +63,8 @@
         break

     @appOptions.ui or= {}
-    @appOptions.ui.windowTitlePrefix or= "#{document.title}: "
+    @appOptions.ui.windowTitleSuffix or= document.title
+    @appOptions.ui.windowTitleSeparator or= ' - '
     @_setWindowTitle()

     @_ui.init @appOptions.ui
@@ -361,8 +362,8 @@
     "#{document.location.origin}#{document.location.pathname}#{suffix}"

   _setWindowTitle: (name) ->
-    if @appOptions?.ui?.windowTitlePrefix
-      document.title = "#{@appOptions.ui.windowTitlePrefix}#{if name?.length > 0 then name else (tr "~MENUBAR.UNTITLED_DOCUMENT")}"
+    if @appOptions?.ui?.windowTitleSuffix
+      document.title = "#{if name?.length > 0 then name else (tr "~MENUBAR.UNTITLED_DOCUMENT")}#{@appOptions.ui.windowTitleSeparator}#{@appOptions.ui.windowTitleSuffix}"

 module.exports =
   CloudFileManagerClientEvent: CloudFileManagerClientEvent


fix(getVersion): add Utils to public interface of this module (#304)

--- src/coffee/main.coffee
+++ src/coffee/main.coffee
@@ -9,3 +9,4 @@
   TaskQueue: require './task-queue'
   RepeaterTaskQueue: require './repeater-task-queue'
   Errors: require './errors'
+  Utils: require './utils'


Escapable linebreaks in heregexes

Escapable linebreaks in heregexes

--- lexer.coffee
+++ lexer.coffee
@@ -255,7 +255,7 @@
   heregexToken: (match) ->
     [heregex, body, flags] = match
     if 0 > body.indexOf '#{'
-      re = body.replace(HEREGEX_OMIT, '$1$2').replace(/\//g, '\\/')
+      re = @escapeLines body.replace(HEREGEX_OMIT, '$1$2').replace(/\//g, '\\/'), yes
       if re.match /^\*/ then @error 'regular expressions cannot begin with `*`'
       @token 'REGEX', "/#{ re or '(?:)' }/#{flags}", 0, heregex.length
       return heregex.length
@@ -810,7 +810,7 @@

 HEREGEX_OMIT = ///
     ((?:\\\\)+)     # consume (and preserve) an even number of backslashes
-  | \\([^\n\S]|/)   # preserve escaped spaces and "de-escape" slashes
+  | \\(\s|/)        # preserve escaped whitespace and "de-escape" slashes
   | \s+(?:#.*)?     # remove whitespace and comments
 ///g


Added _orm_never_cache flag

Added _orm_never_cache flag

--- lib/cache.coffee
+++ lib/cache.coffee
@@ -63,6 +63,7 @@
     return model

   set: (model_name, model) ->
+    return @ if model._orm_never_cache # never cache
     throw new Error "Missing id for model: #{model_name}" unless model.id
     return @ unless model_cache = @getOrCreateModelCache(model_name) # no caching
     if current_model = model_cache.get(model.id)


Add menu and Win32/Linux keybindings

Add menu and Win32/Linux keybindings

--- keymaps/git.cson
+++ keymaps/git.cson
@@ -1,5 +1,8 @@
-'atom-workspace':
+'.platform-darwin':
   'cmd-shift-c': 'git:toggle-git-panel'
+
+'.platform-win32, .platform-linux':
+  'alt-shift-c': 'git:toggle-git-panel'

 '.git-StagingView':
   'left': 'git:focus-diff-view'


Bloody coffee script!

Bloody coffee script!

--- lib/debug.coffee
+++ lib/debug.coffee
@@ -72,7 +72,7 @@

   validateTagName: (tag) ->
     valid = tag? and typeof tag is 'string' and tag.length > 0
-    @assert valid, 'You must pass a non-empty string!'
+    console.assert valid, 'You must pass a non-empty string! Current:', tag
     valid
   enableExtra: (tag) ->
     return (Debug.wrap 'Invalid extra option name') if !@extra tag
@@ -88,7 +88,7 @@
   getAllExtraOptionsAsArray: ->
     Array.from extraOpts
   extra: (tag) ->
-    if @validateTagName tag and !@ex[tag]?
+    if @validateTagName(tag) and !@ex[tag]?
       Object.defineProperty @ex, tag,
         value: new ExOptHandler
         enumerable: true


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/tween.coffee
+++ src/tween.coffee
@@ -1,5 +1,5 @@
 ###*
-The Tween module provides a method to flicker a sprite between solid and 50% opacity.
+The Tween module provides a method to tween objec.

 @name Flickerable
 @module


coordinator: Remove console.log()s

coordinator: Remove console.log()s

--- src/coordinator.coffee
+++ src/coordinator.coffee
@@ -135,9 +135,7 @@
     part = @participants[@participantsByRole(participantId)] if not part?

     debug 'subscribeTo', participantId, outport
-#    console.log part.outports, outport
     port = findPort part, 'outport', outport
-    console.log 'subscribing to exported outport', part, outport, port
     ackHandler = (msg) =>
       return if not @started
       handler msg
@@ -150,7 +148,6 @@
       @broker.addBinding {type: 'pubsub', src: port.queue, tgt: readQueue}, (err) =>
         return callback err if err
         @broker.subscribeToQueue readQueue, ackHandler, (err) ->
-          console.log '!! subscribed'
           return callback err, readQueue # caller should teardown readQueue

   unsubscribeFrom: () -> # FIXME: implement
@@ -250,7 +247,6 @@

       if direction.indexOf('out') == 0
         handler = (msg) =>
-          console.log 'got exported outport data on', external
           @emit 'exported-port-data', external, msg.data, graph
         @subscribeTo node, internal, handler, (err, readQueue) ->
           return callback err if err


Allowing for international filenames to be correctly handled.

--- src/core/server.coffee
+++ src/core/server.coffee
@@ -32,6 +32,7 @@

 normalizeUrl = (anUrl) ->
   anUrl += 'index.html' if anUrl[anUrl.length - 1] is '/'
+  anUrl = decodeURI anUrl
   return anUrl

 urlEqual = (urlA, urlB) ->


Minor inline documentation update.

Minor inline documentation update.

--- src/traits/HasValidatorsView.coffee
+++ src/traits/HasValidatorsView.coffee
@@ -96,6 +96,7 @@
       if @dirty then doValidation()
     , VALIDATION_DEBOUNCE

+    # Trigger a soft validation as soon as the user focuses the component.
     @on 'focus', =>
       @context.validatorGroup.softValidate  @context[@context.validateProperty]


Added redirect snippet

Added redirect snippet

--- completions.cson
+++ completions.cson
@@ -3081,6 +3081,25 @@
       'body': "Director::baseFolder()"
     }
   ]
+'Redirect':
+  'prefix': 'redirect'
+  'module': 'silverstripe/framework'
+  'variations': [
+    {
+      'scope': '.text.html.php'
+      'minVersion': '4.0'
+      'body': "Director::redirect(${1})${0}"
+      'useNamespace': [
+        'SilverStripe\\Control\\Director'
+      ]
+    }
+    {
+      'scope': '.text.html.php'
+      'maxVersion': '4.0'
+      'minVersion': '3.0'
+      'body': "Director::redirect(${1})${0}"
+    }
+  ]

 # General variables
 'Current Member':


add documenter block recognition

add documenter block recognition

--- grammars/weave_md.cson
+++ grammars/weave_md.cson
@@ -49,6 +49,31 @@
       'end' : '$'
     }
     {
+    'begin': '^([`~]{3,})(@(docs|autodocs|ref|meta|index|content|example|repl|eval|setup|raw))\\s+(\\d+)?\\s*$'
+    'beginCaptures':
+      '1':
+        'name': 'markup.heading.weave.md'
+      '2':
+        'name': 'markup.bold.weave.md'
+      '4':
+        'contentName' : 'source.embedded.julia'
+        'patterns': [
+          {
+            'include': 'source.julia'
+          }
+        ]
+    'end': '^[`~]{3,}\\s*$'
+    'endCaptures':
+      '0':
+        'name': 'markup.heading.weave.md'
+    'contentName': 'source.embedded.julia'
+    'patterns': [
+      {
+        'include': 'source.julia'
+      }
+    ]
+    }
+    {
       'include': 'source.gfm'
     }
     {


Initial js2coffee conversion of quotebox

--- example.coffee
+++ example.coffee
@@ -1,5 +1,166 @@
+quoteStr = (last, change, time) ->
+  if change > 0
+    fontColor = "green"
+  else if change < 0
+    fontColor = "red"
+  else
+    fontColor = "black"
+  percentChange = Math.round(1e4 * +change / (+last - +change)) / 100
+  output = last + " Change: "
+  output += change + " (" + percentChange + "%)"
+  output += " (" + time + ")"
+  output
+
+# Round to decimal points
+r2 = (n) ->
+  (+n).toFixed 2
+
+timeout = 5e3
+tid = undefined
+items = []
+months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
+
+# ISO 8601 Parser
+((Date, undefined_) ->
+  origParse = Date.parse
+  numericKeys = [1, 4, 5, 6, 7, 10, 11]
+  Date.parse = (date) ->
+    timestamp = undefined
+    struct = undefined
+    minutesOffset = 0
+
+    # ES5 §15.9.4.2 states that the string should attempt to be parsed as a Date Time String Format string
+    # before falling back to any implementation-specific date parsing, so that¿s what we do, even if native
+    # implementations could be faster
+    #              1 YYYY                2 MM       3 DD           4 HH    5 mm       6 ss        7 msec        8 Z 9 ±    10 tzHH    11 tzmm
+    if struct = /^(\d{4}|[+\-]\d{6})(?:-(\d{2})(?:-(\d{2}))?)?(?:T(\d{2}):(\d{2})(?::(\d{2})(?:\.(\d{3}))?)?(?:(Z)|([+\-])(\d{2})(?::?(\d{2}))?)?)?$/.exec(date)
+
+      # avoid NaN timestamps caused by ¿undefined¿ values being passed to Date.UTC
+      i = 0
+      k = undefined
+
+      while (k = numericKeys[i])
+        struct[k] = +struct[k] or 0
+        ++i
+
+      # allow undefined days and months
+      struct[2] = (+struct[2] or 1) - 1
+      struct[3] = +struct[3] or 1
+      if struct[8] isnt "Z" and struct[9] isnt `undefined`
+        minutesOffset = struct[10] * 60 + struct[11]
+        minutesOffset = 0 - minutesOffset  if struct[9] is "+"
+      timestamp = Date.UTC(struct[1], struct[2], struct[3], struct[4], struct[5] + minutesOffset, struct[6], struct[7])
+    else
+      timestamp = (if origParse then origParse(date) else NaN)
+    timestamp
+
+  return
+) Date
+
+# Convert time to formatted string
+datetimeStr = (time) ->
+  hours = time.getHours()
+  ap = "AM"
+  ap = "PM"  if hours > 11
+  hours -= 12  if hours > 12
+  hours = 12  if hours is 0
+  minutes = time.getMinutes()
+  minutes = "0" + minutes  if minutes < 10
+  months[time.getMonth()] + " " + time.getDate() + ", " + hours + ":" + minutes + ap
+
+# Print quote
+printQuote = ->
+  d = new Date()
+  tickers = "AAPL%7CVXAPL"
+  url = "http://quote.cnbc.com/quote-html-webservice/quote.htm?symbols=" + tickers + "&requestMethod=quick&fund=1&noform=1&exthrs=1&extMode=ALL&extendedMask=2&output=json"
+  yql = "http://query.yahooapis.com/v1/public/yql?q=" + encodeURIComponent("select * from html where url=\"" + url + "\"") + "&format=json&callback=?"
+
+  #$.ajax({url: yql, dataType: 'jsonp text xml'}, function(data)
+  $.getJSON yql, (data) ->
+    items = []
+    data = $.parseJSON(data.query.results.body.p).QuickQuoteResult.QuickQuote
+    $.each data, (key, datum) ->
+      console.log datum
+      time = undefined
+      if datum.hasOwnProperty("reg_last_time")
+        time = new Date(Date.parse(datum.reg_last_time))
+      else
+        time = new Date(+datum.last_time_msec)
+      output = quoteStr(r2(datum.last), r2(datum.change), datetimeStr(time))
+      if datum.symbol is "VXAPL"
+        items.push "" + datum.symbol + " " + output
+        items.push " Range: " + r2(datum.low) + "-" + r2(datum.high)
+      else
+        items.push "" + datum.name + " (" + datum.symbol + ") " + output
+
+        # If after 4:00pm or before 9:30am
+        #var extHours = d.getHours() > 15 || d.getHours() < 9 || (d.getHours() == 9 && d.getMinutes() < 30)
+        #if (extHours)
+        extHours = false
+        extTime = new Date()
+        if datum.hasOwnProperty("ExtendedMktQuote")
+          if datum.ExtendedMktQuote.hasOwnProperty("afthrs_last_time")
+            extTime.setTime Date.parse(datum.ExtendedMktQuote.afthrs_last_time)
+          else
+            extTime.setTime Date.parse(datum.ExtendedMktQuote.last_time)
+          if extTime.getTime() > time.getTime()
+            extHours = true
+            output = quoteStr(r2(datum.ExtendedMktQuote.last), r2(datum.ExtendedMktQuote.change), datetimeStr(extTime))
+            items.push "
Extended Hours: " + output
+        items.push "
Range: " + r2(datum.low) + "-" + r2(datum.high)
+        if extHours
+          items.push " Volume/Average/Ext. Hours: " + r2(datum.volume / 1e6) + "M/" + r2(datum.FundamentalData.tendayavgvol) + "M"
+          items.push "/" + datum.ExtendedMktQuote.volume
+        else
+          items.push " Volume/Average: " + r2(datum.volume / 1e6) + "M/" + r2(datum.FundamentalData.tendayavgvol) + "M"
+        items.push "
PE: " + r2(datum.FundamentalData.pe)
+        items.push " EPS: " + r2(datum.FundamentalData.eps)
+        items.push " Mkt. Cap: " + r2(datum.FundamentalData.mktcap / 1e9) + "B"
+        items.push "
"
+      return
+
+    printAll()
+    return
+
+
+  #printVXAPL();
+  tid = setTimeout(printQuote, timeout)
+  return
+
+printVXAPL = ->
+  d = new Date()
+  n = d.toTimeString()
+  url = "http://query.yahooapis.com/v1/public/yql?q=select%20*%20from%20yahoo.finance.quotes%20where%20symbol%20in%20(%22%5EVXAPL%22)&format=json&env=store%3A%2F%2Fdatatables.org%2Falltableswithkeys&callback=?"
+  $.getJSON url, (data) ->
+    $.each data.query.results, (key, datum) ->
+      items.push "
"
+      output = quoteStr(datum.LastTradePriceOnly, datum.Change, datum.LastTradeTime)
+      items.push "" + datum.symbol + " " + output
+      items.push " Range: " + datum.DaysLow + "-" + datum.DaysHigh
+      return
+
+    printAll()
+    return
+
+  return
+printAll = ->
+  header = "

"
+  footer = "

"
+  $("#ticker_box").html header + items.join("") + footer
+  return
+
+printQuote()
+
+
+# Get quote data from CNBC
+getQuoteCNBC = (tickers) ->
+  url = 'http://quote.cnbc.com/quote-html-webservice/quote.htm?symbols=#{tickers}&requestMethod=quick&fund=1&noform=1&exthrs=1&extMode=ALL&extendedMask=2&output=json'
+  yql = 'http://query.yahooapis.com/v1/public/yql?q=' + encodeURIComponent('select * from html where url="' + url + '"') + '&format=json&callback=?'
+
+
+
 # Example code for ticker box
-$("#ticker_box").html "Ticker box goes here"
+#$("#ticker_box").html "Ticker box goes here"

 # Example code for quote table
 $("#quote_table").html "Quote table goes here"


fix bug

fix bug

--- app/scripts/app.coffee
+++ app/scripts/app.coffee
@@ -22,7 +22,7 @@
                   'memoire.controllers', 'memoire.directives', 'ui.router',
                   'restangular', 'angular-jwt',
                   'ngAnimate', 'chieffancypants.loadingBar', 'ui.bootstrap',
-                  'ngSanitize', 'markdown', '720kb.datepicker'
+                  'ngSanitize', 'markdown', '720kb.datepicker',
                   'iso-3166-country-codes', 'ngFileUpload', 'ngPlacesAutocomplete',
               ])


Add timers back in

Add timers back in

--- src/utils.coffee
+++ src/utils.coffee
@@ -148,8 +148,8 @@
 #
 exports.delay = (time, f) ->
 	timer = setTimeout f, time * config.timeSpeedFactor
-	# window._delayTimers ?= []
-	# window._delayTimers.push timer
+	window._delayTimers ?= []
+	window._delayTimers.push timer
 	return timer


Update imports

Update imports

--- app/src/imports.coffee
+++ app/src/imports.coffee
@@ -21,9 +21,13 @@
         'base/view_controller',
         'base/navigation_controller',

+
+        ## Wallet controllers
         'controllers/wallet_navigation_controller',
+        'controllers/dashboard/dashboard_index_view_controller'
+
+        ## Onboarding controllers
         'controllers/onboarding_navigation_controller',
-
-        # dashboard controllers
-        'controllers/dashboard_index_view_controller'
+        'controllers/onboarding_plug_view_controller',
+        'controllers/onboarding_unplug_view_controller'
       ]


improving readability a bit

improving readability a bit

--- nodes.coffee
+++ nodes.coffee
@@ -708,21 +708,18 @@
     assigns: ["$@tab$val_var = ${ value.compile(o) };"]
     o.top: true
     o.as_statement: true
-    splatted: false
+    splat: false
     for obj, i in @variable.base.objects
       idx: i
       [obj, idx]: [obj.value, obj.variable.base] if @variable.is_object()
       access_class: if @variable.is_array() then IndexNode else AccessorNode
-      if obj instanceof SplatNode and not splatted
-        val: literal(obj.compile_value(o, val_var, (oindex: @variable.base.objects.indexOf(obj)),
+      if obj instanceof SplatNode and not splat
+        val: literal(obj.compile_value(o, val_var,
+          (oindex: @variable.base.objects.indexOf(obj)),
           (olength: @variable.base.objects.length) - oindex - 1))
-        splatted: true
+        splat: true
       else
-        if typeof idx isnt 'object'
-          if splatted
-            idx: literal("${val_var}.length - ${olength - idx}")
-          else
-            idx: literal(idx)
+        idx: literal(if splat then "${val_var}.length - ${olength - idx}" else idx) if typeof idx isnt 'object'
         val: new ValueNode(literal(val_var), [new access_class(idx)])
       assigns.push(new AssignNode(obj, val).compile(o))
     code: assigns.join("\n")


fixes #1009: classes with reserved words as determined names

--- nodes.coffee
+++ nodes.coffee
@@ -892,6 +892,7 @@
   compileNode: (o) ->
     decl  = @determineName()
     name  = decl or @name or '_Class'
+    name = "_#{name}" if name.reserved
     lname = new Literal name

     @setContext name


Bugfix: service selection from search results list.

--- src/views.coffee
+++ src/views.coffee
@@ -172,7 +172,8 @@
             @listenTo @search_results, 'reset', ->
                 @change 'search'
             @listenTo @selected_units, 'reset', (unit, coll, opts) ->
-                @change 'details'
+                unless @selected_units.isEmpty()
+                    @change 'details'
             @listenTo @selected_units, 'remove', (unit, coll, opts) ->
                 @change null
         right_edge_coordinate: ->
@@ -452,7 +453,10 @@
             'mouseenter': 'highlight_result'
         template: 'search-result'
         select_result: (ev) ->
-            app.commands.execute 'selectUnit', @model
+            if @model.get('object_type') == 'unit'
+                app.commands.execute 'selectUnit', @model
+            else if @model.get('object_type') == 'service'
+                app.commands.execute 'addService', @model
         highlight_result: (ev) ->
             @model.marker?.openPopup()


improve error message for unmatched extensions

--- lib/index.coffee
+++ lib/index.coffee
@@ -40,7 +40,8 @@
     ext = path.extname(filepath).substring(1)
     name = lookupAdapter(ext)
     if not name
-      throw new Error("File extension '#{ext}' not supported")
+      throw new Error("File extension '#{ext}' could not be matched to an
+      adapter. Specify an adapter manually with --adapter")
   else if argv.adapter?
     name = argv.adapter
   else


update

update

--- lib/index.coffee
+++ lib/index.coffee
@@ -1,8 +1,28 @@
 async = require 'async'
+doppel = require 'doppel'

 module.exports =

-  scaffold: (questions, actions, answers, done) ->
+  autoScaffold: ({questions, actions, answers, templateDir, done}) ->
+
+    async.eachSeries Object.keys(answers), (key, next) ->
+
+      question = questions[key]
+      answer = answers[key]
+
+      doppel.doppel
+        templateDir: templateDir
+        to: '/Users/funkytek/apps/tmp/GENTRY'
+        key: key
+        answer: answer
+        sandbox:
+          answers: answers
+        done: ->
+          console.log 'done'
+          done()
+
+
+  runActions: (questions, actions, answers, done) ->

     async.eachSeries Object.keys(answers), (key, next) ->

@@ -14,7 +34,7 @@
       isKeyAction    = -> actions[key]?
       isAnswerAction = -> actions[key]?[answers[key]]?

-      callAnswerKey  = -> actions[key]?[answers[key]]? next
+      callAnswerKey  = -> actions[key]?[answers[key]]?.bind(answers: answers) next
       passVal        = -> actions[key]? answers[key], next

       next() unless isKeyAction() or isAnswerAction()


remove string decoder for simpler method

--- lib/index.coffee
+++ lib/index.coffee
@@ -1,8 +1,6 @@
 gutil         = require 'gulp-util'
 accord        = require 'accord'
 map           = require 'map-stream'
-StringDecoder = require('string_decoder').StringDecoder
-decoder       = new StringDecoder

 module.exports = (lang, opts) ->

@@ -16,6 +14,6 @@
     throw new Error("#{PLUGIN_NAME}: #{lang} not installed. Try 'npm i #{lang} -S'")

   map (file, cb) ->
-    adapter.render(decoder.write(file.contents), opts)
+    adapter.render(String(file.contents), opts)
     .done ((res) -> cb(null, res)), (err) ->
       cb(new gutil.PluginError(PLUGIN_NAME, err))


Add @send, @json, @jsonp, @redirect, and @format, to middleware inside @wrap.

--- src/zappa.coffee
+++ src/zappa.coffee
@@ -311,9 +311,12 @@
         response: res
         res: res
         next: next
-
+        send: -> res.send.apply res, arguments
+        json: -> res.json.apply res, arguments
+        jsonp: -> res.jsonp.apply res, arguments
+        redirect: -> res.redirect.apply res, arguments
+        format: -> res.format.apply res, arguments
       apply_helpers ctx
-
       f.call ctx, req, res, next

   context.use = ->


check for null view

check for null view

--- lib/mylyn.coffee
+++ lib/mylyn.coffee
@@ -80,7 +80,7 @@
       reloadTask = =>
           @view.treeViewOpenPromise
                     .then (view)=>
-                          selectedPath = view.selectedEntry()?.getPath()
+                          selectedPath = view?.selectedEntry()?.getPath()
                           view.updateRoots()
                           if selectedPath
                             view.selectEntryForPath selectedPath


Add BEAST mitigation config

Add BEAST mitigation config

--- lib/proxy.coffee
+++ lib/proxy.coffee
@@ -24,6 +24,9 @@
     options.https = {
       key: config.https.key
       cert: config.https.cert
+      # mitigate BEAST
+      honorCipherOrder: true
+      ciphers: "ECDHE-RSA-AES128-SHA256:AES128-GCM-SHA256:RC4:HIGH:!MD5:!aNULL:!EDH"
     }

   proxyServer = httpProxy.createServer(options)


Add TODO comment about possible memory leak in state.getMatchCommand()

--- lib/state.coffee
+++ lib/state.coffee
@@ -19,6 +19,13 @@
 	async.eachSeries commands, (command, done) ->
 		command.signature.matches signature, (result) ->
 			return done() if not result
+
+			# TODO: Breaking from the async look this way may
+			# cause a memory leak.
+			# One possible solution is to call done() with the result
+			# tricking async that is an error, and handle accordingly
+			# in the final callback. However the solution looks ugly.
+			# Search for alternatives
 			return callback(null, command)
 	, (error) ->
 		return callback(error) if error?


Check in a working shadow impl, breaks dynamic super


fixture has multiple dirnames

fixture has multiple dirnames

--- src/lib/fixture.coffee
+++ src/lib/fixture.coffee
@@ -14,7 +14,7 @@
     ###*
     @constructor
     @param {Object} [options]
-    @param {String} [options.dirname='./fixtures'] directory to have fixture files. /data, /tsvs should be included in the directory.
+    @param {String|Array} [options.dirname='./fixtures'] director(y|ies) to have fixture files. /data, /tsvs should be included in the directory.
     @param {Object} [options.data={}] default data, merged to dataPool
     @param {String} [options.debug] if true, shows debug log
     ###
@@ -25,15 +25,24 @@
         # loading model files
         @fxModelMap = {}

-        @dirname = options.dirname ? __dirname + '/fixtures'
-
-        files = fs.readdirSync(@dirname + '/data')
-
-        for file in files
-            modelName = file.split('.').shift()
-            setting = require(@dirname + '/data/' + file)
-
-            @fxModelMap[modelName] = new FixtureModel(@, modelName, setting)
+        dirnames =
+            if options.dirname?
+                if Array.isArray options.dirname
+                    options.dirname
+                else
+                    [ options.dirname ]
+
+            else
+                [ __dirname + '/fixtures' ]
+
+        for dirname in dirnames
+            files = fs.readdirSync(dirname + '/data')
+
+            for file in files
+                modelName = file.split('.').shift()
+                setting = require(dirname + '/data/' + file)
+
+                @fxModelMap[modelName] = new FixtureModel(@, modelName, setting, dirname)

         # initial data pool
@@ -167,7 +176,7 @@
     ###*
     @constructor
     ###
-    constructor: (@fx, @name, setting = {}) ->
+    constructor: (@fx, @name, setting = {}, @dirname) ->

         @dependencies = setting.dependencies ? []
         @data = setting.data ? ->
@@ -226,7 +235,7 @@

         objs = {}

-        lines = fs.readFileSync(@fx.dirname + '/tsvs/' + filename, 'utf8').split('\n')
+        lines = fs.readFileSync(@dirname + '/tsvs/' + filename, 'utf8').split('\n')

         tsv = (line.split('\t') for line in lines)


Improve atom line lenght and wrapping config

--- atom/config/config.cson
+++ atom/config/config.cson
@@ -6,6 +6,10 @@
     ]
   editor:
     fontFamily: "Fira Code"
+    preferredLineLength: 100
+    softWrap: true
+    softWrapAtPreferredLineLength: true
+    softWrapHangingIndent: 2
   "exception-reporting":
     userId: "f96e777f-ace7-9fe4-3f87-ac9f33d8e474"
   "ide-haskell": {}


Ensure Package Can Be Uninstalled

Ensure Package Can Be Uninstalled

--- lib/gofmt.coffee
+++ lib/gofmt.coffee
@@ -7,6 +7,9 @@
   constructor: ->
     atom.workspace.eachEditor (editor) =>
       @handleBufferEvents(editor)
+
+  destroy: ->
+    @unsubscribe

   handleBufferEvents: (editor) ->
     buffer = editor.getBuffer()


break execution on error

break execution on error

--- lib/index.coffee
+++ lib/index.coffee
@@ -42,7 +42,7 @@
       cwd = options.cwd || ''

       glob path, options, (err, files) ->
-        callback err if err
+        return callback err if err
         files.forEach (file, i) ->
           fs.stat "#{cwd}/#{file}", (err, stats) ->
             if stats.isFile()


Added support for using the contentful preview API based on CONTENTFUL_ENV environment variable

--- lib/index.coffee
+++ lib/index.coffee
@@ -15,6 +15,11 @@
   reserved for storing Contentful system metadata, please rename this field to
   a different value.'

+hosts = {
+  develop: 'preview.contentful.com',
+  production: 'cdn.contentful.com'
+}
+
 module.exports = (opts) ->
   # throw error if missing required config
   if not (opts.access_token && opts.space_id)
@@ -22,6 +27,7 @@

   # setup contentful api client
   client = contentful.createClient
+    host: hosts[process.env.CONTENTFUL_ENV] || hosts.production
     accessToken: opts.access_token
     space:       opts.space_id


fixing bug when a list gets added to

fixing bug when a list gets added to

--- src/mixins/list.coffee
+++ src/mixins/list.coffee
@@ -41,6 +41,7 @@
     #   console.log(" => #{event}")
     @listenTo @collection, "add", (m) =>
       @added(m)
+      @showAlerts()
     @listenTo @collection, 'update', =>
       @listMeta.set({state: "listing"})
       @updateFilters()


Bug making reordering fail

Bug making reordering fail

--- lib/pages.coffee
+++ lib/pages.coffee
@@ -562,6 +562,7 @@

     handle2 = c.observeChanges
       movedBefore: ((sub, id, before) ->
+        at=-1
         ref = false
         (@Collection.find get "filters",
           sort: get "sort"
@@ -575,7 +576,7 @@
           if ref
             sub.changed(@id, o._id, _.object([["_#{@id}_i", i + 1]]))

-        sub.changed(@id, id, _.object([["_#{@id}_i", i]]))
+        sub.changed(@id, id, _.object([["_#{@id}_i", at]]))
       ).bind @, sub

       changed: ((sub, id, fields) ->


Fixed a bug where it would show duplicate panels

--- lib/panel.coffee
+++ lib/panel.coffee
@@ -80,6 +80,7 @@
   init:->
     self = this
     @status = 1
+    return if $('.hh-panel').length isnt 0
     @panel = $('
')
     @panel_heading = $('
 Hack report
').appendTo(@panel)
     $('

').click(->


is_uri

--- lib/quest.coffee
+++ lib/quest.coffee
@@ -4,10 +4,6 @@
 _         = require 'underscore'
 url       = require 'url'
 cookiejar = require 'cookiejar'
-
-isUri = (uri) ->
-  uri_pattern = /^https?:\/\//
-  uri_pattern.test uri

 handle =
   form: (options) ->
@@ -31,6 +27,7 @@
     options.headers.cookie = if not options.headers.cookie? then '' else "#{options.headeers.cookie}; "
     options.headers.cookie = "#{options.headers.cookie}#{cookie_string}"

+is_uri = (uri) -> /^https?:\/\//.test uri
 normalize_uri = (options) -> options.uri = "http://#{options.uri}" if not isUri options.uri

 handle_options = (options) -> _(_(handle).values()).map (handler) -> handler options


doublesafe

doublesafe

--- lib/queue.coffee
+++ lib/queue.coffee
@@ -24,6 +24,7 @@
   kill: =>
     try
       @worker.disconnect()
+      @worker.kill()
     catch err
       return if err.message is "IPC channel is already disconnected" # Doesn't matter, had disconnect
       throw err


Add fake lifehacker to referral spam list

--- lib/setup.coffee
+++ lib/setup.coffee
@@ -61,7 +61,7 @@
   app.use sharify

   # Referral spam blocker
-  blocker.addToReferrers ['tkpassword.com']
+  blocker.addToReferrers ['tkpassword.com', 'lifehacĸer.com']
   app.use blocker.send404

   # Development only


#689 auto-relax start or end \b anchor for occurrence-pattern when it never match

--- lib/utils.coffee
+++ lib/utils.coffee
@@ -457,9 +457,14 @@
   boundarizeForWord = options.boundarizeForWord ? true
   delete options.boundarizeForWord
   {range, kind} = getWordBufferRangeAndKindAtBufferPosition(editor, point, options)
-  pattern = _.escapeRegExp(editor.getTextInBufferRange(range))
+  text = editor.getTextInBufferRange(range)
+  pattern = _.escapeRegExp(text)
+
   if kind is 'word' and boundarizeForWord
-    pattern = "\\b" + pattern + "\\b"
+    # Set word-boundary( \b ) anchor only when it's effective #689
+    startBoundary = if /^\w/.test(text) then "\\b" else ''
+    endBoundary = if /\w$/.test(text) then "\\b" else ''
+    pattern = startBoundary + pattern + endBoundary
   new RegExp(pattern, 'g')

 getSubwordPatternAtBufferPosition = (editor, point, options={}) ->


indirectly get editorElement, although don't think this save some failure

--- lib/utils.coffee
+++ lib/utils.coffee
@@ -60,7 +60,7 @@
       editor.setCursorBufferPosition(cursorPosition)
     for row in foldStartRows.reverse() when not editor.isFoldedAtBufferRow(row)
       editor.foldBufferRow(row)
-    editorElement.setScrollTop(scrollTop)
+    editor.element.setScrollTop(scrollTop)

 requireFrom = (pack, path) ->
   packPath = atom.packages.resolvePackagePath(pack)
@@ -125,7 +125,7 @@
   atom.workspace.getPanes()
     .map (pane) -> pane.getActiveEditor()
     .filter (editor) -> editor?
-
+
 module.exports = {
   getAdjacentPaneForPane
   activatePaneItemInAdjacentPane


OPCE19: word contains a reference to rte object

--- lib/y-rte.coffee
+++ lib/y-rte.coffee
@@ -49,7 +49,7 @@
   # Construct a new list of words
   # @param [String] word The initial string value
   # @return [Word] a word instance
-  constructor: (@word) ->
+  constructor: (@word, @rte) ->
     # Selections that have this word as left bound
     @left = []
     # Selections that have this word as right bound
@@ -75,10 +75,8 @@
         break

   # Get index of word in rte list
-  index: (rte) ->
-    if not (rte instanceof Rte)
-      throw new Error "Expected an RTE instance as first argument, got #{rte}"
-    return rte._rte.words.indexOf @
+  index: ->
+    return @rte._rte.words.indexOf @

 # A class describing a selection with a style (bold, italic, …)
@@ -358,7 +356,7 @@
   # @param index [Integer] the index of the word to return
   getWord: (index) ->
     if @_rte.words.length == 0 or index == @_rte.words.length
-      return new Word ""
+      return new Word "", @

     if not (0 <= index < @_rte.words.length)
       throw new Error "Index out of bounds #{index}"
@@ -384,7 +382,7 @@
   # @param content [String] the content to set the word to
   setWord: (index, content) ->
     if index == @_rte.words.length
-      @_rte.words.push(new Word content)
+      @_rte.words.push(new Word content, @)
     if not (0 <= index < @_rte.words.length)
       throw new Error "Index out of bounds"

@@ -395,11 +393,11 @@
   push: (content) ->
     preSpaces = content.match PreSpacesRegExp
     if preSpaces isnt null
-      @_rte.words.push(new Word (preSpaces[0]))
+      @_rte.words.push(new Word (preSpaces[0]), @)
     words = content.match WordRegExp
     if _.isArray(words)
       for w in words
-        @_rte.words.push (new Word w)
+        @_rte.words.push (new Word w, @)

   # Insert words at position
   #
@@ -413,7 +411,7 @@
     if not _.isArray words
       throw new Error "Expected a string array as second parameter, got #{words}"
     if 0 <= position <= @_rte.words.length
-      wordsObj = (new Word w for w in words)
+      wordsObj = ((new Word w, @) for w in words)

       left = @_rte.words.slice(0, position)
       right = @_rte.words.slice(position)
@@ -512,7 +510,7 @@
       if pos == 0
         if index == 0
           index += 1
-          @insertWord 0, (new Word '')
+          @insertWord 0, (new Word '', @)
         prevWord = @getWord(index-1).word
         prevWord += preSpaces
         content = content.substring(preSpaces.length)
@@ -647,6 +645,16 @@
     # unbind selection
     selection.unbind()

+  garbageCollect: ->
+    sels = @_rte.selections
+    for index in [0..sels.length-1]
+      for key, value of sels[index]
+        console.log sels[index], "has", key, value, "deleting!"
+        if value == null or value == ""
+          sels[index].unbind()
+          @removeSel sels[index]
+
+
 if window?
   window.Rte = Rte
   window.Selection = Selection


OPCE28: Updated Selection.clone to fix bug

--- lib/y-rte.coffee
+++ lib/y-rte.coffee
@@ -138,6 +138,7 @@
       @rightPos = retEnd.pos

       if options.bind
+        # console.log "New selection — binding", @
         @bind @left, @right
         @rte.pushSel @

@@ -265,13 +266,18 @@

     # Check that they have all keys in common (but not necessarily same value!)
     keys = _.keys(@style)
+    # console.log "keys", keys
     for key in keys
       if not(_.has(selection.style, key))
         return
+    # console.log "same keys, going forward", @, selection

     if @in(selection)
       outSelLeft = selection
       outSelRight = selection.clone()
+
+      # console.log "~~~~~~~~~~~~~~",outSelRight,"~~~~~~~~~~~~~~",
+      #   "~~~~~~~~~~~~~~", @rte._rte.selections

       # joke here, because Insel means island in German
       inSel = @
@@ -290,6 +296,7 @@
       [outSelRight, inSel, outSelLeft].forEach (sel) ->
         if sel.isEmpty()
           sel.unbind()
+          # console.log "Removing", sel
           @rte.removeSel sel

     else
@@ -414,6 +421,7 @@
     newSel.setStyle _.clone(@style)

     newSel.bind @left, @right
+    newSel.rte.pushSel newSel

     newSel


Simplify use API.

Simplify use API.

--- src/use.coffee
+++ src/use.coffee
@@ -1,18 +1,8 @@
-task = require './task'
-
-{option}               = require './cake'
 {isFunction, isString} = require './utils'

-module.exports = (pkg, opts) ->
+module.exports = (pkg, opts = {}) ->
   if isString pkg
     pkg = require pkg

-    if opts?
-      pkg = pkg opts
-    else
-      pkg = pkg() unless /function \(task/.test pkg
-
   if isFunction pkg
-    return pkg task, option
-
-  pkg
+    pkg opts


tests: fix race

tests: fix race

--- src/z.coffee
+++ src/z.coffee
@@ -117,12 +117,12 @@
             child.__dirtySubject.onNext true

       child._zorium_hook = createHook ($el) ->
+        # TODO: add a test for this checking if unhook can be called after hook
+        # in a single update might be an issue because of virtual-dom hook order
+        child.state?._bind_subscriptions()
+
         # Wait for insertion into the DOM
         setTimeout ->
-          # bind after insertion because of hook execution order
-          # (unhook can be called after hook in one update)
-          # TODO: add a test for this
-          child.state?._bind_subscriptions()
           child.afterMount?($el)
       , ->
         child.state?._unbind_subscriptions()


:green_heart: Fix invalidation strategy assertions for folds

--- spec/display-layer-spec.coffee
+++ spec/display-layer-spec.coffee
@@ -275,12 +275,12 @@
       expect(displayLayer.getText()).toBe 'a⋯i j⋯pqr\n⋯ vwx'

       buffer.insert([0, 3], 'y')
-      expect(displayLayer.getText()).toBe 'abcy def\nghi j⋯pqr\n⋯ vwx'
+      expect(displayLayer.getText()).toBe 'a⋯i j⋯pqr\n⋯ vwx'

       buffer.setTextInRange([[1, 6], [3, 4]], 'z')
-      expect(displayLayer.getText()).toBe 'abcy def\nghi jkzvwx'
-
-      expect(displayLayer.foldsIntersectingBufferRange([[0, 0], [Infinity, 0]])).toEqual []
+      expect(displayLayer.getText()).toBe 'a⋯i jkzvwx'
+
+      expect(displayLayer.foldsIntersectingBufferRange([[0, 0], [Infinity, 0]]).length).toBe 1

   describe "soft wraps", ->
     it "soft wraps the line at the first word start at or preceding the softWrapColumn", ->


updated specs to account for the extraFiles config

--- spec/sync-settings-spec.coffee
+++ spec/sync-settings-spec.coffee
@@ -92,6 +92,17 @@
           , (err, res) ->
             expect(res.files['keymap.cson']).toBeDefined()

+      it "uploads the files defined in config.extraFiles", ->
+        atom.config.set 'sync-settings.extraFiles', ['test.tmp', 'test2.tmp']
+        run (cb) ->
+          SyncSettings.upload cb
+        , ->
+          run (cb) =>
+            SyncSettings.createClient().gists.get({id: @gistId}, cb)
+          , (err, res) ->
+            for file in atom.config.get 'sync-settings.extraFiles'
+              expect(res.files[file]).toBeDefined()
+
     describe "::download", ->
       it "updates settings", ->
         atom.config.set "some-dummy", true
@@ -115,3 +126,15 @@
           , ->
             expect(SyncSettings.fileContent(atom.keymap.getUserKeymapPath())).toEqual original
             fs.writeFileSync atom.keymap.getUserKeymapPath(), original
+
+      it "downloads the files defined in config.extraFiles", ->
+        atom.config.set 'sync-settings.extraFiles', ['test.tmp', 'test2.tmp']
+        run (cb) ->
+          SyncSettings.upload cb
+        , ->
+          run (cb) =>
+            SyncSettings.download cb
+          , ->
+            for file in atom.config.get 'sync-settings.extraFiles'
+              expect(fs.existsSync("#{atom.config.configDirPath}/#{file}")).toBe(true)
+              # fs.unlink "#{atom.config.configDirPath}/#{file}"


yeoman skip-install to true

yeoman skip-install to true

--- src/steroids/ProjectCreator.coffee
+++ src/steroids/ProjectCreator.coffee
@@ -14,7 +14,7 @@

     env.plugins "node_modules", paths.npm
     env.lookup '*:*'
-    env.run "steroids:app #{targetDirectory}", {"skip-install": "nobodychecksthisvalue"}, deferred.resolve
+    env.run "steroids:app #{targetDirectory}", {"skip-install": true}, deferred.resolve

     return deferred.promise


Export jQuery global for imagesLoaded

Export jQuery global for imagesLoaded

--- src/assets/javascripts/application.coffee
+++ src/assets/javascripts/application.coffee
@@ -1,4 +1,4 @@
-$ = require 'jquery'
+window.jQuery = $ = require 'jquery'
 Device = require './device'
 Web = require './web'
 Mobile = require './mobile'


Add comment for markMessageAssRead

Add comment for markMessageAssRead

--- src/persistence.coffee
+++ src/persistence.coffee
@@ -203,6 +203,7 @@
   # 16. deleteAssignment needs to remove memberAssignments
   # 17. assignments need to load after saveMemberAssignment
   # 18. assignments need to load after deleteMemberAssignment
+  # 19. messages and messageData need to reload after markMessageAsRead

   # Load related records when a member is created
   wrapSave sdk, 'saveMember', (member) ->


fix(query): query failing because archivedAt key not being serialised so removing for now

--- vue/src/shared/models/user_model.coffee
+++ vue/src/shared/models/user_model.coffee
@@ -34,12 +34,12 @@

   groups: ->
     @recordStore.groups.collection.chain().
-      find(id: { $in: @groupIds() }, archivedAt: null).
+      find(id: { $in: @groupIds() }).
       simplesort('fullName').data()

   formalGroups: ->
     @recordStore.groups.collection.chain().
-      find(id: { $in: @groupIds() }, type: "FormalGroup", archivedAt: null).
+      find(id: { $in: @groupIds() }, type: "FormalGroup").
       simplesort('fullName').data()

   adminGroups: ->


no need to interate, when indexOf works as well

--- src/xml.coffee
+++ src/xml.coffee
@@ -36,13 +36,11 @@
                     if @closed and @pending.length is 0
                         @end()
             else
-                for known, i in @pending
-                    if tag is known
-                        @pending.splice(i, 1)
-                        if @closed is 'pending'
-                            @end()
-                        return
-                throw new Error("this shouldn't happen D:")
+                if (i = @pending.indexOf(tag)) is -1
+                    throw new Error("this shouldn't happen D:")
+                @pending.splice(i, 1)
+                if @closed is 'pending'
+                    @end()
             return

         @pending.push tag if tag.closed is no


guest vms: step 1: send username "guest" in the case of an undefined username

--- workers/auth/lib/auth/authworker.coffee
+++ workers/auth/lib/auth/authworker.coffee
@@ -51,7 +51,7 @@
     servicesOfType.push {serviceUniqueName, serviceGenericName, loadBalancing}

   removeService: ({serviceGenericName, serviceUniqueName}) ->
-    servicesOfType = @services[serviceGenericName]
+    servicesOfType = @services[serviceGenericName]
     [index] = (i for s, i in servicesOfType \
                  when s.serviceUniqueName is serviceUniqueName)
     servicesOfType.splice index, 1
@@ -132,11 +132,12 @@
         serviceInfo = @getNextServiceInfo messageData.name
         return console.error "No service info! #{messageData.name}"  unless serviceInfo?
         { serviceUniqueName, serviceGenericName, loadBalancing } = serviceInfo
+
         params = {
           serviceGenericName
           serviceUniqueName
           routingKey
-          username        : session.username
+          username        : session.username ? 'guest'
           correlationName : messageData.correlationName
           # maybe the callback wants this:
           socketId
@@ -175,7 +176,7 @@
             if err or not account then fail err
             else JGroup.one {slug: messageData.group}, (err, group) =>
               if err or not group then fail err
-              else
+              else
                 group.fetchRolesByAccount account, (err, roles) =>
                   if err or not roles then fail err
                   else
@@ -254,7 +255,7 @@
       correlationName, username } = messageData

     servicesOfType = @services[serviceGenericName]
-
+
     [matchingService] = (service for service in servicesOfType \
                          when service.serviceUniqueName is serviceUniqueName)
     params = {


stop calling the constructor once for every method on the store

--- src/superflux.coffee
+++ src/superflux.coffee
@@ -25,9 +25,9 @@
                     @callbacks[actionName] = []
                 @callbacks[actionName].push(value.bind(store))

-            # Call the constructor if present
-            if 'init' of store
-                store.init()
+        # Call the constructor if present
+        if 'init' of store
+            store.init()

         return store


Remove unnecessary requirejs config file

--- src/requirejs_config.coffee
+++ src/requirejs_config.coffee
@@ -1,9 +0,0 @@
-baseUrl: '.'
-paths:
-  'ice-view': 'js/view.js'
-  'ice-controller': 'js/controller.js'
-  'ice-model': 'js/model.js'
-  'ice-draw': 'js/draw.js'
-  'ice-coffee': 'js/coffeu.js'
-name: 'main'
-out: 'dist/ice.js'


Fix pan tool on retina

Fix pan tool on retina

--- src/tools/Pan.coffee
+++ src/tools/Pan.coffee
@@ -19,9 +19,13 @@
       @pointerStart = {x: rawX, y: rawY}

     unsubscribeFuncs.push lc.on 'lc-pointerdrag', ({rawX, rawY}) =>
+      # okay, so this is really bad:
+      # lc.position is "buggy screen coordinates": correct on non-retina,
+      # probably wrong on retina. compensate here; in v0.5 we should put the
+      # offset in drawing coordinates.
       dp = {
-        x: (rawX - @pointerStart.x),
-        y: (rawY - @pointerStart.y)
+        x: (rawX - @pointerStart.x) * lc.backingScale,
+        y: (rawY - @pointerStart.y) * lc.backingScale
       }
       lc.setPan(@oldPosition.x + dp.x, @oldPosition.y + dp.y)


Break out of the middle of a block

Break out of the middle of a block

--- src/trix/models/composition.coffee
+++ src/trix/models/composition.coffee
@@ -66,6 +66,10 @@
           @insertPlaceholderBlock()
       when range.end.offset is block.getLength() and block.hasAttributes() and block.text.endsWithString("\n")
         @insertPlaceholderBlock()
+      when block.hasAttributes() and block.text.getTextAtRange([0, range.end.offset]).endsWithString("\n\n")
+        if range.isCollapsed()
+          @selectionDelegate?.expandSelectionInDirectionWithGranularity("backward", "character")
+        @insertPlaceholderBlock()
       else
         @insertString("\n")