Generate documentation site css on gulp dist

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -55,7 +55,7 @@
   concatenated

 gulp.task 'less-min', ->
-  return gulp.src './src/less/cyclops.less'
+  return gulp.src [ './src/less/cyclops.less', './src/less/site/site.less' ]
     .pipe sourcemaps.init()
     .pipe less()
     .pipe autoprefixer


task for phonegap

task for phonegap

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -42,6 +42,7 @@

 gulp.task 'copy', ['copy-dependencies', 'copy-resources-content', 'copy-others']
 gulp.task 'copy-dependencies', ->
+  # TODO link instead of copy
   gulp
     .src paths.dependencies
     .pipe gulp.dest 'dist/bower_components'
@@ -123,6 +124,22 @@
     .pipe $.replace 'href=\"elements/elements.html\"', 'href=\"elements/elements.vulcanized.html\"'
     .pipe gulp.dest 'dist/'

+gulp.task 'clean-phonegap', (cb) ->
+  del(
+    [ '../aozora-phonegap-build/*', '!../aozora-phonegap-build/.gitignore', '!../aozora-phonegap-build/README.md', '!../aozora-phonegap-build/LICENSE' ],
+    {
+      force: true
+    },
+    cb)
+
+gulp.task 'copy-phonegap', ->
+  gulp
+    .src [
+      'dist/**/*',
+      'config.xml'
+    ]
+    .pipe gulp.dest '../aozora-phonegap-build'
+
 gulp.task 'build', (cb) ->
   runSequence(
     'clean',
@@ -133,4 +150,7 @@
     cb
   )

+gulp.task 'build-phonegap', (cb) ->
+  runSequence [ 'build', 'clean-phonegap' ], 'copy-phonegap'
+
 gulp.task 'default', [ 'build' ]


chore: fix builded files is not up-to-date (temporary)

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -109,7 +109,7 @@
     entries: './jThree/lib/jThree.js'
     name: 'j3.js'
     extensions: ['.js', '.json', '.glsl', '.html','.xmml']
-    dest: ['./jThree/bin/product', './jThree/wwwroot']
+    dest: ['./jThree/wwwroot', './jThree/bin/product']
     target: 'web'
     minify: false
     transform: ['shaderify', 'txtify']


Gulpfile: Copy each output (kd, libs and css) also under the playground directory

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -33,8 +33,10 @@

   stream = gulp.src(STYLES_PATH)
     .pipe(stylus())
-    .pipe(concat "kd.#{version}css")
+    .pipe(concat "kd.css")
     .pipe(gulpif useMinify, minifyCSS())
+    .pipe(gulp.dest "playground/css")
+    .pipe(rename "kd.#{version}css")
     .pipe(gulp.dest "#{buildDir}/css")

   stream.pipe(livereload())  if useLiveReload
@@ -45,7 +47,9 @@
   stream = gulp.src(LIBS)
     # .pipe(gulpif useUglify, uglify())
     .pipe(uglify())
-    .pipe(concat "kd.libs.#{version}js")
+    .pipe(concat "kd.libs.js")
+    .pipe(gulp.dest "playground/js")
+    .pipe(rename "kd.libs.#{version}js")
     .pipe(gulp.dest "#{buildDir}/js")

   stream.pipe(livereload())  if useLiveReload
@@ -64,6 +68,8 @@
         extensions  : ['.coffee']
         debug       : yes )
       .pipe(gulpif useUglify, uglify())
+      .pipe(concat "kd.js")
+      .pipe(gulp.dest "playground/js")
       .pipe(rename "kd.#{version}js")
       .pipe(gulp.dest "#{buildDir}/js")


Modify gulp

Modify gulp

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -72,13 +72,13 @@
   dir = path.join(path.tempdir(), "poi-v#{POI_VERSION}-#{PLATFORM}-#{SYSTEM_BIT[PLATFORM]}")
   fs.ensureDirSync dir
   try
-    yield fs.accessAsync path.join(path.tempdir(), "electron-v#{ELECTRON_VERSION}-#{PLATFORM}.zip")
+    yield fs.accessAsync path.join(path.homedir(), "electron-v#{ELECTRON_VERSION}-#{PLATFORM}.zip")
   catch e
     log "Downloding Electron #{PLATFORM} #{ELECTRON_VERSION}"
     [response, body] = yield requestAsync
       url: url
       encoding: null
-    yield fs.writeFileAsync path.join(path.tempdir(), "electron-v#{ELECTRON_VERSION}-#{PLATFORM}.zip"), body
+    yield fs.writeFileAsync path.join(path.homedir(), "electron-v#{ELECTRON_VERSION}-#{PLATFORM}.zip"), body
   log "Extract Electron #{ELECTRON_VERSION}"
   zip = new AdmZip path.join(path.tempdir(), "electron-v#{ELECTRON_VERSION}-#{PLATFORM}.zip")
   zip.extractAllTo dir, true
@@ -105,10 +105,10 @@
     linux: 'http://7xj6zx.com1.z0.glb.clouddn.com/poi/PepperFlash/linux.zip'
     darwin: 'http://7xj6zx.com1.z0.glb.clouddn.com/poi/PepperFlash/darwin.zip'
   url = plugins[PLATFORM]
-  dir = path.join(path.tempdir(), "poi-v#{POI_VERSION}-#{PLATFORM}-#{SYSTEM_BIT[PLATFORM]}", 'PepperFlash')
+  dir = path.join(path.homedir(), "poi-v#{POI_VERSION}-#{PLATFORM}-#{SYSTEM_BIT[PLATFORM]}", 'PepperFlash')
   fs.ensureDirSync dir
   try
-    yield fs.accessAsync path.join(path.tempdir(), "flashplayer-#{PLATFORM}.zip"), fs.R_OK
+    yield fs.accessAsync path.join(path.homedir(), "flashplayer-#{PLATFORM}.zip"), fs.R_OK
   catch e
     log "Downloading flash plugin #{PLATFORM}"
     [response, body] = yield requestAsync


adding callback

adding callback

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -157,16 +157,18 @@
   if opts.dev
     wintersmith.settings.configFile = 'config.json'
     wintersmith.build ->
-      gutil.log "Successfully built wintersmith for local dev."
+      gutil.log "Successfully built wintersmith for --> local dev."
       cb()
   if opts.staging
     wintersmith.settings.configFile = 'config_staging.json'
     wintersmith.build ->
-      gutil.log "Successfully built wintersmith for staging."
+      gutil.log "Successfully built wintersmith for --> staging."
+      cb()
   if opts.production
     wintersmith.settings.configFile = 'config_production.json'
     wintersmith.build ->
-      gutil.log "Successfully built wintersmith for **production**."
+      gutil.log "Successfully built wintersmith for --> production."
+      cb()

 gulp.task "css", ->
   return css()


Use isnt.

Use isnt.

--- src/coffee/flight.coffee
+++ src/coffee/flight.coffee
@@ -228,7 +228,7 @@
     @moved = false
     @theTarget = e.target

-    if @theTarget.nodeName and @theTarget.nodeName.toLowerCase() is not 'a'
+    if @theTarget.nodeName and @theTarget.nodeName.toLowerCase() isnt 'a'
       @theTarget = @theTarget.parentNode

     @theTarget.className+= ' pressed'


test(service): use command instead of which

--- packages/service/test/index.config.startup.coffee
+++ packages/service/test/index.config.startup.coffee
@@ -66,7 +66,7 @@
       nikita
         ssh: ssh
       , ->
-        {status} = await @execute 'which chkconfig', code_skipped: 1
+        {status} = await @execute 'command -v chkconfig', code_skipped: 1
         return unless status
         @service.remove
           name: service.name


A couple of preset bug fixes.

A couple of preset bug fixes.

--- presets.coffee
+++ presets.coffee
@@ -136,7 +136,7 @@
           devicePreset.customCommands[filename] ?= {}
           devicePreset.customCommands[filename].preset ?= {}
           devicePreset.customCommands[filename].preset.value = c.value
-          devicePreset.customCommands[filename].preset.valueCommand = c.valueCommand
+          devicePreset.customCommands[filename].preset.valueCommand = c.command
           continue if not deviceCustomCommands[filename]? or commandName not in deviceCustomCommands[filename]
           if cmd = common.getParamValueFromPath(device, "_customCommands.#{filename}")
             devicePreset.customCommands[filename].current = cmd
@@ -149,7 +149,7 @@
           devicePreset.customCommands[filename] ?= {}
           devicePreset.customCommands[filename].preset ?= {}
           devicePreset.customCommands[filename].preset.expiry = parseInt(c.expiry)
-          devicePreset.customCommands[filename].preset.expiryCommand = c.expiryCommand
+          devicePreset.customCommands[filename].preset.expiryCommand = c.command
           continue if not deviceCustomCommands[filename]? or commandName not in deviceCustomCommands[filename]
           if cmd = common.getParamValueFromPath(device, "_customCommands.#{filename}")
             devicePreset.customCommands[filename].current = cmd
@@ -158,7 +158,7 @@

         when 'software_version'
           devicePreset.softwareVersion ?= {}
-          devicePreset.softwareVersion.preset = c.softwareVersion
+          devicePreset.softwareVersion.preset = c.software_version
           devicePreset.softwareVersion.current = common.getParamValueFromPath(device, 'InternetGatewayDevice.DeviceInfo.SoftwareVersion')

         else
@@ -209,7 +209,7 @@
                 setParameterValues.push(["#{parameterPath}.#{i}.#{paramName}", presetValue, paramDetails.type])
         else
           vals = []
-          for k,v of allObjects[objectName]
+          for k,v of objectDetails.preset
             vals.push([k, v]) if k[0] != '_'
           taskList.push({device : deviceId, name : 'addObject', objectName : parameterPath, parameterValues : vals, instanceName : objectName})
       else if Object.keys(objectDetails.current) > 0


More stable streaming test (first chunk can be smaller than 2049)

--- tests/html/src/domtests.coffee
+++ tests/html/src/domtests.coffee
@@ -100,16 +100,14 @@

 ajax_streaming_factory = (name) ->
     asyncTest name + ' streaming', ->
-        expect(3)
+        expect(4)
         x = new u[name]('GET', '/streaming.txt', null)
-        chunkno = 0
         x.onchunk = (status, text) ->
-            switch chunkno
-                when 0
-                    equal(text.length, 2049)
-                    equal(text.slice(-2), 'a\n')
-            chunkno += 1
+            equal(status, 200)
+            ok(text.length <= 2049)
+            delete x.onchunk
         x.onfinish = (status, text) ->
+            equal(status, 200)
             equal(text.slice(-4), 'a\nb\n')
             start()


Clean up duplicate language manager

Clean up duplicate language manager

--- project.coffee
+++ project.coffee
@@ -7,8 +7,10 @@

 languageManager = new zooniverse.LanguageManager({
   translations: {
-    en: { label: "English", strings: enUs }
-    es: { label: "Español", strings: "./translations/es.json" }
+    en: label: "English", strings: enUs
+    es: label: "Español", strings: "./translations/es.json"
+    ru: label: "русский", strings: './translations/ru.json'
+    cs: label: 'Čeština', strings: './translations/cs.json'
   }
 })

@@ -18,17 +20,6 @@
 )

 translate = t7e
-
-LangugeManager = require 'zooniverse/lib/language-manager'
-languageManager = new LangugeManager
-  translations:
-    en: label: 'English', strings: enUs
-    ru: label: "русский", strings: './translations/ru.json'
-    cs: label: 'Čeština', strings: './translations/cs.json'
-
-languageManager.on 'change-language', (e, code, strings) ->
-  t7e.load strings
-  t7e.refresh()

 # Let's make translating keys easier.
 buildObject = (fn) ->


Fix Backbone localstorage dependency.

Fix Backbone localstorage dependency.

--- web/js/main.coffee
+++ web/js/main.coffee
@@ -5,6 +5,8 @@
       deps: ['underscore', 'jquery']
       exports: 'Backbone'
     backboneAnalytics:
+      deps: ['backbone']
+    backboneLocalStorage:
       deps: ['backbone']
     underscore:
       exports: '_'


removed returning array

removed returning array

--- workers/social/lib/social/models/kites.coffee
+++ workers/social/lib/social/models/kites.coffee
@@ -104,7 +104,7 @@
     options or= {}

     Relationship.some selector, options, (err, relationships)=>
-      if err then callback err, []
+      if err then callback err
       else if relationships.length is 0 then callback null, []
       else
         teasers = []


bug fix

bug fix

--- fav.coffee
+++ fav.coffee
@@ -8,8 +8,7 @@
   "http://localhost:3000"

 require("zappa") port, ->
-  @use
-    'static'
+  @use 'static',
     basicAuth:((u, p) -> u is 'hanachin' and p is 'zxcvbnm')
     'bodyParser'
     'cookieParser'


code review feedback

code review feedback

--- lib/cli.coffee
+++ lib/cli.coffee
@@ -608,6 +608,7 @@
   when 'clone'

     id_mapping = {}
+    objects_cloned = []
     src_slug = args[0]
     dst_slug = args[1]

@@ -667,40 +668,35 @@
                 data.site_id = dst_site.get('_id').val()
                 db.get('objects').insert data, (err, new_data) ->
                   id_mapping[old_id] = new_data.val()._id
+                  objects_cloned.push new_data
                   exit err if err
                   next()
               ), (err) ->

                 # clean up references
