split 'optimize' into its own command

split 'optimize' into its own command

pertains to #3

--- component.coffee
+++ component.coffee
@@ -311,8 +311,6 @@
      - install dependencies to the build directory
      - write the runtime config if the [templatepath] option is given (stuff
        like the require.js config, string bundles, etc.)
-     - make optimized files if the [optimize] option is given (i.e. compiling
-       all JS into one file)
     """
     action: () ->
       config = getConfig()
@@ -323,101 +321,6 @@
         wrench.copyDirSyncRecursive defaultStaticpath(), argv.staticpath

       commands.install.action()
-
-      if argv.optimize
-
-        # returns the on-disk filename for a css dep like "css!100:foo.css"
-        cssFilename = (moduleName) ->
-          _.compose.apply(_, [
-            # remove plugin prefix css!100:
-            (n) -> n.replace /^css!(\d+:)?/, ""
-
-            # check the paths config, replace the path if it's in there
-            (n) ->
-              for pfx,pth of config.paths
-                if n[...pfx.length] is pfx
-                  return pth + n[pfx.length..]
-              n
-
-            # add the .css extension
-            (n) -> if n[-4..] isnt ".css" then n + ".css" else n
-
-            # prefix the path with the path to the static directory on disk
-            (n) -> join argv.staticpath, n
-          ].reverse())(moduleName)
-
-        # returns the order for a css dep like "css!100:foo.css"
-        cssOrder = (moduleName) ->
-          +(moduleName.match(/^css!((\d+):)?/)[2] or "0")
-
-        # get the css text from the css object compiled during the build
-        compileCss = (allCss) ->
-          ordered = (css for n,css of allCss).sort (a, b) -> a.order > b.order
-          (css.contents for css in ordered).join("\n")
-
-        # return the contents of `filename` w/ the css url(...) paths
-        # re-written appropriately
-        cssWithPathsReWritten = (fname) ->
-          updateCssPaths read(fname), (u) ->
-            if u[0] isnt "/" and u[0..4] isnt "data:"
-              join(argv.baseurl, dirname(withOutStaticPath(fname)), u)
-            else
-              u
-
-        # go through all the components build configs and create a "paths"
-        # config for the require.js optimizer that includes empty modules for
-        # all excluded files
-        pathsConfig = () ->
-          allComponents().reduce (paths, component) ->
-            if component.build?.exclude
-              paths[module] = "empty:" for module in component.build.exclude
-            paths
-          , extend(true, {strings: "empty:"}, config.paths)
-
-
-        output = (text, opts) ->
-          text = minify(text) if opts.minify
-          cssText = if opts.minify then cssmin(opts.css) else opts.css
-          hash = crypto.createHash("md5").update(text + cssText).digest("hex")
-          id = "#{module.name}-#{hash}#{if opts.minify then ".min" else ""}"
-          rId = re('(define\\([\'"])' + module.name)
-          text = text.replace rId, "$1" + id
-
-          name = join argv.staticpath, "#{id}.js"
-          write name, text
-          log "writing optimized#{opts.minify and "/minified" or ""} file to #{name}"
-
-          cssName = join argv.staticpath, "#{id}.css"
-          write cssName, cssText
-          log "writing optimized#{opts.minify and "/minified" or ""} file to #{cssName}"
-
-        profile = buildProfile()
-        for module in profile.modules
-          css = {}
-          if isComponent() and /^\.[\/\\]/.test(module.name)
-            module.name = join(config.paths[componentName()], module.name[2..])
-          requirejs.optimize
-            baseUrl: argv.staticpath
-            paths: pathsConfig()
-            shim: config.shim
-            name: module.name
-            optimize: "none"
-            keepBuildDir: true
-            onBuildWrite: (moduleName, path, contents) ->
-              if moduleName[..3] is "css!"
-                filename = cssFilename moduleName
-                css[moduleName] =
-                  name: moduleName
-                  order: cssOrder moduleName
-                  path: path
-                  filename: filename
-                  contents: cssWithPathsReWritten filename, moduleName, path
-              contents
-            out: (text) ->
-              cssText = compileCss css
-              output text, module: module, css: cssText
-              if argv.minify
-                output text, minify: true, module: module, css: cssText

       # this must be after the optimization step, since it checks for optimized
       # files and includes them in the config
@@ -428,6 +331,106 @@
         log "writing context json to #{contextjsonname}"
         write contextjsonname, JSON.stringify(templateObj)

+
+  optimize:
+    description: """
+    optimize the static resources by creating one .js and one .css file.  the
+    files may be optionally minified.
+    """
+    action: () ->
+      config = getConfig()
+      # returns the on-disk filename for a css dep like "css!100:foo.css"
+      cssFilename = (moduleName) ->
+        _.compose.apply(_, [
+          # remove plugin prefix css!100:
+          (n) -> n.replace /^css!(\d+:)?/, ""
+
+          # check the paths config, replace the path if it's in there
+          (n) ->
+            for pfx,pth of config.paths
+              if n[...pfx.length] is pfx
+                return pth + n[pfx.length..]
+            n
+
+          # add the .css extension
+          (n) -> if n[-4..] isnt ".css" then n + ".css" else n
+
+          # prefix the path with the path to the static directory on disk
+          (n) -> join argv.staticpath, n
+        ].reverse())(moduleName)
+
+      # returns the order for a css dep like "css!100:foo.css"
+      cssOrder = (moduleName) ->
+        +(moduleName.match(/^css!((\d+):)?/)[2] or "0")
+
+      # get the css text from the css object compiled during the build
+      compileCss = (allCss) ->
+        ordered = (css for n,css of allCss).sort (a, b) -> a.order > b.order
+        (css.contents for css in ordered).join("\n")
+
+      # return the contents of `filename` w/ the css url(...) paths
+      # re-written appropriately
+      cssWithPathsReWritten = (fname) ->
+        updateCssPaths read(fname), (u) ->
+          if u[0] isnt "/" and u[0..4] isnt "data:"
+            join(argv.baseurl, dirname(withOutStaticPath(fname)), u)
+          else
+            u
+
+      # go through all the components build configs and create a "paths"
+      # config for the require.js optimizer that includes empty modules for
+      # all excluded files
+      pathsConfig = () ->
+        allComponents().reduce (paths, component) ->
+          if component.build?.exclude
+            paths[module] = "empty:" for module in component.build.exclude
+          paths
+        , extend(true, {strings: "empty:"}, config.paths)
+
+
+      output = (text, opts) ->
+        text = minify(text) if opts.minify
+        cssText = if opts.minify then cssmin(opts.css) else opts.css
+        hash = crypto.createHash("md5").update(text + cssText).digest("hex")
+        id = "#{module.name}-#{hash}#{if opts.minify then ".min" else ""}"
+        rId = re('(define\\([\'"])' + module.name)
+        text = text.replace rId, "$1" + id
+
+        name = join argv.staticpath, "#{id}.js"
+        write name, text
+        log "writing optimized#{opts.minify and "/minified" or ""} file to #{name}"
+
+        cssName = join argv.staticpath, "#{id}.css"
+        write cssName, cssText
+        log "writing optimized#{opts.minify and "/minified" or ""} file to #{cssName}"
+
+      profile = buildProfile()
+      for module in profile.modules
+        css = {}
+        if isComponent() and /^\.[\/\\]/.test(module.name)
+          module.name = join(config.paths[componentName()], module.name[2..])
+        requirejs.optimize
+          baseUrl: argv.staticpath
+          paths: pathsConfig()
+          shim: config.shim
+          name: module.name
+          optimize: "none"
+          keepBuildDir: true
+          onBuildWrite: (moduleName, path, contents) ->
+            if moduleName[..3] is "css!"
+              filename = cssFilename moduleName
+              css[moduleName] =
+                name: moduleName
+                order: cssOrder moduleName
+                path: path
+                filename: filename
+                contents: cssWithPathsReWritten filename, moduleName, path
+            contents
+          out: (text) ->
+            cssText = compileCss css
+            output text, module: module, css: cssText
+            if argv.minify
+              output text, minify: true, module: module, css: cssText

   completion:
     description: """
@@ -523,11 +526,6 @@
       "default": "/static"
       describe: "specify the baseurl\n(cmd: build)"

-    .option "optimize",
-      boolean: true
-      alias: "o"
-      describe: "make an optimized build\n(cmd: build)"
-
     .option "buildprofile",
       string: true
       alias: "P"
@@ -535,14 +533,14 @@
       describe: """
       this is a file containing parameters for running the r.js optimizer to produce a single file.  for example:
           ({ modules: [ {name: "static/index"} ] })
-      (cmd: build)
+      (cmd: optimize)
       """

     .option "minify",
       boolean: true
       alias: "m"
       "default": false
-      describe: "minify javascript in optimized builds\n(cmd: build)"
+      describe: "minify javascript in optimized builds\n(cmd: optimize)"

     .alias("h", "help")


Remove unnecesary space on coffeescript file

--- app/assets/javascripts/advanced_search.js.coffee
+++ app/assets/javascripts/advanced_search.js.coffee
@@ -22,7 +22,7 @@

     $('.js-calendar').datepicker
       regional: locale
-      maxDate:  "+0d"
+      maxDate: "+0d"
     $('.js-calendar-full').datepicker
       regional: locale


Remove trailing whitespace

Remove trailing whitespace

--- app/assets/javascripts/finish_planting.js.coffee
+++ app/assets/javascripts/finish_planting.js.coffee
@@ -9,11 +9,11 @@
     finished = $('#planting_finished_at')
     if @checked
       if previousValue.length
-        date = previousValue
+        date = previousValue
         finished.val(date)
       else
         finished.trigger('focus')
     else
       previousValue = finished.val()
       finished.val('')
-  )+  )


OX-484 removed sorting

OX-484 removed sorting

--- app/assets/javascripts/oxalis/model/route.coffee
+++ app/assets/javascripts/oxalis/model/route.coffee
@@ -598,7 +598,7 @@
     return null

-  getTrees : -> @trees.sort(@compareNames)
+  getTrees : -> @trees

   # returns a list of nodes that are connected to the parent


adding login component to header

adding login component to header

--- app/assets/javascripts/partials/main-header.cjsx
+++ app/assets/javascripts/partials/main-header.cjsx
@@ -1,6 +1,7 @@
 # @cjsx React.DOM

 React = require 'react'
+Login = require '../components/login'
 # LoadingIndicator = require '../components/loading-indicator'
 # MainNav = require './main-nav'
 # AccountBar = require './account-bar'
@@ -25,7 +26,7 @@

   render: ->

-
+

           Scribe 2.0
         Mark
@@ -34,7 +35,7 @@
         { @props.pages.map (page, key) =>
             {page.name}
         }
-
+        <Login></Login>


IMPROVED: getVersions returns Neptune's version too FIX: if for some reason we load Neptune twice, don't replace the global namespace! (this happens when loading compiled versions of NN)

--- source/Runtime/Neptune.coffee
+++ source/Runtime/Neptune.coffee
@@ -63,6 +63,7 @@

   @getVersions: ->
     out = {}
+    out.version = @version if @ == Neptune
     for key, subNamespace of @namespaces
       out[key] = recurse if 0 < Object.keys(recurse = subNamespace.getVersions()).length
       (out[key] ||= {}).version = subNamespace.version if subNamespace.version?
@@ -181,7 +182,7 @@
     else
       @[propName] = value

-module.exports = global.Neptune = class Neptune extends Base
+module.exports = global.Neptune ||= class Neptune extends Base
   @Base: Base
   @namespacePath: "Neptune"
   @namespace: null


remove regression stuff, can't get it working correctly

--- source/javascripts/all.coffee
+++ source/javascripts/all.coffee
@@ -4,8 +4,8 @@

 x = d3.scale.linear().range [padding / 2, size - padding / 2]
 y = d3.scale.linear().range [size - padding / 2, padding / 2]
-xreg = d3.scale.linear().range [padding / 2, size - padding / 2]
-yreg = d3.scale.linear().range [size - padding / 2, padding / 2]
+xreg = d3.scale.linear().range [padding, size - padding]
+yreg = d3.scale.linear().range [size - padding, padding]

 xax = d3.svg.axis().scale(x).orient('bottom').ticks(5)
 yax = d3.svg.axis().scale(y).orient('left').ticks(5)
@@ -71,18 +71,6 @@
         .attr('r', 2)
         .attr('class', (d) -> "#{d.position.toLowerCase().split('-')[0]}")

