Support KR sprite importing

Support KR sprite importing

--- app/lib/sprites/SpriteParser.coffee
+++ app/lib/sprites/SpriteParser.coffee
@@ -183,6 +183,9 @@
         name = node.parent?.left?.property?.name
         if name
           expression = node.parent.parent
+          unless expression.parent?.right?.right
+            if /frame_[\d]+/.test name  # Skip some useless KR function things
+              return
           kind = expression.parent.right.right.callee.property.name
           statement = node.parent.parent.parent.parent
           statementIndex = _.indexOf statement.parent.body, statement
@@ -321,6 +324,9 @@
       if name.search('shape') isnt -1 and shape.fc is 'rgba(0,0,0,0.451)' and not shape.ss and not shape.sc
         console.log 'Skipping a shadow', name, shape, 'because we\'re doing shadows separately now.'
         return
+      #if name.search('shape') isnt -1 and shape.fc is 'rgba(0,0,0,0.498)' and not shape.ss and not shape.sc
+      #  console.log 'Skipping a KR shadow', name, shape, 'because we\'re doing shadows separately now.'
+      #  return
       shapeKeys.push shapeKey = @addShape shape
       localShape = {bn: name, gn: shapeKey}
       localShape.m = mask if mask
@@ -400,6 +406,7 @@
         argsSource = argsSource.replace(/mask/g, 'this.mask') # so the mask thing will be handled correctly as a blockName in the next line
         argsSource = argsSource.replace(/this\.([a-z_0-9]+)/ig, '"$1"') # turns this.shape literal to 'shape' string
         argsSource = argsSource.replace(/cjs(.+)\)/, '"createjs$1)"') # turns cjs.Ease.get(0.5)
+        argsSource = '{}' if argsSource is 'this' # not sure what this should be but it looks like we don't need it for KR sprites

         args = eval "[#{argsSource}]"
         shadowTween = args[0]?.search?('shape') is 0 and not _.find(localShapes, bn: args[0])


Added clean up rules for Walled Village to State.doCleanupPhase

--- gameState.coffee
+++ gameState.coffee
@@ -572,6 +572,17 @@

   # Handle all the things that happen at the end of the turn.
   doCleanupPhase: () ->
+    # Clean up Walled Villages first
+    actionCardsInPlay = 0
+    for card in @current.inPlay
+      if card.isAction
+        actionCardsinPlay += 1
+
+    if actionCardsInPlay =< 2
+      while c['Walled Village'] in @current.inPlay
+        transferCardToTop(c['Walled Village'], @current.inPlay, @current.draw)
+        state.log("#{@ai} returns a Walled Village to the top of the deck.")
+
     # Discard old duration cards.
     @current.discard = @current.discard.concat @current.duration
     @current.duration = []


Check user cookie before accessing user

Check user cookie before accessing user

--- server/testSystems/Informatics.coffee
+++ server/testSystems/Informatics.coffee
@@ -33,14 +33,12 @@
 class LoggedInformaticsUser
     @getUser: (username, password) ->
         key = username + "::" + password
-        if not userCache[key] or (new Date() - userCache[key].loginTime > 1000 * 60 * 60)
+        if not userCache[key] or not (await userCache[key].getId())
             logger.info "Creating new InformaticsUser ", username
             newUser = new LoggedInformaticsUser(username, password)
             await newUser._login()
-            userCache[key] =
-                user: newUser
-                loginTime: new Date()
-        return userCache[key].user
+            userCache[key] = newUser
+        return userCache[key]

     constructor: (@username, @password) ->
         @jar = request.jar()
@@ -59,20 +57,24 @@
                 followAllRedirects: true,
                 timeout: 30 * 1000
             })
-            document = (new JSDOM(page)).window.document
-            el = document.getElementsByClassName("logininfo")
-            if el.length == 0 or el[0].children.length == 0
-                throw "Can't log user #{username} in"
-            a = el[0].children[0]
-            id = a.href.match(/view.php\?id=(\d+)/)
-            @name = a.innerHTML
-            if not id or id.length < 2
-                throw "Can't detect id, href=#{a.href} username=#{@username}"
-            logger.info "Logged in user #{@username} href=#{a.href}"
-            @id = id[1]
+            @id = await @getId()
+            if not @id
+                throw "Can not log user #{@username} in"
         catch e
             logger.error "Can not log in new Informatics user #{@username}", e.message, e
-
+
+    getId: () ->
+        page = await download("https://informatics.mccme.ru/", @jar)
+        document = (new JSDOM(page)).window.document
+        el = document.getElementsByClassName("logininfo")
+        if el.length == 0 or el[0].children.length == 0
+            return null
+        a = el[0].children[0]
+        id = a.href.match(/view.php\?id=(\d+)/)
+        @name = a.innerHTML
+        if not id or id.length < 2
+            return null
+        return id[1]

     download: (href, options) ->
         if @requests >= REQUESTS_LIMIT


Added: a tasky to copy assets

Added: a tasky to copy assets

--- client/gulpfile.coffee
+++ client/gulpfile.coffee
@@ -26,6 +26,10 @@
 global = {}

 gulp.task 'setWatch', -> global.isWatching = true
+
+gulp.task 'assets', ->
+    gulp.src ['./app/assets/*', './vendor/assets/*'], base: './'
+        .pipe gulp.dest './public'

 gulp.task 'stylus', ->
   gulp.src ['./vendor/styles/**/*.css', './app/styles/application.styl']
@@ -74,5 +78,6 @@

 gulp.task 'watch', ['setWatch',  'coffee'], ->
     gulp.watch './app/styles/application.styl', ['stylus']
+    gulp.watch './app/assets/**', ['assets']

-gulp.task 'build',   ['stylus', 'coffee']
+gulp.task 'build',   ['assets', 'stylus', 'coffee']


Removed additional current password check.

--- client/settings.coffee
+++ client/settings.coffee
@@ -162,8 +162,6 @@
     return

   try
-    # We check this manually because changePassword error throws global 'Match failed' error if current password is empty string
-    throw new ValidationError 400, "Incorrect password.", 'current-password' unless currentPassword
     Accounts.changePassword currentPassword, newPassword, (error) ->
       formError = new ValidationError error.error, error.reason, 'current-password' if error
       callback formError


Hacky fix for inbox list (it really should be a custom listItem)

--- app/controllers/lists.coffee
+++ app/controllers/lists.coffee
@@ -27,6 +27,13 @@
     List.bind "changeList", @change
     List.bind "destroy", @showInbox
     Task.bind "refresh create update destroy", @updateAll
+
+    # Set up draggable on inbox
+    @inbox.droppable
+      hoverClass: "ui-state-active"
+      drop: (event, ui) =>
+        task = ui.draggable.data("item")
+        task.updateAttribute("list", "inbox")

   new: (e) ->
     val = @input.val()
@@ -69,6 +76,6 @@

   updateAll: =>
     @allCount.text Task.active().length
-    @inboxCount.text Task.list("inbox").length
+    @inboxCount.text Task.active("inbox").length

 module.exports = Lists


save/load bug fixes

save/load bug fixes

--- app/controllers/state.coffee
+++ app/controllers/state.coffee
@@ -22,8 +22,9 @@

     tools = []
     for tool in dashboard.tools
-      pos = tool.el.offset()
+      pos = @getOffset tool
       z_index = @getZIndex tool
+      settings_toggle = @getSettingsToggle tool
       obj = {
           name: tool.name
           className: tool.className
@@ -35,17 +36,17 @@
           binding: tool.bindOptions
           index: tool.count
           pos: pos
-          z_index: parseInt(z_index)
+          z_index: z_index
+          settings_toggle: settings_toggle
         }
-
       tools.push obj

     state = {
-        'tools': tools
+        tools: tools
       }

     $.post 'http://localhost:3001/state', {state: JSON.stringify(state)}, (data) ->
-      console.log 'state saved', data
+      console.log 'id: ', data.id, 'state:', JSON.parse(data.state)

   load: (params) =>
     @trigger 'remove-all-tools'
@@ -58,9 +59,8 @@
       new_tools = []

       # Normalize z-index
-      min_z_index = _.min data.tools, (tool) -> tool.z_index
-      _.each data.tools, (tool) ->
-        tool.z_index -= min_z_index
+      lowest_z_index = (_.min data.tools, (tool) -> tool.z_index).z_index
+      _.each data.tools, (tool) -> tool.z_index -= lowest_z_index

       # Build initial state
       for tool in data.tools
@@ -80,7 +80,6 @@
           when "Statistics" then new_tool = params.dashboard.createTool Statistics, options
           when "WWT" then new_tool = params.dashboard.createTool WWT, options

-        console.log 'NT: ', new_tool
         new_tools.push new_tool

         if _.isUndefined tool.bind_options.params
@@ -101,13 +100,24 @@
       for new_tool in new_tools
         # Set settings
         toolWindow = new_tool.el.closest('.window-container')
+        unless new_tool.settings_toggle
+          toolWindow.find(".settings").removeClass('active')
         toolWindow.find(".data-sources [data-source=#{new_tool.bindOptions.type}]").click()
         toolWindow.find(".source-choices").val(new_tool.bindOptions.source)
         toolWindow.find(".data-points input[name=params]").val(new_tool.bindOptions.params)

   # Helpers
+  getOffset: (tool) ->
+    toolWindow = tool.el.closest('.window-container')
+    toolWindow.offset()
+
   getZIndex: (tool) ->
-    z_index = if _.isNaN(parseInt(tool.el.css('z-index'))) then 0 else tool.el.css('z-index')
+    toolWindow = tool.el.closest('.window-container')
+    z_index = if toolWindow.css('z-index') is 'auto' then 0 else parseInt(toolWindow.css('z-index'))
+
+  getSettingsToggle: (tool) ->
+    toolWindow = tool.el.closest('.window-container')
+    if toolWindow.find('.settings').hasClass('active') then true else false

 module.exports = State


predictor coffescript rendering bugfix

predictor coffescript rendering bugfix

--- app/assets/javascripts/angular/controllers/PredictorCtrl.js.coffee
+++ app/assets/javascripts/angular/controllers/PredictorCtrl.js.coffee
@@ -118,9 +118,10 @@
     total = 0
     _.each(assignments, (assignment)->
       # use raw score to keep weighting calculation on assignment type level
+      debugger
       if assignment.grade.raw_score != null
         total += assignment.grade.raw_score
-      else if ! assignment.pass_fail && ! assignment.has_closed? && includePredicted
+      else if ! assignment.pass_fail && ! assignment.has_closed && includePredicted
         total += assignment.grade.predicted_score
     )
     total


Fix month grammar on life spent watching

--- app/assets/javascripts/controllers/user_index_controller.js.coffee
+++ app/assets/javascripts/controllers/user_index_controller.js.coffee
@@ -140,7 +140,7 @@
     if months > 0
       if str.length > 0
         str += ", "
-      str += months + " " + (if years == 1 then "month" else "months")
+      str += months + " " + (if month == 1 then "month" else "months")
     if days > 0
       if str.length > 0
         str += ", "


[skip] remove unused code

[skip] remove unused code

--- app/assets/javascripts/houston-scheduler/views/project_view.coffee
+++ app/assets/javascripts/houston-scheduler/views/project_view.coffee
@@ -1,9 +1,4 @@
 class Scheduler.ProjectView extends Backbone.View
-
-
-  events:
-    'click .nav > li': 'selectTab'
-

   initialize: ->
@@ -40,7 +35,3 @@

     count = @ticketsWaitingForValueEstimate().length
     $('.tickets-without-value-count').html(count).toggleClass('zero', count == 0)
-
-
-  selectTab: (e)->
-


Fix specs on ns-clean-up-package-manager branch

--- spec/deprecation-cop-view-spec.coffee
+++ spec/deprecation-cop-view-spec.coffee
@@ -8,12 +8,13 @@
   beforeEach ->
     workspaceElement = atom.views.getView(atom.workspace)
     jasmine.attachToDOM(workspaceElement)
-    activationPromise = atom.packages.activatePackage('deprecation-cop')

     expect(Grim.getDeprecationsLength()).toBe 0
-    spyOn(Grim, 'deprecate').andCallThrough()
     deprecatedMethod = -> Grim.deprecate("A test deprecation. This isn't used")
     deprecatedMethod()
+
+    spyOn(Grim, 'deprecate') # Don't fail tests if when using deprecated APIs in deprecation cop's activation
+    activationPromise = atom.packages.activatePackage('deprecation-cop')

     atom.commands.dispatch workspaceElement, 'deprecation-cop:view'

@@ -21,11 +22,10 @@
       activationPromise

     runs ->
+      jasmine.unspy(Grim, 'deprecate')
       deprecationCopView = atom.workspace.getActivePane().getActiveItem()

   afterEach ->
-    # Make sure the only deprecated calls were the ones being tested
-    expect(Grim.deprecate.callCount).toBe 1
     Grim.clearDeprecations()

   it "displays deprecated methods", ->


Allow for local and global macros within regex strings

