Visual/edit modes: continued development...

- Some better comments.
- More commands.
- The "o" command for visual mode (the implementation is poor, there has
  to be a better way).

--- content_scripts/mode_visual_edit.coffee
+++ content_scripts/mode_visual_edit.coffee
@@ -1,18 +1,21 @@

-# This prevents unmapped printable characters from being passed through to underlying page.
+# This prevents printable characters from being passed through to underlying page.  It should, however, allow
+# through chrome keyboard shortcuts.  It's a backstop for all of the modes following.
 class SuppressPrintable extends Mode
   constructor: (options) ->

     handler = (event) =>
       if KeyboardUtils.isPrintable event
         if event.type == "keydown"
-          DomUtils. suppressPropagation
+          DomUtils.suppressPropagation
           @stopBubblingAndTrue
         else
           @suppressEvent
       else
         @stopBubblingAndTrue

+    # This is pushed onto the handler stack before calling super().  Therefore, it ends up underneath (or
+    # after) all of the other handlers associated with the mode.
     @suppressPrintableHandlerId = handlerStack.push
       _name: "movement/suppress-printable"
       keydown: handler
@@ -22,14 +25,11 @@
     super options
     @onExit => handlerStack.remove @suppressPrintableHandlerId

-# This watches keyboard events, and maintains @countPrefix as count-prefic and other keys are pressed.
+# This watches keyboard events and maintains @countPrefix as count and other keys are pressed.
 class MaintainCount extends SuppressPrintable
   constructor: (options) ->
     @countPrefix = ""
     super options
-
-    isNumberKey = (keyChar) ->
-      keyChar and keyChar.length == 1 and "0" <= keyChar <= "9"

     @push
       _name: "movement/maintain-count"
@@ -37,23 +37,68 @@
         @alwaysContinueBubbling =>
           unless event.metaKey or event.ctrlKey or event.altKey
             keyChar = String.fromCharCode event.charCode
-            @countPrefix = if isNumberKey keyChar then @countPrefix + keyChar else ""
+            @countPrefix =
+              if keyChar and keyChar.length == 1 and "0" <= keyChar <= "9"
+                @countPrefix + keyChar
+              else
+                ""

-  countPrefixTimes: (func) ->
-    countPrefix = if 0 < @countPrefix.length then parseInt @countPrefix else 1
-    @countPrefix = ""
-    func() for [0...countPrefix]
+  runCountPrefixTimes: (func) ->
+    count = if 0 < @countPrefix.length then parseInt @countPrefix else 1
+    func() for [0...count]

 # This implements movement commands with count prefixes (using MaintainCount) for visual and edit modes.
 class Movement extends MaintainCount
+
+  # Get the direction of the selection, either "forward" or "backward".
+  # FIXME(smblott).  There has to be a better way!
+  getDirection: (selection) ->
+    length = selection.toString().length
+    # Try to move the selection forward, then check whether it got bigger or smaller (then restore it).
+    selection.modify "extend", "forward", "character"
+    if length != selection.toString().length
+      direction = if selection.toString().length < length then "backward" else "forward"
+      selection.modify "extend", "backward", "character"
+      direction
+    else
+      # If we can't move forward, we could be at the end of the document, so try moving backward instead.
+      selection.modify "extend", "backward", "character"
+      if length != selection.toString().length
+        direction = if selection.toString().length < length then "forward" else "backward"
+        selection.modify "extend", "forward", "character"
+        direction
+      else
+        # Surely one of those has to work.  What now?
+        "unknown"
+
   movements:
-    h: "backward character"
-    l: "forward character"
-    k: "backward line"
-    j: "forward line"
-    b: "backward word"
-    e: "forward word"
+    "l": "forward character"
+    "h": "backward character"
+    "j": "forward line"
+    "k": "backward line"
+    "e": "forward word"
+    "b": "backward word"
+    ")": "forward sentence"
+    "(": "backward sentence"
+    "}": "forward paragraph"
+    "{": "backward paragraph"
+    "$": "forward lineboundary"
+    "0": "backward lineboundary"
+    "G": "forward documentboundary"
+    "g": "backward documentboundary"

+    "o": ->
+      selection = window.getSelection()
+      length = selection.toString().length
+      switch @getDirection selection
+        when "forward"
+          selection.collapseToEnd()
+          selection.modify "extend", "backward", "character" for [0...length]
+        when "backward"
+          selection.collapseToStart()
+          selection.modify "extend", "forward", "character" for [0...length]
+
+  # TODO(smblott). What do we do if there is no initial selection?  Or multiple ranges?
   constructor: (options) ->
     @alterMethod = options.alterMethod || "extend"
     super options
@@ -65,11 +110,12 @@
           unless event.metaKey or event.ctrlKey or event.altKey
             keyChar = String.fromCharCode event.charCode
             if @movements[keyChar]
-              @countPrefixTimes =>
-                if "string" == typeof @movements[keyChar]
-                  window.getSelection().modify @alterMethod, @movements[keyChar].split(/\s+/)...
-                else if "function" == typeof @movements[keyChar]
-                  @movements[keyChar]()
+              @runCountPrefixTimes =>
+                switch typeof @movements[keyChar]
+                  when "string"
+                    window.getSelection().modify @alterMethod, @movements[keyChar].split(" ")...
+                  when "function"
+                    @movements[keyChar].call @

 class VisualMode extends Movement
   constructor: (options = {}) ->


Visual/edit modes: escape in visual mode clears selection.

--- content_scripts/mode_visual_edit.coffee
+++ content_scripts/mode_visual_edit.coffee
@@ -111,7 +111,7 @@
     if @options.parentMode
       @options.parentMode.launchSubMode mode, options
     else
-      new mode extend options, initialRangeOnLaunch: @initialRangeOnLaunch
+      new mode options

   # Return the character following (to the right of) the focus, and leave the selection unchanged.  Returns
   # undefined if there is no such character.
@@ -309,11 +309,6 @@
       @runMovementKeyChar @options.immediateMovement, @getCountPrefix()
       return

-    # Track the initial selection range.  We'll restore it if the user exits with Escape.
-    unless @options.parentMode
-      @initialRangeOnLaunch = options.initialRangeOnLaunch ||
-        (if @selection.type == "None" then "None" else @selection.getRangeAt 0)
-
     # This is the main keyboard-event handler for movements and commands.
     @push
       _name: "#{@id}/keypress"
@@ -369,15 +364,6 @@

   exit: (event, target) ->
     super event, target
-
-    unless @options.parentMode
-      # When the user exits via Escape, we reinstall the pre-launch selection (or, if there was none, just
-      # collapse the selection to the current anchor).
-      if @initialRangeOnLaunch? and event?.type == "keydown" and KeyboardUtils.isEscape event
-        if @initialRangeOnLaunch == "None"
-          @collapseSelectionToAnchor()
-        else
-          @setSelectionRange @initialRangeOnLaunch unless @initialRangeOnLaunch == "None"

   # Yank the selection; always exits; either deletes the selection or collapses it; set @yankedText and
   # returns it.


update

update

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/tscholz/knipser.git/'


Update level session schema

Update level session schema

--- app/schemas/models/level_session.coffee
+++ app/schemas/models/level_session.coffee
@@ -142,7 +142,18 @@
       type: 'object'
       additionalProperties:
         type: 'string'
-        format: 'javascript'
+
+  submittedCodeLanguage:
+    type: 'string'
+    default: 'javascript'
+    enum: ['javascript', 'coffeescript', 'clojure', 'lua','python']
+
+  transpiledCode:
+    type: 'object'
+    additionalProperties:
+      type: 'object'
+      additionalProperties:
+        type: 'string'

   isRanking:
     type: 'boolean'


Retain previous filter selections

Retain previous filter selections

--- app/scripts/alchemy/core/filters.coffee
+++ app/scripts/alchemy/core/filters.coffee
@@ -164,16 +164,12 @@
           .attr({"id":"toggle-edges","class":"list-group-item active-label toggle"})
           .html("Toggle Edges")
           .on("click", ->
-            if d3.selectAll(".edge.active")[0].length == 0
+            if d3.selectAll(".edge.hidden")[0].length == 0
                 d3.selectAll(".edge")
-                  .classed({ "inactive": false, "active": true })
-                d3.selectAll(".edgeType")
-                  .classed({ "disabled": false, "active-label": true })
+                  .classed("hidden", true)
             else
                 d3.selectAll(".edge")
-                  .classed({ "inactive": true, "active": false })
-                d3.selectAll(".edgeType")
-                  .classed({"active-label":true, "disabled":false})
+                  .classed("hidden", false)
             )

     #create nodes toggle
@@ -183,16 +179,12 @@
           .attr({"id":"toggle-nodes","class":"list-group-item active-label toggle"})
           .html("Toggle Nodes")
           .on("click", ->
-            if d3.selectAll(".node.active")[0].length == 0
+            if d3.selectAll(".node.hidden")[0].length == 0
                 d3.selectAll(".node:not(.root)")
-                  .classed({ "inactive": false, "active": true })
-                d3.selectAll(".nodeType")
-                  .classed({ "disabled": false, "active-label": true })
+                  .classed("hidden", true)
             else
-                d3.selectAll(".node:not(.root), .edge")
-                  .classed({ "inactive": true, "active": false })
-                d3.selectAll(".nodeType, .edgeType")
-                  .classed({"active-label":false, "disabled":true})
+                d3.selectAll(".node.hidden:not(.root), .edge")
+                  .classed("hidden", false)
             )


:lipstick: Use unless instead of if not

:lipstick: Use unless instead of if not

--- src/package.coffee
+++ src/package.coffee
@@ -371,7 +371,7 @@
   # to minimize the impact on startup time.
   getIncompatibleNativeModules: ->
     localStorageKey = "installed-packages:#{@name}:#{@metadata.version}"
-    if not atom.inDevMode()
+    unless atom.inDevMode()
       try
         {incompatibleNativeModules} = JSON.parse(global.localStorage.getItem(localStorageKey)) ? {}
       return incompatibleNativeModules if incompatibleNativeModules?


flush more frequently to console.log

flush more frequently to console.log

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/bubblegroup/bubblebot.git/'


correclty update open status for popcal

correclty update open status for popcal

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/artsyca/emberui.git/'


Stub in parser library

Stub in parser library

error: pathspec 'src/lib/parse.coffee' did not match any file(s) known to git

--- src/lib/parse.coffee
+++ src/lib/parse.coffee
@@ -0,0 +1,6 @@
+# parse.coffee
+
+exports.parse = {
+
+
+}


Finishes Transform, fixes #1

Finishes Transform, fixes #1

error: pathspec 'spec/Transform.coffee' did not match any file(s) known to git

