Follow mixin

Follow mixin

--- src/follow.coffee
+++ src/follow.coffee
@@ -3,7 +3,7 @@
     followSpeed: 1
     velocity: Point(0, 0)

-  follow: (obj, fo) ->
+  follow: (obj, followSpeed) ->
     I.velocity = obj.I.position().subtract(self.position()).norm().scale(I.followSpeed)

   return self


follow docs

follow docs

--- src/follow.coffee
+++ src/follow.coffee
@@ -13,6 +13,7 @@
 enemy = GameObject
   x: 100
   y: 50
+

 player.update()
 </pre></code>


Spanish Wave 9 and a couple fixes

Spanish Wave 9 and a couple fixes

-Translated all the cards from Wave 9 except the upgrades from TIE/sf and Shadowcaster. Hope to get them soon.
-Added Wave 9 Expansion Packs and ships translations
-Added the generic Protectorate Starfighter/Shadowcaster/TIE SF pilots
-Added "ship:" tags to all the Wave 9 pilots to avoid problems with the ship name translation
-Added "ship:" tags to the HotR T-70 pilots as they weren't showing up.
-Added "ship:" tags (and the two unspoiled pilots) for the Sabine TIE pilots, as they weren't showing up.

--- coffeescripts/cards-es.coffee
+++ coffeescripts/cards-es.coffee
@@ -75,6 +75,10 @@
         "Mist Hunter Expansion Pack": "Pack de Expansión Cazador de la Niebla"
         "Punishing One Expansion Pack": "Pack de Expansión Castigadora"
         "Imperial Veterans Expansion Pack": "Pack de Expansión Veteranos Imperiales"
+        "Protectorate Starfighter Expansion Pack": "Pack de Expansión Caza estelar del Protectorado"
+        "Shadow Caster Expansion Pack": "Pack de Expansión Sombra Alargada"
+        "Special Forces TIE Expansion Pack": "Pack de Expansión TIE de las Fuerzas Especiales"
+        "ARC-170 Expansion Pack": "Pack de Expansión ARC-170"
     ui:
         shipSelectorPlaceholder: "Selecciona una nave"
         pilotSelectorPlaceholder: "Selecciona un piloto"
@@ -185,6 +189,7 @@
     exportObj.renameShip 'TIE Advanced Prototype', 'Prototipo de TIE Avanzado'
     exportObj.renameShip 'VT-49 Decimator', 'VT-49 Diezmador'
     exportObj.renameShip 'TIE/fo Fighter', 'Caza TIE/fo'
+    exportObj.renameShip 'TIE/sf Fighter', 'Caza TIE/sf'
     exportObj.renameShip 'A-Wing', 'Ala-A'
     exportObj.renameShip 'B-Wing', 'Ala-B'
     exportObj.renameShip 'E-Wing', 'Ala-E'
@@ -197,12 +202,15 @@
     exportObj.renameShip 'CR90 Corvette (Fore)', 'Corbeta CR90 (Proa)'
     exportObj.renameShip 'GR-75 Medium Transport', 'Transporte mediano GR-75'
     exportObj.renameShip 'T-70 X-Wing', 'T-70 Ala-X'
+    exportObj.renameShip 'ARC-170', 'ARC-170'
     exportObj.renameShip 'M3-A Interceptor', 'Interceptor M3-A'
     exportObj.renameShip 'StarViper', 'Víbora Estelar'
     exportObj.renameShip 'Aggressor', 'Agresor'
     exportObj.renameShip 'Kihraxz Fighter', 'Caza Kihraxz'
     exportObj.renameShip 'G-1A Starfighter', 'Caza Estelar G-1A'
     exportObj.renameShip 'JumpMaster 5000', 'Saltador Maestro 5000'
+    exportObj.renameShip 'Protectorate Starfighter', 'Caza Estelar del Protectorado'
+    exportObj.renameShip 'Lancer-class Pursuit Craft', 'Nave de persecución clase Lancero'

     pilot_translations =
         "Wedge Antilles":
@@ -805,10 +813,6 @@
         "Poe Dameron (PS9)":
             text: """When attacking or defending, if you have a focus token, you may change 1 of your %FOCUS% results to a %HIT% or %EVADE% result."""
             ship: "T-70 Ala-X"
-        '''"Snap" Wexley''':
-            ship: "T-70 Ala-X"
-        'Jess Pava':
-            ship: "T-70 Ala-X"
         "Rey":
             text: """When attacking or defending, if the enemy ship is inside of your firing arc, you may reroll up to 2 of your blank results."""
         'Han Solo (TFA)':
@@ -816,32 +820,71 @@
         'Chewbacca (TFA)':
             text: '''After another friendly ship at Range 1-3 is destroyed (but has not fled the battlefield), you may perform an attack.'''
         'Norra Wexley':
-            text: '''When attacking or defending, you may spend a target lock you have on the enemy ship to add 1 %FOCUS% result to your roll.'''
+            ship: "ARC-170"
+            text: '''Cuando ataques o te defiendas, puedes gastar una ficha de Blanco fijado que tengas sobre la nave enemiga para añadir 1 resultado %FOCUS% a tu tirada.'''
         'Shara Bey':
-            text: '''When another friendly ship at Range 1-2 is attacking, it may treat your blue target lock tokens as its own.'''
+            ship: "ARC-170"
+            text: '''Cuando otra nave aliada que tengas a alcance 1-2 esté atacando, puede usar tus fichas azules de Blanco fijado como si fuesen suyas.'''
         'Thane Kyrell':
-            text: '''After an enemy ship in your firing arc at Range 1-3 attacks another friendly ship, you may perform a free action.'''
+            ship: "ARC-170"
+            text: '''Después de que una nave enemiga que tengas dentro de tu arco de fuego a alcance 1-3 ataque a otra nave aliada, puedes realizar una acción gratuita.'''
         'Braylen Stramm':
-            text: '''After you execute a maneuver, you may roll an attack die.  On a %HIT% or %CRIT% result, remove 1 stress token from your ship.'''
+            ship: "ARC-170"
+            text: '''Después de que ejecutes una maniobra, puedes tirar 1 dado de ataque. Si sacas %HIT% o %CRIT%, quita 1 ficha de Tensión de tu nave.'''
         '"Quickdraw"':
-            text: '''Once per round, when you lose a shield token, you may perform a primary weapon attack.'''
+            name: "Centella"
+            ship: "Caza TIE/sf"
+            text: '''Una vez por ronda, cuando pierdas una ficha de Escudos, puedes realizar un ataque de armamento principal.'''
         '"Backdraft"':
-            text: '''When attacking a ship inside your auxiliary firing arc, you may add 1 %CRIT% result.'''
+            name: "Llamarada"
+            ship: "Caza TIE/sf"
+            text: '''Cuando ataques a una nave dentro de tu arco de fuego auxiliar,  puedes añadir 1 resultado %CRIT%.'''
+        '"Omega Specialist"':
+            name: "Especialista Omega"
+            ship: "Caza TIE/sf"
+        '"Zeta Specialist"':
+            name: "Especialista Zeta"
+            ship: "Caza TIE/sf"
         'Fenn Rau':
-            text: '''When attacking or defending, if the enemy ship is at Range 1, you may roll 1 additional die.'''
+            ship: "Caza estelar del Protectorado"
+            text: '''Cuando ataques o te defiendas, si tienes la nave enemiga a alcance 1, puedes tirar 1 dado adicional.'''
         'Old Teroch':
-            text: '''At the start of the Combat phase, you may choose 1 enemy ship at Range 1.  If you are inside its firing arc, it discards all focus and evade tokens.'''
+            name: "Viejo Teroch"
+            ship: "Caza estelar del Protectorado"
+            text: '''Al comienzo de la fase de Combate, puedes elegir 1 nave enemiga que tengas a alcance 1. Si estás dentro de su arco de fuego, esa nave descarta todas sus fichas de Concentración y Evasión.'''
         'Kad Solus':
-            text: '''After you execute a red maneuver, assign 2 focus tokens to your ship.'''
+            ship: "Caza estelar del Protectorado"
+            text: '''Después de que ejecutes una maniobra roja, asigna 2 fichas de Concentración a tu nave.'''
+        'Concord Dawn Ace':
+            name: "As de Concord Dawn"
+            ship: "Caza estelar del Protectorado"
+        'Concord Dawn Veteran':
+            name: "Veterano de Concord Dawn"
+            ship: "Caza estelar del Protectorado"
+        'Zealous Recruit':
+            name: "Recluta entusiasta"
+            ship: "Caza estelar del Protectorado"
         'Ketsu Onyo':
-            text: '''At the start of the Combat phase, you may choose a ship at Range 1.  If it is inside your primary and mobile firing arcs, assign 1 tractor beam token to it.'''
+            ship: "Nave de persecución clase Lancero"
+            text: '''Al comienzo de la fase de Combate, puedes elegir una nave que tengas a alcance 1. Si esa nave está dentro de tus arcos de fuego normal y móvil, asígnale 1 ficha de Campo de tracción.'''
         'Asajj Ventress':
-            text: '''At the start of the Combat phase, you may choose a ship at Range 1-2.  If it inside your mobile firing arc, assign 1 stress token to it.'''
+            ship: "Nave de persecución clase Lancero"
+            text: '''Al comienzo de la fase de Combate, puedes elegir una nave que tengas a alcance 1-2. Si esa nave está dentro de tu arco de fuego móvil, asígnale 1 ficha de Tensión.'''
         'Sabine Wren (Scum)':
-            text: '''When defending against an enemy ship inside your mobile firing arc at Range 1-2, you may add 1 %FOCUS% result to your roll.'''
+            ship: "Nave de persecución clase Lancero"
+            text: '''Cuando te defiendas contra una nave enemiga que tengas dentro de tu arco de fuego móvil a alcance 1-2, puedes añadir 1 resultado %FOCUS% a tu tirada.'''
+        'Shadowport Hunter':
+            name: "Cazador de puerto clandestino"
+            ship: "Nave de persecución clase Lancero"
         'Sabine Wren (TIE Fighter)':
+            ship: "Caza TIE"
             text: '''Immediately before you reveal your maneuver, you may perform a free boost or barrel roll action.'''
+        'Cap???':
+            ship: "Caza TIE"
+        '"Ahso???':
+            ship: "Caza TIE"
         '"Zeb" Orrelios (TIE Fighter)':
+            ship: "Caza TIE"
             text: '''When defending, you may cancel %CRIT% results before %HIT% results.'''
         'Kylo Ren':
             text: '''The first time you are hit by an attack each round, deal the "I'll Show You the Dark Side" Condition card to the attacker.'''
@@ -852,10 +895,13 @@
         '''"Duchess"''':
             text: '''While you have the "Adaptive Ailerons" Upgrade card equipped, you may choose to ignore its card ability.'''
         'Nien Nunb':
+            ship: "T-70 Ala-X"
             text: '''When you receive a stress token, if there is an enemy ship inside your firing arc at Range 1, you may discard that stress token.'''
         '"Snap" Wexley':
+            ship: "T-70 Ala-X"
             text: '''After you execute a 2-, 3-, or 4-speed maneuver, if you are not touching a ship, you may perform a free boost action.'''
         'Jess Pava':
+            ship: "T-70 Ala-X"
             text: '''When attacking or defending, you may reroll 1 of your dice for each other friendly ship at Range 1.'''

     upgrade_translations =
@@ -1372,15 +1418,18 @@
             name: "Oficial de Sistemas"
             text: '''%IMPERIALONLY%%LINEBREAK%Después de que ejecutes una maniobra verde, elige otra nave aliada que tengas a alcance 1. Esa nave puede fijar un blanco.'''
         'Tail Gunner':
-            text: '''When attacking from your rear-facing auxiliary firing arc, reduce the defender's agility by 1 (to a minimum of "0").'''
+            name: "Artillero de cola"
+            text: '''Cuando ataques desde tu arco de fuego auxiliar trasero, la Agilidad del defensor se reduce en 1 (hasta un mínimo de 0).'''
         'R3 Astromech':
-            text: '''Once per round, when attacking with a primary weapon, you may cancel 1 of your %FOCUS% results during the "Modify Attack Dice" step to assign 1 evade token to your ship.'''
+            name: "Droide astromecánico R3"
+            text: '''Una vez por ronda, cuando ataques con un armamento principal, durante el paso "Modificar la tirada de ataque" puedes anular 1 de tus resultados %FOCUS% para asignar 1 ficha de Evasión a tu nave.'''
         'Collision Detector':
             text: '''When performing a boost, barrel roll, or decloak, your ship and maneuver template can overlap obstacles.%LINEBREAK%When rolling for obstacle damage, ignore all %CRIT% results.'''
         'Sensor Cluster':
             text: '''When defending, you may spend a focus token to change 1 of your blank results to an %EVADE% result.'''
         'Fearlessness':