--- grammars/stata.cson
+++ grammars/stata.cson
@@ -860,9 +860,9 @@
         'beginCaptures':
           '0':
             'name': 'punctuation.definition.string.begin.stata'
-        'end': '(\\})'
+        'end': '\\}'
         'endCaptures':
-          '1':
+          '0':
             'name': 'punctuation.definition.string.end.stata'
         'patterns': [
           {
@@ -878,11 +878,11 @@
         ]
       }
       {
-        'begin': '(\\$)'
+        'begin': '\\$'
         'beginCaptures':
           '0':
             'name': 'punctuation.definition.string.begin.stata'
-        'end': '(?=\\"|\\s|\\n|/|,)'
+        'end': '(?=\\"|\\s|\\n|/|,|\\$)'
         'endCaptures':
           '1':
             'name': 'punctuation.definition.string.end.stata'
@@ -1374,7 +1374,11 @@
     'patterns': [
       # The following match the insides of a regular expression
       {
-        'match': '\\^|\\$'
+        'match': '\\^'
+        'name': 'keyword.control.anchor.stata'
+      }
+      {
+        'match': '\\$(?![a-zA-Z\\{])'
         'name': 'keyword.control.anchor.stata'
       }
       {
@@ -1402,6 +1406,12 @@
       }
       {
         'include': '#ascii-regex-character-class'
+      }
+      {
+        'include': '#macro-local'
+      }
+      {
+        'include': '#macro-global'
       }
       {
         # 'comment' 'NOTE: Error if I have \.+ No idea why but it works fine it seems with just \.'
@@ -1746,7 +1756,11 @@
   'unicode-regex-internals':
     'patterns': [
       {
-        'match': '\\\\[bBAZzG]|\\^|\\$'
+        'match': '\\\\[bBAZzG]|\\^'
+        'name': 'keyword.control.anchor.stata'
+      }
+      {
+        'match': '\\$(?![a-zA-Z\\{])'
         'name': 'keyword.control.anchor.stata'
       }
       {
@@ -1824,6 +1838,12 @@
         'include': '#unicode-regex-character-class'
       }
       {
+        'include': '#macro-local'
+      }
+      {
+        'include': '#macro-global'
+      }
+      {
         # 'comment' 'NOTE: Error if I have \.+ No idea why but it works fine it seems with just \.'
         # Also extremely weird but if I have the previous 'comment' line uncommented, everything inside the regexm(, "...") goes white.
         'match': '.'


removed out-of-date configuration

removed out-of-date configuration

--- cs/observing.coffee
+++ cs/observing.coffee
@@ -73,27 +73,6 @@

             ``convertBack``: a function that takes the value provided by the
             model and converts into something palatable for the element
-
-        Example declaration of bindings:
-
-        bindings:
-            '[name=first-name] keyup': 'firstName'
-            '[name=last-name]': 'lastName'
-
-            '#name':
-                # the '#name' element observes 'firstName' and 'lastName'
-                observes: ['firstName', 'lastName']
-                handler: 'getFullName'
-                interface: 'text'
-
-            '#onTwitter':
-                observes: 'onTwitter'
-                handler: 'visible'
-
-            '#other-name':
-                observes: ['firstName', 'lastName']
-                handler: 'getFancyFullName'
-                interface: 'html'
     ###

     setupBindings: ->


Reset log manager on log rerender

Reset log manager on log rerender

--- assets/scripts/app/views/job.coffee
+++ assets/scripts/app/views/job.coffee
@@ -148,6 +148,7 @@

       version: (->
         @rerender()
+        @set 'logManager', Travis.Log.create(target: this)
       ).observes('log.version')

       logDidChange: (->


adding default values for hypotheses criterion

--- app/models/hypothesis.coffee
+++ app/models/hypothesis.coffee
@@ -7,9 +7,9 @@
   risk:              DS.belongsTo 'item', async: true
   board:             DS.belongsTo 'board', async: true
   method:            DS.attr 'string'
-  criterion_total:   DS.attr 'number'
-  criterion_valid:   DS.attr 'number'
-  criterion_invalid: DS.attr 'number'
+  criterion_total:   DS.attr 'number', defaultValue: 0
+  criterion_valid:   DS.attr 'number', defaultValue: 0
+  criterion_invalid: DS.attr 'number', defaultValue: 0
   ## -1 = invalid; 0 = unfinished validation; 1 = valid!
   status:            DS.attr 'number', defaultValue: 0
   text:              DS.attr 'string'


Don't show empty pages on project nav

Don't show empty pages on project nav

If someone created a page but hasn't added content don't show it

Closes zooniverse/Panoptes#1100

--- app/pages/project/index.cjsx
+++ app/pages/project/index.cjsx
@@ -75,7 +75,7 @@
               <Translate content="project.nav.research" />
             </Link>
             <PromiseRenderer promise={@props.project.get 'pages'}>{(pages) =>
-              pageTitles = pages.reduce(((accum, page) -> accum[page.url_key] = page.title; accum), {})
+              pageTitles = pages.filter((page) -> page.content isnt '' and page.content?).reduce(((accum, page) -> accum[page.url_key] = page.title; accum), {})
               <span>
                 {if pageTitles.result
                   <Link to="project-results" params={params} className="tabbed-content-tab">


Events: Fixed movement_since_touch.

Events: Fixed movement_since_touch.

--- engine/events.coffee
+++ engine/events.coffee
@@ -149,7 +149,7 @@
                 touch.y = t.clientY - root_element.rect.top
                 touch.rel_x = rel_x
                 touch.rel_y = rel_y
-                touch.movement_since_touch = Math.abs(rel_x) + Math.abs(rel_y)
+                touch.movement_since_touch += Math.abs(rel_x) + Math.abs(rel_y)
                 @touch.touch_events[touch.id] = touch
             @touch.touches = event.targetTouches.length


fix sort order

fix sort order

--- dl/src/chain/serializer_types.coffee
+++ dl/src/chain/serializer_types.coffee
@@ -339,7 +339,7 @@
             return "0:0"
         object.type + ":" + object.id
     compare: (a, b) ->
-        parseInt(a.split(':')[1]) - parseInt(b.split(':')[1])
+        parseInt(a.id) - parseInt(b.id)

 Types.optional = (st_operation)->
     v.required st_operation, "st_operation"


Remove unused controller in stack.

Remove unused controller in stack.

--- client/app/controllers/stack.coffee
+++ client/app/controllers/stack.coffee
@@ -32,14 +32,12 @@
     chart: ChartsShow
     charts: ChartsList
     index: Index
-    offline: Offline
     search: ChartsSearch
   logPrefix: "(Stack)"
   routes:
     "/": "index"
     "/charts/:slug": "chart"
     "/charts": "charts"
-    "/offline": "offline"
     "/search/:q": "search"


Fix not appearing calendar and events after import. (#595)

--- client/app/views/import_view.coffee
+++ client/app/views/import_view.coffee
@@ -3,6 +3,7 @@
 helpers = require '../helpers'
 request = require '../lib/request'

+Calendar = require '../models/calendar'
 Event = require '../models/event'
 EventList = require './import_event_list'

@@ -117,29 +118,33 @@
         @targetCalendar = @calendarCombo.value()
         if not @targetCalendar? or @targetCalendar is ''
             @targetCalendar = t('default calendar name')
-        @calendarCombo.save()
-
-        # Initialize counter.
-        @initCounter()
-
-        # Show loading spinner.
-        @confirmButton.html '        '
-        @confirmButton.spin 'tiny'
-
-        # Save every imported events to the database.
-        async.eachSeries @eventLists, @importEvents, (err) =>
-
-            # When import is finished, the import form is reset and the
-            # calendar view is displayed.
-            alert t 'import finished'
-            @$(".confirmation").fadeOut()
-
-            @$(".results").slideUp =>
-                @$(".import-form").fadeIn()
-                @confirmButton.html t 'confirm import'
-
-                if $('.import-errors').html().length is 0
-                    app.router.navigate "calendar", true
+
+        calendar = new Calendar name: @targetCalendar
+        calendar.save calendar.attributes,
+        success: =>
+            app.calendars.add calendar
+
+            # Initialize counter.
+            @initCounter()
+
+            # Show loading spinner.
+            @confirmButton.html '        '
+            @confirmButton.spin 'tiny'
+
+            # Save every imported events to the database.
+            async.eachSeries @eventLists, @importEvents, (err) =>
+
+                # When import is finished, the import form is reset and the
+                # calendar view is displayed.
+                alert t 'import finished'
+                @$(".confirmation").fadeOut()
+
+                @$(".results").slideUp =>
+                    @$(".import-form").fadeIn()
+                    @confirmButton.html t 'confirm import'
+
+                    if $('.import-errors').html().length is 0
+                        app.router.navigate "calendar", true

     # Create events by sending them all via a single request to the backend.
@@ -159,7 +164,8 @@
             else
                 # When an element is successfully imported, it is added
                 # to the current calendar view.
-                #app.events.add event for event in result.events
+                result.events.forEach (event) ->
+                    app.events.add new Event(event)

                 # Events which was not properly imported are listed.
                 for event in result.errors


Fix black account color

Fix black account color

--- app/src/models/wallet.coffee
+++ app/src/models/wallet.coffee
@@ -48,3 +48,5 @@
     # Ensure every account is bound to the wallet
     for account in Account.all()
       account.set('wallet', this).save()
+      unless account.get('color')?
+        account.set('color', ledger.preferences.defaults.Accounts.firstAccountColor).save()


Only display mask for 1.5 seconds

Only display mask for 1.5 seconds

--- client/lanes/views/TimedMask.coffee
+++ client/lanes/views/TimedMask.coffee
@@ -18,7 +18,7 @@
             color: 'firebrick'

     failsafeTimeout: 25000
-    defaultTimeout:   2000
+    defaultTimeout:   1500

     constructor: ( @element, options={} )->
         _.bindAll(this,'destroy')


app.showView improvements

app.showView improvements

--- SingularityUI/app/application.coffee
+++ SingularityUI/app/application.coffee
@@ -97,9 +97,16 @@
             @page.appendChild view.el

     showView: (view) ->
+        # Clean up events & stuff
+        @currentView.remove()
+
+        @currentView = view
+        # Render & display the view
         view.render()
-        @views.current = view
-        @show view
+        if @page.children.length
+            @page.replaceChild view.el, @page.children[0]
+        else
+            @page.appendChild view.el

     setupAppCollections: ->
         @state = new State


this was a nasty one...

this was a nasty one...

--- src/client/component/component.coffee
+++ src/client/component/component.coffee
@@ -26,8 +26,7 @@
         @refresh ?= _.throttle((-> @invokeWithCallbacks 'render' ), 200)
         @componentName = options.name

-        for k, v of @options
-          options[k] ||= v
+        options[k] ?= v for k, v of @options

         unless @className?
           @className = "hull-component"


Retira alguns consoles

Retira alguns consoles

--- src/coffee/portal-sku-selector.coffee
+++ src/coffee/portal-sku-selector.coffee
@@ -212,8 +212,6 @@
 		dimensionName = $(this).attr('data-dimension')
 		dimensionValue = $(this).attr('data-value')

-		console.log 'dimensionChangeHandler: ' + $(this).attr('data-value')
-
 		# Limpa classe de selecionado para todos dessa dimensao
 		$('item-dimension-' + sanitize(dimensionName)).removeClass('checked')
 		# Adiciona classe de selecionado para seu label
@@ -389,7 +387,6 @@
 	# Fourth, disable next dimensions
 	for dimension in undefinedDimensions[1..]
 		$('input[dimension="' + sanitize(dimension) + '"]', $template).each ->
-			console.log 'Disabling next dimensions', this
 			$(this).attr('disabled', 'disabled')
 			$('.dimension-' + sanitize(dimension) + ' label', $template).addClass('disabled')
 			$(this).removeAttr('checked')


Activity: new message modal to not to align off screen when the screen is small

--- client/Social/Activity/AppView.coffee
+++ client/Social/Activity/AppView.coffee
@@ -156,15 +156,15 @@

     bounds = @sidebar.sections.messages.options.headerLink.getBounds()

-    top       = bounds.y - 310
-    left      = bounds.x + bounds.w + 40
-    arrowTop  = 310 + (bounds.h / 2) - 10 #10 = arrow height
+    top      = bounds.y - 310
+    left     = bounds.x + bounds.w + 40
+    arrowTop = 310 + (bounds.h / 2) - 10 #10 = arrow height

     modal = new PrivateMessageModal
       delegate     : this
       _lastMessage : @_lastMessage
       position     :
-        top        : top
+        top        : Math.max top, 0
         left       : left
       arrowTop     : arrowTop


Activity: Back button callback is bound to router.back()

--- client/Social/Activity/AppView.coffee
+++ client/Social/Activity/AppView.coffee
@@ -156,7 +156,7 @@
     @warning   = new KDCustomHTMLView
     @goBack    = new KDButtonView
       cssClass : 'goback-button'
-      callback : => KD.singletons.router.handleRoute '/Activity'
+      callback : => KD.singletons.router.back()

   pistachio:->
     """


add missing binding

add missing binding

--- client/app/lib/view_collection.coffee
+++ client/app/lib/view_collection.coffee
@@ -52,7 +52,7 @@
         @add view
         @

-    renderAll: ->
+    renderAll: =>
         @collection.each @renderOne
         @


kill dead code

kill dead code

--- client/lanes/models/Collection.coffee
+++ client/lanes/models/Collection.coffee
@@ -217,14 +217,12 @@

     _prepareModel: (attrs, options = {}) ->
         if @associationFilter
-#            needsReset =  _.isEmpty(attrs)
             _.extend(attrs, @associationFilter)
         model = super
         if @options.inverse
             parent = this.parent.clone()
             parent[@options.inverse.without].reset() if @options.inverse.without
             model.set(@options.inverse.name, parent, options)
-#        model.changeMonitor.reset() if needsReset
         model

     fetch: (options) ->


update prompt based on event

update prompt based on event

--- client/views/terminal/terminal.coffee
+++ client/views/terminal/terminal.coffee
@@ -14,6 +14,8 @@
   console.log instance
   service = instance.services[@data.serviceName]
   connectionId = null
+  prompt = null
+  setPrompt = null

   window.document.title = "RoboChick ~ #{service.hostname}"
   $('html').css 'cssText', 'height:100% !important;'
@@ -25,9 +27,14 @@
     @$('#terminal').terminal().echo "Connection established to #{service.hostname}"
   evt.addEventListener 'data', (event) =>
     @$('#terminal').terminal().echo EJSON.parse(event.data).data
+  evt.addEventListener 'prompt', (event) ->
+    console.log 'prompt', event.data
+    prompt = EJSON.parse(event.data).data
+    setPrompt prompt
   evaluator = (command, term) ->
     console.log (EJSON.stringify cmd: command)
+    term.echo "#{prompt}#{command}"
     HTTP.post "/api/v1/stream/#{connectionId}/send", (data: cmd: command), (err, response) ->
       term.error err if err
     undefined
-  @$('#terminal').terminal evaluator, prompt: '$ ', greetings: robochick, exit: false
+  @$('#terminal').terminal evaluator, prompt: ((x)-> setPrompt = x), greetings: robochick, exit: false


More consistent, CoffeeScript-y coding style, more comments

--- javascripts/tres.coffee
+++ javascripts/tres.coffee
@@ -1,210 +1,266 @@
+# Tres
+# ====
+
+# The whole framework should fit nicely in a single file. Avoid magic,
+# avoid indirection
+
+# Grab all variables to shorter references we'll use throughout
 Tres      = {}
 $         = window.jQuery
 _         = window._
 Backbone  = window.Backbone
 JST       = window.JST
-$window   = $(window)
-$body     = $('body')
+$window   = $ window
+$body     = $ 'body'
 make      = Backbone.View.prototype.make

-defaultTemplate = _.template("""
+# Default screen template
+defaultTemplate = _.template """

Tres

Welcome to Tres

-""")
-
-# Tres.Device handles/exposes device events (orientation change, accellerometer, etc), screen width, and
-# other hardware-related goodies.
+"""
+# ---
+
+# Tres.Device handles/exposes device events (orientation change, accellerometer,
+# etc), screen width, and other hardware-related goodies.
 class Device
-  constructor : ->
+  constructor: ->
     _.extend @, Backbone.Events
     $window.on 'orientationchange', _.bind @trigger, @, 'orientation:change'

-  width       : -> window.outerWidth
-  height      : -> window.outerHeight
-  orientation : ->
+  width: -> window.outerWidth
+  height: -> window.outerHeight
+  orientation: ->

 Tres.Device = new Device

-# Tres.Screen is the base screen class. So you'll pretty have one of these for each "route" in your app.
+# ---
+
+# Tres.Screen is the base screen class. So you'll pretty have one of these for
+# each "route" in your app.
 class Tres.Screen extends Backbone.View

   # Defaults to using sections for screens
-  tagName   : 'section'
+  tagName: 'section'

   # Ensure one can still declare events in a view without getting in the way of the defaults.
-  __events   :
-    # Click/touch on links will trigger pushState, but stay in the app. Except
-    # for links with the "outlink" class.
-    "click a[href]:not([href^='http://'])" : 'touchLink'
+  __events:
+    "click a[href]:not([href^='http://'])": 'touchLink'

     # Provide a convenience method `submit` which gets fired when you submit a form in a screen.
     # You can still trap forms normally. This is just a shortcut in case you have 1 form in a screen.
-    "submit form"                   : '__submit'
-
-  events    : {}
-
-  initialize : (options = {}) ->
-    _.extend @, options
-
-  # Returns the title of the screen, which will only exist if there's a header with a

-  # inside of it.
-  title : (title = null) ->
+    "submit form": '__submit'
+
+  events: {}
+
+  initialize: (options = {}) ->
+    _.extend @, options
+
+  # Returns the title of the screen, which will only exist if there's a
+  # header with a
 inside of it
+  title: (title = null) ->
     if title?
-      @$el.find('h1').html(title)
+      @$el.find('h1').html title
     else
       @$el.find('h1').html()

-  render : ->
-    @$el.html( (@template or defaultTemplate)(@model) )
-    @delegateEvents _.extend(@events, @__events)
+  # Renders the screen's template into the container element. Also
+  # (re)delegates the screen's events
+  render: ->
+    @$el.html (@template or defaultTemplate)(@model)
+    @delegateEvents _.extend @events, @__events
     @

-  # Embeds a Tres.Screen into the . Returns false in case it's already embedded.
-  embed : ->
+  # Embeds a Tres.Screen into the . Returns false in case it's already
+  # embedded.
+  embed: ->
     return false if @embedded
     @render()
     $body.append @el
     @embedded = true
     @

-  touchLink : (event) ->
+  # Click/touch on links will trigger pushState, but stay in the app
+  touchLink: (event) ->
     event.preventDefault()
     Tres.Router.navigate $(event.currentTarget).attr('href'), true

-  __submit : (event) ->
+  # Runs whatever @submit method is declared, with the added bonus of
+  # un-focusing any text fields that are currently focused
+  __submit: (event) ->
     @submit.apply @, arguments
     @$el.find(':focus').blur()

-  submit : ->
-
-  # Sets the class "current" to this screen, removing the class from whatever other sections
-  # that have it. Call the `active` method in case a screen has one.
-  activate : ->
+  # Default @submit to noop
+  submit: ->
+
+  # Sets the class "current" to this screen, removing the class from
+  # whatever other sections that have it. Call the `active` method in
+  # case a screen has one
+  activate: ->
     $body.find('>section').removeClass 'current'
     @$el.addClass 'current'
-    @active.apply(@, arguments) if _.isFunction(@active)
-
+    @active.apply @, arguments if _.isFunction @active
+
+# ---
+
+# This class is used as controller for each Tres.List item
 class Tres.ListEntry extends Backbone.View
   initialize : (options = {}) ->
     _.extend @, options

-  render : ->
-    @$el.html( @template(@model) )
-    @delegateEvents('click' : 'touch') if _.isFunction(@url)
+  render: ->
+    @$el.html @template @model
+    @delegateEvents 'click': 'touch' if _.isFunction @url
     @

-  touch : (event) ->
+  # If a @url method is defined, means we want the user to touch and
+  # visit the URL
+  touch: (event) ->
     Tres.Router.navigate @url(), true

-
-# Tres.List is a convenience class for rendering lists of things, interactible or not.
+# ---
+
+# Tres.List is a convenience class for rendering lists of things
 class Tres.List extends Backbone.View
+
+  # A map of likely child tags for each parent tag
   _tagMap :
     'UL'  : 'LI'
     'OL'  : 'LI'
     'DIV' : 'DIV'

-  initialize : (options = {}) ->
-    _.extend @, options
-    @setElement(@el)
+  # Keep the DOM in sync with all interactions made with the collection
+  initialize: (options = {}) ->
+    _.extend @, options
+    @setElement @el
     @collection.on 'add',     @__add,    @
     @collection.on 'remove',  @__remove, @
     @collection.on 'reset',   @__addAll, @

-  __add : (model) ->
+  # Adds one record to the list, wrapping it in a Tres.List
+  __add: (model) ->
     tag = @entry?.tagName or @_tagMap[@$el.get(0).tagName]
-    template = new Tres.ListEntry(_.extend(@entry, { tagName : tag, model : model }))
+    template = new Tres.ListEntry _.extend(@entry, { tagName : tag, model : model })
     model.template = template
     @$el.append template.render().el

-  __remove : (model) ->
+  # Removes the list from the DOM
+  __remove: (model) ->
     model.template.remove()

-  __addAll : ->
+  # Adds all the elements the @collection to the list, removing any existing
+  # ones
+  __addAll: ->
     @$el.empty()
-    @collection.each (model) => @__add(model)
-
-  remove : ->
+    @collection.each (model) => @__add model
+
+  # Deletes the instance of Tres.List, ensuring any bound events to
+  # @collection are removed
+  remove: ->
     @collection.off() if @collection?
     super

+# ---
+
+# Handles form submissions and a few other helpers such as accessing
+# fieldsets individually, and loading model data into fields
 class Tres.Form
-  constructor : (@$el) ->
-
-  fieldset : (filter) ->
-    new Tres.Form(@$el.find('fieldset').filter(filter))
-
-  attributes : (options = {})->
+
+  # Takes a jQuery-wrapped form object
+  constructor: (@$el) ->
+
+  # Returns a new instance of Tres.Form, narrowed down to a single
+  # fieldset
+  fieldset: (filter) ->
+    new Tres.Form @$el.find('fieldset').filter(filter)
+
+  # Returns all the form data in a single object, in key/value
+  # pairs by `name` attribute and value
+  attributes: (options = {})->
     attributes = {}
     if options.only?
-      inputs = @$el.find("#{options.only} :input[name]")
+      inputs = @$el.find "#{options.only} :input[name]"
     else
-      inputs = @$el.find(':input[name]')
+      inputs = @$el.find ':input[name]'
       _.each inputs, (el) ->
-        $el = $(el)
-        if $el.is(':checkbox')
-          attributes[ $el.attr('name') ] = $el.is(':checked')
+        $el = $ el
+        if $el.is ':checkbox'
+          attributes[ $el.attr 'name' ] = $el.is ':checked'
         else
-          attributes[ $el.attr('name') ] = $el.val()
+          attributes[ $el.attr 'name' ] = $el.val()
     attributes
-
-  setFromModel : (model) ->
-    _.each _.keys(model.attributes), (key) =>
-      $el = @$el.find("[name=\"#{key}\"]")
-      if $el.is(':checkbox') and model.attributes[key] is true
-        $el.attr('checked', 'checked')
+
+  # Fills the form's fields whose `name` match the model's attributes
+  setFromModel: (model) ->
+    _.each _.keys model.attributes, (key) =>
+      $el = @$el.find "[name=\"#{key}\"]"
+      if $el.is ':checkbox' and model.attributes[key] is true
+        $el.attr 'checked', 'checked'
       else
         $el.val model.attributes[key]
-
-  clear : ->
-    _.each @$el.find(':input'), (el) ->
-      $el = $(el)
-      $el.removeAttr('checked') if $el.is(':checkbox')
-      $el.val('')
-    @
-
-# Tres.Notifier: handles displaying in-app notifications
+
+  # Clears the form
+  clear: ->
+    _.each @$el.find ':input', (el) ->
+      $el = $ el
+      $el.removeAttr 'checked' if $el.is ':checkbox'
+      $el.val ''
+
+# ---
+
+# Handles displaying in-app notifications
 Tres.Notifier =
-  $el : $(make('ul', id : 'notifications'))
+  $el: $ make 'ul', id: 'notifications'

   notify : (message, options = { duration : 5000, type : 'exclamation-sign' }) ->
     @$el.appendTo $body
-    $li = $(make('li', { class : "icon-#{options.type}"}, message))
+    $li = $ make 'li', { class: "icon-#{options.type}" }, message
     @$el.append $li
     $li.slideDown 250, =>
       _.delay =>
         $li.slideUp => $li.remove()
       , options.duration

+# ---
+
 # Just a regular Backbone.Router for now. By default you'll have one for application
 class Router extends Backbone.Router

-# Make the router it accessible
 Tres.Router = new Router

+# ---
+
 class Tres.App
-  constructor : (options = {}) ->
-    _.extend @, options
-
-  screens : []
-
-  on : (map = {}) ->
+
+  constructor: (options = {}) ->
+    _.extend @, options
+
+  # All the screens that are currently instanced
+  screens: []
+
+  # Takes a hash to use as a mapping of URLs -> Tres.Screen objects
+  on: (map = {}) ->
     _.each _.keys(map), (url) =>
       Tres.Router.route url, _.uniqueId('r'), =>
         screen = map[url]
         screen.embed() unless screen.embedded is true
         args = arguments
-        _.defer => screen.activate.apply(screen, args)
-
-  boot : (options = {}) ->
+        _.defer => screen.activate.apply screen, args
+
+  # Boots the app, executing the routes and adding a handy `before`
+  # event to the router
+  boot: (options = {}) ->
     __super = Backbone.history.loadUrl
     Backbone.history.loadUrl = =>
-      Tres.Router.before.call(@) if _.isFunction(Tres.Router.before)
+      Tres.Router.before.call @ if _.isFunction Tres.Router.before
       Tres.Router.trigger 'navigate'
-      window.scroll(0, 0)
+      window.scroll 0, 0
       __super.apply Backbone.history, arguments
-    Backbone.history.start(_.extend(options, pushState : true))
-
-
-window.Tres = Tres+    Backbone.history.start _.extend(options, pushState : true)
+
+# ---
+
+# Export to `window`
+window.Tres = Tres


update example

update example

--- examples/config.cson
+++ examples/config.cson
@@ -78,6 +78,7 @@
             path: "g++ ${CURRENT_FILE} -std=c++14 -o ${path.basename(CURRENT_FILE, '.cpp')}"

             console: true
+			paneItemPosition: 'right'

       }


updateの実装

updateの実装

--- public/coffee/gdrive.coffee
+++ public/coffee/gdrive.coffee
@@ -29,7 +29,6 @@
     return multipartRequestBody

   insert: (title, content, callback) ->
-    console.log('insert file')
     multipartRequestBody = this.requestBody(title, content)
     request = gapi.client.request({
       'path': '/upload/drive/v2/files',
@@ -45,9 +44,25 @@
         console.log(file)
     request.execute(callback)

+  update: (fileId, title, content, callback) ->
+    console.log('update file')
+    multipartRequestBody = this.requestBody(title, content)
+    request = gapi.client.request({
+      'path': '/upload/drive/v2/files/' + fileId,
+      'method': 'PUT',
+      'params': {'uploadType': 'multipart'},
+      'headers': {
+        'Content-Type': 'multipart/mixed; boundary="' +
+        this.BOUNDARY + '"'
+      },
+      'body': multipartRequestBody})
+    if (!callback)
+      callback = (file) ->
+        console.log(file)
+    request.execute(callback)
+
   utf8_to_b64: (str) ->
     window.btoa( unescape(encodeURIComponent( str ) ) )
-

 GDriveModel = Backbone.Model.extend({
   initialize: ()->
@@ -67,10 +82,8 @@
         # immediate: trueが失敗したときはflaseで再チャレンジ
         # 今度はダイアログが開く
         if (authResult && !authResult.error)
-          console.log("immediate true")
           that.authResult = authResult
         else
-          console.log("immediate false")
           gapi.auth.authorize({
             'client_id': this.gdrive.CLIENT_ID,
             'scope': this.gdrive.SCOPES,
@@ -81,13 +94,23 @@
     )

   upload: (title, content) ->
+    if this.file == null
+      this.insert(title, content)
+    else
+      this.update(title, content)
+
+  insert: (title, content) ->
     that = this
-    console.log('upload file')
-
-    console.log(content)
     gapi.client.load('drive', 'v2', ->
       that.gdrive.insert(title, content, (file)->
-        console.log(file)
+        that.file = file
+        )
+    )
+
+  update: (title, content) ->
+    that = this
+    gapi.client.load('drive', 'v2', ->
+      that.gdrive.update(that.file.id, title, content, (file)->
         that.file = file
         )
     )


Update test data

Update test data


code cleanup in KDTooltip

code cleanup in KDTooltip

--- components/tooltip/KDTooltip.coffee
+++ components/tooltip/KDTooltip.coffee
@@ -140,7 +140,7 @@
     # check the default values for overlapping boundaries, then
     # recalculate if there are overlaps

-    violations = boundaryViolations getCoordsFromPlacement(d, placement, direction),\
+    violations = getBoundaryViolations getCoordsFromPlacement(d, placement, direction),\
     d.container.width, d.container.height

     if Object.keys(violations).length > 0 # check for possible alternatives
@@ -160,7 +160,7 @@
       ]

       for variant in variants
-        if Object.keys(boundaryViolations(getCoordsFromPlacement(d,variant[0],variant[1]),\
+        if Object.keys(getBoundaryViolations(getCoordsFromPlacement(d,variant[0],variant[1]),\
         d.container.width, d.container.height)).length is 0
           [placement,direction] = variant
           break
@@ -262,7 +262,7 @@
     right   : "right"

   # will return an object with the amount of clipped pixels
-  boundaryViolations = (coordinates,width,height)=>
+  getBoundaryViolations = (coordinates,width,height)=>
     violations = {}
     if coordinates.left < 0
       violations.left   = -(coordinates.left)
@@ -282,12 +282,15 @@
     coordinates =
       top  : dimensions.selector.offset.top
       left : dimensions.selector.offset.left
+
     [staticAxis,dynamicAxis,staticC,dynamicC,exclusion] =
       if /o/.test placement then ['height','width','top','left','right']
       else ['width','height','left','top','bottom']
+
     coordinates[staticC]+= unless placement.length<5
         (dimensions.selector[staticAxis]+10)
       else -(dimensions.container[staticAxis]+10)
+
     unless direction is exclusion
       coordinates[dynamicC]+=getCoordsDiff(dimensions,dynamicAxis,direction is 'center')
     return coordinates


Update remaining functions

Update remaining functions

--- js/flight.coffee
+++ js/flight.coffee
@@ -1,29 +1,29 @@
 class Flight
-  # Private
-  version: '0.0.1'
+
   isTransitioning: false
   currentPage: ''
   targetPage: ''
-  pageHistory: [""]
+  pageHistory: []
   os: {}
   hideUrlBar: false
   noClickDelay: true
-  # options
-  mainMenu: '#slide-out-menu'
   menuOpen: false
+  back: false
+
   transitionAnimation: true
   speed: '0.4s'
-  back: false
+  mainMenu: '#slide-out-menu'

-  # Public:
+  # Public: Instantiate Flight and set any options.
+  #
+  # options - A Hash of options for flight.
   #
   # Returns nothing.
   constructor: (options = {}) ->
     if options.transitionAnimation?
       @transitionAnimation = options.transitionAnimation

-    if options.speed?
-      @speed = options.speed
+    @speed = options.speed if options.speed?

     @mainMenu = options.mainMenu if options.mainMenu?
     @mainMenu = document.querySelector @mainMenu if typeof @mainMenu is "string"
@@ -31,7 +31,7 @@
     os = @detectUserAgent()
     @transitionAnimation = false if os.android and os.version <= '2.1'

-    if @hideUrlBar is true then @hideUrlBar()
+    @hideUrlBar() if @hideUrlBar

   # Public: Go to a specific page.
   #
@@ -76,7 +76,7 @@

     , 10

-  # Public: Perform a slide transition.
+  # Private: Perform a slide transition.
   #
   # Returns nothing.
   slideTransition: ->
@@ -96,10 +96,11 @@
     window.setTimeout =>
       @translate(@targetPage, "X", "0%")
       @currentPage.addEventListener "webkitTransitionEnd", @finishTransition, false
-      @resetState()
+      @back = false
+      @isTransitioning = false
     , 5

-  # Public: Perform a slide from bottom transition.
+  # Private: Perform a slide from bottom transition.
   #
   # Returns nothing.
   slideFromBottom: ->
@@ -119,9 +120,10 @@
     window.setTimeout =>
       @targetPage.addEventListener "webkitTransitionEnd", @finishTransition, false
     , 20
-    @resetState()
+    @back = false
+    @isTransitioning = false

-  # Public: Perform a slide out transition for the menu.
+  # Private: Perform a slide out transition for the menu.
   #
   # Returns nothing.
   slideOutMenu: =>
@@ -133,32 +135,38 @@
       @translate(@currentPage, "X", "250px", "0.3s")
       @menuOpen = true

-  # call on transition end
+  # Private: Finish any transitions.
+  #
+  # Returns nothing.
   finishTransition: =>
     @currentPage.style.display = "none"
     @currentPage.removeEventListener "webkitTransitionEnd", @finishTransition, false
-    # swap pages
     @currentPage = @targetPage
-
-   resetState: =>
-    @back = false
-    @isTransitioning = false

-   # translate page on secified axis. Duration defaults to speed property when not passed. Delay defaults to 0.
-   translate: (page, axis, distance, duration) =>
-    if not duration? then duration = @speed
+  # Private: Translate page on a specified axis.
+  #
+  # page     - An Element of the page.
+  # axis     - A String of the axis.
+  # distance - A String of the distance.
+  # duration - A String of the duration, defaults to speed.
+  #
+  # Returns nothing.
+  translate: (page, axis, distance, duration) =>
+    duration = @speed unless duration?
     page.style.webkitTransition = "#{duration} ease"
     page.style.webkitTransform = "translate#{axis}(#{distance})"
-
-   # displays pages when transiton is false, back animations do not matter here.
-   displayPage: =>
+
+  # Private: Display the current page.
+  #
+  # Returns nothing.
+  displayPage: =>
     @targetPage.style.display = "-webkit-box"
     @currentPage.style.display = "-webkit-box"
     @targetPage.style.left = "0%"
     @currentPage.style.left = "100%"
     @isTransitioning = false

-  # Public: Get a Hash of browser user agent information.
+  # Private: Get a Hash of browser user agent information.
   #
   # Returns a Hash of user agent information.
   detectUserAgent: ->
@@ -180,6 +188,9 @@
     os.desktop = not (os.ios or os.android or os.blackberry or os.opera or os.fennec)
     os

+  # Private: Hide the URL bar in mobile browsers.
+  #
+  # Returns nothing.
   hideUrlBar: ->
     setTimeout ->
         window.scrollTo 0, 1


Clearer intentions for using token auth

Clearer intentions for using token auth

--- server/lib/session_authentication.coffee
+++ server/lib/session_authentication.coffee
@@ -4,14 +4,16 @@
 if process.env.NODE_ENV is 'production'
   httpVerbWhitelist = []

+tokenAuthVerbs = ['POST', 'DELETE', 'PUT']
+
 module.exports = (req, res, next) ->
   return next() if process.env.NODE_ENV is 'test'

   if /^\/api\/users/.test(req.path)
-    if _.contains(httpVerbWhitelist, req.method)
-      return res.redirect('/login')
+    if _.contains(tokenAuthVerbs, req.method)
+      return next()

-    return next()
+    return res.redirect('/login')

   return next() if /^\/login/.test(req.path)
   return next() if req.isAuthenticated() or _.contains(httpVerbWhitelist, req.method)


Fix wording about free classes

Fix wording about free classes

--- server/materials/data/level_about.coffee
+++ server/materials/data/level_about.coffee
@@ -151,7 +151,7 @@

Для школьников из Нижегородской области

Для нижегородских школьников занятия бесплатны, и в обозримом будущем останутся бесплатными (т.к. мне проведение занятий оплачивает лицей 40 и ВШЭ).

-
Формально вы считаетесь нижегородским школьников, если учитесь в школе, находящейся в Нижегородской области, и имеете теоретическую возможность, пройдя школьный и муниципальный этапы, участвовать в Нижегородской областной олимпиаде школьников по информатике.

+
Формально вы считаетесь нижегородским школьником, если можете участвовать во Всероссийской олимпиаде школьников по информатике и имеете теоретическую возможность, пройдя школьный и муниципальный этапы, участвовать в региональном этапе ВсОШ в Нижегородской области (т.е. в Нижегородской областной олимпиаде школьников по информатике).

Если у меня возникнут подозрения, являетесь ли вы нижегородским школьников или нет, я могу попросить вас как-то это подтвердить.


Avoid hiding VCS-ignored files in tree view

--- .config/config.cson
+++ .config/config.cson
@@ -23,13 +23,13 @@
       "atom-compare-files"
       "background-tips"
       "welcome"
-      "docblockr"
     ]
   "exception-reporting":
     userId: "4b2dfe3a-7eef-a871-4d35-230cb11366f8"
   welcome:
     showOnStartup: false
   "tree-view":
+    hideVcsIgnoredFiles: false
     hideIgnoredNames: true
   whitespace:
     removeTrailingWhitespace: false
@@ -49,5 +49,3 @@
   "status-bar": {}
   hex: {}
   docblockr: {}
-  minimap:
-    autoToggle: false


reset routes with haibu data at start

reset routes with haibu data at start

--- config/initializers/resetproxy.coffee
+++ config/initializers/resetproxy.coffee
@@ -1,10 +1,44 @@
 Client = require('request-json').JsonClient

 client = new Client 'http://localhost:9104/'
+haibu = require('haibu-api')
+haibuClient = haibu.createClient(
+    host: 'localhost'
+    port: 9002
+)

-if process.env.NODE_ENV != "test"
+
+haibuClient =  new Client 'http://localhost:9002/'
+
+resetRoutes = ->
+    Application.all (err, installedApps) ->
+        appDict = {}
+        for installedApp in installedApps
+            appDict[installedApp.name] = installedApp
+
+        haibuClient.get 'drones/running', (err, res, apps) ->
+            updateApps(apps, appDict, resetProxy)
+
+
+updateApps = (apps, appDict, callback) ->
+    app = apps.pop()
+    if apps.length > 0
+        installedApp = appDict[app.name]
+        if installedApp? and installedApp.port != app.port
+            instaledApp.updateAttributes port: app.port, (err) ->
+                updateApps(apps, appDict, callback)
+        else
+            updateApps(apps, appDict, callback)
+    else
+        callback()
+
+resetProxy = ->
     client.get 'routes/reset/', (err, res, body) ->
         if res.statusCode is 200
             console.info 'Proxy successfuly reseted.'
         else
             console.info 'Something went wrong while reseting proxy.'
+
+if process.env.NODE_ENV != "test"
+    resetRoutes()
+


Update coordinates even when not selected

--- app/assets/javascripts/events.coffee
+++ app/assets/javascripts/events.coffee
@@ -20,6 +20,12 @@
   selects[8].value = ("0" + endhour).slice(-2)

 document.addEventListener "page:change", ->
+
+  updateCoordinates = (suggestion) ->
+    coords = suggestion.geometry.coordinates
+    $('#event_lng').val coords[0]
+    $('#event_lat').val coords[1]
+
   addressSearch = new Bloodhound
     datumTokenizer: Bloodhound.tokenizers.obj.whitespace('value')
     queryTokenizer: Bloodhound.tokenizers.whitespace
@@ -27,6 +33,7 @@
     remote:
       url: "http://data.wien.gv.at/daten/OGDAddressService.svc/GetAddressInfo?Address=%QUERY&crs=EPSG:4326"
       filter: (addresses) ->
+        updateCoordinates addresses.features[0]
         addresses.features
       wildcard: '%QUERY'

@@ -41,6 +48,4 @@
       suggestion: (data) ->
         "<div>#{data.properties.Adresse}</div>"
   ).on 'typeahead:selected typeahead:autocompleted', (event, suggestion) ->
-    coords = suggestion.geometry.coordinates
-    $('#event_lng').val coords[0]
-    $('#event_lat').val coords[1]
+    updateCoordinates suggestion


update popup after fusion

update popup after fusion

--- app/assets/javascripts/map.js.coffee
+++ app/assets/javascripts/map.js.coffee
@@ -114,6 +114,8 @@
               old_ch = @list[ind].character
               if old_ch.id != new_ch.id
                 # Not the same character at the same position as before
+                console.log "Not the same character at the same position as before"
+                debugger
                 @construct_list()
                 break
               if old_ch.updated_at != new_ch.updated_at
@@ -126,6 +128,8 @@
                 @list[ind].update_character_nodes(new_ch)
           else
             # not the same number of characters as before
+            console.log "Not the same number of characters as before"
+            debugger
             @construct_list()
       # Updates the date for all the characters of the map
       update_date: (new_date)->
@@ -513,9 +517,7 @@
               map.addLayer(@marker)
               @on_map = true

-          @resize_marker()
-          @update_popup()
-          @update_latlng()
+          @update_marker()

       # Return the content of the popup of the marker.
       # Must be null to not create a popup
@@ -564,6 +566,10 @@
         else
           # Not real node
           @destroy_popup()
+      update_marker: ->
+        @resize_marker()
+        @update_popup()
+        @update_latlng()

       # Destroy popup and marker
       destroy: ->
@@ -607,6 +613,7 @@
       # Update @node on server
       update_on_server: ->
         alert("update a non real node !") unless @node.id != 0
+        @update_marker()
         $.ajax
           type: "PUT"
           url: "/nodes/"+@node.id
@@ -620,6 +627,7 @@
       # Create @node on server
       create_on_server: ->
         #alert('create on server !')
+        @update_marker()
         $.ajax
           type: "POST"
           url: "/nodes/"


Add support for larger audio files by passing them directly through javascript

--- app/assets/javascripts/mix.js.coffee
+++ app/assets/javascripts/mix.js.coffee
@@ -183,10 +183,7 @@
       if e.dataTransfer.files.length
         fileDropped = true
         wavesurfer.loadBlob e.dataTransfer.files[0]
-        reader = new FileReader()
-        reader.addEventListener 'loadend', ->
-          localStorage.aud = reader.result
-        reader.readAsBinaryString(e.dataTransfer.files[0].slice())
+        window.inputAudio = e.dataTransfer.files[0].slice()
       else
         wavesurfer.fireEvent 'error', 'Not a file'
       return


Move rounding to 1000, since that is what subtitles use

--- app/assets/javascripts/mix.js.coffee
+++ app/assets/javascripts/mix.js.coffee
@@ -11,8 +11,8 @@
 editAnnotation = (region) ->
   form = document.forms.edit
   form.classList.remove 'hide'
-  form.elements.start.value = round100(region.start)
-  form.elements.end.value = round100(region.end)
+  form.elements.start.value = round1000(region.start)
+  form.elements.end.value = round1000(region.end)
   form.elements.note.value = region.data.note or ''
   if region.data.type == 'segment'
     form.elements.note.setAttribute('disabled', 'disabled')
@@ -45,8 +45,8 @@
   time ||= wavesurfer.getCurrentTime()
   # Assume that time is always within a region, since we initialize a region over full audio at start
   splitCandidate = this.getRegionsAt(time)[0]
-  wavesurfer.addRegion { start: splitCandidate.start, end: round100(time), drag: false, resize: false, data: { type: 'segment' }, color: randomColor(0.3) }
-  wavesurfer.addRegion { start: round100(time), end: splitCandidate.end, drag: false, resize: false, data: { type: 'segment' }, color: randomColor(0.3) }
+  wavesurfer.addRegion { start: splitCandidate.start, end: round1000(time), drag: false, resize: false, data: { type: 'segment' }, color: randomColor(0.3) }
+  wavesurfer.addRegion { start: round1000(time), end: splitCandidate.end, drag: false, resize: false, data: { type: 'segment' }, color: randomColor(0.3) }
   wavesurfer.regions.list[splitCandidate.region_id].remove()

 wavesurfer.getRegionsAt = (time) ->
@@ -66,7 +66,7 @@
       wavesurfer.regions.list[regionsArray()[1].region_id].update(start: 0)
     else
       # set prev's end as this end
-      replacer = (region for region_id, region of wavesurfer.regions.list when round100(region.end) == round100(deleteCandidate.start))[0]
+      replacer = (region for region_id, region of wavesurfer.regions.list when round1000(region.end) == round1000(deleteCandidate.start))[0]
       wavesurfer.regions.list[replacer.id].update(end: deleteCandidate.end)
   if regionId
     wavesurfer.regions.list[regionId].remove()
@@ -76,15 +76,15 @@
 wavesurfer.setMarker = (time, text) ->
   time ||= wavesurfer.getCurrentTime()
   text ||= ''
-  wavesurfer.addRegion(start: round100(time), drag: true, resize: false, data: { type: 'segment_item', note: text }, color: 'rgba(0,0,0,0.5)')
+  wavesurfer.addRegion(start: round1000(time), drag: true, resize: false, data: { type: 'segment_item', note: text }, color: 'rgba(0,0,0,0.5)')

 regionsArray = ->
   mapped = Object.keys(wavesurfer.regions.list).map((id) ->
     region = wavesurfer.regions.list[id]
     {
       region_id: id
-      start: round100(region.start)
-      end: round100(region.end)
+      start: round1000(region.start)
+      end: round1000(region.end)
       data: region.data
       texts: []
       drag: (if region.data.type == 'segment_item' then true else false)
@@ -95,7 +95,7 @@
   mapped.sort (a,b)->
     a.start - b.start

-round100 = (input) ->
+round1000 = (input) ->
   Math.round(input * 100)/100

 loadRegions = (regions) ->
@@ -139,7 +139,7 @@
     if localStorage.regions && JSON.parse(localStorage.regions).length > 0
       loadRegions JSON.parse(localStorage.regions)
     else
-      wavesurfer.addRegion(start: 0, end: round100(wavesurfer.getDuration()), drag: false, resize: false, data: { type: 'segment' })
+      wavesurfer.addRegion(start: 0, end: round1000(wavesurfer.getDuration()), drag: false, resize: false, data: { type: 'segment' })
       saveRegions()
     return


Welcome

Welcome

--- def/th/welcome.cson
+++ def/th/welcome.cson
@@ -2,13 +2,13 @@
   tabTitle: "ยินดีต้นรับ"
   subtitle: {
     text1: "อิดิเตอร์ที่แฮคได้ของศตวรรษที่ 21"
-    superscript: ""
-    text2: ""
+    superscript: " st"
+    text2: " "
   }
   help: {
     forHelpVisit: "สำหรับความช่วยเหลือ, โปรดเข้าไปที่ "
     atomDocs: {
-      text1: ""
+      text1: " "
       link: "เอกสารของ Atom"
       text2: " สำหรับแนวทางและคู่มือ API"
       _template: "${text1}${link}${text2}"
@@ -16,11 +16,11 @@
     atomForum: {
       text1: "กระทู้ของ Atom ที่ "
       link: "discuss.atom.io"
-      text2: ""   # optional
+      text2: " "   # optional
       _template: "${text1}${link}${text2}"
     }
     atomOrg: {
-      text1: ""
+      text1: " "
       link: "Atom org"
       text2: " คือที่ที่จะพบแพคเก็จของ Atom ที่สร้างบน Github"
       _template: "${text1}${link}${text2}"


Handle Expertise and Concentration

Handle Expertise and Concentration

--- site/modules/common/common.module.coffee
+++ site/modules/common/common.module.coffee
@@ -267,6 +267,8 @@
         switch (name)
             when 'CritDamage' then 'Ferocity'
             when 'ConditionDamage' then 'Condition Damage'
+            when 'ConditionDuration' then 'Expertise'
+            when 'BoonDuration' then 'Concentration'
             else name
 .filter 'weaponTypeName', ->
     (name) ->


fixup rebase error

fixup rebase error

--- exercises/src/components/search.cjsx
+++ exercises/src/components/search.cjsx
@@ -62,11 +62,8 @@
   render: ->

       {<NetworkActivity /> if ExerciseStore.isLoading(@state.loading) }
-<<<<<<< f35db2e2cf11c149bad08a15edd7b829c0d1d706
       {
Search is empty, please type an exercise ID into the input.
 if @state.showEmptyWarning}
-=======
       <@Errors />
->>>>>>> display error message when exercise id is blank
       {<RecordNotFound
         recordType="Exercise" id={@state.loading}
         /> if ExerciseStore.isFailed(@state.loading)}


translations for cancel booking page

translations for cancel booking page

--- addon/initializers/i18n-zh-tw.coffee
+++ addon/initializers/i18n-zh-tw.coffee
@@ -99,4 +99,15 @@
     "text" : "{{text}}"
     "view": "View"

+  "delivery":
+    "cancel":
+      "title": "Cancel Booking"
+      "no_ggv_cancel": "Unfortunately we cannot cancel a confirmed GoGoVan boking for you."
+      "cancel_ggv": "Please cancel your GoGoVan booking:"
+      "call_driver": "call the driver on"
+      "call_ggv": "call GoGoVan on"
+      "back": "Go Back"
+      "cancel_booking": "Cancel Booking"
+      "confirm_ggv_cancel": "Once GoGoVan confirms the order is cancelled you will be able to proceed to cancel your booking."
+
 `export default I18nTranslationsZhTw`


HUB-89 : Setting logger

HUB-89 : Setting logger

--- lib/actor/hactor.coffee
+++ lib/actor/hactor.coffee
@@ -28,7 +28,7 @@
 forker = require "child_process"
 #Third party modules
 zmq = require "zmq"
-logger = require "winston"
+winston = require "winston"
 _ = require "underscore"
 #Hactor modules
 {OutboundAdapter} = require "./../adapters"
@@ -48,11 +48,6 @@
   Class that defines an Actor
 ###
 class Actor extends EventEmitter
-  #Init logger
-  logger.exitOnError = false
-  logger.remove(logger.transports.Console)
-  logger.add(logger.transports.Console, {handleExceptions: true, level: "INFO"})
-  logger.add(logger.transports.File, {handleExceptions: true, filename: "./log/hubiquitus.log", level: "debug"})

   # Possible running states of an actor
   STATUS_STARTING = "starting"
@@ -76,6 +71,13 @@
     @topology {object} Launch topology of the actor
   ###
   constructor: (topology) ->
+    # init logger
+    if topology.log
+      @log_properties = topology.log
+      @initLogger(topology.log.logLevel or "info", topology.log.logFile)
+    else
+      @initLogger("info")
+
     # setting up instance attributes
     if(validator.validateFullURN(topology.actor))
       @actor = topology.actor
@@ -368,6 +370,7 @@
     unless _.isString(method) then throw new Error "'method' parameter must be a string"

     unless properties.trackers then properties.trackers = @trackers
+    unless properties.log then properties.log = @log_properties

     # prefixing actor's id automatically
     unless classname is "hchannel"
@@ -403,20 +406,42 @@
     properties.actor

   ###*
+    Method called by constructor to initializing logger
+  ###
+  initLogger: (logLevel, logFile) ->
+    logLevels =
+      debug: 0,
+      info: 1,
+      warn: 2,
+      error:3
+    @logger = new winston.Logger({levels: logLevels})
+    # Don't crash on uncaught exception
+    @logger.exitOnError = false
+
+    # Set log display
+    @logger.add(winston.transports.Console, {handleExceptions: true, level: logLevel})
+    if logFile
+      try
+        @logger.add(winston.transports.File, {handleExceptions: true, filename: logFile, level: logLevel})
+      catch err
+
+  ###*
     Method that enrich a message with actor details and logs it to the console
     @message {object} the message to log
   ###
   log: (type, message) ->
-    # TODO properly configure logging system
     switch type
       when "debug"
-        logger.debug "#{validator.getBareURN(@actor)} | #{message}"
+        @logger.debug "#{validator.getBareURN(@actor)} | #{message}"
         break
       when "info"
-        logger.info "#{validator.getBareURN(@actor)} | #{message}"
+        @logger.info "#{validator.getBareURN(@actor)} | #{message}"
         break
       when "warn"
-        logger.warn "#{validator.getBareURN(@actor)} | #{message}"
+        @logger.warn "#{validator.getBareURN(@actor)} | #{message}"
+        break
+      when "error"
+        @logger.error "#{validator.getBareURN(@actor)} | #{message}"
         break

   ###*


use requestanimationframe and accommodate deferred cassowary load

--- @client/dock.coffee
+++ @client/dock.coffee
@@ -216,6 +216,9 @@
 # For console output:
 debug = false

+
+window.ccnt = 0
+
 dockingStation =

   ####
@@ -242,7 +245,8 @@

     if !dockingStation.listening_to_scroll_events

-      @on_scroll = -> dockingStation.user_scrolled = true
+      @on_scroll = ->
+        window.requestAnimationFrame dockingStation.onScroll

       window.addEventListener "scroll", @on_scroll
       window.addEventListener "resize", dockingStation.onResize
@@ -254,12 +258,7 @@

       dockingStation.listening_to_scroll_events = true

-      # Recompute layout if we've seen a scroll in past X ms
-      dockingStation.interval = setInterval ->
-        if dockingStation.user_scrolled
-          dockingStation.user_scrolled = false
-          dockingStation.onScroll()
-      , 100
+

   unregister : (key) ->
     delete dockingStation.registry[key]
@@ -275,6 +274,8 @@
   #######
   # onScroll
   onScroll : ->
+    ccnt += 1
+
     dockingStation.updateViewport()

     # At most we will shift the docked components by the distance scrolled
@@ -347,6 +348,7 @@
     if docked.length > 0
       # Calculate y-positions for all docked components
       y_pos = dockingStation.solveForY docked, docks, max_change
+      return if !y_pos?

       for k in docked
         dock = fetch k
@@ -443,7 +445,22 @@
   #
   # max_change constrains how far each docking element is allowed to move
   # since the last time it was laid out.
-  solveForY : (docked, docks, max_change) ->
+  solveForY : (docked, docks, max_change) ->
+
+    if !cassowary?
+
+      if !dockingStation.cassowary_loading?
+        dockingStation.cassowary_loading = setInterval ->
+          if cassowary?
+            dockingStation.solveForY(docked, docks, max_change)
+            clearInterval dockingStation.cassowary_loading
+        , 10
+
+      return
+
+
+
+
     c = cassowary

     # cassowary constraint solver


fix key regex

fix key regex

--- lib/parse.coffee
+++ lib/parse.coffee
@@ -41,7 +41,7 @@
     ls.slice(start, end).join('\n')

   parsekey: (code) ->
-    match = code.match /\s*(\w*):\s*([^]*)/
+    match = code.match /^\s*(\w*):\s*([^]*)/
     return {code: code} unless match?
     [_, key, code] = match
     return {key: key, code: code}


fixing path for javac

fixing path for javac

--- browser/frontend.coffee
+++ browser/frontend.coffee
@@ -197,7 +197,7 @@
 commands =
   javac: (args, cb) ->
     rs = new runtime.RuntimeState(stdout, user_input, read_classfile)
-    jvm.run_class(rs, 'test/special/Javac', args, -> controller.reprompt())
+    jvm.run_class(rs, 'classes/util/Javac', args, -> controller.reprompt())
     return null  # no reprompt, because we handle it ourselves
   java: (args, cb) ->
     if !args[0]? or (args[0] == '-classpath' and args.length < 3)


removed a console.log

removed a console.log

--- lib/block-cursor.coffee
+++ lib/block-cursor.coffee
@@ -30,7 +30,6 @@
       maximum: 500

   activate: ->
-    console.log atom.config.get 'block-cursor.primaryColor'
     @cursorTypeObserveSubscription =
       atom.config.observe 'block-cursor.cursorType', (val) => @applyCursorType val
     @primaryColorObserveSubscription =


working base code for the flowing line

working base code for the flowing line

--- 3d/lines-app.coffee
+++ 3d/lines-app.coffee
@@ -6,7 +6,52 @@
 scene = undefined
 renderer = undefined
 mesh = undefined
+lines = []

+class FlowingLine
+  constructor: (@segments) ->
+    @geometry = new THREE.BufferGeometry()
+    @geometry.dynamic = true
+    @material = new THREE.LineBasicMaterial({vertexColors: THREE.VertexColors, linewidth:3})
+    @positions = new Float32Array(segments * 3)
+    @colors = new Float32Array(segments * 3)
+
+    @geometry.addAttribute "position", new THREE.BufferAttribute(@positions, 3)
+    @geometry.addAttribute "color", new THREE.BufferAttribute(@colors, 3)
+    #@geometry.computeBoundingSphere()
+    @mesh = new THREE.Line(@geometry, @material)
+
+  setDirty: ->
+    @geometry.attributes[ "position" ].needsUpdate = true
+    @geometry.attributes[ "color" ].needsUpdate = true
+    @geometry.needsUpdate = true
+
+  initial: (x, y, z0, z1) ->
+    ps = @positions
+    cs = @colors
+    for i in [0...@segments] by 1
+      i3=i*3
+      z = z0 + ((z1-z0)/(@segments-1))*i
+      ps[i3] = x
+      ps[i3+1] = y
+      ps[i3+2] = z
+      console.log "Add line point #{x} #{y} #{z}"
+      cs[i3]=1
+      cs[i3+1]=1
+      cs[i3+2]=1
+    @setDirty()
+
+  flow: (x, y) ->
+    ps = @positions
+    for i in [0...(@segments-1)] by 1
+      i3=i*3
+      ps[i3] = ps[i3+3]
+      ps[i3+1] = ps[i3+4]
+    i3 = (@segments-1)*3
+    ps[i3]   =x
+    ps[i3+1] =y
+    @setDirty()
+
 init = ->
   container = document.getElementById("container")

@@ -14,30 +59,16 @@
   camera = new THREE.PerspectiveCamera(27, window.innerWidth / window.innerHeight, 1, 4000)
   camera.position.z = 2750
   scene = new THREE.Scene()
-  segments = 10000
-  geometry = new THREE.BufferGeometry()
-  material = new THREE.LineBasicMaterial(vertexColors: THREE.VertexColors)
-  positions = new Float32Array(segments * 3)
-  colors = new Float32Array(segments * 3)
-  r = 800
-  for i in [0...segments] by 1
-    x = Math.random() * r - r / 2
-    y = Math.random() * r - r / 2
-    z = Math.random() * r - r / 2
-
-    # positions
-    positions[i * 3] = x
-    positions[i * 3 + 1] = y
-    positions[i * 3 + 2] = z
-
-    # colors
-    colors[i * 3] = (x / r) + 0.5
-    colors[i * 3 + 1] = (y / r) + 0.5
-    colors[i * 3 + 2] = (z / r) + 0.5
-  geometry.addAttribute "position", new THREE.BufferAttribute(positions, 3)
-  geometry.addAttribute "color", new THREE.BufferAttribute(colors, 3)
-  geometry.computeBoundingSphere()
-  mesh = new THREE.Line(geometry, material)
+
+
+  line = new FlowingLine 100 #segments
+  line.initial 0, 0, -200, 200
+
+  lines.push line
+
+  mesh = new THREE.Object3D
+  mesh.add line.mesh
+
   scene.add mesh

   #
@@ -63,12 +94,18 @@
   renderer.setSize window.innerWidth, window.innerHeight
   return

-#
+geometryTime = 0
+
 animate = ->
   requestAnimationFrame animate
   render()
   stats.update()
+  for line in lines
+    #updateGeometry line, geometryTime, true
+    line.flow Math.random()*200+100, Math.random()*200+100
+  geometryTime += 0.001
   return
+
 render = ->
   time = Date.now() * 0.001
   mesh.rotation.x = time * 0.25


Add proper rejection in scanBufferForVariables

--- lib/color-buffer.coffee
+++ lib/color-buffer.coffee
@@ -227,7 +227,7 @@
     markers.map (marker) => @variableMarkersByMarkerId[marker.id]

   scanBufferForVariables: ->
-    return if @destroyed
+    return Promise.reject("This ColorBuffer is already destroyed") if @destroyed
     results = []
     taskPath = require.resolve('./tasks/scan-buffer-variables-handler')
     editor = @editor


only activate another tab if the pane's active tab wasn't dragged away

--- src/packages/tabs/lib/tab-view.coffee
+++ src/packages/tabs/lib/tab-view.coffee
@@ -51,12 +51,14 @@

   onDragStart: (event) =>
     super
+
     pane = $(event.target).closest('.pane')
     paneIndex = rootView.indexOfPane(pane)
     event.originalEvent.dataTransfer.setData 'from-pane-index', paneIndex

   onDrop: (event) =>
     super
+
     transfer = event.originalEvent.dataTransfer
     previousDraggedTabIndex = transfer.getData 'sortable-index'

@@ -84,5 +86,5 @@
     toEditor.setActiveEditSessionIndex(currentDraggedTabIndex)
     toEditor.focus()

-    if fromPaneIndex != toPaneIndex
+    if fromPaneIndex != toPaneIndex && !draggedTab.hasClass('active')
       fromEditor.setActiveEditSessionIndex(0)


Log deprecations from apm test output

Log deprecations from apm test output

--- build/tasks/spec-task.coffee
+++ build/tasks/spec-task.coffee
@@ -10,6 +10,10 @@
   {isAtomPackage, spawn} = require('./task-helpers')(grunt)

   packageSpecQueue = null
+
+  logDeprecations = ({stdout}={}) ->
+    if process.env.JANKY_SHA1 and stdout?.indexOf('Calls to deprecated functions') isnt -1
+      grunt.log.error(stdout)

   getAppPath = ->
     contentsDir = grunt.config.get('atom.contentsDir')
@@ -54,6 +58,7 @@
           fs.unlinkSync(path.join(packagePath, 'ci.log'))

         failedPackages.push path.basename(packagePath) if error
+        logDeprecations(results)
         callback()

     modulesDirectory = path.resolve('node_modules')
@@ -87,6 +92,7 @@
       else
         # TODO: Restore concurrency on Windows
         packageSpecQueue.concurrency = concurrency
+        logDeprecations(results)

       callback(null, error)


Decrease chunk size

Decrease chunk size


more test fixes

more test fixes

--- apps/fair/test/client/for_you.coffee
+++ apps/fair/test/client/for_you.coffee
@@ -41,9 +41,9 @@
       done()

   after ->
+    sd.CURRENT_USER = undefined
     benv.teardown()
     Backbone.sync.restore()
-    sd.CURRENT_USER = false

   describe '#initialize', ->


Fix proxy setting in express config

Fix proxy setting in express config

--- app/express.coffee
+++ app/express.coffee
@@ -15,7 +15,7 @@
 # Config
 configure = () ->
   app.configure ->
-    app.set 'trust proxy' if config.proxy
+    app.enable 'trust proxy' if config.proxy
     app.set 'views', __dirname + '/../views'
     app.set 'view engine', 'jade'
     app.set 'view options', {layout: false}


add template id fields to project model

add template id fields to project model

--- services/web/app/coffee/models/Project.coffee
+++ services/web/app/coffee/models/Project.coffee
@@ -49,6 +49,8 @@
 		}
 	tokenAccessReadOnly_refs         : [ type:ObjectId, ref:'User' ]
 	tokenAccessReadAndWrite_refs     : [ type:ObjectId, ref:'User' ]
+	templateId: { type: Number }
+	templateVersionId: { type: Number }
 	overleaf          :
 		id              : { type: Number }
 		imported_at_ver_id : { type: Number }


syntax detection based on scope, not grammar

this allows emmet to work in html/css based languages
like hanbdlebars or erb.

--- lib/editor-proxy.coffee
+++ lib/editor-proxy.coffee
@@ -128,13 +128,13 @@

   # Returns the editor's syntax mode.
   getSyntax: ->
-    grammar = @editor.getGrammar().name.toLowerCase()
-    if /\b(less|scss|sass|css|stylus)\b/.test(grammar)
-      return "css"
-    else if /\b(html|xml|haml|slim)\b/.test(grammar)
-      return "html"
-    else
-      return null
+    scopes = @editor.getCursorScopes()
+    for scope in scopes
+      if /^text\.html\./.test(scope)
+        return "html"
+      else if /^source\.css\./.test(scope)
+        return "css"
+    return null

   # Returns the current output profile name
   #


fix mistake

fix mistake

--- keymaps/narrow.cson
+++ keymaps/narrow.cson
@@ -11,7 +11,7 @@
   'ctrl-cmd-n': 'narrow:next-item'
   'cmd-[': 'narrow:previous-query-history'
   'cmd-]': 'narrow:next-query-history'
-  'cmd-e': 'narrow:next-query-history'
+  'cmd-e': 'narrow:query-current-word'

 # Only on narrow-editor
 # ---------------------------


Woops

Woops

--- framer/Components/NavComponent.coffee
+++ framer/Components/NavComponent.coffee
@@ -80,9 +80,6 @@

 	@define "previous",
 		get: -> return @_stack[@_stack.length - 2]?.layer
-
-	@define "scrollComponent",
-		get: -> return @_stack[@_stack.length - 1]?

 	##############################################################
 	# Header and footer


Fixing Mood Poll (#53)

Fixing Mood Poll (#53)

--- scripts/awesome.team.coffee
+++ scripts/awesome.team.coffee
@@ -34,45 +34,43 @@
       robot.messageRoom room, "Are there any achievements from the week that someone @here needs to add to your board '" + board.name + "'?"
   , null, true, 'America/New_York'

-  moodPoll = new CronJob '00 20 20 * * 1-5', ->
+  moodPoll = new CronJob '00 40 20 * * 1-5', ->
     for room, board of robot.brain.data._private.board
       buttonName = "mood_" + robot.brain.data._private.team[room]._id
-      pollMessage = {
-        "attachments": [{
-          "text": "How was your day today?",
-          "fallback": "Oh no! Something went horribly wrong!"
-          "callback_id": "dab_mood"
-          "color": "#3AA3E3"
-          "attachment_type": "default"
-          "actions": [{
-            "name": buttonName
-            "text": ":bolt-ecstatic:"
-            "type": "button"
-            "value": "ecstatic"
-          },{
-            "name": buttonName
-            "text": ":bolt-happy:"
-            "type": "button"
-            "value": "happy"
-          },{
-            "name": buttonName
-            "text": ":bolt-indifferent:"
-            "type": "button"
-            "value": "indifferent"
-          },{
-            "name": buttonName
-            "text": ":bolt-disappointed:"
-            "type": "button"
-            "value": "disappointed"
-          },{
-            "name": buttonName
-            "text": ":bolt-sad:"
-            "type": "button"
-            "value": "sad"
+      robot.emit 'slack.attachment',
+        message: room
+        content:
+          text: "How was your day today?"
+          fallback: "Oh no! Something went horribly wrong!"
+          callback_id: "dab_mood"
+          color: "#3AA3E3"
+          attachment_type: "default"
+          actions: [{
+            name: buttonName
+            text: ":bolt-ecstatic:"
+            type: "button"
+            value: "ecstatic"
+          },{
+            name: buttonName
+            text: ":bolt-happy:"
+            type: "button"
+            value: "happy"
+          },{
+            name: buttonName
+            text: ":bolt-indifferent:"
+            type: "button"
+            value: "indifferent"
+          },{
+            name: buttonName
+            text: ":bolt-disappointed:"
+            type: "button"
+            value: "disappointed"
+          },{
+            name: buttonName
+            text: ":bolt-sad:"
+            type: "button"
+            value: "sad"
           }]
-        }]
-      }
-      robot.messageRoom room, pollMessage
   , null, true, 'America/New_York'

   robot.hear /what is the team's mood in the past (.*) days/i, (msg) ->


Search completion; tweaks.

Search completion; tweaks.

--- background_scripts/completion.coffee
+++ background_scripts/completion.coffee
@@ -395,14 +395,16 @@
         # Suppress the "w" from "w query terms" in the vomnibar input.
         suppressLeadingKeyword: true

-    # Exclude results from other completers if this is a custom search engine and we have a completer.
+    haveCompletionEngine = CompletionEngines.haveCompletionEngine searchUrl
+    # If this is a custom search engine and we have a completer, then exclude results from other completers.
     filter =
-      if custom and CompletionEngines.haveCompletionEngine searchUrl
+      if custom and haveCompletionEngine
         (suggestion) -> suggestion.type == description
       else
         null

-    if queryTerms.length == 0
+    # Post suggestions and bail if there is no prospect of adding further suggestions.
+    if queryTerms.length == 0 or not haveCompletionEngine
       return onComplete suggestions, { filter }

     onComplete suggestions,


Fixed incorrect RAM size.

Fixed incorrect RAM size.

--- emulator/core/units/cpu-memory.coffee
+++ emulator/core/units/cpu-memory.coffee
@@ -46,7 +46,7 @@
     ###########################################################

     createRAM: ->
-        @ram = (0 for [0...0x07FF]) # 2KB of RAM (mirrored in 8K at $0000-$1FFF)
+        @ram = (0 for [0..0x07FF]) # 2KB of RAM (mirrored in 8K at $0000-$1FFF)
         undefined

     readRAM: (address) ->


require ink 0.11.2

require ink 0.11.2

--- lib/julia-client.coffee
+++ lib/julia-client.coffee
@@ -8,7 +8,7 @@
 semver = require 'semver'

 # TODO: Update me when tagging a new relase:
-INK_VERSION_COMPAT  = "^0.11"
+INK_VERSION_COMPAT  = "^0.11.2"

 INK_LINK            = '[`ink`](https://github.com/JunoLab/atom-ink)'
 LANGUAGE_JULIA_LINK = '[`language-julia`](https://github.com/JuliaEditorSupport/atom-language-julia)'


make the last commit an actually valid coffescript

--- lib/line_wrapper.coffee
+++ lib/line_wrapper.coffee
@@ -102,7 +102,7 @@
         @emit 'sectionEnd', options, this

         if ++@column > @columns
-            var x = @document.x
+            x = @document.x
             @document.addPage()
             @column = 1
             @startY = @document.page.margins.top


Fixes #428

Fixes #428

--- lib/line_wrapper.coffee
+++ lib/line_wrapper.coffee
@@ -57,7 +57,7 @@
     # setup a unicode line breaker
     breaker = new LineBreaker(text)
     last = null
-    wordWidths = {}
+    wordWidths = Object.create(null)

     while bk = breaker.nextBreak()
       word = text.slice(last?.position or 0, bk.position)


Make sure we don't pass the maximum Y position

--- lib/line_wrapper.coffee
+++ lib/line_wrapper.coffee
@@ -89,7 +89,7 @@

                 # if we've reached the edge of the page,
                 # continue on a new page or column
-                if @document.y > @maxY
+                if @document.y + @document.currentLineHeight(true) > @maxY
                     @nextSection()

                 # reset the space left and buffer


:art: Make gutter independent of underlineIssues

--- lib/linter-views.coffee
+++ lib/linter-views.coffee
@@ -172,12 +172,12 @@
     activeEditor = atom.workspace.getActiveTextEditor()
     @messages.forEach (message) =>
       if @scope is 'file' then return unless message.currentFile
-      if @underlineIssues and message.currentFile and message.range #Add the decorations to the current TextEditor
+      if message.currentFile and message.range #Add the decorations to the current TextEditor
         @markers.push marker = activeEditor.markBufferRange message.range, {invalidate: 'never'}
         activeEditor.decorateMarker(
           marker, type: 'line-number', class: "linter-highlight #{message.class}"
         )
-        activeEditor.decorateMarker(
+        if @underlineIssues then activeEditor.decorateMarker(
           marker, type: 'highlight', class: "linter-highlight #{message.class}"
         )
       Element = Message.fromMessage(message, addPath: @scope is 'project', cloneNode: true)


add namespace to subscribe/unsubscribe @editor event handlers

--- lib/minimap-view.coffee
+++ lib/minimap-view.coffee
@@ -78,14 +78,12 @@
     @scrollView = @editorView.scrollView
     @scrollViewLines = @scrollView.find('.lines')

-    # current editor binds scroll events
-    @unsubscribe @editor, 'screen-lines-changed'
-    @unsubscribe @editor, 'scroll-top-changed.editor'
-    @unsubscribe @editor, 'scroll-left-changed.editor'
-
-    @subscribe @editor, 'screen-lines-changed', => @miniEditorView.update()
-    @subscribe @editor, 'scroll-top-changed.editor', @updateScroll
-    @subscribe @editor, 'scroll-left-changed.editor', @updateScroll
+    # current editor binds event handlers
+    @unsubscribe @editor, '.minimap'
+
+    @subscribe @editor, 'screen-lines-changed.minimap', => @miniEditorView.update()
+    @subscribe @editor, 'scroll-top-changed.minimap', @updateScroll
+    @subscribe @editor, 'scroll-left-changed.minimap', @updateScroll

   getEditorView: -> @paneView.viewForItem(@activeItem)


improve less

improve less

--- lib/minimap-view.coffee
+++ lib/minimap-view.coffee
@@ -24,7 +24,8 @@
   initialize: ->
     @attach()

-    @on('mousewheel', @mousewheel.bind(this))
+    @on('mousewheel', @mouseWheel.bind(this))
+    @on('mousedown', @mouseDown.bind(this))

     @subscribe @paneView.model.$activeItem, @onActiveItemChanged
     @subscribe @paneView.model, 'destroy', => @destroy()
@@ -134,7 +135,7 @@
   getScrollViewClientRect: ->
     @scrollViewLines[0].getBoundingClientRect()

-  mousewheel: (e) ->
+  mouseWheel: (e) ->
     {wheelDeltaX, wheelDeltaY} = e.originalEvent
     if wheelDeltaX
       @editorView.scrollLeft(@editorView.scrollLeft() - wheelDeltaX)
@@ -159,6 +160,9 @@
       @miniOverlayer[0].style.webkitTransform =
         @miniOverlayer[0].style.transform = 'translate3d(0, ' + top + 'px, 0)'

+  mouseDown: (e) ->
+    console.dir(e)
+
   transform: (width, scale, xy) ->
     scaleStr = 'scale(' + scale.join(',') + ')'
     translateStr = 'translate3d(' + xy.join(',') + 'px, 0)'


:bug: Fix (all?) first render and resize issues

-Use observeItem instead of deprecated $activeItem, observeItem is called a bit later apparently
-Force full render on pane change, this fixes a few issues:
 - incorrect update when the buffer has changed outside the editor
 - minimap not updated when syntax theme has changed

--- lib/minimap-view.coffee
+++ lib/minimap-view.coffee
@@ -57,7 +57,8 @@
     @on 'mousedown', @onMouseDown
     @miniVisibleArea.on 'mousedown', @onDragStart

-    @subscribe @paneView.model.$activeItem, @onActiveItemChanged
+    @paneView.model.observeActiveItem @onActiveItemChanged
+
     # Fix items movin to another pane.
     @subscribe @paneView.model, 'item-removed', (item) -> item.off? '.minimap'

@@ -241,14 +242,11 @@

   # EVENT CALLBACKS

-  onActiveItemChanged: (item) =>
-    # Fix called twice when opening minimap!
-
-    activeView = @paneView.viewForItem(item)
-    if activeView is @editorView
+  onActiveItemChanged: (activeItem) =>
+    if activeItem is @editor
       @attachToPaneView() if @parent().length is 0
-      @updateMinimapEditorView()
       @updateMinimapView()
+      @miniEditorView.forceUpdate()
     else
       @detachFromPaneView() if @parent().length is 1
       @paneView.addClass('with-minimap') if activeView?.hasClass('editor')


Stupidly simple implementation but works well #601

--- lib/misc-command.coffee
+++ lib/misc-command.coffee
@@ -49,17 +49,24 @@
     newRanges = []
     oldRanges = []

-    disposable = @editor.getBuffer().onDidChange ({oldRange, newRange}) ->
-      if newRange.containsRange(oldRange)
-        newRanges.push(newRange)
-        return
-
-      if oldRange.containsRange(newRange)
-        oldRanges.push(oldRange)
-        return
-
-      oldRanges.push(oldRange) unless oldRange.isEmpty()
-      newRanges.push(newRange) unless newRange.isEmpty()
+    disposable = @editor.getBuffer().onDidChange ({oldRange, newRange, oldText, newText}) ->
+      switch
+        when not newRange.isEmpty() and oldRange.isEmpty()
+          console.log "Add only"
+          newRanges.push(newRange)
+        when newRange.isEmpty() and not oldRange.isEmpty()
+          console.log "Delete only"
+          oldRanges.push(oldRange)
+        when not newRange.isEmpty() and not oldRange.isEmpty()
+          if newRange.containsRange(oldRange)
+            console.log "replace longer area"
+            newRanges.push(newRange)
+          else
+            console.log 'replace shorter area'
+            newRanges.push(newRange)
+            oldRanges.push(new Range(newRange.end, oldRange.end))
+        when newRange.isEmpty() and oldRange.isEmpty()
+          console.log 'never happens'

     fn()


fixed initialize of datatables

fixed initialize of datatables

--- app/assets/javascripts/datatables_init.js.coffee
+++ app/assets/javascripts/datatables_init.js.coffee
@@ -1,4 +1,4 @@
-jQuery ->
+init_datatables = ->
   $('#datos').dataTable
     oLanguage:
       "sProcessing":     "Procesando...",
@@ -22,4 +22,7 @@
       "oAria": {
           "sSortAscending":  ": Activar para ordenar la columna de manera ascendente",
           "sSortDescending": ": Activar para ordenar la columna de manera descendente"
-      }+      }
+
+jQuery init_datatables
+$(document).on "page:load", init_datatables


EDSC-109: Fix dataset spatial display

EDSC-109: Fix dataset spatial display

--- app/assets/javascripts/modules/map/map.js.coffee
+++ app/assets/javascripts/modules/map/map.js.coffee
@@ -40,8 +40,6 @@
       map.addControl(new SpatialSelection())
       @setProjection(projection)

-      # TODO: I need to be subscribed to selected.details, but I can't because details doesn't
-      # exist until a dataset is selected
       @_datasetSubscription = page.ui.datasetsList.selected.subscribe(@_showDatasetSpatial)
       @_granuleVisualizationSubscription = Dataset.visible.subscribe (datasets) ->
         map.fire('edsc.visibledatasetschange', datasets: datasets)
@@ -166,10 +164,6 @@
       @_rebuildLayers()

     _showDatasetSpatial: (dataset) =>
-      # TODO: This is failing because details() doesn't exist yet
-      dataset = dataset.details().summaryData
-      # dataset = dataset.summaryData
-
       @_hideDatasetSpatial()

       layer = new L.FeatureGroup()


cleanup/docs

cleanup/docs

--- app/assets/javascripts/monitoring/util.js.coffee
+++ app/assets/javascripts/monitoring/util.js.coffee
@@ -28,6 +28,7 @@
   footer_text = sprintf("Last Value: %0.2f%s", last_val, unit)
   footer.text(footer_text)

+  # draw graph
   opts = {
     labels: [ "Date/Time", "v" ]
     strokeWidth: 2
@@ -43,6 +44,8 @@
     # set range if known
     opts.valueRange = [ 0, 100 ]

+  ####
+  # custom zoom/pan handling
   opts.interactionModel = _.clone(Dygraph.Interaction.defaultModel)

   # override mousedown to allow toggling pan mode
@@ -105,6 +108,7 @@
         g.updateOptions({ file: all_data })

+  ####
   # draw
   g = new Dygraph(el, vals, opts)
   g._bixby_mode = "zoom"
@@ -150,6 +154,7 @@

   }
   g.updateOptions(opts)
+
   return g


saving two lines of code

saving two lines of code

--- app/assets/javascripts/mtl/document_modal.coffee
+++ app/assets/javascripts/mtl/document_modal.coffee
@@ -83,15 +83,13 @@
 initDocumentModal = ($fileCard) ->
   filename = $fileCard.data('mtl-document-name')
   url = $fileCard.prop('href')
-  hasNext = hasNextFileCard($fileCard)
-  hasPrev = hasPrevFileCard($fileCard)
   $modal = $(
     MTL.renderTemplate(
       'document_modal',
       filename: filename,
       url: url,
-      hasNext: hasNext,
-      hasPrev: hasPrev
+      hasNext: hasNextFileCard($fileCard),
+      hasPrev: hasPrevFileCard($fileCard)
     )
   )
   $modal.find('.document-modal-next').on('click', -> initDocumentModal(nextFileCard($fileCard)))


Remove old code

Remove old code

--- app/assets/javascripts/stores/block_store.coffee
+++ app/assets/javascripts/stores/block_store.coffee
@@ -7,7 +7,6 @@
 _persisted = {}
 _trainingModule = {}
 _editableBlockIds = []
-_editingAddedBlock = false

 # Utilities
 setBlocks = (data, persisted=false) ->
@@ -32,7 +31,7 @@
 addBlock = (week_id) ->
   week_blocks = BlockStore.getBlocksInWeek week_id
   week_blocks = $.grep week_blocks, (block) -> !block.deleted
-  block = {
+  setBlock {
     id: Date.now(),
     is_new: true,
     kind: 0,
@@ -43,12 +42,9 @@
     order: week_blocks.length,
     duration: null
   }
-  setBlock block
-  setEditableBlockId(block.id)

 removeBlock = (block_id) ->
   delete _blocks[block_id]
-  _editingAddedBlock = false
   BlockStore.emitChange()

 insertBlock = (block, toWeek, targetIndex) ->
@@ -75,18 +71,12 @@
   if fromWeekId != toWeek.id
     toWeekBlocks = BlockStore.getBlocksInWeek(toWeek.id)
     toWeekBlocks.forEach (b, i) ->
-      if b == block
       b.order = i
       setBlock b, true
   BlockStore.emitChange()

-isAddedBlock = (blockId) ->
-  # new block ids are set to Date.now()
-  blockId > 1000000000
-
 setEditableBlockId = (blockId) ->
   _editableBlockIds.push(blockId)
-  _editingAddedBlock = true if isAddedBlock(blockId)
   BlockStore.emitChange()

@@ -104,21 +94,17 @@
       .sort((a,b) -> a.order - b.order)
   restore: ->
     _blocks = $.extend(true, {}, _persisted)
-    _editingAddedBlock = false
     BlockStore.emitChange()
   getTrainingModule: ->
     return _trainingModule
-  getEditableBlockIds: ->
+  getEditableBlockId: ->
     return _editableBlockIds
   clearEditableBlockIds: ->
     _editableBlockIds = []
     BlockStore.emitChange()
   cancelBlockEditable: (block_id) ->
     _editableBlockIds.splice(_editableBlockIds.indexOf(block_id), 1)
-    _editingAddedBlock = false
     BlockStore.emitChange()
-  editingAddedBlock: ->
-    return _editingAddedBlock

 , (payload) ->
   data = payload.data
@@ -141,7 +127,6 @@
       break
     when 'SET_BLOCK_EDITABLE'
       setEditableBlockId data.block_id
-      break
   return true

 module.exports = BlockStore


Fix typo

Fix typo

--- app/app_view.coffee
+++ app/app_view.coffee
@@ -58,7 +58,7 @@

     @dashboardModel.save().done =>
       @dashboardModel.on 'sync:tools', (tool) =>
-        paramsFromatted = new Array
+        paramsFormatted = new Array
         for param in params
           [key, value...] = param.split('_')
           paramsFormatted.push {key: key, val: value.join('_')}


@options should propagate up to their original value.

--- src/view/bindings/view_binding.coffee
+++ src/view/bindings/view_binding.coffee
@@ -46,5 +46,11 @@
   constructor: (definition, @option, @targetView) ->
     super(definition)

+    @targetView.observe @option, (value) =>
+      return if @isDataChanging
+      @view.set(@keyPath, value)
+
   dataChange: (value) ->
+    @isDataChanging = true
     @targetView.set(@option, value)
+    @isDataChanging = false


Routes: First fetch machine then workspace.

--- client/ide/lib/routes.coffee
+++ client/ide/lib/routes.coffee
@@ -177,22 +177,24 @@
       { machineLabel } = info.params

       # we assume that if machineLabel is all numbers it is the channelId - SY
-      if /^[0-9]+$/.test machineLabel
-        loadCollaborativeIDE machineLabel
+      if /^[0-9]+$/.test machineLabel then loadCollaborativeIDE machineLabel
       else
-        dataProvider.fetchMachineByLabel machineLabel, (machine) ->
+        dataProvider.fetchMachine machineLabel, (machine) ->
           if machine then routeToMachineWorkspace machine
           else routeToLatestWorkspace()

     when 'workspace'
       { params } = info
-      params.username = username = nick()
-
-      dataProvider.fetchMachineAndWorkspace params, (machine, workspace) ->
-
-        if machine and workspace
-          loadIDE { machine, workspace, username }
-        else if machine
-          routeToMachineWorkspace machine
+
+      dataProvider.fetchMachine params.machineLabel, (machine) =>
+
+        if machine
+          username = machine.getOwner()
+          data = machineUId: machine.uid, workspaceSlug: params.workspaceSlug
+
+          dataProvider.fetchWorkspaceByMachineUId data, (workspace) =>
+            if workspace then loadIDE { machine, workspace, username }
+            else routeToMachineWorkspace machine
+
         else
           routeToLatestWorkspace()


IDE: Removed terminal icon from dummy file tree header.

--- client/ide/namespaces.coffee
+++ client/ide/namespaces.coffee
@@ -6,7 +6,7 @@
     return """

#{nickname}:~$

"""

   getFileTree: (nickname, machineLabel) ->
-    return """

#{machineLabel}
<button type="button" class="kdbutton terminal" id="kd-276"></button>

/home/#{nickname}

Applications

Backup

Documents

Web

.bash_logout

.bashrc

.profile

"""
+    return """

#{machineLabel}

/home/#{nickname}

Applications

Backup

Documents

Web

.bash_logout

.bashrc

.profile

"""

 window.IDE.contents =


Add log

Add log

--- app/src/tasks/wallet_layout_recovery_task.coffee
+++ app/src/tasks/wallet_layout_recovery_task.coffee
@@ -18,6 +18,7 @@
     unconfirmedTxs = @_findUnconfirmedTransaction()
     startDate = new Date()
     $info "Start synchronization", startDate.toString()
+    $info "Looking for mempool tx", unconfirmedTxs
     @_performRecovery(unconfirmedTxs)
     .then (transactionsNotFound) =>
       $info "Recovery completed"


removed unused code

removed unused code

--- imports/server/stateReconciler.coffee
+++ imports/server/stateReconciler.coffee
@@ -32,10 +32,6 @@
   overallState = stateF doc, services
   overallState = 'failing' unless overallState
   Instances.update doc._id, $set: state: overallState
-
-  for serviceName, service of doc.services
-    if service.state is 'running'
-      Instances.update {_id: doc._id, 'steps.name': serviceName}, {$set: {'steps.$.completed': true}}

 f = false


Fix bug in collection proxy build

Fix bug in collection proxy build

--- src/associations/collection_proxy.coffee
+++ src/associations/collection_proxy.coffee
@@ -111,7 +111,7 @@
       else
         _.extend(attributes, @queryParams()['filter'])

-    resources = @base.build(attributes)
+    resources = ActiveResource::Collection.build(@base.build(attributes))
     resources.each (r) =>
       r.assignResourceRelatedQueryParams(@queryParams())


Fix storing of preview tab

Fix storing of preview tab

--- lib/tab-bar-view.coffee
+++ lib/tab-bar-view.coffee
@@ -15,7 +15,7 @@
     @subscriptions = new CompositeDisposable

     @subscriptions.add atom.commands.add atom.views.getView(@pane),
-      'tabs:keep-preview-tab': => tab.clearPreview() for tab in @getTabs()
+      'tabs:keep-preview-tab': => @clearPreviewTabs()

     @subscriptions.add atom.commands.add @element,
       'tabs:close-tab': => @closeTab()
@@ -36,6 +36,7 @@

     @paneContainer = @pane.getContainer()
     @addTabForItem(item) for item in @pane.getItems()
+    @clearPreviewTabs()

     @subscriptions.add @pane.onDidDestroy =>
       @unsubscribe()
@@ -51,8 +52,8 @@

     @subscriptions.add @pane.onDidChangeActiveItem (item) =>
       if @isPreviewableItem(item) and @getTabs().length > 1
-        previewTab = @getPreviewTab()
-        @pane.destroyItem(previewTab.item) if previewTab
+        @pane.destroyItem(@previewTab.item) if @previewTab?.isPreviewTab
+        @previewTab = @tabForItem(item)
       @updateActiveTab()

     @subscriptions.add atom.config.observe 'tabs.tabScrolling', => @updateTabScrolling()
@@ -94,13 +95,15 @@
   isPreviewableItem: (item) ->
     atom.config.get('tabs.usePreviewTabs') and item instanceof TextEditor

+  clearPreviewTabs: ->
+    tab.clearPreview() for tab in @getTabs()
+    return
+
   addTabForItem: (item, index) ->
     tabView = new TabView()
     tabView.initialize(item)
+    @previewTab ?= tabView if tabView.isPreviewTab
     @insertTabAtIndex(tabView, index)
-
-  getPreviewTab: ->
-    _.detect @getTabs(), (tab) -> tab.isPreviewTab

   moveItemTabToIndex: (item, index) ->
     if tab = @tabForItem(item)