newfiles: don't download as zip when single file is selected

fixes: CNVS-17037 CNVS-17036

test plan:

in newfiles,
select a single file row.
click the download link in the toolbar,
it should download that file, just as if you clicked
"download" from the cog.
select multiple things or a single folder:
it should download as a zip, like it did before.

Change-Id: I4a990fb89f07e03f0977580e6cd376dd1da5083f
Reviewed-on: https://gerrit.instructure.com/44708
Reviewed-by: Dan Minkevitch <2591e5f46f28d303f9dc027d475a5c60d8dea17a@instructure.com>
Tested-by: Jenkins <d95b56ce41a2e1ac4cecdd398defd7414407cc08@instructure.com>
QA-Review: Jahnavi Yetukuri <ed2b2672ccc2e59e56c0615df7f01b579202e014@instructure.com>
Product-Review: Ryan Shaw <ea3cd978650417470535f3a4725b6b5042a6ab59@instructure.com>

--- app/coffeescripts/react_files/components/Toolbar.coffee
+++ app/coffeescripts/react_files/components/Toolbar.coffee
@@ -121,16 +121,28 @@
             },
               i className: 'icon-cloud-lock'

-          button {
-            type: 'button'
-            disabled: !showingButtons
-            className: 'ui-button btn-download'
-            onClick: @downloadSelectedAsZip
-            title: downloadTitle
-            'aria-label': downloadTitle
-            'data-tooltip': ''
-          },
-            i className: 'icon-download'
+          if (@props.selectedItems.length is 1) and @props.selectedItems[0].get('url')
+            a {
+              tabIndex: -1 unless showingButtons
+              className: 'ui-button btn-download'
+              href: @props.selectedItems[0].get('url')
+              download: true
+              title: downloadTitle
+              'aria-label': downloadTitle
+              'data-tooltip': ''
+            },
+              i className: 'icon-download'
+          else
+            button {
+              type: 'button'
+              disabled: !showingButtons
+              className: 'ui-button btn-download'
+              onClick: @downloadSelectedAsZip
+              title: downloadTitle
+              'aria-label': downloadTitle
+              'data-tooltip': ''
+            },
+              i className: 'icon-download'

           if @props.userCanManageFilesForContext
             button {


REFACTOR VCO CREATION

REFACTOR VCO CREATION

--- src/scripts/Syntho.coffee
+++ src/scripts/Syntho.coffee
@@ -5,44 +5,9 @@
 class @Syntho
     constructor: (@audioContext) ->

-        @vco1 = @audioContext.createOscillator()
-        @vco1.type = 'sawtooth'
-        @vco1.frequency.value = 440
-        @vco1.start()
-        @vco1.octave = 3
-        @vco1.amp = @audioContext.createGain()
-        @vco1.amp.gain.value = 1
-        @vco1.lfoHookup = @audioContext.createGain()
-        @vco1.lfoHookup.gain.value = 0
-        @vco1.lfoHookup.connect(@vco1.frequency)
-        @vco1.connect(@vco1.amp)
-
-
-        @vco2 = @audioContext.createOscillator()
-        @vco2.type = 'sawtooth'
-        @vco2.frequency.value = 440
-        @vco2.detune.value = 0
-        @vco2.start()
-        @vco2.octave = 3
-        @vco2.amp = @audioContext.createGain()
-        @vco2.amp.gain.value = 1
-        @vco2.lfoHookup = @audioContext.createGain()
-        @vco2.lfoHookup.gain.value = 0
-        @vco2.lfoHookup.connect(@vco2.frequency)
-        @vco2.connect(@vco2.amp)
-
-        @vco3 = @audioContext.createOscillator()
-        @vco3.type = 'sawtooth'
-        @vco3.frequency.value = 440
-        @vco3.detune.value = 0
-        @vco3.start()
-        @vco3.octave = 3
-        @vco3.amp = @audioContext.createGain()
-        @vco3.amp.gain.value = 1
-        @vco3.lfoHookup = @audioContext.createGain()
-        @vco3.lfoHookup.gain.value = 0
-        @vco3.lfoHookup.connect(@vco3.frequency)
-        @vco3.connect(@vco3.amp)
+        @vco1 = @.initVCO(@audioContext)
+        @vco2 = @.initVCO(@audioContext)
+        @vco3 = @.initVCO(@audioContext)

         @gate = @audioContext.createGain()
         @gate.gain.value = 0
@@ -68,9 +33,6 @@
         @lfo.amp.connect(@vco2.lfoHookup)
         @lfo.amp.connect(@vco3.lfoHookup)
         @lfo.amp.connect(@filter.lfoHookup)
-
-
-
         @vco1.amp.connect(@filter)
         @vco2.amp.connect(@filter)
         @vco3.amp.connect(@filter)
@@ -78,3 +40,17 @@
         @gate.connect(@audioContext.destination)

+    initVCO: (audioContext) ->
+        vco = audioContext.createOscillator()
+        vco.type = 'sawtooth'
+        vco.frequency.value = 440
+        vco.detune.value = 0
+        vco.start()
+        vco.octave = 2
+        vco.amp = audioContext.createGain()
+        vco.amp.gain.value = 1
+        vco.lfoHookup = audioContext.createGain()
+        vco.lfoHookup.gain.value = 0
+        vco.lfoHookup.connect(vco.frequency)
+        vco.connect(vco.amp)
+        return vco


Added spring with damping-ratio & duration

--- framer/Animator.coffee
+++ framer/Animator.coffee
@@ -1,6 +1,9 @@
+{computeDerivedCurveOptions} = require "./Animators/SpringCurveValueConverter"
+
 AnimatorClasses = {}

 AnimatorClassBezierPresets = ["ease", "ease-in", "ease-out", "ease-in-out"]
+

 class exports.Animator

@@ -74,9 +77,16 @@
 				curveOptions.values = parsedCurve.args.map (v) -> parseFloat(v) or 0

 			if animatorClass is SpringRK4Animator
-				for k, i in ["tension", "friction", "velocity", "tolerance"]
-					value = parseFloat parsedCurve.args[i]
-					curveOptions[k] = value if value
+				if animatorClassName is "spring-dd"
+					dampingRatio = parseFloat parsedCurve.args[0] ? 0
+					duration = options.time ? 1
+					mass = parseFloat parsedCurve.args[1] ? 1
+					velocity = parseFloat parsedCurve.args[2] ? 0
+					curveOptions = computeDerivedCurveOptions(dampingRatio, duration, velocity, mass)
+				else
+					for k, i in ["tension", "friction", "velocity", "tolerance"]
+						value = parseFloat parsedCurve.args[i]
+						curveOptions[k] = value if value

 			if animatorClass is SpringDHOAnimator
 				for k, i in ["stiffness", "damping", "mass", "tolerance"]
@@ -94,6 +104,7 @@
 AnimatorClasses["linear"] = LinearAnimator
 AnimatorClasses["bezier-curve"] = BezierCurveAnimator
 AnimatorClasses["spring-rk4"] = SpringRK4Animator
+AnimatorClasses["spring-dd"] = SpringRK4Animator
 AnimatorClasses["spring-dho"] = SpringDHOAnimator

 AnimatorClasses["spring"] = AnimatorClasses["spring-rk4"]


Fix missing assertions in union unit test

--- Src/coffeescript/cql-execution/test/datatypes/interval.coffee
+++ Src/coffeescript/cql-execution/test/datatypes/interval.coffee
@@ -2274,16 +2274,16 @@

     ivl = new Interval(8, 17)
     i = ivl.union(uIvl)
-    i.low.low is 5
-    i.low.high is 8
-    i.high.low is 17
-    i.high.high is 20
+    i.low.low.should.equal 5
+    i.low.high.should.equal 8
+    i.high.low.should.equal 17
+    i.high.high.should.equal 20

     i = uIvl.union(ivl)
-    i.low.low is 5
-    i.low.high is 8
-    i.high.low is 17
-    i.high.high is 20
+    i.low.low.should.equal 5
+    i.low.high.should.equal 8
+    i.high.low.should.equal 17
+    i.high.high.should.equal 20

     ivl = new Interval(10, 15)
     i = ivl.union(uIvl)


[Graph Refactor] make function inline

[Graph Refactor] make function inline

--- workers/social/lib/social/models/activity/index.coffee
+++ workers/social/lib/social/models/activity/index.coffee
@@ -319,11 +319,7 @@
         targetId: delegate.getId()
         as: 'like'
       , {}, (err, likedRels)=>
-        likedIds = []
-        for likedRel in likedRels
-          likedIds.push likedRel.sourceId
-
-        callback err, likedIds
+        callback err, (likedRel.sourceId for likedRel in likedRels)

   notifyCache = (event, contents)->
     routingKey = contents.group or 'koding'


Fixing typo

Fixing typo

--- source/quo.element.coffee
+++ source/quo.element.coffee
@@ -15,7 +15,7 @@
   Get/Set attribute to a given instance element
   @method attr
   @param  {string} Name of attribute
-  @param  {string} [OPTIONAL] Value of atribbute
+  @param  {string} [OPTIONAL] Value of attribute
   ###
   $$.fn.attr = (name, value) ->
     if @length > 0 and $$.toType(name) is "string"


FIX: Better config options. User can set defaults

--- src/Moon.coffee
+++ src/Moon.coffee
@@ -19,7 +19,14 @@
         return this

     Moon.fn = Moon.prototype =
-
+        # config object
+        config:
+            duration: 1000
+            delay: 0
+            easing: "ease"
+            before: undefined
+            after: undefined
+
         # already returned prefixes
         _prefixes: {}

@@ -84,11 +91,11 @@
         # must have at least one step
         animate: (args) ->
             animationProps =
-                duration: 0
-                delay: 0
-                easing: "ease"
-                before: undefined
-                after: undefined
+                duration: Moon.config.duration
+                delay: Moon.config.delay
+                easing: Moon.config.easing
+                before: Moon.config.before
+                after: Moon.config.after

             for arg, value of args
                 animationProps[arg] = value


Make main.coffee a no-op by default

Make main.coffee a no-op by default

--- harvest/template/project_name/static/scripts/coffeescript/main.coffee
+++ harvest/template/project_name/static/scripts/coffeescript/main.coffee
@@ -2,9 +2,4 @@
 # must be access via the namespaced path 'project/main'. This is to
 # ensure modules of the same name do not conflict Cilantro's modules.

-define [
-    'environ'
-    'jquery'
-    'underscore'
-    'backbone'
-], (environ, $, _, Backbone) ->
+define ->


Do not require a caption on figures

Do not require a caption on figures

--- lib/written/app/assets/javascripts/written/parsers/block/image.coffee
+++ lib/written/app/assets/javascripts/written/parsers/block/image.coffee
@@ -78,7 +78,7 @@
     if onerror
       img.classList.add 'error'

-Image.rule = /^(!{1}\[([^\]]+)\])(\(([^\s]*)?\))$/i
+Image.rule = /^(!{1}\[([^\]]*)\])(\(([^\s]*)?\))$/i

 Image.uploader = (uploader) ->
   Image::configure = uploader.initialize


:memo: Doc what paths are returned

:memo: Doc what paths are returned

--- src/atom.coffee
+++ src/atom.coffee
@@ -344,10 +344,14 @@
     app.getVersion()

   # Public: Get the directory path to Atom's configuration area.
+  #
+  # Returns the absolute path to ~/.atom
   getConfigDirPath: ->
     @configDirPath ?= fs.absolute('~/.atom')

   # Public: Get the directory path to Atom's storage area.
+  #
+  # Returns the absoluste path to ~/.atom/storage
   getStorageDirPath: ->
     @storageDirPath ?= path.join(@getConfigDirPath(), 'storage')


Simplyify check for optionalRequestHeaders

--- src/auth.coffee
+++ src/auth.coffee
@@ -105,7 +105,7 @@
   @request.hook (xhr) ->
     xhr.setRequestHeader 'Authorization', 'Bearer ' + token

-    if options.optionalRequestHeaders and options.optionalRequestHeaders.length
+    if options?.optionalRequestHeaders?.length
       for option in options.optionalRequestHeaders
         xhr.setRequestHeader option.headerName, option.headerValue
   this


added example for base

added example for base

--- src/base.coffee
+++ src/base.coffee
@@ -1,8 +1,25 @@
 # New-style Classes
+#
 # Benefits:
-#  - Avoid the usage of 'name: =>' for bound-method declarations, which is inconsistent with the rest of CoffeeScript.
-#  - Decorators work flawlessly with bound methods.
-#  - Declarative syntax for methods, for more literate code.
+#
+# -  Avoid the usage of 'name: =>' for bound-method declarations, which is inconsistent with the rest of CoffeeScript.
+# -  Decorators work flawlessly with bound methods.
+# -  Declarative syntax for methods, for more literate code.
+#
+# Usage:
+#
+#     class Foo extends Base
+#
+#       @instance
+#         info:     -> "Foo.info:#{this}"
+#         toString: -> "<Foo>"
+#
+#       @class
+#         toString: -> "[class:Foo]"
+#
+#       @static
+#         static:   -> "static:#{this}" # depends on how the function is invoked
+#

 {clone, extend} = require 'underscore'


Store tile state in a instance variable

Store tile state in a instance variable

--- src/tile-component.coffee
+++ src/tile-component.coffee
@@ -34,29 +34,32 @@
       @oldState = {tiles: {}}
       @oldState.tiles[@id] = { lines: {}}

