Remove redundant `filePath` in Config.constructor.

--- src/config.coffee
+++ src/config.coffee
@@ -2,7 +2,7 @@
 fs = require('fs')

 class Config
-  constructor: (filePath) ->
+  constructor: () ->
     filePath = @findConfig()
     data = @parse(filePath)
     @validate(data)
@@ -36,5 +36,5 @@
       throw new Error("Define `#{apiName}.url` in the config")

-config = new Config(__dirname + '/../config.json')
+config = new Config()
 module.exports = config


Add 'filtered (n) items' counter.

Add 'filtered (n) items' counter.

--- github.com.coffee
+++ github.com.coffee
@@ -172,9 +172,23 @@

 class NewsFeedFilter
   inject: ->
+    $("head").append ""
     @rebind_events()
+    @inject_counter()
     @apply_to(@$alerts())
     @maybe_load_more()
+
+  inject_counter: ->
+    @$news().prepend("""
+

+        Filtered 0 events
+        Show all
+

+    """)
+
+    @$news().find(".amck-gh-filter-counter a").click (event) =>
+      @$news().find(".alert:hidden").show()
+      event.preventDefault()

   rebind_events: ->
     @$news().delegate ".ajax_paginate", "click", (event) =>
@@ -202,12 +216,16 @@
         })

   apply_to: (items) ->
+    filtered = 0
+
     items.each (i, element) =>
       event_name = element.className.replace /alert\s+/, ""
       if @filter_set(element).is_filtered(event_name)
-        #console.log "Filtering: #{event_name} on #{@repo_name(element)}"
-        #$(element).css "opacity", 0.2
-        $(element).remove()
+        $(element).css "opacity", 0.4
+        $(element).hide()
+        filtered++
+
+    @increment_counter(filtered)

   maybe_load_more: ->
     if @$alerts().length < 20
@@ -217,7 +235,11 @@
     $("#dashboard > .news")

   $alerts: ->
-    @$news().find(".alert")
+    @$news().find(".alert:visible")
+
+  increment_counter: (n) ->
+    span = @$news().find(".amck-gh-filter-counter span")
+    span.html(parseInt(span.html(), 10) + n)

   filter_set: (element) ->
     new FilterSet(@repo_name(element))
@@ -225,6 +247,27 @@
   repo_name: (element) ->
     links = $("div.title > a", element)
     $(links[links.length - 1]).attr("href")
+
+  css: ->
+    """
+    .amck-gh-filter-counter {
+      border: 1px solid #eee;
+      border-radius: 5px;
+      background: #f8f8f8;
+      font-size: 12px;
+      color: #333;
+      margin-bottom: 25px;
+      padding: 10px;
+    }
+
+    .amck-gh-filter-counter span {
+      font-weight: bold;
+    }
+
+    .amck-gh-filter-counter a {
+      float: right;
+    }
+    """


Equation Builder Tweaks

Equation Builder Tweaks