-                db.get('objects').find {
-                  site_id: dst_site.get('_id').val()
-                }, {
-                  limit: 100000
-                }, (err, objects) ->
-
-                  async.forEach objects, ((object, next) ->
-                    object_val = object.val()
-                    str_json = JSON.stringify(object_val, null, 3)
-                    replaced = false
-
-                    # replace mapping
-                    for key, value of id_mapping
-                      if str_json.indexOf(key) > -1
-                        og_key = new RegExp(key, 'g');
-                        replaced = true
-                        str_json = str_json.replace(og_key, value)
-
-                    if replaced
-                      new_json = JSON.parse(str_json)
-
-                      # set new data with updated mapping
-                      object.get('data').set new_json.data, (err) ->
-                        next()
-                    else
+                async.map objects_cloned ((object, next) =>
+                  object_val = object.val()
+                  str_json = JSON.stringify(object_val, null, 3)
+                  replaced = false
+
+                  # replace mapping
+                  for key, value of id_mapping
+                    if str_json.indexOf(key) > -1
+                      og_key = new RegExp(key, 'g');
+                      replaced = true
+                      str_json = str_json.replace(og_key, value)
+
+                  if replaced
+                    new_json = JSON.parse(str_json)
+
+                    # set new data with updated mapping
+                    object.get('data').set new_json.data, (err) ->
                       next()
-                  ), (err) ->
-                    exit err if err
-                    exit 'clone complete may need to wait 60 seconds for cache to clear'
+                  else
+                    next()
+                ), (err) ->
+                  exit err if err
+                  exit 'clone complete may need to wait 60 seconds for cache to clear'

   # create a new package


added missing colon in the '--showdown' option defintion

--- lib/cli.coffee
+++ lib/cli.coffee
@@ -101,7 +101,7 @@
       alias:    's'
       default:  'Default'

-    showdown
+    showdown:
       describe: "Showdown extension(s) to load, repeat option to add more extensions (--no-showdown and the option itself discards default)."
       alias:    'sd'
       default:  ['github','table']


performance fix

performance fix

--- app/assets/javascripts/table_filterable.coffee
+++ app/assets/javascripts/table_filterable.coffee
@@ -16,6 +16,7 @@
     columns = @table.querySelectorAll("thead th, thead td")
     tableBody = @table.querySelector('tbody')

+    tableBody.setAttribute("hidden", "hidden")
     index = 0
     filterColumnIndexes = []
     columns.forEach (column) ->
@@ -37,6 +38,7 @@
         row.removeAttribute("hidden")
       else
         row.setAttribute("hidden", "hidden")
+    tableBody.removeAttribute("hidden")

   @changeListener = (e) ->
     if (e.target.dataset.filters)


Added highlighting for user's desired weight change for #166


Deleted old javascript for autocomplete text field javascript

--- app/assets/javascripts/users/promote.js.coffee
+++ app/assets/javascripts/users/promote.js.coffee
@@ -1,14 +1,6 @@
 $(document).ready ->
-  $('.promote-table').on 'change', '#select-all', ->
-    if $(this).is(':checked')
-      $('.promote-select').prop 'checked', true
-    else
-      $('.promote-select').prop 'checked', false
-    return
-  $('.promote-table').on 'change', '.promote-select', ->
-    if $('.promote-select:checked').length == 0
-      $('#select-all').prop 'checked', false
-    else if $('.promote-select:checked').length == $('.promote-table').find('tbody > tr').length
-      $('#select-all').prop 'checked', true
+  $ ->
+    users = $("#promote-search").data("users")
+    $("#promote-search").autocomplete source: users
     return
   return


Add `destroyOnHide` option to modals

Add `destroyOnHide` option to modals

--- app/assets/javascripts/exo/views/modal.js.coffee
+++ app/assets/javascripts/exo/views/modal.js.coffee
@@ -10,6 +10,7 @@
   visible: false
   scrollable: true
   zIndex: 1024
+  destroyOnHide: false

   headerSelector:  "> .modal-header"
   sidebarSelect:   "> .modal-sidebar"
@@ -100,6 +101,8 @@
     @$el.hide()
     @getOverlay().hide()
     @trigger "hide"
+    @destroy() if @destroyOnHide
+    this

   toggle: ->
     if @visible


add padding to map

add padding to map

--- app/assets/javascripts/protected_areas.js.coffee
+++ app/assets/javascripts/protected_areas.js.coffee
@@ -1,8 +1,5 @@
 $(document).ready ->
-  map = L.map('map', {
-    center: [51.505, -0.09]
-    zoom: 14
-  })
+  map = L.map('map')

   L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png').addTo(map)
   L.tileLayer('http://carbon-tool.cartodb.com/tiles/wdpa_poly_feb2014_0/{z}/{x}/{y}.png').addTo(map)
@@ -10,4 +7,7 @@
   window.fitToBounds = (bounds) ->
     mapSize = map.getSize()
     paddingLeft = mapSize.x/5
-    map.fitBounds(bounds, {paddingTopLeft: [paddingLeft, 0]})
+    map.fitBounds(bounds, {
+      paddingTopLeft: [paddingLeft, 50],
+      paddingBottomRight: [0, 50]
+    })


Add a scope-forwarding test, refs #429

Add a scope-forwarding test, refs #429

--- spec/Scoping.coffee
+++ spec/Scoping.coffee
@@ -138,3 +138,38 @@
       ins.post new noflo.IP 'data', 'foo'
       ins.post new noflo.IP 'closeBracket', 'a'
       ins.post new noflo.IP 'closeBracket', 1
+    it 'should forward scopes as expected', (done) ->
+      expected = [
+        'x < 1'
+        'x < a'
+        'x DATA barWpPc'
+        'x >'
+        'x >'
+      ]
+      received = []
+      brackets = []
+
+      out.on 'ip', (ip) ->
+        switch ip.type
+          when 'openBracket'
+            received.push "#{ip.scope} < #{ip.data}"
+            brackets.push ip.data
+          when 'data'
+            received.push "#{ip.scope} DATA #{ip.data}"
+          when 'closeBracket'
+            received.push "#{ip.scope} >"
+            brackets.pop()
+            return if brackets.length
+            chai.expect(received).to.eql expected
+            done()
+
+      ins.post new noflo.IP 'openBracket', 1,
+        scope: 'x'
+      ins.post new noflo.IP 'openBracket', 'a',
+        scope: 'x'
+      ins.post new noflo.IP 'data', 'foo',
+        scope: 'x'
+      ins.post new noflo.IP 'closeBracket', 'a',
+        scope: 'x'
+      ins.post new noflo.IP 'closeBracket', 1,
+        scope: 'x'


Client: Activity: Deleted unnecessary lines and updated updateMessage method to delete message when input value empty and pressed enter

--- client/activity/lib/components/chatlistitem/index.coffee
+++ client/activity/lib/components/chatlistitem/index.coffee
@@ -250,6 +250,8 @@

     return @deletePostButtonHandler()  if @state.isDeleting

+    return @setState isDeleting: yes  unless @state.editInputValue
+
     name  = @props.channelName
     value = @state.editInputValue
     messageId = @props.message.get '_id'
@@ -286,20 +288,11 @@

   onEditInputKeyDown: (event) ->

-    { ESC, ENTER, BACKSPACE } = KeyboardKeys
+    { ESC, ENTER } = KeyboardKeys

     switch event.which
       when ESC         then @cancelEdit()
       when ENTER       then @updateMessage()
-      when BACKSPACE   then @onEditInputBackspace event
-
-
-  onEditInputBackspace: (event) ->
-
-    return yes  if @state.editInputValue
-
-    kd.utils.stopDOMEvent event
-    @setState isDeleting: yes

   getEditModeClassNames: -> classnames


reactivity: using ScrollableDropbox in EmojiDropbox

--- client/activity/lib/components/emojidropbox/index.coffee
+++ client/activity/lib/components/emojidropbox/index.coffee
@@ -1,14 +1,14 @@
-$                      = require 'jquery'
-kd                     = require 'kd'
-React                  = require 'kd-react'
-immutable              = require 'immutable'
-classnames             = require 'classnames'
-Dropbox                = require 'activity/components/dropbox/portaldropbox'
-EmojiDropboxItem       = require 'activity/components/emojidropboxitem'
-ImmutableRenderMixin   = require 'react-immutable-render-mixin'
-ScrollableDropboxMixin = require 'activity/components/dropbox/scrollabledropboxmixin'
+$                    = require 'jquery'
+kd                   = require 'kd'
+React                = require 'kd-react'
+immutable            = require 'immutable'
+classnames           = require 'classnames'
+Dropbox              = require 'activity/components/dropbox/portaldropbox'
+EmojiDropboxItem     = require 'activity/components/emojidropboxitem'
+ImmutableRenderMixin = require 'react-immutable-render-mixin'
+ScrollableDropbox    = require 'activity/components/dropbox/scrollabledropbox'

-module.exports = class EmojiDropbox extends React.Component
+class EmojiDropbox extends React.Component

   @propTypes =
     query           : React.PropTypes.string
@@ -75,5 +75,7 @@
     </Dropbox>

-EmojiDropbox.include [ ImmutableRenderMixin, ScrollableDropboxMixin ]
+EmojiDropbox.include [ ImmutableRenderMixin ]

+module.exports = ScrollableDropbox EmojiDropbox
+


dont show invite unless new group

dont show invite unless new group

--- client/angular/components/group/modal/group_modal.coffee
+++ client/angular/components/group/modal/group_modal.coffee
@@ -10,7 +10,7 @@

     applySequence $scope,
       steps: ->
-        if $scope.group.isNew() or $scope.group.parentId
+        if $scope.group.isNew()
           ['create', 'announce']
         else
           ['create']


Ace: Reopened saved as file to rebind its events.