--- spec/Transform.coffee
+++ spec/Transform.coffee
@@ -0,0 +1,80 @@
+noflo = require 'noflo'
+
+unless noflo.isBrowser()
+  chai = require 'chai' unless chai
+  Transform = require '../components/Transform.coffee'
+else
+  Transform = require 'noflo-color/components/Transform.js'
+
+describe 'Transform component', ->
+  c = null
+  sock_color = null
+  sock_lighten = null
+  sock_darken = null
+  sock_saturate = null
+  sock_desaturate = null
+  sock_whiten = null
+  sock_blacken = null
+  sock_clearer = null
+  sock_opaquer = null
+  sock_rotate = null
+  sock_outcolor = null
+
+  beforeEach ->
+    c = Transform.getComponent()
+    sock_color = noflo.internalSocket.createSocket()
+    sock_lighten = noflo.internalSocket.createSocket()
+    sock_darken = noflo.internalSocket.createSocket()
+    sock_saturate = noflo.internalSocket.createSocket()
+    sock_desaturate = noflo.internalSocket.createSocket()
+    sock_whiten = noflo.internalSocket.createSocket()
+    sock_blacken = noflo.internalSocket.createSocket()
+    sock_clearer = noflo.internalSocket.createSocket()
+    sock_opaquer = noflo.internalSocket.createSocket()
+    sock_rotate = noflo.internalSocket.createSocket()
+    sock_outcolor = noflo.internalSocket.createSocket()
+    c.inPorts.color.attach sock_color
+    c.inPorts.lighten.attach sock_lighten
+    c.inPorts.darken.attach sock_darken
+    c.inPorts.saturate.attach sock_saturate
+    c.inPorts.desaturate.attach sock_desaturate
+    c.inPorts.whiten.attach sock_whiten
+    c.inPorts.blacken.attach sock_blacken
+    c.inPorts.clearer.attach sock_clearer
+    c.inPorts.opaquer.attach sock_opaquer
+    c.inPorts.rotate.attach sock_rotate
+    c.outPorts.color.attach sock_outcolor
+
+  describe 'when instantiated', ->
+    it 'should have ten input ports', ->
+      chai.expect(c.inPorts.color).to.be.an 'object'
+      chai.expect(c.inPorts.lighten).to.be.an 'object'
+      chai.expect(c.inPorts.darken).to.be.an 'object'
+      chai.expect(c.inPorts.saturate).to.be.an 'object'
+      chai.expect(c.inPorts.desaturate).to.be.an 'object'
+      chai.expect(c.inPorts.whiten).to.be.an 'object'
+      chai.expect(c.inPorts.blacken).to.be.an 'object'
+      chai.expect(c.inPorts.clearer).to.be.an 'object'
+      chai.expect(c.inPorts.opaquer).to.be.an 'object'
+      chai.expect(c.inPorts.rotate).to.be.an 'object'
+    it 'should have one output port', ->
+      chai.expect(c.outPorts.color).to.be.an 'object'
+
+  describe 'single color transformation', ->
+    it 'should output a color string', ->
+      sock_outcolor.once 'data', (data) ->
+        chai.expect(data).to.equal 'hsl(100, 50%, 75%)'
+      sock_color.send 'hsl(100, 50%, 50%)'
+      sock_lighten.send 0.5
+
+  describe 'multiple color transformation', ->
+    it 'should output an array of color strings', ->
+      sock_outcolor.once 'data', (data) ->
+        chai.expect(data).to.be.an 'array'
+        chai.expect(data[0]).to.equal 'hsl(100, 50%, 75%)'
+        chai.expect(data[1]).to.equal 'hsl(100, 50%, 75%)'
+      sock_color.send ['hsl(100, 50%, 50%)', 'hsl(100, 50%, 50%)']
+      sock_lighten.send 0.5
+
+
+


Moved check coffee into extra file

Moved check coffee into extra file

error: pathspec 'src/core/check.coffee' did not match any file(s) known to git

--- src/core/check.coffee
+++ src/core/check.coffee
@@ -0,0 +1,56 @@
+do (root = exports ? this) ->
+  'use strict'
+
+  root.check = (variable, checkObject) ->
+
+    stringedVar = {}.toString.call variable
+    typeName = stringedVar.slice(8, stringedVar.length - 1).toLowerCase()
+
+    checkType = (typeString, cb, inverse) ->
+      if inverse
+        cb?(variable) unless typeName is typeString
+      else
+        cb?(variable) if typeName is typeString
+
+      ###
+        Else is a reserved keyword, while CoffeeScript interpolates it correctly,
+        it can only be written as check(...).['else']...
+        check(...).otherwise(...) is a better choice if using plain JavaScript
+      ###
+      unless checkObject
+        result.else = result.otherwise = (cb) -> checkType typeString, cb, !inverse
+        result
+
+    types = (inverse) ->
+      valid: (cb) ->
+        if inverse
+          cb(variable) unless variable?
+        else
+          cb(variable) if variable?
+        @
+      undefined: (cb) -> checkType "undefined", cb, inverse
+      null: (cb) -> checkType "null", cb, inverse
+      string: (cb) -> checkType "string", cb, inverse
+      number: (cb) -> checkType "number", cb, inverse
+      object: (cb) -> checkType "object", cb, inverse
+      array: (cb) -> checkType "array", cb, inverse
+      function: (cb) -> checkType "function", cb, inverse
+
+    if checkObject
+      typeFuncs = types(false)
+      for key, value of checkObject
+        if key.indexOf(',') > -1
+          keyArray = key.split ','
+          typeFuncs[k.trim()](value) for k in keyArray
+        else
+          typeFuncs[key] value
+
+      result = undefined
+    else
+      result = types(false)
+      result.not = types(true)
+
+    result
+
+  # AMDs are awesome :)
+  root.define?('check', [], -> root.check) unless exports?


more aggresive rate limiting i guess

more aggresive rate limiting i guess

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/Mikuia/Mikuia.git/'


add email script

add email script

error: pathspec 'scripts/emails/contestant-8hr-to-go.coffee' did not match any file(s) known to git

--- scripts/emails/contestant-8hr-to-go.coffee
+++ scripts/emails/contestant-8hr-to-go.coffee
@@ -0,0 +1,53 @@
+require 'colors'
+env = require '../../config/env'
+mongoose = require('../../models')(env.mongo_url)
+util = require 'util'
+postageapp = require('postageapp')(env.secrets.postageapp)
+_ = require('underscore')
+async = require('async')
+
+template_name = "contestant_8hr_to_go"
+
+Team = mongoose.model 'Team'
+Person = mongoose.model 'Person'
+
+currentTeam = null
+
+msgTeam = (team, callback) ->
+  currentTeam = team
+  currentTeam.people (err, people) ->
+    return callback(err) if err
+    emailable = (person for person in people when /@/.test(person.email))
+    util.log "Sending '#{template_name}' to '#{team.name}'".yellow
+    async.forEach emailable, msgPerson, callback
+
+msgPerson = (person, callback) ->
+  email = person.email
+  name = person.name or person.slug
+  firstName = name?.split(/\s+/)[0] ? ''
+
+  address = if name
+      "\"#{name}\" <#{email}>"
+    else
+      email
+  util.log "\t -> #{address} (#{firstName})".yellow
+
+  postageapp.sendMessage
+    recipients: address
+    template: template_name
+    variables:
+      team: "#{currentTeam.name}"
+      team_id: "#{currentTeam.id}"
+      slug: "#{currentTeam.slug}"
+    , (args...) ->
+      # console.log "completed sending"
+      # console.log args
+      # callback(args...)
+      callback()
+
+Team.find {}, (err, teams) ->
+  throw err if err
+
+  async.forEachSeries teams, msgTeam, (err) ->
+    util.error(err) if err
+    mongoose.connection.close()


bug fix

bug fix


reload takes a parameter - starting index

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/zagushka/ui-scroll.git/'


don't label playlist items when we don't have to

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/Qbuer/groovebasin.git/'


Deactivate keymaps when deactivating packages

Refs #8130

--- src/package.coffee
+++ src/package.coffee
@@ -410,6 +410,7 @@
     @activationCommandSubscriptions?.dispose()
     @deactivateResources()
     @deactivateConfig()
+    @deactivateKeymaps()
     if @mainActivated
       try
         @mainModule?.deactivate?()


Delete config.cson

Delete config.cson

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/reiesu/dotfiles.git/'


Add request helper

Add request helper

This will handle settings things such as strict SSL or proxy settings
that should be loaded from the npm config files

error: pathspec 'src/request.coffee' did not match any file(s) known to git

--- src/request.coffee
+++ src/request.coffee
@@ -0,0 +1,17 @@
+url = require 'url'
+
+npm = require 'npm'
+request = require 'request'
+
+config = require './config'
+
+module.exports =
+  get: (requestOptions, callback) ->
+    npmOptions =
+      userconfig: config.getUserConfigPath()
+      globalconfig: config.getGlobalConfigPath()
+
+    npm.load npmOptions, ->
+      requestOptions.proxy ?= npm.config.get('https-proxy') or npm.config.get('proxy')
+      requestOptions.strictSSL ?= npm.config.get('strict-ssl')
+      request.get(requestOptions, callback)


Only watch path once

Only watch path once

--- src/directory.coffee
+++ src/directory.coffee
@@ -96,8 +96,9 @@

   # Private:
   subscribeToNativeChangeEvents: ->
-    @watchSubscription = pathWatcher.watch @path, (eventType) =>
-      @emit "contents-changed" if eventType is "change"
+    if @watchSubscription?
+      @watchSubscription = pathWatcher.watch @path, (eventType) =>
+        @emit "contents-changed" if eventType is "change"

   # Private:
   unsubscribeFromNativeChangeEvents: ->


Upcase the vomnibar command name.

Upcase the vomnibar command name.

We'll want to revert this later, to divorce the name of the command from the implementation.

--- background_scripts/commands.coffee
+++ background_scripts/commands.coffee
@@ -91,7 +91,7 @@
        "openCopiedUrlInCurrentTab", "openCopiedUrlInNewTab", "goUp",
        "enterInsertMode", "focusInput",
        "LinkHints.activateMode", "LinkHints.activateModeToOpenInNewTab", "LinkHints.activateModeWithQueue",
-       "vomnibar.activate", "vomnibar.activateWithCurrentUrl", "vomnibar.activateTabSelection",
+       "Vomnibar.activate", "Vomnibar.activateWithCurrentUrl", "Vomnibar.activateTabSelection",
        "goPrevious", "goNext", "nextFrame"]
     findCommands: ["enterFindMode", "performFind", "performBackwardsFind"]
     historyNavigation:
@@ -163,10 +163,10 @@
   "x": "removeTab"
   "X": "restoreTab"

-  "o": "vomnibar.activate"
-  "O": "vomnibar.activateWithCurrentUrl"
-
-  "T": "vomnibar.activateTabSelection"
+  "o": "Vomnibar.activate"
+  "O": "Vomnibar.activateWithCurrentUrl"
+
+  "T": "Vomnibar.activateTabSelection"

   "gf": "nextFrame"

@@ -228,9 +228,9 @@
   removeTab: ["Close current tab", { background: true }]
   restoreTab: ["Restore closed tab", { background: true }]

-  "vomnibar.activate": ["Open URL, bookmark, or history entry"]
-  "vomnibar.activateWithCurrentUrl": ["Open URL, bookmark, history entry, starting with the current URL"]
-  "vomnibar.activateTabSelection": ["Search through your open tabs"]
+  "Vomnibar.activate": ["Open URL, bookmark, or history entry"]
+  "Vomnibar.activateWithCurrentUrl": ["Open URL, bookmark, history entry, starting with the current URL"]
+  "Vomnibar.activateTabSelection": ["Search through your open tabs"]

   nextFrame: ["Cycle forward to the next frame on the page", { background: true, passCountToFunction: true }]


Making sure Model has app context if its Collection does

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/SNASW/rendr.git/'


adding BaseModel.fetchFromCache(id)

adding BaseModel.fetchFromCache(id)


openDoor FE slave fix

openDoor FE slave fix

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/drsdrs/dooropener.git/'


Replace Public with Essential

Replace Public with Essential

Still not sure about the value of this distinction, but let’s blend in.

--- src/text-editor.coffee
+++ src/text-editor.coffee
@@ -14,7 +14,7 @@
 {Directory} = require "pathwatcher"
 GutterContainer = require './gutter-container'

-# Public: This class represents all essential editing state for a single
+# Essential: This class represents all essential editing state for a single
 # {TextBuffer}, including cursor and selection positions, folds, and soft wraps.
 # If you're manipulating the state of an editor, use this class. If you're
 # interested in the visual appearance of editors, use {TextEditorView} instead.
@@ -341,7 +341,7 @@
   onDidInsertText: (callback) ->
     @emitter.on 'did-insert-text', callback

-  # Public: Invoke the given callback after the buffer is saved to disk.
+  # Essential: Invoke the given callback after the buffer is saved to disk.
   #
   # * `callback` {Function} to be called after the buffer is saved.
   #   * `event` {Object} with the following keys:
@@ -351,7 +351,7 @@
   onDidSave: (callback) ->
     @getBuffer().onDidSave(callback)

-  # Public: Invoke the given callback when the editor is destroyed.
+  # Essential: Invoke the given callback when the editor is destroyed.
   #
   # * `callback` {Function} to be called when the editor is destroyed.
   #