-    if @newState.tiles[@id].display isnt @oldState.tiles[@id]?.display
-      @domNode.style.display = @newState.tiles[@id].display
-      @oldState.tiles[@id]?.display = @newState.tiles[@id].display
-
-    if @newState.tiles[@id].height isnt @oldState.tiles[@id]?.height
-      @domNode.style.height = @newState.tiles[@id].height + 'px'
-      @oldState.tiles[@id]?.height = @newState.tiles[@id].height
-
-    if (@newState.tiles[@id].top isnt @oldState.tiles[@id]?.top) or (@newState.tiles[@id].left isnt @oldState.tiles[@id]?.left)
-      @domNode.style['-webkit-transform'] = "translate3d(#{@newState.tiles[@id].left}px, #{@newState.tiles[@id].top}px, 0px)"
-      @oldState.tiles[@id]?.top = @newState.tiles[@id].top
-      @oldState.tiles[@id]?.left = @newState.tiles[@id].left
-
-    if @newState.tiles[@id].newlyCreated
+    @newTileState = @newState.tiles[@id]
+    @oldTileState = @oldState.tiles[@id]
+
+    if @newTileState.display isnt @oldTileState.display
+      @domNode.style.display = @newTileState.display
+      @oldTileState.display = @newTileState.display
+
+    if @newTileState.height isnt @oldTileState.height
+      @domNode.style.height = @newTileState.height + 'px'
+      @oldTileState.height = @newTileState.height
+
+    if @newTileState.top isnt @oldTileState.top or @newTileState.left isnt @oldTileState.left
+      @domNode.style['-webkit-transform'] = "translate3d(#{@newTileState.left}px, #{@newTileState.top}px, 0px)"
+      @oldTileState.top = @newTileState.top
+      @oldTileState.left = @newTileState.left
+
+    if @newTileState.newlyCreated
       newLineIds = []
       newLinesHTML = ""

-      for id, lineState of @newState.tiles[@id].lines
+      for id, lineState of @newTileState.lines
         newLineIds.push(id)
         newLinesHTML += @buildLineHTML(id)
         @screenRowsByLineId[id] = lineState.screenRow
         @lineIdsByScreenRow[lineState.screenRow] = id
-        @oldState.tiles[@id]?.lines[id] = cloneObject(lineState)
+        @oldTileState.lines[id] = cloneObject(lineState)

       return if newLineIds.length is 0

@@ -77,7 +80,7 @@
     @oldState.scrollWidth = @newState.scrollWidth

   removeLineNodes: ->
-    @removeLineNode(id) for id of @oldState.tiles[@id]?.lines
+    @removeLineNode(id) for id of @oldTileState.lines
     return

   removeLineNode: (id) ->
@@ -85,18 +88,18 @@
     delete @lineNodesByLineId[id]
     delete @lineIdsByScreenRow[@screenRowsByLineId[id]]
     delete @screenRowsByLineId[id]
-    delete @oldState.tiles[@id]?.lines[id]
+    delete @oldTileState.lines[id]

   updateLineNodes: ->
-    for id of @oldState.tiles[@id]?.lines
-      unless @newState.tiles[@id].lines.hasOwnProperty(id)
+    for id of @oldTileState.lines
+      unless @newTileState.lines.hasOwnProperty(id)
         @removeLineNode(id)

     newLineIds = null
     newLinesHTML = null

-    for id, lineState of @newState.tiles[@id].lines
-      if @oldState.tiles[@id]?.lines.hasOwnProperty(id)
+    for id, lineState of @newTileState.lines
+      if @oldTileState.lines.hasOwnProperty(id)
         @updateLineNode(id)
       else
         newLineIds ?= []
@@ -105,7 +108,7 @@
         newLinesHTML += @buildLineHTML(id)
         @screenRowsByLineId[id] = lineState.screenRow
         @lineIdsByScreenRow[lineState.screenRow] = id
-        @oldState.tiles[@id]?.lines[id] = cloneObject(lineState)
+        @oldTileState.lines[id] = cloneObject(lineState)

     return unless newLineIds?

@@ -120,7 +123,7 @@

   buildLineHTML: (id) ->
     {scrollWidth} = @newState
-    {screenRow, tokens, text, top, lineEnding, fold, isSoftWrapped, indentLevel, decorationClasses} = @newState.tiles[@id].lines[id]
+    {screenRow, tokens, text, top, lineEnding, fold, isSoftWrapped, indentLevel, decorationClasses} = @newTileState.lines[id]

     classes = ''
     if decorationClasses?
@@ -141,7 +144,7 @@

   buildEmptyLineInnerHTML: (id) ->
     {indentGuidesVisible} = @newState
-    {indentLevel, tabLength, endOfLineInvisibles} = @newState.tiles[@id].lines[id]
+    {indentLevel, tabLength, endOfLineInvisibles} = @newTileState.lines[id]

     if indentGuidesVisible and indentLevel > 0
       invisibleIndex = 0
@@ -164,7 +167,7 @@

   buildLineInnerHTML: (id) ->
     {indentGuidesVisible} = @newState
-    {tokens, text, isOnlyWhitespace} = @newState.tiles[@id].lines[id]
+    {tokens, text, isOnlyWhitespace} = @newTileState.lines[id]
     innerHTML = ""

     scopeStack = []
@@ -178,7 +181,7 @@
     innerHTML

   buildEndOfLineHTML: (id) ->
-    {endOfLineInvisibles} = @newState.tiles[@id].lines[id]
+    {endOfLineInvisibles} = @newTileState.lines[id]

     html = ''
     if endOfLineInvisibles?
@@ -212,8 +215,8 @@
     "<span class=\"#{scope.replace(/\.+/g, ' ')}\">"

   updateLineNode: (id) ->
-    oldLineState = @oldState.tiles[@id]?.lines[id]
-    newLineState = @newState.tiles[@id].lines[id]
+    oldLineState = @oldTileState.lines[id]
+    newLineState = @newTileState.lines[id]

     lineNode = @lineNodesByLineId[id]

@@ -248,7 +251,7 @@
     @lineNodesByLineId[@lineIdsByScreenRow[screenRow]]

   measureCharactersInNewLines: ->
-    for id, lineState of @oldState.tiles[@id]?.lines
+    for id, lineState of @oldTileState.lines
       unless @measuredLines.has(id)
         lineNode = @lineNodesByLineId[id]
         @measureCharactersInLine(id, lineState, lineNode)


Update new home

Update new home

--- app/locale/es-419.coffee
+++ app/locale/es-419.coffee
@@ -14,65 +14,65 @@
     for_developers: "Para Desarrolladores" # Not currently shown on home page.
     or_ipad: "O descargar para iPad"

-#  new_home:
-#    slogan: "The most engaging game for learning programming."
-#    classroom_edition: "Classroom Edition:"
-#    learn_to_code: "Learn to code:"
-#    teacher: "Teacher"
-#    student: "Student"
-#    play_now: "Play Now"
-#    im_a_teacher: "I'm a Teacher"
-#    im_a_student: "I'm a Student"
-#    learn_more: "Learn more"
-#    classroom_in_a_box: "A classroom in-a-box for teaching computer science."
-#    codecombat_is: "CodeCombat is a platform for students to learn computer science while playing through a real game."
-#    our_courses: "Our courses have been specifically playtested to excel in a classroom setting, even by teachers with little to no prior programming experience."
-#    designed_with: "Designed with teachers in mind"
-#    real_code: "Real, typed code"
-#    from_the_first_level: "from the first level"
-#    getting_students: "Getting students to typed code as quickly as possible is critical to learning programming syntax and proper structure."
-#    educator_resources: "Educator resources"
-#    course_guides: "and course guides"
-#    teaching_computer_science: "Teaching computer science does not require a costly degree, because we provide tools to support educators of all backgrounds."
-#    accessible_to: "Accessible to"
-#    everyone: "everyone"
-#    democratizing: "Democratizing the process of learning coding is at the core of our philosophy. Everyone should be able to learn to code."
-#    forgot_learning: "I think they actually forgot that they were actually learning something."
-#    wanted_to_do: " Coding is something I've always wanted to do, and I never thought I would be able to learn it in school."
-#    why_games: "Why is learning through games important?"
-#    games_reward: "Games reward the productive struggle."
-#    encourage: "Gaming is a medium that encourages interaction, discovery, and trial-and-error. A good game challenges the player to master skills over time, which is the same critical process students go through as they learn."
-#    excel: "Games excel at rewarding"
-#    struggle: "productive struggle"
-#    kind_of_struggle: "the kind of struggle that results in learning that’s engaging and"
-#    motivating: "motivating"
-#    not_tedious: "not tedious."
-#    gaming_is_good: "Studies suggest gaming is good for children’s brains. (it’s true!)"
-#    game_based: "When game-based learning systems are"
-#    compared: "compared"
-#    conventional: "against conventional assessment methods, the difference is clear: games are better at helping students retain knowledge, concentrate and"
-#    perform_at_higher_level: "perform at a higher level of achievement"
-#    feedback: "Games also provide real-time feedback that allows students to adjust their solution path and understand concepts more holistically, instead of being limited to just “correct” or “incorrect” answers."
-#    real_game: "A real game, played with real coding."
-#    great_game: "A great game is more than just badges and achievements - it’s about a player’s journey, well-designed puzzles, and the ability to tackle challenges with agency and confidence."
-#    agency: "CodeCombat is a game that gives players that agency and confidence with our robust typed code engine, which helps beginner and advanced students alike write proper, valid code."
-#    curious: "Curious? Request a demo and we'll show you the ropes"
-#    create_class: "Or create a class and see it for yourself!"
-#    request_demo: "Request a Demo"
-#    create_a_class: "Create a Class"
-#    have_an_account: "Already have an account?"
-#    logged_in_as: "You are currently logged in as"
-#    view_my_classes: "View my classes"
-#    computer_science: "Computer science courses for all ages"
-#    show_me_lesson_time: "Show me lesson time estimates for:"
-#    curriculum: "Total curriculum hours:"
-#    ffa: "Free for all students"
-#    lesson_time: "Lesson time:"
-#    coming_soon: "Coming soon!"
-#    courses_available_in: "Courses are available in JavaScript, Python, and Java (coming soon!)"
-#    boast: "Boasts riddles that are complex enough to fascinate gamers and coders alike."
-#    winning: "A winning combination of RPG gameplay and programming homework that pulls off making kid-friendly education legitimately enjoyable."
-#    run_class: "Everything you need to run a computer science class in your school today, no CS background required."
+  new_home:
+    slogan: "El juego más interesante para aprender programación."
+    classroom_edition: "Versión para el Aula:"
+    learn_to_code: "Aprende a programar:"
+    teacher: "Maestro"
+    student: "Estudiante"
+    play_now: "Jugar Ahora"
+    im_a_teacher: "Soy un Maestro"
+    im_a_student: "Soy un Estudiante"
+    learn_more: "Aprender más"
+    classroom_in_a_box: "Un aula prediseñada para enseñar informática."
+    codecombat_is: "CodeCombat es una plataforma para que los estudiantes aprendan informática mientras están jugando un juego de verdad."
+    our_courses: "Nuestros cursos fueron probados especificamente para destacarse en el aula, incluso con maestros con poca o ninguna experiencia previa en programación."
+    designed_with: "Diseñado teniendo en cuenta a los maestros"
+    real_code: "Escritura real de código"
+    from_the_first_level: "desde el primer nivel"
+    getting_students: "Conseguir que los estudiantes escriban código lo más pronto posible es fundamental para que aprendan la sintaxis y la estructura apropiada."
+    educator_resources: "Recursos para el educador"
+    course_guides: "y guías de curso"
+    teaching_computer_science: "Enseñar informática no requiere un título costoso, porque proporcionamos herramientas para ayudar a educadores con cualquier conocimiento previo."
+    accessible_to: "Comprensible para"
+    everyone: "todos"
+    democratizing: "Democratizar el proceso de aprender a programar es la esencia de nuestra filosofía. Todos deberían poder aprender a programar"
+    forgot_learning: "Creo que ellos en realidad olvidaron de que estaban aprendiendo algo."
+    wanted_to_do: "Programar es algo que siempre quise hacer, y que nunca pensé que sería capaz de aprenderlo en la escuela ."
+    why_games: "¿Por qué el aprendizaje mediante juegos es importante?"
+    games_reward: "Los juegos recompensan el esfuerzo productivo."
+    encourage: "El juego es un medio que incentiva la interacción, el descubrimiento, y la prueba-error. Un buen juego desafía al jugador a dominar habilidades con el paso del tiempo, que es un proceso crítico similar al que los estudiantes atraviesan mientran aprenden"
+    excel: "Los juegos se destacan en recompensar el"
+    struggle: "esfuerzo productivo"
+    kind_of_struggle: "el tipo de esfuerzo que se traduce en aprendizaje que interesa y "
+    motivating: "que motiva"
+    not_tedious: "no tedioso."
+    gaming_is_good: "Los estudios sugieren que jugar es bueno para el cerebro de los niños (¡es verdad!)"
+    game_based: "Cuando los sistemas de aprendizaje basados en juegos son"
+    compared: "comparados"
+    conventional: "con los métodos de evaluación convencionales, la diferencia es clara: los juegos son mejores al ayudar a los estudiantes a retener el conocimiento, a concentrarse y"
+    perform_at_higher_level: "a desempeñarse con un mayor nivel de éxito"
+    feedback: 'Los juegos también proveen retroalimentación en tiempo real que permite a los estudiantes ajustar su camino a la solución y entender conceptos de forma más holística, en lugar de estar limitados solamente a respuestas "correctas" e "incorrectas"'
+    real_game: "Un juego de verdad, jugado con código auténtico."
+    great_game: "Un gran juego es más que solo medallas y logros - se trata sobre una travesía del jugador, acertijos bien diseñados, y la habilidad de abordar desafios con voluntad y confianza."
+    agency: "CodeCombat es un juego que ofrece a los jugadores esa voluntad y confianza mediante nuestro motor de código robusto, que ayuda tanto a principiantes como a estudiantes avanzados por igual a escribir código correcto y válido."
+    curious: "¿Curioso? Solicita un demo y te mostraremos cómo funciona "
+    create_class: "¡O crea una clase y miralo vos mismo!"
+    request_demo: "Solicitar un Demo"
+    create_a_class: "Crear una Clase"
+    have_an_account: "¿Ya tenes una cuenta?"
+    logged_in_as: "Estás actualmente conectado como"
+    view_my_classes: "Ver mis clases"
+    computer_science: "Cursos de informática para todas las edades"
+    show_me_lesson_time: "Muéstrame la duración estimada de las clases para:"
+    curriculum: "Horas totales del plan de estudios:"
+    ffa: "Gratis para todos los estudiantes"
+    lesson_time: "Duración de la clase:"
+    coming_soon: "¡Proximamente!"
+    courses_available_in: "Los cursos están disponibles en JavaScript, Python, y Java (¡proximamente!)"
+    boast: "Cuenta con acertijos lo suficiente complejos como para fascinar a jugadores y programadores por igual."
+    winning: "Una combinación ganadora entre un juego de rol y una tarea sobre programación que consigue hacer de la educación amigable y disfrutable para los niños."
+    run_class: "Todo lo que necesitas para organizar una clase de informática en tu escuela hoy mismo, sin necesidad de ningún conocimiento previo de informática."

   nav:
     play: "Jugar" # The top nav bar entry where players choose which levels to play