-      regdata = data.map (d) -> [d[p.x], d[p.y]]
-      reg = regression 'exponential', regdata
-      xreg.domain(d3.extent reg.points, (d) -> d[0])
-      yreg.domain(d3.extent reg.points, (d) -> d[1])
-
-      path = d3.svg.line().x((d) -> xreg d[0]).y((d) -> yreg d[1])
-
-      el.append('path')
-        .datum(reg.points)
-        .attr('d', path)
-        .attr('class', 'regression')
-
   cell = vis.selectAll('.cell')
     .data(crossed)
   .enter().append('g')


Fix zig zag offset for backwards; stop being clever

--- components/zig_zag_banner/index.coffee
+++ components/zig_zag_banner/index.coffee
@@ -1,7 +1,7 @@
 _ = require 'underscore'
 Backbone = require 'backbone'
+Cookies = require 'cookies-js'
 template = -> require('./template.jade') arguments...
-Cookies = require 'cookies-js'

 module.exports = class ZigZagBanner extends Backbone.View
   className: 'zig-zag-banner'
@@ -20,17 +20,20 @@
       throw new Error('You must pass a name, $target, and message')

     if @backwards
-      @$el.addClass('zig-zag-backwards').css "margin-left": @$target.width()
+      @$el.addClass('zig-zag-backwards').css marginLeft: @$target.outerWidth()

     if @persist
-      if Cookies.get("zig_zag_#{@name}")?
+      if Cookies.get(@cookieName())?
         # Has already seen this... ignore
         return @remove()
       else
         # Will see this once until cookie expires
-        Cookies.set "zig_zag_#{@name}", true, expires: 60 * 60 * 24 * 365
+        Cookies.set @cookieName(), true, expires: 60 * 60 * 24 * 365

     @transitionIn()
+
+  cookieName: ->
+    "zig_zag_#{@name}"

   render: ->
     @$el.html @template(message: @message)
@@ -49,36 +52,35 @@
     newMargin = offset + existingMargin
     @$el.css marginTop: newMargin

-  sequence: (arr) ->
-    _.map arr, (fn, i) =>
-      dfd = $.Deferred()
-      _.delay =>
-        dfd.resolve fn()
-      , (@segmentTransitionLength * (i + 1))
-      dfd.promise()
+  delay: (cb) ->
+    _.delay cb, @segmentTransitionLength

-  transitionIn: ->
+  transitionIn: (cb) ->
     @render()
     _.defer =>
-      @sequence [
-        => @$one.addClass 'is-in'
-        => @$two.addClass 'is-in'
-        => @$three.addClass 'is-in'
-        => @$el.addClass 'is-done'
-      ]
+      @delay =>
+        @$one.addClass 'is-in'
+        @delay =>
+          @$two.addClass 'is-in'
+          @delay =>
+            @$three.addClass 'is-in'
+            @delay =>
+              @$el.addClass 'is-done'
+              @delay(cb) if cb?

-  transitionOut: ->
-    dfd = $.Deferred()
-    @sequence([
-      => @$el.removeClass 'is-done'
-      => @$three.removeClass 'is-in'
-      => @$two.removeClass 'is-in'
-      => @$one.removeClass 'is-in'
-    ]).then dfd.resolve
-    dfd.promise()
+  transitionOut: (cb) ->
+    @delay =>
+      @$el.removeClass 'is-done'
+      @delay =>
+        @$three.removeClass 'is-in'
+        @delay =>
+          @$two.removeClass 'is-in'
+          @delay =>
+            @$one.removeClass 'is-in'
+            @delay(cb) if cb?

   close: (e) ->
     e?.preventDefault()
     e?.stopPropagation()
-    @transitionOut().then =>
+    @transitionOut =>
       @remove()


Disable grab-back-focus in all frames.

Disable grab-back-focus in all frames.

When the user begins interacting with one frame, we disable the
grab-back-focus mode in *all* frames.

Previously, grab-back-focus was preventing users from selecting inputs
in frames other than the active frame.

Fixes #2296 (possibly).

--- content_scripts/vimium_frontend.coffee
+++ content_scripts/vimium_frontend.coffee
@@ -41,14 +41,20 @@
 # If an input grabs the focus before the user has interacted with the page, then grab it back (if the
 # grabBackFocus option is set).
 class GrabBackFocus extends Mode
+
   constructor: ->
+    exitEventHandler = =>
+      @alwaysContinueBubbling =>
+        @exit()
+        chrome.runtime.sendMessage handler: "sendMessageToFrames", message: name: "userIsInteractingWithThePage"
+
     super
       name: "grab-back-focus"
-      keydown: => @alwaysContinueBubbling => @exit()
+      keydown: exitEventHandler

     @push
       _name: "grab-back-focus-mousedown"
-      mousedown: => @alwaysContinueBubbling => @exit()
+      mousedown: exitEventHandler

     Settings.use "grabBackFocus", (grabBackFocus) =>
       # It is possible that this mode exits (e.g. due to a key event) before the settings are ready -- in
@@ -62,6 +68,15 @@
           @grabBackFocus document.activeElement if document.activeElement
         else
           @exit()
+
+    # This mode is active in all frames.  A user might have begun interacting with one frame without other
+    # frames detecting this.  When one GrabBackFocus mode exits, we broadcast a message to inform all
+    # GrabBackFocus modes that they should exit; see #2296.
+    chrome.runtime.onMessage.addListener listener = ({name}) =>
+      if name == "userIsInteractingWithThePage"
+        chrome.runtime.onMessage.removeListener listener
+        @exit() if @modeIsActive
+      false # We will not be calling sendResponse.

   grabBackFocus: (element) ->
     return @continueBubbling unless DomUtils.isFocusable element
@@ -167,10 +182,12 @@
     linkHintsMessage: (request) -> HintCoordinator[request.messageType] request

   chrome.runtime.onMessage.addListener (request, sender, sendResponse) ->
-    # These requests are intended for the background page, but they're delivered to the options page too.
+    # Some requests intended for the background page are delivered to the options page too; ignore them.
     unless request.handler and not request.name
-      if isEnabledForUrl or request.name in ["checkEnabledAfterURLChange", "runInTopFrame"]
-        requestHandlers[request.name] request, sender, sendResponse
+      # Some request are handled elsewhere; ignore them too.
+      unless request.name in ["userIsInteractingWithThePage"]
+        if isEnabledForUrl or request.name in ["checkEnabledAfterURLChange", "runInTopFrame"]
+          requestHandlers[request.name] request, sender, sendResponse
     false # Ensure that the sendResponse callback is freed.

 # Wrapper to install event listeners.  Syntactic sugar.


:white_check_mark: Add tests for buffer markers removal

--- spec/color-buffer-spec.coffee
+++ spec/color-buffer-spec.coffee
@@ -108,6 +108,29 @@
           expect(updateSpy.argsForCall[0][0].destroyed.length).toEqual(0)
           expect(updateSpy.argsForCall[0][0].created.length).toEqual(1)

+      describe 'when a variable marker is removed', ->
+        [colorsUpdateSpy] = []
+        beforeEach ->
+          updateSpy = jasmine.createSpy('did-update-variable-markers')
+          colorsUpdateSpy = jasmine.createSpy('did-update-color-markers')
+          colorBuffer.onDidUpdateVariableMarkers(updateSpy)
+          colorBuffer.onDidUpdateColorMarkers(colorsUpdateSpy)
+          editBuffer '', start: [0,0], end: [0,17]
+          waitsFor -> updateSpy.callCount > 0
+
+        it 'updates the modified variable marker', ->
+          expect(colorBuffer.getVariableMarkerByName('base-color')).toBeUndefined()
+
+        it 'dispatches the new marker in a did-update-variable-markers event', ->
+          expect(updateSpy.argsForCall[0][0].destroyed.length).toEqual(1)
+          expect(updateSpy.argsForCall[0][0].created.length).toEqual(0)
+
+        it 'invalidates colors that were relying on the deleted variables', ->
+          waitsFor -> colorsUpdateSpy.callCount > 0
+          runs ->
+            expect(colorBuffer.getColorMarkers().length).toEqual(3)
+            expect(colorBuffer.getValidColorMarkers().length).toEqual(2)
+
     describe 'with a buffer with only colors', ->
       beforeEach ->
         waitsForPromise ->
@@ -172,6 +195,28 @@
           expect(colorsUpdateSpy.argsForCall[0][0].destroyed.length).toEqual(0)
           expect(colorsUpdateSpy.argsForCall[0][0].created.length).toEqual(1)

+      describe 'when a color marker is edited', ->
+        [colorsUpdateSpy] = []
+
+        beforeEach ->
+          waitsForPromise -> colorBuffer.variablesAvailable()
+
+          runs ->
+            colorsUpdateSpy = jasmine.createSpy('did-update-color-markers')
+            colorBuffer.onDidUpdateColorMarkers(colorsUpdateSpy)
+            editBuffer '', start: [1,2], end: [1,23]
+            waitsFor -> colorsUpdateSpy.callCount > 0
+
+        it 'updates the modified color marker', ->
+          expect(colorBuffer.getColorMarkers().length).toEqual(2)
+
+        it 'updates only the affected marker', ->
+          expect(colorsUpdateSpy.argsForCall[0][0].destroyed.length).toEqual(1)
+          expect(colorsUpdateSpy.argsForCall[0][0].created.length).toEqual(0)
+
+        it 'removes the previous editor markers', ->
+          expect(editor.findMarkers(type: 'pigments-color').length).toEqual(2)
+
   describe 'when the editor is destroyed', ->
     it 'destroys the color buffer at the same time', ->
       editor.destroy()


Make sure git-ignored files are created by specs

--- spec/path-scanner-spec.coffee
+++ spec/path-scanner-spec.coffee
@@ -74,6 +74,7 @@
     beforeEach ->
       rootPath = fs.realpathSync("spec/fixtures/git")
       fs.rename(path.join(rootPath, 'git.git'), path.join(rootPath, '.git'))
+      fs.writeFileSync(path.join(rootPath, 'ignored.txt'), "This must be added in the spec because the file can't be checked in!")

     afterEach ->
       fs.rename(path.join(rootPath, '.git'), path.join(rootPath, 'git.git'))
@@ -93,8 +94,8 @@
         expect(paths.length).toBe 2
         expect(paths).not.toContain path.join(rootPath, 'ignored.txt')

-    it "includes files specified with .gitignore with excludeVcsIgnores == false", ->
-      scanner = new PathScanner(rootPath)
+    it "includes files matching .gitignore patterns when excludeVcsIgnores == false", ->
+      scanner = new PathScanner(rootPath, excludeVcsIgnores: false)
       scanner.on('path-found', pathHandler = createPathCollector())
       scanner.on('finished-scanning', finishedHandler = jasmine.createSpy())


Remove unused showNewCardOverlay action

Remove unused showNewCardOverlay action

--- app/assets/javascripts/routes/application_route.js.coffee
+++ app/assets/javascripts/routes/application_route.js.coffee
@@ -22,31 +22,6 @@
       transition.abort()
       @get('spinner')?.stop()

-    showNewCardOverlay: (tmplName, taskType, phase) ->
-      paper = @controllerFor('paperManage').get('model')
-      newTaskParams = {phase: phase, type: taskType.replace(/^new/, ''), paper: paper}
-      newTask = @store.createRecord(taskType, newTaskParams)
-      controllerName = 'newCardOverlay'
-      if taskType == 'MessageTask'
-        controllerName = 'newMessageCardOverlay'
-        currentUser = @getCurrentUser()
-        @store.createRecord('participation', participant: currentUser, task: newTask)
-        newTask.get('comments').pushObject(@store.createRecord('comment', commenter: currentUser))
-
-      taskParticipations = @store.filter 'participation', (part) ->
-        part.get('task') == newTask
-
-      @controllerFor(controllerName).setProperties({
-        model: newTask
-        paper: paper
-        participations: taskParticipations
-      })
-
-      @render(tmplName,
-        into: 'application'
-        outlet: 'overlay'
-        controller: controllerName)
-
     closeOverlay: ->
       @animateOverlayOut().then =>
         @disconnectOutlet


stop error messages from interrupting themselves when removed

fixes CNVS-21480

test plan
- create an assignment
- leave the name field blank, select a submission type but check off no boxes
  for sumbission methods, and make "Assign To" blank in the date override area
- attempt to save
- error messages should all read out properly with no interruptions
- enter a name for the assignment and attempt to save
- only the remaining relevant error messages should be read
- test in some other areas where we utilize flash error messages, such as
  assignment quick add modal and when creating an outcome

