Removed white lines

Removed white lines

--- core/app/backbone/models/wheel.coffee
+++ core/app/backbone/models/wheel.coffee
@@ -87,9 +87,7 @@
         mp_track "Factlink: Opinionate",
           factlink: fact_id
           opinion: opinion_type
-
         options.success?()
-
       error: =>
         # TODO: This is not a proper undo. Should be restored to the current
         #       state when the request fails.
@@ -107,7 +105,6 @@
         mp_track "Factlink: De-opinionate",
           factlink: fact_id
         options.success?()
-
       error: =>
         @turnOnActiveOpinionType opinion_type
         options.error?()


Font and base support added

Font and base support added

--- main/static/src/coffee/site/countdown.coffee
+++ main/static/src/coffee/site/countdown.coffee
@@ -64,15 +64,26 @@
     months: (milliseconds / MONTH).toFixed(7)
     years: (milliseconds / YEAR).toFixed(8)

+  base = get_parameter_by_name('base') or 10
+  try
+    base = Math.max(2, Math.min(16, parseInt(base)))
+  catch
+    base = 10
+
   for unit in UNITS
-    ($ '.int', ".#{unit}").text add_commas(values[unit].split('.')[0])
+    if base == 10
+      ($ '.int', ".#{unit}").text add_commas(values[unit].split('.')[0])
+    else
+      ($ '.int', ".#{unit}").text parseInt(values[unit].split('.')[0]).toString(base)
+
     ($ '.dec', ".#{unit}").text values[unit].split('.')[1]

     ($ ".#{unit}").css('opacity', Math.max(0.1, Math.min(1, parseFloat(values[unit]))))

 window.set_theme = ->
-  background = get_parameter_by_name 'background'
-  color = get_parameter_by_name 'color'
+  background = get_parameter_by_name('background') or get_parameter_by_name('b')
+  color = get_parameter_by_name('color') or get_parameter_by_name('c')
+  font = get_parameter_by_name('font') or get_parameter_by_name('f')

   background = new one.color(background)
   color = new one.color(color)
@@ -80,3 +91,7 @@
   ($ 'body').css 'background', background.hex() if background._red?
   ($ 'body, h1, h2, h3').css 'color', color.hex() if color._red?

+  if font
+    ($ 'head').append("<link href='http://fonts.googleapis.com/css?family=#{font}' rel='stylesheet' type='text/css'>")
+    ($ 'body, h1, h2, h3').css 'font-family', font
+


Fix help capture.

Fix help capture.

--- src/testing/page.coffee
+++ src/testing/page.coffee
@@ -119,7 +119,7 @@
         expect(helpBox, 'The help box is missing').to.exist
         helpBox.getInnerHtml()
     else
-      findButton().then (btn) ->
+      findButton().then (btn) =>
         expect(btn, 'The help button is missing').to.exist
         # Open the help box...
         btn.click().then =>
@@ -127,9 +127,10 @@
           @find('.qi-help').then (helpBox) =>
             expect(helpBox, "The help box is not shown after click")
               .to.exist
-          # Click again to restore the initial state.
-          btn.click().then ->
-            helpBox.getInnerHtml()
+            help = helpBox.getInnerHtml()
+            # Click again to restore the initial state.
+            btn.click().then =>
+              help

 # The help suggestion box hyperlink constant.


payment: also call workflow.enter from domain product form

--- client/app/Applications/Environments.kdapplication/views/domains/domainbuyform.coffee
+++ client/app/Applications/Environments.kdapplication/views/domains/domainbuyform.coffee
@@ -27,9 +27,9 @@
         title       : "Register <em>#{ domain }</em>"
         workflow    : workflow

+      workflow.enter()
+
       workflow.on 'DataCollected', -> debugger
-#      ({ productData, paymentMethodId }) =>
-#        @buyDomain { domain, year, price, paymentMethodId, productData }

   buyDomain: (options) ->
     { JDomain } = KD.remote.api


levels and default game states

levels and default game states

--- src/title_screen.coffee
+++ src/title_screen.coffee
@@ -7,6 +7,6 @@
   self.bind "overlay", (canvas) ->
     self.centerText canvas, App.name

-    self.centerText canvas, "Press "
+    self.centerText canvas, "Press any key to "

   return self


remove unused helpers.coffee

remove unused helpers.coffee

fixes #46

--- app/assets/javascripts/application/modules/helpers.coffee
+++ app/assets/javascripts/application/modules/helpers.coffee
@@ -1,29 +0,0 @@
-Postal.Helpers =
-  numberWithDelimiters: (number)->
-    number.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",")
-
-  pluralize: (number, word)->
-    if number == 1
-      "1 #{word}"
-    else
-      "#{number} #{word}s"
-
-  numberToHumanSize: (sizeInBytes)->
-    wholes = Math.floor(Math.log(sizeInBytes) / Math.log(1024))
-    unit = ['bytes', 'KB', 'MB', 'GB', 'TB'][wholes]
-    i = (sizeInBytes / Math.pow(1024, wholes))
-    if unit
-      i = if unit == 'bytes' then i.toFixed(0) else i.toFixed(2)
-      "#{i} #{unit}"
-    else
-      "0 bytes"
-
-  styleWidth: (width)->
-    width = 100 if width > 100.0
-    width = 0 if width < 0
-    string = "width:#{width}%;"
-    if width >= 100
-      string = string + "background-color:#e2383a;"
-    else if width >= 90
-      string = string + "background-color:#e8581f;"
-    string


bind publish method

bind publish method

--- src/scaleApp.Mediator.coffee
+++ src/scaleApp.Mediator.coffee
@@ -44,7 +44,7 @@
   #                              By default the data object gets copied so that
   #                              other modules can't influence the original
   #                              object.
-  publish: (channel, data, publishReference) ->
+  publish: (channel, data, publishReference) =>

     if @channels[channel]?
       for subscription in @channels[channel]


Fix manatee detection

Fix manatee detection

Adds word boundaries to yelling regexp for more appropriate
administration of manatees.

--- src/scripts/calm-down.coffee
+++ src/scripts/calm-down.coffee
@@ -21,6 +21,6 @@

   unless process.env.HUBOT_LESS_MANATEES
     robot.hear ///
-      (([A-Z]{2,}\s+)([A-Z]{2,})\s?)|
-      ([A-Z]{5,})
-    ///, (msg) -> msg.send manatee()+      (\b([A-Z]{2,}\s+)([A-Z]{2,})\b)|
+      (\b[A-Z]{5,}\b)
+    ///, (msg) -> msg.send manatee()


Resolve commands

Resolve commands

--- src/scripts/pagerduty.coffee
+++ src/scripts/pagerduty.coffee
@@ -257,28 +257,28 @@
       return

     force = msg.match[5]?
-    pagerduty.getIncidents "acknowledged", (err, incidents) ->
-      if err?
-        robot.emit 'error', err, msg
-        return
-
-      email  = msg.message.user.pagerdutyEmail || msg.message.user.email_address
-      filteredIncidents = if force
-                            incidents # don't filter at all
-                          else
-                            incidentsForEmail(incidents, email) # filter by email
-      if filteredIncidents.length is 0
-        # nothing assigned to the user, but there were others
-        if incidents.length > 0 and not force
-          msg.send "Nothing assigned to you to resolve. Resolve someone else's incident with `hubot pager ack <nnn>`"
-        else
-          msg.send "Nothing to resolve"
-        return
-
-      incidentNumbers = (incident.incident_number for incident in filteredIncidents)
-
-      # only resolve things that are acknowledged
-      updateIncidents(msg, incidentNumbers, 'acknowledged', 'resolved')
+    pagerduty.getIncidents 'acknowledged', (err, incidents) ->
+      if err?
+        robot.emit 'error', err, msg
+        return
+
+      campfireUserToPagerDutyUser msg, msg.message.user, (user) ->
+        filteredIncidents = if force
+                              incidents # don't filter at all
+                            else
+                              incidentsByUserId(incidents, user.id) # filter by id
+        if filteredIncidents.length is 0
+          # nothing assigned to the user, but there were others
+          if incidents.length > 0 and not force
+            msg.send "Nothing assigned to you to resolve. Resolve someone else's incident with `hubot pager ack <nnn>`"
+          else
+            msg.send "Nothing to resolve"
+          return
+
+        incidentNumbers = (incident.incident_number for incident in filteredIncidents)
+
+        # only resolve things that are acknowledged
+        updateIncidents(msg, incidentNumbers, 'acknowledged', 'resolved')

   robot.respond /(pager|major)( me)? notes (.+)$/i, (msg) ->
     msg.finish()


Make sure the user specified exists in hubot's brain

--- src/scripts/pagerduty.coffee
+++ src/scripts/pagerduty.coffee
@@ -208,6 +208,10 @@
   robot.respond /(pager|major)( me)? (override) ([\w\-:]*) - ([\w\-:]*)( (.*))?$/i, (msg) ->
     if msg.match[7]
       overrideUser = robot.brain.userForName(msg.match[7])
+
+      unless overrideUser
+        msg.send "Sorry, I don't seem to know who that is. Are you sure they are in chat?"
+        return
     else
       overrideUser = msg.message.user


general js refactoring - refactor sticky_mobile_nav to use more ActiveScript style and prevent pixel jumps

--- app/assets/javascripts/sticky_mobile_nav/presenter.coffee
+++ app/assets/javascripts/sticky_mobile_nav/presenter.coffee
@@ -1,30 +1,33 @@
 Clarat.StickyMobileNav = {}
 class Clarat.StickyMobileNav.Presenter extends ActiveScript.Presenter
+  constructor: ->
+    @bannerAndNavHeight =
+      $('.Banner').outerHeight() + $('.nav-welt--notstart').outerHeight()
+    @formDistributor = $('.Distributor__search-form')
+    @navDistributor = $('.Distributor--navigation')
+    super()

   CALLBACKS:
     window:
-      resize: 'handler'
-      load: 'handler'
+      resize: 'handleNewScreenWidth'
+      load: 'handleNewScreenWidth'
+      scroll: 'handleScrolled'

-  handler: ()  =>
+  handleNewScreenWidth: =>
+    @isSmartphone = ($(window).width() < 501)

-    wrap = $(window)
-    smartphone = ($(window).width() < 501)
-    $homeTemplate = $("body.template--pages-home").length
-    bannerAndNavHeight = $('.Banner').outerHeight() + $('.nav-welt--notstart').outerHeight() # + 20 if
-    distributor = $('.Distributor__search-form')
+  handleScrolled: =>
+    return unless @isSmartphone

-    if $homeTemplate
-      return
-
-    if smartphone
-      wrap.on "scroll", ->
-
-        if $(this).scrollTop() > bannerAndNavHeight
-          distributor.addClass 'is-fixed'
-        else
-          distributor.removeClass 'is-fixed'
+    if $(window).scrollTop() > @bannerAndNavHeight
+      @formDistributor.addClass 'is-fixed'
+      # prevent pixel jump:
+      @navDistributor.css 'margin-bottom', "#{@bannerAndNavHeight}px"
+    else
+      @formDistributor.removeClass 'is-fixed'
+      @navDistributor.css 'margin-bottom', '0'

 $(document).ready ->
+  return if $("body.template--pages-home").length
   new Clarat.StickyMobileNav.Presenter


Client: Activity: Created sidebarModalPublicChannelThreads. It returns followed private channel threads mapped with relevant channel instances for sidebar modal.

--- client/activity/lib/flux/getters.coffee
+++ client/activity/lib/flux/getters.coffee
@@ -151,6 +151,18 @@
       return thread.set 'channel', channel
 ]

+# Returns followed public channel threads mapped with relevant channel instances for sidebar modal.
+sidebarModalPublicChannelThreads = [
+  channelThreads
+  followedPublicChannels
+  (threads, channels) ->
+    {
+      followed: channels.map (channel) -> threads.get channel.get('id')
+      unfollowed: threads.filterNot (thread) ->
+        channels.includes thread.getIn ['channel', 'id']
+    }
+]
+
 # Returns followed private channel threads mapped with relevant channel
 # instances.
 followedPrivateChannelThreads = [


Set shadow type for text layer in constructor option


Update weapon leveling text

Update weapon leveling text

--- app/scripts/components/player/PlayerControlledShip.coffee
+++ app/scripts/components/player/PlayerControlledShip.coffee
@@ -87,7 +87,7 @@
       @primaryWeapon = Crafty.e('OldWeaponLaser')
       @primaryWeapon.install(this)
       @listenTo @primaryWeapon, 'levelUp', (level) =>
-        @scoreText 'L +1'
+        @scoreText "L +#{level}"
       return true
     if item is 'lasers'
       @primaryWeapon?.destroy()
@@ -95,7 +95,7 @@
       @primaryWeapon = Crafty.e('RapidWeaponLaser')
       @primaryWeapon.install(this)
       @listenTo @primaryWeapon, 'levelUp', (level) =>
-        @scoreText 'L +1'
+        @scoreText "L +#{level}"
       return true
     if item is 'rockets'
       @secondaryWeapon?.destroy()
@@ -118,11 +118,12 @@
         x: @x
         y: @y - 10
         z: 990
+        w: 100
       )
       .textFont({
-        size: '16px',
+        size: '10px',
         weight: 'bold',
-        family: 'Bank Gothic'
+        family: 'Press Start 2P'
       })
       .tween(y: @y - 40, alpha: 0.5, 1000)
       .one('TweenEnd', -> @destroy())


