fixing selections on fastpath

fixing selections on fastpath

--- static/coffee/renderers/glyph/circle.coffee
+++ static/coffee/renderers/glyph/circle.coffee
@@ -68,8 +68,8 @@
           props =  @selection_glyphprops
         else
           props = @glyph_props
-        @_fast_path(ctx, props, 'selected')
-        @_fast_path(ctx, @nonselection_glyphprops, 'unselected')
+        @_fast_path(ctx, props, true)
+        @_fast_path(ctx, @nonselection_glyphprops, false)
       else
         @_fast_path(ctx)
     else
@@ -88,7 +88,7 @@
     if not glyph_props
       glyph_props = @glyph_props
     if glyph_props.fill_properties.do_fill
-      @glyph_props.fill_properties.set(ctx, @glyph_props)
+      glyph_props.fill_properties.set(ctx, @glyph_props)
       ctx.beginPath()
       for i in [0..@sx.length-1]
         if isNaN(@sx[i] + @sy[i] + @radius[i]) or not @mask[i]
@@ -102,7 +102,7 @@
       ctx.fill()

     if glyph_props.line_properties.do_stroke
-      @glyph_props.line_properties.set(ctx, @glyph_props)
+      glyph_props.line_properties.set(ctx, @glyph_props)
       ctx.beginPath()
       for i in [0..@sx.length-1]
         if isNaN(@sx[i] + @sy[i] + @radius[i]) or not @mask[i]


Fix list of routes on startup

Fix list of routes on startup

--- coffeescripts/app.coffee
+++ coffeescripts/app.coffee
@@ -17,7 +17,7 @@
 # Setup routes
 for route in routes
 	for name, config of route
-		console.log "name : #{config.auth}"
+		console.log "#{name}"
 		url = name.split('.')[0];
 		if config.auth in ['true', 'optional']
 			app[config.method](url, (req, res, next) -> utils.setupAuthData req, res, next, config)


Test enrollment code form

Test enrollment code form

--- test/components/course-settings.spec.coffee
+++ test/components/course-settings.spec.coffee
@@ -46,3 +46,8 @@
       ), 'textContent')
       expect(rendered_names)
         .to.deep.equal(names)
+
+
+  it 'renders input box to copy enrollment code from', ->
+    url = @state.div.querySelector('.enrollment-code-link input').value
+    expect(url).to.equal(COURSE.periods[0].enrollment_url)


Show modal overlay in create-your-first-factlink

--- core/app/assets/javascripts/tour.coffee
+++ core/app/assets/javascripts/tour.coffee
@@ -110,6 +110,12 @@
           @popoverRemove '.factlink.fl-first'
           @state.transition()

+        onopen_modal: =>
+          FactlinkApp.Overlay.show()
+
+        onclose_modal: =>
+          FactlinkApp.Overlay.hide()
+
   factlinkFirstExists: ->
     @$('.factlink.fl-first').length > 0


Added queueing support.

Added queueing support.

--- app/assets/javascripts/app/lib/app_post/ajax.js.coffee
+++ app/assets/javascripts/app/lib/app_post/ajax.js.coffee
@@ -19,10 +19,15 @@
     cache: false
     async: true

-  queue_list: {}
+  current_request: {}
+  queue_list: []
+  queue_running: false
   count: 0

   constructor: (@args) ->
+
+    # run queue
+    @_run()

     # bindings
     $('body').bind( 'ajaxSend', =>
@@ -57,16 +62,28 @@
       )
     )

-  ajax: (params, defaults) ->
-    data = $.extend({}, @defaults, defaults, params)
+  ajax: (params) ->
+    data = $.extend({}, @defaults, params )
     if params['id']
-      if @queue_list[ params['id'] ]
-        @queue_list[ params['id'] ].abort()
-      @queue_list[ params['id'] ] = $.ajax( data )
+      if @current_request[ params['id'] ]
+        @current_request[ params['id'] ].abort()
+      @current_request[ params['id'] ] = $.ajax( data )
     else
-      $.ajax( data )
+      if params['queue']
+        @queue_list.push data
+        if !@queue_running
+          @_run()
+      else
+        $.ajax(data)

-#    console.log('AJAX', params['url'] )
+  _run: =>
+    if @queue_list && @queue_list[0]
+      @queue_running = true
+      request = @queue_list.shift()
+      request.complete = =>
+        @queue_running = false
+        @_run()
+      $.ajax( request )

   _show_spinner: =>
     @count++


Make the details link for info working dynamically

--- app/assets/javascripts/components/users/base.js.coffee
+++ app/assets/javascripts/components/users/base.js.coffee
@@ -7,15 +7,16 @@

   @userDetailsTooltip: ->
     tooltipClass = '.information-tooltip'