[Fixes #4767204], [Fixes #4767079], [Fixes #4767075],
[Fixes #4767384], [Fixes #4767387]

--- javascripts/widgets/math_buttons.coffee
+++ javascripts/widgets/math_buttons.coffee
@@ -35,7 +35,7 @@
   negative_slash_positive: (opts)->
     @button({
       value: '-/+'
-      label: "–/+"
+      label: "(–/+)"
       class: 'math-button number-specifier negative-slash-positive'
     }, opts)

@@ -140,9 +140,9 @@
     @button({
       value: 'fraction'
       label: """
-
#{math_var('a')}

+
a

—

-
#{math_var('b')}

+
b

         """
       class: 'math-button other fraction'
     }, opts)


Don't expect the return value to contain a secret

--- src/WebSocket.coffee
+++ src/WebSocket.coffee
@@ -341,6 +341,7 @@
           connection.once 'message', (message) ->
             data = JSON.parse message.utf8Data
             validateSchema data, '/graph/output/addinitial'
+            delete expects[0].payload.secret
             chai.expect(data.payload).to.eql expects[0].payload

             done()
@@ -423,6 +424,7 @@
           connection.once 'message', (message) ->
             data = JSON.parse message.utf8Data
             validateSchema data, '/graph/output/renamenode'
+            delete expects[0].payload.secret
             chai.expect(data.payload).to.eql expects[0].payload

             done()


Add pattern-matching for bracket groups

Add pattern-matching for bracket groups

--- grammars/apl.cson
+++ grammars/apl.cson
@@ -13,6 +13,28 @@
 	{
 		match: "\\A#!.*$"
 		name:  "comment.line.shebang.apl"
+	}
+
+
+	# Brackets
+	{
+		name: "meta.round.bracketed.group.apl"
+		patterns: [{include: "#main"}]
+		begin: "\\("
+		end:   "\\)"
+		beginCaptures: {0: {name: "punctuation.round.bracket.begin.apl"}}
+		endCaptures:   {0: {name: "punctuation.round.bracket.end.apl"}}
+	}
+
+
+	# Square brackets
+	{
+		name: "meta.square.bracketed.group.apl"
+		patterns: [{include: "#main"}]
+		begin: "\\["
+		end:   "\\]"
+		beginCaptures: {0: {name: "punctuation.square.bracket.begin.apl"}}
+		endCaptures:   {0: {name: "punctuation.square.bracket.end.apl"}}
 	}


Set id field on meta profile model (meta post id)

--- lib/assets/javascripts/models/meta_profile.js.coffee
+++ lib/assets/javascripts/models/meta_profile.js.coffee
@@ -23,6 +23,7 @@
       server_meta_post = res.post

       attrs = _.extend({
+        id: server_meta_post.id
         entity: server_meta_post.content.entity
         avatar_digest: server_meta_post.attachments?[0]?.digest
       }, server_meta_post.content.profile || {})


optimization: only tokens containing tabs are split into multiple tokens

--- src/app/token.coffee
+++ src/app/token.coffee
@@ -17,6 +17,8 @@
     [new Token(value: value1, type: @type), new Token(value: value2, type: @type)]

   breakOutTabCharacters: (tabText) ->
+    return [this] unless /\t/.test(tabText)
+
     for substring in @value.match(/([^\t]+|\t)/g)
       if substring == '\t'
         @buildTabToken(tabText)


:memo: Correct broken references

:memo: Correct broken references

--- src/editor.coffee
+++ src/editor.coffee
@@ -1074,11 +1074,11 @@

   # Public: Get all the decorations within a screen row range.
   #
-  # startScreenRow - the {int} beginning screen row
-  # endScreenRow - the {int} end screen row (inclusive)
+  # startScreenRow - the {Number} beginning screen row
+  # endScreenRow - the {Number} end screen row (inclusive)
   #
   # Returns an {Object} of decorations in the form `{1: [{type: 'gutter', class: 'someclass'}], 2: ...}`
-  #   where the keys are markerIds, and the values are an array of {Decoration} objects attached to the marker.
+  #   where the keys are markerIds, and the values are an array of decoration objects attached to the marker.
   # Returns an empty object when no decorations are found
   decorationsForScreenRowRange: (startScreenRow, endScreenRow) ->
     @displayBuffer.decorationsForScreenRowRange(startScreenRow, endScreenRow)


Turns out neither of them need the 'px'

Turns out neither of them need the 'px'

--- src/editor.coffee
+++ src/editor.coffee
@@ -739,7 +739,7 @@
       false

   bringHiddenInputIntoView: ->
-    @hiddenInput.css(top: @scrollTop() + 'px', left: @scrollLeft())
+    @hiddenInput.css(top: @scrollTop(), left: @scrollLeft())

   selectOnMousemoveUntilMouseup: ->
     lastMoveEvent = null


make gulp dbtest task check for connection string in env var

--- gulp/tasks/dbtest.coffee
+++ gulp/tasks/dbtest.coffee
@@ -8,22 +8,25 @@
 config = require '../config'
 log = require '../helpers/log'

-exec = require("child_process").exec
+conn = process.env.DATABASE_URL

 gulp.task 'dbtest', (done) ->
   log.info 'Checking database connection'
-  pg.connect process.env.DATABASE_URL, (error, client, close) ->
-    if error
-      gutil.beep()
-      if error.code is 'ECONNREFUSED'
-        log.error 'Unable to connect to the database'
+  if !conn
+    log.error 'DATABASE_URL env var has not been set'
+  else
+    pg.connect conn, (error, client, close) ->
+      if error
+        gutil.beep()
+        if error.code is 'ECONNREFUSED'
+          log.error 'Unable to connect to the database'
+        else
+          log.error error
       else
-        log.error error
-    else
-      client.query "SELECT $1::int AS number", ["1"], (error, result) ->
-        if error
-          log.error error
-        else
-          log.info 'Successfully connected to the database'
-        close()
-    pg.end()
+        client.query "SELECT $1::int AS number", ["1"], (error, result) ->
+          if error
+            log.error error
+          else
+            log.info 'Successfully connected to the database'
+          close()
+      pg.end()


Respect preserveFolds attribute when creating selections from markers

--- src/editor.coffee
+++ src/editor.coffee
@@ -1722,7 +1722,7 @@
     selection = new Selection(_.extend({editor: this, marker, cursor}, options))
     @selections.push(selection)
     selectionBufferRange = selection.getBufferRange()
-    @mergeIntersectingSelections()
+    @mergeIntersectingSelections(preserveFolds: marker.getAttributes().preserveFolds)
     if selection.destroyed
       for selection in @getSelections()
         if selection.intersectsBufferRange(selectionBufferRange)


Removed exports

Removed exports

--- src/export.coffee
+++ src/export.coffee
@@ -1,22 +1,5 @@
-# Rivets.factory
-# --------------
-
-# Rivets.js module factory.
 Rivets.factory = (sightglass) ->
-  # Integrate sightglass.
   Rivets.sightglass = sightglass
-
-  # Allow access to private members (for testing).
-  Rivets.public._ = Rivets
-
-  # Return the public interface.
   Rivets.public

-# Exports Rivets.js for CommonJS, AMD and the browser.
-if typeof module?.exports is 'object'
-  module.exports = Rivets.factory require('sightglass')
-else if typeof define is 'function' and define.amd
-  define ['sightglass'], (sightglass) ->
-    @rivets = Rivets.factory sightglass
-else
-  @rivets = Rivets.factory sightglass
+module.exports = Rivets.factory require('sightglass')


Fixes #2645 -- block comments should always be closed


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/follow.coffee
+++ src/follow.coffee
@@ -65,6 +65,5 @@
   @param {GameObject} obj The object you want to follow
   ###
   follow: (obj) ->
-    #
     if obj
       I.velocity = obj.position().subtract(self.position()).norm(I.speed)


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/follow.coffee
+++ src/follow.coffee
@@ -4,7 +4,7 @@
     velocity: Point(0, 0)

   follow: (obj) ->
-    I.velocity = obj.I.position().subtract(self.position()).norm().scale()
+    I.velocity = obj.I.position().subtract(self.position()).norm().scale(I.followS)

   self.bind "update", ->
     I.velocity = player.position().subtract(self.position()).norm().scale(5)


updated welcome message

updated welcome message

--- src/chat/chat.coffee
+++ src/chat/chat.coffee
@@ -27,11 +27,11 @@
     @switchToWindow new chat.Window Chat.NoConnName
     @winList = new chat.WindowList()

-    @currentWindow.message '*', "Welcome to irciii, a v2 Chrome app.", "circ"
-    @currentWindow.message '*', "Type /connect <server> [port] to connect, then /nick <my_nick> and /join <#channel>.", "circ"
-    @currentWindow.message '*', "Alt+[0-9] switches windows.", "circ"
+    @currentWindow.message '*', "Welcome to CIRC, a packaged Chrome app.", "circ"
+    @currentWindow.message '*', "Type /server <server> [port] to connect, then /nick <my_nick> and /join <#channel>.", "circ"
+    @currentWindow.message '*', "Switch windows with alt+[0-9] or clicking in the channel list on the left.", "circ"

-    @updateStatus 'hi!'
+    @updateStatus 'Welcome to CIRC! Type /help to see a full list of comamnds.'

     @connections = {}


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/clampable.coffee
+++ src/clampable.coffee
@@ -1,5 +1,5 @@
 ###*
-The `Clampable` module provides helper methods to clamp object properties. This module is included
+The `Clampable` module provides helper methods to clamp object properties. This module is included by default in Gam

     player = GameObject
       x: 40


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/collision.coffee
+++ src/collision.coffee
@@ -215,7 +215,7 @@
     @returns {Boolean} true if the line intersects the rectangle, false otherwise
     ###
     rayRectangle: (source, direction, target) ->
-
+      if
       xw = target.xw
       yw = target.yw


Fixed styles

Fixed styles

--- src/chocolate.coffee
+++ src/chocolate.coffee
@@ -423,9 +423,9 @@

         popover = document.querySelector "[data-pid=\"#{cid}\"]"
         setStyle popover,
-          'width':      image.offsetWidth
-          'height':     image.offsetHeight
-          'margin-top': -1 * image.offsetHeight + 'px'
+          'width':      "#{image.offsetWidth}px"
+          'height':     "#{image.offsetHeight}px"
+          'margin-top': "#{-1 * image.offsetHeight}px"

         addHandler image, ['mouseenter', 'mouseleave'], ->
           toggleClass popover[0], 'choco-hover'


add some demo stuff/test

add some demo stuff/test

--- src/monad.coffee
+++ src/monad.coffee
@@ -96,7 +96,41 @@
 evalState = (state, input) -> head (runState state, input)
 execState = (state, input) -> last (runState state, input)

+#
+# Demonstration
+#
+
+demoTest = () ->
+  # Cross product
+  cross = (ls1, ls2) ->
+    ls1.bind ((x) ->
+      ls2.bind ((y) ->
+        List [x,y]))
+  (cross (List 'A','B'), (List 1, 2, 3)).log()
+
+  # Normal iteration
+  for item in (List 5, 6)
+    util.log item
+
+  # Some list operations
+  (reverse (append (List 5, 6), (List 7, 8))).log()
+  (intersperse 0, (List 1,2,3,4,5)).log()
+  (intercalate (List 0), (List (List 1, 2), (List 3, 4), (List 5, 6))).log()
+
+  # Simple state computations
+  countdown = get.bind (i) ->
+    util.log i
+    (put i-1).bind ->
+      @unit i
+
+  state = 10
+  while state > 0
+    state = execState countdown, state
+  util.log "Blast off!"
+exports.test = demoTest
+
 exports.TMonad = TMonad
 exports.Just = Just
 exports.Nothing = Nothing
 exports.List = List
+demoTest()


collision

collision

--- src/collision.coffee
+++ src/collision.coffee
@@ -97,11 +97,9 @@

   @name rayCircle
   @methodOf Collision
-
   @param {Point} source The starting position
   @param {Point} direction A vector from the point
   @param {Object} target The circle
-
   @returns {Boolean} true if the line intersects the circle, false otherwise
   ###
   rayCircle: (source, direction, target) ->
@@ -141,7 +139,7 @@
     height: 20

   Collision.rayRectangle(Point(0, 0), Point(1, 0), rect)
-  => true
+  # => true
   </pre></code>

   @name rayRectangle


Copy gameboard library

Copy gameboard library

--- grunt/bowercopy.coffee
+++ grunt/bowercopy.coffee
@@ -6,6 +6,7 @@
       'build/debug/js/detectr.js': 'detectr/dist/detectr.js'
       'build/debug/js/eventmap.js': 'eventmap/dist/eventmap.js'
       'build/debug/js/fastclick.js': 'fastclick/lib/fastclick.js'
+      'build/debug/js/gameboard.js': 'gameboard/dist/gameboard.js'
       'build/debug/js/handlebars.js': 'handlebars/handlebars.amd.js'
       'build/debug/js/jquery.js': 'jquery/dist/jquery.js'
       'build/debug/js/lyria.js': 'lyria/dist/js/lyria.js'


Variable formated time precission (days  hours | hours  min | min  sec)

--- src/common_ui.coffee
+++ src/common_ui.coffee
@@ -82,14 +82,17 @@
     hours = Math.floor (time / (1000*60*60)) % 24
     days = Math.floor time / (1000*60*60*24)

+    formated_time = ''
     if days or hours or min or sec
-        formated_time = "#{sec} sec"
+        if not(hours or days)
+            formated_time = "#{sec} sec"
         if days or hours or min
-            formated_time = "#{min} min " + formated_time
+            if not (days)
+                formated_time = "#{min} min " + formated_time
             if days or hours
-                formated_time = "#{hours} hours " + formated_time
+                formated_time = "#{hours} hour#{if hours > 1 then 's' else ''} " + formated_time
                 if days
-                    formated_time = "#{days} day#{if days > 1 then 's'} " + formated_time
+                    formated_time = "#{days} day#{if days > 1 then 's' else ''} " + formated_time
     return formated_time

 module.exports = {react_utils, theme, mixins, components, sounds, markdown, format_time}


progress api

progress api

--- lib/ui.coffee
+++ lib/ui.coffee
@@ -10,6 +10,9 @@
     @client.onDisconnected =>
       @ink?.Result.invalidateAll()

+    @client.handle 'progress', (p) =>
+      @progress?.progress = p
+
   consumeInk: (@ink) ->
     @views.ink = @ink


fix a bug 界面iOS，android 打包界面改动

fix a bug
界面iOS，android 打包界面改动

--- lib/build/android-fastBuild-view.coffee
+++ lib/build/android-fastBuild-view.coffee
@@ -42,22 +42,22 @@
             @subview 'content_src', new EditorView(mini: true, placeholderText: 'click here to content-src')

   initialize: ->
-    [
-      {view: @content_src, suffix: 'html', relative: true}
-    ]
-    .forEach (each) ->
-      #disable input
-      each.view.setInputEnabled false
-      #select file
-      each.view.on 'click', ->
-        openFile
-          title: "Select .#{each.suffix} File"
-          filters: [{name: ".#{each.suffix} file", extensions: [each.suffix]}]
-        .then (destPath) ->
-          if each.relative?
-            each.view.setText path.relative(atom.project.path, destPath[0])
-          else
-            each.view.setText destPath[0]
+    # [
+    #   {view: @content_src, suffix: 'html', relative: true}
+    # ]
+    # .forEach (each) ->
+    #   #disable input
+    #   each.view.setInputEnabled false
+    #   #select file
+    #   each.view.on 'click', ->
+    #     openFile
+    #       title: "Select .#{each.suffix} File"
+    #       filters: [{name: ".#{each.suffix} file", extensions: [each.suffix]}]
+    #     .then (destPath) ->
+    #       if each.relative?
+    #         each.view.setText path.relative(atom.project.path, destPath[0])
+    #       else
+    #         each.view.setText destPath[0]

     @title.setText _.last(atom.project.path.split(path.sep)) if atom.project.path
@@ -67,9 +67,10 @@

     @content_src.setText 'main/index.html'

-    # @readOnlyEditorView @title
-    # @readOnlyEditorView @version
-    # @readOnlyEditorView @build
+    @readOnlyEditorView @title
+    @readOnlyEditorView @version
+    @readOnlyEditorView @build
+    @readOnlyEditorView @content_src

   onClickIcon: ->
     openFile


Applying replaceCreateView hack to all SuggestionList providers

--- lib/elixir-autocomplete-provider.coffee
+++ lib/elixir-autocomplete-provider.coffee
@@ -240,12 +240,11 @@
     marked(mdText)

   replaceUpdateDescription = ->
-    SuggestionList = require "#{atom.packages.getActivePackage('autocomplete-plus').path}/lib/suggestion-list"
-    SuggestionListElement = require "#{atom.packages.getActivePackage('autocomplete-plus').path}/lib/suggestion-list-element"
-    autocompleteManager = atom.packages.getActivePackage('autocomplete-plus').mainModule.autocompleteManager
-    viewProvider = (p for p in atom.views.providers when p.modelConstructor.name is 'SuggestionList')[0]
-
+    replaceCreateView(p) for p in atom.views.providers when p.modelConstructor.name is 'SuggestionList'
+
+  replaceCreateView = (viewProvider) ->
     viewProvider.createView = (model) ->
+      SuggestionListElement = require "#{atom.packages.getActivePackage('autocomplete-plus').path}/lib/suggestion-list-element"
       element = new SuggestionListElement().initialize(model)
       element.updateDescription = (item) ->
         suggestionList = atom.packages.getActivePackage('autocomplete-plus').mainModule.autocompleteManager.suggestionList


Fix onDidChangeKernel in plugin API

Fix onDidChangeKernel in plugin API

--- lib/plugin-api/hydrogen-provider.coffee
+++ lib/plugin-api/hydrogen-provider.coffee
@@ -5,7 +5,10 @@

     onDidChangeKernel: (callback) ->
         @_hydrogen.emitter.on 'did-change-kernel', (kernel) ->
-            return kernel.getPluginWrapper()
+            if kernel?
+                callback kernel.getPluginWrapper()
+            else
+                callback null

     getActiveKernel: ->
         unless @_hydrogen.kernel


err -> error

err -> error

Closes atom/atom#7407

--- src/directory.coffee
+++ src/directory.coffee
@@ -58,7 +58,7 @@
         Q.Promise (resolve, reject) =>
           fs.mkdir @getPath(), mode, (error) ->
             if error
-              reject err
+              reject error
             else
               resolve true
   ###


Made the hash data encoding explicit.

Made the hash data encoding explicit.

The hashing should be done in utf-8 mode, since the string is utf-8. Before Node v6, the default was binary.

https://nodejs.org/api/crypto.html#crypto_hash_update_data_input_encoding
https://github.com/nodejs/node/issues/6813

--- src/klarna.coffee
+++ src/klarna.coffee
@@ -48,7 +48,7 @@

     # Create (digestive) biscuit from payload and hash from biscuit
     biscuit = if payload? then JSON.stringify(payload) + credentials.secret else credentials.secret
-    hash = crypto.createHash('sha256').update(biscuit).digest('base64')
+    hash = crypto.createHash('sha256').update(biscuit, 'utf-8').digest('base64')

     # Return headers
     'Accept': klarna.headers.accept
@@ -242,4 +242,4 @@
     wrapper(f)()

 # Export module
-module.exports = publicMethods+module.exports = publicMethods


fixed key of brain

fixed key of brain

--- src/dockerhub.coffee
+++ src/dockerhub.coffee
@@ -84,7 +84,7 @@
     robot.logger.debug("hubot-docker build-trigger del")
     repo = res.match[1]
     robot.logger.debug("repo: #{repo}")
-    triger_tokens = (robot.brain.get("dockerhub-trigger-tokens-repository-to-rooms") || {})
+    triger_tokens = (robot.brain.get("dockerhub-trigger-tokens") || {})
     if triger_tokens[repo]
       delete triger_tokens[repo]
       robot.brain.set("dockerhub-trigger-tokens", triger_tokens)


dont delete _stream entry immideadly

dont delete _stream entry immideadly

--- src/dt-stream.coffee
+++ src/dt-stream.coffee
@@ -159,9 +159,10 @@
         if @opened_tags is 0 and @builder.closed is 'pending' and not @queue.length
             @closed?()
             @closed = yes
-        # cleanup element
-        el._stream.delete()
-        delete el._stream
+        el._stream.write ->
+            # cleanup element
+            el._stream.delete()
+            delete el._stream

     onclose: (el) ->
         el._stream.write =>


Showing the main date in UTC

Showing the main date in UTC

--- main/static/src/coffee/site/countdown.coffee
+++ main/static/src/coffee/site/countdown.coffee
@@ -24,7 +24,7 @@
   else
     window.goal = moment(timestamp)

-  ($ '.date').html goal.zone(moment().zone()).format('MMMM Do YYYY')
+  ($ '.date').text goal.utc().format('MMMM Do YYYY')
   ($ '.utc', '.time').html 'UTC ' + goal.utc().format('HH:mm')
   ($ '.local', '.time').html 'LOCAL ' + goal.zone(moment().zone()).format('HH:mm')


See #864. Fix Logger to use renamed loggerLevel setting key

--- src/logger.coffee
+++ src/logger.coffee
@@ -48,7 +48,7 @@
     })
     wlogger.on('logging', (transport, level, msg, meta)->
       loggerLevel = atom?.config.get(\
-        'atom-beautify._loggerLevel') ? "warn"
+        'atom-beautify.general.loggerLevel') ? "warn"
       # console.log('logging', loggerLevel, arguments)
       loggerLevelNum = levels[loggerLevel]
       levelNum = levels[level]


changed boundingrect to offset for performance

Former-commit-id: b8301ce7238c50c6ff3591e5a241865a219dc676 [formerly 091b428f3c3ee809369590789f72ad90957c9d07]
Former-commit-id: 3a5c54f37efa225fe3448c20a8e8035362976604