seeking works on firefox, now

seeking works on firefox, now

--- src/scripts/directives/seek.coffee
+++ src/scripts/directives/seek.coffee
@@ -4,7 +4,8 @@
     return {
         link: (scope, element, attrs) ->
             element.on 'click', (e) ->
-                progress = e.offsetX / e.target?.offsetWidth
+                x = e.offsetX or e.layerX
+                progress = x / e.target?.offsetWidth
                 if progress
                     scope.player.seek progress
     }


Added 'on_tree_change' callback to SmartTreeTable js component

--- assets/vendor/smart-tree-table/smart-tree-table.js.coffee
+++ assets/vendor/smart-tree-table/smart-tree-table.js.coffee
@@ -44,6 +44,7 @@
                     after: ''  # contents for 'after' insert mode
         debug: false
         on_select: null
+        on_tree_change: null

     # Creates a SmartTreeTable object using an HTML table specified by +el+ selector.
     #
@@ -246,6 +247,9 @@

         # finishing touches
         @expand to_id if insert_mode == 'into'
+        if @settings.on_tree_change?
+            @settings.on_tree_change()
+

     # Moves tree node children in place.


Update badges.coffee

Update badges.coffee

Spelling error on sojourner3 badge

--- src/badges.coffee
+++ src/badges.coffee
@@ -41,7 +41,7 @@
   'recruiter1', 'recruiter2', 'recruiter3', 'recruiter4', 'recruiter5',
   'seer1', 'seer2', 'seer3', 'seer4', 'seer5',
   'shonin',
-  'sojourner1', 'sojourner2', 'sojouner3', 'sojourner4', 'sojourner5',
+  'sojourner1', 'sojourner2', 'sojourner3', 'sojourner4', 'sojourner5',
   'specops1', 'specops2', 'specops3', 'specops4', 'specops5',
   'trekker1', 'trekker2', 'trekker3', 'trekker4', 'trekker5',
   'translator1', 'translator2', 'translator3', 'translator4',


Enable hidden-inset on OS X

Enable hidden-inset on OS X

--- src/browser/atom-window.coffee
+++ src/browser/atom-window.coffee
@@ -26,6 +26,7 @@
     options =
       show: false
       title: 'Atom'
+      'title-bar-style': 'hidden-inset'
       'web-preferences':
         'direct-write': true


webapp/errors: "getViewport" property is undefined

--- src/smc-webapp/editor.coffee
+++ src/smc-webapp/editor.coffee
@@ -1542,6 +1542,8 @@
         return @codemirror.getValue()

     _set: (content) =>
+        if not @codemirror?
+            return
         {from} = @codemirror.getViewport()
         @codemirror.setValue(content)
         @codemirror.scrollIntoView(from)


fix file_use notification marking

fix file_use notification marking

--- src/smc-webapp/file_use.cjsx
+++ src/smc-webapp/file_use.cjsx
@@ -95,7 +95,8 @@
         if not account_id?
             # nothing to do -- non-logged in users shouldn't be marking files
             return
-        if not @redux.getStore('projects').getIn(['projects_map', project_id])?
+        project_is_known = @redux.getStore('projects').get('project_map')?.has(project_id)
+        if not project_is_known
             # user is not currently a collaborator on this project, so definitely shouldn't
             # mark file use.
             return


change use of mark to mark_file to properly use caching

--- src/smc-webapp/file_use.cjsx
+++ src/smc-webapp/file_use.cjsx
@@ -290,7 +290,7 @@
     if not redux? or not info?.project_id? or not info?.path?
         return
     # mark this file_use entry read
-    redux.getActions('file_use').mark(info.id, 'read')
+    redux.getActions('file_use').mark_file(info.project_id, info.path, 'read')
     # open the file
     require.ensure [], =>
         # ensure that we can get the actions for a specific project.
@@ -423,7 +423,7 @@

     render_mark_all_read_button : ->
         <Button key='mark_all_read_button' bsStyle='warning'
-            onClick={=>@props.redux.getActions('file_use').mark_all('read')}>
+            onClick={=>@props.redux.getActions('file_use').mark_all('read'); hide_notification_list()}>
             <Icon name='check-square'/> Mark all Read
         </Button>


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/string_extensions.coffee
+++ src/string_extensions.coffee
@@ -136,7 +136,7 @@

 @name startsWith
 @methodOf String#
-@param {String} str The
+@param {String} str The string to check

 @returns {Boolean} True if this string starts with the given string, false otherwise.
 ###


Fixes #4721315 pi displaying with symbol instead of '?'

--- math/expression_to_string_spec.coffee
+++ math/expression_to_string_spec.coffee
@@ -59,3 +59,8 @@
       exp = @exp_pos_builder(10, '*', [5])
       actual = @to_html_string(exp)
       expect(actual).toEqual "10 × ( 5 )"
+
+  it "correctly displays pi", ->
+    exp = @exp_pos_builder('pi')
+    actual = @to_html_string(exp)
+    expect(actual).toEqual "π"


Add x/yscale props onto renderer for glyph mapping

--- bokehjs/src/coffee/models/renderers/graph_renderer.coffee
+++ bokehjs/src/coffee/models/renderers/graph_renderer.coffee
@@ -12,6 +12,10 @@
     super(options)
     @edges = @build_glyph_view(@model.edges)
     @nodes = @build_glyph_view(@model.nodes)
+
+    @xscale = @plot_view.frame.xscales[@model.x_range_name]
+    @yscale = @plot_view.frame.yscales[@model.y_range_name]
+
     @set_data()

   connect_signals: () ->
@@ -53,7 +57,6 @@
     @edges.render(ctx, edge_indices, @edges)
     @nodes.render(ctx, node_indices, @nodes)

-
 export class GraphRenderer extends Renderer
   default_view: GraphRendererView
   type: 'GraphRenderer'


Make NodesOnly policy

Make NodesOnly policy

--- bokehjs/src/coffee/models/renderers/graph_renderer.coffee
+++ bokehjs/src/coffee/models/renderers/graph_renderer.coffee
@@ -1,4 +1,5 @@
 import {Renderer, RendererView} from "../renderers/renderer"
+import {NodesOnly} from "../graphs/graph_hit_test_policy"

 import * as p from "core/properties"
 import {build_views} from "core/build_views"
@@ -71,8 +72,8 @@
       layout_provider:    [ p.Instance                              ]
       node_renderer:      [ p.Instance                              ]
       edge_renderer:      [ p.Instance                              ]