revert locale change

revert locale change

--- app/locale/locale.coffee
+++ app/locale/locale.coffee
@@ -10,11 +10,11 @@
   'en': require('./en') # Include these in the main bundle
   'en-US': require('./en-US')
   'en-GB': { nativeDescription: 'English (UK)', englishDescription: 'English (UK)' }
-  'zh-HANS': require('./zh-HANS')
+  'zh-HANS': { nativeDescription: '简体中文', englishDescription: 'Chinese (Simplified)' }
   'zh-HANT': { nativeDescription: '繁體中文', englishDescription: 'Chinese (Traditional)' }
-  'ru': require('./ru')
+  'ru': { nativeDescription: 'русский', englishDescription: 'Russian' }
   'es-ES': { nativeDescription: 'español (ES)', englishDescription: 'Spanish (Spain)' }
-  'es-419': require('./es-419')
+  'es-419': { nativeDescription: 'español (América Latina)', englishDescription: 'Spanish (Latin America)' }
   'fr': { nativeDescription: 'français', englishDescription: 'French' }
   'pt-PT': { nativeDescription: 'Português (Portugal)', englishDescription: 'Portuguese (Portugal)' }
   'pt-BR': { nativeDescription: 'Português (Brasil)', englishDescription: 'Portuguese (Brazil)' }


Added code to merge target level config with task level options

Necessitates removing required entry property since
it can (and likely will) be specified as part of
task options.

--- tasks/browserify2.coffee
+++ tasks/browserify2.coffee
@@ -23,15 +23,16 @@
     browserify = require 'browserify'
     config = grunt.config.get(@name)
     targetConfig = config[@target]
-    {entry, mount, server, debug, compile, beforeHook, afterHook} = targetConfig
+
+    options = @options @data
+
+    {entry, mount, server, debug, compile, beforeHook, afterHook} = options
     bundle = browserify entry

     exposeOpts = []
     exposeOpts.push targetConfig.options?.expose if targetConfig.options?.expose
     exposeOpts.push config.options?.expose if config.options?.expose
     expose grunt, bundle, key, val for key, val of opt for opt in exposeOpts
-
-    grunt.config.requires("#{@name}.#{@target}.entry")

     if beforeHook
       beforeHook.call this, bundle


fix a bug 当没有输入文件夹名称时，不允许点击next

fix a bug
当没有输入文件夹名称时，不允许点击next

--- lib/project/project-info-view.coffee
+++ lib/project/project-info-view.coffee
@@ -19,6 +19,16 @@
           @input type: "checkbox", id: 'withBootstrap'
           @text('With Bootstrap')

+
+  initialize: (wizardView) ->
+    @editor.getEditor().onDidChange =>
+      unless @editor.getText() is ""
+        wizardView.enableNext()
+      else
+        wizardView.disableNext()
+
+
+
   attachTo: (parentView)->
     parentView.append(this)
     @editor.focus()


Don't need this extra level of indentation.

--- spec/git-diff-spec.coffee
+++ spec/git-diff-spec.coffee
@@ -74,10 +74,11 @@
         expect(editorView.rootElement.querySelectorAll('.git-line-modified').length).toBe 1
         editor.backspace()
         advanceClock(editor.getBuffer().stoppedChangingDelay)
-        waitsFor ->
-          editorView.rootElement.querySelectorAll('.git-line-modified').length is 0
-        runs ->
-          expect(editorView.rootElement.querySelectorAll('.git-line-modified').length).toBe 0
+
+      waitsFor ->
+        editorView.rootElement.querySelectorAll('.git-line-modified').length is 0
+      runs ->
+        expect(editorView.rootElement.querySelectorAll('.git-line-modified').length).toBe 0

   describe "when a modified file is opened", ->
     it "highlights the changed lines", ->


Remove console.log cruft

Remove console.log cruft

--- bokehjs/src/coffee/models/graphs/graph_hit_test_policy.coffee
+++ bokehjs/src/coffee/models/graphs/graph_hit_test_policy.coffee
@@ -65,8 +65,6 @@

     @_edge_selector = edge_view.model.data_source.selection_manager.selector
     @_edge_selector.update(linked_index, final, append)
-    console.log('loop')
-    console.log(@_edge_selector.indices.is_empty())

     return not @_node_selector.indices.is_empty()


randomise building colour and height

randomise building colour and height

--- views/gorillas.coffee
+++ views/gorillas.coffee
@@ -1,9 +1,9 @@
 class Building
-  constructor:(context, color) ->
+  constructor:(context) ->
     @context = context
     @width = 100
     @base_height = 135
-    @color = color
+    @randomize_color()

   draw:(x,y) ->
     @context.fillStyle = @color
@@ -29,6 +29,10 @@
   create_window:(x, y) ->
     @context.fillRect x, y, 8, 16

+  randomize_color:() ->
+    colors = [ '#C0C0C0', '#800000', '#00FFFF' ]
+    random = Math.floor(Math.random()*colors.length)
+    @color = colors[random]

 class Sun
   constructor:(context) ->
@@ -83,7 +87,6 @@
     x: x + d * Math.cos(a),
     y: y + d * Math.sin(a)

-
 class Painter
   constructor: ->
     @canvas = document.getElementById "gorillas"
@@ -91,29 +94,12 @@
     @color = '#00FFFF'

   draw_buildings:()->
-    @draw_building(0, 145)
-    @draw_building(101, 105)
+    @draw_building(position) for position in [0, 101, 202, 303, 404, 505, 606, 707, 808, 909 ]

-    @set_color '#800000'
-    @draw_building(202, 165)
-    @draw_building(303, 155)
-    @draw_building(404, 95)
+  draw_building:(x) ->
+    building = new Building(@context)

-    @set_color '#C0C0C0'
-    @draw_building(505, 15)
-
-    @set_color '#800000'
-    @draw_building(606, 215)
-
-    @set_color '#C0C0C0'
-    @draw_building(707, 55)
-    @set_color '#800000'
-    @draw_building(808, 15)
-    @draw_building(909, 0)
-
-
-  draw_building:(x, y) ->
-    building = new Building(@context, @color)
+    y = Math.floor(Math.random()*215)
     building.draw(x, y)

   draw_the_sun: ->


Log game response packets only when `dbg.extra('gameResponse')` is enabled

--- views/services.coffee
+++ views/services.coffee
@@ -73,7 +73,7 @@
 window.addEventListener 'game.response', (e) ->
   {method, body, postBody} = e.detail
   resPath = e.detail.path
-  console.log [resPath, body, postBody] if dbg.isEnabled()
+  dbg.extra('gameResponse').log [resPath, body, postBody]
   log "#{__ 'Hit'} #{method} #{resPath}"
 window.addEventListener 'network.error.retry', (e) ->
   {counter} = e.detail


grid: work only when inside 360px in the viewport

--- app/_src/components/grid.coffee
+++ app/_src/components/grid.coffee
@@ -104,12 +104,10 @@
 		@checkBreakpoint()

 		if currentBreakpointKey isnt null
-
 			# Setup the grid on how many cols and rows
 			@gridSetup()

-			# Go!
-			@createGrid()
+		@updateContextHeight()

 		# Animate them in only when in viewport
 		if Modernizr.touch or
@@ -118,7 +116,7 @@
 				# Except for mobile...
 				@onEnterViewport()
 		else
-			watcher = scrollMonitor.create( @$context, -300 )
+			watcher = scrollMonitor.create( @$context, -360 )
 			watcher.recalculateLocation()
 			@addWatcherListeners()

@@ -199,7 +197,9 @@
 	###
 	onEnterViewport: () =>
 		return if hasBeenShown
-		@showGrid()
+		if currentBreakpointKey isnt null
+			@createGrid()
+			@showGrid()
 		@addEventListeners()
 		@onWindowResize() # in case we have resized before all this happened
 		hasBeenShown = true
@@ -260,7 +260,7 @@
 			@updateContextHeight true
 			return

-		currentNumberOfCols = if DOMModel[0] isnt undefined then DOMModel[0].length else 0
+		currentNumberOfCols = if DOMModel[0]? then DOMModel[0].length else 0
 		row = 0

 		# Which breakpoint should we base this on
@@ -385,7 +385,6 @@
 	###
 	createGrid: () ->
 		@addGridRows()
-		@updateContextHeight()
 		@refreshImages()

 	###
@@ -408,7 +407,7 @@
 		@param {Boolean} reset If we are resetting the containers height
 	###
 	updateContextHeight: (reset) =>
-		if reset is true
+		if reset is true or currentBreakpointKey is null
 			@$context.height 'auto'
 		else
 			@$context.height numAvailable.rows * (@getCellHeight('1') + GRID_CELL_GAP)


base64 encode data for _maximum_ security :p

--- app/adapters/application.coffee
+++ app/adapters/application.coffee
@@ -1,4 +1,10 @@
 `import Save from '../models/save'`
+
+utf8_to_b64 = (str) ->
+    btoa encodeURIComponent escape str
+
+b64_to_utf8 = (str) ->
+    unescape decodeURIComponent atob str

 guid = (->
     s4 = ->
@@ -8,11 +14,11 @@
         s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4()
 )()

-getKey = (type, id) ->
+getKey = (id) ->
     if typeof id is 'object'
         id = id.get 'id'

-    type.typeKey + "#" + id
+    "#{id}"

 Promise = Ember.RSVP.Promise

@@ -21,12 +27,12 @@
     defaultSerializer: '-rest'

     find: (store, type, id) -> new Promise (resolve, reject) ->
-        key = getKey type, id
+        key = getKey id
         json = localStorage.getItem key

         try
             result = { }
-            hash = JSON.parse json
+            hash = JSON.parse b64_to_utf8 json
             hash.id = id
             result[type.typeKey] = hash
             resolve result
@@ -44,19 +50,19 @@
         record.set 'id', guid()
         serializer = store.serializerFor type.typeKey

-        key = getKey type, record
+        key = getKey record
         data = serializer.serialize record

-        localStorage.setItem key, JSON.stringify data
+        localStorage.setItem key, utf8_to_b64 JSON.stringify data
         resolve()

     updateRecord: (store, type, record) -> new Promise (resolve) ->
         serializer = store.serializerFor type.typeKey

-        key = getKey type, record
+        key = getKey record
         data = serializer.serialize record

-        localStorage.setItem key, JSON.stringify data
+        localStorage.setItem key, utf8_to_b64 JSON.stringify data
         resolve()

     deleteRecord: (store, type, record) ->


removed unused color lib

removed unused color lib

--- app/coffeescripts/colors.coffee
+++ app/coffeescripts/colors.coffee
@@ -1,51 +0,0 @@
-Color = (color) ->
-  if typeof color == "string"
-    if color[0] = '#'
-      parseHex(color)
-    else if color.substr(0, 4) == 'rgb('
-      parseRGB(color)
-    else if color.substr(0, 4) == 'rgba'
-      parseRGBA(color)
-  if typeof color == "array"
-    r = parseInt(color[0])
-    g = parseInt(color[1])
-    b = parseInt(color[2])
-    if color[3]
-      a = parseFloat(color[3])
-    else
-      a = 1
-
-  channels: [
-    typeof I.r == 'string' && parseHex(I.r) || I.r
-    typeof I.g == 'string' && parseHex(I.g) || I.g
-    typeof I.b == 'string' && parseHex(I.b) || I.b
-    (typeof I.a != 'string' && typeof I.a != 'number') && 1 || typeof I.a == 'string' && parseFloat(I.a) || I.a
-  ]
-
-  getValue = ->
-    (channels[0] * 0x10000) | (channels[1] * 0x100) | channels[2]
-
-  parseRGB = (color) ->
-    results = color.match(/[\d,]+/).join(',').split(',')
-    return [parseInt(results[0]), parseInt(results[1]), parseInt(results[2])]
-
-  parseRGBA = (color) ->
-    results = color.match(/(\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3}),?\s*(\d\.?\d*)/)[0].join(',').split(',')
-    return [parseInt(results[0]), parseInt(results[1]), parseInt(results[2]), parseFloat(results[3])]
-
-  self =
-    channels: channels
-
-    equals: (other) ->
-      return other.r == I.r && other.g == I.g && other.b == I.b && other.a = I.a
-
-    hexTriplet: ->
-      return "#" + ("00000" + getValue().toString(16)).substr(-6)
-
-    rgba: ->
-      return "rgba(#{I.channels.join(',')})"
-
-    toString: ->
-      return (if channels[3] == 1 then self.hexTriplet() else self.rgba())
-
-  return self