--- js/msa/zoomer.coffee
+++ js/msa/zoomer.coffee
@@ -35,8 +35,7 @@
       stepSize

     guessZoomLevel:(tSeqs) ->
-      _rect = @msa.container.getBoundingClientRect()
-      width = _rect.right - _rect.left
+      width = @msa.container.offsetWidth
       @len = @getMaxLength tSeqs
       @maxLabelLength =  @getMaxLabelLength tSeqs


Add a TODO

Add a TODO

--- js/outer/docs.coffee
+++ js/outer/docs.coffee
@@ -123,4 +123,5 @@

       super cx
     catch e
+      # TODO: go to 404 page
       return cx.redirect "/docs"


Option to display legend

Option to display legend

--- src/graphview.coffee
+++ src/graphview.coffee
@@ -82,7 +82,9 @@
       series = series.concat(@config.settings.graphiteAdditionalSeries)
     url = "#{@config.sources.graphite}/render/?width=#{@imageWidth}&height=#{@imageHeight}"
     url += "&target=#{series.join('&target=')}&from=#{from}"
-    url += "&graphOnly=false&hideLegend=true&areaMode=first&lineWidth=2"
+    url += "&graphOnly=false&areaMode=first&lineWidth=2"
+    if not @config.settings.showLegend
+      url += "&hideLegend=true"
     if graph.yMin?
       url += "&yMin=#{graph.yMin}"
     if graph.yMax?


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/game_over.coffee
+++ src/game_over.coffee
@@ -1,5 +1,5 @@
 ###*
-The Game Over class sets up a simple game title screen using App.name
+The Game Over class sets up a simple game stag

 @name update
 @methodOf TitleScreen#


Replace Node with Steps

Replace Node with Steps

--- js/inner.coffee
+++ js/inner.coffee
@@ -149,31 +149,15 @@
   appendLog: (json) =>
     @out.push(json.out)

-
-class Node extends Base
+class Step extends Base
   constructor: (json) ->
-
-    json.logs = (new ActionLog(j) for j in json.logs) if json.logs
-    super json, {index: null, logs :null}, ["index"]
-
-    @tab_id = @komp =>
-      "#node#{@index}"
-
-  # adds a new ActionLog
-  addLog: (json) =>
-    @logs.push(new ActionLog(json.log))
-
-  # adds output to an existing ActionLog
-  appendLog: (json) =>
-    @logs[json.log_index].appendLog(json)
-
-
+    super json, {}, []

 #TODO: next step is to add the vcs_url, which is why I was looking at the knockout.model and knockout.mapping plugin
 class Build extends HasUrl
   constructor: (json) ->

-    json.nodes = (new Node(j) for j in json.nodes) if json.nodes
+    json.steps = (new Step(j) for j in json.steps) if json.steps

     super json, {}, ["build_num", "status", "committer_name", "committer_email", "why", "user", "job_name", "branch", "vcs_revision", "start_time", "build_time_millis"]

@@ -278,7 +262,6 @@
   report_build: () =>
     VM.raiseIntercomDialog('I think I found a bug in Circle at ' + window.location + '\n\n')

-
   description: (include_project) =>
     return unless @build_num?


Don't load a subpage by default in the admin view

--- js/inner.coffee
+++ js/inner.coffee
@@ -443,10 +443,11 @@

   loadAdminPage: (cx, subpage) =>
     subpage = subpage[0].replace('/', '')
-    subpage = subpage || "projects"
-
-    $.getJSON "/api/v1/admin/#{subpage}", (data) =>
-      @admin(data)
+    subpage = subpage
+
+    if subpage
+      $.getJSON "/api/v1/admin/#{subpage}", (data) =>
+        @admin(data)

     $('#main').html(HAML['admin']({}))
     $('#subpage').html(HAML['admin_' + subpage]())


Got rid of testing ending line for indent visualizer

--- app/views/play/level/tome/SpellView.coffee
+++ app/views/play/level/tome/SpellView.coffee
@@ -324,9 +324,6 @@