Change-Id: I4bf9d3d320f18e9c98dd1bcb56f641878e46e6e6
Reviewed-on: https://gerrit.instructure.com/60636
Reviewed-by: John Corrigan <3d73713d0cc718326e4fd7c06c64e067ad6e2270@instructure.com>
Tested-by: Jenkins
Reviewed-by: Ryan Taylor <2eafa1f6bf34395b543d6ca7f438461b0774d24f@instructure.com>
QA-Review: Michael Hargiss <59e3fde81b8d9a4f5e0cc016a3c2695591912604@instructure.com>
Product-Review: Jason Sparks <542e620f0c6c3447ce6e2e8c83a97102b99f89c6@instructure.com>

--- app/coffeescripts/jquery.rails_flash_notifications.coffee
+++ app/coffeescripts/jquery.rails_flash_notifications.coffee
@@ -35,8 +35,10 @@
     """)

     $screenreader_node.appendTo($screenreader_holder)
-    # By not removing these in a timely manner, they can stack up and become repetitive
-    window.setTimeout((-> $screenreader_node.remove()), 1000)
+    # we're hiding these instead of removing them, since calling .remove() on a
+    # DOM node contained in an 'assertive' live region causes another alert that
+    # interrupts whatever alert message(s) are in progress
+    window.setTimeout((-> $screenreader_node.attr('aria-hidden', 'true')), 1000)

   flashBox = (type, content, timeout, cssOptions = {}) ->
     $node = $("""


typo fixes.

typo fixes.

--- assets/coffeescript/product_list.coffee
+++ assets/coffeescript/product_list.coffee
@@ -163,8 +163,8 @@
     cost: 800
     label: "Oculus Rift DK2 Monocular Add-on"
     specs: "120hz monocular eye tracking add-on for Oculus Rift DK2. Includes 1 x 120hz eye cameras and USB cables."
-    cartlabel: "Monocular Eye Tracking add-on for Oculus Rift DK2"
-    cartSpecs: "1 x 120hz eye tracking add-on for Oculus Rift DK2."
+    cartlabel: "Oculus Rift DK2 Monocular Add-on"
+    cartSpecs: "1 x 120hz eye tracking add-on for Oculus Rift DK2. Includes 1 x 120hz eye cameras and USB cables."
     dimensions: 0
     weight: 20
     img: "../media/images/additional_products/web/addon_oculus_dk2.png"
@@ -174,8 +174,8 @@
     cost: 1400
     label: "Oculus Rift DK2 Binocular Add-on"
     specs: "120hz binocular eye tracking add-on for Oculus Rift DK2. Includes 2 x 120hz eye cameras and USB cables."
-    cartlabel: "Pupil Binocular for Oculus Rift DK2"
-    cartSpecs: "2 x 120hz IR eye cameras, 1 x 120hz world camera, custom designed mounts for Epson Moverio BT-200"
+    cartlabel: "Oculus Rift DK2 Binocular Add-on"
+    cartSpecs: "2 x 120hz eye tracking add-on for Oculus Rift DK2. Includes 2 x 120hz eye cameras and USB cables."
     dimensions: 0
     weight: 20
     img: "../media/images/additional_products/web/addon_oculus_dk2.png"


Computed property observes @each on arrays

--- packages/emberella/lib/mixins/mq_mixin.coffee
+++ packages/emberella/lib/mixins/mq_mixin.coffee
@@ -372,7 +372,7 @@
     inProgressLength = +get(@, 'inProgress.length')

     queueLength > 0 and completedLength >= queueLength and inProgressLength is 0
-  .property 'queue.length', 'completed.length', 'inProgress.length'
+  .property 'queue.@each', 'completed.@each', 'inProgress.@each'

   ###
     An array of objects waiting to be processed. Once items are added to the


Arrange controller code to improve read

Arrange controller code to improve read