-            text: '''%SCUMONLY%%LINEBREAK%When attacking, if you are inside the defender's firing arc at Range 1 and the defender is inside your firing arc, you may add 1 %HIT% result to your roll.'''
+            name: "Intrepidez"
+            text: '''%SCUMONLY%%LINEBREAK%Cuando ataques, si estás dentro del arco de fuego del defensor a alcance 1 y el defensor está dentro de tu arco de fuego, puedes añadir 1 resultado %HIT% a tu tirada.'''
         'Ketsu Onyo':
             text: '''%SCUMONLY%%LINEBREAK%At the start of the End phase, you may choose 1 ship in your firing arc at Range 1-2.  That ship does not remove its tractor beam tokens.'''
         'Latts Razzi':
@@ -1390,7 +1439,8 @@
         'Rigged Cargo Chute':
             text: '''%LARGESHIPONLY%%LINEBREAK%Action: Discard this card to drop one cargo token.'''
         'Seismic Torpedo':
-            text: '''Action: Discard this card to choose an obstacle at Range 1-2 and inside your primary firing arc.  Each ship at Range 1 of the obstacle rolls 1 attack die and suffers any damage (%HIT%) or critical damage (%CRIT%) rolled.  Then remove the obstacle.'''
+            name: "Torpedo sísmico"
+            text: '''Acción: Descarta esta carta para elegir un obstáculo que tengas a alcance 1-2 y dentro de tu arco de fuego normal. Toda nave situada a alcance 1 del obstáculo tira 1 dado de ataque y sufre cualquier daño (%HIT%) o o daño crítico (%CRIT%) otenido. Luego retira el obstáculo.'''
         'Black Market Slicer Tools':
             text: '''Action: Choose a stressed enemy ship at Range 1-2 and roll 1 attack die. On a (%HIT%) or (%CRIT%) result, remove 1 stress token and deal it 1 facedown Damage card.'''
         # Wave X
@@ -1495,7 +1545,8 @@
             name: "Chips de Guiado"
             text: """Una vez por ronda, cuando ataques con un sistema de armamento secundario %TORPEDO% o %MISSILE%, puedes cambiar 1 de tus resultados de dado por un resultado %HIT% (o por un resultad %CRIT% si tu valor de Armamento principal es de 3 o más)."""
         'Vectored Thrusters':
-            text: '''%SMALLSHIPONLY%%LINEBREAK%Your action bar gains the %BARRELROLL% action icon.'''
+            name: "Propulsores vectoriales"
+            text: '''%SMALLSHIPONLY%%LINEBREAK%Tu barra de acciones gana el icono de acción %BARRELROLL%.'''
         'Smuggling Compartment':
             text: '''YT-1300 and YT-2400 only.%LINEBREAK%Your upgrade bar gains the %ILLICIT% upgrade icon.%LINEBREAK%You may equip 1 additional Modification upgrade that costs 3 or fewer squad points.'''
         'Gyroscopic Targeting':
@@ -1634,11 +1685,13 @@
         'Millennium Falcon (TFA)':
             text: '''After you execute a 3-speed bank maneuver (%BANKLEFT% or %BANKRIGHT%), if you are not touching another ship and you are not stressed, you may receive 1 stress token to rotate your ship 180°.'''
         'Alliance Overhaul':
-            text: '''ARC-170 only.%LINEBREAK%When attacking with a primary weapon from your primary firing arc, you may roll 1 additional attack die.  When attacking from your auxiliary firing arc, you may change 1 of your %FOCUS% results to a %CRIT% result.'''
+            name: "Reacondicionado por la Alianza"
+            text: '''Sólo ARC-170.%LINEBREAK%Cuando ataques con un armamento principal desde tu arco de fuego normal, puedes tirar 1 dado de ataque adicional. Cuando ataques desde tu arco de fuego auxiliar, puedes cambiar 1 de tus resultados %FOCUS% por 1 resultado %CRIT%.'''
         'Special Ops Training':
             text: '''TIE/sf only.%LINEBREAK%When attacking with a primary weapon from your primary firing arc, you may roll 1 additional attack die.  If you do not, you may perform an additional attack from your auxiliary firing arc.'''
         'Concord Dawn Protector':
-            text: '''Protectorate Starfighter only.%LINEBREAK%When defending, if you are inside the attacker's firing arc and at Range 1 and the attacker is inside your firing arc, add 1 %EVADE% result.'''
+            name: "Protector de Concord Dawn"
+            text: '''Sólo Caza estelar del Protectorado.%LINEBREAK%Cuando te defiendas, si estás dentro del arco de fuego del atacante y a alcance 1 y el atacante está dentro de tu arco de fuego, añade 1 resultado %EVADE%.'''
         'Shadow Caster':
             text: '''Lancer-class Pursuit Craft only.%LINEBREAK%After you perform an attack that hits, if the defender is inside your mobile firing arc and at Range 1-2, you may assign the defender 1 tractor beam token.'''
         # Wave X


chr2dpanelframe: fix gridline positions

chr2dpanelframe: fix gridline positions

--- src/chr2dpanelframe.coffee
+++ src/chr2dpanelframe.coffee
@@ -153,8 +153,12 @@
              .data(data.chr[0..(data.chr.length - 2)])
              .enter()
              .append("line")
-             .attr("y1", (d,i) -> yscale[d](data.end[i])+chrGap/2)
-             .attr("y2", (d,i) -> yscale[d](data.end[i])+chrGap/2)
+             .attr("y1", (d,i) ->
+                 return yscale[d](data.end[i])+chrGap/2 if oneAtTop
+                 yscale[d](data.end[i])-chrGap/2)
+             .attr("y2", (d,i) ->
+                 return yscale[d](data.end[i])+chrGap/2 if oneAtTop
+                 yscale[d](data.end[i])-chrGap/2)
              .attr("x1", margin.left)
              .attr("x2", margin.left + plot_width)
              .attr("stroke", chrlinecolor)


Fix window resize bug

Fix window resize bug

--- scripts/modules/featured-books/featured-books.coffee
+++ scripts/modules/featured-books/featured-books.coffee
@@ -14,6 +14,11 @@
       initialize: () ->
         super()
         @listenTo(library, 'reset', @render)
+
+        # Don't run any animations while the window is being resized
+        $(window).resize () ->
+          @stopCarousel()
+          @startCarousel()

       # @template must be evaluated prior to rendering
       beforeRender: () -> @template = template({books: library.toJSON()})
@@ -64,4 +69,4 @@

         # Start the carousel
         if @_carousel then clearInterval(@_carousel)
-        @_carousel = setInterval(nextFeatured, 2000)
+        @_carousel = setInterval(nextFeatured, 3000)


Minor fix in the kernel srv unreg

Minor fix in the kernel srv unreg

--- library/nucleus/kernel.coffee
+++ library/nucleus/kernel.coffee
@@ -121,9 +121,9 @@
     shutdownKernel: (reason) ->
         util.puts require("os").EOL
         logger.warn reason.toString().red
-        try @router.shutdownRouter()
-        registry = @router.registry or []
-        @router.unregister s for s in registry
+        try @router.shutdownRouter?()
+        snapshot = _.clone(@router.registry or [])
+        @router.unregister srv for srv in snapshot
         try @server.close(); try @secure.close()
         shutdown = "Shutting the kernel instance down".red
         logger.warn shutdown; @emit "shutdown"


#46 Adding http port to WS connection when set and if not using TLS.

--- src/client.coffee
+++ src/client.coffee
@@ -57,7 +57,7 @@
                 @authenticated = true
                 # Continue happy flow here
                 @token = headers.token
-                @socketUrl = (if useTLS then 'wss://' else 'ws://') + @host + (if (useTLS and @options.wssPort?) then ':'+@options.wssPort else '') + '/api/v4/websocket'
+                @socketUrl = (if useTLS then 'wss://' else 'ws://') + @host + (if (useTLS and @options.wssPort?) then ':'+@options.wssPort else (if @options.httpPort? then ':' + @options.httpPort else '')) + '/api/v4/websocket'
                 @logger.info 'Websocket URL: ' + @socketUrl
                 @self = new User data
                 @emit 'loggedIn', @self


Server: API/SSOTokenLogin: return api response in async callback

--- servers/lib/server/handlers/api/ssotokenlogin.coffee
+++ servers/lib/server/handlers/api/ssotokenlogin.coffee
@@ -13,6 +13,7 @@
   { token } = req.query
   user      = null
   group     = null
+  session   = null
   account   = null
   username  = null

@@ -22,20 +23,20 @@

     (next) ->
       validateJWTToken token, (err, data) ->
-        return sendApiError res, err  if err
+        return next err  if err
         { username, group } = data

         # making sure subdomain is same with group slug
         unless group in req.subdomains
-          return sendApiError res, apiErrors.invalidRequestDomain
+          return next apiErrors.invalidRequestDomain

         next()

     (next) ->
       # checking if user exists
       JAccount.one { 'profile.nickname' : username }, (err, account_) ->
-        return sendApiError res, apiErrors.internalError    if err
-        return sendApiError res, apiErrors.invalidUsername  unless account_
+        return next apiErrors.internalError    if err
+        return next apiErrors.invalidUsername  unless account_
         account = account_
         next()

@@ -43,19 +44,22 @@
       # checking if user is a member of the group of api token
       client = { connection : { delegate : account } }
       account.checkGroupMembership client, group, (err, isMember) ->
-        return sendApiError res, apiErrors.internalError   if err
-        return sendApiError res, apiErrors.notGroupMember  unless isMember
+        return next apiErrors.internalError   if err
+        return next apiErrors.notGroupMember  unless isMember
         next()

     (next) ->
       # creating a user session for the group if everything is ok
-      JSession.createNewSession { username, groupName : group }, (err, session) ->
-        return sendApiError res, apiErrors.internalError           if err
-        return sendApiError res, apiErrors.failedToCreateSession   unless session
-
-        setSessionCookie res, session.clientId
-        res.redirect('/')
+      JSession.createNewSession { username, groupName : group }, (err, session_) ->
+        return next apiErrors.internalError           if err
+        return next apiErrors.failedToCreateSession   unless session_
+        session = session_
+        next()

   ]

-  async.series queue
+  async.series queue, (err) ->
+    return sendApiError res, err  if err
+
+    setSessionCookie res, session.clientId
+    res.redirect('/')


implement `position` and `getStyles` methods

--- src/coffee/domUtils.coffee
+++ src/coffee/domUtils.coffee
@@ -51,13 +51,27 @@
     children

   ###
-  # @param {String} el
+   * @param {HTMLElement} el
+   * @type Object
+  ###
+  getStyles:( el )->
+    if el.ownerDocument.defaultView.opener
+      el.ownerDocument.defaultView.getComputedStyle el, null
+    else
+      window.getComputedStyle el, null
+
+  ###
+  # @param {HTMLElement} el
   # @param {Object} attrs
   ###
-  css:( el, attrs )->
+  css:( el, attrs, extra = false )->

-    for key, attr of attrs
-      el.style[key] = attr
+    if typeof attrs is "string"
+      val = @getStyles( el )[ attrs ]
+      if extra is true then parseFloat val else val
+    else
+      for key, attr of attrs
+        el.style[key] = attr

   ###
   # @param {HTMLElement} el
@@ -70,6 +84,48 @@
     left:box.left + ( docElem.scrollLeft ) - ( docElem.clientLeft || 0 )

   ###
+   * @param {HTMLElement} el
+  ###
+  position:( el )->
+    parentOffset =
+      top:0
+      left:0
+
+    if @css( el, 'position' ) is 'fixed'
+      el.getBoundingClientRect()
+    else
+
+      offsetParent = @offsetParent el.offsetParent
+
+      if offsetParent and !@nodeName( offsetParent, 'html' )
+        parentOffset = domUtils.offset offsetParent
+
+      offset = @offset el
+
+      parentOffset.top += @css offsetParent, 'borderTopWidth', true
+      parentOffset.left += @css offsetParent, 'borderLeftWidth', true
+
+      top:  offset.top  - parentOffset.top -  @css el, 'marginTop', true
+      left: offset.left - parentOffset.left - @css el, 'marginLeft', true
+
+  ###
+   * @param {HTMLElement} el
+  ###
+  offsetParent:( el )->
+    docElem = document.documentElement
+    offsetParent = offsetParent or docElem
+
+    while offsetParent and ( !@nodeName( offsetParent, 'html' ) and @css( offsetParent, "position" ) is "static" )
+      offsetParent = offsetParent.offsetParent
+    offsetParent or docElem
+
+  ###
+   * @param {HTMLElement} el
+   * @param {String} name
+  ###
+  nodeName:( el, name )-> el.nodeName and el.nodeName.toLowerCase() is name.toLowerCase()
+
+  ###
   # @param {HTMLElement} el
   # @param {String} name
   # @param {*} detail