-    $("#{tooltipClass}-trigger").toggle( (ev) ->
+    $(document).on('click', "#{tooltipClass}-trigger", (ev) ->
       ev.preventDefault()
-      $("#{tooltipClass}-trigger").not(@).siblings(tooltipClass).hide()
-      $(@).html(' close')
-      $(@).siblings(tooltipClass).show()
-    , (ev) ->
-      ev.preventDefault()
-      $(@).html(' details')
-      $(@).siblings(tooltipClass).hide()
+      if ($(@).find('.fa-align-left').length)
+        $("#{tooltipClass}-trigger").not(@).siblings(tooltipClass).hide()
+        $("#{tooltipClass}-trigger").not(@).html(' details')
+        $(@).html(' close')
+        $(@).siblings(tooltipClass).show()
+      else
+        $(@).html(' details')
+        $(@).siblings(tooltipClass).hide()
     )

 $(document).ready -> UsersComponent.initialize()


EDSC-58: Fix parameters not being applied to paginated queries

--- app/assets/javascripts/models/data/xhr_model.js.coffee
+++ app/assets/javascripts/models/data/xhr_model.js.coffee
@@ -24,10 +24,13 @@
       params.page_num = @page = 1
       @_loadAndSet params, [], callback

-    loadNextPage: (params=@query.params(), callback=null) =>
+    loadNextPage: (params=@params(), callback=null) =>
       if @hasNextPage() and !@isLoading()
         params.page_num = ++@page
         @_loadAndSet params, @results(), callback
+
+    params: ->
+      @query.params()

     abort: ->
       if @currentRequest? && @currentRequest.readystate != 4


fixed breaking change with npm5 (#2312)

fixed breaking change with npm5 (#2312)

--- gulpfile.iced
+++ gulpfile.iced
@@ -134,33 +134,42 @@
 task 'init', "" ,(done)->
   Fail "YOU MUST HAVE NODEJS VERSION GREATER THAN 6.9.5" if semver.lt( process.versions.node , "6.9.5" )

-  if (! test "-d","#{basefolder}/src/autorest-core")
-    echo warning "\n#{ error 'NOTE:' } #{ info 'src/autorest-core'} appears to be missing \n      fixing with #{ info 'git checkout src/autorest-core'}"
-    echo warning "      in the future do a #{ info 'gulp clean'} before using #{ info 'git clean'} .\n"
-    exec "git checkout #{basefolder}/src/autorest-core"
+  execute "npm -v", (code,stdout,stderr) ->
+    isV5 = stdout.startsWith( "5" )

-  return done() if initialized
-  global.initialized = true
-  # if the node_modules isn't created, do it.
-  doit = true if (newer "#{basefolder}/package.json",  "#{basefolder}/node_modules")
+    if (! test "-d","#{basefolder}/src/autorest-core")
+      echo warning "\n#{ error 'NOTE:' } #{ info 'src/autorest-core'} appears to be missing \n      fixing with #{ info 'git checkout src/autorest-core'}"
+      echo warning "      in the future do a #{ info 'gulp clean'} before using #{ info 'git clean'} .\n"
+      exec "git checkout #{basefolder}/src/autorest-core"

-  typescriptProjectFolders()
-    .on 'end', ->
-      if doit
-        echo warning "\n#{ info 'NOTE:' } 'node_modules' may be out of date - running 'npm install' for you.\n"
-        exec "npm install",{silent:false},(c,o,e)->
-          # after npm, hookup symlinks/junctions for dependent packages in projects
-          echo warning "\n#{ info 'NOTE:' } it also seems prudent to do a 'gulp clean' at this point.\n"
-          exec "gulp clean", (c,o,e) ->
-            done null
-      else
-        done null
+    return done() if initialized
+    global.initialized = true
+    # if the node_modules isn't created, do it.
+    if isV5
+      doit = true if (newer "#{basefolder}/package.json",  "#{basefolder}/package-lock.json")
+    else
+      doit = true if (newer "#{basefolder}/package.json",  "#{basefolder}/node_modules")
+
+    typescriptProjectFolders()
+      .on 'end', ->
+        if doit
+          echo warning "\n#{ info 'NOTE:' } 'node_modules' may be out of date - running 'npm install' for you.\n"
+          exec "npm install",{silent:false},(c,o,e)->
+            # after npm, hookup symlinks/junctions for dependent packages in projects
+            echo warning "\n#{ info 'NOTE:' } it also seems prudent to do a 'gulp clean' at this point.\n"
+            exec "gulp clean", (c,o,e) ->
+              done null
+        else
+          done null

-    .pipe foreach (each,next) ->
-      # is any of the TS projects node_modules out of date?
-      doit = true if (! test "-d", "#{each.path}/node_modules") or (newer "#{each.path}/package.json",  "#{each.path}/node_modules")
-      next null
-
+      .pipe foreach (each,next) ->
+        # is any of the TS projects node_modules out of date?
+        if isV5
+          doit = true if (! test "-d", "#{each.path}/node_modules") or (newer "#{each.path}/package.json",  "#{each.path}/package-lock.json")
+        else
+          doit = true if (! test "-d", "#{each.path}/node_modules") or (newer "#{each.path}/package.json",  "#{each.path}/node_modules")
+        next null
+    return null
   return null

 task 'find-rogue-node-modules','Shows the unrecognized node_modules folders in the source tree', ->


test(body): don't leak settings into other tests

--- modules/directives/body/test/test.coffee
+++ modules/directives/body/test/test.coffee
@@ -1,15 +1,30 @@
 injector = angular.injector ['ng', 'bp']

-module 'body', setup: ->
-  @$scope   = injector.get('$rootScope').$new()
-  @$compile = injector.get '$compile'
+module 'body',
+  setup: ->
+    @$scope   = injector.get('$rootScope').$new()
+    @$compile = injector.get '$compile'
+    @bpConfig = injector.get 'bpConfig'
+    @pristineConfig = _.clone @bpConfig
+  teardown: ->
+    for key, value of @pristineConfig
+      @bpConfig[key] = value

 test 'bodyDirective', ->
   element = @$compile('<body></body>') @$scope
   @$scope.$apply()

-  ok element.hasClass @$scope.config.platform, 'test'
+  ok element.hasClass 'ios', 'element has default class ios'
+  ok element.hasClass @$scope.config.platform, 'element has platform as class'
+
+  @$scope.config.platform = 'ios7'
+  @$scope.$apply()
+  ok element.hasClass 'ios7', 'element has class ios7'
+  ok element.hasClass @$scope.config.platform, 'element has platform as class'

   @$scope.config.platform = 'android'
   @$scope.$apply()
-  ok element.hasClass @$scope.config.platform, 'foo'
+  ok element.hasClass 'android', 'element has class android'
+  ok element.hasClass @$scope.config.platform, 'element has platform as class'
+
+


CHG: pageSelector defualt value to body

CHG: pageSelector defualt value to body

--- frontend/javascripts/application.coffee
+++ frontend/javascripts/application.coffee
@@ -11,7 +11,7 @@
   @removeInitializer: (name) ->
     @_initializers[name] = null

-  pageSelector: 'body > .tao-page'
+  pageSelector: 'body'

   _init: ->
     @_initGon()


cleanup

cleanup

--- index_.coffee
+++ index_.coffee
@@ -3,6 +3,7 @@
 randexp = do () ->
   _randexp = require 'randexp'
   (pattern) -> new _randexp(pattern).gen()
+

 module.exports = generate = (schema, method = 'all', depth = 0) ->
   return schema  unless type(schema) is 'object'
@@ -13,16 +14,17 @@
     when 'string', 'number', 'integer', 'boolean', 'array', 'object', 'null'
       return generate[schema.type] schema, method, depth
     else
+      if type(schema.type) is 'array'
+        return generate.oneOf schema, method, depth
       # FIXME what if type(schema.type) = 'object' and _.size(schema.type) > 0 ?
       if schema.type is 'any' or type(schema.type) is 'undefined' or (type(schema.type) is 'object' and _.size(schema.type) is 0)
         return generate.any schema, method, depth
-      if type(schema.type) is 'array'
-        return generate.oneOf schema, method, depth
       # FIXME what type is this?
       console.log schema.type
       throw "what type is this ?"

+# enum
 generate.enum = (schema, method = 'all', depth = 0) ->
   randomOptions =
     minimum: 0
@@ -32,15 +34,17 @@
   generate enumValue, method, depth + 1

-generate.string = (schema, method = 'all', depth = 0) ->
-  return randexp schema.pattern  if schema.pattern
-  # FIXME format should not be ignored
-  randomOptions =
-    minLength: schema.minLength
-    maxLength: schema.maxLength
-  random 'string', randomOptions
+# type = null
+generate.null = () ->
+  null

+# type = boolean
+generate.boolean = (schema, method = 'all', depth = 0) ->
+  random 'boolean'
+
+
+# type = number / integer
 generate.number = generate.integer = (schema, method = 'all', depth = 0) ->
   minimum = schema.exclusiveMinimum + 1  if schema.exclusiveMinimum
   minimum ?= schema.minimum
@@ -57,22 +61,24 @@
   random 'number', randomOptions

-generate.boolean = (schema, method = 'all', depth = 0) ->
-  random 'boolean'
+# type = string
+generate.string = (schema, method = 'all', depth = 0) ->
+  return randexp schema.pattern  if schema.pattern
+  # FIXME format should not be ignored
+  randomOptions =
+    minLength: schema.minLength
+    maxLength: schema.maxLength
+  random 'string', randomOptions

+# type = array
 generate.array = (schema, method = 'all', depth = 0) ->
   o = []
   if type(schema.items) is 'array' and schema.items.length
     for itemSchema in schema.items
       o.push generate itemSchema, method, depth + 1
-  else if type(schema.items) is 'object'
-    # FIXME items should not be a schema, really
-    console.log schema.items
-    throw "items should not be a schema, really"

   if schema.additionalItems
-    # break  if schema.additionalItems.$ref?[0] is '#'
     if type(schema.minItems) isnt 'undefined'
       minimum = schema.minItems - o.length
     else
@@ -98,6 +104,7 @@
   o

+# type = object
 generate.object = (schema, method = 'all', depth = 0) ->
   o = {}
   for key, prop of schema.properties
@@ -122,10 +129,7 @@
   return o

-generate.null = () ->
-  null
-
-
+# type = any
 generate.any = (schema, method = 'all', depth = 0) ->
   types = ['string', 'number', 'integer', 'boolean', 'object', 'null']
   randomOptions =
@@ -135,6 +139,7 @@
   generate {type: types[typeIndex]}, method, depth + 1

+# type = []
 generate.oneOf = (schema, method = 'all', depth = 0) ->
   return 0  unless schema.type.length # treat as 'any'
   randomOptions =


Cleaned up.

Cleaned up.

--- records.coffee
+++ records.coffee
@@ -103,6 +103,50 @@
     type: "radio"
     desc: "Neues Icon–Set"
    ,
+    name: "color"
+    type: "color"
+    desc: "Highlight–Farbe"
+   ,
+    name: "scaling"
+    type: "percent"
+    desc: "Skalierung"
+   ,
+    name: "scaling_min"
+    type: "percent"
+    desc: "Skalierung min"
+   ,
+    name: "scaling_max"
+    type: "percent"
+    desc: "Skalierung max"
+   ,
+    name: "xpos"
+    type: "pixel"
+    desc: "Startkoordinate X"
+   ,
+    name: "ypos"
+    type: "pixel"
+    desc: "Startkoordinate Y"
+   ,
+    name: "zip"
+    type: "zip_de"
+    desc: "Wetter–PLZ"
+   ,
+    name: "do_hide_pi"
+    type: "radio"
+    desc: "PI verstecken?"
+   ,
+    name: "do_display_errors"
+    type: "radio"
+    desc: "Display–Errors?"
+   ,
+    name: "have_mouse"
+    type: "radio"
+    desc: "Maus?"
+   ,
+    name: "have_fixed_navigation"
+    type: "radio"
+    desc: "Fixierte Navigation unten?"
+   ,
     name: "direct_mail"
     type: "struct"
     desc: "Direktversand"
@@ -141,57 +185,18 @@
       desc: "Intervall in Sekunden"
     ]
    ,
-    name: "color"
-    type: "color"
-    desc: "Highlight–Farbe"
-   ,
-    name: "scaling"
-    type: "percent"
-    desc: "Skalierung"
-   ,
-    name: "scaling_min"
-    type: "percent"
-    desc: "Skalierung min"
-   ,
-    name: "scaling_max"
-    type: "percent"
-    desc: "Skalierung max"
-   ,
-    name: "xpos"
-    type: "pixel"
-    desc: "Startkoordinate X"
-   ,
-    name: "ypos"
-    type: "pixel"
-    desc: "Startkoordinate Y"
-   ,
-    name: "zip"
-    type: "zip_de"
-    desc: "Wetter–PLZ"
-   ,
-    name: "do_background_update"
-    type: "radio"
-    desc: "Update im Hintergrund"
-   ,
-    name: "update_interval"
-    type: "radio"
-    desc: "Update–Interval in Sekunden"
-   ,
-    name: "do_hide_pi"
-    type: "radio"
-    desc: "PI verstecken?"
-   ,
-    name: "do_display_errors"
-    type: "radio"
-    desc: "Display–Errors?"
-   ,
-    name: "have_mouse"
-    type: "radio"
-    desc: "Maus?"
-   ,
-    name: "have_fixed_navigation"
-    type: "radio"
-    desc: "Fixierte Navigation unten?"
+    name: "update"
+    type: "struct"
+    desc: "Updates"
+    data: [
+      name: "do_background_update"
+      type: "radio"
+      desc: "Update im Hintergrund?"
+     ,
+      name: "update_interval"
+      type: "seconds"
+      desc: "Update–Interval in Sekunden"
+    ]
    ,
     name: "email"
     type: "struct"


Update createInlineDateTangePicker to abbreviate months

--- imports/ui/inlineDateRangePicker.coffee
+++ imports/ui/inlineDateRangePicker.coffee
@@ -1,23 +1,28 @@
+trimMonthHeader = ($rangeContainer) ->
+  $rangeContainer.find('.calendar-table').each ->
+    $(@).find('thead tr').last().children().each ->
+      $day = $(@)
+      $day.html($day.html().slice(0,-1))
+
 createInlineDateRangePicker = ($parentElement, options) ->
   parentId = $parentElement.prop("id")
   if !parentId
     # If the parent element doesn't have an id, generate one
-    parentId = "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) ->
+    parentId = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace /[xy]/g, (c) ->
       r = Math.random()*16|0
       v = if c is 'x' then r else (r&0x3|0x8)
       return v.toString(16)
-    )
-    $parentElement.prop("id", parentId)
+    $parentElement.prop('id', parentId)

   if !options then options = {}
-  allOptions = _.extend({
-    parentEl: "#" + parentId
+  allOptions = _.extend
+    parentEl: '#' + parentId
     template: Blaze.toHTML(Template.inlineDateRangePicker)
     singleDatePicker: options.singleDatePicker
     autoUpdateInput: false
     maxDate: options.maxDate
     minDate: options.minDate
-  }, _.omit(options, 'singleDatePicker'))
+  , _.omit(options, 'singleDatePicker')

   if options.startDate and options.endDate
     allOptions.startDate = options.startDate
@@ -27,29 +32,37 @@
     allOptions.autoApply = true

   $rangeContainer = $parentElement.daterangepicker(allOptions)
-  picker = $rangeContainer.data("daterangepicker")
+  picker = $rangeContainer.data('daterangepicker')
+
+  # Replace two letter day abbreviations with single-letter
+  $rangeContainer.on 'show.daterangepicker', ->
+    trimMonthHeader($rangeContainer)
+  $rangeContainer.find('.calendar')
+    .off('click.daterangepicker', '.next')
+    .off('click.daterangepicker', '.prev')
+    .on 'click.daterangepicker', '.next', (event) ->
+      picker.clickNext(event)
+      trimMonthHeader($rangeContainer)
+    .on 'click.daterangepicker', '.prev', (event) ->
+      picker.clickPrev(event)
+      trimMonthHeader($rangeContainer)
+
   if options.singleDatePicker
-    $(".singleDatePickerInput").show()
-    picker.clickApply = (e) ->
-      this.setEndDate(this.startDate)
-      this.updateView()
-      this.element.trigger('apply.daterangepicker', this);
+    $('.singleDatePickerInput').show()
+    picker.clickApply = (event) ->
+      @setEndDate(@startDate)
+      @updateView()
+      @element.trigger('apply.daterangepicker', @);

   # Prevent the calendar from hiding
   picker.hide = -> @

-  $(".inlineRangePicker").off("click.daterangepicker")
-  $(".inlineRangePicker .daterangepicker").removeClass("opensright")
+  $('.inlineRangePicker').off('click.daterangepicker')
+  $('.inlineRangePicker .daterangepicker').removeClass('opensright')

   picker.show()

-  $(document).off("mousedown.daterangepicker touchend.daterangepicker click.daterangepicker focusin.daterangepicker")
-
-  # Replace two letter day abbreviations with single-letter
-  $rangeContainer.find('.calendar-table').each ->
-    $(@).find('thead tr').last().children().each ->
-      $day = $(@)
-      $day.html($day.html().slice(0,-1))
+  $(document).off('mousedown.daterangepicker touchend.daterangepicker click.daterangepicker focusin.daterangepicker')

   picker


Remove repocloner plugin and pull documentation with a separate script. Use skipUnsupportedPlugins for latest docpad running thumbnail plugin.

--- docpad.coffee
+++ docpad.coffee
@@ -10,6 +10,10 @@
     ignorePaths: [
             __dirname + '/src/documents/documentation/api'
     ]
+
+  #docpad-image-thumbnail plugin has not been updated.
+
+  skipUnsupportedPlugins: true

 	# =================================
 	# Template Data
@@ -282,15 +286,6 @@

         thumbnails:
         	imageMagick: true
-
- 		repocloner:
-            repos: [
-                    name: 'HaxeFlixel Documentation'
-                    branch: 'master'
-                    path: 'src/documents/documentation'
-                    url: 'https://github.com/HaxeFlixel/flixel-docs.git'
-            ]
-
 }

 # Export our DocPad Configuration


Regenerations works itself!

Regenerations works itself!

--- docpad.coffee
+++ docpad.coffee
@@ -1,12 +1,7 @@
 # DocPad Configuration File
 # http://docpad.org/docs/config

-values = (map)->
-  v for k, v of map
-
 #@poweredByDocPad = false
-
-@regeneratePaths = ['raw']

 @templateData =
   site:


aside-nav always needs projects loaded

aside-nav always needs projects loaded

--- js/app.coffee
+++ js/app.coffee
@@ -127,6 +127,7 @@
       mixpanel.name_tag(@current_user().login)
       mixpanel.identify(@current_user().login)
       _rollbarParams.person = {id: @current_user().login}
+      @loadProjects() # needed on every page for aside-nav

     @logged_in = @komp =>
       @current_user?()
@@ -174,7 +175,6 @@
       "/img/arrow_refresh.png"

   loadDashboard: (cx) =>
-    @loadProjects()
     page = parseInt(cx.params.page) or 0
     @loadRecentBuilds(page)
     if window._gaq? # we dont use ga in test mode
@@ -211,8 +211,6 @@
     @loadBuilds('/api/v1/recent-builds', page, refresh)

   loadOrg: (username, page, refresh) =>
-    if !@projects_have_been_loaded() then @loadProjects()
-
     @loadBuilds("/api/v1/organization/#{username}", page, refresh)

     if not refresh
@@ -220,8 +218,6 @@
         builds_table: 'org'

   loadProject: (username, project, branch, page, refresh) =>
-    if !@projects_have_been_loaded() then @loadProjects()
-
     project_name = "#{username}/#{project}"
     path = "/api/v1/project/#{project_name}"
     path += "/tree/#{encodeURIComponent(branch)}" if branch?


"Cancel" button to close "Connecting..." dialogue

--- client/connect.coffee
+++ client/connect.coffee
@@ -91,8 +91,8 @@
     if !/^[a-z0-9\.\-:]+$/i.test host then $.alert 'Invalid host', 'Error', -> $host.focus(); return
     else if !(0 < port < 0xffff) then $.alert 'Invalid port', 'Error', -> $port.focus(); return
     else
-      $connectDialog = $ "

"
-        .dialog modal: 1, width: 350, title: 'Connecting...'
+      $connectDialog = $ '

'
+        .dialog modal: 1, width: 350, title: 'Connecting...', buttons: Cancel: -> $(@).dialog 'close'; false
       D.socket.emit '*connect', {host, port}
     false
   $new.click ->