--- app/assets/javascripts/controllers/TimesController.coffee
+++ app/assets/javascripts/controllers/TimesController.coffee
@@ -1,14 +1,15 @@
 controllers = angular.module('controllers')
 controllers.controller("TimesController", [ '$scope', '$routeParams', '$location', 'Restangular', '$interval'
   ($scope, $routeParams, $location, Restangular, $interval) ->
+    # Controller variables
+    trackedTimes = Restangular.all('tracked_times');
+    timer = null
+    onError = (_httpResponse)-> console.log "Something went wrong"
+
+    # Scope Initialization
     $scope.times = []
     $scope.newTrackedTime = {}
     $scope.flash_error = null
-
-    trackedTimes = Restangular.all('tracked_times');
-
-    timer = null
-
     $scope.times = trackedTimes.getList().$object

     trackedTimes.customGET("current").then (result) ->
@@ -18,14 +19,15 @@
             $scope.current.duration += 1
           , 1000)

-    onError = (_httpResponse)-> console.log "Something went wrong"

+    # Event handlers
     $scope.clearErrors = ->
       $scope.flash_error = null

     $scope.startTracking = (newTrackedTime) ->
       trackedTimes.post(newTrackedTime).then(
         (result)->
+          $scope.newTrackedTime = {}
           $scope.current = result
           timer = $interval( ->
               $scope.current.duration += 1
@@ -36,12 +38,12 @@
       )

     $scope.stopTracking = ->
-      trackedTimes.customPUT(null, "stop").then((result) ->
-        $scope.current = null
-        $interval.cancel(timer);
-        $scope.times = trackedTimes.getList().$object
-      ,
-      onError
+      trackedTimes.customPUT(null, "stop").then(
+        (result) ->
+          $scope.current = null
+          $interval.cancel(timer);
+          $scope.times = trackedTimes.getList().$object
+        onError
       )

     $scope.delete = (id) ->


Fix dynamic filters [SJVC-11]

Fix dynamic filters [SJVC-11]

--- src/core/javascripts/controllers/event_list.js.coffee
+++ src/core/javascripts/controllers/event_list.js.coffee
@@ -206,10 +206,10 @@
       $scope.filterChanged()

-  $scope.$watch 'pick.date', (newValue, oldValue) =>
-    if newValue
-      $scope.start_date = moment(newValue)
-      $scope.end_date = moment(newValue)
+  $scope.$watch 'pick.date', (new_val, old_val) =>
+    if new_val
+      $scope.start_date = moment(new_val)
+      $scope.end_date = moment(new_val)
       $scope.init()


fix getOthers

fix getOthers

git-svn-id: 62c02ef84702a3047cfe9caf550d919eac72bfff@388 b2333513-9f29-43eb-bce0-aa7d4792c706

--- src/PersistenceManager.coffee
+++ src/PersistenceManager.coffee
@@ -215,10 +215,14 @@
     className = options.className or model.className
     definition = @_getDefinition className

-    where = _getInitializeCondition @, model, className, definition, _.extend {}, options,
-        useDefinitionColumn: false
-        useAttributes: false
-
+    try
+        where = _getInitializeCondition @, model, className, definition, _.extend {}, options,
+            useDefinitionColumn: false
+            useAttributes: false
+    catch err
+        callback err
+        return
+
     if where.length is 0
         @insert model, _.extend({}, options, reflect: true), callback
     else
@@ -246,10 +250,14 @@
     className = options.className or model.className
     definition = @_getDefinition className

-    where = _getInitializeCondition @, model, className, definition, _.extend {}, options,
-        useDefinitionColumn: false
-        useAttributes: false
-
+    try
+        where = _getInitializeCondition @, model, className, definition, _.extend {}, options,
+            useDefinitionColumn: false
+            useAttributes: false
+    catch err
+        callback err
+        return
+
     if where.length is 0
         @insert model, _.extend({}, options, reflect: true), callback
     else
@@ -282,7 +290,12 @@
     options = _.extend {}, options,
         models: [model]

-    options.where = _getInitializeCondition @, model, className, definition, _.extend {}, options, {useDefinitionColumn: false}
+    try
+        options.where = _getInitializeCondition @, model, className, definition, _.extend {}, options, {useDefinitionColumn: false}
+    catch err
+        callback err
+        return
+
     @list className, options, callback

 # return where condition to be parsed by RowMap
@@ -939,7 +952,11 @@
             id = model.get definition.id.name

             if 'undefined' is typeof id
-                where = _getInitializeCondition pMgr, model, definition.className, definition, _.extend {}, options, useDefinitionColumn: false
+                try
+                    where = _getInitializeCondition pMgr, model, definition.className, definition, _.extend {}, options, useDefinitionColumn: false
+                catch err
+                    callback err
+                    return
             else if definition.id.hasOwnProperty 'column'
                 where = '{' + pMgr.getIdName(definition.className) + '} = ' + id


OX-601 added hasChanged to plane2d

OX-601 added hasChanged to plane2d

--- app/assets/javascripts/oxalis/model/binary/plane2d.coffee
+++ app/assets/javascripts/oxalis/model/binary/plane2d.coffee
@@ -85,18 +85,9 @@
     @changed = true

-  getBucketArray : (center, range, area) ->
-
-    buckets = []
-
-    for u in [-(range-area[0])..(area[2]-range)]
-      for v in [-(range-area[1])..(area[3]-range)]
-        bucket = center.slice(0)
-        bucket[@u] += u
-        bucket[@v] += v
-        buckets.push if _.min(bucket) >= 0 then bucket else null
-
-    buckets
+  hasChanged : ->
+
+    @changed

   get : (position, {zoomStep, area}) ->


更新gulp命令

更新gulp命令

--- gulp/tasks/bower.coffee
+++ gulp/tasks/bower.coffee
@@ -13,9 +13,13 @@
 #--------------------------------------------------------

 gulp.task "bower", ->
-  gulp.src mainBowerFiles()
+  gulp.src mainBowerFiles('**/*.js')
     .pipe plugins.concat("vendor.js")
     .pipe gulp.dest "#{config.outputPath}/#{config.jsDirectory}"

+  gulp.src mainBowerFiles('**/*.css')
+    .pipe plugins.concat("vendor.css")
+    .pipe gulp.dest "#{config.outputPath}/#{config.cssDirectory}"
+
   gulp.src "#{config.vendorPath}/**/*"
     .pipe gulp.dest "#{config.outputPath}/vendor/"


fixes #2

fixes #2


Fixed config.public loading

Fixed config.public loading

--- app/loader.coffee
+++ app/loader.coffee
@@ -149,7 +149,15 @@
 	]

 expandPublics = ->
+
 	for href, components of radeditPublic
+		for componentRel, index in components
+			parts = componentRel.split ' '
+			if parts[0] isnt 'npm'
+				components[index] = publicRel + '/' + componentRel
+		config.public[href] = components
+
+	for href, components of config.public
 		for componentRel, index in components
 			parts = componentRel.split ' '
 			if parts[0] is 'npm'
@@ -162,11 +170,9 @@
 						expandPublics[path] = true
 						loader.loadFile path
 				scheduleLoading path
-				rel = getRel path
-			else
-				rel = publicRel + '/' + componentRel
-			components[index] = rel
+				components[index] = getRel path
 		config.public[href] = components
+

 expandPublics()

@@ -423,8 +429,6 @@
 	asset = createPublicAsset rel, content
 	loader.public.assets[rel] = asset

-	if /jymin/.test rel
-		something = true # TODO: Figure out why removing this fucks things up.
 	if groups = loader.public.parents[rel]
 		groups.forEach (group) ->
 			loader.public.pending[group] = true


Force save Slider's value

Force save Slider's value

--- bokehjs/src/coffee/widget/slider.coffee
+++ bokehjs/src/coffee/widget/slider.coffee
@@ -38,6 +38,7 @@
       value = ui.value
       console.log('sliding', value)
       @mset('value', value)
+      @model.save()

   class Slider extends HasParent
     type : "Slider"


pricing: reset group form and thank you views when a workflow is set

--- client/Pricing/AppView.coffee
+++ client/Pricing/AppView.coffee
@@ -2,6 +2,8 @@

   setWorkflow: (workflow) ->
     @workflow.destroy()  if @workflow
+    @groupForm?.destroy()
+    @thankYou?.destroy()
     @workflow = workflow
     @addSubView @workflow
     @workflow.on 'Finished', @bound "workflowFinished"
@@ -33,7 +35,7 @@
       then "Please check your email to complete your registration."
       else "Now it’s time, time to start Koding!"

-    @addSubView thankYou = new KDCustomHTMLView
+    @addSubView @thankYou = new KDCustomHTMLView
       cssClass : "pricing-thank-you"
       partial  :
         """


about browserified +1

about browserified +1

--- client/about/lib/index.coffee
+++ client/about/lib/index.coffee
@@ -1,8 +1,13 @@
-class AboutAppController extends KDViewController
+kd = require 'kd'
+KDViewController = kd.ViewController
+AboutAppView = require './aboutappview'

-  KD.registerAppClass this,
+
+module.exports = class AboutAppController extends KDViewController
+
+  @options =
     name  : 'About'
-    route : '/About'
+    route : '/:name?/About'

   constructor:(options = {}, data)->


Admin: Fix reference error.

Admin: Fix reference error.

--- client/admin/lib/index.coffee
+++ client/admin/lib/index.coffee
@@ -86,8 +86,8 @@
           targetPane = pane

       if targetPane
-        @mainView.tabs.showPane pane
-        pane.getMainView().handleIdentifier? identifier  if identifier
+        @mainView.tabs.showPane targetPane
+        targetPane.getMainView().handleIdentifier? identifier  if identifier
       else
         kd.singletons.router.handleRoute '/Admin/Settings'


add the ability to delete comments

add the ability to delete comments

--- client/views/blog/blog.coffee
+++ client/views/blog/blog.coffee
@@ -61,6 +61,10 @@
               sectionId: comment.sectionId
               authorName: comment.authorName
               comment: 'Please login to post comments'
+
+      sideComments.on 'commentDeleted', (comment) ->
+        Comment.destroyAll comment.id
+        sideComments.removeComment comment.sectionId, comment.id

 Template.blogShowBody.rendered = ->


Update cards-es.coffee

Update cards-es.coffee

--- coffeescripts/cards-es.coffee
+++ coffeescripts/cards-es.coffee
@@ -1014,9 +1014,17 @@
             ship: "Caza Kihraxz"
             text: '''After defending, if you did not roll exactly 2 defense dice, the attacker receives 1 stress token.'''
         'Lowhhrick':
-            text: '''When another friendly ship at Range 1 is defending, you may spend 1 reinforce token. If you do, the defender adds 1 %EVADE% result.'''
+            ship: "Cañonera Auzituck"
+            text: '''Cuando otr anave aliada que tengas a alcance 1 se esté defendiendo, puedes gastar 1 ficha de Refuerzo. Si lo haces, el defensor añade 1 resultado %EVADE%.'''
         'Wullffwarro':
-            text: '''When attacking, if you have no shields and at least 1 Damage card assigned to you, roll 1 additional attack die.'''
+            ship: "Cañonera Auzituck"
+            text: '''Cuando ataques, si no tienes ninguna ficha de Escudos y tienes asignada como mínimo 1 carta de Daño, tira 1 dado de ataque adicional.'''
+        'Wookiee Liberator':
+            name: "Libertador wookie"
+            ship: "Cañonera Auzituck"
+        'Kashyyyk Defender ':
+            name: "Defensor de KAshyyyk"
+            ship: "Cañonera Auzituck"
         'Captain Nym (Scum)':
             name: "Capitán Nym (Scum)"
             ship: "Bombardero Scurrg H-6"
@@ -1679,7 +1687,8 @@
             name: "Proyector ARC"
             text: '''Sólo Escoria y Rebelde.%DUALCARD%%LINEBREAK%Cara A:%LINEBREAK%Ataque: Ataca a 1 nave. Si este ataque impacta, debes elegir 1 otra nave a alcance 1 del defensor para que sufra 1 punto de daño.%LINEBREAK%Luego dale la vuelta a esta carta.%LINEBREAK%Cara B:%LINEBREAK%(Recargándose) Al comienzo de la fase de Combate, puedes recibir una ficha de Armas inutilizadas para darle la vuelta a esta carta.'''
         'Wookiee Commandos':
-            text: '''When attacking, you may reroll your %FOCUS% results.'''
+            name: "Comandos wookiees"
+            text: '''Cuando ataques, puedes volver a tirar tus resultados %FOCUS%.'''
         'Synced Turret':
             name: "Torreta sincronizada"
             text: '''Ataque (Blanco fijado: Ataca a 1 nave (aunque esté fuera de tu arco de fuego).%LINEBREAK%Si el defensor está dentro de tu arco de fuego principal, puedes volver a tirar tantos dados de ataque como tu valor de Armamento principal.'''
@@ -1696,19 +1705,24 @@
             name: "Droides matones IG-RM"
             text: '''Cuando ataques, puedes cambiar 1 de tus resultados %HIT% por un resultado %CRIT%.'''
         'Selflessness':
-            text: '''%SMALLSHIPONLY% %REBELONLY%%LINEBREAK%When a friendly ship at Range 1 is hit by an attack, you may discard this card to suffer all uncanceled %HIT% results instead of the target ship.'''
+            name: "Autosacrificio"
+            text: '''%SMALLSHIPONLY% %REBELONLY%%LINEBREAK%Cuando una nave aliada que tengas a alcance 1 sea impactada por un ataque, puedes descartar esta carta para sufrir todos los resultados %HIT% no anulados en vez de la nave objetivo.'''
         'Breach Specialist':
-            text: '''When you are dealt a faceup Damage card, you may spend 1 reinforce token to flip it facedown (without resolving its effect).  If you do, until the end of the round, when you are dealt a faceup Damage card, flip it facedown (without resolving its effect).'''
+            name: "Especialista en brechas"
+            text: '''Cuando recibas una carta de Daño boca arriba, puedes gastar 1 ficha de Refuerzo para darle la vuelta y ponerla boca abajo (sin resolver su efecto). Si lo haces, hasta el final de la ronda, cuando recibas una carta de Daño boca arriba, dale la vuelta par aponerla boca abajo (sin resolver su efecto).'''
         'Bomblet Generator':
-            text: '''When you reveal your maneuver, you may drop 1 Bomblet token.%LINEBREAK%This token detonates at the end of the Activation phase.%LINEBREAK%Bomblet Token: When this token detonates, each ship at Range 1 rolls 2 attack dice and suffers all damage (%HIT%) and critical damage (%CRIT%) rolled. Then discard this token.'''
+            name: "Generador de minibombas"
+            text: '''Cuando reveles tu maniobra, puedes soltar 1 ficha de Minibomba%LINEBREAK%Esta ficha se detona al final de la fase de Activación.%LINEBREAK%Ficha de Minibomba: Cuando esta ficha detone, cada nave a Alcance 1 tira 2 dados de ataque y sufre todo el daño (%HIT%) y daño crítico (%CRIT%) obtenido en la tirada. Después se descarta esta ficha.'''
         'Cad Bane':
-            text: '''%SCUMONLY%%LINEBREAK%Your upgrade bar gains the %BOMB% icon. Once per round, when an enemy ship rolls attack dice due to a friendly bomb detonating, you may choose any number of %FOCUS% and blank results.  It must reroll those results.'''
+            text: '''%SCUMONLY%%LINEBREAK%Tu barra de mejoras gana el icono %BOMB%. Una vez por ronda, cuando una nave enemiga tire dados de ataque debido a la detonación de una bomba aliada, puedes elegir cualquier cantidad de resultados %FOCUS% y de cara vacía. La nave enemiga debe volver a tirar esos resultados.'''
         'Minefield Mapper':
-            text: '''During Setup, after the "Place Forces" step, you may discard any number of your equipped %BOMB% Upgrade cards.  Place all corresponding bomb tokens in the play area beyond Range 3 of enemy ships.'''
+            name: "Trazador de campos de minas"
+            text: '''Durante la preparación de la partida, después del paso "Desplegar fuerzas", puedes descartar cualquier cantidad de tus cartas de Mejora %BOMB% equipadas. Coloca las correspondientes fichas de Bomba en la zona de juego más allá de alcance 3 de las naves enemigas.'''
         'R4-E1':
-            text: '''You can perform actions on your %TORPEDO% and %BOMB% Upgrade cards even if you are stressed. After you perform an action in this way, you may discard this card to remove 1 stress token from your ship.'''
+            text: '''Puedes realizar acciones que figuren en tus cartas de Mejora %TORPEDO% y %BOMB% incluso aunque estés bajo tensión. Después de que realices una acción de esta manera, puedes descartar esta carta para retirar 1 ficha de Tensión de tu nave.'''
         'Cruise Missiles':
-            text: '''Attack (Target Lock): Discard this card to perform this attack.%LINEBREAK%You may roll additional attack dice equal to the speed of the manuever you performed this round, to a maximum of 4 additional dice.'''
+            name: "Misiles de crucero"
+            text: '''Ataque (Blanco Fijado): Descarta esta carta para efectuar este ataque. %LINEBREAK%Puedes tirar tantos dados de ataque adicionales como la velocidad de la maniobra que has ejecutado en esta ronda, hasta un máximo de 4 dados adicionales.'''
         'Ion Dischargers':
             text: '''After you receive an ion token, you may choose an enemy ship at Range 1.  If you do, remove that ion token. Then that ship may choose to receive 1 ion token. If it does, discard this card.'''
         'Harpoon Missiles':
@@ -2000,7 +2014,9 @@
             ship: "Crucero C-ROC"
             text: '''Cuando te defiendas, si tienes asignada una ficha de Refuerzo, puedes añadir 1 resultado %EVADE% adicional.  Si lo haces, tras defenderte, descarta tu ficha de Refuerzo.'''
         'Havoc':
-            text: '''Scurrg H-6 Bomber only.%LINEBREAK%Your upgrade bar gains the %SYSTEM% and %SALVAGEDASTROMECH% icons and loses the %CREW% upgrade icon.%LINEBREAK%You cannot equip non-unique %SALVAGEDASTROMECH% Upgrade cards.'''
+            name: "Estrago"
+            ship: "Bombardero Scurrg H-6"
+            text: '''Sólo Bombarder Scurrg H-6.%LINEBREAK%Tu barra de mejoras gana los iconos %SYSTEM% y %SALVAGEDASTROMECH% y pierde el icono %CREW%.%LINEBREAK%No puedes equiparte con cartas de mejora %SALVAGEDASTROMECH% que no sean únicas.'''
         'Vaksai':
             ship: "Caza Kihraxz"
             text: '''Kihraxz Fighter only.%LINEBREAK%The squad point cost of each of your equipped upgrades is reduced by 1 (to a minimum of 0).%LINEBREAK%You may equip up to 3 different Modification upgrades.'''


Remove incorrect ship tags from Heavy Scyk.

fixes #305

--- coffeescripts/cards-pl.coffee
+++ coffeescripts/cards-pl.coffee
@@ -1142,15 +1142,12 @@
             text: """Tylko StarViper.%LINEBREAK%Twój pasek rozwinięć zyskuje symbole rozwinięć %SYSTEM% i %ILLICIT%. Nie możesz wyposażyć swojego statku w tę kartę jeśli wartość umiejętności twojego pilota wynosi „3” lub mniej."""
         '"Heavy Scyk" Interceptor (Cannon)':
             name: 'Interceptor typu Heavy Scyk (Działo)'
-            ship: "Interceptor M3-A"
             text: """Tylko Interceptor M3-A.%LINEBREAK%Twój pasek rozwinięć zyskuje jeden z symboli rozwinięć: %CANNON%, %TORPEDO% lub %MISSILE%."""
         '"Heavy Scyk" Interceptor (Torpedo)':
             name: 'Interceptor typu Heavy Scyk (Torpeda)'
-            ship: "Interceptor M3-A"
             text: """Tylko Interceptor M3-A.%LINEBREAK%Twój pasek rozwinięć zyskuje jeden z symboli rozwinięć: %CANNON%, %TORPEDO% lub %MISSILE%."""
         '"Heavy Scyk" Interceptor (Missile)':
             name: 'Intercepteur "Scyk Lourd" (Rakieta)'
-            ship: "Interceptor M3-A"
             text: """Tylko Interceptor M3-A.%LINEBREAK%Twój pasek rozwinięć zyskuje jeden z symboli rozwinięć: %CANNON%, %TORPEDO% lub %MISSILE%."""
         "IG-2000":
             text: """Tylko Aggressor.%LINEBREAK%Masz zdolność pilota każdego innego przyjaznego statku z kartą ulepszenia IG-2000 (jako dodatek do swojej zdolności pilota)."""


Correct number of Ruthlessness cards.

Correct number of Ruthlessness cards.

fixes #248

--- coffeescripts/manifest.coffee
+++ coffeescripts/manifest.coffee
@@ -1126,7 +1126,7 @@
         {
             name: 'Ruthlessness'
             type: 'upgrade'
-            count: 1
+            count: 2
         }
         {
             name: 'Dauntless'


Fixes to completion formatting

Fixes to completion formatting

--- app/assets/javascripts/components/training_modules.cjsx
+++ app/assets/javascripts/components/training_modules.cjsx
@@ -42,13 +42,11 @@
           linkText = 'Start'
           iconClassName += 'icon-rt_arrow'

-        progressClass += 'overdue' if module.overdue is true
-        progressClass += 'complete' if module.deadline_status is 'complete'
-
-        deadlineStatus = module.deadline_status
+        progressClass += ' overdue' if module.overdue is true
+        progressClass += ' complete' if module.deadline_status is 'complete'

         if module.deadline_status is 'overdue'
-          deadlineStatus = "(#{deadlineStatus}, due on #{module.due_date})"
+          deadlineStatus = "(#{module.deadline_status}, due on #{module.due_date})"

         (
           <tr className="training-module">


PivotChart: Add Ratio (sumOverSum) aggregator

--- app/assets/javascripts/notebook/magic/pivotChart.coffee
+++ app/assets/javascripts/notebook/magic/pivotChart.coffee
@@ -86,6 +86,12 @@
       pivotOptions.onRefresh = refresh
       pivotOptions.rendererOptions = rendererOptions

+      customAggregators = {
+        # allows calculating ratios over any dimensions (sum is additive, ratio is not)
+        "Ratio - sumOverSum": $.pivotUtilities.aggregatorTemplates.sumOverSum()
+      }
+      pivotOptions.aggregators = $.extend($.pivotUtilities.aggregators, customAggregators)
+
       p = $("<div>")
       p.addClass("pivotChart").appendTo(container)


Added logic to populate Product.options

Added logic to populate Product.options

--- app/assets/javascripts/sprangular/models/product.coffee
+++ app/assets/javascripts/sprangular/models/product.coffee
@@ -19,4 +19,18 @@
       @master = Sprangular.extend(@master, Sprangular.Variant)
       @variants = [@master]

-    _.each @variants, (variant) -> variant.product = this
+    self = @
+    @options = {}
+
+    _.each @option_types, (type) ->
+      self.options[type.id] = {type: type, values: {}}
+
+    _.each @variants, (variant) ->
+      variant.product = self
+
+      _.each variant.option_values, (value) ->
+        type = _.find(self.option_types, (type) -> type.id == value.option_type_id )
+        option = self.options[type.id]
+
+        option.values[value.id] = {value: value, variants: []} unless option.values[value.id]
+        option.values[value.id].variants.push(variant)


removing some legacy js for the cities/index

--- app/assets/javascripts/views/cities/legacy_stuff.coffee
+++ app/assets/javascripts/views/cities/legacy_stuff.coffee
@@ -163,18 +163,3 @@
           item = $(".right-menu ul li a.active").eq(value)
           item.removeClass('active')

-  Views.Cities.Index = Backbone.Marionette.CompositeView.extend
-    template: '#cities_index-template'
-    model: Models.Cities
-    itemView: Views.Cities.IndexItem
-
-    initialize: (item) ->
-      @model = item.model
-
-    #onRender: ->
-    #  $('.right-container .inner').ready ->
-    #    $('.right-container .inner').append( 'Aaa Eee Brr' )
-
-  Views.Cities.IndexItem = Backbone.Marionette.ItemView.extend
-    template: '#cities_index_item-template'
-    model: Models.City


Add scrollToFix for content bar in paper show page

[Fixes #69382112]

--- app/assets/javascripts/views/paper_index_view.js.coffee
+++ app/assets/javascripts/views/paper_index_view.js.coffee
@@ -6,3 +6,11 @@
   resetBackgroundColor:(->
     $('html').removeClass('matte')
   ).on('willDestroyElement')
+
+  setupScrollFixing: (->
+    $('.control-bar').scrollToFixed()
+    $('#tahi-container > main > aside > div').scrollToFixed
+      marginTop: $('.control-bar').outerHeight(true)
+      unfixed: ->
+        $(this).css('top', '0px')
+  ).on('didInsertElement')


Render error clarification

Render error clarification

--- moon-phase.widget/index.coffee
+++ moon-phase.widget/index.coffee
@@ -284,8 +284,11 @@

 renderError: (data) ->
   moonEl = @$domEl.find('.moon')
-  moonEl.find('.current').text "moon-phase: #{data.message}"
-  moonEl.append "
moon-phase: #{data.message}
"
+  moonEl.find('.current').text "#{data.message}"
+  if /geocode/i.test(data.message)
+    moonEl.append "
if waking from sleep, try a refresh
"
+  else
+    moonEl.append "
#{data.message}
"

 update: (output, domEl) ->


add a method to get songs' info

add a method to get songs' info

--- test/anison.coffee
+++ test/anison.coffee
@@ -23,15 +23,15 @@

 describe "_getSearchHtml", ->
   beforeEach (done) ->
+    @fixture = fs.readFileSync('test/fixture/search_result.html').toString()
+    nock('http://anison.info')
+      .get("/data/n.php?q=#{encodeURIComponent '未確認で進行形'}&m=pro")
+      .reply(200, @fixture)
+
     anison = new Anison("未確認で進行形")
     anison._getSearchHtml().then (res) =>
       @result = res
       done()

   it "should return html scraped by anison.info", ->
-    fixture = fs.readFileSync('test/fixture/search_result.html').toString()
-    nock('http://anison.info/')
-      .get("/data/n.php?q=#{encodeURIComponent '未確認で進行形'}&m=pro")
-      .reply(200, fixture)
-
-    expect(@result).to.equal fixture
+    expect(@result).to.equal @fixture


Follow mixin

Follow mixin

--- test/follow.coffee
+++ test/follow.coffee
@@ -30,6 +30,6 @@

   ok fastEnemy.I.velocity.equal(Point(0, -5)), 'fastEnemy should head toward player with velocity Point(0, 5)'

-  rightEnemuy
+  rightEnemy =

 module()


Follow mixin

Follow mixin

--- test/follow.coffee
+++ test/follow.coffee
@@ -17,6 +17,6 @@

   enemy.follow(player)

-  ok enemy.I.velocity.equals(Point(1, 0))
+  ok enemy.I.velocity.equal(Point(1, 0))

 module()


check the scopes

check the scopes

--- test/hinoki.coffee
+++ test/hinoki.coffee
@@ -52,6 +52,8 @@

             hinoki.inject container, (a) ->
                 test.equals a, 5
+                test.deepEqual container.scope,
+                    a: 5
                 test.done()

         'exception in factory': (test) ->
@@ -86,6 +88,10 @@
                 test.equals a, 1
                 test.equals b, 2
                 test.equals c, 4
+                test.deepEqual container.scope,
+                    a: 1
+                    b: 2
+                    c: 4
                 test.done()

         'one factory and two seeds': (test) ->
@@ -100,6 +106,10 @@
                 test.equals a, 1
                 test.equals b, 2
                 test.equals c, 4
+                test.deepEqual container.scope,
+                    a: 1
+                    b: 2
+                    c: 4
                 test.done()

         'circular dependency': (test) ->
@@ -113,6 +123,7 @@
                 hinoki.inject container, (a) ->
             catch error
                 test.equals error.message, "circular dependency a <- c <- a"
+                test.deepEqual container.scope, {}
                 test.done()

         'self dependency': (test) ->
@@ -124,6 +135,7 @@
                 hinoki.inject container, (a) ->
             catch error
                 test.equals error.message, "circular dependency a <- a"
+                test.deepEqual container.scope, {}
                 test.done()

         'long circular dependency': (test) ->
@@ -140,6 +152,7 @@
                 hinoki.inject container, (a) ->
             catch error
                 test.equals error.message, "circular dependency a <- b <- c <- d <- e <- f <- a"
+                test.deepEqual container.scope, {}
                 test.done()

         'one async dependency with success': (test) ->
@@ -153,20 +166,23 @@

             hinoki.inject container, (a) ->
                 test.equals a, 5
+                test.deepEqual container.scope,
+                    a: 5
                 test.done()

         'one async dependency with error': (test) ->
+            container =
+                factories:
+                    a: ->
+                        deferred = q.defer()
+                        q.nextTick ->
+                            deferred.reject 5
+                        return deferred.promise
+
             q.onerror = (err) ->
                 test.equals err.message, "error resolving promise returned from factory 'a'"
-                test.done()
-
-            container =
-                factories:
-                    a: ->
-                        deferred = q.defer()
-                        q.nextTick ->
-                            deferred.reject 5
-                        return deferred.promise
+                test.deepEqual container.scope, {}
+                test.done()

             hinoki.inject container, (a) ->

@@ -190,4 +206,8 @@
                 test.equals a, 1
                 test.equals b, 2
                 test.equals c, 4
-                test.done()
+                test.deepEqual container.scope,
+                    a: 1
+                    b: 2
+                    c: 4
+                test.done()


AWS: Changed one more config

AWS: Changed one more config

--- config/main.production.coffee
+++ config/main.production.coffee
@@ -91,7 +91,7 @@
     index       : "./website/index.html"
     includesFile: '../CakefileIncludes.coffee'
     useStaticFileServer: no
-    staticFilesBaseUrl: 'https://rc.koding.com'
+    staticFilesBaseUrl: 'https://koding.com'
     runtimeOptions:
       resourceName: socialQueueName
       suppressLogs: yes


fix callback

fix callback

--- lib/atomizr.coffee
+++ lib/atomizr.coffee
@@ -610,8 +610,12 @@
       parentDir = path.dirname inputFile
       baseName = path.basename inputFile, path.extname inputFile
       outputFile = path.join parentDir, baseName + ".#{extension}"
-      fs.rename inputFile, outputFile
-      editor.saveAs(outputFile)
+      fs.rename inputFile, outputFile, (err) ->
+        if err
+          throw err
+        editor.saveAs(outputFile)
+        return
+

   makeCoffee: (editor, input) ->
     output = CSON.createCSONString(input)


Added hint if a person description is missing.

--- fetsy-hammertag/scripts/states/scanSingleObject.coffee
+++ fetsy-hammertag/scripts/states/scanSingleObject.coffee
@@ -44,14 +44,20 @@
             else if type is 'singleObject'
                 @lastSupplies = null
                 if @lastPerson
-                    DatabaseFactory.saveObject(@scanInputValue, @lastPerson.id)
-                    .then(
-                        (response) =>
-                            @lastObject = response.data.object
-                            @resetInputField()
-                            return
-                        errorHandling
-                    )
+                    if @lastPerson.description?
+                        DatabaseFactory.saveObject(
+                            @scanInputValue
+                            @lastPerson.id
+                        ).then(
+                            (response) =>
+                                @lastObject = response.data.object
+                                @resetInputField()
+                                return
+                            errorHandling
+                        )
+                    else
+                        @scanInputValue = ''
+                        @error = 'Unknown person. Description is missing.'
                 else
                     DatabaseFactory.fetchObject(@scanInputValue)
                     .then(
@@ -64,16 +70,20 @@
             else if type is 'supplies'
                 @lastObject = null
                 if @lastPerson
-                    DatabaseFactory.saveSupplies(
-                        @scanInputValue
-                        @lastPerson.id
-                    ).then(
-                        (response) =>
-                            @lastSupplies = response.data.supplies
-                            @resetInputField()
-                            return
-                        errorHandling
-                    )
+                    if @lastPerson.description?
+                        DatabaseFactory.saveSupplies(
+                            @scanInputValue
+                            @lastPerson.id
+                        ).then(
+                            (response) =>
+                                @lastSupplies = response.data.supplies
+                                @resetInputField()
+                                return
+                            errorHandling
+                        )
+                    else
+                        @scanInputValue = ''
+                        @error = 'Unknown person. Description is missing.'
                 else
                     DatabaseFactory.fetchSupplies(@scanInputValue)
                     .then(


Matrix copy constructor

Matrix copy constructor

--- test/matrix.coffee
+++ test/matrix.coffee
@@ -15,7 +15,7 @@
     equalEnough(m1.tx, m2.tx, TOLERANCE)
     equalEnough(m1.ty, m2.ty, TOLERANCE)

-  test ""
+  test "copy construct"

   test "Matrix() (Identity)", ->
     matrix = Matrix()


Dismiss Failure Notification when "Show build log" Is Pressed

--- lib/builder.coffee
+++ lib/builder.coffee
@@ -96,7 +96,9 @@
           onDidClick: => @latex.logger.clearBuildError()
         }, {
           text: "Show build log"
-          onDidClick: => @latex.logger.showLog()
+          onDidClick: ()=>
+            @latex.logger.clearBuildError()
+            @latex.logger.showLog()
         }]
       )
       @latex.logger.log.push({


(#158) Fix filenames escaping with whitespace on Unix

--- lib/builder.coffee
+++ lib/builder.coffee
@@ -55,7 +55,8 @@
       type: 'status',
       text: """Step #{@buildLogs.length}> #{cmd}"""
     })
-    toolchain = cmd.split(' ') # Split into array of cmd + arguments
+    # Split into array of cmd + arguments (un-escaping "" again)
+    toolchain = cmd.match(/(?:[^\s"]+|"[^"]*")+/g).map((arg) -> arg.replace(/"/g,''))
     @currentProcess = cp.spawn(toolchain.shift(), toolchain, {cwd:path.dirname @latex.mainFile})

     # Register callbacks for the spawnprocess


Simplify ratesForPeriod() output

Simplify ratesForPeriod() output

--- src/services/currency-rates/currency-rates.svc.coffee
+++ src/services/currency-rates/currency-rates.svc.coffee
@@ -35,7 +35,7 @@
                 rates.push rate

             $log.info("Impac! - CurrencyRatesSvc: ratesForPeriod params #{params}")
-            return {organizations: params.metadata.organization_ids, currencyRates: rates }
+            return rates

           (err) ->
             $log.error('Impac! - CurrencyRatesSvc: Cannot retrieve current rates :', err)
@@ -55,13 +55,12 @@
       return url

   #  @ratesForPeriod= (hist_parameters) ->
-    #      org = ["org-fbbj"]
     #      currencyRates = []
     #      currencyRates.push({base_currency: 'USD', transaction_currency: 'AUD', date: moment.parse('2017-02-20'), rate: 1.30423})
     #      currencyRates.push({base_currency: 'USD', transaction_currency: 'AUD', date: moment.parse('2017-02-17'), rate: 1.29898})
     #      currencyRates.push({base_currency: 'USD', transaction_currency: 'AUD', date: moment.parse('2017-02-12'), rate: 1.30216})
     #      currencyRates.push({base_currency: 'USD', transaction_currency: 'AUD', date: moment.parse('2017-02-10'), rate: 1.31136})
-    #      return {organizations: org, currencyRates: currencyRates}
+    #      return currencyRates

     return @
   )


Stripe V3 doesnt have a name, so breaks AB tests

--- js/ab_test_definitions.coffee
+++ js/ab_test_definitions.coffee
@@ -26,4 +26,5 @@
         window.circleEnvironment is 'test'
       options:
         github_warning_modal: false
+        stripe_v3: false
     ]


{ clearCache } and { fallback } are preflight-relevant keys

--- lib/assets/javascripts/unpoly/classes/render_options.coffee
+++ lib/assets/javascripts/unpoly/classes/render_options.coffee
@@ -25,6 +25,8 @@
     'headers',
     'params',
     'cache',
+    'clearCache',
+    'fallback',  # this may produce a different X-Up-Target request header if { target } is missing on page
     'solo',
     'confirm',
     'feedback',
@@ -57,7 +59,6 @@
     'history',      # Note that regardless of setting, we only set history for reloadable responses (GET).
     'source',       # No strong opinions about that one. Wouldn't one always have a source? Might as well not be an option.
     'saveScroll',   # No strong opinions about that one. Wouldn't one always want to saveScroll? Might as wellnot be an option.
-    'fallback',     # If I always want to see the server response, I also want to see it for the fail case
     'navigate'      # Also set navigate defaults for fail options
   ])


Fix broken link /opening-overlays

Fix broken link /opening-overlays

--- lib/assets/javascripts/unpoly/pages/opening-overlays.coffee
+++ lib/assets/javascripts/unpoly/pages/opening-overlays.coffee
@@ -68,4 +68,6 @@
   ```

   Choose a different layer mode by passing a `{ mode }` option.
+
+  @page opening-overlays
   ###


Basic tabular support

Basic tabular support

--- lib/formats.coffee
+++ lib/formats.coffee
@@ -237,6 +237,17 @@
   .replace /\\end\s*{(quote)}/g, '
'
   .replace /\\begin\s*{(proof|pf)}(\s*\[([^\]]*)\])?/g, (m, env, x, opt) -> "Proof#{if opt then " (#{opt})" else ''}: "
   .replace /\\end\s*{(proof|pf)}/g, ' ∎

'
+  .replace /\\begin\s*{tabular}\s*{([^{}]*)}([^]*)\\end\s*{tabular}/g, (m, cols, body) ->
+    '
' +
+      (for row in body.split /\\\\(?:\s*\\(?:hline|cline\s*{[^{}]*}))?/
+         continue if row[...2] == '\\\\'
+         "
\n" +
+         (for col in row.split '&'
+            "#{col}	\n"
+         ).join('') +
+         "
\n"
+      ).join('') +
+    '
'
   .replace /\\(dots|ldots|textellipsis)\b\s*/g, '…'
   .replace /\\textasciitilde\b\s*/g, '∼'  ## Avoid ~ -> \nbsp
   .replace /\\textasciicircum\b\s*/g, '^'


Support triply nested macros

Support triply nested macros


Glob Asynchronousely

Glob Asynchronousely

- Fixes #96

--- lib/gobuild.coffee
+++ lib/gobuild.coffee
@@ -79,7 +79,7 @@
       console.log @name + ' - stdout: ' + stdout if stdout? and stdout.trim() isnt ''
       messages = @mapMessages(stderr, cwd, splitgopath) if stderr? and stderr isnt ''
       pattern = cwd + '/*' + output
-      glob pattern, {mark: false, sync:true}, (er, files) ->
+      glob pattern, {mark: false}, (er, files) ->
         for file in files
           do (file) ->
             fs.unlinkSync(file)


:bug: Do not subtract one, if it's 0

:bug: Do not subtract one, if it's 0

--- lib/helpers.coffee
+++ lib/helpers.coffee
@@ -60,9 +60,14 @@
     throw new Error('Invalid lineNumber provided') if typeof lineNumber is 'undefined'
     unless typeof colStart is 'number'
       colStart = (textEditor.indentationForBufferRow(lineNumber) * textEditor.getTabLength())
+      if colStart isnt 0
+        colStart -= 1
+    colEnd = textEditor.getBuffer().lineLengthForRow(lineNumber)
+    if colEnd isnt 0
+      colEnd -= 1
     return [
-      [lineNumber, colStart - 1],
-      [lineNumber, textEditor.getBuffer().lineLengthForRow(lineNumber) - 1]
+      [lineNumber, colStart],
+      [lineNumber, colEnd]
     ]

   # Due to what we are attempting to do, the only viable solution right now is


documented loglevels

documented loglevels

--- lib/logging.coffee
+++ lib/logging.coffee
@@ -1,10 +1,19 @@
 winston = require 'winston'
+
+#  debug: 0
+#  info: 1
+#  notice: 2
+#  warning: 3
+#  error: 4
+#  crit: 5
+#  alert: 6
+#  emerg: 7

 log = new winston.Logger
   levels: winston.config.syslog.levels
   transports: [
     new winston.transports.Console
-      level: "debug"
+      level: "notice"
       colorize: on
     new winston.transports.File
        level: "error"


Fix the down motion to stop on last line

--- lib/motions.coffee
+++ lib/motions.coffee
@@ -28,7 +28,8 @@

 class MoveDown extends Motion
   execute: ->
-    @editor.moveCursorDown()
+    {column, row} = @editor.getCursorScreenPosition()
+    @editor.moveCursorDown() if row < (@editor.getBuffer().getLineCount() - 1)

 class MoveToPreviousWord extends Motion
   execute: ->


remove debug code

remove debug code

--- lib/pagerv2.coffee
+++ lib/pagerv2.coffee
@@ -315,7 +315,6 @@
           pages = Math.ceil(data.total / 100)
           Promise.each [1..pages], (offset) =>
             query['offset'] =  offset * 100
-            console.log query
             @request('GET', '/incidents', query)
             .then (page) ->
               data.incidents = data.incidents.concat(page.incidents)


Remove sessions when password recovery is completed

--- workers/social/lib/social/models/passwordrecovery.coffee
+++ workers/social/lib/social/models/passwordrecovery.coffee
@@ -235,6 +235,11 @@
           queue.next()

       ->
+        # kill user sessions
+        user.killSessions {}, (err) ->
+          queue.next()
+
+      ->
         # invalidating other active tokens
         JPasswordRecovery.invalidate { username }, (err) ->
           return callback err, username


Fix bug with overlapping timestamp declarations

--- lib/session.coffee
+++ lib/session.coffee
@@ -1163,8 +1163,8 @@
       if currentPath.wildcard == 0
         if attrs = allDeclareAttributeTimestamps.get(path)
           if declareAttributeTimestamps
-            delcareAttributeTimestamps = Object.assign({}, declareAttributeTimestamps)
-            declareAttributeTimestamps[k] = Math.max(v, attrs[k] or 0) for k, v of attrs
+            declareAttributeTimestamps = Object.assign({}, declareAttributeTimestamps)
+            declareAttributeTimestamps[k] = Math.max(v, declareAttributeTimestamps[k] or 0) for k, v of attrs
           else
             declareAttributeTimestamps = attrs


fix bugs

fix bugs

--- lib/steedos.coffee
+++ lib/steedos.coffee
@@ -3,6 +3,21 @@
 logger = require 'winston'

 uuid = require './uuid'
+
+generateResponse = (subscriber, cb) ->
+    subscriber.getSubscriptions (subs) ->
+        if subs?
+            result = {}
+            result.pushToken = subscriber.id
+            result.pushTokenTTL = 6000
+            result.registeredTopics = []
+
+            for sub in subs
+                result.registeredTopics.push(sub.event.name)
+
+            result.webCourierURL = "http://pushws.steedos.com:2001/webcourier"
+            if (cb)
+                cb(result)

 exports.setup  = (app, createSubscriber, getEventFromId, authorize, testSubscriber, eventPublisher, getSubscriberFromId) ->
@@ -65,8 +80,8 @@
                             events[eventName] = {}
                         subscriber.addSubscriptions(events)

-                    res.header 'Location', "/getToken/#{subscriber.id}"
-                    res.json {}, if created then 201 else 200
+                    generateResponse subscriber, (result)->
+                        res.json result, 200

         catch error
             logger.error "Creating token failed: #{error.message}"
@@ -92,8 +107,8 @@
                             events[eventName] = {}
                         subscriber.addSubscriptions(events)

-                    res.header 'Location', "/getToken/#{subscriber.id}"
-                    res.json {}, if created then 201 else 200
+                    generateResponse subscriber, (result)->
+                        res.json result, 200

         catch error
             logger.error "Creating token failed: #{error.message}"
@@ -119,8 +134,8 @@
                             events[eventName] = {}
                         subscriber.addSubscriptions(events)

-                    res.header 'Location', "/getToken/#{subscriber.id}"
-                    res.json {}, if created then 201 else 200
+                    generateResponse subscriber, (result)->
+                        res.json result, 200

         catch error
             logger.error "Creating token failed: #{error.message}"
@@ -145,8 +160,8 @@
                     events[eventName] = {}
                 req.subscriber.addSubscriptions(events)

-            res.header 'Location', "/getToken/#{req.subscriber.id}"
-            res.json {}, 201
+            generateResponse req.subscriber, (result)->
+                res.json result, 200

         catch error
             logger.error "registerTopics failed: #{error.message}"
@@ -170,9 +185,9 @@
                         eventName = eventName + "|" + req.param("steedosId").replace("@", "_").replace(".", "_")
                     events[eventName] = {}
                 req.subscriber.removeSubscriptions(events)
-
-            res.header 'Location', "/getToken/#{req.subscriber.id}"
-            res.json {}, 201
+
+            generateResponse req.subscriber, (result)->
+                res.json result, 200

         catch error
             logger.error "registerTopics failed: #{error.message}"


fix(retina-img): Escape mask url, so it works with spaces

--- src/components/retina-img.cjsx
+++ src/components/retina-img.cjsx
@@ -57,7 +57,7 @@
     style.zoom = if pathIsRetina then 0.5 else 1

     if @props.colorfill
-      style.WebkitMaskImage = "url(#{path})"
+      style.WebkitMaskImage = "url('#{path}')"
       style.objectPosition = "10000px"
       className = "colorfill"


Add spec for disabling spell check of a grammar

--- src/packages/spell-check/spec/spell-check-spec.coffee
+++ src/packages/spell-check/spec/spell-check-spec.coffee
@@ -45,3 +45,17 @@
       editor.insertText('a')
       advanceClock(editor.getBuffer().stoppedChangingDelay)
       expect(editor.find('.misspelling')).toBeHidden()
+
+  describe "when spell checking for a grammar is removed", ->
+    it "removes all current decorations", ->
+      editor.setText('notaword')
+      advanceClock(editor.getBuffer().stoppedChangingDelay)
+      config.set('spell-check.grammars', ['source.js'])
+
+      waitsFor ->
+        editor.find('.misspelling').length > 0
+
+      runs ->
+        expect(editor.find('.misspelling').length).toBe 1
+        config.set('spell-check.grammars', [])
+        expect(editor.find('.misspelling').length).toBe 0


Client: Activity: Feed: Added makeLike method to render like count and check count

--- client/activity/lib/components/comments/commentlistitem.coffee
+++ client/activity/lib/components/comments/commentlistitem.coffee
@@ -51,11 +51,16 @@
           messageId={comment.get('id')}
           interactions={comment.get('interactions').toJS()}>
           Like
-
-            {comment.getIn ['interactions', 'like', 'actorsCount']}
-
+          {makeLike comment.getIn ['interactions', 'like', 'actorsCount']}
         </ActivityLikeLink>
         <Link onClick={ @bound 'onClick' }>Mention</Link>

+
+makeLike = (count) ->
+
+  return null  unless count
+
+  {count}
+


Workspace: Emitting an event when chat pane closed.

--- client/landing/app/CommonViews/workspace/panes/chatpane.coffee
+++ client/landing/app/CommonViews/workspace/panes/chatpane.coffee
@@ -19,6 +19,7 @@
         @toggle.toggleClass "active"
         @unreadCount = 0
         @title.updatePartial "Chat"
+        @emit "WorkspaceChatClosed"  unless @isVisible()

     @title       = new KDCustomHTMLView
       tagName    : "span"


landing: start using utils.isLoggedIn

landing: start using utils.isLoggedIn

--- client/landing/site.landing/coffee/core/oauthcontroller.coffee
+++ client/landing/site.landing/coffee/core/oauthcontroller.coffee
@@ -55,7 +55,7 @@
     {mainController} = kd.singletons
     mainController.once "ForeignAuthCompleted", (provider)=>

-      isUserLoggedIn = kd.isLoggedIn()
+      isUserLoggedIn = utils.isLoggedIn()

       params = {isUserLoggedIn, provider}


client: team-creation: animated input labels removed

--- client/landing/site.landing/coffee/login/logininputview.coffee
+++ client/landing/site.landing/coffee/login/logininputview.coffee
@@ -33,32 +33,12 @@
     @errors       = {}
     @errorMessage = ''

-    # @input.on 'keyup',                     @bound 'inputReceivedKeyup'
-    @input.on 'focus',                     @bound 'inputReceivedFocus'
-    @input.on 'blur',                      @bound 'inputReceivedBlur'
     @input.on 'ValidationError',           @bound 'decorateValidation'
     @input.on 'ValidationPassed',          @bound 'decorateValidation'
     @input.on 'ValidationFeedbackCleared', @bound 'decorateValidation'

   setFocus: -> @input.setFocus()
-
-  inputReceivedKeyup: ->
-
-    if   @input.getValue().length > 0
-    then @placeholder.setClass 'out'
-    else @placeholder.unsetClass 'out'
-
-
-  inputReceivedFocus: -> @placeholder.setClass 'out'
-
-  inputReceivedBlur: ->
-
-    if @input.getValue().length > 0
-      @placeholder.setClass 'puff'
-    else
-      @placeholder.unsetClass 'puff'
-      @placeholder.unsetClass 'out'

   resetDecoration: -> @unsetClass 'validation-error validation-passed'


dashboard: Topic link is added

dashboard: Topic link is added

--- client/dashboard/lib/index.coffee
+++ client/dashboard/lib/index.coffee
@@ -54,9 +54,14 @@
       ,
         name         : 'Membership policy'
         viewOptions  :
+          viewClass  : GroupTopicDashboardView
+          lazy       : yes
+          callback   : @bound 'policyViewAdded'
+      ,
+        name         : 'Topic Lists'
+        viewOptions  :
           viewClass  : GroupsMembershipPolicyDetailView
           lazy       : yes
-          callback   : @bound 'policyViewAdded'
       # ,
       #   name         : 'Payment'
       #   viewOptions  :


Adjust logo property on change

Adjust logo property on change

Since the onchange does slightly wrong thing

--- client/skr/models/Location.coffee
+++ client/skr/models/Location.coffee
@@ -21,9 +21,14 @@
         address:  { model: "Address" }
         sku_locs: { collection: "SkuLoc" }

+
     @initialize: (data) ->
         SHARED_DATA = data.locations

+    addChangeSet: (change) ->
+        if change.update.logo
+            change.update.logo[1] = change.update.logo[1].logo
+        super

 Object.defineProperties Skr.Models.Location, {
     all:


Slice before reversing the array

Slice before reversing the array

--- src/chaplin/lib/router.coffee
+++ src/chaplin/lib/router.coffee
@@ -57,7 +57,7 @@
       # Remove leading hash or slash
       path = path.replace /^(\/#|\/)/, ''

-      for handler in Backbone.history.handlers.reverse()
+      for handler in Backbone.history.handlers.slice().reverse()
         if handler.route.test(path)
           handler.callback path, changeURL: true
           return true


using the validation version of the method

--- server/methods/saveUserProfile.coffee
+++ server/methods/saveUserProfile.coffee
@@ -8,7 +8,7 @@
 				Accounts.setPassword Meteor.userId(), settings.password, { logout: false }

 			if settings.username?
-                                Meteor.users.update Meteor.userId(), { $set: { "username": settings.username } }
+				Meteor.call 'setUsername', settings.username

 			profile = {}


remove msg.reply

remove msg.reply

--- src/hubot-graphite-send.coffee
+++ src/hubot-graphite-send.coffee
@@ -37,10 +37,10 @@
   required_args = [
     "HUBOT_GRAPHITE_SEND_HOST"
   ]
-
+  name = "hubot-graphite-send"
   for arg in required_args
     if !nconf.get(arg)
-      buf = "#hubot-graphite-send is not properly configured. #{arg} is not set."
+      buf = "#{name} is not properly configured. #{arg} is not set."
       msg.reply buf
       return false

@@ -78,7 +78,7 @@

 deployment_event = (msg, deployment) ->
   text = msg.message.text
-  msg.reply "#{deployment}: #{text}"
+  console.log "#{deployment}: #{text}"
   client = get_graphite_client()
   metric = get_graphite_metric text, deployment
   value = get_graphite_metric_value()
@@ -86,7 +86,7 @@
     if err
       console.log "Error adding metric #{err}"
     else
-      msg.reply "added metric #{metric}"
+      console.log "added metric #{metric}"
   )

 module.exports = (robot) ->


add unique id for every area

add unique id for every area

--- src/jquery-select-areas.coffee
+++ src/jquery-select-areas.coffee
@@ -11,7 +11,9 @@
 }

 class Area
+  @amount: 0
   constructor: (@parent, size, @draggable = true) ->
+    @id = Area.amount++
     @area = $('<div>').addClass('area').css(size)
     @parent.append(@area)
     if draggable
@@ -60,6 +62,9 @@
       .addClass('area-draggable')
       .append(dragger)
     return
+
+  getID: ->
+    return @id

   dimensions: ->
     return getBoxCSS(@area)


removed a lot of old code

removed a lot of old code

--- src/coffee/bokeh_views.coffee
+++ src/coffee/bokeh_views.coffee
@@ -35,9 +35,7 @@
   tagName : 'div'
   className:"gridplot_container"

-  default_options : {
-    scale:1.0
-  }
+  default_options : { scale:1.0}
   initialize : (options) ->
     super(_.defaults(options, @default_options))
     @childviews = {}
@@ -61,51 +59,6 @@
     for row, ridx in @mget('children')
       for plotspec, cidx in row
         @childviews[plotspec.id].render_deferred_components(force)
-
-  render_old  : ->
-    super()
-    trans_string = "scale(#{@options.scale}, #{@options.scale})"
-    trans_string += "translate(#{@mget('border_space')}, #{@mget('border_space')})"
-    @d3plot.attr('transform', trans_string)
-    d3el = d3.select(@el)
-    d3el.attr('width', @options.scale * @mget('outerwidth'))
-      .attr('height', @options.scale * @mget('outerheight'))
-      .attr('x', @model.position_x())
-      .attr('y', @model.position_y())
-    row_heights =  @model.layout_heights()
-    col_widths =  @model.layout_widths()
-    y_coords = [0]
-    _.reduceRight(row_heights[1..]
-      ,
-        (x, y) ->
-          val = x + y
-          y_coords.push(val)
-          return val
-      , 0
-    )
-    y_coords.reverse()
-    x_coords = [0]
-    _.reduce(col_widths[..-1]
-      ,
-        (x,y) ->
-          val = x + y
-          x_coords.push(val)
-          return val
-      , 0
-    )
-
-    for row, ridx in @mget('children')
-      for plotspec, cidx in row
-        plot = @model.resolve_ref(plotspec)
-        plot.set(
-          offset : [x_coords[cidx], y_coords[ridx]]
-          usedialog : false
-        )
-
-    for own key, view of @childviews
-      tojq(@d3plot).append(view.$el)
-    @render_end()
-

   render : ->
     super()
@@ -162,28 +115,17 @@
 class PlotView extends Continuum.DeferredView
   default_options : {scale:1.0}

+  model_specs : ->
+   {plot_id : @id, plot_model : @model, plot_view : @}
+
   build_renderers : ->
-    build_views(@model, @renderers, @mget('renderers'),
-      {
-        plot_id : @id,
-        plot_model : @model
-        plot_view : @},
-      @options)
+    build_views(@model, @renderers, @mget('renderers'), @model_specs(), @options)

   build_axes : ->
-    build_views(@model, @axes, @mget('axes'),
-      {
-        plot_id : @id
-        plot_model : @model
-        plot_view : @},
-      @options)
-
+    build_views(@model, @axes, @mget('axes'), @model_specs(), @options)

   build_tools : ->
-    build_views(@model, @tools, @mget('tools'),
-        plot_id : @id,
-        plot_model : @model
-        plot_view : @)
+    build_views(@model, @tools, @mget('tools'), @model_specs())

   build_overlays : ->
     #add ids of renderer views into the overlay spec
@@ -195,10 +137,7 @@
       overlayspec['options']['rendererviews'] = []
       for renderer in overlay.get('renderers')
         overlayspec['options']['rendererviews'].push(@renderers[renderer.id])
-    build_views(@model, @overlays, overlays,
-        plot_id : @id,
-        plot_model : @model
-        plot_view : @)
+    build_views(@model, @overlays, overlays, @model_specs())

   bind_overlays : ->
     for overlayspec in @mget('overlays')
@@ -306,25 +245,18 @@
       @$el.append(view.$el)
     for own key, view of @renderers
       @$el.append(view.$el)
-
-    @render_end()
-  render_mainsvg : ->
-    if true
-      return
+    @render_end()

   render_deferred_components: (force) ->
     super(force)
     all_views = _.flatten(_.map([@tools, @axes, @renderers, @overlays], _.values))
-
     if _.any(all_views, (v) -> v._dirty)
       @ctx.clearRect(0,0,  @mget('width'), @mget('height'))
       for v in all_views
         v._dirty = true
         v.render_deferred_components(true)

-
 build_views = Continuum.build_views
-
 class XYRendererView extends PlotWidget
   initialize : (options) ->
     safebind(this, @model, 'change', @request_render)
@@ -339,7 +271,7 @@
     pv = @plot_view
     pvo = @plot_view.options
     own_options = @options
-    #debugger;
+
     xmapper = @model.get_ref('xmapper')
     ymapper = @model.get_ref('ymapper')
     xfield = @model.get('xfield')
@@ -411,9 +343,7 @@

   render_x : ->
     xmapper = @mget_ref('mapper')
-
     can_ctx = @plot_view.x_can_ctx
-
     data_range = xmapper.get_ref('data_range')
     interval = ticks.auto_interval(
       data_range.get('start'), data_range.get('end'))
@@ -488,31 +418,6 @@

     can_ctx.stroke()
     @plot_view.ctx.stroke()
-    @render_end()
-
-  render_old : ->
-    super()
-
-    window.axisview = @
-    node = d3.select(@el)
-    node
-      .attr('style', '  font: 12px sans-serif; fill:none; stroke-width:1.5px; shape-rendering:crispEdges')
-      .attr('stroke', @mget('foreground_color'))
-    offsets = @get_offsets(@mget('orientation'))
-    offsets['h'] = @plot_model.get('height')
-    node.attr('transform', "translate(#{offsets.x}, #{offsets.y})")
-
-    axis = d3.svg.axis()
-    ticksize = @get_tick_size(@mget('orientation'))
-    scale_converted = @convert_scale(@mget_ref('mapper').get('scale'))
-    temp = axis.scale(scale_converted)
-    temp.orient(@mget('orientation'))
-      .ticks(@mget('ticks'))
-      .tickSubdivide(@mget('tickSubdivide'))
-      .tickSize(ticksize)
-      .tickPadding(@mget('tickPadding'))
-    node.call(axis)
-    node.selectAll('.tick').attr('stroke', @mget('tick_color'))
     @render_end()

@@ -636,8 +541,6 @@
     a = new Date()
     @calc_buffer(data)
     @plot_view.ctx.beginPath()
-    #if navigator.userAgent.indexOf("WebKit") != -1
-      #@ctx.scale(0.5, 0.5)
     if navigator.userAgent.indexOf("WebKit") != -1
       @plot_view.ctx.scale(@options.scale, @options.scale)

@@ -659,16 +562,12 @@
         @addPolygon(@screenx[idx], @screeny[idx])
       else
         @addCircle(@screenx[idx], @screeny[idx])
-
     @plot_view.ctx.stroke()
     @render_end()
     b = new Date()
     render_time = b-a
     $('#timer').html( "render time #{render_time}")
     return null
-
-
-

 class OverlayView extends PlotWidget
   initialize : (options) ->
@@ -739,53 +638,7 @@
     @plot_view.ctx.stroke()
     @render_end()
     return null
-
-
 window.overlay_render = 0
-class ZoomToolView extends PlotWidget
-  initialize : (options) ->
-    super(options)
-
-  mouse_coords : () ->
-    plot = @plot_view.d3plotwindow
-    [x, y] = d3.mouse(plot[0][0])
-    [x, y] = [@plot_model.rxpos(x), @plot_model.rypos(y)]
-    return [x, y]
-
-  _zoom_mapper : (mapper, eventpos, factor) ->
-    screen_range = mapper.get_ref('screen_range')
-    data_range = mapper.get_ref('data_range')
-    screenlow = screen_range.get('start')
-    screenhigh = screen_range.get('end')
-    start = screenlow - (eventpos - screenlow) * factor
-    end = screenhigh + (screenhigh - eventpos) * factor
-    [start, end] = [mapper.map_data(start), mapper.map_data(end)]
-    data_range.set({
-      'start' : start
-      'end' : end
-    }, {'local' : true})
-
-  _zoom : () ->
-    [x, y] = @mouse_coords()
-    factor = - @mget('speed') * d3.event.wheelDelta
-    xmappers = (@model.resolve_ref(mapper) for mapper in @mget('xmappers'))
-    ymappers = (@model.resolve_ref(mapper) for mapper in @mget('ymappers'))
-    for xmap in xmappers
-      @_zoom_mapper(xmap, x, factor)
-    for ymap in ymappers
-      @_zoom_mapper(ymap, y, factor)
-
-  bind_events : (plotview) ->
-    @plotview = plotview
-    node = d3.select(@plot_view.el)
-    node.attr('pointer-events' , 'all')
-    node.on("mousewheel.zoom"
-      ,
-        () =>
-          @_zoom()
-          d3.event.preventDefault()
-          d3.event.stopPropagation()
-    )

 Bokeh.PlotWidget = PlotWidget
 Bokeh.PlotView = PlotView
@@ -793,9 +646,6 @@
 Bokeh.LineRendererView = LineRendererView
 Bokeh.BarRendererView = BarRendererView
 Bokeh.GridPlotContainerView = GridPlotContainerView
-#Bokeh.PanToolView = PanToolView
-
-#Bokeh.SelectionToolView = SelectionToolView
 Bokeh.ScatterSelectionOverlayView = ScatterSelectionOverlayView
 Bokeh.D3LinearAxisView = D3LinearAxisView
 Bokeh.D3LinearDateAxisView = D3LinearDateAxisView


Add Maria & Juampe (MJ) to console

Add Maria & Juampe (MJ) to console


Add test cases that show that DateTime2 parameters are failing.

--- test/integration/parameterised-statements-test.coffee
+++ test/integration/parameterised-statements-test.coffee
@@ -187,6 +187,12 @@

 exports.dateTimeNull = (test) ->
   execSql(test, TYPES.DateTime, null)
+
+exports.dateTime2 = (test) ->
+  execSql(test, TYPES.DateTime2, new Date('December 4, 2011 10:04:23'))
+
+exports.dateTime2Null = (test) ->
+  execSql(test, TYPES.DateTime2, null)

 exports.outputBitTrue = (test) ->
   execSqlOutput(test, TYPES.Bit, true)


Update en.coffee

Update en.coffee

Updates to willett, cardamone, skibba and wong details.

--- app/lib/en.coffee
+++ app/lib/en.coffee
@@ -1149,10 +1149,9 @@
       schawinski: 'Kevin Schawinski - Co-Founder: Astronomer, ETH Zurich
Works on the formation and evolution of galaxies and their supermassive black holes. Usually trying to get more sleep. And coffee.'
       edmondson: 'Edd Edmondson - Astronomer, University of Portsmouth
Zookeeper, aiding in accessing Sloan data, and general helper-outer.'
       keel: '''Bill Keel - Astronomer, University of Alabama
Author of 'The Road to Galaxy Formation' and 'The Sky at Einstein's Feet'. With research interests in galaxy formation and evolution, dust content of galaxies, and active galactic nuclei, he couldn't stay away when Galaxy Zoo got started.'''
-      skibba: 'Ramin Skibba - Astronomer, UC San Diego
Leading the use of marked correlation functions to analyse the Galaxy Zoo morphologies. Keenly interested in science communication and science policy.'
-      cardamone: '''Carie Cardamone - Astronomer, Wheelock College
An educator and astronomer, studying the Zoo's green peas, interested in understanding why galaxies grow they way they do. She's very enthusiastic about engaging the broader public in scientific research and the nature of science.'''
-      willett: 'Kyle Willett - Astronomer, University of Minnesota
  Studies the roles of both morphology and environment on galaxies and their active black holes. He is leading the data reduction and catalog creation from Galaxy Zoo 2 classifications, and is also working on building new tools to enable independent research by citizen science volunteers. In his spare time, he runs probably more than is healthy.'
-      casteels: 'Kevin Casteels - Astronomer, University of Victoria
  Interested in galaxy evolution and morphologies, specifically how they are affected by mergers and interactions.  Kevin is currently working with the makers of the Starry Night astronomy software, helping to develop new astronomy apps and keep the systems running!  In his spare time he is also a SCUBA instructor and loves the great outdoors.'
+      skibba: 'Ramin Skibba - Science Writer, Former Astronomer, San Diego
Ramin lead the use of marked clustering statistics and dark matter halo models to analyse the environmental dependence of Galaxy Zoo morphologies. Always keenly interested in science communication and science policy, he's shifted from research astronomy to science writing.'
+      cardamone: '''Carie Cardamone - Astronomer, Wheelock College
An educator and astronomer, studying the Zoo's green peas, interested in understanding why galaxies grow they way they do. She's very enthusiastic about engaging the broader public in scientific research and the nature of science.'''
+      willett: 'Kyle Willett - Lead Data Scientist: Astronomer, University of Minnesota
  Studies the roles of both morphology and environment on galaxies and their active black holes. He lead the data reduction and catalog creation from Galaxy Zoo 2 classifications, has worked on building new tools to enable independent research by citizen science volunteers and now leads efforts to include new data sets in Galaxy Zoo. In his spare time, he runs probably more than is healthy.'      casteels: 'Kevin Casteels - Astronomer, University of Victoria
  Interested in galaxy evolution and morphologies, specifically how they are affected by mergers and interactions.  Kevin is currently working with the makers of the Starry Night astronomy software, helping to develop new astronomy apps and keep the systems running!  In his spare time he is also a SCUBA instructor and loves the great outdoors.'
       urry: 'Meg Urry - Physicist, Yale University
Works on supermassive black holes (growth history, unification, relativistic jets, blazars) and their interplay with galaxies. In her spare time, she writes for CNN.com and works to increase the participation of women and minorities in science.'
       nichol: '''Bob Nichol - Cosmologist, University of Portsmouth
An astronomer, astrophysicist and cosmologist with interests in computer science and statistics. A senior member (or 'Builder') of the Sloan Digital Sky Survey, the most ambitious digital survey of the sky ever undertaken. A long suffering supporter of Manchester City, suffering no more.'''
       fortson: '''Lucy Fortson - Astrophysicist, University of Minnesota
Interested in galaxy evolution, black holes and the jets of material beaming from the centers of active galactic nuclei. Started the Zooniverse effort at the Adler Planetarium, now bringing the light to the University of Minnesota. On the odd weekend, when she's not preparing lecture or writing grants, Lucy can be found hanging out with her husband and son at one of Minneapolis' fine dining establishments.'''
@@ -1164,8 +1163,8 @@
       haussler: 'Boris Häußler - Astronomer, University of Oxford/Hertfordshire
An expert on analysing Hubble Space Telescope images and automatic morphology methods.'
       kaviraj: 'Sugata Kaviraj - Astronomer, University of Hertfordshire and University of Oxford
Works on the formation and evolution of galaxies.'
       raddick: 'Jordan Raddick - Science Education and Outreach Coordinator, Johns Hopkins University
A science writer and educator, creator of the science projects on the SDSS SkyServer web site, and an avid science fiction reader and writer. Jordan wishes he could visit galaxies in their natural habitat.'
-      wong: 'Ivy Wong - Astronomer, ICRAR/Perth
 A Super Science Fellow in Australia, Ivy studies why galaxies start and stop forming stars, how they grow supermassive black holes in their centre, and why they look like they do today, especially their radio properties.'
-
+      wong: 'Ivy Wong - Astronomer, ICRAR/Perth
 A Super Science Fellow in Australia, Ivy studies why galaxies start and stop forming stars, how they grow supermassive black holes in their centre, and why they look like they do today, especially their radio properties. Project Scientist for Radio Galaxy Zoo'
+
     dev:
       title: 'Zooniverse Development Team'
       carstensen: 'Brian Carstensen
Brian is a web developer working on the Zooniverse family of projects at the Adler Planearium. Brian has a degree in graphic design from Columbia College in Chicago, and worked in that field for a number of years before finding a niche in web development.'


IDEView: move new session to bottom, add terminate all menu item.

--- client/ide/views/tabview/ideview.coffee
+++ client/ide/views/tabview/ideview.coffee
@@ -271,10 +271,10 @@

     appManager.tell 'IDE', 'setActiveTabView', @tabView
     appManager.tell 'IDE', 'setFindAndReplaceViewDelegate'
-
-
+
+
   openSavedFile: (file, content) ->
-
+
       pane = @tabView.getActivePane()
       if pane.data instanceof FSFile and pane.data.path is @getDummyFilePath()
         @tabView.removePane @tabView.getActivePane()
@@ -347,26 +347,31 @@

     sessions = machine?.getBaseKite().terminalSessions or []

-    terminalSessions =
-      "New Session"  :
-        callback     : => @createTerminal { machine }
-        separator    : sessions.length > 0
-
-    activeSessions = []
+    terminalSessions = {}
+    activeSessions   = []
+
     frontApp.forEachSubViewInIDEViews_ 'terminal', (pane) =>
-      if pane.remote?
-        activeSessions.push pane.remote.session
+      activeSessions.push pane.remote.session  if pane.remote?

     sessions.forEach (session, i) =>
       isActive = session in activeSessions
       terminalSessions["Session (#{session[0..5]}) &nbsp"] =
         disabled          : isActive
+        separator         : sessions.length is i
         children          :
           'Open'          :
             disabled      : isActive
             callback      : => @createTerminal { machine, session }
           'Terminate'     :
             callback      : => @terminateSession machine, session
+
+    terminalSessions["New Session"] =
+      callback            : => @createTerminal { machine }
+      separator           : sessions.length > 0
+
+    if sessions.length > 0
+      terminalSessions["Terminate all"] =
+        callback          : => @terminateSessions machine

     items =
       'New File'          : callback : => @createEditor()
@@ -423,7 +428,19 @@
     contextMenu.once 'ContextMenuItemReceivedClick', -> contextMenu.destroy()

-
+  terminateSessions: (machine)->
+
+    machine.getBaseKite().webtermKillSessions()
+
+    .catch (err)->
+      # This `webterm.killSessions` is not deployed yet
+      # when I was writing these lines, so I've added
+      # a feedback for user. ~ GG
+      if err.type is "methodNotFound"
+        new KDNotificationView
+          title : "Coming soon"
+      else
+        warn "Failed to terminate sessions", err

   terminateSession: (machine, session)->