added explicit extent check on load

added explicit extent check on load

--- bokehjs/src/coffee/renderer/tile/dynamic_image_renderer.coffee
+++ bokehjs/src/coffee/renderer/tile/dynamic_image_renderer.coffee
@@ -32,7 +32,9 @@
     image_data.img = e.target
     @mget('image_source').add_image(image_data)
     @lastImage = image_data
-    @request_render()
+
+    if @get_extent() == image_data.bounds
+      @request_render()

   _on_tile_error: (e) =>
     return ''


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/color2.coffee
+++ src/color2.coffee
@@ -110,8 +110,11 @@
     a: parsedColor[3]

   Color:: =
-    aMethod: ->
-      true
+    equals: (other) ->
+      other.r == @r &&
+      other.g == @g &&
+      other.b == @b &&
+      other.a == @a

   (exports ? this)["Color2"] = Color2
 )()


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/clamp.coffee
+++ src/clamp.coffee
@@ -3,7 +3,9 @@
     clamps: {}

   self.bind "update", ->
-    for name, cooldownOptions of I.clamps
+    for , cooldownOptions of I.clamps
+
+
       {approachBy, target} = cooldownOptions

       I[name] = I[name].approach(target, approachBy)


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/color.coffee
+++ src/color.coffee
@@ -41,17 +41,17 @@
     parsedColor = null

     if arguments.length == 2
-      if arguments[0].length == 3 && Object.prototype.toString.call(arguments[0]) == '[object Array]'
+      if arguments[0].length == 3 && Object.prototype.toString.call(color) == '[object Array]'
         parsedColor = [parseInt(arguments[0][0]), parseInt(arguments[0][1]), parseInt(arguments[0][2]), parseFloat(arguments[1])]
     else if arguments.length > 2
       alpha = if arguments[3]? then arguments[3] else 1
       parsedColor = [parseInt(arguments[0]), parseInt(arguments[1]), parseInt(arguments[2]), parseFloat(alpha)]
-    else if arguments.length > 2 && Object.prototype.toString.call(arguments[0]) == '[object Array]'
-      alpha = if arguments[3]? then arguments[3] else 1
+    else if arguments.length > 2 && Object.prototype.toString.call(color) == '[object Array]'
+      alpha = if color[3]? then color[3] else 1
       parsedColor = [
-        parseInt(arguments[0]),
-        parseInt(arguments[1]),
-        parseInt(arguments[2]),
+        parseInt(color[0]),
+        parseInt(color[1]),
+        parseInt(color[2]),
         parseFloat(alpha)
       ]
     else


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/color.coffee
+++ src/color.coffee
@@ -6,11 +6,6 @@

 (->
   rgbParser = /^rgba?\((\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3}),?\s*(\d\.?\d*)?\)$/