-

             """


Debugging

Debugging

--- src/instagram.coffee
+++ src/instagram.coffee
@@ -34,8 +34,10 @@
       return
     Instagram.tags.recent
       name: tag
+      count: count
       complete: (data) ->
         index = 0
+        console.log('Count'+data.length)
         while index < count
           msg.send data[index]['images']['standard_resolution']['url']
           index++


Add default case for creating blank matrix of size 0

--- src/matrix.coffee
+++ src/matrix.coffee
@@ -68,6 +68,8 @@
 		@createBlankMatrix = (numOfRows, numOfCols = numOfRows) ->
 			assert(numOfRows).withName('numOfRows').notNegative numOfRows
 			assert(numOfCols).withName('numOfCols').notNegative numOfCols
+			if numOfRows is 0 or numOfCols is 0
+				return new Matrix()
 			b = ((0 for j in [0...numOfCols]) for i in [0...numOfRows])
 			return new Matrix(b)


this. => @

this. => @

--- src/matrix.coffee
+++ src/matrix.coffee
@@ -102,12 +102,12 @@
     ###
     concat: (matrix) ->
       Matrix(
-        this.a * matrix.a + this.c * matrix.b,
-        this.b * matrix.a + this.d * matrix.b,
-        this.a * matrix.c + this.c * matrix.d,
-        this.b * matrix.c + this.d * matrix.d,
-        this.a * matrix.tx + this.c * matrix.ty + this.tx,
-        this.b * matrix.tx + this.d * matrix.ty + this.ty
+        @a * matrix.a + @c * matrix.b,
+        @b * matrix.a + @d * matrix.b,
+        @a * matrix.c + @c * matrix.d,
+        @b * matrix.c + @d * matrix.d,
+        @a * matrix.tx + @c * matrix.ty + @tx,
+        @b * matrix.tx + @d * matrix.ty + @ty
       )

     ###*
@@ -124,8 +124,8 @@
     ###
     deltaTransformPoint: (point) ->
       Point(
-        this.a * point.x + this.c * point.y,
-        this.b * point.x + this.d * point.y
+        @a * point.x + @c * point.y,
+        @b * point.x + @d * point.y
       )

     ###*
@@ -138,15 +138,15 @@
     @type Matrix
     ###
     inverse: ->
-      determinant = this.a * this.d - this.b * this.c
+      determinant = @a * @d - @b * @c

       Matrix(
-        this.d / determinant,
-        -this.b / determinant,
-        -this.c / determinant,
-        this.a / determinant,
-        (this.c * this.ty - this.d * this.tx) / determinant,
-        (this.b * this.tx - this.a * this.ty) / determinant
+        @d / determinant,
+        -@b / determinant,
+        -@c / determinant,
+        @a / determinant,
+        (@c * @ty - @d * @tx) / determinant,
+        (@b * @tx - @a * @ty) / determinant
       )

     ###*
@@ -162,7 +162,7 @@
     @type Matrix
     ###
     rotate: (theta, aboutPoint) ->
-      this.concat(Matrix.rotation(theta, aboutPoint))
+      @concat(Matrix.rotation(theta, aboutPoint))

     ###*
     Returns a new matrix that corresponds this matrix multiplied by a
@@ -177,7 +177,7 @@
     @type Matrix
     ###
     scale: (sx, sy, aboutPoint) ->
-      this.concat(Matrix.scale(sx, sy, aboutPoint))
+      @concat(Matrix.scale(sx, sy, aboutPoint))

     ###*
     Returns the result of applying the geometric transformation represented by the
@@ -191,8 +191,8 @@
     ###
     transformPoint: (point) ->
       Point(
-        this.a * point.x + this.c * point.y + this.tx,
-        this.b * point.x + this.d * point.y + this.ty
+        @a * point.x + @c * point.y + @tx,
+        @b * point.x + @d * point.y + @ty
       )

     ###*
@@ -207,7 +207,7 @@
     @type Matrix
     ###
     translate: (tx, ty) ->
-      this.concat(Matrix.translation(tx, ty))
+      @concat(Matrix.translation(tx, ty))

   ###*


using Registry.createSID removed not logged in error in authorize handler

--- modules/http/lib/sessions/Sessions.coffee
+++ modules/http/lib/sessions/Sessions.coffee
@@ -74,28 +74,6 @@

         ##
-        ## BLOCKER: review and apply some common security strategies
-        ##
-        _getSID: (req)->
-
-
-            #console.log 'search cookie', @data.cookie.name, req.url, req.headers.cookie
-
-            if !(sid = req.cookies.get @data.cookie.name)
-
-                sid = @_createSID()
-
-                #console.log 'create cookie', sid
-
-                req.cookies.set @data.cookie.name, sid
-
-            #console.log 'sid', sid
-
-            ##
-            return sid
-
-
-        ##
         ##
         ##
         login: (token, user, pass, _fn)->
@@ -194,7 +172,7 @@
                         fn null, sess.public.user = data

                 else
-                    fn new Error 'not logged in'
+                    fn()

             else
@@ -274,10 +252,31 @@

         ##
+        ##
+        ##
+        _getSID: (req)->
+
+            #console.log 'search cookie', @data.cookie.name, req.url, req.headers.cookie
+
+            if !(sid = req.cookies.get @data.cookie.name)
+
+                sid = @_createSID()
+
+                #console.log 'create cookie', sid
+
+                req.cookies.set @data.cookie.name, sid
+
+            #console.log 'sid', sid
+
+            ##
+            return sid
+
+
+        ##
         ##
         ##
         _createSID: ()->
-
-            floyd.tools.strings.uuid()
+            @_registry.createSID()
+


deprecate createSID

deprecate createSID

--- modules/http/lib/sessions/Sessions.coffee
+++ modules/http/lib/sessions/Sessions.coffee
@@ -65,7 +65,7 @@
                         floyd.tools.objects.intercept res, 'end', (args..., end)=>
                             end.apply res, args

-                            req.session = res.session = null ## <-- remove references to avoid memory leaks
+                            req.session = res.session = null

                         ##
                         next()
@@ -84,7 +84,7 @@

             else
                 #console.log 'create SID'
-                @createSID (err, SID)=>
+                @_registry.createSID (err, SID)=>

                     #console.log 'create cookie', SID

@@ -276,4 +276,5 @@
         ##
         ##
         createSID: (fn)->
+            @logger.warning 'pubic createSID is deprecated and will be removed in future'
             @_registry.createSID fn


Ensure ports are lowercased in graph

Ensure ports are lowercased in graph

--- src/lib/Graph.coffee
+++ src/lib/Graph.coffee
@@ -159,9 +159,9 @@

     for conn in definition.connections
         if conn.data
-            graph.addInitial conn.data, conn.tgt.process, conn.tgt.port
+            graph.addInitial conn.data, conn.tgt.process, conn.tgt.port.toLowerCase()
             continue
-        graph.addEdge conn.src.process, conn.src.port, conn.tgt.process, conn.tgt.port
+        graph.addEdge conn.src.process, conn.src.port.toLowerCase(), conn.tgt.process, conn.tgt.port.toLowerCase()

     success graph


Graph: Allow to set/reset metadata entries

Needed to make metadata additions invertible.

--- src/lib/Graph.coffee
+++ src/lib/Graph.coffee
@@ -167,7 +167,10 @@
     before = clone @inports[publicPort].metadata
     @inports[publicPort].metadata = {} unless @inports[publicPort].metadata
     for item, val of metadata
-      @inports[publicPort].metadata[item] = val
+      if val?
+        @inports[publicPort].metadata[item] = val
+      else
+        delete @inports[publicPort].metadata[item]
     @emit 'changeInport', publicPort, @inports[publicPort], before
     @checkTransactionEnd()

@@ -213,7 +216,10 @@
     before = clone @outports[publicPort].metadata
     @outports[publicPort].metadata = {} unless @outports[publicPort].metadata
     for item, val of metadata
-      @outports[publicPort].metadata[item] = val
+      if val?
+        @outports[publicPort].metadata[item] = val
+      else
+        delete @outports[publicPort].metadata[item]
     @emit 'changeOutport', publicPort, @outports[publicPort], before
     @checkTransactionEnd()

@@ -249,7 +255,10 @@
       continue unless group.name is groupName
       before = clone group.metadata
       for item, val of metadata
-        group.metadata[item] = val
+        if val?
+          group.metadata[item] = val
+        else
+          delete group.metadata[item]
       @emit 'changeGroup', group, before
     @checkTransactionEnd()

@@ -395,7 +404,10 @@
     node.metadata = {} unless node.metadata

     for item, val of metadata
-      node.metadata[item] = val
+      if val?
+        node.metadata[item] = val
+      else
+        delete node.metadata[item]

     @emit 'changeNode', node, before
     @checkTransactionEnd()
@@ -490,7 +502,10 @@
     edge.metadata = {} unless edge.metadata

     for item, val of metadata
-      edge.metadata[item] = val
+      if val?
+        edge.metadata[item] = val
+      else
+        delete edge.metadata[item]

     @emit 'changeEdge', edge, before
     @checkTransactionEnd()


Cache fingerprint, fixes #44

Cache fingerprint, fixes #44

--- src/lib/https.coffee
+++ src/lib/https.coffee
@@ -38,6 +38,17 @@
     unblockSettings = gConf.get "unblock"

     tlsLog = gNewLogger "TLSServer"
+
+    # We fetch the fingerprint directly using OpenSSL and then make sure we got the right thing.
+    fingerPrint = ""
+    require("child_process").exec "openssl x509 -fingerprint -sha256 -text -noout -in #{httpSettings.tlsCert} | grep SHA256", (err, stdout, stderr) ->
+        if err? then throw err
+        if stderr.length > 0 then throw new Error stderr
+        fingerPrint = stdout.trim()[-95..]
+        if not /^([0-9A-F]{2}:){31}[0-9A-F]{2}$/.test fingerPrint then throw new Error "Could not validate the certificate fingerprint (#{fingerPrint})"
+        tlsLog.info "Your certificate fingerprint is #{fingerPrint}"
+    setTimeout (() -> if fingerPrint.length == 0 then throw new Error "Took too long to fetch fingerprint"), 1000
+
     tlsOptions = try
         {
             key: fs.readFileSync httpSettings.tlsKey
@@ -108,6 +119,10 @@
             @server.listen httpSettings.tlsPort, httpSettings.host, =>
                 @log.info gLineInfo("started HTTPS server "), httpSettings

+        getFingerPrint: ->
+                if fingerPrint.length == 0 then throw new Error "Cached fingerprint couldn't be read, this should not be possible."
+                fingerPrint
+
         shutdown: ->
             @log.debug gLineInfo "HTTPS servers shutting down!"
             @server.close()


Replace search paths in overriden extension handlers

--- src/judge/run.coffee
+++ src/judge/run.coffee
@@ -68,7 +68,30 @@
 							# Replace module path.
 							module.id = module.filename = filename = judgeCaseModulePath

+							# Replace search paths.
+							searchPathsToAdd = [path.join judgeCaseModulePath, '..', 'node_modules']
+							module.paths.shift()
+
+							loop
+								searchPath = getNextSearchPath searchPathsToAdd[searchPathsToAdd.length - 1]
+								break if searchPath is module.paths[0]
+								searchPathsToAdd.push searchPath
+
+							module.paths = searchPathsToAdd.concat module.paths
+
 					originalExtensions[extension](module, filename)
+
+getNextSearchPath = (searchPath) ->
+	searchPath = path.join searchPath, '..', '..'
+
+	lastPosition = searchPath.length - "#{path.sep}node_modules".length
+	lastIndex = searchPath.indexOf "#{path.sep}node_modules", lastPosition
+
+	if lastIndex is -1
+		return path.join searchPath, 'node_modules'
+	else
+		# We are already in a node_modules directory.
+		return searchPath

 clearModuleCache = ->
 	for module of require.cache


failure message wording tweak

failure message wording tweak

--- oriDomi.coffee
+++ oriDomi.coffee
@@ -59,7 +59,7 @@
 if !transformProp or !transitionProp or !perspectiveProp or
   !backfaceProp or !transformOriginProp or !transformStyleProp
     oriDomiSupport = false
-    console?.warn 'oriDomi: Browser does not support CSS 3D tranforms, disabling'
+    console?.warn 'oriDomi: Browser does not support oriDomi'

 defaults =


Update config.cson

Update config.cson

--- home/.atom/config.cson
+++ home/.atom/config.cson
@@ -1,19 +1,7 @@
 "*":
-  "aligner-ruby": {}
-  "atom-beautify":
-    general:
-      _analyticsUserId: "18424183-ca74-4a29-a264-61cce8fc6fa7"
   core:
     autoHideMenuBar: true
     automaticallyUpdate: false
-    disabledPackages: [
-      "language-ember-script"
-      "atom-emblem"
-      "es6-javascript"
-      "linter-ruby"
-      "linter-scss-lint"
-      "move-panes"
-    ]
   editor:
     fontFamily: "DejaVu Sans Mono"
     fontSize: 13
@@ -21,12 +9,9 @@
     showIndentGuide: true
     showInvisibles: true
     zoomFontWhenCtrlScrolling: false
-  "exception-reporting":
-    userId: "083ea2df-f816-ca42-389c-32ad23e9320f"
   linter:
     showErrorPanel: false
   "linter-rubocop": {}
-  "linter-ruby": {}
   "one-dark-ui":
     layoutMode: "Compact"
     tabSizing: "Minimum"


Updated Atom font and search settings

Updated Atom font and search settings

--- home/.atom/config.cson
+++ home/.atom/config.cson
@@ -67,12 +67,14 @@
       "city-lights-syntax"
     ]
   editor:
-    fontFamily: "Source Code Pro for Powerline"
+    fontFamily: "Knack Nerd Font"
     invisibles: {}
     showInvisibles: true
     softWrapHangingIndent: 4
   "exception-reporting":
     userId: "3e371242-4a5b-6e92-780d-36c1679a069e"
+  "find-and-replace":
+    scrollToResultOnLiveSearch: true
   "git-log":
     fontScale: 0.8
   "git-plus":
@@ -94,7 +96,7 @@
     honorStyleSettings: true
   "linter-jshint": {}
   "linter-ui-default":
-    panelHeight: 36
+    panelHeight: 81
   "merge-conflicts": {}
   minimap:
     plugins:


add counter of queries

add counter of queries

--- src/quest.coffee
+++ src/quest.coffee
@@ -263,7 +263,10 @@
     if split
       queries = new Splitter(@splitter).split queries
     result = null
+    count = queries.length
     for i, query of queries
+      counter = parseInt(i)
+      console.log "\nNow executing #{counter+1} of #{count}\n"
       console.log "\n#{query}\n".green
       if cb?
         @client.query(query, params, cb)


Don't add empty lines.

Don't add empty lines.

--- src/quote.coffee
+++ src/quote.coffee
@@ -112,9 +112,9 @@
         hours += 12 if ampm is 'PM'
         timestamp = moment({hours: parseInt(hours), minutes: parseInt(minutes)})
         ts = timestamp.format('h:mm A d MMM YYYY')
-      else if speaker?
+      else if speaker? and line.length > 0
         processed.push "[#{ts}] #{speaker}: #{line}"
-      else
+      else if line.length > 0
         processed.push line
     processed.push ''


Fix bug where @pingIntervalId is not being held

setupExpress() assigns @pingIntervalId but then it is being reset to null
right after.

--- src/robot.coffee
+++ src/robot.coffee
@@ -48,13 +48,13 @@
     @commands  = []
     @listeners = []
     @logger    = new Log process.env.HUBOT_LOG_LEVEL or 'info'
+    @pingIntervalId = null

     @parseVersion()
     if httpd
       @setupExpress()
     else
       @setupNullRouter()
-    @pingIntervalId = null

     @loadAdapter adapterPath, adapter


PackageManager::normalizePath implementation

--- src/pkgman.coffee
+++ src/pkgman.coffee
@@ -4,6 +4,18 @@
 config = require 'config'

 class PackageManager
+
+  @normalizePath: (path, capitalize = false) ->
+
+    i8n = require 'inflection'
+
+    parts = for part, i in path.split '/'
+      i8n.camelize i8n.underscore(
+        part.replace /[^\w]/g, '_'
+        0 is i
+      )
+
+    i8n.camelize (i8n.underscore parts.join ''), not capitalize

   # ## PackageManager#constructor
   constructor: ->


BF-111 changed scroll prevention to jQuery

--- app/assets/javascripts/controller.coffee
+++ app/assets/javascripts/controller.coffee
@@ -13,8 +13,8 @@

     # FIXME probably not the best place?!
     # avoid scrolling while pressing space
-    document.onkeydown = (event) ->
-      if event.keyCode == 32 or 37<=event.keyCode<=40 then return false
+    $(document).keydown (event) ->
+      if event.which == 32 or 37 <= event.which <= 40 then event.preventDefault(); return

     Model.User.Configuration.initialize().then(
       (data) =>


Bring GridToolManager up to date

Bring GridToolManager up to date

Missed some fixes and changes applied to ToolManager

--- bokehjs/src/coffee/common/grid_plot.coffee
+++ bokehjs/src/coffee/common/grid_plot.coffee
@@ -100,28 +100,23 @@
       if tools.length == 0
         continue
       info.tools = _.sortBy(tools, (tool) -> tool.get('default_order'))
-      info.tools[0].set('active', true)
+      if et not in ['pinch', 'scroll']
+        info.tools[0].set('active', true)

   _active_change: (tool) =>
-    et = tool.get('event_type')
-
-    active = tool.get('active')
-    if not active
-      return null
-
+    event_type = tool.get('event_type')
     gestures = @get('gestures')
-    prev = gestures[et].active
-    if prev?
-      logger.debug(
-        "GridToolManager: deactivating tool: #{prev.type} (for event type '#{et}'"
-      )
-      prev.set('active', false)
-
-    gestures[et].active = tool
+
+    # Toggle between tools of the same type by deactivating any active ones
+    currently_active_tool = gestures[event_type].active
+    if currently_active_tool? and currently_active_tool != tool
+      logger.debug("GridToolManager: deactivating tool: #{currently_active_tool.type} (#{currently_active_tool.id}) for event type '#{event_type}'")
+      currently_active_tool.set('active', false)
+
+    # Update the gestures with the new active tool
+    gestures[event_type].active = tool
     @set('gestures', gestures)
-    logger.debug(
-      "GridToolManager: activating tool: #{tool.type} (for event type '#{et}'"
-    )
+    logger.debug("GridToolManager: activating tool: #{tool.type} (#{tool.id}) for event type '#{event_type}'")
     return null

   defaults: () ->


use native json

use native json

--- app/assets/javascripts/erd/erd.js.coffee
+++ app/assets/javascripts/erd/erd.js.coffee
@@ -10,7 +10,15 @@
     $(rows).each (i, row) ->
       existing = row if (action == $(row[0]).html()) && (model == $(row[1]).html()) && (column == $(row[2]).html())
     if existing == null
-      $('#changes > tbody').append("
#{action}	#{model}	#{column}	#{from}	#{to}
")
+      $('#changes > tbody').append("""
+

+          #{action}
+          #{model}
+          #{column}
+          #{from}
+          #{to}
+

