feat(collections): Add validator to support nested backbone collections

--- src/meshweaver.validators.coffee
+++ src/meshweaver.validators.coffee
@@ -1,5 +1,8 @@
 # Custom Validators for Backbone.Validation
 # Arana Software 2013-2015
+
+_.extend Backbone.Validation.messages,
+  validCollection: '{0} contains invalid entries: {1}'

 patterns = Backbone.Validation.patterns
 messages = Backbone.Validation.messages
@@ -8,6 +11,28 @@
   return _.isNumber(value) or (_.isString(value) and value.match(patterns.number))

 _.extend Backbone.Validation.validators,
+  validCollection: (value, attr, valid, model, computed) ->
+    isValid =
+      if _.isFunction(valid)
+        valid.call(model, value, attr, computed)
+      else valid
+
+    return unless isValid
+
+    errors = value.map (entry) ->
+      return entry.validate()
+
+    unless _.find(errors, (error) -> !!error)?
+      return
+
+    errorMessage = _.chain(errors.filter (error) -> !!error)
+                .map (error) -> _.values(error)
+                .flatten()
+                .value()
+                .join('; ')
+
+    @.format messages.validCollection, @.formatLabel(attr, model), errorMessage
+
   min: (value, attr, minValue, model, computed) ->
     minVal =
       if _.isFunction(minValue)


Added Atoms.icons task

Added Atoms.icons task

--- gruntfile.coffee
+++ gruntfile.coffee
@@ -42,6 +42,8 @@
           'style/theme/organism.*.styl']
         templates: [
           'style/theme/template.*.styl']
+        icons: [
+          'bower_components/atoms_icons/*.styl']

     coffee:
       core: files: '<%=meta.build%>/<%=pkg.name%>.debug.js'         : '<%= source.core %>'
@@ -79,7 +81,9 @@
       templates:
         options: compress: false, import: [ '__init']
         files: '<%=meta.bower%>/<%=pkg.name%>.templates.css': '<%=source.stylus.templates%>'
-
+      icons:
+        options: compress: false
+        files: '<%=meta.bower%>_icons/<%=pkg.name%>.icons.css': '<%=source.stylus.icons%>'

     watch:
       core:
@@ -106,6 +110,9 @@
       stylus_templates:
         files: ['<%= source.stylus.templates %>']
         tasks: ['stylus:templates']
+      stylus_icons:
+        files: ['<%= source.stylus.icons %>']
+        tasks: ['stylus:icons']

   grunt.loadNpmTasks 'grunt-contrib-coffee'
   grunt.loadNpmTasks 'grunt-contrib-concat'


уменьшает первую букву в посте соц сети + правильнее очищает поле текста причины

--- app/assets/javascripts/share.coffee
+++ app/assets/javascripts/share.coffee
@@ -7,6 +7,7 @@

   # При нажатии по кнопке подсказать причину включает лоадер
   $("#get_random_container").click ->
+    share.is_user_input = false
     $(this).addClass("loading")
     $(this).find("img").removeClass("hidden")

@@ -27,13 +28,15 @@

   # Очищает поле причины при нажатии по нему
   $(reason_field).on "click", ->
-    reason_field.val("")
-
+    unless share.is_user_input
+      reason_field.select()
+      share.is_user_input = true

 # Параметры и функции, связанные с соц кнопками и полем ввода причины
 share =
   title: "Симулятор эвакуации из Рашки"
   text: "Я решил валить из рашки, потому что"
+  is_user_input: false

   # Меняет текст вк кнопки
@@ -88,7 +91,7 @@
   # @param textarea [jQuery DOM] поле ввода причины
   update_share_buttons: (textarea) ->
     reason = textarea.val()
-    shortened_reason = reason.substr(0, 80)
+    shortened_reason = reason.substr(0, 80).charAt(0).toLowerCase() + reason.slice(1)
     share.change_vk_link (shortened_reason)
     share.change_tw_link (shortened_reason)
     share.change_bl_link (shortened_reason)


GroupStackSettings v2: fix broken cancel flow

--- client/admin/lib/views/groupstacksettingsv2.coffee
+++ client/admin/lib/views/groupstacksettingsv2.coffee
@@ -33,5 +33,5 @@

     defineStackView.on ['Cancel', 'Completed'], =>
       @initialView.show()
-      @destroy()
+      defineStackView.destroy()


For the neurotic, tempNotes is gone.

For the neurotic, tempNotes is gone.

--- client/coffeescript/templates/notifications.coffee
+++ client/coffeescript/templates/notifications.coffee
@@ -13,13 +13,8 @@
       event.stopPropagation()
   }
 )
-
-tempNotes = [{message: 'hi'},{message: 'there'},
-      {message: 'friend'},{message: 'how'},
-      {message: 'are'},{message: 'you'},
-      {message: 'today'}]

-Session.set('notifications', tempNotes)
+Session.set('notifications', [])

 $(document).click ->
   if Session.get('state', 'open')


Workspace: Added missing terminal class to TerminalPane.

--- client/landing/Workspace/panes/terminalpane.coffee
+++ client/landing/Workspace/panes/terminalpane.coffee
@@ -2,7 +2,7 @@

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

-    options.cssClass = "terminal-pane"
+    options.cssClass = "terminal-pane terminal"

     super options, data


getUserDefaults bug fix

getUserDefaults bug fix

--- JSKit/01_JSUserDefaults.coffee
+++ JSKit/01_JSUserDefaults.coffee
@@ -18,7 +18,10 @@
 			"mode": "getUserDefaults"
 			"forKey": forKey
 		, (data)=>
-			data2 = JSON.parse(data)
+			if (data != "")
+				data2 = JSON.parse(data)
+			else
+				data2 = ""
 			action(data2)

 	removeObjectForKey:(forKey)->


Made the copy a real copy again per @markijbema's comment

--- core/app/backbone/contrib/monkey_backbone_history.coffee
+++ core/app/backbone/contrib/monkey_backbone_history.coffee
@@ -10,11 +10,8 @@

   old_loadUrl = Backbone.History.prototype.loadUrl
   Backbone.History.prototype.loadUrl = (fragmentOverride) ->
-    fragment = @getFragment(fragmentOverride) # copied from Backbone
-
-    if FactlinkApp.DiscussionModalOnFrontend.closeDiscussionAndAlreadyOnBackgroundPage(fragment)
-      @fragment = fragment
-      return true
+    fragment = @fragment = @getFragment(fragmentOverride) # copied from Backbone
+    return true if FactlinkApp.DiscussionModalOnFrontend.closeDiscussionAndAlreadyOnBackgroundPage(fragment)

     FactlinkApp.DiscussionModalOnFrontend.setBackgroundPageUrl(fragment)
     old_loadUrl.apply this, arguments


Update sdk sdk references in auth.coffee

Change-type:patch

--- lib/actions/auth.coffee
+++ lib/actions/auth.coffee
@@ -136,7 +136,7 @@
 			$ resin logout
 	'''
 	action: (params, options, done) ->
-		resin = require('resin-sdk-preconfigured')
+		resin = require('resin-sdk').fromSharedOptions()
 		resin.auth.logout().nodeify(done)

 exports.signup =
@@ -157,7 +157,7 @@
 			johndoe
 	'''
 	action: (params, options, done) ->
-		resin = require('resin-sdk-preconfigured')
+		resin = require('resin-sdk').fromSharedOptions()
 		form = require('resin-cli-form')
 		validation = require('../utils/validation')


:bug: Add the refresh button to the main page

--- src/components/index.cjsx
+++ src/components/index.cjsx
@@ -6,6 +6,7 @@
 App = require './app'
 Task = require './task'
 LoadableItem = require './loadable-item'
+RefreshButton = require './refresh-button'
 {TaskActions, TaskStore} = require '../flux/task'
 {CourseActions, CourseStore} = require '../flux/course'
 {CurrentUserActions, CurrentUserStore} = require '../flux/current-user'
@@ -76,7 +77,9 @@
     else
       CurrentUserActions.loadAllCourses()

-
Loading?

+
Loading...
+        <RefreshButton />
+

 SingleTask = React.createClass


:bug: fix documentation not being used

:bug: fix documentation not being used

--- lib/atom-ternjs-client.coffee
+++ lib/atom-ternjs-client.coffee
@@ -21,7 +21,7 @@
         types: true
         sort: atom.config.get('atom-ternjs.sort')
         guess: atom.config.get('atom-ternjs.guess')
-        docs: docs and atom.config.get('atom-ternjs.docs')
+        docs: docs and atom.config.get('atom-ternjs.documentation')
         urls: docs and atom.config.get('atom-ternjs.urls')
         origins: docs and atom.config.get('atom-ternjs.origins')
         lineCharPositions: true


no more querySelector

no more querySelector

--- lib/autohide-tree-view.coffee
+++ lib/autohide-tree-view.coffee
@@ -101,14 +101,14 @@
   registerTreeView: (treeViewPkg) ->
     @treeView = treeViewPkg.mainModule.createView()
     @treeViewEl = @treeView.element
-    @treeViewEl.classList.add 'autohide', 'autohide-hover'
+    @treeViewEl.classList.add 'autohide'

     @disposables.add new Disposable =>
       @treeViewEl.classList.remove 'autohide', 'autohide-hover'
       @treeViewEl.style.position = ''
       @treeViewEl.style.minWidth = ''
       @treeViewEl.parentNode?.style?.width = ''
-      @treeViewEl.querySelector('.tree-view-scroller').style.display = ''
+      @treeView.list[0].style.display = ''
       [@treeView, @treeViewEl] = []

   observe: ->
@@ -196,8 +196,8 @@
   show: (delay = @conf.showDelay, disableHoverEvents = false) ->
     @disableHoverEvents() if disableHoverEvents
     @storeFocusedElement()
-    @treeViewEl.querySelector('.tree-view-scroller').style.display = ''
-    targetWidth = @treeViewEl.querySelector('.tree-view').clientWidth
+    @treeView.scroller[0].style.display = ''
+    targetWidth = @treeView.list[0].clientWidth
     @animate(targetWidth, delay).then (finished) =>
       @visible = true
       @treeView.focus() if finished
@@ -208,7 +208,7 @@
     @recoverFocus()
     targetWidth = @conf.hiddenWidth
     @animate(targetWidth, delay).then (finished) =>
-      @treeViewEl.querySelector('.tree-view-scroller').style.display = 'none' if finished
+      @treeView.scroller[0].style.display = 'none' if finished

   toggle: (disableHoverEvents = true) ->
     if @visible then @hide 0 else @show 0, disableHoverEvents


fixed some indentation error

fixed some indentation error

--- app/assets/javascripts/admin/views/tasktype/task_type_list_item_view.coffee
+++ app/assets/javascripts/admin/views/tasktype/task_type_list_item_view.coffee
@@ -11,23 +11,23 @@
     <%- formattedShortText %>

       <% _.each(settings.allowedModes, function (mode) { %>
-	<% var modename = mode[0].toUpperCase() + mode.slice(1); %>
-	<% if(mode == settings.preferredMode) { %>
-	  <%= modename %>

-	<% } else { %>
-	  <%= modename %>

-	<% } %>
+      <% var modename = mode[0].toUpperCase() + mode.slice(1); %>
+      <% if(mode == settings.preferredMode) { %>
+        <%= modename %>

+      <% } else { %>
+        <%= modename %>

+      <% } %>
       <% }) %>

       <% if(settings.branchPointsAllowed) { %>
-	Branchpoints
+      Branchpoints
       <% } %>
       <% if(settings.somaClickingAllowed) { %>
-	Soma clicking
+      Soma clicking
       <% } %>
       <% if(settings.advancedOptionsAllowed) { %>
-	Advanced Options
+      Advanced Options
       <% } %>

@@ -37,19 +37,19 @@
     <%- fileName %>

       <a href="/taskTypes/<%- id %>/edit" >
-	edit
+      edit

       <a href="/annotations/CompoundTaskType/<%- id %>" title="view all finished tracings">
-	view
+      view

       <a href="/taskTypes/<%- id %>/tasks" title="View Tasks">
-	tasks
+      tasks

       <a href="/api/taskTypes/<%- id %>/download" >
-	download
+      download