--- client/app/Applications/Ace.kdapplication/AppView.coffee
+++ client/app/Applications/Ace.kdapplication/AppView.coffee
@@ -133,7 +133,6 @@
             name   = @inputFileName.getValue()

             if name is '' or /^([a-zA-Z]:\\)?[^\x00-\x1F"<>\|:\*\?/]+$/.test(name) is false
-              # @_message 'Wrong file name', "Please type valid file name"
               @ace.notify "Please type valid file name!", "error"
               return

@@ -145,6 +144,17 @@
             file.emit "file.requests.saveAs", @ace.getContents(), name, parent.path
             saveDialog.hide()
             @ace.emit "AceDidSaveAs", name, parent.path
+            oldCursorPosition = @ace.editor.getCursorPosition()
+            file.on "fs.saveAs.finished", =>
+              {tabView} = @getDelegate()
+              @getDelegate().openFile FSHelper.createFileFromPath "#{parent.path}/#{name}", yes
+              @utils.defer =>
+                newIndex = tabView.panes.indexOf tabView.getActivePane()
+                tabView.removePane tabView.getPaneByIndex newIndex - 1
+                {ace} = tabView.getActivePane().getOptions().aceView
+                ace.on "ace.ready", =>
+                  ace.editor.moveCursorTo oldCursorPosition.row, oldCursorPosition.column
+
         Cancel      :
           style     : "modal-cancel"
           callback  : =>


SidebarMachineBox: Added removeWorkspace method.

--- client/app/lib/activity/sidebar/sidebarmachinebox.coffee
+++ client/app/lib/activity/sidebar/sidebarmachinebox.coffee
@@ -63,6 +63,15 @@

     { workspaces } = @getData()
     workspaces.push wsData
+
+
+  removeWorkspace: (wsId) ->
+
+    @listController.removeItem @workspaceListItemsById[wsId]
+    @handleWorkspaceDeleted wsId
+
+    if @getData().workspaces.length is 0
+      @destroy()

   createWorkspacesLabel: ->


update for async clone

update for async clone

--- lib/lt3.coffee
+++ lib/lt3.coffee
@@ -862,17 +862,18 @@
           #get objects of old site
           db.get('objects').find {site_id:old_site_id} , (err, objects) =>
             throw err if err
-
-            for obj in objects
-
+            async.forEach objects , (obj, next) =>
               #wipe data on old objects
               data=obj.val()
               delete data._id
               data.site_id=new_site_id
-
               #insert them as new objects with site
-              db.get('objects').insert data , (err , obj) ->
+              db.get('objects').insert data , (err , obj) =>
                 throw err if err
+                next()
+            ,(err) =>
+              log "objects injected"
+              rl.prompt();

   when 'add:page'


More robust hOCR CSS rewriting

More robust hOCR CSS rewriting

--- js/hocr-reader.coffee
+++ js/hocr-reader.coffee
@@ -102,7 +102,10 @@
             console.log "hOCR fetch error: #{textStatus}"
           success: (data) ->
             hocr_html = atob(decodeURIComponent(escape(data.content.replace(/\s/g, ""))))
-            css_rewrite = hocr_html.replace('http://heml.mta.ca/Rigaudon/hocr.css','{{ site.url }}/hocr.css')
+            css_rewrite = if hocr_html.match('<link')
+              hocr_html.replace('http://heml.mta.ca/Rigaudon/hocr.css','{{ site.url }}/hocr.css')
+            else
+              hocr_html.replace('<head>',"<head>\n<link rel='stylesheet' type='text/css' href='{{ site.url }}/hocr.css'>")
             $('#page_right').append($('<iframe>').attr('style','width:100%').attr('height','800').attr('frameBorder','0').attr('src','data:text/html;charset=utf-8;base64,'+btoa(css_rewrite)))

 hocr_reader = (req) ->


Better nav logic

Better nav logic

--- js/hocr-reader.coffee
+++ js/hocr-reader.coffee
@@ -44,10 +44,14 @@

 make_nav_bar = (user, repo, page, total_pages) ->
   nav_bar = $('<div>').attr('id','nav_bar')
-  nav_bar.append($('<a>',class:'nav_left').attr('href',"/hocr-reader/#/read/#{user}/#{repo}/1").text('First'))
-  nav_bar.append($('<a>',class:'nav_left').attr('href',"/hocr-reader/#/read/#{user}/#{repo}/#{format_page(page - 1)}").text('Prev'))
-  nav_bar.append($('<a>',class:'nav_right').attr('href',"/hocr-reader/#/read/#{user}/#{repo}/#{format_page(total_pages)}").text('Last'))
-  nav_bar.append($('<a>',class:'nav_right').attr('href',"/hocr-reader/#/read/#{user}/#{repo}/#{format_page(page + 1)}").text('Next'))
+  console.log "page #{page}"
+  console.log "total_pages #{total_pages}"
+  if page > 1
+    nav_bar.append($('<a>',class:'nav_left').attr('href',"/hocr-reader/#/read/#{user}/#{repo}/1").text('First'))
+    nav_bar.append($('<a>',class:'nav_left').attr('href',"/hocr-reader/#/read/#{user}/#{repo}/#{format_page(page - 1)}").text('Prev'))
+  if page < total_pages
+    nav_bar.append($('<a>',class:'nav_right').attr('href',"/hocr-reader/#/read/#{user}/#{repo}/#{format_page(total_pages)}").text('Last'))
+    nav_bar.append($('<a>',class:'nav_right').attr('href',"/hocr-reader/#/read/#{user}/#{repo}/#{format_page(page + 1)}").text('Next'))
   $('.header').append(nav_bar)

 hocr_handler = (req) ->
@@ -67,8 +71,8 @@
     page = format_page(req.params['page'])
     if book
       repo.getTree book.sha, (err, book_tree) ->
-        all_pages = _.filter(book_tree, (node) -> node.path.match(new RegExp("^p\d+\.html$")))
-        make_nav_bar(req.params['github_user'],req.params['github_repo'],parseInt(page),all_pages.size)
+        all_pages = _.filter(book_tree, (node) -> node.path.match(new RegExp("^p[0-9]+\.html$")))
+        make_nav_bar(req.params['github_user'],req.params['github_repo'],parseInt(page),all_pages.length)
         page_image = _.filter(book_tree, (node) -> node.path.match(new RegExp("^i#{page}\.jpg$")))[0]
         page_hocr = _.filter(book_tree, (node) -> node.path.match(new RegExp("^p#{page}\.html$")))[0]
         console.log page_image


rtmp: Update SPS/PPS when they arrive

rtmp: Update SPS/PPS when they arrive

--- rtmp.coffee
+++ rtmp.coffee
@@ -32,9 +32,6 @@

 # The latest timestamp of video or audio data
 lastTimestamp = null
-
-# Buffer for retaining codec config NAL units
-codecConfigPackets = []

 # NAL unit type 7: Sequence parameter set
 spsPacket = null
@@ -59,15 +56,7 @@
   return clientID

 retainCodecConfigPacket = (buf) ->
-  if codecConfigPackets.length >= 2
-    console.error "Error: no more codec config is needed"
-    return
-  codecConfigPackets.push buf
   nalUnitType = buf[0] & 0x1f
-  console.log "[rtmp] Retained codec config for NAL unit type #{nalUnitType}"
-  for b in buf
-    process.stdout.write b.toString(16) + " "
-  console.log()
   if nalUnitType is 7
     spsPacket = buf
   else if nalUnitType is 8
@@ -1542,7 +1531,8 @@

   startStream: (timeForVideoRTPZero) ->
     console.log "RTMP server startStream"
-    codecConfigPackets = []
+    spsPacket = null
+    ppsPacket = null

   handleRTMPTRequest: (req, callback) ->
     # /fcs/ident2 will be handled in another place


Select highways only

Select highways only

--- webpack/src/index.coffee
+++ webpack/src/index.coffee
@@ -19,7 +19,7 @@
   lng = latlng.lng
   """
   [out:json];
-  way(around:100,#{lat},#{lng})->.ways;
+  way(around:100,#{lat},#{lng})[highway]->.ways;
   node(around:100,#{lat},#{lng})->.nodes;
   .ways >->.way_nodes;
   node.nodes.way_nodes;
@@ -46,8 +46,8 @@

   map.on('click', (ev) ->
     latlng = ev.latlng
-    # TODO: don't select rivers! Check for highway tag presence.
     # TODO: handle no nodes returned
+    console.log(latlng)
     fetch("http://overpass-api.de/api/interpreter?data=#{overpass_query(latlng)}")
       .then((response) => response.text())
       .then((body) =>


simple click events and functions for nav buttons

--- public/scripts/backbone-schedule/views/nav.coffee
+++ public/scripts/backbone-schedule/views/nav.coffee
@@ -14,10 +14,30 @@

 		events:
-			'click #save' : 'save'
+			'click #save-button' : 'save'
+			'click #load-button' : 'load'
+			'click #new-button' : 'new'
+			'click #print-button' : 'print'
+			'click #link-button' : 'link'
+			'click #ical-button' : 'ical'

 		save: ->
 			console.log 'save clicked -- probably call router'
+
+		load: ->
+			console.log 'load clicked'
+
+		new: ->
+			console.log 'new clicked'
+
+		print: ->
+			console.log 'print clicked'
+
+		link: ->
+			console.log 'share link clicked'
+
+		ical: ->
+			console.log 'ical clicked'

 		# Renders the actual view from the template


Don't be clever.

Don't be clever.

--- src/ThreeBSP.coffee
+++ src/ThreeBSP.coffee
@@ -323,7 +323,10 @@
     front = @front.clipPolygons front if @front
     back  = @back.clipPolygons  back  if @back

-    return front.concat if @back then back else []
+    if @back
+      return front.concat back
+    else
+      return front

   clipTo: (node) => returning this, => @options.timer.doTask =>
     @polygons = node.clipPolygons @polygons


Fix FrequencySelector delete

Fix FrequencySelector delete

--- scripts/controllers/FrequencySelector.coffee
+++ scripts/controllers/FrequencySelector.coffee
@@ -136,6 +136,7 @@

 	onDeleteClick: (e) =>
 		e.stopPropagation()
+		@deselect()
 		@range.destroy()

 exports = FrequencySelector


spec: browser-window-created event

spec: browser-window-created event

--- spec/api-app-spec.coffee
+++ spec/api-app-spec.coffee
@@ -26,19 +26,30 @@
       assert.equal app.getName(), 'test-name'
       app.setName 'Electron Test'

-  describe 'focus/blur event', ->
+  describe 'BrowserWindow events', ->
     w = null
-    beforeEach ->
-      w.destroy() if w?
-      w = new BrowserWindow(show: false, width: 400, height: 400)
     afterEach ->
       w.destroy() if w?
       w = null
-    it 'should emit focus event', (done) ->
+
+    it 'should emit browser-window-focus event when window is focused', (done) ->
+      app.once 'browser-window-focus', (e, window) ->
+        assert.equal w.id, window.id
+        done()
+      w = new BrowserWindow(show: false)
+      w.emit 'focus'
+
+    it 'should emit browser-window-blur event when window is blured', (done) ->
       app.once 'browser-window-blur', (e, window) ->
         assert.equal w.id, window.id
         done()
-      app.once 'browser-window-focus', (e, window) ->
-        assert.equal w.id, window.id
-        w.emit 'blur'
-      w.emit 'focus'
+      w = new BrowserWindow(show: false)
+      w.emit 'blur'
+
+    it 'should emit browser-window-created event when window is created', (done) ->
+      app.once 'browser-window-created', (e, window) ->
+        setImmediate ->
+          assert.equal w.id, window.id
+          done()
+      w = new BrowserWindow(show: false)
+      w.emit 'blur'


Fix grammar spec

Fix grammar spec

--- spec/grammar-spec.coffee
+++ spec/grammar-spec.coffee
@@ -1,4 +1,5 @@
 {TextEditor} = require 'atom'
+path = require 'path'

 describe "Ledger grammar", ->
   [grammar] = []


:fire: Remove for...of helper

:fire: Remove for...of helper

--- spec/palette-spec.coffee
+++ spec/palette-spec.coffee
@@ -1,9 +1,7 @@
 Color = require '../lib/color'
 Palette = require '../lib/palette'

-forOf = (iter, fn) -> `for (value of iter) { fn(value) }`; return
-
-describe 'Palette', ->
+fdescribe 'Palette', ->
   [colors, palette] = []

   beforeEach ->
@@ -37,7 +35,7 @@
       ]
       map = palette.getUniqueColorsMap()
       keys = []
-      forOf map.keys(), (k) -> keys.push(k)
+      map.forEach (v,k) -> keys.push(k)

       expect(keys.length).toEqual(3)


core/run Fix memory require

core/run Fix memory require

--- lib/run.coffee
+++ lib/run.coffee
@@ -149,7 +149,7 @@
   params = command: params if typeof params is 'string'
   params.end ?= true
   services = @config.services
-  engine = require('nikita/src/core/kv/engines/memory')()
+  engine = require('nikita/lib/core/kv/engines/memory')()
   each @contexts
   .parallel true
   .call (context, callback) ->


Fix #3

Fix #3

--- node/tracking.coffee
+++ node/tracking.coffee
@@ -79,9 +79,9 @@
 		# Data specific parsing

 		req.query.language = req.query.language.toLowerCase()
-		req.query.platform = switch
+		req.query.platform = switch req.query.platform
 			when req.query.platform.indexOf	'windows' isnt -1	then 'Windows'
-			when req.query.platform.indexOf	'Windows' isnt -1	then 'Windows'
+			when req.query.platform.indexOf	'OS X' isnt -1		then 'OS X'
 			when req.query.platform.indexOf	'Ubuntu' isnt -1	then 'Ubuntu'
 			else req.query.platform


Don't squash profile pictures

Don't squash profile pictures

--- angular/core/components/user_avatar/user_avatar.coffee
+++ angular/core/components/user_avatar/user_avatar.coffee
@@ -25,10 +25,8 @@
           if _2x() then 'medium' else 'small'
         when 'medium'
           if _2x() then 'large' else 'medium'
-        when 'large'
+        when 'large', 'featured'
           'large'
-        when 'featured'
-          'original'
       $scope.user.avatarUrl[size]

     return


De-activates intervention filter customization because of side-effect

--- app/assets/javascripts/backend/interventions.js.coffee
+++ app/assets/javascripts/backend/interventions.js.coffee
@@ -211,13 +211,13 @@
     $(this).each ->
       E.interventions.updateAvailabilityInstant($(this).val())

-  $(document).on "click", '.view-toolbar a', (event) ->
-    E.interventions.hideKujakuFilters($(event.target).is('[data-janus-href="cobbles"]'))
+  # $(document).on "click", '.view-toolbar a', (event) ->
+  #   E.interventions.hideKujakuFilters($(event.target).is('[data-janus-href="cobbles"]'))

   $(document).ready ->

-    E.interventions.hideKujakuFilters($('.view-toolbar a[data-janus-href="cobbles"]').hasClass('active'))
+    # E.interventions.hideKujakuFilters($('.view-toolbar a[data-janus-href="cobbles"]').hasClass('active'))

     if $('.taskboard').length > 0


Trigger boolean toggle callback on page load

--- app/assets/javascripts/lib/models/input_element.coffee
+++ app/assets/javascripts/lib/models/input_element.coffee
@@ -18,6 +18,7 @@

     if @get('unit') == 'boolean'
       @bind('change:user_value', @handle_boolean_callbacks)
+      @bind('initial-set:user_value', @handle_boolean_callbacks)
       @handle_boolean_callbacks()

   conversions: ->
@@ -171,6 +172,9 @@
         disabled: this.isDisabled(i.get('key')) || i.get('disabled') || values.disabled
       }, { silent: true })

+      i.trigger('initial-set:user_value')
+      i.trigger('initial-set:disabled')
+
       @addInputElement(i)

   # Get the string which contains the update values for all dirty input elements


no unhandled rejection if trying to play nothing

--- resources/assets/coffee/_classes/osu-audio.coffee
+++ resources/assets/coffee/_classes/osu-audio.coffee
@@ -43,7 +43,8 @@
     promise = @player().play()
     # old api returns undefined
     promise?.catch (error) ->
-      throw error unless error.name == 'AbortError'
+      return if error.name == 'AbortError' || error.name == 'NotSupportedError' && !osu.present(@player.src)
+      throw error

   player: =>


Add App.groupBindingLog option

Add App.groupBindingLog option

--- app/assets/javascripts/ultimate/backbone/app.js.coffee
+++ app/assets/javascripts/ultimate/backbone/app.js.coffee
@@ -7,6 +7,7 @@
   warnOnMultibind: true
   preventMultibind: false
   performanceReport: true
+  groupBindingLog: true

   Models: {}
   Collections: {}
@@ -43,7 +44,8 @@
       cout 'info', "Processed #{bindersCount} custom element binder#{if bindersCount is 1 then '' else 's'} in #{Math.round((performanceBinders - performanceViews) * 1000)}\u00B5s"

   bindViews: (jRoot = $('html')) ->
-    console?.groupCollapsed? 'bindViews on', jRoot
+    if @groupBindingLog
+      console?.groupCollapsed? 'bindViews on', jRoot
     bindedViews = []
     sortedViews = _.sortBy(_.pairs(@Views), (p) -> p[1].priority)
     for [viewName, viewClass] in sortedViews when viewClass::el
@@ -54,7 +56,8 @@
           cout 'info', "Binded view #{viewName}:", view
           @viewInstances.push view
           bindedViews.push view
-    console?.groupEnd?()
+    if @groupBindingLog
+      console?.groupEnd?()
     bindedViews

   canBind: (element, viewClass) ->


another fix for problematic event handling getting in the way of following points in IE8

Former-commit-id: 745e690e954baddb4b862f0fa4d43c7e02e34db9 [formerly c5fdcf1c07829d6df89766f535c11679c13b0c06] [formerly 1e2758689af2f81a3e0d7cf16503720d85246961 [formerly 1e2758689af2f81a3e0d7cf16503720d85246961 [formerly 58e64dd367b152fd963a79c314a68a08bb096e06]]]
Former-commit-id: afe3ea479ee394c9e3207ab93611ed32d6e854f3 [formerly c77819bcba69b104ebf27949e30ad7bd2b8bf699]
Former-commit-id: c2531ae54e1b49695f765811868905dddca485d7
Former-commit-id: 9df4a6d8d77ea754733a25935131b66076190e68

--- app/assets/javascripts/views_list/pointlistview.coffee
+++ app/assets/javascripts/views_list/pointlistview.coffee
@@ -200,11 +200,10 @@

       # when clicking outside of pointlist, close browsing
       $(document).on 'click.m-pointlist-browsing', (ev)  =>
-        if $('.m-point-expanded, .l-dialog-detachable').length == 0
+        if $(ev.target).closest('.m-pointlist-sort-option').length == 0 && $(ev.target).closest('.m-pointlist-browsing')[0] != @$el[0] && $('.m-point-expanded, .l-dialog-detachable').length == 0
           @toggle_browse(false)
           ev.stopPropagation()

-      @$el.on 'click.m-pointlist-browsing', (ev) => ev.stopPropagation() #if !$(ev.target).data('target') && $('.m-point-expanded, #registration_overlay').length == 0
       $(document).on 'keyup.m-pointlist-browsing', (ev) => @toggle_browse(false) if ev.keyCode == 27 && $('.m-point-expanded, .l-dialog-detachable').length == 0

     else


[Resurrection] working sprite generator

[Resurrection] working sprite generator

--- Builder.coffee
+++ Builder.coffee
@@ -52,12 +52,7 @@

     @config = require('koding-config-manager').load("main.#{options.configFile}")

-    sprite.sprites {path: './images'}, (err, res)->
-      appSprite       = res.app
-      frameworkSprite = res.framework
-      console.log appSprite.filename()
-      console.log frameworkSprite.filename()
-      console.log 'black-bird-128', appSprite.image('black-bird-128')
+    sprite.sprites {path: './website/sprites'}

     try fs.mkdirSync ".build"


fix the haml custom-binding

fix the haml custom-binding

--- js/libs/custom-bindings.coffee
+++ js/libs/custom-bindings.coffee
@@ -127,10 +127,10 @@

   update: (el, valueAccessor, allBindingsAccessor, viewModel, bindingContext) =>
     options = valueAccessor()
-    template = ko.utils.unwrapObservable(options.template)
-    args = ko.utils.unwrapObservable(options.args)
+    template = ko.toJS(options.template)
+    args = ko.toJS(options.args)

-    $(el).html HAML[template].call(args)
+    $(el).html HAML[template].call(undefined, args)
     ko.applyBindingsToDescendants bindingContext, el

 ko.observableArray["fn"].setIndex = (index, newItem) ->


IDEView: Terminal: hide terminate all item for participants

--- client/ide/views/tabview/ideview.coffee
+++ client/ide/views/tabview/ideview.coffee
@@ -391,11 +391,13 @@
           'Terminate'     :
             callback      : => @terminateSession machine, session

+    canTerminateSessions  = sessions.length > 0 and frontApp.amIHost
+
     terminalSessions["New Session"] =
       callback            : => @createTerminal { machine }
-      separator           : sessions.length > 0
-
-    if sessions.length > 0
+      separator           : canTerminateSessions
+
+    if canTerminateSessions
       terminalSessions["Terminate all"] =
         callback          : => @terminateSessions machine


new queryAs option to customize query params

--- client/lanes/models/QueryResults.coffee
+++ client/lanes/models/QueryResults.coffee
@@ -10,10 +10,10 @@
             format: 'array', total_count: 't'
             start: @pageNum * @result.pageSize, limit: @result.pageSize,
             query: query, url: @result.query.url,
+            fields: _.pluck( @result.query.fields.where(query: true), 'id')
         }
-        types = @result.query.fields.groupBy('queryAs')
-        options.fields = _.pluck(types.field, 'id')    unless _.isEmpty types.field
-        options.with   = _.pluck(types.optional, 'id') unless _.isEmpty types.optional
+
+        _.extend(options, Lanes.u.invokeOrReturn(@result.query.syncOptions))

         Lanes.Models.Sync.perform('GET', options).then (resp) =>
             @result.total = resp.total
@@ -34,7 +34,7 @@
             attrs = {}
             for field, i in @result.query.fields.models
                 attrs[field.id] = row[i]
-            new @result.query.modelClass(attrs)
+            new @result.query.src(attrs)
         )

     idForRow: (row) ->
@@ -68,7 +68,6 @@
     modelAt: (index) ->
         @pageForIndex(index).modelAt(index)

-
     saveModelChanges: (model) ->
         page = @pageForIndex
         @_calculateCache() unless @idCache
@@ -78,7 +77,7 @@
         model

     addBlankRow: ->
-        model = new @query.modelClass()
+        model = new @query.model
         row = []
         for field, i in @query.fields.models
             row[i] = model[field.id]


update dbUrl

update dbUrl

--- env.coffee
+++ env.coffee
@@ -9,7 +9,7 @@
 		admin:	'Admin'
 	serverUrl:	serverUrl		# app server url
 	path:		envClient.path
-	dbUrl:		"mongodb://#{envClient.proj}rw:password@localhost/#{envClient.proj}"
+	dbUrl:		"mongodb://mobilerw:password@localhost/mobile"
 	oauth2:
 		authorizationURL:	"#{url}/oauth2/authorize/"
 		tokenURL:			"#{url}/oauth2/token/"


Don’t generate docs when using metadata option

--- src/biscotto.coffee
+++ src/biscotto.coffee
@@ -247,14 +247,15 @@

           if options.metadata
             @generateMetadata(package_json_path, parser, options)
-
-          generator = new Generator(parser, options)
-          generator.generate(file_generator_cb)
-
-          if options.json && options.json.length
-            fs.writeFileSync options.json, JSON.stringify(parser.toJSON(generator.referencer), null, "    ");
-
-          parser.showResult(generator) unless options.quiet
+          else
+            generator = new Generator(parser, options)
+            generator.generate(file_generator_cb)
+
+            if options.json && options.json.length
+              fs.writeFileSync options.json, JSON.stringify(parser.toJSON(generator.referencer), null, "    ");
+
+            parser.showResult(generator) unless options.quiet
+
           done() if done

     catch error


Fix the coffeescript api behaving eratically

--- Web/js/rohbot-api.coffee
+++ Web/js/rohbot-api.coffee
@@ -10,7 +10,7 @@
                 Content: message

         manualSysMessage "Connecting to RohBot..."
-        @connect url
+        # @connect url

         # polite connection management
         connected = false


Change keymap

Change keymap

--- keymaps/impressjs-preview.cson
+++ keymaps/impressjs-preview.cson
@@ -8,4 +8,4 @@
 # For more detailed documentation see
 # https://atom.io/docs/latest/advanced/keymaps
 '.workspace':
-  'ctrl-alt-o': 'impressjs-preview:toggle'
+  'ctrl-alt-i': 'impressjs-preview:toggle'


Use correct config variable

Use correct config variable

--- kites/sharedHosting/api.coffee
+++ kites/sharedHosting/api.coffee
@@ -264,7 +264,7 @@

     {username,uid,domainName} = options

-    domainName ?= "#{username}.#{config.defaultVhostFiles}"
+    domainName ?= "#{username}.#{config.defaultDomain}"
     targetPath = "/Users/#{username}/Sites/#{domainName}"
     cmd        = "mkdir -p #{targetPath} && cp -r #{config.defaultVhostFiles}/website #{targetPath} && chown #{uid}:#{uid} -R #{targetPath}/*"
     log.debug "executing CreateVhost:",cmd


Removed escaping characters by using double quotes.

--- app/assets/javascripts/admin.js.coffee
+++ app/assets/javascripts/admin.js.coffee
@@ -40,7 +40,7 @@
       Turbolinks.visit(location.href)

     showBlacklistType = ->
-      if $('input[name=\'blacklist_type\']:checked').val() == 'file'
+      if $("input[name='blacklist_type']:checked").val() == 'file'
         $('.blacklist-file').show()
         $('.blacklist-raw').hide()
       else


Mekko: smart legend

Mekko: smart legend

Use 3 columns if there are a lot of series. Closes #1153

--- app/assets/javascripts/d3/mekko.coffee
+++ app/assets/javascripts/d3/mekko.coffee
@@ -74,7 +74,7 @@
         right: 10

       @width = @available_width() - (margins.left + margins.right)
-      @height = 410 - (margins.top + margins.bottom)
+      @height = @outer_height() - (margins.top + margins.bottom)
       @series_height = 190 # the rest of the height will be taken by the legend
       @svg = d3.select(@container_selector())
         .append("svg:svg")
@@ -125,7 +125,7 @@
         series: @carrier_list.models
         width: @width
         vertical_offset: @series_height + 130
-        columns: 2
+        columns: if @carrier_list.length > 9 then 3 else 2

       $("#{@container_selector()} rect.carrier").qtip
         content: -> $(this).attr('data-tooltip')


Fixed mekko

Fixed mekko

--- app/assets/javascripts/d3/mekko.coffee
+++ app/assets/javascripts/d3/mekko.coffee
@@ -62,24 +62,39 @@
           color: s.get('color')
           view: this

+    margins:
+      top: 10
+      bottom: 10
+      left: 35
+      right: 10
+
     draw: =>
       @prepare_data()
-      margins =
-        top: 10
-        bottom: 100
-        left: 35
-        right: 10
-
-      @width = @available_width() - (margins.left + margins.right)
-      @height = @outer_height() - (margins.top + margins.bottom)
-      @series_height = 190 # the rest of the height will be taken by the legend
-      @label_offset = @series_height + 10
+      @width  = @available_width()  - (@margins.left + @margins.right)
+      @height = @available_height() - (@margins.top + @margins.bottom)
+
+      legend_columns = if @carrier_list.length > 9 then 3 else 2
+      legend_rows = @carrier_list.length / legend_columns
+      legend_height = legend_rows * @legend_cell_height
+      label_height = 120 # rotated labels
+      label_margin = 5
+
+      @series_height = @height - legend_height - label_height - label_margin
+      @label_offset = @series_height + label_margin
+
       @svg = d3.select(@container_selector())
         .append("svg:svg")
-        .attr("height", @height + margins.top + margins.bottom)
-        .attr("width", @width + margins.left + margins.right)
+        .attr("height", @height + @margins.top + @margins.bottom)
+        .attr("width", @width + @margins.left + @margins.right)
         .append("svg:g")
-        .attr("transform", "translate(#{margins.left}, #{margins.top})")
+        .attr("transform", "translate(#{@margins.left}, #{@margins.top})")
+
+      @draw_legend
+        svg: @svg
+        series: @carrier_list.models
+        width: @width
+        vertical_offset: @series_height + label_height
+        columns: legend_columns

       @x = d3.scale.linear().range([0, @width])
       @y = d3.scale.linear().range([0, @series_height])
@@ -122,13 +137,6 @@
         .append("svg:text")
         .attr("text-anchor", "end")
         .attr("transform", "rotate(-70)")
-
-      @draw_legend
-        svg: @svg
-        series: @carrier_list.models
-        width: @width
-        vertical_offset: @series_height + 120
-        columns: if @carrier_list.length > 9 then 3 else 2

       $("#{@container_selector()} rect.carrier").qtip
         content:


Reduce the amount of legend columns for each mekko to 2

--- app/assets/javascripts/d3/mekko.coffee
+++ app/assets/javascripts/d3/mekko.coffee
@@ -250,8 +250,6 @@
     display_legend: =>
       $(@container_selector()).find('.legend').remove()

-      legend_columns = if @carrier_list.length > 9 then 3 else 2
-
       series = _.filter(@carrier_list.models, (s) ->
         Math.abs(s.total_value()) > 1e-7
       )
@@ -260,7 +258,7 @@
         series: series
         width: @width
         vertical_offset: @series_height + @label_height
-        columns: legend_columns
+        columns: 2

     wrapLabels: =>
       label_height = @label_height


Successfully compile CommonJS, browser and submodule targeted modules

--- src/compiler.coffee
+++ src/compiler.coffee
@@ -207,7 +207,8 @@
     @classParams = newMap()
     @types = []
     @isMalformed = no
-    @moduleName = null
+    @modulePath = null
+    @submodules = newSet()
     @importedModules = newSet()
     @_requested = newMap()

@@ -225,6 +226,12 @@

   isRequesting: ->
     @_requesting
+
+  addSubmodule: (name) ->
+    addToSet @submodules, name
+
+  isSubmodule: (name) ->
+    inSet @submodules, name

   markMalformed: ->
     @isMalformed = yes
@@ -369,7 +376,7 @@
     scope.deferred = []
     scope.deferredBindings = []
     scope.boundTypeVariables = newSet()
-    scope.exportedNames = []
+    scope.exportedNames = newSet()
     scope.classes = newMap()
     scope.typeNames = newMap()
     scope.typeAliases = newMap()
@@ -400,7 +407,7 @@
     @_scope().topLevel

   exportName: (name) ->
-    @_scope().exportedNames.push name
+    addToSet @_scope().exportedNames, name

   addTypeName: (dataType) ->
     if dataType instanceof TypeApp
@@ -1486,9 +1493,42 @@
 topLevel = (ctx, form) ->
   definitionList ctx, spaceSeparatedPairs form

-topLevelModule = (moduleName, defaultImports) -> (ctx, form) ->
-  [(jsAssignStatement (jsAccess "Shem", (validIdentifier moduleName)),
-    (exportAll ctx, (join (importAny defaultImports), (topLevel ctx, form))))]
+topLevelModule = (typedModulePath, defaultImports) -> (ctx, form) ->
+  # [(jsAssignStatement (jsAccess "Shem", (validIdentifier moduleName)),
+  #   (exportAll ctx, (join (importAny defaultImports), (topLevel ctx, form))))]
+  definitions = (join (importAny defaultImports), (topLevel ctx, form))
+  shouldBeExported = (name, declaration) ->
+    not declaration.virtual and declaration.final and isExported name
+  isExported = (name) ->
+    inSet ctx._scope().exportedNames, name
+  nonVirtual = filterMap shouldBeExported, ctx._scope()
+  exported = concatSets (subtractSets nonVirtual, builtInDefinitions()),
+    (filterSet isExported, ctx.submodules) # TODO: separate exporting namespaces according to the separation of namespaces
+  exportList = map validIdentifier, (setToArray exported)
+  exportDictionary = (jsDictionary exportList, exportList)
+  [..., type] = typedModulePath.types
+  switch type
+    when 'commonJs'
+      (concat [
+        toJsString 'use strict'
+        definitions
+        [(jsAssignStatement 'module.exports', exportDictionary)]])
+    when 'browser'
+      [(jsAssignStatement (jsAccess "Shem", (validIdentifier (modulePathToName typedModulePath.names))),
+        (iife (concat [
+          toJsString 'use strict'
+          definitions
+          [(jsReturn exportDictionary)]])))]
+    when 'submodule'
+      [..., name] = typedModulePath.names
+      [(jsVarDeclaration (validIdentifier name),
+        (iife (join definitions, [(jsReturn exportDictionary)])))]
+    else
+      throw new Error "Unspecified ctx.moduleCompilationType"
+
+importAny = (defaultImports) ->
+  concat (for name, names of values defaultImports
+    irImport name, names)

 topLevelExpressionInModule = (defaultImports) -> (ctx, expression) ->
   (iife (concat [
@@ -1653,47 +1693,33 @@
   else
     compiled

-importAny = (defaultImports) ->
-  concat (for name, names of values defaultImports
-    imports name, names)
-
-exportAll = (ctx, definitions) ->
-  shouldBeExported = (name, declaration) ->
-    not declaration.virtual and declaration.final
-  nonVirtual = filterMap shouldBeExported, ctx._scope()
-  exported = subtractSets nonVirtual, builtInDefinitions()
-  exportList = map validIdentifier, (setToArray exported)
-  (iife (concat [
-    toJsString 'use strict'
-    definitions
-    [(jsReturn (jsDictionary exportList, exportList))]]))
-
 ms = {}

-
-# module+ (syntax [..args]
-#   (` export (module ,..args)))
 ms['module+'] = ms_module_export = (ctx, call) ->
-  (call_ (token_ 'export'), [(call_ (token_ 'module'), (_arguments call))])
-
-ms.module = ms_module = (ctx, call) ->
+  (ms_module ctx, call, yes)
+
+ms.module = ms_module = (ctx, call, isExported = no) ->
   requireName ctx, 'Module name required'
   name = ctx.definitionName()
   if name
-    # TODO: here we need to propagate request and use compiled js
-    result = compileModuleTopLevelAst (form_ (_arguments call)), joinModuleNames ctx.moduleName, name
-    if result.request
-      ctx.req result.request, newSet()
-    else
-      console.log "submodule", result.js
-      if result.errors
-        ctx.extendSubstitution fails: result.errors # propagate errors TODO: rename substitution which is only used for type errors now
-      jsValue result.js
+    if ctx.isSubmodule name
+      # TODO: enabled malformed errors to point to other points in the code
+      malformed ctx, ctx.definitionPattern(), 'A submodule with this name already exists'
+    else
+      typedModulePath =
+        names: (join ctx.typedModulePath.names, [name])
+        types: (join ctx.typedModulePath.types, ['submodule'])
+        exported: isExported
+      result = compileModuleTopLevelAst (form_ (_arguments call)), typedModulePath
+      if result.request
+        ctx.req result.request, newSet()
+      else
+        ctx.addSubmodule name
+        if result.errors
+          ctx.extendSubstitution fails: result.errors # propagate errors TODO: rename substitution which is only used for type errors now
+        jsValue result.js
   else
     jsNoop()
-
-joinModuleNames = (parent, child) ->
-  "#{parent}/#{child}"

 ms.export = ms_export = (ctx, call) ->
   requireName ctx, 'Name to export required'
@@ -2404,8 +2430,10 @@
   if not moduleNameAtom or not isName moduleNameAtom
     return malformed ctx, call, 'req requires a module name to require from'
   moduleNameAtom.label = 'module'
-  moduleName = moduleNameAtom.symbol
-  requiredNames = (arrayToSet (filter _is, (map _symbol, reqs)))
+  declaredModuleName = moduleNameAtom.symbol
+  requiredNames =
+  names:  (arrayToSet (filter _is, (map _symbol, reqs)))
+  moduleName = (resolveModuleName ctx, declaredModuleName)
   ctx.req moduleName, requiredNames
   if ctx.isModuleLoaded moduleName
     for arg in reqs
@@ -2424,7 +2452,17 @@
           declareImported ctx, name
       else
         malformed ctx, arg, 'Name required'
-  imports moduleName, setToArray requiredNames
+  irImport moduleName, (setToArray requiredNames), moduleNameAtom
+
+resolveModuleName = (ctx, declaredModuleName) ->
+  modulePathToName (resolveModulePathFrom ctx.typedModulePath.names,
+      (moduleNameToPath declaredModuleName))
+
+resolveModulePathFrom = (currentPath, declaredPath) ->
+  switch _fst declaredPath
+    when '.' then join currentPath, declaredPath[1...]
+    when '..' then join currentPath[...-1], declaredPath[1...]
+    else declaredPath

 declareImportedByDefault = (ctx, modules) ->
   for moduleName, names of values modules
@@ -2435,12 +2473,6 @@
 declareImported = (ctx, name) ->
   ctx.assignType name, (ctx.tempType name)
   ctx.declareAsFinal name, ctx.currentScopeIndex()
-
-imports = (moduleName, names) ->
-  validModuleName = validIdentifier moduleName
-  for name in names
-    validName = validIdentifier name
-    jsVarDeclaration validName, (jsAccess (jsAccess "Shem", validModuleName), validName)

 ms.format = ms_format = (ctx, call) ->
   typeTable =
@@ -3562,6 +3594,49 @@
     (toMatchTypes (applyKindFn hashmapType, newVar(), newVar()), type) or
     (toMatchTypes (applyKindFn hashsetType, newVar()), type)

+
+# Possible compilations:
+#
+#   var _temp = require('Some');
+#
+#   var _temp = require('./Some');
+#
+#   var _temp = require('./Some/Other')
+#
+#   var _temp = Some;
+#
+#   var _temp = Shem.Some;
+#
+#   // [a] (req ./Some/Submodule)              => I need to know the type of the module in the path
+#   var _temp = require('./Some').Submodule;
+#
+irImport = (moduleName, names, moduleNameAtom) ->
+  {ir: irImportTranslate, moduleName, names, moduleNameAtom}
+
+irImportTranslate = (ctx, {moduleName, names, moduleNameAtom}) ->
+  # TODO: pass this info in through context instead of a direct call
+  importedTypedModulePath = moduleNameToTypedModulePath moduleName, ctx.typedModulePath
+  if not importedTypedModulePath
+    return malformed ctx, moduleNameAtom, "Module #{moduleName} could not be found."
+  else if importedTypedModulePath.nonExportedLink
+    return malformed ctx, moduleNameAtom, "Module #{importedTypedModulePath.nonExportedLink} must be exported."
+  {names: pathNames, types} = importedTypedModulePath
+  baseType = types[i]
+  numModules = (filter ((type) -> type is baseType), types).length
+  moduleHandle =
+    switch baseType
+      when 'commonJs'
+        jsCall 'require', (toJsString (pathNames[0...numModules].join '/'))
+      when 'browser'
+        acc = "Shem"
+        for i in [0...numModules]
+          acc = (jsAccess acc, validIdentifier pathNames[i])
+        acc
+  temp = ctx.newJsVariable()
+  join [jsVarDeclaration temp, moduleHandle],
+    for name in names
+      validName = (validIdentifier name)
+      jsVarDeclaration validName, (jsAccess temp, validName)

 isTypeAnnotation = (expression) ->
   (isCall expression) and (':' is _symbol _operator expression)
@@ -5790,32 +5865,55 @@
 compiledModules = newMap()
 moduleGraph = newMap()

+# TODO: pass this info in through context instead of a direct call
+moduleNameToTypedModulePath = (moduleName, currentTypedModulePath) ->
+  module = (lookupInMap moduleGraph, moduleName)
+  if module
+    if violating = moduleAccessViolation (moduleNameToPath moduleName), currentTypedModulePath
+      nonExportedLink: violating
+  module.typedModulePath
+
+moduleAccessViolation = (toModulePath, fromTypedModulePath) ->
+  for i, moduleName in toModulePath
+    if fromTypedModulePath.names[i] isnt moduleName
+      checkedModuleName = (modulePathToName toModulePath[0..i])
+      module = (lookupInMap moduleGraph, checkedModuleName)
+      if not module
+        throw new Error "missing module #{checkedModuleName}
+         that should have been compiled for #{moduleName}"
+      if not module.exported
+        return checkedModuleName
+  false
+
 lookupCompiledModule = (name) ->
   lookupInMap compiledModules, name

 compileModule = (source) ->
-  compileModuleTopLevel source
-
-compileModuleTopLevel = (source, moduleName = '@unnamed', requiredMap = newMap()) ->
-  compileModuleTopLevelAst (astFromSource "(#{source})", -1, -1), moduleName, requiredMap
-
-compileModuleTopLevelAst = (ast, moduleName = '@unnamed', requiredMap = newMap()) ->
+  compileModuleTopLevel source, (names: ['.'], types: ['commonJs'])
+
+defaultTypedModulePath = (names: ['@unnamed'], types: ['browser'])
+
+compileModuleTopLevel = (source, typedModulePath = defaultTypedModulePath, requiredMap = newMap()) ->
+  compileModuleTopLevelAst (astFromSource "(#{source})", -1, -1), typedModulePath, requiredMap
+
+compileModuleTopLevelAst = (ast, typedModulePath = defaultTypedModulePath, requiredMap = newMap()) ->
   # addToMap requiredMap, 'Prelude', yes # TODO: Hardcoded prelude dependency
+  moduleName = modulePathToName typedModulePath.names
   removeFromMap requiredMap, moduleName
   for requiredModuleName of values requiredMap
     if not lookupCompiledModule requiredModuleName
       return request: requiredModuleName
-  replaceOrAddToMap moduleGraph, moduleName, requires: requiredMap
+  replaceOrAddToMap moduleGraph, moduleName, requires: requiredMap, typedModulePath: typedModulePath
   toInject = requiresFor moduleName
   ctx = injectedContext toInject
-  ctx.moduleName = moduleName
+  ctx.typedModulePath = typedModulePath
   defaultImports = importsFor newSet()#(subtractSets (newSetWith 'Prelude'), (newSetWith moduleName))
   declareImportedByDefault ctx, defaultImports
-  compilationFn = (topLevelModule moduleName, defaultImports)
+  compilationFn = (topLevelModule typedModulePath, defaultImports)
   {request, ir, js} = compileCtxAstToJs compilationFn, ctx, ast
   if request
     if not allInjected request, requiredMap
-      return compileModuleTopLevelAst ast, moduleName, request
+      return compileModuleTopLevelAst ast, typedModulePath, request
     else
       {js} = compileCtxIrToJs ctx, ir
   errors = checkTypes ctx
@@ -5894,6 +5992,12 @@

 reverseModuleDependencies = (moduleName) ->
   concatSets (newSetWith moduleName), (requiresFor moduleName)
+
+modulePathToName = (path) ->
+  path.join '/'
+
+moduleNameToPath = (name) ->
+  name.split '/'

 # Primitive type checking for now
 checkTypes = (ctx) ->
@@ -6297,7 +6401,7 @@

 _snd = ([a, b]) -> b

-_fst = ([a, b]) -> a
+_fst = ([a]) -> a

 _labelName = (atom) -> (_symbol atom)[0...-1]


add more debug

add more debug

--- octoblu-oauth.coffee
+++ octoblu-oauth.coffee
@@ -45,7 +45,9 @@
     meshblu = new @MeshbluHttp _.extend({}, uuid: token[1], token: token[2], @meshbluOptions)
     debug 'generateToken', token[1], token[2]
     meshblu.generateAndStoreToken token[1], (error, response) =>
+      debug 'generateAndStoreToken response', response
       newToken = response.token
+      debug 'generateAndStoreToken', token[1], newToken
       meshblu.revokeToken token[1], token[2]
       callback null, btoa token[1] + ':' + newToken


Dropped some erroneous semicolons and trailing spaces. Also dropped the console call which was making IE9 shit itself.

--- vendor/assets/javascripts/simple_datatables.js.coffee
+++ vendor/assets/javascripts/simple_datatables.js.coffee
@@ -4,40 +4,40 @@
 root = exports ? this

 root.simpleDatatables = ( sSource, aoData, fnCallback ) ->
-  columns = [];
-  searchcolumns = [];
-  sortcolumns = [];
+  columns = []
+  searchcolumns = []
+  sortcolumns = []

-  sEcho = 1;
-  sSearch = "";
-  bRegex = false;
-  iDisplayStart = 0;
-  iDisplayLength = 0;
-  data = [];
+  sEcho = 1
+  sSearch = ""
+  bRegex = false
+  iDisplayStart = 0
+  iDisplayLength = 0
+  data = []
   data_types = ["integer" , "date" , "date", "string" , "nil"]

-  $.each(aoData, (index, dataObj) ->
+  $.each(aoData, (index, dataObj) ->
     switch dataObj.name
       when "sColumns"
-        columns = dataObj.value.split(",");
+        columns = dataObj.value.split(",")
       when "sEcho"
-        sEcho = dataObj.value;
+        sEcho = dataObj.value
       when "sSearch"
-        sSearch = dataObj.value;
+        sSearch = dataObj.value
       when "bRegex"
-        bRegex = dataObj.value;
+        bRegex = dataObj.value
       when "iDisplayStart"
-        iDisplayStart = dataObj.value;
+        iDisplayStart = dataObj.value
       when "iDisplayLength"
-        iDisplayLength = dataObj.value;
+        iDisplayLength = dataObj.value
       when "iSortCol_0"
-        console.log("yobanarot!");
-        iSortCol = dataObj.value;
+        console.log("yobanarot!")
+        iSortCol = dataObj.value
       when "sSortDir_0"
-        sSortDir = dataObj.value;
+        sSortDir = dataObj.value
       else
-        console.log(dataObj.name);
-  );
+        # console.log(dataObj.name)
+  )

   # Using the HTML5 data attributes to create the meta_where filtering attributes.
   $.each($('table.datatable thead th input, table.datatable thead th button.active[value!="all"]'), (index, field) ->
@@ -47,39 +47,39 @@
       searchcolumns.push($(field).attr('data-meta-where').replace(/_contains$/, ''))
   )

-  $.each(aoData, (index, dataObj) ->
+  $.each(aoData, (index, dataObj) ->
     search_regexp = ///iSortCol_([0-9]+)///
     if (col = dataObj.name.match(search_regexp))
-      sortcolumns[parseInt(col[1])]=columns[parseInt(dataObj.value)];
+      sortcolumns[parseInt(col[1])]=columns[parseInt(dataObj.value)]

     search_regexp = ///sSortDir_([0-9]+)///
     if (col = dataObj.name.match(search_regexp)) and dataObj.value
-      sortcolumns[col[1]]=sortcolumns[col[1]]+"."+dataObj.value;
-  );
+      sortcolumns[col[1]]=sortcolumns[col[1]]+"."+dataObj.value
+  )

-  data.push({name: "sEcho", value: sEcho});
-  data.push({name: "page", value: iDisplayStart/iDisplayLength + 1});
-  data.push({name: "per_page", value: iDisplayLength});
-  data.push({name: "search[meta_sort]", value: sortcolumns[0]});
+  data.push({name: "sEcho", value: sEcho})
+  data.push({name: "page", value: iDisplayStart/iDisplayLength + 1})
+  data.push({name: "per_page", value: iDisplayLength})
+  data.push({name: "search[meta_sort]", value: sortcolumns[0]})

-  $.each(columns, (index, val) ->
-    data.push({name: "columns["+index+"]", value: val});
-  );
+  $.each(columns, (index, val) ->
+    data.push({name: "columns["+index+"]", value: val})
+  )

   if sSearch
     if bRegex
-      op = "_contains"
-    else
       op = "_contains"
-    data.push({name: "search["+searchcolumns.join("_or_")+op+"]", value: sSearch});
+    else
+      op = "_contains"
+    data.push({name: "search["+searchcolumns.join("_or_")+op+"]", value: sSearch})

   $.ajax({
-    "dataType": 'json',
-    "type": "GET",
-    "url": sSource,
-    "data": data,
-    "success": (data, textStatus, jqXHR) ->
-      fnCallback(data, textStatus, jqXHR);
-      if (typeof(dataSummary) == typeof(Function))
+    "dataType": 'json',
+    "type": "GET",
+    "url": sSource,
+    "data": data,
+    "success": (data, textStatus, jqXHR) ->
+      fnCallback(data, textStatus, jqXHR)
+      if (typeof(dataSummary) == typeof(Function))
         dataSummary()
-  });
+  })


Sampling working, abstracted to module

Sampling working, abstracted to module

--- loadHIT.coffee
+++ loadHIT.coffee
@@ -2,55 +2,46 @@
 #
 mongoose = require 'mongoose'
 async = require 'async'
-mongoose.connect 'mongodb://localhost/test'
-db = mongoose.connection
+_ = require 'underscore'
+#mongoose.connect 'mongodb://localhost/test'
+#mongoose.connect 'mongodb://localhost/test'
+#db = mongoose.connection

 TaskSet = require './models/taskset'
 Hit = require './models/hit'
 ObjectId = mongoose.Types.ObjectId

-user = "TEST"
-hit = "Test HIT"
-hit_id = "557df4899b962f181de3da0c" # The mongo ID for the hit item
+loadHIT = (opts) ->
+  #db.on('error', console.error.bind(console, 'connection error:'))

-db.on('error', console.error.bind(console, 'connection error:'))
-db.once('open', () ->
-  async.waterfall([
-    getBasicInfo,
-    getCondition,
-    getMaxedItems,
-    sampleTaskItems
-  ], (err, results) ->
-    if (err) then return console.error err
-    console.log "Waterfall done"
-    console.log results
-  )
-)
+  #db.once('open', () ->
+    async.waterfall([
+      (cb) -> cb(null, opts) # Pipe opts to first func in waterfal
+      getBasicInfo,
+      getCondition,
+      getMaxedItems,
+      sampleTaskItems
+    ], (err, results) ->
+      if (err) then return console.error err
+      console.log "Waterfall done"
+      console.log results
+    )
+  #)

 # 1. Count how many taskSets this user has done in this condition
 # Also fetch HIT info, since it can be done asynchronously
-getBasicInfo = (callback) ->
+getBasicInfo = (opts, callback) ->
   async.parallel({
+    opts: (callback) -> callback(null, opts)
     # Count of all taskSets done by the user
-    countUserAll: (callback) -> TaskSet.count({user:user}).exec(callback)
+    countUserAll: (callback) -> TaskSet.countUserAll(opts.user, callback)
     # Count of tasksets in the given condition done
-    countUserHIT: (callback) -> TaskSet.count({user:user, hit_id:hit_id}).exec(callback)
+    countUserHIT: (callback) -> TaskSet.countUserHIT(opts.user, opts.hit_id, callback)
     # List of user's past items completed (not specific to the current hit)
-    userItemList: (callback) ->
-      TaskSet.aggregate([{$match: {user:user}},
-        {$project:{"tasks.item.id":1}},
-        {$unwind : "$tasks"},
-        {$group: {_id:"$tasks.item.id"}}]
-      ).exec(callback)
+    userItemList: (callback) -> TaskSet.userItemList(opts.user, callback)
     # Load HIT info
-    hit: (callback) ->
-      Hit.find({_id: ObjectId(hit_id)}, (err, result) ->
-        if result.length is 0 then return callback("No matching hit", result)
-        callback(err, result[0])
-      )
+    hit: (callback) -> Hit.findOne({_id: ObjectId(opts.hit_id)}, callback)
   },
-  # Pass (err, results) directly to the callback, which
-  # will send it down the waterfall
   callback
   )

@@ -63,7 +54,6 @@

   switch obj.condition
     when 'feedback'
-      '''TODO get user feedback and add to obj'''
       callback('Feedback condition not yet designed', obj)
     else
       callback(null, obj)
@@ -74,9 +64,11 @@
 # 3.1 Determine with items have already been done enough
 getMaxedItems = (obj, callback) ->
   if obj.condition is 'teaching'
-    callback(obj, null)
+    callback(null, obj)
+  console.log "!!"+obj.opts.hit_id+"!!"
   # Get a list of all currentHIT items completed
-  TaskSet.aggregate({$match: {hit_id:hit_id}},
+  TaskSet#.find({hit_id:hit_id}
+    .aggregate({$match:{hit_id:obj.opts.hit_id}},
                   {$project:{"tasks.item.id":1}},
                   {$unwind : "$tasks"},
                   # Matching on task item id *after* unwinding
@@ -85,25 +77,39 @@
                   # We want to know which tasks have already be done max times
                   {$match:{count:{$gte:obj.hit.setsPerItem}}}
   ).exec((err, results) ->
+      if (err) then return callback(err, obj)
       obj.maxed = (result._id for result in results)
-      callback(obj, null)
+      callback(null, obj)
   )
+  return

 sampleTaskItems = (obj, callback) ->
   switch obj.condition
     when 'teaching'
       ''' TODO get teaching set '''
-      callback(obj, "Teaching set not ready yet")
+      callback("Teaching set not ready yet", bj)
     when 'fast'
       ''' TODO Prep larger set for fast completion '''
-      callback(obj, "Fast set not ready yet")
+      callback("Fast set not ready yet", obj)
     when 'basic'
       ''' doStuff '''
-      console.log obj.hit.items
-      callback(obj, null)
+      excludes = obj.maxed.concat (item._id for item in obj.userItemList)
+      candidates = _.difference(obj.hit.items, excludes)
+      itemSampleIds = _.sample(candidates, if obj.hit.maxSetSize then obj.hit.maxSetSize else 200)
+      # Load Item model
+      ItemModel = require('./models/' + obj.hit.itemModel)
+      # Query info for all the sampled items
+      ItemModel.find({_id:$in:itemSampleIds}, (err, results) ->
+        if (err) then return callback(err, obj)
+        console.log results
+        #obj.sample = results
+        callback(null, obj)
+      )
       # Lock in-progress files

       # If there are no tasks left,
       # doStuff()
       # Find items which are *not* is set of items completed by user and
       # in set where HIT(CompletedItems).item.Count is less than hit.setsPerItem
+
+module.exports = loadHIT


fix angular compresion bug

fix angular compresion bug

--- app/assets/javascripts/tasks.js.coffee
+++ app/assets/javascripts/tasks.js.coffee
@@ -1,9 +1,9 @@
 app = angular.module("Todo", ["ngResource", "ui.bootstrap", 'templates', "xeditable"])

-app.run (editableOptions) ->
+app.run ["editableOptions", (editableOptions) ->
   editableOptions.theme = "bs3"
   return
-
+]
 app.factory "Task", ["$resource" , ($resource) ->
   $resource("/tasks/:id.json", {id: "@id"}, {update: {method: "PUT"}, query:  {method: 'GET', isArray: true}})

@@ -16,7 +16,7 @@

 @TaskCtrl = ["$scope", "$filter", "Task", "$modal" ,  ($scope, $filter, Task, $modal) ->
   $scope.tasks = Task.query()
-  orderBy = $filter('orderBy');
+  orderBy = $filter('orderBy')

   $scope.addTask = ->
     task = Task.save($scope.newTask)


refresh the Admin recent builds instead of rendering them

--- js/app.coffee
+++ js/app.coffee
@@ -67,7 +67,7 @@
     VM.loadProjects()
     sel = VM.selected()
     if sel.admin_builds
-      VM.loadAdminRecentBuilds()
+      VM.refreshAdminRecentBuilds()
     else if sel.project_name
       VM.loadProject(sel.username, sel.project, sel.branch, true)
     else
@@ -265,6 +265,10 @@
     $.getJSON '/api/v1/admin/recent-builds', (data) =>
       @recent_builds((new CI.inner.Build d for d in data))
     @renderAdminPage "recent_builds"
+
+  refreshAdminRecentBuilds: () =>
+    $.getJSON '/api/v1/admin/recent-builds', (data) =>
+      @recent_builds((new CI.inner.Build d for d in data))

   adminRefreshIntercomData: (data, event) =>
     $.ajax(


bug fix: show the content unless there's a show button

--- app/assets/javascripts/words.js.coffee
+++ app/assets/javascripts/words.js.coffee
@@ -34,6 +34,6 @@
     $(this).hide()
     $('#body').show()

-  # show the body if the action is not 'clips#next'
-  if(!(//.test(window.location.pathname)))
+  # show the body if there's no show buttons
+  if $('#show').length == 0
     $('#body').show()


Allow drawing tools to receive focus Focus them when they are first created, and whenever they are selected.

--- app/classifier/drawing-tools/root.cjsx
+++ app/classifier/drawing-tools/root.cjsx
@@ -24,6 +24,9 @@

   getInitialState: ->
     destroying: false
+
+  componentDidMount: ->
+    @root?.focus()

   render: ->
     toolProps = @props.tool.props
@@ -45,10 +48,12 @@
         STROKE_WIDTH / scale

     unless toolProps.disabled
-      startHandler = toolProps.onSelect
+      startHandler = (e) =>
+        @root?.focus()
+        toolProps.onSelect(e)

     <g className="drawing-tool" {...rootProps} {...@props}>
-      <g className="drawing-tool-main" {...mainStyle} onMouseDown={startHandler} onTouchStart={startHandler}>
+      <g className="drawing-tool-main" ref={(element) => @root = element} {...mainStyle} onMouseDown={startHandler} onTouchStart={startHandler} tabIndex=-1>
         {@props.children}
       </g>


use @projects_have_been_loaded() instead of checking @projects length

--- js/app.coffee
+++ js/app.coffee
@@ -214,7 +214,7 @@
     @loadBuilds('/api/v1/recent-builds')

   loadOrg: (username, refresh) =>
-    if @projects().length is 0 then @loadProjects()
+    if !@projects_have_been_loaded() then @loadProjects()

     @loadBuilds("/api/v1/organization/#{username}", refresh)

@@ -223,7 +223,7 @@
         builds_table: 'org'

   loadProject: (username, project, branch, refresh) =>
-    if @projects().length is 0 then @loadProjects()
+    if !@projects_have_been_loaded() then @loadProjects()

     project_name = "#{username}/#{project}"
     path = "/api/v1/project/#{project_name}"


remove debugging

remove debugging

--- js/app.coffee
+++ js/app.coffee
@@ -421,7 +421,6 @@
         if project_name is VM.selected().project_name
           sel = _.extend(VM.selected(), sel)

-        console.log(sel)
         VM.selected sel

         VM.loadEditPage cx, cx.params.username, cx.params.project, cx.params.splat


Firefox: Fix visual mode.

Firefox: Fix visual mode.

The problem with visual mode is that `@selection.type` isn't implemented
in Firefox/Gecko.  Here, we simulate the same effect with `anchorNode`
and the length of the selection.

Mention @mrmr1993.

--- content_scripts/mode_visual.coffee
+++ content_scripts/mode_visual.coffee
@@ -159,7 +159,7 @@

   # Scroll the focus into view.
   scrollIntoView: ->
-    unless @selection.type == "None"
+    if @selection.anchorNode
       elementWithFocus = DomUtils.getElementWithFocus @selection, @getDirection() == backward
       Scroller.scrollIntoView elementWithFocus if elementWithFocus

@@ -240,7 +240,7 @@
       commandHandler: @commandHandler.bind this

     # If there was a range selection when the user lanuched visual mode, then we retain the selection on exit.
-    @shouldRetainSelectionOnExit = @options.userLaunchedMode and @selection.type == "Range"
+    @shouldRetainSelectionOnExit = @options.userLaunchedMode and 0 < @selection.toString().length

     @onExit (event = null) =>
       if @shouldRetainSelectionOnExit
@@ -269,7 +269,7 @@

     # Establish or use the initial selection.  If that's not possible, then enter caret mode.
     unless @name == "caret"
-      if @selection.type in [ "Caret", "Range" ]
+      if @selection.anchorNode?
         selectionRect = @selection.getRangeAt(0).getBoundingClientRect()
         if window.vimiumDomTestsAreRunning
           # We're running the DOM tests, where getBoundingClientRect() isn't available.
@@ -277,7 +277,7 @@
         selectionRect = Rect.intersect selectionRect, Rect.create 0, 0, window.innerWidth, window.innerHeight
         if selectionRect.height >= 0 and selectionRect.width >= 0
           # The selection is visible in the current viewport.
-          if @selection.type == "Caret"
+          if @selection.toString().length == 0
             # The caret is in the viewport. Make make it visible.
             @movement.extendByOneCharacter(forward) or @movement.extendByOneCharacter backward
         else
@@ -285,7 +285,7 @@
           # more likely to be interested in visible content.
           @selection.removeAllRanges()

-      if @selection.type != "Range" and @name != "caret"
+      unless @selection.anchorNode
         new CaretMode
         HUD.showForDuration "No usable selection, entering caret mode...", 2500

@@ -341,15 +341,14 @@
     super extend options, name: "caret", indicator: "Caret mode", alterMethod: "move"

     # Establish the initial caret.
-    switch @selection.type
-      when "None"
-        @establishInitialSelectionAnchor()
-        if @selection.type == "None"
-          @exit()
-          HUD.showForDuration "Create a selection before entering visual mode.", 2500
-          return
-      when "Range"
-        @movement.collapseSelectionToAnchor()
+    if @selection.anchorNode
+      @movement.collapseSelectionToAnchor()
+    else
+      @establishInitialSelectionAnchor()
+      unless @selection.anchorNode
+        @exit()
+        HUD.showForDuration "Create a selection before entering visual mode.", 2500
+        return

     @movement.extendByOneCharacter forward
     @movement.scrollIntoView()


Example now saves to account upon creation

--- public/scripts/MarkatoPage.cjsx
+++ public/scripts/MarkatoPage.cjsx
@@ -20,10 +20,15 @@
       formatUserBucket @props.userBucket
     else
       songs: [
-        name: "I Wanna Hold Your Hand [Example]"
+        name: "I Wanna Hold Your Hand"
         content: example
       ]
       currentSongIndex: 0
+
+  componentDidMount: ->
+    # If we have a new account, save the default song that gets loaded
+    unless @props.userBucket?
+      @saveCurrentSong()

   componentWillReceiveProps: (nextProps) ->
     if nextProps.userBucket?


src/sugar.js.coffe updated

src/sugar.js.coffe updated

--- src/sugar.js.coffee
+++ src/sugar.js.coffee
@@ -1,4 +1,4 @@
-JasmineSugar = ()->
+JasmineSugar = ->
   @it = ->

   @iit = ->


reset page number when searching

reset page number when searching

--- source/app/controllers/shows_controller.js.coffee
+++ source/app/controllers/shows_controller.js.coffee
@@ -24,3 +24,7 @@
   disablePerviousPage: (->
     @get('page') <= 1
   ).property('page')
+
+  resetPage: (->
+    @set('page', 1)
+  ).observes('filteredContent.[]')


Add specs for backspace behavior

Add specs for backspace behavior

--- spec/autocomplete-manager-integration-spec.coffee
+++ spec/autocomplete-manager-integration-spec.coffee
@@ -113,16 +113,19 @@
         runs ->
           expect(editorView.querySelector('.autocomplete-plus')).not.toExist()

-      it 'should show the suggestion list when backspacing', ->
+      it "keeps the suggestion list open when it's already open on backspace", ->
         expect(editorView.querySelector('.autocomplete-plus')).not.toExist()

         # Trigger an autocompletion
         editor.moveToBottom()
-        editor.insertText('fu')
+        editor.insertText('f')
+        editor.insertText('u')

         waitForAutocomplete()

         runs ->
+          expect(editorView.querySelector('.autocomplete-plus')).toExist()
+
           key = atom.keymaps.constructor.buildKeydownEvent('backspace', {target: document.activeElement})
           atom.keymaps.handleKeyboardEvent(key)

@@ -131,6 +134,21 @@
         runs ->
           expect(editorView.querySelector('.autocomplete-plus')).toExist()
           expect(editor.lineTextForBufferRow(13)).toBe('f')
+
+      it "does not open the suggestion on backspace when it's closed", ->
+        expect(editorView.querySelector('.autocomplete-plus')).not.toExist()
+
+        # Trigger an autocompletion
+        editor.setCursorBufferPosition([2, 39]) # at the end of `items`
+
+        runs ->
+          key = atom.keymaps.constructor.buildKeydownEvent('backspace', {target: document.activeElement})
+          atom.keymaps.handleKeyboardEvent(key)
+
+          waitForAutocomplete()
+
+        runs ->
+          expect(editorView.querySelector('.autocomplete-plus')).not.toExist()

       it 'should not update the suggestion list while composition is in progress', ->
         triggerAutocompletion(editor)


add api, set charset

add api, set charset


Remove extra whitespace.

Remove extra whitespace.

--- elements/Greet.QuetzalElement2.coffee
+++ elements/Greet.QuetzalElement2.coffee
@@ -5,7 +5,5 @@

 class window.EmphaticGreet extends Greet
   template: """
-    <super punctuation='!'>
-      *<content></content>*
-    </super>
+    <super punctuation='!'>*<content></content>*</super>
   """


Fix subject metadata popup (#4773)

Fix subject metadata popup (#4773)

Check that value.startsWith exists before calling it.

--- app/components/subject-viewer.cjsx
+++ app/components/subject-viewer.cjsx
@@ -295,7 +295,7 @@

           {for key, value of @props.subject?.metadata when key.charAt(0) not in @props.metadataFilters and key[...2] isnt '//'
-            if (value.startsWith('http'))
+            if (value.startsWith and value.startsWith('http'))
               value = "[#{value}](+tab+#{value})"

               {key.replace(///^(#{@props.metadataPrefixes.join('|')})///, '')}


Add more readability

Add more readability

--- src/assets/scripts/global/coffee/NS.rating.coffee
+++ src/assets/scripts/global/coffee/NS.rating.coffee
@@ -4,10 +4,10 @@
   scope = checkboxes[0].parentNode

   removeHalf = ->
-    star = scope.querySelector('.is-half')
-    a = star.className.split(' ')
-    a = a.filter (x)-> return x isnt 'is-half'
-    star.className = a.join(' ')
+    hlf = scope.querySelector('.is-half')
+    tmp = hlf.className.split(' ')
+    tmp = tmp.filter (arr)-> return arr isnt 'is-half'
+    hlf.className = tmp.join(' ')
     return

   for checkbox in checkboxes by -1


Add card flipping to CardView class.

Add card flipping to CardView class.

--- src/card/CardView.coffee
+++ src/card/CardView.coffee
@@ -3,10 +3,12 @@
 #
 # @module stout-ui/card/CardView
 ###
-defaults        = require 'lodash/defaults'
-InteractiveView = require '../interactive/InteractiveView'
-template        = require './card.template'
-vars            = require '../vars'
+defaults              = require 'lodash/defaults'
+InteractiveView       = require '../interactive/InteractiveView'
+Promise               = require 'stout-core/promise/Promise'
+template              = require './card.template'
+TransitionCanceledExc = require('stout-client/exc').TransitionCanceledExc
+vars                  = require '../vars'

 # Require shared input variables.
 require '../vars/card'
@@ -20,6 +22,12 @@
 ###
 TAG_NAME = vars.readPrefixed 'card/card-tag'

+
+CARD_CLASS = vars.read 'card/card-class'
+FLIPPABLE_CLASS = vars.read 'card/card-flippable-class'
+FLIPPED_CLASS = vars.read 'card/card-flipped-class'
+FLIPPING_CLASS = vars.read 'card/card-flipping-class'
+FLIP_TIME = vars.readTime 'card/card-flip-time'

 ###*
@@ -35,3 +43,52 @@
   constructor: (init, events) ->
     defaults init, {template, tagName: TAG_NAME}
     super init, events
+    @prefixedClasses.add CARD_CLASS
+
+    @on 'ready', =>
+      if @select('.ui-card-back')
+        @flippable = true
+        @prefixedClasses.add FLIPPABLE_CLASS
+
+
+  @property 'flippable',
+    default: false
+
+
+  @property 'flipping',
+    default: false
+
+
+  @property 'flipped',
+    default: false
+
+
+  @property 'sideVisible',
+    default: 'front'
+    values: ['front', 'back']
+
+
+  ###*
+  # Flips the card, if it's flippable.
+  #
+  # @method flip
+  # @memberof stout-ui/card/CardView
+  ###
+  flip: ->
+    new Promise (fulfill, reject) =>
+      if not @flippable then reject(
+        new TransitionCanceledExc "Card not flippable.")
+      if @flipping then reject(
+        new TransitionCanceledExc "Card currently flipping.")
+      @flipping = true
+      @prefixedClasses.add FLIPPING_CLASS
+      if @flipped
+        @prefixedClasses.remove FLIPPED_CLASS
+      else
+        @prefixedClasses.add FLIPPED_CLASS
+      setTimeout =>
+        @prefixedClasses.remove FLIPPING_CLASS
+        @flipping = false
+        @flipped = not @flipped
+        fulfill()
+      , FLIP_TIME


Support for event_ids array

Support for event_ids array

--- src/core/javascripts/models/basket_item.js.coffee
+++ src/core/javascripts/models/basket_item.js.coffee
@@ -500,6 +500,8 @@
         data.time = @time.time
         if @time.event_id
           data.event_id = @time.event_id
+        else if @time.event_ids
+          data.event_ids = @time.event_ids
       else if @date and @date.event_id
         data.event_id = @date.event_id
       data.price = @price


config: updated internal domain to kiln.wtf

--- src/coffee/config.coffee
+++ src/coffee/config.coffee
@@ -2,4 +2,4 @@
   FC_API_URL:
     if process.env.MOCK
     then ''
-    else process.env.FC_API_URL or 'http://flak-cannon.i.clay.io'
+    else process.env.FC_API_URL or 'https://flak-cannon.kiln.wtf'


Separate init iScroll into own function so it can be used outside of flight

* Useful when dealing with form input bug

--- src/coffee/flight.coffee
+++ src/coffee/flight.coffee
@@ -91,7 +91,7 @@
     return if @targetPage is @currentPage

     if @os.android and @os.version < '4'
-      @iScroll = new iScroll @targetPage.querySelector('.scrollview-inner')
+      @initIscroll @targetPage

     unless @currentPage
       @targetPage.style.display = "-webkit-box"
@@ -244,6 +244,18 @@
   hideTransitionedPage: (page) =>
     if @hasClass(page,'page')
       page.style.display = "none" unless page.id is @targetPage.id
+
+  # Initiates iScroll when needed for android devices 2.3
+  #
+  # targetPage  - String or DOM element of the page that you want to use iScroll
+  #
+  # Returns nothing
+  initIscroll: (targetPage) =>
+    targetPage = @targetPage unless targetPage?
+    targetPage = document.querySelector targetPage if typeof targetPage is "string"
+    @iScroll = new iScroll targetPage.querySelector('.scrollview-inner'),
+      onBeforeScrollMove: =>
+        @iScroll.refresh()

   # Private: Get a Hash of browser user agent information.
   #


Improved TikZ support

Improved TikZ support

--- grammars/tikz.cson
+++ grammars/tikz.cson
@@ -7,77 +7,43 @@
 limitLineLength: false

 patterns: [
+  { include: '#coordinate' }
+  { include: '#options' }
+  { include: '#pathDescriptor' }
   {
-    include: '#beginTikzPicture'
+    comment: 'This prevents .tikz files being scoped as LaTeX & embedded with TikZ'
+    match: '((\\\\)(begin|end))(\\{)(tikzpicture)(\\})'
+    captures:
+      1: name: 'support.function.environment.$3.latex'
+      2: name: 'punctuation.definition.function.$3.latex'
+      # 3: For knowing if the capture is `begin` or `end`
+      4: name: 'punctuation.definition.arguments.begin.latex'
+      5: name: 'variable.parameter.environment.tikzpicture.latex'
+      6: name: 'punctuation.definition.arguments.end.latex'
   }
-  {
-    include: '#drawCommandMeta'
-  }
-  {
-    include: 'text.tex.latex'
-  }
+  { include: 'text.tex.latex' }
 ]

 repository:
-  beginTikzPicture: {
-    comment: 'Looks for begin/end tikzpicture. This env should be in the file, even if separate from the main document.'
-    begin: '(\\\\)begin\\{tikzpicture\\}'
-    beginCaptures:
-      0:
-        name: 'keyword.control.environment.tikzpicture.tikz'
-    end: '(\\\\)end\\{tikzpicture\\}'
-    endCaptures:
-      0:
-        name: 'keyword.control.environment.tikzpicture.tikz'
-    patterns: [
-      {
-        include: '$self'
-      }
-    ]
+  # General structures that apply anywhere
+  coordinate: {
+    comment: 'The contents is either a point, or name (which refers to a point).'
+    match: '(\\()(.*?)(\\))'
+    captures:
+      1: name: 'punctuation.definition.begin.coordinate.tikz'
+      2:
+        name: 'markup.other.coordinate.tikz'
+        patterns: [{ include: '$self' }]
+      3: name: 'punctuation.definition.end.coordinate.tikz'
   }
-  drawCommandMeta: {
-    begin: '(\\\\)draw\\b'
-    beginCaptures:
-      0:
-        name: 'support.function.draw.tikz'
-    end: '\\;'
-    endCaptures:
-      0:
-        name: 'punctuation.definition.lineend.tikz'
-    patterns: [
-      {
-        include: '#squareBrackets'
-      }
-      {
-        include: '#parentheses'
-      }
-      {
-        include: '#pathDescriptor'
-      }
-    ]
-  }
-  squareBrackets: {
+  options: {
+    begin: '\\['
+    beginCaptures: 0: name: 'punctuation.definition.argument.optional.begin.tikz'
+    end: '\\]'
+    endCaptures: 0: name: 'punctuation.definition.argument.optional.end.tikz'
     contentName: 'variable.parameter.optional.tikz'
-    begin: '\\['
-    beginCaptures:
-      0:
-        name: 'punctuation.definition.optional.begin.tikz'
-    end: '\\]'
-    endCaptures:
-      0:
-        name: 'punctuation.definition.optional.end.tikz'
-  }
-  parentheses: {
-    contentName: 'variable.parameter.tikz'
-    begin: '\\('
-    beginCaptures:
-      0:
-        name: 'punctuation.definition.begin.tikz'
-    end: '\\)'
-    endCaptures:
-      0:
-        name: 'punctuation.definition.end.tikz'
+    patterns: [{ include: '$self' }]
   }
   pathDescriptor: {
     comment: 'For the constructions like --, -|, |-, etc.'


Update Atom's configuration file

Update Atom's configuration file

--- .config/config.cson
+++ .config/config.cson
@@ -5,8 +5,8 @@
     softTabs: false
     autoIndentOnPaste: false
     invisibles: {}
+    lineHeight: 1.3
     fontSize: 11
-    showIndentGuide: true
   core:
     themes: [
       "atom-light-ui"
@@ -14,6 +14,16 @@
     ]
     disabledPackages: [
       "snippets"
+      "SFTP-deployment"
+      "spell-check"
+      "autocomplete-css"
+      "autocomplete-plus"
+      "compare-files"
+      "merge-conflicts"
+      "atom-compare-files"
+      "background-tips"
+      "welcome"
+      "docblockr"
     ]
   "exception-reporting":
     userId: "4b2dfe3a-7eef-a871-4d35-230cb11366f8"
@@ -22,7 +32,22 @@
   "tree-view":
     hideIgnoredNames: true
   whitespace:
-    ensureSingleTrailingNewline: false
     removeTrailingWhitespace: false
   "asciidoc-preview": {}
   "autoclose-html": {}
+  "SFTP-deployment": {}
+  "remote-sync": {}
+  "remote-edit": {}
+  "file-icons":
+    coloured: false
+  "recent-files":
+    updated: false
+  "spell-check": {}
+  "autocomplete-plus": {}
+  "atom-beautify":
+    _analyticsUserId: "84a7da30-e058-42d8-9fd2-cc0519eb2781"
+  "status-bar": {}
+  hex: {}
+  docblockr: {}
+  minimap:
+    autoToggle: false


Using custom version compare. Will fix vibe.d dependencies!

Signed-off-by: Dan Printzell <69f25cbe4b2608181d39803083cb5ee0c30f2794@gmail.com>

--- lib/dub-config.coffee
+++ lib/dub-config.coffee
@@ -60,6 +60,43 @@
 			return config if config?.name == name
 		return undefined

+	compareVersion: (aOrg, bOrg) ->
+		if aOrg == undefined
+			return bOrg
+		a = aOrg.split(/(?:\.|_)+/)
+		b = bOrg.split(/(?:\.|_)+/)
+		l = Math.min(a.length, b.length)
+		i = 0
+		while i < l
+			an = parseInt(a[i], 10)
+			bn = parseInt(b[i], 10)
+			d = an - bn
+			if d != 0
+				if d < 0
+					return bOrg
+				else
+					return aOrg
+			if ("" + an).length != a[i] # a will contain letters after the number
+				if ("" + bn).length == b[i] # b will NOT contain letters after the number
+					return aOrg # A > B
+
+				aExtra = a[i].substr(("" + an).length)
+				bExtra = b[i].substr(("" + bn).length)
+
+				if aExtra < bExtra
+					return bOrg
+				else
+					return aOrg
+
+			if ("" + bn).length != b[i]
+				return bOrg # A < B
+			i++
+		d = a.length - (b.length)
+		if d < 0
+			return bOrg
+		else
+			return aOrg
+
 	getImports: (config, actualCallback, imports = atom.config.get("atomize-d.dImportPaths"), cwd = atom.project.getPaths()[0]) ->

 		callback = (imports) ->
@@ -114,7 +151,9 @@
 				async.each allFound, ((found, cb) ->
 					versions = []
 					versions.push entry.substr(found.name.length + 1) for entry in found.entries when entry.substr(found.name.length + 1) != "master"
-					max = semver.maxSatisfying(versions, found.target)
+					max = undefined
+					max = self.compareVersion(max, ver) for ver in versions
+
 					if !max
 						return cb() # not found
 					bestPathMatch = path.join(self.userPath, "packages", "#{found.name}-#{max}")


Update name for clarity

Update name for clarity

--- lib/guide-view.coffee
+++ lib/guide-view.coffee
@@ -134,7 +134,7 @@

   getUri: -> @uri

-  getTitle: -> "Guide"
+  getTitle: -> "Welcome Guide"

   onDidChangeTitle: -> new Disposable ->
   onDidChangeModified: -> new Disposable ->


Some tweaks to the TrailMaster, consolidating some changes and altering the dot style.

--- app/lib/surface/TrailMaster.coffee
+++ app/lib/surface/TrailMaster.coffee
@@ -2,13 +2,15 @@
 PAST_PATH_WIDTH = 5
 FUTURE_PATH_ALPHA = 0.4
 FUTURE_PATH_WIDTH = 2
+TARGET_ALPHA = 1
+TARGET_WIDTH = 10
+FUTURE_PATH_INTERVAL_DIVISOR = 4
+PAST_PATH_INTERVAL_DIVISOR = 2

 Camera = require './Camera'
 CocoClass = require 'lib/CocoClass'

 module.exports = class TrailMaster extends CocoClass
-  paths: null # dictionary of thang ids to containers for their paths
-  pathDisplayObject: null
   world: null

   constructor: (@camera, @layerAdapter) ->
@@ -22,13 +24,13 @@
     @generatingPaths = true
     @cleanUp()
     @createGraphics()
-    @pathDisplayObject = new createjs.SpriteContainer(@layerAdapter.spriteSheet)
-    @pathDisplayObject.mouseEnabled = @pathDisplayObject.mouseChildren = false
-    @pathDisplayObject.addChild @createFuturePath()
-#    @pathDisplayObject.addChild @createPastPath() # Just made the animated path the full path... do we want to have past and future look different again?
-    @pathDisplayObject.addChild @createTargets()
+    pathDisplayObject = new createjs.SpriteContainer(@layerAdapter.spriteSheet)
+    pathDisplayObject.mouseEnabled = pathDisplayObject.mouseChildren = false
+    pathDisplayObject.addChild @createFuturePath()
+#    pathDisplayObject.addChild @createPastPath() # Just made the animated path the full path... do we want to have past and future look different again?
+    pathDisplayObject.addChild @createTargets()
     @generatingPaths = false
-    return @pathDisplayObject
+    return pathDisplayObject

   cleanUp: ->
     createjs.Tween.removeTweens(sprite) for sprite in @tweenedSprites
@@ -36,9 +38,8 @@
     @tweens = []

   createGraphics: ->
-    color = @colorForThang(@thang.team, PAST_PATH_ALPHA)
-    @targetDotKey = @cachePathDot(10, color)
-    @pastDotKey = @cachePathDot(PAST_PATH_WIDTH, color)
+    @targetDotKey = @cachePathDot(TARGET_WIDTH, @colorForThang(@thang.team, TARGET_ALPHA))
+    @pastDotKey = @cachePathDot(PAST_PATH_WIDTH, @colorForThang(@thang.team, PAST_PATH_ALPHA))
     @futureDotKey = @cachePathDot(FUTURE_PATH_WIDTH, @colorForThang(@thang.team, FUTURE_PATH_ALPHA))

   cachePathDot: (width, color) ->
@@ -47,7 +48,7 @@
     unless key in @layerAdapter.spriteSheet.getAnimations()
       circle = new createjs.Shape()
       radius = width/2
-      circle.graphics.setStrokeStyle(1).beginFill(color).beginStroke('#000000').drawCircle(0, 0, radius)
+      circle.graphics.setStrokeStyle(width/5).beginFill(color).beginStroke('#000000').drawCircle(0, 0, radius)
       @layerAdapter.addCustomGraphic(key, circle, [-radius*1.5, -radius*1.5, radius*3, radius*3])
     return key

@@ -60,12 +61,13 @@

   createPastPath: ->
     return unless points = @world.pointsForThang @thang.id, @camera
-    params = { interval: 8, frameKey: @pastDotKey }
+    interval = Math.max(1, parseInt(@world.frameRate / PAST_PATH_INTERVAL_DIVISOR))
+    params = { interval: interval, frameKey: @pastDotKey }
     return @createPath(points, params)

   createFuturePath: ->
     return unless points = @world.pointsForThang @thang.id, @camera
-    interval = Math.max(1, parseInt(@world.frameRate / 4))
+    interval = Math.max(1, parseInt(@world.frameRate / FUTURE_PATH_INTERVAL_DIVISOR))
     params = { interval: interval, animate: true, frameKey: @futureDotKey }
     return @createPath(points, params)

@@ -77,6 +79,7 @@
       sup = @camera.worldToSurface x: x, y: y
       sprite = new createjs.Sprite(@layerAdapter.spriteSheet)
       sprite.scaleX = sprite.scaleY = 1 / @layerAdapter.resolutionFactor
+      sprite.scaleY *= @camera.y2x
       sprite.gotoAndStop(@targetDotKey)
       sprite.x = sup.x
       sprite.y = sup.y
@@ -93,6 +96,7 @@
       y = points[i + 1]
       sprite = new createjs.Sprite(@layerAdapter.spriteSheet)
       sprite.scaleX = sprite.scaleY = 1 / @layerAdapter.resolutionFactor
+      sprite.scaleY *= @camera.y2x
       sprite.gotoAndStop(key)
       sprite.x = x
       sprite.y = y