+      """)
     else
       $(existing[3]).text(from)
       $(existing[4]).text(to)
@@ -91,14 +99,16 @@
     $('form.add_column_form').on 'submit', @handle_add_column
     $('#changes_form').on 'submit', @handle_save

-  handle_save: =>
-    j = '['
-    rows = ($(tr).find('td') for tr in $('#changes > tbody > tr'))
-    $(rows).each (i, row) ->
-      j += "{\"action\": \"#{$(row[0]).html()}\", \"model\": \"#{$(row[1]).html()}\", \"column\": \"#{$(row[2]).html()}\", \"from\": \"#{$(row[3]).html()}\", \"to\": \"#{$(row[4]).html()}\"}"
-      j += ',' if i < rows.length - 1
-    j += ']'
-    $('#changes_form').find('input[name=changes]').val(j)
+  handle_save: (ev) =>
+    changes = $('#changes > tbody > tr').map(->
+      change = {}
+      $(this).find('td').each ->
+        name = $(this).data('name')
+        value = $(this).html()
+        change[name] = value
+      change
+    ).toArray()
+    $('#changes_form').find('input[name=changes]').val(JSON.stringify(changes))

   handle_add_column: (ev) =>
     ev.preventDefault()


It's just a flesh mistake...

It's just a flesh mistake...

--- app/assets/javascripts/reports.js.coffee
+++ app/assets/javascripts/reports.js.coffee
@@ -156,11 +156,11 @@
                                 @state_update(update.update) if update.update
                                 @state_set(update.state) if update.state
                                 @current_version = update.version
-                                @current_timestamp = message.timestamp
+                                @current_timestamp = update.timestamp
                         else if update.state?
                                 @state_set(update.state)
                                 @current_version = update.version
-                                @current_timestamp = message.timestamp
+                                @current_timestamp = update.timestamp
                         else
                                 console.log("we lost some updates, requesting full data.",@current_version, update.version)
                                 @request_full()


Add link to author profile in displaying fragment

--- app/assets/javascripts/stories.js.coffee
+++ app/assets/javascripts/stories.js.coffee
@@ -48,7 +48,7 @@
         $('#read-link').attr('href', Routes.story_fragment_read_path(story_id, fragment_id))
         $('#fragment-content').empty().append(data.content)
         $('#fragment_parent').val(data.id)
-        $('#author').empty().append("
Author: #{data.author_name}
")
+        $('#author').empty().append("
Author: #{data.author_name}
")
       error: (xhr, status, thrownError) ->
         alert(xhr.responseText, status, thrownError);
     )


wMinor fixes in default filter (ignore 0 and '')

--- app/coffee/modules/common/filters.coffee
+++ app/coffee/modules/common/filters.coffee
@@ -22,7 +22,7 @@

 defaultFilter = ->
     return (value, defaultValue) ->
-        if not value
+        if value in [null, undefined]
             return defaultValue
         return value


fix(kanban): prevent missing role points error

--- app/coffee/modules/taskboard/main.coffee
+++ app/coffee/modules/taskboard/main.coffee
@@ -725,7 +725,7 @@
         pointsByRole = _.reduce @scope.userstories, (result, us, key) =>
             _.forOwn us.points, (pointId, roleId) ->
                 role = getRole(roleId)
-                point = getPoint(pointId)
+                point = getPoint(pointId) || { value: 0 }

                 if !result[role.id]
                     result[role.id] = role


multiple fixes

multiple fixes

--- src/netrunner.coffee
+++ src/netrunner.coffee
@@ -52,27 +52,19 @@
   text += '*Type*: ' + card.type + ' - ' + card.subtype + '\n'
   if card.type == "Agenda"
     text += '*Adv/Pts*: '+ card.advancementcost + '\/' + card.agendapoints +'\n'
-  end
   if card.type == "ICE" || card.type == "Upgrade" || card.type == "Asset"
     text += '*Rez Cost*: ' + card.cost + '\n'
     if card.type == "ICE"
       text += '*Strength*: ' + card.strength + '\n'
-    end
-  end
   if card.type == "Program" || card.type == "Resource" || card.type == "Hardware"
     text += '*Install Cost*: ' + card.cost
     if /Icebreaker/i.test(card.subtype)
       text += '*Strength*: ' + card.strength + '\n'
-    end
-  end
   if card.type == "Operation" || card.type == "Event"
     text += '*Cost*: ' + card.cost + '\n'
-  end
   if card.factioncost != null || card.factioncost != 0
     text += '*Influence*: ' + card.factioncost + '\n'
-  end
   text += '*Set*: ' + card.setname + '\n'
-
   text += '*Text*: ' + card.text
     # Wrap icons in Slack-friendly colons
     .replace(/[\[|\]]/g, ':')


Horizontal directive: Fixing total label and value

--- app/scripts/directives/horizontal.coffee
+++ app/scripts/directives/horizontal.coffee
@@ -62,10 +62,9 @@
     @width = if @scope.percentage > idealRatio then @scope.percentage * @maxWidth else idealRatio * @maxWidth
     @elements.value.css('width', @width)

-    if totalLabel?
+    if @elements.totalLabel?
       if @width > @maxWidth - @totalLabelWidth
         @elements.totalLabel.fadeOut()
-        @elements.totalValue.fadeOut() if @elements.totalValue?
       else
         @elements.totalLabel.fadeIn()
         @elements.totalValue.fadeIn() if @elements.totalValue?


Clean up formattedValue implementation.

Clean up formattedValue implementation.

--- src/rivets.coffee
+++ src/rivets.coffee
@@ -37,17 +37,17 @@
     for formatter in @formatters
       args = formatter.split /\s+/
       id = args.shift()
-      value = if @model[id] instanceof Function
-        @model[id] value, args...
-      else if Rivets.formatters[id]
-        if Rivets.formatters[id].read instanceof Function
-          Rivets.formatters[id].read value, args...
-        else if Rivets.formatters[id] instanceof Function  # could occur if fmt = { publish: function() {}}
-          Rivets.formatters[id] value, args...
-        else    # skip if no read function exists
-          value
-      else      # skip if no formatter exists
-        value
+
+      formatter = if @model[id] instanceof Function
+        @model[id]
+      else
+        Rivets.formatters[id]
+
+      if formatter?.read instanceof Function
+        value = formatter.read value, args...
+      else if formatter instanceof Function
+        value = formatter value, args...
+
     value

   # Sets the value for the binding. This Basically just runs the binding routine


Runner: Fix JSON parser error when data contains </script>

Embed the JSON into a HTML comment,
so that the parser will not attempt to do anything with it

--- src/runner.coffee
+++ src/runner.coffee
@@ -64,7 +64,8 @@
   var main = function() {
     console.log('poly: main start');
     var dataElement = document.getElementById("poly-input-data");
-    var data = JSON.parse(dataElement.innerHTML);
+    var json = dataElement.innerHTML.substring("<!--".length, dataElement.innerHTML.length-"-->".length);
+    var data = JSON.parse(json);
     console.log('poly: starting solving');
     window.jsJobRun(data.page, data.options, cb);
     console.log('poly: started');
@@ -84,7 +85,7 @@
       #{scriptTags}
       <script>#{library}</script>
       <script src="#{filter}"></script>
-      <script id="poly-input-data" type="application/json">#{json}</script>
+      <script id="poly-input-data" type="application/json"><!--#{json}--></script>
     </head>
     <body>
       <script>#{script}</script>


runner: clearTimeout when stopping process

--- src/runner.coffee
+++ src/runner.coffee
@@ -318,7 +318,7 @@
       @stopping = true
       @child.kill 'SIGKILL'
       # should now fire exit handler
-    setTimeout onHardTimeout, @options.hardtimeout
+    @child.hardTimeout = setTimeout onHardTimeout, @options.hardtimeout

     @stdout = ""
     @child.stdout.on 'data', (data) =>
@@ -354,8 +354,10 @@
         callback @cancelError
       else
         callback null, job.id
+
       callback = null
-      @stopping = null
+      @stopping = false
+      clearTimeout @child.hardTimeout
       return

   stop: () ->


setInterval

setInterval

--- src/server.coffee
+++ src/server.coffee
@@ -50,19 +50,16 @@
   # if buffer has content, at 30ms resolution. recursively call this function on success
   #
   _write_pipe: ->
-    setTimeout (=>
-      console.log "setTimeout called with buffer #{buffer}"
-      if buffer.length == 0
-        @_write_pipe()
-        return
-      else
-        ws.write(buffer, (err, written) =>
-          throw err if err
-          buffer = ''
-          @_write_pipe()
-        )
-        return
-    ), 30
+    setInterval (=>
+      # console.log "setInterval called with buffer #{buffer}"
+      return if buffer.length == 0
+      ws.write(buffer, (err, written) =>
+        throw err if err
+        buffer = ''
+        # @_write_pipe()
+      )
+      # return
+    ), 15

   #
   # convert halo rgba string to a UART instruction


Fixed problem with authorization parsing

--- src/server.coffee
+++ src/server.coffee
@@ -8,13 +8,13 @@
 	#libs
 	fs = require 'fs'
 	restify = require 'restify'
-	bodyParser = require "body-parser"
+	bodyParser = require "body-parser"
 	cookieParser = require "cookie-parser"
 	session = require "express-session"
 	https = require 'https'
 	Path = require 'path'

-
+
 	PLModule = require './presentationLayer'

 	# Server config and launch
@@ -34,6 +34,7 @@

 	server.use restify.queryParser()
 	server.use restify.bodyParser mapParams:false
+	server.use restify.authorizationParser()

 	# Adds the 'always' middleware for each request
 	for k, middleware of env.middlewares.always
@@ -57,6 +58,6 @@
 							env.debug '%s listening at %s for %s', server.name, server.url, env.config.host_url
 							env.events.emit 'server', null
 							callback null, server
-
+
 						server.listen.apply server, listen_args
 	}


changed logger from error to info

changed logger from error to info

--- src/server.coffee
+++ src/server.coffee
@@ -210,8 +210,8 @@
       logger.error "Job " + job.attrs.name + " failed with " + err.message

     agenda.on "complete", (job)->
-      logger.error "Job " + job.attrs.name + " has completed"
-
+      logger.info "Job " + job.attrs.name + " has completed"
+
     alerts.setupAgenda agenda if config.alerts.enableAlerts
     reports.setupAgenda agenda if config.reports.enableReports
     if config.polling.enabled


Call hide on the views instead of the jQuery collection

This ensures that views with custom ::hide methods can invoke special
behavior.

--- src/pane-view.coffee
+++ src/pane-view.coffee
@@ -149,7 +149,7 @@
     hasFocus = @hasFocus()
     item.on? 'title-changed', @activeItemTitleChanged
     view = @viewForItem(item)
-    @itemViews.children().not(view).hide()
+    otherView.hide() for otherView in @itemViews.children().not(view).views()
     @itemViews.append(view) unless view.parent().is(@itemViews)
     view.show() if @attached
     view.focus() if hasFocus


logger support

logger support

--- src/sirius.coffee
+++ src/sirius.coffee
@@ -93,8 +93,10 @@
     #@param [Object] is a routes object where keys is a route and value is a array where
     # first element is a controller name, and second element is a action
     # or it's a function
-    # @todo: custom events
-    create: (routes) ->
+    # @event application:hashchange [current_url, prev_url]
+    # @event application:404 when url not found
+    # @event application:run after running
+    create: (routes, fn) ->
       current = prev = window.location.hash

       is_f = (f) ->
@@ -112,9 +114,6 @@
         f = controller[action]
         throw "Action must be a Function" if Object.prototype.toString.call(f) isnt '[object Function]'
         f
-
-      is_custom_event = (str) ->
-        str.indexOf(":") != -1

       for url, action of routes when url.indexOf("#") != 0 && url.toString() != "404"
         do (url, action) =>
@@ -137,6 +136,10 @@
         prev = current
         current = window.location.hash
         result = false
+
+        SiriusApplication.logger("Url change to: #{current}")
+        SiriusApplication.adapter.fire(document, "application:hashchange", current, prev)
+
         #call first matched function
         for part in array_of_routes
           do(part) =>
@@ -150,9 +153,11 @@

         #when no results, then call 404 or empty function
         if !result
+          SiriusApplication.adapter.fire(document, "application:404", current, prev)
           (if routes['404'] then a2f(routes['404']) else empty)(current)

-  #@note not implemented
+      fn()
+
   log: false
   #adapter for application @see adapter documentation
   adapter: null
@@ -160,12 +165,26 @@
   running: false
   #route object
   route: {}
+  #base logger when not support will be call a alert function
+  logger: (msg) ->
+    return if !@log
+    if window.console
+      console.log msg
+    else
+      alert "Not supported `console`"
   run: (options = {}) ->
     @running = true
     @log     = options["log"]     || @log
     @adapter = options["adapter"] || throw new Error("Specify adapter")
     @route   = options["route"]   || @route
+    @logger  = options["logger"]  || @logger

+    @logger("Logger enabled? #{@log}")
+    n = @adapter.constructor.name
+    @logger("Adapter: #{n}")
     # start
-    SiriusApplication.RouteSystem.create(@route);
+    SiriusApplication.RouteSystem.create(@route, () =>
+      @adapter.fire(document, "application:run", new Date());
+    );

+


fix a bug: 添加一个提醒

fix a bug:
添加一个提醒

--- lib/atom-chameleon.coffee
+++ lib/atom-chameleon.coffee
@@ -101,6 +101,7 @@
         webview.openDevTools()
     catch
       console.log "webview not open"
+      alert "Please open the Launch Emulator."

   cmdDebug: ->


remove global debugging assignment, make router constructor neater

--- shared/src/helpers/router.coffee
+++ shared/src/helpers/router.coffee
@@ -15,10 +15,8 @@
 class OXRouter

   constructor: (routes) ->
-    routesMap = mapRoutes(routes)
-
     @getRoutes = -> routes
-    @getRoutesMap = -> routesMap
+    @getRoutesMap = -> mapRoutes(routes)

   pathToEntry: (path = window.location.pathname) =>
     findRoutePatternMemoed(path, @getRoutesMap())
@@ -38,7 +36,7 @@

   getRenderableRoutes: (renderers) =>
     routes = @getRoutes()
-    window.renderers = renderers
+
     traverseRoutes(routes, (route) ->
       return null unless renderers[route.name]?
       route.render = renderers[route.name]


:electric_plug: apm publish with atom.io

--- src/publisher.coffee
+++ src/publisher.coffee
@@ -1,9 +1,12 @@
 path = require 'path'
+url = require 'url'

 require 'colors'
 CSON = require 'season'
 optimist = require 'optimist'
-
+request = require 'request'
+
+auth = require './auth'
 config = require './config'
 Command = require './command'

@@ -25,40 +28,41 @@

       If a new version or version increment is specified than a new Git tag is
       created and the package.json file is updated with that new version before
-      it is published to the apm registry.
+      it is published to the apm registry. The HEAD branch and the new tag are
+      pushed up to the remote repository automatically using this option.

       Run `apm available` to see all the currently published packages.
     """
     options.alias('h', 'help').describe('help', 'Print this usage message')