-	delete
+      delete

   """)
@@ -68,7 +68,7 @@

     if window.confirm("Do you really want to delete this task type?")
       @model.destroy().then((response) =>
-	Toast.message(response.messages)
+      Toast.message(response.messages)
       )


Cleanup debugging

Cleanup debugging

--- src/config/turtles.coffee
+++ src/config/turtles.coffee
@@ -68,17 +68,13 @@
   run: (world, args) ->
     step = world.get_raw('step')
     {speed} = args
-    console.log "run with args", args, speed
     assert _.isFunction(step), "step is not a function"
     assert speed?, "run: requires speed"
-    console.log "run with speed", speed
     world.owner('speed').put('speed', speed)
     step_and_repeat = (self) ->
       speed = world.get('speed')
-      console.log "called with speed", speed
       if speed > 0
         delay = world.get('interval') / speed
-        console.log "run with delay", delay
         step(world, args)
         setTimeout((-> self(self)), delay)
     step_and_repeat(step_and_repeat)


Allow strike with ~~

Allow strike with ~~

--- packages/rocketchat-markdown/markdown.coffee
+++ packages/rocketchat-markdown/markdown.coffee
@@ -11,7 +11,7 @@
 		# Process MD like for strong, italic and strike
 		msg = msg.replace(/\*([^*]+)\*/g, '$1')
 		msg = msg.replace(/\_([^_]+)\_/g, '$1')
-		msg = msg.replace(/\~([^_]+)\~/g, '$1')
+		msg = msg.replace(/\~{1,2}([^~]+)\~{1,2}/g, '$1')

 		message.html = msg
 		return message


Add watch on questions, so that scores are updated in real time

--- app/scripts/controllers/main.coffee
+++ app/scripts/controllers/main.coffee
@@ -10,7 +10,15 @@
   $scope.sections = _.keys(questionsByCategory)

   $scope.totalsPerSection = scores.totals(questions)
-  $scope.resultsPerSection = scores.userScores(questions)
+
+  updateResults = () ->
+    $scope.resultsPerSection = scores.userScores(questions)
+
+  $scope.$watch(
+    'questions',
+    updateResults,
+    true
+  )

   $scope.generateReport = () ->
-    $scope.resultsPerSection = scores.userScores(questions)
+    # TODO: add report generation


Turn intention-revealing variable + comment into intention-revealing method alias

--- lib/assets/javascripts/turbolinks.js.coffee
+++ lib/assets/javascripts/turbolinks.js.coffee
@@ -11,12 +11,9 @@
 fetchReplacement = (url) ->
   triggerEvent 'page:fetch', url: url

-  # Remove hash from url to ensure IE 10 compatibility
-  safeUrl = removeHash url
-
   xhr?.abort()
   xhr = new XMLHttpRequest
-  xhr.open 'GET', safeUrl, true
+  xhr.open 'GET', removeHashForIE10compatiblity(url), true
   xhr.setRequestHeader 'Accept', 'text/html, application/xhtml+xml, application/xml'
   xhr.setRequestHeader 'X-XHR-Referer', referer

@@ -114,6 +111,11 @@
     document.location.href = document.location.href
   else
     window.scrollTo 0, 0
+
+
+# Intention revealing function alias
+removeHashForIE10compatiblity = (url) ->
+  removeHash url

 removeHash = (url) ->
   link = url


:fire: logs

:fire: logs

--- lib/color-buffer.coffee
+++ lib/color-buffer.coffee
@@ -214,7 +214,6 @@
       new ColorMarker {marker, color: result.color, text: result.match}

   updateColorMarkers: (results) ->
-    console.log results
     newMarkers = []
     toCreate = []
     for result in results


Turn off caching for deployment

Turn off caching for deployment

--- app/scripts/natdashboardctrl.coffee
+++ app/scripts/natdashboardctrl.coffee
@@ -7,7 +7,7 @@

     # should http calls be cached
     # FIXME: should be application level setting
-    cacheHttp = true
+    cacheHttp = false

     # a flag to keep track if the plots should be redrawn
     # next time the tab is made visible


Update particles demo to the new interface

--- demos/assets/js/particles.coffee
+++ demos/assets/js/particles.coffee
@@ -14,56 +14,57 @@
   Point = require('agt/geom/point')
   Impulse = require('agt/impulse')

-  particleSubSystem = new p.initializers.ParticleSubSystem(
-    new p.initializers.Life(800,1200),
-    new p.actions.MacroAction([
+  particleSubSystem = new p.initializers.ParticleSubSystem({
+    initializer: new p.initializers.Life(800,1200)
+    action: new p.actions.MacroAction([
       new p.actions.Live,
       new p.actions.Move,
       new p.actions.Force(new Point(5, 20))
       new p.actions.Friction(0.2)
-    ]),
-    (particle) ->
-      new p.Emission(
-        p.Particle,
-        new p.emitters.Ponctual(particle.position),
-        new p.timers.UntilDeath(particle),
-        new p.counters.ByRate(10),
-        initialize: (particle) -> particle.parasite.color = '#00ff00'
-      )
-  )
+    ])
+    emissionFactory: (particle) ->
+      new p.Emission({
+        class: p.Particle
+        emitter: new p.emitters.Ponctual(particle.position)
+        timer: new p.timers.UntilDeath(particle)
+        counter: new p.counters.ByRate(10)
+        initializer:
+          initialize: (particle) -> particle.parasite.color = '#00ff00'
+      })
+  })

-  system = new p.System(
-    new p.initializers.MacroInitializer([
+  system = new p.System({
+    initializer: new p.initializers.MacroInitializer([
       new p.initializers.Life(3000,4000),
       particleSubSystem
-    ]),
-    new p.actions.MacroAction([
+    ])
+    action: new p.actions.MacroAction([
       new p.actions.Live,
       new p.actions.Move,
       new p.actions.Force(new Point(5, 20))
       new p.actions.Friction(0.2)
-    ]),
-    new p.SubSystem(
-      new p.initializers.Life(800,1200),
-      new p.actions.MacroAction([
+    ])
+    subSystem: new p.SubSystem({
+      initializer: new p.initializers.Life(800,1200)
+      action: new p.actions.MacroAction([
         new p.actions.Live,
         new p.actions.Move,
         new p.actions.Force(new Point(5, 20))
         new p.actions.Friction(0.2)
-      ]),
-      (particle) ->
-        new p.Emission(
-          p.Particle,
-          new p.emitters.Ponctual(particle.position.clone()),
-          new p.timers.Limited(10),
-          new p.counters.Fixed(20),
-          new p.initializers.MacroInitializer([
+      ])
+      emissionFactory: (particle) ->
+        new p.Emission({
+          class: p.Particle
+          emitter: new p.emitters.Ponctual(particle.position.clone())
+          timer: new p.timers.Limited(10)
+          counter: new p.counters.Fixed(20)
+          initializer: new p.initializers.MacroInitializer([
             new p.initializers.Explosion(10,20),
             initialize: (particle) -> particle.parasite.color = '#ff0000'
           ])
-        )
-    )
-  )
+        })
+    })
+  })

   # system.particlesCreated.add (o,p) -> console.log 'particlesCreated'
   # system.particlesDied.add (o,p) -> console.log 'particlesDied'
@@ -75,10 +76,8 @@
   canvas.addEventListener 'click', ({clientX, clientY}) ->
     unless Impulse.instance().running
       Impulse.instance().add ->
-
         context.fillStyle = '#ffffff'
         context.fillRect(0,0,canvas.width,canvas.height)
-

         particles = system.particles
         particles = particles.concat(system.subSystem.particles)
@@ -89,13 +88,13 @@

     x = clientX
     y = clientY
-    system.emit new p.Emission(
-      p.Particle,
-      new p.emitters.Ponctual(new Point(x, y)),
-      new p.timers.Unlimited,
-      new p.counters.ByRate(1),
-      new p.initializers.MacroInitializer([
+    system.emit new p.Emission({
+      class: p.Particle,
+      emitter: new p.emitters.Ponctual(new Point(x, y)),
+      timer: new p.timers.Unlimited,
+      counter: new p.counters.ByRate(1),
+      initializer: new p.initializers.MacroInitializer([
         new p.initializers.Stream(new Point(1,-3),40,50,0.2),
         initialize: (particle) -> particle.parasite.color = '#000000'
       ])
-    )
+    })


Allow for configuration of event DB

Allow for configuration of event DB

--- lib/event_store/couchdb.coffee
+++ lib/event_store/couchdb.coffee
@@ -66,7 +66,7 @@
             callback null, event

   _urlToDatabase: (database = "albums_development") ->
-    "http://localhost:5984/#{database}"
+    process.env['EVENT_STORE_COUCHDB_URL'] || "http://localhost:5984/#{database}"

   _urlToDocument: (document) ->
     "#{@_urlToDatabase()}/#{document}"


Added m.prop to the data, so that if it exists, it won't query the database unless it changes.

--- app/static/coffee/test/hello.coffee
+++ app/static/coffee/test/hello.coffee
@@ -6,13 +6,11 @@
         getData: -> m.request({
           method: "GET",
           url: urlhello
-          background: true,
-          initialValue: []
         })

     model=
       flowers: ["roses","violets"]
-      data: controller.getData()
+      data: m.prop(controller.getData())

     view: () ->
         [


Cleaned up patch rendering logic

Cleaned up patch rendering logic

--- app/views/editor/patch_modal.coffee
+++ app/views/editor/patch_modal.coffee
@@ -20,15 +20,9 @@
     targetID = @patch.get('target').id
     if targetID is @targetModel.id
       @originalSource = @targetModel.clone(false)
-      @targetIsCurrent = true
     else
       @originalSource = new @targetModel.constructor({_id:targetID})
       @supermodel.loadModel @originalSource, 'source_document'
-      @targetIsCurrent = false
-
-  onLoaded: ->
-    super()
-    @render()

   applyDelta: ->
     @headModel = null
@@ -44,8 +38,7 @@
     @pendingModel.loaded = true

   render: ->
-    return @showLoading() if not (@targetIsCurrent or @supermodel.finished()) # finished() is true when no model is loaded
-    @applyDelta()
+    @applyDelta() if @supermodel.finished()
     super()

   getRenderData: ->


:lipstick:

:lipstick:

--- lib/editor-proxy.coffee
+++ lib/editor-proxy.coffee
@@ -2,45 +2,46 @@
 utils = emmet.require("utils")

 module.exports =
+  @editor: null
+  @syntax: null

   setupContext: (editor) ->
-    @atom = editor
-    @indentation = @atom.activeEditSession.getTabText()
+    @editor = editor
+    @indentation = @editor.activeEditSession.getTabText()
     emmet.require("resources").setVariable("indentation", @indentation)

-    @$syntax = null
-    @$syntax = @getSyntax()
+    @syntax = @getSyntax()

   # Fetches the character indexes of the selected text.
   #
   # Returns an {Object} with `start` and `end` properties.
   getSelectionRange: ->
-    range = @atom.getSelection().getBufferRange()
+    range = @editor.getSelection().getBufferRange()
     return {
-      start: @atom.activeEditSession.indexForBufferPosition(range.start),
-      end: @atom.activeEditSession.indexForBufferPosition(range.end)
+      start: @editor.activeEditSession.indexForBufferPosition(range.start),
+      end: @editor.activeEditSession.indexForBufferPosition(range.end)
     }

   # Fetches the current line's start and end indexes.
   #
   # Returns an {Object} with `start` and `end` properties
   getCurrentLineRange: ->
-    row = @atom.getCursor().getBufferRow()
-    lineLength = @atom.lineLengthForBufferRow(row)
-    index = @atom.activeEditSession.indexForBufferPosition({row: row, column: 0})
+    row = @editor.getCursor().getBufferRow()
+    lineLength = @editor.lineLengthForBufferRow(row)
+    index = @editor.activeEditSession.indexForBufferPosition({row: row, column: 0})
     return {
       start: index,
       end: index + lineLength
     }

   getCaretPos: ->
-    row = @atom.getCursor().getBufferRow()
-    column = @atom.getCursor().getBufferColumn()
+    row = @editor.getCursor().getBufferRow()
+    column = @editor.getCursor().getBufferColumn()

-    return @atom.activeEditSession.indexForBufferPosition( {row: row, column: column} )
+    return @editor.activeEditSession.indexForBufferPosition( {row: row, column: column} )

   getContent: ->
-    @atom.getText()
+    @editor.getText()

   # Replace the editor's content (or part of it, if using `start` to
   # `end` index).
@@ -84,18 +85,18 @@
         start: value.length + start
         end: value.length + start

-    range = @atom.getSelection()
-    range.start = @atom.activeEditSession.indexForBufferPosition(start)
-    range.end = @atom.activeEditSession.indexForBufferPosition(end)
+    range = @editor.getSelection()
+    range.start = @editor.activeEditSession.indexForBufferPosition(start)
+    range.end = @editor.activeEditSession.indexForBufferPosition(end)

     range.insertText(value)

-    range.start = @atom.activeEditSession.indexForBufferPosition(firstTabStop.start)
-    range.end = @atom.activeEditSession.indexForBufferPosition(firstTabStop.end)
-    @atom.setSelectedBufferRange(range)
+    range.start = @editor.activeEditSession.indexForBufferPosition(firstTabStop.start)
+    range.end = @editor.activeEditSession.indexForBufferPosition(firstTabStop.end)
+    @editor.setSelectedBufferRange(range)

   getSyntax: ->
-    return @$syntax if @$syntax
+    @syntax if @syntax

     "html"


remove debug lines

remove debug lines

--- echoes/midground/lattice/main.coffee
+++ echoes/midground/lattice/main.coffee
@@ -9,7 +9,6 @@
     @push @spiral1

     if @doubled
-      console.log 'doubled'
       @spiral2 = new Spiral !@flipped, @spiral1
       @push @spiral2

@@ -50,9 +49,7 @@
     @rotation.z += @rotSpeed      * @rotDirection * elapsed / stage.song.bps

   beat: ->
-    console.log 'before', @rotSpeed
     @rotSpeed = @rotSpeedTarget
-    console.log 'after', @rotSpeed

 class Strut extends Echotron.Echo
   uniformAttrs:


Use a flag instead of comparing paths.

Use a flag instead of comparing paths.

--- lib/load-paths-handler.coffee
+++ lib/load-paths-handler.coffee
@@ -19,7 +19,7 @@
       @repo = repo if repo?.relativize(path.join(@rootPath, 'test')) is 'test'

   load: (done) ->
-    @loadPath @rootPath, =>
+    @loadPath @rootPath, true, =>
       @flushPaths()
       @repo?.destroy()
       done()
@@ -45,8 +45,8 @@
     emit('load-paths:paths-found', @paths)
     @paths = []

-  loadPath: (pathToLoad, done) ->
-    return done() if @isIgnored(pathToLoad) and pathToLoad isnt @rootPath
+  loadPath: (pathToLoad, root, done) ->
+    return done() if @isIgnored(pathToLoad) and not root
     fs.lstat pathToLoad, (error, stats) =>
       return done() if error?
       if stats.isSymbolicLink()
@@ -75,7 +75,7 @@
       async.each(
         children,
         (childName, next) =>
-          @loadPath(path.join(folderPath, childName), next)
+          @loadPath(path.join(folderPath, childName), false, next)
         done
       )


keep old range on focus loss

keep old range on focus loss

--- src/core/selection.coffee
+++ src/core/selection.coffee
@@ -8,8 +8,10 @@
 class Selection
   constructor: (@doc, @iframe, @emitter) ->
     @document = @doc.root.ownerDocument
-    @range = this.getRange()
+    @focus = false
+    @range = new Range(0, 0)
     @nullDelay = false
+    this.update('silent')

   checkFocus: ->
     return false unless @document.activeElement == @doc.root
@@ -17,16 +19,20 @@
       return document.activeElement == @iframe
     return true

-  getRange: ->
-    return null unless this.checkFocus()
-    nativeRange = this._getNativeRange()
-    return null unless nativeRange?
-    start = this._positionToIndex(nativeRange.startContainer, nativeRange.startOffset)
-    if nativeRange.startContainer == nativeRange.endContainer and nativeRange.startOffset == nativeRange.endOffset
-      end = start
+  getRange: (ignoreFocus = false) ->
+    if this.checkFocus()
+      nativeRange = this._getNativeRange()
+      return null unless nativeRange?
+      start = this._positionToIndex(nativeRange.startContainer, nativeRange.startOffset)
+      if nativeRange.startContainer == nativeRange.endContainer and nativeRange.startOffset == nativeRange.endOffset
+        end = start
+      else
+        end = this._positionToIndex(nativeRange.endContainer, nativeRange.endOffset)
+      return new Range(Math.min(start, end), Math.max(start, end)) # Handle backwards ranges
+    else if ignoreFocus
+      return @range
     else
-      end = this._positionToIndex(nativeRange.endContainer, nativeRange.endOffset)
-    return new Range(Math.min(start, end), Math.max(start, end))
+      return null

   preserve: (fn) ->
     nativeRange = this._getNativeRange()
@@ -60,16 +66,19 @@
       this.setRange(range, 'silent')

   update: (source) ->
-    range = this.getRange()
-    emit = source != 'silent' and !Range.compare(range, @range)
+    focus = this.checkFocus()
+    range = this.getRange(true)
+    emit = source != 'silent' and (!Range.compare(range, @range) or focus != @focus)
+    toEmit = if focus then range else null
     # If range changes to null, require two update cycles to update and emit
-    if range == null and source =='user' and !@nullDelay
+    if toEmit == null and source == 'user' and !@nullDelay
       @nullDelay = true
     else
       @nullDelay = false
       @range = range
+      @focus = focus
       # Set range before emitting to prevent infinite loop if listeners call quill.getSelection()
-      @emitter.emit(@emitter.constructor.events.SELECTION_CHANGE, range, source) if emit
+      @emitter.emit(@emitter.constructor.events.SELECTION_CHANGE, toEmit, source) if emit

   _decodePosition: (node, offset) ->
     if dom(node).isElement()


app.getAppByOwner: Use the slug for filtering

Change-type: minor
Depends-on: https://github.com/resin-io/resin-api/pull/1570
Signed-off-by: Thodoris Greasidis <5791bdc4541e79bd43d586e3c3eff1e39c16665f@balena.io>

--- lib/models/application.coffee
+++ lib/models/application.coffee
@@ -272,17 +272,7 @@
 			options:
 				mergePineOptions
 					$filter:
-						$eq: [
-							$tolower: $: 'app_name'
-							appName
-						],
-						user:
-							$any:
-								$alias: 'u',
-								$expr: $eq: [
-									$tolower: $: 'username'
-									owner
-								]
+						slug: "#{owner}/#{appName}"
 				, options
 		.tap (applications) ->
 			if isEmpty(applications)


revert since order matter because of requireFrom utils

--- lib/move-selected-text.coffee
+++ lib/move-selected-text.coffee
@@ -1,9 +1,3 @@
-Base = requireFrom('vim-mode-plus', 'base')
-Operator = Base.getClass('Operator')
-
-StateManager = require './state-manager'
-stateManager = new StateManager()
-
 {
   ensureBufferEndWithNewLine
   extendLastBufferRowToRow
@@ -20,6 +14,12 @@
   switchToLinewise
 } = require './utils'

+Base = requireFrom('vim-mode-plus', 'base')
+Operator = Base.getClass('Operator')
+
+StateManager = require './state-manager'
+stateManager = new StateManager()
+
 class MoveOrDuplicateSelectedText extends Operator
   @commandScope: 'atom-text-editor.vim-mode-plus.visual-mode'
   @commandPrefix: 'vim-mode-plus-user'


Use bit.ly instead of git.io.

Use bit.ly instead of git.io.

--- lib/notification-issue.coffee
+++ lib/notification-issue.coffee
@@ -40,16 +40,10 @@
   getIssueUrlForSystem: ->
     new Promise (resolve, reject) =>
       @getIssueUrl().then (issueUrl) ->
-        if UserUtilities.getPlatform() is 'win32'
-          # win32 can only handle a 2048 length link, so we use the shortener.
-          $.ajax 'https://git.io',
-            type: 'POST'
-            data: url: issueUrl
-            success: (data, status, xhr) ->
-              resolve(xhr.getResponseHeader('Location'))
-            error: -> resolve(issueUrl)
-        else
-          resolve(issueUrl)
+        issueUrl = encodeURIComponent(issueUrl)
+        $.ajax "https://api-ssl.bitly.com/v3/shorten?access_token=04ffc90d972077e493f9958177db31410cb23973&format=json&longUrl=#{issueUrl}",
+          success: (data) -> resolve(data.data.url)
+          error: -> resolve(issueUrl)
       return

   getIssueUrl: ->


cleanup

cleanup

--- js/nodes.coffee
+++ js/nodes.coffee
@@ -350,9 +350,6 @@
       s = f()
      catch e
       s = e.message
-     Weya elem: options.elem, context: this, ->
-      @$.elem = @div "##{PREFIX}#{@$.id}.html", null
-     @elem.innerHTML = s
     else if @lang is 'coffee'
      try
       c = CoffeeScript.compile "return (#{@text})"
@@ -360,9 +357,6 @@
       s = f()
      catch e
       s = e.message
-     Weya elem: options.elem, context: this, ->
-      @$.elem = @div "##{PREFIX}#{@$.id}.html", null
-     @elem.innerHTML = s
     else if @lang is 'weya'
      try
       w = "Weya.markup {}, ->\n"
@@ -374,15 +368,13 @@
       s = f()
      catch e
       s = e.message
-     Weya elem: options.elem, context: this, ->
-      @$.elem = @div "##{PREFIX}#{@$.id}.html", null
-     @elem.innerHTML = s
-
-    else
-     Weya elem: options.elem, context: this, ->
-      @$.elem = @div "##{PREFIX}#{@$.id}.html", null
-
-     @elem.innerHTML = @text
+    else
+     s = @text
+
+    Weya elem: options.elem, context: this, ->
+     @$.elem = @div "##{PREFIX}#{@$.id}.html", null
+
+    @elem.innerHTML = s

   class HtmlInline extends Node


Code Style

Code Style

--- main/static/src/script/common/upload.coffee
+++ main/static/src/script/common/upload.coffee
@@ -19,10 +19,10 @@
         @drop_area.on 'dragover', @file_drag_hover
         @drop_area.on 'dragleave', @file_drag_hover
         @drop_area.on 'drop', (e) =>
-          @file_select_handler(e)
+          @file_select_handler e
         @drop_area.show()

-      window.onbeforeunload = () =>
+      window.onbeforeunload = =>
         if @confirm_message? and @active_files > 0
           return @confirm_message

@@ -32,9 +32,9 @@
       e.stopPropagation()
       e.preventDefault()
       if e.type is 'dragover'
-        @drop_area.addClass('drag-hover')
+        @drop_area.addClass 'drag-hover'
       else
-        @drop_area.removeClass('drag-hover')
+        @drop_area.removeClass 'drag-hover'

     file_select_handler: (e) =>
       @file_drag_hover(e)
@@ -45,17 +45,17 @@
     upload_files: (files) =>
       @get_upload_urls files.length, (error, urls) =>
         if error
-          console.log('Error getting URLs', error)
+          console.log 'Error getting URLs', error
           return
-        @process_files(files, urls, 0)
+        @process_files files, urls, 0

     get_upload_urls: (n, callback) =>
       return if n <= 0
-      api_call 'GET', @upload_url, {count:n}, (error, result) ->
+      api_call 'GET', @upload_url, {count: n}, (error, result) ->
         if error
-          callback(error)
+          callback error
           throw error
-        callback(undefined, result)
+        callback undefined, result

     process_files: (files, urls, i) =>
       return if i >= files.length
@@ -66,34 +66,34 @@
       xhr = new XMLHttpRequest()
       if @allowed_types?.length > 0
         if file.type not in @allowed_types
-          progress(0, undefined, 'wrong_type')
+          progress 0, undefined, 'wrong_type'
           callback()
           return

       if @max_size?
         if file.size > @max_size
-          progress(0, undefined, 'too_big')
+          progress 0, undefined, 'too_big'
           callback()
           return

       @active_files += 1

       xhr.upload.addEventListener 'progress', (event) ->
-        progress(parseInt(event.loaded / event.total * 100.0))
+        progress parseInt event.loaded / event.total * 100.0

       xhr.onreadystatechange = (event) =>
         if xhr.readyState == 4
           if xhr.status == 200
             response = JSON.parse(xhr.responseText)
-            progress(100.0, response.result)
+            progress 100.0, response.result
             @active_files -= 1
           else
-            progress(0, undefined, 'error')
+            progress 0, undefined, 'error'
             @active_files -= 1

-      xhr.open('POST', url, true)
+      xhr.open 'POST', url, true
       data = new FormData()
-      data.append('file', file)
-      xhr.send(data)
+      data.append 'file', file
+      xhr.send data
       callback()
 )()


better exit ui

better exit ui

--- lib/connection/process.coffee
+++ lib/connection/process.coffee
@@ -11,7 +11,8 @@
     comm.booting()
     @proc = process.spawn @jlpath(), [@jlargs()..., '-e', "import Atom; @sync Atom.connect(#{port})"]
     @proc.on 'exit', (code, signal) =>
-      console.log "Julia Exit: #{code}, #{signal}"
+      cons.err "Julia has stopped: #{code}, #{signal}"
+      cons.input()
       @proc = null
     @proc.stdout.on 'data', (data) =>
       text = data.toString().trim()


Adding some cache & avoid multiple instance of the same command #25

--- lib/services/php-proxy.coffee
+++ lib/services/php-proxy.coffee
@@ -1,4 +1,5 @@
 exec = require "child_process"
+process = require "process"
 config = require "../config.coffee"
 md5 = require 'MD5'
 fs = require 'fs'
@@ -10,6 +11,8 @@
   parent: [],
   composer: null

+currentProcesses = []
+
 ###*
  * Executes a command to PHP proxy
  * @param  {string}  command Command to exectue
@@ -23,8 +26,14 @@
         c.replace(/\\/g, '\\\\')

       try
-        stdout = exec.spawnSync(config.config.php, [__dirname + "/../../php/parser.php", directory.path].concat(command)).output[1].toString('ascii')
-        res = JSON.parse(stdout)
+        # avoid multiple processes of the same command
+        if not currentProcesses[command]?
+          currentProcesses[command] = true
+
+          stdout = exec.spawnSync(config.config.php, [__dirname + "/../../php/parser.php",  directory.path].concat(command)).output[1].toString('ascii')
+
+          delete currentProcesses[command]
+          res = JSON.parse(stdout)
       catch err
         console.log err
         res =
@@ -40,11 +49,13 @@
     else
       command.replace(/\\/g, '\\\\')

-      console.log 'Building index'
-      exec.exec(config.config.php + " " + __dirname + "/../../php/parser.php " + directory.path + " " + command, (error, stdout, stderr) ->
-        console.log 'Build done'
-        return []
-      )
+      if not currentProcesses[command]?
+        console.log 'Building index'
+        currentProcesses[command] = exec.exec(config.config.php + " " + __dirname + "/../../php/parser.php " + directory.path + " " +   command, (error, stdout, stderr) ->
+          delete currentProcesses[command]
+          console.log 'Build done'
+          return []
+        )

 ###*
  * Reads an index by its name (file in indexes/index.[name].json)
@@ -105,6 +116,7 @@
     data =
       error: false,
       statics: [],
+      autocomplete: [],
       methods: [],
       parent: [],
       composer: null
@@ -176,8 +188,13 @@
    * @return {array}
   ###
   autocomplete: (className, name) ->
-    res = execute(["--autocomplete", className, name], false)
-    return res
+    cacheKey = className + "." + name
+
+    if not data.autocomplete[cacheKey]?
+      res = execute(["--autocomplete", className, name], false)
+      data.autocomplete[cacheKey] = res
+
+    return data.autocomplete[cacheKey]

   autoloadClassMap: () ->
       res = execute("--autoloadClassMap", false)


Remove quantity in stock and requested on new order view

--- app/assets/javascripts/orders.coffee
+++ app/assets/javascripts/orders.coffee
@@ -60,7 +60,7 @@
 populateQuantity = (current_quantity, requested_quantity, element) ->
   available_quantity = parseInt(current_quantity) - parseInt(requested_quantity)
   element.val("")
-  element.attr("placeholder", "#{available_quantity} available | #{current_quantity} in stock | #{requested_quantity} requested")
+  element.attr("placeholder", "#{available_quantity} available")
   element.attr("data-guard", "required int")
   element.attr("data-guard-int-min", "1")
   element.attr("data-guard-int-max", available_quantity)


consistency

consistency

--- src/formats/inline.coffee
+++ src/formats/inline.coffee
@@ -33,14 +33,14 @@

 class InlineCode extends Parchment.Inline
   @blotName: 'inline-code'
-  @tagName: 'code'
+  @tagName: 'CODE'

 # TODO: implement exclusitivity

 class Script extends Parchment.Inline
   @blotName: 'script'
-  @tagName: ['sub', 'sup']
+  @tagName: ['SUB', 'SUP']

   constructor: (value) ->
     if (typeof value == 'string')


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/metered.coffee
+++ test/metered.coffee
@@ -6,6 +6,6 @@

   ok obj.meter

-test
+test ''

 module()


useless assignment

useless assignment

--- resources/assets/coffee/_classes/beatmap-discussion-helper.coffee
+++ resources/assets/coffee/_classes/beatmap-discussion-helper.coffee
@@ -205,7 +205,6 @@
     options.forceDiscussionId ?= false

     url = new URL(urlString ? document.location.href)
-    params = url.searchParams
     [__, pathBeatmapsets, beatmapsetId, pathDiscussions, beatmapId, mode, filter] = url.pathname.split '/'

     return if pathBeatmapsets != 'beatmapsets' || pathDiscussions != 'discussion'


remove link to beatmapset in delete event

...because you can't see it anyway.

--- resources/assets/coffee/react/profile-page/recent-activity.coffee
+++ resources/assets/coffee/react/profile-page/recent-activity.coffee
@@ -80,7 +80,7 @@
           className: 'profile-extra-entries__text'
           dangerouslySetInnerHTML:
             __html: osu.trans 'events.beatmapset_delete',
-              beatmapset: link(event.beatmapset.url, event.beatmapset.title)
+              beatmapset: event.beatmapset.title

       when 'beatmapsetRevive'
         text = div


movable velocity and acceleration getters

--- test/movable.coffee
+++ test/movable.coffee
@@ -34,8 +34,8 @@

 test "should increase velocity according to acceleration", ->
   particle = GameObject
-    velocity: Point(0, )
-    acceleration: Point(0, -10)
+    velocity: Point(0, 1)
+    acceleration: Point(0, -60)

   60.times ->
     particle.update(1/60)


Shorten conditional statement

Shorten conditional statement

--- src/property_validator.coffee
+++ src/property_validator.coffee
@@ -7,7 +7,7 @@
   constructor: ({ @inputString, @property, @schemaName, @parent, @scheme }) ->
     @validators = []
     @location = @getLocation()
-    @parent.addRequiredProperty(@property) if @parent?
+    @parent?.addRequiredProperty(@property)
     @addValidations(@inputString)


fix major bug in subject sync

fix major bug in subject sync

--- app/controllers/classifier.coffee
+++ app/controllers/classifier.coffee
@@ -63,7 +63,6 @@
     @viewer = new Viewer({el: @el.find('.viewer')[0], classifier: @})

     # Setup events
-    @bind 'start', @start
     @bind 'tutorial', @startTutorial

     User.on 'change', @onUserChange
@@ -107,6 +106,13 @@

   onUserChange: (e, user) =>

+    # Reset subjects
+    Subject.instances = []
+    $('.subjects').empty()
+    Subject.off 'fetch', @onFetch
+    Subject.off 'select', @onSubjectSelect
+    Subject.off 'no-more', @onNoMoreSubjects
+
     # Set default counts
     @nClassified  = 0
     @nPotentials  = 0
@@ -122,7 +128,7 @@
         @nClassified  = project.classification_count or 0
         @nPotentials  = project.annotation_count or 0
         @nFavorites   = project.favorite_count or 0
-        @trigger 'start'
+        @start()
     else
       @trigger 'tutorial'

@@ -143,25 +149,19 @@
       $(".zootorial-blocker").remove()
       $(".zootorial-focuser").remove()

-    Subject.instances = []
-    @el.find('.subjects').empty()
-
     # Initial fetch for subjects
     Subject.next()

-  onTutorialComplete: (e) =>
-    console.log 'onTutorialComplete'
-
   onTutorialEnd: (e) ->
     $("circle[r='50']").remove()

   startTutorial: =>
+
     # Create tutorial and bind to handlers
     @tutorial = new Tutorial
       id: 'tutorial'
       firstStep: 'welcome'
       steps: TutorialSteps
-    @tutorial.el.bind('complete-tutorial', @onTutorialComplete)
     @tutorial.el.bind('end-tutorial', @onTutorialEnd)

     # Move tutorial to classifier div
@@ -225,7 +225,7 @@
         training:
           type: 'lens'
           x: 100
-          y: 85
+          y: 355
         id: 'CFHTLS_079_2328'
       tutorial: true
       zooniverse_id: 'ASW0000002'
@@ -641,7 +641,7 @@
       training = @classification.subject.metadata.training
       trainingType = training.type
       x = (training.x + 30) / @subjectDimension
-      y = training.y / @subjectDimension
+      y = 1 - training.y / @subjectDimension

       if trainingType in ['lens', 'lensed galaxy', 'lensed quasar']

@@ -662,30 +662,31 @@
                 header: 'Nice catch!'
                 details: "You found a simulated #{trainingType}!"
                 attachment: "left center .primary #{x} #{y}"
-                blocks: '.primary .controls'
+                blocks: '.annotation .controls'
+                className: 'arrow-left'
                 nextButton: 'Next image'
                 next: true
                 onExit: =>
                   @submit(e)
           @tutorial.start()
-        else
-          @tutorial = new Tutorial
-            id: 'sim-missed'
-            firstStep: 'missed'
-            steps:
-              length: 1
-
-              missed: new Step
-                number: 1
-                header: 'Whoops!'
-                details: "You missed a simulated #{trainingType}!  Don't worry, let's move to the next image."
-                attachment: "left center .primary #{x} #{y}"
-                blocks: '.primary .controls'
-                nextButton: 'Next image'
-                next: true
-                onExit: =>
-                  @submit(e)
-          @tutorial.start()
+        # else
+        #   @tutorial = new Tutorial
+        #     id: 'sim-missed'
+        #     firstStep: 'missed'
+        #     steps:
+        #       length: 1
+        #
+        #       missed: new Step
+        #         number: 1
+        #         header: 'Whoops!'
+        #         details: "You missed a simulated #{trainingType}!  Don't worry, let's move to the next image."
+        #         attachment: "left center .primary #{x} #{y}"
+        #         blocks: '.annotation .controls'
+        #         nextButton: 'Next image'
+        #         next: true
+        #         onExit: =>
+        #           @submit(e)
+        #   @tutorial.start()

       else
         # Subject is an empty field
@@ -701,7 +702,7 @@
                 header: 'There is no gravitational lens in this field!'
                 details: "This is a different kind of Training Image, one that has already been inspected by the Science Team and found to contain nothing interesting."
                 attachment: 'center center .primary center center'
-                blocks: '.primary'
+                blocks: '.annotation'
                 nextButton: 'Next image'
                 next: true
                 onExit: =>
@@ -718,7 +719,7 @@
                 header: 'Nice! There is no gravitational lens in this field!'
                 details: "This is a different kind of Training Image, one that has already been inspected by the Science Team and found to contain nothing interesting."
                 attachment: 'center center .primary center center'
-                blocks: '.primary'
+                blocks: '.annotation'
                 nextButton: 'Next image'
                 next: true
                 onExit: =>


improve logging

improve logging

--- assets/scripts/app/views/log.coffee
+++ assets/scripts/app/views/log.coffee
@@ -127,9 +127,11 @@

 Log.Logger = ->
 Log.Logger.prototype = $.extend new Log.Listener,
-  # receive: (log, number) ->
-  #   console.log 'log engine: receive', arguments
-  remove: (log, ids) ->
-    console.log 'log engine: remove', ids
-  insert: (log, after, nodes) ->
-    console.log "log engine: insert", after, nodes
+  receive: (log, num, string) ->
+    @log("rcv #{num} #{JSON.stringify(string)}")
+  insert: (log, after, datas) ->
+    @log("ins #{datas.map((data) -> data.id).join(', ')}, after: #{after || '?'}, #{JSON.stringify(datas)}")
+  remove: (log, id) ->
+    @log("rem #{id}")
+  log: (line) ->
+    console.log(line)


remove unused commented out code

remove unused commented out code

--- examples/passing.coffee
+++ examples/passing.coffee
@@ -38,41 +38,3 @@
           context2.pass()

-#Testify.test "allfail", (context0) ->
-
-  #context0.test "allfail sync fail", ->
-    #assert.ok(false)
-
-  #context0.test "allfail async", (context1) ->
-    #process.nextTick ->
-      #context1.fail("Intentional failure")
-
-  #context0.test "allfail async.mixed", (context1) ->
-    #saneTimeout 300, ->
-
-      #context1.test "allfail async.mixed sync", ->
-        #assert.ok(false)
-
-      #context1.test "allfail async.mixed async", (context2) ->
-        #saneTimeout 300, ->
-          #context2.fail("Intentional failure")
-
-      #context1.test "allfail async.mixed error", ->
-        #throw new Error("hello!")
-
-  #context0.test "allfail async.sync", (context1) ->
-    #saneTimeout 300, ->
-
-      #context1.test "allfail async.sync 1", ->
-        #assert.ok(false)
-
-      #context1.test "allfail async sync 2", ->
-        #assert.ok(false)
-
-  #context0.test "allfail async.async", (context1) ->
-    #saneTimeout 300, ->
-      #context1.test "allfail async.async async", (context2) ->
-        #saneTimeout 300, ->
-          #context2.fail("Intentional Failure")
-
-


Coffeescript style

Coffeescript style

--- lib/python-tools.coffee
+++ lib/python-tools.coffee
@@ -104,11 +104,10 @@
             initialColumn: column
             searchAllPanes: true

-          atom.workspace.open(first_def['path'], options).then((editor) ->
+          atom.workspace.open(first_def['path'], options).then (editor) ->
             # scroll to top first to get it centered correctly
             editor.scrollToTop()
             editor.scrollToCursorPosition()
-          )
       else
         atom.notifications.addError(
           "python-tools error. #{@_issueReportLink}", {


Add first and last functions

Add first and last functions

Allow user to go directly to first or last slide with `Home` or `End` key

--- coffee/ruban.coffee
+++ coffee/ruban.coffee
@@ -36,6 +36,8 @@
   bindKeys: ->
     key('right, down, space, return, j, l, pagedown', @next)
     key('left, up, backspace, k, h, pageup', @prev)
+    key('home', @first)
+    key('last', @last)

   bindGestures: ->
     Hammer(document, {
@@ -109,6 +111,13 @@

   highlight: ->
     hljs.initHighlightingOnLoad()
+
+  first: =>
+    @firstSlide()
+
+  firstSlide: ->
+    $first = @$current.prevAll('section:first-child')
+    @go($first, direction: 'backward')

   prev: =>
     if @hasSteps()
@@ -130,6 +139,13 @@
         @prevStep()
     else
       @prevSlide()
+
+  last: =>
+    @lastSlide()
+
+  lastSlide: ->
+    $last = @$current.nextAll('section:last-child')
+    @go($last, direction: 'forward')

   next: =>
     if @hasSteps()


Add 520 status

Add 520 status

--- server/testSystems/informatics/InformaticsSubmitDownloader.coffee
+++ server/testSystems/informatics/InformaticsSubmitDownloader.coffee
@@ -34,6 +34,7 @@
     96: "Тестирование...",
     98: "Компилирование..."
     377: "Задача в очереди на тестирование"
+    520: "Ошибка проверки, переотправьте"

 export default class InformaticsSubmitDownloader extends TestSystemSubmitDownloader


Adapt aceEditor to use new spell check manager with adapter

--- services/web/public/coffee/ide/editor/directives/aceEditor.coffee
+++ services/web/public/coffee/ide/editor/directives/aceEditor.coffee
@@ -7,6 +7,7 @@
 	"ide/editor/directives/aceEditor/undo/UndoManager"
 	"ide/editor/directives/aceEditor/auto-complete/AutoCompleteManager"
 	"ide/editor/directives/aceEditor/spell-check/SpellCheckManager"
+	"ide/editor/directives/aceEditor/spell-check/HighlightedWordManager"
 	"ide/editor/directives/aceEditor/highlights/HighlightsManager"
 	"ide/editor/directives/aceEditor/cursor-position/CursorPositionManager"
 	"ide/editor/directives/aceEditor/track-changes/TrackChangesManager"
@@ -15,7 +16,7 @@
 	"ide/graphics/services/graphics"
 	"ide/preamble/services/preamble"
     "ide/files/services/files"
-], (App, Ace, SearchBox, Vim, ModeList, UndoManager, AutoCompleteManager, SpellCheckManager, HighlightsManager, CursorPositionManager, TrackChangesManager, MetadataManager) ->
+], (App, Ace, SearchBox, Vim, ModeList, UndoManager, AutoCompleteManager, SpellCheckManager, HighlightedWordManager, HighlightsManager, CursorPositionManager, TrackChangesManager, MetadataManager) ->
 	EditSession = ace.require('ace/edit_session').EditSession
 	ModeList = ace.require('ace/ext/modelist')
 	Vim = ace.require('ace/keyboard/vim').Vim
@@ -103,7 +104,8 @@

 				if scope.spellCheck # only enable spellcheck when explicitly required
 					spellCheckCache = $cacheFactory.get("spellCheck-#{scope.name}") || $cacheFactory("spellCheck-#{scope.name}", {capacity: 1000})
-					spellCheckManager = new SpellCheckManager(scope, editor, element, spellCheckCache, $http, $q)
+					spellCheckManager = new SpellCheckManager(scope, spellCheckCache, $http, $q, new SpellCheckAdapter(editor))
+
 				undoManager           = new UndoManager(scope, editor, element)
 				highlightsManager     = new HighlightsManager(scope, editor, element)
 				cursorPositionManager = new CursorPositionManager(scope, editor, element, localStorage)
@@ -361,6 +363,19 @@
 						session.setScrollTop(session.getScrollTop() + 1)
 						session.setScrollTop(session.getScrollTop() - 1)

+				onSessionChange = (e) ->
+					spellCheckManager.onSessionChange()
+					e.oldSession?.getDocument().off "change", spellCheckManager.onChange
+					e.session.getDocument().on "change", spellCheckManager.onChange
+
+				attachToSpellCheck = () ->
+					spellCheckManager.init()
+					editor.on 'changeSession', onSessionChange
+					onSessionChange({ session: editor.getSession() }) # Force initial setup
+
+				detachFromSpellCheck = () ->
+					editor.off 'changeSession', onSessionChange
+
 				attachToAce = (sharejs_doc) ->
 					lines = sharejs_doc.getSnapshot().split("\n")
 					session = editor.getSession()
@@ -406,6 +421,7 @@
 					editor.initing = false
 					# now ready to edit document
 					editor.setReadOnly(scope.readOnly) # respect the readOnly setting, normally false
+					attachToSpellCheck()

 					resetScrollMargins()

@@ -467,6 +483,7 @@

 				scope.$on '$destroy', () ->
 					if scope.sharejsDoc?
+						detachFromSpellCheck()
 						detachFromAce(scope.sharejsDoc)
 						session = editor.getSession()
 						session?.destroy()
@@ -585,3 +602,9 @@
 		SearchBox::$init = () ->
 			@element = $compile(searchHtml)($rootScope.$new())[0];
 			$init.apply(@)
+
+	class SpellCheckAdapter
+		constructor: (@editor) ->
+			@wordManager = new HighlightedWordManager(@editor)
+		getLines: () -> @editor.getValue().split('\n')
+		normalizeChangeEvent: (e) -> e


fold aceEditor resetSession into session set up

it is not used elsewhere, and mode setting is now done during the
initialisation.

--- services/web/public/coffee/ide/editor/directives/aceEditor.coffee
+++ services/web/public/coffee/ide/editor/directives/aceEditor.coffee
@@ -207,11 +207,6 @@

 				editor.setOption("scrollPastEnd", true)

-				resetSession = () ->
-					session = editor.getSession()
-					session.setUseWrapMode(true)
-					session.setMode("ace/mode/latex")
-
 				updateCount = 0
 				onChange = () ->
 					updateCount++
@@ -235,6 +230,7 @@
 					editor.setSession(new EditSession(lines, mode))

 					session = editor.getSession()
+					session.setUseWrapMode(true)

 					doc = session.getDocument()
 					doc.on "change", onChange


forcefully update view when a packet is claimed

--- app/js/controllers/packets.coffee
+++ app/js/controllers/packets.coffee
@@ -48,6 +48,7 @@

     .then (succ) ->
       $scope.refresh_recent_packets()
+      $scope.$apply()
     , () ->
       # cancelled, do nothing


Improved formatting of customMessage for notifications

--- src/helpers/logger.coffee
+++ src/helpers/logger.coffee
@@ -76,7 +76,9 @@
       if msg
         if robot and robot.adapter? and robot.adapter.customMessage?
           attachment = {
+            username: robot.name,
             channel: channel,
+            fallback: msg.replace(/\W/g, ''),
             text: msg
           }
           robot.adapter.customMessage attachment


Imagemin: Add grunt-watch and grunt-newer.

--- src/imagemin/index.coffee
+++ src/imagemin/index.coffee
@@ -39,6 +39,9 @@
 					dest: 'images/'
 				]
 		gf.addTask 'default', ['imagemin']
+		gf.addWatcher 'imagemin',
+			files: 'images_src/**/*.{png,jpg,gif}'
+			tasks: 'newer:imagemin'

 	# svgmin
 	unless not @svg or gf.hasSection 'svgmin'
@@ -51,10 +54,13 @@
 					dest: 'images/'
 				]
 		gf.addTask 'default', ['svgmin']
+		gf.addWatcher 'svgmin',
+			files: 'images_src/**/*.svg'
+			tasks: 'newer:svgmin'

 	gf.save()

 Generator::dependencies = ->
-	modules = ['grunt', 'load-grunt-tasks', 'grunt-contrib-imagemin']
+	modules = ['grunt', 'load-grunt-tasks', 'grunt-contrib-imagemin', 'grunt-contrib-watch', 'grunt-newer']
 	modules.push 'grunt-svgmin'  if @svg
 	@installFromNpm modules


:bug: Fix error raised when updating minimap for a destroyed buffer

--- lib/minimap-render-view.coffee
+++ lib/minimap-render-view.coffee
@@ -110,6 +110,7 @@
   # Performs an update of the minimap.
   update: =>
     return unless @editorView?
+    return if @buffer.isDestroyed()

     #reset canvas virtual width/height
     @lineCanvas[0].width = @lineCanvas[0].offsetWidth * devicePixelRatio


Fuuuuu OSX Lowercase FS

Fuuuuu OSX Lowercase FS

--- lib/updateIfAuthorized.coffee
+++ lib/updateIfAuthorized.coffee
@@ -3,7 +3,7 @@
 module.exports = (fromDevice, query, params, callback=_.noop, dependencies={}) ->
   securityImpl = dependencies.securityImpl ? require('./getSecurityImpl')
   getDevice = dependencies.getDevice ? require('./getDevice')
-  Device = dependencies.Device ? require('./models/Device')
+  Device = dependencies.Device ? require('./models/device')

   getDevice query.uuid, (error, toDevice) =>
     securityImpl.canConfigure fromDevice, toDevice, query, (error, permission) =>


Use faster event that text is changed

Use faster event that text is changed

--- lib/Refactoring.coffee
+++ lib/Refactoring.coffee
@@ -28,7 +28,7 @@
     @ripper.destruct()

     @editor.off 'grammar-changed', @checkGrammar
-    @editor.off 'contents-modified', @parse
+    @editor.buffer.off 'changed', @parse

     delete @editor
     delete @ripper
@@ -42,9 +42,9 @@
     isCoffee = @editor.getGrammar().name is 'CoffeeScript'
     return if isCoffee is @isCoffee

-    @editor.off 'contents-modified', @parse
+    @editor.buffer.off 'changed', @parse
     if isCoffee
-      @editor.on 'contents-modified', @parse
+      @editor.buffer.on 'changed', @parse
       @parse()

@@ -83,5 +83,5 @@
   ###

   parse: =>
-    @ripper.parse @editor.getText()
+    @ripper.parse @editor.buffer.getText()
     @emit 'parsed'


if not editing then set the default to be the folder view

--- web/volume/spreadsheet.coffee
+++ web/volume/spreadsheet.coffee
@@ -365,7 +365,7 @@
             if $scope.views[i].name == 'participant'
               partindex = true
             i++
-          if partindex == false
+          if !Editing or partindex == false
             $scope.views.unshift pseudoCategory.slot
           else
             $scope.views.splice 1, 0, pseudoCategory.slot


Fixed a bug.

Fixed a bug.

--- app/views/play/ladder_view.coffee
+++ app/views/play/ladder_view.coffee
@@ -20,7 +20,7 @@
   constructor: (level, options) ->
     super()
     options ?= {}
-   @url = "/db/level/#{level.get('original')}.#{level.get('version').major}/leaderboard?#{$.param(options)}"
+    @url = "/db/level/#{level.get('original')}.#{level.get('version').major}/leaderboard?#{$.param(options)}"
     #@url = "/db/level/#{level.get('original')}/leaderboard?#{$.param(options)}"

@@ -58,7 +58,7 @@
     @leaderboards = {}
     @challengers = {}
     for team in teams
-      teamSession = _.find @sessions.models, (session) -> session.get('team') is team
+#      teamSession = _.find @sessions.models, (session) -> session.get('team') is team
       teamSession = null
       console.log "Team session: #{JSON.stringify teamSession}"
       @leaderboards[team] = new LeaderboardData(@level, team, teamSession)


Removed logs in render code

Removed logs in render code

--- src/io/web.coffee
+++ src/io/web.coffee
@@ -132,8 +132,6 @@
 		@camera.x //= 1
 		@camera.y //= 1

-		log 'Hello world! Render time!'
-
 		mapSymbols =
 			'#': 'wall'
 			'.': 'floor'
@@ -158,8 +156,6 @@

 		@renderEntities x, y, entities
 		@renderEffects x, y
-
-		log 'Done rendering!!'

 	renderEntities: (x, y, entities) ->
 		for e in entities when @camera.target.canSee e


Call afterTagOpen at end of openTag

Call afterTagOpen at end of openTag

--- lib/symbols-view.coffee
+++ lib/symbols-view.coffee
@@ -42,13 +42,10 @@
       @openTag(tag)

   openTag: (tag) ->
-    editor = atom.workspace.getActiveEditor()
-    if editor
+    if editor = atom.workspace.getActiveEditor()
       previous =
         position: editor.getCursorBufferPosition()
         file: editor.getUri()
-
-      @afterTagOpen?(previous)

     position = tag.position
     position = @getTagLine(tag) unless position
@@ -57,6 +54,8 @@
         @moveToPosition(position) if position
     else if position
       @moveToPosition(position)
+
+    @afterTagOpen?(previous) if previous?

   moveToPosition: (position, beginningOfLine=true) ->
     editorView = atom.workspaceView.getActiveView()


Remove sortable list class      Because sortable list was only being used by tab bar view, I got rid of the class hierarchy.

--- lib/tab-bar-view.coffee
+++ lib/tab-bar-view.coffee
@@ -1,15 +1,18 @@
 $ = require 'jquery'
+{View} = require 'space-pen'
 _ = require 'underscore'
-SortableList = require 'sortable-list'
 TabView = require './tab-view'

 module.exports =
-class TabBarView extends SortableList
+class TabBarView extends View
   @content: ->
-    @ul class: "tabs #{@viewClass()}"
+    @ul class: "tabs sortable-list"

   initialize: (@pane) ->
-    super
+    @on 'dragstart', '.sortable', @onDragStart
+    @on 'dragend', '.sortable', @onDragEnd
+    @on 'dragover', @onDragOver
+    @on 'drop', @onDrop

     @paneContainer = @pane.getContainer()
     @addTabForItem(item) for item in @pane.getItems()
@@ -74,13 +77,39 @@
     (@paneContainer.getPanes().length > 1) or (@pane.getItems().length > 1)

   onDragStart: (event) =>
-    super
+    unless @shouldAllowDrag(event)
+      event.preventDefault()
+      return
+
+    el = $(event.target).closest('.sortable')
+    el.addClass 'is-dragging'
+    event.originalEvent.dataTransfer.setData 'sortable-index', el.index()
+
     pane = $(event.target).closest('.pane')
     paneIndex = @paneContainer.indexOfPane(pane)
     event.originalEvent.dataTransfer.setData 'from-pane-index', paneIndex

+  onDragEnd: (event) =>
+    @find(".is-dragging").removeClass 'is-dragging'
+
+  onDragOver: (event) =>
+    event.preventDefault()
+    currentDropTargetIndex = @find(".is-drop-target").index()
+    newDropTargetIndex = @getDropTargetIndex(event)
+
+    if newDropTargetIndex != currentDropTargetIndex
+      @children().removeClass 'is-drop-target drop-target-is-after'
+      sortableObjects = @find(".sortable")
+      if newDropTargetIndex < sortableObjects.length
+        sortableObjects.eq(newDropTargetIndex).addClass 'is-drop-target'
+      else
+        sortableObjects.eq(newDropTargetIndex - 1).addClass 'drop-target-is-after'
+
+
   onDrop: (event) =>
-    super
+    event.stopPropagation()
+    @children('.is-drop-target').removeClass 'is-drop-target'
+    @children('.drop-target-is-after').removeClass 'drop-target-is-after'

     dataTransfer  = event.originalEvent.dataTransfer
     fromIndex     = parseInt(dataTransfer.getData('sortable-index'))
@@ -98,3 +127,16 @@
       fromPane.moveItemToPane(item, toPane, toIndex--)
     toPane.showItem(item)
     toPane.focus()
+
+  getDropTargetIndex: (event) ->
+    el = $(event.target).closest('.sortable')
+    el = $(event.target).find('.sortable').last()  if el.length == 0
+
+    elementCenter = el.offset().left + el.width() / 2
+
+    if event.originalEvent.pageX < elementCenter
+      el.index()
+    else if el.next().length > 0
+      el.next().index()
+    else
+      el.index() + 1


Bugfixes

Bugfixes

--- app/partials/field-guide-choice.cjsx
+++ app/partials/field-guide-choice.cjsx
@@ -47,7 +47,7 @@

-
+

@@ -76,7 +76,7 @@

Trail Camera Images

-        {images.map (src) ->
+        {images.map (src, i) ->

}


Fix month

Fix month

--- views/akashic-resource-chart.cjsx
+++ views/akashic-resource-chart.cjsx
@@ -6,10 +6,10 @@

 toDateLabel = (datetime) ->
   date = new Date(datetime)
-  "#{date.getFullYear()}/#{date.getMonth()}/#{date.getDate()}-#{date.getHours()}"
+  "#{date.getFullYear()}/#{date.getMonth() + 1}/#{date.getDate()}-#{date.getHours()}"
 toDateString = (datetime)->
   date = new Date(datetime)
-  "#{date.getFullYear()}/#{date.getMonth()}/#{date.getDate()}"
+  "#{date.getFullYear()}/#{date.getMonth() + 1}/#{date.getDate()}"

 AkashicResourceChart = React.createClass
   getInitialState: ->


host not required for exec

host not required for exec


Implement bower sink specification facility

--- library/membrane/bower.coffee
+++ library/membrane/bower.coffee
@@ -69,6 +69,21 @@
             target: target
             entry: entry

+    # Either get or set the bower sink directory name. If no args
+    # supplied the method will return the automatically deduced the
+    # bower sink. If you supply an argument the method will set it
+    # as a bower sink and later will return it, unless overriden by
+    # the global configuration. See the implementation for the info.
+    @bowerSink: (sink) ->
+        assert hash = crypto.createHash "md5"
+        id = hash.update(@identify()).digest "hex"
+        automatic = => global or @$bowerSink or id
+        global = nconf.get "bower:global_sink_dir"
+        return automatic() if arguments.length is 0
+        assert _.isString(sink), "has to be a string"
+        assert not _.isEmpty(sink), "got empty sink"
+        return @$bowerSink = sink.toString()
+
     # A hook that will be called prior to registering the service
     # implementation. Please refer to this prototype signature for
     # information on the parameters it accepts. Beware, this hook
@@ -77,7 +92,7 @@
     register: (kernel, router, next) ->
         hash = crypto.createHash "md5"
         hash.update @constructor.identify()
-        id = hash.digest("hex").toString()
+        assert id = @constructor.bowerSink()
         bowerings = @constructor.bowerings ?= []
         options = _.map(bowerings, (b) -> b.options)
         options = _.merge Object.create({}), options...


implement the kernel preemption

implement the kernel preemption

--- library/nucleus/kernel.coffee
+++ library/nucleus/kernel.coffee
@@ -70,6 +70,13 @@
     # Please use this static method instead of manually launching up.
     # Refer to the static method `makeKernelSetup` for information.
     @bootstrap: (options={}) -> new this @makeKernelSetup options
+
+    # The kernel preemption routine is called once the kernel has
+    # passed the initial launching and configuration phase, but is
+    # yet to start up the router, connect services and instantiate
+    # an actual application. This method gets passes continuation
+    # that does that. The method can either invoke it or omit it.
+    kernelPreemption: (continuation) -> continuation.apply this

     # Either get or set an identica token. This token is application
     # identification string of a free form, but usually formed by a
@@ -123,8 +130,9 @@
     # then override this static method, rather than the `bootstrap`.
     # Be careful about the relations between methods when overriding.
     @makeKernelSetup: (options) -> ->
+        assert @options = _.cloneDeep options
         @broker = new content.JsonBroker this
-        assert _.isObject(options), "no options"
+        assert _.isObject(@options), "no options"
         manifest = "Using %s as instance identica"
         message = "Booted up framework kernel instance"
         sigint = "Received the SIGINT (interrupt signal)"
@@ -133,13 +141,14 @@
         process.on "SIGTERM", => @shutdownKernel sigterm
         assert _.isObject @setupScaffolding()
         this.constructor.configure().apply this
-        assert not _.isEmpty @setupConnectPipeline()
-        assert not _.isEmpty @setupListeningServers()
-        assert not _.isEmpty @setupSocketServers()
-        assert not _.isEmpty @setupHotloadWatcher()
-        assert identica = @constructor.identica()
-        logger.info manifest, identica.bold
-        logger.info message.red; return @
+        return @kernelPreemption.call this, =>
+            assert not _.isEmpty @setupConnectPipeline()
+            assert not _.isEmpty @setupListeningServers()
+            assert not _.isEmpty @setupSocketServers()
+            assert not _.isEmpty @setupHotloadWatcher()
+            assert identica = @constructor.identica()
+            logger.info manifest, identica.bold
+            logger.info message.red; return @

     # The public constructor of the kernel instrances. Generally
     # you should neither use it directly, not override. It serves


minor test

minor test

--- src/server/src/Tickets.coffee
+++ src/server/src/Tickets.coffee
@@ -12,9 +12,11 @@
   map_Lodges: ->
     ids = ['OK413','OK414','OK415','OK416','OK426','OK427','OK428','OK309','OK310','OK312','OK313','OK314','OK315','OK316','OK319','OK320','OK321']
     data =
-      ids: ids
-      lodges: {}
+      ids       : ids
+      lodges    : {}
+      pre_summit: {}

+    # Summit tickets
     tickets_Data = @.file_Json_Tickets.load_Json()
     need_Room    = []
     for key,value of tickets_Data.by_Ticket
@@ -32,6 +34,10 @@
       data.lodges[id] =
         id   : id
         names: []
+
+    # PreSummit
+    #for key,value of @.jekyll_Data.participants_Data
+    #  console.log key

     @.jekyll_Data.save_Data data, @.file_Json_Lodges, @.file_Yaml_Lodges
     return data


Make docs consistent with other commands.

Both isup and mobilefriendly use <domain>, we will do that too.

--- src/scripts/pingdom-tools.coffee
+++ src/scripts/pingdom-tools.coffee
@@ -8,8 +8,8 @@
 #   none
 #
 # Commands:
-#   hubot is github.com (fast|slow)?
-#   hubot (pagetest|fpt) github.com
+#   hubot is <domain> (fast|slow)? - Request a Pingdom pagetest report on <domain>
+#   hubot (pagetest|fpt) <domain> - Request a Pingdom pagetest report on <domain>

 class PingdomTools


runner: Fix .node->.process in addedge

runner: Fix .node->.process in addedge


Use pre-compiled parser when available

Use pre-compiled parser when available

--- lib/snippet-body-parser.coffee
+++ lib/snippet-body-parser.coffee
@@ -1,4 +1,10 @@
-PEG = require 'pegjs'
-{fs} = require 'atom'
-grammarSrc = fs.readFileSync(require.resolve('./snippet-body.pegjs'), 'utf8')
-module.exports = PEG.buildParser(grammarSrc, trackLineAndColumn: true)
+try
+  parser = require './snippet-body'
+catch
+  {fs} = require 'atom'
+  PEG = require 'pegjs'
+
+  grammarSrc = fs.readFileSync(require.resolve('./snippet-body.pegjs'), 'utf8')
+  parser = PEG.buildParser(grammarSrc, trackLineAndColumn: true)
+
+module.exports = parser


Use Atom as FileDescription

Use Atom as FileDescription

This is the text presented when the app is presented in a select list.

Closes #3179

--- build/tasks/set-version-task.coffee
+++ build/tasks/set-version-task.coffee
@@ -45,8 +45,8 @@

         strings =
           CompanyName: 'GitHub, Inc.'
-          FileDescription: 'The hackable editor'
-          LegalCopyright: 'Copyright (C) 2013 GitHub, Inc. All rights reserved'
+          FileDescription: 'Atom'
+          LegalCopyright: 'Copyright (C) 2014 GitHub, Inc. All rights reserved'
           ProductName: 'Atom'
           ProductVersion: version


Pan vs Pinch

Pan vs Pinch

--- framer/GestureInputRecognizer.coffee
+++ framer/GestureInputRecognizer.coffee
@@ -16,12 +16,13 @@
 		@session = null

 	touchstart: (event) =>
-
+		return if event.touches.length > 1
 		@em.wrap(window).addEventListener("touchmove", @touchmove)
 		@em.wrap(window).addEventListener("touchend", @touchend)

 		@session =
 			startEvent: @_getGestureEvent(event)
+			startMultiEvent: null
 			startTime: Date.now()
 			pressTimer: window.setTimeout(@longpressstart, 250)
 			started: {}
@@ -41,6 +42,7 @@
 		@_process(@_getGestureEvent(event))

 	touchend: (event) =>
+		return if event.touches.length != 0
 		@em.wrap(window).removeEventListener("touchmove", @touchmove)
 		@em.wrap(window).removeEventListener("touchend", @touchend)
 		event = @_getGestureEvent(event)
@@ -189,12 +191,16 @@

 		return unless @session

+		# if event.fingers > 1 and not not @session.startMultiEvent
+		# 	@session.startMultiEvent = event
+		# if event.fingers == 1
+		# 	@session.startMultiEvent = null
+
 		# Detect pan events
-		if event.fingers == 1
-			if not @session.started.pan and (Math.abs(event.offset.x) > 0 or Math.abs(event.offset.y) > 0)
-				@panstart(event)
-			else if @session.started.pan
-				@pan(event)
+		if not @session.started.pan and (Math.abs(event.offset.x) > 0 or Math.abs(event.offset.y) > 0)
+			@panstart(event)
+		else if @session.started.pan
+			@pan(event)

 		# Detect pinch, rotate and scale events
 		if @session.started.pinch and event.fingers == 1
@@ -249,7 +255,13 @@
 		if @session?.lastEvent
 			event.deltaTime = event.time - @session.lastEvent.time
 			event.previous = @session.lastEvent.point
-			event.delta = Utils.pointSubtract(event.point, event.previous)
+
+			if @session.lastEvent.fingers != event.fingers
+				event.delta = {x:0, y:0}
+			else if @session.lastEvent.fingers == event.fingers == 2
+				event.delta = Utils.pointSubtract(event.touchCenter, @session.lastEvent.touchCenter)
+			else
+				event.delta = Utils.pointSubtract(event.point, event.previous)

 		if event.touches.length > 0


terminal: add template content to init file, to explain whats going on

--- src/smc-webapp/console.coffee
+++ src/smc-webapp/console.coffee
@@ -42,6 +42,24 @@
 IS_MOBILE = feature.IS_MOBILE

 CSI = String.fromCharCode(0x9b)
+
+initfile_content = (fn) ->
+    """# This initialization file is associated with your terminal in #{fn}.
+# It is automatically run whenever it starts up -- restart it via Ctrl-d and Return-key.
+
+# Usually, your ~/.bashrc is executed and this behavior is emulated for completeness:
+source ~/.bashrc
+
+# You can export environment variables, e.g. to set custom GIT_* variables
+#export GIT_AUTHOR_NAME="Your Name"
+#export GIT_AUTHOR_EMAIL="your@email.address"
+
+# It is also possible to start a program ...
+#top
+
+# ... or even define a terminal specific function.
+#hello () { echo "hello world"; }
+"""

 codemirror_renderer = (start, end) ->
     terminal = @
@@ -640,7 +658,18 @@

         @element.find("a[href=#initfile]").click () =>
             initfn = misc.console_init_filename(@opts.filename)
-            @opts.editor?.editor.project_page.open_file(path:initfn)
+            content = initfile_content(initfn)
+            {salvus_client} = require('./salvus_client')
+            salvus_client.exec
+                    project_id  : @opts.editor?.editor.project_id
+                    command     : "test ! -r '#{initfn}' && echo '#{content}' > '#{initfn}'"
+                    bash        : true
+                    err_on_exit : false
+                    cb          : (err, output) =>
+                        if err
+                            console.log("ERROR creating initfile:", err)
+                        else
+                            @opts.editor?.editor.project_page.open_file(path:initfn)

     _init_input_line: () =>
         #if not IS_MOBILE


Add wrapped terminal

Add wrapped terminal

--- src/smc-webapp/console.coffee
+++ src/smc-webapp/console.coffee
@@ -139,8 +139,6 @@

         if @opts.project_id
             @project_id = @opts.project_id
-        else
-            @project_id = @opts.editor?.editor.project_id

         @_project_actions = smc.redux.getProjectActions(@project_id)

@@ -695,7 +693,7 @@
             content = initfile_content(@opts.filename)
             {salvus_client} = require('./salvus_client')
             salvus_client.exec
-                project_id  : @opts.editor?.editor.project_id
+                project_id  : @project_id
                 command     : "test ! -r '#{initfn}' && echo '#{content}' > '#{initfn}'"
                 bash        : true
                 err_on_exit : false
@@ -717,7 +715,7 @@
         content = initfile_content(@opts.filename)
         {salvus_client} = require('./salvus_client')
         salvus_client.exec
-            project_id  : @opts.editor?.editor.project_id
+            project_id  : @project_id
             command     : "test ! -r '#{initfn}' && echo '#{content}' > '#{initfn}'"
             bash        : true
             err_on_exit : false


hub: fix broken compile+run

hub: fix broken compile+run

--- src/smc-hub/postgres-base.coffee
+++ src/smc-hub/postgres-base.coffee
@@ -22,9 +22,6 @@
 ###
 PostgreSQL -- basic queries and database interface
 ###
-
-# Make loading typescript just work.
-require('ts-node').register({ cacheDirectory: process.env.HOME + '/.ts-node-cache' })

 exports.DEBUG = true


Fix tab hover border shape

Fix tab hover border shape

--- src/smc-webapp/project_page.cjsx
+++ src/smc-webapp/project_page.cjsx
@@ -42,10 +42,10 @@
     getInitialState : () ->
         x_hovered : false

-    mouse_over: ->
+    mouse_over_x: ->
         @setState(x_hovered:true)

-    mouse_out: ->
+    mouse_out_x: ->
         @setState(x_hovered:false)
         @actions({project_id:@props.project_id}).clear_ghost_file_tabs()

@@ -90,7 +90,7 @@

                     {<Icon
-                        onMouseOver={@mouse_over} onMouseOut={@mouse_out}
+                        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}
@@ -298,9 +298,17 @@
         page_styles ='
             #smc-file-tabs-fixed>li>a {
                 padding: 8px 10px;
+                border-bottom-left-radius:0px;
+                border-bottom-right-radius:0px;
+                border-top-left-radius:5px;
+                border-top-right-radius:5px;
             }
             #smc-file-tabs-files>li>a {
                 padding: 13px 15px 7px;
+                border-bottom-left-radius:0px;
+                border-bottom-right-radius:0px;
+                border-top-left-radius:5px;
+                border-top-right-radius:5px;
             }
             .smc-file-tab-floating {
                 background-color: rgb(237, 237, 237);


use new Ink API for onDidDestroy callback

--- lib/runtime/evaluation.coffee
+++ lib/runtime/evaluation.coffee
@@ -71,12 +71,8 @@
     @errorLines?.lights.destroy()
     lights = @ink.highlights.errorLines (file: file, line: line-1 for {file, line} in lines)
     @errorLines = {r, lights}
-
-    destroyResult = r.destroy.bind r
-    r.destroy = =>
-      if @errorLines?.r == r
-        @errorLines.lights.destroy()
-      destroyResult()
+    r.onDidDestroy =>
+      if @errorLines?.r == r then @errorLines.lights.destroy()

   # Working Directory


Client: Login: add setting custom data to password reset form

--- client/Login/AppController.coffee
+++ client/Login/AppController.coffee
@@ -3,18 +3,23 @@
   handler = (callback)->-> KD.singleton('appManager').open 'Login', callback

   KD.registerAppClass this,
-    name                    : "Login"
-    routes                  :
-      '/:name?/Login'       : handler (app)-> app.getView().animateToForm 'login'
-      '/:name?/Redeem'      : handler (app)-> app.getView().animateToForm 'redeem'
-      '/:name?/Register'    : handler (app)-> app.getView().animateToForm 'register'
-      '/:name?/Recover'     : handler (app)-> app.getView().animateToForm 'recover'
-      '/:name?/ResendToken' : handler (app)-> app.getView().animateToForm 'resendEmail'
-    hiddenHandle            : yes
-    behavior                : 'application'
-    preCondition            :
-      condition             : (options, cb)-> cb not KD.isLoggedIn()
-      failure               : -> KD.getSingleton('router').handleRoute "/Activity"
+    name                        : "Login"
+    routes                      :
+      '/:name?/Login'           : handler (app)-> app.getView().animateToForm 'login'
+      '/:name?/Redeem'          : handler (app)-> app.getView().animateToForm 'redeem'
+      '/:name?/Register'        : handler (app)-> app.getView().animateToForm 'register'
+      '/:name?/Recover'         : handler (app)-> app.getView().animateToForm 'recover'
+      '/:name?/Reset/:token'    : ({params:{token}})->
+        do handler (app)=>
+          app.getView().setCustomDataToForm('reset', {recoveryToken:token})
+          app.getView().animateToForm('reset')
+
+      '/:name?/ResendToken'     : handler (app)-> app.getView().animateToForm 'resendEmail'
+    hiddenHandle                : yes
+    behavior                    : 'application'
+    preCondition                :
+      condition                 : (options, cb)-> cb yes #not KD.isLoggedIn()
+      failure                   : -> KD.getSingleton('router').handleRoute "/Activity"

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


Remove TODO statement

Remove TODO statement

--- lib/text-editor-parser.coffee
+++ lib/text-editor-parser.coffee
@@ -64,7 +64,6 @@
     return new FunctionInfo(functionText, functionRange)

   getFunctionInfoForTab: (startingRow) ->
-    # TODO handle if there is a line break with no tab
     tabText = @textEditor.getTabText()

     regExp = new RegExp("#{tabText}", "g")


Minor cleanups

Minor cleanups

--- src/tools/remote.proto.coffee
+++ src/tools/remote.proto.coffee
@@ -21,7 +21,7 @@
     }#{file}"

 exports.fetch = ->
-  echo "Fetching <#{uri = @uri()}>..."
+  echo "Fetching:", uri = @uri()
   ajax.dl uri, @msi true

 exports.extract = ->
@@ -37,11 +37,10 @@
   fs.DeleteFolder extract2

 exports.shortcuts = ->
-  echo "Creating shortcuts..."
-
-  if 'node' != @dist
-    fs.CopyFile fs.BuildPath(@dst, "#{@dist}.exe"),
-      fs.BuildPath(@dst, "node.exe")
+  return if 'node' == @dist
+  echo "Creating shortcut..."
+  fs.CopyFile fs.BuildPath(@dst, "#{@dist}.exe"),
+    fs.BuildPath(@dst, "node.exe")

 exports.prefix = ->
   echo "Adjusting NPM prefix..."
@@ -55,7 +54,7 @@
   npmrc.Close()

 exports.use = ->
-  echo "Using #{ver = @ver()}..."
+  echo "Using:", ver = @ver()
   junction.exec ver

 exports.set64= (is64)->


now support importing online pdf files

now support importing online pdf files

--- lib/file-import.coffee
+++ lib/file-import.coffee
@@ -5,6 +5,9 @@
 less = null
 loophole = null
 subjects = require './custom-comment.coffee'
+temp = null
+DOWNLOADS_TEMP_FOLDER = null
+md5 = null

 {protocolsWhiteListRegExp} = require('./protocols-whitelist')
 PDF = null
@@ -39,6 +42,27 @@
   output += '  '
   output

+downloadFileIfNecessary = (filePath)->
+  new Promise (resolve, reject)->
+    if !filePath.match(/^https?\:\/\//)
+      return resolve(filePath)
+
+    request ?= require 'request'
+    temp ?= require('temp').track()
+    md5 ?= require('md5')
+
+    DOWNLOADS_TEMP_FOLDER = temp.mkdirSync('mpe_downloads')
+    request.get {url: filePath, encoding: 'binary'}, (error, response, body)->
+      if error
+        return reject(error)
+      else
+        localFilePath = path.resolve(DOWNLOADS_TEMP_FOLDER, md5(filePath)) + path.extname(filePath)
+        fs.writeFile localFilePath, body, 'binary', (error)->
+          if error
+            return reject error
+          else
+            return resolve localFilePath
+
 loadFile = (filePath, {imageDirectoryPath, fileDirectoryPath}, filesCache={})->
   new Promise (resolve, reject)->
     if filesCache[filePath]
@@ -56,13 +80,17 @@
             else
               resolve(output.css or '')
     else if filePath.endsWith('.pdf') # pdf file
-      PDF ?= require('./pdf')
-      PDF.toSVGMarkdown filePath, {svgDirectoryPath: imageDirectoryPath, markdownDirectoryPath: fileDirectoryPath}, (error, svgMarkdown)->
-        if error
-          reject error
-        else
-          resolve(svgMarkdown)
-    else if filePath.match(/https?\:\/\//) # online file
+      downloadFileIfNecessary(filePath)
+      .then (localFilePath)->
+        PDF ?= require('./pdf')
+        PDF.toSVGMarkdown localFilePath, {svgDirectoryPath: imageDirectoryPath, markdownDirectoryPath: fileDirectoryPath}, (error, svgMarkdown)->
+          if error
+            return reject error
+          else
+            return resolve(svgMarkdown)
+      # .catch (error)->
+      #  return reject(error)
+    else if filePath.match(/^https?\:\/\//) # online file
       # github
       if filePath.startsWith 'https://github.com/'
         filePath = filePath.replace('https://github.com/', 'https://raw.githubusercontent.com/').replace('/blob/', '/')


Add assertion to debug NaN startRow on TextEditorPresenter

Signed-off-by: Nathan Sobo <2e8aa918660411855c6d44d5bb2da677aa033255@github.com>

--- src/text-editor-presenter.coffee
+++ src/text-editor-presenter.coffee
@@ -622,6 +622,18 @@
     return unless @scrollTop? and @lineHeight?

     @startRow = Math.max(0, @lineTopIndex.rowForPixelPosition(@scrollTop))
+    atom.assert(
+      Number.isFinite(@startRow),
+      'Invalid start row',
+      (error) =>
+        error.metadata = {
+          startRow: @startRow?.toString(),
+          scrollTop: @scrollTop?.toString(),
+          scrollHeight: @scrollHeight?.toString(),
+          clientHeight: @clientHeight?.toString(),
+          lineHeight: @lineHeight?.toString()
+        }
+    )

   updateEndRow: ->
     return unless @scrollTop? and @lineHeight? and @height?


Extract method: #gitUrl

Extract method: #gitUrl

--- lib/github-file.coffee
+++ lib/github-file.coffee
@@ -9,16 +9,18 @@
   constructor: (@editSession) ->

   open: ->
-    gitUrl = git.getRepo().getConfigValue("remote.#{@remoteName()}.url")
-    return unless gitUrl? # TODO Log/notify if we're returning here?
+    return unless @gitUrl() # TODO Log/notify if we're returning here?

-    repoUrl = @githubRepoUrl(gitUrl)
+    repoUrl = @githubRepoUrl(@gitUrl())
     return unless repoUrl? # TODO Log/notify if we're returning here?

     blobUrl = "#{repoUrl}/blob/#{@branch()}/#{@filePath()}"

     child_process.exec "open #{blobUrl}", (error, stdout, stderr) ->
       throw error if error?
+
+  gitUrl: ->
+    git.getRepo().getConfigValue("remote.#{@remoteName()}.url")

   githubRepoUrl: (gitUrl) ->
     if gitUrl.match /https:\/\/github.com\// # e.g., https://github.com/foo/bar.git


Pass useCapture argument for compatibility with older browsers that require it

--- src/turbolinks/history.coffee
+++ src/turbolinks/history.coffee
@@ -3,6 +3,7 @@
 addEventListener "load", ->
   Turbolinks.defer ->
     pageLoaded = true
+, false

 class Turbolinks.History
   constructor: (@delegate) ->


change interface to for_all_types to execute done() func

--- v1meta.coffee
+++ v1meta.coffee
@@ -126,11 +126,12 @@
         constructor: (@server) ->
             @global_cache = {}

-        for_all_types: (callback) ->
+        for_all_types: (options) ->
             @server.get_meta_xml {asset_type_name: ''}, (err, meta_xml) =>
-                if not err?
-                    meta_xml.iter 'AssetType', (asset_xml) =>
-                        callback(@build_asset_class_from_xml(asset_xml))
+                return options.error(err) if err?
+                meta_xml.iter 'AssetType', (asset_xml) =>
+                    options.asset_callback(@build_asset_class_from_xml(asset_xml))
+                options.done()

         build_asset_class_from_xml: (xml) ->
             asset_type_name = xml.get('name')


Minor syntactic touch ups in the augment module

--- library/nucleus/augment.coffee
+++ library/nucleus/augment.coffee
@@ -53,7 +53,7 @@
     constructor: (@resource, @foundation) ->
         @service = class extends @foundation
         @service.nick = @resource.unescape()
-        @emit("construct", @resource, @service)
+        @emit "construct", @resource, @service
         @service.publish @foundation.EVERYWHERE
         @service.domain @foundation.ANY
         @service.resource @resource
@@ -106,6 +106,6 @@
         regexify = (s) -> new RegExp "^#{RegExp.escape(s)}$"
         resource = regexify resource  if _.isString resource
         notRegexp = "The #{inspected} is not a valid regular expression"
-        throw new Error(notRegexp) unless _.isRegExp resource
+        throw new Error notRegexp unless _.isRegExp resource
         return augment if augment = storage[resource.source]
         storage[resource.source] = new Augment resource, foundation


refactor @associate in the broker

refactor @associate in the broker

--- library/nucleus/content.coffee
+++ library/nucleus/content.coffee
@@ -53,11 +53,13 @@
     # and response pair. If specific negotiator cannot handle the
     # pair, it should return anything other than a function object.
     @associate: (negotiator) ->
-        assert registry = @registry or []
         isValid = try _.isFunction negotiator
-        invalid = "checker is not valid method"
+        assert registry = @registry or Array()
+        invalid = "negotiator is not a function"
         throw new Error invalid unless isValid
         @registry = registry.concat negotiator
+        assert negotiator in (@registry or [])
+        assert negotiator.apply?; negotiator

     # Content negotiate the request/response pair to use the correct
     # protocol. The protocol is implemented by the content negotiator


for #19 - implement has_one

for #19 - implement has_one

--- lib/minimongoid.coffee
+++ lib/minimongoid.coffee
@@ -89,6 +89,26 @@
           # e.g. where {user_id: @id}
           if global[class_name]
             HasManyRelation.fromRelation(global[class_name].where(mod_selector, options), foreign_key, @id)
+
+    # set up has_one methods, e.g. user.recipes()
+    for has_one in @constructor.has_one
+      relation = has_one.name
+      selector = {}
+      unless foreign_key = has_one.foreign_key
+        foreign_key = "#{_.singularize(@constructor.to_s().toLowerCase())}_id"
+      if @constructor._object_id
+        selector[foreign_key] = new Meteor.Collection.ObjectID @id
+      else
+        selector[foreign_key] = @id
+      # set up default class name, e.g. "has_one: user" ==> 'User'
+      class_name = has_one.class_name || _.titleize(relation)
+      @[relation] = do(relation, selector, class_name) ->
+        (mod_selector = {}, options = {}) ->
+          # first consider any passed in selector options
+          mod_selector = _.extend mod_selector, selector
+          # e.g. where {user_id: @id}
+          if global[class_name]
+            global[class_name].first(mod_selector, options)

     # set up HABTM methods, e.g. user.friends()
@@ -117,7 +137,8 @@

   # /--------------------
-  # DEPRECATED: r() and related() methods
+  # DEPRECATED: r() and related() methods
+  #             Also does not support has_one.
   # --------------------

   # alias to @related
@@ -283,6 +304,7 @@

   @belongs_to: []
   @has_many: []
+  @has_one: []
   @has_and_belongs_to_many: []

   @embedded_in: null


Fix end-of-block <br> rendering

Fix end-of-block <br> rendering

--- src/trix/views/block_view.coffee
+++ src/trix/views/block_view.coffee
@@ -17,10 +17,7 @@
     container.trixIndex = @blockIndex

     if @block.isEmpty()
-      br = document.createElement("br")
-      br.trixPosition = 0
-      br.trixLength = 1
-      br.trixIndex = @blockIndex
+      br = @createBRElementForPosition(0)
       container.appendChild(br)
     else
       @createElementsForText()
@@ -86,12 +83,11 @@
   # A single <br> at the end of a block element has no visual representation
   # so add an extra one.
   createExtraNewlineElement: ->
-    if string = @currentRun?.string
-      if /\n$/.test(string)
-        @currentRun = { string: "\n", position: @text.getLength() }
-        node = @createStringNodesForCurrentRun()[0]
-        @cacheNode(node)
-        @elements.push(node)
+    if string = @block.toString()
+      # A newline followed by the block break newline
+      if /\n\n$/.test(string)
+        br = @createBRElementForPosition(string.length - 1)
+        @elements.push(br)

   createAttachmentElementForCurrentRun: ->
     {attachment, attributes, position, piece} = @currentRun
@@ -117,11 +113,8 @@

     for substring, index in string.split("\n")
       if index > 0
-        node = document.createElement("br")
-        node.trixPosition = position
-        node.trixIndex = @blockIndex
-        position += 1
-        node.trixLength = 1
+        node = @createBRElementForPosition(position)
+        position++
         nodes.push(node)

       if length = substring.length
@@ -133,6 +126,13 @@
         nodes.push(node)

     nodes
+
+  createBRElementForPosition: (position) ->
+    element = document.createElement("br")
+    element.trixPosition = position
+    element.trixLength = 1
+    element.trixIndex = @blockIndex
+    element

   createElementsForAttributes = (attributes, parentAttribute) ->
     elements = []


added color options

added color options

--- isoblocks.coffee
+++ isoblocks.coffee
@@ -19,7 +19,7 @@

 onKeyPress = (e) ->
 	if e.key is 13 or e.keyCode is 13
-		if iso then iso.generate $(e.target).val(), 320, 500
+		if iso then iso.generate $(e.target).val(), 320, 500, $("input[name=color]:checked").map( -> return this.value; ).get();

 class Cube
 	constructor: ->
@@ -30,7 +30,7 @@
 class Colors
 	constructor: ->

-	@colors: ['yellow', 'green', '']
+	@colors: ['yellow', 'gren']

 	@getRandomColor: ->
 		Colors.colors[Math.floor Math.random() * Colors.colors.length]
@@ -42,6 +42,9 @@
 	# Starting y cordinate on the screen
 	origin_y: 0

+	# Colors for the blocks as CSS class string
+	colors: ''
+
 	# Spacing between 2 characters in terms of number of blocks
 	character_spacing: 1

@@ -51,14 +54,18 @@
 	# Current cube index in the @cubes array to be used for drawing
 	current_cube_index: 0

+	# Should blocks be single colored or not.
+	mono: true
+
 	# An HTML string template for a block
-	cube_template: '

'
+	cube_template: '

'

 	###
 	Constructor
-	@param	num_cubes	Number 	Number of cubes to pre-generate
+	@param	num_cubes	Number 	Number of cubes to pre-generate. Default is 450.
+	@param	mono 		Boolean	Should block be single colored. Default is true.
 	###
-	constructor: (num_cubes = 450) ->
+	constructor: (num_cubes = 450, @mono = true) ->
 		@preGenerateCubes num_cubes

 	###
@@ -66,16 +73,19 @@
 	@params	start_x	Number 	Starting x cordinate of the string
 	@params	start_y	Number 	Starting y cordinate of the string
 	###
-	generate: (s, start_x, start_y) ->
+	generate: (s, start_x, start_y, colors) ->
 		@origin_x = start_x
 		@origin_y = start_y
+		@colors = ''
+		# Make a class string from the colors array and cache it
+		@colors = (color.toLowerCase().replace(/^/,' iso_color_') for color in colors) if colors?

 		# Initialize the @current_cube_index to start using the cubes from end for current string
 		@current_cube_index = @cubes.length - 1

 		# Hide all cubes
 		for cube in @cubes
-			$(cube).addClass('unused')#.removeClass('iso_color_yellow iso_color_green')
+			$(cube).addClass('unused')

 		current_row = 0
 		current_col = 0
@@ -129,9 +139,16 @@
 						cube = @cubes[@current_cube_index]
 						cube.style.left = pos_x + 'px'
 						cube.style.top = pos_y + 'px'
+						# Remove previous color class, pick a new random color class and add it
+						class_string = $(cube).attr 'class'
+						class_string = class_string.replace(/iso_color_\w+/g, '')
+						if @colors.length
+							new_color = @colors[Math.floor Math.random() * @colors.length]
+							class_string = class_string.concat(" #{new_color}")
+						console.log class_string
+						$(cube).removeClass().addClass class_string
 						cube.style.zIndex = z
 						$(cube).removeClass 'unused'
-						#$(cube).addClass 'iso_color_' + color
 						@current_cube_index--
 					else
 						console.warn 'Cubes got Over!'
@@ -149,13 +166,10 @@

 			current_cube = current_cube.replace '@left', (150 + Math.random() * (window.screen.width-400))
 			current_cube = current_cube.replace '@top', (200 + Math.random() * (window.screen.height - 500))
-			#current_cube = current_cube.replace '@color', 'iso_color_' + Colors.getRandomColor()
+			#current_cube = current_cube.replace '@color', 'iso_color_' + Colors.getRandomColor() unless @mono
 			html_string += current_cube
-			#console.log current_cube

 		# Insert the cubes into the DOM. Uses innerHTML for faster DOM Insertion.
-
-		#console.log html_string
 		if html_string then iso_container.innerHTML += html_string

 		# Get the cube references


Throwing an error when trying to set a get-only property

--- framer/BaseClass.coffee
+++ framer/BaseClass.coffee
@@ -24,9 +24,9 @@
 		if @ isnt BaseClass
 			@_addDescriptor(propertyName, descriptor)

-		# if not descriptor.set?
-		# 	descriptor.set = (value) ->
-		# 		throw Error("#{@constructor.name}.#{propertyName} is readonly")
+		if not descriptor.set?
+			descriptor.set = (value) ->
+				throw Error("#{@constructor.name}.#{propertyName} is readonly")

 		# Define the property on the prototype
 		Object.defineProperty(@prototype, propertyName, descriptor)


Added new "All Pick" game_mode index.

Added new "All Pick" game_mode index.

--- public/src/services/steam-api/steam-api-dota-match-service.coffee
+++ public/src/services/steam-api/steam-api-dota-match-service.coffee
@@ -27,6 +27,7 @@
         13: 'New Player Pool'
         14: 'Compendium Matchmaking'
         16: 'Captains Draft'
+        22: 'All Pick'
       }

       constructor: (matchId) ->


:bug: Just another try to make it work on windows

--- lib/typechecker.coffee
+++ lib/typechecker.coffee
@@ -8,6 +8,7 @@
   ActiveFile = Editor?.getPath()
   ScrollSubscription = null
   ScrollTimeout = null
+  Path = require 'path'
   class TypeChecker
     @activate:->
       return unless not Main.Status.TypeChecker
@@ -47,12 +48,21 @@
       ScrollSubscription?.dispose()
     @Lint:->
       Main.V.H.exec(['--json'],null,ActiveFile).then (result)=>
+        if result.stderr.substr(0,5) is 'Error'
+          setTimeout ->
+            throw new Error(result.stderr.substr(7)); # Throw an error that can not be caught
+          ,0
+          return ;
         try
           result = JSON.parse(result.stderr.substr(result.stderr.indexOf('{')))
         catch error
           console.log "Invalid JSON"
           console.log result
         Errors = result.errors
+        if Main.V.H.config.type is 'remote'
+          Errors.forEach (ErrorEntry)->
+            ErrorEntry.message.forEach (ErrorMessageEntry)->
+              ErrorMessageEntry.path = ErrorMessageEntry.path.replace(Main.V.H.config.remoteDir, atom.project.getPath()).split('/').join(Path.sep)
         try
           @ProcessErrors()
         catch error
@@ -74,7 +84,9 @@
           Color = if LeFirst then 'red' else 'blue'
           LeErrors.push new Main.V.TE(I,TraceEntry.path,TraceEntry.line,TraceEntry.start,TraceEntry.end,Color,Error.message)
           LeFirst = false
-      try setTimeout @OnScroll.bind(this),70
+      setTimeout =>
+        try @OnScroll.call(this)
+      ,70
     @OnScroll:->
       RowStart = EditorView.getFirstVisibleScreenRow()
       RowEnd = EditorView.getLastVisibleScreenRow()


Conditionally include activePane/activePaneItem properties

--- src/pane-container.coffee
+++ src/pane-container.coffee
@@ -1,7 +1,8 @@
 {find, flatten} = require 'underscore-plus'
-{Model} = require 'theorist'
+Grim = require 'grim'
 {Emitter, CompositeDisposable} = require 'event-kit'
 Serializable = require 'serializable'
+Model = require './model'
 Pane = require './pane'
 PaneElement = require './pane-element'
 PaneContainerElement = require './pane-container-element'
@@ -18,15 +19,7 @@

   @version: 1

-  @properties
-    activePane: null
-
   root: null
-
-  @behavior 'activePaneItem', ->
-    @$activePane
-      .switch((activePane) -> activePane?.$activeItem)
-      .distinctUntilChanged()

   constructor: (params) ->
     super
@@ -236,3 +229,15 @@

   removedPaneItem: (item) ->
     @itemRegistry.removeItem(item)
+
+if Grim.includeDeprecatedAPIs
+  PaneContainer.properties
+    activePane: null
+
+  PaneContainer.behavior 'activePaneItem', ->
+    @$activePane
+      .switch((activePane) -> activePane?.$activeItem)
+      .distinctUntilChanged()
+
+else
+  PaneContainer::activePane = null