Lab Landing Page Merge Issue Resolution

Lab Landing Page Merge Issue Resolution

--- app/pages/lab/landing-page.cjsx
+++ app/pages/lab/landing-page.cjsx
@@ -21,11 +21,8 @@
       policies: 'Project Builder Policies'
       bestPractices: 'Best Practices Guide'
       buildTalk: 'Project Builder Talk'
-<<<<<<< HEAD
       acknowledgements: 'Acknowledgements'
-=======
       glossary: 'Glossary'
->>>>>>> master

 module.exports = React.createClass
   displayName: 'ProjectBuilderLandingPage'


Fixed documentation for browser.css to indicate it returns array, not NodeList.

--- src/zombie/browser.coffee
+++ src/zombie/browser.coffee
@@ -283,10 +283,10 @@
       return "" unless @document.documentElement
       @css(selector, context).map((e)-> e.outerHTML.trim()).join("")

-    # ### browser.css(selector, context?) => NodeList
+    # ### browser.css(selector, context?) => Array
     #
     # Evaluates the CSS selector against the document (or context node) and
-    # return a node list.  Shortcut for `document.querySelectorAll`.
+    # return array of nodes.  Shortcut for `document.querySelectorAll`.
     this.css = (selector, context)->
       context ||= @document
       if selector then context.querySelectorAll(selector).toArray() else [context]


Remove log in operation model

Remove log in operation model

--- app/src/models/operation.coffee
+++ app/src/models/operation.coffee
@@ -11,8 +11,6 @@
   get: (key) ->
     switch key
       when 'total_value'
-        l super 'fees'
-        l super 'value'
         if super('type') == 'sending'
           ledger.wallet.Value.from(super 'value').add(super 'fees')
         else


Increase subjects per queue

Increase subjects per queue

--- app/stores/subject-store.coffee
+++ app/stores/subject-store.coffee
@@ -27,7 +27,8 @@
         #default 10 subjects when the workflow is fully classified/retired. By
         #increasing the number of subjects to 100 (order is randomised), we
         #mask the repetition of subjects for classroom users.
-        #This workaround is requested by HHMI themselves. (@shaun 20180214)
+        #This workaround is requested by HHMI themselves.
+        #(@shaun 20180214)

       api.get('/subjects/queued', query)
         .then (subjects) =>


Remove old Code

Remove old Code

--- app/views/home-page-view.coffee
+++ app/views/home-page-view.coffee
@@ -129,13 +129,9 @@

     if actions['interact']
       player.onAction()
-      placeholder = true
-      mediator.getActiveLevel().entityObjects[3].moveToPosition(player.position, 50)
-      # code

     if actions['cancel']
       placeholder = true
-      # code

     if moveDir.LengthSquared() and not mediator.blockInput
       moveDir.Normalize()


Fix selectors for enabled test

Fix selectors for enabled test

--- apps/artist/client/index.coffee
+++ apps/artist/client/index.coffee
@@ -16,7 +16,12 @@
   Backbone.history.start pushState: true

   if user?.isAdmin?()
-    attachArtworkModal '.carousel-figure, .artwork-item-image-link', {
+    selectors = [
+      '.carousel-figure a[href^="/artwork"]'
+      '#artwork-section .artwork-item-image-link'
+    ]
+
+    attachArtworkModal selectors.join(', '), {
       'artist/:id': 'close'
       'artwork/:id': 'modal'
     }


chore(routes): removed import

chore(routes): removed import

--- vue/src/routes.coffee
+++ vue/src/routes.coffee
@@ -6,7 +6,6 @@
 import ProfilePage from './components/profile/page.vue'
 import StartPollPage from './components/start_poll/page.vue'
 import PollPage from './components/poll/page.vue'
-import MembershipRequestsPage from './components/membership_requests/page.vue'

 import GroupPage from './components/group/page.vue'
 import GroupDiscussionsPanel from './components/group/discussions_panel'


Well that explains why this always behaved differently than everything else.

--- apps/feature/client/view.coffee
+++ apps/feature/client/view.coffee
@@ -134,7 +134,7 @@
   renderAuctionInfo: (sale) ->
     @$('#feature-description-register-container').html auctionRegisterButtonTemplate
       sale: sale
-      registered: @currentUser?.get('registered_to_bid')?
+      registered: @currentUser?.get('registered_to_bid')
     @$('#feature-auction-info-countdown-container').html auctionCountdownTemplate(sale: sale)
     @setupClock sale


sample printing of the embed string

sample printing of the embed string

--- static/coffee/base.coffee
+++ static/coffee/base.coffee
@@ -599,6 +599,7 @@
   marksize : 3
   initialize : (options) ->
     @plot_model = options.plot_model
+    console.log("script_inject", @plot_model.get('script_inject'))
     @plot_view = options.plot_view
     ctx = @plot_view.ctx
     if (!ctx.setLineDash)


fullpieagent testing

fullpieagent testing

--- widgets/fullpie_agent/fullpie_agent.coffee
+++ widgets/fullpie_agent/fullpie_agent.coffee
@@ -234,9 +234,7 @@

         path.enter().append("path")
             .attr("class", "pie")
-            .attr("fill", (d, i) ->
-                return color i
-            )
+            .attr("fill", "#ffffff")#(d, i) -> return color i)
             .attr("d", arc)
         #path.transition()
         #    .duration(1500)


Agent pie: start/end angle pie transition work

--- widgets/fullpie_agent/fullpie_agent.coffee
+++ widgets/fullpie_agent/fullpie_agent.coffee
@@ -6,8 +6,7 @@
     ready: ->
         @testData = 123
         @oldPieData = ''
-        @instanceDataId = $(@node).parent().attr('data-id')
-        console.log('@instanceDataId',@instanceDataId)
+        @instanceDataId = ''

     onData: (data) ->
         console.log('testData',@testData)
@@ -16,6 +15,8 @@

         #$(@node).fadeOut().fadeIn()
         @container = $(@node).parent()
+        @instanceDataId = @container.attr('data-id')
+        console.log('@instanceDataId',@instanceDataId)

         #
         # CONFIG ZONE
@@ -319,7 +320,7 @@
                 return

             if conflicts.length
-                console.log d, ' conflicts with ', conflicts
+                #console.log d, ' conflicts with ', conflicts
                 rightEdge = d3.max(conflicts, (d2) ->
                     `var maxLabelHeight`
                     `var maxLabelWidth`
@@ -328,8 +329,9 @@
                 d.l = rightEdge
                 d.x = d.l + bbox.width / 2 + 5
                 d.r = d.l + bbox.width + 10
-            else
-                console.log 'no conflicts for ', d
+            #else
+            #    console.log 'no conflicts for ', d
+
             ### add this label to the quadtree, so it will show up as a conflict
                for future labels.
             ###


fullpieagent testing

fullpieagent testing

--- widgets/fullpie_agent/fullpie_agent.coffee
+++ widgets/fullpie_agent/fullpie_agent.coffee
@@ -101,7 +101,7 @@
     path = pathGroup.selectAll('path.pie').data(piedata)
     #console.log("path",this.path.empty())
     path.enter().append('path').attr('class', 'pie').attr 'fill', (d, i) ->
-      @color i
+      color i
     #@path.transition().duration(1500).attrTween 'd', that.pieTween
     path.transition()
         .duration(1000)


fullpieagent testing

fullpieagent testing

--- widgets/fullpie_agent/fullpie_agent.coffee
+++ widgets/fullpie_agent/fullpie_agent.coffee
@@ -287,29 +287,28 @@
         #arcs.append("svg:path").attr("fill", (d, i) -> color i)
         #    .attr("fill-opacity", 0.4).attr("d", arc)

-        #path.transition()
-        #    .duration(1500)
-        #    #.attrTween("d", pieTween)
-        #    .attrTween("d", (d,i) ->
-        #        console.log("d",d)
-        #        console.log("i",i)
-        #        #i = d3.interpolate({startAngle: 0, endAngle: 0}, {startAngle: d.startAngle, endAngle: d.endAngle})
-        #        i = d3.interpolate({startAngle: 0, endAngle: 0}, {startAngle: piedata[i].startAngle, endAngle: piedata[i].endAngle})
-        #        return (t) ->
-        #            b = i(t)
-        #            return arc(b)
-        #    )
-
-        #path.exit()
-        #    .transition()
-        #    .duration(300)
-        #    .attrTween("d", (d,i) ->
-        #        i = d3.interpolate({startAngle: d.startAngle,endAngle: d.endAngle},{startAngle: 2 * Math.PI,endAngle: 2 * Math.PI})
-        #        return (t) ->
-        #            b = i(t)
-        #            return arc(b)
-        #    )
-        #    .remove()
+        path.transition()
+            .duration(1500)
+            #.attrTween("d", pieTween)
+            .attrTween("d", (d,i) ->
+                console.log("d",d)
+                console.log("i",i)
+                i = d3.interpolate({startAngle: 0, endAngle: 0}, {startAngle: d.startAngle, endAngle: d.endAngle})
+                return (t) ->
+                    b = i(t)
+                    return arc(b)
+            )
+
+        path.exit()
+            .transition()
+            .duration(300)
+            .attrTween("d", (d,i) ->
+                i = d3.interpolate({startAngle: d.startAngle,endAngle: d.endAngle},{startAngle: 2 * Math.PI,endAngle: 2 * Math.PI})
+                return (t) ->
+                    b = i(t)
+                    return arc(b)
+            )
+            .remove()

         labels = labelGroup.selectAll("text")
             .data(piedata.sort((p1,p2) -> return p1.startAngle - p2.startAngle))


fullpieagent testing

fullpieagent testing

--- widgets/fullpie_agent/fullpie_agent.coffee
+++ widgets/fullpie_agent/fullpie_agent.coffee
@@ -23,10 +23,9 @@
         radiuso = 130                           # Outer radius of the pie
         radiusi = 65                            # Inner radius of the pie (zero = pie, non-zero = donut)
         color = d3.scale.category20()           # Color scale for pie slices
-        dropshadowx = 2                         # X-offset for drop shadow filter
-        dropshadowy = 2                         # Y-offset for drop shadow filter
+        dropshadowx = 4                         # X-offset for drop shadow filter
+        dropshadowy = 4                         # Y-offset for drop shadow filter
         dropshadowblur = "1.1"                  # [STRING] Blur value for drop shadow filter
-        totallabelfontsize = radiusi            # Font size for total count label

         pie = d3.layout.pie().value((d) -> d.value)
         arc = d3.svg.arc().innerRadius(radiusi).outerRadius(radiuso)


test commit and build

test commit and build

--- feed/server.coffee
+++ feed/server.coffee
@@ -63,7 +63,7 @@

     xml = template
         self: self
-        updated: '2015-01-02'
+        updated: '2015-03-10 19:58:00'
         geocaches: geocaches
     res.set 'Content-Type', 'application/atom+xml'
     res.send xml


no autostart for socket.io

no autostart for socket.io

--- proxy.coffee
+++ proxy.coffee
@@ -195,7 +195,7 @@
     # @arg slug the app slug
     # @arg cb(err, port) a callback
     #   err.code / err.msg
-    ensureStarted: (slug, cb) =>
+    ensureStarted: (slug, doStart, cb) =>
         if not @routes[slug]?
             cb code:404, msg:'app unknown'
             return
@@ -205,12 +205,15 @@
             when 'installing'   then cb code:404, msg:'app is still installing'
             when 'installed'    then cb null, @routes[slug].port
             when 'stopped'
+                unless doStart
+                    return code: 500, msg: 'wont start'
+
                 @startApp slug, (err) =>
                     if err?
-                        cb code:500, msg:"cannot start app : #{err}"
+                        cb code: 500, msg: "cannot start app : #{err}"
                     else
                         cb null, @routes[slug].port
-            else cb code:500, msg:'incorrect app state'
+            else cb code: 500, msg: 'incorrect app state'

     # request home to start a new app
     startApp: (slug, cb) =>
@@ -236,7 +239,10 @@
         buffer = httpProxy.buffer(req)
         appName = req.params.name
         req.url = req.url.substring "/apps/#{appName}".length
-        @ensureStarted appName, (err, port) =>
+
+        doStart = -1 isnt req.url.indexOf 'socket.io'
+
+        @ensureStarted appName, doStart, (err, port) =>
             return res.send err.code, err.msg if err?
             @proxy.proxyRequest req, res,
                 host: 'localhost'
@@ -251,7 +257,10 @@
         appName = req.params.name
         req.url = req.url.substring "/public/#{appName}".length
         req.url = "/public#{req.url}"
-        @ensureStarted appName, (err, port) =>
+
+        doStart = -1 isnt req.url.indexOf 'socket.io'
+
+        @ensureStarted appName, doStart, (err, port) =>
             return res.send err.code, err.msg if err?
             @proxy.proxyRequest req, res,
                 host: 'localhost'


STL/lang stashing the Array/List extensions

--- stl/lang/src/array.coffee
+++ stl/lang/src/array.coffee
@@ -4,6 +4,109 @@
 # Copyright (C) 2011 Nikolay Nemshilov
 #
 ext Array.prototype,
+  #
+  # Returns the size of the list
+  #
+  # @return {Number} list size
+  #
+  size: -> @length
+
+  #
+  # Checks if the list is empty
+  #
+  # @return {Boolean} check result
+  #
+  empty: -> @length is 0
+
+  clean: ->
+    @length = 0
+    return @
+
+  #
+  # Clones the list with all the internal data
+  #
+  # @return {List} new
+  #
+  clone: -> new @constructor(A(@))
+
+  #
+  # Returns the first items on the list
+  #
+  # @return {mixed} the first item or `undefined`
+  #
+  first: ->
+    if arguments.length is 0 then @[0]
+    else @filter.apply(@, arguments).first()
+
+  #
+  # Returns the last item on the list
+  #
+  # @return {mixed} the last item or `undefined`
+  #
+  last: ->
+    if arguments.length is 0 then @[@length - 1]
+    else @filter.apply(@, arguments).last()
+
+  random: ->
+    if @length is 0 then undefined else @[Math.random(@.length-1)]

   includes: (item) ->
-    this.indexOf(item) isnt -1+    this.indexOf(item) isnt -1
+
+  walk: ->
+
+  #
+  # Creates a new list without the specified items
+  #
+  # @param {mixed} item
+  # .....
+  # @return {List} new
+  #
+  without: ->
+    filter = new List(arguments)
+    @reject (item) ->
+      filter.includes(item)
+
+  #
+  # Creates a new list that doesn't have 'null' and 'undefined' values
+  #
+  # @return {List} new
+  #
+  compact: ->
+    @without(null, undefined)
+
+  flatten: ->
+    list = new List()
+
+    @forEach (item)->
+      if isArray(item)
+        list = list.concat(new List(item).flatten())
+      else
+        list.push(item)
+
+    list
+
+  uniq: -> new List().merge(@)
+
+  merge: (list)->
+    new List(list).forEach (item)->
+      @.push(item) unless @includes(item)
+    , @
+    return @
+
+  shuffle: `function() {
+    var list = this.clone(), j, x, i = list.length;
+    for (; i > 0; j = Math.random(i-1), x = list[--i], list[i] = list[j], list[j] = x) {}
+    return list;}`
+
+  sort: ->
+  sortBy: ->
+
+  reverse: ->
+
+  min: -> Math.min.apply(Math, @)
+  max: -> Math.max.apply(Math, @)
+  sum: ->
+    sum = 0
+    sum += item for item in @
+    sum


fixed typo

fixed typo

--- _assets/js/_keyboard.coffee
+++ _assets/js/_keyboard.coffee
@@ -1,12 +1,10 @@
-currentSelection = $("#search-results a.selected")
-
-results = $('#search-results')
+# results = $('#search-results')

 searchSubmit = () ->
   btn = document.querySelector('.search-submit')
   btn.click () ->
     selected = document.querySelector('#search-results a.selected')
-    url = selected.getAttribute('href1') if selected
+    url = selected.getAttribute('href') if selected
     window.open url, '_self'
     return
   return


Fixes generation of third-party module names

--- grunt/amd_tamer.coffee
+++ grunt/amd_tamer.coffee
@@ -1,12 +1,13 @@
 module.exports =
-  options:
-    namespace: '<%= package.name %>',
-    base: 'src/'
   dist:
+    options:
+      namespace: '<%= package.name %>',
+      base: 'src/'
     src: ['src/**/*.js'],
     dest: 'build/debug/js/<%= package.name %>.js'
   all:
     options:
       footer: '//# sourceMappingURL=all.js.map'
+      base: 'build/debug/js'
     src: ['build/debug/js/**/*.js']
-    dest: 'build/debug/all.js'+    dest: 'build/debug/all.js'


Updated default server address

Updated default server address

--- www/coffee/index.coffee
+++ www/coffee/index.coffee
@@ -37,7 +37,7 @@
 	serverAddress = serverAddress.replace(/\/$/, '')

 	if serverAddress.length is 0
-		serverAddress = 'https://demo.rocket.chat'
+		serverAddress = 'https://open.rocket.chat'

 	if not /(^https?:\/\/)|(\.)|(^localhost(:\d+)?$)/.test serverAddress
 		serverAddress = 'https://' + serverAddress + '.rocket.chat'


Add buy_gems and  earn_gems translations

Add buy_gems and  earn_gems translations

--- app/locale/es-419.coffee
+++ app/locale/es-419.coffee
@@ -643,13 +643,13 @@
     prompt_button: "Entrar al mercado"
     recovered: "Se recuperaron las anteriores compras de gemas. Por favor recarga la página"
     price: "x{{gems}} / mes"
-#    buy_premium: "Buy Premium"
-#    purchase: "Purchase"
-#    purchased: "Purchased"
-
-#  earn_gems:
-#    prompt_title: "Not Enough Gems"
-#    prompt_body: "Keep playing to earn more!"
+    buy_premium: "Comprar Premium"
+    purchase: "Compra"
+    purchased: "Comprado"
+
+  earn_gems:
+    prompt_title: "Gemas Insuficientes"
+    prompt_body: "¡Sigue jugando para ganar más!"

   subscribe:
 #    best_deal: "Best Deal!"


Update new service button text.

Update new service button text.


Draw recursively

Draw recursively

--- src/draw.coffee
+++ src/draw.coffee
@@ -4,8 +4,7 @@
   SVG = world.SVG()

   draw_self = (world) ->
-    assert !world.has_children()
-    #console.log "draw_self #{world}"
+    console.log "draw_self #{world}"
     dict = {
       class: world.labels(['draw', 'self'])
       stroke: world.get 'stroke'
@@ -13,7 +12,7 @@
     }
     dict['stroke'] = 'goldenrod' if world.get('selected')
     paths = world.get 'path'
-    return unless paths?
+    return undefined unless paths?
     paths = [paths] if !world.is_array(paths)
     elements = paths.map (path) ->
       dict['d'] = path
@@ -25,11 +24,10 @@
     }, world.bind() -> world.get('name')
     elements

-  draw_children = (world) ->
-    world.map_children(draw_world) if world.has_children()
+  draw_children = (world) -> world.map_children(draw_world)

   draw_world = (world) ->
-    #console.log "draw_world #{world} kids:#{world.has_children()}", world
+    console.log "draw_world #{world} kids:#{world.has_children()}", world
     dict = {
       class: world.labels(['draw', 'world'])
       transform: world.bind() -> world.get('transform')
@@ -37,8 +35,13 @@
     clicker = world.get_raw 'click'
     dict['click'] = -> clicker(world) if clicker? and !world.has_children()
     SVG.g dict, world.bind() ->
-      result = draw_children(world) or draw_self(world)
-      if _.isArray(result) then result else result.all()
+      paths = draw_self(world)
+      paths = [] unless paths?
+      child_paths = draw_children(world)
+      child_paths = child_paths.all() unless _.isArray(child_paths)
+      for child in child_paths
+        paths.push child
+      paths

   #console.log world
   device_width = world.get('device_width')


Add ability to add custom tasks to entities

--- app/models/Entity.coffee
+++ app/models/Entity.coffee
@@ -267,3 +267,7 @@
     else
       task = @tasks.shift()
       task(@) if task
+
+
+  addTask: (task) =>
+    @tasks.push task


Add a mechanizem to disable the i18n-db transformation on specific routes

The transformation replaces the document fields with those that exist in
the
i18n subdocument for the current user lang.

There are situations where we don't want the transformation on certain
routes
in which we use 3rd party packages that use the collection but are not
under
our control (translation editor route for example).

Note: it is possible to disable translation for a specific find/findOne operation
by passing `transform: null` as an option.

--- tap_i18n_db-client.coffee
+++ tap_i18n_db-client.coffee
@@ -2,6 +2,10 @@
 extend = $.extend

 share.i18nCollectionTransform = (doc, collection) ->
+  for route in collection._disabledOnRoutes
+    if route.test(window.location.pathname)
+      return doc
+
   collection_base_language = collection._base_language
   language = TAPi18n.getLanguage()

@@ -46,6 +50,10 @@
       local_session.set "force_lang_switch_reactivity_hook", TAPi18n.getLanguage()

     return
+
+  obj._disabledOnRoutes = []
+  obj._disableTransformationOnRoute = (route) ->
+    obj._disabledOnRoutes.push(route)

   if Package.autopublish?
     obj.forceLangSwitchReactivity()


Bring GitBridge specs up to date.

Bring GitBridge specs up to date.

--- spec/git-bridge-spec.coffee
+++ spec/git-bridge-spec.coffee
@@ -4,7 +4,15 @@

 describe 'GitBridge', ->

-  repoBase = -> atom.project.getRepositories()[0].getWorkingDirectory()
+  gitWorkDir = "/fake/gitroot/"
+
+  repo =
+    getWorkingDirectory: -> gitWorkDir
+    relativize: (fullpath) ->
+      if fullpath.startsWith gitWorkDir
+        fullpath[gitWorkDir.length..]
+      else
+        fullpath

   beforeEach ->
     done = false
@@ -27,7 +35,7 @@
       { process: { on: (callback) -> } }

     conflicts = []
-    GitBridge.withConflicts (err, cs) ->
+    GitBridge.withConflicts repo, (err, cs) ->
       throw err if err
       conflicts = cs

@@ -37,7 +45,7 @@
     ])
     expect(c).toBe('/usr/bin/git')
     expect(a).toEqual(['status', '--porcelain'])