-      selection_policy:   [ p.Instance,                             ]
-      inspection_policy:  [ p.Instance,                             ]
+      selection_policy:   [ p.Instance,      () -> new NodesOnly()  ]
+      inspection_policy:  [ p.Instance,      () -> new NodesOnly()  ]
     }

   @override {


Show notification for updates - fix #29

Show notification for updates - fix #29

--- src/client.coffee
+++ src/client.coffee
@@ -160,6 +160,13 @@
   if not continuing
     window.close()

+runUpdateCheck = ->
+  initSteps.updateMessage()
+    .subscribe ([message, url]) ->
+      if window.confirm "#{message}.
+        \nPress OK to download the update or Cancel to not do that."
+        require("shell").openExternal url
+
 runProcess = ->
   Rx.Observable.just folderListPath
     .flatMap pathSteps.readVDF
@@ -223,6 +230,8 @@
     .subscribe makeSteamRunningObserver()

   watchForKonamiCode()
+
+  runUpdateCheck()

   Rx.Observable.fromEvent $("#refresh"), 'click'
     .startWith "initial load event"


Show transfer progress by counting games

It's not perfect but it's better than nothing

--- src/client.coffee
+++ src/client.coffee
@@ -156,8 +156,12 @@
     $("tfoot#selection").toggle(hasSelection)

   updateProgress = (current) ->
-    percent = current / parseInt($("#progress-inner").data("total")) * 100
-
+    # save the current state
+    $("#progress-inner").data("current", current)
+
+    # do the calculation
+    total = parseInt($("#progress-inner").data("total"))
+    percent = current / total * 100
     $("#progress-inner").width("#{percent}%")

   $(document).on "click", "#globalSelect i.fa-check-square-o", (event) ->
@@ -198,8 +202,11 @@
         totalSize = _(games).pluck("size").reduce((a,b)->a+b)
         $("#progress-inner").data("total", totalSize)

+        # set total to number of games because we're lazy
+        $("#progress-inner").data("total", games.length)
+
         # make sure the progress bar starts at zero
-        $("#progress-inner").width("0%")
+        updateProgress 0

         # show the progress bar
         $("#progress-container").show()
@@ -212,7 +219,7 @@
       .flatMap (x) -> moveGame x
       .subscribe (x) ->
         # TODO usefully calculate current progress instead of always using 100%
-        updateProgress parseInt $("#progress-inner").data("total")
+        updateProgress parseInt $("#progress-inner").data("current") + 1
       , ((x) -> console.log "Error while moving: #{x}")
       , -> $("#progress-container").hide()
     console.log "Moving stuff"


also error on firefix 61 once app starts

--- src/smc-webapp/client_browser.coffee
+++ src/smc-webapp/client_browser.coffee
@@ -106,8 +106,8 @@

     _firefox60_bug: =>
         {name, version} = require('misc/browser').get_browser()
-        if name == 'Firefox' and (version == '59' or version == '60')
-            @alert_message(type:'error', block:'true', timeout:10000, message:"Firefox versions 59 and 60 have a MAJOR bug. You *must* use a different web browser. See https://tinyurl.com/y9hphj39 and https://tinyurl.com/yboeepsf")
+        if name == 'Firefox' and (version == '59' or version == '60' or version == '61')
+            @alert_message(type:'error', block:'true', timeout:10000, message:"Firefox versions 59, 60 and 61 have a MAJOR bug. You *must* use a different web browser or change your TLS settings. See https://tinyurl.com/y9hphj39 and https://tinyurl.com/yboeepsf")

     _setup_window_smc: () =>
         # if we are in DEBUG mode, inject the client into the global window object


fix https://github.com/sagemathinc/smc/issues/1169 -- "jupyter exception right when closing tab"

--- src/smc-webapp/editor_jupyter.coffee
+++ src/smc-webapp/editor_jupyter.coffee
@@ -964,6 +964,8 @@

     _handle_dom_change: () =>
         #dbg()
+        if not @dom?
+            return
         new_ver = @dom.get(true)  # true = save any newly created images to blob store.
         @_last_dom = new_ver
         @syncstring.live(new_ver)
@@ -974,6 +976,8 @@
     init_dom_change: () =>
         if @read_only
             # read-only mode: ignore any DOM changes
+            return
+        if not @dom?
             return
         #dbg = @dbg("dom_change")
         @_last_dom = @dom.get()


quest-completed view shows 0 instead of NaN on first quest

--- www/src/views/quest/completed.coffee
+++ www/src/views/quest/completed.coffee
@@ -4,7 +4,8 @@
         events:
             "click .btn-primary": "stop"

-        afterInitialize: ->
+        initialize: ->
+            super
             @setElement $("#quest-completed-modal")

         start: ->
@@ -12,23 +13,11 @@
             @$(".modal").modal "show"
             $.getScript "http://platform.twitter.com/widgets.js"

-
-        #!function (d,s,id) {
-        #    var js,fjs=d.getElementsByTagName(s)[0];
-        #    if(!d.getElementById(id)){
-        #        js=d.createElement(s);
-        #        js.id=id;
-        #        js.src="//platform.twitter.com/widgets.js";
-        #        fjs.parentNode.insertBefore(js,fjs);
-        #    }
-        #} (document,"script","twitter-wjs");
         stop: ->
             @$(".modal").modal "hide"

         serialize: ->
             params = @model.serialize()
             params.gotTwitter = Boolean(currentUser.get("twitter"))
-            params.totalPoints = params.reward + currentUser.get("rp")[@model.get("realm")]
+            params.totalPoints = params.reward + (currentUser.get("rp")[@model.get("realm")] || 0)
             params
-
-


Clean console log on droppable.

Clean console log on droppable.

--- src/base/DragDropSelect/Droppable.coffee
+++ src/base/DragDropSelect/Droppable.coffee
@@ -201,7 +201,6 @@

 		if not @__selectedTarget
 			if @insideSaveZone(coord)
-				console.info("Inside save zone...")
 				return

 			@resetMargin()
@@ -213,7 +212,6 @@
 				@__dropTargetPos = null
 				@syncDropHelper()
 			else
-				console.info("No selected target, no dropHelper...")
 				@removeHelper()
 				# bubble
 				return true


:memo: doc return value of Config.observe

--- src/config.coffee
+++ src/config.coffee
@@ -198,6 +198,9 @@
   # options - An optional {Object} containing the `callNow` key.
   # callback - The {Function} that fires when the. It is given a single argument, `value`,
   #            which is the new value of `keyPath`.
+  #
+  # Returns an {Object} with the following keys:
+  #  :off - A {Function} that unobserves the `keyPath` with called.
   observe: (keyPath, options={}, callback) ->
     if _.isFunction(options)
       callback = options


Add links to the new doc in the API docs

--- src/config.coffee
+++ src/config.coffee
@@ -338,6 +338,8 @@
   # * `scopeDescriptor` (optional) {Array} of {String}s describing a path from
   #   the root of the syntax tree to a token. Get one by calling
   #   {editor.getLastCursor().getScopeDescriptor()}. See {::get} for examples.
+  #   See [the scopes docs](https://atom.io/docs/latest/advanced/scopes-and-scope-descriptors)
+  #   for more information.
   # * `keyPath` {String} name of the key to observe
   # * `callback` {Function} to call when the value of the key changes.
   #   * `value` the new value of the key
@@ -374,6 +376,8 @@
   # * `scopeDescriptor` (optional) {Array} of {String}s describing a path from
   #   the root of the syntax tree to a token. Get one by calling
   #   {editor.getLastCursor().getScopeDescriptor()}. See {::get} for examples.
+  #   See [the scopes docs](https://atom.io/docs/latest/advanced/scopes-and-scope-descriptors)
+  #   for more information.
   # * `keyPath` (optional) {String} name of the key to observe. Must be
   #   specified if `scopeDescriptor` is specified.
   # * `callback` {Function} to call when the value of the key changes.
@@ -443,6 +447,8 @@
   # * `scopeDescriptor` (optional) {Array} of {String}s describing a path from
   #   the root of the syntax tree to a token. Get one by calling
   #   {editor.getLastCursor().getScopeDescriptor()}
+  #   See [the scopes docs](https://atom.io/docs/latest/advanced/scopes-and-scope-descriptors)
+  #   for more information.
   # * `keyPath` The {String} name of the key to retrieve.
   #
   # Returns the value from Atom's default settings, the user's configuration
@@ -487,6 +493,8 @@
   # ```
   #
   # * `scopeSelector` (optional) {String}. eg. '.source.ruby'
+  #   See [the scopes docs](https://atom.io/docs/latest/advanced/scopes-and-scope-descriptors)
+  #   for more information.
   # * `keyPath` The {String} name of the key.
   # * `value` The value of the setting. Passing `undefined` will revert the
   #   setting to the default value.


Upgrade node version to 0.16.0

Upgrade node version to 0.16.0

--- src/config.coffee
+++ src/config.coffee
@@ -45,7 +45,7 @@
     process.env.ATOM_API_URL ? 'https://atom.io/api'

   getNodeVersion: ->
-    process.env.ATOM_NODE_VERSION ? '0.11.13'
+    process.env.ATOM_NODE_VERSION ? '0.16.0'

   getNodeArch: ->
     switch process.platform


config.validateObject(): Don't parse, just validate

--- src/config.coffee
+++ src/config.coffee
@@ -126,11 +126,10 @@
   parsed = parse str
   return loadConfig parsed

-validateConfig = (str, options) ->
+validateConfigObject = (parsed, options) ->
   tv4 = require 'tv4'
   tv4.addSchema schemas.roleconfig.id, schemas.roleconfig
   tv4.addSchema schemas.config.id, schemas.config
-  parsed = parse str

   options.allowKeys = [] if not options.allowKeys

@@ -158,6 +157,10 @@
     errors.push err

   return errors
+
+validateConfig = (str, options) ->
+  parsed = parse str
+  return validateConfigObject parsed, options

 estimateRates = (cfg) ->
   rates = {}
@@ -207,6 +210,7 @@
 main() if not module.parent

 exports.validate = validateConfig
+exports.validateObject = validateConfigObject
 exports.parse = parseConfig
 exports.parseOnly = parse
 exports.load = loadConfig


Models: JStackTemplate: add _descriptions per field, as example.

--- workers/social/lib/social/models/computeproviders/stacktemplate.coffee
+++ workers/social/lib/social/models/computeproviders/stacktemplate.coffee
@@ -13,6 +13,7 @@

   @trait __dirname, '../../traits/protected'
   @trait __dirname, '../../traits/notifiable'
+

   @share()

@@ -72,14 +73,23 @@

     schema            :

-      machines        : [ Object ]
+      machines        :
+        type          : [ Object ]
+        _description  : 'Machines found in stack template'
+        default       : -> []

       title           :
         type          : String
         required      : yes
-
-      description     : String
-      config          : Object
+        _description  : 'Title of this stack template'
+
+      description     :
+        type          : String
+        _description  : 'Stack template description'
+
+      config          :
+        type          : Object
+        _description  : 'Private config data for stack template'

       accessLevel     :
         type          : String
@@ -99,10 +109,18 @@
         required      : yes

       template        :
-        content       : String
-        sum           : String
-        details       : Object
-        rawContent    : String
+        content       :
+          type        : String
+          _description: 'Stack template content in JSON format'
+        sum           :
+          type        : String
+          _description: 'Sum of stringified JSON content, auto-generated'
+        details       :
+          type        : Object
+          _description: 'Details data for stack template'
+        rawContent    :
+          type        : String
+          _description: 'Stack template content in YAML format'

       # Identifiers of JCredentials
       # structured like following;


JStackTemplate::update: fix update logic for template and template.details

--- workers/social/lib/social/models/computeproviders/stacktemplate.coffee
+++ workers/social/lib/social/models/computeproviders/stacktemplate.coffee
@@ -207,8 +207,14 @@
       delete data.originId
       delete data.group

-      if data.template?
-        data.template = generateTemplateObject data.template
+      # Update template sum if template update requested
+      { template, templateDetails } = data
+      if template?
+        data.template = generateTemplateObject template, templateDetails
+
+        # Keep the existing template details if not provided
+        if not templateDetails?
+          data.template.details = @getAt 'template.details'

       @update $set: data, (err)-> callback err


Make Internal Hubot disabled by default

Make Internal Hubot disabled by default

--- packages/rocketchat-internal-hubot/settings.coffee
+++ packages/rocketchat-internal-hubot/settings.coffee
@@ -1,4 +1,4 @@
 RocketChat.settings.addGroup 'InternalHubot'
-RocketChat.settings.add 'InternalHubot_Enabled', true, { type: 'boolean', group: 'InternalHubot', i18nLabel: 'Enabled' }
+RocketChat.settings.add 'InternalHubot_Enabled', false, { type: 'boolean', group: 'InternalHubot', i18nLabel: 'Enabled' }
 RocketChat.settings.add 'InternalHubot_Username', 'Rocket.Cat', { type: 'string', group: 'InternalHubot', i18nLabel: 'Username', i18nDescription: 'InternalHubot_Username_Description' }
 RocketChat.settings.add 'InternalHubot_ScriptsToLoad', 'hello.coffee,zen.coffee', { type: 'string', group: 'InternalHubot'}


Implement methods to handle indexes in Models

--- packages/rocketchat-lib/server/models/_Base.coffee
+++ packages/rocketchat-lib/server/models/_Base.coffee
@@ -22,3 +22,21 @@

 	deny: ->
 		return @model.allow.apply @model, arguments
+
+	ensureIndex: ->
+		@model._ensureIndex.apply @model, arguments
+
+	dropIndex: ->
+		@model._dropIndex.apply @model, arguments
+
+	tryEnsureIndex: ->
+		try
+			return @ensureIndex.apply @model, arguments
+		catch e
+			console.log e
+
+	tryDropIndex: ->
+		try
+			return @dropIndex.apply @model, arguments
+		catch e
+			console.log e


Make Cursor::updateVisibility private

Make Cursor::updateVisibility private

--- src/cursor.coffee
+++ src/cursor.coffee
@@ -594,10 +594,6 @@
   Section: Visibility
   ###

-  # Public: If the marker range is empty, the cursor is marked as being visible.
-  updateVisibility: ->
-    @setVisible(@marker.getBufferRange().isEmpty())
-
   # Public: Sets whether the cursor is visible.
   setVisible: (visible) ->
     if @visible != visible
@@ -608,6 +604,9 @@

   # Public: Returns the visibility of the cursor.
   isVisible: -> @visible
+
+  updateVisibility: ->
+    @setVisible(@marker.getBufferRange().isEmpty())

   ###
   Section: Comparing to another cursor


implement formatLine

implement formatLine

--- src/editor.coffee
+++ src/editor.coffee
@@ -43,7 +43,11 @@
     @parchment.deleteAt(start, end - start)

   formatLine: (start, end, formats, source) ->
-    # TODO implement
+    @parchment.children.forEachAt(start, end - start, (line, offset) ->
+      _.each(formats, (value, name) ->
+        line.format(name, value)
+      )
+    )

   formatText: (start, end, formats, source) ->
     _.each(formats, (value, name) ->


:memo: Doc save, saveAs, and getPath as public

--- src/editor.coffee
+++ src/editor.coffee
@@ -404,13 +404,19 @@
     else
       _.multiplyString("\t", Math.floor(number))

-  # {Delegates to: TextBuffer.save}
+  # Public: Saves the editor's text buffer.
+  #
+  # See {TextBuffer::save} for more details.
   save: -> @buffer.save()

-  # {Delegates to: TextBuffer.saveAs}
-  saveAs: (path) -> @buffer.saveAs(path)
-
-  # {Delegates to: TextBuffer.getPath}
+  # Public: Saves the editor's text buffer as the given path.
+  #
+  # See {TextBuffer::saveAs} for more details.
+  #
+  # filePath - A {String} path.
+  saveAs: (filePath) -> @buffer.saveAs(filePath)
+
+  # Public: Returns the {String} path of this editor's text buffer.
   getPath: -> @buffer.getPath()

   # Public: Returns a {String} representing the entire contents of the editor.


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/engine.coffee
+++ src/engine.coffee
@@ -303,7 +303,7 @@
         I.paused = false

       ###*
-      Pause the simulation.
+      Toggle the paused state of the simulation.

       engine.pause()
@@ -311,9 +311,13 @@

       @methodOf Engine#
       @name pause
-      ###
-      pause: ->
-        I.paused = true
+      @param {Boolean} [setTo] Force to pause by passing true or unpause by passing false.
+      ###
+      pause: (setTo) ->
+        if setTo?
+          I.paused = setTo
+        else
+          I.paused = !I.paused

       ###*
       Query the engine to see if it is paused.


shorter accessToken in debug log

shorter accessToken in debug log

--- src/util/debug-logger.coffee
+++ src/util/debug-logger.coffee
@@ -102,7 +102,7 @@
         @logger.info "┃ #{@mark} #{@logger.now()}"
         @logger.info "┃ loopback-promised  #{@baseURL}"
         @logger.info "┃ #{title}  [#{@http_method}]: #{@endpoint}"
-        @logger.info "┃ #{tab}accessToken: #{if @accessToken then @accessToken.slice(0, -10) + '...' else null}"
+        @logger.info "┃ #{tab}accessToken: #{if @accessToken then @accessToken.slice(0, 20) + '...' else null}"
         return


Add divisionSearchMembers to members collection

--- src/collections/members.coffee
+++ src/collections/members.coffee
@@ -97,6 +97,13 @@
   @collections.members.exec('disableMember', params)
     .pop().callback callback

+exports.divisionSearchMembers = (params, callback) ->
+  unless params.divisionId
+    throw new TSArgsError 'teamsnap.divisionSearchMembers',
+      "`divisionId` must be provided"
+
+  @collections.members.queryItems('divisionSearch', params, callback)
+

 # Helper to output a member's name, forward or reverse (reverse will use comma)
 exports.memberName = (member, reverse, forSort) ->


follow docs

follow docs

--- src/follow.coffee
+++ src/follow.coffee
@@ -1,7 +1,7 @@
 ###*
 The Follow module provides a simple method to set an object's
 velocity so that it will approach another object. This method
-relies on bo objects having `bounds` methods. GameObject
+relies on both objects having `bounds` methods. GameObject

 <code><pre>


added an explaining sentence to batch approve requests modal

--- client/app/Applications/Groups.kdapplication/Views/groupsinvitationrequestsview.coffee
+++ client/app/Applications/Groups.kdapplication/Views/groupsinvitationrequestsview.coffee
@@ -126,6 +126,7 @@
     modal = new KDModalViewWithForms
       cssClass               : options.cssClass
       title                  : options.title
+      content                : options.content
       overlay                : yes
       width                  : options.width or 400
       height                 : options.height or 'auto'
@@ -207,9 +208,10 @@
     @batchApprove = @showModalForm
       title            : 'Batch Approve Requests'
       callback         : @emit.bind @, 'BatchApproveRequests'
+      content          : "
Enter how many of the pending requests you want to approve:
"
       fields           :
         count          :
-          label        : '# of requests'
+          label        : 'No. of requests'
           type         : 'text'
           defaultValue : 10
           placeholder  : 'how many requests do you want to approve?'


Initial tooltip in Tour

Initial tooltip in Tour

--- core/app/assets/javascripts/tour.coffee
+++ core/app/assets/javascripts/tour.coffee
@@ -32,6 +32,14 @@
     @bindLibraryLoad()

     @createStateMachine()
+
+    @createTooltips()
+
+  createTooltips: ->
+    @tooltipAdd '#create-your-first-factlink > p:first',
+        "Let's create a Factlink!",
+        "With Factlink you can select any statement, on any website. Let's try that on this example page. Select any statement on the right to start creating your Factlink.",
+        { side: 'bottom' }

   createStateMachine: ->
     @state = StateMachine.create


fix gist

fix gist

--- src/github.coffee
+++ src/github.coffee
@@ -82,7 +82,7 @@
         site = github.get_github gist

         if github?
-          [id, rest...] = uri.filename().split '.'
+          [id, rest...] = URI(gist).filename().split '.'
           build_url site, id
         else
           gist


disable automation button when a request has been send

--- plugins/compute/app/assets/javascripts/compute/cloud_init_configuration.coffee
+++ plugins/compute/app/assets/javascripts/compute/cloud_init_configuration.coffee
@@ -29,12 +29,14 @@
   spinner = button.find('i.loading-spinner-button')
   icon.addClass('hide')
   spinner.removeClass('hide')
+  button.addClass('disabled');

 @stopSpinner = (button) ->
   icon = button.find('i.fa-plus')
   spinner = button.find('i.loading-spinner-button')
   icon.removeClass('hide')
   spinner.addClass('hide')
+  button.removeClass('disabled');

 @addEventListenerOnSelect= (button) ->
   $( "#server_image_id" ).unbind "change.automtion"


new css layout: improvements for firefox

--- src/smc-webapp/project_page.cjsx
+++ src/smc-webapp/project_page.cjsx
@@ -328,14 +328,16 @@

         editor  = @render_editor(path)

+        # WARNING: every CSS style below is hard won.  Don't f!$k with them without knowing what
+        # you are doing and testing on all supported browser.  - wstein
         if is_chat_open
             # 2 column layout with chat
             content =\

-

+

                         {editor}

                     {@render_drag_bar(path)}
@@ -348,7 +350,7 @@
         else
             # just the editor
             content =\
-

+

                     {editor}

         # Finally render it


display is publishing only if publishing for the first time

--- src/components/course-calendar/plan.cjsx
+++ src/components/course-calendar/plan.cjsx
@@ -164,8 +164,7 @@
         courseId={courseId}
         className={planClasses}
         ref='details'/>
-
-    if isPublishing
+    else if isPublishing
       planModal = <CoursePlanPublishingDetails
         plan={plan}
         courseId={courseId}
@@ -194,7 +193,10 @@
         syncClosePlan: @syncClosePlan
       }

-      <displayComponent {...displayComponentProps} ref="display#{index}" key="display#{index}"/>
+      <displayComponent
+        {...displayComponentProps}
+        ref="display#{index}"
+        key="display#{index}"/>
     )

     <div>