-
-  toHex = (bits) ->
-    s = parseInt(bits).toString(16)
-
-    s = '0' + s if s.length == 1

   parseHex = (hexString) ->
     hexString = hexString.replace(/#/, '')


allowing export of hsl and hsla string (#17)

--- src/color.coffee
+++ src/color.coffee
@@ -158,11 +158,24 @@
             return @
         @_rgb[3]

-    css: () ->
-        if @_rgb[3] < 1
-            'rgba('+@_rgb.join(',')+')'
-        else
-            'rgb('+@_rgb.slice(0,3).join(',')+')'
+    css: (mode='rgb') ->
+        me = @
+        rgb = me._rgb
+        if mode.length == 3 and rgb[3] < 1
+            mode += 'a'
+        if mode == 'rgb'
+            mode+'('+rgb.slice(0,3).join(',')+')'
+        else if mode == 'rgba'
+            mode+'('+rgb.join(',')+')'
+        else if mode == 'hsl' or mode == 'hsla'
+            hsl = me.hsl()
+            rnd = (a) -> Math.round(a*100)/100
+            hsl[0] = rnd(hsl[0])
+            hsl[1] = rnd(hsl[1]*100) + '%'
+            hsl[2] = rnd(hsl[2]*100) + '%'
+            if mode.length == 4
+                hsl[3] = rgb[3]
+            mode + '(' + hsl.join(',') + ')'

     interpolate: (f, col, m) ->
         ###


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/color.coffee
+++ src/color.coffee
@@ -1571,8 +1571,9 @@
 ]

 names.each (element) ->
-  debugger
   lookup[element[1].toLowerCase().replace(/ /g, "")] = Color(element[0])
+
+log lookup

 ###
   check for named colors in a hash table


Use `@constructor` instead of `this.constructor`

--- src/config.coffee
+++ src/config.coffee
@@ -19,8 +19,8 @@

   constructor: (options={}) ->
     @options = _.chain(options)
-      .pick(_.keys this.constructor.DEFAULTS)
-      .defaults(this.constructor.DEFAULTS)
+      .pick(_.keys @constructor.DEFAULTS)
+      .defaults(@constructor.DEFAULTS)
       .value()
     _.extend this, @options

@@ -86,7 +86,7 @@
     conditions = @conditions()
     for option, value of @options
       # Skip options that default to null
-      if _.isNull(value) and this.constructor.DEFAULTS[option] is null
+      if _.isNull(value) and @constructor.DEFAULTS[option] is null
         continue

       # Disabled via -1


Add more missing at signs

Add more missing at signs

--- src/config.coffee
+++ src/config.coffee
@@ -36,16 +36,16 @@
     fs.existsSync "C:\\Windows\\SysWow64\\Notepad.exe"

   x86ProgramFilesDirectory: ->
-    process.env["ProgramFiles(x86)"] || process.env["ProgramFiles"]
+    process.env["ProgramFiles(x86)"] or process.env["ProgramFiles"]

   isVs2010Installed: ->
     return false unless @isWin32()

-    vsPath = path.join x86ProgramFilesDirectory(), "Microsoft Visual Studio 10.0", "Common7", "IDE", "devenv.exe"
+    vsPath = path.join @x86ProgramFilesDirectory(), "Microsoft Visual Studio 10.0", "Common7", "IDE", "devenv.exe"
     fs.existsSync vsPath

   isVs2012Installed: ->
     return false unless @isWin32()

-    vsPath = path.join x86ProgramFilesDirectory(), "Microsoft Visual Studio 11.0", "Common7", "IDE", "devenv.exe"
+    vsPath = path.join @x86ProgramFilesDirectory(), "Microsoft Visual Studio 11.0", "Common7", "IDE", "devenv.exe"
     fs.existsSync vsPath


:art:

:art:

--- spec/fuzzy-finder-spec.coffee
+++ spec/fuzzy-finder-spec.coffee
@@ -1230,12 +1230,12 @@
           runs ->
             gitRepository.getPathStatus(editor.getPath())

-            waitsForPromise ->
-              bufferView.toggle()
-
-            runs ->
-              expect(bufferView.element.querySelectorAll('.status.status-added').length).toBe 1
-              expect(bufferView.element.querySelector('.status.status-added').closest('li').querySelector('.file').textContent).toBe 'newsample.js'
+          waitsForPromise ->
+            bufferView.toggle()
+
+          runs ->
+            expect(bufferView.element.querySelectorAll('.status.status-added').length).toBe 1
+            expect(bufferView.element.querySelector('.status.status-added').closest('li').querySelector('.file').textContent).toBe 'newsample.js'

     describe "when core.excludeVcsIgnoredPaths is set to true", ->
       beforeEach ->


Rename .write() to writeSync()

Rename .write() to writeSync()

--- spec/fuzzy-finder-spec.coffee
+++ spec/fuzzy-finder-spec.coffee
@@ -240,15 +240,15 @@
       editor = rootView.getActiveView()
       originalText = editor.getText()
       originalPath = editor.getPath()
-      fsUtils.write(originalPath, 'making a change for the better')
+      fsUtils.writeSync(originalPath, 'making a change for the better')
       git.getPathStatus(originalPath)

       newPath = project.resolve('newsample.js')
-      fsUtils.write(newPath, '')
+      fsUtils.writeSync(newPath, '')
       git.getPathStatus(newPath)

     afterEach ->
-      fsUtils.write(originalPath, originalText)
+      fsUtils.writeSync(originalPath, originalText)
       fsUtils.remove(newPath) if fsUtils.exists(newPath)

     it "displays all new and modified paths", ->
@@ -370,7 +370,7 @@

       beforeEach ->
         ignoreFile = path.join(project.getPath(), '.gitignore')
-        fsUtils.write(ignoreFile, 'sample.js')
+        fsUtils.writeSync(ignoreFile, 'sample.js')
         config.set("core.excludeVcsIgnoredPaths", true)

       afterEach ->
@@ -504,10 +504,10 @@
       originalText = editor.getText()
       originalPath = editor.getPath()
       newPath = project.resolve('newsample.js')
-      fsUtils.write(newPath, '')
+      fsUtils.writeSync(newPath, '')

     afterEach ->
-      fsUtils.write(originalPath, originalText)
+      fsUtils.writeSync(originalPath, originalText)
       fsUtils.remove(newPath) if fsUtils.exists(newPath)

     describe "when a modified file is shown in the list", ->


Move pythonTools assignment to beforeEach

--- spec/python-tools-spec.coffee
+++ spec/python-tools-spec.coffee
@@ -1,13 +1,15 @@
 PythonTools = require '../lib/python-tools'

 describe "PythonTools", ->
+  pythonTools = null
   beforeEach ->
     waitsForPromise ->
       atom.packages.activatePackage('python-tools')
+    runs ->
+      pythonTools = atom.packages.getActivePackage('python-tools').mainModule

   describe "when a response is returned from tools.py", ->
     it "correctly deserializes JSON", ->
-      pythonTools = atom.packages.getActivePackage('python-tools').mainModule
       pythonTools.handleJsonResponse('''{
         "type": "usages",
         "definitions": {
@@ -16,7 +18,6 @@
       }''')

     it "informs the user with an info notification when no items were found", ->
-      pythonTools = atom.packages.getActivePackage('python-tools').mainModule
       pythonTools.handleJediToolsResponse(
         type: "usages"
         definitions: []
@@ -25,15 +26,12 @@
       expect(notification.type).toBe 'info'

     it "informs the user with an error notifiation on invalid type", ->
-      pythonTools = atom.packages.getActivePackage('python-tools').mainModule
       pythonTools.handleJediToolsResponse(
         type: "monkeys"
-        definitions: [
-          {
+        definitions: [{
             line: 0
             column: 0
-          }
-        ]
+        }   ]
       )
       [notification] = atom.notifications.getNotifications()
       expect(notification.type).toBe 'error'


Dispatch event on editor, not workspace

Dispatch event on editor, not workspace

--- spec/symbols-view-spec.coffee
+++ spec/symbols-view-spec.coffee
@@ -327,7 +327,7 @@
           editor = atom.workspace.getActiveTextEditor()
           editor.setCursorBufferPosition([8,14])
           spyOn(SymbolsView.prototype, "moveToPosition").andCallThrough()
-          atom.commands.dispatch(getWorkspaceView(), 'symbols-view:go-to-declaration')
+          atom.commands.dispatch(getEditorView(), 'symbols-view:go-to-declaration')

         waitsFor ->
           SymbolsView::moveToPosition.callCount is 1


:lipstick: further readability and consistencies

--- spec/text-objects-spec.coffee
+++ spec/text-objects-spec.coffee
@@ -1,6 +1,8 @@
 helpers = require './spec-helper'

-describe "TextObjects", ->
+_ = require 'underscore-plus'
+
+fdescribe "TextObjects", ->
   [editor, editorElement, vimState] = []

   beforeEach ->
@@ -70,54 +72,58 @@
   classListContains = (klass) ->
     expect editorElement.classList.contains(klass)

-  ensure = (spec={}) ->
-    if spec.text?
-      text().toBe spec.text
-    if spec.cursor?
-      cursor().toEqual spec.cursor
-    if spec.register?
-      register('"').toBe spec.register
-
-    if spec.selectedBufferRange?
-      selectedBufferRange().toEqual spec.selectedBufferRange
-    if spec.selectedBufferRanges?
-      selectedBufferRanges().toEqual spec.selectedBufferRanges
-
-    if spec.selectedScreenRange?
-      selectedScreenRange().toEqual spec.selectedScreenRange
-    if spec.selectedScreenRanges?
-      selectedScreenRanges().toEqual spec.selectedScreenRanges
+  ensure = (_keystroke, options={}) ->
+    keystroke(_keystroke)
+    if options.text?
+      text().toBe options.text
+    if options.cursor?
+      cursor().toEqual options.cursor
+    if options.register?
+      register('"').toBe options.register
+
+    if options.selectedBufferRange?
+      selectedBufferRange().toEqual options.selectedBufferRange
+    if options.selectedBufferRanges?
+      selectedBufferRanges().toEqual options.selectedBufferRanges
+
+    if options.selectedScreenRange?
+      selectedScreenRange().toEqual options.selectedScreenRange
+    if options.selectedScreenRanges?
+      selectedScreenRanges().toEqual options.selectedScreenRanges

     notEnsureNormalMode = [
-      spec.selectedBufferRange, spec.selectedBufferRanges
-      spec.selectedScreenRange, spec.selectedScreenRanges
+      options.selectedBufferRange, options.selectedBufferRanges
+      options.selectedScreenRange, options.selectedScreenRanges
     ].some((bool) -> bool)
     unless notEnsureNormalMode
       classListContains('operator-pending-mode').toBe(false)
       classListContains('normal-mode').toBe(true)

-  ensureKeystroke = (_keystroke, spec={}) ->
-    keystroke(_keystroke)
-    ensure(spec)
+  set = (options={}) ->
+    text(options.text) if options.text?
+    cursor(options.cursor) if options.cursor?
+    addCursor(options.addCursor) if options.addCursor?

   describe "the 'iw' text object", ->
     beforeEach ->
-      text "12345 abcde ABCDE"
-      cursor [0, 9]
-
-    it "applies operators inside the current word in operator-pending mode", ->
-      ensureKeystroke 'diw',
+      set
+        text: "12345 abcde ABCDE"
+        cursor: [0, 9]
+
+    it "applies operators inside the current word in operator-pending mode", ->
+      ensure 'diw',
         text:     "12345  ABCDE"
         cursor:   [0, 6]
         register: 'abcde'

     it "selects inside the current word in visual mode", ->
-      ensureKeystroke 'viw',
+      ensure 'viw',
         selectedScreenRange: [[0, 6], [0, 11]]

     it "works with multiple cursors", ->
-      addCursor [0, 1]
-      ensureKeystroke 'viw',
+      set
+        addCursor: [0, 1]
+      ensure 'viw',
         selectedBufferRanges: [
           [[0, 6], [0, 11]]
           [[0, 0], [0, 5]]
@@ -125,40 +131,44 @@

   describe "the 'iW' text object", ->
     beforeEach ->
-      text "12(45 ab'de ABCDE"
-      cursor [0, 9]
+      set
+        text: "12(45 ab'de ABCDE"
+        cursor: [0, 9]

     it "applies operators inside the current whole word in operator-pending mode", ->
-      ensureKeystroke 'diW',
+      ensure 'diW',
         text:     "12(45  ABCDE"
         cursor:   [0, 6]
         register: "ab'de"

     it "selects inside the current whole word in visual mode", ->
-      ensureKeystroke 'viW',
+      ensure 'viW',
         selectedScreenRange: [[0, 6], [0, 11]]

   describe "the 'i(' text object", ->
     beforeEach ->
-      text "( something in here and in (here) )"
-      cursor [0, 9]
-
-    it "applies operators inside the current word in operator-pending mode", ->
-      ensureKeystroke 'di(',
+      set
+        text: "( something in here and in (here) )"
+        cursor: [0, 9]
+
+    it "applies operators inside the current word in operator-pending mode", ->
+      ensure 'di(',
         text: "()"
         cursor: [0, 1]

     it "applies operators inside the current word in operator-pending mode (second test)", ->
-      cursor [0, 29]
-      ensureKeystroke 'di(',
+      set
+        cursor: [0, 29]
+      ensure 'di(',
         text: "( something in here and in () )"
         cursor: [0, 28]

     it "works with multiple cursors", ->
-      text "( a b ) cde ( f g h ) ijk"
-      cursorBuffer [0, 2]
-      addCursor [0, 18]
-      ensureKeystroke 'vi(',
+      set
+        text: "( a b ) cde ( f g h ) ijk"
+        cursor: [0, 2]
+        addCursor: [0, 18]
+      ensure 'vi(',
         selectedBufferRanges: [
           [[0, 1],  [0, 6]]
           [[0, 13], [0, 20]]
@@ -166,105 +176,116 @@

   describe "the 'i{' text object", ->
     beforeEach ->
-      text "{ something in here and in {here} }"
-      cursor [0, 9]
-
-    it "applies operators inside the current word in operator-pending mode", ->
-      ensureKeystroke 'di{',
+      set
+        text: "{ something in here and in {here} }"
+        cursor: [0, 9]
+
+    it "applies operators inside the current word in operator-pending mode", ->
+      ensure 'di{',
         text: "{}"
         cursor: [0, 1]

     it "applies operators inside the current word in operator-pending mode (second test)", ->
-      cursor [0, 29]
-      ensureKeystroke 'di{',
+      set
+        cursor: [0, 29]
+      ensure 'di{',
         text: "{ something in here and in {} }"
         cursor: [0, 28]

   describe "the 'i<' text object", ->
     beforeEach ->
-      text "< something in here and in <here> >"
-      cursor [0, 9]
-
-    it "applies operators inside the current word in operator-pending mode", ->
-      ensureKeystroke 'di<',
+      set
+        text: "< something in here and in <here> >"
+        cursor: [0, 9]
+
+    it "applies operators inside the current word in operator-pending mode", ->
+      ensure 'di<',
         text: "<>"
         cursor: [0, 1]

     it "applies operators inside the current word in operator-pending mode (second test)", ->
-      cursor [0, 29]
-      ensureKeystroke 'di<',
+      set
+        cursor: [0, 29]
+      ensure 'di<',
         text: "< something in here and in <> >"
         cursor: [0, 28]

   describe "the 'it' text object", ->
     beforeEach ->
-      text "<something>here</something><again>"
-      cursor [0, 5]
+      set
+        text: "<something>here</something><again>"
+        cursor: [0, 5]

     it "applies only if in the value of a tag", ->
-      ensureKeystroke 'dit',
+      ensure 'dit',
         text: "<something></something><again>"
         cursor: [0, 11]

     it "applies operators inside the current word in operator-pending mode", ->
-      cursor [0, 13]
-      ensureKeystroke 'dit',
+      set
+        cursor: [0, 13]
+      ensure 'dit',
         text: "<something></something><again>"
         cursor: [0, 11]

   describe "the 'ip' text object", ->
     beforeEach ->
-      text "\nParagraph-1\nParagraph-1\nParagraph-1\n\n"
-      cursor [2, 2]
+      set
+        text: "\nParagraph-1\nParagraph-1\nParagraph-1\n\n"
+        cursor: [2, 2]

     it "applies operators inside the current paragraph in operator-pending mode", ->
-      ensureKeystroke 'yip',
+      ensure 'yip',
         text: "\nParagraph-1\nParagraph-1\nParagraph-1\n\n"
         cursor: [1, 0]
         register: "Paragraph-1\nParagraph-1\nParagraph-1\n"

     it "selects inside the current paragraph in visual mode", ->
-      ensureKeystroke 'vip',
+      ensure 'vip',
         selectedScreenRange: [[1, 0], [4, 0]]

   describe "the 'ap' text object", ->
     beforeEach ->
-      text "text\n\nParagraph-1\nParagraph-1\nParagraph-1\n\nmoretext"
-      cursor [3, 2]
+      set
+        text: "text\n\nParagraph-1\nParagraph-1\nParagraph-1\n\nmoretext"
+        cursor: [3, 2]

     it "applies operators around the current paragraph in operator-pending mode", ->
-      ensureKeystroke 'yap',
+      ensure 'yap',
         text: "text\n\nParagraph-1\nParagraph-1\nParagraph-1\n\nmoretext"
         cursor: [2, 0]
         register: "Paragraph-1\nParagraph-1\nParagraph-1\n\n"

     it "selects around the current paragraph in visual mode", ->
-      ensureKeystroke 'vap',
+      ensure 'vap',
         selectedScreenRange: [[2, 0], [6, 0]]

   describe "the 'i[' text object", ->
     beforeEach ->
-      text "[ something in here and in [here] ]"
-      cursor [0, 9]
-
-    it "applies operators inside the current word in operator-pending mode", ->
-      ensureKeystroke 'di[',
+      set
+        text: "[ something in here and in [here] ]"
+        cursor: [0, 9]
+
+    it "applies operators inside the current word in operator-pending mode", ->
+      ensure 'di[',
         text: "[]"
         cursor: [0, 1]

     it "applies operators inside the current word in operator-pending mode (second test)", ->
-      cursor [0, 29]
-      ensureKeystroke 'di[',
+      set
+        cursor: [0, 29]
+      ensure 'di[',
         text: "[ something in here and in [] ]"
         cursor: [0, 28]

   describe "the 'i\'' text object", ->
     beforeEach ->
-      text "' something in here and in 'here' ' and over here"
-      cursor [0, 9]
+      set
+        text: "' something in here and in 'here' ' and over here"
+        cursor: [0, 9]

     it "applies operators inside the current string in operator-pending mode", ->
-      ensureKeystroke "di'",
+      ensure "di'",
         text: "''here' ' and over here"
         cursor: [0, 1]

@@ -272,179 +293,201 @@
     # Simply selecting area between quote is that normal user expects.
     # it "applies operators inside the next string in operator-pending mode (if not in a string)", ->
     it "[Changed behavior] applies operators inside area between quote", ->
-      cursor [0, 29]
-      ensureKeystroke "di'",
+      set
+        cursor: [0, 29]
+      ensure "di'",
         text: "' something in here and in '' ' and over here"
         cursor: [0, 28]

     it "makes no change if past the last string on a line", ->
-      cursor [0, 39]
-      ensureKeystroke "di'",
+      set
+        cursor: [0, 39]
+      ensure "di'",
         text: "' something in here and in 'here' ' and over here"
         cursor: [0, 39]

   describe "the 'i\"' text object", ->
     beforeEach ->
-      text '" something in here and in "here" " and over here'
-      cursor [0, 9]
+      set
+        text: '" something in here and in "here" " and over here'
+        cursor: [0, 9]

     it "applies operators inside the current string in operator-pending mode", ->
-      ensureKeystroke 'di"',
+      ensure 'di"',
         text: '""here" " and over here'
         cursor: [0, 1]

     it "[Changed behavior] applies operators inside area between quote", ->
-      cursor [0, 29]
-      ensureKeystroke 'di"',
+      set
+        cursor: [0, 29]
+      ensure 'di"',
         text: '" something in here and in "" " and over here'
         cursor: [0, 28]

     it "makes no change if past the last string on a line", ->
-      cursor [0, 39]
-      ensureKeystroke 'di"',
+      set
+        cursor: [0, 39]
+      ensure 'di"',
         text: '" something in here and in "here" " and over here'
         cursor: [0, 39]

   describe "the 'aw' text object", ->
     beforeEach ->
-      text "12345 abcde ABCDE"
-      cursor [0, 9]
+      set
+        text: "12345 abcde ABCDE"
+        cursor: [0, 9]

     it "applies operators from the start of the current word to the start of the next word in operator-pending mode", ->
-      ensureKeystroke 'daw',
+      ensure 'daw',
         text: "12345 ABCDE"
         cursor: [0, 6]
         register: "abcde "

     it "selects from the start of the current word to the start of the next word in visual mode", ->
-      ensureKeystroke 'vaw',
+      ensure 'vaw',
         selectedScreenRange: [[0, 6], [0, 12]]

     it "doesn't span newlines", ->
-      text "12345\nabcde ABCDE"
-      cursor [0, 3]
-      ensureKeystroke 'vaw',
+      set
+        text: "12345\nabcde ABCDE"
+        cursor: [0, 3]
+      ensure 'vaw',
         selectedBufferRange: [[0, 0], [0, 5]]

     it "doesn't span special characters", ->
-      text "1(345\nabcde ABCDE"
-      cursor [0, 3]
-      ensureKeystroke 'vaw',
+      set
+        text: "1(345\nabcde ABCDE"
+        cursor: [0, 3]
+      ensure 'vaw',
         selectedBufferRange: [[0, 2], [0, 5]]

   describe "the 'aW' text object", ->
     beforeEach ->
-      text "12(45 ab'de ABCDE"
-      cursor [0, 9]
+      set
+        text: "12(45 ab'de ABCDE"
+        cursor: [0, 9]

     it "applies operators from the start of the current whole word to the start of the next whole word in operator-pending mode", ->
-      ensureKeystroke 'daW',
+      ensure 'daW',
         text: "12(45 ABCDE"
         cursor: [0, 6]
         register: "ab'de "

     it "selects from the start of the current whole word to the start of the next whole word in visual mode", ->
-      ensureKeystroke 'vaW',
+      ensure 'vaW',
         selectedScreenRange: [[0, 6], [0, 12]]

     it "doesn't span newlines", ->
-      text "12(45\nab'de ABCDE"
-      cursor [0, 4]
-      ensureKeystroke 'vaW',
+      set
+        text: "12(45\nab'de ABCDE"
+        cursor: [0, 4]
+      ensure 'vaW',
         selectedBufferRange: [[0, 0], [0, 5]]

   describe "the 'a(' text object", ->
     beforeEach ->
-      text "( something in here and in (here) )"
-      cursor [0, 9]
+      set
+        text: "( something in here and in (here) )"
+        cursor: [0, 9]

     it "applies operators around the current parentheses in operator-pending mode", ->
-      ensureKeystroke 'da(',
+      ensure 'da(',
         text: ''
         cursor: [0, 0]

     it "applies operators around the current parentheses in operator-pending mode (second test)", ->
-      cursor [0, 29]
-      ensureKeystroke 'da(',
+      set
+        cursor: [0, 29]
+      ensure 'da(',
         text: "( something in here and in  )"
         cursor: [0, 27]

   describe "the 'a{' text object", ->
     beforeEach ->
-      text "{ something in here and in {here} }"
-      cursor [0, 9]
+      set
+        text: "{ something in here and in {here} }"
+        cursor: [0, 9]

     it "applies operators around the current curly brackets in operator-pending mode", ->
-      ensureKeystroke 'da{',
+      ensure 'da{',
         text: ''
         cursor: [0, 0]

     it "applies operators around the current curly brackets in operator-pending mode (second test)", ->
-      cursor [0, 29]
-      ensureKeystroke 'da{',
+      set
+        cursor: [0, 29]
+      ensure 'da{',
         text: "{ something in here and in  }"
         cursor: [0, 27]

   describe "the 'a<' text object", ->
     beforeEach ->
-      text "< something in here and in <here> >"
-      cursor [0, 9]
+      set
+        text: "< something in here and in <here> >"
+        cursor: [0, 9]

     it "applies operators around the current angle brackets in operator-pending mode", ->
-      ensureKeystroke 'da<',
+      ensure 'da<',
         text: ''
         cursor: [0, 0]

     it "applies operators around the current angle brackets in operator-pending mode (second test)", ->
-      cursor [0, 29]
-      ensureKeystroke 'da<',
+      set
+        cursor: [0, 29]
+      ensure 'da<',
         text: "< something in here and in  >"
         cursor: [0, 27]

   describe "the 'a[' text object", ->
     beforeEach ->
-      text "[ something in here and in [here] ]"
-      cursor [0, 9]
+      set
+        text: "[ something in here and in [here] ]"
+        cursor: [0, 9]

     it "applies operators around the current square brackets in operator-pending mode", ->
-      ensureKeystroke 'da[',
+      ensure 'da[',
         text: ''
         cursor: [0, 0]

     it "applies operators around the current square brackets in operator-pending mode (second test)", ->
-      cursor [0, 29]
-      ensureKeystroke 'da[',
+      set
+        cursor: [0, 29]
+      ensure 'da[',
         text: "[ something in here and in  ]"
         cursor: [0, 27]

   describe "the 'a\'' text object", ->
     beforeEach ->
-      text "' something in here and in 'here' '"
-      cursor [0, 9]
+      set
+        text: "' something in here and in 'here' '"
+        cursor: [0, 9]

     it "applies operators around the current single quotes in operator-pending mode", ->
-      ensureKeystroke "da'",
+      ensure "da'",
         text: "here' '"
         cursor: [0, 0]

     it "applies operators around the current single quotes in operator-pending mode (second test)", ->
-      cursor [0, 29]
-      ensureKeystroke "da'",
+      set
+        cursor: [0, 29]
+      ensure "da'",
         text: "' something in here and in  '"
         cursor: [0, 27]

   describe "the 'a\"' text object", ->
     beforeEach ->
-      text '" something in here and in "here" "'
-      cursor [0, 9]
+      set
+        text: '" something in here and in "here" "'
+        cursor: [0, 9]

     it "applies operators around the current double quotes in operator-pending mode", ->
-      ensureKeystroke 'da"',
+      ensure 'da"',
         text: 'here" "'
         cursor: [0, 0]

     it "applies operators around the current double quotes in operator-pending mode (second test)", ->
-      cursor [0, 29]
-      ensureKeystroke 'da"',
+      set
+        cursor: [0, 29]
+      ensure 'da"',
         text: '" something in here and in  "'
         cursor: [0, 27]


hipster-compatible grey text for config name and ip addresses in li list

--- code/li.coffee
+++ code/li.coffee
@@ -173,7 +173,7 @@
     ipstuff += ' Pub:' + item.ip_address
   if item.private_ip_address?
     ipstuff += ' Priv:' + item.private_ip_address
-  console.log "#{colour}#{item.name}#{colour_off} [#{item.config.name}]#{ipstuff} #{item.comments}"
+  console.log "#{colour}#{item.name}#{colour_off} \x1b[22;37m[#{item.config.name}]#{ipstuff}\x1b[0m #{item.comments}"

 friendly_state = (state) ->
   if LINODE_STATE[state]?


fixing missing file finding logic for litcoffee

--- command.coffee
+++ command.coffee
@@ -88,7 +88,7 @@
   fs.stat source, (err, stats) ->
     throw err if err and err.code isnt 'ENOENT'
     if err?.code is 'ENOENT'
-      if topLevel and source[-7..] not in coffee_exts
+      if topLevel and path.extname(source) not in coffee_exts
         source = sources[sources.indexOf(source)] = "#{source}.coffee"
         return compilePath source, topLevel, base
       if topLevel


add cursor test case

add cursor test case

--- tests/scripts/unit/cursor.coffee
+++ tests/scripts/unit/cursor.coffee
@@ -71,5 +71,13 @@
       inner = cursor.querySelector('.cursor-inner')
       expect(inner.style.height).to.equal('21px')
     )
+
+    it('should set in end of large font', ->
+      cursorManager.setCursor('id', 17, 'Test', 'red')
+      cursor = cursorManager.container.querySelector('.cursor')
+      expect(cursor.style.left).to.equal('46px')
+      expect(cursor.style.top).to.equal('35px')
+      inner = cursor.querySelector('.cursor-inner')
+    )
   )
 )


Tests: Fixed some helpers.

Tests: Fixed some helpers.

--- tests/src/helpers/helpers.coffee
+++ tests/src/helpers/helpers.coffee
@@ -240,7 +240,7 @@
       .click                   '.vm-header .buttons'
       .waitForElementPresent   '.context-list-wrapper', 50000
       .click                   '.context-list-wrapper .refresh'
-      .waitForElementVisible   webSelector, 10000
+      .waitForElementVisible   webSelector, 50000
       .click                   webSelector
       .click                   webSelector + ' + .chevron'

@@ -256,11 +256,11 @@
     browser
       .waitForElementVisible    'li.new-file', 50000
       .click                    'li.new-file'
-      .waitForElementVisible    'li.selected .rename-container .hitenterview', 5000
+      .waitForElementVisible    'li.selected .rename-container .hitenterview', 50000
       .clearValue               'li.selected .rename-container .hitenterview'
       .setValue                 'li.selected .rename-container .hitenterview', filename + '\n'
-      .pause                    2000 # required
-      .waitForElementPresent    "span[title='" + webPath + '/' + filename + "']", 5000 # Assertion
+      .pause                    3000 # required
+      .waitForElementPresent    "span[title='" + webPath + '/' + filename + "']", 50000 # Assertion

     return filename

@@ -281,24 +281,21 @@

     browser
       .waitForElementVisible   '.kdscrollview li a.more-link', 20000
-      .pause                   1000
       .click                   '.kdscrollview li a.more-link'
       .waitForElementVisible   '.kdmodal-inner', 20000
       .click                   '.kdmodal-inner button'
-      .pause                   2000 # required
+      .pause                   3000 # required
       .waitForElementVisible   '.add-workspace-view', 20000
       .setValue                '.add-workspace-view input.kdinput.text', workspaceName + '\n'
       .waitForElementVisible   '.vm-info', 20000
-      .pause                   2000, =>
-
-        browser.url (data) =>
-          url    = data.value
-          vmName = url.split('/IDE/')[1].split('/')[0]
-
-          browser
-            .assert.urlContains      workspaceName # Assertion
-            .assert.containsText     '.vm-info', '~/Workspaces/' + workspaceName # Assertion
-            .waitForElementPresent   'a[href="/IDE/' + vmName + '/' + workspaceName + '"]', 20000 # Assertion
+      .url (data) =>
+        url    = data.value
+        vmName = url.split('/IDE/')[1].split('/')[0]
+
+        browser
+          .waitForElementPresent   'a[href="/IDE/' + vmName + '/' + workspaceName + '"]', 40000 # Assertion
+          .assert.urlContains      workspaceName # Assertion
+          .assert.containsText     '.vm-info', '~/Workspaces/' + workspaceName # Assertion

     return workspaceName


Tweak Google Maps URL.

Tweak Google Maps URL.

Conflicts:
	misc/completion_engines/completion_engines.md

--- background_scripts/completion_engines.coffee
+++ background_scripts/completion_engines.coffee
@@ -59,7 +59,7 @@
 # then strip "map of" from the resulting suggestions.
 class GoogleMaps extends GoogleWithPrefix
   # Example search URL: https://www.google.com/maps?q=%s
-  constructor: -> super "map of", "https?://[a-z]+\\.google\\.(com|ie|co\\.uk|ca|com\\.au)/maps"
+  constructor: -> super "map of", "^https?://[a-z]+\\.google\\.(com|ie|co\\.uk|ca|com\\.au)/maps"

 class Youtube extends GoogleXMLRegexpEngine
   # Example search URL: http://www.youtube.com/results?search_query=%s


Don't log parser recovery failure.

Don't log parser recovery failure.

--- src/CoffeeScriptParser.coffee
+++ src/CoffeeScriptParser.coffee
@@ -181,7 +181,6 @@
             return super
         catch error
             throw error unless /Parsing halted/.test error.message
-            @report_error error.message

     # Add a `.yylloc` to lexer.
     lexer:
@@ -205,7 +204,7 @@
             t

     # Override Jison parser error function
-    parseError: (_, {line, token:tag}) ->
+    parseError: (_, {line, token:tag, recoverable}) ->
         switch tag
             when 'TERMINATOR'
                 message = 'unexpected end of expression'
@@ -235,6 +234,9 @@

         @report_error message, location

+        # If the parser can't recover it will throw the just reported error.
+        # Throw a more expectable error instead, so it won't get logged.
+        throw Error 'Parsing halted' unless recoverable

 # Parser and tree builder
 # -----------------------


Fixed a bug that was preventing CoffeeScript from running in Chrome 13 and Firefox 5. Apparently, the CoffeeScript "in" operator for arrays uses Array.prototype.indexOf.call(array, item) for its implementation. In Chrome 12, if array were null or undefined, then this would return -1. In Chrome 13 and Firefox 5, this triggers an exception: "TypeError: Array.prototype.indexOf called on null or undefined." It is probably not correct to change CoffeeScript's "in" operator to accept null for the array argument, so in the one place I discovered where this change in behavior was causing me problems, I changed the code to check for null before using "in".

--- src/nodes.coffee
+++ src/nodes.coffee
@@ -970,7 +970,7 @@
   children: ['variable', 'value']

   isStatement: (o) ->
-    o?.level is LEVEL_TOP and "?" in @context
+    o?.level is LEVEL_TOP and @context and "?" in @context

   assigns: (name) ->
     @[if @context is 'object' then 'value' else 'variable'].assigns name


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/matrix.coffee
+++ src/matrix.coffee
@@ -270,7 +270,7 @@

   ###*
   Returns a matrix that corresponds to a skew of skewX, skewY.
-  @see Matrix#translate
+  @see Matrix#skew
   @param {Number} tx The amount to translate in the x direction.
   @param {Number} ty The amount to translate in the y direction.
   @return {Matrix} A matrix transformation representing a translation by tx and ty.


updated methods

updated methods

--- src/drm-tablefilter.coffee
+++ src/drm-tablefilter.coffee
@@ -3,10 +3,11 @@
 ###############################################################################
 "use strict"

+$ = jQuery
 # adds case insensitive contains to jQuery

 $.extend $.expr[":"], {
-    "containsNC": (elem, i, match, array) ->
+    "containsNC": (elem, i, match) ->
         (elem.textContent || elem.innerText || "").toLowerCase().indexOf((match[3] || "").toLowerCase()) >= 0
 }

@@ -23,38 +24,38 @@
     filterRows: =>
         self = @
         # check other inputs
-        inputs = self.table.find('th').find ".#{self.searchInput}"
+        _inputs = self.table.find('th').find ".#{self.searchInput}"
         filterValues = []

         # get all input values and add them to filterValues array
-        $.each inputs, (key, value) ->
-            that = $ value
+        $.each _inputs, (key, value) ->
+            _that = $ value

-            if $.trim(that.val()).length isnt 0 then filterValues.push value
+            if $.trim(_that.val()).length isnt 0 then filterValues.push value

         # get filtered rows
         if filterValues.length is 0
             rows = self.fullRows
         else
             $.each filterValues, (key, value) ->
-                that = $ value
-                input = $.trim(that.val()).toLowerCase()
-                columnNum = that.closest('th').index()
+                _that = $ value
+                _input = $.trim(_that.val()).toLowerCase()
+                _columnNum = _that.closest('th').index()

                 if filterValues.length is 1
-                    rows = self.fullRows.has "td:eq(#{columnNum}):containsNC(#{input})"
+                    rows = self.fullRows.has "td:eq(#{_columnNum}):containsNC(#{_input})"
                 else if key is 0
-                    rows = self.fullRows.has "td:eq(#{columnNum}):containsNC(#{input})"
+                    rows = self.fullRows.has "td:eq(#{_columnNum}):containsNC(#{_input})"
                 else
-                    rows = rows.has "td:eq(#{columnNum}):containsNC(#{input})"
+                    rows = rows.has "td:eq(#{_columnNum}):containsNC(#{_input})"
                 rows
         rows

     renderTable: =>
-        filteredRows = @filterRows()
+        _filteredRows = @filterRows()
         tableBody = @table.find('tbody').empty()

-        $.each filteredRows, (key, value) ->
+        $.each _filteredRows, (key, value) ->
             tableBody.append value

 new DrmTableFilter()


change component name

change component name

--- samples/CoffeeScript/example.cjsx
+++ samples/CoffeeScript/example.cjsx
@@ -1,12 +1,12 @@
 ###* @cjsx React.DOM ###
-define 'myProject.myComponent', [
+define 'myProject.ReactExampleComponent', [
   'React'
   'myProject.ExampleStore'
   'myProject.ExampleActions'
   'myProject.ReactExampleTable'
 ], (React, ExampleStore, ExampleActions, ReactExampleTable ) ->

-  ReactListDetail = React.createClass
+  ReactExampleComponent = React.createClass
     mixins: [ListenMixin]

     getInitialState: ->


only compute renderers attribute once

only compute renderers attribute once

Was computing on every "update" (every render, basically) and this
caused excessive patches sent to the server

--- bokehjs/src/coffee/range/data_range1d.coffee
+++ bokehjs/src/coffee/range/data_range1d.coffee
@@ -20,6 +20,8 @@
     @set('_end', end)

   initialize: (attrs, options) ->
+    @renderers_computed = false
+
     # TODO this is broken; start/end are serializable attributes
     # that need to be synced with the server, so they can't be 'virtual'
     # properties with the current implementation of HasProperties
@@ -53,19 +55,20 @@
       'flipped', 'sources', 'default_span', 'plots'])

   update: (bounds, dimension, plot_view) ->