@@ -470,7 +470,7 @@
   onDidUpdateMarkers: (callback) ->
     @displayBuffer.onDidUpdateMarkers(callback)

-  # Public: Retrieves the current {TextBuffer}.
+  # Essential: Retrieves the current {TextBuffer}.
   getBuffer: -> @buffer

   # Retrieves the current buffer's URI.
@@ -616,7 +616,7 @@
   # See {TextBuffer::save} for more details.
   save: -> @buffer.save(backup: atom.config.get('editor.backUpBeforeSaving'))

-  # Public: Saves the editor's text buffer as the given path.
+  # Essential: Saves the editor's text buffer as the given path.
   #
   # See {TextBuffer::saveAs} for more details.
   #
@@ -729,7 +729,7 @@
   # {Delegates to: TextBuffer.getEndPosition}
   getEofBufferPosition: -> @buffer.getEndPosition()

-  # Public: Get the {Range} of the paragraph surrounding the most recently added
+  # Essential: Get the {Range} of the paragraph surrounding the most recently added
   # cursor.
   #
   # Returns a {Range}.
@@ -1328,7 +1328,7 @@
       decorationParams.type = 'line-number'
     @displayBuffer.decorateMarker(marker, decorationParams)

-  # Public: Get all the decorations within a screen row range.
+  # Essential: Get all the decorations within a screen row range.
   #
   # * `startScreenRow` the {Number} beginning screen row
   # * `endScreenRow` the {Number} end screen row (inclusive)
@@ -2308,7 +2308,7 @@
   #     * `replace` Call this {Function} with a {String} to replace the match.
   scan: (regex, iterator) -> @buffer.scan(regex, iterator)

-  # Public: Scan regular expression matches in a given range, calling the given
+  # Essential: Scan regular expression matches in a given range, calling the given
   # iterator function on each match.
   #
   # * `regex` A {RegExp} to search for.
@@ -2322,7 +2322,7 @@
   #   * `replace` Call this {Function} with a {String} to replace the match.
   scanInBufferRange: (regex, range, iterator) -> @buffer.scanInRange(regex, range, iterator)

-  # Public: Scan regular expression matches in a given range in reverse order,
+  # Essential: Scan regular expression matches in a given range in reverse order,
   # calling the given iterator function on each match.
   #
   # * `regex` A {RegExp} to search for.
@@ -2434,7 +2434,7 @@
   # Returns a {Boolean}.
   toggleSoftWrapped: -> @setSoftWrapped(not @isSoftWrapped())

-  # Public: Gets the column at which column will soft wrap
+  # Essential: Gets the column at which column will soft wrap
   getSoftWrapColumn: -> @displayBuffer.getSoftWrapColumn()

   ###
@@ -2669,7 +2669,7 @@
       @emit('did-insert-text', didInsertEvent) if includeDeprecatedAPIs
       @emitter.emit 'did-insert-text', didInsertEvent

-  # Public: For each selection, if the selection is empty, cut all characters
+  # Essential: For each selection, if the selection is empty, cut all characters
   # of the containing line following the cursor. Otherwise cut the selected
   # text.
   cutToEndOfLine: ->
@@ -2821,7 +2821,7 @@
   Section: Gutters
   ###

-  # Public: Creates and returns a {Gutter}.
+  # Essential: Creates and returns a {Gutter}.
   # * `options` An {Object} with the following fields:
   #   * `name` (required) A unique {String} to identify this gutter.
   #   * `priority` (optional) A {Number} that determines stacking order between
@@ -2832,11 +2832,11 @@
   addGutter: (options) ->
     @gutterContainer.addGutter(options)

-  # Public: Returns the {Array} of all gutters on this editor.
+  # Essential: Returns the {Array} of all gutters on this editor.
   getGutters: ->
     @gutterContainer.getGutters()

-  # Public: Returns the gutter with the given name, or null if it doesn't exist.
+  # Essential: Returns the gutter with the given name, or null if it doesn't exist.
   gutterWithName: (name) ->
     @gutterContainer.gutterWithName(name)

@@ -2940,13 +2940,13 @@
   Section: TextEditor Rendering
   ###

-  # Public: Retrieves the greyed out placeholder of a mini editor.
+  # Essential: Retrieves the greyed out placeholder of a mini editor.
   #
   # Returns a {String}.
   getPlaceholderText: ->
     @placeholderText

-  # Public: Set the greyed out placeholder of a mini editor. Placeholder text
+  # Essential: Set the greyed out placeholder of a mini editor. Placeholder text
   # will be displayed when the editor has no content.
   #
   # * `placeholderText` {String} text that is displayed when the editor has no content.


Fix log origin.

Fix log origin.

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/philbooth/GitHubReminder.git/'


Tooltip - Explain why we are creating a custom tooltip and not using Atom's tooltip manager.

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/Adirelle/atom-autocomplete-php.git/'


Close #11

Close #11

We at least have a point of reference in case future bugs arise.

error: pathspec 'test/files/hmax.iced' did not match any file(s) known to git

--- test/files/hmax.iced
+++ test/files/hmax.iced
@@ -0,0 +1,25 @@
+
+{data} = require '../fixed-data/hmax'
+{sign} = require '../../lib/hmax'
+{WordArray} = require '../../lib/wordarray'
+
+test_vector = (T,v, i) ->
+  key = WordArray.from_hex v.key
+  input = WordArray.from_hex v.msg
+  sig = sign { key, input }
+  T.equal v.sig, sig.to_hex(), "Test vector #{i} checks out"
+  if key.sigBytes > 0
+    key.words[0]++
+    sig = sign { key, input }
+    T.assert not (sig.to_hex() is v.sig), "test vector #{i} fails key mangling"
+    key.words[0]--
+  if input.sigBytes > 0
+    input.words[0]++
+    sig = sign { key, input }
+    T.assert not (sig.to_hex() is v.sig), "test vector #{i} fails input mangling"
+    input.words[0]--
+
+exports.test_vectors = (T,cb) ->
+  for v,i in data
+    test_vector T, v, i
+  cb()


Do a better job imitating the old SpacePen-based editor

--- src/react-editor-view.coffee
+++ src/react-editor-view.coffee
@@ -4,14 +4,20 @@

 module.exports =
 class ReactEditorView extends View
-  @content: -> @div class: 'react-wrapper'
+  @content: -> @div class: 'editor react-wrapper'

   constructor: (@editor) ->
     super

+  getEditor: -> @editor
+
   afterAttach: (onDom) ->
     return unless onDom
+    @attached = true
     @component = React.renderComponent(EditorComponent({@editor}), @element)
+    @trigger 'editor:attached', [this]

   beforeDetach: ->
     React.unmountComponentAtNode(@element)
+    @attached = false
+    @trigger 'editor:detached', this


book base

book base

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/Bit18/cyoab_modular_repo.git/'


Revert "chore(Gruntfile.coffee): FF 22 is most current"
Selenium doesn't support FF 22 yet

This reverts commit d8fb57344be0dd37c7880d2b622e7748ad3561b7.

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -112,7 +112,7 @@
         {
             browserName: 'firefox'
             platform: 'Windows 7'
-            version: '22'
+            version: '21'
         }
     ]


Highly illogical.

Highly illogical.

error: pathspec 'src/scripts/illogical.coffee' did not match any file(s) known to git

--- src/scripts/illogical.coffee
+++ src/scripts/illogical.coffee
@@ -0,0 +1,19 @@
+# Highly illogical.
+#
+# illogical - Display an example of illogicality.
+#
+
+vulcans = [
+  "http://www.katzy.dsl.pipex.com/Smileys/illogical.gif",
+  "http://icanhascheezburger.files.wordpress.com/2010/08/e95f76c6-469b-486e-9d18-b2c600ff7ab6.jpg",
+  "http://fc01.deviantart.net/fs46/i/2009/191/d/6/Spock_Finds_You_Illogical_by_densethemoose.jpg",
+  "http://cache.io9.com/assets/images/8/2008/11/medium_vulcan-cat-is-logical.jpg",
+  "http://roflrazzi.files.wordpress.com/2011/01/funny-celebrity-pictures-karaoke.jpg",
+  "http://i13.photobucket.com/albums/a292/macota/MCCOYGOBLET.jpg",
+  "http://spike.mtvnimages.com/images/import/blog//1/8/7/5/1875583/200905/1242167094687.jpg",
+  "http://randomoverload.com/wp-content/uploads/2010/12/fc5558bae4issors.jpg.jpg"
+]
+
+module.exports = (robot) ->
+  robot.hear /.*(illogical).*/i, (msg) ->
+    msg.send msg.random vulcans


Support for managing scalarium apps and couds

error: pathspec 'src/scripts/scalarium.coffee' did not match any file(s) known to git