-    options.string('tag').describe('tag', 'Specify a tag to publish under')
-    options.boolean('force').describe('force', 'Force publish')
+    options.alias('t', 'tag').string('tag').describe('tag', 'Specify a tag to publish')

   showHelp: (argv) -> @parseOptions(argv).showHelp()

+  # Create a new version and tag use the `npm version` command.
+  #
+  #  * version: The new version or version increment.
+  #  * callback: The callback function to invoke with an error as the first
+  #    argument and a the generated tag string as the second argument.
   versionPackage: (version, callback) ->
     process.stdout.write 'Preparing and tagging a new version '
     versionArgs = ['version', version, '-m', 'Prepare %s release']
     @fork @atomNpmPath, versionArgs, (code, stderr='', stdout='') ->
       if code is 0
         process.stdout.write '\u2713\n'.green
-        callback()
+        callback(null, stdout.trim())
       else
         process.stdout.write '\u2717\n'.red
         callback("#{stdout}\n#{stderr}".red)

-  publishPackage: (options, callback) ->
-    process.stdout.write 'Publishing '
-    try
-      {name, version} = CSON.readFileSync(CSON.resolve('package')) ? {}
-      process.stdout.write "#{name}@#{version} "
-
-    publishArgs = ['--userconfig', @userConfigPath, 'publish']
-    if tag = options.argv.tag
-      publishArgs.push('--tag', tag)
-    if force = options.argv.force
-      publishArgs.push('--force')
-
-    @fork @atomNpmPath, publishArgs, (code, stderr='', stdout='') ->
+  # Push a tag to the remote repository.
+  #
+  #  * tag: The tag to push.
+  #  * callback: The callback function to invoke with an error as the first
+  #    argument.
+  pushVersion: (tag, callback) ->
+    process.stdout.write "Pushing #{tag} tag "
+    pushArgs = ['push', 'origin', 'HEAD', tag]
+    @spawn 'git', pushArgs, (code, stderr='', stdout='') ->
       if code is 0
         process.stdout.write '\u2713\n'.green
         callback()
@@ -66,15 +70,151 @@
         process.stdout.write '\u2717\n'.red
         callback("#{stdout}\n#{stderr}".red)

+  # Does the given package already exist in the registry?
+  #
+  #  * packageName: The string package name to check.
+  #  * callback: The callback function invoke with an error as the first
+  #    argument and true/false as the second argument.
+  packageExists: (packageName, callback) ->
+    auth.getToken (error, token) ->
+      if error?
+        callback(error)
+        return
+
+      requestSettings =
+        url: "#{config.getAtomPackagesUrl()}/#{packageName}"
+        json: true
+        headers:
+          authorization: token
+      request.get requestSettings, (error, response, body={}) ->
+        if error?
+          callback(error)
+        else
+          callback(null, response.statusCode is 404)
+
+  # Parse the repository in `name/owner` format from the package metadata.
+  #
+  # Returns a name/owner string or null if not parseable.
+  getRepository: (pack) ->
+    if repository = pack.repository?.url ? pack.repository
+      repoPath = url.parse(repository.replace(/\.git$/, '')).pathname
+      [name, owner] = repoPath.split('/')[-2..]
+      return "#{name}/#{owner}" if name and owner
+    null
+
+  # Register the current repository with the package registry.
+  #
+  #  * repository: The string repository in `name/owner` format.
+  #  * callback: The callback function to
+  registerPackage: (repository, callback) ->
+    auth.getToken (error, token) ->
+      if error?
+        callback(error)
+        return
+
+      requestSettings =
+        url: config.getAtomPackagesUrl()
+        json: true
+        method: 'POST'
+        body:
+          repository: repository
+        headers:
+          authorization: token
+      request.get requestSettings, (error, response, body={}) ->
+        if error?
+          callback(error)
+        else if response.statusCode isnt 200
+          message = body.message ? body
+          callback("Registering package failed: #{message}".red)
+        else
+          callback()
+
+  # Create a new package version at the given Git tag.
+  #
+  #  * packageName: The string name of the package.
+  #  * tag: The string Git tag of the new version.
+  #  * callback: The callback function to invoke with an error as the first
+  #    argument.
+  createPackageVersion: (packageName, tag, callback) ->
+    auth.getToken (error, token) ->
+      if error?
+        callback(error)
+        return
+
+      requestSettings =
+        url: "#{config.getAtomPackagesUrl()}/#{packageName}/versions"
+        json: true
+        method: 'POST'
+        body:
+          tag: tag
+        headers:
+          authorization: token
+      request.get requestSettings, (error, response, body={}) ->
+        if error?
+          callback(error)
+        else if response.statusCode isnt 200
+          message = body.message ? body
+          callback("Creating new version failed: #{message}".red)
+        else
+          callback()
+
+  # Publish the version of the package associated with the given tag.
+  #
+  #  * tag: The Git tag string of the package version to publish.
+  #  * callback: The callback function to invoke when done with an error as the
+  #    first argument.
+  publishPackage: (tag, callback) ->
+    try
+      pack = CSON.readFileSync(CSON.resolve('package')) ? {}
+
+    unless repository = @getRepository(pack)
+      callback('Unable to parse repository name/owner from package.json repository field'.red)
+      return
+
+    publishNewVersion = =>
+      process.stdout.write "Publishing #{pack.name}@#{pack.version} "
+      @createPackageVersion pack.name, tag, (error) ->
+        if error?
+          process.stdout.write '\u2717\n'.red
+          callback(error)
+        else
+          process.stdout.write '\u2713\n'.green
+          callback()
+
+    @packageExists pack.name, (error, exists) =>
+      if error?
+        callback(error)
+        return
+
+      if exists
+        process.stdout.write "Registering #{pack.name} (#{repository})"
+        @registerPackage repository, (error) =>
+          if error?
+            process.stdout.write '\u2717\n'.red
+            callback(error)
+          else
+            process.stdout.write '\u2713\n'.green
+            publishNewVersion()
+      else
+        publishNewVersion()
+
+  # Run the publish command with the given options
   run: (options) ->
     {callback} = options
     options = @parseOptions(options.commandArgs)

     if version = options.argv._[0]
-      @versionPackage version, (error) =>
-        if error?
-          callback(error)
-        else
-          @publishPackage(options, callback)
+      @versionPackage version, (error, tag) =>
+        if error?
+          callback(error)
+        else
+          @pushVersion tag, (error) =>
+            if error?
+              callback(error)
+            else
+              @publishPackage(tag, callback)
     else
-      @publishPackage(options, callback)
+      if tag = options.argv.tag
+        @publishPackage(tag, callback)
+      else
+        callback('Missing required tag to publish'.red)


fix: found a lost callback for OK scenario

--- src/pagerduty.coffee
+++ src/pagerduty.coffee
@@ -76,16 +76,18 @@
     async.mapSeries processedConfig['SCHEDULE'], (i, next) ->
       getSchedule i, next
     , (err, results) ->
-      if err then cb err
+      if err then return cb err
       if results
         processSchedules results, processedConfig['EXCLUSION_DAYS'], (err, message) ->
-          messages = messages.concat(message)
           debug('processSchedules:', processedConfig)
-          if processedConfig['NOTIFICATIONS'] && message isnt "OK"
-            debug('Sending notifications.')
-            sendNotification processedConfig['NOTIFICATIONS'], message, cb
+          if message isnt "OK"
+            messages = messages.concat(message)
+            if processedConfig['NOTIFICATIONS']
+              debug('Sending notifications.')
+              sendNotification processedConfig['NOTIFICATIONS'], message, cb
+          return cb()
       else
-        cb new Error "No schedule to process."
+        return cb new Error "No schedule to process."
   , (err) ->
     if err then return done err
     done null, messages


tweak

tweak

--- src/selection.coffee
+++ src/selection.coffee
@@ -93,10 +93,10 @@
         clearTimeout(@blurTimer)
         @blurTimer = null
       else
-        @editor.emit(@editor.constructor.events.FOCUS_CHANGE, true)
+        @emitter.emit(@emitter.constructor.events.FOCUS_CHANGE, true)
     else if !@blurTimer?
       @blurTimer = setTimeout( =>
-        @editor.emit(@editor.constructor.events.FOCUS_CHANGE, false)  if @hasFocus == false
+        @emitter.emit(@emitter.constructor.events.FOCUS_CHANGE, false)  if @hasFocus == false
         @blurTimer = null
       , 200)
   @hasFocus = hasFocus


Disable caching for now.

Disable caching for now.

--- src/requisite.coffee
+++ src/requisite.coffee
@@ -116,7 +116,7 @@
   iterate filename, entry

 # Wraps a required module in a define statement.
-wrap = (file, {minify}) ->
+wrap = (file) ->

   # update require statements
   if file.resolved.length and file.ast


namespaced Movable

namespaced Movable

--- src/rotatable.coffee
+++ src/rotatable.coffee
@@ -36,7 +36,7 @@
     rotationalVelocity: 0

   self.bind 'update', (dt) ->
-    I.rotation += I.rotationalVelocity
+    I.rotation += I.rotationalVelocity * dt

   return {}


Adds feature: Restore column position after editor:delete-line

--- src/selection.coffee
+++ src/selection.coffee
@@ -487,6 +487,7 @@
   deleteLine: ->
     if @isEmpty()
       start = @cursor.getScreenRow()
+      startColumn = @cursor.getScreenColumn()
       range = @editor.bufferRowsForScreenRows(start, start + 1)
       if range[1] > range[0]
         @editor.buffer.deleteRows(range[0], range[1] - 1)
@@ -496,9 +497,11 @@
       range = @getBufferRange()
       start = range.start.row
       end = range.end.row
+      startColumn = range.start.column
       if end isnt @editor.buffer.getLastRow() and range.end.column is 0
         end--
       @editor.buffer.deleteRows(start, end)
+    @cursor.setScreenPosition({row: @cursor.getScreenRow(), column: startColumn})

   # Public: Joins the current line with the one below it. Lines will
   # be separated by a single space.


remove html_document support

remove html_document support

--- lib/pandoc-wrapper.coffee
+++ lib/pandoc-wrapper.coffee
@@ -8,8 +8,6 @@
     'pdf'
   else if documentType == 'word_document'
     'docx'
-  else if documentType == 'html_document'
-    'html'
   else if documentType == 'rtf_document'
     'rtf'
   else if documentType == 'custom_document'


Prevent downloading multiple previews

Prevent downloading multiple previews

--- resources/assets/coffee/_classes/post-preview.coffee
+++ resources/assets/coffee/_classes/post-preview.coffee
@@ -29,7 +29,13 @@
     $form = $(target).closest('form')
     body = target.value
     $preview = $form.find('.js-post-preview--preview')
+    preview = $preview[0]
     $previewBox = $form.find('.js-post-preview--box')
+
+    if !preview?
+      return
+
+    preview._xhr?.abort()

     if body == ''
       $previewBox.addClass 'hidden'
@@ -39,7 +45,7 @@
       $previewBox.removeClass 'hidden'
       return

-    $.post(laroute.route('bbcode-preview'), text: body)
+    preview._xhr = $.post(laroute.route('bbcode-preview'), text: body)
     .done (data) =>
       $preview.html data
       $preview.attr 'data-raw', body


Use get/setAttribute directly

Use get/setAttribute directly

--- src/space-pen.coffee
+++ src/space-pen.coffee
@@ -71,7 +71,7 @@
     jQuery.data(@[0], 'view', this)
     for element in @[0].getElementsByTagName('*')
       jQuery.data(element, 'view', this)