-    expect(o).toEqual({ cwd: repoBase() })
+    expect(o).toEqual({ cwd: gitWorkDir })

   describe 'isStaged', ->

@@ -48,7 +56,7 @@
         { process: { on: (callback) -> } }

       staged = null
-      GitBridge.isStaged checkPath, (err, b) ->
+      GitBridge.isStaged repo, checkPath, (err, b) ->
         throw err if err
         staged = b
       staged
@@ -73,14 +81,14 @@
       { process: { on: (callback) -> } }

     called = false
-    GitBridge.checkoutSide 'ours', 'lib/file1.txt', (err) ->
+    GitBridge.checkoutSide repo, 'ours', 'lib/file1.txt', (err) ->
       throw err if err
       called = true

     expect(called).toBe(true)
     expect(c).toBe('/usr/bin/git')
     expect(a).toEqual(['checkout', '--ours', 'lib/file1.txt'])
-    expect(o).toEqual({ cwd: repoBase() })
+    expect(o).toEqual({ cwd: gitWorkDir })

   it 'stages changes to a file', ->
     [c, a, o] = []
@@ -89,14 +97,14 @@
       exit(0)

     called = false
-    GitBridge.add 'lib/file1.txt', (err) ->
+    GitBridge.add repo, 'lib/file1.txt', (err) ->
       throw err if err
       called = true

     expect(called).toBe(true)
     expect(c).toBe('/usr/bin/git')
     expect(a).toEqual(['add', 'lib/file1.txt'])
-    expect(o).toEqual({ cwd: repoBase() })
+    expect(o).toEqual({ cwd: gitWorkDir })

   describe 'rebase detection', ->


Fix reading all.json

Fix reading all.json

--- helper/settings.coffee
+++ helper/settings.coffee
@@ -7,7 +7,7 @@
   settings = {}
   splinters = parse file
   files = [
-    "#{splinters.dir}/all.#{splinters.ext}"
+    "#{splinters.dir}/all#{splinters.ext}"
     "#{splinters.dir}/all.specific#{splinters.ext}"
     "#{splinters.dir}/all.local#{splinters.ext}"
     "#{splinters.dir}/#{splinters.name}#{splinters.ext}" # file itself
@@ -15,7 +15,9 @@
     "#{splinters.dir}/#{splinters.name}.local#{splinters.ext}"
     "#{process.env[if process.platform is "win32" then "USERPROFILE" else "HOME"]}/.fixin/settings/#{splinters.name}.private#{splinters.ext}"
   ]
+  console.log files
   for file in files
     if fs.existsSync(file)
       settings = _.deepExtend settings, JSON.parse fs.readFileSync file, {encoding: "UTF-8"}
+  console.log settings
   settings


[improvement] Sorted a Goal’s TDLs by completion (5m)

--- app/assets/javascripts/houston/roadmaps/views/goal2_view.coffee
+++ app/assets/javascripts/houston/roadmaps/views/goal2_view.coffee
@@ -12,7 +12,8 @@
     @showRegression = !@goal.get('completed') && !@goal.get('closed')
     @minDate = App.parseDate(options.minDate) if options.minDate
     @targetDate = App.parseDate(options.targetDate) if options.targetDate
-    @todoLists = new TodoLists(@goal.get('todoLists'))
+    @todoLists = new TodoLists _.sortBy @goal.get('todoLists'), (tdl)->
+      [ -tdl.completedItemsCount / tdl.itemsCount, tdl.name ]
     @unattachedTodoLists = options.unattachedTodoLists
     @connectableAccounts = options.connectableAccounts
     @goal.on("change", _.bind(@render, @))


Broken circular dependency between subscriptionSvc and currentFeedSvc

--- app/assets/javascripts/ng-services/ng-SubscriptionSvc.js.coffee
+++ app/assets/javascripts/ng-services/ng-SubscriptionSvc.js.coffee
@@ -3,9 +3,9 @@
 ########################################################

 angular.module('feedbunch').service 'subscriptionSvc',