--- src/scripts/scalarium.coffee
+++ src/scripts/scalarium.coffee
@@ -0,0 +1,81 @@
+# Interact with Scalarium cloud hosting
+#
+# The following variables are requiered
+#   HUBOT_SCALARIUM_TOKEN: for authenticating the requests (see https://manage.scalarium.com/users/<user-id>/api)
+#
+# hubot scalarium list (apps|clouds) - Lists all applications/clouds on scalarium.
+# hubot scalarium deploy <app id> <message> - Triggers an deployment of *app id* with *message*.
+
+###
+Written by Ole 'nesQuick' Michaelis (Ole.Michaelis@googlemail.com)
+Follow me on Twitter (@CodeStars)
+Digital Pioneers N.V. - WE ARE HIRING (http://digitalpioneers.de/jobs)
+###
+
+https = require 'https'
+token = process.env.HUBOT_SCALARIUM_TOKEN
+
+class ScalariumClient
+
+  constructor: (@https, @token) ->
+
+  getApplications: (cb) ->
+    @request '/applications', 'GET', {}, cb
+
+  getClouds: (cb) ->
+    @request '/clouds', 'GET', {}, cb
+
+  deploy: (appId, msg, cb, finishedCb) ->
+    that = this
+    @request "/applications/#{appId}/deploy", 'POST', {command: 'deploy', comment: "Hubot deploy - #{msg}"}, (result) ->
+      that.registerRunningDeploy(result, finishedCb)
+      cb result
+
+  registerRunningDeploy: (deploy, cb) ->
+    that = this
+    intervalId = setInterval () ->
+      that.request "/applications/#{deploy.application_id}/deployments/#{deploy.id}", 'GET', {}, (result) ->
+        if result.status isnt 'running'
+          cb result
+          clearInterval intervalId
+    , 10000
+
+  request: (path, method, body, cb) ->
+    options =
+      host: 'manage.scalarium.com'
+      method: method
+      path: "/api#{path}"
+      headers:
+        'X-Scalarium-Token': @token
+        Accept: 'application/vnd.scalarium-v1+json'
+    req = @https.request options, (res) ->
+      data = ''
+      res.on('data', (chunk) -> data = "#{data}#{chunk}")
+      res.on('end', () -> cb JSON.parse data)
+    req.write JSON.stringify body
+    req.end()
+
+client = new ScalariumClient https, token
+
+module.exports = (robot)->
+
+  robot.respond /scalarium list apps/i, (message)->
+    client.getApplications (apps) ->
+      for app in apps
+        message.send "#{app.name} - #{app.id}"
+
+  robot.respond /scalarium list clouds/i, (message)->
+    client.getClouds (clouds) ->
+      for cloud in clouds
+        message.send "#{cloud.name} - #{cloud.id}"
+
+  robot.respond /scalarium deploy ([0-9a-f]+) (.+)$/i, (message)->
+    client.deploy message.match[1], message.match[2], (deploy) ->
+      message.send "Yes Sir! Deployment triggered with id #{deploy.id}. Will drop a note when it's done."
+    , (finished) ->
+      success = finished.status == 'successful'
+      message.send """#{if success then 'Success' else 'FAIL! FAIL! FAIL!!'}! Your deployment "#{finished.comment}" with id #{finished.id} #{if success then 'is done' else 'failed'}."""
+
+  robot.scalarium =
+    getApplications: client.getApplications
+    getClouds: client.getClouds


add konnector fetching from module files

error: pathspec 'server/lib/konnector_hash.coffee' did not match any file(s) known to git

--- server/lib/konnector_hash.coffee
+++ server/lib/konnector_hash.coffee
@@ -0,0 +1,24 @@
+fs = require 'fs'
+path = require 'path'
+
+currentPath = path.dirname fs.realpathSync __filename
+modulesPath = path.join currentPath, '..', 'konnectors'
+
+isCoffeeFile = (fileName) ->
+    extension = fileName.split('.')[1]
+    firstChar = fileName[0]
+    firstChar isnt '.' and extension is 'coffee'
+
+getKonnectorModules = ->
+    modules = {}
+    moduleFiles = fs.readdirSync modulesPath
+    for moduleFile in moduleFiles
+        if isCoffeeFile moduleFile
+            name = moduleFile.split('.')[0]
+            modulePath = "../konnectors/#{name}"
+            modules[name] = require modulePath
+    modules
+
+hash = getKonnectorModules()
+
+module.exports = hash


Improve code in src/commands/new a bit.

Improve code in src/commands/new a bit.

--- src/commands/create.coffee
+++ src/commands/create.coffee
@@ -33,8 +33,16 @@
         return logger.error "Skeleton '#{skeletonPath}' doesn't exist"
       copyDirectory skeletonPath

-cloneSkeleton = (address, rootPath, isGitHubUrl, callback) ->
-  URL = if isGitHubUrl
+# Clones skeleton from URI.
+#
+# address     - String, URI. https:, github: or git: may be used.
+# rootPath    - String, directory to which skeleton files will be copied.
+# callback    - Function.
+#
+# Returns nothing.
+cloneSkeleton = (address, rootPath, callback) ->
+  gitHubRe = /(gh|github)\:\/\//
+  URL = if gitHubRe.test address
     "git://github.com/#{address.replace('github://', '')}.git"
   else
     address
@@ -48,14 +56,11 @@

 module.exports = create = (options, callback = (->)) ->
   {rootPath, skeleton} = options
-  re = /(?:https?|git(hub)?)(?::\/\/|@)/
+  uriRe = /(?:https?|git(hub)?|gh)(?::\/\/|@)/

   fs_utils.exists (sysPath.join rootPath, 'package.json'), (exists) ->
     if exists
       return logger.error "Directory '#{rootPath}' is already an npm project"
-    isGitUrl = skeleton?.match re
-    isGitHubUrl = Boolean isGitUrl?[1]
-    if isGitUrl
-      cloneSkeleton skeleton, rootPath, isGitHubUrl, callback
-    else
-      copySkeleton skeleton, rootPath, callback
+    isGitUri = skeleton and uriRe.test skeleton
+    get = if isGitUri then cloneSkeleton else copySkeleton
+    get skeleton, rootPath, callback


Fix limited fair partnership status display

--- apps/artwork_2/components/commercial/query.coffee
+++ apps/artwork_2/components/commercial/query.coffee
@@ -15,6 +15,7 @@
       name
       type
       is_pre_qualify
+      is_limited_fair_partner
       contact_message
     }
     fair {


Added method for getting UTM offset distance from a GeoPoint origin.

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/urbanetic/meteor-atlas-util.git/'


changes

changes

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/sgehrman/distantfuture.git/'


Handle null incision date time better

Handle null incision date time better

--- app/assets/javascripts/procedure.js.coffee
+++ app/assets/javascripts/procedure.js.coffee
@@ -39,4 +39,8 @@
   ###*
   @returns {Date} The actual or intended start of an incision.
   ###
-  incisionDatetime: -> hQuery.dateFromUtcSeconds @json['incisionDatetime']+  incisionDatetime: ->
+    if @json['incisionDatetime']
+      hQuery.dateFromUtcSeconds @json['incisionDatetime']
+    else
+      null


Search completion; add support for Google Maps completion.

--- background_scripts/completion_engines.coffee
+++ background_scripts/completion_engines.coffee
@@ -34,16 +34,38 @@

 class Google extends GoogleXMLRegexpEngine
   # Example search URL: http://www.google.com/search?q=%s
-  constructor: ->
-    super [
+  constructor: (regexps = null) ->
+    regexps ?= [
       # We match the major English-speaking TLDs.
       new RegExp "^https?://[a-z]+\.google\.(com|ie|co\.uk|ca|com\.au)/"
       new RegExp "localhost/cgi-bin/booky" # Only for smblott.
       ]
+    super regexps

   getUrl: (queryTerms) ->
     Utils.createSearchUrl queryTerms,
       "http://suggestqueries.google.com/complete/search?ss_protocol=legace&client=toolbar&q=%s"
+
+# A wrapper class for Google completions.  This adds prefix terms to the query, and strips those terms from
+# the resulting suggestions.  For example, for Google Maps, we add "map of" as a prefix, then strip "map of"
+# from the resulting suggestions.
+class GoogleWithPrefix
+  constructor: (prefix, args...) ->
+    @engine = new Google args...
+    @prefix = "#{prefix.trim()} "
+    @queryTerms = @prefix.split /\s+/
+  match: (args...) -> @engine.match args...
+  getUrl: (queryTerms) -> @engine.getUrl [ @queryTerms..., queryTerms... ]
+  parse: (xhr) ->
+    @engine.parse(xhr)
+      .filter (suggestion) => suggestion.startsWith @prefix
+      .map (suggestion) => suggestion[@prefix.length..].ltrim()
+
+# For Google Maps, we add the prefix "map of" to the query, and send it to Google's general search engine,
+# then strip "map of" from the resulting suggestions.
+class GoogleMaps extends GoogleWithPrefix
+  # Example search URL: https://www.google.com/maps?q=%s
+  constructor: -> super "map of", [ new RegExp "https?://[a-z]+\.google\.(com|ie|co\.uk|ca|com\.au)/maps" ]

 class Youtube extends GoogleXMLRegexpEngine
   # Example search URL: http://www.youtube.com/results?search_query=%s
@@ -66,19 +88,6 @@

   parse: (xhr) ->
     JSON.parse(xhr.responseText)[1]
-
-## Does not work...
-## class GoogleMaps extends RegexpEngine
-##   # Example search URL: https://www.google.com/maps/search/%s
-##   constructor: ->
-##     super [ new RegExp "^https?://www\.google\.com/maps/search/" ]
-##
-##   getUrl: (queryTerms) ->
-##     "https://www.google.com/s?tbm=map&fp=1&gs_ri=maps&source=hp&suggest=p&authuser=0&hl=en&pf=p&tch=1&ech=2&q=#{Utils.createSearchQuery queryTerms}"
-##
-##   parse: (xhr) ->
-##     data = JSON.parse xhr.responseText
-##     []

 class Bing extends RegexpEngine
   # Example search URL: https://www.bing.com/search?q=%s
@@ -120,6 +129,7 @@
 # Note: Order matters here.
 CompletionEngines = [
   Youtube
+  GoogleMaps
   Google
   DuckDuckGo
   Wikipedia


:lipstick: Follow whitespace conventions for specs

--- spec/app/text-buffer-spec.coffee
+++ spec/app/text-buffer-spec.coffee
@@ -1236,12 +1236,14 @@
         data = buffer.serialize()
         expect(data.path).toBe(buffer.getPath())
         expect(data.text).toBeFalsy()
+
     describe "when the buffer has unsaved changes", ->
       it "stores the file path and the changed buffer text", ->
         buffer.setText("abc")
         data = buffer.serialize()
         expect(data.path).toBe(buffer.getPath())
         expect(data.text).toBe("abc")
+
     describe "when the buffer has never been saved", ->
       it "stores the changed buffer text", ->
         buffer.release()
@@ -1261,12 +1263,14 @@
       path = buffer.getPath()
       reloadBuffer()
       expect(buffer.getPath()).toBe(path)
+
     it "loads the stored changes if the file was modified", ->
       path = buffer.getPath()
       buffer.setText("abc")
       reloadBuffer()
       expect(buffer.getPath()).toBe(path)
       expect(buffer.getText()).toBe("abc")
+
     it "loads the stored changes if the file was never saved", ->
       buffer.release()
       buffer = new Buffer()


Make debug mode obvious.

Make debug mode obvious.

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/damianb/crimson.git/'


Compare against realpath of HOME

Compare against realpath of HOME

--- spec/stdlib/fs-utils-spec.coffee
+++ spec/stdlib/fs-utils-spec.coffee
@@ -1,4 +1,5 @@
 fsUtils = require 'fs-utils'
+fs = require 'fs'
 path = require 'path'

 describe "fsUtils", ->
@@ -127,6 +128,6 @@

   describe ".absolute(relativePath)", ->
     it "converts a leading ~ segment to the HOME directory", ->
-      expect(fsUtils.absolute('~')).toBe process.env.HOME
-      expect(fsUtils.absolute(path.join('~', 'a'))).toBe path.join(process.env.HOME, 'a')
+      expect(fsUtils.absolute('~')).toBe fs.realpathSync(process.env.HOME)
+      expect(fsUtils.absolute(path.join('~', 'a'))).toBe path.join(fs.realpathSync(process.env.HOME), 'a')
       expect(fsUtils.absolute('~test')).toBe '~test'


Write specs for Walrus.Utils#trim

Write specs for Walrus.Utils#trim

error: pathspec 'test/utils_spec.coffee' did not match any file(s) known to git

--- test/utils_spec.coffee
+++ test/utils_spec.coffee
@@ -0,0 +1,29 @@
+Walrus = require '../bin/walrus'
+should = require 'should'
+
+describe 'Walrus.Utils', ->
+
+  it 'should be defined', -> should.exist Walrus.Utils
+
+  describe '#trim', ->
+
+    it 'should be defined', -> should.exist Walrus.Utils.trim
+
+    it 'should trim leading whitespace', ->
+      Walrus.Utils.trim( '   foo' ).should.equal 'foo'
+
+    it 'should trim trailing whitespace', ->
+      Walrus.Utils.trim( 'foo   ' ).should.equal 'foo'
+
+    it 'should trim leading and trailing whitespace', ->
+      Walrus.Utils.trim( '  foo  ' ).should.equal 'foo'
+
+    it 'should leave internal whitespace alone', ->
+      Walrus.Utils.trim( 'foo   bar' ).should.equal 'foo   bar'
+
+    it 'should trim newlines', ->
+      Walrus.Utils.trim( """
+
+      foo
+
+      """ ).should.equal 'foo'


:shit:

:shit:

--- src/atom/vim-mode/motions.coffee
+++ src/atom/vim-mode/motions.coffee
@@ -17,7 +17,6 @@
 class MoveRight extends Motion
   execute: ->
     {column, row} = @editor.getCursorPosition()
-    console.log @editor.getCurrentLine().length
     isOnLastCharachter = @editor.getCursorColumn() == @editor.getCurrentLine().length - 1
     @editor.moveCursorRight() unless isOnLastCharachter


Fix mysterious click bug by removing unused save controls view

--- components/artwork_item/views/sale_artwork.coffee
+++ components/artwork_item/views/sale_artwork.coffee
@@ -18,14 +18,6 @@

   initialize: (options = {}) ->
     { @currentUser, @sale, @artworkCollection } = options
-
-    saveView = new SaveControls
-      artworkCollection: @artworkCollection
-      el: @$el
-      model: @model
-
-    saveView.analyticsRemoveMessage = @analyticsRemoveMessage
-    saveView.analyticsSaveMessage = @analyticsSaveMessage

     if @sale?.isAuction()
       if @sale.has 'auctionState'


bullshit

bullshit

error: pathspec 'scripts/bullshit.coffee' did not match any file(s) known to git

--- scripts/bullshit.coffee
+++ scripts/bullshit.coffee
@@ -0,0 +1,6 @@
+# bullshit
+
+module.exports = (robot) ->
+  robot.hear /(that's|such|what) bullshit/i, (msg) ->
+    msg.send "http://s.mlkshk.com/r/13DRV.gif"
+


add User object to Response context

add User object to Response context

 On branch master

	modified:   nodejs_server/middleware/session.coffee

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/FireworksProject/platform.y.git/'


DO IT LIVE

DO IT LIVE

error: pathspec 'scripts/doitlive.coffee' did not match any file(s) known to git

--- scripts/doitlive.coffee
+++ scripts/doitlive.coffee
@@ -0,0 +1,6 @@
+# FUCK IT
+
+module.exports = (robot) ->
+  robot.hear /do it live/i, (msg) ->
+    msg.send "http://media.wnyc.org/media/photologue/photos/cache/Do%20It%20Live_medium_image.jpg"
+


Kill trailing whitespace

Kill trailing whitespace

--- build/tasks/create-installer.coffee
+++ build/tasks/create-installer.coffee
@@ -17,7 +17,7 @@

     packageInfo = grunt.file.readJSON(path.join(atomDir, 'resources', 'app', 'package.json'))
     inputTemplate = grunt.file.read(path.join('build', 'windows', 'atom.nuspec.erb'))
-
+
     # NB: Build server has some sort of stamp on the version number
     packageInfo.version = packageInfo.version.replace(/-.*$/, '')


Only call _.isEqual when needed

Only call _.isEqual when needed

Invert the logic to return if all are equal so we can fall through
on the first false call so only the minimum isEqual calls are made.

Also perform the boolean check first instead of last since it
cheaper than a Point comparison.

This showed up in the profile when profiling moving the cursor.

--- src/display-buffer-marker.coffee
+++ src/display-buffer-marker.coffee
@@ -185,13 +185,11 @@
     newTailScreenPosition = @getTailScreenPosition()
     isValid = @isValid()

-    changed = false
-    changed = true unless _.isEqual(newHeadBufferPosition, @oldHeadBufferPosition)
-    changed = true unless _.isEqual(newHeadScreenPosition, @oldHeadScreenPosition)
-    changed = true unless _.isEqual(newTailBufferPosition, @oldTailBufferPosition)
-    changed = true unless _.isEqual(newTailScreenPosition, @oldTailScreenPosition)
-    changed = true unless _.isEqual(isValid, @wasValid)
-    return unless changed
+    return if _.isEqual(isValid, @wasValid) and
+      _.isEqual(newHeadBufferPosition, @oldHeadBufferPosition) and
+      _.isEqual(newHeadScreenPosition, @oldHeadScreenPosition) and
+      _.isEqual(newTailBufferPosition, @oldTailBufferPosition) and
+      _.isEqual(newTailScreenPosition, @oldTailScreenPosition)

     @emit 'changed', {
       @oldHeadScreenPosition, newHeadScreenPosition,


fix broken drawer code

fix broken drawer code

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/AppGyver/supersonic.git/'


resurrect empty boot.coffee

resurrect empty boot.coffee

error: pathspec 'app/boot.coffee' did not match any file(s) known to git

--- app/boot.coffee
+++ app/boot.coffee
@@ -0,0 +1,19 @@
+null
+
+### @ngInject ###
+global.cobudgetApp.run ($rootScope, Records, $q, $location) ->
+
+  # userValidatedDeferred = $q.defer()
+
+  # global.cobudgetApp.userValidated = userValidatedDeferred.promise
+
+  # $rootScope.$on 'auth:validation-success', (user) ->
+  #   console.log('validation-success')
+  #   global.cobudgetApp.currentUserId = user.id
+  #   userValidatedDeferred.resolve()
+
+  # $rootScope.$on 'auth:validation-error', () ->
+  #   console.log('validation-error')
+  #   $location.path('/')
+  #   Toast.show('Please log in to continue')
+  #   userValidatedDeferred.reject()


:fire: Remove leftover code

:fire: Remove leftover code

--- src/text-editor-presenter.coffee
+++ src/text-editor-presenter.coffee
@@ -943,7 +943,6 @@
       @emitDidUpdateState()

   setScrollingTileId: (tileId) ->
-    tileId = tileId.toString()
     if @scrollingTileId isnt tileId
       @scrollingTileId = tileId
       @didStartScrolling()


Now check permission, not role to show menu

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/MicrosmartSA/ubiquity-ui.git/'


adding debug info for ie8

adding debug info for ie8

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/SNASW/rendr.git/'


incl. tests

incl. tests

error: pathspec 'source/pubsub_http/node_list_test.coffee' did not match any file(s) known to git

--- source/pubsub_http/node_list_test.coffee
+++ source/pubsub_http/node_list_test.coffee
@@ -0,0 +1,80 @@
+assert = require 'assert'
+sinon  = require 'sinon'
+require( './node_list.coffee' )
+
+describe "pubsub.http node_list", ->
+
+    describe "starting with an empty list", ->
+
+        beforeEach ->
+            @list = new PubSub.NodeList
+
+        it "will return no element, if empty", ->
+            assert.strictEqual null, @list.get { a: '1', b: '2' }
+            assert.strictEqual null, @list.get { abc: '1' }
+            assert.strictEqual null, @list.get { a: '1', b: '2', c: 'foo' }
+
+        it "inserting an element will not cause an problems", ->
+            @list.insert { a: '1', b: '2' }, 1
+            @list.insert { abc: '1' }, { a: 1, b: true, c: false }
+
+        it "will throw an exception if using keys, with no string values", ->
+            for test in [ { a: 1 }, { abc: 'a', c:null }, { a: 'b', b: false }, { c: 1.4 } ]
+                assert.throws =>
+                    @list.insert test, 1
+                , 'node name must contain only strings'
+
+        it "will throw if key is not an object",->
+            assert.throws =>
+                @list.insert 1, 1
+            , 'node name must be an object'
+
+        it "will throw if node name is empty", ->
+            assert.throws =>
+                @list.insert {}, 1
+            , 'node name must not be empty'
+
+        it "an inserted element will be found", ->
+            @list.insert { a: 'a', b: '3' }, 42
+            assert.strictEqual 42, @list.get { a: 'a', b: '3' }
+            assert.strictEqual 42, @list.get { b: '3', a: 'a' }
+
+    describe "starting with a not empty list", ->
+
+        to_be_found = null
+
+        beforeEach ->
+            @list = new PubSub.NodeList
+
+            to_be_found = [
+                { key: { v: "1", b: "a" }, value: 22 }
+                { key: { v: "2", b: "a" }, value: 23 }
+                { key: { v: "1", b: "c" }, value: 24 }
+                { key: { v: "1" }, value: 25 }
+                { key: { b: "a" }, value: 26 }
+            ]
+
+            @list.insert( ele.key, ele.value ) for ele in to_be_found
+
+        it "different elements can be stored in the list and found again", ->
+            for ele in to_be_found
+                 assert.strictEqual @list.get( ele.key ), ele.value
+
+        it "if one element removed, it will not be able to find all othere",->
+            assert @list.remove( to_be_found[ 3 ].key )
+            to_be_found.splice 3, 1
+
+            for ele in to_be_found
+                 assert.strictEqual @list.get( ele.key ), ele.value
+
+        it "if one element is removed, it should not be in the list", ->
+            assert @list.remove( to_be_found[ 3 ].key )
+            assert.strictEqual @list.get( to_be_found[ 3 ].key ), null
+
+        it "should contain the new value, if updated", ->
+            @list.insert to_be_found[ 0 ].key, 44
+            to_be_found[ 0 ].value = 44
+
+            for ele in to_be_found
+                 assert.strictEqual @list.get( ele.key ), ele.value
+


auth: RegExp#test already returns a bool

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/andrewjcasal/koding.git/'


+ 消息分发

+ 消息分发

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/hohogpb/qqbot.git/'


setting code fires the change event, so clear dirty after

--- src/atom/editor.coffee
+++ src/atom/editor.coffee
@@ -79,7 +79,6 @@
       return false if @url

     @url = url
-    @dirty = false

     code = if @url then fs.read @url else ''
     session = @ace.getSession()
@@ -90,6 +89,7 @@
     @setModeForSession session

     window.setTitle @title()
+    @dirty = false

     true


Scroll page last because other actions may change page height

And boot first to avoid triggering anything when scrolling to target post.

--- resources/assets/coffee/_classes/forum.coffee
+++ resources/assets/coffee/_classes/forum.coffee
@@ -24,11 +24,18 @@
   loadMoreLinks: document.getElementsByClassName('js-forum-posts-show-more')

   boot: =>
-    @initialScrollTo()
     @refreshCounter()
     @refreshLoadMoreLinks()

+    # Scroll last because other actions may change page's height.
+    @initialScrollTo()
+
+
   constructor: ->
+    # `boot` is called first to avoid triggering anything when scrolling to
+    # target post.
+    @boot()
+
     $(window).on 'throttled-scroll', @refreshCounter

     $(document).on 'ready page:load osu:page:change', @boot
@@ -38,8 +45,6 @@
     $(document).on 'submit', '.js-forum-posts-jump-to', @jumpToSubmit

     $.subscribe 'stickyHeader', @stickHeader
-
-    @boot()

   totalPosts: =>


Clean up ajax query logic

Clean up ajax query logic

--- resources/assets/coffee/react/beatmaps.coffee
+++ resources/assets/coffee/react/beatmaps.coffee
@@ -74,6 +74,9 @@
       when 'r' then 'rank'
       when 'q' then 'query'

+  buildSearchQuery: (query) =>
+    $.extend {'q': query}, @getFilterState(), @getSortState()
+
   search: =>
     searchText = $('#searchbox').val()?.trim()
     @showLoader()
@@ -99,23 +102,24 @@
       if !@state.just_restored
         history.pushState(@state, "¯\_(ツ)_/¯", "/beatmaps/?#{params.join('&')}")

-      $.ajax(@state.paging.url,
+      $.ajax @state.paging.url,
         method: 'get'
         dataType: 'json'
-        data: $.extend({'q': searchText}, @getFilterState(), @getSortState())).done ((data) ->
-          more = data['data'].length > 10
-          @setState
-            beatmaps: data['data']
-            query: searchText
-            paging:
-              page: 1
-              url: @state.paging.url
-              loading: false
-              more: more
-            just_restored: false
-            loading: false, ->
-              $(document).trigger 'beatmap:search:done'
-      ).bind(this)
+        data: @buildSearchQuery(searchText)
+      .done (data) =>
+        newState =
+          beatmaps: data['data']
+          query: searchText
+          paging:
+            page: 1
+            url: @state.paging.url
+            loading: false
+            more: data['data'].length > 10
+          just_restored: false
+          loading: false
+
+        @setState newState, ->
+            $(document).trigger 'beatmap:search:done'

   loadMore: =>
     if @state.loading or @state.paging.loading or !@state.paging.more
@@ -128,20 +132,21 @@

     searchText = $('#searchbox').val()?.trim()

-    $.ajax(@state.paging.url,
+    $.ajax @state.paging.url,
       method: 'get'
       dataType: 'json'
-      data: $.extend({'q': searchText}, @getFilterState(), @getSortState(),
-        'page': @state.paging.page + 1)).done ((data) =>
-          more = data['data'].length > 10
-          @setState
-            beatmaps: @state.beatmaps.concat(data['data'])
-            paging:
-              page: @state.paging.page + (if more then 1 else 0)
-              url: @state.paging.url
-              more: more
-            loading: false
-    ).bind(this)
+      data: $.extend(@buildSearchQuery(searchText), 'page': @state.paging.page + 1)
+    .done (data) =>
+      more = data['data'].length > 10
+      newState =
+        beatmaps: @state.beatmaps.concat(data['data'])
+        paging:
+          page: @state.paging.page + (if more then 1 else 0)
+          url: @state.paging.url
+          more: more
+        loading: false
+
+      @setState newState

   showLoader: =>
     @setState loading: true


Added more methods to mongo

Added more methods to mongo

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/renanhangai/servitude.git/'


:lipstick: Use this instead of @

:lipstick: Use this instead of @

--- src/cursor-view.coffee
+++ src/cursor-view.coffee
@@ -16,7 +16,7 @@
     @cursorViews ?= []
     @cursorViews.push(cursorView)
     if @cursorViews.length is 1
-      @blinkInterval = setInterval(@blinkCursors.bind(@), @blinkPeriod / 2)
+      @blinkInterval = setInterval(@blinkCursors.bind(this), @blinkPeriod / 2)

   @stopBlinking: (cursorView) ->
     cursorView[0].classList.remove('blink-off')


use default for toobusy

use default for toobusy

--- lib/getThrottles.coffee
+++ lib/getThrottles.coffee
@@ -5,7 +5,7 @@

 tokenthrottle = require 'tokenthrottle'

-require('toobusy').maxLag 40
+require('toobusy')
 # if config.redis?.host
 #   redis = require './redis'
 #   tokenthrottle = require 'tokenthrottle-redis'


add otherLoginServices

add otherLoginServices

--- shared/start.coffee
+++ shared/start.coffee
@@ -15,6 +15,10 @@
 Handlebars.registerHelper 'signupClass', ->
   if Accounts.oauth && Accounts.oauth.serviceNames().length > 0
     "collapse"
+
+Handlebars.registerHelper 'otherLoginServices', ->
+  Accounts.oauth &&
+  Accounts.oauth.serviceNames().length > 0

 Handlebars.registerHelper 'loginServices', ->
   Accounts.oauth.serviceNames()


Make coffeelint happy

Make coffeelint happy

* Restructured ReaderRdy tests so that long descriptive test messages
don't wrap lines.

--- test/readerrdy_test.coffee
+++ test/readerrdy_test.coffee
@@ -132,10 +132,12 @@

     msg.on Message.RESPOND, (responseType, wireData) =>
       if responseType is Message.FINISH
-        StateChangeLogger.log 'NSQDConnection', null, @conn.localPort, 'msg finished'
+        StateChangeLogger.log 'NSQDConnection', null, @conn.localPort,
+          'msg finished'
         @emit NSQDConnection.FINISHED
       else if responseType is Message.REQUEUE
-        StateChangeLogger.log 'NSQDConnection', null, @conn.localPort, 'msg requeued'
+        StateChangeLogger.log 'NSQDConnection', null, @conn.localPort,
+          'msg requeued'
         @emit NSQDConnection.REQUEUED
     msg.on Message.BACKOFF, =>
       @emit NSQDConnection.BACKOFF
@@ -267,274 +269,278 @@
     expect(setRdyStub2.lastCall.args[0]).is.eql 50

-  it 'should periodically redistribute RDY in low RDY conditions', (done) ->
-    # Set to true to see the debug the test.
-    StateChangeLogger.debug = false
-
-    # Shortening the periodica `balance` calls to every 10ms.
-    readerRdy = new ReaderRdy 1, 128, 0.01
-
-    connections = for i in [1..2]
-      createNSQDConnection i
-
-    # Add the connections and trigger the NSQDConnection event that tells
-    # listeners that the connections are connected and ready for message flow.
-    for conn in connections
-      readerRdy.addConnection conn
-      conn.emit NSQDConnection.SUBSCRIBED
-
-    # Given the number of connections and the maxInFlight, we should be in low
-    # RDY conditions.
-    expect(readerRdy.isLowRdy()).is.eql true
-
-    checkRdyCounts = ->
-      entries = connRdyEntries()
-      expect(rdyAlternates entries).should.be.ok
-      done()
-
-    # We have to wait a small period of time for log events to occur since the
-    # `balance` call is invoked perdiocally.
-    setTimeout checkRdyCounts, 50
-
-  it 'should handle the transition from normal to low RDY conditions', (done) ->
-    # Set to true to see the debug the test.
-    StateChangeLogger.debug = false
-
-    # Shortening the periodica `balance` calls to every 10ms.
-    readerRdy = new ReaderRdy 1, 128, 0.01
-
-    conn1 = createNSQDConnection 1
-    conn2 = createNSQDConnection 2
-
-    # Add the connections and trigger the NSQDConnection event that tells
-    # listeners that the connections are connected and ready for message flow.
-    readerRdy.addConnection conn1
-    conn1.emit NSQDConnection.SUBSCRIBED
-
-    expect(readerRdy.isLowRdy()).is.eql false
-
-    addConnection = ->
-      readerRdy.addConnection conn2
-      conn2.emit NSQDConnection.SUBSCRIBED
+  describe 'low RDY conditions', ->
+    it 'should periodically redistribute RDY', (done) ->
+      # Set to true to see the debug the test.
+      StateChangeLogger.debug = false
+
+      # Shortening the periodica `balance` calls to every 10ms.
+      readerRdy = new ReaderRdy 1, 128, 0.01
+
+      connections = for i in [1..2]
+        createNSQDConnection i
+
+      # Add the connections and trigger the NSQDConnection event that tells
+      # listeners that the connections are connected and ready for message flow.
+      for conn in connections
+        readerRdy.addConnection conn
+        conn.emit NSQDConnection.SUBSCRIBED

       # Given the number of connections and the maxInFlight, we should be in low
       # RDY conditions.
       expect(readerRdy.isLowRdy()).is.eql true

-    # Add the 2nd connections after some duration to simulate a new nsqd being
-    # discovered and connected.
-    setTimeout addConnection, 20
-
-    checkRdyCounts = ->
-      entries = connRdyEntries()
-      expect(rdyAlternates entries).should.be.ok
-      done()
-
-    # We have to wait a small period of time for log events to occur since the
-    # `balance` call is invoked perdiocally.
-    setTimeout checkRdyCounts, 40
-
-  it 'should handle the transition from low RDY to normal conditions', (done) ->
-    # Set to true to see the debug the test.
-    StateChangeLogger.debug = false
-
-    # Shortening the periodica `balance` calls to every 10ms.
-    readerRdy = new ReaderRdy 1, 128, 0.01
-
-    connections = for i in [1..2]
-      createNSQDConnection i
-
-    # Add the connections and trigger the NSQDConnection event that tells
-    # listeners that the connections are connected and ready for message flow.
-    for conn in connections
-      readerRdy.addConnection conn
-      conn.emit NSQDConnection.SUBSCRIBED
-
-    expect(readerRdy.isLowRdy()).is.eql true
-
-    removeConnection = ->
-      StateChangeLogger.log 'NSQDConnection', 'CLOSED', '2', 'connection closed'
-      connections[1].emit NSQDConnection.CLOSED
-
-      setTimeout checkNormal, 20
-
-    checkNormal = ->
+      checkRdyCounts = ->
+        entries = connRdyEntries()
+        expect(rdyAlternates entries).should.be.ok
+        done()
+
+      # We have to wait a small period of time for log events to occur since the
+      # `balance` call is invoked perdiocally.
+      setTimeout checkRdyCounts, 50
+
+    it 'should handle the transition from normal', (done) ->
+      # Set to true to see the debug the test.
+      StateChangeLogger.debug = false
+
+      # Shortening the periodica `balance` calls to every 10ms.
+      readerRdy = new ReaderRdy 1, 128, 0.01
+
+      conn1 = createNSQDConnection 1
+      conn2 = createNSQDConnection 2
+
+      # Add the connections and trigger the NSQDConnection event that tells
+      # listeners that the connections are connected and ready for message flow.
+      readerRdy.addConnection conn1
+      conn1.emit NSQDConnection.SUBSCRIBED
+
       expect(readerRdy.isLowRdy()).is.eql false
-      expect(readerRdy.balanceId).is.null
-
-      if readerRdy.connections[0].lastRdySent isnt 1
-        StateChangeLogger.print()
-        console.log readerRdy.connections
-        console.log readerRdy.roundRobinConnections
-
-      expect(readerRdy.connections[0].lastRdySent).is.eql 1
-      done()
-
-    # Remove a connection after some period of time to get back to normal
-    # conditions.
-    setTimeout removeConnection, 20
-
-  it 'should handle the transition from low RDY to normal conditions with connections in backoff', (done) ->
-    ###
-    1. Create two nsqd connections
-    2. Close the 2nd connection when the first connection is in the BACKOFF
-         state.
-    3. Check to see if the 1st connection does get it's RDY count.
-    ###
-
-    # Set to true to see the debug the test.
-    StateChangeLogger.debug = false
-
-    # Shortening the periodica `balance` calls to every 10ms.
-    readerRdy = new ReaderRdy 1, 128, 0.01
-
-    connections = for i in [1..2]
-      createNSQDConnection i
-
-    for conn in connections
-      readerRdy.addConnection conn
-      conn.emit NSQDConnection.SUBSCRIBED
-
-    expect(readerRdy.isLowRdy()).is.eql true
-
-    removeConnection = _.once ->
-      StateChangeLogger.log 'NSQDConnection', 'CLOSED', '2', 'connection closed'
-      connections[1].emit NSQDConnection.CLOSED
-      setTimeout checkNormal, 30
-
-    removeOnBackoff = ->
-      connRdy1 = readerRdy.connections[0]
-      connRdy1.on ConnectionRdy.STATE_CHANGE, ->
-        if connRdy1.statemachine.current_state_name is 'BACKOFF'
-          # If we don't do the connection CLOSED in the next tick, we remove
-          # the connection immediately which leaves `@connections` within
-          # `balance` in an inconsistent state which isn't possible normally.
-          setTimeout removeConnection, 0
-
-    checkNormal = ->
-      expect(readerRdy.isLowRdy()).is.eql false
-      expect(readerRdy.balanceId).is.null
-      expect(readerRdy.connections[0].lastRdySent).is.eql 1
-      done()
-
-    # Remove a connection after some period of time to get back to normal
-    # conditions.
-    setTimeout removeOnBackoff, 20
-
-
-  it 'should not exceed maxInFlight in low RDY conditions for long running message.', (done) ->
-    # Set to true to see the debug the test.
-    StateChangeLogger.debug = false
-
-    # Shortening the periodica `balance` calls to every 10ms.
-    readerRdy = new ReaderRdy 1, 128, 0.01
-
-    connections = for i in [1..2]
-      createNSQDConnection i
-
-    for conn in connections
-      readerRdy.addConnection conn
-      conn.emit NSQDConnection.SUBSCRIBED
-
-    # Handle the message but delay finishing the message so that several
-    # balance calls happen and the check to ensure that RDY count is zero for
-    # all connections.
-    handleMessage = (msg) ->
-      finish = ->
-        msg.finish()
+
+      addConnection = ->
+        readerRdy.addConnection conn2
+        conn2.emit NSQDConnection.SUBSCRIBED
+
+        # Given the number of connections and the maxInFlight, we should be in
+        # low RDY conditions.
+        expect(readerRdy.isLowRdy()).is.eql true
+
+      # Add the 2nd connections after some duration to simulate a new nsqd being
+      # discovered and connected.
+      setTimeout addConnection, 20
+
+      checkRdyCounts = ->
+        entries = connRdyEntries()
+        expect(rdyAlternates entries).should.be.ok
         done()
-      setTimeout finish, 40
-
-    for conn in connections
-      conn.on NSQDConnection.MESSAGE, handleMessage
-
-    sendMessageOnce = _.once ->
-      connections[1].createMessage '1', Date.now(), new Buffer('test')
-      setTimeout checkRdyCount, 20
-
-    # Send a message on the 2nd connection when we can. Only send the message
-    # once so that we don't violate the maxInFlight count.
-    sendOnRdy = ->
-      connRdy2 = readerRdy.connections[1]
-      connRdy2.on ConnectionRdy.STATE_CHANGE, ->
-        if connRdy2.statemachine.current_state_name in ['ONE', 'MAX']
-          sendMessageOnce()
-
-    # When the message is in-flight, balance cannot give a RDY count out to any
-    # of the connections.
-    checkRdyCount = ->
+
+      # We have to wait a small period of time for log events to occur since the
+      # `balance` call is invoked perdiocally.
+      setTimeout checkRdyCounts, 40
+
+    it 'should handle the transition to normal conditions', (done) ->
+      # Set to true to see the debug the test.
+      StateChangeLogger.debug = false
+
+      # Shortening the periodica `balance` calls to every 10ms.
+      readerRdy = new ReaderRdy 1, 128, 0.01
+
+      connections = for i in [1..2]
+        createNSQDConnection i
+
+      # Add the connections and trigger the NSQDConnection event that tells
+      # listeners that the connections are connected and ready for message flow.
+      for conn in connections
+        readerRdy.addConnection conn
+        conn.emit NSQDConnection.SUBSCRIBED
+
       expect(readerRdy.isLowRdy()).is.eql true
-      expect(readerRdy.connections[0].lastRdySent).is.eql 0
-      expect(readerRdy.connections[1].lastRdySent).is.eql 0
-
-    # We have to wait a small period of time for log events to occur since the
-    # `balance` call is invoked perdiocally.
-    setTimeout sendOnRdy, 20
-
-  it 'should recover in low RDY conditions losing a connection with a message in-flight', (done) ->
-    ###
-    # Detailed description:
-    1. Connect to 5 nsqds and add them to the ReaderRdy
-    2. When the 1st connection has the shared RDY count, it receives a message.
-    3. On receipt of a message, the 1st connection will process the message for
-         a long period of time.
-    4. While the message is being processed, the 1st connection will close.
-    5. Finally, check that the other connections are indeed now getting the RDY
-         count.
-    ###
-
-    # Set to true to see the debug the test.
-    StateChangeLogger.debug = false
-
-    # Shortening the periodica `balance` calls to every 10ms.
-    readerRdy = new ReaderRdy 1, 128, 0.01
-
-    connections = for i in [1..5]
-      createNSQDConnection i
-
-    # Add the connections and trigger the NSQDConnection event that tells
-    # listeners that the connections are connected and ready for message flow.
-    for conn in connections
-      readerRdy.addConnection conn
-      conn.emit NSQDConnection.SUBSCRIBED
-
-    handleMessage = (msg) ->
-      delayFinish = ->
-        msg.finish()
+
+      removeConnection = ->
+        StateChangeLogger.log 'NSQDConnection', 'CLOSED', '2',
+          'connection closed'
+        connections[1].emit NSQDConnection.CLOSED
+
+        setTimeout checkNormal, 20
+
+      checkNormal = ->
+        expect(readerRdy.isLowRdy()).is.eql false
+        expect(readerRdy.balanceId).is.null
+
+        if readerRdy.connections[0].lastRdySent isnt 1
+          StateChangeLogger.print()
+          console.log readerRdy.connections
+          console.log readerRdy.roundRobinConnections
+
+        expect(readerRdy.connections[0].lastRdySent).is.eql 1
         done()

-      setTimeout closeConnection, 10
-      setTimeout checkRdyCount, 30
-      setTimeout delayFinish, 50
-
-    for conn in connections
-      conn.on NSQDConnection.MESSAGE, handleMessage
-
-    closeConnection = _.once ->
-      connections[0].emit NSQDConnection.CLOSED
-
-    sendMessageOnce = _.once ->
-      connections[0].createMessage '1', Date.now(), new Buffer('test')
-
-    # Send a message on the 2nd connection when we can. Only send the message
-    # once so that we don't violate the maxInFlight count.
-    sendOnRdy = ->
-      connRdy = readerRdy.connections[0]
-      connRdy.on ConnectionRdy.STATE_CHANGE, ->
-        if connRdy.statemachine.current_state_name in ['ONE', 'MAX']
-          sendMessageOnce()
-
-    # When the message is in-flight, balance cannot give a RDY count out to any
-    # of the connections.
-    checkRdyCount = ->
+      # Remove a connection after some period of time to get back to normal
+      # conditions.
+      setTimeout removeConnection, 20
+
+    it 'should move to normal conditions with connections in backoff', (done) ->
+      ###
+      1. Create two nsqd connections
+      2. Close the 2nd connection when the first connection is in the BACKOFF
+          state.
+      3. Check to see if the 1st connection does get it's RDY count.
+      ###
+
+      # Set to true to see the debug the test.
+      StateChangeLogger.debug = false
+
+      # Shortening the periodica `balance` calls to every 10ms.
+      readerRdy = new ReaderRdy 1, 128, 0.01
+
+      connections = for i in [1..2]
+        createNSQDConnection i
+
+      for conn in connections
+        readerRdy.addConnection conn
+        conn.emit NSQDConnection.SUBSCRIBED
+
       expect(readerRdy.isLowRdy()).is.eql true

-      rdyCounts = for connRdy in readerRdy.connections
-        connRdy.lastRdySent
-
-      expect(readerRdy.connections.length).is.eql 4
-      expect(1 in rdyCounts).is.ok
-
-    # We have to wait a small period of time for log events to occur since the
-    # `balance` call is invoked perdiocally.
-    setTimeout sendOnRdy, 10
+      removeConnection = _.once ->
+        StateChangeLogger.log 'NSQDConnection', 'CLOSED', '2',
+          'connection closed'
+        connections[1].emit NSQDConnection.CLOSED
+        setTimeout checkNormal, 30
+
+      removeOnBackoff = ->
+        connRdy1 = readerRdy.connections[0]
+        connRdy1.on ConnectionRdy.STATE_CHANGE, ->
+          if connRdy1.statemachine.current_state_name is 'BACKOFF'
+            # If we don't do the connection CLOSED in the next tick, we remove
+            # the connection immediately which leaves `@connections` within
+            # `balance` in an inconsistent state which isn't possible normally.
+            setTimeout removeConnection, 0
+
+      checkNormal = ->
+        expect(readerRdy.isLowRdy()).is.eql false
+        expect(readerRdy.balanceId).is.null
+        expect(readerRdy.connections[0].lastRdySent).is.eql 1
+        done()
+
+      # Remove a connection after some period of time to get back to normal
+      # conditions.
+      setTimeout removeOnBackoff, 20
+
+
+    it 'should not exceed maxInFlight for long running message.', (done) ->
+      # Set to true to see the debug the test.
+      StateChangeLogger.debug = false
+
+      # Shortening the periodica `balance` calls to every 10ms.
+      readerRdy = new ReaderRdy 1, 128, 0.01
+
+      connections = for i in [1..2]
+        createNSQDConnection i
+
+      for conn in connections
+        readerRdy.addConnection conn
+        conn.emit NSQDConnection.SUBSCRIBED
+
+      # Handle the message but delay finishing the message so that several
+      # balance calls happen and the check to ensure that RDY count is zero for
+      # all connections.
+      handleMessage = (msg) ->
+        finish = ->
+          msg.finish()
+          done()
+        setTimeout finish, 40
+
+      for conn in connections
+        conn.on NSQDConnection.MESSAGE, handleMessage
+
+      sendMessageOnce = _.once ->
+        connections[1].createMessage '1', Date.now(), new Buffer('test')
+        setTimeout checkRdyCount, 20
+
+      # Send a message on the 2nd connection when we can. Only send the message
+      # once so that we don't violate the maxInFlight count.
+      sendOnRdy = ->
+        connRdy2 = readerRdy.connections[1]
+        connRdy2.on ConnectionRdy.STATE_CHANGE, ->
+          if connRdy2.statemachine.current_state_name in ['ONE', 'MAX']
+            sendMessageOnce()
+
+      # When the message is in-flight, balance cannot give a RDY count out to
+      # any of the connections.
+      checkRdyCount = ->
+        expect(readerRdy.isLowRdy()).is.eql true
+        expect(readerRdy.connections[0].lastRdySent).is.eql 0
+        expect(readerRdy.connections[1].lastRdySent).is.eql 0
+
+      # We have to wait a small period of time for log events to occur since the
+      # `balance` call is invoked perdiocally.
+      setTimeout sendOnRdy, 20
+
+    it 'should recover losing a connection with a message in-flight', (done) ->
+      ###
+      # Detailed description:
+      1. Connect to 5 nsqds and add them to the ReaderRdy
+      2. When the 1st connection has the shared RDY count, it receives a
+         message.
+      3. On receipt of a message, the 1st connection will process the message
+         for a long period of time.
+      4. While the message is being processed, the 1st connection will close.
+      5. Finally, check that the other connections are indeed now getting the
+         RDY count.
+      ###
+
+      # Set to true to see the debug the test.
+      StateChangeLogger.debug = false
+
+      # Shortening the periodica `balance` calls to every 10ms.
+      readerRdy = new ReaderRdy 1, 128, 0.01
+
+      connections = for i in [1..5]
+        createNSQDConnection i
+
+      # Add the connections and trigger the NSQDConnection event that tells
+      # listeners that the connections are connected and ready for message flow.
+      for conn in connections
+        readerRdy.addConnection conn
+        conn.emit NSQDConnection.SUBSCRIBED
+
+      handleMessage = (msg) ->
+        delayFinish = ->
+          msg.finish()
+          done()
+
+        setTimeout closeConnection, 10
+        setTimeout checkRdyCount, 30
+        setTimeout delayFinish, 50
+
+      for conn in connections
+        conn.on NSQDConnection.MESSAGE, handleMessage
+
+      closeConnection = _.once ->
+        connections[0].emit NSQDConnection.CLOSED
+
+      sendMessageOnce = _.once ->
+        connections[0].createMessage '1', Date.now(), new Buffer('test')
+
+      # Send a message on the 2nd connection when we can. Only send the message
+      # once so that we don't violate the maxInFlight count.
+      sendOnRdy = ->
+        connRdy = readerRdy.connections[0]
+        connRdy.on ConnectionRdy.STATE_CHANGE, ->
+          if connRdy.statemachine.current_state_name in ['ONE', 'MAX']
+            sendMessageOnce()
+
+      # When the message is in-flight, balance cannot give a RDY count out to
+      # any of the connections.
+      checkRdyCount = ->
+        expect(readerRdy.isLowRdy()).is.eql true
+
+        rdyCounts = for connRdy in readerRdy.connections
+          connRdy.lastRdySent
+
+        expect(readerRdy.connections.length).is.eql 4
+        expect(1 in rdyCounts).is.ok
+
+      # We have to wait a small period of time for log events to occur since the
+      # `balance` call is invoked perdiocally.
+      setTimeout sendOnRdy, 10


Added script for Bing searches

Added script for Bing searches

error: pathspec 'src/scripts/bing.coffee' did not match any file(s) known to git

--- src/scripts/bing.coffee
+++ src/scripts/bing.coffee
@@ -0,0 +1,15 @@
+# Returns the URL of the first bing hit for a query
+#
+# bing me <query>   - Bings <query> & returns 1st result's URL
+
+module.exports = (robot) ->
+  robot.respond /(bing)( me)? (.*)/i, (msg) ->
+    bingMe msg, msg.match[3], (url) ->
+      msg.send url
+
+bingMe = (msg, query, cb) ->
+  msg.http('http://www.bing.com/search')
+    .query(q: query)
+    .get() (err, res, body) ->
+      cb body.match(/

<a href="([^"]*)"/)?[1] || "Sorry, Bing had zero results for '#{query}'"
+


Don't kill yourself v0.7

Don't kill yourself v0.7

--- gameState.coffee
+++ gameState.coffee
@@ -663,6 +663,54 @@
     total += @trash.length
     total += @prizes.length
     total
+
+  buyCausesToLose: (player, state, card) ->
+    if (state.gainsToEndGame() > 1)
+      return false
+    if (not card?)
+      return false
+
+    # One level of recursion is enough for first
+    [hypState, hypMy] = [this, state.current]
+    if (this.depth==0)
+      [hypState, hypMy] = state.hypothetical(player.ai)
+
+    # try to buy this card
+    # C&P from below
+    #
+    [coinCost, potionCost] = card.getCost(this)
+    hypMy.coins -= coinCost
+    hypMy.potions -= potionCost
+    hypMy.buys -= 1
+
+    hypState.gainCard(hypMy, card, 'discard', true)
+    card.onBuy(hypState)
+
+
+    for i in [hypMy.inPlay.length-1...-1]
+        cardInPlay = hypMy.inPlay[i]
+      if cardInPlay?
+        cardInPlay.buyInPlayEffect(hypState, card)
+
+      goonses = hypMy.countInPlay('Goons')
+      if goonses > 0
+        this.log("...gaining #{goonses} VP.")
+        hypMy.chips += goonses
+    #
+    # C&P until here
+
+    #finish buyPhase
+    hypState.doBuyPhase()
+
+    # find out if game ended and who if we have won it
+    hypState.phase = 'start'
+    if not hypState.gameIsOver()
+      return false
+    if ( hypMy.ai.toString() in hypState.getWinners() )
+      return false
+    state.log("Forbid buying #{card}!")
+    return true
+

   #### Playing a turn
   #
@@ -818,7 +866,16 @@
         [coinCost, potionCost] = card.getCost(this)
         if coinCost <= @current.coins and potionCost <= @current.potions
           buyable.push(card)
-
+
+    # Don't allow cards that will lose us the game
+    #
+    # Not completely optimal because further buys could still cause us winning
+    # But Geronimoo also does not do better, so for first we should do it like that
+    #
+    # Also note that this just cares for the buyPhase, gains by other means (Workshop) are not covered
+
+    buyable = (card for card in buyable when (not this.buyCausesToLose(@current, this, card)) )
+
     # Ask the AI for its choice.
     this.log("Coins: #{@current.coins}, Potions: #{@current.potions}, Buys: #{@current.buys}")
     choice = @current.ai.chooseGain(this, buyable)


Fix up docs with Extended tag and rewording

--- src/text-editor.coffee
+++ src/text-editor.coffee
@@ -1057,11 +1057,13 @@
   deleteToBeginningOfWord: ->
     @mutateSelectedText (selection) -> selection.deleteToBeginningOfWord()

-  # Similar to above, but deletes back to the previous word boundary
+  # Extended: Similar to {::deleteToBeginningOfWord}, but deletes only back to the
+  # previous word boundary.
   deleteToPreviousWordBoundary: ->
     @mutateSelectedText (selection) -> selection.deleteToPreviousWordBoundary()

-  # Similar to above, but deletes up to the next word boundary
+  # Extended: Similar to {::deleteToEndOfWord}, but deletes only up to the
+  # next word boundary.
   deleteToNextWordBoundary: ->
     @mutateSelectedText (selection) -> selection.deleteToNextWordBoundary()


refactored Timing_ from Foxie

refactored Timing_ from Foxie

error: pathspec 'scripts/coffee/lib/el/Timing_.coffee' did not match any file(s) known to git

--- scripts/coffee/lib/el/Timing_.coffee
+++ scripts/coffee/lib/el/Timing_.coffee
@@ -0,0 +1,157 @@
+{array} = require 'utila'
+timing = require 'raf-timing'
+
+module.exports = class Timing_
+
+	__initMixinTiming: ->
+
+		@_quittersForTiming = []
+
+		null
+
+	__clonerForTiming: (newEl) ->
+
+		newEl._quittersForTiming = []
+
+	__quitterForTiming: ->
+
+		loop
+
+			return if @_quittersForTiming.length < 1
+
+			@_quittersForTiming.pop()()
+
+		return
+
+	_getMethodChain: ->
+
+		unless @constructor.__methodChain?
+
+			@constructor.__methodChain = new MethodChain
+
+			for key, fn of @
+
+				continue if key[0] is '_' or key is 'constructor'
+
+				continue unless fn instanceof Function
+
+				@constructor.__methodChain.addMethod key
+
+		@constructor.__methodChain
+
+	_getNewInterface: ->
+
+		@_getMethodChain().getInterface()
+
+	wait: (ms, rest...) ->
+
+		@_eventEnabledMethod rest, (cb) =>
+
+			timing.wait ms, =>
+
+				cb.call @
+
+	immediately: ->
+
+		@_eventEnabledMethod arguments, (cb) =>
+
+			timing.afterFrame =>
+
+				cb.call @
+
+	eachFrame: ->
+
+		@_eventEnabledMethod arguments, (cb) =>
+
+			startTime = new Int32Array 1
+			startTime[0] = -1
+
+			canceled = no
+
+			canceler = =>
+
+				return if canceled
+
+				timing.cancelFrames theCallback
+
+				array.pluckOneItem @_quittersForTiming, canceler
+
+				canceled = yes
+
+			@_quittersForTiming.push canceler
+
+			theCallback = (t) =>
+
+				if startTime[0] < 0
+
+					startTime[0] = t
+
+					elapsedTime = 0
+
+				else
+
+					elapsedTime = t - startTime[0]
+
+				cb.call @, elapsedTime, canceler
+
+				null
+
+			timing.frames theCallback
+
+	run: ->
+
+		@_eventEnabledMethod arguments, (cb) =>
+
+			cb.call @
+
+		@
+
+	every: (ms, args...) ->
+
+		@_eventEnabledMethod args, (cb) =>
+
+			canceled = no
+
+			canceler = =>
+
+				return if canceled
+
+				timing.cancelEvery theCallback
+
+				array.pluckOneItem @_quittersForTiming, canceler
+
+				canceled = yes
+
+			@_quittersForTiming.push canceler
+
+			theCallback = =>
+
+				cb.call @, canceler
+
+			timing.every ms, theCallback
+
+	everyAndNow: (ms, args...) ->
+
+		@_eventEnabledMethod args, (cb) =>
+
+			canceled = no
+
+			canceler = =>
+
+				return if canceled
+
+				timing.cancelEvery theCallback
+
+				array.pluckOneItem @_quittersForTiming, canceler
+
+				canceled = yes
+
+			@_quittersForTiming.push canceler
+
+			theCallback = =>
+
+				cb.call @, canceler
+
+			timing.every ms, theCallback
+
+			timing.afterFrame theCallback


[optimise] "Inline" constants

[optimise] "Inline" constants

 - Constant variables
 - Constant if statements

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/demhydraz/cafe.git/'


Create Stream Broadcast feature

Create Stream Broadcast feature

error: pathspec 'server/stream/streamBroadcast.coffee' did not match any file(s) known to git

--- server/stream/streamBroadcast.coffee
+++ server/stream/streamBroadcast.coffee
@@ -0,0 +1,43 @@
+@connections = {}
+@startStreamBroadcast = (streams) ->
+	console.log 'startStreamBroadcast'
+
+	# connections = {}
+
+	InstanceStatus.getCollection().find().observe
+		added: (record) ->
+			if record.extraInformation.port is process.env.PORT
+				return
+
+			console.log 'connecting in', "localhost:#{record.extraInformation.port}"
+			connections[record.extraInformation.port] = DDP.connect("localhost:#{record.extraInformation.port}", {_dontPrintErrors: true})
+
+		removed: (record) ->
+			if connections[record.extraInformation.port]?
+				console.log 'disconnecting from', "localhost:#{record.extraInformation.port}"
+				connections[record.extraInformation.port].disconnect()
+				delete connections[record.extraInformation.port]
+
+	broadcast = (streamName, eventName, args, userId) ->
+		for port, connection of connections
+			if connection.status().connected is true
+				connection.call 'stream', streamName, eventName, args
+
+	emitters = {}
+
+	for streamName, stream of streams
+		do (streamName, stream) ->
+			emitters[streamName] = stream.emit
+			stream.emit = (eventName, args...) ->
+				broadcast streamName, eventName, args
+				emitters[streamName].call {}, arguments
+
+	Meteor.methods
+		stream: (streamName, eventName, args) ->
+			args.unshift eventName
+			emitters[streamName]?.apply {}, args
+
+
+Meteor.startup ->
+	startStreamBroadcast
+		'webrtc.stream': webrtc.stream


Create turbolinks_slide.js.coffee

Create turbolinks_slide.js.coffee

error: pathspec 'turbolinks_slide.js.coffee' did not match any file(s) known to git

--- turbolinks_slide.js.coffee
+++ turbolinks_slide.js.coffee
@@ -0,0 +1,61 @@
+class TurbolinksSlide
+  constructor: ->
+    @current_absolute_path = null
+    @current = {controller: null, action: null, param: null}
+    @width = $(window).width()
+    @_set_params()
+
+  animation_param: ->
+    @_set_params()
+    switch (@_animation_type())
+      when 1
+        return { marginLeft: "-#{@width}px", marginRight: "#{@width}px", opacity: 0.3 }
+      when -1
+        return { marginLeft: "#{@width}px", marginRight: "-#{@width}px", opacity: 0.3 }
+      else
+        return { opacity: 0 }
+
+  _animation_type: ->
+    if (@previous.controller == @current.controller)
+      switch (@previous.action)
+        when 'index'
+          return 1 if (@current.action != 'index')
+          break
+        when 'new', 'edit'
+          return -1 if (@current.action == 'index')
+          break
+        else
+          break
+    return 0
+
+  _domain_removed_current_url: ->
+    port = if document.location.port == '' then '' else ":#{document.location.port}"
+    document.location.href.replace(document.location.protocol + '//' + document.location.host + port + '/', '')
+
+  _get_controller: ->
+    return @current_absolute_path[0] if @current_absolute_path.length >= 1
+    return null
+
+  _get_action: ->
+    return @current_absolute_path[2] if @current_absolute_path.length >= 3
+    if @current_absolute_path.length >= 2
+      return 'new' if @current_absolute_path[1] == 'new'
+    return 'index'
+
+  _get_param: ->
+    return @current_absolute_path[1] if @current_absolute_path.length >= 2
+    return null
+
+  _set_current: ->
+    @current = {controller: null, action: null, param: null}
+    @current.controller = @_get_controller()
+    @current.action = @_get_action()
+    @current.param = @_get_param()
+    @current.action = 'show' if @current.action == 'index' && @current.param
+
+  _set_params: ->
+    @current_absolute_path = @_domain_removed_current_url().split('/')
+    @previous = @current if @current
+    @_set_current()
+
+@TurbolinksSlide = TurbolinksSlide


Img fix

Img fix

Steve's image fix for CK 0.3

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/Postmedia/timbits-example.git/'


No dropdown for sound in readOnly Treemas.

--- app/treema-ext.coffee
+++ app/treema-ext.coffee
@@ -106,7 +106,7 @@
     if @data
       valEl.append(playButton)
       valEl.append(stopButton)
-    valEl.append(dropdown) if files.length
+    valEl.append(dropdown) if files.length and @canEdit()
     if @data
       path = @data.split('/')
       name = path[path.length-1]


Restore package concurrency

Restore package concurrency

--- tasks/spec-task.coffee
+++ tasks/spec-task.coffee
@@ -52,9 +52,7 @@
       continue unless isAtomPackage(packagePath)
       packageSpecQueue.push(packagePath)

-    # TODO: Restore concurrency on Windows
-    packageSpecQueue.concurrency = 1 unless process.platform is 'win32'
-
+    packageSpecQueue.concurrency = if process.platform is 'win32' then 2 else 1
     packageSpecQueue.drain = -> callback(null, failedPackages)

   runCoreSpecs = (callback) ->
@@ -79,10 +77,8 @@
       if process.platform is 'win32'
         process.stderr.write(fs.readFileSync('ci.log'))
         fs.unlinkSync('ci.log')
-      else
-        # TODO: Restore concurrency on Windows
-        packageSpecQueue.concurrency = 2

+      packageSpecQueue.concurrency = 2
       callback(null, error)

   grunt.registerTask 'run-specs', 'Run the specs', ->


fix/optimize take()

fix/optimize take()

--- src/Bacon.coffee
+++ src/Bacon.coffee
@@ -103,13 +103,17 @@
         @push end()
         Bacon.noMore
   take: (count) ->
+    assert "take: count must >= 1", (count>=1)
     @withHandler (event) ->
-      if event.isEnd() or count > 0
-        count--
-        @push event
-      else
+      if event.isEnd()
+        @push event
+      else if (count == 1)
+        @push event
         @push end()
         Bacon.noMore
+      else
+        count--
+        @push event
   map: (f) ->
     @withHandler (event) ->
       @push event.fmap(f)


Improve Client class docs

Improve Client class docs

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/laterpay/laterpay-client-node.git/'