-    @attr('callAttachHooks', true)
+    @[0].setAttribute('callAttachHooks', true)
     step(this) for step in postProcessingSteps
     @initialize?(args...)

@@ -84,10 +84,10 @@

   wireOutlets: (view) ->
     for element in view[0].querySelectorAll('[outlet]')
+      outlet = element.getAttribute('outlet')
       element = $(element)
-      outlet = element.attr('outlet')
       view[outlet] = element
-      element.attr('outlet', null)
+      element[0].removeAttribute('outlet', null)

     undefined

@@ -96,12 +96,12 @@
       selector = "[#{eventName}]"
       for element in view[0].querySelectorAll(selector)
         do (element) ->
+          methodName = element.getAttribute(eventName)
           element = $(element)
-          methodName = element.attr(eventName)
           element.on eventName, (event) -> view[methodName](event, element)

       if view[0].webkitMatchesSelector(selector)
-        methodName = view.attr(eventName)
+        methodName = view[0].getAttribute(eventName)
         do (methodName) ->
           view.on eventName, (event) -> view[methodName](event, view)

@@ -198,7 +198,7 @@
   onDom = element.parents?('html').length > 0

   elementsWithHooks = []
-  elementsWithHooks.push(element[0]) if element.attr?('callAttachHooks')
+  elementsWithHooks.push(element[0]) if element[0].getAttribute('callAttachHooks')
   if onDom
     for child in element[0].querySelectorAll('[callAttachHooks]')
       elementsWithHooks.push(child)


feature(product-import): update callback to resolve promises

--- lib/product-import.coffee
+++ lib/product-import.coffee
@@ -419,7 +419,12 @@
         existingProducts or @_getExistingProductsForSkus(@_extractUniqueSkus([prodToProcess]))
       .then (existingProducts) =>
         if (@beforeCreateOrUpdateCallback)
-          @beforeCreateOrUpdateCallback(prodToProcess, existingProducts)
+          return Promise.resolve(@beforeCreateOrUpdateCallback(prodToProcess, existingProducts))
+            .then () ->
+              return existingProducts
+        else
+          return existingProducts
+      .then (existingProducts) =>
         if existingProducts.length
           @_updateProductRepeater(prodToProcess, existingProducts, reassignmentRetries)
         else


linting changs

linting changs

--- lib/remote_payload.coffee
+++ lib/remote_payload.coffee
@@ -40,6 +40,8 @@
   reqPayload = getReqPayload req.payload, req.headers['content-type']
   params = getExecutionParams req.query, reqPayload

+  # Several changes were necessary when copying this logic from coderunner due to the lack
+  # of a platform request to get configuration for the snippet execution
   deisRequestBody =
     request:
       body: if _.isEmpty(reqPayload) then '' else reqPayload
@@ -54,15 +56,14 @@
       api_key: req.headers['x-cloudmine-apikey'] or req.query.apikey
       app_id: req.params.appid
       session_token: req.headers['x-cloudmine-sessiontoken'] or null
-      user_id: '[User ID not populated in local deployments]' #snippetConfig.userId -- needs platform request
+      user_id: '[User ID not populated in local deployments]'
     params: if _.isEmpty(params) then null else params
     config:
       async: isTruthy req.query.async
-      timeout: 30 #getExecTimeout snippetConfig.timeout -- needs platform request
-      version: 2 #if snippetConfig.backend_version then parseInt(snippetConfig.backend_version) else DEFAULT_BACKEND_VERSION -- needs platform request
+      timeout: 30
+      version: 2
       type: 'post'
     code: undefined
-#  deisRequestBody.response.body = a.platformResult if a.platformResult -- requires platform request
   new_req = {payload: deisRequestBody}
   new_req


swap order

swap order

--- lib/app/index.coffee
+++ lib/app/index.coffee
@@ -57,8 +57,8 @@
     trackSignup('facebook'),
     ssoAndRedirectBack
   app.get opts.linkedinCallbackPath,
+    afterSocialAuth('linkedin'),
     trackSignup('linkedin'),
-    afterSocialAuth('linkedin'),
     ssoAndRedirectBack

   # Twitter "one last step" UI


prevent sync on empty buffer

prevent sync on empty buffer

--- src/ui-scroll.coffee
+++ src/ui-scroll.coffee
@@ -257,6 +257,7 @@
 				bottomPadding.height (buffer.maxIndex() - buffer.next + 1) * viewport.averageItemHeight

 			viewport.syncDatasource = (datasource) ->
+				return if not buffer.length
 				delta = buffer.syncDatasource(datasource) * viewport.averageItemHeight
 				topPadding.height topPadding.height() + delta
 				viewport.scrollTop viewport.scrollTop() + delta


Added popup to vehicle marker

Added popup to vehicle marker

--- lib/vehicle_marker.coffee
+++ lib/vehicle_marker.coffee
@@ -14,6 +14,11 @@
   shadowSize: new L.Point(15, 16)
   iconAnchor: new L.Point(8,8)

+SM.PopupContent = Handlebars.compile """
+
{{registration}}

+
Speed: {{speed}}mph

+"""
+
 # Initialise this type of marker with a sensor vehicle
 # and the current lat and lon, e.g.
 #   marker = SM.VehicleMarker.create
@@ -26,6 +31,18 @@
     @set('stoppedIcon', new SM.VehicleStoppedIcon())
     state = @getPath('vehicle.state')
     @mapObject = new L.Marker(new L.LatLng(@get('lat'),@get('lon')), icon: @get("#{state}Icon"))
+    @get('map').addObject(this) # Add the object to the map
+    @_createPopup()
+  _createPopup: ->
+    @popup = new L.Popup()
+    @mapObject._popup = @popup
+    @mapObject.on('click', @mapObject.openPopup, @mapObject)
+    @_setPopupContent()
+  _setPopupContent: ->
+    html = SM.PopupContent
+      registration: @getPath('vehicle.registration')
+      speed: @getPath('vehicle.speed') || 0
+    @mapObject._popup.setContent(html)
   bounds: ->
     latlng = @get('mapObject')._latlng
     new L.LatLngBounds(latlng,latlng)
@@ -37,6 +54,7 @@
     console.log "Marker latitude did change"
     mapObject = @get('mapObject')
     mapObject.setLatLng(new L.LatLng(@getPath('vehicle.lat'),@getPath('vehicle.lon')))
+    @_setPopupContent()
   ).observes('vehicle.lat')
   _iconDidChange: ( ->
     mapObject = @get('mapObject')


try to survive to the flop

try to survive to the flop

--- src/valueHand.coffee
+++ src/valueHand.coffee
@@ -21,6 +21,9 @@

   value = 0

+  if suits.length == 1
+    value = value + (1 - value) * 0.5
+
   if myPairs > 0
     if hand.all.length < 4
       return 1
@@ -29,21 +32,18 @@
   else
     value -= 0.1

-  highCards = _.filter hand.hole, (card) -> card.rank > 9
-
-  if highCards.length > 0
-    value = value + (highCards.length / hand.all.length - value) * 0.5
-    if hand.community.length == 0
-      return highCards.length / 2
-  else
-    value -= 0.1
-
   if hand.community.length == 0
     if hand.hole[0].suit == hand.hole[1].suit
       return 1

-  if suits.length == 1
-    value = value + (1 - value) * 0.5
+  if hand.community.length < 3 or value > 0
+    highCards = _.filter hand.hole, (card) -> card.rank > 9
+    if highCards.length > 0
+      value = value + (highCards.length / hand.all.length - value) * 0.5
+      if hand.community.length == 0
+        return highCards.length / 2
+    else
+      value -= 0.1

   if value > 0
     value /= variance


Recognise inline child elements of height=0 elements caused by font-size

If an element with style="display: inline; font-size: 0px;" surrounds
another element with style="display: inline; font-size: apx;", where a
is non-zero, then the inner element and its contents will be visible.

However, the outer element will register height=0 in its clientRects,
which previously caused us to reject these elements. This changes the
behaviour to handle this case correctly.

--- lib/dom_utils.coffee
+++ lib/dom_utils.coffee
@@ -62,17 +62,29 @@
     # Note: this call will be expensive if we modify the DOM in between calls.
     clientRects = (Rect.copy clientRect for clientRect in element.getClientRects())

+    # Inline elements with font-size: 0px; will declare a height of zero, even if a child with non-zero
+    # font-size contains text.
+    isInlineZeroHeight = ->
+      elementComputedStyle = window.getComputedStyle element, null
+      isInlineZeroFontSize = (0 == elementComputedStyle.getPropertyValue("display").indexOf "inline") and
+        (elementComputedStyle.getPropertyValue("font-size") == "0px")
+      # Override the function to return this value for the rest of this context.
+      isInlineZeroHeight = -> isInlineZeroFontSize
+      isInlineZeroFontSize
+
     for clientRect in clientRects
       # If the link has zero dimensions, it may be wrapping visible but floated elements. Check for this.
       if (clientRect.width == 0 or clientRect.height == 0) and testChildren
         for child in element.children
           computedStyle = window.getComputedStyle(child, null)
-          # Ignore child elements which are not floated and not absolutely positioned for parent elements with
-          # zero width/height
+          # Ignore child elements which are not floated and not absolutely positioned for parent elements
+          # with zero width/height, as long as the case described at isInlineZeroHeight does not apply.
           # NOTE(mrmr1993): This ignores floated/absolutely positioned descendants nested within inline
           # children.
-          continue if (computedStyle.getPropertyValue('float') == 'none' &&
-            computedStyle.getPropertyValue('position') != 'absolute')
+          continue if (computedStyle.getPropertyValue("float") == "none" and
+            computedStyle.getPropertyValue("position") != "absolute" and
+            not (clientRect.height == 0 and isInlineZeroHeight() and
+              0 == computedStyle.getPropertyValue("display").indexOf "inline"))
           childClientRect = @getVisibleClientRect child, true
           continue if childClientRect == null or childClientRect.width < 3 or childClientRect.height < 3
           return childClientRect


Set selected text in replace editor on fnr:show-replace

Regs #433

--- lib/find-view.coffee
+++ lib/find-view.coffee
@@ -221,6 +221,9 @@
     @findEditor.getModel().selectAll()

   focusReplaceEditor: =>
+    selectedText = atom.workspace.getActiveTextEditor()?.getSelectedText?()
+    if selectedText and selectedText.indexOf('\n') < 0
+      @replaceEditor.setText(selectedText)
     @replaceEditor.focus()
     @replaceEditor.getModel().selectAll()


also don't inline objects if they're in lists

--- lib/js2coffee.coffee
+++ lib/js2coffee.coffee
@@ -337,7 +337,10 @@
   # A parameter list.

   'list': (n) ->
-    list = _.map(n.children, (item) => @build(item))
+    list = _.map(n.children, (item) =>
+      if n.children.length > 1
+        item.is_list_element = true
+      @build(item))

     list.join(", ")

@@ -510,7 +513,7 @@
     if n.children.length == 0
       "{}"

-    else if n.children.length == 1 and not n.is_property_value
+    else if n.children.length == 1 and not (n.is_property_value or n.is_list_element)
       @build n.children[0]

     else


Crude implementation of list-edit-cut

Crude implementation of list-edit-cut

--- lib/list-edit.coffee
+++ lib/list-edit.coffee
@@ -56,26 +56,74 @@
     #  1 elt:  remove, including whitespace
     # >1 elts: if not last, remove, put pre whitespace on pre whitespace of next element
     #         if last, put post whitespace on previous element
-
-    # Not cut, but some test code to easily visualize ranges
+    # [pre1ELT1post1,pre2ELT2post2,pre3ELT3post3]
+    # [pre1ELT1post1,pre2ELT3post3]
+
+    # spec:
+    # if newElts.length > 0
+    #   if cut == 0  # then newElts[0] will exist, so not the last one and no need to fix post
+    #     newElts[0].pre = cutElts[0].pre
+    #   else
+    #    if cut == newElts.length # newElts.length > 1, so newElts[n-1] exists, not first and no need to fix pre
+    #      newElts[n-1].post = cutElts[m].post
+
     @withSelectedList (editor, textBuffer, bufferText, selectionIxRange, listElements) ->
-      cursorPos = editor.getCursorBufferPosition()
-      console.log 'cursor row ' + cursorPos.row + ' col ' + cursorPos.column
-
-      bufferRanges = _.map listElements, (elt) ->
-        TextManipulation.getRangeForIxRange textBuffer, [elt.eltStart, elt.eltEnd]
-
-      console.log 'bufferRanges:'
-      console.log bufferRanges
-
-      if bufferRanges? && bufferRanges.length > 0
-        editor.setSelectedBufferRanges(bufferRanges)
-
+      if listElements.length == 0
+        atom.notifications.addWarning 'List cut in empty list.'
+        # TODO: clear the clipboard here?
+      else
+        # TODO: move to wrapper
+        [selStart,selEnd] = TextManipulation.getSelectionForRange listElements, selectionIxRange
+
+        # TODO: probably want to put this in withSelectedList
+        if selEnd > listElements.length
+          atom.notifications.addWarning 'List selection end outside list.'
+          # won't happen for start, since we use this to select the list in the first place
+          # TODO: maybe make this less strict, as selection in sublists is now asymmetric:
+          #       in "[1,[a,b],2]": "[a," selects entire sublist element, but ",b]" fails with warning.
+        else
+          # TODO: refactor copy so we can call it here
+          cutStart = selStart
+          cutEnd = selEnd
+          console.log 'List elements'
+          console.log e.show() for e in listElements
+          console.log 'Selection: ' + cutStart + ' <-> ' + cutEnd
+
+          # copy selected elements to clipboard
+          selectionText = bufferText.slice listElements[cutStart].eltStart, listElements[cutEnd-1].eltEnd
+          atom.clipboard.write selectionText, @mkListEditMeta()
+
+          # elts[0 .. cutStart .. cutEnd .. n-1]
+          newLength = listElements.length - (cutEnd - cutStart) # +1 since cut range is inclusive
+          console.log newLength
+
+          if newLength == 0
+            cutIxRange = [listElements[cutStart].start, listElements[cutEnd-1].end]
+            newWhitespace = ''
+          else
+            if cutStart == 0  # newLength > 0, so not the last one and no need to fix post (and elts[cutEnd] exists)
+              cutIxRange = [ listElements[0].start, listElements[cutEnd].eltStart ]
+              newWhitespace = listElements[0].leadingWhitespace  # newElts[0].pre = cutElts[0].pre
+            else # listElements[cutStart-1] exists
+              if cutEnd < listElements.length
+                cutIxRange = [ listElements[cutStart-1].end, listElements[cutEnd-1].end] # remove from preceding separator until post whitespace of last cut elt
+                newWhitespace = ''
+              else
+                cutIxRange = [ listElements[cutStart-1].eltEnd, listElements[cutEnd-1].end]
+                newWhitespace = listElements[cutEnd-1].trailingWhitespace   #newlistElements[n-1].post = cutElts[m].post
+          console.log 'cut index range:' + cutIxRange
+          console.log 'inserted: "' + newWhitespace + '"'
+          cutRange = TextManipulation.getRangeForIxRange textBuffer, cutIxRange
+          console.log cutRange.start
+          # editor.setSelectedBufferRange cutRange # for debugging: select the range that will be cut
+          textBuffer.setTextInRange cutRange, newWhitespace
+
+  # Store first pre, last post, one middle, and column of opening bracket for handling empty/one elt insertions (column is only for multi line lists)
   copyCmd: ->
     console.log 'Executing command list-edit-copy'
     @withSelectedList (editor, textBuffer, bufferText, selectionIxRange, listElements) ->
       if listElements.length == 0
-        atom.notifications.addWarning 'List selection in empty list.'
+        atom.notifications.addWarning 'List copy in empty list.'
         # TODO: clear the clipboard here?
       else
         [selStart,selEnd] = TextManipulation.getSelectionForRange listElements, selectionIxRange
@@ -88,11 +136,19 @@
           #       in "[1,[a,b],2]": "[a," selects entire sublist element, but ",b]" fails with warning.
         else
           selectionText = bufferText.slice listElements[selStart].eltStart, listElements[selEnd-1].eltEnd
+          # Clip includes separators, which seems logical when we use it for a non-list paste
           #console.log "Copied: '#{selectionText}'"
           atom.clipboard.write selectionText, @mkListEditMeta()

   pasteCmd: ->
     console.log 'Executing command list-edit-paste'
+    clip = atom.clipboard.readWithMetadata()
+    clipMeta = @getListEditMeta clip
+    if not clipMeta
+      atom.notifications.addError 'Clipboard does not contain list-edit clip.'
+      # TODO: For now this is an error, but we can probably still do something with it in the future
+    else
+      console.log 'Clipboard contains list-edit clip'

     # On element, replace element with stripped clipboard, while preserving whitespace
     #   1 elt,  make up bracket space, just use sep+' ' for now


Documentation

Documentation

--- lib/rich-text.coffee
+++ lib/rich-text.coffee
@@ -1,14 +1,20 @@
 Characters = require 'characters'
 Selections = require '../../y-selections'

+# A class holding the information about rich text
 class RichText
-  constructor: (content, editor) ->
+  # @param content [String] an initial string
+  # @param editor [Editor] an editor instance
+  # @param author [String] the name of the local author
+  constructor: (content, editor, author) ->
     # TODO: generate a UID
     @selections = new Selections
     @characters = new Characters content, @selections
-    @_cursors = []
+
+    @author = author

     @editor = editor
+    # shouldn't go there
     @editor.cursors = @editor.getModule("multi-cursor")

   _getModel: (Y, Operation) ->
@@ -16,7 +22,17 @@
       sels = @selections._getModel(Y, Operation)
       chars = @characters._getModel(Y, Operation)
       cursors = new Operation.ListManager(@).execute()
-      cursors.insert 0, @_cursors
+      # add self to cursors
+      if @editor.cursors.getSelection()
+        position = @characters.val @editor.cursors.getSelection().start
+      else
+        position = @characters.val 0
+
+      selfCursor =
+        author: @author
+        position: position
+        color: "grey" # FIXME
+      cursors.insert 0, selfCursor

       @_model = new Operation.MapManager(@).execute()
       @_model.val "selections", sels
@@ -30,6 +46,7 @@
     # TODO: check that our cursor is in the cursors
     @_model = model
     delete @_cursors
+
   set: (key, val) ->
     @_model.val key, val
   get: (key) ->
@@ -65,7 +82,6 @@
             ]}
           @editor.setContents delta