-['$rootScope', '$http', 'currentFeedSvc', 'currentFolderSvc', 'readSvc', 'folderSvc', 'timerFlagSvc',
+['$rootScope', '$http', 'readSvc', 'folderSvc', 'timerFlagSvc',
 'entriesPaginationSvc', 'openFolderSvc', 'feedsFoldersSvc', 'cleanupSvc', 'favicoSvc', 'startPageSvc',
-($rootScope, $http, currentFeedSvc, currentFolderSvc, readSvc, folderSvc, timerFlagSvc,
+($rootScope, $http, readSvc, folderSvc, timerFlagSvc,
 entriesPaginationSvc, openFolderSvc, feedsFoldersSvc, cleanupSvc, favicoSvc, startPageSvc)->

   #---------------------------------------------
@@ -28,10 +28,11 @@
         else
           entriesPaginationSvc.set_busy false
           # Show alert
+          startPageSvc.show_start_page()
           timerFlagSvc.start 'error_subscribing'

   unsubscribe: ->
-    current_feed = currentFeedSvc.get()
+    current_feed = $rootScope.current_feed
     if current_feed
       # Before deleting from the global scope, save some data we'll need later
       folder_id = current_feed.folder_id
@@ -40,7 +41,7 @@
       $rootScope.subscribed_feeds_count -= 1

       # Tell the model that no feed is currently selected.
-      currentFeedSvc.unset()
+      startPageSvc.show_start_page()

       # Remove feed from feeds list
       cleanupSvc.remove_feed current_feed.id


Add AWS.CloudWatchLogs browser integ tests

--- test/browser.spec.coffee
+++ test/browser.spec.coffee
@@ -109,6 +109,22 @@
         noData(data)
         done()

+  describe 'AWS.CloudWatchLogs', ->
+    it 'makes a request', (done) ->
+      cloudwatchlogs.describeLogGroups (err, data) ->
+        noError(err)
+        expect(Array.isArray(data.logGroups)).to.equal(true)
+        done()
+
+    it 'handles errors', (done) ->
+      params =
+        logGroupName: 'fake-group'
+        logStreamName: 'fake-stream'
+      cloudwatchlogs.getLogEvents params, (err, data) ->
+        assertError(err, 'ResourceNotFoundException')
+        noData(data)
+        done()
+
   describe 'AWS.S3', ->
     testWrite = (done, body, compareFn, svc) ->
       svc = svc || s3


Disable Atom minimap "Auto Toggle".

Disable Atom minimap "Auto Toggle".

--- home/.atom/config.cson
+++ home/.atom/config.cson
@@ -95,6 +95,7 @@
   minimap:
     absoluteMode: true
     scrollAnimation: true
+    autoToggle: false
   "atom-material-ui":
     accentColor: "Pink"
     compactTreeView: true


Tweak Atom accent color.

Tweak Atom accent color.

--- home/.atom/config.cson
+++ home/.atom/config.cson
@@ -7,9 +7,9 @@
         blue: 146
         alpha: 1
       accentColor:
-        red: 72
-        green: 255
-        blue: 168
+        red: 115
+        green: 252
+        blue: 214
         alpha: 1
       genAccent: true
     fonts:


Disabled autocomplete and atom-jshint

Disabled autocomplete and atom-jshint

--- home/.atom/config.cson
+++ home/.atom/config.cson
@@ -16,6 +16,9 @@
   ]
   'disabledPackages': [
     'term'
+    'autocomplete-snippets'
+    'autocomplete-plus'
+    'atom-jshint'
   ]
   'useReactEditor': true
 'minimap':
@@ -23,7 +26,8 @@
 'autosave': {}
 'browser-refresh':
   'saveFilesBeforeRefresh': true
-'autocomplete-plus': {}
+'autocomplete-plus':
+  'enableAutoActivation': false
 'iTunes':
   'showEqualizer (WindowResizePerformanceIssue)': false
 'highlight-line':


Map ', t t b' to symbols-tree-view toggle in atom to mirror vim.

symbols-tree-view is an atom analogue to tagbar in vim. In the
current configuration, tabbar in vim is mapped to `nmap <leader>ttb`,
with <leader> mapped to comma. This atom mapping mirrors that from
vim.

--- home/.atom/keymap.cson
+++ home/.atom/keymap.cson
@@ -34,6 +34,20 @@
   'ctrl-down': 'editor:move-line-down'

 ###############################################################################
+# map ', t t b' to symbols-tree-view toggle.
+#
+# symbols-tree-view is an atom analogue to tagbar in vim. In the current
+# configuration, tabbar in vim is mapped to `nmap <leader>ttb`, with <leader>
+# mapped to comma. This atom mapping mirrors that from vim.
+#
+# https://github.com/xndcn/symbols-tree-view
+#
+# NOTE: the default  mapping is to ctrl-alt-o
+###############################################################################
+'atom-text-editor.vim-mode.normal-mode':
+  ', t t b': 'symbols-tree-view:toggle'
+
+###############################################################################
 # vim-mode-visual-block keymappings
 #
 # As of vim-mode-visual-block@0.2.4 there are no default keymappings and


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/controllable.coffee
+++ test/controllable.coffee
@@ -32,7 +32,7 @@

   player.update()

-  equals player.I.x, 11
+  equals player.I.velocity.x, 11

   player.update()


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/controllable.coffee
+++ test/controllable.coffee
@@ -71,5 +71,8 @@

   window.keydown.left = true

+  player.update()
+
+

 module()


Small improvements submit of data on window close.

--- app/assets/javascripts/app/lib/app_init/track.js.coffee
+++ app/assets/javascripts/app/lib/app_init/track.js.coffee
@@ -25,6 +25,7 @@
     @browser = App.Browser.detection()
     @data    = []
     @url     = 'https://portal.znuny.com/api/ui'
+#    @url     = 'api/ui'

     @log( 'start', 'notice', {} )

@@ -59,7 +60,7 @@
       if settings.url.substr(0,length) isnt @url && settings.url.substr(0,6) isnt 'api/ui'
         level = 'notice'
         responseText = ''
-        if request.status > 200
+        if request.status >= 400
           level = 'error'
           responseText = request.responseText
         @log(
@@ -68,6 +69,7 @@
           {
             type:         settings.type
             dataType:     settings.dataType
+            async:        settings.async
             url:          settings.url
             data:         settings.data
             status:       request.status
@@ -80,7 +82,8 @@
       'beforeunload'
       =>
         @log( 'end', 'notice', {} )
-        @send()
+        @send(false)
+        return
     )

@@ -93,7 +96,7 @@
       data:     args
     @data.push info

-  send: =>
+  send: (async = true) =>
     return if _.isEmpty @data
     newData = _.clone( @data )
     @data = []
@@ -109,10 +112,11 @@
         newDataNew.push itemNew
       catch e
         # nothing
-
+
     App.Com.ajax(
-      type:  'POST'
-      url:   @url
+      type:   'POST'
+      url:    @url
+      async:  async
       data:   JSON.stringify(
         track_id: @trackId
         log:      newDataNew
@@ -132,6 +136,9 @@
     @data

 `
+window.onerror = function(errorMsg, url, lineNumber) {
+  console.error(errorMsg + " - in " + url + ", line " + lineNumber);
+};

 (function() {
   var console = window.console


Updated Cart.load to handle bill/ship address

--- app/assets/javascripts/sprangular/resources/cart.coffee
+++ app/assets/javascripts/sprangular/resources/cart.coffee
@@ -15,6 +15,16 @@
       order.taxTotal = data.tax_total
       order.shipTotal = data.ship_total
       order.total = data.total
+      order.shipToBillAddress = true
+
+      if data.bill_address
+        order.billingAddress = Sprangular.extend(data.bill_address, Sprangular.Address)
+
+      if data.ship_address
+        order.shippingAddress = Sprangular.extend(data.ship_address, Sprangular.Address)
+
+      if order.shippingAddress && order.billingAddress && order.billingAddress.id != order.shippingAddress.id
+        order.shipToBillAddress = false

       products = Sprangular.extend(data.products, Sprangular.Product)


change color theme, use fira code

change color theme, use fira code

--- atom.symlink/config.cson
+++ atom.symlink/config.cson
@@ -30,11 +30,11 @@
     telemetryConsent: "no"
     themes: [
       "atom-material-ui"
-      "atom-material-syntax"
+      "one-dark-syntax"
     ]
   docblockr: {}
   editor:
-    fontFamily: "Hack"
+    fontFamily: "Fira Code"
     fontSize: 15
     invisibles: {}
     preferredLineLength: 130


Atom config updates

Atom config updates

--- atom.symlink/config.cson
+++ atom.symlink/config.cson
@@ -7,21 +7,14 @@
   core:
     disabledPackages: [
       "Atom-Syntax-highlighting-for-Sass"
-      "autocomplete-php"
       "symbols-view"
       "regex-railroad-diagram"
       "busy"
       "linter-stylelint"
-      "linter-eslint"
-      "php-integrator-call-tips-legacy-php56"
-      "php-integrator-tooltips-legacy-php56"
       "whitespace"
       "hard-wrap"
+      "linter-php"
       "linter-phpcs"
-      "php-integrator-annotations"
-      "php-integrator-navigation"
-      "php-integrator-tooltips"
-      "php-integrator-call-tips"
     ]
     ignoredNames: [
       ".git"
@@ -65,6 +58,7 @@
   "linter-phpcs":
     autoConfigSearch: false
     codeStandardOrConfigFile: "/Users/montchr/dev/spiritedmedia.dev/public/phpcs.ruleset.xml"
+    disableWhenNoConfigFile: true
     executablePath: "/Users/montchr/dev/spiritedmedia.dev/public/vendor/bin/phpcs"
   "linter-phpmd": {}
   "linter-ui-default":
@@ -103,10 +97,9 @@
     hideVcsIgnoredFiles: true
   welcome:
     showOnStartup: false
-".blade.html.php.text":
+".html.php.text":
   editor:
-    commentEnd: " --}}"
-    commentStart: "{{-- "
+    tabLength: 4
 ".hyperlink.text":
   editor:
     softWrapHangingIndent: 4


Add linter to config.cson

Add linter to config.cson

--- atom.symlink/config.cson
+++ atom.symlink/config.cson
@@ -17,6 +17,7 @@
   "exception-reporting":
     userId: "f66e1044-d904-5c31-3ec2-1cb9fec28b42"
   linter: {}
+  "linter-jshint": {}
   minimap:
     plugins:
       "highlight-selected": true


Change atom seti theme colour to steel

Change atom seti theme colour to steel

--- atom.symlink/config.cson
+++ atom.symlink/config.cson
@@ -22,6 +22,9 @@
   "linter-pylint":
     executable: "/usr/local/bin/pylint"
   "one-dark-ui": {}
+  "seti-ui":
+    compactView: true
+    themeColor: "Steel"
   "vim-mode": {}
   welcome:
     showOnStartup: false


Atom: switch keymap to vim-mode-plus

Atom: switch keymap to vim-mode-plus

--- atom.symlink/keymap.cson
+++ atom.symlink/keymap.cson
@@ -1,374 +1,3 @@
-'atom-text-editor.vim-mode':
-  'escape': 'vim-mode:reset-normal-mode'
-  'ctrl-c': 'vim-mode:reset-normal-mode'
-  'ctrl-[': 'vim-mode:reset-normal-mode'
-
-'atom-text-editor.vim-mode:not(.normal-mode)':
-  'escape': 'vim-mode:activate-normal-mode'
-  'ctrl-[': 'vim-mode:activate-normal-mode'
-
-'.platform-darwin atom-text-editor.vim-mode:not(.normal-mode)':
-  'ctrl-c': 'vim-mode:activate-normal-mode'
-
-'atom-text-editor.vim-mode.insert-mode':
-  'ctrl-u': 'editor:delete-to-beginning-of-line'
-  'ctrl-y': 'vim-mode:copy-from-line-above'
-  # disabled for compatibility with the common binding for going to the end of the line
-  # 'ctrl-e': 'vim-mode:copy-from-line-below'
-
-  'ctrl-r a': 'vim-mode:insert-mode-put'
-  'ctrl-r b': 'vim-mode:insert-mode-put'
-  'ctrl-r c': 'vim-mode:insert-mode-put'
-  'ctrl-r d': 'vim-mode:insert-mode-put'
-  'ctrl-r e': 'vim-mode:insert-mode-put'
-  'ctrl-r f': 'vim-mode:insert-mode-put'
-  'ctrl-r g': 'vim-mode:insert-mode-put'
-  'ctrl-r h': 'vim-mode:insert-mode-put'
-  'ctrl-r i': 'vim-mode:insert-mode-put'
-  'ctrl-r j': 'vim-mode:insert-mode-put'
-  'ctrl-r k': 'vim-mode:insert-mode-put'
-  'ctrl-r l': 'vim-mode:insert-mode-put'
-  'ctrl-r m': 'vim-mode:insert-mode-put'
-  'ctrl-r n': 'vim-mode:insert-mode-put'
-  'ctrl-r o': 'vim-mode:insert-mode-put'
-  'ctrl-r p': 'vim-mode:insert-mode-put'
-  'ctrl-r q': 'vim-mode:insert-mode-put'
-  'ctrl-r r': 'vim-mode:insert-mode-put'
-  'ctrl-r s': 'vim-mode:insert-mode-put'
-  'ctrl-r t': 'vim-mode:insert-mode-put'
-  'ctrl-r u': 'vim-mode:insert-mode-put'
-  'ctrl-r v': 'vim-mode:insert-mode-put'
-  'ctrl-r w': 'vim-mode:insert-mode-put'
-  'ctrl-r x': 'vim-mode:insert-mode-put'
-  'ctrl-r y': 'vim-mode:insert-mode-put'
-  'ctrl-r z': 'vim-mode:insert-mode-put'
-  'ctrl-r A': 'vim-mode:insert-mode-put'
-  'ctrl-r B': 'vim-mode:insert-mode-put'
-  'ctrl-r C': 'vim-mode:insert-mode-put'
-  'ctrl-r D': 'vim-mode:insert-mode-put'
-  'ctrl-r E': 'vim-mode:insert-mode-put'
-  'ctrl-r F': 'vim-mode:insert-mode-put'
-  'ctrl-r G': 'vim-mode:insert-mode-put'
-  'ctrl-r H': 'vim-mode:insert-mode-put'
-  'ctrl-r I': 'vim-mode:insert-mode-put'
-  'ctrl-r J': 'vim-mode:insert-mode-put'
-  'ctrl-r K': 'vim-mode:insert-mode-put'
-  'ctrl-r L': 'vim-mode:insert-mode-put'
-  'ctrl-r M': 'vim-mode:insert-mode-put'
-  'ctrl-r N': 'vim-mode:insert-mode-put'
-  'ctrl-r O': 'vim-mode:insert-mode-put'
-  'ctrl-r P': 'vim-mode:insert-mode-put'
-  'ctrl-r Q': 'vim-mode:insert-mode-put'
-  'ctrl-r R': 'vim-mode:insert-mode-put'
-  'ctrl-r S': 'vim-mode:insert-mode-put'
-  'ctrl-r T': 'vim-mode:insert-mode-put'
-  'ctrl-r U': 'vim-mode:insert-mode-put'
-  'ctrl-r V': 'vim-mode:insert-mode-put'
-  'ctrl-r W': 'vim-mode:insert-mode-put'
-  'ctrl-r X': 'vim-mode:insert-mode-put'
-  'ctrl-r Y': 'vim-mode:insert-mode-put'
-  'ctrl-r Z': 'vim-mode:insert-mode-put'
-  'ctrl-r *': 'vim-mode:insert-mode-put'
-  'ctrl-r +': 'vim-mode:insert-mode-put'
-  'ctrl-r %': 'vim-mode:insert-mode-put'
-  'ctrl-r _': 'vim-mode:insert-mode-put'
-  'ctrl-r "': 'vim-mode:insert-mode-put'
-  'ctrl-r escape': 'abort!'
-
-'atom-text-editor.vim-mode.replace-mode':
-  'backspace': 'vim-mode:replace-mode-backspace'
-
-'atom-text-editor.vim-mode:not(.insert-mode)':
-  'h': 'vim-mode:move-left'
-  'left': 'vim-mode:move-left'
-  'backspace': 'core:move-left'
-  'l': 'vim-mode:move-right'
-  'space': 'vim-mode:move-right'
-  'right': 'vim-mode:move-right'
-  'k': 'vim-mode:move-up'
-  'up': 'vim-mode:move-up'
-  'j': 'vim-mode:move-down'
-  'down': 'vim-mode:move-down'
-
-  'w': 'vim-mode:move-to-next-word'
-  'W': 'vim-mode:move-to-next-whole-word'
-  'e': 'vim-mode:move-to-end-of-word'
-  'E': 'vim-mode:move-to-end-of-whole-word'
-  'b': 'vim-mode:move-to-previous-word'
-  'B': 'vim-mode:move-to-previous-whole-word',
-  ')': 'vim-mode:move-to-next-sentence',
-  '(': 'vim-mode:move-to-previous-sentence',
-  '}': 'vim-mode:move-to-next-paragraph'
-  '{': 'vim-mode:move-to-previous-paragraph'
-  '0': 'vim-mode:move-to-beginning-of-line'
-  '^': 'vim-mode:move-to-first-character-of-line'
-  '_': 'vim-mode:move-to-first-character-of-line-and-down'
-  '$': 'vim-mode:move-to-last-character-of-line'
-  'g _': 'vim-mode:move-to-last-nonblank-character-of-line-and-down'
-  '-': 'vim-mode:move-to-first-character-of-line-up'
-  '+': 'vim-mode:move-to-first-character-of-line-down'
-  'enter': 'vim-mode:move-to-first-character-of-line-down'
-  'g g': 'vim-mode:move-to-start-of-file'
-  'ctrl-u': 'vim-mode:scroll-half-screen-up'
-  'ctrl-b': 'vim-mode:scroll-full-screen-up'
-  'ctrl-d': 'vim-mode:scroll-half-screen-down'
-  'ctrl-f': 'vim-mode:scroll-full-screen-down'
-  'ctrl-e': 'vim-mode:scroll-down'
-  'ctrl-y': 'vim-mode:scroll-up'
-  'z enter': 'vim-mode:scroll-cursor-to-top'
-  'z t': 'vim-mode:scroll-cursor-to-top-leave'
-  'z .': 'vim-mode:scroll-cursor-to-middle'
-  'z z': 'vim-mode:scroll-cursor-to-middle-leave'
-  'z -': 'vim-mode:scroll-cursor-to-bottom'
-  'z b': 'vim-mode:scroll-cursor-to-bottom-leave'
-  'z s': 'vim-mode:scroll-cursor-to-left'
-  'z e': 'vim-mode:scroll-cursor-to-right'
-  'z M': 'editor:fold-all'
-  'z R': 'editor:unfold-all'
-  'z c': 'editor:fold-current-row'
-  'z o': 'editor:unfold-current-row'
-  'G': 'vim-mode:move-to-line'
-  'H': 'vim-mode:move-to-top-of-screen'
-  'L': 'vim-mode:move-to-bottom-of-screen'
-  'M': 'vim-mode:move-to-middle-of-screen'
-
-  'd': 'vim-mode:delete'
-  'D': 'vim-mode:delete-to-last-character-of-line'
-  'c': 'vim-mode:change'
-  'C': 'vim-mode:change-to-last-character-of-line'
-  'S': 'vim-mode:substitute-line'
-  'r': 'vim-mode:replace'
-  'I': 'vim-mode:insert-at-beginning-of-line'
-  '>': 'vim-mode:indent'
-  '<': 'vim-mode:outdent'
-  '=': 'vim-mode:auto-indent'
-  'J': 'vim-mode:join'
-
-  'y': 'vim-mode:yank'
-  'Y': 'vim-mode:yank-line'
-  'P': 'vim-mode:put-before'
-  'p': 'vim-mode:put-after'
-
-  'g ~': 'vim-mode:toggle-case'
-  'g U': 'vim-mode:upper-case'
-  'g u': 'vim-mode:lower-case'
-  '~': 'vim-mode:toggle-case-now'
-
-  'g t': 'pane:show-next-item'
-  'g T': 'pane:show-previous-item'
-
-  'm': 'vim-mode:mark'
-  '`': 'vim-mode:move-to-mark-literal'
-  '\'': 'vim-mode:move-to-mark'
-
-  'f': 'vim-mode:find'
-  'F': 'vim-mode:find-backwards'
-  't': 'vim-mode:till'
-  'T': 'vim-mode:till-backwards'
-  ';': 'vim-mode:repeat-find'
-  ',': 'vim-mode:repeat-find-reverse'
-
-  '/': 'vim-mode:search'
-  '?': 'vim-mode:reverse-search'
-  '*': 'vim-mode:search-current-word'
-  '#': 'vim-mode:reverse-search-current-word'
-  'n': 'vim-mode:repeat-search'
-  'N': 'vim-mode:repeat-search-backwards'
-
-  '%': 'vim-mode:bracket-matching-motion'
-
-  '1': 'vim-mode:repeat-prefix'
-  '2': 'vim-mode:repeat-prefix'
-  '3': 'vim-mode:repeat-prefix'
-  '4': 'vim-mode:repeat-prefix'
-  '5': 'vim-mode:repeat-prefix'
-  '6': 'vim-mode:repeat-prefix'
-  '7': 'vim-mode:repeat-prefix'
-  '8': 'vim-mode:repeat-prefix'
-  '9': 'vim-mode:repeat-prefix'
-
-'atom-text-editor.vim-mode.normal-mode':
-  'i': 'vim-mode:activate-insert-mode'
-  'insert': 'vim-mode:activate-insert-mode'
-  'R': 'vim-mode:activate-replace-mode'
-  'v': 'vim-mode:activate-characterwise-visual-mode'
-  'V': 'vim-mode:activate-linewise-visual-mode'
-  'ctrl-v': 'vim-mode:activate-blockwise-visual-mode'
-
-  'u': 'vim-mode:undo'
-  'ctrl-r': 'core:redo'
-
-  'O': 'vim-mode:insert-above-with-newline'
-  'o': 'vim-mode:insert-below-with-newline'
-  'a': 'vim-mode:insert-after'
-  'A': 'vim-mode:insert-after-end-of-line'
-  'x': 'vim-mode:delete-right'
-  'X': 'vim-mode:delete-left'
-  's': 'vim-mode:substitute'
-  '.': 'vim-mode:repeat'
-
-  'ctrl-]': 'symbols-view:go-to-declaration'
-
-  'ctrl-a': 'vim-mode:increase'
-  'ctrl-x': 'vim-mode:decrease'
-
-  '" a': 'vim-mode:register-prefix'
-  '" b': 'vim-mode:register-prefix'
-  '" c': 'vim-mode:register-prefix'
-  '" d': 'vim-mode:register-prefix'
-  '" e': 'vim-mode:register-prefix'
-  '" f': 'vim-mode:register-prefix'
-  '" g': 'vim-mode:register-prefix'
-  '" h': 'vim-mode:register-prefix'
-  '" i': 'vim-mode:register-prefix'
-  '" j': 'vim-mode:register-prefix'
-  '" k': 'vim-mode:register-prefix'
-  '" l': 'vim-mode:register-prefix'
-  '" m': 'vim-mode:register-prefix'
-  '" n': 'vim-mode:register-prefix'
-  '" o': 'vim-mode:register-prefix'
-  '" p': 'vim-mode:register-prefix'
-  '" q': 'vim-mode:register-prefix'
-  '" r': 'vim-mode:register-prefix'
-  '" s': 'vim-mode:register-prefix'
-  '" t': 'vim-mode:register-prefix'
-  '" u': 'vim-mode:register-prefix'
-  '" v': 'vim-mode:register-prefix'
-  '" w': 'vim-mode:register-prefix'
-  '" x': 'vim-mode:register-prefix'
-  '" y': 'vim-mode:register-prefix'
-  '" z': 'vim-mode:register-prefix'
-  '" A': 'vim-mode:register-prefix'
-  '" B': 'vim-mode:register-prefix'
-  '" C': 'vim-mode:register-prefix'
-  '" D': 'vim-mode:register-prefix'
-  '" E': 'vim-mode:register-prefix'
-  '" F': 'vim-mode:register-prefix'
-  '" G': 'vim-mode:register-prefix'
-  '" H': 'vim-mode:register-prefix'
-  '" I': 'vim-mode:register-prefix'
-  '" J': 'vim-mode:register-prefix'
-  '" K': 'vim-mode:register-prefix'
-  '" L': 'vim-mode:register-prefix'
-  '" M': 'vim-mode:register-prefix'
-  '" N': 'vim-mode:register-prefix'
-  '" O': 'vim-mode:register-prefix'
-  '" P': 'vim-mode:register-prefix'
-  '" Q': 'vim-mode:register-prefix'
-  '" R': 'vim-mode:register-prefix'
-  '" S': 'vim-mode:register-prefix'
-  '" T': 'vim-mode:register-prefix'
-  '" U': 'vim-mode:register-prefix'
-  '" V': 'vim-mode:register-prefix'
-  '" W': 'vim-mode:register-prefix'
-  '" X': 'vim-mode:register-prefix'
-  '" Y': 'vim-mode:register-prefix'
-  '" Z': 'vim-mode:register-prefix'
-  '" *': 'vim-mode:register-prefix'
-  '" +': 'vim-mode:register-prefix'
-  '" %': 'vim-mode:register-prefix'
-  '" _': 'vim-mode:register-prefix'
-
-'atom-text-editor.vim-mode.operator-pending-mode, atom-text-editor.vim-mode.visual-mode':
-  'i w': 'vim-mode:select-inside-word'
-  'i W': 'vim-mode:select-inside-whole-word'
-  'i "': 'vim-mode:select-inside-double-quotes'
-  'i \'': 'vim-mode:select-inside-single-quotes'
-  'i `': 'vim-mode:select-inside-back-ticks'
-  'i {': 'vim-mode:select-inside-curly-brackets'
-  'i }': 'vim-mode:select-inside-curly-brackets'
-  'i B': 'vim-mode:select-inside-curly-brackets'
-  'i <': 'vim-mode:select-inside-angle-brackets'
-  'i >': 'vim-mode:select-inside-angle-brackets'
-  'i t': 'vim-mode:select-inside-tags'
-  'i [': 'vim-mode:select-inside-square-brackets'
-  'i ]': 'vim-mode:select-inside-square-brackets'
-  'i (': 'vim-mode:select-inside-parentheses'
-  'i )': 'vim-mode:select-inside-parentheses'
-  'i b': 'vim-mode:select-inside-parentheses'
-  'i p': 'vim-mode:select-inside-paragraph'
-  'a w': 'vim-mode:select-a-word'
-  'a W': 'vim-mode:select-a-whole-word'
-  'a "': 'vim-mode:select-around-double-quotes'
-  'a \'': 'vim-mode:select-around-single-quotes'
-  'a `': 'vim-mode:select-around-back-ticks'
-  'a {': 'vim-mode:select-around-curly-brackets'
-  'a }': 'vim-mode:select-around-curly-brackets'
-  'a B': 'vim-mode:select-around-curly-brackets'
-  'a <': 'vim-mode:select-around-angle-brackets'
-  'a >': 'vim-mode:select-around-angle-brackets'
-  'a [': 'vim-mode:select-around-square-brackets'
-  'a ]': 'vim-mode:select-around-square-brackets'
-  'a (': 'vim-mode:select-around-parentheses'
-  'a )': 'vim-mode:select-around-parentheses'
-  'a b': 'vim-mode:select-around-parentheses'
-  'a p': 'vim-mode:select-around-paragraph'
-  'x': 'vim-mode:reset-normal-mode'
-
-'atom-text-editor.vim-mode.visual-mode':
-  'x': 'vim-mode:delete'
-  's': 'vim-mode:change'
-  'o': 'vim-mode:reverse-selections'
-  'U': 'vim-mode:upper-case'
-  'u': 'vim-mode:lower-case'
-
-  'v':      'vim-mode:activate-characterwise-visual-mode'
-  'V':      'vim-mode:activate-linewise-visual-mode'
-  'ctrl-v': 'vim-mode:activate-blockwise-visual-mode'
-
-  '" a': 'vim-mode:register-prefix'
-  '" b': 'vim-mode:register-prefix'
-  '" c': 'vim-mode:register-prefix'
-  '" d': 'vim-mode:register-prefix'
-  '" e': 'vim-mode:register-prefix'
-  '" f': 'vim-mode:register-prefix'
-  '" g': 'vim-mode:register-prefix'
-  '" h': 'vim-mode:register-prefix'
-  '" i': 'vim-mode:register-prefix'
-  '" j': 'vim-mode:register-prefix'
-  '" k': 'vim-mode:register-prefix'
-  '" l': 'vim-mode:register-prefix'
-  '" m': 'vim-mode:register-prefix'
-  '" n': 'vim-mode:register-prefix'
-  '" o': 'vim-mode:register-prefix'
-  '" p': 'vim-mode:register-prefix'
-  '" q': 'vim-mode:register-prefix'
-  '" r': 'vim-mode:register-prefix'
-  '" s': 'vim-mode:register-prefix'
-  '" t': 'vim-mode:register-prefix'
-  '" u': 'vim-mode:register-prefix'
-  '" v': 'vim-mode:register-prefix'
-  '" w': 'vim-mode:register-prefix'
-  '" x': 'vim-mode:register-prefix'
-  '" y': 'vim-mode:register-prefix'
-  '" z': 'vim-mode:register-prefix'
-  '" A': 'vim-mode:register-prefix'
-  '" B': 'vim-mode:register-prefix'
-  '" C': 'vim-mode:register-prefix'
-  '" D': 'vim-mode:register-prefix'
-  '" E': 'vim-mode:register-prefix'
-  '" F': 'vim-mode:register-prefix'
-  '" G': 'vim-mode:register-prefix'
-  '" H': 'vim-mode:register-prefix'
-  '" I': 'vim-mode:register-prefix'
-  '" J': 'vim-mode:register-prefix'
-  '" K': 'vim-mode:register-prefix'
-  '" L': 'vim-mode:register-prefix'
-  '" M': 'vim-mode:register-prefix'
-  '" N': 'vim-mode:register-prefix'
-  '" O': 'vim-mode:register-prefix'
-  '" P': 'vim-mode:register-prefix'
-  '" Q': 'vim-mode:register-prefix'
-  '" R': 'vim-mode:register-prefix'
-  '" S': 'vim-mode:register-prefix'
-  '" T': 'vim-mode:register-prefix'
-  '" U': 'vim-mode:register-prefix'
-  '" V': 'vim-mode:register-prefix'
-  '" W': 'vim-mode:register-prefix'
-  '" X': 'vim-mode:register-prefix'
-  '" Y': 'vim-mode:register-prefix'
-  '" Z': 'vim-mode:register-prefix'
-  '" *': 'vim-mode:register-prefix'
-  '" +': 'vim-mode:register-prefix'
-  '" %': 'vim-mode:register-prefix'
-  '" _': 'vim-mode:register-prefix'
+'atom-text-editor.vim-mode-plus:not(.insert-mode)':
+  'j': 'vim-mode-plus:move-down-screen'
+  'k': 'vim-mode-plus:move-up-screen'


Convert to lodash

Convert to lodash

--- shared/src/components/scroll-to-mixin.cjsx
+++ shared/src/components/scroll-to-mixin.cjsx
@@ -1,6 +1,11 @@
 React = require 'react'
 ReactDOM = require 'react-dom'
-_ = require 'underscore'
+
+extend  = require 'lodash/extend'
+isEmpty = require 'lodash/isEmpty'
+delay   = require 'lodash/delay'
+result  = require 'lodash/result'
+

 # Note that the GetPositionMixin methods are called directly rather than mixing it in
 # since we're a mixin ourselves our consumers also include GetPosition and it causes
@@ -32,7 +37,7 @@
   _scrollingTargetDOM: -> @scrollingTargetDOM?() or ReactDOM.findDOMNode(@)

   scrollToSelector: (selector, options) ->
-    options = _.extend({updateHistory: true, unlessInView: false}, options)
+    options = extend({updateHistory: true, unlessInView: false}, options)

     el = @getElement(selector)
     return false unless el
@@ -52,7 +57,7 @@
     visibleHeight > height / 2

   getElement: (selector) ->
-    return if _.isEmpty(selector)
+    return if isEmpty(selector)
     @_scrollingTargetDOM().querySelector(selector)

   _onBeforeScroll: (el) ->
@@ -61,7 +66,7 @@

   _onAfterScroll: (el, options) ->
     if el?.classList?.contains('target-scroll')
-      _.delay(el.classList.remove.bind(el.classList, 'target-scroll'), 150)
+      delay(el.classList.remove.bind(el.classList, 'target-scroll'), 150)
     @props.windowImpl.history.pushState(null, null, "##{el.id}") if options.updateHistory
     @onAfterScroll?(el)

@@ -83,7 +88,7 @@

   scrollToElement: (el, options = {} ) ->
     win       = @props.windowImpl
-    endPos    = @_desiredTopPosition(el)
+    endPos    = @_desiredTopPosition(el, options)

     if options.immediate is true
       win.scroll(0, endPos)
@@ -92,8 +97,8 @@

     startPos  = win.pageYOffset
     startTime = Date.now()
-    duration  = _.result(@, 'getScrollDuration', DEFAULT_DURATION)
-    requestAnimationFrame = win.requestAnimationFrame or _.defer
+    duration  = result(@, 'getScrollDuration', DEFAULT_DURATION)
+    requestAnimationFrame = win.requestAnimationFrame or delay
     options.attemptNumber ||= 0

     step = =>


Throw errors on failures

Throw errors on failures

--- src/hull.coffee
+++ src/hull.coffee
@@ -82,11 +82,11 @@

 # Wraps config failure
 onConfigFailure = (err)->
-  throw new Error(err)
+  throw err

 # Wraps init failure
 onInitFailure = (err)->
-  throw new Error("We couldn't load the Hull server after trying for 30 seconds. Something about connectivity ?")
+  throw err

 # Parse the tracked events configuration and standardize it.
 getTrackConfig = (cfg)->


Drop error prefix from failure message

Drop error prefix from failure message

--- src/init.coffee
+++ src/init.coffee
@@ -40,7 +40,7 @@
       @generateFromTemplate(themePath, templatePath)
       callback()
     else
-      callback('Error: You must specify either --package or --theme to `apm init`')
+      callback('You must specify either --package or --theme to `apm init`')

   generateFromTemplate: (packagePath, templatePath) ->
     packageName = path.basename(packagePath)


Fixed armor giving

Fixed armor giving

--- init.coffee
+++ init.coffee
@@ -332,12 +332,14 @@
 	console.log chalk.green 'ok'

 	process.stdout.write '  '+'Fetching users'+'... '
-	users = query.all 'SELECT id, username FROM uniusers', []
+	users = query.all(
+		'SELECT username, characters.id AS character_id '+
+		'FROM uniusers, characters WHERE uniusers.id = characters.player')
 	console.log chalk.green "found #{users.length}"
 	for user in users
 		process.stdout.write '  '+"Giving some armor to #{user.username}"+'... '
 		for item in prototypes
-			query 'INSERT INTO armor (prototype, owner, strength) VALUES ($1,$2,$3)', [item[0], user.id, item[3]]
+			query 'INSERT INTO armor (prototype, owner, strength) VALUES ($1,$2,$3)', [item[0], user.character_id, item[3]]
 		console.log chalk.green 'ok'


add latest version preview to history modal

the latest versions snippet model is copied from the snippet model of
the version that is on the page. This has to be done to make sure they
have different id's and the instance in the history modal would not
overwrite the pages instances entry in the mapMediators registry.

--- app/scripts/controllers/history_modal_controller.coffee
+++ app/scripts/controllers/history_modal_controller.coffee
@@ -1,7 +1,8 @@
 angular.module('ldEditor').controller 'HistoryModalController',
 class HistoryModalController

-  constructor: (@$scope, @$modalInstance, @snippet, @documentService, @editorService, @uiStateService) ->
+  constructor: (@$scope, @$modalInstance, @snippet, @documentService, @$timeout,
+    @editorService, @uiStateService, @angularTemplateService, @mapMediatorService) ->
     @$scope.close = (event) => @close(event)
     @$scope.snippet = @snippet
     @documentService.getHistory(@editorService.getCurrentDocument().id, @snippet.id).then (history) =>
@@ -11,8 +12,27 @@
     @uiStateService.set('autosave', false)

+    @$timeout => # we need a timeout to make sure the modal html template is ready
+      @setupLatestVersion()
+
+
+  setupLatestVersion: ->
+    $previewRoot = $('.upfront-snippet-history .latest-preview')
+    @latestVersionSnippet = @snippet.copy(doc.document.design)
+    @angularTemplateService.insertTemplateInstance @latestVersionSnippet, $previewRoot, new ChoroplethMap
+      id: @latestVersionSnippet.id
+      mapMediatorService: @mapMediatorService
+
+
+  removeLatestVersionInstance: ->
+    @angularTemplateService.removeAngularTemplate(@latestVersionSnippet)
+    delete @latestVersionSnippet
+
+
   close: (event) ->
-    # TODO: call save
+    # TODO: put the changed data from @latestVersionSnippet to @snippet and save
+
+    @removeLatestVersionInstance()
     # re-enable autosave
     @uiStateService.set('autosave', {})
     @$modalInstance.dismiss('close')


:racehorse: Use an EditSession instead of an Editor in specs

--- spec/whitespace-spec.coffee
+++ spec/whitespace-spec.coffee
@@ -8,12 +8,11 @@
     path = "/tmp/atom-whitespace.txt"
     fsUtils.writeSync(path, "")
     window.rootView = new RootView
-    rootView.open(path)
+    editor = project.open(path)

     atom.activatePackage('whitespace')

     rootView.focus()
-    editor = rootView.getActiveView()

   afterEach ->
     fsUtils.remove(path) if fsUtils.exists(path)
@@ -29,7 +28,7 @@
     expect(editor.getText()).toBe "foo\nbar\n\nbaz"

     # works for buffers that are opened after extension is initialized
-    rootView.open(require.resolve('fixtures/sample.txt'))
+    editor = project.open(require.resolve('fixtures/sample.txt'))
     editor.moveCursorToEndOfLine()
     editor.insertText("           ")

@@ -94,19 +93,19 @@
       grammar = syntax.addGrammar.argsForCall[0][0]

     it "trims GFM text with a single space", ->
-      editor.activeEditSession.setGrammar(grammar)
+      editor.setGrammar(grammar)
       editor.insertText "foo \nline break!"
       editor.getBuffer().save()
       expect(editor.getText()).toBe "foo\nline break!\n"

     it "leaves GFM text with double spaces alone", ->
-      editor.activeEditSession.setGrammar(grammar)
+      editor.setGrammar(grammar)
       editor.insertText "foo  \nline break!"
       editor.getBuffer().save()
       expect(editor.getText()).toBe "foo  \nline break!\n"

     it "trims GFM text with a more than two spaces", ->
-      editor.activeEditSession.setGrammar(grammar)
+      editor.setGrammar(grammar)
       editor.insertText "foo   \nline break!"
       editor.getBuffer().save()
       expect(editor.getText()).toBe "foo\nline break!\n"


Fix tests for https://github.com/atom/atom/pull/13795

Signed-off-by: Antonio Scandurra <0e17b4d558b07b12cd221e35c438be378ba118cf@github.com>

--- spec/wrap-guide-spec.coffee
+++ spec/wrap-guide-spec.coffee
@@ -83,14 +83,21 @@

   describe "when the editor's scroll left changes", ->
     it "updates the wrap guide position to a relative position on screen", ->
+      spyOn(editorElement.component, 'measureDimensions').andCallThrough()
       editor.setText("a long line which causes the editor to scroll")
       editorElement.style.width = "100px"
-      atom.views.performDocumentPoll()
-      initial = getLeftPosition(wrapGuide)
-      expect(initial).toBeGreaterThan(0)
-      editorElement.setScrollLeft(10)
-      expect(getLeftPosition(wrapGuide)).toBe(initial - 10)
-      expect(wrapGuide).toBeVisible()
+
+      if atom.views.performDocumentPoll # TODO: Remove this branch once atom.views.performDocumentPoll is gone
+        atom.views.performDocumentPoll()
+      else
+        waitsFor -> editorElement.component.measureDimensions.callCount > 0
+
+      runs ->
+        initial = getLeftPosition(wrapGuide)
+        expect(initial).toBeGreaterThan(0)
+        editorElement.setScrollLeft(10)
+        expect(getLeftPosition(wrapGuide)).toBe(initial - 10)
+        expect(wrapGuide).toBeVisible()

   describe "when the editor's grammar changes", ->
     it "updates the wrap guide position", ->


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/active_bounds.coffee
+++ test/active_bounds.coffee
@@ -1,6 +1,6 @@
 module "ActiveBounds"

-window.App = {}
+window.App ||= {}

 App.width = 640
 App.height = 480


テスト修正

テスト修正

--- test/calc-box.spec.coffee
+++ test/calc-box.spec.coffee
@@ -140,9 +140,9 @@
     should.exists r
     r.should.be.true

-    box.width.should.eql 100
+    box.width.should.eql 90
     box.height.should.eql 200
-    box.depth.should.eql 290
+    box.depth.should.eql 300

 describe '複数個の荷物を使ったテスト', ->
   it '荷物を入れたあとに箱の残り容量が減る', ->