-    # TODO (bev)
-    # check that renderers actually configured with this range
-    renderers = @get('renderers') ? []
+    if not @renderers_computed
+      @renderers_computed = true
+      # TODO (bev)
+      # check that renderers actually configured with this range
+      renderers = @get('renderers')

-    all_renderers = []
-    if renderers.length == 0
-      for plot in @get('plots')
-        rs = plot.get('renderers')
-        rs = (r for r in rs when r.type == "GlyphRenderer")
-        all_renderers = all_renderers.concat(rs)
-      renderers = all_renderers
-
-    @set('renderers', renderers)
+      all_renderers = []
+      if renderers.length == 0
+        for plot in @get('plots')
+          rs = plot.get('renderers')
+          rs = (r for r in rs when r.type == "GlyphRenderer")
+          all_renderers = all_renderers.concat(rs)
+        renderers = all_renderers
+        @set('renderers', renderers)

     result = new bbox.empty()
     for r in @get('renderers')
@@ -98,6 +101,7 @@
     return _.extend {}, super(), {
       plots: []
       sources: []
+      renderers: []
       range_padding: 0.1
       default_span: 2
       flipped: false


(refactor) Refactor exercise editor to allow using it with inheritance.

--- src/exercise_editor.coffee
+++ src/exercise_editor.coffee
@@ -1,7 +1,7 @@

 _ = require 'lodash'

-module.exports = (ko, config) ->
+module.exports = (ko) ->
   if !ko.mapping?
     throw Error "You need to load knockout-mapping for the exercise editor."
   class TaskViewModel
@@ -11,8 +11,6 @@
       @hasTitle = ko.computed => @title()? and @title().trim() isnt ''

       @solution ?= ko.observable ''
-      @init = =>
-        config.taskInitialized @

   class ExercisePageViewModel
     constructor: (params) ->
@@ -21,7 +19,7 @@
       @exerciseNotFound = ko.observable(no)
       @isOld = ko.observable true

-      config.getExercise params.id, (exercise, status, error) =>
+      @getExercise params.id, (exercise, status, error) =>
         if exercise?
           @exercise = exercise
           @number exercise.number
@@ -39,14 +37,14 @@

     submit: (task) =>
       if task? #submit this task only
-
+        @saveTask task.id, ko.mapping.toJS(task), (status) ->
+          console.log 'Submit: ' + status
       else #submit everything
-        config.saveExercise exercise.id, ko.mapping.toJS(@tasks), (status) ->
+        @saveExercise exercise.id, ko.mapping.toJS(@tasks), (status) ->
           console.log 'Submit: ' + status

     # called through afterRender event in template html
     init: () =>
       $(".popupinfo").popup()

-  viewModel: ExercisePageViewModel
-  template: config.html
+  ExercisePageViewModel: ExercisePageViewModel


msgflo: Fix help description for --wait-timeout

--- src/msgflo.coffee
+++ src/msgflo.coffee
@@ -20,7 +20,7 @@
     .option('--ignore [process]', "Don't set up these processes", collectArray, [])
     .option('--forward <stderr,stdout>', "Forward these streams from child", String, 'stderr,stdout')
     .option('--auto-save [true|false]', "Autosave changes to graph", Boolean, false)
-    .option('--wait-timeout [true|false]', "How long to wait for participants", Number, 45)
+    .option('--wait-timeout <seconds>', "How long to wait for participants", Number, 45)
     .option('--runtime-id <UUID>', 'Unique identifier for this runtime instance', String, '')
     .option('--ping-url <URL>', 'An URL that can be pinged periodically to signal aliveness',
             String, 'https://api.flowhub.io/runtimes/$RUNTIME_ID')


Fixed incorrect parameter in user search method

--- src/octonode/search.coffee
+++ src/octonode/search.coffee
@@ -24,7 +24,7 @@
       if s isnt 200 then cb(new Error('Search repos error')) else cb null, b, h

   # Search users
-  users: (keyword, start_page, cb) ->
+  users: (params, start_page, cb) ->
     @client.get "/search/users", params, (err, s, b, h) ->
       return cb(err) if err
       if s isnt 200 then cb(new Error('Search users error')) else cb null, b, h


FinderTreeController: use rename with the new name

--- client/Finder/filetree/controllers/findertreecontroller.coffee
+++ client/Finder/filetree/controllers/findertreecontroller.coffee
@@ -248,7 +248,7 @@
     nodeView.showRenameView (newValue)=>
       return if newValue is nodeData.name

-      nodeData.rename name: newValue, (err)=>
+      nodeData.rename newValue, (err)=>
         if err then @notify null, null, err

       # @setKeyView()


Source environment shell file in run script

Signed-off-by: Sonmez Kartal <d2d15c3d37396a5f2a09f8b42cf5f27d43a3fbde@koding.com>

--- config/generateRunFile.coffee
+++ config/generateRunFile.coffee
@@ -93,6 +93,14 @@
     #!/bin/bash

     # ------ THIS FILE IS AUTO-GENERATED ON EACH BUILD ----- #
+
+    ENV_SHELL_FILE=${ENV_SHELL_FILE:-$(dirname $0)/.env.sh}
+    if [ -f "$ENV_SHELL_FILE" ]; then
+      source $ENV_SHELL_FILE
+    else
+      echo "error: shell environment file does not exist"
+      exit 1
+    fi

     mkdir $KONFIG_PROJECTROOT/.logs &>/dev/null

@@ -669,6 +677,14 @@
     #!/bin/bash
     export HOME=/home/ec2-user

+    ENV_SHELL_FILE=${ENV_SHELL_FILE:-$(dirname $0)/.env.sh}
+    if [ -f "$ENV_SHELL_FILE" ]; then
+      source $ENV_SHELL_FILE
+    else
+      echo "error: shell environment file does not exist"
+      exit 1
+    fi
+
     COMMAND=$1
     shift


Adding recursive function to count nestedRules

--- src/parser.coffee
+++ src/parser.coffee
@@ -9,6 +9,16 @@
   type: 'stylesheet'
   stylesheet:
     rules: rules
+
+# Recursively count nested rules
+#
+countNestedSelectors = (rule) ->
+  numNestedRules = 0;
+  return numNestedRules if typeof rule.rules is 'undefined'
+  for nestedRule in rule.rules
+    numNestedRules += countNestedSelectors nestedRule
+    numNestedRules += nestedRule.selectors.length if typeof nestedRule.selectors isnt 'undefined'
+  numNestedRules

 parser = (data) ->

@@ -72,14 +82,10 @@
         # Check if adding this rule will break the selector limit. If so,
         # produce a new AST first.
         #
-        numNestedRuleSelectors = 0

         break if typeof rule.rules is 'undefined'

-        for nestedRule in rule.rules
-          break if typeof nestedRule.selectors is 'undefined'
-
-          numNestedRuleSelectors += nestedRule.selectors.length
+        numNestedRuleSelectors = countNestedSelectors rule

         startNewAst() if numSelectors + numNestedRuleSelectors > SELECTOR_LIMIT


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/easing.coffee
+++ src/easing.coffee
@@ -1,5 +1,4 @@
 ( ->
-
   Easing =
     sinusoidal: (begin, end) ->
       change = end - begin


Add Editor::setSelectedScreenRange

Add Editor::setSelectedScreenRange

--- src/editor.coffee
+++ src/editor.coffee
@@ -1196,6 +1196,16 @@
   setSelectedBufferRange: (bufferRange, options) ->
     @setSelectedBufferRanges([bufferRange], options)

+  # Public: Set the selected range in screen coordinates. If there are multiple
+  # selections, they are reduced to a single selection with the given range.
+  #
+  # screenRange - A {Range} or range-compatible {Array}.
+  # options - An options {Object}:
+  #   :reversed - A {Boolean} indicating whether to create the selection in a
+  #     reversed orientation.
+  setSelectedScreenRange: (screenRange, options) ->
+    @setSelectedBufferRange(@bufferRangeForScreenRange(screenRange, options), options)
+
   # Public: Set the selected ranges in buffer coordinates. If there are multiple
   # selections, they are replaced by new selections with the given ranges.
   #


don't error when opening untitled buffer

--- src/editor.coffee
+++ src/editor.coffee
@@ -30,7 +30,7 @@
     Event.on 'window:open', (e) => @addBuffer e.details
     Event.on 'window:close', (e) => @removeBuffer e.details

-    @addBuffer path
+    @addBuffer path if path

     # Resize editor when panes are added/removed
     el = document.body
@@ -60,8 +60,6 @@
       null

   addBuffer: (path) ->
-    return if not path
-
     throw "#{@constructor.name}: Cannot create buffer from a directory `#{path}`" if fs.isDirectory path

     buffer = @buffers[path]
@@ -75,6 +73,7 @@
       mode = @modeForPath path
       buffer.setMode new mode if mode

+      return
       @buffers[path] = buffer

     buffer.on 'change', -> buffer.$atom_dirty = true
@@ -119,7 +118,7 @@
         @ace.setSession  new EditSession ''

   focusBuffer: (path) ->
-    return if @activePath == path
+    return if not path or @activePath == path

     @activePath = path


Better autodetection of Soft Tabs.

Better autodetection of Soft Tabs.

Check for the presence of a space, instead of the absence of a tab.
This ensures that Soft Tabs will not be activated on a file that (for
example) starts with a BOM or something, but is otherwise full of hard
tabs.

--- src/editor.coffee
+++ src/editor.coffee
@@ -354,7 +354,7 @@
     for bufferRow in [0..@buffer.getLastRow()]
       continue if @displayBuffer.tokenizedBuffer.lineForScreenRow(bufferRow).isComment()
       if match = @buffer.lineForRow(bufferRow).match(/^\s/)
-        return match[0][0] != '\t'
+        return match[0][0] == ' '
     undefined

   # Public: Clip the given {Point} to a valid position in the buffer.


Choose delta with fewest insertions, fixes #98

When trying to pick a delta describing document change. Previously used
delta with fewest operations.

--- src/editor.coffee
+++ src/editor.coffee
@@ -129,7 +129,14 @@
     catch ignored
     decomposeB = newDelta.decompose(@delta)
     if decomposeA and decomposeB
-      decompose = if decomposeA.ops.length < decomposeB.ops.length then decomposeA else decomposeB
+      # Choose delta with fewest insertions
+      [lengthA, lengthB] = _.map([decomposeA, decomposeB], (delta) ->
+        return _.reduce(delta.ops, (count, op) ->
+          count += op.value.length if op.value?
+          return count
+        , 0)
+      )
+      decompose = if lengthA < lengthA then decomposeA else decomposeB
     else
       decompose = decomposeA or decomposeB
     return decompose


NavigationItem: Added NewWorkspace item.

--- client/Main/navigation/navigationitem.coffee
+++ client/Main/navigation/navigationitem.coffee
@@ -13,6 +13,7 @@

     if      data.jMachine                then @createMachineItem      data
     else if data.type is 'title'         then @createTitleView        data
+    else if data.type is 'new-workspace' then @createNewWorkspaceView data
     else if data.type is 'workspace'     then @createWorkspaceItem    data
     else if data.type is 'app'           then @createAppItem          data

@@ -37,6 +38,12 @@
       """

+  createNewWorkspaceView: ->
+    @setClass 'workspace'
+    {machineUId} = @getData()
+    @child       = new AddWorkspaceView {}, { machineUId }
+
+
   createAppItem: ->
     @setClass 'app'
     @child    = new KDCustomHTMLView


CP: cleanup

CP: cleanup

--- client/Main/providers/computeprovider.coffee
+++ client/Main/providers/computeprovider.coffee
@@ -61,16 +61,3 @@
     { @label, @publicAddress, @state } = @jMachine = @getData()

-
-    # kayt = null
-    # KD.singletons.kontrol.fetchKite(
-    #   query:
-    #     username:"gokmen"
-    #     name: "klient"
-    #     environment: "public-host"
-    # ).then (oskite)->
-    #   log {oskite}
-    #   kayt = oskite
-    #   oskite.connect()
-    #   oskite.tell "exec", [command : "ls /"]
-    # .then log, warn


namespaced Movable

namespaced Movable

--- src/engine.coffee
+++ src/engine.coffee
@@ -150,7 +150,7 @@

     step = ->
       if !I.paused || frameAdvance
-        secondsPerFrame = (1000 / I.FPS)
+        secondsPerFrame = (1 / I.FPS)
         update(secondsPerFrame)
         I.age += 1


NotificatonController: pass all the data to remote-extension

--- client/app/lib/notificationcontroller.coffee
+++ client/app/lib/notificationcontroller.coffee
@@ -191,5 +191,5 @@
               global.location.reload yes

-    @on 'InstanceChanged', (change) ->
-      remote_extensions.updateInstance change.id
+    @on 'InstanceChanged', (data) ->
+      remote_extensions.updateInstance data


client: registration cutoff date updated

--- client/app/lib/util/issoloproductlite.coffee
+++ client/app/lib/util/issoloproductlite.coffee
@@ -4,6 +4,4 @@

   return no  if globals.config.environment in [ 'dev', 'sandbox' ]

-  cutOffDate = new Date 2016, 2, 1  # March 1st, 2016
-
-  return Date.now() > cutOffDate.getTime()+  return yes


DNDUploader: Fix recursive folder creation by using new create helper

--- client/finder/commonviews/DNDUploader.coffee
+++ client/finder/commonviews/DNDUploader.coffee
@@ -224,10 +224,20 @@
                 callback: ->
                   modalStack.destroy()

-    fsFolderItem.exists (err, exists)=>
-      unless exists
-      then FSHelper.createRecursiveFolder fsFolderItem, -> upload()
-      else upload()
+    fsFolderItem.exists (err, exists)->
+
+      return if  KD.showError err
+
+      if exists
+      then do upload
+      else
+        FSItem.create {
+          path      : fsFolderItem.path
+          type      : 'folder'
+          recursive : yes
+          machine
+        } , (err) ->
+          do upload unless KD.showError err

     return fsFileItem


A small typo.

A small typo.

--- client/landing/app/MainApp/AppStorage.coffee
+++ client/landing/app/MainApp/AppStorage.coffee
@@ -32,7 +32,7 @@

     return if @getValue(key) is value

-    pack = zip key, group, value
+    pack = @zip key, group, value

     @fetchStorage (storage)=>
       storage.update {
@@ -41,7 +41,7 @@

   unsetKey: (key, callback, group = 'bucket')->

-    pack = zip key, group, 1
+    pack = @zip key, group, 1

     @fetchStorage (storage)=>
       storage.update {


@translators is different on macosx?!

@translators is different on macosx?!

--- chrome/content/zotero-better-bibtex/zotero-better-bibtex.coffee
+++ chrome/content/zotero-better-bibtex/zotero-better-bibtex.coffee
@@ -144,7 +144,8 @@
   return

 Zotero.BetterBibTeX.SQLColumns = (table) ->
-  statement = Zotero.DB.getStatement("pragma betterbibtex.table_info(#{table})", null, true)
+  schema = if table == 'cache' then 'cache' else ''
+  statement = Zotero.DB.getStatement("pragma betterbibtex#{schema}.table_info(#{table})", null, true)

   # Get name column
   for i in [0...statement.columnCount]
@@ -189,7 +190,7 @@
   installing = @version(@release)
   Zotero.DB.query("insert or replace into betterbibtex.schema (lock, version) values ('schema', ?)", [@release])

-  return if installed == installing
+  return if installed == installing && !(['cache', 'keys', 'autoexport', 'exportoptions'].some((table) => !@SQLColumns(table)))

   progressWin = new Zotero.ProgressWindow()
   progressWin.changeHeadline('Better BibTeX: updating database')
@@ -397,6 +398,7 @@
       if @translator?[0] && @location && typeof @location == 'object'
         translatorID = @translator[0]
         translatorID = translatorID.translatorID if translatorID.translatorID
+        Zotero.BetterBibTeX.log('translate:', translatorID)
         for own name, header of Zotero.BetterBibTeX.translators
           if header.translatorID == translatorID
             @_displayOptions.exportPath = @location.path.slice(0, -@location.leafName.length)


activity: use generic message link in channel messages

--- client/activity/lib/components/channelmessageslist/index.coffee
+++ client/activity/lib/components/channelmessageslist/index.coffee
@@ -4,7 +4,7 @@
 immutable     = require 'immutable'
 formatContent = require 'app/util/formatContent'
 emojify       = require 'emojify.js'
-MessageLink   = require 'activity/components/publicchannelmessagelink'
+MessageLink   = require 'activity/components/messagelink'
 MessageBody   = require 'activity/components/common/messagebody'

 module.exports = class ChannelMessagesList extends React.Component


Remove cookie correctly

Remove cookie correctly

--- client/landing/app/MainApp/maincontroller/maincontroller.coffee
+++ client/landing/app/MainApp/maincontroller/maincontroller.coffee
@@ -280,7 +280,7 @@
   showInstructionsBookIfNeeded:->
     if $.cookie 'newRegister'
       @emit "ShowInstructionsBook", 9
-      $.cookie 'newRegister', no
+      $.cookie 'newRegister', erase: yes

   decorateBodyTag:->
     if KD.checkFlag 'super-admin'


client: fix for linter error

client: fix for linter error

https://app.wercker.com/buildstep/57de89e2d55ace0100119e8b

  scripts/../client/stack-editor/lib/onboarding/providerselectionview.coffee
       ✗ #37: Line ends with trailing whitespace.

--- client/stack-editor/lib/onboarding/providerselectionview.coffee
+++ client/stack-editor/lib/onboarding/providerselectionview.coffee
@@ -34,7 +34,7 @@
       if provider in enabledProviders
         extraClass = ''
         label      = ''
-
+
         if provider in betaProviders
           beta       = 'beta'
           betaLabel  = 'BETA'


Add verbose option for detailed output

Add verbose option for detailed output


Confirm client is found and authenticated before "yield next"

--- src/middleware/tlsAuthentication.coffee
+++ src/middleware/tlsAuthentication.coffee
@@ -44,7 +44,13 @@

 			// lookup client by subject.CN (CN = clientDomain) and set them as the authenticated user
 			this.authenticated = yield Client.findOne({ clientDomain: subject.CN }).exec();
-			yield next;
+
+			if ( this.authenticated ){
+				yield next;
+			}else{
+				this.response.status = "unauthorized";
+				logger.info("Certificate Authentication Failed: Certificate Name did not match your Client Domain");
+			}
 		} else {
 			this.response.status = "unauthorized";
 			logger.info("Request is NOT authenticated via TLS: " + this.req.client.authorizationError);


some additions to formviewwithfields

some additions to formviewwithfields

--- components/forms/KDFormViewWithFields.coffee
+++ components/forms/KDFormViewWithFields.coffee
@@ -14,7 +14,7 @@
     for key,option of options
       option.title or= key
       option
-
+
   createFields:(fields)->
     @addSubView @createField fieldData for fieldData in fields

@@ -23,30 +23,41 @@
     buttons.forEach (buttonOptions)=>
       @buttonField.addSubView button = @createButton buttonOptions
       @buttons[buttonOptions.title] = button
-
+
   createField:(data, field)->
-    {itemClass,title} = data
-    itemClass or= KDInputView
-    field or= new KDView cssClass : "formline #{data.name}"
+    {itemClass, title} = data
+    itemClass     or= KDInputView
+    data.cssClass or= ""
+    field or= new KDView cssClass : "formline #{data.name} #{data.cssClass}"
     field.addSubView label = data.label = @createLabel(data) if data.label
     field.addSubView input = @createInput itemClass,data
+    if data.hint
+      field.addSubView hint  = new KDCustomHTMLView
+        partial  : data.hint
+        tagName  : "cite"
+        cssClass : "hint"
     @fields[title] = field
     if data.nextElement
       for key, next of data.nextElement
         next.title or= key
         @createField next, field
-
+
     return field
-
+
   createLabel:(data)->
-    return new KDLabelView(title : data.label)
-
+    return new KDLabelView
+      title    : data.label
+      cssClass : @utils.slugify data.label
+
   createInput:(itemClass,options)->
     @inputs[options.title] = input = new itemClass options
     return input

   createButton:(options)->
-    return button = new KDButtonView options
+    options.itemClass or= KDButtonView
+    o = $.extend {}, options
+    delete o.itemClass
+    button = new options.itemClass o

 # new KDFormViewWithFields
@@ -64,10 +75,10 @@
 #       title           : "Reset"
 #       style           : "modal-clean-red"
 #       type            : "reset"
-#   callback            : (formOutput)->
+#   callback            : (formOutput)->
 #     log formOutput,"  ::::::"
 #   fields              :
-#     Title             :
+#     Title             :
 #       label           : "Title:"
 #       type            : "text"
 #       name            : "title"
@@ -77,7 +88,7 @@
 #           required    : yes
 #         messages      :
 #           required    : "topic name is required!"
-#     Zikkko            :
+#     Zikkko            :
 #       label           : "Zikkko"
 #       type            : "textarea"
 #       name            : "zikko"
@@ -87,4 +98,3 @@
 #           type        : "text"
 #           name        : "lulu"
 #           placeholder : "lulu..."
-


KDBlockingModalView: Overlay refactor.

KDBlockingModalView: Overlay refactor.

--- components/modals/KDBlockingModalView.coffee
+++ components/modals/KDBlockingModalView.coffee
@@ -1,25 +1,23 @@
 class KDBlockingModalView extends KDModalView
-  constructor:->
-    super
-    $(window).off "keydown.modal"
+
+  constructor: (options = {}, data) ->
+
+    super options, data
+
+    $(window).off "keydown.modal" # to unbind ESC key

   putOverlay:->
-    @$overlay = $ "<div/>",
-      class : "kdoverlay"
-    @$overlay.hide()
-    @$overlay.appendTo "body"
-    @$overlay.fadeIn 200
-    @$overlay.bind "click", => @doBlockingAnimation()
+    @overlay = new KDOverlayView
+      isRemovable: no
+
+    @overlay.on "click", =>
+      @doBlockingAnimation()

   doBlockingAnimation:->
-    @unsetClass "blocking-animation"
-    @setClass   "blocking-animation"
-
-    @$overlay.off "click"
+    @setClass "blocking-animation"

     KD.utils.wait 200, =>
       @unsetClass "blocking-animation"
-      @$overlay.bind "click", => @doBlockingAnimation()

   setDomElement:(cssClass)->
     @domElement = $ """


Fix external

Fix external

--- server/controllers/devices.coffee
+++ server/controllers/devices.coffee
@@ -74,14 +74,13 @@

 module.exports.external = (req, res, next) ->

-    if auth
-        # Add his creadentials for CouchDB
-        if process.env.NODE_ENV is "production"
-            req.headers['authorization'] = getCredentialsHeader()
-        else
-            # Do not forward 'authorization' header in other environments
-            # in order to avoid wrong authentications in CouchDB
-            req.headers['authorization'] = null
+    # Add his credentials for CouchDB
+    if process.env.NODE_ENV is "production"
+        req.headers['authorization'] = getCredentialsHeader()
+    else
+        # Do not forward 'authorization' header in other environments
+        # in order to avoid wrong authentications in CouchDB
+        req.headers['authorization'] = null

-        # Forward the request to couchdb
-        getProxy().web req, res, target: "http://localhost:5984"
+    # Forward the request to couchdb
+    getProxy().web req, res, target: "http://localhost:5984"


Added news

Added news

--- server/materials/data/news.coffee
+++ server/materials/data/news.coffee
@@ -3,16 +3,11 @@

 export default allNews = () ->
     return news([
-        newsItem("Областная (региональная) олимпиада", String.raw"""
-            Добавлен текст про областную (региональную) олимпиаду
+        newsItem("Еще два опроса", String.raw"""
+            Заполните, пожалуйста, еще два опроса про алгопрог: раз, два.
         """),
-        newsItem("Алгопрог-Новый год: новости", String.raw"""
-            Алгопрог-Новый год состоится 6 января, приглашаются все ученики алгопрога, кто будет в Нижнем Новгороде.
-            Напишите мне, чтобы я вас добавил в секретный телеграм-чат :)
-        """),
-        newsItem("Занятия в праздники", String.raw"""
-            26 декабря и 2 января занятий во ВШЭ не будет, следующее занятие 9 января.

-            22 декабря занятие в лицее 40 будет, 29 декабря и 5 января занятий в лицее 40 не будет, первое занятие в новом году — 12 января.
+        newsItem("Занятия 19 января в лицее 40 не будет", String.raw"""
+            19 января занятия в лицее 40 не будет.
         """),
         newsItem("Опрос про алгопрог", String.raw"""
             Ответьте, пожалуйста, на несколько вопросов. Тем, кто уже отвечал — я там добавил несколько вопросов, можете ответить еще раз.


Use the exception data to reject the promise

--- src/remote.coffee
+++ src/remote.coffee
@@ -39,7 +39,7 @@
       @server.stderr.on "end", ->
         if @verbose
           console.log "The server is opened on port #{@port}"
-    catch
+    catch e
       defer.reject(e)
     finally
       return defer.promise


Leave touch events disabled to avoid mutation and other conflicts

--- src/render.coffee
+++ src/render.coffee
@@ -27,7 +27,8 @@
     class: labels
     style: world.bind() -> style
     click: clicker(world)
-    #touchend: clicker(world)
+    # TODO: add touch events that do not mess up mutation
+    # touchend: clicker(world)
   }

 text_attrs = (world) -> {


follow docs

follow docs

--- src/follow.coffee
+++ src/follow.coffee
@@ -1,7 +1,7 @@
 ###*
 The Follow module provides a simple method to set an object's
 velocity so that it will approach another object. This method
-relies on the objects having
+relies on the objects having ``

 player = GameObject


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/follow.coffee
+++ src/follow.coffee
@@ -7,7 +7,7 @@

 This method relies on both objects having position methods.

-include this module by calling self.include Follow
+Include this module by calling self.include Follow

 player = GameObject


guard against weird style strings

guard against weird style strings

--- src/format.coffee
+++ src/format.coffee
@@ -57,10 +57,10 @@
   @getStyleObject: (container) ->
     styleString = container.getAttribute('style') or ''
     return _.reduce(styleString.split(';'), (styles, str) ->
-      if str.length > 0
-        [name, value] = str.split(':')
-        name = name.slice(1) if name[0] == ' '
-        value = value.slice(1) if value[0] == ' '
+      [name, value] = str.split(':')
+      if name and value
+        name = name.slice(1) if name.slice(0, 1) == " "
+        value = value.slice(1) if value.slice(0, 1) == " "
         styles[name.toLowerCase()] = value
       return styles
     , {})


重新规划路由，根据配置文件创建路由，有更大的扩展性

重新规划路由，根据配置文件创建路由，有更大的扩展性

--- src/router.coffee
+++ src/router.coffee
@@ -32,6 +32,67 @@
   #删除数据
   app.delete "#{path}/:id", (req, res, next)->

+#获取crud的默认path
+getPaths = (map)->
+  paths =
+    post: (map.paths && map.paths.post) || map.path
+    get: (map.paths && map.paths.get) || "#{map.path}/:id?"
+    put: (map.paths && map.paths.get) || "#{map.path}/:id"
+    delete: "#{map.path}/:id"
+
+  pathPuffix =
+    post: ""
+    get: "/:id?"
+    put: "/:id"
+    delete: "/:id"
+
+  ["post", "get", "put", "delete"].forEach (method)->
+    path = (map.paths && map.paths[method]) || "#{map.path}#{pathPuffix[method]}"
+    paths[method] = path    #"/api/#{path}"
+  paths
+
+apiRouterTo = (app, map)->
+  biz = require "./biz/#{map.biz}"
+  paths = getPaths(map)
+
+  ["get", "post", "delete", "put"].forEach (method)->
+    path = paths[method]
+    #如果在map中，有指定业务逻辑的处理方法，则交给业务逻辑处理
+    specialMethod = (map.methods || {})[method]
+    #如果指定的方法为false，则不处理这个method
+    return if specialMethod is false
+    #由业务逻辑指定的处理处理
+    return app[method](path, biz[specialMethod]) if specialMethod and biz[specialMethod]
+
+    #处理常规则的method
+    app[method] path, (req, res, next)->
+      #处理data部分
+      data = {}
+      switch method
+        when "get" then data = req.query
+        when "post", "put" then data = req.body
+
+      #将params合并
+      data = _.extend data, req.params
+
+      #根据不同的类型，交由默认的业务逻辑处理
+      switch method
+        when "get"
+          biz.find data, (err, results)->
+            res.json results
+          break
+        when "post", "put"
+          #保存数据
+          biz.save data, (err, new_id)->
+            res.json {id: new_id}
+          break
+        when "delete"
+          biz.remove {id: id}, (error)->
+            res.end()
+          break
+
+
+###
 #项目的路由
 projectRounter = (app)->
   #获取project信息
@@ -49,12 +110,17 @@
   #删除数据
   app.delete "#{path}/:id", (req, res, next)->
     _project.remove
+###

 #响应404错误
 response404 = (req, res, next)->
+  res.statusCode = 404
+  res.end('404 Not Found')

 #响应403权限错误
 response403 = (req, res, next)->
+  res.statusCode = 403
+  res.end('')

 #权限校验
 checkAuthority = (req, res, next)->
@@ -68,13 +134,55 @@

   #校验校验权限

+  apiRoot = "/api/"
+  mapping = [
+      path: "#{apiRoot}project"
+      biz: "project"
+    ,
+      #素材
+      path: "#{apiRoot}project/:project_id/asset"
+      ### paths
+      paths:
+        post: "post path"
+        get: "get path"
+      ###
+      biz: "asset"
+      methods:
+        post: "uploadFile"
+        delete: false
+        put: false
+    ,
+      #查看素材
+      path: "assets/:project_id/:file",
+      biz: "asset"
+      methods:
+        get: "readFile"
+        put: false,
+        post: false,
+        delete: false
+    ,
+      path: "#{apiRoot}project/:project_id/issue"
+      biz: "issue"
+    ,
+      path: "#{apiRoot}issue/:issue_id/comment"
+      biz: "comment"
+  ]

+  mapping.forEach (map)->
+    apiRouterTo app, map
+
+  app.get "*", response404
+
+  #require('fs').renameSync '/Users/conis/WorkStation/BHF/src/uploads/2692-10y4zi.png', '/Users/conis/WorkStation/BHF/src/assets/2692-10y4zi.png'
+  ###
+  return
   mapping =
     "project": "project"
     "issue": "project/:project_id/issue"
     "comment": "issue/:issue_id/comment"
-    "asset": "project/:parent_id/asset"
+    "asset": "project/:project_id/asset"

   for key, path of mapping
     biz = require "./biz/#{key}"
-    apiRouter biz, path, app+    apiRouter biz, path, app
+###


Warn about http error 404: not found

Warn about http error 404: not found

--- src/routes.coffee
+++ src/routes.coffee
@@ -45,7 +45,7 @@

   # If all else failed, show 404 page
   app.all '/*', (req, res) ->
-#    console.warn "error 404: ", req.url
+    console.warn "error 404: ", req.url
     res.statusCode = 404
     res.render '404', 404


Make spread and rest work

Make spread and rest work

--- src/nodes.coffee
+++ src/nodes.coffee
@@ -665,9 +665,6 @@
   # Compile a vanilla function call.
   compileNode: (o) ->
     @variable?.front = @front
-    compiledArray = Splat.compileSplattedArray o, @args, true
-    if compiledArray.length
-      return @compileSplat o, compiledArray
     compiledArgs = []
     for arg, argIndex in @args
       if argIndex then compiledArgs.push @makeCode ", "
@@ -980,8 +977,6 @@
   compileNode: (o) ->
     return [@makeCode '[]'] unless @objects.length
     o.indent += TAB
-    answer = Splat.compileSplattedArray o, @objects
-    return answer if answer.length

     answer = []
     compiledObjs = (obj.compileToFragments o, LEVEL_LIST for obj in @objects)
@@ -1388,15 +1383,8 @@
     delete o.isExistentialEquals
     params = []
     exprs  = []
-    for param in @params when param not instanceof Expansion
+    for param in @params
       o.scope.parameter param.asReference o
-    for param in @params when param.splat or param instanceof Expansion
-      for p in @params when p not instanceof Expansion and p.name.value
-        o.scope.add p.name.value, 'var', yes
-      splats = new Assign new Value(new Arr(p.asReference o for p in @params)),
-                          new Value new Literal 'arguments'
-      break
-    for param in @params
       if param.isComplex()
         val = ref = param.asReference o
         val = new Op '?', ref, param.value if param.value
@@ -1407,9 +1395,8 @@
           lit = new Literal ref.name.value + ' == null'
           val = new Assign new Value(param.name), param.value, '='
           exprs.push new If lit, val
-      params.push ref unless splats
+      params.push ref
     wasEmpty = @body.isEmpty()
-    exprs.unshift splats if splats
     @body.expressions.unshift exprs... if exprs.length
     for p, i in params
       params[i] = p.compileToFragments o
@@ -1455,7 +1442,9 @@
   children: ['name', 'value']

   compileToFragments: (o) ->
-    @name.compileToFragments o, LEVEL_LIST
+    name = @name.compileToFragments o, LEVEL_LIST
+    name.unshift @makeCode "..." if @.splat
+    name

   asReference: (o) ->
     return @reference if @reference
@@ -1524,36 +1513,12 @@
     @name.assigns name

   compileToFragments: (o) ->
-    @name.compileToFragments o
+    name = @name.compileToFragments o
+    name.unshift @makeCode "..."
+
+    name

   unwrap: -> @name
-
-  # Utility function that converts an arbitrary number of elements, mixed with
-  # splats, to a proper array.
-  @compileSplattedArray: (o, list, apply) ->
-    index = -1
-    continue while (node = list[++index]) and node not instanceof Splat
-    return [] if index >= list.length
-    if list.length is 1
-      node = list[0]
-      fragments = node.compileToFragments o, LEVEL_LIST
-      return fragments if apply
-      return [].concat node.makeCode("#{ utility 'slice', o }.call("), fragments, node.makeCode(")")
-    args = list[index..]
-    for node, i in args
-      compiledNode = node.compileToFragments o, LEVEL_LIST
-      args[i] = if node instanceof Splat
-      then [].concat node.makeCode("#{ utility 'slice', o }.call("), compiledNode, node.makeCode(")")
-      else [].concat node.makeCode("["), compiledNode, node.makeCode("]")
-    if index is 0
-      node = list[0]
-      concatPart = (node.joinFragmentArrays args[1..], ', ')
-      return args[0].concat node.makeCode(".concat("), concatPart, node.makeCode(")")
-    base = (node.compileToFragments o, LEVEL_LIST for node in list[...index])
-    base = list[0].joinFragmentArrays base, ', '
-    concatPart = list[index].joinFragmentArrays args, ', '
-    [..., last] = list
-    [].concat list[0].makeCode("["), base, list[index].makeCode("].concat("), concatPart, last.makeCode(")")

 #### Expansion