fixes

fixes

--- lib/browser-plus.coffee
+++ lib/browser-plus.coffee
@@ -33,6 +33,7 @@
           uri.indexOf('http:') is 0 or uri.indexOf('https:') is 0 or
           uri.indexOf('localhost') is 0 or uri.indexOf('file:') is 0 or
           uri.indexOf('browser-plus:') is 0 ) #or opt.src
+         uri = uri.replace('localhost','http://127.0.0.1')
          bp = new BrowserPlusModel @,uri,opt.src
          if uri.indexOf('browser-plus://history') is 0
            bp.on 'destroyed', =>


Reformat method call

Reformat method call

--- client/app/lib/activity/sidebar/sidebarmachinelist.coffee
+++ client/app/lib/activity/sidebar/sidebarmachinelist.coffee
@@ -49,9 +49,8 @@
   addMachineBoxes: (boxes) ->

     data = boxes or @getData()
+    data.forEach @bound 'addMachineBox'

-    for obj in data
-      @addMachineBox obj

   addMachineBox: (machineAndWorkspaceData) ->


Removed tooltips when ReceivedClickedElsewhere when no overlay option selected.

--- client/landing/app/CommonViews/introductiontooltip.coffee
+++ client/landing/app/CommonViews/introductiontooltip.coffee
@@ -50,7 +50,9 @@
             item.expiryDate = snippet.expiryDate
             @createInstance null, item

-        @addOverlay() if @shouldAddOverlay and @visibleTooltips.length > 0
+        if @visibleTooltips.length > 0
+          if @shouldAddOverlay then @addOverlay() else @addLayers()
+

   createInstance: (parentView, data) ->
     assets = @getAssets parentView, data
@@ -63,7 +65,8 @@
     }
     , assets.data

-    @visibleTooltips.push tooltip
+    if @visibleTooltips.indexOf(parentView.tooltip) is -1
+      @visibleTooltips.push parentView.tooltip

     tooltip.on "IntroductionTooltipClosed", =>
       @close parentView.getOptions().introId
@@ -125,4 +128,13 @@
       for tooltipInstance in @visibleTooltips
         tooltipInstance.getOptions().parentView.tooltip?.destroy()
       @overlay.remove()
-      @visibleTooltips.length = 0+      @visibleTooltips.length = 0
+
+  addLayers: ->
+    windowController = KD.getSingleton("windowController")
+    mainView         = KD.getSingleton("contentPanel")
+    for tooltip in @visibleTooltips
+      windowController.addLayer tooltip
+      tooltip.on "ReceivedClickElsewhere", =>
+        tooltip.destroy() for tooltip in @visibleTooltips when tooltip
+        @visibleTooltips.length = 0


groups: adding DOM stuff to new view, setting resizing events

--- client/landing/app/MainApp/maincontroller/mainview.coffee
+++ client/landing/app/MainApp/maincontroller/mainview.coffee
@@ -205,6 +205,8 @@
     if state
       LoginLink.click = ->
         $('.group-landing').css 'height', 0
+
+      LoginLink.updatePartial 'Go to Group'
     else
       LoginLink.click = ->
         @getSingleton('mainController').loginScreen.show()
@@ -217,6 +219,13 @@

   decorateLoginState:(isLoggedIn = no)->

+    groupLandingView = new KDView
+      lazyDomId : 'group-landing'
+
+    groupLandingView.listenWindowResize()
+    groupLandingView._windowDidResize = =>
+      groupLandingView.setHeight window.innerHeight - 50
+
     if isLoggedIn
       if @groupsEnabled() then @switchGroupState yes
       else $('body').addClass "loggedIn"


stacks: wrap environment/flux actions in update stack template

--- client/stacks/lib/views/stacks/updatestacktemplate.coffee
+++ client/stacks/lib/views/stacks/updatestacktemplate.coffee
@@ -1,4 +1,5 @@
 remote = require('app/remote').getInstance()
+EnvironmentFlux = require 'app/flux/environment'

 module.exports = updateStackTemplate = (data, callback) ->
@@ -11,9 +12,6 @@

   if stackTemplate?.update

-    inuse   = stackTemplate.inuse
-    updated = stackTemplate._updated
-
     dataToUpdate = if machines \
     then { machines, config }
     else {
@@ -21,17 +19,15 @@
       templateDetails, config, description
     }

-    stackTemplate.update dataToUpdate, (err, _stackTemplate) ->
-
-      if _stackTemplate
-        _stackTemplate._updated = updated
-        _stackTemplate.inuse    = inuse
-
-      callback err, _stackTemplate
+    EnvironmentFlux.actions.updateStackTemplate(stackTemplate, dataToUpdate)
+      .then ({ stackTemplate }) -> callback null, stackTemplate
+      .catch (err) -> callback err

   else

-    remote.api.JStackTemplate.create {
+    EnvironmentFlux.actions.createStackTemplate({
       title, template, credentials, rawContent
       templateDetails, config, description
-    }, callback
+    }).then ({ stackTemplate }) ->
+      callback null, stackTemplate
+    .catch (err) -> callback err


Change the function of prev button and next button in my course page

--- common/lib/xmodule/xmodule/js/src/sequence/display.coffee
+++ common/lib/xmodule/xmodule/js/src/sequence/display.coffee
@@ -5,6 +5,9 @@
     @num_contents = @contents.length
     @id = @el.data('id')
     @modx_url = @el.data('course_modx_root')
+    #20151026b Analysis the url and get the parameter
+    @url_split_array = @el[0].baseURI.split('/')
+    @prev_button_para = @url_split_array[@url_split_array.length-1]
     @initProgress()
     @bind()
     @render parseInt(@el.data('position'))
@@ -85,7 +88,8 @@
     if @position == 1
       if prev_section != null
         @$('.sequence-nav-buttons .prev a').removeClass('disabled').click(()=>
-          window.location.href=prev_section
+          #20151026b Add parameter 0 at the end of url
+          window.location.href = prev_section + "0"
         )
       else
         @$('.sequence-nav-buttons .prev a').addClass('disabled')
@@ -95,7 +99,8 @@
     if @position == @contents.length
       if next_section != null
         @$('.sequence-nav-buttons .next a').removeClass('disabled').click(()=>
-          window.location.href=next_section
+          #20151026b Add parameter 1 at the end of url
+          window.location.href = next_section + "1"
         )
       else
         @$('.sequence-nav-buttons .next a').addClass('disabled')
@@ -112,6 +117,12 @@
       # On Sequence change, fire custom event "sequence:change" on element.
       # Added for aborting video bufferization, see ../video/10_main.js
       @el.trigger "sequence:change"
+      #20151026b If the parameter from url equal to "0", then change the value of new_positon and save to DB
+      if @prev_button_para == "0"
+        @prev_button_para = null
+        new_position = @num_contents
+        modx_full_url = @modx_url + '/' + @id + '/goto_position'
+        $.postWithPrefix modx_full_url, position: new_position
       @mark_active new_position
       @$('#seq_content').html @contents.eq(new_position - 1).text()
       XModule.loadModules(@$('#seq_content'))


events

events

--- app/assets/javascripts/svg_nodes/map.coffee
+++ app/assets/javascripts/svg_nodes/map.coffee
@@ -1,9 +1,6 @@
 window.SvgNodes = {}

 class SvgNodes.Map
-
-  this._eventNameToHandlerName = (en) ->
-    en.replace ':', '_'
   constructor: (parent = null, params = {} )->
     @w = params.w
     @h = params.h
@@ -118,9 +115,10 @@
     m.scale(@transformations.zoom)
     @svg.transform(m)

-  _freeze: ()=>
+  freeze: ()=>
     @interactive = false
-    setTimeout =>
+    clearTimeout(@_freezeTimeout) if @_freezeTimeout
+    @_freezeTimeout = setTimeout =>
       @interactive = true
     , 500

@@ -136,27 +134,53 @@
   enableInteractivity: ()=>
     map = this
     @svg.click (e)->
+      e.stopPropagation()
+      return unless map.interactive
       key = switch e.target.tagName
         when 'svg' then 'click:map'
         when 'circle' then 'click:node'
+      return unless key?
       fn(e) for fn in map.handlers[key]
+      true
+
     @svg.drag (dx, dy, x, y, e) ->
+      e.stopPropagation()
+      map._dxdy = 1 if dx*dy > 0
+      return unless map.interactive
       key = switch e.target.tagName
         when 'svg' then 'drag:map:move'
         when 'circle' then 'drag:node:move'
+      return unless key?
       fn(dx, dy, x, y, e) for fn in map.handlers[key]
+      true
+
     ,         (x, y, e) ->
+      e.stopPropagation()
+      map._dxdy = 0
+      return unless map.interactive
       key = switch e.target.tagName
         when 'svg' then 'drag:map:start'
         when 'circle' then 'drag:node:start'
+      return unless key?
       fn(x, y, e) for fn in map.handlers[key]
+      true
+
     ,         (e) ->
+      e.stopPropagation()
+      return unless map.interactive
       key = switch e.target.tagName
         when 'svg' then 'drag:map:end'
         when 'circle' then 'drag:node:end'
+      return unless key?
       fn(e) for fn in map.handlers[key]
+      map.freeze() if map._dxdy > 0
+      true
+
     @svg.node.addEventListener 'wheel', (e)->
+      e.stopPropagation()
+      return unless map.interactive
       fn(e) for fn in map.handlers['zoom']
+      true

   _setDefaultHandlers: () =>
@@ -172,46 +196,33 @@
   ###
   # HANDLERS
   ###
-  # FIXME:
-  # fix drag -> click behavior
-  #

   _onMapClick: (e) =>
-    e.stopPropagation()
-    return unless @interactive
     this.deselectAll() unless e.ctrlKey
+  _onMapDrag: (dx, dy, x, y, e) =>
+    @transformations.dx = dx
+    @transformations.dy = dy
+    this._applyTransformations()
+  _onMapDragStart: (x, y, e) =>
+  _onMapDragEnd: (e) =>
+    @transformations.x += @transformations.dx
+    @transformations.y += @transformations.dy
+    @transformations.dx = 0
+    @transformations.dy = 0
   _onNodeClick: (e) =>
-    e.stopPropagation()
-    return unless @interactive
     node = this.fetchNode(e.target.node_id)
     if e.target.node_id in this.getSelectedIds()
       this.deselect(node)
     else
       this.select(node, e.ctrlKey)
-    this._freeze()
   _onNodeDragStart: (x, y, e) =>
-    node = this.fetchNode(e.target.node_id)
-    this.select(node)
   _onNodeDrag: (dx, dy, x, y, e) =>
-    @selection[0].move(x+dx, y+dy)
+    return if dx*dy = 0
+    return unless node = @selection[0]
+    x = e.clientX - @bbox.x - @transformations.x
+    y = e.clientY - @bbox.y - @transformations.y
+    node.move(x, y)
   _onNodeDragEnd: (e) =>
-    this._freeze()
-  _onMapDrag: (dx, dy, x, y, e) =>
-    e.stopPropagation()
-    return unless dx*dy > 0
-    @transformations.dx = dx
-    @transformations.dy = dy
-    this._applyTransformations()
-    this._freeze()
-  _onMapDragStart: (x, y, e) =>
-    e.stopPropagation()
-  _onMapDragEnd: (e) =>
-    e.stopPropagation()
-    @transformations.x = @transformations.dx
-    @transformations.y = @transformations.dy
-    @transformations.dx = 0
-    @transformations.dy = 0
-    this._freeze()
   _onScroll: (e) =>
     factor = e.deltaY * 0.05
     @transformations.zoom += factor
@@ -228,21 +239,6 @@
     @svg.node.node_id = @id
     @label = new SvgNodes.Label(@map, @x + @r, @y + @r, @text)
     @svg.attr(@map.options.nodes)
-    #this.setDraggable() if @map.options.nodes.draggable
-    #this.setSelectable()
-
-  #setDraggable: () =>
-    #@svg.drag (dx, dy, x, y, e)=>
-      #x = x - @map.bbox.x
-      #y = y - @map.bbox.y
-      #this.move(x,y)
-      #@map.interactive = false
-
-  #setSelectable: =>
-    #@svg.click (e)=>
-      #if not @highlighted then this.highlight() else this.dim()
-      #@map.select(this)
-      #e.stopPropagation()

   move: (x, y)=>
     @x = x
@@ -255,12 +251,12 @@

   highlight: =>
     @highlighted = true
-    @svg.transform(Snap.matrix().scale(1.1, 1.1, @x, @y))
+    #@svg.transform(Snap.matrix().scale(1.1, 1.1, @x, @y))
     @label.attr(fontWeight: 'bold')

   dim: =>
     @highlighted = false
-    @svg.transform(Snap.matrix())
+    #@svg.transform(Snap.matrix())
     @label.attr(fontWeight: 'normal')

   _moveEdges: =>


Removes 'Artsypedia' relationships

Removes 'Artsypedia' relationships

--- models/mixins/relations/artist.coffee
+++ models/mixins/relations/artist.coffee
@@ -5,14 +5,11 @@
   related: ->
     return @__related__ if @__related__?

-    # Deferred requires:
     Artist = require '../../artist.coffee'
     PartnerShows = require '../../../collections/partner_shows.coffee'
     Artworks = require '../../../collections/artworks.coffee'
-    WebArticles = Books = require '../../../components/artsypedia/collection.coffee'
     Articles = require '../../../collections/articles.coffee'

-    # Setup:
     artists = new Backbone.Collection [], model: Artist
     artists.url = "#{API_URL}/api/v1/related/layer/main/artists?artist[]=#{@id}&exclude_artists_without_artworks=true"

@@ -32,34 +29,13 @@
     artworks = new Artworks
     artworks.url = "#{@url()}/artworks?published=true"

-    webArticles = new WebArticles
-    webArticles.url = "#{APP_URL}/artist/data/#{@id}/publications?merchandisable[]=false"
-
     articles = new Articles
     articles.url = =>
       "#{POSITRON_URL}/api/articles?artist_id=#{@get '_id'}&published=true"

-    merchandisable = new Books
-    merchandisable.url = "#{APP_URL}/artist/data/#{@id}/publications?merchandisable[]=true"
-
-    bibliography = new Backbone.Collection
-    bibliography.url = "#{APP_URL}/artist/data/#{@id}/publications"
-
-    collections = new Backbone.Collection
-    collections.url = "#{APP_URL}/artist/data/#{@id}/collections"
-
-    exhibitions = new Backbone.Collection
-    exhibitions.url = "#{APP_URL}/artist/data/#{@id}/exhibitions"
-
-    # Return:
     @__related__ =
       artists: artists
       contemporary: contemporary
       shows: shows
       artworks: artworks
-      webArticles: webArticles
       articles: articles
-      merchandisable: merchandisable
-      bibliography: bibliography
-      collections: collections
-      exhibitions: exhibitions


HIRE-94: corrected transmission of file name

--- src/main/java/app/profile/profile.coffee
+++ src/main/java/app/profile/profile.coffee
@@ -83,7 +83,9 @@
         file = files[i]
         Upload.upload(
           url: '/api/resource/upload'
-          file: file).progress((evt) ->
+          file: file
+          fileFormDataName: file.name)
+        .progress((evt) ->
           progressPercentage = parseInt(100.0 * evt.loaded / evt.total)
           return
         ).success (data, status, headers, config) ->


Scenario 17. added

Scenario 17. added

'Scenario 17. Child with attribute'

--- test/scenario-based-testing.coffee
+++ test/scenario-based-testing.coffee
@@ -186,4 +186,13 @@
     # console.log expectedOutput
     expect(output).to.equal(expectedOutput)

+  it 'Scenario 17. Child with attribute', ->

+    input = fs.readFileSync './test/scenarios/scenario-17-input.cohtml', 'utf8'
+    # console.log input
+    output = toHtml5 input
+    # console.log output
+    expectedOutput = fs.readFileSync './test/scenarios/scenario-17-expected.html', 'utf8'
+    # console.log expectedOutput
+    expect(output).to.equal(expectedOutput)
+


Use fully-qualified module name

Use fully-qualified module name

--- src/lib/ComponentLoader.coffee
+++ src/lib/ComponentLoader.coffee
@@ -32,7 +32,7 @@
     prefix = @getModulePrefix definition.name
     if definition.noflo.components
       for name, cPath of definition.noflo.components
-        @registerComponent prefix, name, "/#{definition.name}/#{cPath}"
+        @registerComponent prefix, name, "/#{moduleName}/#{cPath}"

   listComponents: (callback) ->
     return callback @components unless @components is null


fix scope problems

fix scope problems

--- src/loaders/AssetLoader.coffee
+++ src/loaders/AssetLoader.coffee
@@ -10,6 +10,7 @@
 	# Constructs an asset loader
 	# @param {Array} assets The assets you want to load
 	constructor: (assets) ->
+		super

 		# Assets to load
 		@assets = assets
@@ -34,7 +35,7 @@
 			filetype = (asset.split ".").pop().toLowerCase()
 			loader = @assetsTypes[filetype] or throw new Error "[Rotten.AssetLoader] Filetype '#{filetype}' is not supported"
 			loader = new loader asset
-			loader.listen "loaded", (-> @.assetLoaded asset)
+			loader.listen "loaded", (=> @assetLoaded asset)

 	# Called when an asset has just been loaded


updated description text for each question type

--- dkobo/koboform/static/js/xlform/view_xlformbuilder.coffee
+++ dkobo/koboform/static/js/xlform/view_xlformbuilder.coffee
@@ -192,9 +192,20 @@
     @line.html viewTemplates.xlfRowSelector.line()
     $menu = @line.find(".well")
     mItemLabels =
-      image: "Take a photo"
+      image: "Upload photo"
+      audio: "Record audio"
+      video: "Record video"
+      barcode: "Scan barcode"
+      text: "Text"
+      integer: "Integer"
+      decimal: "Decimal"
+      note: "Read a note"
       select_one: "Select one"
