Better handling of loading extra fields.

--- src/control.coffee
+++ src/control.coffee
@@ -64,9 +64,11 @@
             else if not @units.contains unit
                 @units.add unit
                 @units.trigger 'reset', @units
-            department = unit.get 'department'
-            municipality = unit.get 'municipality'
-            if department? and typeof department == 'object' and municipality? and typeof municipality == 'object'
+            hasObject = (unit, key) ->
+                o = unit.get(key)
+                o? and typeof o == 'object'
+            requiredObjects = ['department', 'municipality', 'services']
+            unless _(requiredObjects).find((x)->!hasObject(unit, x))
                 @selectedUnits.trigger 'reset', @selectedUnits
                 sm.resolveImmediately()
             else
@@ -133,7 +135,7 @@
             unit = new Models.Unit id: id
             unit.fetch
                 data:
-                    include: 'department,municipality'
+                    include: 'department,municipality,services'
                 success: =>
                     @setUnit unit
                     @selectUnit unit


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/game_state_camera.coffee
+++ src/game_state_camera.coffee
@@ -5,11 +5,23 @@
     self.cameras().each (camera) ->
       camera.trigger('afterUpdate')

+  self.bind 'draw', (canvas) ->
+    self.cameras().invoke 'trigger', 'draw', canvas, self.objects()
+
   self.bind 'overlay', (canvas) ->
     self.cameras().each (camera) ->
       camera.trigger 'overlay', canvas

   return {
-    addCamera: ->
-      ;
+    addCamera: (data) ->
+      cameras.push(Camera(data))
+    ###*
+    Returns the array of camera objects.
+
+    @name cameras
+    @methodOf Engine#
+    @returns {Array}
+    ###
+    cameras: ->
+      return cameras
   }


pass atom env to server

pass atom env to server

--- lib/connection/process/server.coffee
+++ lib/connection/process/server.coffee
@@ -32,7 +32,9 @@
     @removeSocket('juno-server').then =>
       new Promise (resolve, reject) =>
         console.info 'booting julia server'
-        proc = child_process.fork path.join(__dirname, 'boot.js'), detached: true
+        proc = child_process.fork path.join(__dirname, 'boot.js'),
+          detached: true
+          env: process.env
         proc.on 'message', (x) ->
           if x == 'ready' then resolve()
           else console.log 'julia server:', x


bubbles: organizations were filtered out as nodes

--- app/charts/bubbles.coffee
+++ app/charts/bubbles.coffee
@@ -454,7 +454,7 @@
     nodesWithFlows = data.nodes.filter(
       (node) -> hasFlows(node, "inbound") or hasFlows(node, "outbound")
     )
-    .filter (node) -> projectNode(node)?
+    #.filter (node) -> projectNode(node)?

     #nodesWithLocation = nodesWithFlows.filter (node) -> projectNode(node)?


Make sure the next task exists

Make sure the next task exists

--- app/classifier/index.cjsx
+++ app/classifier/index.cjsx
@@ -62,9 +62,10 @@
               when 'multiple'
                 waitingForAnswer = currentTask.required and currentAnnotation.value.length is 0

-            nextTaskKey = if currentTask.type is 'single' and currentAnswer?
+            # If the next task key exists, make sure the task it points to actually exists.
+            nextTaskKey = if currentTask.type is 'single' and currentAnswer? and @props.workflow.tasks[currentAnswer.next]?
               currentAnswer.next
-            else
+            else if @props.workflow.tasks[currentTask.next]?
               currentTask.next

             <div className="task-container">


Rename update.

Rename update.

--- app/assets/javascripts/_site.js.coffee
+++ app/assets/javascripts/_site.js.coffee
@@ -457,6 +457,10 @@

         event.preventDefault()
         event.stopPropagation()
+
+      $(document).on 'click', '.rename-feed-input', (event, xhr) ->
+        if !$(@).hasClass('disabled')
+          return false

     changeSearchSort: (sort) ->
       $(document).on 'click', '[data-sort-option]', ->


Simon again

Simon again

Whoops

--- app/data/users/simon.cson
+++ app/data/users/simon.cson
@@ -1,6 +1,6 @@
 name: "Simon Bramble"
 job_title: "Content Designer"
-grade: SEO
+grade: "SEO"
 salary: "£50,000"
 pay_date: "29 February 2016"
 annual_leave: 25


fixed rotationAndTranslation

fixed rotationAndTranslation

--- app/assets/javascripts/core_ext.coffee
+++ app/assets/javascripts/core_ext.coffee
@@ -1,7 +1,5 @@
 # Applies a transformation matrix on an array of points.
-M4x4.transformPointsAffine = (m, points, r) ->
-
-  r = new MJS_FLOAT_ARRAY_TYPE(points.length) unless r?
+M4x4.transformPointsAffine = (m, points, r = new MJS_FLOAT_ARRAY_TYPE(points.length)) ->

   for i in [0...points.length] by 3
     v0 = points[i]
@@ -14,32 +12,48 @@

   r

-M4x4.makeRotate2 = (axis, r) ->
+# Applies a transformation matrix on an array of points.
+M4x4.transformPointsAffineWithTranslation = (m, translationVector, points, r = new MJS_FLOAT_ARRAY_TYPE(points.length)) ->

-  r = new MJS_FLOAT_ARRAY_TYPE(16) unless r?
+  [tx, ty, tz] = translationVector
+
+  for i in [0...points.length] by 3
+    v0 = points[i]
+    v1 = points[i + 1]
+    v2 = points[i + 2]
+
+    r[i]     = m[0] * v0 + m[4] * v1 + m[8] * v2 + m[12] + tx
+    r[i + 1] = m[1] * v0 + m[5] * v1 + m[9] * v2 + m[13] + ty
+    r[i + 2] = m[2] * v0 + m[6] * v1 + m[10] * v2 + m[14] + tz
+
+  r
+
+M4x4.makeRotate2 = (direction, r = new MJS_FLOAT_ARRAY_TYPE(16)) ->

   # orthogonal vector to (0,1,0) and rotation vector
-  ortho = V3.normalize([axis[2], 0, -axis[0]])
+  axis = V3.normalize([direction[2], 0, -direction[0]])

   # dot product of (0,1,0) and rotation
-  dotProd = axis[1]
+  dotProd = direction[1]

   # transformation of dot product for cosA
-  cosA = dotProd / V3.length(axis)
+  cosA = dotProd / V3.length(direction)
   sinA = Math.sqrt(1 - cosA * cosA)
+
+  [axis_x, axix_y, axis_z] = axis

   # calculate rotation matrix
-  r[0] = cosA + ortho[0] * ortho[0] * (1 - cosA)
-  r[1] = -ortho[2] * sinA
-  r[2] = ortho[0] * ortho[2] * (1 - cosA)
-  r[3] = 0
-  r[4] = ortho[2] * sinA
-  r[5] = cosA
-  r[6] = -ortho[0] * sinA
-  r[7] = 0
-  r[8] = ortho[0] * ortho[2] * (1 - cosA)
-  r[9] = ortho[0] * sinA
-  r[10] = cosA + ortho[2] * ortho[2] * (1 - cosA)
+  r[0]  = axis_x * axis_x * (1 - cosA) + cosA
+  r[1]  = axis_z * sinA
+  r[2]  = axis_x * axis_z * (1 - cosA)
+  r[3]  = 0
+  r[4]  = -axis_z * sinA
+  r[5]  = cosA
+  r[6]  = axis_x * sinA
+  r[7]  = 0
+  r[8]  = axis_x * axis_z * (1 - cosA)
+  r[9]  = -axis_x * sinA
+  r[10] = axis_z * axis_z * (1 - cosA) + cosA
   r[11] = 0
   r[12] = 0
   r[13] = 0
@@ -72,9 +86,8 @@
   else

     mat = M4x4.makeRotate2 directionVector
-    mat = M4x4.translateSelf translationVector, mat
-
-    M4x4.transformPointsAffine(mat, vertices, output)
+
+    M4x4.transformPointsAffineWithTranslation(mat, translationVector, vertices, output)

 # `_.throttle2` makes a function only be executed once in a given
 # time span -- no matter how often you it. We don't recomment to use


mekko: set individual margins, fix label position

--- app/assets/javascripts/d3/mekko.coffee
+++ app/assets/javascripts/d3/mekko.coffee
@@ -56,15 +56,20 @@
             carrier: carrier

     draw: =>
-      @margin = 50
-      @width = (@container_node().width()   || 490) - 2 * @margin
-      @height = (@container_node().height() || 402) - 2 * @margin
+      margins =
+        top: 20
+        bottom: 100
+        left: 30
+        right: 30
+
+      @width = (@container_node().width()   || 490) - (margins.left + margins.right)
+      @height = (@container_node().height() || 402) - (margins.top + margins.bottom)
       @svg = d3.select("#d3_container_mekko").
         append("svg:svg").
-        attr("height", @height + 2 * @margin).
-        attr("width", @width + 2 * @margin).
+        attr("height", @height + margins.top + margins.bottom).
+        attr("width", @width + margins.left + margins.right).
         append("svg:g").
-        attr("transform", "translate(#{@margin}, #{@margin})")
+        attr("transform", "translate(#{margins.left}, #{margins.top})")

       y_scale = d3.scale.linear().domain([100,0]).range([0, @height])
       @y_axis = d3.svg.axis().scale(y_scale).ticks(4).orient("left")
@@ -126,7 +131,7 @@
       @svg.selectAll("text.sector_label").
         data(@sector_list.models, (d) -> d.get 'key' ).
         transition().duration(500).
-        attr("dy", (d) => @y(d.total_value() / 2) + 5 ).
+        attr("dy", (d) => @x(d.total_value() / 2) + 5 ).
         text((d) -> "#{d.get 'key'} #{Metric.autoscale_value d.total_value(), 'MJ', 2}")

       # we need to track the offset for every sector


Bugfix, check all

Bugfix, check all

--- app/assets/javascripts/roles.js.coffee
+++ app/assets/javascripts/roles.js.coffee
@@ -3,16 +3,16 @@
   checkcheck = ($toggler, $roles) ->
     allchecked($toggler, $roles)
     $toggler.change ->
-      $roles.find(":checkbox").attr("checked", $(@).is(':checked') ? true : false)
+      $roles.find(":checkbox").prop("checked", $(@).prop('checked') ? true : false)
     $roles.change ->
       allchecked($toggler, $roles)

   # Check/uncheck the "All" checkbox depending on the role checkbox values
   allchecked = ($toggler, $roles) ->
     if $roles.find(":checkbox:checked").length == $roles.find(":checkbox").length
-      $toggler.attr("checked", true)
+      $toggler.prop('checked', true)
     else
-      $toggler.attr("checked", false)
+      $toggler.prop('checked', false)

   # Check all/none checkbox for role assignment
   $toggleDepartments = $("#toggle-departments")


KDView: getWidth uses jquery outerwidth now since it is faster

--- core/KDView.coffee
+++ core/KDView.coffee
@@ -365,7 +365,7 @@
     positionOptions.position = "absolute"
     @$().css positionOptions

-  getWidth:-> @$().width()
+  getWidth:-> @$().outerWidth no

   setWidth:(w, unit = "px")->
     @getElement().style.width = "#{w}#{unit}"


Bind 1 event and delegate to childs

Bind 1 event and delegate to childs

--- app/assets/javascripts/votes.js.coffee
+++ app/assets/javascripts/votes.js.coffee
@@ -1,16 +1,16 @@
 App.Votes =

-  hoverize: (votes) ->
-    $(votes).hover ->
-      $("div.anonymous-votes", votes).show();
-      $("div.organizations-votes", votes).show();
-      $("div.not-logged", votes).show();
-    , ->
-      $("div.anonymous-votes", votes).hide();
-      $("div.organizations-votes", votes).hide();
-      $("div.not-logged", votes).hide();
+  hoverize: (selector, delegateTo) ->
+    $(selector).on "mouseenter", delegateTo, ->
+      $(this).find("div.anonymous-votes").show()
+      $(this).find("div.organizations-votes").show()
+      $(this).find("div.not-logged").show()
+    $(selector).on "mouseleave", delegateTo, ->
+      $(this).find("div.anonymous-votes").hide()
+      $(this).find("div.organizations-votes").hide()
+      $(this).find("div.not-logged").hide()

   initialize: ->
-    App.Votes.hoverize votes for votes in $("div.votes")
-    App.Votes.hoverize votes for votes in $("div.supports")
+    App.Votes.hoverize $("#debates"), "div.votes"
+    App.Votes.hoverize $("#proposals, #featured-proposals"), "div.supports"
     false


adds refreshDashboard to app

adds refreshDashboard to app

--- js/app.coffee
+++ js/app.coffee
@@ -54,6 +54,16 @@
     @query_results_query = ko.observable(null)
     @query_results = ko.observableArray([])

+  # Project dashboard will eventually be merged into regular dashboard,
+  # and this name will be more correct
+  refreshDashboard: () =>
+    VM.loadProjects()
+    sel = VM.selected()
+    if sel.project_name
+      VM.loadProject(sel.username, sel.project, sel.branch)
+    else
+      VM.loadRecentBuilds()
+
   searchArticles: (vm, event) ->
     $.ajax
       url: "/search-articles"
@@ -139,10 +149,6 @@
     path = "/api/v1/project/#{project_name}"
     path += "/tree/#{encodeURIComponent(branch)}" if branch?

-    # @selected
-    #   project_name: project_name,
-    #   branch: branch
-
     @builds.removeAll()
     @project_builds_have_been_loaded(false)
     $.getJSON path, (data) =>
@@ -328,15 +334,20 @@
     @get '^/gh/:username/:project/tree/:branch',
       (cx) ->
         VM.selected
+          username: cx.params.username
+          project: cx.params.project
           project_name: "#{cx.params.username}/#{cx.params.project}"
           branch: cx.params.branch

         VM.loadProject cx, cx.params.username, cx.params.project, cx.params.branch
+
     @get '^/gh/:username/:project/:build_num',
       (cx) -> VM.loadBuild cx, cx.params.username, cx.params.project, cx.params.build_num
     @get('^/gh/:username/:project',
       (cx) ->
         VM.selected
+          username: cx.params.username
+          project: cx.params.project
           project_name: "#{cx.params.username}/#{cx.params.project}"

         VM.loadProject cx, cx.params.username, cx.params.project


better scaling constant

better scaling constant

--- js/app.coffee
+++ js/app.coffee
@@ -116,7 +116,7 @@
   MAX_FONT_SIZE = 200
   MIN_FONT_SIZE = 30
   container?.style.fontSize = Math.max(Math.min(
-    container.clientWidth / (Math.log(longestWord) * 2.5), MAX_FONT_SIZE
+    container.clientWidth / (Math.log(longestWord) * 2.6), MAX_FONT_SIZE
     ), MIN_FONT_SIZE) + 'px'

 addEvent 'resize', fitText


remove old commented out code

remove old commented out code

--- js/app.coffee
+++ js/app.coffee
@@ -18,8 +18,6 @@
     document.title = "#{VM.current_page().title} - CircleCI"
   else
     document.title = "Continuous Integration and Deployment - CircleCI"
-
-  # ko.applyBindings(VM)

   $("main").focus()


Partially fix ss record data update affecting next generateRow

Only affect edited row -- other instances of this record are still
affected.  See #3901

--- web/volume/spreadsheet.coffee
+++ web/volume/spreadsheet.coffee
@@ -797,8 +797,8 @@

         updateDatum = (info, v) ->
           info.v = v
+          info.d[info.metric.id] = v
           if info.category.fixed
-            info.d[info.metric.id] = v
             generateText(info)
           else
             for li in TBody.getElementsByClassName(info.p + info.d.id + '_' + info.metric.id)


Fix cleanup method on FormService

Fix cleanup method on FormService

--- angular/core/services/form_service.coffee
+++ angular/core/services/form_service.coffee
@@ -38,7 +38,7 @@
           model: model
           response: response

-    cleanup = (scope, model, options) ->
+    cleanup = (scope, model, options = {}) ->
       ->
         options.cleanupFn(scope, model) if typeof options.cleanupFn is 'function'
         scope.isDisabled = false


Improve Table of Contents nested list handling

--- app/assets/javascripts/application.coffee
+++ app/assets/javascripts/application.coffee
@@ -61,5 +61,7 @@

   tocElement = $( '.js-toc' )

-  tocElement.find( 'ul' ).addClass( 'list list--s list--divided list--divided--s mbm' )
+  tocElement.find( 'ul' ).addClass( 'list list--s' )
+  tocElement.children( 'ul' ).addClass( 'list--divided list--divided--s mbm' )
+  tocElement.find( 'ul' ).find( 'ul' ).addClass( 'mlm mts' )
   tocElement.find( 'li' ).addClass( 'list-item tsm' )


Hijack label[for]

Hijack label[for]

--- app/assets/javascripts/date_picker.coffee
+++ app/assets/javascripts/date_picker.coffee
@@ -1,4 +1,6 @@
 window.DatePickerView = class DatePickerView extends Uniform
+  @::counter = 0
+
   elements: (add) ->
     add('select', 'select')

@@ -7,13 +9,16 @@

   init: ->
     super
-
     @select.hide()
     @insert_date_input()

   date_input = ->
     $ """
-      <input type="text" class="input-date" />
+      <input
+        type="text"
+        class="input-date"
+        id="input-date-#{++DatePickerView::counter}"
+        />
     """

   insert_date_input: ->
@@ -21,6 +26,7 @@
       $input = date_input()
       $input.appendTo(@)
       $input.datepicker(format: 'dd/mm/yyyy')
+      $input.siblings('label').attr('for', $input.attr('id'))

   update_select_values: (el) ->
     date_parts = el.value.split('/')


OX-578 added node count to tree list

OX-578 added node count to tree list

--- app/assets/javascripts/oxalis/view.coffee
+++ app/assets/javascripts/oxalis/view.coffee
@@ -36,14 +36,18 @@
         @updateTrees()
       reloadTrees : =>
         @updateTrees()
-      newNode : => @updateActiveComment()
+      newNode : =>
+        @updateActiveComment()
+        @updateTrees()
       newTreeName : =>
         @updateTrees()
         @updateActiveTree()
       newTree : =>
         @updateActiveComment()
         @updateTrees()
-        @updateActiveTree() })
+        @updateActiveTree()
+      deleteActiveNode : =>
+        @updateTrees() })

     @model.route.stateLogger.on
       pushFailed       : (critical) =>
@@ -200,8 +204,11 @@
     for tree in trees
       newContent.appendChild((
         $('<li>').append($('<a>', {"href": "#", "data-treeid": tree.treeId}).append(
+          $('<i>', {"class": "icon-bull"}),
+          $('<span>', {"title": "nodes", "text": tree.nodes.length}).css("display": "inline-block", "width": "50px"),
           $('<i>', {"class": "icon-sign-blank"}).css(
-            "color": "##{('000000'+tree.color.toString(16)).slice(-6)}"), $('<span>', {"text": tree.name}))) )[0])
+            "color": "##{('000000'+tree.color.toString(16)).slice(-6)}"),
+          $('<span>', {"title": "name", "text": tree.name}) )) )[0])

     treeList.append(newContent)


russian text for tokeninput

russian text for tokeninput

--- app/assets/javascripts/profiles.js.coffee
+++ app/assets/javascripts/profiles.js.coffee
@@ -4,7 +4,7 @@

 initTokenInput = ->
   input = $("#my-genres")
-  input.tokenInput("/genres", {prePopulate: input.data('pre'), propertyToSearch: 'title', preventDuplicates: 'true', theme: 'playhard'})
+  input.tokenInput("/genres", {prePopulate: input.data('pre'), propertyToSearch: 'title', preventDuplicates: 'true', theme: 'playhard', hintText: 'Начните вводить название жанра или системы ...', searchingText: 'Ищем ...', noResultsText: 'Ничего не найдено :('})

 $ ->
   initTokenInput()


min_amount was only doing 1 byte

min_amount was only doing 1 byte

--- src/base.iced
+++ src/base.iced
@@ -132,7 +132,7 @@
   min_confirmations : () -> @icfg('min-confirmations', 3)
   abs_min_marginal_fee_per_byte: () -> btcjs.networks.bitcoin.feePerKb/1000
   # each small transaction is roughly 180B, so we pay for that plus dust
-  min_amount : () -> @amount() + @marginal_fee_per_byte
+  min_amount : () -> @amount() + @marginal_fee_per_byte * SUP_MIN_TX_SIZE

   # Some reasonable lower bound on the total cost to transact a 1 input/1output transaction
   abs_min_amount : () -> @amount() + @abs_min_marginal_fee_per_byte * SUP_MIN_TX_SIZE


consider custom hourly rate as well

consider custom hourly rate as well

--- app/assets/javascripts/worklogs.js.coffee
+++ app/assets/javascripts/worklogs.js.coffee
@@ -39,15 +39,22 @@
   updateEndTimeStartTime: ->
     $('#worklog_to_date').val($('#worklog_from_date').val())
   updateTotal: ->
-    per_hour = $('#worklog_client_id option:selected').data().hourlyRateCents
     start = this.getStartDate()
     end = this.getEndDate()
-    new_total = this.calcTotal(start, end, per_hour)
+    console.log this.getHourlyCentRate()
+    new_total = this.calcTotal(start, end, this.getHourlyCentRate())
     return if !start || !end || !new_total
     total_dom = $('.worklog-total')
     currency = total_dom.data().currency
     new_total = new_total + currency
     total_dom.html(new_total)
+  getHourlyCentRate: ->
+    custom_rate_per_hour = parseFloat($('#worklog_hourly_rate').val())
+    if custom_rate_per_hour && custom_rate_per_hour > 0
+      # Convert into cents
+      return custom_rate_per_hour * 100
+    else
+      return $('#worklog_client_id option:selected').data().hourlyRateCents
   calcTotal: (start, end, per_hour) ->
     return if !start || !end
     time_span_seconds = (end.getTime() - start.getTime()) / 1000


Remove more magic numbers

Remove more magic numbers

--- app/coffeescripts/DemandVisualizer.coffee
+++ app/coffeescripts/DemandVisualizer.coffee
@@ -40,14 +40,17 @@
     @$el.html @vizWindow(elemId: @link.id, content: windowContent)

   renderLegend: (graphEl) ->
-    textY = 30
+    titleFontSize = 20
+    titleWidth = titleFontSize * 5
+    textY = titleFontSize + 10
+    labelFontSize = titleFontSize * 7 / 10
     legendTitleWidth = 100
     legendLabelWidth = 75

     graphEl.append('svg:text').
       attr('x', 10).
       attr('y', textY).
-      style('font-size', '20px').
+      style('font-size', "#{titleFontSize}px").
       text('Legend')

     _.each @typeOrder.get('vehicle_type'), (vtype, idx) =>
@@ -56,16 +59,16 @@
       graphEl.append('svg:text').
         attr('x', legendTitleWidth + legendLabelWidth*idx).
         attr('y', textY).
-        style('font-size', '14px').
+        style('font-size', "#{labelFontSize}px").
         style('background-color', vehicleTypeColors[idx]).
         text(name)

       graphEl.append('svg:rect').
         attr('id', "legend-#{idx}").
-        attr('x', 100 + legendLabelWidth*idx).
+        attr('x', titleWidth + legendLabelWidth*idx).
         attr('y', 10).
         attr('width', legendLabelWidth).
-        attr('height', 30).
+        attr('height', textY).
         attr('opacity', '0.4').
         attr('enabled', true).
         attr('fill', vehicleTypeColors[idx]).
@@ -81,8 +84,8 @@
             d3.selectAll(".vehicle-graph-#{idx}").style('visibility','visible')

   renderGraph: (sel) ->
-    textSizeImprecisionOffset = 2
     labelFontSize = 10
+    textSizeImprecisionOffset = Math.ceil(.2 * labelFontSize)
     width = 450
     height = 400
     padding = 50
@@ -102,8 +105,8 @@
       attr('id', 'legend').
       attr('x', 5).
       attr('y', 5).
-      attr('width', width + padding - 10).
-      attr('height', padding - 12).
+      attr('width', width + padding - labelFontSize).
+      attr('height', padding - labelFontSize - textSizeImprecisionOffset).
       attr('fill', 'white').
       attr('stroke', 'lightGray')

@@ -206,9 +209,10 @@
           attr('class', "vehicle-graph-#{i}").
           attr 'height', (d) =>
             maxHeight = yScale(Math.max(2*stdDevAdd, 2*stdDevMult*d))
-            topY = topYVal(height, yScale, d, @demand) + padding - 2
-            if maxHeight > height - topY + padding - 10
-              height - topY + padding - 10
+            topY = topYVal(height, yScale, d, @demand) +
+              padding - textSizeImprecisionOffset
+            if maxHeight > height - topY + padding - labelFontSize
+              height - topY + padding - labelFontSize
             else
               # Enforce coloring even if stddev. not present to distinguish
               # vehicle types


:fire: Remove view scale adjust method

:fire: Remove view scale adjust method

--- lib/minimap-find-results-view.coffee
+++ lib/minimap-find-results-view.coffee
@@ -18,18 +18,6 @@

       if minimap?
         minimap.miniOverlayer.append(this)
-        # @adjustResults()
-
-    # As there's a slightly different char width between the minimap font
-    # and the editor font we'll retrieve both widths and compute the
-    # ratio to properly scale the find results.
-    # FIXME I can't wrap my head on why the fixed version of redacted
-    # still returns different widths for chars, so during that time
-    # I'll use fixed scale.
-    adjustResults: ->
-      return if @adjusted
-      @css '-webkit-transform', "scale3d(#{minimapInstance.getCharWidthRatio()},1,1)"
-      @adjusted = true

     getEditor: ->
       activeView = atom.workspaceView.getActiveView()


Specs for #posterImageUrl

Specs for #posterImageUrl

--- test/models/partner_show.coffee
+++ test/models/partner_show.coffee
@@ -1,16 +1,21 @@
-_             = require 'underscore'
-{ fabricate } = require 'antigravity'
-sd            = require('sharify').data
-should        = require 'should'
-Backbone      = require 'backbone'
-PartnerShow   = require '../../models/partner_show'
-PartnerLocation = require '../../models/partner_location'
-FairLocation    = require '../../models/partner_location'
+_                 = require 'underscore'
+{ fabricate }     = require 'antigravity'
+sd                = require('sharify').data
+should            = require 'should'
+Backbone          = require 'backbone'
+PartnerShow       = require '../../models/partner_show'
+PartnerLocation   = require '../../models/partner_location'
+FairLocation      = require '../../models/partner_location'
+sinon             = require 'sinon'

 describe 'PartnerShow', ->

   beforeEach ->
+    sinon.stub Backbone, 'sync'
     @partnerShow = new PartnerShow fabricate('show')
+
+  afterEach ->
+    Backbone.sync.restore()

   describe '#url', ->

@@ -89,3 +94,25 @@
           display: 'Booth 1234'

       @partnerShow.fairLocationDisplay().should.equal "<i>New York</i> – Booth 1234"
+
+  describe '#posterImageUrl', ->
+    it 'returns an image', ->
+      @partnerShow.posterImageUrl().should.include 'partner_show_images/51f6a51d275b24a787000c36/1/large.jpg'
+
+    it 'returns a featured image', ->
+      @partnerShow.posterImageUrl(true).should.include '/partner_show_images/51f6a51d275b24a787000c36/1/featured.jpg'
+
+    it 'returns larger if featured or large is unavailable', (done) ->
+      @partnerShow.on 'fetch:posterImageUrl', (url) ->
+        url.should.include 'additional_images/4e7cb83e1c80dd00010038e2/1/large.jpg'
+        done()
+
+      @partnerShow.unset 'image_versions'
+      @partnerShow.posterImageUrl()
+      Backbone.sync.args[0][2].url.should.include "/api/v1/partner/#{@partnerShow.get('partner').id}/show/#{@partnerShow.id}/artworks"
+      Backbone.sync.args[0][2].success [fabricate 'artwork']
+
+    it 'returns empty when there really is no image', ->
+      @partnerShow.unset 'image_versions'
+      @partnerShow.posterImageUrl()
+      Backbone.sync.args[0][2].success []


removing temporary file created during the test case [https://github.com/o2platform/fluentnode/issues/82]

--- test/node-native/fs.test.coffee
+++ test/node-native/fs.test.coffee
@@ -178,6 +178,9 @@
     file_Name.length                .assert_Is(255);
     file_Content                    .save_As(file_Name)
     file_Name.exists()              .assert_Is_True()
+    #deleting just created temporary file
+    file_Name.delete_File()
+    file_Name.exists()              .assert_Is_False()

   it 'Save_As file path must be 255 characters max (file must not be created)', ->
     file_Name    = '_tmp_file_'.add_Random_String(1000)


linting

linting

--- test/phabs_commands_test.coffee
+++ test/phabs_commands_test.coffee
@@ -1965,14 +1965,14 @@
         hubot 'ph T424242 unsub nobody', 'user_with_phid'
         it 'tells user that that task is actually not subscribed so, whatever', ->
           expect(hubotResponse())
-          .to.eql 'Sorry, I can\'t figure nobody email address. ' +
+          .to.eql 'Sorry, I can\'t figure nobody email address. ' +
                   'Can you ask them to `.phab me as <email>`?'

       context 'phab T424242 sub nobody', ->
         hubot 'ph T424242 sub nobody', 'user_with_phid'
         it 'tells user that that task is actually not subscribed so, whatever', ->
           expect(hubotResponse())
-          .to.eql 'Sorry, I can\'t figure nobody email address. ' +
+          .to.eql 'Sorry, I can\'t figure nobody email address. ' +
                   'Can you ask them to `.phab me as <email>`?'

 # --------------------------------------------------------------------------------------------------


Fix typo

Fix typo

--- app/js/filters/format_operation.coffee
+++ app/js/filters/format_operation.coffee
@@ -2,7 +2,7 @@
     (type) ->
         name = Blockchain.type_name_map[type]

-        if name then name else "Unknow Operation"
+        if name then name else "Unknown Operation"

 angular.module("app").filter "filterByOperations", (Blockchain)->
     (operations, filter) ->


App: add CodeEditor into the scene with some js content

--- app/src/app.coffee
+++ app/src/app.coffee
@@ -1,10 +1,22 @@
-kd = require 'kd.js'
+kd         = require 'kd.js'
+CodeEditor = require './codeeditor'

 do ->

-  view = new kd.View
-    cssClass : 'hello-view center-all'
-    partial  : 'Hello kodemirror kd.js project!'
+  mainView = new kd.View
+  mainView.appendToDomBody()

-  # add it to DOM
-  view.appendToDomBody()
+  editor = new CodeEditor
+    mode     : 'javascript'
+  , content  : """
+    // Tip: Hit Cmd+Enter to run this code right now!
+
+    var foo;
+    foo = 'This is KodeMirror (kd.js) with CodeMirror!';
+
+    alert(foo);
+  """
+
+  kd.utils.defer editor.bound 'focus'
+
+  mainView.addSubView editor


Also fixed horizontal offset when using extension button

--- app/js/jail_iframe/classes/buttons.coffee
+++ app/js/jail_iframe/classes/buttons.coffee
@@ -48,6 +48,6 @@
   placeNearSelection: (mouseX=null) ->
     selectionBox = window.document.getSelection().getRangeAt(0).getBoundingClientRect()
     top = selectionBox.top + $(window).scrollTop() - 2
-    left = mouseX ? (selectionBox.left + selectionBox.width/2)
+    left = mouseX ? ($(window).scrollLeft() + selectionBox.left + selectionBox.width/2)

     @setCoordinates top, left


Individual page now loads when visited from other page

--- app/scripts/controllers/individual.coffee
+++ app/scripts/controllers/individual.coffee
@@ -95,6 +95,9 @@
 ##############
 ## Initial Calls
 ##############
-
+    $scope.rep = $scope.reps[$scope.selected.rep1.bioguide_id]
+    $scope.get_transparencydata_id($scope.selected.rep1.bioguide_id)
+    ApiGet.littleSis "entities/bioguide_id/#{$scope.selected.rep1.bioguide_id}.json?", $scope.callback_littleSis_id, this, $scope.selected.rep1.bioguide_id
+    ApiGet.nyt "members/#{$scope.selected.rep1.bioguide_id}", $scope.callback_nyt, this, $scope.selected.rep1.bioguide_id

   ])


Don't simulate in background when playing elemental-wars, too intensive

--- app/views/play/level/PlayLevelView.coffee
+++ app/views/play/level/PlayLevelView.coffee
@@ -574,7 +574,7 @@
     return false if $.browser?.msie or $.browser?.msedge
     return false if $.browser.linux
     return false if me.level() < 8
-    return false if @level.get('slug') in ['zero-sum', 'ace-of-coders']
+    return false if @level.get('slug') in ['zero-sum', 'ace-of-coders', 'elemental-wars']
     if @level.isType('course', 'game-dev', 'web-dev')
       return false
     else if @level.isType('hero') and gamesSimulated


cleanup keymap

cleanup keymap

--- keymap.cson
+++ keymap.cson
@@ -35,12 +35,11 @@

 '.platform-darwin atom-workspace':
   'alt-shift-G': 'symbol-gen:generate'
+
 'atom-workspace':
   'ctrl-{': 'last-cursor-position:previous'
   'ctrl-}': 'last-cursor-position:next'
   'alt-ctrl-f': 'platformio-ide-terminal:focus'
-  # 'alt-up': 'multi-cursor-plus:move-up'
-  # 'alt-down': 'multi-cursor-plus:move-down'

 'atom-text-editor':
   'ctrl-alt-f': 'platformio-ide-terminal:focus'
@@ -54,26 +53,17 @@
   'ctrl-right': 'window:focus-pane-on-right'
   'ctrl-shift-left': 'window:move-active-item-to-pane-on-left'
   'ctrl-shift-right': 'window:move-active-item-to-pane-on-right'
-  # Pane movement
+  # Pane management
   'alt-z': 'maximize-panes:maximize'
   'alt-ctrl-right': 'move-panes:move-right'
   'alt-ctrl-left': 'move-panes:move-left'
   'alt-ctrl-up': 'move-panes:move-up'
   'alt-ctrl-down': 'move-panes:move-down'
-  # 'shift-cmd-enter': 'maximize-panes:maximize'
-  # ###############################'ctrl-arrows works'########################
-  # 'ctrl-alt-cmd-shift-right': 'window:focus-pane-on-right'
-  # 'ctrl-alt-cmd-shift-left': 'window:focus-pane-on-left'
-  # 'ctrl-alt-cmd-shift-up': 'window:focus-pane-above'
-  # 'ctrl-alt-cmd-shift-down': 'window:focus-pane-below'
-  # End Pane movement
-
-
+  # End Pane management

 '.platform-darwin atom-text-editor':
   'cmd-|': 'tree-view:toggle-focus'

-  # 'ctrl-r': 'script:run'
   'ctrl-shift-c': 'run:terminate'
   'ctrl-shift-r': 'script:run-with-profile'
   'ctrl-w': 'script:close-view'
@@ -89,15 +79,6 @@

   # 'shift-cmd-j': 'script:run-by-line-number'
   # 'shift-cmd-i': 'script:run-options'
-
-# '.platform-win32 atom-text-editor, .platform-linux atom-text-editor':
-#   'alt-r': 'run:script'
-#   'alt-shift-r': 'run:copy'
-# '.workspace':
-  # 'cmd-w': 'Kite: Close Sidebar'
-
-# '.platform-darwin .atom-runner':
-#   'cmd-c': 'run:copy'

 '.atom-script':
   'escape': 'run:exit'
@@ -117,6 +98,15 @@
   'ctrl-alt-left':  'multi-cursor:move-last-cursor-left'
   'ctrl-alt-up':    'multi-cursor:move-last-cursor-up'

+
+# '.platform-win32 atom-text-editor, .platform-linux atom-text-editor':
+#   'alt-r': 'run:script'
+#   'alt-shift-r': 'run:copy'
+# '.workspace':
+  # 'cmd-w': 'Kite: Close Sidebar'
+
+# '.platform-darwin .atom-runner':
+#   'cmd-c': 'run:copy'

 # Linux keybindings


Fix keymap, no duplicate keys

Fix keymap, no duplicate keys

--- keymap.cson
+++ keymap.cson
@@ -83,7 +83,7 @@
   'ctrl-shift-o `': 'expand-region:select-around-back-ticks'
   'ctrl-shift-o t': 'expand-region:select-around-tags'

-'atom-text-editor:not([mini])':
+'atom-workspace atom-text-editor:not([mini])':
   # override snippets:available
   'shift-alt-s': 'find-and-till:select-till'

@@ -102,6 +102,11 @@
   'ctrl-y': 'clip-history:paste'
   'ctrl-shift-y': 'clip-history:paste-last'

+  # project-find-navigation
+  'ctrl-cmd-n': 'project-find-navigation:next'
+  'ctrl-cmd-p': 'project-find-navigation:prev'
+  'ctrl-cmd-shift-f': 'project-find-navigation:activate-results-pane'
+
 'atom-workspace':
   'cmd-b': 'recent-files-fuzzy-finder:toggle-finder'

@@ -114,8 +119,3 @@
 '.preview-pane.project-find-navigation':
   'ctrl-cmd-n': 'project-find-navigation:select-next-and-confirm'
   'ctrl-cmd-p': 'project-find-navigation:select-prev-and-confirm'
-
-'atom-workspace atom-text-editor:not([mini])':
-  'ctrl-cmd-n': 'project-find-navigation:next'
-  'ctrl-cmd-p': 'project-find-navigation:prev'
-  'ctrl-cmd-shift-f': 'project-find-navigation:activate-results-pane'


Make lorem function more friendly.

Make lorem function more friendly.

--- app/templates/website/Gruntfile.coffee
+++ app/templates/website/Gruntfile.coffee
@@ -1,8 +1,17 @@
 livereloadPort = 35729
 httpServerPort = 9000

-fs    = require 'fs'
-path  = require 'path'
+fs         = require 'fs'
+path       = require 'path'
+loremIpsum = require 'lorem-ipsum'
+
+lorem = (count, options={}) ->
+  if typeof count == 'number'
+    options.count = count
+  else
+    options = count ? {}
+  options.units ?= 'words'
+  loremIpsum options

 module.exports = (grunt) ->
   require('load-grunt-tasks')(grunt)
@@ -88,7 +97,7 @@
         options:
           pretty: true
           data:
-            lorem: require('lorem-ipsum')
+            lorem: lorem
     <% } else if(options.html === 'ejs') { %>
     ejs:
       dev:
@@ -100,7 +109,7 @@
           ext: '.html'
         ]
         options:
-          lorem: require('lorem-ipsum')
+          lorem: lorem
     <% } %>
     connect:
       server:


Remove unused code

Remove unused code

--- build/Gruntfile.coffee
+++ build/Gruntfile.coffee
@@ -2,7 +2,6 @@
 # Grunt build script for your Backbone.Andamio apps
 #--------------------------------------------------------------------------------

-fs = require('fs')
 module.exports = ->

   @initConfig


Rebuild all efforts from the cached data of previously stored effort objects when hubot reloads this script.

--- scripts/time-tracker.coffee
+++ scripts/time-tracker.coffee
@@ -6,13 +6,20 @@
 # reset my timesheet - Clear all entries from my timesheet

 class Timesheets
-  #TODO: Is it necessary to hold on to @robot?
   constructor: (@robot) ->
     @cache = {}

     @robot.brain.on 'loaded', =>
-      if @robot.brain.data.timesheets
-        @cache = @robot.brain.data.timesheets
+      if (cachedTimesheets = @robot.brain.data.timesheets)
+        console.log "Reloading #{Object.keys(cachedTimesheets).length} previously cached timesheet(s)..."
+        for key of cachedTimesheets
+          @cache[key] = (Timesheets.buildEffort(cachedEffort) for cachedEffort in cachedTimesheets[key])
+
+  @buildEffort: (cachedEffort) ->
+    effort = new Effort(cachedEffort.participant, cachedEffort.id)
+    effort.starting = new Date(cachedEffort.starting)
+    effort.ending = new Date(cachedEffort.ending) if cachedEffort.ending?
+    effort

   add: (effort) ->
     (@cache[effort.participant] ||= []).push effort
@@ -54,9 +61,9 @@
   pluralize: (elapsedTime) ->
     if elapsedTime == 1 then '' else 's'

-  start: -> @starting = new Date()
+  start: -> @starting = new Date

-  stop: -> @ending = new Date()
+  stop: -> @ending = new Date

   endTime: -> @ending or new Date


removed show button manager

removed show button manager

--- app/js/models/fact.coffee
+++ app/js/models/fact.coffee
@@ -1,21 +1,6 @@
 highlight_time_on_load    = 1500
 highlight_time_on_in_view = 1500
 delay_between_highlight_and_show_button_open = 400
-
-
-class ShowButtonManager
-  constructor: (@dom_events) ->
-    @show_button = new Factlink.ShowButton @dom_events
-
-  set_coordinates: (top, left) =>
-    @show_button.setCoordinates(top, left)
-
-  openShowButton:  ->  @show_button.show()
-  closeShowButton: ->  @show_button.hide()
-
-  startLoading: -> @show_button.startLoading()
-
-  destroy: -> @show_button.destroy()

 class Highlighter
   constructor: (@$elements) ->
@@ -57,7 +42,7 @@

     @highlighter = new Highlighter $(@elements)

-    @show_button_manager = new ShowButtonManager
+    @show_button = new Factlink.ShowButton
       mouseenter: => @onFocus()
       mouseleave: => @onBlur()
       click:      => @openFactlinkModal()
@@ -78,18 +63,18 @@
   onBlur: -> @attention_span.neglect()
   onFocus: (e) =>
     if e?
-      @show_button_manager.set_coordinates($(e.target).offset().top, e.pageX)
+      @show_button.setCoordinates($(e.target).offset().top, e.pageX)
     @attention_span.attend()

   startEmphasis: ->
     @highlighter.highlight()
-    @show_button_manager.openShowButton()
+    @show_button.show()

   stopEmphasis: =>
     return if @shouldHaveEmphasis()

     @highlighter.dehighlight()
-    @show_button_manager.closeShowButton()
+    @show_button.hide()

   shouldHaveEmphasis: =>
     @attention_span.has_attention() || @_loading
@@ -100,7 +85,7 @@

   startLoading: ->
     @_loading = true
-    @show_button_manager.startLoading()
+    @show_button.startLoading()

   stopLoading: ->
     @_loading = false
@@ -114,4 +99,4 @@

       $el.remove()

-    @show_button_manager.closeShowButton()
+    @show_button.destroy()


better way of adding a method to our user model


look for dom node instead of blindly waiting

--- zechat/static/test_conversation.coffee
+++ zechat/static/test_conversation.coffee
@@ -30,7 +30,8 @@
       el: $app[0]
     )

-    _.defer ->
+    zc.waitfor(1000, -> $app.find('.conversation-compose').length or null)
+    .then ->
       $form = $app.find('.conversation-compose form')
       $form.find('[name=message]').val('hello world')
       $form.submit()
@@ -40,14 +41,14 @@
         messages = $history.find('.message-text').text()
         return messages if messages.length > 0

-      zc.waitfor(2000, get_messages)
-      .then (messages) ->
-        expect(messages).toEqual("hello world")
-      .catch (err) ->
-        if err == 'timeout'
-          expect('timed out').toBe(false)
-          return
-        throw(err)
-      .finally ->
-        zc.remove_handlers(app)
-        done()
+      return zc.waitfor(2000, get_messages)
+    .then (messages) ->
+      expect(messages).toEqual("hello world")
+    .catch (err) ->
+      if err == 'timeout'
+        expect('timed out').toBe(false)
+        return
+      throw(err)
+    .finally ->
+      zc.remove_handlers(app)
+      done()


Fix italic/bold formatter regexp

Fix italic/bold formatter regexp


Don’t maintainHistory for the bookmarks layer

…this requires some logic to be added, and we should probably do so in
another PR.

--- lib/bookmarks.coffee
+++ lib/bookmarks.coffee
@@ -11,7 +11,7 @@
       'bookmarks:jump-to-previous-bookmark': @jumpToPreviousBookmark
       'bookmarks:clear-bookmarks': @clearBookmarks

-    @markerLayer = @editor.addMarkerLayer({maintainHistory: true})
+    @markerLayer = @editor.addMarkerLayer()
     @editor.decorateMarkerLayer(@markerLayer, {type: 'line-number', class: 'bookmarked'})
     @disposables.add @editor.onDidDestroy(@destroy.bind(this))


Requested fixes [INTEGRALCS-15217]

Requested fixes [INTEGRALCS-15217]


only hide all episodes link in default theme

--- src/javascripts/theme.coffee
+++ src/javascripts/theme.coffee
@@ -43,7 +43,7 @@
   context: =>
     attrs = _.merge(@app.episode, @app.podcast.forTheme(), @themeConfig())
     # hide All Episodes link when on the page that is linked to
-    if Utils.onSameUrl(attrs.podcastUrl)
+    if @app.options.theme == 'default' && Utils.onSameUrl(attrs.podcastUrl)
       attrs.podcastUrl = null
     attrs


Add core selection mixin to tip instances.

Also refactor _inflateByTrigger.

--- src/js/jquery.hlf.tip.coffee
+++ src/js/jquery.hlf.tip.coffee
@@ -247,10 +247,12 @@
     _inflateByTrigger: ($trigger) ->
       compoundDirection = if $trigger.data(@attr('direction')) then $trigger.data(@attr('direction')).split(' ') else @defaultDirection
       @debugLog 'update direction class', compoundDirection
+      $content = @selectByClass('content')
+      $content.text $trigger.data @attr('content')
+      $content
+        .width contentSize.width
+        .height contentSize.height
       @$tip
-        .find ".#{@classNames.content}"
-          .text $trigger.data @attr('content')
-        .end()
         .removeClass [
           @classNames.north
           @classNames.south
@@ -534,12 +536,14 @@
     namespace: hlf.tip
     apiClass: Tip
     asSingleton: yes
+    baseMixins: ['selection']
     compactOptions: yes
   hlf.createPlugin
     name: 'snapTip'
     namespace: hlf.tip.snap
     apiClass: SnapTip
     asSingleton: yes
+    baseMixins: ['selection']
     compactOptions: yes

 )


close flash on esc key

close flash on esc key

--- lib/carte/client/views/slideshow.cjsx
+++ lib/carte/client/views/slideshow.cjsx
@@ -26,6 +26,7 @@

   onKeyDown: (event)->
     switch event.keyCode
+      when 27 then @props.router.navigate(@closeLink())
       when 37 then @onClickPrev()
       when 39 then @onClickNext()


preliminary group support

preliminary group support

--- src/decking.coffee
+++ src/decking.coffee
@@ -97,8 +97,8 @@
     iterator = (details, callback) ->
       name = details.name
       container = docker.getContainer name
-      container.inspect (err, data) ->
-        if not data.State.Running
+      isRunning container, (err, running) ->
+        if not running
           logAction name, "starting..."
           container.start callback
         else
@@ -115,11 +115,14 @@

   stop: (cluster, done) ->

+    # @TODO reverse dependency order? shutdown process might
+    # involve signalling to them (e.g. final write, disconnect)
+
     iterator = (details, callback) ->
       name = details.name
       container = docker.getContainer name
-      container.inspect (err, data) ->
-        if data.State.Running
+      isRunning container, (err, running) ->
+        if running
           logAction name, "stopping..."
           container.stop callback
         else
@@ -145,8 +148,6 @@
       name = details.name
       container = docker.getContainer name
       container.attach options, (err, stream) ->
-        # woah! the stream data is a bit complex. ideally i want to pipe
-        # each container's logs to a different writable stream, not process.stdout
         new MultiplexStream container, stream, padName(name, "(", ")")

         callback null
@@ -241,7 +242,12 @@
         #log "Container #{name} already exists, skipping..."
         # @TODO check if this container has dependents or not...
         logAction name, "already exists - running in case of dependents"
-        return command.container.start callback
+        return isRunning command.container, (err, running) ->
+          return command.container.start callback if not running
+
+          # container exists AND is running - stop, restart
+          return command.container.stop (err) ->
+            command.container.start callback

       logAction name, "creating..."

@@ -252,8 +258,10 @@
       container.stop callback

     resolveOrder @config, cluster, (list) ->
-      async.eachSeries list, fetchIterator, ->
-        async.eachSeries commands, createIterator, ->
+      async.eachSeries list, fetchIterator, (err) ->
+        throw err if err
+        async.eachSeries commands, createIterator, (err) ->
+          throw err if err
           # @FIXME hack to avoid ghosts with quick start/stop combos
           setTimeout ->
             async.eachLimit list, 5, stopIterator, done
@@ -315,10 +323,23 @@

   log "#{padName(name)}  #{message}"

+# @TODO rename; this does more than just order resolution now!
 resolveOrder = (config, cluster, callback) ->
+  if cluster.group
+    # right! specifying a group modifier. let's pump it up...
+    groupName = cluster.group
+    containers = cluster.containers
+    group = config.groups[groupName]
+  else
+    if cluster.containers
+      containers = cluster.containers
+    else
+      containers = cluster
+
   containerDetails = {}

-  for containerName in cluster
+  # map container names to actual container definitions
+  for containerName in containers
     container = config.containers[containerName]
     containerDetails[containerName] = container

@@ -328,11 +349,33 @@
       if not containerDetails[dependency]
         containerDetails[dependency] = config.containers[dependency]

+  # rename any containers based on group stuff, calc some max length stuff
+  # merge group overrides if present
+  for _, container of containerDetails
+    container.originalName = container.name
+    if groupName
+      container.group = groupName
+      container.name += ".#{groupName}"
+
+      # first up, completely replace any container config with
+      # the group-wide options
+      for key, value of group.options
+        container[key] = value
+
+      # now check for container specific overrides...
+      if group.containers?[container.originalName]?
+        for key, value of group.containers[container.originalName]
+          # @TODO we're overwriting here, these should MERGE with
+          # those specified group-wide... I think. But only if there
+          # was a group wide key maybe?
+          container[key] = value
+
+    maxNameLength = container.name.length if container.name.length > maxNameLength
+
+  # resolve dependency order
   depTree = new DepTree
-  for name, details of containerDetails
-    depTree.add name, details.dependencies
-    # @NOTE while we're here, work out the longest name in the cluster (dirty)
-    maxNameLength = name.length if name.length > maxNameLength
+  for _, container of containerDetails
+    depTree.add container.originalName, container.dependencies

   list = (containerDetails[item] for item in depTree.resolve())

@@ -380,6 +423,8 @@

     when "dependencies"
       for v,k in val
+        if object.group
+          v += ".#{object.group}"
         # we trust that the aliases array has the correct matching
         # indices here such that alias[k] is the correct alias for dependencies[k]
         alias = object.aliases[k]
@@ -399,10 +444,19 @@

     # no cluster specified, but there's only one, so just default to it
     cluster = key for key of config.clusters
-    log "Defaulting to cluster '#{cluster}'\n"
+    log "Defaulting to cluster '#{cluster}'"

   target = config.clusters[cluster]

   throw new Error "Cluster #{cluster} does not exist in decking.json"  if not target

+  if target.group
+    log "Using overrides from group '#{target.group}'\n"
+
   return target
+
+isRunning = (container, callback) ->
+  container.inspect (err, data) ->
+    return callback err if err
+
+    return callback null, data.State.Running


Delete data.type once it's used to transform batch data

--- src/kissmetrics-batch.coffee
+++ src/kissmetrics-batch.coffee
@@ -30,6 +30,8 @@
       data.alias = data._n
       delete data._n

+    delete data.type
+
   _validate_queue: ->
     for method in ['add', 'get']
       unless typeof @queue[method] is 'function'


update regex pattern

update regex pattern

--- app/scripts/editor.coffee
+++ app/scripts/editor.coffee
@@ -194,7 +194,8 @@
 				@status.cmd = true
 				$('.cmd').removeClass('hidden')
 			when 8
-				if $('#editor').html().match /^<(h1|h2|p|pre|code) name=".{4}">
<\/(h1|h2|p|pre|code)>$/
+				if $('#editor').html().match /^<(h1|h2|p|pre|code) .*>
<\/(h1|h2|p|pre|code)>$/
+					console.log 'matched!!!!'
 					e.preventDefault()
 					e.stopPropagation()


Download last_build for a branch when we get the branch from pusher

`lastBuild` is a synchronous relationship on a branch model, so we need to
have a build record present when we put a default branch from a repository
model into the store. We don't send lastBuild's payload in pusher, so
we need to get it using an ajax call, if it's not already in the store.
In the future we may decide to make the relationship async, but I don't
want to change the code at the moment

--- app/services/store.coffee
+++ app/services/store.coffee
@@ -71,9 +71,21 @@
     if type == 'build' && (json.repository || json.repo)
       data = json.repository || json.repo

-      if data.default_branch
-        data.default_branch.default_branch = true
+      default_branch = data.default_branch
+      if default_branch
+        default_branch.default_branch = true

-      @push(this.normalize('repo', data))
+      last_build_id = default_branch.last_build_id
+      # a build is a synchronous relationship on a branch model, so we need to
+      # have a build record present when we put default_branch from a repository
+      # model into the store. We don't send last_build's payload in pusher, so
+      # we need to get it here, if it's not already in the store. In the future
+      # we may decide to make this relationship async, but I don't want to
+      # change the code at the moment
+      if build = @peekRecord('build', last_build_id)
+        @push(this.normalize('repo', data))
+      else
+        @findRecord('build', last_build_id).then =>
+          @push(this.normalize('repo', data))

 `export default Store`


Show unknown users in ps section

Show unknown users in ps section

--- server/api/dashboard.coffee
+++ server/api/dashboard.coffee
@@ -75,10 +75,11 @@
     promises = []
     for key, query of queries
         query["total"] = 1
-        if userLists?.length
-            query["userList"] = {$in: userLists}
-        else
-            query["userList"] = {$ne: "unknown"}
+        if key != "ps"
+            if userLists?.length
+                query["userList"] = {$in: userLists}
+            else
+                query["userList"] = {$ne: "unknown"}
         promises.push(runDashboardQuery(key, query, result))
     promises.push(runCfQuery(result))
     await awaitAll(promises)


Safeguard: Override execute instead.

Safeguard: Override execute instead.

--- server/bw/data/moves.coffee
+++ server/bw/data/moves.coffee
@@ -1604,7 +1604,7 @@
       @fail(battle, user)

 extendMove 'Safeguard', ->
-  @hit = (battle, user, target) ->
+  @execute = (battle, user, target) ->
     team = user.team
     if !team.attach(Attachment.Safeguard, {source: user})
       @fail(battle)


inversed

inversed

--- app/assets/javascripts/view.coffee
+++ app/assets/javascripts/view.coffee
@@ -244,7 +244,7 @@

 		#Rotate up
 		if buttonDown
-			cam.pitchDistance (curCoords[1]-mouseY)/mouseRotateDivision
+			cam.pitchDistance -(curCoords[1]-mouseY)/mouseRotateDivision
 			curCoords[1] = mouseY

 		if keyboard.isKeyDown(KEY_UP)
@@ -252,7 +252,7 @@

 		#Rotate down
 		if buttonDown
-			cam.pitchDistance -(curCoords[1]-mouseY)/mouseRotateDivision
+			cam.pitchDistance (curCoords[1]-mouseY)/mouseRotateDivision
 			curCoords[1] = mouseY

 		if keyboard.isKeyDown(KEY_DOWN)


Smalltalk support

Smalltalk support

--- app/utils/keys-map.coffee
+++ app/utils/keys-map.coffee
@@ -6,6 +6,7 @@
   perl6:       'Perl6'
   python:      'Python'
   scala:       'Scala'
+  smalltalk:   'Smalltalk'
   ruby:        'Ruby'
   d:           'D'
   julia:       'Julia'


Add back Atom `pane-layout-plus` package

To get keyboard navigation among panes with CTRL + <number>

--- atom/packages.cson
+++ atom/packages.cson
@@ -15,6 +15,7 @@
   "linter-write-good"
   "merge-conflicts"
   "package-sync"
+  "pane-layout-plus"
   "rails-snippets"
   "rails-transporter"
   "ruby-test"


remove unused uglify task

remove unused uglify task

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -61,5 +61,5 @@

   grunt.registerTask 'test', ['jasmine:default']
   grunt.registerTask 'dev', ['coffeelint', 'coffee:default', 'less:default', 'test']
-  grunt.registerTask 'build', ['dev', 'uglify:default']
+  grunt.registerTask 'build', ['dev']
   grunt.registerTask 'default', ['watch:default']


:memo: doc Directory::isPathIgnored

:memo: doc Directory::isPathIgnored

--- lib/directory.coffee
+++ lib/directory.coffee
@@ -49,6 +49,7 @@

     @status = newStatus if newStatus isnt @status

+  # Private: Is the given path ignored?
   isPathIgnored: (filePath) ->
     if atom.config.get('tree-view.hideVcsIgnoredFiles')
       repo = atom.project.getRepo()


levels and default game states

levels and default game states

--- src/game_over.coffee
+++ src/game_over.coffee
@@ -12,4 +12,4 @@

     drawTitleText canvas, titleText, "Press any key to start over"

-  return self+  return self


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com


push currency default to seperate var

push currency default to seperate var

--- services/web/app/coffee/Features/Subscription/SubscriptionController.coffee
+++ services/web/app/coffee/Features/Subscription/SubscriptionController.coffee
@@ -37,10 +37,11 @@
 				if hasSubscription or !plan?
 					res.redirect "/user/subscription"
 				else
+					currency = req.query.currency || "USD"
 					RecurlyWrapper.sign {
 						subscription:
 							plan_code : req.query.planCode
-							currency: req.query.currency
+							currency: currency
 						account_code: user.id
 					}, (error, signature) ->
 						return next(error) if error?
@@ -48,7 +49,7 @@
 							title      : "subscribe"
 							plan_code: req.query.planCode
 							recurlyConfig: JSON.stringify
-								currency: req.query.currency || "USD"
+								currency: currency
 								subdomain: Settings.apis.recurly.subdomain
 							subscriptionFormOptions: JSON.stringify
 								acceptedCards: ['discover', 'mastercard', 'visa']


CoffeeScript 1.3.1 (quick bugfix for compound assignment to a global variable

--- coffee-script.coffee
+++ coffee-script.coffee
@@ -21,7 +21,7 @@
   require.registerExtension '.coffee', (content) -> compile content

 # The current CoffeeScript version number.
-exports.VERSION = '1.3.0'
+exports.VERSION = '1.3.1'

 # Words that cannot be used as identifiers in CoffeeScript code
 exports.RESERVED = RESERVED


Ajout du nouveau format d'events

Ajout du nouveau format d'events

--- coffee/events.coffee
+++ coffee/events.coffee
@@ -1,24 +1,14 @@
 $(document).keydown((e) ->
-	switch e.which
-		when 37
-			input = "l"
-		when 39
-			input = "r"
-	server.postMessage "-#{input}"
+	server.postMessage {name: "+", data: e.which}
 	return
 ).keyup((e) ->
-	switch e.which
-		when 37
-			input = "l"
-		when 39
-			input = "r"
-	server.postMessage "-#{input}"
+	server.postMessage {name: "-", data: e.which}
 	return
 ).on "mousemove", (e) ->
+	movement = (e.originalEvent.movementX or e.originalEvent.webkitMovementX or 0)/(two.width/100)
+	server.postMessage {name: "m", data: movement}
 	x = Math.abs(e.clientX - window.innerWidth)/window.innerWidth
 	y = Math.abs(e.clientY - window.innerHeight)/window.innerHeight
-	movement = (e.originalEvent.movementX or e.originalEvent.webkitMovementX or 0)/(two.width/100)
-	server.postMessage "m#{movement}"
 	requestAnimationFrame ->
 		$('#viewport').css 'background-position', (x * 15) + '% ' + (y * 15) + '%, ' + (x * 10) + '% ' + (y * 10) + '%, ' + (x * 5) + '% ' + (y * 5) + '%'
 	return


Refactor Some Generator Code

Refactor Some Generator Code

Don’t be alarmed, most of these are whitespace changes!

--- lib/generator.coffee
+++ lib/generator.coffee
@@ -33,7 +33,6 @@
   start or= START

   log 'Beginning to process', (if verbose then glob else ''), duration(start)
-  deferred = Q.defer()

   # Load Style
   style = require "../styles/#{style}"
@@ -46,66 +45,61 @@

   # get extra styles first
   getExtraContent(opts['ext-styles'])
-    .then(
-      (styles) ->
-        externals.styles = styles
-        return getExtraContent(opts['ext-scripts'])
-      (err) ->
-        log colors.red err
-        return getExtraContent(opts['ext-scripts'])
-    )
-    # and extra scripts then
-    .then(
-      (scripts) ->
-        externals.scripts = scripts
+  .then (styles) ->
+    externals.styles = styles
+    return getExtraContent(opts['ext-scripts'])
+  .catch (err) ->
+    # failed to get styles, continue with scripts
+    log colors.red err
+    return getExtraContent(opts['ext-scripts'])
+  # and extra scripts then
+  .then (scripts) ->
+    externals.scripts = scripts

-        # Create output directory
-        dest = out or 'docs/'
-        log 'Writing to', dest
-        unless fs.existsSync dest
-          fs.mkdirSync dest
+    # Create output directory
+    dest = out or 'docs/'
+    log 'Writing to', dest
+    unless fs.existsSync dest
+      fs.mkdirSync dest

-        # ### Processing Pipeline
-        vfs.src glob, base: root
-        .pipe plumber()
-        .pipe map (file, cb) ->
-          if file.stat.isFile() then cb(null, file) else cb()
-        .pipe map (file, cb) ->
-          # Save start time
-          file.timingStart = process.hrtime()
-          cb(null, file)
-        .pipe t.getLanguage()
-        .pipe t.splitCodeAndComments(requireWhitespaceAfterToken: opts['whitespace-after-token'])
-        .pipe t.highlight()
-        .pipe t.renderDocTags()
-        .pipe t.markdownComments()
-        .pipe t.renderTemplates(style: style, repositoryUrl: repositoryUrl, externals: externals)
-        .pipe t.indexFile(index)
-        .pipe t.indexFiles(indexes)
-        .pipe vfs.dest(dest)
-        .pipe t.renderFileTree(path.join(dest, "toc.js"))
-        .pipe map (file, cb) ->
-          # Log process duration
-          log.verbose file.relative, duration(file.timingStart)
-          cb(null, file)
-        .on 'error', (err) ->
-          log err
-          deferred.reject(err)
-        .on 'end', ->
-          # ### Process Style
-          assetsTiming = process.hrtime()
-          style.copy(dest: dest)
-          .then ->
-            log.verbose "Style copied", duration(assetsTiming)
-            log "Done.", colors.magenta("Generated in"), duration(start)
-            deferred.resolve()
-          .then null, (err) ->
-            log colors.red("It exploded!")
-            log.verbose err.stack or err
-            deferred.reject()
+    deferred = Q.defer()

-      (err) ->
-        log colors.red err
-    )
+    # ### Processing Pipeline
+    vfs.src glob, base: root
+    .pipe plumber()
+    .pipe map (file, cb) ->
+      if file.stat.isFile() then cb(null, file) else cb()
+    .pipe map (file, cb) ->
+      # Save start time
+      file.timingStart = process.hrtime()
+      cb(null, file)
+    .pipe t.getLanguage()
+    .pipe t.splitCodeAndComments(requireWhitespaceAfterToken: opts['whitespace-after-token'])
+    .pipe t.highlight()
+    .pipe t.renderDocTags()
+    .pipe t.markdownComments()
+    .pipe t.renderTemplates(style: style, repositoryUrl: repositoryUrl, externals: externals)
+    .pipe t.indexFile(index)
+    .pipe t.indexFiles(indexes)
+    .pipe vfs.dest(dest)
+    .pipe t.renderFileTree(path.join(dest, "toc.js"))
+    .pipe map (file, cb) ->
+      # Log process duration
+      log.verbose file.relative, duration(file.timingStart)
+      cb(null, file)
+    .on 'error', deferred.reject
+    .on 'end', ->
+      # ### Process Style
+      assetsTiming = process.hrtime()
+      style.copy(dest: dest)
+      .then ->
+        log.verbose "Style copied", duration(assetsTiming)
+        deferred.resolve()
+      .catch deferred.reject

-  return deferred.promise+    return deferred.promise
+  .then (result) ->
+    log "Done.", colors.magenta("Generated in"), duration(start)
+  .catch (err) ->
+    log colors.red "It exploded!", err.message or ''
+    log.verbose err.stack or err


eliminate need for save button on forum settings

--- @client/dashboard/forum_settings.coffee
+++ @client/dashboard/forum_settings.coffee
@@ -14,7 +14,6 @@
   }
   .FORUM_SETTINGS .input_group {
     margin-bottom: 24px;
-    position: relative;
   }

   .FORUM_SETTINGS_section {
@@ -70,8 +69,9 @@
           name: 'lang'
           value: lang
           onChange: (ev) =>
-            @local.language = ev.target.value
-            save @local
+            subdomain.lang = ev.target.value
+            save subdomain
+
           style:
             fontSize: 18
             marginLeft: 12
@@ -131,6 +131,9 @@
             type: 'text'
             name: 'google_analytics_code'
             defaultValue: subdomain.google_analytics_code
+            onChange: (ev) ->
+              subdomain.google_analytics_code = ev.target.value
+              save subdomain

       else
         DIV className: 'input_group',
@@ -145,18 +148,8 @@
               'hello@consider.it'
             ' to inquire further.'

-      ########################
-      # Plan
-      if current_user.is_super_admin
-
-        DIV className: 'input_group',
-          LABEL htmlFor: 'plan', 'Account Plan (0,1,2)'
-          INPUT
-            id: 'plan'
-            type: 'text'
-            name: 'plan'
-            defaultValue: subdomain.plan
-            placeholder: '0 for free plan, 1 for custom, 2 for consulting.'
+
+

       ########################
@@ -172,6 +165,11 @@
             type: 'checkbox'
             name: 'anonymize_everything'
             defaultChecked: customization('anonymize_everything')
+            onChange: (ev) ->
+              subdomain.customizations ||= {}
+              subdomain.customizations.anonymize_everything = ev.target.checked
+              save subdomain, ->
+                arest.serverFetch('/users') # anonymity may have changed, so force a refetch

           SPAN
             className: 'toggle_switch_circle'
@@ -200,7 +198,12 @@
             type: 'checkbox'
             name: 'hide_opinions'
             defaultChecked: customization('hide_opinions')
-
+            onChange: (ev) ->
+              subdomain.customizations ||= {}
+              subdomain.customizations.hide_opinions = ev.target.checked
+              save subdomain, ->
+                arest.serverFetch('/users') # anonymity may have changed, so force a refetch
+
           SPAN
             className: 'toggle_switch_circle'

@@ -230,7 +233,11 @@
             type: 'checkbox'
             name: 'frozen'
             defaultChecked: customization('frozen')
-
+            onChange: (ev) ->
+              subdomain.customizations ||= {}
+              subdomain.customizations.frozen = ev.target.checked
+              save subdomain
+
           SPAN
             className: 'toggle_switch_circle'

@@ -259,7 +266,11 @@
             type: 'checkbox'
             name: 'email_notifications_disabled'
             defaultChecked: customization('email_notifications_disabled')
-
+            onChange: (ev) ->
+              subdomain.customizations ||= {}
+              subdomain.customizations.email_notifications_disabled = ev.target.checked
+              save subdomain
+
           SPAN
             className: 'toggle_switch_circle'

@@ -279,42 +290,31 @@
       # MODERATION SETTINGS
       @drawModerationSettings()

-
-      ########################
-      # SAVE Button
-      DIV
-        className: 'input_group'
-        BUTTON
-          className: 'primary_button button'
-          style:
-            backgroundColor: focus_color()
-          onClick: @submit
-
-          'Save'
-
-      if @local.save_complete
-        DIV style: {color: 'green'}, 'Saved.'
-
-      if @local.file_errors
-        DIV style: {color: 'red'}, 'Error uploading files!'
-
-      if @local.errors
-        if @local.errors && @local.errors.length > 0
-          DIV
+      ########################
+      # Plan
+      if current_user.is_super_admin
+
+        DIV
+          className: 'input_group'
+          LABEL htmlFor: 'plan', 'Account Plan'
+          SELECT
             style:
-              borderRadius: 8
-              margin: 20
-              padding: 20
-              backgroundColor: '#FFE2E2'
-
-            H1 style: {fontSize: 18}, 'Ooops!'
-
-            for error in @local.errors
-              DIV
-                style:
-                  marginTop: 10
-                error
-
+              display: 'block'
+            onChange: (ev) ->
+              console.log ev, ev.target.value
+              subdomain.plan = ev.target.value
+              save subdomain
+            defaultValue: subdomain.plan
+
+            OPTION
+              value: 0
+              'Free'
+            OPTION
+              value: 1
+              'Customized'
+            OPTION
+              value: 2
+              'Premium'

       if current_user.is_super_admin
         FORM
@@ -352,7 +352,7 @@
     moderatable_models = ['points', 'comments', 'proposals']

     DIV
-      className: 'FORUM_SETTINGS_section'
+      className: 'FORUM_SETTINGS_section input_group'

       H4 null,

@@ -410,35 +410,3 @@
               DIV
                 className: 'explanation field_explanation'
                 option.explanation
-
-
-  submit : ->
-
-    subdomain = fetch '/subdomain'
-    current_user = fetch '/current_user'
-
-    fields = ['plan', 'google_analytics_code', 'lang']
-
-    for f in fields
-      el = document.getElementById(f)
-      if el
-        subdomain[f] = el.value
-
-    customization_fields = ['frozen', 'email_notifications_disabled', 'hide_opinions', 'anonymize_everything']
-    customizations = subdomain.customizations
-    for f in customization_fields
-      el = document.getElementById(f)
-      if el
-        customizations[f] = el.checked
-
-    @local.save_complete = @local.file_errors = false
-    save @local
-
-    save subdomain, =>
-      if subdomain.errors
-        @local.errors = subdomain.errors
-
-      @local.save_complete = true
-      save @local
-
-      arest.serverFetch('/users') # anonymity may have changed, so force a refetch


fix for photoalbum

fix for photoalbum

--- app/scripts/views/main_view.coffee
+++ app/scripts/views/main_view.coffee
@@ -5,6 +5,7 @@
   SHORT_HEIGHT = 0
   ITTER = 0
   WIDTH = 0
+  MAX_WIDTH = 0
   DATE = [2014, 5, 21]

   constructor: ->
@@ -59,20 +60,20 @@

   slidePhoto: (event) =>
     self = $(event.currentTarget)
-    width = 0

     if self.hasClass('left-button')
-      ITTER-- if ITTER > 0
+      if ITTER > 0
+        WIDTH -= @ui.img.eq(ITTER).width()
+        ITTER--
     else if self.hasClass('right-button')
-      ITTER++ if ITTER < @count - 2
+      if ITTER < @count - 2
+        WIDTH += @ui.img.eq(ITTER).width()
+        ITTER++

-    for i in [0...ITTER]
-      width += @ui.img.eq(i).width()
-
-    if width > WIDTH
+    if WIDTH > MAX_WIDTH
+      @ui.photoalbumInner.stop().animate('left': -MAX_WIDTH, 300)
+    else
       @ui.photoalbumInner.stop().animate('left': -WIDTH, 300)
-    else
-      @ui.photoalbumInner.stop().animate('left': -width, 300)

   registrationForm: (event) =>
     event.preventDefault()
@@ -120,7 +121,9 @@
     @ui.reviewList.height(SHORT_HEIGHT)

   _initPhotoalbum: ->
+    width = 0
+
     @ui.img.each ->
-      WIDTH += $(@).width()
+      width += $(@).width()

-    WIDTH = WIDTH - $(document).width()
+    MAX_WIDTH = width - $(document).width()


Make document.coffee complain if a model tries to change-notify its 'id' attribute

This seems to be mostly when a model has save() called on it.

--- bokehjs/src/coffee/common/document.coffee
+++ bokehjs/src/coffee/common/document.coffee
@@ -289,6 +289,9 @@
         console.log("Cannot create a patch using events from a different document, event had ", event.document, " we are ", @)
         throw new Error("Cannot create a patch using events from a different document")
       if event instanceof ModelChangedEvent
+        if event.attr == 'id'
+          console.log("'id' field is immutable and should never be in a ModelChangedEvent ", event)
+          throw new Error("'id' field should never change, whatever code just set it is wrong")
         value = event.new_
         value_json = HasProperties._value_to_json('new_', value, event.model)
         value_refs = {}


Fixing bone-hand.coffee to require THREE if npm'd

--- main/bone-hand/leap.bone-hand.coffee
+++ main/bone-hand/leap.bone-hand.coffee
@@ -1,4 +1,6 @@
 scope = null
+
+if require then THREE = require('three')

 initScene = (targetEl, scale)->
   scope.scene = new THREE.Scene()
@@ -439,4 +441,4 @@

     hand: onHand

-  }+  }


misc(gruntfile.coffee): compiling and watching e2e.coffee files

--- gruntfile.coffee
+++ gruntfile.coffee
@@ -192,7 +192,7 @@
           process: processBuildScripts
       build_protractor:
         cwd: './src'
-        src: ['**/*.protractor.js', '**/*.e2e.js']
+        src: ['**/*.protractor.js', '**/*.e2e.coffee']
         dest: '<%= build_dir %>src/e2e/'
         expand: true
         flatten: true
@@ -481,7 +481,7 @@
         options:
           livereload: false
       tests:
-        files: ['src/**/*.spec.js', 'src/**/*.e2e.js', 'src/**/*.protractor.js']
+        files: ['src/**/*.spec.js', 'src/**/*.e2e.coffee', 'src/**/*.protractor.coffee']
         tasks: [
           'newer:copy:build_karma'
           'newer:copy:build_unit'


Don't process special tags twice - they're unpacked into classattributes. Also, don't process specialtags on the restored instance.

--- layout.coffee
+++ layout.coffee
@@ -466,7 +466,7 @@
         # console.log 'defer', child
         child.$defer = true

-    processSpecialTags(el, attributes, attributes.type)
+    processSpecialTags(el, attributes, attributes.type) unless tagname is 'class'

     parent = new lz[tagname](el, attributes)

@@ -584,7 +584,7 @@
         for child in children
           child.$defer = null
           # console.log 'creating class child in parent', child, parent
-          initFromElement(child, parent)
+          initFromElement(child, parent) unless child.localName in specialtags
         return


Added a logoff function

Added a logoff function

--- src/fogbugz.coffee
+++ src/fogbugz.coffee
@@ -5,6 +5,11 @@
 LogOn = (username, password, token, callback) ->
 	CallApi('cmd=logon&email=' + username + '&password=' + password, null, (err, result) ->
 		if err then callback(new Error('Error logging in ' + err.message)) else callback(null, result['token'][0])
+	)
+
+LogOff = (token, callback) ->
+	CallApi('cmd=logoff&token=',token, (err, result) ->
+		if err then callback(new Error('Error logging off ' + err.message)) else callback(true)
 	)

 SetURL = (url) -> fogbugzURL = url


Added dd() blade directive snippet.

Added dd() blade directive snippet.

--- snippets/laravel_blade.cson
+++ snippets/laravel_blade.cson
@@ -12,6 +12,12 @@
     'rightLabelHTML': 'Blade {{ __(\'key.val\') }}'
     'body': "{{ __('${1:file_name}.${2:key_name}') }}"

+  'translate blade directive trans':
+    'prefix': 'dd'
+    'leftLabelHTML': 'Ⓢ'
+    'rightLabelHTML': 'Blade {{ dd($something) }}'
+    'body': "{{ dd(${1:something}) }}"
+
   'translate blade directive':
     'prefix': 'lara:translation-file-declaration'
     'leftLabelHTML': 'Ⓢ'


remove old spreadsheet key

remove old spreadsheet key

--- src/mailButler.appsscript.coffee
+++ src/mailButler.appsscript.coffee
@@ -70,7 +70,6 @@
   @LABEL_OUTBOX:        MailButler.LABEL_BASE + '/' + 'Scheduled'
   @FREQUENCY_MINUTES:   5
   @DB:                  MailButlerDBLibrary.Db
-  @SPREADSHEET_KEY:     '0Av57s24WBhpmdHRPVU9UQkx6bXhoMzlXWVJNVzJGT0E'

   prefix: null


update all plugins

update all plugins

--- atom/config.cson
+++ atom/config.cson
@@ -13,3 +13,13 @@
     fontFamily: "Source Code Pro"
     showIndentGuide: true
     showInvisibles: true
+  minimap:
+    plugins:
+      "git-diff": true
+      "find-and-replace": true
+  "git-log":
+    fontFamily: "Source Code Pro"
+    fontScale: 1
+  "atom-ternjs": {}
+  "atom-beautify":
+    _analyticsUserId: "83429072-4046-4dd1-85f5-5a216dc56d2a"


Removed expression as a transient quality for equality feature -- confusing

--- math/expression_equality_spec.coffee
+++ math/expression_equality_spec.coffee
@@ -22,13 +22,6 @@
           @comps.number.build(value: 10),
           @comps.number.build(value: 10)
           )).toEqual true
-
-    describe "checking against an expression", ->
-      it "matches if the first element in the expression matches", ->
-        expect(@comps.number.build(value: 10)).toBeAnEqualExpressionTo(
-          @comps.expression.build(expression: [
-              @comps.number.build(value: 10)
-              ]))

   describe "expression comparison", ->
     it "accepts for empty expressions", ->
@@ -61,26 +54,6 @@
             ])
           )).toEqual false

-
-    describe "comparing an expression against non-expressions", ->
-      it "accepts if the first part of the sub expression is equal to what it is comparing against ", ->
-        expect(
-          @isEqual(
-            @comps.expression.build(expression: [
-              @comps.number.build(value: 10)
-              ]),
-            @comps.number.build(value: 10)
-          )).toEqual true
-
-      it "rejects if the first part of the sub expression is not equal", ->
-        expect(
-          @isEqual(
-            @comps.expression.build(expression: [
-              @comps.number.build(value: 10)
-              ]),
-            @comps.number.build(value: 11)
-          )).toEqual false
-
   describe "addition", ->
     it "accepts two addition symbols", ->
       expect(
@@ -104,17 +77,6 @@
           @comps.blank.build(),
           @comps.number.build(value: 1)
         )).toEqual false
-    it "accepts against expressions that contain a single blank element", ->
-      expect(
-        @isEqual(
-          @comps.blank.build(),
-          @exp_builder(null)
-        )).toEqual true
-      expect(
-        @isEqual(
-          @exp_builder(null),
-          @comps.blank.build()
-        )).toEqual true

   describe "exponentiation", ->
     it "accepts exponents with same base and power", ->


Add test for B-Wing/E #19

Add test for B-Wing/E #19

--- tests/test_ship_selector.coffee
+++ tests/test_ship_selector.coffee
@@ -143,6 +143,36 @@
     casper.then ->
         test.assertDoesntExist("#rebel-builder #{common.selectorForUpgradeIndex 1, 5}", "Elite slot was rescinded")

+    # B-Wing/E grants crew slot
+    common.setShipType('#rebel-builder', 1, 'B-Wing')
+    common.setPilot('#rebel-builder', 1, 'Blue Squadron Pilot')
+    common.addUpgrade('#rebel-builder', 1, 1, 'Advanced Sensors')
+    common.addUpgrade('#rebel-builder', 1, 2, 'Heavy Laser Cannon')
+    common.addUpgrade('#rebel-builder', 1, 3, 'Proton Torpedoes')
+    common.addUpgrade('#rebel-builder', 1, 4, 'Advanced Proton Torpedoes')
+    common.addUpgrade('#rebel-builder', 1, 5, 'B-Wing/E')
+    common.addUpgrade('#rebel-builder', 1, 6, 'Munitions Failsafe')
+    common.addUpgrade('#rebel-builder', 1, 7, 'Gunner')
+    # Switching to Ten Numb should keep everything (except for new empty elite slot)
+    common.setPilot('#rebel-builder', 1, 'Ten Numb')
+    common.assertNoUpgradeInSlot(test, '#rebel-builder', 1, 1)
+    common.assertUpgradeInSlot(test, '#rebel-builder', 1, 2, 'Advanced Sensors')
+    common.assertUpgradeInSlot(test, '#rebel-builder', 1, 3, 'Heavy Laser Cannon')
+    common.assertUpgradeInSlot(test, '#rebel-builder', 1, 4, 'Proton Torpedoes')
+    common.assertUpgradeInSlot(test, '#rebel-builder', 1, 5, 'Advanced Proton Torpedoes')
+    common.assertUpgradeInSlot(test, '#rebel-builder', 1, 6, 'B-Wing/E')
+    common.assertUpgradeInSlot(test, '#rebel-builder', 1, 7, 'Munitions Failsafe')
+    common.assertUpgradeInSlot(test, '#rebel-builder', 1, 8, 'Gunner')
+    # Switching down to Dagger squad should keep everything but elite
+    common.setPilot('#rebel-builder', 1, 'Dagger Squadron Pilot')
+    common.assertUpgradeInSlot(test, '#rebel-builder', 1, 1, 'Advanced Sensors')
+    common.assertUpgradeInSlot(test, '#rebel-builder', 1, 2, 'Heavy Laser Cannon')
+    common.assertUpgradeInSlot(test, '#rebel-builder', 1, 3, 'Proton Torpedoes')
+    common.assertUpgradeInSlot(test, '#rebel-builder', 1, 4, 'Advanced Proton Torpedoes')
+    common.assertUpgradeInSlot(test, '#rebel-builder', 1, 5, 'B-Wing/E')
+    common.assertUpgradeInSlot(test, '#rebel-builder', 1, 6, 'Munitions Failsafe')
+    common.assertUpgradeInSlot(test, '#rebel-builder', 1, 7, 'Gunner')
+
     common.removeShip('#rebel-builder', 1)

     common.openEmpireBuilder()


Fix routes bug

Fix routes bug

--- routes/index.coffee
+++ routes/index.coffee
@@ -10,7 +10,7 @@
   # GET main photo page
   home: router.get '/home', (req, res) ->
     if req.user
-      api.get.photos (body) ->
+      api.get.photos null, (body) ->
         res.render 'home',
           user: req.user
           selfies: body


Add progress message for package installation

--- src/installer.coffee
+++ src/installer.coffee
@@ -213,14 +213,29 @@

             async.waterfall(commands, callback)

+  installPackageDependencies: (options, callback) ->
+    process.stdout.write 'Installing packages '
+
+    options = _.extend({}, options, installGlobally: false)
+    commands = []
+    for name, version of @getPackageDependencies()
+      commands.push (callback) =>
+        @installPackage({name, version}, options, callback)
+
+    async.waterfall commands, (error) ->
+      if error?
+        process.stdout.write '\u2717\n'.red
+      else
+        process.stdout.write '\u2713\n'.green
+
+      callback(error)
+
   installDependencies: (options, callback) ->
     options.installGlobally = false
     commands = []
     commands.push(@installNode)
     commands.push (callback) => @installModules(options, callback)
-    for name, version of @getPackageDependencies()
-      commands.push (callback) =>
-        @installPackage({name, version}, options, callback)
+    commands.push (callback) => @installPackageDependencies(options, callback)
     if options.argv.dev
       commands.push (callback) => @installDevDependencies(options, callback)


heatmap: remove some extraneous code

heatmap: remove some extraneous code

--- src/heatmap.coffee
+++ src/heatmap.coffee
@@ -11,8 +11,6 @@
     colors = chartOpts?.colors ? ["slateblue", "white", "crimson"] # vector of three colors for the color scale (negative - zero - positive)
     zlim = chartOpts?.zlim ? null # z-axis limits (if null take from data, symmetric about 0)
     zthresh = chartOpts?.zthresh ? null # z threshold; if |z| < zthresh, not shown
-    boxcolor = chartOpts?.boxcolor ? "black"     # color of outer rectangle box
-    boxwidth = chartOpts?.boxwidth ? 2           # width of outer box in pixels
     tipclass = chartOpts?.tipclass ? "tooltip" # class name for tool tips
     # chartOpts end
     xscale = null
@@ -148,18 +146,6 @@
                                                   if data.ycat?
                                                       svg.select("text#ylab#{d.y}").attr("opacity", 0))

-        # add box again
-        svg.append("rect")
-           .attr("height", svg.attr("height")-margin.top-margin.bottom)
-           .attr("width", svg.attr("width")-margin.left-margin.right)
-           .attr("x", margin.left)
-           .attr("y", margin.top)
-           .attr("fill", "none")
-           .attr("stroke", boxcolor)
-           .attr("stroke-width", boxwidth)
-           .attr("shape-rendering", "crispEdges")
-           .style("pointer-events", "none")
-
         # handle categorical scales:
         #    replace text with category labels, add IDs, and hide them initially
         if data.xcat?


Improve regex to fix case where multiple mentions did not work, fixes #415

--- src/scripts/jira.coffee
+++ src/scripts/jira.coffee
@@ -177,7 +177,7 @@
     search msg, msg.match[2], (text) ->
       msg.reply text

-  robot.hear /([^\w\-]|^)(\w+-[0-9]+)([^\w]|$)/ig, (msg) ->
+  robot.hear /([^\w\-]|^)(\w+-[0-9]+)(?=[^\w]|$)/ig, (msg) ->
     if msg.message.user.id is robot.name
       return


Fixed bug where punch feedback was not being adjusted for user timezone

--- src/scripts/time.coffee
+++ src/scripts/time.coffee
@@ -120,7 +120,7 @@
             dateQualifier = "yesterday"
           else
             dateQualifier = "on #{time.format('MMM Do, YYYY')}"
-          timeQualifier = " at #{time?.format('h:mma')} #{dateQualifier}"
+          timeQualifier = " at #{time?.tz(user.timetable?.timezone?.name).format('h:mma')} #{dateQualifier}"
         if punch.elapsed? and not punch.times.block?
           elapsedQualifier = " (#{+punch.elapsed.toFixed(2)} hours)"
         else


updated backlog

updated backlog

--- src/ki-router.coffee
+++ src/ki-router.coffee
@@ -19,7 +19,8 @@
 "use strict"

 # Bugs
-# - IE9, use hashEvent to trigger rendering
+# - IE9, use hashEvent to trigger first rendering
+# - ki-router should catch clicks for # and #! urls
 # TODO:
 # - Restore scroll position
 # - Assign multiple routes with one invocation


TODO teste isSkuSelectable

TODO teste isSkuSelectable

--- spec/SkuSelectorSpec.coffee
+++ spec/SkuSelectorSpec.coffee
@@ -45,6 +45,8 @@
 		it 'should find available skus', ->
 			availableSkus = (sku for sku in @ss.skus when sku.available is true)
 			expect(@ss.findAvailableSkus()).toEqual(availableSkus)
+
+		#TODO it 'should assert that a sku is selectable', ->

 		#TODO it 'should find the selectable skus', ->


Update to auto-change block size by algo

--- main.coffee
+++ main.coffee
@@ -370,14 +370,28 @@
         $('#algo').change(->
                 selected = $('#algo').val()

-                sort =
-                        switch $(@).children(':selected').attr('id')
-                                when 'bsort'  then bsort
-                                when 'isort'  then isort
-                                when 'qsort1' then -> qsort(false)
-                                when 'qsort2' then -> qsort(true)
-                                when 'hsort' then hsort
-                                when 'ssort' then ssort
+                switch $(@).children(':selected').attr('id')
+                        when 'ssort'
+                                rectWidth = 32
+                                sort = ssort
+                        when 'bsort'
+                                rectWidth = 32
+                                sort = bsort
+                        when 'isort'
+                                rectWidth = 32
+                                sort = isort
+                        when 'qsort1'
+                                rectWidth = 10
+                                sort = -> qsort(false)
+                        when 'qsort2'
+                                rectWidth = 10
+                                sort = -> qsort(true)
+                        when 'hsort'
+                                rectWidth = 10
+                                sort = hsort
+
+                rectHeight = rectWidth
+                $('#squareSize').val(rectWidth)

                 reset()
         )


stop inlining invokeClosure (as we already do in ejs-es6), copy over the loadCachedEjsValue changes, and also pre-allocate the array to the correct length when emitting code for ArrayExpressions.

--- lib/compiler.coffee
+++ lib/compiler.coffee
@@ -279,7 +279,7 @@
                         slot              : true
                         setSlot           : true

-                        invokeClosure     : true
+                        invokeClosure     : false
                         makeClosure       : true
                         makeAnonClosure   : true
                         createArgScratchArea : true
@@ -347,53 +347,32 @@
                 rv.setAlignment 8
                 rv

+        loadCachedEjsValue: (name, init) ->
+                alloca_name = "#{name}_alloca"
+                load_name = "#{name}_load"
+
+                if @currentFunction[alloca_name]
+                        alloca = @currentFunction[alloca_name]
+                else
+                        alloca = @createAlloca(@currentFunction, types.EjsValue, alloca_name)
+                        @currentFunction[alloca_name] = alloca
+                        @doInsideBBlock(@currentFunction.entry_bb, () -> init(alloca))
+
+                ir.createLoad(alloca, load_name)
+
         loadBoolEjsValue: (n) ->
-                name = if n then "true_alloca" else "false_alloca"
-                if @currentFunction[name]?
-                        bool_alloca = @currentFunction[name]
-                else
-                        bool_alloca = @createAlloca @currentFunction, types.EjsValue, name
-                        @currentFunction[name] = bool_alloca
-                        @doInsideBBlock @currentFunction.entry_bb, =>
-                                alloca_as_int64 = ir.createBitCast bool_alloca, types.int64.pointerTo(), "alloca_as_pointer"
-                                if @options.target_pointer_size is 64
-                                        ir.createStore consts.int64_lowhi(0xfff98000, if n then 0x00000001 else 0x000000000), alloca_as_int64
-                                else
-                                        ir.createStore consts.int64_lowhi(0xffffff83, if n then 0x00000001 else 0x00000000), alloca_as_int64
-                rv = ir.createLoad bool_alloca, "#{name}_load"
+                rv = @loadCachedEjsValue n, (alloca) =>
+                        alloca_as_int64 = ir.createBitCast(alloca, types.int64.pointerTo(), "alloca_as_pointer")
+                        if @options.target_pointer_size is 64
+                                ir.createStore(consts.int64_lowhi(0xfff98000,  if n then 0x00000001 else 0x000000000), alloca_as_int64)
+                        else
+                                ir.createStore(consts.int64_lowhi(0xffffff83,  if n then 0x00000001 else 0x000000000), alloca_as_int64)
                 rv._ejs_returns_ejsval_bool = true
                 rv

-        loadDoubleEjsValue: (n) ->
-                name = "num_#{n}_alloca"
-                if @currentFunction[name]?
-                        num_alloca = @currentFunction[name]
-                else
-                        num_alloca = @createAlloca @currentFunction, types.EjsValue, name
-                        @currentFunction[name] = num_alloca
-                        @doInsideBBlock @currentFunction.entry_bb, =>
-                                @storeDouble num_alloca, n
-                ir.createLoad num_alloca, "numload"
-
-        loadNullEjsValue: ->
-                if @currentFunction.null_alloca?
-                        null_alloca = @currentFunction.null_alloca
-                else
-                        null_alloca = @createAlloca @currentFunction, types.EjsValue, "null_alloca"
-                        @currentFunction.null_alloca = null_alloca
-                        @doInsideBBlock @currentFunction.entry_bb, =>
-                                @storeNull null_alloca
-                ir.createLoad null_alloca, "nullload"
-
-        loadUndefinedEjsValue: ->
-                if @currentFunction.undef_alloca?
-                        undef_alloca = @currentFunction.undef_alloca
-                else
-                        undef_alloca = @createAlloca @currentFunction, types.EjsValue, "undef_alloca"
-                        @currentFunction.undef_alloca = undef_alloca
-                        @doInsideBBlock @currentFunction.entry_bb, =>
-                                @storeUndefined undef_alloca
-                ir.createLoad undef_alloca, "undefload"
+        loadDoubleEjsValue:    (n) -> @loadCachedEjsValue("num_#{n}", (alloca) => @storeDouble(alloca, n))
+        loadNullEjsValue:      ()  -> @loadCachedEjsValue("null",     (alloca) => @storeNull(alloca))
+        loadUndefinedEjsValue: ()  -> @loadCachedEjsValue("undef",    (alloca) => @storeUndefined(alloca))

         storeUndefined: (alloca, name) ->
                 alloca_as_int64 = ir.createBitCast alloca, types.int64.pointerTo(), "alloca_as_pointer"
@@ -1341,7 +1320,7 @@
                 obj

         visitArrayExpression: (n) ->
-                obj = @createCall @ejs_runtime.array_new, [consts.int32(0), consts.false()], "arrtmp", !@ejs_runtime.array_new.doesNotThrow
+                obj = @createCall @ejs_runtime.array_new, [consts.int32(n.elements.length), consts.false()], "arrtmp", !@ejs_runtime.array_new.doesNotThrow
                 i = 0;
                 for el in n.elements
                         val = @visit el


add args check for '--' to stop processing args

--- bin/op/args.coffee
+++ bin/op/args.coffee
@@ -12,8 +12,11 @@

   for arg in args # look at each arg and decide what to do with it

+    # stop when we see --
+    if '--' is arg then break
+
     # if it's an existing directory then it's our basedir
-    if isDir arg then op.options.basedir = arg
+    else if isDir arg then op.options.basedir = arg

     # if it's a file ending with 'browserify.js' then it's the input file
     else if arg[-13...] is 'browserify.js'


Don't spam console with resize messages.

--- lib/term-view.coffee
+++ lib/term-view.coffee
@@ -138,7 +138,7 @@
     return unless @term
     return if @term.rows is rows and @term.cols is cols
     return unless cols > 0 and rows > 0 and isFinite(cols) and isFinite(rows)
-    console.log @term.rows, @term.cols, "->", rows, cols
+    # console.log @term.rows, @term.cols, "->", rows, cols
     try
       if @ptyProcess
         @ptyProcess.send {event: 'resize', rows, cols}


Get selection and expansion working

Get selection and expansion working

--- lib/tree-view.coffee
+++ lib/tree-view.coffee
@@ -183,7 +183,7 @@
       @show()

   entryClicked: (e) ->
-    entry = $(e.currentTarget).view()
+    entry = e.currentTarget
     isRecursive = e.altKey || false
     switch e.originalEvent?.detail ? 1
       when 1
@@ -191,9 +191,9 @@
         @openSelectedEntry(false) if entry instanceof FileView
         entry.toggleExpansion(isRecursive) if entry instanceof DirectoryView
       when 2
-        if entry.is('.selected.file')
+        if entry instanceof FileView
           atom.workspaceView.getActiveView()?.focus()
-        else if entry.is('.selected.directory')
+        else if DirectoryView
           entry.toggleExpansion(isRecursive)

     false
@@ -276,18 +276,15 @@
       atom.clipboard.write(pathToCopy)

   entryForPath: (entryPath) ->
-    return null
-
-    fn = (bestMatchEntry, element) ->
-      entry = $(element).view()
+    bestMatchEntry = @root
+    for entry in @list.element.querySelectorAll('.entry')
       if entry.getPath() is entryPath
-        entry
-      else if entry.getPath().length > bestMatchEntry.getPath().length and entry.directory?.contains(entryPath)
-        entry
-      else
-        bestMatchEntry
-
-    @list.find(".entry").toArray().reduce(fn, @root)
+        bestMatchEntry = entry
+        break
+      else if entry.getPath().length > bestMatchEntry.getPath().length and entry.directory?.contains?(entryPath)
+        bestMatchEntry = entry
+
+    bestMatchEntry

   selectEntryForPath: (entryPath) ->
     @selectEntry(@entryForPath(entryPath))
@@ -534,15 +531,15 @@
     @list.find('.selected')?.view()

   selectEntry: (entry) ->
-    entry = entry?.view?()
-    return false unless entry?
-
-    @selectedPath = entry.getPath()
-    @deselect()
-    entry.addClass('selected')
+    if entry?
+      @selectedPath = entry.getPath()
+      @deselect()
+      entry.classList.add('selected')

   deselect: ->
-    @list.find('.selected').removeClass('selected')
+    for selected in @list.element.querySelectorAll('.selected')
+      selected.classList.remove('selected')
+    undefined

   scrollTop: (top) ->
     if top?


Use new methods for observing workspace, project, config

--- lib/tree-view.coffee
+++ lib/tree-view.coffee
@@ -112,17 +112,17 @@
       else
         @selectActiveFile()

-    @subscribe atom.workspaceView, 'pane-container:active-pane-item-changed', =>
+    @subscribe atom.workspace.onDidChangeActivePaneItem =>
       @selectActiveFile()
-    @subscribe atom.project, 'path-changed', =>
+    @subscribe atom.project.onDidChangePaths =>
       @updateRoot()
-    @subscribe atom.config.observe 'tree-view.hideVcsIgnoredFiles', callNow: false, =>
+    @subscribe atom.config.onDidChange 'tree-view.hideVcsIgnoredFiles', =>
       @updateRoot()
-    @subscribe atom.config.observe 'tree-view.hideIgnoredNames', callNow: false, =>
+    @subscribe atom.config.onDidChange 'tree-view.hideIgnoredNames', =>
       @updateRoot()
-    @subscribe atom.config.observe 'core.ignoredNames', callNow: false, =>
+    @subscribe atom.config.onDidChange 'core.ignoredNames', =>
       @updateRoot() if atom.config.get('tree-view.hideIgnoredNames')
-    @subscribe atom.config.observe 'tree-view.showOnRightSide', callNow: false, (newValue) =>
+    @subscribe atom.config.onDidChange 'tree-view.showOnRightSide', ({newValue}) =>
       @onSideToggled(newValue)

   toggle: ->


Finished fixing inline tree display

Finished fixing inline tree display

--- lib/tree-view.coffee
+++ lib/tree-view.coffee
@@ -67,22 +67,22 @@
     view

   treeView: (head, children, btnOptions) ->
+    icon = span('icon icon-chevron-right open')
+    body = div('body gutted', children)

     if btnOptions.button_text
       btn = button(treeButtonClasses(btnOptions), btnOptions.button_text)
-      header = div('header gutted', [head, btn])
+      header = div("header gutted", [span('header-text', [head]), btn])
+      children = [icon, header, body]
     else
-      header = div('header gutted', [head])
+      header = div("header gutted", [span('header-text', [head])])
+      children = [icon, header, body]

-    view = views.render(div('ink proto-tree', [
-      span('icon icon-chevron-right open'),
-      header,
-      div('body gutted', children)
-    ]))
+    view = views.render(div('ink proto-tree', children))
     if btnOptions.button_text
       view.querySelector('button').onclick = => treeButtonClicked(btnOptions, view)

-    for sel in [':scope > .header', ':scope > .icon']
+    for sel in [':scope > .header > .header-text', ':scope > .icon']
       view.querySelector(sel).onclick = =>
         setTimeout (=> @toggle view), 0
     @toggle view


fix final sso redirect

fix final sso redirect

--- src/smc-hub/auth.coffee
+++ src/smc-hub/auth.coffee
@@ -216,7 +216,7 @@
                             cb            : cb
             ], cb)
         (cb) ->
-            target = BASE_URL + "/#login"
+            target = BASE_URL + "/app#login"

             if has_valid_remember_me
                 opts.res.redirect(target)


:art:

:art:

--- lib/vim-state.coffee
+++ lib/vim-state.coffee
@@ -205,9 +205,9 @@
   # Private: Push the given operations onto the operation stack, then process
   # it.
   pushOperations: (operations) ->
-    @processing = true
+    return unless operations?
     try
-      return unless operations?
+      @processing = true
       operations = [operations] unless _.isArray(operations)

       for operation in operations


Use `.val()` instead of `.attr('value', …)`, fix #2543

--- src/resources-packaged/ops/javascript/orbeon/xforms/control/Calendar.coffee
+++ src/resources-packaged/ops/javascript/orbeon/xforms/control/Calendar.coffee
@@ -30,9 +30,11 @@
 Event.onDOMReady ->

     setValue = (control, formattedDate) ->
-        attributeName = if $(control).is(".xforms-input-appearance-minimal") then "alt" else "value"
         inputHolder = $(control).children('.xforms-input-input').first()
-        $(inputHolder).attr(attributeName, formattedDate)
+        isMinimal = $(control).is(".xforms-input-appearance-minimal")
+        if   isMinimal
+        then $(inputHolder).attr('alt', formattedDate)
+        else $(inputHolder).val(formattedDate)
         value = Controls.getCurrentValue(control)
         changeEvent = new ORBEON.xforms.server.AjaxServer.Event(null, control.id, value, "xxforms-value")
         ORBEON.xforms.server.AjaxServer.fireEvents([changeEvent], false)


Correct compile cache afterEach cleanup

Correct compile cache afterEach cleanup

--- spec/compile-cache-spec.coffee
+++ spec/compile-cache-spec.coffee
@@ -21,8 +21,8 @@
     spyOn(TypeScriptSimple::, 'compile').andReturn 'the-typescript-code'

   afterEach ->
+    CompileCache.setAtomHomeDirectory(process.env.ATOM_HOME)
     CSON.setCacheDir(CompileCache.getCacheDirectory())
-    CompileCache.setAtomHomeDirectory(process.env.ATOM_HOME)
     temp.cleanupSync()

   describe 'addPathToCache(filePath, atomHome)', ->