Add a failing test for that odd select/option error

Needed to be put into a try/catch since if it fails, it could either be because the structure of the html is not what we expected, or because it 'successfully' populates it with the incorrect data.

--- tests/batman/view/select_rendering_test.coffee
+++ tests/batman/view/select_rendering_test.coffee
@@ -7,6 +7,28 @@
     Batman.DOM.IteratorBinding.deferEvery = false
   teardown: ->
     Batman.DOM.IteratorBinding::deferEvery = oldDeferEvery
+
+asyncTest 'it should still render the select boxes properly even after the binded data has been reset', 3, ->
+  leo = new Batman.Object ({name: 'leo', id: 1})
+  mikey = new Batman.Object ({name: 'mikey', id: 2})
+
+  context = Batman
+    heroes: new Batman.Set(leo, mikey)
+    selected: mikey
+    showBox: false
+
+  helpers.render  '<select data-bind="selected.id">' +
+                    '<option data-foreach-hero="heroes" data-bind-value="hero.id" data-bind="hero.name" />' +
+                  '</selected>', context, (node) ->
+    equal node[0].childNodes[0].innerHTML, 'leo'
+    equal node[0].childNodes[1].innerHTML, 'mikey'
+    context.set 'heroes', new Batman.Set(leo, mikey)
+    try
+      equal node[0].childNodes[1].innerHTML, 'mikey'
+    catch error
+      ok false, "Unable to test value of option because HTML was not formed as expected"
+
+    QUnit.start()

 asyncTest 'it should bind the value of a select box and update when the javascript land value changes', 2, ->
   context = Batman


increase pdfng page load/render timeouts to 1 minute

--- services/web/public/coffee/ide/pdfng/directives/pdfRenderer.coffee
+++ services/web/public/coffee/ide/pdfng/directives/pdfRenderer.coffee
@@ -7,8 +7,8 @@

 		class PDFRenderer
 			JOB_QUEUE_INTERVAL: 25
-			PAGE_LOAD_TIMEOUT: 30*1000
-			PAGE_RENDER_TIMEOUT: 30*1000
+			PAGE_LOAD_TIMEOUT: 60*1000
+			PAGE_RENDER_TIMEOUT: 60*1000

 			constructor: (@url, @options) ->
 				PDFJS.disableFontFace = true  # avoids repaints, uses worker more


Fix result handle

Fix result handle

--- server/api/setupApi.coffee
+++ server/api/setupApi.coffee
@@ -384,7 +384,10 @@
         res.json(await BlogPost.findLast(5, 1000 * 60 * 60 * 24 * 60))

     app.get '/api/result/:id', wrap (req, res) ->
-        result = (await Result.findById(req.params.id)).toObject()
+        result = (await Result.findById(req.params.id))?.toObject()
+        if not result
+            res.json({})
+            return
         result.fullUser = await User.findById(result.user)
         result.fullTable = await Problem.findById(result.table)
         res.json(result)


Additional diagnostics

Additional diagnostics

--- server/lib/download.coffee
+++ server/lib/download.coffee
@@ -18,7 +18,7 @@
     return statistics

 export default download = (href, jar, options) ->
-    logger.info "Downloading", href
+    logger.info "Downloading", href, options
     if not jar
         jar = request.jar()
     delay = 5


Allow direct updates to multiple users in model

--- server/models/users.coffee
+++ server/models/users.coffee
@@ -135,8 +135,15 @@
       else
         crud.create json, callback

+  updateQuery = (query, change, callback) ->
+    if not query.systemId?
+      callback 'SystemId not specified'
+    else
+      model.update query, change, {multi: true}, callback
+
   exports = gi.common.extend {}, crud
   exports.update = update
+  exports.updateQuery = updateQuery
   exports.findOrCreate = findOrCreate
   exports.findOneByProviderId = findOneByProviderId
   exports.resetAPISecret = resetAPISecret


Delete angular-date-range-picker.coffee

Delete angular-date-range-picker.coffee

--- src/angular-date-range-picker.coffee
+++ src/angular-date-range-picker.coffee
@@ -1,283 +0,0 @@
-angular.module "dateRangePicker", ['pasvaz.bindonce']
-
-angular.module("dateRangePicker").directive "dateRangePicker", ["$compile", "$timeout", ($compile, $timeout) ->
-  # constants
-  pickerTemplate = """
-

-

-      ◀
-

-

-

-

-
-
-

-

-
-

-
-

-

-

-      ▶
-

-

-

-        Select range: <select ng-click="prevent_select($event)" ng-model="quick" ng-options="e.range as e.label for e in quickList"></select>
-

-

-        Apply
-        cancel
-

-

-

-  """
-  CUSTOM = "CUSTOM"
-
-  restrict: "AE"
-  replace: true
-  template: """
-
-
-      {{ model.start.format("ll") }} - {{ model.end.format("ll") }}
-      Select date range
-
-
-      {{ model.format("ll") }}
-      Select date
-
-
-  """
-  scope:
-    model: "=ngModel" # can't use ngModelController, we need isolated scope
-    customSelectOptions: "="
-    ranged: "="
-    pastDates: "@"
-    callback: "&"
-
-  link: ($scope, element, attrs) ->
-    $scope.quickListDefinitions = $scope.customSelectOptions
-    $scope.quickListDefinitions ?= [
-      {
-        label: "This week",
-        range: moment().range(
-          moment().startOf("week").startOf("day"),
-          moment().endOf("week").startOf("day")
-        )
-      }
-      {
-        label: "Next week",
-        range: moment().range(
-          moment().startOf("week").add(1, "week").startOf("day"),
-          moment().add(1, "week").endOf("week").startOf("day")
-        )
-      }
-      {
-        label: "This fortnight",
-        range: moment().range(
-          moment().startOf("week").startOf("day"),
-          moment().add(1, "week").endOf("week").startOf("day")
-        )
-      }
-      {
-        label: "This month",
-        range: moment().range(
-          moment().startOf("month").startOf("day"),
-          moment().endOf("month").startOf("day")
-        )
-      }
-      {
-        label: "Next month",
-        range: moment().range(
-          moment().startOf("month").add(1, "month").startOf("day"),
-          moment().add(1, "month").endOf("month").startOf("day")
-        )
-      }
-    ]
-    $scope.quick = null
-    $scope.range = null
-    $scope.selecting = false
-    $scope.visible = false
-    $scope.start = null
-    # Backward compatibility - if $scope.ranged is not set in the html, it displays normal date range picker.
-    $scope.showRanged = if $scope.ranged == undefined then true else $scope.ranged
-
-    _makeQuickList = (includeCustom = false) ->
-      return unless $scope.showRanged
-      $scope.quickList = []
-      $scope.quickList.push(label: "Custom", range: CUSTOM) if includeCustom
-      for e in $scope.quickListDefinitions
-        $scope.quickList.push(e)
-
-    _calculateRange = () ->
-      if $scope.showRanged
-        $scope.range = if $scope.selection
-          start = $scope.selection.start.clone().startOf("month").startOf("day")
-          end = start.clone().add(2, "months").endOf("month").startOf("day")
-          moment().range(start, end)
-        else
-          moment().range(
-            moment().startOf("month").subtract(1, "month").startOf("day"),
-            moment().endOf("month").add(1, "month").startOf("day")
-          )
-      else
-        $scope.selection = false
-        $scope.selection = $scope.model || false
-        $scope.date = moment($scope.model) || moment()
-        $scope.range = moment().range(
-          moment($scope.date).startOf("month"),
-          moment($scope.date).endOf("month")
-        )
-
-    _checkQuickList = () ->
-      return unless $scope.showRanged
-      return unless $scope.selection
-      for e in $scope.quickList
-        if e.range != CUSTOM and $scope.selection.start.startOf("day").unix() == e.range.start.startOf("day").unix() and
-            $scope.selection.end.startOf("day").unix() == e.range.end.startOf("day").unix()
-          $scope.quick = e.range
-          _makeQuickList()
-          return
-
-      $scope.quick = CUSTOM
-      _makeQuickList(true)
-
-
-    _prepare = () ->
-      $scope.months = []
-      startIndex = $scope.range.start.year()*12 + $scope.range.start.month()
-      startDay = moment().startOf("week").day()
-
-      $scope.range.by "days", (date) ->
-        d = date.day() - startDay
-        d = 7+d if d < 0 # (d == -1 fix for sunday)
-        m = date.year()*12 + date.month() - startIndex
-        w = parseInt((7 + date.date() - d) / 7)
-
-        sel = false
-        dis = false
-
-        if $scope.showRanged
-          if $scope.start
-            sel = date == $scope.start
-            dis = date < $scope.start
-          else
-            sel = $scope.selection && $scope.selection.contains(date)
-        else
-          sel = date.isSame($scope.selection)
-          dis = moment().diff(date, 'days') > 0 if $scope.pastDates
-
-        $scope.months[m] ||= {name: date.format("MMMM YYYY"), weeks: []}
-        $scope.months[m].weeks[w] ||= []
-        $scope.months[m].weeks[w][d] =
-          date:     date
-          selected: sel
-          disabled: dis
-          start:    ($scope.start && $scope.start.unix() == date.unix())
-
-      # Remove empty rows
-      for m in $scope.months
-        if !m.weeks[0]
-          m.weeks.splice(0, 1)
-
-      _checkQuickList()
-
-    $scope.show = () ->
-      $scope.selection = $scope.model
-      _calculateRange()
-      _prepare()
-      $scope.visible = true
-
-    $scope.hide = ($event) ->
-      $event?.stopPropagation?()
-      $scope.visible = false
-      $scope.start = null
-
-    $scope.prevent_select = ($event) ->
-      $event?.stopPropagation?()
-
-
-    $scope.ok = ($event) ->
-      $event?.stopPropagation?()
-      $scope.model = $scope.selection
-      $timeout -> $scope.callback() if $scope.callback
-      $scope.hide()
-
-    $scope.select = (day, $event) ->
-      $event?.stopPropagation?()
-      return if day.disabled
-
-      if $scope.showRanged
-        $scope.selecting = !$scope.selecting
-
-        if $scope.selecting
-          $scope.start = day.date
-        else
-          $scope.selection = moment().range($scope.start, day.date)
-          $scope.start = null
-      else
-        $scope.selection = moment(day.date)
-
-      _prepare()
-
-    $scope.move = (n, $event) ->
-      $event?.stopPropagation?()
-      if $scope.showRanged
-        $scope.range = moment().range(
-          $scope.range.start.add(n, 'months').startOf("month").startOf("day"),
-          $scope.range.start.clone().add(2, "months").endOf("month").startOf("day")
-        )
-      else
-        $scope.date.add(n, 'months')
-        $scope.range = moment().range(
-          moment($scope.date).startOf("month"),
-          moment($scope.date).endOf("month")
-        )
-
-      _prepare()
-
-    $scope.handlePickerClick = ($event) ->
-      $event?.stopPropagation?()
-
-    $scope.$watch "quick", (q, o) ->
-      return if !q || q == CUSTOM
-      $scope.selection = $scope.quick
-      $scope.selecting = false
-      $scope.start = null
-      _calculateRange()
-      _prepare()
-
-    $scope.$watch "customSelectOptions", (value) ->
-      return unless customSelectOptions?
-      $scope.quickListDefinitions = value
-
-    # create DOM and bind event
-    domEl = $compile(angular.element(pickerTemplate))($scope)
-    element.append(domEl)
-
-    element.bind "click", (e) ->
-      e?.stopPropagation?()
-      $scope.$apply ->
-        if $scope.visible then $scope.hide() else $scope.show()
-
-    documentClickFn = (e) ->
-      $scope.$apply -> $scope.hide()
-      true
-
-    angular.element(document).bind "click", documentClickFn
-
-    $scope.$on '$destroy', ->
-      angular.element(document).unbind 'click', documentClickFn
-
-    _makeQuickList()
-    _calculateRange()
-    _prepare()
-]


fix lint

fix lint

--- js/src/http/DirectoryApi.coffee
+++ js/src/http/DirectoryApi.coffee
@@ -167,7 +167,7 @@
         url    : usersInRealmUrl() + '/initialized/' + realm
         method : 'GET'
       })))
-      .then (response) =>
+      .then (response) ->
         uuids = response.data
         return uuids

@@ -177,7 +177,7 @@
         url    : usersInRealmUrl() + '/' + realm
         method : 'GET'
       })))
-      .then (response) =>
+      .then (response) ->
         uuids = response.data
         return uuids


Fix imageUrl helper