fix okCancelEvents on name_input (closes #22)

--- client/general.coffee
+++ client/general.coffee
@@ -205,29 +205,28 @@
     return true  if @my_id is d.my_id & @collection is d.collection
     false

-Template.name_field.events
-  "click .edit-name": (e, tmpl) ->
-    Session.set "editing_name", tmpl.data
-    Deps.flush() # update DOM before focus
-    ni = tmpl.find("#name_input")
-    ni.value = @name
-    window.activateInput ni
-    return
-
-  window.okCancelEvents("#name_input",
-    ok: (value) ->
-      coll = window.get_collection_from_name(@collection)
-      coll.update @my_id,
-        $set:
-          name: value
-
-      Session.set "editing_name", null
-      return
-
-    cancel: ->
-      Session.set "editing_name", null
-      return
-  )
+Template.name_field.events "click .edit-name": (e, tmpl) ->
+  Session.set "editing_name", tmpl.data
+  Deps.flush() # update DOM before focus
+  ni = tmpl.find("#name_input")
+  ni.value = @name
+  window.activateInput ni
+  return
+
+Template.name_field.events window.okCancelEvents("#name_input",
+  ok: (value) ->
+    coll = window.get_collection_from_name(@collection)
+    coll.update @my_id,
+      $set:
+        name: value
+
+    Session.set "editing_name", null
+    return
+
+  cancel: ->
+    Session.set "editing_name", null
+    return
+)

 Template.delete_button.events
   "click .delete": (e) ->


Messy global integration with compiler

Messy global integration with compiler

--- coffee/machine.coffee
+++ coffee/machine.coffee
@@ -22,6 +22,7 @@
   MILLIS_FOR_SCROLLED_INSTRUCTIONS_TENTH = 5

   constructor: (line_height, setTimeout, code_mirror) ->
+    @program = null
     @state = 'OFF'
     @next_line = null
     @line_height = line_height
@@ -62,19 +63,22 @@
     @setTimeout scrollConsole, 1 # timeout to avoid weird layout bug

   _codeWithLineNums: ->
-    lines = $one('textarea.code-editor').textContent.split("\n")
+    lines = @code_mirror.getValue().split("\n")
     new_lines = ["
\n"] # blank line at beginning
     line_num = 1
     for line in lines
       new_lines.push "
#{line_num}
 " +
         "
#{escapeHTML(line)}
"
       line_num += 1
-    new_lines.join("\n") + "
\n" # blank line at end
+
+    # blank lines at end, so we can scroll if program is short
+    new_lines.join("\n") + "

"

   clickPower: ->
     if @state == 'OFF'
       @state = 'WAITING'
-      @next_line = 1
+      @program = compile(@code_mirror.getValue())
+      @next_line = @program.map['start']
       $one('div.machine .before-cursor').textContent = ''
       $one('div.machine .instructions .content').innerHTML =
         @_codeWithLineNums()
@@ -86,16 +90,14 @@
     @_showNextLine (->)

   _executeNextLineGettingOutput: ->
-    if @next_line == 1
-      @next_line = 2
-      return "1\n"
-    else if @next_line == 2
-      @next_line = 3
-      return "2\n"
-    else if @next_line == 3
+    next_line = @program.map[@next_line].map.next
+    output    = @program.map[@next_line].map.output
+    if next_line == 'finish'
       @state = 'WAITING'
       @next_line = null
-      return "3\n"
+    else
+      @next_line = next_line
+    return output

   clickRun: ->
     return unless @state == 'WAITING'


为了去除 IDE 的警告

为了去除 IDE 的警告

--- core/router/index.coffee
+++ core/router/index.coffee
@@ -8,7 +8,7 @@
   post: {}

 for item in ['user']
-  for url, controller of require("./#{item}")
+  for url, controller of require("./" + item)
     routers.post[url] = controller

 module.exports = routers


add coffee call to left-drawer-trigger class

--- app/assets/javascripts/beers.js.coffee
+++ app/assets/javascripts/beers.js.coffee
@@ -1,3 +1,10 @@
 # Place all the behaviors and hooks related to the matching controller here.
 # All this logic will automatically be available in application.js.
 # You can use CoffeeScript in this file: http://coffeescript.org/
+$ ->
+  $(".left-drawer-trigger").click Ink.requireModules ["Ink.UI.Drawer_1"], (Drawer) ->
+    inkDrawer = new Drawer(
+      sides: "left"
+      mode:  "push"
+    )
+    return


Error handling on ajax load of feed entries

--- app/assets/javascripts/feeds.js.coffee
+++ app/assets/javascripts/feeds.js.coffee
@@ -2,24 +2,29 @@
   # Load more feed entries async on click
   $('.feeds').on "click", '.load-more input', (event) ->
     event.preventDefault()
-    $trigger = $(@)
-    $trigger.val("Hämtar fler...").addClass('disabled')
-
-    $.get $trigger.attr('data-path'), (data) ->
-      $trigger.parent().replaceWith(data)
+    getMore $(@)

   # Lazy loading more news when combined news is displayed
   if $('#combined').length
     currentlyLoading = false
     $(window).on 'DOMContentLoaded load resize scroll', () ->
       if $('#combined')[0].getBoundingClientRect().bottom <= $(window).height() + 100 and not currentlyLoading
-        currentlyLoading = true
-        $trigger = $(".load-more input")
-        $trigger.val("Hämtar fler...").addClass('disabled')
-
-        $.get $trigger.attr('data-path'), (data) ->
-          $trigger.parent().replaceWith(data)
-          currentlyLoading = false
+        getMore $(".load-more input")

-# TODO: replace $.get with $.ajax and catch errors (redirs to sso)
+  # Load and inject more feed entries
+  getMore = ($trigger, url) ->
+    currentlyLoading = true
+    $trigger.val("Hämtar fler...").addClass('disabled')
+
+    $.ajax
+      url: $trigger.attr('data-path')
+      timeout: 10000
+      success: (data) ->
+        $trigger.parent().replaceWith(data)
+      error: (x, y, z) ->
+        $trigger.parent().html('
Ett fel inträffade, prova att ladda om webbsidan
')
+      complete: ->
+        currentlyLoading = false
+        $trigger.val("Visa fler").removeClass('disabled')
+


When refactoring, it pays to finish the job with one's javascript

--- app/assets/javascripts/meals.js.coffee
+++ app/assets/javascripts/meals.js.coffee
@@ -37,7 +37,7 @@
   )

   $('.close-orders').click( () ->
-    meal_id = $(this).data('meal-id')
+    meal_id = $('#meal_id').data('meal-id')
     global.askYesNoQuestion("Are you sure?", "Do you really want to close orders?",
       (res) ->
         if (res == true)


Binds player events only when element exists

--- app/assets/javascripts/mixes.js.coffee
+++ app/assets/javascripts/mixes.js.coffee
@@ -8,22 +8,25 @@
       animation: 150

   # minxtape player
-  document.getElementById("minxtape-player").addEventListener 'ended', ->
-    nextPosition = parseInt(this.dataset.position) + 1
-    nextTrack = document.getElementById("minxtape-track-#{nextPosition}")
-    this.children[0].src = nextTrack.dataset.source
-    this.dataset.position = nextPosition
-    this.load()
-    this.play()
-
-  document.getElementById("minxtape-player").addEventListener 'play', ->
-    currentPosition = parseInt(this.dataset.position)
-    currentTrackIndicator = document.getElementById("minxtape-track-indicator-#{currentPosition}")
-    currentTrackIndicator.style.display = ''
-    prevPosition = parseInt(this.dataset.position) - 1
-    prevTrackIndicator = document.getElementById("minxtape-track-indicator-#{prevPosition}")
-    if prevTrackIndicator
-      prevTrackIndicator.style.display = 'none'
+  el = document.getElementById("minxtape-player")
+  if el
+    # load and play next track when playback of current track ends
+    el.addEventListener 'ended', ->
+      nextPosition = parseInt(this.dataset.position) + 1
+      nextTrack = document.getElementById("minxtape-track-#{nextPosition}")
+      this.children[0].src = nextTrack.dataset.source
+      this.dataset.position = nextPosition
+      this.load()
+      this.play()
+    # update current track indicator
+    el.addEventListener 'play', ->
+      currentPosition = parseInt(this.dataset.position)
+      currentTrackIndicator = document.getElementById("minxtape-track-indicator-#{currentPosition}")
+      currentTrackIndicator.style.display = ''
+      prevPosition = parseInt(this.dataset.position) - 1
+      prevTrackIndicator = document.getElementById("minxtape-track-indicator-#{prevPosition}")
+      if prevTrackIndicator
+        prevTrackIndicator.style.display = 'none'

   # show audio uploader when track edit button clicked
   [].forEach.call document.getElementsByClassName('edit-audio-file'), (v,i,a) ->


Provide @max_polling_steps instead of @limit_polling_interval

--- app/assets/javascripts/notes.js.coffee
+++ app/assets/javascripts/notes.js.coffee
@@ -17,7 +17,7 @@
     @noteable_url = document.URL
     @notesCountBadge ||= $(".issuable-details").find(".notes-tab .badge")
     @base_polling_interval = 15000
-    @limit_polling_interval = 120000
+    @max_polling_steps = 5

     @cleanBinding()
     @addBinding()
@@ -127,9 +127,10 @@
   if there aren't new notes coming from the server
   ###
   setPollingInterval: (shouldReset = true) ->
+    nthInterval = @base_polling_interval * Math.pow(2, @max_polling_steps - 1)
     if shouldReset
       @polling_interval = @base_polling_interval
-    else if @polling_interval < @limit_polling_interval
+    else if @polling_interval < nthInterval
       @polling_interval *= 2

     @initRefresh()


Don't display tooltips on touch devices

Don't display tooltips on touch devices

--- app/assets/javascripts/pages.js.coffee
+++ app/assets/javascripts/pages.js.coffee
@@ -30,7 +30,7 @@
         $(data).each (index, val) ->
           link = $('
'+val.title+'
')
           callout.parent().before link
-          $("a", link).tipsy
+          $("a", link).not(".touch a").tipsy
             html: true
             fade: true
             gravity: "w"
@@ -53,12 +53,12 @@
     if title
       title.style.top = titleparallax + "px"

-  $(".post-tags [title]").tipsy
+  $(".post-tags [title]").not(".touch .post-tags").tipsy
     html: true
     fade: true
     delayOut: 1500

-  $("[title]").tipsy
+  $("[title]").not(".touch [title]").tipsy
     html: true
     fade: true
     gravity: "w"


simplified progress to avoid crash

simplified progress to avoid crash

--- app/assets/javascripts/progress.coffee
+++ app/assets/javascripts/progress.coffee
@@ -169,15 +169,15 @@
 			bounds = new L.LatLngBounds()

 			if data.yes_poly.features.length>0
-				yes_json.addTo(m)
+				# yes_json.addTo(m)
 				bounds.extend(yes_json.getBounds())

 			if data.no_poly.features.length>0
-				no_json.addTo(m)
+				# no_json.addTo(m)
 				bounds.extend(no_json.getBounds())

 			if data.fix_poly.features.length>0
-				fix_json.addTo(m)
+				# fix_json.addTo(m)
 				bounds.extend(fix_json.getBounds())

 			m.addLayer(markers)


Better errors

Better errors

--- driver.coffee
+++ driver.coffee
@@ -97,21 +97,27 @@
             delete doc.__labels if _.has doc, '__labels'
             delete doc.metadata if _.has doc, 'metadata'
             Meteor.neo4j.call '___Neo4jObserveAdded', {properties: doc, _id: doc._id, __labels: labels}, collectionName, doc, (error) ->
-              return throw new Meteor.Error '500', '[___Neo4jObserveRemoved]', error if error
+              if error
+                console.error {error, collectionName}
+                throw new Meteor.Error 500, '[___Neo4jObserveRemoved]'
         changed: (doc, old) ->
           labels = getLabels doc
           if not Meteor.neo4j.collections[collectionName].isMapping
             delete doc.__labels if _.has doc, '__labels'
             delete doc.metadata if _.has doc, 'metadata'
             Meteor.neo4j.call '___Neo4jObserveChanged', {_id: old._id, properties: doc, __labels: labels}, collectionName, doc, (error) ->
-              return throw new Meteor.Error '500', '[___Neo4jObserveRemoved]', error if error
+              if error
+                console.error {error, collectionName}
+                throw new Meteor.Error 500, '[___Neo4jObserveRemoved]'
         removed: (doc) ->
           labels = getLabels doc
           if not Meteor.neo4j.collections[collectionName].isMapping
             delete doc.__labels if _.has doc, '__labels'
             delete doc.metadata if _.has doc, 'metadata'
             Meteor.neo4j.call '___Neo4jObserveRemoved', {_id: doc._id, __labels: labels}, collectionName, doc, (error) ->
-              return throw new Meteor.Error '500', '[___Neo4jObserveRemoved]', error if error
+              if error
+                console.error {error, collectionName}
+                throw new Meteor.Error 500, '[___Neo4jObserveRemoved]'

     if Meteor.isServer
       collection.publish = (name, func, onSubscribe) ->
@@ -173,12 +179,14 @@
     check link, String

     isReady = new ReactiveVar false
-    throw new Meteor.Error '404', "[Meteor.neo4j.subscribe] | Collection: #{collectionName} not found! | Use Meteor.neo4j.collection(#{collectionName}) to create collection" if not @collections[collectionName]
+    throw new Meteor.Error 404, "[Meteor.neo4j.subscribe] | Collection: #{collectionName} not found! | Use Meteor.neo4j.collection(#{collectionName}) to create collection" if not @collections[collectionName]

     @subscriptions["#{collectionName}_#{name}"] ?= []

     @call "Neo4jReactiveMethod_#{collectionName}_#{name}", opts, collectionName, link, (error, data) =>
-      throw new Meteor.Error '500', '[Meteor.neo4j.subscribe]', error if error
+      if error
+        console.error {error, collectionName, name, opts, link}
+        throw new Meteor.Error 500, '[Meteor.neo4j.subscribe]'
       @mapLink collectionName, data, link, "#{collectionName}_#{name}"
       isReady.set true

@@ -282,7 +290,8 @@
         _n = new RegExp(value + ' ', 'i')
         @search _n, query, (isFound) ->
           if isFound
-            throw new Meteor.Error '401', '[Meteor.neo4j.check] "#{value}" is not allowed!', query
+            console.warn {query}
+            throw new Meteor.Error 401, '[Meteor.neo4j.check] "#{value}" is not allowed!'

   ###
   # @isomorphic
@@ -432,7 +441,9 @@
         @run uid, optuid, query, opts, new Date
       else if @allowClientQuery == true and Meteor.isClient
         Meteor.call 'Neo4jRun', uid, optuid, query, opts, new Date, (error) ->
-          throw new Meteor.Error '500', 'Exception on calling method [Neo4jRun]', {error, query, opts} if error
+          if error
+            console.error {error, query, opts}
+            throw new Meteor.Error 500, 'Exception on calling method [Neo4jRun]'
         @uids.set _.union(@uids.get(), [ optuid ])
     @cache.get optuid, callback

@@ -582,7 +593,9 @@
         sensitivities: Meteor.neo4j.parseSensitivities queryString, opts, parsedData
       ,
         (error) ->
-          throw new Meteor.Error '500', 'Meteor.neo4j.cacheCollection.upsert: [Meteor.neo4j.cache.put]: ', {error, uid, data, queryString, opts, date} if error
+          if error
+            console.error {error, uid, optuid, data, queryString, opts, date}
+            throw new Meteor.Error 500, 'Meteor.neo4j.cacheCollection.upsert: [Meteor.neo4j.cache.put]'
     ) else undefined

   ###
@@ -650,7 +663,8 @@
     Meteor.N4JDB.query query, opts, (error, data) ->
       bound ->
         if error
-          throw new Meteor.Error '[Meteor.N4JDB.query]: ', JSON.stringify {error, uid, optuid, query, opts, date}
+          console.error {error, uid, optuid, query, opts, date}
+          throw new Meteor.Error 500, '[Meteor.N4JDB.query]'
         else
           return Meteor.neo4j.cache.put uid, optuid, data or null, query, opts, date
   ) else undefined
@@ -801,8 +815,10 @@
         optuid = Package.sha.SHA256 _query + JSON.stringify opts
         if collectionName
           self.query _query, opts, (error, data) ->
-            throw new Meteor.Error '500', "[Meteor.neo4j.methods]", error if error
-            throw new Meteor.Error '404', "[Meteor.neo4j.methods] | Collection: #{collectionName} not found! | Use Meteor.neo4j.collection(#{collectionName}) to create collection" if not self.collections[collectionName]
+            if error
+              console.error {error, uid, optuid, query, opts, date}
+              throw new Meteor.Error 500, "[Meteor.neo4j.methods]"
+            throw new Meteor.Error 404, "[Meteor.neo4j.methods] | Collection: #{collectionName} not found! | Use Meteor.neo4j.collection(#{collectionName}) to create collection" if not self.collections[collectionName]
             self.mapLink collectionName, data, link, _cmn

           self.onSubscribes[_cmn].call(opts) if self.onSubscribes[_cmn] and _.isFunction self.onSubscribes[_cmn]
@@ -881,7 +897,9 @@
     callback = param for param in arguments when _.isFunction param

     Meteor.call methodName, opts, name, link, (error, uids) =>
-      throw new Meteor.Error '500', "[Meteor.neo4j.call] Method: [\"#{methodName}\"] returns error!", error if error
+      if error
+        console.error {error, methodName, opts, name, link}
+        throw new Meteor.Error '500', "[Meteor.neo4j.call] Method: [\"#{methodName}\"] returns error!"

       cached = @cacheCollection.find uid: uids.uid
       _uids = @uids.get()


removing group on successful group deletion

--- jsapp/xlform_model_view/view.row.coffee
+++ jsapp/xlform_model_view/view.row.coffee
@@ -110,6 +110,7 @@
     deleteGroup: (evt)->
       if confirm('Are you sure you want to delete this group? All questions will be lost')
         @model.detach()
+        @$el.remove()
       evt.preventDefault()
     toggleAdvanced: (evt)->
       @$('.group').toggleClass('group--expanded-settings')


load utils file before rest of javascripts

Transit.storage_* methods must be defined before geocode.js is loaded.

--- app/assets/transit/lib/index.js.coffee
+++ app/assets/transit/lib/index.js.coffee
@@ -4,6 +4,7 @@
 #= require_tree ./models
 #= require_tree ./collections
 #= require_tree ./views
+#= require ./utils
 #= require_tree .

 window.Transit =


Don't show autocompletion if a word is blank

--- lib/rubymotion-autocomplete-view.coffee
+++ lib/rubymotion-autocomplete-view.coffee
@@ -32,7 +32,7 @@

   attach: ->
     word = @editor.getWordUnderCursor(includeNonWordCharacters: false)
-    return if word is ''
+    return if word.replace(/^\s*|\s*$/, '') is ''
     super

   confirmed: (match) ->


Emit "sending" signal or res object

Emit "sending" signal or res object

--- library/nucleus/plumbs.coffee
+++ library/nucleus/plumbs.coffee
@@ -68,13 +68,14 @@
 # rather than writing and ending the request in a direct way.
 module.exports.sender = (kernel) ->
     (request, response, next) ->
-        response.send = (content, typed, keepAlive) ->
+        response.send = (content, typed, keepalive) ->
+            @emit "sending", content, typed, keepalive
             @setHeader "Content-Type", typed if typed?
             return @write content.toString() if typed?
             negotiator = kernel.broker.negotiate
             negotiator = negotiator.bind kernel.broker
             negotiator request, response, content
-            response.end() unless keepAlive
+            response.end() unless keepalive
         next() unless request.headersSent

 # This plumbing add an `accepts` method onto the HTTP resonse object


Automatically update balance votes on balance update

--- app/js/controllers/account_vote.coffee
+++ app/js/controllers/account_vote.coffee
@@ -10,11 +10,10 @@
         approve = !Wallet.approved_delegates[name]
         Wallet.approve_delegate(name, approve).then ->
             $scope.trust_level = approve
-
-    WalletAPI.account_vote_summary($scope.account_name).then (data) ->
-        console.log('account_vote_summary', data)
-        console.log($scope.balances)
-        $scope.votes=data
+    $scope.$watch('$scope.balances[Info.symbol]', ->
+        WalletAPI.account_vote_summary($scope.account_name).then (data) ->
+            $scope.votes=data
+    )

     yesSend = ->
         WalletAPI.transfer(balMinusFee, Info.symbol, $scope.account_name, $scope.account_name, 'Transfer for voting', $scope.transfer_info.vote).then (response) ->


Updated to pull only 404 and 500 with using docpad internals

--- src/lib/docpad.coffee
+++ src/lib/docpad.coffee
@@ -2348,32 +2348,6 @@
 		config = @config
 		server = null

-		# Error Handling Stuff
-		errorFiles = {}
-		# read the out directory for available files
-		fileList = fsUtil.readdirSync(config.outPath)
-
-		for file in fileList
-			if file.match /^[0-9x]{3}\.html/
-				code = file.match /^([0-9x]{3})\.html/
-				errorFiles[code[1]] = pathUtil.join(config.outPath, file)
-
-		# Find the closest matching error page
-		findClosest = (code) ->
-			code = code.toString()
-			match = null
-			count = 2
-
-			while match == null
-				if errorFiles.hasOwnProperty(code)
-					match = code
-				else
-					code = code.substr(0, count)
-					while code.length < 3
-						code += 'x'
-					count -= 1
-			return match
-
 		# Handlers
 		complete = (err) ->
 			return next(err)  if err
@@ -2486,11 +2460,13 @@

 						# 404 Middleware
 						server.use (req, res, next) ->
+							database = docpad.getDatabase()
+							return next() unless database
 							notFound = 404
 							if config.useCustomErrors
-								code = findClosest(notFound)
-								if errorFiles.hasOwnProperty(code) && pathUtil.existsSync errorFiles[code]
-									data = fsUtil.readFileSync(errorFiles[code], 'utf8')
+								file = database.findOne(relativePath: '404.html')
+								if file
+									data = file.get('contentRendered') or document.get('content')
 								else
 									data = notFound + ' ' + errorCodes[notFound]

@@ -2500,12 +2476,13 @@

 						# 500 Middleware
 						server.use (err, req, res, next) ->
-							# Assume 500 for now
+							database = docpad.getDatabase()
+							return next() unless database
 							serverError = 500
 							if config.useCustomErrors
-								code = findClosest(serverError)
-								if errorFiles.hasOwnProperty(code) && pathUtil.existsSync errorFiles[code]
-									data = fsUtil.readFileSync(errorFiles[code], 'utf8')
+								file = database.findOne(relativePath: '404.html')
+								if file
+									data = file.get('contentRendered') or document.get('content')
 								else
 									data = serverError + ' ' + errorCodes[serverError]


Lint style.

Lint style.

--- client/library.coffee
+++ client/library.coffee
@@ -53,12 +53,12 @@
     return unless name

     Meteor.call 'create-collection', name, (error, collectionId) =>
-        return Notify.meteorError error, true if error
+      return Notify.meteorError error, true if error

-        # Clear the collection name from the form
-        $(template.findAll '.name').val('')
+      # Clear the collection name from the form
+      $(template.findAll '.name').val('')

-        Notify.success "Collection created."
+      Notify.success "Collection created."

     return # Make sure CoffeeScript does not return anything


logged out user responds to redirect option

--- models/logged_out_user.coffee
+++ models/logged_out_user.coffee
@@ -9,7 +9,7 @@
 injectToken = (success) ->
   _.wrap success, (success, model, response, options) ->
     $.ajaxSettings.headers = _.extend ($.ajaxSettings.headers or {}),
-      'X-ACCESS-TOKEN': response.user.accessToken
+      'X-ACCESS-TOKEN': response.user?.accessToken
     success model, response, options

 module.exports = class LoggedOutUser extends User
@@ -19,7 +19,11 @@
   login: (options = {}) ->
     settings = _.defaults options,
       url: '/me/sign_in'
-      success: injectToken -> location.reload()
+      success: injectToken ->
+        if options.redirect
+          document.location.href = options.redirect
+        else
+          location.reload()
     new Backbone.Model().save (@pick 'email', 'password'), settings

   signup: (options = {}) ->


Don't assume skin assets exist

Don't assume skin assets exist

On branch master
Changes not staged for commit:
	modified:   packages/shrub-form/client/index.coffee
no changes added to commit (use "git add" and/or "git commit -a")

--- packages/shrub-skin/client/index.coffee
+++ packages/shrub-skin/client/index.coffee
@@ -27,7 +27,7 @@
 			# compilation step.
 			link = directive.link
 			directive.link = (scope, element, attrs, controller) ->
-
+
 				# Save top-level arguments for later calls to link functions.
 				topLevelArgs = arguments

@@ -36,6 +36,11 @@

 				recalculateCandidate = ->

+					# Get the skin assets.
+					skinAssets = config.get(
+						"packageConfig:shrub-skin:assets:#{currentSkinKey}"
+					)
+
 					# Track changes to the current template candidate.
 					oldCandidate = candidate

@@ -67,16 +72,12 @@
 						"#{path}--#{candidate_}.html"
 					candidateTemplates.push "#{path}.html"

-					skinAssets = config.get(
-						"packageConfig:shrub-skin:assets:#{currentSkinKey}"
-					)
-
 					# Return the first existing template. The asset
 					# templates are already sorted by descending
 					# specificity.
 					candidate = do ->
 						for uri in candidateTemplates
-							return uri if skinAssets.templates[uri]?
+							return uri if skinAssets?.templates?[uri]

 						return

@@ -86,7 +87,7 @@
 						candidateHooksInvoked = {}

 						# Insert and compile the template HTML if it exists.
-						if skinAssets.templates[candidate]
+						if skinAssets?.templates?[candidate]

 							# Insert and compile HTML.
 							element.html skinAssets.templates[candidate]


Minor fix to generic user agent to read the correct PUT link from the POST response.

--- useragent-examples/useragent-generic-javascript/src/main/webapp/coffee/ResourceView.coffee
+++ useragent-examples/useragent-generic-javascript/src/main/webapp/coffee/ResourceView.coffee
@@ -136,11 +136,13 @@

 # Render a form with the response data obtained from a POST command
   renderPOSTResponseForm: (model) =>
-    #assume there's only one link on this POST resource
     _.each model._links, (relModel) =>
+      debugger
       if (relModel.length)
-        _.each relModel, (linkModel) =>
-        @commitLink = @getLink(this, linkModel, model.body)
+        for item in relModel
+          debugger
+          if (item.href.substring(0,3) == "PUT")
+            @commitLink = @getLink(this, item, model.body)
       else
         @commitLink = @getLink(this, relModel, model.body)
     #alert("Link = " + @commitLink.model.href)


テンプレートプリコンパイルにて、サブディレクトリを参照するよう修正

テンプレートプリコンパイルにて、サブディレクトリを参照するよう修正

--- Gulpfile.coffee
+++ Gulpfile.coffee
@@ -45,7 +45,7 @@
     .pipe gulp.dest './dest/js/'

 gulp.task 'build-templates',->
-  gulp.src './src/tpl/*.hbs'
+  gulp.src './src/tpl/**/*.hbs'
     .pipe handlebars()
     .pipe wrap 'Handlebars.template(<%= contents %>)'
     .pipe declare {


:bug: fix missing CSS file when building locally to run selenium

--- Gulpfile.coffee
+++ Gulpfile.coffee
@@ -47,7 +47,6 @@
   plugins = [
     # Use the production version of React (no warnings/runtime checks)
     new webpack.DefinePlugin({ 'process.env': {NODE_ENV: process.env.NODE_ENV} })
-    new WPExtractText("tutor.min.css")
   ]

   webpackConfig = require './webpack.config'
@@ -55,8 +54,10 @@

   if process.env.NODE_ENV is JSON.stringify('production') and process.env.NODE_MINIFY isnt JSON.stringify(false)
     plugins.push(new webpack.optimize.UglifyJsPlugin({minimize: true}))
+    plugins.push(new WPExtractText("tutor.min.css"))
     config.output.filename = 'tutor.min.js'
   else
+    plugins.push(new WPExtractText("tutor.css"))
     config.output.filename = 'tutor.js'

   webpack(config, (err, stats) ->


use homepage_tabs configuration for list order

--- @client/list.coffee
+++ @client/list.coffee
@@ -1273,6 +1273,10 @@
 window.proposals_in_lists = ->
   proposals = fetch '/proposals'
   homepage_list_order = customization 'homepage_list_order'
+  if homepage_list_order.length == 0 && customization('homepage_tabs')
+    homepage_list_order = []
+    for k,v of customization('homepage_tabs')
+      homepage_list_order = homepage_list_order.concat v

   # By default sort proposals by the newest of the proposals.
   # But we'll only do this on page load, so that clusters don't move


Fix undefined SimpleMDE outside admin

Fix undefined SimpleMDE outside admin

--- app/assets/javascripts/application.js.coffee
+++ app/assets/javascripts/application.js.coffee
@@ -13,30 +13,31 @@
 jQuery ->
   $("abbr.timeago").timeago()

-  mde = new SimpleMDE(
-    element: $("#markdownify")[0],
-    spellChecker: false
-    autofocus: true
-    forceSync: true
-    indentWithTabs: false
-    lineWrapping: false
-    blockStyles:
-      bold: "**"
-      italic: "_"
-    insertTexts:
-      link: ["[", "](#play-)"]
-    toolbar: ["bold", "italic", "strikethrough", "|",
-              "heading-1", "heading-2", "heading-3", "|",
-              "unordered-list", "ordered-list", "code", "quote", "link", "|",
-              "preview", "side-by-side", "fullscreen", "guide", "|"
-    ],
-  )
+  if (typeof SimpleMDE != "undefined")
+    mde = new SimpleMDE(
+      element: $("#markdownify")[0],
+      spellChecker: false
+      autofocus: true
+      forceSync: true
+      indentWithTabs: false
+      lineWrapping: false
+      blockStyles:
+        bold: "**"
+        italic: "_"
+      insertTexts:
+        link: ["[", "](#play-)"]
+      toolbar: ["bold", "italic", "strikethrough", "|",
+                "heading-1", "heading-2", "heading-3", "|",
+                "unordered-list", "ordered-list", "code", "quote", "link", "|",
+                "preview", "side-by-side", "fullscreen", "guide", "|"
+      ],
+    )

-  mde.codemirror.on 'refresh', ->
-    if (mde.isFullscreenActive())
-      $('.navbar').hide()
-    else
-      $('.navbar').show()
+    mde.codemirror.on 'refresh', ->
+      if (mde.isFullscreenActive())
+        $('.navbar').hide()
+      else
+        $('.navbar').show()

   $('.open_player').click (event) ->
     open_player this.href


removing ember from the manifest file

removing ember from the manifest file

--- app/assets/javascripts/application.js.coffee
+++ app/assets/javascripts/application.js.coffee
@@ -1,14 +1,2 @@
 #= require jquery
-#= require handlebars
-#= require ember
-#= require ember-data
-#= require ic-ajax.js
 #= require_self
-#= require theoj
-#= require pdfjs-1.0.68-dist/build/pdf.js
-#= require pdfjs-1.0.68-dist/text_layer_builder.js
-#= require pdfjs-1.0.68-dist/ui_utils.js
-
-# for more details see: http://emberjs.com/guides/application/
-window.ajax = ic.ajax
-window.Theoj = Ember.Application.create()


Fixing double click to submit issue

Fixing double click to submit issue

When clicking the submit button on the last page, two event handlers are
fired:

1) To check validations, and prevent submission
2) To see if any unsaved changes needed to be autosaved before
submitting

The second one, after autosaving, would trigger the form submit event
once again, this time bypassing the changes unsaved check and would
trigger the form to be submitted normally (no AJAX).

However, for some reason still unknown, the submission after the
autosave was being blocked.

I have tried removing the event handlers, but to no avail.
I saw that there were multiple submit buttons across the various
sections, and they were not disabled. I then thought that these could be
interfering in the submit handlers.

Triggering a click on the current step submit button works beautifully.

--- app/assets/javascripts/application.js.coffee
+++ app/assets/javascripts/application.js.coffee
@@ -285,7 +285,7 @@
       e.stopPropagation()

       autosave ->
-        $(".qae-form").trigger("submit")
+        $(".steps-progress-content .step-current button[type='submit']").click()

   #
   # In case if was attempt to submit and validation errors are present


update login view to include icon for cas auth

--- app/assets/javascripts/components/login.cjsx
+++ app/assets/javascripts/components/login.cjsx
@@ -50,7 +50,7 @@

   renderLoginOptions: (label,classNames) ->
     links = @props.loginProviders.map (link) ->
-      icon_id = if link.id == 'zooniverse' then 'dot-circle-o' else link.id
+      icon_id = if link.id == 'zooniverse' then 'dot-circle-o' else if link.id == 'cas' then 'graduation-cap' else link.id


Fixed issue with multi select not working with arrow keys

--- app/assets/javascripts/gl_dropdown.js.coffee
+++ app/assets/javascripts/gl_dropdown.js.coffee
@@ -32,10 +32,8 @@
       else if @input.val() is "" and $inputContainer.hasClass HAS_VALUE_CLASS
         $inputContainer.removeClass HAS_VALUE_CLASS

-      if keyCode is 13 and @input.val() isnt ""
-        if @options.enterCallback
-          @options.enterCallback()
-        return
+      if keyCode is 13
+        return false

       clearTimeout timeout
       timeout = setTimeout =>
@@ -132,7 +130,6 @@
       @filterInput = @getElement(FILTER_INPUT)
       @highlight = false
       @filterInputBlur = true
-      @enterCallback = true
     } = @options

     self = @
@@ -178,9 +175,6 @@
         callback: (data) =>
           currentIndex = -1
           @parseData data
-        enterCallback: =>
-          if @enterCallback
-            @selectRowAtIndex 0

     # Event listeners


OOPSIE

OOPSIE

--- app/assets/javascripts/models/user.js.coffee
+++ app/assets/javascripts/models/user.js.coffee
@@ -70,7 +70,7 @@
     if @get('remove_avatar')
       obj.remove_avatar = @get('remove_avatar')
     if @get('skype_name')
-      obj.remove_avatar = @get('skype_name')
+      obj.skype_name = @get('skype_name')

     return obj


dodano info o za duzym zdjeciu

dodano info o za duzym zdjeciu

--- app/assets/javascripts/profile/photos.coffee
+++ app/assets/javascripts/profile/photos.coffee
@@ -29,11 +29,15 @@
     ]

     add: (e, data) ->
-      $photo_hidden.removeClass('hidden')
-      $photo_upload.hide()
-      jqXHR = data.submit()
-      $cancel_upload.click (e) ->
-        jqXHR.abort()
+      console.log(data.originalFiles[0]['size'])
+      if data.originalFiles[0]['size'] > 2097152
+        alert("Plik przekracza 2mb")
+      else
+        $photo_hidden.removeClass('hidden')
+        $photo_upload.hide()
+        jqXHR = data.submit()
+        $cancel_upload.click (e) ->
+          jqXHR.abort()

     progressall: (e, data) ->
       progress = parseInt(data.loaded / data.total * 100, 10)


No longer needed

No longer needed

--- apps/personalize/client/views/artists.coffee
+++ apps/personalize/client/views/artists.coffee
@@ -9,22 +9,10 @@
 BookmarkedArtists = require '../mixins/bookmarked_artists.coffee'
 { FollowButton, Following } = require '../../../../components/follow_button/index.coffee'
 Artists = require '../../../../collections/artists.coffee'
-analytics = require '../../../../lib/analytics.coffee'
 mediator = require '../../../../lib/mediator.coffee'
 imagesLoaded = require 'imagesloaded'
 template = -> require('../../templates/artists.jade') arguments...
 suggestedArtistsTemplate = -> require('../../templates/suggested_artists.jade') arguments...
-
-Analytics =
-  attach: ->
-    mediator.on 'follow-button:follow', ($el, model) ->
-      analytics.track.click "Followed artist from personalize #{$el.data 'analyticsLabel'}"
-
-    mediator.on 'follow-button:unfollow', ($el, model) ->
-      analytics.track.click "Unfollowed artist from personalize #{$el.data 'analyticsLabel'}"
-
-  detach: ->
-    mediator.off 'follow-button:unfollow follow-button:follow'

 module.exports = class ArtistsView extends StepView
   _.extend @prototype, Followable
@@ -53,8 +41,6 @@
     @listenTo @followed, 'remove', @disposeSuggestionSet
     @listenTo @suggestions, 'add', @renderSuggestions
     @listenTo @suggestions, 'remove', @renderSuggestions
-
-    Analytics.attach()

   initializeArtistsFromFavorites: ->
     new Backbone.Collection().fetch
@@ -157,7 +143,6 @@
     this

   remove: ->
-    Analytics.detach()
     @searchBarView.remove()
     @suggestions.each (suggestionSet) => @disposeSuggestionSet(suggestionSet)
     super


clean

clean

--- project/coffee/AppView.coffee
+++ project/coffee/AppView.coffee
@@ -86,15 +86,7 @@
         return

     showModalTest :=>
-        switch @clicks
-            when -1
-                @modalManager.hideOpenModal()
-                @clicks = 0
-
-            when 0
-                @modalManager.showModal 'overlayContent'
-                @clicks = -1
-
+        @modalManager.showModal 'overlayContent'
         null

     onAllRendered : =>


GITLAB_CONFIG_FILEの説明を追加


Equation Builder removing parentheses around -/+

--- javascripts/widgets/math_buttons.coffee
+++ javascripts/widgets/math_buttons.coffee
@@ -35,7 +35,7 @@
   negative_slash_positive: (opts)->
     @button({
       value: '-/+'
-      label: "(–/+)"
+      label: "–/+"
       class: 'math-button number-specifier negative-slash-positive'
     }, opts)


Limit the gallery partnership page to only admins.

--- apps/gallery_partnerships/index.coffee
+++ apps/gallery_partnerships/index.coffee
@@ -9,8 +9,8 @@
 app.set 'views', __dirname + '/templates'
 app.set 'view engine', 'jade'

-app.get '/gallery-partnerships', routes.index
-app.get /^\/gallery-partnerships\/((?!edit$).)+$/, routes.index # Scroll routes
+app.get '/gallery-partnerships', routes.adminOnly, routes.index
+app.get /^\/gallery-partnerships\/((?!edit$).)+$/, routes.adminOnly, routes.index # Scroll routes

 # Safely init upload routes for missing S3 env vars (like in test)
 try


Update boolean type mapping

Update boolean type mapping

--- layout.coffee
+++ layout.coffee
@@ -111,7 +111,7 @@

   typemappings =
     number: parseFloat
-    boolean: (val) -> (!! val)
+    boolean: (val) -> (if (typeof val == 'string') then val == 'true' else (!! val))

   installMethod = (scope, methodname, method) ->
     if methodname of scope


Minor search improvements

Minor search improvements

--- assets/javascripts/app/searcher.coffee
+++ assets/javascripts/app/searcher.coffee
@@ -26,7 +26,7 @@
 lastIndex = value.lastIndexOf(query)

 if index isnt lastIndex
-  return Math.max(scoreExactMatch(), (index = lastIndex) and scoreExactMatch())
+  return Math.max(scoreExactMatch(), ((index = lastIndex) and scoreExactMatch()) or 0)
 else
   return scoreExactMatch()
 `}`
@@ -112,7 +112,7 @@
     max_results: app.config.max_results
     fuzzy_min_length: 3

-  SEPARATORS_REGEXP = /\:?\ |#|::|->|\$(?=\w)|\-(?=\w)/g
+  SEPARATORS_REGEXP = /#|::|:-|->|\$(?=\w)|\-(?=\w)|\:(?=\w)|\ [\/\-&]\ |:\ |\ /g
   INFO_PARANTHESES_REGEXP = /\ \(\w+?\)$/
   EMPTY_PARANTHESES_REGEXP = /\(\)/
   EVENT_REGEXP = /\ event$/
@@ -166,7 +166,7 @@
     return

   isValid: ->
-    queryLength > 0
+    queryLength > 0 and query isnt SEPARATOR

   end: ->
     @triggerResults [] unless @totalResults


Redirect to signout redirect url when 401 accessing config

--- lib/assets/javascripts/config.js.coffee
+++ lib/assets/javascripts/config.js.coffee
@@ -15,6 +15,9 @@
   }]
   callback: (res, xhr) ->
     if xhr.status != 200