-      select_multiple: "Select multiple"
+      select_multiple: "Select many"
+      date: "Date"
+      datetime: "Date + time"
+      geopoint: "GPS Location"
+      calculation: "Calculate value"

     mItems = [
       ["image", "audio", "video", "barcode"],


fix text assertion

fix text assertion

--- test/spec_helper.coffee
+++ test/spec_helper.coffee
@@ -47,7 +47,7 @@
       @assert @obj.querySelectorAll(selector).length > 0, "expected #{selector} to occur", "expected #{selector} not to occur, but it did"

   chai.Assertion::text = (text) ->
-    actual = @obj.textContent.trim().replace(/\s/g, ' ')
+    actual = @obj.textContent.trim().replace(/\s+/g, ' ')
     @assert actual is text, "expected #{actual} to be #{text}"
   chai.Assertion::attribute = (name) ->
     @assert @obj.hasAttribute(name), "expected #{@obj} to have attribute #{name}"


Added link to add points

Added link to add points

--- src/scripts/components/ProfileAside.cjsx
+++ src/scripts/components/ProfileAside.cjsx
@@ -25,8 +25,9 @@

{profile.name.full}

-
{profile.points} crpoints

+
{profile.points} points

+
<Link to="/addpoints">Add Points</Link>

Rewards Exchanged

Edit Profile

 Log Out


Remove debug code

Remove debug code

--- src/scripts/modules/media/nav/nav.coffee
+++ src/scripts/modules/media/nav/nav.coffee
@@ -10,11 +10,10 @@
       @content = options.content
       @hideProgress = options.hideProgress

-      @listenTo(@content, 'change:currentPage', @render)
+      if not @hideProgress
+        @listenTo(@content, 'change:currentPage', @render)

     render: () ->
-      console.log 'render'
-      console.log @content.toJSON()
       tmplOptions = @content.toJSON()
       tmplOptions._hideProgress = @hideProgress
       @template = template tmplOptions


fix use of get in

fix use of get in

--- src/smc-hub/kucalc/compute-client.coffee
+++ src/smc-hub/kucalc/compute-client.coffee
@@ -349,7 +349,7 @@
                     return
                 dbg('it is running')
                 address =
-                    host         : project?.getIn(['state', 'ip'])
+                    host         : @getIn(['state', 'ip'])
                     port         : LOCAL_HUB_PORT
                     secret_token : @getIn(['status', 'secret_token'])
                 if not address.secret_token


Add support for DATABASE_MIRRORING_PARTNER environment change notification.

Fixes #22

--- src/token/env-change-token-parser.coffee
+++ src/token/env-change-token-parser.coffee
@@ -24,6 +24,9 @@
     name: 'COMMIT_TXN'
   10:
     name: 'ROLLBACK_TXN'
+  13:
+    name: 'DATABASE_MIRRORING_PARTNER'
+    event: 'partnerNode'
   17:
     name: 'TXN_ENDED'

@@ -34,7 +37,7 @@

   if type
     switch type.name
-      when 'DATABASE', 'LANGUAGE', 'CHARSET', 'PACKET_SIZE'
+      when 'DATABASE', 'LANGUAGE', 'CHARSET', 'PACKET_SIZE', 'DATABASE_MIRRORING_PARTNER'
         newValue = buffer.readBVarchar()
         oldValue = buffer.readBVarchar()
       when 'SQL_COLLATION'


:art: Simplify scope detection in EditorLinter::_lint

--- lib/editor-linter.coffee
+++ lib/editor-linter.coffee
@@ -52,7 +52,7 @@
   _lint: (wasTriggeredOnChange, scopes) ->
     return @linter.linters.map (linter) =>
       return if wasTriggeredOnChange isnt linter.lintOnFly
-      return unless (scopes.filter (entry) -> linter.grammarScopes.indexOf(entry) isnt -1 ).length
+      return unless scopes.some (entry) -> entry in linter.grammarScopes

       new Promise((resolve) =>
         resolve(linter.lint(@editor))


DRY up how diffs are generated and rendered

--- lib/git-diff-view.coffee
+++ lib/git-diff-view.coffee
@@ -13,10 +13,10 @@
     @editor.on 'editor:display-updated', => @renderDiffs()
     git.on 'statuses-changed', =>
       @diffs = {}
-      @scheduleDiffs()
+      @scheduleUpdate()
     git.on 'status-changed', (path) =>
       delete @diffs[path]
-      @scheduleDiffs() if path is @editor.getPath()
+      @scheduleUpdate() if path is @editor.getPath()

     @subscribeToBuffer()

@@ -28,15 +28,15 @@
       @buffer = null

     if @buffer = @editor.getBuffer()
-      @scheduleDiffs() unless @diffs[@buffer.getPath()]?
-      @buffer.on 'contents-modified.git-diff', =>
-        @generateDiffs()
-        @renderDiffs()
+      @scheduleUpdate() unless @diffs[@buffer.getPath()]?
+      @buffer.on 'contents-modified.git-diff', => @updateDiffs()

-  scheduleDiffs: ->
-    _.nextTick =>
-      @generateDiffs()
-      @renderDiffs()
+  scheduleUpdate: ->
+    _.nextTick => @updateDiffs()
+
+  updateDiffs: ->
+    @generateDiffs()
+    @renderDiffs()

   generateDiffs: ->
     if path = @buffer.getPath()


tweak these loops

tweak these loops

--- node_modules_koding/jspath/src/jspath.coffee
+++ node_modules_koding/jspath/src/jspath.coffee
@@ -37,7 +37,8 @@
       path = path.split '.'
     else
       path = path.slice()
-    while ref? and prop = path.shift()
+    # while ref? and prop = path.shift()
+    for ref, prop in path
      ref = ref[prop]
     ref
   ###
@@ -60,7 +61,8 @@
     last = path.pop()
     prev = []
     ref = obj
-    while component = path.shift()
+    # while component = path.shift()
+    for component in path
       if primTypes.test typeof ref[component]
         throw new Error \
           """
@@ -96,7 +98,8 @@
       path = path.slice()
     prev = []
     last = path.pop()
-    while component = path.shift()
+    # while component = path.shift()
+    for component in path
       if primTypes.test typeof ref[component]
         throw new Error \
           """


componentlib: Throw nice errors

componentlib: Throw nice errors

--- lib/componentlib.coffee
+++ lib/componentlib.coffee
@@ -74,7 +74,9 @@
         return components

     getComponent: (componentName) ->
-        @definition.components[componentName]
+        throw new Error "getComponent(): component name not specified" if not componentName
+        c = @definition.components[componentName]
+        return c

     getComponentById: (componentId) ->
         for name of @getComponents()
@@ -107,9 +109,13 @@
             return callback new Error 'Could not find component source'

     outputPortsFor: (componentName) ->
-        @getComponent(componentName).outPorts
+        c = @getComponent(componentName)
+        throw new Error "Could not find outports for #{componentName}" if not c
+        return c.outPorts
     inputPortsFor: (componentName) ->
-        @getComponent(componentName).inPorts
+        c = @getComponent(componentName)
+        throw new Error "Could not find outports for #{componentName}" if not c
+        return c.inPorts
     inputPort: (componentName, portName) ->
         @inputPortsFor(componentName)[portName]
     outputPort: (componentName, portName) ->


Add main CMS controller

Add main CMS controller

Main is in index now, and blog controller moved to separate file

--- services/web/public/coffee/main.coffee
+++ services/web/public/coffee/main.coffee
@@ -24,6 +24,7 @@
 	"main/affiliations/controllers/UserAffiliationsController"
 	"main/affiliations/factories/UserAffiliationsDataService"
 	"main/keys"
+	"main/cms/blog"
 	"main/cms/index"
 	"analytics/AbTestingManager"
 	"directives/asyncForm"


refactor a pile of nested if's into data that should go into a cfg

--- node.js/server.coffee
+++ node.js/server.coffee
@@ -3,46 +3,64 @@

 port = 8888

+#where to find what - yes, cfg file.
+filetype = {
+  json: {
+      content_type: 'application/json', encoding: 'ascii', dir: '../data/pages/'
+    },
+  index: {
+      content_type: 'text/html', encoding: 'ascii', dir: './'
+    },
+  css: {
+      content_type: 'text/css', encoding: 'ascii', dir: './'
+    },
+  js: {
+      content_type: 'text/javascript', encoding: 'ascii', dir: '../client/'
+    },
+  jpg: {
+      content_type: 'image/jpg', encoding: 'binary', dir: '../client/'
+    },
+  png: {
+      content_type: 'image/png', encoding: 'binary', dir: '../client/'
+    },
+}
+
 process.serve_url = (req, res) ->
     console.log req.method, req.url

     file = req.url[1..]
+    if file == ''
+      console.log 'getting / == welcome-visitors.html topic'
+      file = 'welcome-visitors.html'
+    #TODO: security, don't allow non-topic id chars - avoid ../ etc
+    file = file.replace(/[\/\\]/g, '')    #take unescaped unix and windows slashes in a request, and throw them away
+
+    #remove ?random= - or other urlparams
+    file = file.replace(/\?.*$/i, '')
+

     filePath = file
     contentType =  'application/json'
     encoding = 'ascii'

-    if file == ''
-      console.log 'getting index file: '+file
-      contentType =  'text/html'
-      filePath = './index.html'
-    else
-      file_extension = file.match(/\.([^\.]*)$/i)
-      console.log file_extension
-      if file_extension[0]
-        file_extension = file_extension[1].toLowerCase()
-
-        if file_extension == 'css'
-          console.log 'getting style.css file: '+file
-          contentType =  'text/css'
-          filePath = './style.css'
-        if file_extension == 'js'
-          console.log 'getting js file: '+file
-          contentType =  'text/javascript'
-          filePath = '../client/'+file
-        if file_extension == 'png' || file_extension == 'jpg'
-          encoding = 'binary'
-          console.log 'getting image file: '+file
-          contentType =  'image/'+file_extension
-          filePath = '../client/'+file
-        else if file.match(/\.json\?random=........$/i)
-          file = file.replace(/\.json\?random=........$/i, '')
-          console.log 'getting wiki page: '+file
-          filePath = '../data/pages/'+file
-      else
-        #a wiki page in json
-        filePath = '../data/pages/'+file
+    file_extension = file.match(/\.([^\.]*)$/i)
+    console.log file_extension
+    if !file_extension[0]
+      file_extension = 'json'
+    else
+      file_extension = file_extension[1].toLowerCase()

+    if ! filetype[file_extension]
+      file_extension = 'index'
+      file = 'index.html'
+      #TODO: do something to load the right page (and here we start with html templating
+
+    encoding = filetype[file_extension]['encoding']
+    contentType =  filetype[file_extension]['content_type']
+    filePath = filetype[file_extension]['dir']+file
+
+    #and here's a pointless exception to the filename.extension :(
+    filePath = filePath.replace(/\.json/, '') if file_extension == 'json'

     console.log 'reading: '+filePath


:shirt:

:shirt:

--- lib/introspection.coffee
+++ lib/introspection.coffee
@@ -212,7 +212,7 @@

 inspectInstance = (obj, options={}) ->
   indent = _.multiplyString(' ', options.indent ? 0)
-  rep = report(obj.constructor,options)
+  rep = report(obj.constructor, options)
   [
     "## #{obj}: #{rep.ancesstorsNames[0..1].join(" < ")}"
     inspectObject(obj, options)


As per PR comments from reviewer, fixing coding style.

--- common/lib/xmodule/xmodule/js/spec/videoalpha/display/video_caption_spec.coffee
+++ common/lib/xmodule/xmodule/js/spec/videoalpha/display/video_caption_spec.coffee
@@ -269,9 +269,11 @@
       expect(parseInt($('.subtitles').css('maxHeight'))).toBeCloseTo $('.video-wrapper').height(), 2

     it 'set the height of caption spacing', ->
-      expect(Math.abs(parseInt($('.subtitles .spacing:first').css('height')) - @caption.topSpacingHeight())).toBeLessThan 1
-      expect(Math.abs(parseInt($('.subtitles .spacing:last').css('height')) - @caption.bottomSpacingHeight())).toBeLessThan 1
-
+      firstSpacing = Math.abs(parseInt($('.subtitles .spacing:first').css('height')))
+      lastSpacing = Math.abs(parseInt($('.subtitles .spacing:last').css('height')))
+
+      expect(firstSpacing - @caption.topSpacingHeight()).toBeLessThan 1
+      expect(lastSpacing - @caption.bottomSpacingHeight()).toBeLessThan 1

     it 'scroll caption to new position', ->
       expect($.fn.scrollTo).toHaveBeenCalled()
@@ -308,8 +310,10 @@
           @caption.scrollCaption()

         it 'scroll to current caption', ->
+          offset = -0.5 * ($('.video-wrapper').height() - $('.subtitles .current:first').height())
+
           expect($.fn.scrollTo).toHaveBeenCalledWith $('.subtitles .current:first', @caption.el),
-            offset: - ($('.video-wrapper').height() / 2 - $('.subtitles .current:first').height() / 2)
+            offset: offset

   describe 'seekPlayer', ->


The closing bracket was not indent properly

--- grails-app/assets/javascripts/angular/pathway-editor/controllers.coffee
+++ grails-app/assets/javascripts/angular/pathway-editor/controllers.coffee
@@ -40,7 +40,7 @@
 			$scope.deleteKeyPressed = (event, node) ->
 				if event.keyCode == 46
 					$scope.deleteNode(node)
-	])
+		])
 .controller('NodePropertiesCtrl', ['$scope', 'NodeSelector', ($scope, NodeSelector) ->
 		$scope.selectedNode = null
 		$scope.switchToSubPathway = ->


Fixed that sync was anything but synced...

--- js/song.coffee
+++ js/song.coffee
@@ -4,6 +4,9 @@
   _.extend @::, Backbone.Events

   constructor: ->
+    @lastScheduledIndices = {}
+    for eventType in @eventTypes
+      @lastScheduledIndices[eventType] = 0

   # Each kind of event the song can emit
   eventTypes: [
@@ -65,22 +68,60 @@

   # Schedule the song's event callbacks
   scheduleEvents: ->
+    # Current time of the audio playback.
+    playHead =
+      if @audio
+        @audio[0].currentTime
+      else
+        @startedAt ||= Date.now()
+        (Date.now() - @startedAt) / 1000
+
+    # Cycle through all event types.
     for eventType in @eventTypes
-      for eventData in @data["#{eventType}s"]
+
+      # The array of events of this eventType.
+      events = @data["#{eventType}s"]
+
+      # Loop until we find a break condition.
+      while true
+
+        # Find the earliest unprocessed event.
+        eventData = events[@lastScheduledIndices[eventType]]
+
+        # Out of events! Abort while loop.
+        break unless eventData
+
+        # Event too far away! Abort while loop.
+        break unless eventData.start < playHead + 1
+
+        # Increment event index for next loop iteration.
+        @lastScheduledIndices[eventType]++
+
+        # Close over event type and data
         do (eventType, eventData) =>
           setTimeout =>
+
+            # Regularly log out how far behind we are.
+            if @audio && eventType is 'bar'
+              console.log 'audio sync', @audio[0].currentTime - eventData.start
+
+            # What it's all about, trigger the event of this type and pass in the event data.
             @trigger eventType, eventData
-          , eventData.start * 1000
+
+          # Schedule the event trigger in the future.
+          , (eventData.start - playHead) * 1000

     return

   # Start the audio player
   start: (playAudio = yes) ->
     if @noSong
-      @scheduleEvents()
+      setInterval =>
+        @scheduleEvents()
+      , 250

     else
-      @audio.on 'playing', => @scheduleEvents()
+      @audio.on 'timeupdate', => @scheduleEvents()

       @audio[0].volume = if playAudio then 0.25 else 0
       @audio[0].play()


Zoom with control.

Zoom with control.

--- packages/landsofillusions-assets/spriteeditor/pixelcanvas/camera.coffee
+++ packages/landsofillusions-assets/spriteeditor/pixelcanvas/camera.coffee
@@ -1,3 +1,4 @@
+AC = Artificial.Control
 AE = Artificial.Everywhere
 LOI = LandsOfIllusions

@@ -58,19 +59,50 @@

         effectiveScale = @effectiveScale()

-        windowDelta =
-          x: event.originalEvent.deltaX
-          y: event.originalEvent.deltaY
+        if event.ctrlKey
+          # User is zooming in/out.
+          delta = event.originalEvent.deltaY

-        canvasDelta =
-          x: windowDelta.x / effectiveScale
-          y: windowDelta.y / effectiveScale
+          scale = @scale()
+          scaleChange = Math.pow(0.99, delta)

-        oldOrigin = @origin()
+          @setScale scale * scaleChange

-        @originData().value
-          x: oldOrigin.x + canvasDelta.x
-          y: oldOrigin.y + canvasDelta.y
+          # Also move the origin, depending on how much off-center we were zooming.
+          canvasOrigin = $pixelCanvas.offset()
+
+          mouseWindowCoordinate =
+            x: event.originalEvent.pageX - canvasOrigin.left
+            y: event.originalEvent.pageY - canvasOrigin.top
+
+          mouseCanvasCoordinate = @transformWindowToCanvas mouseWindowCoordinate
+
+          oldOrigin = @origin()
+
+          offCenter =
+            x: mouseCanvasCoordinate.x - oldOrigin.x
+            y: mouseCanvasCoordinate.y - oldOrigin.y
+
+          @originData().value
+            x: oldOrigin.x + offCenter.x * (scaleChange - 1)
+            y: oldOrigin.y + offCenter.y * (scaleChange - 1)
+
+        else
+          # User is translating.
+
+          windowDelta =
+            x: event.originalEvent.deltaX
+            y: event.originalEvent.deltaY
+
+          canvasDelta =
+            x: windowDelta.x / effectiveScale
+            y: windowDelta.y / effectiveScale
+
+          oldOrigin = @origin()
+
+          @originData().value
+            x: oldOrigin.x + canvasDelta.x
+            y: oldOrigin.y + canvasDelta.y

   setScale: (scale) ->
     @scaleData().value scale


Wrap built-in flash path in function

Wrap built-in flash path in function

--- lib/flash-loader.coffee
+++ lib/flash-loader.coffee
@@ -17,8 +17,10 @@
     return false
   true

-builtInPath = join EXECROOT, innerPath if EXECROOT?
-builtInPath = join ROOT, innerPath if not validatePath builtInPath
+getBuiltInFlashPath = ->
+  builtInPath = join EXECROOT, innerPath if EXECROOT?
+  builtInPath = join ROOT, innerPath if not validatePath builtInPath
+  if validatePath builtInPath then builtInPath else null

 reVerNum = /(\d+)\.(\d+)\.(\d+)\.(\d+)/
 getNewerVersion = (ver1, ver2) ->
@@ -84,7 +86,7 @@
     @system = getFlashVersion systemFlashPath if systemFlashPath?

 getAllVersions = ->
-  new FlashPlayerVersions builtInPath, findChromeFlashPath(), findSystemFlashPath()
+  new FlashPlayerVersions getBuiltInFlashPath(), findChromeFlashPath(), findSystemFlashPath()

 parseCLIArg = (arg) ->
   # --flash=useChrome


one second

one second

--- lib/getThrottles.coffee
+++ lib/getThrottles.coffee
@@ -9,9 +9,9 @@
   redisClient = redis.client

 module.exports =
-  connection : tokenthrottle rate: config.rateLimits.connection, expiry: 60, redisClient
-  message : tokenthrottle rate: config.rateLimits.message, expiry: 60, redisClient
-  data : tokenthrottle rate: config.rateLimits.data, expiry: 60, redisClient
-  query : tokenthrottle rate: config.rateLimits.query, expiry: 60, redisClient
-  whoami : tokenthrottle rate: config.rateLimits.whoami, expiry: 60, redisClient
+  connection : tokenthrottle rate: config.rateLimits.connection, expiry: 1, redisClient
+  message : tokenthrottle rate: config.rateLimits.message, expiry: 1, redisClient
+  data : tokenthrottle rate: config.rateLimits.data, expiry: 1, redisClient
+  query : tokenthrottle rate: config.rateLimits.query, expiry: 1, redisClient
+  whoami : tokenthrottle rate: config.rateLimits.whoami, expiry: 1, redisClient
   unthrottledIps : config.rateLimits.unthrottledIps


Add .ico to image extensions array

Add .ico to image extensions array

Closes atom/atom#1435

--- lib/image-editor.coffee
+++ lib/image-editor.coffee
@@ -19,7 +19,7 @@

   @activate: ->
     # Files with these extensions will be opened as images
-    imageExtensions = ['.gif', '.jpeg', '.jpg', '.png']
+    imageExtensions = ['.gif', '.ico', '.jpeg', '.jpg', '.png']
     atom.project.registerOpener (filePath) ->
       if _.include(imageExtensions, path.extname(filePath))
         new ImageEditor(path: filePath)


:new: Add LinterViews::updateTabs

:new: Add LinterViews::updateTabs

--- lib/linter-views.coffee
+++ lib/linter-views.coffee
@@ -96,6 +96,11 @@
     @tabs.get('file').count = counts.file
     @tabs.get('project').count = counts.project
     @bottomStatus.count = counts.project
+
+  updateTabs: ->
+    visible = @getVisibleTabs()
+    @tabs.forEach (tab, key) ->
+      tab.visibility = visible.has(key)

   # consumed in editor-linter, _renderPanel
   updateBubble: (point) ->


:art: Move Tab count logic to classifyMessages

--- lib/linter-views.coffee
+++ lib/linter-views.coffee
@@ -74,16 +74,11 @@

   # This message is called in editor-linter.coffee
   render: ->
-    counts = {project: 0, file: 0}
-    @classifyMessages(@linter.getMessages(), counts)
-
+    @classifyMessages(@linter.getMessages())
     @updateLineMessages()
     @updateBubble()
     @renderPanelMarkers()
     @renderPanelMessages()
-    @tabs['File'].count = counts.file
-    @tabs['Project'].count = counts.project
-    @bottomStatus.count = counts.project

   updateTabs: ->
     first = null
@@ -216,7 +211,8 @@
     @markers = []

   # This method is called in render, and classifies the messages according to scope
-  classifyMessages: (Gen, counts) ->
+  classifyMessages: (Gen) ->
+    counts = {File: 0, Project: 0}
     isProject = @state.scope is 'Project'
     activeEditor = atom.workspace.getActiveTextEditor()
     activeFile = activeEditor?.getPath()
@@ -225,13 +221,16 @@
       Entry.forEach (message) =>
         # If there's no file prop on message and the panel scope is file then count is as current
         if activeEditor and ((not message.filePath and not isProject) or message.filePath is activeFile)
-          counts.file++
-          counts.project++
+          counts.File++
+          counts.Project++
           message.currentFile = true
         else
-          counts.project++
+          counts.Project++
           message.currentFile = false
         @messages.add message
+    @tabs['File'].count = counts.File
+    @tabs['Project'].count = counts.Project
+    @bottomStatus.count = counts.Project

   # this method is called on package deactivate
   destroy: ->


:fire: Remove an unnecessary observer

:fire: Remove an unnecessary observer

--- lib/linter-views.coffee
+++ lib/linter-views.coffee
@@ -17,9 +17,6 @@
     @count = File: 0, Line: 0, Project: 0

     @subscriptions.add @panel
-    @subscriptions.add atom.config.observe('linter.underlineIssues', (underlineIssues) =>
-      @underlineIssues = underlineIssues
-    )
     @subscriptions.add atom.config.observe('linter.showErrorInline', (showBubble) =>
       @showBubble = showBubble
     )


Removes unnecessary specific unsubscribe in destroy

--- lib/minimap-view.coffee
+++ lib/minimap-view.coffee
@@ -41,9 +41,7 @@
   destroy: ->
     @off 'mousewheel', @mouseWheel
     @off 'mousedown', @mouseDown
-    @unsubscribe @paneView.model.$activeItem
-    @unsubscribe @paneView.model, 'destroy'
-    @unsubscribe $(window), 'resize:end'
+    @unsubscribe()

     @paneView.removeClass('with-minimap')


:art: use return value(range) of selection.insertText()

--- lib/mode-manager.coffee
+++ lib/mode-manager.coffee
@@ -67,8 +67,7 @@
       char = @replacedCharsBySelection[s.id].pop()
       if char? # char maybe empty char ''.
         s.selectLeft()
-        s.insertText(char)
-        s.cursor.moveLeft() if char
+        s.cursor.moveLeft() unless s.insertText(char).isEmpty()

   setInsertionCheckpoint: ->
     @insertionCheckpoint ?= @editor.createCheckpoint()


fixed bug in how we display latest time to eat / burn hames when ereb pesach is on Shabbat

We need to display the same time twice--once for latest time to burn on Friday,
and once for latest time to nullify on Shabbat--and we need to display latest time
to eat on Shabbat between them.

--- source/javascripts/index/mincha.coffee
+++ source/javascripts/index/mincha.coffee
@@ -212,7 +212,13 @@
     if @hebrew_date.isErebPesach()
       latest_time_to_eat = @shaa_zemani(4).format('h:mm A')
       latest_time_to_burn = @shaa_zemani(5).format('h:mm A')
-      "Stop eating חָמֵץ before #{latest_time_to_eat}#{if @hebrew_date.isShabbat() then " on Friday"  else ""}
Burn חָמֵץ before #{latest_time_to_burn}#{if @hebrew_date.isShabbat() then " on שַׁבָּת" else ""}"
+      stop_eating = "Stop eating חָמֵץ before #{latest_time_to_eat}#{if @hebrew_date.isShabbat() then " on שַׁבָּת"  else ""}"
+      burn = "Burn חָמֵץ before #{latest_time_to_burn}#{if @hebrew_date.isShabbat() then " on Friday" else ""}"
+      if @hebrew_date.isShabbat()
+        nullify = "Make sure no חָמֵץ is in your possesion and say כָּל חֲמִירָא before #{latest_time_to_burn} on שַׁבָּת"
+        "#{burn}
#{stop_eating}
#{nullify}"
+      else
+        "#{stop_eating}
#{burn}"
     else
       a = window.announcements[@hebrew_date.getHebrewYear().getYearFromCreation()]
       a = a[@hebrew_date.weekOfYear()] if a?


Better error messaging

Better error messaging

--- lib/python-tools.coffee
+++ lib/python-tools.coffee
@@ -26,8 +26,7 @@

   subscriptions: null

-  _issueReportLink: ['If issue persists please report it at https://github.com',
-                     '/michaelaquilina/python-tools/issues/new'].join('')
+  _issueReportLink: "https://github.com/michaelaquilina/python-tools/issues/new"

   activate: (state) ->
     # Events subscribed to in atom's system can be easily cleaned up with a CompositeDisposable
@@ -84,25 +83,38 @@

     @provider.on 'error', (err) =>
       if err.code == 'ENOENT'
-        atom.notifications.addWarning(
-          "python-tools unable to find python executable: please set " +
-          "the path to python directory manually in the package settings and " +
-          "restart your editor. #{@_issueReportLink}", {
+        atom.notifications.addWarning("""
+          python-tools was unable to find your machine's python executable.
+
+          Please try set the path in package settings and then restart atom.
+
+          If the issue persists please post an issue on
+          #{@_issueReportLink}
+          """, {
             detail: err,
             dismissable: true
           }
         )
       else
-        atom.notifications.addError(
-          "python-tools error. #{@_issueReportLink}", {
-            detail: err,
-            dismissable: true
-          }
+        atom.notifications.addError("""
+          python-tools unexpected error.
+
+          Please consider posting an issue on
+          #{@_issueReportLink}
+          """, {
+              detail: err,
+              dismissable: true
+            }
         )
     @provider.on 'exit', (code, signal) =>
       if signal != 'SIGTERM'
         atom.notifications.addError(
-          "python-tools provider exit. #{@_issueReportLink}", {
+          """
+          python-tools experienced an unexpected exit.
+
+          Please consider posting an issue on
+          #{@_issueReportLink}
+          """, {
             detail: "exit with code #{code}, signal #{signal}",
             dismissable: true
           }


fix for group.logoUrl

fix for group.logoUrl

--- vue/src/shared/models/group_model.coffee
+++ vue/src/shared/models/group_model.coffee
@@ -141,7 +141,7 @@
   logoUrl: ->
     if @logoUrlMedium
       @logoUrlMedium
-    else if @isSubgroup()
+    else if @parent()
       @parent().logoUrl()
     else
       AppConfig.theme.icon_src


:fire: Remove jQuery from ::onDragOver

:fire: Remove jQuery from ::onDragOver

--- lib/tab-bar-view.coffee
+++ lib/tab-bar-view.coffee
@@ -283,13 +283,18 @@

     tabBar = @getTabBar(event.target)[0]
     sortableObjects = tabBar.querySelectorAll(".sortable")
+    placeholder = @getPlaceholder()[0]
+    return unless placeholder?

     if newDropTargetIndex < sortableObjects.length
       element = sortableObjects[newDropTargetIndex].classList.add 'is-drop-target'
-      @getPlaceholder().insertBefore(element)
+      placeholder.insertBefore(element)
     else
       element = sortableObjects[newDropTargetIndex - 1].classList.add 'drop-target-is-after'
-      @getPlaceholder().insertAfter(element)
+      if sibling = element.nextSibling
+        placeholder.insertBefore(sibling)
+      else
+        element.parentElement.appendChild(placeholder)

   onDropOnOtherWindow: (fromPaneId, fromItemIndex) =>
     if @pane.id is fromPaneId


:art: handleActiveThemeButtons() -> handleActiveThemeButtonEvents()

--- lib/themes-panel.coffee
+++ lib/themes-panel.coffee
@@ -154,9 +154,9 @@
     @activeSyntaxTheme = @getActiveSyntaxTheme()
     @populateThemeMenus()
     @toggleActiveThemeButtons()
-    @handleActiveThemeButtons()
-
-  handleActiveThemeButtons: ->
+    @handleActiveThemeButtonEvents()
+
+  handleActiveThemeButtonEvents: ->
     @activeUiThemeSettings.on 'click', (event) =>
       event.stopPropagation()
       activeUiTheme = atom.themes.getActiveThemes().filter((theme) -> theme.metadata.theme is 'ui')[0]?.metadata


Fix call to KD.remote.api on ChannelController

--- client/landing/app/Applications/Chat.kdapplication/Controllers/ChannelController.coffee
+++ client/landing/app/Applications/Chat.kdapplication/Controllers/ChannelController.coffee
@@ -15,7 +15,7 @@
         @view.setUnreadCount @unreadCount

   addOnlineUser: (name) ->
-    KD.remote.JAccount.one "profile.nickname" : name, (err, account)=>
+    KD.remote.api.JAccount.one "profile.nickname" : name, (err, account)=>
       viewInstance = @view.addRosterItem account
       @participants[name] = viewInstance
       viewInstance


file render: convert tests to ssh-they

file render: convert tests to ssh-they

--- test/file/render.coffee
+++ test/file/render.coffee
@@ -9,8 +9,10 @@

   describe 'error', ->

-    it 'when source doesnt exist', ->
-      nikita.file.render
+    they 'when source doesnt exist', (ssh) ->
+      nikita
+        ssh: ssh
+      .file.render
         source: "oups"
         target: "#{scratch}/output"
         relax: true
@@ -20,8 +22,9 @@

   describe 'nunjunks', ->

-    it 'use `content`', ->
-      nikita
+    they 'use `content`', (ssh) ->
+      nikita
+        ssh: ssh
       .file.render
         engine: 'nunjunks'
         content: 'Hello {{ who }}'
@@ -34,38 +37,36 @@
         content: 'Hello you'
       .promise()

-    it 'use `source`', ->
-      source = "#{scratch}/render.j2"
-      target = "#{scratch}/render.txt"
-      nikita
-      .file
-        target: source
+    they 'use `source`', (ssh) ->
+      nikita
+        ssh: ssh
+      .file
+        target: "#{scratch}/source.j2"
         content: 'Hello {{ who }}'
       .file.render
-        source: source
-        target: target
-        context: who: 'you'
-      , (err, status) ->
-        status.should.be.true() unless err
-      .file.assert
-        target: target
-        content: 'Hello you'
-      .promise()
-
-    it 'test nikita type filters', ->
-      source = "#{scratch}/render.j2"
-      target = "#{scratch}/render.txt"
-      nikita
-      .file
-        target: source
+        source: "#{scratch}/source.j2"
+        target: "#{scratch}/target.txt"
+        context: who: 'you'
+      , (err, status) ->
+        status.should.be.true() unless err
+      .file.assert
+        target: "#{scratch}/target.txt"
+        content: 'Hello you'
+      .promise()
+
+    they 'test nikita type filters', (ssh) ->
+      nikita
+        ssh: ssh
+      .file
+        target: "#{scratch}/source.j2"
         content: """
         {% if randArray | isArray and randObject | isObject and not randArray | isObject %}
         Hello{% endif %}
         {% if who | isString and not anInt | isString %}{{ who }}{% endif %}
         """
       .file.render
-        source: source
-        target: target
+        source: "#{scratch}/source.j2"
+        target: "#{scratch}/target.txt"
         context:
           randArray: [1, 2]
           randObject: toto: 0
@@ -74,16 +75,15 @@
       , (err, status) ->
         status.should.be.true() unless err
       .file.assert
-        target: target
+        target: "#{scratch}/target.txt"
         content: '\nHello\nworld'
       .promise()

-    it 'test nikita isEmpty filter', ->
-      source = "#{scratch}/render.j2"
-      target = "#{scratch}/render.txt"
-      nikita
-      .file
-        target: source
+    they 'test nikita isEmpty filter', (ssh) ->
+      nikita
+        ssh: ssh
+      .file
+        target: "#{scratch}/source.j2"
         content: """
         {% if fake | isEmpty and emptyArray | isEmpty and not fullArray | isEmpty
         and emptyObject | isEmpty and not fullObject | isEmpty and emptyString | isEmpty and not fullString | isEmpty %}
@@ -91,8 +91,8 @@
         {% endif %}
         """
       .file.render
-        source: source
-        target: target
+        source: "#{scratch}/source.j2"
+        target: "#{scratch}/target.txt"
         context:
           emptyArray: []
           fullArray: [0]
@@ -103,20 +103,19 @@
       , (err, status) ->
         status.should.be.true() unless err
       .file.assert
-        target: target
+        target: "#{scratch}/target.txt"
         content: '\nsucceed\n'
       .promise()

-    it 'test personal filter', ->
-      source = "#{scratch}/render.j2"
-      target = "#{scratch}/render.txt"
-      nikita
-      .file
-        target: source
+    they 'test personal filter', (ssh) ->
+      nikita
+        ssh: ssh
+      .file
+        target: "#{scratch}/source.j2"
         content: 'Hello {% if who | isString %}{{ who }} {% endif %}{% if anInt | isNum %}{{ anInt }} {% endif %}{% if arr | contains("toto") %}ok{% endif %}'
       .file.render
-        source: source
-        target: target
+        source: "#{scratch}/source.j2"
+        target: "#{scratch}/target.txt"
         context:
           who: 'you'
           anInt: 42
@@ -125,20 +124,19 @@
       , (err, status) ->
         status.should.be.true() unless err
       .file.assert
-        target: target
+        target: "#{scratch}/target.txt"
         content: 'Hello you 42 ok'
       .promise()

-    it 'check autoescaping (disabled)', ->
-      source = "#{scratch}/render.j2"
-      target = "#{scratch}/render.txt"
-      nikita
-      .file
-        target: source
+    they 'check autoescaping (disabled)', (ssh) ->
+      nikita
+        ssh: ssh
+      .file
+        target: "#{scratch}/source.j2"
         content: 'Hello "{{ who }}" \'{{ anInt }}\''
       .file.render
-        source: source
-        target: target
+        source: "#{scratch}/source.j2"
+        target: "#{scratch}/target.txt"
         context:
           who: 'you'
           anInt: 42
@@ -146,14 +144,15 @@
       , (err, status) ->
         status.should.be.true() unless err
       .file.assert
-        target: target
+        target: "#{scratch}/target.txt"
         content: 'Hello "you" \'42\''
       .promise()

   describe 'eco', ->

-    it 'should use `content`', ->
-      nikita
+    they 'should use `content`', (ssh) ->
+      nikita
+        ssh: ssh
       .file.render
         engine: 'eco'
         content: 'Hello <%- @who %>'
@@ -166,21 +165,23 @@
         content: 'Hello you'
       .promise()

-    it 'detect `source`', ->
-      nikita
-      .file.render
-        source: "#{__dirname}/../resources/render.eco"
-        target: "#{scratch}/render.eco"
-        context: who: 'you'
-      , (err, status) ->
-        status.should.be.true() unless err
-      .file.assert
-        target: "#{scratch}/render.eco"
-        content: 'Hello you'
-      .promise()
-
-    it 'skip empty lines', ->
-      nikita
+    they 'detect `source`', (ssh) ->
+      nikita
+        ssh: ssh
+      .file.render
+        source: "#{__dirname}/../resources/render.eco"
+        target: "#{scratch}/render.eco"
+        context: who: 'you'
+      , (err, status) ->
+        status.should.be.true() unless err
+      .file.assert
+        target: "#{scratch}/render.eco"
+        content: 'Hello you'
+      .promise()
+
+    they 'skip empty lines', (ssh) ->
+      nikita
+        ssh: ssh
       .file.render
         engine: 'eco'
         content: "Hello\n\n\n<%- @who %>"
@@ -211,9 +212,10 @@
         status.should.be.false()
       .promise()

-    it 'detect extention and accept target as a callback', ->
+    they 'detect extention and accept target as a callback', (ssh) ->
       content = null
       nikita
+        ssh: ssh
       .file.render
         source: "#{__dirname}/../resources/render.eco"
         target: (c) -> content = c
@@ -222,8 +224,9 @@
         content.should.eql 'Hello you'
       .promise()

-    it 'when syntax is incorrect', ->
-      nikita
+    they 'when syntax is incorrect', (ssh) ->
+      nikita
+        ssh: ssh
       .file.render
         content: '<%- @host ->'
         engine: 'eco'


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/flickerable.coffee
+++ test/flickerable.coffee
@@ -19,6 +19,9 @@

   equals player.I.alpha, 0.9

-  5.times
+  5.times ->
+    player.update()
+
+  equals player.I.alpha, 0.2

 module()


update to use dispose instead of deprecated config.unobserve

--- lib/linter-flake8.coffee
+++ lib/linter-flake8.coffee
@@ -23,38 +23,38 @@

     @configFile = findFile @cwd, ['setup.cfg', 'tox.ini', '.pep8']

-    atom.config.observe 'linter-flake8.executableDir', =>
+    @executableDirListener = atom.config.observe 'linter-flake8.executableDir', =>
       executableDir = atom.config.get 'linter-flake8.executableDir'

       if executableDir
         @executablePath = if executableDir.length > 0 then executableDir else null

-    atom.config.observe 'linter-flake8.binaryName', =>
+    @binaryNameListener = atom.config.observe 'linter-flake8.binaryName', =>
       @updateCommand()

-    atom.config.observe 'linter-flake8.maxLineLength', =>
+    @maxLineLengthListener = atom.config.observe 'linter-flake8.maxLineLength', =>
       @updateCommand()

-    atom.config.observe 'linter-flake8.ignoreErrorCodes', =>
+    @ignoreErrorCodesListener = atom.config.observe 'linter-flake8.ignoreErrorCodes', =>
       @updateCommand()

-    atom.config.observe 'linter-flake8.maxComplexity', =>
+    @maxComplexityListener = atom.config.observe 'linter-flake8.maxComplexity', =>
       @updateCommand()

-    atom.config.observe 'linter-flake8.selectErrors', =>
+    @selectErrorsListener = atom.config.observe 'linter-flake8.selectErrors', =>
       @updateCommand()

-    atom.config.observe 'linter-flake8.hangClosing', =>
+    @hangClosingListener = atom.config.observe 'linter-flake8.hangClosing', =>
       @updateCommand()

   destroy: ->
-    atom.config.unobserve 'linter-flake8.executableDir'
-    atom.config.unobserve 'linter-flake8.binaryName'
-    atom.config.unobserve 'linter-flake8.maxLineLength'
-    atom.config.unobserve 'linter-flake8.ignoreErrorCodes'
-    atom.config.unobserve 'linter-flake8.maxComplexity'
-    atom.config.unobserve 'linter-flake8.selectErrors'
-    atom.config.unobserve 'linter-flake8.hangClosing'
+    @executableDirListener.dispose()
+    @binaryNameListener.dispose()
+    @maxLineLengthListener.dispose()
+    @ignoreErrorCodesListener.dispose()
+    @maxComplexityListener.dispose()
+    @selectErrorsListener.dispose()
+    @hangClosingListener.dispose()

   updateCommand: ->
     binary_name = atom.config.get 'linter-flake8.binaryName'


coffeelint

coffeelint

--- lib/linter-prolog.coffee
+++ lib/linter-prolog.coffee
@@ -6,7 +6,7 @@
       type: 'string'
       default: 'swipl'
   activate: ->
-    require("atom-package-dependencies").install();
+    require("atom-package-dependencies").install()

   provideLinter: ->
     LinterProvider = require './linter-provider'


Properly decrease request rate

Properly decrease request rate

--- server/testSystems/Informatics.coffee
+++ server/testSystems/Informatics.coffee
@@ -48,9 +48,9 @@
     _login: () ->
         logger.info "Logging in new InformaticsUser ", @username
         try
-            page = await download('https://informatics.msk.ru/login/index.php', @jar)
+            page = await @download('https://informatics.msk.ru/login/index.php')
             token = /<input type="hidden" name="logintoken" value="([^"]*)">/.exec(page)?[1]
-            page = await download("https://informatics.msk.ru/login/index.php", @jar, {
+            page = await @download("https://informatics.msk.ru/login/index.php", {
                 method: 'POST',
                 form: {
                     username: @username,
@@ -72,7 +72,7 @@
             logger.error "Can not log in new Informatics user #{@username}", e.message, e

     getId: () ->
-        page = await download("https://informatics.msk.ru/course/view.php?id=1135", @jar)
+        page = await @download("https://informatics.msk.ru/course/view.php?id=1135")
         @name = /([^<]*)</.exec(page)?[1]
         id = /<a href="https:\/\/informatics.msk.ru\/user\/profile.php\?id=(\d+)"/.exec(page)?[1]
         if not @name or not id or id.length < 2
@@ -96,7 +96,7 @@
         return result

     _runSubmit: (problemId, addParams) ->
-        page = await download("https://informatics.msk.ru/py/problem/#{problemId}/submit", @jar, {
+        page = await @download("https://informatics.msk.ru/py/problem/#{problemId}/submit", {
             addParams...,
             method: 'POST',
             followAllRedirects: true,


added comment for per-user route to top-level directory

--- services/web/app/coffee/router.coffee
+++ services/web/app/coffee/router.coffee
@@ -129,6 +129,7 @@
 				next()
 			), AuthorizationMiddlewear.ensureUserCanReadProject, CompileController.getFileFromClsi

+		# direct url access to output files for user but no build, to retrieve files when build fails
 		webRouter.get  /^\/project\/([^\/]*)\/user\/([0-9a-f-]+)\/output\/(.*)$/,
 			((req, res, next) ->
 				params =


Redis cosmetic

Redis cosmetic

--- lib/plugins/redis.coffee
+++ lib/plugins/redis.coffee
@@ -9,7 +9,7 @@

 -   `config`   , Path to the configuration file. Required to launch redis.
 -   `attach`   , Wether the Redis process should be attached to the current process. If not defined, default to `false` (the server run as a daemon).
--   `pidfile` Path to the file storing the daemon process id. Defaults to `"/.node_shell/#{md5}.pid"`
+-   `pidfile`  , Path to the file storing the daemon process id. Defaults to `"/.node_shell/#{md5}.pid"`
 -   `stdout`   , Writable stream or file path to redirect cloud9 stdout.
 -   `stderr`   , Writable stream or file path to redirect cloud9 stderr.