Fix imageUrl helper

--- src/client/handlebars-helpers.coffee
+++ src/client/handlebars-helpers.coffee
@@ -1,29 +1,29 @@
 define ['aura-extensions/hull-utils', 'handlebars'], (utils, handlebars)->

-  ->
-
+  (app)->
+
     __seq = new Date().getTime()

     HandlebarsHelpers = {}

     ###*
      * # Template Helpers
-     * The helpers below are bundled in, so you can start to use them immediately in your handlebars templates.
+     * The helpers below are bundled in, so you can start to use them immediately in your handlebars templates.
      * Feel free to add your own or override those to fit your needs.
-     *
+     *
      * # Inflections
      * The inflections helpers proxy the [inflection.js](http://code.google.com/p/inflection-js/) library.
     ###

     ###*
-     * Generates an image representation for a social object.
-     *
+     * Generates an image representation for a social object.
+     *
      *
      *     =>
-     *
+     *
      * @param  {String} id           The Object's ID
-     * @param  {String} size="small" An optional size preset. See docs for available presets
+     * @param  {String} size="small" An optional size preset. See docs for available presets
      * @param  {String} fallback=""  If the ID is null, this url will be returned
      * @return {String}              An image URL
     ###
@@ -32,16 +32,16 @@
       return fallback unless id
       id = id.replace(/\/(large|small|medium|thumb)$/,'')
       size = 'small' unless _.isString(size)
-      "//hullapp.dev/img/#{id}/#{size}"
-
-
-    ###*
-     * Return a relative date.
-     * Uses [moment.js](http://momentjs.com/) behind the scenes.
-     *
+      "#{app.config.orgUrl}/img/#{id}/#{size}"
+
+
+    ###*
+     * Return a relative date.
+     * Uses [moment.js](http://momentjs.com/) behind the scenes.
+     *
      *     {{fromNow date}}
      *     => 2 days ago
-     *
+     *
      * @param  {String} date The date string to format, can be any format moment.js understands
      * @return {String}      A pretty date
     ###
@@ -49,13 +49,13 @@
       moment(date).fromNow()

     ###*
-     * Return the Stringified version of an object.
-     *
+     * Return the Stringified version of an object.
+     *
      *     {{json object}}
      *     => string dump of object
      *
      * Mainly for debug purposes
-     *
+     *
      * @param  {Object} obj Any javascript object
      * @return {String}     A String dump of the object
     ###
@@ -64,41 +64,41 @@

     ###*
-     * Create a loop where `key` is the key of the hash, and `value` is it's value.
-     *
-     * In your code :
-     *
+     * Create a loop where `key` is the key of the hash, and `value` is it's value.
+     *
+     * In your code :
+     *
      *     h = {
      *       propertyName  : propertyValue,
      *       propertyName2 : propertyValue2,
      *       …
      *      }
      *
-     * propertyValue can be anything, including another hash.
-     * In your templates :
+     * propertyValue can be anything, including another hash.
+     * In your templates :
      *
      *

      *       {{#key_value h}}
      *         {{key}} is {{value}} !
      *       {{/key_value}}
      *

-     *
+     *
      *     =>
-     *
+     *
      *

-     *       propertyName is propertyValue !
-     *       propertyName2 is propertyValue2 !
+     *       propertyName is propertyValue !
+     *       propertyName2 is propertyValue2 !
      *

-     *
-     *
+     *
+     *
      * @param  {Object} hash    The hash to iterate on
     ###
     HandlebarsHelpers.key_value = (hash, options)->
       (options.fn(key: key, value: value) for own key, value of hash).join('')

     ###*
-     * List helper.
-     *
+     * List helper.
+     *
      * @param  {[type]} obj     [description]
      * @param  {[type]} options [description]
      * @return {[type]}         [description]
@@ -109,17 +109,17 @@

     ###*
-     * Output a page-unique sequential number.
+     * Output a page-unique sequential number.
      * Mainly useful to assign a unique id to each entry in a list
      *
      *

      *

      *
      *     =>
-     *
+     *
      *

      *

-     *
+     *
      * @param  {String} prefix='seq' The sequence's prefix
      * @return {String}              An Unique identifier, incremented everytime `seq` is called
     ###
@@ -128,11 +128,11 @@

     ###*
-     * Renders a singular English language noun into its plural form normal results can be overridden by passing in an alternative
-     *
+     * Renders a singular English language noun into its plural form normal results can be overridden by passing in an alternative
+     *
      *     {{pluralize "activity"}}
      *     => 'activities'
-     *
+     *
      * @param  {String} string A text string
      * @return {String}        A processed string
     ###
@@ -140,11 +140,11 @@
       string.pluralize()

     ###*
-     * Renders a plural English language noun into its singular form normal results can be overridden by passing in an alterative
-     *
+     * Renders a plural English language noun into its singular form normal results can be overridden by passing in an alterative
+     *
      *     {{singularize "activies"}}
      *     => 'activity'
-     *
+     *
      * @param  {String} string A text string
      * @return {String}        A processed string
     ###
@@ -153,11 +153,11 @@

     ###*
      * Renders a lower case underscored word into camel case.
-     * Also translates "/" into "::" (underscore does the opposite)
-     *
+     * Also translates "/" into "::" (underscore does the opposite)
+     *
      *     {{camelize "resource/image_gallery"}}
      *     => 'Resource::ImageGallery'
-     *
+     *
      * @param  {String} string A text string
      * @return {String}        A processed string
     ###
@@ -165,12 +165,12 @@
       string.camelize()

     ###*
-     * Renders a camel cased word into words seperated by underscores
+     * Renders a camel cased word into words seperated by underscores
      * also translates "::" back into "/" (camelize does the opposite)
-     *
+     *
      *     {{underscore "Resource::ImageGallery"}}
      *     => 'resource/image_gallery'
-     *
+     *
      * @param  {String} string A text string
      * @return {String}        A processed string
     ###
@@ -178,11 +178,11 @@
       string.underscore()

     ###*
-     * Renders a lower case and underscored word into human readable form defaults to making the first letter capitalized unless you pass true
-     *
+     * Renders a lower case and underscored word into human readable form defaults to making the first letter capitalized unless you pass true
+     *
      *     {{humanize "image_gallery"}}
      *     => 'Image Gallery'
-     *
+     *
      * @param  {String} string A text string
      * @return {String}        A processed string
     ###
@@ -190,11 +190,11 @@
       string.humanize()

     ###*
-     * Renders all characters to lower case and then makes the first upper
-     *
+     * Renders all characters to lower case and then makes the first upper
+     *
      *     {{capitalize "image gallery"}}
      *     => 'Image gallery'
-     *
+     *
      * @param  {String} string A text string
      * @return {String}        A processed string
     ###
@@ -202,11 +202,11 @@
       string.capitalize()

     ###*
-     * Renders all underbars and spaces as dashes
-     *
+     * Renders all underbars and spaces as dashes
+     *
      *     {{dasherize "image_gallery one"}}
      *     => 'image-gallery-one'
-     *
+     *
      * @param  {String} string A text string
      * @return {String}        A processed string
     ###
@@ -214,11 +214,11 @@
       string.dasherize()

     ###*
-     * Renders words into title casing (as for book titles)
-     *
+     * Renders words into title casing (as for book titles)
+     *
      *     {{titleize "image_gallery one"}}
      *     => 'Image Gallery One'
-     *
+     *
      * @param  {String} string A text string
      * @return {String}        A processed string
     ###
@@ -226,11 +226,11 @@
       string.titlelize()

     ###*
-     * Renders class names that are prepended by modules into just the class
-     *
+     * Renders class names that are prepended by modules into just the class
+     *
      *     {{demodulize "Resources::ImageGallery"}}
      *     => 'ImageGallery'
-     *
+     *
      * @param  {String} string A text string
      * @return {String}        A processed string
     ###
@@ -238,11 +238,11 @@
       string.demodulize()

     ###*
-     * Renders camel cased singular words into their underscored plural form
-     *
+     * Renders camel cased singular words into their underscored plural form
+     *
      *     {{tableize "ImageGallery"}}
      *     => 'image_galleries'
-     *
+     *
      * @param  {String} string A text string
      * @return {String}        A processed string
     ###
@@ -250,11 +250,11 @@
       string.tableize()

     ###*
-     * Renders an underscored plural word into its camel cased singular form
-     *
+     * Renders an underscored plural word into its camel cased singular form
+     *
      *     {{tableize "image_galleries"}}
      *     => 'ImageGallery'
-     *
+     *
      * @param  {String} string A text string
      * @return {String}        A processed string
     ###
@@ -263,11 +263,11 @@

     ###*
      * Renders a class name (camel cased singular noun) into a foreign key.
-     * defaults to seperating the class from the id with an underbar unless you pass true
-     *
+     * defaults to seperating the class from the id with an underbar unless you pass true
+     *
      *     {{tableize "ImageGallery"}}
      *     => 'image_gallery_id'
-     *
+     *
      * @param  {String} string A text string
      * @return {String}        A processed string
     ###
@@ -275,11 +275,11 @@
       string.foreign_key()

     ###*
-     * Renders all numbers found in the string into their sequence like "22nd"
-     *
+     * Renders all numbers found in the string into their sequence like "22nd"
+     *
      *     {{ordinalize "the 1 is before the 2 man"}}
      *     => 'the 1st is before the 2nd man'
-     *
+     *
      * @param  {String} string A text string
      * @return {String}        A processed string
     ###
@@ -287,11 +287,11 @@
       string.ordinalize()

     ###*
-     * Remove surrounding whitespace
-     *
+     * Remove surrounding whitespace
+     *
      *     {{trim "  images   "}}
      *     => 'images'
-     *
+     *
      * @param  {String} string A text string
      * @return {String}        A processed string
     ###
@@ -301,7 +301,7 @@

     ###*
      * Compare two values and enters the scope if true
-     *
+     *
      *     var color = 'blue'
      *
      *     {{#ifEqual color "red"}}
@@ -311,11 +311,11 @@
      *     {{#ifEqual color "blue"}}
      *       The Color is blue !
      *     {{/ifEqual}}
-     *
+     *
      *     =>
      *       'The Color is not red'
      *       'The Color is blue !'
-     *
+     *
      * @param  {Anything} v1    Left side of comparison
      * @param  {Anything} v2    Right side of comparison
     ###
@@ -324,7 +324,7 @@

     ###*
-     * loops over an array and provides a .index property on the object so you can use it as a counter.
+     * loops over an array and provides a .index property on the object so you can use it as a counter.
      *
      *     var counter = [{name:10},{name:11},{name:12}]
      *
@@ -332,7 +332,7 @@
      *       Name : {{name}}, Index : {{index}} |
      *     {{/eachWithIndex}}
      *     => Name : 10, Index : 0 | Name : 11, Index : 1 | Name : 12, Index : 2 |
-     *
+     *
      * @param  {Array}   array  The Array of objects to iterate on
     ###
     HandlebarsHelpers.eachWithIndex = (array, fn)->


add default state of overlay

add default state of overlay

does this look like the right way to pass default for toggleable?

this should close my issue #37 please pull if this is correct.

thanks.

--- src/coffee/directives/overlay.coffee
+++ src/coffee/directives/overlay.coffee
@@ -5,8 +5,9 @@
   link: (scope, elem, attrs) ->
     body = elem.html()
     id = attrs.overlay
+    active = "default='#{attrs.default}'" if attrs.default?
     html = """
-

+


fix #34 : app/app/assets/images does not exist

--- lib/alloyCompletionRules.coffee
+++ lib/alloyCompletionRules.coffee
@@ -58,7 +58,7 @@
       assetPath = path.join(alloyRootPath,'assets')
       imgPath = path.join(assetPath,'images')

-      if @regExp.test(line)
+      if @regExp.test(line) and util.isExistAsDirectory(imgPath)
         completions = []
         files = find.fileSync imgPath
         for file in files


Fill up the progress bar when you resolve.

--- lib/merge-conflicts-view.coffee
+++ lib/merge-conflicts-view.coffee
@@ -51,6 +51,8 @@
     (event) ->
       p = $(event.target).find('.path').text()
       GitBridge.checkoutSide side, p, ->
+        full = path.join atom.project.path, p
+        atom.emit 'merge-conflicts:resolved', file: full, total: 1, resolved: 1
         atom.workspace.open p

   instance: null


Use ‘Stack Trace’ rather than ‘Stack’

Use ‘Stack Trace’ rather than ‘Stack’

--- lib/notification-element.coffee
+++ lib/notification-element.coffee
@@ -136,10 +136,10 @@
   handleStackTraceToggleClick: (e, container) ->
     e.preventDefault?()
     if container.style.display is 'none'
-      e.target.innerHTML = 'Hide Stack'
+      e.target.innerHTML = 'Hide Stack Trace'
       container.style.display = 'block'
     else
-      e.target.innerHTML = 'Show Stack'
+      e.target.innerHTML = 'Show Stack Trace'
       container.style.display = 'none'

   autohide: ->


made osx resolver manager clean up after itself

--- lib/osx_resolver_manager.coffee
+++ lib/osx_resolver_manager.coffee
@@ -11,6 +11,10 @@
 class OsxResolverManager
   constructor: (@axle) ->
     @domains = {}
+    @files_created = {}
+    process.on 'exit', =>
+      walkabout(f).unlink_sync() for f, x of @files_created
+
     @axle.routes.forEach (d) => @add_domain(d)

     @axle.on 'route:add', (route) =>
@@ -36,15 +40,23 @@
   create_resolver: (domain) ->
     return unless @running is true

-    Logger.info "write /etc/resolver/#{domain}"
-    walkabout('/etc/resolver').mkdirp_sync()
-    walkabout("/etc/resolver/#{domain}").write_file_sync(AXLE_RESOLVER)
+    file = walkabout("/etc/resolver/#{domain}")
+
+    Logger.info "write #{file.absolute_path}"
+    unless file.exists_sync()
+      file.directory().mkdirp_sync()
+      file.write_file_sync(AXLE_RESOLVER)
+      @files_created[file.absolute_path] = 1
+    @

   remove_resolver: (domain) ->
     return unless @running is true

-    Logger.info "rm /etc/resolver/#{domain}"
-    walkabout("/etc/resolver/#{domain}").unlink_sync() if walkabout("/etc/resolver/#{domain}").exists_sync()
+    file = walkabout("/etc/resolver/#{domain}")
+
+    Logger.info "rm #{file.absolute_path}"
+    file.unlink_sync() if file.exists_sync()
+    delete @files_created[file.absolute_path]

   start: ->
     @running = true


added zip code and credit card patterns

added zip code and credit card patterns


Split to the right when opening preview

Split to the right when opening preview

--- lib/restructured-preview.coffee
+++ lib/restructured-preview.coffee
@@ -32,4 +32,7 @@
   toggle: ->
     editor = atom.workspace.getActiveTextEditor()
     path = editor.getPath()
-    atom.workspace.open("restructured-preview://#{path}")
+    options =
+      split: 'right'
+      searchAllPanes: true
+    atom.workspace.open("restructured-preview://#{path}", options)


Implemented failed login

Implemented failed login

--- lib/spark-ide-login-view.coffee
+++ lib/spark-ide-login-view.coffee
@@ -18,6 +18,7 @@
           @span ' key'
       @subview 'emailEditor', new EditorView(mini: true, placeholderText: 'Could I please have an email address?'), outlet: 'emailEditor'
       @subview 'passwordEditor', new EditorView(mini: true, placeholderText: 'and a password?'), outlet: 'passwordEditor'
+      @div class: 'text-error block', outlet: 'errorLabel'
       @div class: 'block', =>
         @button click: 'login', class: 'btn btn-primary', outlet: 'loginButton', 'Log in'
         @button click: 'cancel', class: 'btn', outlet: 'cancelButton', 'Cancel'
@@ -79,6 +80,7 @@
       @emailEditor.getEditor().setText ''
       @passwordEditor.getEditor().setText ''
       @passwordEditor.originalText = ''
+      @errorLabel.hide()

   hide: ->
     if @hasParent()
@@ -86,7 +88,9 @@

   cancel: (event, element) ->
     if !!@loginPromise
-      # TODO: Cancel login
+      @loginPromise = null
+
+    @unlockUi()
     @hide()

   validateInputs: ->
@@ -110,6 +114,10 @@

     isOk

+  unlockUi: ->
+    @emailEditor.hiddenInput.removeAttr 'disabled'
+    @passwordEditor.hiddenInput.removeAttr 'disabled'
+    @loginButton.removeAttr 'disabled'

   login: (event, element) ->
     if !@validateInputs()
@@ -121,5 +129,18 @@
     @passwordEditor.hiddenInput.attr 'disabled', 'disabled'
     @loginButton.attr 'disabled', 'disabled'
     @spinner.removeClass 'hidden'
+    @errorLabel.hide()

+    self = @
     client = new ApiClient settings.apiUrl
+    @loginPromise = client.login 'spark-ide', @email, @password
+    @loginPromise.done (e) ->
+      self.spinner.addClass 'hidden'
+      if !self.loginPromise
+        return
+    , (e) ->
+      self.spinner.addClass 'hidden'
+      if !self.loginPromise
+        return
+      self.unlockUi()
+      self.errorLabel.text(e).show()


TODO im JS gefixt

TODO im JS gefixt

--- app/assets/javascripts/jquery_mobile_helpers.js.coffee
+++ app/assets/javascripts/jquery_mobile_helpers.js.coffee
@@ -1,5 +1,3 @@
-# TODO Sven: Script throws errors
-
 # collapse page navs after use
 $( () ->
   $('body').delegate('.content-secondary .ui-collapsible-content',
@@ -8,17 +6,17 @@
   )
 )

-default_transition = () -> {
+default_transition = () ->
   winwidth = $( window ).width()
   trans ="slide"
-  if( winwidth >= 1000 ){
+  if ( winwidth >= 1000 )
     trans = "none"
-  }
-  else if( winwidth >= 650 ){
+
+  else if ( winwidth >= 650 )
     trans = "fade"
-  }
+
   $.mobile.defaultPageTransition = trans
-}
+

 # set default documentation
 $( document ).bind( "mobileinit", setDefaultTransition )


#460 Make sure no bucket is sent before it's merged

--- app/assets/javascripts/oxalis/model/binary/cube.coffee
+++ app/assets/javascripts/oxalis/model/binary/cube.coffee
@@ -233,6 +233,7 @@
         bucketData[i] = currentBucket[i] or bucketData[i]

     cube[bucketIndex] = bucketData
+    currentBucket?.bucketReplacedCallback?()

   setArbitraryBucketByZoomedAddress : ([bucket_x, bucket_y, bucket_z, zoomStep], bucketData) ->
@@ -365,7 +366,13 @@
       for i in [0...@BYTE_OFFSET]
         bucket[voxelIndex + i] = (label >> (i * 8) ) & 0xff

-      @pushQueue.insert(@positionToZoomedAddress(voxel))
+      # Make sure temporal buckets are not saved before merged
+      if bucket.temporal
+        bucket.bucketReplacedCallback = =>
+          @pushQueue.insert(@positionToZoomedAddress(voxel))
+          @pushQueue.push()
+      else
+        @pushQueue.insert(@positionToZoomedAddress(voxel))

   getDataValue : ( voxel ) ->


Fix bug

Fix bug

--- app/backbone/views/users/react_user_groups_edit.coffee
+++ app/backbone/views/users/react_user_groups_edit.coffee
@@ -3,7 +3,7 @@
   mixins: [UpdateOnSignInOrOutMixin, React.BackboneMixin('user')]

   getInitialState: ->
-    groups = new Groups
+    groups = new AllGroups
     groups.fetch()
     groups: groups


[client][userlist] always place local thumbnail first

--- src/frontend/modules/userList.coffee
+++ src/frontend/modules/userList.coffee
@@ -116,12 +116,14 @@
                     id    : "video_#{uid}"
                     class : 'thumbnail-wrapper video-wrapper user-square color-one'
                 })
-                li.addClass (if @users[uid]['isLocal'] is on then 'localLi' else 'remoteLi')
                 @addInterface li, @users[uid].name
                 if @users[uid].video?
                     li.append @users[uid].video
                     do @users[uid].video.play
-                @jqContainer.append li
+                if @users[uid].isLocal? and @users[uid].isLocal
+                    @jqContainer.prepend li
+                else
+                    @jqContainer.append li
         do @updateColumns

 if window?


fixing misnomers in the docs

fixing misnomers in the docs

--- library/exposure/publish.coffee
+++ library/exposure/publish.coffee
@@ -52,9 +52,15 @@
     # to be used for matching HTTP requests against this service.
     # Typically an HTTP pathname pattern and a domain name pattern.
     # Try not to put constraints on the domain, unless necessary.
-    # Also, the compounds for the composition system belong here.
+    # Also, other similar and related directives belong in here.
     @resource "/api/publish"
     @documentation yes
+
+    # These declarations below are implantations of the abstracted
+    # components by the means of the dynamic recomposition system.
+    # Please take a look at the `Composition` class implementation
+    # for all sorts of information on the composition system itself.
+    # Each of these will be dynamicall integrated in class hierarchy.
     @implanted OnlySsl

     # This block describes certain method of abrbitrary service. The


Set Kind

Set Kind

--- src/config/controls.coffee
+++ src/config/controls.coffee
@@ -8,6 +8,7 @@
   stroke: "black"
   path: (world, args) -> world.get('rect_path')
   _AUTHORITY: {
+    _KIND: "Control"
     fill: my.color.button
     x: (world, args) -> world.index*my.control.spacing + my.control.margin
     y: (world, args) -> my.control.margin


Synchronize percentage for new progress bars

According to the HTML specification:

> The default value is the minimum plus half the difference between the
> minimum and the maximum, unless the maximum is less than the minimum,
> in which case the default value is the minimum.

So for new progress bars, we had a numeric value of `nil` and a range
value of `50`, meaning the input fields weren't in sync.

Manually triggering the event on the progress, while not an ideal
solution (ideally we would be able to define `0` as default), sets the
value of the numeric field.

--- app/assets/javascripts/forms.js.coffee
+++ app/assets/javascripts/forms.js.coffee
@@ -28,6 +28,8 @@
       input: ->
         $("[name='#{this.name}']").val($(this).val())

+    $("[name='progress_bar[percentage]'][type='range']").trigger("input")
+
   hideOrShowFieldsAfterSelection: ->
     $("[name='progress_bar[kind]']").on
       change: ->


fix upload progress

fix upload progress

--- app/assets/javascripts/views.js.coffee
+++ app/assets/javascripts/views.js.coffee
@@ -48,7 +48,7 @@
         accept: 'application/json',
         autoclear: true,
         progress: (event, completed, loaded, total) ->
-          $(this).siblings('meter:first').attr('value',(completed *100).toFixed(2))
+          $(this).find('meter:first').attr('value',(completed *100).toFixed(2))
         , complete: (event, responseText, status) ->
           if status is 201
             $.blabbrNotify 'success', 'Smiley added !'


Refactor context code

Refactor context code

--- src/context/context.coffee
+++ src/context/context.coffee
@@ -5,7 +5,10 @@
     @_isInitialized = false
     @_isDestroyed = false
     @_params = @_eventric.get()
-    @_di = {}
+    @_di =
+      $query: => @query.apply @, arguments
+      $projectionStore: => @getProjectionStore.apply @, arguments
+      $emitDomainEvent: => @emitDomainEvent.apply @, arguments
     @_aggregateClasses = {}
     @_commandHandlers = {}
     @_queryHandlers = {}
@@ -66,12 +69,12 @@

   subscribeToAllDomainEvents: (handlerFn) ->
-    domainEventHandler = () => handlerFn.apply @_di, arguments
+    domainEventHandler = => handlerFn.apply @_di, arguments
     @_eventBus.subscribeToAllDomainEvents domainEventHandler

   subscribeToDomainEvent: (domainEventName, handlerFn) ->
-    domainEventHandler = () => handlerFn.apply @_di, arguments
+    domainEventHandler = => handlerFn.apply @_di, arguments
     @_eventBus.subscribeToDomainEvent domainEventName, domainEventHandler

@@ -109,12 +112,6 @@
     @_initializeStores()
     .then =>
       @log.debug "[#{@name}] Finished initializing Store"
-      @_di =
-        $query: => @query.apply @, arguments
-        $projectionStore: => @getProjectionStore.apply @, arguments
-        $emitDomainEvent: => @emitDomainEvent.apply @, arguments
-
-    .then =>
       @log.debug "[#{@name}] Initializing Projections"
       @_initializeProjections()
     .then =>
@@ -146,20 +143,13 @@

   _initializeProjections: ->
     initializeProjectionsPromise = Promise.resolve()
-    projections = []
     for projectionName, ProjectionClass of @_projectionClasses
-      projections.push
-        name: projectionName
-        class: ProjectionClass
-
-    projections.forEach (projection) =>
-      eventNames = null
-      @log.debug "[#{@name}] Initializing Projection #{projection.name}"
+      @log.debug "[#{@name}] Initializing Projection #{projectionName}"

       initializeProjectionsPromise = initializeProjectionsPromise.then =>
-        @projectionService.initializeInstance projection.name, projection.class, {}
+        @projectionService.initializeInstance projectionName, ProjectionClass, {}
       .then (projectionId) =>
-        @log.debug "[#{@name}] Finished initializing Projection #{projection.name}"
+        @log.debug "[#{@name}] Finished initializing Projection #{projectionName}"

     return initializeProjectionsPromise


fix bug dragging from sprint to backlog

fix bug dragging from sprint to backlog

--- app/coffee/modules/backlog/main.coffee
+++ app/coffee/modules/backlog/main.coffee
@@ -479,6 +479,7 @@

         # refresh order
         @scope.userstories = _.sortBy @scope.userstories, (it) => @.backlogOrder[it.id]
+        @scope.visibleUserStories = _.map @scope.userstories, (it) -> return it.ref

         for sprint in @scope.sprints
             sprint.user_stories = _.sortBy sprint.user_stories, (it) => @.milestonesOrder[sprint.id][it.id]


tests: Attempt NoFlo 0.6.x compat for RemoteRuntimes

--- spec/RemoteRuntimes.coffee
+++ spec/RemoteRuntimes.coffee
@@ -123,7 +123,8 @@
             c.runtime.removeListener 'execution', checkRunning
             return done()
         c.runtime.on 'execution', checkRunning
-        noflo.graph.loadFBP forward, (graph) ->
+        noflo.graph.loadFBP forward, (err, graph) ->
+          graph = err if not graph # NoFlo <0.6 compat
           c.runtime.setMain graph # XXX: neccesary/correct?
           connection.sendGraph graph, c.runtime, () ->
             c.runtime.start() # does actual upload, MicroFlo specific
@@ -207,7 +208,8 @@
             c.runtime.removeListener 'execution', checkRunning
             return done()
         c.runtime.on 'execution', checkRunning
-        noflo.graph.loadFBP echoNoflo, (graph) ->
+        noflo.graph.loadFBP echoNoflo, (err, graph) ->
+          graph = err if not graph # NoFlo <0.6 compat
           graph.setProperties { id: 'echoNoflo', main: true }
           c.setGraph graph, ->
             c.runtime.start()


Add specs for find-view config settings

Add specs for find-view config settings

--- spec/find-view-spec.coffee
+++ spec/find-view-spec.coffee
@@ -73,6 +73,21 @@
       runs ->
         expect(atom.workspaceView.find('.find-and-replace')).toExist()
         expect(findView.findEditor.getText()).toBe('')
+
+    it "honors config settings for find options", ->
+      atom.config.set('find-and-replace.useRegex', true)
+      atom.config.set('find-and-replace.caseSensitive', true)
+      atom.config.set('find-and-replace.inCurrentSelection', true)
+
+      editorView.trigger 'find-and-replace:show'
+
+      waitsForPromise ->
+        activationPromise
+
+      runs ->
+        expect(findView.caseOptionButton).toHaveClass 'selected'
+        expect(findView.regexOptionButton).toHaveClass 'selected'
+        expect(findView.selectionOptionButton).toHaveClass 'selected'

   describe "when find-and-replace:toggle is triggered", ->
     it "toggles the visibility of the FindView", ->


#458 Add specs testing that the regex grammar is properly set.

--- spec/find-view-spec.coffee
+++ spec/find-view-spec.coffee
@@ -1243,3 +1243,26 @@
       atom.commands.dispatch findView.replaceEditor.element, 'find-and-replace:focus-next'
       expect(findView.findEditor).toHaveClass('is-focused')
       expect(findView.replaceEditor).not.toHaveClass('is-focused')
+
+  describe "when language-javascript is also active", ->
+    beforeEach ->
+      waitsForPromise ->
+        atom.packages.activatePackage("language-javascript")
+
+      atom.commands.dispatch editorView, 'find-and-replace:show'
+      waitsForPromise -> activationPromise
+
+    it "does not use regexp grammar when in non-regex mode", ->
+      expect(findView.model.getFindOptions().useRegex).not.toBe true
+      expect(findView.findEditor.getModel().getGrammar().scopeName).toBe 'text.plain.null-grammar'
+
+    it "uses regexp grammar when in regex mode and clears the regexp grammar when regex is disabled", ->
+      atom.commands.dispatch(findView.findEditor.element, 'find-and-replace:toggle-regex-option')
+
+      expect(findView.model.getFindOptions().useRegex).toBe true
+      expect(findView.findEditor.getModel().getGrammar().scopeName).toBe 'source.js.regexp'
+
+      atom.commands.dispatch(findView.findEditor.element, 'find-and-replace:toggle-regex-option')
+
+      expect(findView.model.getFindOptions().useRegex).not.toBe true
+      expect(findView.findEditor.getModel().getGrammar().scopeName).toBe 'text.plain.null-grammar'


:bug: Fix broken test cases

:bug: Fix broken test cases

These test cases were not running due to indentation errors.
The tests for commands 'dj' and 'dk' now correctly fail.

--- spec/operators-spec.coffee
+++ spec/operators-spec.coffee
@@ -397,55 +397,61 @@

     describe "when followed by an j", ->
       beforeEach ->
-        originalText = "12345\nabcde\nABCDE"
-        editor.setText(originalText)
-
-        describe "on the beginning of the file", ->
+        @originalText = "12345\nabcde\nABCDE"
+        editor.setText(@originalText)
+
+      describe "on the beginning of the file", ->
+        beforeEach ->
           editor.setCursorScreenPosition([0, 0])
-          it "deletes the next two lines", ->
-            keydown('d')
-            keydown('j')
-            expect(editor.getText()).toBe("ABCDE")
-
-        describe "on the end of the file", ->
-          editor.setCursorScreenPosition([4,2])
-          it "deletes nothing", ->
-            keydown('d')
-            keydown('j')
-            expect(editor.getText()).toBe(originalText)
-
-        describe "on the middle of second line", ->
-          editor.setCursorScreenPosition([2,1])
-          it "deletes the last two lines", ->
-            keydown('d')
-            keydown('j')
-            expect(editor.getText()).toBe("12345")
+        it "deletes the next two lines", ->
+          keydown('d')
+          keydown('j')
+          expect(editor.getText()).toBe("ABCDE")
+
+      describe "on the end of the file", ->
+        beforeEach ->
+          editor.setCursorScreenPosition([2, 4])
+        it "deletes nothing", ->
+          keydown('d')
+          keydown('j')
+          expect(editor.getText()).toBe(@originalText)
+
+      describe "on the middle of second line", ->
+        beforeEach ->
+          editor.setCursorScreenPosition([1,2])
+        it "deletes the last two lines", ->
+          keydown('d')
+          keydown('j')
+          expect(editor.getText()).toBe("12345")

     describe "when followed by an k", ->
       beforeEach ->
-        originalText = "12345\nabcde\nABCDE"
-        editor.setText(originalText)
-
-        describe "on the end of the file", ->
-          editor.setCursorScreenPosition([4, 2])
-          it "deletes the bottom two lines", ->
-            keydown('d')
-            keydown('k')
-            expect(editor.getText()).toBe("ABCDE")
-
-        describe "on the beginning of the file", ->
+        @originalText = "12345\nabcde\nABCDE"
+        editor.setText(@originalText)
+
+      describe "on the end of the file", ->
+        beforeEach ->
+          editor.setCursorScreenPosition([2,4])
+        it "deletes the bottom two lines", ->
+          keydown('d')
+          keydown('k')
+          expect(editor.getText()).toBe("ABCDE")
+
+      describe "on the beginning of the file", ->
+        beforeEach ->
           editor.setCursorScreenPosition([0,0])
-          it "deletes nothing", ->
-            keydown('d')
-            keydown('k')
-            expect(editor.getText()).toBe(originalText)
-
-        describe "when on the middle of second line", ->
-          editor.setCursorScreenPosition([2,1])
-          it "deletes the first two lines", ->
-            keydown('d')
-            keydown('k')
-            expect(editor.getText()).toBe("12345")
+        it "deletes nothing", ->
+          keydown('d')
+          keydown('k')
+          expect(editor.getText()).toBe(@originalText)
+
+      describe "when on the middle of second line", ->
+        beforeEach ->
+          editor.setCursorScreenPosition([1,2])
+        it "deletes the first two lines", ->
+          keydown('d')
+          keydown('k')
+          expect(editor.getText()).toBe("12345")

     describe "when followed by a G", ->
       beforeEach ->
@@ -771,11 +777,11 @@
         keydown('y')
         keydown('h')

-        it "saves the left letter to the default register", ->
-          expect(vimState.getRegister('"').text).toBe " "
-
-        it "moves the cursor position to the left", ->
-          expect(editor.getCursorScreenPosition()).toEqual [0, 3]
+      it "saves the left letter to the default register", ->
+        expect(vimState.getRegister('"').text).toBe " "
+
+      it "moves the cursor position to the left", ->
+        expect(editor.getCursorScreenPosition()).toEqual [0, 3]

     describe "with a down motion", ->
       beforeEach ->


one more test

one more test

--- spec/specs/holdwhen.coffee
+++ spec/specs/holdwhen.coffee
@@ -45,24 +45,30 @@
         source.holdWhen(flag).fold(0, ((x,y) -> x+1), { eager: true})
       [count-1])

-  describe "Works with Bacon.once()", ->
+  describe "Works with synchronous sources", ->
     expectStreamTimings(
       ->
         Bacon.once("2").
           holdWhen(Bacon.later(1000, false).toProperty(true))
       [[1000, "2"]])
-  describe "Works with Bacon.once(), case 2", ->
+  describe "Works with synchronous sources, case 2", ->
     expectStreamTimings(
       ->
         Bacon.once(2).
           holdWhen(Bacon.once(true))
       [])
-  describe "Works with Bacon.once(), case 3", ->
+  describe "Works with synchronous sources, case 3", ->
     expectStreamTimings(
       ->
         Bacon.once("2").
           holdWhen(Bacon.constant(false))
       [[0, "2"]])
+  describe "Works with synchronous sources, case 4", ->
+    expectStreamTimings(
+      ->
+        Bacon.fromArray([new Bacon.Error(), "2"]).
+          holdWhen(later(20, false).startWith(true))
+      [error(), [20, "2"]])
   it "toString", ->
     expect(Bacon.once(1).holdWhen(Bacon.constant(true)).toString()).to.equal(
       "Bacon.once(1).holdWhen(Bacon.constant(true))")


:art:

:art:

--- spec/tree-view-spec.coffee
+++ spec/tree-view-spec.coffee
@@ -225,7 +225,7 @@
     it "restores the focus state of the tree view", ->
       jasmine.attachToDOM(workspaceElement)
       treeView.focus()
-      expect(treeView.element.querySelector('.tree-view')).toHaveFocus()
+      expect(treeView.list).toMatchSelector(":focus")
       atom.packages.deactivatePackage("tree-view")

       waitsForPromise ->
@@ -233,7 +233,7 @@

       runs ->
         treeView = atom.workspace.getLeftPanels()[0].getItem()
-        expect(treeView.element.querySelector('.tree-view')).toHaveFocus()
+        expect(treeView.list).toMatchSelector(":focus")

     it "restores the scroll top when toggled", ->
       workspaceElement.style.height = '5px'
@@ -294,7 +294,7 @@
         treeView.detach()
         atom.commands.dispatch(workspaceElement, 'tree-view:toggle')
         expect(treeView.hasParent()).toBeTruthy()
-        expect(treeView.element.querySelector('.tree-view')).toHaveFocus()
+        expect(treeView.list).toMatchSelector(":focus")

     describe "when tree-view:toggle-side is triggered on the root view", ->
       describe "when the tree view is on the left", ->
@@ -330,7 +330,7 @@
         treeView.detach()
         atom.commands.dispatch(workspaceElement, 'tree-view:toggle-focus')
         expect(treeView.hasParent()).toBeTruthy()
-        expect(treeView.element.querySelector('.tree-view')).toHaveFocus()
+        expect(treeView.list).toMatchSelector(":focus")

     describe "when the tree view is shown", ->
       it "focuses the tree view", ->
@@ -342,7 +342,7 @@
           expect(treeView).toBeVisible()
           atom.commands.dispatch(workspaceElement, 'tree-view:toggle-focus')
           expect(treeView).toBeVisible()
-          expect(treeView.element.querySelector('.tree-view')).toHaveFocus()
+          expect(treeView.list).toMatchSelector(":focus")

       describe "when the tree view is focused", ->
         it "unfocuses the tree view", ->
@@ -354,7 +354,7 @@
             expect(treeView).toBeVisible()
             atom.commands.dispatch(workspaceElement, 'tree-view:toggle-focus')
             expect(treeView).toBeVisible()
-            expect(treeView.element.querySelector('.tree-view')).not.toHaveFocus()
+            expect(treeView.list).not.toMatchSelector(":focus")

   describe "when tree-view:reveal-active-file is triggered on the root view", ->
     beforeEach ->
@@ -464,10 +464,10 @@
       runs ->
         jasmine.attachToDOM(workspaceElement)
         treeView.focus()
-        expect(treeView.element.querySelector('.tree-view')).toHaveFocus()
+        expect(treeView.list).toMatchSelector(":focus")
         atom.commands.dispatch(treeView.element, 'tool-panel:unfocus')
         expect(treeView).toBeVisible()
-        expect(treeView.element.querySelector('.tree-view')).not.toHaveFocus()
+        expect(treeView.list).not.toMatchSelector(":focus")
         expect(atom.workspace.getActivePane().isActive()).toBe(true)

   describe "copy path commands", ->
@@ -2132,7 +2132,7 @@
           it "removes the dialog and focuses the tree view", ->
             atom.commands.dispatch moveDialog.element, 'core:cancel'
             expect(atom.workspace.getModalPanels().length).toBe 0
-            expect(treeView.find(".tree-view")).toHaveFocus()
+            expect(treeView.querySelector(".tree-view")).toHaveFocus()

         describe "when the move dialog's editor loses focus", ->
           it "removes the dialog and focuses root view", ->
@@ -2250,7 +2250,7 @@
             jasmine.attachToDOM(treeView.element)
             atom.commands.dispatch copyDialog.element, 'core:cancel'
             expect(atom.workspace.getModalPanels().length).toBe 0
-            expect(treeView.find(".tree-view")).toHaveFocus()
+            expect(treeView.querySelector(".tree-view")).toHaveFocus()

         describe "when the duplicate dialog's editor loses focus", ->
           it "removes the dialog and focuses root view", ->
@@ -2836,9 +2836,9 @@
       it 'switches the contextual menu to muli-select mode', ->
         fileView1.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1}))
         fileView2.dispatchEvent(new MouseEvent('mousedown', {bubbles: true, shiftKey: true}))
-        expect(treeView.element.querySelector('.tree-view')).toHaveClass('multi-select')
+        expect(treeView.list).toHaveClass('multi-select')
         fileView3.dispatchEvent(new MouseEvent('mousedown', {bubbles: true}))
-        expect(treeView.element.querySelector('.tree-view')).toHaveClass('full-menu')
+        expect(treeView.list).toHaveClass('full-menu')

     describe 'selecting multiple items', ->
       it 'switches the contextual menu to muli-select mode', ->
@@ -2904,16 +2904,16 @@
             it 'displays the full contextual menu', ->
               fileView1.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1}))
               fileView3.dispatchEvent(new MouseEvent('mousedown', {bubbles: true, ctrlKey: true}))
-              expect(treeView.element.querySelector('.tree-view')).toHaveClass('full-menu')
-              expect(treeView.element.querySelector('.tree-view')).not.toHaveClass('multi-select')
+              expect(treeView.list).toHaveClass('full-menu')
+              expect(treeView.list).not.toHaveClass('multi-select')

           describe 'previous item is selected including the ctrl clicked', ->
             it 'displays the multi-select menu', ->
               fileView1.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1}))
               fileView3.dispatchEvent(new MouseEvent('mousedown', {bubbles: true, metaKey: true}))
               fileView3.dispatchEvent(new MouseEvent('mousedown', {bubbles: true, ctrlKey: true}))
-              expect(treeView.element.querySelector('.tree-view')).not.toHaveClass('full-menu')
-              expect(treeView.element.querySelector('.tree-view')).toHaveClass('multi-select')
+              expect(treeView.list).not.toHaveClass('full-menu')
+              expect(treeView.list).toHaveClass('multi-select')

             it 'does not deselect any of the items', ->
               fileView1.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1}))
@@ -2926,8 +2926,8 @@
             it 'displays the full contextual menu', ->
               fileView1.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1}))
               fileView3.dispatchEvent(new MouseEvent('mousedown', {bubbles: true, ctrlKey: true}))
-              expect(treeView.element.querySelector('.tree-view')).toHaveClass('full-menu')
-              expect(treeView.element.querySelector('.tree-view')).not.toHaveClass('multi-select')
+              expect(treeView.list).toHaveClass('full-menu')
+              expect(treeView.list).not.toHaveClass('multi-select')

           describe 'when no item is selected', ->
             it 'selects the ctrl clicked item', ->
@@ -2936,8 +2936,8 @@

             it 'displays the full context menu', ->
               fileView3.dispatchEvent(new MouseEvent('mousedown', {bubbles: true, ctrlKey: true}))
-              expect(treeView.element.querySelector('.tree-view')).toHaveClass('full-menu')
-              expect(treeView.element.querySelector('.tree-view')).not.toHaveClass('multi-select')
+              expect(treeView.list).toHaveClass('full-menu')
+              expect(treeView.list).not.toHaveClass('multi-select')

         describe "right-clicking", ->
           describe 'when multiple items are selected', ->
@@ -2947,15 +2947,15 @@
               fileView3.dispatchEvent(new MouseEvent('mousedown', {bubbles: true, button: 2}))
               expect(fileView1).toHaveClass('selected')
               expect(fileView3).toHaveClass('selected')
-              expect(treeView.element.querySelector('.tree-view')).not.toHaveClass('full-menu')
-              expect(treeView.element.querySelector('.tree-view')).toHaveClass('multi-select')
+              expect(treeView.list).not.toHaveClass('full-menu')
+              expect(treeView.list).toHaveClass('multi-select')

           describe 'when a single item is selected', ->
             it 'displays the full context menu', ->
               fileView1.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1}))
               fileView3.dispatchEvent(new MouseEvent('mousedown', {bubbles: true, button: 2}))
-              expect(treeView.element.querySelector('.tree-view')).toHaveClass('full-menu')
-              expect(treeView.element.querySelector('.tree-view')).not.toHaveClass('multi-select')
+              expect(treeView.list).toHaveClass('full-menu')
+              expect(treeView.list).not.toHaveClass('multi-select')

             it 'selects right clicked item', ->
               fileView1.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1}))
@@ -2975,8 +2975,8 @@
             it 'shows the full context menu', ->
               fileView3.dispatchEvent(new MouseEvent('mousedown', {bubbles: true, button: 2}))
               expect(fileView3).toHaveClass('selected')
-              expect(treeView.element.querySelector('.tree-view')).toHaveClass('full-menu')
-              expect(treeView.element.querySelector('.tree-view')).not.toHaveClass('multi-select')
+              expect(treeView.list).toHaveClass('full-menu')
+              expect(treeView.list).not.toHaveClass('multi-select')

   describe "the sortFoldersBeforeFiles config option", ->
     [dirView, fileView, dirView2, fileView2, fileView3, rootDirPath, dirPath, filePath, dirPath2, filePath2, filePath3] = []
@@ -3337,7 +3337,7 @@
         runs ->
           expect(sampleJs).toHaveClass 'selected'
           expect(atom.workspace.getActivePaneItem().getPath()).toBe atom.project.getDirectories()[0].resolve('tree-view.js')
-          expect(treeView.element.querySelector('.tree-view')).toHaveFocus()
+          expect(treeView.list).toMatchSelector(":focus")

         waitsForFileToOpen ->
           sampleTxt.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1}))
@@ -3346,7 +3346,7 @@
           expect(sampleTxt).toHaveClass 'selected'
           expect(treeView.element.querySelectorAll('.selected').length).toBe 1
           expect(atom.workspace.getActivePaneItem().getPath()).toBe atom.project.getDirectories()[0].resolve('tree-view.txt')
-          expect(treeView.element.querySelector('.tree-view')).toHaveFocus()
+          expect(treeView.list).toMatchSelector(":focus")

     describe "opening existing opened files in existing split panes", ->
       beforeEach ->
@@ -3422,7 +3422,7 @@
             expect(treeView).toHaveFocus()

           it "doesn't open the file in the active pane", ->
-            expect(atom.views.getView(treeView)).toHaveFocus()
+            expect(atom.views.getView(treeView).element).toHaveFocus()
             expect(activePaneItem.getPath()).toBe atom.project.getDirectories()[0].resolve('tree-view.js')

       describe "when a file is double-clicked", ->


Improved error handling when serializing and deserializing

--- src/events/eventmap.coffee
+++ src/events/eventmap.coffee
@@ -8,15 +8,26 @@
       @validEvents = []

     serialize: ->
-      JSON.stringify @events, (key, value) ->
-        value = value.toString() if typeof value is 'function'
-        value
+      try
+        result = JSON.stringify @events, (key, value) ->
+          value = value.toString() if typeof value is 'function'
+          value
+      catch err
+        console.error "Error while serializing eventmap: #{err}"
+      result

     deserialize: (string) ->
-      @events = JSON.parse string, (key, value) ->
-        if value.indexOf('function') is 0
-          value = new Function(value)()
-        value
+      try
+        events = JSON.parse string, (key, value) ->
+          if value.indexOf('function') is 0
+            value = new Function(value)()
+          value
+      catch err
+        console.error "Error while deserializing eventmap: #{err}"
+        return false
+
+      @events = events
+      true

     on: (eventName, eventFunction) ->
       return unless eventFunction


make more browserwindow resize binding comment more specific

--- src/coffee/breadcrumbsEatr.coffee
+++ src/coffee/breadcrumbsEatr.coffee
@@ -349,7 +349,7 @@
                             # trigger the custom event named hide on the dropdownWrapper element
                             o.dropdownWrapper.trigger "hide.#{ pluginName }"

-            # bind resize event to the window
+            # bind main resize logic to the resize event of the window object
             o.browserWindow.on o.resizeKey, o.resize

             # create deferred for triggering onAfterLoad callback


Kontrol: createKite: allow to override transportOptions for individual kite instance creation

--- src/kontrol/kontrol.coffee
+++ src/kontrol/kontrol.coffee
@@ -42,7 +42,10 @@

     return

-  createKite: ({ kite: kiteDescriptor, token, url }) ->
+  createKite: ({ kite: kiteDescriptor, token, url, transportOptions }) ->
+
+    transportOptions ?= @options.transportOptions
+
     kite = new @constructor.Kite
       username         : kiteDescriptor.username
       environment      : kiteDescriptor.environment
@@ -56,7 +59,7 @@
         type           : 'token'
         key            : token
       transportClass   : @options.transportClass
-      transportOptions : @options.transportOptions
+      transportOptions : transportOptions
     .on 'tokenExpired', =>
       @renewToken kite, kiteDescriptor


name: explicitly call JName.one (rather than @one)

New relic magic was causing the web server to fail here.  Since we don't expect to subclass JName, this is an almost-ok solution.  We need to watch out for new relic causing us other problems via this function reapplication.  I couldn't manage to create a trivial failing case, so I'll wait to open a bug report for now.

--- workers/social/lib/social/models/name.coffee
+++ workers/social/lib/social/models/name.coffee
@@ -119,7 +119,7 @@

     fetchModels = (name, callback)->
       if 'string' is typeof name
-        @one {name}, (err, nameObj)->
+        JName.one {name}, (err, nameObj)->
           if err then callback err
           else if nameObj?
             fetchByNameObject nameObj, callback


Allow horizontal scrolling

Allow horizontal scrolling

--- src/lines-component.coffee
+++ src/lines-component.coffee
@@ -12,14 +12,15 @@

   render: ->
     if @isMounted()
-      {editor, renderedRowRange, scrollTop, lineHeight, showIndentGuide} = @props
+      {editor, renderedRowRange, scrollTop, scrollLeft, lineHeight, showIndentGuide} = @props
       [startRow, endRow] = renderedRowRange
-      scrollOffset = -scrollTop % lineHeight
+      verticalScrollOffset = -scrollTop % lineHeight
+      horizontalScrollOffset = -scrollLeft

       lines =
         for tokenizedLine, index in editor.linesForScreenRows(startRow, endRow - 1)
           screenRow = startRow + index
-          LineComponent({key: tokenizedLine.id, tokenizedLine, showIndentGuide, lineHeight, index, scrollOffset, screenRow})
+          LineComponent({key: tokenizedLine.id, tokenizedLine, showIndentGuide, lineHeight, index, verticalScrollOffset, horizontalScrollOffset, screenRow})

     div {className: 'lines'}, lines

@@ -105,10 +106,11 @@
   displayName: 'LineComponent'

   render: ->
-    {index, screenRow, scrollOffset, lineHeight} = @props
+    {index, screenRow, verticalScrollOffset, horizontalScrollOffset, lineHeight} = @props

-    top = index * lineHeight + scrollOffset
-    style = WebkitTransform: "translate3d(0px, #{top}px, 0px)"
+    top = index * lineHeight + verticalScrollOffset
+    left = horizontalScrollOffset
+    style = WebkitTransform: "translate3d(#{left}px, #{top}px, 0px)"

     div className: 'line', style: style, 'data-screen-row': screenRow, dangerouslySetInnerHTML: {__html: @buildInnerHTML()}

@@ -138,4 +140,4 @@
       "<span>#{scopeTree.getValueAsHtml({hasIndentGuide: @props.showIndentGuide})}</span>"

   shouldComponentUpdate: (newProps) ->
-    not isEqualForProperties(newProps, @props, 'showIndentGuide', 'lineHeight', 'screenRow', 'index', 'scrollOffset')
+    not isEqualForProperties(newProps, @props, 'showIndentGuide', 'lineHeight', 'screenRow', 'index', 'verticalScrollOffset', 'horizontalScrollOffset')


AdminModal: Display defaultChannels in textbox.

--- client/admin/lib/views/groupgeneralsettingsview.coffee
+++ client/admin/lib/views/groupgeneralsettingsview.coffee
@@ -111,6 +111,7 @@
       description  : 'Your new members will automatically join to <b>#general</b> channel. Here you can specify more channels for new team members to join automatically.'
       name         : 'channels'
       placeholder  : 'product, design, ux, random etc'
+      defaultValue : Encoder.htmlDecode group.defaultChannels.join(', ') ? ''
       nextElement  : new KDCustomHTMLView
         cssClass   : 'warning-text'
         tagName    : 'span'


AdminModal: Remove unused requires.

AdminModal: Remove unused requires.

--- client/admin/lib/views/members/adminmembersview.coffee
+++ client/admin/lib/views/members/adminmembersview.coffee
@@ -1,12 +1,8 @@
 kd                    = require 'kd'
-remote                = require('app/remote').getInstance()
 KDView                = kd.View
 KDTabView             = kd.TabView
 KDTabPaneView         = kd.TabPaneView
 TeamMembersView       = require './teammembersview.coffee'
-KDCustomHTMLView      = kd.CustomHTMLView
-KDHitEnterInputView   = kd.HitEnterInputView
-KDListViewController  = kd.ListViewController

 module.exports = class AdminMembersView extends KDView


Updates relevant to new gem version

Updates relevant to new gem version

jasmine changed internally, so spec filter no longer works. removed

--- support/spec_helpers.coffee
+++ support/spec_helpers.coffee
@@ -1,7 +1,5 @@
 #= require lib/logger

-# a number of jasmine-jquery helpers / extensions
-# it totally used to do this natively, but now doesn't...

 LOGGING_TYPE="silent"
 window.logger = switch LOGGING_TYPE
@@ -10,22 +8,6 @@
   else window.ttm.Logger.buildProduction(stringify_objects: false)
 class_mixer = ttm.class_mixer

-class RegexpSpecFilter
-  initialize: (@regexp)->
-  forSpec: (spec)->
-    spec.getFullName().toLowerCase().match @regexp
-
-class_mixer RegexpSpecFilter
-
-override_spec_filter_with = RegexpSpecFilter.build(/opening/)
-override_spec_filter_with = RegexpSpecFilter.build(/wip/)
-override_spec_filter_with = RegexpSpecFilter.build(/calc/)
-override_spec_filter_with = false
-
-if override_spec_filter_with
-  env = jasmine.getEnv()
-  env.specFilter = (spec)->
-    override_spec_filter_with.forSpec spec

 jasmine.getFixtures().findContainer = ->
   $(document.body).find("##{@containerId}")


Provide a sample code for sample app

Provide a sample code for sample app

--- client/landing/app/MainApp/kodingappscontroller.coffee
+++ client/landing/app/MainApp/kodingappscontroller.coffee
@@ -624,7 +624,22 @@
             method      : "uploadFile"
             withArgs    :
               path      : escapeFilePath "#{fsFolder.path}/index.coffee"
-              contents  : "do ->"
+              contents  : if not isBlank then """
+
+                            {KDView} = KD.classes
+
+                            do ->
+                              console.log "Hello World!"
+
+                              appView.addSubView aLabel = new KDView
+                                partial: "<marquee>
Hello Koding!
</marquee>"
+
+                              aLabel.setRandomBG()
+
+                          """ else """
+                            do->
+
+                          """
           , cb

         stack.push (cb)=>


Fix newAppModal create logic

Fix newAppModal create logic

--- client/landing/app/MainApp/kodingappscontroller.coffee
+++ client/landing/app/MainApp/kodingappscontroller.coffee
@@ -562,17 +562,18 @@
                   appPath     = getAppPath manifest

                   FSItem.doesExist appPath, (err, exists)=>
-                    newAppModal.modalTabs.forms.form.buttons.Create.hideLoader()
                     if exists
+                      newAppModal.modalTabs.forms.form.buttons.Create.hideLoader()
                       new KDNotificationView
                         type      : "mini"
                         cssClass  : "error"
                         title     : "App folder with that name is already exists, please choose a new name."
                         duration  : 3000
                     else
-                      @prepareApplication {isBlank : type is "blank", name}, (err, response)->
+                      @prepareApplication {isBlank : type is "blank", name}, (err, response)=>
                         callback? err
-                      newAppModal.destroy()
+                        newAppModal.modalTabs.forms.form.buttons.Create.hideLoader()
+                        newAppModal.destroy()
             fields                :
               type                :
                 label             : "Type"


Do not add all icons that we not provided in resources

--- client/landing/app/MainApp/kodingappscontroller.coffee
+++ client/landing/app/MainApp/kodingappscontroller.coffee
@@ -26,15 +26,9 @@
             # post    : ""
       options       :
         type        : "tab"
-
-    if type is "sample"
-      raw.icns      =
-        "64"        : "./resources/icon.64.png"
+      icns          :
         "128"       : "./resources/icon.128.png"
-        "160"       : "./resources/icon.160.png"
-        "256"       : "./resources/icon.256.png"
-        "512"       : "./resources/icon.512.png"
-      raw.source.stylesheets = [ "./resources/style.css" ]
+      stylesheets   : [ "./resources/style.css" ]

     json = JSON.stringify raw, null, 2


Readded search box, same functionality as before but not changing ace source


fix using programs as actions

fix using programs as actions

--- src/layout/mixins/programs.coffee
+++ src/layout/mixins/programs.coffee
@@ -73,7 +73,7 @@

     perform: (world, key) ->
       contents = sprite.get('actions').get(key)
-      return fetch_program(key) unless _.isString contents
+      return world.call('fetch_program', key) unless _.isString contents
       world.call 'perform_instruction', contents

     perform_instruction: (world, contents) ->


hopefully fix #4353 -- "mouse_out_x" → Object must have project_id attribute

--- src/smc-webapp/project/file-tab.cjsx
+++ src/smc-webapp/project/file-tab.cjsx
@@ -142,7 +142,7 @@
         body =

                     {<Icon
-                        onMouseOver = {@mouse_over_x} onMouseOut={@mouse_out_x}
+                        onMouseOver = {=>@mouse_over_x()} onMouseOut={=>@mouse_out_x()}
                         name        = 'times'
                         onClick     = {(e)=>@close_file(e, misc.tab_to_path(@props.name))}
                     /> if @props.file_tab}


removed the apostrophies on the watch parameters because they annoy me

--- src/main/quadcopter.coffee
+++ src/main/quadcopter.coffee
@@ -4,9 +4,9 @@

   fly: ->
     watch
-      'name': 'throttle'
-      'pin': 56
-      'onChange': (throttle) -> throttleRepository.save(throttle)
+      name: 'throttle'
+      pin: 56
+      onChange: (throttle) -> throttleRepository.save(throttle)

     scheduler.every(2).execute(adjustThrottles)


Fix unit tests with logger.warn stub

Fix unit tests with logger.warn stub

--- services/track-changes/test/unit/coffee/DiffManager/DiffManagerTests.coffee
+++ services/track-changes/test/unit/coffee/DiffManager/DiffManagerTests.coffee
@@ -8,7 +8,7 @@
 describe "DiffManager", ->
 	beforeEach ->
 		@DiffManager = SandboxedModule.require modulePath, requires:
-			"logger-sharelatex": @logger = { log: sinon.stub(), error: sinon.stub() }
+			"logger-sharelatex": @logger = { log: sinon.stub(), error: sinon.stub(), warn: sinon.stub() }
 			"./UpdatesManager": @UpdatesManager = {}
 			"./DocumentUpdaterManager": @DocumentUpdaterManager = {}
 			"./DiffGenerator": @DiffGenerator = {}


added a function to allow the iphone to index the editor, but also commented out the hook that causes the indexing b/c other stuff was broken

--- demo/scripts/iphone-ot.coffee
+++ demo/scripts/iphone-ot.coffee
@@ -3,7 +3,7 @@
   window.toolbar = toolbar = new Tandem.Toolbar(editor)

   toolbar.on 'update', (attributes) ->
-    $.post('/ios-message', {json: JSON.stringify(attributes)})
+    $.post('/ios-message/format-change', {json: JSON.stringify(attributes)})

   editor.getText = -> ""
@@ -27,6 +27,10 @@

   textState.applyDeltaToText = (delta, authorId) ->   # Hacky overwrite
     editor.applyDelta(delta)
+    data =
+      docId: Stypi.configs.docId
+    #$.post('/ios-message/text-init', {json: JSON.stringify(data)});
+
   textState.applyDeltaToCursors = ->
   Stypi.Presence = {
     setUsers: ->
@@ -40,4 +44,13 @@
     textState.localUpdate(delta)
     jetClient.checkRunwayReady()
   )
+
+  window.docText = ->
+    _($('iframe').contents().find('body').find('.line'))
+      .map( (line) ->
+        $(line).text()
+      )
+      .join(' ')
+      .replace(/\s+/g,' ')
 )
+


Github client patch request

Github client patch request

--- src/octonode/client.coffee
+++ src/octonode/client.coffee
@@ -168,6 +168,18 @@
       return callback(err) if err
       @errorHandle res, body, callback

+  # Github api PATCH request
+  patch: (path, content, callback) ->
+    @request @requestOptions(
+      uri: @buildUrl path
+      method: 'PATCH'
+      body: JSON.stringify content
+      headers:
+        'Content-Type': 'application/json'
+    ), (err, res, body) =>
+      return callback(err) if err
+      @errorHandle res, body, callback
+
   # Github api PUT request
   put: (path, content, callback) ->
     @request @requestOptions(


Remove old gene client file

Remove old gene client file

--- desktop/apps/gene/client.coffee
+++ desktop/apps/gene/client.coffee
@@ -1,79 +0,0 @@
-_ = require 'underscore'
-Backbone = require 'backbone'
-Gene = require '../../models/gene.coffee'
-scrollFrame = require 'scroll-frame'
-CurrentUser = require '../../models/current_user.coffee'
-blurb = require '../../components/gradient_blurb/index.coffee'
-ShareView = require '../../components/share/view.coffee'
-ArtistFillwidthList = require '../../components/artist_fillwidth_list/view.coffee'
-RelatedGenesView = require '../../components/related_links/types/gene_genes.coffee'
-{ Following, FollowButton } = require '../../components/follow_button/index.coffee'
-{ GENE, CURRENT_USER, API_URL, MODE } = sd = require('sharify').data
-{ setupFilter } = require '../../components/filter2/index.coffee'
-aggregationParams = require './aggregations.coffee'
-
-RelatedArtistsTemplate = -> require('./templates/related_artists.jade') arguments...
-
-module.exports.GeneView = class GeneView extends Backbone.View
-
-  initialize: ({ @user, @relatedArtists, @mode, @params }) ->
-    @listenTo @relatedArtists, 'sync', @renderRelatedArtists
-    @listenTo @params, 'change', => @mode.set mode: 'artworks'
-    @listenTo @mode, 'change', =>
-      @$('#gene-filter').attr 'data-state', @mode.get('mode')
-
-  renderRelatedArtists: (artists) ->
-    @$('.related-artists').html(
-      RelatedArtistsTemplate
-        artists: artists.models[...10]
-      ).addClass 'is-fade-in'
-
-    if @model.mode() is 'artist'
-      new ArtistFillwidthList(
-        collection: @relatedArtists
-        el: $('#gene-artists')
-        user: @user
-      ).fetchAndRender()
-
-module.exports.init = ->
-  gene = new Gene GENE
-  user = CurrentUser.orNull()
-
-  { params } = setupFilter
-    el: $ '#gene-filter'
-    defaultHeading: gene.displayName()
-    stuckParam: { 'gene_id': gene.id }
-    aggregations: aggregationParams
-    filterRoot: gene.href() + '/artworks'
-    includeMediumFilterInAggregation: { include_medium_filter_in_aggregation: true }
-
-  view = new GeneView
-    user: user
-    el: $ 'body'
-    model: gene
-    relatedArtists: gene.relatedArtists
-    mode: new Backbone.Model
-    params: params
-
-  view.mode.set mode: MODE
-
-  gene.fetchArtists 'related'
-
-  new ShareView el: $('.js-gene-share-buttons')
-
-  following = if user then new Following(null, kind: 'gene') else null
-  new FollowButton
-    el: $('.follow-button')
-    following: following
-    modelName: 'gene'
-    model: gene
-    context_page: "Gene page"
-  following?.syncFollows [ gene.id ]
-
-  blurb $('.js-gene-blurb'), limit: 250
-
-  new RelatedGenesView
-    el: $('.main-layout-container .related-genes')
-    id: gene.id
-
-  scrollFrame '#gene-filter-content a' unless sd.EIGEN


Fixed error by calling 'warning' on ''. Should be 'warn'. Also changed angular.element.find(...) to document.getElementById(...) as AngularJS's jqLite does not support 'find'


groups: move onboarding text to local var from instance method

--- client/app/Applications/Groups.kdapplication/AppController.coffee
+++ client/app/Applications/Groups.kdapplication/AppController.coffee
@@ -105,23 +105,22 @@
       group: @currentGroup ? 'koding', user: account.profile.nickname
     }

-  onboardingText:->
-
-    """
-
Koding groups is a simple way to connect and interact with people who share
-      your interests.

-
-
When you join a group such as your univeristy or your company, you can share virtual
-      machines, collaborate on projects and stay up to date on the activites of others in your
-      group.

-
-
Easy to get started

-
-
Groups are free to create. You decide who can join, what actions they can do inside the
-      group and what they see.

-    """
-
   createFeed:(view, loadFeed = no)->
+
+    onboardingText =
+      """
+
Koding groups is a simple way to connect and interact with people who share
+        your interests.

+
+
When you join a group such as your univeristy or your company, you can share virtual
+        machines, collaborate on projects and stay up to date on the activites of others in your
+        group.

+
+
Easy to get started

+
+
Groups are free to create. You decide who can join, what actions they can do inside the
+        group and what they see.

+      """

     KD.getSingleton("appManager").tell 'Feeder', 'createContentFeedController', {
       itemClass             : @listItemClass
@@ -134,8 +133,8 @@
           title             : "
Groups are the basic unit of Koding society.
"
           placement         : "above"
       onboarding            :
-        everything          : @onboardingText()
-        mine                : @onboardingText()
+        everything          : onboardingText
+        mine                : onboardingText
       filter                :
         everything          :
           title             : "All groups"


component-lab: minor styling improvements

--- client/component-lab/SubscriptionHeader/SubscriptionHeader.coffee
+++ client/component-lab/SubscriptionHeader/SubscriptionHeader.coffee
@@ -8,6 +8,7 @@
 Box = require 'lab/Box'
 Label = require 'lab/Text/Label'
 textStyles = require 'lab/Text/Text.stylus'
+

 module.exports = class SubscriptionHeader extends Component


Added renderers

Added renderers

--- rest_controller.coffee
+++ rest_controller.coffee
@@ -1,6 +1,7 @@
 util = require 'util'
 _ = require 'underscore'
 Utils = require 'backbone-orm/lib/utils'
+JSONUtils = require 'backbone-orm/lib/json_utils'

 module.exports = class RESTController

@@ -22,8 +23,17 @@
     try
       cursor = @model_type.cursor(Utils.parseRawQuery(req.query))
       cursor = cursor.whiteList(@white_lists.index) if @white_lists.index
-      cursor.toJSON (err, json) ->
-        if err then res.send(404) else res.json(if req.query.$count then {result: json} else json)
+      cursor.toJSON (err, json) =>
+        return res.send(404) if err
+        return res.json({result: json}) if req.query.$count
+        return res.json(json) unless @renderer
+
+        # TODO: intgrate the cache -> would need to be done as part of the fetch
+        # TODO: can be optimized by using one model, etc
+        JSONUtils.renderModelsJSON _.map(json, (model_json) => new @model_type(@model_type::parse(model_json))), @renderer, @render_options or {}, (err, json) =>
+          return res.status(500).send(error: err.toString()) if err
+          res.json(json)
+
     catch err
       res.status(500).send(error: err.toString())

@@ -35,7 +45,11 @@
         return res.status(404).send(error: err.toString()) if err
         return res.status(404).send("Model not found with id: #{req.params.id}") unless json
         json = _.pick(json, @white_lists.show) if @white_lists.show
-        res.json(json)
+        return res.json(json) unless @renderer
+
+        JSONUtils.renderModelJSON new @model_type(@model_type::parse(model_json)), @renderer, @render_options or {}, (err, json) =>
+          return res.status(500).send(error: err.toString()) if err
+          res.json(json)
     catch err
       res.status(500).send(error: err.toString())

@@ -47,7 +61,11 @@
         success: =>
           json = model.toJSON()
           json = _.pick(json, @white_lists.create) if @white_lists.create
-          res.json(json)
+          return res.json(json) unless @renderer
+
+          JSONUtils.renderModelJSON new @model_type(@model_type::parse(model_json)), @renderer, @render_options or {}, (err, json) =>
+            return res.status(500).send(error: err.toString()) if err
+            res.json(json)
         error: -> res.send(404)
       }
     catch err
@@ -63,7 +81,11 @@
           success: =>
             json = model.toJSON()
             json = _.pick(json, @white_lists.update) if @white_lists.update
-            res.json(json)
+            return res.json(json) unless @renderer
+
+            JSONUtils.renderModelJSON new @model_type(@model_type::parse(model_json)), @renderer, @render_options or {}, (err, json) =>
+              return res.status(500).send(error: err.toString()) if err
+              res.json(json)
           error: -> res.send(404)
         }
     catch err


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/engine_selector.coffee
+++ test/engine_selector.coffee
@@ -30,6 +30,7 @@
   obj2 = engine.add
     type: 'testObj'
     x: 9
-    y
+    y: 10
+

 module()


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/engine_selector.coffee
+++ test/engine_selector.coffee
@@ -20,6 +20,7 @@
 test "#first", ->
   engine.add
     type: 'testObj'
-    x:
+    x: 4
+    y: 20

 module()


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/engine_selector.coffee
+++ test/engine_selector.coffee
@@ -33,7 +33,7 @@
     else if index is 2
       equals obj, obj3
     else if index is 3
-
+      equals

 test "#find", ->
   engine = Engine()


add writer/reader html to fuzzer error

add writer/reader html to fuzzer error

--- tests/scripts/fuzzer.coffee
+++ tests/scripts/fuzzer.coffee
@@ -78,6 +78,8 @@
           callback(null)
         else
           console.error operation, curHtml
+          console.error 'writer', writerHtml
+          console.error 'reader', readerHtml
           callback('Editor html diversion')
       else
         callback(null)


fixup specs

fixup specs

--- test/course/confirm-join.spec.coffee
+++ test/course/confirm-join.spec.coffee
@@ -10,21 +10,13 @@

   beforeEach ->
     @props =
-      title: 'Join my Course'
       course: new Course(COURSE)

-  it 'sets title from props', ->
-    Testing.renderComponent( ConfirmJoin, props: @props ).then ({dom}) =>
-      expect(dom.querySelector('h3').textContent).equal(@props.title)
-
-      expect(dom.querySelector('label').textContent)
-        .equal('Enter your school issued ID:')
-
-  it 'changes instructions if optionalStudentId is true', ->
-    @props.optionalStudentId = true
+  it 'sets title with coure', ->
     Testing.renderComponent( ConfirmJoin, props: @props ).then ({dom}) ->
-      expect(dom.querySelector('label').textContent)
-        .equal('Update school issued ID(leave blank to leave unchanged):')
+      title = dom.querySelector('h3.title').textContent
+      expect(title).to.include("You are joining")
+      expect(title).to.include("Biology I")

   it 'confirms model when submit is clicked', ->
     sinon.stub(@props.course, 'confirm')


show plan legs again

show plan legs again

--- app/assets/javascripts/views/navigation.coffee
+++ app/assets/javascripts/views/navigation.coffee
@@ -3,12 +3,12 @@
   el: 'div#navigation'

   events:
-    'submit': 'plan'
+    'submit': 'geocode'
     'click #from-location': 'from_current_location'
     'click #to-location': 'to_current_location'

   initialize: =>
-    #Transit.events.on 'plan:complete', @add_segments
+    Transit.events.on 'plan:complete', @add_segments
     #Transit.events.on 'plan:complete', @get_real_time
     #Transit.events.on 'plan:clear', @render
     #Transit.events.on 'real_time:complete', @render_real_time
@@ -17,7 +17,7 @@
     $(@el).html(@template())
     this

-  plan: (event) ->
+  geocode: (event) ->
     event.preventDefault()
     Transit.plan.geocode_from_to @$('#from_query').val(), @$('#to_query').val()


- Added more error handling - Added Work-around for two WebKit bugs. - Simplified some internal methods

--- app/lib/dom_text_mapper/dom_text_mapper.coffee
+++ app/lib/dom_text_mapper/dom_text_mapper.coffee
@@ -1,4 +1,7 @@
 class window.DomTextMapper
+
+  USE_TH_TBODY_WORKAROUND = true
+
   constructor: ->
     @setRealRoot()
     @restrictToSerializable false
@@ -187,6 +190,7 @@
   # Return the character range mappings for a given path in the DOM
   getRangeForPath: (path) ->
     result = @mappings[path]
+    unless result? then throw new Error "Found no range for path '" + path + "'!"
     if @restricted
       result = $.extend {}, result;
       result.pathInfo = $.extend {}, result.pathInfo
@@ -268,7 +272,7 @@
 #      f = ((start: mapping.start, end: mapping.end) for p, mapping of @mappings when mapping.atomic)
 #      console.log f
 #      console.log "[" + mapping.start + ":" + mapping.end + "]" for p, mapping of @mappings when mapping.atomic
-      throw new Error "No matches found!"
+      throw new Error "No matches found for [" + start + ":" + end + "]!"

     # Create a DOM range object
@@ -328,6 +332,47 @@

   # ===== Private methods (never call from outside the module) =======

+  # There is some weird, bogus behaviour in Chrome,
+  # triggered by whitespaces between the table tag and it's children.
+  # See the select-tbody-problem described here:
+  #    https://github.com/hypothesis/h/issues/280
+  # And the WebKit bug report here:
+  #    https://bugs.webkit.org/show_bug.cgi?id=110595
+  #
+  # To work around this, we have to do two things.
+  #
+  # 1. We remove all the text elements created from those whitespaces.
+  # (Text elements are not allowed to be children of the table tag, anyway.)
+  # That fixes the problem with selecting the whole table, when trying te select
+  #  an empty text element.
+  #
+  # The second part of the trick is implemented in the selectNode method.
+
+  purgeDebris: ->
+    @debris = []
+    @collectDebris @pathStartNode
+    console.log "Dropping " + @debris.length + " debris elements."
+    for node in @debris
+#      console.log "Dropping debris text element: " + @getPathTo node
+      node.remove()
+
+  # Go through this sub-tree, and collect all immadiate tech children of the tables
+  collectDebris: (node) ->
+    # If this node has no children, don't bother
+    unless node.hasChildNodes() then return
+
+    # Check if this is a table
+    inTable = node.nodeType is Node.ELEMENT_NODE and node.tagName.toLowerCase() is "table"
+
+    # Go through all the children
+    for child in node.childNodes
+      if inTable and child.nodeType is Node.TEXT_NODE
+        # Found a text that should be dropped
+        @debris.push child
+      else
+        # Do this to the child, recursively
+        @collectDebris child
+
   timestamp: -> new Date().getTime()

   stringStartsWith: (string, prefix) -> prefix is string.substr 0, prefix.length
@@ -363,39 +408,24 @@
     xpath = xpath.replace /\/$/, ''
     xpath

-  collectPathsForNode: (node, visible = true) ->
-    path = @getPathTo node
-#    if @stringStartsWith path, "/HTML/BODY/DIV/TABLE/TBODY/TR/TD[2]/DIV/DIV/TABLE"
-#      console.log "Collecting " + path
-
-    # Step one: get rendered node content, and store path info
+  # This method is called recursively, to collect all the paths in a given sub-tree of the DOM.
+  collectPathsForNode: (node) ->
+    # Step one: get rendered node content, and store path info, if there is valuable content
     cont = @getNodeContent node, false
     if cont.length
-      if node.data? and node.data.replace(/\n/g, " ").trim().length is 0
-        console.log "Encountered FAKE selection for path:" + path + ". Ignoring this."
-        visible = false
-      else
-        @allPaths[path] =
-          path: path
-          content: cont
-          length: cont.length
-          node : node
-#        if @stringStartsWith path, "/HTML/BODY/DIV/TABLE/TBODY/TR/TD[2]/DIV/DIV/TABLE"
-        unless visible then console.log path + ": collected info; visible = " + visible
-    else
-#      if @stringStartsWith path, "/HTML/BODY/DIV/TABLE/TBODY/TR/TD[2]/DIV/DIV/TABLE"
-#        console.log path + ": no real content"
-#        visible = false
+      path = @getPathTo node
+      @allPaths[path] =
+        path: path
+        content: cont
+        length: cont.length
+        node : node

     # Step two: cover all children.
     # Q: should we check children even if the goven node had no rendered content?
     # I seem to remember that the answer is yes, but I don't remember why.
-    if node.hasChildNodes
-      children = node.childNodes
-      i = 0
-      while i < children.length
-        @collectPathsForNode children[i], visible
-        i++
+    if node.hasChildNodes()
+      for child in node.childNodes
+        @collectPathsForNode child
     null

   getBody: -> (@rootWin.document.getElementsByTagName "body")[0]
@@ -432,8 +462,22 @@

     # create our range, and select it
     range = @rootWin.document.createRange()
-    range.setStartBefore node
-    range.setEndAfter node
+
+    # work-around 2 for table selection inconsistency issue,
+    # See https://github.com/hypothesis/h/issues/280
+    if USE_TH_TBODY_WORKAROUND and node.nodeType is Node.ELEMENT_NODE and
+        node.tagName.toLowerCase() in ["thead", "tbody"] and node.hasChildNodes()
+      # This is a thead or a tbody, and selection those is problematic,
+      # because if the WebKit bug.
+      # (Sometimes it selects nothing, sometimes it selects the whole table.)
+      # So we select directly the children instead.
+      children = node.childNodes
+      range.setStartBefore children[0]
+      range.setEndAfter children[children.length - 1]
+    else
+      range.setStartBefore node
+      range.setEndAfter node
+
     sel.addRange range
     if scroll
       sn = node
@@ -445,8 +489,7 @@
   # Read the "text content" of a sub-tree of the DOM by creating a selection from it
   getNodeSelectionText: (node, shouldRestoreSelection = true) ->
     if shouldRestoreSelection then @saveSelection()
-
-    # select the node
+
     sel = @selectNode node

     # read (and convert) the content of the selection
@@ -529,7 +572,7 @@
     startIndex = if parentContent? then (parentContent.indexOf content, index) else index
     if startIndex is -1
        # content of node is not present in parent's content - probably hidden, or something similar
-#       console.log "Content is not present in parent, returning"
+#       console.log "Content of this not is not present in content of parent, at path " + path
        return index


Fix clicking beacon marker shadow

Fix clicking beacon marker shadow

Now clicking the shadow of the marker will go through to the beacon
circle, and properly open/close the popup.

--- css.client.coffee
+++ css.client.coffee
@@ -26,6 +26,9 @@
 		bottom: '0'
 		left: '0'
 		backgroundColor: '#DDD'
+# Ensure clicking the shadow of a marker goes to the element below (beacon circle in this case)
+	'.leaflet-shadow-pane img':
+		pointerEvents: 'none'
 # Plugin setup pages
 	'.stepbar': # The bar at the top
 		color: "white"


Allow escape to cancel out. Fixes #14

Allow escape to cancel out. Fixes #14

--- jquery.immybox.coffee
+++ jquery.immybox.coffee
@@ -6,8 +6,9 @@
     blankIfNull: true
     maxResults: 50
     filterFn: (query) ->
+      # default filer function does case insensitive "contains" matching
       (choice) ->
-        choice.text.indexOf(query) >= 0
+        choice.text.toLowerCase().indexOf(query.toLowerCase()) >= 0

   _id = 1
   nextId = -> "#{pluginName}_#{_id++}"
@@ -99,6 +100,9 @@
     # @element.on 'keydown'
     # select the highlighted choice
     doSelection: (e) =>
+      if e.which is 27
+        @display()
+        @hideResults()
       if @queryResultArea.is(':visible')
         switch e.which
           when 9 # tab


Update BsPanelComponent.coffee

Update BsPanelComponent.coffee

Send the event with the clicked action.

--- app/scripts/components/BsPanelComponent.coffee
+++ app/scripts/components/BsPanelComponent.coffee
@@ -21,7 +21,7 @@
             ).bind(@), 250

     click: (event) ->
-        @sendAction('clicked')
+        @sendAction('clicked', event)

     collapsibleBodyId: (->
         "#{@get('elementId')}_body"
@@ -32,4 +32,4 @@
     ).property('collapsibleBodyId')
 )

-Ember.Handlebars.helper 'bs-panel', Bootstrap.BsPanelComponent+Ember.Handlebars.helper 'bs-panel', Bootstrap.BsPanelComponent


add clonejob once

add clonejob once

--- node_modules/hubot-twitter-egosearch/scripts/egosearch.coffee
+++ node_modules/hubot-twitter-egosearch/scripts/egosearch.coffee
@@ -21,6 +21,7 @@
 dateformat = require 'dateformat'
 cronJob = require('cron').CronJob

+cronOn = false
 queries = {}
 config =
   consumer_key: process.env.HUBOT_TWITTER_EGOSEARCH_CONSUMER_KEY
@@ -114,12 +115,13 @@
     loadQueriesFromBrain robot
     loopSearching robot

-  new cronJob('0 */1 * * * *', () ->
-    robot.logger.debug 'croned loop tweet check'
-    loadQueriesFromBrain robot
-    loopSearching robot
-  ).start()
-
+  if !cronOn
+    @cronOn = true
+    new cronJob('0 */1 * * * *', () ->
+      robot.logger.debug 'croned loop tweet check'
+      loadQueriesFromBrain robot
+      loopSearching robot
+    ).start()

   robot.respond /egosearch (?:add|new|start) (.+)/i, (msg) ->
     robot.logger.debug 'add search'


change terms

change terms

--- api/apps/articles/model/save.coffee
+++ api/apps/articles/model/save.coffee
@@ -61,12 +61,12 @@
   text = text.join(' ').substring(0,150).concat('...')
   text

-getStopWords = (title) ->
+removeStopWords = (title) ->
   newTitle = _.difference(title.split(' '), stopWords.stopWords).join(' ')
   return newTitle

 @generateSlugs = (article, cb) ->
-  stoppedTitle = getStopWords article.thumbnail_title
+  stoppedTitle = removeStopWords article.thumbnail_title
   slug = _s.slugify article.author?.name + ' ' + stoppedTitle
   return cb null, article if slug is _.last(article.slugs)
   db.articles.count { slugs: slug }, (err, count) ->


Fix replay speed for raids

Fix replay speed for raids

--- app/assets/javascripts/raids.coffee
+++ app/assets/javascripts/raids.coffee
@@ -120,3 +120,17 @@
     $("#play-icon").removeClass("glyphicon-play").addClass("glyphicon-pause")
     replayRaid()
   false
+
+$("#slower").click ->
+  if window.replayPlaying
+    window.replaySpeed *= 2
+    window.clearInterval(window.iid)
+    window.iid = window.setInterval(nextTick, window.replaySpeed)
+  false
+
+$("#faster").click ->
+  if window.replayPlaying
+    window.replaySpeed /= 2
+    window.clearInterval(window.iid)
+    window.iid = window.setInterval(nextTick, window.replaySpeed)
+  false


Add autoset and dependent methods to select inputs.

* Autoset allows one select to influence another.
* Dependent shows or hides elements based on the value of a select.

--- app/assets/javascripts/ui.js.coffee
+++ app/assets/javascripts/ui.js.coffee
@@ -1,7 +1,10 @@
 jQuery ->
+  # Crude way to make large blocks .clickable by definiting a.primary-link in them
   $(".clickable").live "click", (e) ->
     window.location.href = $(this).find("a.primary-link").attr("href")

+  # When .collapsible item is hovered in/out the .collapse elements inside
+  # expand and collapse
   $(".collapsible")
     .hover ->
       $(this).find(".collapse").slideDown()
@@ -9,5 +12,29 @@
       $(this).find(".collapse").slideUp()
     .find(".collapse").hide()

+  # Apply date selector to all date inputs
   $(":input.date").dateinput
     format: "dddd, dd mmmm yyyy"
+
+  # When a select box is changed search for other selects that
+  # are linked via the autoset and autoset-param data attributes
+  # and update them with the new value.
+  $(document).on "change", "select", ->
+    source_select = $ this
+    $("select[data-autoset='##{this.id}']").each ->
+      target_select = $ this
+      param = target_select.data("autoset-param")
+      new_value = source_select.find("option:selected").data(param)
+      console.info source_select, target_select, param, new_value
+      target_select.val(new_value)
+
+  # When a select box is changed find any dependent elements and
+  # hide or show based on whether the new value is blank or not.
+  $(document).on "change", "select", ->
+    source_select = $ this
+    $("*[data-dependent='##{this.id}']").each ->
+      target = $ this
+      if source_select.val() != ""
+        target.show()
+      else
+        target.hide()


Asset Record

Asset Record

--- app/js/controllers/delegates.coffee
+++ app/js/controllers/delegates.coffee
@@ -7,6 +7,7 @@
     Wallet.refresh_accounts()
     Blockchain.refresh_delegates()

+    #blockchain_get_asset_record  XTS

     $scope.toggleVoteUp = (name) ->
         if name not of Wallet.approved_delegates or Wallet.approved_delegates[name] < 1


fix md-update

fix md-update

--- lib/assets/javascripts/rails_updater.coffee
+++ lib/assets/javascripts/rails_updater.coffee
@@ -17,7 +17,8 @@
   update: (value) ->
     @value = if @override then scope.$eval(@atomName) else value
     object = {id: @scope.$eval(@modelName).id}
-    object[@atomName] = value
+    object[@modelName] = {}
+    object[@modelName][@atomName] = value
     unless @scope[@modelName].currently_updating
       @scope[@modelName].currently_updating = true
       up = @


close log group after redraw

close log group after redraw

--- lib/assets/javascripts/stark/model_binding.js.coffee
+++ lib/assets/javascripts/stark/model_binding.js.coffee
@@ -41,6 +41,7 @@
       @log "redraw handler fired (sync) due to model binding on: ", model
       @begin_closed_group "redrawing view: #{@.className()}"
       @redraw()
+      @end_group()

     # @onR view, "change", ->
     #   @log "redraw handler fired (change) due to model binding on: ", model


Autoscroll after getting new output

Autoscroll after getting new output

--- app/assets/javascripts/angular/directives/form_output.js.coffee
+++ app/assets/javascripts/angular/directives/form_output.js.coffee
@@ -1,9 +1,13 @@
 angular.module("grumbles")
-  .directive "formOutput", ($rootScope, Output) ->
+  .directive "formOutput", ($rootScope, $timeout, Output) ->
     template: '<output ng-bind-html="outputHtml"></output>'

     scope: {}

-    link: (scope) ->
+    link: (scope, element) ->
       $rootScope.$on "outputChanged", ->
         scope.outputHtml = Output.combinedLog()
+
+        $timeout ->
+          child = element.children()[0]
+          child.scrollTop = child.scrollHeight