+      if xhr.status == 401
+        return window.location.href = TentStatus.config.SIGNOUT_REDIRECT_URL
+
       return setImmediate =>
         throw "failed to load json config via GET #{TentStatus.config.JSON_CONFIG_URL}: #{xhr.status} #{JSON.stringify(res)}"


Add roomid when room name can't be got

Add roomid when room name can't be got

--- src/rocketchat.coffee
+++ src/rocketchat.coffee
@@ -170,8 +170,10 @@
 									user.room = roomName
 								)
 							else
+								user.room = newmsg.rid
 								return Q()
 						).catch((err) =>
+							user.room = newmsg.rid
 							return Q()
 						).then(() =>
 							user.roomID = newmsg.rid


improve browser example

improve browser example

--- examples/browser.coffee
+++ examples/browser.coffee
@@ -1,4 +1,4 @@
-# an stream of things to search for
+# a stream of things to search for
 search = new Bacon.Bus

 # a stream of clicks on leaves. our finder is created asynchronously, so we need a bus for clicks
@@ -29,19 +29,15 @@

 # create an input field. it will reflect the current search
 i = text_input()
-search.onValue (v) -> i.set v
-
-# the value of our form is the latest user input or clicked search
-input_search = search.merge(i.changes()).toProperty()
+i.addSource search

 submit = button 'find'

 # send the value of the form when the submit button is clicked
-triggered_search = input_search.sampledBy(submit)
-search.plug triggered_search
+triggered_search = submit.map i

 # when searches happen, kick off a finder
-live search, (n) ->
+live search.merge(triggered_search), (n) ->
   finder = find n
   # send leaf clicks on to our bus
   leaf_clicks.plug finder.clicks.filter('.is_leaf').map('.path')


fix for prev/next proposals

fix for prev/next proposals

--- @client/shared.coffee
+++ @client/shared.coffee
@@ -246,7 +246,7 @@
 window.clustered_proposals_with_tabs = ->
   all_clusters = clustered_proposals()
   homepage_tabs = fetch 'homepage_tabs'
-  if homepage_tabs.filter?
+  if homepage_tabs.filter? && homepage_tabs.filter != 'Show all'
     to_remove = []
     for cluster, index in all_clusters or []
       cluster_key = "list/#{cluster.name}"


Edit tasks/redirect

Edit tasks/redirect

--- tasks/redirect.coffee
+++ tasks/redirect.coffee
@@ -39,15 +39,17 @@
   get_commands = get_commands = (options) ->
     # split into command and args
     keys = _(options).keys()
-    commands = _(keys).reduce((commands, key) ->
+    _(keys).reduce((commands, key) ->
       obj = {}
       command_text = options[key]
+      # has args
       if /\s/.test(command_text)
         command = command_text.match(/(.*)\s/)[1]
         args = get_args(command_text)
         obj[key] =
           command: command
           args: args
+      # command only
       else
         obj[key] =
           command: command_text
@@ -55,7 +57,6 @@
       _(commands).extend obj
       commands
     , {})
-    commands

   # to boolean
@@ -68,9 +69,9 @@
   #
   redirect_task_func = redirect_task_func = ->
     done_callback = @async()
-    options = @data
-    commands = get_commands(options.files)
-    files = get_files(options.files)
+    options       = @data
+    commands      = get_commands(options.files)
+    files         = get_files(options.files)

     # extend flags
     _(flags).extend @options()
@@ -88,14 +89,14 @@

           # set written data
           write_body = ""
-          write_body += result.stdout  if flags.stdout
-          write_body += result.stderr  if flags.stderr
+          write_body += result.stdout if flags.stdout
+          write_body += result.stderr if flags.stderr

           # check to exist the file if flags.overwrite equals false
-          throw new Error("E003: File is found (" + filename + ")")  if fs.existsSync(filename)  unless flags.overwrite
+          throw new Error("E003: File is found (#{filename})") if fs.existsSync(filename)  unless flags.overwrite
           fs.writeFileSync filename, write_body
         else
-          grunt.log.writeln command.command + " > " + filename + ": NG"
+          grunt.log.writeln "#{command.command} > #{filename}: NG"
         callback res

@@ -104,7 +105,7 @@
     # run command each file
     #
     functions = _(files).map((filename) ->
-      exec_command.bind this, filename
+      exec_command.bind @, filename
     )

     grunt.log.writeln "Processing tasks..."


handle empty variations

handle empty variations

--- public/views/product_detail_view.coffee
+++ public/views/product_detail_view.coffee
@@ -18,15 +18,19 @@
   """

   render: ->
-    @model.variations.fetch(reset: true).done =>
-      @$el.html @template
-        name: @model.name()
-        image: @model.mediumImage()
-        description: @model.get("description")
-        availabilityText: @model.get("availabilityText")
-        price: @model.price()
-        shopId: "TODO" # TODO
-      @$el.find("#variations").
-        html(new VariationListView(collection: @model.variations).render().el)
+    @$el.html @template
+      name: @model.name()
+      image: @model.mediumImage()
+      description: @model.get("description")
+      availabilityText: @model.get("availabilityText")
+      price: @model.price()
+      shopId: "TODO" # TODO
+    @model.variations.fetch
+      reset: true
+      success: =>
+        @$el.find("#variations").
+          html(new VariationListView(collection: @model.variations).render().el)
+      error: =>
+        @$el.find("#variations").html "No variations found"

     this


this reads slightly better.

this reads slightly better.

--- public/views/product_detail_view.coffee
+++ public/views/product_detail_view.coffee
@@ -142,11 +142,13 @@
     event.preventDefault()
     event.target.disabled = true # disable button

+    addLineItem = =>
+      App.cart.addLineItem @model.id()
+
     if App.cart.isNew()
-      App.cart.save().done =>
-        App.cart.addLineItem @model.id()
+      App.cart.save().done(addLineItem)
     else
-      App.cart.addLineItem @model.id()
+      addLineItem()

   updateVariations: =>
     matchingVariationItem = @model.variationItems().find (item) =>


Finished implementation of all CPU instructions.

--- src/CPU.coffee
+++ src/CPU.coffee
@@ -66,6 +66,19 @@
     ROL: 41
     RTI: 42
     RTS: 43
+    SBC: 44
+    SEC: 45
+    SED: 46
+    SEI: 47
+    LDA: 48
+    LDX: 49
+    LDY: 50
+    TAX: 51
+    TAY: 52
+    TSX: 53
+    TXA: 54
+    TXS: 55
+    TYA: 56

 Interrupt =
     IRQ:   1
@@ -533,6 +546,62 @@
             tick()
             tick()

+        @registerInstruction Instruction.RTS, (address) ->
+            operand = @readByte address
+            result = @accumulator - operand
+            result-- if @carryFlag is off
+            @carryFlag = not @isOverflow result
+            @zeroFlag = @isZero result
+            @overflowFlag = @isSignedOverflow @accumulator, operand, result
+            @negativeFlag = @isNegative result
+            @accumulator = result & 0xFF
+
+        @registerInstruction Instruction.SEC, ->
+            @carryFlag = on
+
+        @registerInstruction Instruction.SED, ->
+            @decimalMode = on
+
+        @registerInstruction Instruction.SEI, ->
+            @interruptDisable = on
+
+        @registerInstruction Instruction.LDA, (address) ->
+            @writeByte address @accumulator
+
+        @registerInstruction Instruction.LDX, (address) ->
+            @writeByte address @registerX
+
+        @registerInstruction Instruction.LDY, (address) ->
+            @writeByte address @registerY
+
+        @registerInstruction Instruction.TAX, ->
+            @registerX = @accumulator
+            @zeroFlag = @isZero @registerX
+            @negativeFlag = @isNegative @registerX
+
+        @registerInstruction Instruction.TAY, ->
+            @registerY = @accumulator
+            @zeroFlag = @isZero @registerY
+            @negativeFlag = @isNegative @registerY
+
+        @registerInstruction Instruction.TSX, ->
+            @registerX = @stackPointer
+            @zeroFlag = @isZero @registerX
+            @negativeFlag = @isNegative @registerX
+
+        @registerInstruction Instruction.TXA, ->
+            @accumulator = @registerX
+            @zeroFlag = @isZero @accumulator
+            @negativeFlag = @isNegative @accumulator
+
+        @registerInstruction Instruction.TXS, ->
+            @stackPointer = @registerX
+
+        @registerInstruction Instruction.TYA, ->
+            @accumulator = @registerY
+            @zeroFlag = @isZero @accumulator
+            @negativeFlag = @isNegative @accumulator
+
     registerInstruction: (instruction, execution) ->
         @instructionsTable[instruction] = execution

@@ -559,7 +628,7 @@
     compareRegisterAndMemory: (register, address) ->
         operand = @readByte address
         result = register - operand
-        @carryFlag = result >= 0 # Exception on carry computation
+        @carryFlag = result >= 0
         @computeZeroFlag result
         @computeNegativeFlag result

@@ -679,9 +748,9 @@

         @registerOperation 0xA9, Instruction.LDX, AddressingMode.Immediate,   no,  no
         @registerOperation 0xA5, Instruction.LDX, AddressingMode.ZeroPage,    no,  no
-        @registerOperation 0xB5, Instruction.LDX, AddressingMode.ZeroPageX,   no,  no
+        @registerOperation 0xB5, Instruction.LDX, AddressingMode.ZeroPageY,   no,  no
         @registerOperation 0xAD, Instruction.LDX, AddressingMode.Absolute,    no,  no
-        @registerOperation 0xBD, Instruction.LDX, AddressingMode.AbsoluteX,   no,  no
+        @registerOperation 0xBD, Instruction.LDX, AddressingMode.AbsoluteY,   no,  no

         @registerOperation 0xA9, Instruction.LDY, AddressingMode.Immediate,   no,  no
         @registerOperation 0xA5, Instruction.LDY, AddressingMode.ZeroPage,    no,  no
@@ -730,6 +799,49 @@

         @registerOperation 0x60, Instruction.RTS, AddressingMode.Implied,     no,  no

+        @registerOperation 0xE9, Instruction.SBC, AddressingMode.Immediate,   no,  no
+        @registerOperation 0xE5, Instruction.SBC, AddressingMode.ZeroPage,    no,  no
+        @registerOperation 0xF5, Instruction.SBC, AddressingMode.ZeroPageX,   no,  no
+        @registerOperation 0xED, Instruction.SBC, AddressingMode.Absolute,    no,  no
+        @registerOperation 0xFD, Instruction.SBC, AddressingMode.AbsoluteX,   no,  no
+        @registerOperation 0xF9, Instruction.SBC, AddressingMode.AbsoluteY,   no,  no
+        @registerOperation 0xE1, Instruction.SBC, AddressingMode.IndirectX,   no,  no
+        @registerOperation 0xF1, Instruction.SBC, AddressingMode.IndirectY,   no,  no
+
+        @registerOperation 0x38, Instruction.SEC, AddressingMode.Implied,     no,  no
+
+        @registerOperation 0xF8, Instruction.SED, AddressingMode.Implied,     no,  no
+
+        @registerOperation 0x78, Instruction.SEI, AddressingMode.Implied,     no,  no
+
+        @registerOperation 0x85, Instruction.STA, AddressingMode.ZeroPage,    no,  no
+        @registerOperation 0x95, Instruction.STA, AddressingMode.ZeroPageX,   no,  no
+        @registerOperation 0x8D, Instruction.STA, AddressingMode.Absolute,    no,  no
+        @registerOperation 0x9D, Instruction.STA, AddressingMode.AbsoluteX,   yes, no
+        @registerOperation 0x99, Instruction.STA, AddressingMode.AbsoluteY,   yes, no
+        @registerOperation 0x81, Instruction.STA, AddressingMode.IndirectX,   no,  no
+        @registerOperation 0x91, Instruction.STA, AddressingMode.IndirectY,   yes, no
+
+        @registerOperation 0x86, Instruction.STX, AddressingMode.ZeroPage,    no,  no
+        @registerOperation 0x96, Instruction.STX, AddressingMode.ZeroPageY,   no,  no
+        @registerOperation 0x8E, Instruction.STX, AddressingMode.Absolute,    no,  no
+
+        @registerOperation 0x84, Instruction.STY, AddressingMode.ZeroPage,    no,  no
+        @registerOperation 0x94, Instruction.STY, AddressingMode.ZeroPageX,   no,  no
+        @registerOperation 0x8C, Instruction.STY, AddressingMode.Absolute,    no,  no
+
+        @registerOperation 0xAA, Instruction.TAX, AddressingMode.Implied,     no,  no
+
+        @registerOperation 0xA8, Instruction.TAY, AddressingMode.Implied,     no,  no
+
+        @registerOperation 0xBA, Instruction.TSX, AddressingMode.Implied,     no,  no
+
+        @registerOperation 0x8A, Instruction.TXA, AddressingMode.Implied,     no,  no
+
+        @registerOperation 0x9A, Instruction.TXS, AddressingMode.Implied,     no,  no
+
+        @registerOperation 0x98, Instruction.TYA, AddressingMode.Implied,     no,  no
+
     registerOperation: (operationCode, instruction, addressingMode, emptyReadCycle, emptyWriteCycle) ->
         @operationsTable[operationCode] =
             instruction: instruction


payment: boilerplate for plans section of group products pane

--- client/landing/app/Applications/Dashboard.kdapplication/views/groupproductsettingsview.coffee
+++ client/landing/app/Applications/Dashboard.kdapplication/views/groupproductsettingsview.coffee
@@ -48,6 +48,8 @@
   setProducts: (products) ->
     @productsView.setProducts products

+  setPlans: (plans) -> console.log 'need to set the plans', plans
+
   pistachio: ->
     """
     {{> @productsView}}


collision with regular bounds

collision with regular bounds

--- test/collision.coffee
+++ test/collision.coffee
@@ -51,6 +51,6 @@
   rect = GameObject
     x: 25
     y: 0
-
+    width: 10
 module()


only execute replace if str

only execute replace if str

--- lib/atom-ternjs-documentation-view.coffee
+++ lib/atom-ternjs-documentation-view.coffee
@@ -34,17 +34,18 @@
     @element.classList.remove('active')

   setTitle: (name, params) ->
+    return unless name
     @title.innerHTML = name
     @sub.innerHTML = params

   setContent: (str) ->
     if str
       @allowed = true
+      str = str.replace(/(?:\r\n|\r|\n)/g, '
')
+      @content.innerHTML = str
     else
       @allowed = false
       @hide()
-    str = str.replace(/(?:\r\n|\r|\n)/g, '
')
-    @content.innerHTML = str

   # Tear down any state and detach
   destroy: ->


Adding tests

Adding tests


Detect attempted use of an unconfigured LCID, and throw an error.

--- lib/token/colmetadata-token-parser.coffee
+++ lib/token/colmetadata-token-parser.coffee
@@ -57,6 +57,8 @@

       collation.codepage = codepageByLcid[collation.lcid]
       collation.iconv = iconvByLcid[collation.lcid]
+      if !collation.iconv
+        throw new Error("No codepage for LCID #{collation.lcid}")

       # This may not be extracting the correct nibbles in the correct order.
       collation.flags = collationData[3] >> 4


Check redid keys after each test

Check redid keys after each test

--- test/type_date.coffee
+++ test/type_date.coffee
@@ -104,4 +104,4 @@
               Records.get records[0].record_id, (err, record) ->
                 should.not.exist err
                 record.a_date.getTime().should.eql date.getTime()
-                next()
+                Records.clear next


Fixed annoying bug to do with the move-to-mat buttons sometimes not working. Also, bubble tips now disappear when loosing focus

--- client/src/Screen/GameScreen.coffee
+++ client/src/Screen/GameScreen.coffee
@@ -415,6 +415,8 @@
   ###
   drawAllocationMoveMenu: (clickedOn) ->
     @allocationMoveMenuOn = clickedOn
+    console.log "draw Allocation menu"
+    console.log clickedOn
     #Create the new allocation menu
     html = '
Required
             Optional
@@ -438,7 +440,7 @@
         effect: -> $(this).fadeIn(400)
         ready: true
       hide:
-        event: false
+        event: 'unfocus'
         effect: -> $(this).fadeOut(100)
       style:
         classes: 'qtip-shadow qtip-light qtip-rounded'
@@ -446,6 +448,7 @@
         height: '70px'
       events:
         render: (event, api) ->
+          console.log "Render fired"
           #Add event listener for each of the "required" "optional" and "forbidden" buttons inside the menu
           clickedIndex = $('#unallocated li').index($(clickedOn))
           $("#mamenu-required-btn").click -> network.moveToRequired(clickedIndex)
@@ -453,7 +456,13 @@
           $("#mamenu-forbidden-btn").click -> network.moveToForbidden(clickedIndex)

           #In general, remove the allocation menu one someone clicks any of the buttons
-          $("#move-allocation-menu span").click (event) -> api.hide()
+          $(".mamenu-button").click (event) -> api.hide()
+
+        hidden: (event, api) ->
+          # Removes the buttons since even though they're hidden, they're still there.
+          # This fixes the click listner bug.
+          $('.mamenu-button').remove()
+

   ###*


Update example

Update example

--- example/index.coffee
+++ example/index.coffee
@@ -13,8 +13,12 @@

   constructor: (@todos) ->

-  add: (todo) ->
+  push: (todo) ->
     @todos.push(todo)
+    @emit 'update'
+
+  shift: ->
+    @todos.shift()
     @emit 'update'

 class TodoView extends Component
@@ -43,4 +47,7 @@

 window.addEventListener 'load', ->
   new Mount(todoListView).mount(document.getElementById('main'))
-  setInterval (-> todoCollection.add {title: 'New task'}), 1000
+  shift = ->
+    todoCollection.shift()
+    todoCollection.push {title: 'New task'}
+  setInterval shift, 1000


Use damage type for color, show damage type in title.

--- scripts/bungie-data-helper.coffee
+++ scripts/bungie-data-helper.coffee
@@ -8,16 +8,23 @@
       @vendorDefs = JSON.parse(body)

   'serializeFromApi': (response) ->
-    rarityColor =
-      Uncommon: '#f5f5f5'
-      Common: '#2f6b3c'
-      Rare: '#557f9e'
-      Legendary: '#4e3263'
-      Exotic: '#ceae32'
+    # rarityColor =
+    #   Uncommon: '#f5f5f5'
+    #   Common: '#2f6b3c'
+    #   Rare: '#557f9e'
+    #   Legendary: '#4e3263'
+    #   Exotic: '#ceae32'
+
+    damageColor =
+      Kinetic: '#d9d9d9'
+      Arc: '#80b3ff'
+      Solar: '#e68a00'
+      Void: '#400080'

     item = response.data.item
     hash = item.itemHash
     itemDefs = response.definitions.items[hash]
+    damageTypeName = response.definitions.damageTypes[item.damageTypeHash].damageTypeName

     prefix = 'http://www.bungie.net'
     iconSuffix = itemDefs.icon
@@ -27,24 +34,23 @@
     itemDescription: itemDefs.itemDescription
     itemTypeName: itemDefs.itemTypeName
     rarity: itemDefs.tierTypeName
-    color: rarityColor[itemDefs.tierTypeName]
+    color: damageColor[damageTypeName] or '#d9d9d9'
     iconLink: prefix + iconSuffix
     itemLink: prefix + itemSuffix
     primaryStat: item.primaryStat
     stats: item.stats
     nodes: response.data.talentNodes
     nodeDefs: response.definitions.talentGrids[item.talentGridHash].nodes
-    damageType: item.damageTypeHash
-    damageDefs: response.definitions.damageTypes
-
+    damageType: damageTypeName

   'parseItemsForAttachment': (items) ->
     items.map (item) => @parseItemAttachment(item)

   'parseItemAttachment': (item, showDetails) ->
     hasStats = item.stats
+    name = "#{item.itemName}"
+    name+= " [#{item.damageType}]" unless item.damageType is "Kinetic"
     statFields = if hasStats then @buildStats(item.stats, item.primaryStat) else []
-    damageEmoji = @emojiFromDamageType(item)
     filtered = @filterNodes(item.nodes, item.nodeDefs)
     nodeFields = @buildFields(filtered, item.nodeDefs)
     textHash = @buildText(filtered, item.nodeDefs)
@@ -53,7 +59,7 @@
       string.slice(0, -3)

     fallback: item.itemDescription
-    title: "#{item.itemName} #{damageEmoji}"
+    title: name
     title_link: item.itemLink
     color: item.color
     text: formattedText.join('\n')
@@ -82,19 +88,6 @@
       foundStats.unshift(primaryStat)

     foundStats.filter (x) -> x
-
-  # returns emoji based on damage type for use in title
-  'emojiFromDamageType': (item) ->
-    arcEmoji = ':bladedancer:'
-    solarEmoji = ':firebolt:'
-    voidEmoji = ':voidwalker:'
-
-    damageType = item.damageDefs[item.damageType].damageTypeName
-    switch damageType
-      when "Arc" then arcEmoji
-      when "Solar" then solarEmoji
-      when "Void" then voidEmoji
-      else  ""

   # removes invalid nodes, orders according to column attribute
   'filterNodes': (nodes, nodeDefs) ->


Updating module in root

Updating module in root

--- iOSKit.coffee
+++ iOSKit.coffee
@@ -83,7 +83,7 @@

 		# iPhone 6s
 		when 750
-			device = "apple-ipad-6s-silver"
+			device = "apple-iphone-6s-silver"
 			Framer.Device.deviceType = "fullscreen"

 		# iPhone 6s+


compile functions need to take options

compile functions need to take options

--- test/multipass/plugins/blarg.coffee
+++ test/multipass/plugins/blarg.coffee
@@ -5,7 +5,7 @@
   file_type: 'blarg'
   target: ''

-exports.compile = (file, callback) ->
+exports.compile = (file, options, callback) ->
   error = false
   compiled_contents = null


:bug: no longer loose focus from editor when `narrow:close` without restoreEditorState

--- lib/ui.coffee
+++ lib/ui.coffee
@@ -495,7 +495,7 @@

   close: ->
     @provider.restoreEditorStateIfNecessary()
-    @destroy()
+    @getPane().destroyItem(@editor, true)

   resetQuery: ->
     @setQuery() # clear query


cleanup

cleanup

--- lib/ui.coffee
+++ lib/ui.coffee
@@ -264,10 +264,6 @@
       if @query and @autoPreviewOnQueryChange
         @preview() unless previewd

-  moveToBeginningOfSelectedItem: ->
-    point = @items.getFirstPositionForSelectedItem()
-    @editor.setCursorBufferPosition(point)
-
   observeStopChangingActivePaneItem: ->
     atom.workspace.onDidStopChangingActivePaneItem (item) =>
       if item isnt @editor
@@ -425,12 +421,12 @@
       @grammar.update(filterSpec.include) # No need to highlight excluded items

     promiseForItems.then (items) =>
-      items = @provider.filterItems(items, filterSpec)
       if @excludedFiles.length
         items = items.filter ({filePath}) => filePath not in @excludedFiles
+      items = @provider.filterItems(items, filterSpec)

       oldSelectedItem = @items.getSelectedItem()
-      wasCursorSyncWithSelectedItem = not @isCursorOutOfSyncWithSelectedItem()
+      wasAtSelectedItem = @isAtSelectedItem()
       oldColumn = @editor.getCursorBufferPosition().column

       @items.setItems([@promptItem, items...])
@@ -439,7 +435,7 @@
       if @items.hasNormalItem()
         if (not selectFirstItem) and item = @items.findItem(oldSelectedItem)
           @items.selectItem(item)
-          if wasCursorSyncWithSelectedItem
+          if wasAtSelectedItem
             @moveToSelectedItem(ignoreCursorMove: not @isActive())
             row = @editor.getCursorBufferPosition().row
             @editor.setCursorBufferPosition([row, oldColumn])
@@ -584,17 +580,13 @@
         @highlighter.flashItem(editor, item) if flash
         @emitDidConfirm({editor, item})

-  moveToNextFileItem: ->
-    @moveToDifferentFileItem('next')
-
-  moveToPreviousFileItem: ->
-    @moveToDifferentFileItem('previous')
-
-  isCursorOutOfSyncWithSelectedItem: ->
-    @editor.getCursorBufferPosition().row isnt @items.getRowForSelectedItem()
+  # Cursor move and position status
+  # ------------------------------
+  isAtSelectedItem: ->
+    @editor.getCursorBufferPosition().row is @items.getRowForSelectedItem()

   moveToDifferentFileItem: (direction) ->
-    if @isCursorOutOfSyncWithSelectedItem()
+    unless @isAtSelectedItem()
       @moveToSelectedItem(ignoreCursorMove: false)
       return

@@ -604,19 +596,27 @@
       @items.selectItem(item)
       @moveToSelectedItem(ignoreCursorMove: false)

+  moveToNextFileItem: ->
+    @moveToDifferentFileItem('next')
+
+  moveToPreviousFileItem: ->
+    @moveToDifferentFileItem('previous')
+
   moveToPromptOrSelectedItem: ->
-    row = @items.getRowForSelectedItem()
-    if (row is @editor.getCursorBufferPosition().row) or not (row >= 0)
+    if @isAtSelectedItem()
       @moveToPrompt()
     else
-      # move to current item
-      @editor.setCursorBufferPosition([row, 0])
+      @moveToBeginningOfSelectedItem()
+
+  moveToBeginningOfSelectedItem: ->
+    @editor.setCursorBufferPosition(@items.getFirstPositionForSelectedItem())

   moveToPrompt: ->
     @withIgnoreCursorMove =>
       @editor.setCursorBufferPosition(@getPromptRange().end)
       @setReadOnly(false)
       @emitDidMoveToPrompt()
+  # -------------------------

   isPromptRow: (row) ->
     row is 0


Wrapped kill function

Wrapped kill function

--- tests/unit/specs/spec_server.coffee
+++ tests/unit/specs/spec_server.coffee
@@ -33,6 +33,7 @@
         "write": sinon.spy()
         "end": sinon.spy()
       "on": sinon.spy()
+      "kill": sinon.spy()
     exec = sinon.stub().returns exec_ret
     plugin = karmaServer.invoke(
       "server": server


bugfix user find if id is not part of the entity

--- modules/http/lib/sessions/Sessions.coffee
+++ modules/http/lib/sessions/Sessions.coffee
@@ -132,7 +132,7 @@
                     @_checkPasswordHash user, pass, data, (err, data)=>
                         return fn(err) if err

-                        @parent.children.users.set data.id, data, (err)=>
+                        @parent.children.users.set (data.id||user), data, (err)=>

                             data = floyd.tools.objects.clone data,
                                 login: user
@@ -148,13 +148,16 @@
         ##
         ##
         _loadUser: (id, fn)->
-            query = {}
-            query[@data.login.id] = id
-
-            @parent.children.users.find query,
-                limit: 1
-            , null, (err, items)=>
-                fn err, items?[0]
+            if @data.login.id is 'id'
+                @parent.children.users.get id, fn
+
+            else
+                query = {}
+                query[@data.login.id] = id
+                @parent.children.users.find query,
+                    limit: 1
+                , null, (err, items)=>
+                    fn err, items?[0]

         ##


Updated app to AMD

Updated app to AMD

--- src/app.coffee
+++ src/app.coffee
@@ -1,7 +1,7 @@
-do (window = @, MilkSugar = @MilkSugar) ->
+define 'app', ['milksugar/app'], (MilkSugarApp) ->
   'use strict';

-  class AppModel extends MilkSugar.App
+  class AppModel extends MilkSugarApp

     constructor: ->
       super()
@@ -12,5 +12,5 @@

-  window.App = new AppModel()
-  window.App.run()
+  App = new AppModel()
+  App.run()


Remove all artifacts on unlink: config, logs, history, etc.

--- src/app.coffee
+++ src/app.coffee
@@ -1,5 +1,5 @@
 async     = require 'async'
-fs        = require 'fs'
+fs        = require 'fs-extra'
 os        = require 'os'
 path      = require 'path-extra'
 readdirp  = require 'readdirp'
@@ -177,10 +177,11 @@
             if err and err.message isnt "This device doesn't exist"
                 log.error 'An error occured while unregistering your device.'
                 log.error err
+                callback? err
             else
-                @config.removeRemoteCozy deviceName
                 log.info 'Current device properly removed from remote cozy.'
-            callback? err
+                fs.remove @basePath, ->
+                    callback? err

     # Send an issue by mail to the support


Remove console.log.

Remove console.log.

--- src/app.coffee
+++ src/app.coffee
@@ -161,7 +161,6 @@
                 @units.reset()

         setUnits: (units, filter) ->
-            console.log filter
             @services.set []
             @_setSelectedUnits()
             @units.reset units.toArray()


Fix images not deploying properly

Fix images not deploying properly

--- Web/gruntfile.coffee
+++ Web/gruntfile.coffee
@@ -56,7 +56,7 @@
 				flatten: true
 			img:
 				src:  'img/rohbot.png'
-				dest: 'dist/'
+				dest: 'dist/rohbot.png'
 			index:
 				src:  'index.htm'
 				dest: 'build/'


Updated remaining expression manipulation types

--- math/expression_manipulation_spec.coffee
+++ math/expression_manipulation_spec.coffee
@@ -5,7 +5,7 @@
 it_plays_manipulation_role = (subject, math)->
   describe "acting as an expression manipulation", ->
     beforeEach ->
-      @subject = subject(@)
+      @subject = subject.call(@)

     it "has an invoke method", ->
       expect(typeof @subject.invoke).toEqual "function"
@@ -18,7 +18,7 @@
 it_inserts_component_into_the_last_nested_open_expression = (specifics)->
   it "adds #{specifics.name} to the deepest final open expression", ->
     @exp = @exp_builder(open_expression: {open_expression: []})
-    exp = specifics.manipulator(@).invoke(@exp)
+    exp = specifics.manipulator.call(@).invoke(@exp)
     expected = specifics.should_equal_after(@exp_builder)
     expect(exp).toBeAnEqualExpressionTo expected

@@ -132,7 +132,7 @@

   describe "appending multiplication", ->
     it_plays_manipulation_role (test)->
-      test.manip.append_multiplication.build()
+      @manip.append_multiplication.build()

     it "adds multiplication to the end of the expression", ->
       exp = @exp_builder(1)
@@ -141,13 +141,28 @@

     it_inserts_component_into_the_last_nested_open_expression(
       name: 'multiplication'
-      manipulator: (test)-> test.manip.append_multiplication.build()
+      manipulator: -> @manip.append_multiplication.build()
       should_equal_after: (builder)-> builder(open_expression: {open_expression: ['*']}))

+  describe "appending division", ->
+    it_plays_manipulation_role (test)->
+      @manip.append_division.build()
+
+    it "adds a division to the expression", ->
+      exp = @exp_builder(1)
+
+      new_exp = @manip.append_division.build().invoke(exp)
+      expect(new_exp.last() instanceof @math.components.division).toBeTruthy()
+
+    it_inserts_component_into_the_last_nested_open_expression(
+      name: 'division'
+      manipulator: -> @manip.append_division.build()
+      should_equal_after: (builder)-> builder(open_expression: {open_expression: ['/']}))
+

   describe "appending addition", ->
     it_plays_manipulation_role (test)->
-      test.manip.append_addition.build()
+      @manip.append_addition.build()

     it "adds addition to the end of the expression", ->
       exp = @exp_builder(1)
@@ -156,8 +171,40 @@

     it_inserts_component_into_the_last_nested_open_expression(
       name: 'addition'
-      manipulator: (test)-> test.manip.append_addition.build()
+      manipulator: -> @manip.append_addition.build()
       should_equal_after: (builder)-> builder(open_expression: {open_expression: ['+']}))
+
+
+
+  describe "appending subtraction", ->
+    it_plays_manipulation_role (test)->
+      @manip.append_subtraction.build()
+
+    it "adds subtraction to the end of the expression", ->
+      exp = @exp_builder(1)
+      new_exp = @manip.append_subtraction.build().invoke(exp)
+      expect(new_exp.last() instanceof @math.components.subtraction).toEqual true
+
+    it_inserts_component_into_the_last_nested_open_expression(
+      name: 'subtraction'
+      manipulator: -> @manip.append_subtraction.build()
+      should_equal_after: (builder)-> builder(open_expression: {open_expression: ['-']}))
+
+  describe "appending a decimal", ->
+    it_plays_manipulation_role ->
+      @manip.append_decimal.build(value: 5)
+
+    it "correctly adds a decimal to the value", ->
+      exp = @math.expression.build()
+      exp = @manip.append_number.build(value: 1).invoke(exp)
+      exp = @manip.append_decimal.build().invoke(exp)
+      exp = @manip.append_number.build(value: 1).invoke(exp)
+      @expect_value(exp, '1.1')
+
+    it_inserts_component_into_the_last_nested_open_expression(
+      name: 'decimal'
+      manipulator: -> @manip.append_decimal.build()
+      should_equal_after: (builder)-> builder(open_expression: {open_expression: ['0.']}))

   describe "square expression", ->
@@ -165,48 +212,21 @@
       @square = @manip.square.build()

     it_plays_manipulation_role (test)->
-      test.square
+      @square

     it "returns a squared expression", ->
       exp = @exp_builder(10)
       squared = @square.invoke(exp)
       @expect_value(squared, '100')

-
   describe "(moved over from other test file)", ->
-
-    describe "the decimal manipulation", ->
-      it "correctly adds a decimal to the value", ->
-        exp = @math.expression.build()
-        exp = @manip.append_number.build(value: 1).invoke(exp)
-        exp = @manip.decimal.build().invoke(exp)
-        exp = @manip.append_number.build(value: 1).invoke(exp)
-        @expect_value(exp, '1.1')
-
     describe "NumberManipulation", ->
       it_plays_manipulation_role (test)->
-        test.manip.append_number.build(value: 5)
-
-    describe "DecimalManipulation", ->
-      it_plays_manipulation_role (test)->
-        test.manip.decimal.build(value: 5)
-
-    describe "SubtractionManipulation", ->
-      it_plays_manipulation_role (test)->
-        test.manip.subtraction.build()
-
-      it "adds subtraction to the end of the expression", ->
-        exp = @math.expression.buildWithContent([
-          @math.components.number.build(value: '1')
-        ])
-
-        new_exp = @manip.subtraction.build().invoke(exp)
-
-        expect(new_exp.last() instanceof @math.components.subtraction).toEqual true
+        @manip.append_number.build(value: 5)

     describe "NegationManipulation", ->
       it_plays_manipulation_role (test)->
-        test.manip.negate_last.build()
+        @manip.negate_last.build()

       it "will negate the last element", ->
         exp = @math.expression.buildWithContent([
@@ -215,20 +235,10 @@
         new_exp = @manip.negate_last.build().invoke(exp)
         expect(new_exp.last().value()).toEqual -1

-    describe "DivisionManipulation", ->
-      it_plays_manipulation_role (test)->
-        test.manip.division.build()
-
-      it "adds a division to the expression", ->
-        exp = @math.expression.buildWithContent([
-          @math.components.number.build(value: '1')
-        ])
-        new_exp = @manip.division.build().invoke(exp)
-        expect(new_exp.last() instanceof @math.components.division).toBeTruthy()

     describe "PiManipulation", ->
       it_plays_manipulation_role (test)->
-        test.manip.pi.build()
+        @manip.pi.build()

       it "adds a mulitplication and pi to the expression", ->
         exp = @math.expression.buildWithContent([
@@ -240,7 +250,7 @@

     describe "SquareRootManipulation", ->
       it_plays_manipulation_role (test)->
-        test.manip.square_root.build()
+        @manip.square_root.build()

       it "finds the square root of an expression", ->
         exp = @math.expression.buildWithContent([


Call reconnect() when a ping doesnt arrive in expected time

--- lib/assets/javascripts/cable.js.coffee
+++ lib/assets/javascripts/cable.js.coffee
@@ -117,7 +117,7 @@
   pingReceived: (timestamp) =>
     if @lastPingTime? and (timestamp - @lastPingTime) > @PING_STALE_INTERVAL
       console.log "Websocket connection is stale. Reconnecting.."
-      @connection?.close()
+      @reconnect()
     else
       @startWaitingForPing()
       @lastPingTime = timestamp


move refresh stuff into the page load function

--- js/app.coffee
+++ js/app.coffee
@@ -239,26 +239,28 @@
       display "dashboard",
         builds_table: 'project'

-  loadEditOrg: (username, refresh, callback) =>
-    if refresh
+  loadOrgSettings: (username, callback) =>
+    $.getJSON "/api/v1/organization/#{username}/settings", (data) =>
+      @org().clean() if @org()
+      @org(new CI.inner.Org data)
+      @org_has_been_loaded(true)
+      mixpanel.track("View Org", {"username": username})
+      if callback? then callback()
+
+  loadEditOrgPage: (username, subpage) =>
+    subpage = subpage[0].replace('#', '').replace('-', '_')
+    subpage = subpage || "projects"
+
+    subpage_callback = () => @org().subpage(subpage)
+
+    if !@org() or (@org().name() isnt username)
       @org_has_been_loaded(false)
       @org().clean() if @org()
       @org(null)

-    if refresh or !@org() or (@org().name() isnt username)
-      $.getJSON "/api/v1/organization/#{username}/settings", (data) =>
-        @org().clean() if @org()
-        @org(new CI.inner.Org data)
-        @org_has_been_loaded(true)
-        mixpanel.track("View Org", {"username": username})
-        if callback? then callback()
+      @loadOrgSettings(username, subpage_callback)
     else
-      if callback? then callback()
-
-  loadEditOrgPage: (username, subpage) =>
-    subpage = subpage[0].replace('#', '').replace('-', '_')
-    subpage = subpage || "projects"
-    @loadEditOrg(username, false, () => @org().subpage(subpage))
+      subpage_callback()

     display 'org_settings',
       subpage: subpage


Don't remove the query string

Don't remove the query string

--- js/app.coffee
+++ js/app.coffee
@@ -456,4 +456,4 @@
   if window.circleEnvironment is 'development'
     CI.maybeOverrideABTests(window.location.search, VM.ab)

-  SammyApp.run path
+  SammyApp.run path + window.location.search