-          # propagate new string
         when "selections"
           selectionStart = @characters.indexOf (event.object.left or event.oldValue.left)
           selectionEnd = @characters.indexOf (event.object.right or event.oldValue.right)


Config schema instead of configDefaults

Config schema instead of configDefaults

--- lib/spark-ide.coffee
+++ lib/spark-ide.coffee
@@ -86,9 +86,11 @@

   serialize: ->

-  configDefaults:
+  config:
     # Delete .bin file after flash
-    deleteFirmwareAfterFlash: true
+    deleteFirmwareAfterFlash:
+      type: 'boolean'
+      default: true

   # Require view's module and initialize it
   initView: (name) ->


:lipstick: Call width instead of css

:lipstick: Call width instead of css

--- lib/tree-view.coffee
+++ lib/tree-view.coffee
@@ -127,8 +127,8 @@
     $(document.body).off('mousemove', @resizeTreeView)
     $(document.body).off('mouseup', @resizeStopped)

-  resizeTreeView: (e) =>
-    @css(width: e.pageX)
+  resizeTreeView: ({pageX}) =>
+    @width(pageX)

   updateRoot: ->
     @root?.remove()


Rename operator to operation where ambiguous

We were calling everything we could push to the op-stack an operator,
which wasn't technically correct. For example, motions aren't operators.
This uses the more general term "operation" instead.

--- lib/vim-state.coffee
+++ lib/vim-state.coffee
@@ -65,7 +65,7 @@
   #
   # Returns nothing.
   setupCommandMode: ->
-    # Commands here start a new mode instead of popping the operator stack
+    # Commands here start a new mode instead of popping the operation stack
     # immediately.
     @editorView.command 'vim-mode:search', =>
       @currentSearch = new motions.Search(@editorView, @)
@@ -150,20 +150,20 @@
     _.each commands, (fn, commandName) =>
       eventName = "vim-mode:#{commandName}"
       @editorView.command eventName, (e) =>
-        possibleOperators = fn(e)
-        possibleOperators = if _.isArray(possibleOperators) then possibleOperators else [possibleOperators]
-        for possibleOperator in possibleOperators
+        possibleOperations = fn(e)
+        possibleOperations = if _.isArray(possibleOperations) then possibleOperations else [possibleOperations]
+        for possibleOperation in possibleOperations
           # Motions in visual mode perform their selections.
-          if @mode == 'visual' and possibleOperator instanceof motions.Motion
-            possibleOperator.origExecute = possibleOperator.execute
-            possibleOperator.execute = possibleOperator.select
-
-          @pushOperator(possibleOperator) if possibleOperator?.execute
+          if @mode == 'visual' and possibleOperation instanceof motions.Motion
+            possibleOperation.origExecute = possibleOperation.execute
+            possibleOperation.execute = possibleOperation.select
+
+          @pushOperation(possibleOperation) if possibleOperation?.execute

           # If we've received an operator in visual mode, mark the current
           # selection as the motion to operate on.
-          if @mode == 'visual' and possibleOperator instanceof operators.Operator
-            @pushOperator(new motions.CurrentSelection(@))
+          if @mode == 'visual' and possibleOperation instanceof operators.Operator
+            @pushOperation(new motions.CurrentSelection(@))
             @activateCommandMode() if @mode == 'visual'

   # Private: Attempts to prevent the cursor from selecting the newline
@@ -176,12 +176,12 @@
     if not @editor.getSelection().modifyingSelection and @editor.cursor.isOnEOL() and @editor.getCurrentBufferLine().length > 0
       @editor.setCursorBufferColumn(@editor.getCurrentBufferLine().length - 1)

-  # Private: Adds an operator to the operation stack.
+  # Private: Adds an operation to the operation stack.
   #
   # operation - The operation to add.
   #
   # Returns nothing.
-  pushOperator: (operation) ->
+  pushOperation: (operation) ->
     @opStack.push(operation)
     @processOpStack()

@@ -195,23 +195,23 @@
   #
   # Returns nothing.
   processOpStack: ->
-    return unless @topOperator().isComplete()
-
-    poppedOperator = @opStack.pop()
+    return unless @topOperation().isComplete()
+
+    poppedOperation = @opStack.pop()
     if @opStack.length
       try
-        @topOperator().compose(poppedOperator)
+        @topOperation().compose(poppedOperation)
         @processOpStack()
       catch e
         (e instanceof operators.OperatorError) and @resetCommandMode() or throw e
     else
-      @history.unshift(poppedOperator) if poppedOperator.isRecordable()
-      poppedOperator.execute()
+      @history.unshift(poppedOperation) if poppedOperation.isRecordable()
+      poppedOperation.execute()

   # Private: Fetches the last operation.
   #
   # Returns the last operation.
-  topOperator: ->
+  topOperation: ->
     _.last @opStack

   # Private: Fetches the value of a given register.
@@ -332,7 +332,7 @@
   # Returns nothing.
   registerPrefix: (e) ->
     name = atom.keymap.keystrokeStringForEvent(e.originalEvent)
-    @pushOperator(new prefixes.Register(name))
+    @pushOperation(new prefixes.Register(name))

   # Private: A generic way to create a Number prefix based on the event.
   #
@@ -341,13 +341,13 @@
   # Returns nothing.
   repeatPrefix: (e) ->
     num = parseInt(atom.keymap.keystrokeStringForEvent(e.originalEvent))
-    if @topOperator() instanceof prefixes.Repeat
-      @topOperator().addDigit(num)
+    if @topOperation() instanceof prefixes.Repeat
+      @topOperation().addDigit(num)
     else
       if num is 0
         e.abortKeyBinding()
       else
-        @pushOperator(new prefixes.Repeat(num))
+        @pushOperation(new prefixes.Repeat(num))

   # Private: Figure out whether or not we are in a repeat sequence or we just
   # want to move to the beginning of the line. If we are within a repeat
@@ -357,7 +357,7 @@
   #
   # Returns nothing.
   moveOrRepeat: (e) ->
-    if @topOperator() instanceof prefixes.Repeat
+    if @topOperation() instanceof prefixes.Repeat
       @repeatPrefix(e)
     else
       new motions.MoveToBeginningOfLine(@editor)


coffee: listen to src instead of data-src


Talk messaging

Talk messaging

--- app/lib/en_us.coffee
+++ app/lib/en_us.coffee
@@ -25,7 +25,7 @@
       root: 'Profile'
       recents: 'Recents'
       favorites: 'Favorites'
-    discuss: 'Discuss'
+    discuss: 'Talk'
     blog: 'Blog'

   classify: