Fix overlay rendering bugs @adamschwartz

--- coffee/docBase.coffee
+++ coffee/docBase.coffee
@@ -76,27 +76,23 @@
     propertyPathEl.style.display = 'none'

 positionCodeOverlay = ->
-  position = ->
-    rowEl = editorEl.querySelector '.ace_content .ace_marker-layer .ace_active-line'
-    aceContentEl = editorEl.querySelector '.ace_content'
+  rowEl = editorEl.querySelector '.ace_content .ace_marker-layer .ace_active-line'
+  aceContentEl = editorEl.querySelector '.ace_content'

-    if not rowEl or not aceContentEl
-      overlayEl.setAttribute 'target-off-screen', ''
-      return
+  if not rowEl or not aceContentEl
+    overlayEl.setAttribute 'target-off-screen', ''
+    return

-    top = 0
-    if rowEl and aceContentEl
-      overlayEl.removeAttribute 'target-off-screen'
-      top = rowEl.offsetTop + parseInt(aceContentEl.style.marginTop, 10)
+  top = 0
+  if rowEl and aceContentEl
+    overlayEl.removeAttribute 'target-off-screen'
+    top = rowEl.offsetTop + parseInt(aceContentEl.style.marginTop, 10)

-    if top + overlayEl.clientHeight > document.documentElement.clientHeight
-      top = Math.max(0, document.documentElement.clientHeight - overlayEl.clientHeight)
+  if top + overlayEl.clientHeight > document.documentElement.clientHeight
+    top = Math.max(0, document.documentElement.clientHeight - overlayEl.clientHeight)

-    overlayEl.setAttribute 'overflow-y', (document.documentElement.clientHeight - overlayEl.clientHeight <= 0)
-    overlayEl.style.top = top + 'px'
-
-  position()
-  setTimeout(position, 50)
+  overlayEl.setAttribute 'overflow-y', (document.documentElement.clientHeight - overlayEl.clientHeight <= 0)
+  overlayEl.style.top = top + 'px'

 setEditorOverlayContext = (item) ->
   contextHTML = ''
@@ -110,8 +106,7 @@

       break

-  setTimeout ->
-    overlayElContent.innerHTML = contextHTML
+  overlayElContent.innerHTML = contextHTML

 editor = ace.edit editorId
 editor.setTheme 'ace/theme/textmate'
@@ -147,9 +142,9 @@
   positionCodeOverlay()
   setCodeContextDisplay itemErrored, selectionRange

-window.addEventListener 'resize', -> positionCodeOverlay()
+window.addEventListener 'resize', positionCodeOverlay

-editorSession.on 'changeScrollTop', -> positionCodeOverlay()
+editor.renderer.on 'afterRender', positionCodeOverlay

 editor.focus()
 document.documentElement.classList.add 'page-loaded'
@@ -165,7 +160,4 @@

 processContext context

-positionCodeOverlay 0
 setEditorOverlayContext 'default'
-
-console.log 'asd'


Send disconnect on gate close

Send disconnect on gate close

--- components/Gate.coffee
+++ components/Gate.coffee
@@ -33,5 +33,6 @@
       @open = true
     @inPorts.close.on 'data', =>
       @open = false
+      @outPorts.out.disconnect()

 exports.getComponent = -> new Gate


config/dev: also test client's sub projects for old package.json files

--- config/main.dev.coffee
+++ config/main.dev.coffee
@@ -613,6 +613,16 @@
             sh -c "scripts/install-npm.sh -d client -s"
         fi

+        if [ "#{projectRoot}/run" -ot "#{projectRoot}/client/builder/package.json" ]; then
+            sleep 1
+            sh -c "scripts/install-npm.sh -d client/builder -s"
+        fi
+
+        if [ "#{projectRoot}/run" -ot "#{projectRoot}/client/landing/package.json" ]; then
+            sleep 1
+            sh -c "scripts/install-npm.sh -d client/landing -s"
+        fi
+
         OLD_COOKIE=$(npm list tough-cookie -s | grep 0.9.15 | wc -l | awk \'{printf "%s", $1}\')
         if [  $OLD_COOKIE -ne 0 ]; then
             echo "You have tough-cookie@0.9.15 installed on your system, please remove node_modules directory and do npm i again";


Sanitize emails during migration phase in run script

--- config/main.dev.coffee
+++ config/main.dev.coffee
@@ -1139,6 +1139,9 @@
         # Create default workspaces
         node scripts/create-default-workspace

+        # Sanitize email addresses
+        node #{projectRoot}/scripts/sanitize-email
+
         # Run all the worker daemons in KONFIG.workers
         #{("worker_daemon_"+key+"\n" for key,val of KONFIG.workers when val.supervisord).join(" ")}


 Merge branch 'master.groups.login' into 'master'

group: redirect to "/Login" when invitation banner callback

--- client/Login/AppView.coffee
+++ client/Login/AppView.coffee
@@ -457,7 +457,7 @@

   headBannerShowInvitation:(invite)->
     @showHeadBanner "Cool! you got an invite! If you already have an account click here to sign in.", =>
-      @animateToForm "login"
+      KD.singleton("router").handleRoute "/Login"
       @headBanner.hide()

     KD.getSingleton('router').clear @getRouteWithEntryPoint 'Register'


Storage: Snapshot: KiteStorage write and fetch process are ready to global usage in IDEAppController.

--- client/ide/lib/index.coffee
+++ client/ide/lib/index.coffee
@@ -875,26 +875,31 @@

     return  if @isDestroyed or not @isMachineRunning() or @silent

-    name  = @getWorkspaceSnapshotName nick()
+    key   = @getWorkspaceStorageKey nick()
     value = @getWorkspaceSnapshot()
+
+    @writeToKiteStorage key, value
+    @emit 'SnapshotUpdated'
+
+
+  writeToKiteStorage: (key, value) ->

     # we need to check against kite existence because while a machine
     # is getting destroyed/stopped/reinitialized we are invalidating it's
     # kite instance to make sure every call is stopped. ~ GG
-    @mountedMachine.getBaseKite()?.storageSetQueued name, value
-    @emit 'SnapshotUpdated'
+    @mountedMachine.getBaseKite()?.storageSetQueued key, value

   removeWorkspaceSnapshot: (username = nick()) ->

-    key = @getWorkspaceSnapshotName username
+    key = @getWorkspaceStorageKey username
     @mountedMachine.getBaseKite().storageDelete key

-  getWorkspaceSnapshotName: (username) ->
-
-    if username
-      return "#{username}.wss.#{@workspaceData.slug}"
+  getWorkspaceStorageKey: (prefix) ->
+
+    if prefix
+      return "#{prefix}.wss.#{@workspaceData.slug}"
     else
       return "wss.#{@workspaceData.slug}"

@@ -1651,25 +1656,30 @@

   fetchSnapshot: (callback, username = nick()) ->

+    @fetchFromKiteStorage callback, username
+
+
+  fetchFromKiteStorage: (callback, prefix) ->
+
     if not @mountedMachine or not @mountedMachine.isRunning()
       callback null
       return

     handleError = (err) ->

-      console.warn 'Failed to fetch snapshot:', err
+      console.warn 'Failed to fetch data:', err
       callback null

-    fetch = (username) =>
-
-      key = @getWorkspaceSnapshotName username
+    fetch = (prefix) =>
+
+      key = @getWorkspaceStorageKey prefix
       @mountedMachine.getBaseKite().storageGet key

-    fetch username
-
-      .then (snapshot) =>
-
-        return callback snapshot  if snapshot
+    fetch prefix
+
+      .then (data) =>
+
+        return callback data  if data

         # Backward compatibility plug
         return callback null  unless @mountedMachine.isMine()


IDE: Wait for revive before relaunching IDE after a reinit.

--- client/ide/lib/index.coffee
+++ client/ide/lib/index.coffee
@@ -656,7 +656,11 @@
       when 'Building'
         environmentDataProvider.ensureDefaultWorkspace kd.noop
       when 'Running'
-        @quit()
+        id = @mountedMachine._id
+        { computeController } = kd.singletons
+
+        computeController.once "revive-#{id}", @lazyBound 'quit'
+        computeController.triggerReviveFor id

   showStateMachineModal: (machineItem, event) ->


forgot to rename class="checked" to "m-checked"

--- client/jquery-dyalog.coffee
+++ client/jquery-dyalog.coffee
@@ -39,7 +39,7 @@
               $(@).addClass 'm-checked'; mFocus null; x.action?(); false
         else if x.checked?
           $a.toggleClass 'm-checked', !!x.checked
-            .on 'mousedown mouseup click', (e) -> $(@).toggleClass 'checked'; mFocus null; x.action? $(@).hasClass 'checked'; false
+            .on 'mousedown mouseup click', (e) -> $(@).toggleClass 'm-checked'; mFocus null; x.action? $(@).hasClass 'm-checked'; false
         else
           if x.action then $a.on 'mousedown mouseup click', (e) -> mFocus null; x.action(); false
         if !x.items then return $a


Improving unit test coverage around revision.coffee


order instances alphabetically

order instances alphabetically

--- imports/ui/pages/InstancesPage.cjsx
+++ imports/ui/pages/InstancesPage.cjsx
@@ -1,31 +1,35 @@
 { Meteor }          = require 'meteor/meteor'
 React               = require 'react'
 G                   = require 'grommet'
+_                   = require 'lodash'
 { browserHistory }  = require 'react-router'
 Helpers             = require '../Helpers.coffee'

-module.exports = React.createClass
-  displayName: 'InstancesPage'
-
-  render: ->
-    console.log @
+module.exports = ({instances}) ->
+    sortedInstances = _.sortBy instances, ['name']
+    groups = _.values _.groupBy sortedInstances, (i) -> i.name[0]
     <G.Section pad='none'>
-      <G.Header size='small' justify='start' separator='top' pad={horizontal:'small'}>
-        <G.Label size='small'>Today</G.Label>
-      </G.Header>
-      <G.Tiles selectable=true pad={horizontal:'small'} fill=false flush=false responsive=false>
-        {@props.instances.map (instance) ->
-          onTileClick = -> browserHistory.push "/instances/#{instance.name}"
-          instanceHelper = Helpers.withInstance instance
-          <G.Tile key={instance._id} align='stretch' pad='small' direction='row' size={width: {min: 'small'}} onClick={onTileClick}>
-            <G.Box>
-              {instance.name}
-              <G.Box direction='row'>
-                <G.Icons.Status value={instanceHelper.getStateValue()} size='small' />
-                {instanceHelper.getStatusText()}
-              </G.Box>
-            </G.Box>
-          </G.Tile>
-        }
-      </G.Tiles>
+      {groups.map (group) ->
+        groupName = group[0].name[0].toUpperCase()
+
+          <G.Header size='small' justify='start' separator='top' pad={horizontal:'small'}>
+            <G.Label size='small'>{groupName}</G.Label>
+          </G.Header>
+          <G.Tiles selectable=true pad={horizontal:'small'} fill=false flush=false responsive=false>
+            {group.map (instance) ->
+              onTileClick = -> browserHistory.push "/instances/#{instance.name}"
+              instanceHelper = Helpers.withInstance instance
+              <G.Tile key={instance._id} align='stretch' pad='small' direction='row' size={width: {min: 'small'}} onClick={onTileClick}>
+                <G.Box>
+                  {instance.name}
+                  <G.Box direction='row'>
+                    <G.Icons.Status value={instanceHelper.getStateValue()} size='small' />
+                    {instanceHelper.getStatusText()}
+                  </G.Box>
+                </G.Box>
+              </G.Tile>
+            }
+          </G.Tiles>
+
+      }
     </G.Section>


Fixed file save to keep entered or selected filename [#115354115]

--- src/code/views/file-dialog-tab-view.coffee
+++ src/code/views/file-dialog-tab-view.coffee
@@ -76,7 +76,9 @@
   mixins: [AuthorizeMixin]

   getInitialState: ->
-    @getStateForFolder @props.client.state.metadata?.parent or null
+    initialState = @getStateForFolder @props.client.state.metadata?.parent or null
+    initialState.filename = initialState.metadata?.name or ''
+    initialState

   isOpen: ->
     @props.dialog.action is 'openFile'
@@ -97,7 +99,6 @@
     metadata = if @isOpen() then @state?.metadata or null else @props.client.state.metadata
     folder: folder
     metadata: metadata
-    filename: metadata?.name or ''
     list: []

   fileSelected: (metadata) ->


Fixes #2095 If option passed is 'destroy' then only call the #destroy method and terminate.

--- coffee/chosen.jquery.coffee
+++ coffee/chosen.jquery.coffee
@@ -8,9 +8,11 @@
     this.each (input_field) ->
       $this = $ this
       chosen = $this.data('chosen')
-      if options is 'destroy' && chosen instanceof Chosen
-        chosen.destroy()
-      else unless chosen instanceof Chosen
+      if options is 'destroy'
+        if chosen instanceof Chosen
+          chosen.destroy()
+        return
+      unless chosen instanceof Chosen
         $this.data('chosen', new Chosen(this, options))

       return


use native dom element

use native dom element

--- coffee/chosen.jquery.coffee
+++ coffee/chosen.jquery.coffee
@@ -231,9 +231,9 @@

   set_tab_index: (el) ->
-    if @form_field_jq[0].tabIndex
-      ti = @form_field_jq[0].tabIndex
-      @form_field_jq[0].tabIndex = -1
+    if @form_field.tabIndex
+      ti = @form_field.tabIndex
+      @form_field.tabIndex = -1
       @search_field[0].tabIndex = ti

   set_label_behavior: ->


docs, binding, error classes

docs, binding, error classes

--- src/mixinfactory.coffee
+++ src/mixinfactory.coffee
@@ -1,8 +1,18 @@
-UTIL = require './util'
 _ = require 'underscore'

 class Mixin
+  ###
+    Don't make your own Mixin instances; use the factory method @make_mixin,
+    which creates immutable instances for use with mixin methods on Function.prototype.
+
+    The philosophy, more or less, is like:
+
+      - https://javascriptweblog.wordpress.com/2011/05/31/a-fresh-look-at-javascript-mixins/
+  ###
+
+  @MutabilityError: class MutabilityError extends Error
+  @ArgumentError: class ArgumentError extends Error

   constructor: (@name) ->

@@ -13,19 +23,19 @@
     unless _.isObject(obj) && !_.isArray(obj)
       throw new TypeError "Expected non-empty mixin object"
     unless _.isString(obj.name) && obj.name
-      throw new UTIL.ArgumentError "Expected String name in options argument"
+      throw new @ArgumentError "Expected String name in options argument"

     mixin = new Mixin(obj.name)
     mkeys = Object.keys(obj).sort()

     for key, value of _.extend(obj, mixin_keys: mkeys)
-      do (key, value) ->
+      do (key, value) =>
         Object.defineProperty mixin, key,
           enumerable: true
           get: ->
             value
-          set: ->
-            throw new Error "Cannot change #{key} on #{mixin}; Mixins are immutable"
+          set: =>
+            throw new @MutabilityError "Cannot change #{key} on #{mixin}"
     mixin


Added UUID sanity check

Added UUID sanity check

--- src/models/punch.coffee
+++ src/models/punch.coffee
@@ -90,6 +90,13 @@
       return
     headers = HEADERS.rawdata
     date = moment.tz(row[headers.today], 'MM/DD/YYYY', TIMEZONE)
+
+    # UUID sanity check
+    if row[headers.id].length != 36
+      Logger.debug "#{row[headers.id]} is not a valid UUID, changing to valid UUID"
+      row[headers.id] = uuid.v1()
+      Organization.spreadsheet.saveRow(row)
+
     if row[headers.project1] is 'vacation' or
        row[headers.project1] is 'sick' or
        row[headers.project1] is 'unpaid'


Housekeeping on aryaTest.coffee

Housekeeping on aryaTest.coffee

--- src/test/coffee/aryaTest.coffee
+++ src/test/coffee/aryaTest.coffee
@@ -1,5 +1,5 @@
 # aryaTest.coffee
-# Copyright 2015 Patrick Meade.
+# Copyright 2015-2016 Patrick Meade.
 #
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU Affero General Public License as
@@ -15,8 +15,8 @@
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #----------------------------------------------------------------------

-should = require 'should'
-{Arya} = require '../lib/arya'
+should = require "should"
+{Arya} = require "../lib/arya"
 {AryaMemory} = require "../lib/arya-mem"

 arya = null
@@ -146,7 +146,7 @@
         arya.load uuid, mem.input, (err, obj) ->
           throw err if err?
           should.exist obj
-          obj.should.have.properties ['falseDat', 'trueDat']
+          obj.should.have.properties ["falseDat", "trueDat"]
           obj.falseDat.should.equal false
           obj.trueDat.should.equal true
           obj.should.not.equal TEST_OBJ
@@ -162,7 +162,7 @@
         arya.load uuid, mem.input, (err, obj) ->
           throw err if err?
           should.exist obj
-          obj.should.have.property 'birthdate'
+          obj.should.have.property "birthdate"
           obj.birthdate.should.eql new Date(1427915590)
           obj.should.not.equal TEST_OBJ
           obj.should.eql TEST_OBJ
@@ -171,7 +171,7 @@
     it "can restore objects using constructors", (done) ->
       class ComplexNumber
         constructor: (@r, @i) ->
-      arya.register 'ComplexNumber', ComplexNumber
+      arya.register "ComplexNumber", ComplexNumber
       TEST_OBJ = new ComplexNumber 2, 3
       arya.save TEST_OBJ, mem.output, (err, uuid) ->
         throw err if err?
@@ -180,7 +180,7 @@
           throw err if err?
           should.exist obj
           obj.constructor.name.should.equal "ComplexNumber"
-          obj.should.have.properties ['i', 'r']
+          obj.should.have.properties ["i", "r"]
           obj.r.should.equal 2
           obj.i.should.equal 3
           obj.should.not.equal TEST_OBJ
@@ -194,7 +194,7 @@
         rObj = new ComplexNumber 0, 0
         rObj.square = (x) -> x * x
         return rObj
-      arya.register 'ComplexNumber', constrProxy, true
+      arya.register "ComplexNumber", constrProxy, true
       TEST_OBJ = new ComplexNumber 2, 3
       arya.save TEST_OBJ, mem.output, (err, uuid) ->
         throw err if err?
@@ -203,7 +203,7 @@
           throw err if err?
           should.exist obj
           obj.constructor.name.should.equal "ComplexNumber"
-          obj.should.have.properties ['i', 'r', 'square']
+          obj.should.have.properties ["i", "r", "square"]
           obj.r.should.equal 2
           obj.i.should.equal 3
           obj.square(2).should.equal 4
@@ -221,8 +221,8 @@
         arya.load uuid, mem.input, (err, obj) ->
           throw err if err?
           should.exist obj
-          obj.should.have.properties ['alice', 'carol']
-          obj.should.not.have.property 'bob'
+          obj.should.have.properties ["alice", "carol"]
+          obj.should.not.have.property "bob"
           obj.uuid.should.equal uuid
           obj.alice.should.equal "Alice"
           obj.carol.should.equal "Carol"
@@ -295,7 +295,7 @@
         rObj = new ComplexNumber 0, 0
         rObj.square = (x) -> x * x
         # forgot to: return rObj
-      arya.register 'ComplexNumber', constrProxy, true
+      arya.register "ComplexNumber", constrProxy, true
       TEST_OBJ = new ComplexNumber 2, 3
       arya.save TEST_OBJ, mem.output, (err, uuid) ->
         throw err if err?
@@ -323,7 +323,7 @@
         throw "bad uuid" if not uuid?
         arya.load uuid, mem.input, (err, obj) ->
           should.exist obj
-          obj.should.have.properties ['alice', 'bob']
+          obj.should.have.properties ["alice", "bob"]
           obj.uuid.should.equal uuid
           obj.alice.should.equal "Alice"
           should(obj.bob).equal null


(- style re) Conform to whitespacing-style in parser.coffee

--- Paws/Source/parser.coffee
+++ Paws/Source/parser.coffee
@@ -7,12 +7,12 @@
    slice: -> @source.slice(@begin, @end)

 class Expression
-    constructor: (@contents, @next)->
-
-    append: (expr)->
-       curr = this
-       curr = curr.next while curr.next
-       curr.next = expr
+   constructor: (@contents, @next)->
+
+   append: (expr)->
+      curr = this
+      curr = curr.next while curr.next
+      curr.next = expr

 # A simple recursive descent parser with no backtracking. No lexing is needed here.
 class Parser
@@ -102,4 +102,3 @@

    Expression: Expression
    SourceRange: SourceRange
-


FIX: Switch back to using $interval instead of $timeout

--- src/app/visualisations/visualisations.coffee
+++ src/app/visualisations/visualisations.coffee
@@ -7,7 +7,7 @@
   'doubtfire.visualisations.task-status-pie-chart'
 ])

-.factory('Visualisation', ($timeout) ->
+.factory('Visualisation', ($interval) ->
   Visualisation = (type, opts, conf) ->
     DEFAULT_OPTS =
       objectequality: yes
@@ -53,7 +53,7 @@
     [ { chart: dirtyOpts },  dirtyConf ]

   Visualisation.refreshAll = ->
-    $timeout -> window.dispatchEvent new Event('resize')
+    $interval (() -> window.dispatchEvent(new Event('resize'))), 50, 1

   Visualisation
 )


Remove right block's leading newline when backspacing through a block break

--- src/trix/models/document.coffee
+++ src/trix/models/document.coffee
@@ -114,32 +114,41 @@
       block.copyWithText(block.text.insertTextAtPosition(text, offset))

   removeTextAtRange: (range) ->
-    [startPosition, endPosition] = range = normalizeRange(range)
+    [leftPosition, rightPosition] = range = normalizeRange(range)
     return this if rangeIsCollapsed(range)
-
-    leftLocation = @locationFromPosition(startPosition)
+    [leftLocation, rightLocation] = @locationRangeFromRange(range)
+
     leftIndex = leftLocation.index
+    leftOffset = leftLocation.offset
     leftBlock = @getBlockAtIndex(leftIndex)
-    leftText = leftBlock.text.getTextAtRange([0, leftLocation.offset])
-
-    rightLocation = @locationFromPosition(endPosition)
+
     rightIndex = rightLocation.index
+    rightOffset = rightLocation.offset
     rightBlock = @getBlockAtIndex(rightIndex)
-    rightText = rightBlock.text.getTextAtRange([rightLocation.offset, rightBlock.getLength()])
-
-    text = leftText.appendText(rightText)
-
-    removingLeftBlock = leftIndex isnt rightIndex and leftLocation.offset is 0
-    useRightBlock = removingLeftBlock and leftBlock.getAttributeLevel() >= rightBlock.getAttributeLevel()
-
-    if useRightBlock
-      block = rightBlock.copyWithText(text)
-    else
-      block = leftBlock.copyWithText(text)
-
-    blocks = @blockList.toArray()
-    affectedBlockCount = rightIndex + 1 - leftIndex
-    blocks = @blockList.splice(leftIndex, affectedBlockCount, block)
+
+    removeRightNewline = rightPosition - leftPosition is 1 and
+      leftBlock.getBlockBreakPosition() is leftOffset and
+      rightBlock.getBlockBreakPosition() isnt rightOffset and
+      rightBlock.text.getStringAtPosition(rightOffset) is "\n"
+
+    if removeRightNewline
+      blocks = @blockList.editObjectAtIndex rightIndex, (block) ->
+        block.copyWithText(block.text.removeTextAtRange([rightOffset, rightOffset + 1]))
+    else
+      leftText = leftBlock.text.getTextAtRange([0, leftOffset])
+      rightText = rightBlock.text.getTextAtRange([rightOffset, rightBlock.getLength()])
+      text = leftText.appendText(rightText)
+
+      removingLeftBlock = leftIndex isnt rightIndex and leftOffset is 0
+      useRightBlock = removingLeftBlock and leftBlock.getAttributeLevel() >= rightBlock.getAttributeLevel()
+
+      if useRightBlock
+        block = rightBlock.copyWithText(text)
+      else
+        block = leftBlock.copyWithText(text)
+
+      affectedBlockCount = rightIndex + 1 - leftIndex
+      blocks = @blockList.splice(leftIndex, affectedBlockCount, block)

     new @constructor blocks


Make some of the AttributeTemplate code clearer

--- src/component/AttributeTemplateParser.coffee
+++ src/component/AttributeTemplateParser.coffee
@@ -57,10 +57,12 @@
                     previousValue = value
                     paddedValue = localZeroPad value

-                    displayElem.innerHTML = paddedValue for displayElem in filteredDisplay
+                    (displayElem.innerHTML = paddedValue) for displayElem in filteredDisplay

-                    if !significant #Once a value is no longer significant it cannot return, so one-way is acceptable.
-                        hidableElem.style.display = "none" for hidableElem in filteredHidable
+                    #Once a value is no longer significant it cannot return,
+                    #so the reverse case does not need to be handled
+                    if not significant
+                        (hidableElem.style.display = "none") for hidableElem in filteredHidable
                 false
         else
             false


hashed asset name includes old file base name

--- optimize.coffee
+++ optimize.coffee
@@ -7,6 +7,7 @@
 join         = require('path').join
 minifyImage  = require './minify-image'
 minifyScript = require './minify-javascript'
+parsePath    = require('path').parse
 parseCssUrl  = require './parse-css-url'
 extname      = require('path').extname

@@ -40,8 +41,8 @@
           minified = minifyImage(absPath, image)
           hashed = hash(minified)

-          ext = extname absPath
-          out = join outputPath, "#{hashed}#{ext}"
+          parsed = parsePath absPath
+          out = join outputPath, "#{parsed.name}-#{hashed}#{parsed.ext}"
           fs.writeFileSync out, minified
           renamed[absPath] = out
         else if type is 'javascript'
@@ -49,8 +50,8 @@
           minified = minifyScript(absPath, script)
           hashed = hash(minified)

-          ext = extname absPath
-          out = join outputPath, "#{hashed}#{ext}"
+          parsed = parsePath absPath
+          out = join outputPath, "#{parsed.name}-#{hashed}#{parsed.ext}"
           fs.writeFileSync out, minified
           renamed[absPath] = out

@@ -67,8 +68,8 @@
           minified = minifyImage(absPath, image)
           hashed = hash(minified)

-          ext = extname absPath
-          out = join outputPath, "#{hashed}#{ext}"
+          parsed = parsePath absPath
+          out = join outputPath, "#{parsed.name}-#{hashed}#{parsed.ext}"
           fs.writeFileSync out, minified
           renamed[absPath] = out

@@ -83,9 +84,8 @@
     text = fs.readFileSync path, 'utf8'
     minified = ren path, text, assetsPath, renamed
     hashed = hash(minified)
-    ext = extname path
-
-    out = join outputPath, "#{hashed}#{ext}"
+    parsed = parsePath path
+    out = join outputPath, "#{parsed.name}-#{hashed}#{parsed.ext}"
     fs.writeFileSync out, minified, 'utf8'
     renamed[path] = out


修复textarea 绑定数据之后 dom无法初始化的问题

修复textarea 绑定数据之后 dom无法初始化的问题

--- src/widget/edit/textarea.coffee
+++ src/widget/edit/textarea.coffee
@@ -42,7 +42,7 @@
 		return

 	_initDom: (dom)->
-		super(dom)
+
 		@_doms ?= {}
 		unless dom.nodeName is "TEXTAREA"
 			input = $.xCreate(
@@ -101,6 +101,7 @@
 			if @fire("keyPress", @, arg) == false then return
 			if event.keyCode == 13 && isIE11 then doPost()
 		)
+		super(dom)
 		return

 	_refreshInputValue: (value) ->


Clearer messages

Clearer messages

--- src/yubikey-invalidation.coffee
+++ src/yubikey-invalidation.coffee
@@ -23,15 +23,17 @@
   dvorakCharset  = "jxe.uidchtnbpygk"
   dvorakOtpRegex = new RegExp("^([#{dvorakCharset}]{44})$")

+  messagePrefix = "Was that your YubiKey? :trollface:"
+
   generateNonce = ->
     crypto.pseudoRandomBytes(16).toString('hex')

   invalidateOtp = (msg, otp) ->
     https.get "#{validationUrl}?id=#{apiId}&otp=#{otp}&nonce=#{generateNonce()}", (res) ->
       if res.statusCode != 200
-        msg.reply "I tried to invalidate that OTP for you, but I got a #{res.statusCode} error from the server :cry:"
+        msg.reply "#{messagePrefix} I tried to invalidate that OTP for you, but I got a #{res.statusCode} error from the server :cry:"
       else
-        msg.reply "Was that your Yubikey :trollface:? I went ahead and invalidated that OTP for you :lock:"
+        msg.reply "#{messagePrefix} I went ahead and invalidated that OTP for you :lock:"

   invalidateDvorakOtp = (msg, dvorakOtp) ->
     otp = dvorakOtp
@@ -42,11 +44,8 @@

   missingEnviroment = (msg) ->
     missingSomething = false
-    unless validationUrl?
-      msg.send "Yubikey Validation URL is missing: ensure that HUBOT_YUBIKEY_VALIDATION_URL is set"
-      missingSomething = true
     unless apiId?
-      msg.send "Yubikey Api ID is missing: ensure that HUBOT_YUBIKEY_API_ID is set"
+      msg.reply "#{messagePrefix} I'd like to invalidate that OTP for you, but I'm missing the HUBOT_YUBIKEY_API_ID environment variable. Maybe your local hubot maintainer can help you?"
       missingSomething = true
     return missingSomething


Fix type in parsing method name in date range control

--- src/coffee/cilantro/ui/field/controls/date.coffee
+++ src/coffee/cilantro/ui/field/controls/date.coffee
@@ -22,7 +22,7 @@

         parseMinStat: (value) ->
             return @parseMaxStat(value)
-        parseMaxState: (value) ->
+        parseMaxStat: (value) ->
             return value.replace(/-/g, "/")

         # We need to override the default behavior here so that the value is


:art: Remove unnecessary require

:art: Remove unnecessary require

--- lib/composer.coffee
+++ lib/composer.coffee
@@ -1,7 +1,6 @@
 _ = require 'underscore-plus'
 fs = require 'fs-plus'
 path = require 'path'
-ConfigSchema = require './config-schema'

 module.exports =
 class Composer


update

update

--- src/copy-jizuradb-to-Hollerith2-format.coffee
+++ src/copy-jizuradb-to-Hollerith2-format.coffee
@@ -390,7 +390,7 @@
         target.add ( XNCHR.as_uchr ic ) for ic in ics
     #.......................................................................................................
     if end?
-      resolve_ics glyph                         for glyph of ics_by_glyph
+      resolve_ics glyph                         for glyph        of ics_by_glyph
       ics_by_glyph[ glyph ] = Array.from entry  for glyph, entry of ics_by_glyph
       for glyph, ics of ics_by_glyph
         send [ glyph, 'component/uchr', ics, ]
@@ -409,17 +409,17 @@
   ### Immediate Constituents (ICs) ###
   factors_by_factor = {}
   seen_glyphs       = new Set()
-  glyph_count       = 0
-  #.........................................................................................................
-  resolve_ics_to_factors = ( glyph ) =>
-    return if seen_glyphs.has glyph
-    seen_glyphs.add glyph
-    if ( entry = factors_by_factor[ glyph ] )?
-      for ic in Array.from entry.keys()
-        resolve_ics_to_factors ic
-        if ( sub_entry = factors_by_factor[ ic ] )?
-          for sub_ic in Array.from sub_entry
-            entry.add sub_ic
+  #.........................................................................................................
+  # resolve_ics_to_factors = ( glyph ) =>
+  #   return if seen_glyphs.has glyph
+  #   seen_glyphs.add glyph
+  #   return unless factors.has glyph
+  #   if ( entry = factors_by_factor[ glyph ] )?
+  #     for ic in Array.from entry.keys()
+  #       resolve_ics_to_factors ic
+  #       if ( sub_entry = factors_by_factor[ ic ] )?
+  #         for sub_ic in Array.from sub_entry
+  #           entry.add sub_ic
   #.........................................................................................................
   return $ ( phrase, send, end ) =>
     #.......................................................................................................
@@ -430,24 +430,24 @@
       ### TAINT collecting ICs from outer glyphs might aid in resolving more inner glyphs ###
       return unless XNCHR.is_inner_glyph glyph
       glyph         = XNCHR.as_uchr glyph
-      glyph_count  += +1
       for formula, formula_idx in obj
         ics     = IDLX.find_all_non_operators formula
         target  = factors_by_factor[ glyph ]?= new Set()
         target.add ( XNCHR.as_uchr ic ) for ic in ics
     #.......................................................................................................
     if end?
-      resolve_ics_to_factors glyph                         for glyph of factors_by_factor
-      factors_by_factor[ glyph ] = Array.from entry  for glyph, entry of factors_by_factor
-      # for glyph, ics of factors_by_factor
-      #   send [ glyph, 'component/uchr', ics, ]
+      # factors_lst = Array.from factors.keys()
+      # resolve_ics_to_factors glyph for glyph in factors_lst
+      # for glyph in factors_lst
+      #   sub_factors = Array.from factors_by_factor[ glyph ]
+      #   send [ glyph, 'factor/subfactor/uchr', sub_factors, ]
       end()
     #.......................................................................................................
     return null

 #-----------------------------------------------------------------------------------------------------------
 ### TAINT code duplication ###
-@$add_lineup_postfixes = ->
+@$add_lineup_back_and_forwards = ->
   #.........................................................................................................
   return $ ( phrase, send ) =>
     #.......................................................................................................
@@ -716,7 +716,7 @@
       # .pipe @$add_guide_pairs             factor_infos
       .pipe @$add_factor_membership       factor_infos
       .pipe @$add_components()
-      .pipe @$add_lineup_postfixes()
+      .pipe @$add_lineup_back_and_forwards()
       .pipe @$add_factorial_factors       factor_infos
       .pipe @$add_sims()
       # .pipe D.$show()


add debug flag to enable source maps

add debug flag to enable source maps

--- bokehjs/gulp/tasks/scripts.coffee
+++ bokehjs/gulp/tasks/scripts.coffee
@@ -14,6 +14,7 @@
 gulp.task "scripts:build", ->
   opts =
     extensions: [".coffee", ".eco"]
+    debug: true

   browserified = transform (filename) ->
     browserify filename, opts


Update formatting.

Update formatting.

--- src/checkout.coffee
+++ src/checkout.coffee
@@ -15,24 +15,23 @@
 theme = require './utils/theme'

 # checkout
-#  id: the id refered to by a anchor tag button
-#  api: object of API Class
-#  order: object of Order Class
+#  id:     the id refered to by a anchor tag button
+#  api:    object of API Class
+#  order:  object of Order Class
 #  config: config object such as:
 #    {
 #      screens: [screens.card],
-#      callToActions: ['Pre-order'],  #Put what you want the button to say here
+#      callToActions: ['Pre-order'],
 #    }
 #
-checkout = (id, api, order, user = new User(), config = {})->
-  config.callToActions   = config.callToActions || ['Pre-Order', 'Confirm']
+checkout = (id, api, order, user = new User(), config = {}) ->
+  config.callToActions  = config.callToActions  || ['Pre-Order', 'Confirm']
   config.thankYouHeader = config.thankYouHeader || 'Thank You'
   config.thankYouBody   = config.thankYouBody   || 'You will receive a confirmation email for your preorder.'
   config.shareHeader    = config.shareHeader    || 'Follow us to get the latest updates'
   config.screens        = config.screens        || [screens.card, screens.shipping]

-  # config.screens        = config.screens.append()
-
+  # Configure social sharing
   config.facebook   = config.facebook   || ''
   config.googlePlus = config.googlePlus || ''
   config.twitter    = config.twitter    || ''


fixup viewport system to eliminate edge cases

--- src/javascript/game/systems/viewport_system.coffee
+++ src/javascript/game/systems/viewport_system.coffee
@@ -48,21 +48,21 @@

     viewportArea = worldMap.getAreaAt(viewportPosition.get('x'), viewportPosition.get('y'))
     targetArea = worldMap.getAreaAt(targetPosition.get('x'), targetPosition.get('y'))
-    if !targetArea?
-      console.log "!! viewport_system: WTF cannot find viewportArea based on viewportPosition",viewportPosition.toJS()
+    # if !viewportArea?
+    #   console.log "!! viewport_system: WTF cannot find viewportArea based on viewportPosition",viewportPosition.toJS()
+    #   throw new Error("Can't get viewportArea (see console error)")
     if !targetArea?
       console.log "!! viewport_system: WTF cannot find targetArea based on targetPosition",targetPosition.toJS()
+      throw new Error("Can't get targetArea (see console error)")

-    if viewportArea? and targetArea? and (viewportArea.name != targetArea.name)
-      if withinShuttlingDistance(targetPosition,viewportPosition)
-        @_shuttleToNewArea(worldMap, targetPosition,targetArea, viewportPosition)
-        return
-      else
-        # This will cause trackTarget to directly calculate the proper new viewport
-        viewportArea = targetArea
+    if viewportArea? and targetArea? and (viewportArea.name != targetArea.name) and withinShuttlingDistance(targetPosition,viewportPosition)
+      @_shuttleToNewArea(worldMap, targetPosition,targetArea, viewportPosition)

-    newViewportPos = trackTarget(targetPosition, viewportPosition, viewportArea, config)
-    @updateComp newViewportPos
+    else
+      # just jump to targetArea
+      viewportArea = targetArea
+
+      @updateComp trackTarget(targetPosition, viewportPosition, viewportArea, config)

   _shuttleToNewArea: (worldMap, targetPosition, targetArea, viewportPosition ) ->


fixed identation

fixed identation

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -34,7 +34,7 @@

 gulp.task 'browserify', ->
   browserify
-      extensions: ['.coffee', '.js']
+    extensions: ['.coffee', '.js']
     .transform coffeeify
     .require(src.main, { expose: module.shortcut})
     .ignore('coffee-script')


Switch Back to Default HAML Compiler

Switch Back to Default HAML Compiler

The output of the other one was getting wonky.

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -11,7 +11,7 @@

 gulp.task 'haml', ->
   gulp.src "#{SRC}/haml/**/*.haml"
-    .pipe haml compiler: 'visionmedia'
+    .pipe haml()
     .pipe gulp.dest DEST

 gulp.task 'coffee', ->


Update NWJS to V0.12.3

Update NWJS to V0.12.3

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -19,7 +19,7 @@
     gulp.src './src/**'
       .pipe $.nwBuilder
         platforms: [platform]
-        version: '0.12.2'
+        version: '0.12.3'
         winIco: if process.argv.indexOf('--noicon') > 0 then undefined else './assets-windows/icon.ico'
         macIcns: './assets-osx/icon.icns'
         macZip: true


gulp のサーバーで ya 込みでデバッグできるように変更

gulp のサーバーで ya 込みでデバッグできるように変更

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -136,10 +136,12 @@
 gulp.task 'webserver', ->
   gulp.src 'dist'
     .pipe webserver
-#      host: 'yurie-local.pine.moe'
       livereload: true
       open: false
-#      https: true
+      proxies: [
+        source: '/ya'
+        target: 'http://localhost:3000/'
+      ]

 gulp.task 'default', (cb) ->
   runSequence(
@@ -155,7 +157,7 @@
     ['jade-prod', 'less-prod', 'vendor-prod', 'browserify-prod'],
     cb)

-gulp.task 'watch', (cb) ->
+gulp.task 'watch', ['webserver'], (cb) ->
   runSequence 'default', ->
     gulp.watch ['src/js/**/*.js'], ['browserify']


Remove unused gulp plugin requires

Remove unused gulp plugin requires

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -7,11 +7,9 @@
 karma = require('karma').server
 rename = require 'gulp-rename'
 uglify = require 'gulp-uglify'
-wrap = require 'gulp-wrap-umd'
 fs = require 'fs'
 _ = require 'lodash'
 header = require 'gulp-header'
-amdclean = require 'gulp-amdclean'
 coffeeify = require 'coffeeify'

 karmaConfiguration =


remove extraneous command from deploy task

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -51,5 +51,4 @@
   'sassProduction',
   'coffeeProduction',
   'imagesProduction',
-  'api'
 ]


Get rid of merge option

Get rid of merge option

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -9,8 +9,9 @@
 UPLOAD = 'node aws/upload.js'

 # Git functions
+handler = (err) -> throw err if err

-branch = -> git.revParse 'HEAD', {args: "--abbrev-ref"}
+branch = -> git.revParse 'HEAD', {args: "--abbrev-ref"}, handler

 # Create bundles using browerify

@@ -57,12 +58,3 @@
 # Tag and upload new feature

 gulp.task 'ship', ['tag', 'upload']
-
-# Push branch changes to master and github
-gulp.task 'merge', ->
-  current = branch()
-  git.checkout 'master'
-  git.merge current, (err) ->
-    throw err if err
-  git.checkout current
-


Updated Regex to exclude mailto: links only

The rationale being it's covering all of the cases while still having a good performance.
Tracking a non-standard URL is not a user experience problem in a way. It's embedded in a <a href> tag.

--- src/regexp-utils.coffee
+++ src/regexp-utils.coffee
@@ -34,15 +34,17 @@
   # 5. the closing tag
   linkTagRegex: -> new RegExp(/(<a.*?href\s*?=\s*?['"])(.*?)(['"].*?>)([\s\S]*?)(<\/a>)/gim)

-  # Test cases: https://regex101.com/r/cK0zD8/2
-  # Catches link tags containing a valid URL using the Gruber Regex.
+  # Test cases: https://regex101.com/r/cK0zD8/3
+  # Catches link tags containing which are:
+  # - Non empty
+  # - Not a mailto: link
   # Returns the following capturing groups:
   # 1. start of the opening a tag to href="
-  # 2. The contents of the href without quotes if it's a valid URL
+  # 2. The contents of the href without quotes
   # 3. the rest of the opening a tag
   # 4. the contents of the a tag
   # 5. the closing tag
-  urlLinkTagRegex: -> new RegExp(/(<a.*?href\s*?=\s*?['"])((?:https?:\/\/|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}\/)(?:[^\s()<>]+|\(([^\s()<>]+|(\([^\s()<>]+\)))*\))+(?:\(([^\s()<>]+|(\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:'".,<>?«»“”‘’]))(['"].*?>)([\s\S]*?)(<\/a>)/gim)
+  urlLinkTagRegex: -> new RegExp(/(<a.*?href\s*?=\s*?['"])((?!mailto).+)(['"].*?>)([\s\S]*?)(<\/a>)/gim)

   # https://regex101.com/r/zG7aW4/3
   imageTagRegex: -> /<img\s+[^>]*src="([^"]*)"[^>]*>/g


Fix purging of handlers

Fix purging of handlers

--- handlers.coffee
+++ handlers.coffee
@@ -88,9 +88,14 @@
 			handler = loadedHandlers[name]
 			unloadHandler name, (err) ->
 				unless err?
-					handler.purge -> callback?()
+					handler.purge (err) ->
+						if err?
+							debug "Purging of handler “#{name}” failed: #{err}"
+							callback? err
+						else
+							callback?()
 				else
-					callback?()
+					callback? err
 		else
 			winston.warn "Handler “#{name}” doesn't have a purge function!", name
 			callback? "noFunc"


fix test

fix test

--- spec/concurrency_spec.coffee
+++ spec/concurrency_spec.coffee
@@ -17,7 +17,7 @@
   beforeEach -> instrumentedDelay.reset()

   it 'should be finished when empty', (success) ->
-    new Semaphore(2).finished(success).done()
+    new Semaphore(2).finished().then(success).done()

   it 'should allow at most S jobs to run concurrently', (success) ->
     sem = new Semaphore 2


Remove debugging message

Remove debugging message

--- spec/linter-mypy_project_name-spec.coffee
+++ spec/linter-mypy_project_name-spec.coffee
@@ -34,21 +34,21 @@

       # Create a project root (aka isolated temp folder)
       @ProjectRoot = path.join(os.tmpdir(), "linter-mypy" + Math.floor(Math.random() * 9007199254740991))
-      console.log("Project root: " + @ProjectRoot)

       #Simulate a pseudo project #1 containing one file
       @firstProjectName = 'first_python_project'
       @firstProjectPath = path.join(@ProjectRoot, @firstProjectName)
       @firstFilePath = path.join(@firstProjectPath, 'src', 'first_file.py')
-      console.log("FirstFilePath: " + @firstFilePath)

       #Simulate a pseudo project #2 containing one file
       @secondProjectName = 'second_python_project'
       @secondProjectPath = path.join(@ProjectRoot, @secondProjectName)
       @secondFilePath = path.join(@secondProjectPath, 'src', 'second_file.py')
-      console.log("SecondFilePath: " + @secondFilePath)

-      # Note: The method atom.project.relativizePath(filepath) which is internally use by the tested method requires the folders to exist on HD)
+      # Note: The methods:
+      # * atom.project.relativizePath(filepath)
+      # * atom.project.setPaths
+      # Requires the folders to exist on HD)
       fs.mkdirSync(@ProjectRoot)
       fs.mkdirSync(@firstProjectPath)
       fs.mkdirSync(path.join(@firstProjectPath, 'src'))
@@ -79,7 +79,6 @@
     it "should return the first project's name when given the variable", ->
       firstProjectName = @firstProjectName
       loadedFile = @firstFilePath
-      console.log("loadedFile: " + loadedFile)
       result = LinterMyPystyle.resolvePath("$PROJECT_NAME", loadedFile)
       # Return this project's name (i.e. first_python_project)
       expect(result).toBe(firstProjectName)
@@ -87,7 +86,6 @@
     it "should return the second project's name when given the variable", ->
       secondProjectName = @secondProjectName
       loadedFile = @secondFilePath
-      console.log("loadedFile: " + loadedFile)
       result = LinterMyPystyle.resolvePath("$PROJECT_NAME", loadedFile)
       # Return this project's name (i.e. second_python_project)
       expect(result).toBe(secondProjectName)
@@ -96,7 +94,6 @@
       loadedFile = @firstFilePath
       targetPath = '/home/user/.virtualenvs/$PROJECT_NAME/bin/python'
       expectedPath = '/home/user/.virtualenvs/first_python_project/bin/python'
-      console.log("loadedFile: " + loadedFile)
       result = LinterMyPystyle.resolvePath(targetPath, loadedFile)
       expect(result).toBe(expectedPath)

@@ -104,7 +101,6 @@
       loadedFile = @secondFilePath
       targetPath = '/home/user/.virtualenvs/$PROJECT_NAME/bin/python'
       expectedPath = '/home/user/.virtualenvs/second_python_project/bin/python'
-      console.log("loadedFile: " + loadedFile)
       result = LinterMyPystyle.resolvePath(targetPath, loadedFile)
       expect(result).toBe(expectedPath)


Focus to search field on /search on ready

--- _coffee/search.coffee
+++ _coffee/search.coffee
@@ -25,6 +25,9 @@
     $('#q').val urlQ
   else
     sView.renderBlank()
+
+  # Focus to search field
+  $('#q').focus()

   # Bind to keyup events on the search box
   $('#q').keyup debounce ->


convert project name to underscored path in repl script

--- natal.coffee
+++ natal.coffee
@@ -53,6 +53,10 @@
   line.match(/\[(.+)\]/)[1]

+toUnderscored = (s) ->
+  s.replace(camelRx, '$1_$2').toLowerCase()
+
+
 writeConfig = (config) ->
   try
     fs.writeFileSync '.natal', JSON.stringify config, null, 2
@@ -105,7 +109,7 @@

 init = (projName) ->
   projNameHyph = projName.replace(camelRx, '$1-$2').toLowerCase()
-  projNameUs   = projName.replace(camelRx, '$1_$2').toLowerCase()
+  projNameUs   = toUnderscored projName

   try
     log "Creating #{projName}", 'bgMagenta'
@@ -354,7 +358,7 @@
             :watch-fn
               (fn []
                 (cljs.repl/load-file repl-env
-                  \"src/#{name}/core.cljs\"))
+                  \"src/#{toUnderscored name}/core.cljs\"))
             :analyze-path \"src\"))
           """),
       cwd:   process.cwd()


Revert "switched to canonical two-space indentation in __bind helper"

This reverts commit 6a40807330f4c742b3216a010e831f87794e7463.

--- nodes.coffee
+++ nodes.coffee
@@ -1770,16 +1770,16 @@
   # Create a function bound to the current value of "this".
   bind: '''
     (Function.prototype.bind ? Function.prototype.call.bind(Function.prototype.bind) : function(fn, me){
-      return function bound(){
-        if (!(this instanceof bound))
-          return fn.apply(me, arguments);
-        var ctor = function(){};
-        ctor.prototype = fn.prototype;
-        var obj = new ctor();
-        fn.apply(obj, arguments);
-        return obj;
-      };
-    })
+		return function bound(){
+			if (!(this instanceof bound))
+				return fn.apply(me, arguments);
+			var ctor = function(){};
+			ctor.prototype = fn.prototype;
+			var obj = new ctor();
+			fn.apply(obj, arguments);
+			return obj;
+		};
+	})
   '''

   # Discover if an item is in an array.


fixes #1591: splatted expressions in destructuring assignment must be assignable

--- nodes.coffee
+++ nodes.coffee
@@ -1005,6 +1005,8 @@
           else
             idx = if obj.this then obj.properties[0].name else obj
       if not splat and obj instanceof Splat
+        unless obj.name.unwrapAll().isAssignable()
+          throw SyntaxError "\"#{ obj.name.compile(o) }\" cannot be assigned."
         name = obj.name.unwrap().value
         val = "#{olen} <= #{vvar}.length ? #{ utility 'slice' }.call(#{vvar}, #{i}"
         if rest = olen - i - 1


Update Editor Beautification to use the centralized options manager

--- lib/beautify.coffee
+++ lib/beautify.coffee
@@ -18,13 +18,7 @@
 PlainMessageView = null
 editorconfig = null
 $ = null
-
 #MessageView = require "./message-view"
-findFileResults = {}
-
-# CLI
-getUserHome = ->
-  process.env.HOME or process.env.HOMEPATH or process.env.USERPROFILE

 # function cleanOptions(data, types) {
 #   nopt.clean(data, types);
@@ -50,84 +44,6 @@
     editor.addCursorAtBufferPosition bufferPosition
   return

-verifyExists = (fullPath) ->
-  fs ?= require("fs")
-  (if fs.existsSync(fullPath) then fullPath else null)
-
-# Storage for memoized results from find file
-# Should prevent lots of directory traversal &
-# lookups when liniting an entire project
-
-###
-Searches for a file with a specified name starting with
-'dir' and going all the way up either until it finds the file
-or hits the root.
-
-@param {string} name filename to search for (e.g. .jshintrc)
-@param {string} dir directory to start search from (default:
-current working directory)
-@param {boolean} upwards should recurse upwards on failure? (default: true)
-
-@returns {string} normalized filename
-###
-findFile = (name, dir, upwards=true) ->
-  path ?= require("path")
-  dir = dir or process.cwd()
-  filename = path.normalize(path.join(dir, name))
-  return findFileResults[filename] if findFileResults[filename] isnt `undefined`
-  parent = path.resolve(dir, "../")
-  if verifyExists(filename)
-    findFileResults[filename] = filename
-    return filename
-  if dir is parent
-    findFileResults[filename] = null
-    return null
-  if upwards
-    findFile name, parent
-  else
-    return null
-
-###
-Tries to find a configuration file in either project directory
-or in the home directory. Configuration files are named
-'.jsbeautifyrc'.
-
-@param {string} config name of the configuration file
-@param {string} file path to the file to be linted
-@param {boolean} upwards should recurse upwards on failure? (default: true)
-
-@returns {string} a path to the config file
-###
-findConfig = (config, file, upwards=true) ->
-  path ?= require("path")
-  dir = path.dirname(path.resolve(file))
-  envs = getUserHome()
-  home = path.normalize(path.join(envs, config))
-  proj = findFile(config, dir, upwards)
-  return proj if proj
-  return home if verifyExists(home)
-  null
-getConfigOptionsFromSettings = (langs) ->
-  config = atom.config.get('atom-beautify')
-  options = {}
-  # console.log(langs, config);
-  # Iterate over keys of the settings
-  _.every _.keys(config), (k) ->
-    # Check if keys start with a language
-    p = k.split("_")[0]
-    idx = _.indexOf(langs, p)
-    # console.log(k, p, idx);
-    if idx >= 0
-      # Remove the language prefix and nest in options
-      lang = langs[idx]
-      opt = k.replace(new RegExp("^" + lang + "_"), "")
-      options[lang] = options[lang] or {}
-      options[lang][opt] = config[k]
-      # console.log(lang, opt);
-    true
-  # console.log(options);
-  options
-
 beautify = ({onSave})->
   path ?= require("path")
   MessagePanelView ?= require('atom-message-panel').MessagePanelView
@@ -147,37 +63,7 @@
           message: e.message,
           className: 'text-error'
         }))
-  # Look for .jsbeautifierrc in file and home path, check env variables
-  getConfig = (startPath, upwards=true) ->
-    # Verify that startPath is a string
-    startPath = (if (typeof startPath is "string") then startPath else "")
-    return {} unless startPath
-    # Get the path to the config file
-    configPath = findConfig(".jsbeautifyrc", startPath, upwards)
-    externalOptions = undefined
-    if configPath
-      fs ?= require("fs")
-      contents = fs.readFileSync(configPath,
-        encoding: "utf8"
-      )
-      unless contents
-        externalOptions = {}
-      else
-        try
-          strip ?= require("strip-json-comments")
-          externalOptions = JSON.parse(strip(contents))
-        catch e
-          console.log "Failed parsing config as JSON: " + configPath
-          # Attempt as YAML
-          try
-            yaml ?= require("js-yaml")
-            externalOptions = yaml.safeLoad(contents)
-          catch e
-            console.log "Failed parsing config as YAML: " + configPath
-            externalOptions = {}
-    else
-      externalOptions = {}
-    externalOptions
+
   # Get the path to the config file
   # All of the options
   # Listed in order from default (base) to the one with the highest priority
@@ -225,67 +111,11 @@

   # Get current editor
   editor = atom.workspace.getActiveEditor()
-  # Get current Atom editor configuration
   isSelection = !!editor.getSelectedText()
-  softTabs = editor.softTabs
-  tabLength = editor.getTabLength()
-  editorOptions =
-    indent_size: (if softTabs then tabLength else 1)
-    indent_char: (if softTabs then " " else "\t")
-    indent_with_tabs: not softTabs
-  configOptions = getConfigOptionsFromSettings(languages)
-
   # Get editor path and configurations for paths
   editedFilePath = editor.getPath()
-
-  # Get configuration in User's Home directory
-  userHome = getUserHome()
-  # FAKEFILENAME forces `path` to treat as file path and it's parent directory
-  # is the userHome. See implementation of findConfig
-  # and how path.dirname(DIRECTORY) returns the parent directory of DIRECTORY
-  homeOptions = getConfig(path.join(userHome,"FAKEFILENAME"), false)
-
-  if editedFilePath?
-    # Handle EditorConfig options
-    # http://editorconfig.org/
-    editorconfig ?= require('editorconfig');
-    editorConfigOptions = editorconfig.parse(editedFilePath);
-    # Transform EditorConfig to Atom Beautify's config structure and naming
-    if editorConfigOptions.indent_style is 'space'
-      editorConfigOptions.indent_char = " "
-      # if (editorConfigOptions.indent_size)
-      #   editorConfigOptions.indent_size = config.indent_size
-    else if editorConfigOptions.indent_style is 'tab'
-      editorConfigOptions.indent_char = "\t"
-      editorConfigOptions.indent_with_tabs = true
-      if (editorConfigOptions.tab_width)
-          editorConfigOptions.indent_size = editorConfigOptions.tab_width
-
-    # Get all options in configuration files from this directory upwards to root
-    projectOptions = []
-    p = path.dirname(editedFilePath)
-    # Check if p is root (top directory)
-    while p isnt path.resolve(p,"../")
-      # Get config for p
-      pf = path.join(p, "FAKEFILENAME")
-      pc = getConfig(pf, false)
-      # Add config for p to project's config options
-      projectOptions.push(pc)
-      # console.log p, pc
-      # Move upwards
-      p = path.resolve(p,"../")
-  else
-    editorConfigOptions = {}
-    projectOptions = []
-
-  # Combine all options together
-  allOptions = [
-    editorOptions
-    configOptions
-    homeOptions
-    editorConfigOptions
-  ]
-  allOptions = allOptions.concat(projectOptions)
+  # Get all options
+  allOptions = options.getOptionsForPath(editedFilePath, editor)

   # Get current editor's text
   text = undefined


remove eval_langs

remove eval_langs

--- src/scripts/eval.coffee
+++ src/scripts/eval.coffee
@@ -23,8 +23,8 @@
 module.exports = (robot) ->
   get_languages = (robot, callback) ->
     callback or= () ->
-    if not ready or robot.brain?.data?.eval_langs
-      callback(robot.brain?.data?.eval_langs || {})
+    if not ready
+      callback {}
       return

     url = "http://api.dan.co.jp/lleval.cgi"
@@ -34,7 +34,6 @@
       .query(q: "1")
       .get() (err, res, body) ->
         langs = JSON.parse(body)
-        robot.brain.data.eval_langs = langs
         callback(langs)
         robot.logger.info "Brain received eval language list #{util.inspect(langs)}"


Update link to API

Update link to API

The old link didn't work with new API keys

--- src/scripts/time.coffee
+++ src/scripts/time.coffee
@@ -13,7 +13,7 @@
 #
 # Notes
 #   Request an WWO API KEY in http://www.worldweatheronline.com/
-#   The url is likely to be something like http://api.worldweatheronline.com/free/v1/tz.ashx
+#   The url is likely to be something like http://api.worldweatheronline.com/free/v2/tz.ashx
 #
 #   City parameter can be:
 #     city


:white_check_mark: #188 Forwarding family and AnyPairAllowForwarding

--- spec/text-object-spec.coffee
+++ spec/text-object-spec.coffee
@@ -491,7 +491,6 @@
         it "case-2 normal", -> check close, 'd', text: textFinal, cursor: [0, 1]
         it "case-3 visual", -> check open, 'v', {selectedText}
         it "case-4 visual", -> check close, 'v', {selectedText}
-
   describe "AngleBracket", ->
     describe "inner-angle-bracket", ->
       beforeEach ->
@@ -553,6 +552,133 @@
         it "case-2 normal", -> check close, 'd', text: textFinal, cursor: [0, 1]
         it "case-3 visual", -> check open, 'v', {selectedText}
         it "case-4 visual", -> check close, 'v', {selectedText}
+
+  describe "AllowForwarding family", ->
+    beforeEach ->
+      atom.keymaps.add "text",
+        'atom-text-editor.vim-mode-plus.operator-pending-mode, atom-text-editor.vim-mode-plus.visual-mode':
+          'i }':  'vim-mode-plus:inner-curly-bracket-allow-forwarding'
+          'i >':  'vim-mode-plus:inner-angle-bracket-allow-forwarding'
+          'i ]':  'vim-mode-plus:inner-square-bracket-allow-forwarding'
+          'i )':  'vim-mode-plus:inner-parenthesis-allow-forwarding'
+
+          'a }':  'vim-mode-plus:a-curly-bracket-allow-forwarding'
+          'a >':  'vim-mode-plus:a-angle-bracket-allow-forwarding'
+          'a ]':  'vim-mode-plus:a-square-bracket-allow-forwarding'
+          'a )':  'vim-mode-plus:a-parenthesis-allow-forwarding'
+
+      set
+        text: """
+        __{000}__
+        __<111>__
+        __[222]__
+        __(333)__
+        """
+    describe "inner", ->
+      it "select forwarding range", ->
+        set cursor: [0, 0]; ensure ['escape', 'vi}'], selectedText: "000"
+        set cursor: [1, 0]; ensure ['escape', 'vi>'], selectedText: "111"
+        set cursor: [2, 0]; ensure ['escape', 'vi]'], selectedText: "222"
+        set cursor: [3, 0]; ensure ['escape', 'vi)'], selectedText: "333"
+    describe "a", ->
+      it "select forwarding range", ->
+        set cursor: [0, 0]; ensure ['escape', 'va}'], selectedText: "{000}"
+        set cursor: [1, 0]; ensure ['escape', 'va>'], selectedText: "<111>"
+        set cursor: [2, 0]; ensure ['escape', 'va]'], selectedText: "[222]"
+        set cursor: [3, 0]; ensure ['escape', 'va)'], selectedText: "(333)"
+    describe "multi line text", ->
+      [textOneInner, textOneA] = []
+      beforeEach ->
+        set
+          text: """
+          000
+          000{11
+          111{22}
+          111
+          111}
+          """
+        textOneInner = """
+          11
+          111{22}
+          111
+          111
+          """
+        textOneA = """
+          {11
+          111{22}
+          111
+          111}
+          """
+      describe "forwarding inner", ->
+        it "select forwarding range", ->
+          set cursor: [1, 0]; ensure "vi}", selectedText: textOneInner
+        it "select forwarding range", ->
+          set cursor: [2, 0]; ensure "vi}", selectedText: "22"
+        it "[case-1] no forwarding open pair, fail to find", ->
+          set cursor: [0, 0]; ensure "vi}", selectedText: '0', cursor: [0, 1]
+        it "[case-2] no forwarding open pair, select enclosed", ->
+          set cursor: [1, 4]; ensure "vi}", selectedText: textOneInner
+        it "[case-3] no forwarding open pair, select enclosed", ->
+          set cursor: [3, 0]; ensure "vi}", selectedText: textOneInner
+        it "[case-3] no forwarding open pair, select enclosed", ->
+          set cursor: [4, 0]; ensure "vi}", selectedText: textOneInner
+      describe "forwarding a", ->
+        it "select forwarding range", ->
+          set cursor: [1, 0]; ensure "va}", selectedText: textOneA
+        it "select forwarding range", ->
+          set cursor: [2, 0]; ensure "va}", selectedText: "{22}"
+        it "[case-1] no forwarding open pair, fail to find", ->
+          set cursor: [0, 0]; ensure "va}", selectedText: '0', cursor: [0, 1]
+        it "[case-2] no forwarding open pair, select enclosed", ->
+          set cursor: [1, 4]; ensure "va}", selectedText: textOneA
+        it "[case-3] no forwarding open pair, select enclosed", ->
+          set cursor: [3, 0]; ensure "va}", selectedText: textOneA
+        it "[case-3] no forwarding open pair, select enclosed", ->
+          set cursor: [4, 0]; ensure "va}", selectedText: textOneA
+
+  describe "AnyPairAllowForwarding", ->
+    beforeEach ->
+      atom.keymaps.add "text",
+        'atom-text-editor.vim-mode-plus.operator-pending-mode, atom-text-editor.vim-mode-plus.visual-mode':
+          ";": 'vim-mode-plus:inner-any-pair-allow-forwarding'
+          ":": 'vim-mode-plus:a-any-pair-allow-forwarding'
+
+      set text: """
+        00
+        00[11
+        11"222"11{333}11(
+        444()444
+        )
+        111]00{555}
+        """
+    describe "inner", ->
+      it "select forwarding range within enclosed range(if exists) and skip empty pair", ->
+        set cursor: [2, 0]
+        keystroke 'v'
+        ensure ';', selectedText: "222"
+        ensure ';', selectedText: "333"
+        ensure ';', selectedText: "444()444\n"
+        ensure ';', selectedText: """
+          11
+          11"222"11{333}11(
+          444()444
+          )
+          111
+          """
+    describe "a", ->
+      it "select forwarding range within enclosed range(if exists) and skip empty pair", ->
+        set cursor: [2, 0]
+        keystroke 'v'
+        ensure ':', selectedText: '"222"'
+        ensure ':', selectedText: "{333}"
+        ensure ':', selectedText: "(\n444()444\n)"
+        ensure ':', selectedText: """
+          [11
+          11"222"11{333}11(
+          444()444
+          )
+          111]
+          """

   xdescribe "Tag", ->
     describe "inner-tag", ->


we can inline the unary-not operator if we know the value is a bool

--- lib/compiler.coffee
+++ lib/compiler.coffee
@@ -789,6 +789,17 @@
                                 return @createCall callee, [(@visitOrNull n.argument.object), (@visit fake_literal)], "result"
                         else
                                 throw "unhandled delete syntax"
+                else if n.operator is "!"
+                        arg_value =  @visitOrNull n.argument
+                        if opencode_intrinsics and @options.target_pointer_size is 64 and arg_value._ejs_returns_ejsval_bool
+                                cmp = ir.createICmpEq arg_value, (@loadBoolEjsValue true), "cmpresult"
+                                t = @loadBoolEjsValue true
+                                f = @loadBoolEjsValue false
+                                rv = ir.createSelect cmp, f, t, "sel"
+                                rv._ejs_returns_ejsval_bool = true
+                                rv
+                        else
+                                @createCall callee, [arg_value], "result"
                 else
                         if not callee
                                 throw "Internal error: unary operator '#{n.operator}' not implemented"
@@ -1416,7 +1427,9 @@
                         cmp = ir.createICmpEq mask, (consts.int64_lowhi 0xfffa8000, 0x00000000), "cmpresult"
                         t = @loadBoolEjsValue true
                         f = @loadBoolEjsValue false
-                        ir.createSelect cmp, t, f, "sel"
+                        rv = ir.createSelect cmp, t, f, "sel"
+                        rv._ejs_returns_ejsval_bool = true
+                        rv
                 else
                         @createCall @ejs_runtime.typeof_is_string, [@visitOrNull exp.arguments[0]], "is_string", false

@@ -1431,7 +1444,7 @@

         handleTypeofIsBooleanIntrinsic: (exp) ->
                 @createCall @ejs_runtime.typeof_is_boolean, [@visitOrNull exp.arguments[0]], "is_boolean", false
-
+
 class AddFunctionsVisitor extends NodeVisitor
         constructor: (@module) ->
                 super


Respect sorted order in SetSort#find

Respect sorted order in SetSort#find

--- src/set/set_sort.coffee
+++ src/set/set_sort.coffee
@@ -18,6 +18,12 @@
   stopObserving: -> @_setObserver?.stopObserving()
   toArray: -> @get('_storage')
   forEach: (iterator, ctx) -> iterator.call(ctx,e,i,this) for e,i in @get('_storage')
+
+  find: (block) ->
+    @base.registerAsMutableSource()
+    for item in @get('_storage')
+      return item if block(item)
+
   compare: (a,b) ->
     return 0 if a is b
     return 1 if a is undefined


Routes and models return async; express 'set' options should work

--- platform.coffee
+++ platform.coffee
@@ -19,8 +19,6 @@
 TODOs

 -> Instead of require()ing JSON, it should be read and JSON.parse()d
--> Models and routes should be allowed to return their stuff asynchronously
--> Find out why 'view engine' isn't getting set ('no default engine and no extension specified' for res.render)
 -> Add ability to 'watch' file changes, e.g. routes, models, app, config, etc
 ###

@@ -90,7 +88,7 @@
 				@set 'trust proxy', cfg.server.behind_proxy
 				@set 'port', port
 				@set 'views', ctx.views if ctx.views?
-				@set k, v for k, v in cfg.express unless k in ['env', 'views', 'trust proxy']
+				@set k, v for own k, v of cfg.express when k not in ['env', 'views', 'trust proxy']

 				@use express.compress() if cfg.compress
 				@use express.favicon() unless cfg.app.favicon?
@@ -162,8 +160,13 @@
 				@use express.static ctx.sources
 				@use express.static serve_dir

-				compiled_models = ctx.models cfg, winston, node_env
-				compiled_routes = ctx.routes @, compiled_models, cfg, winston, node_env
+				compiled_models = compiled_routes = undefined
+
+				ctx.models cfg, winston, node_env, (m) ->
+					compiled_models = m
+
+					ctx.routes @, compiled_models, cfg, winston, node_env, (r) ->
+						compiled_routes = r

 				rtprint @, winston


Rework mixin so it can be statically compiled by browserify

--- lib/document.coffee
+++ lib/document.coffee
@@ -62,18 +62,17 @@
     # Add the first page
     @addPage()

-  mixin = (name) =>
-    methods = require './mixins/' + name
+  mixin = (methods) =>
     for name, method of methods
       this::[name] = method

   # Load mixins
-  mixin 'color'
-  mixin 'vector'
-  mixin 'fonts'
-  mixin 'text'
-  mixin 'images'
-  mixin 'annotations'
+  mixin require './mixins/color'
+  mixin require './mixins/vector'
+  mixin require './mixins/fonts'
+  mixin require './mixins/text'
+  mixin require './mixins/images'
+  mixin require './mixins/annotations'

   addPage: (options = @options) ->
     # end the current page if needed


only abstract error handling for now

only abstract error handling for now

--- vue/src/shared/helpers/form.coffee
+++ vue/src/shared/helpers/form.coffee
@@ -1,121 +1,11 @@
-import EventBus       from '@/shared/services/event_bus'
-import AbilityService from '@/shared/services/ability_service'
-import Records        from '@/shared/services/records'
-import Session        from '@/shared/services/session'
-import Flash   from '@/shared/services/flash'
+export onError = (model, cb) ->
+  (response) ->
+    cb(response) if typeof cb is 'function'
+    setErrors(model, response) if _.includes([401, 422], response.status)

-import { fieldFromTemplate } from '@/shared/helpers/poll'
-
-export submitDiscussion = (scope, model, options = {}) ->
-  if model.isForking
-    submitFn = model.moveComments
-  else
-    model.save
-
-  submit(scope, model, _.merge(
-    submitFn: submitFn
-    flashSuccess: if model.isForking then "discussion_fork_actions.moved" else "discussion_form.messages.#{actionName(model)}"
-    failureCallback: ->
-      console.log "failure"
-    successCallback: (data) ->
-      _.invokeMap Records.documents.find(model.removedDocumentIds), 'remove'
-      if model.isForking
-        model.forkTarget().discussion().forkedEventIds = []
-        _.invokeMap Records.events.find(model.forkedEventIds), 'remove'
-      nextOrSkip(data, scope, model)
-  , options))
-
-submit = (scope, model, options = {}) ->
-  submitFn  = options.submitFn  or model.save
-  confirmFn = options.confirmFn or (-> false)
-  (prepareArgs) ->
-    return if scope.isDisabled
-    prepare(scope, model, options, prepareArgs)
-    if confirm(confirmFn(model))
-      setTimeout ->
-        submitFn(model).then(
-          success(scope, model, options),
-          failure(scope, model, options),
-        ).finally(
-          cleanup(scope, model, options)
-        )
-    else
-      cleanup(scope, model, options)
-
-prepare = (scope, model, options, prepareArgs) ->
-  Flash.loading(options.loadingMessage)
-  options.prepareFn(prepareArgs) if typeof options.prepareFn is 'function'
-  EventBus.$emit 'processing'
-  model.cancelDraftFetch()       if typeof model.cancelDraftFetch is 'function'
-  model.clearDrafts()            if typeof model.clearDrafts      is 'function'
-  model.setErrors()
-  scope.isDisabled = true
-
-confirm = (confirmMessage) ->
-  if confirmMessage and typeof window.confirm == 'function'
-    window.confirm(confirmMessage)
-  else
-    true
-
-progress = (scope) ->
-  (progress) ->
-    return unless progress.total > 0
-    scope.percentComplete = Math.floor(100 * progress.loaded / progress.total)
-
-success = (scope, model, options) ->
-  (data) ->
-    # Flash.dismiss()
-    options.successCallback(data) if typeof options.successCallback is 'function'
-    if options.flashSuccess?
-      flashKey     = if typeof options.flashSuccess is 'function' then options.flashSuccess() else options.flashSuccess
-      Flash.success flashKey, calculateFlashOptions(options.flashOptions)
-
-failure = (scope, model, options) ->
-  (response) ->
-    # Flash.dismiss()
-    options.failureCallback(response) if typeof options.failureCallback is 'function'
-    setErrors(scope, model, response) if _.includes([401, 422], response.status)
-    EventBus.$emit errorTypes[response.status] or 'unknownError',
-      model: model
-      response: response
-
-cleanup = (scope, model, options = {}) ->
-  ->
-    Flash.dismiss()
-    options.cleanupFn(scope, model) if typeof options.cleanupFn is 'function'
-    EventBus.$emit 'doneProcessing' unless options.skipDoneProcessing
-    scope.isDisabled = false
-    scope.files = null        if scope.files
-    scope.percentComplete = 0 if scope.percentComplete
-
-calculateFlashOptions = (options) ->
-  _.each _.keys(options), (key) ->
-    options[key] = options[key]() if typeof options[key] is 'function'
-  options
-
-nextOrSkip = (data, scope, model) ->
-  eventData = _.find(data.events, (event) -> event.kind == eventKind(model)) || {}
-  if event = Records.events.find(eventData.id)
-    EventBus.$emit 'nextStep', event
-  else
-    EventBus.$emit 'skipStep'
-
-actionName = (model) ->
-  return 'forked' if model.isA('discussion') and model.isForking
-  if model.isNew() then 'created' else 'updated'
-
-setErrors = (scope, model, response) ->
+setErrors = (model, response) ->
   response.json().then (r) ->
     model.setErrors(r.errors)
-
-export eventKind = (model) ->
-  if model.isA('discussion') and model.isNew()
-    return if model.isForking then 'discussion_forked' else 'new_discussion'
-
-  if model.isNew()
-    "#{model.constructor.singular}_created"
-  else
-    "#{model.constructor.singular}_edited"

 errorTypes =
   400: 'badRequest'


capture highlighted text

capture highlighted text

the injectable script listen to mouseup event (last part of a click) and
check if the windo.getSelection() changed. If it did, then the user
selected a new piece of text, this text is stored in a user event called
highlight. This info should be   of some importance when it come to
analyze what the user focused on.

--- src.coffee/injectable.coffee
+++ src.coffee/injectable.coffee
@@ -50,6 +50,27 @@
     }, null
   return

+mem0r1es.injectable.mouseupListener = (event) ->
+  currentSelection = window.getSelection()
+
+  if typeof(currentSelection) is "undefined" or not lastSelection?
+    lastSelection = currentSelection
+
+  if currentSelection is lastSelection and typeof(currentSelection) isnt "undefined" and currentSelection.toString().length isnt 0
+    currentSelection = currentSelection.toString()
+    mem0r1es.injectable.sendMessage "documentPreprocessor", {
+    title : "mem0r1eEvent"
+    content :
+      pageId : mem0r1es.injectable.getPageId()
+      event :
+        timestamp: new Date().getTime()
+        type : "highlight"
+        data : currentSelection
+    }, null
+
+  lastSelection = currentSelection
+  return
+
 mem0r1es.injectable.unloadListener = (event) ->
   mem0r1es.injectable.sendMessage "documentPreprocessor", {
     title : "mem0r1eEvent"
@@ -88,14 +109,19 @@
             data : data
         }, null
   return
+
+mem0r1es.createListeners = () ->
+  document.getElementsByTagName("html")[0].addEventListener "click", mem0r1es.injectable.clickListener, false
+  document.getElementsByTagName("html")[0].addEventListener "click", mem0r1es.injectable.clickListener, false
+  document.getElementsByTagName("html")[0].addEventListener "mouseup", mem0r1es.injectable.mouseupListener, false
+  window.addEventListener "beforeunload", mem0r1es.injectable.unloadListener, false

 mem0r1es.injectable.initialize = () ->
   if not window.mem0r1es.pageId?
     mem0r1es.injectable.getPageId()
     mem0r1es.injectable.createNewMem0r1e()
     mem0r1es.injectable.loadDSRules()
-    document.getElementsByTagName("html")[0].addEventListener "click", mem0r1es.injectable.clickListener, false
-    window.addEventListener "beforeunload", mem0r1es.injectable.unloadListener, false
+    mem0r1es.createListeners()

 mem0r1es.eventCatched = (title, data, callback) ->
   mem0r1es.injectable.sendMessage "documentPreprocessor", {


Add offerings back in. AoW cleanup.

Add offerings back in. AoW cleanup.

--- lib/provider.coffee
+++ lib/provider.coffee
@@ -275,6 +275,7 @@
     { snippet: '"Scroll of Wisdom"' },
     { snippet: '"Transmutation Shard"' },
     { snippet: '"Vaal Orb"' },
+    { snippet: '"Offering to the Goddess"' },
     { snippet: '"Stacked Deck"' },
     #Perandus
     { snippet: '"Perandus Coin"' },
@@ -283,8 +284,14 @@
     { snippet: '"Prophecy"' },
     #Atlas of Worlds
     { snippet: '"Cartographer\'s Sextant"' },
+    { snippet: '"Apprentice Cartographer\'s Sextant"' },
+    { snippet: '"Journeyman Cartographer\'s Sextant"' },
+    { snippet: '"Master Cartographer\'s Sextant"' },
     { snippet: '"Cartographer\'s Seal"' },
-    { snippet: '"Shaper\'s Orb"' },
+    { snippet: '"Apprentice Cartographer\'s Seal"' },
+    { snippet: '"Journeyman Cartographer\'s Seal"' },
+    { snippet: '"Master Cartographer\'s Seal"' },
+    { snippet: '"Unshaping Orb"' },
     #Essence
     { snippet: '"Whispering Essence"' }, #Convenience
     { snippet: '"Muttering Essence"' }, #Convenience
@@ -398,13 +405,6 @@
     { snippet: '"Essence of Horror"' },
     { snippet: '"Essence of Delirium"' },
     { snippet: '"Remnant of Corruption"' },
-    { snippet: '"Apprentice Cartographer\'s Sextant"' },
-    { snippet: '"Journeyman Cartographer\'s Sextant"' },
-    { snippet: '"Master Cartographer\'s Sextant"' },
-    { snippet: '"Apprentice Cartographer\'s Seal"' },
-    { snippet: '"Journeyman Cartographer\'s Seal"' },
-    { snippet: '"Master Cartographer\'s Seal"' },
-    { snippet: '"Unshaping Orb"' },
   ]

   divcards: [
@@ -866,13 +866,11 @@
     { snippet: '"Jungle Valley Map"' },
     { snippet: '"Arcade Map"' },
     { snippet: '"Desert Map"' },
-
     #Tier 2
     { snippet: '"Beach Map"' },
     { snippet: '"Ghetto Map"' },
     { snippet: '"Oasis Map"' },
     { snippet: '"Factory Map"' },
-
     #Tier 3
     { snippet: '"Channel Map"' },
     { snippet: '"Cavern Map"' },
@@ -881,7 +879,6 @@
     { snippet: '"Grotto Map"' },
     { snippet: '"Arid Lake Map"' },
     { snippet: '"Marshes Map"' },
-
     #Tier 4
     { snippet: '"Waste Pool Map"' },
     { snippet: '"Phantasmagoria Map"' },
@@ -890,7 +887,6 @@
     { snippet: '"Dungeon Map"' },
     { snippet: '"Villa Map"' },
     { snippet: '"Acid Lakes Map"' },
-
     #Tier 5
     { snippet: '"Pit Map"' },
     { snippet: '"Mesa Map"' },
@@ -900,7 +896,6 @@
     { snippet: '"Peninsula Map"' },
     { snippet: '"Primordial Pool Map"' },
     { snippet: '"Tower Map"' },
-
     #Tier 6
     { snippet: '"Quarry Map"' },
     { snippet: '"Vaal City Map"' },
@@ -915,7 +910,6 @@
     { snippet: '"Shaped Crystal Ore Map"' },
     { snippet: '"Shaped Desert Map"' },
     { snippet: '"Shaped Jungle Valley Map"' },
-
     #Tier 7
     { snippet: '"Cells Map"' },
     { snippet: '"Catacombs Map"' },
@@ -928,7 +922,6 @@
     { snippet: '"Shaped Ghetto Map"' },
     { snippet: '"Shaped Oasis Map"' },
     { snippet: '"Shaped Factory Map"' },
-
     #Tier 8
     { snippet: '"Tropical Island Map"' },
     { snippet: '"Shore Map"' },
@@ -946,7 +939,6 @@
     { snippet: '"Shaped Grotto Map"' },
     { snippet: '"Shaped Arid Lake Map"' },
     { snippet: '"Shaped Marshes Map"' },
-
     #Tier 9
     { snippet: '"Coves Map"' },
     { snippet: '"Crypt Map"' },
@@ -963,7 +955,6 @@
     { snippet: '"Shaped Dungeon Map"' },
     { snippet: '"Shaped Villa Map"' },
     { snippet: '"Shaped Acid Lakes Map"' },
-
     #Tier 10
     { snippet: '"Colonnade Map"' },
     { snippet: '"Arsenal Map"' },
@@ -980,7 +971,6 @@
     { snippet: '"Shaped Peninsula Map"' },
     { snippet: '"Shaped Primordial Pool Map"' },
     { snippet: '"Shaped Tower Map"' },
-
     #Tier 11
     { snippet: '"Bazaar Map"' },
     { snippet: '"Chateau Map"' },
@@ -998,7 +988,6 @@
     { snippet: '"Shaped Strand Map"' },
     { snippet: '"Shaped Thicket Map"' },
     { snippet: '"Shaped Racecourse Map"' },
-
     #Tier 12
     { snippet: '"Crematorium Map"' },
     { snippet: '"Estuary Map"' },
@@ -1015,7 +1004,6 @@
     { snippet: '"Shaped Arachnid Tomb Map"' },
     { snippet: '"Shaped Armory Map"' },
     { snippet: '"Shaped Mud Geyser Map"' },
-
     #Tier 13
     { snippet: '"Lair Map"' },
     { snippet: '"Beacon Map"' },
@@ -1034,7 +1022,6 @@
     { snippet: '"Shaped Arachnid Nest Map"' },
     { snippet: '"Shaped Barrows Map"' },
     { snippet: '"Shaped Pier Map"' },
-
     #Tier 14
     { snippet: '"Maze Map"' },
     { snippet: '"Mineral Pools Map"' },
@@ -1050,7 +1037,6 @@
     { snippet: '"Shaped Promenade Map"' },
     { snippet: '"Shaped Reef Map"' },
     { snippet: '"Shaped Temple Map"' },
-
     #Tier 15
     { snippet: '"Abyss Map"' },
     { snippet: '"Colosseum Map"' },
@@ -1064,7 +1050,6 @@
     { snippet: '"Shaped Quay Map"' },
     { snippet: '"Shaped Terrace Map"' },
     { snippet: '"Shaped Underground River Map"' },
-
     #Tier 16
     { snippet: '"Pit of the Chimera Map"' },
     { snippet: '"Lair of the Hydra Map"' },


Require Grim

Require Grim

--- lib/provider.coffee
+++ lib/provider.coffee
@@ -1,5 +1,5 @@
 {View} = require 'atom-space-pen-views'
-{deprecate} = require 'atom-space-pen-views'
+{deprecate} = require 'grim'

 # Public: A provider provides an interface to the autocomplete package. Third-party
 # packages can register providers which will then be used to generate the


Pass correct password

Pass correct password

--- lib/restivus.coffee
+++ lib/restivus.coffee
@@ -283,7 +283,7 @@

         # Try to log the user into the user's account (if successful we'll get an auth token back)
         try
-          auth = Auth.loginWithPassword user, @bodyParams.password
+          auth = Auth.loginWithPassword user, password
         catch e
           return {} =
             statusCode: e.error


Create preview tab if item is pending

Create preview tab if item is pending

Clear preview style on 'did-confirm-pending-state' event

--- lib/tab-view.coffee
+++ lib/tab-view.coffee
@@ -6,7 +6,7 @@
   initialize: (@item) ->
     if typeof @item.getPath is 'function'
       @path = @item.getPath()
-      @isPreviewTab = atom.config.get('tabs.usePreviewTabs')
+      @isPreviewTab = @item.isPending()

     @classList.add('tab', 'sortable')

@@ -37,6 +37,13 @@
       @updateDataAttributes()
       @updateTitle()
       @updateTooltip()
+
+    if typeof @item.onDidConfirmPendingState is 'function'
+      onDidConfirmPendingStateDisposable = @item.onDidConfirmPendingState => @clearPreview()
+      if Disposable.isDisposable(onDidConfirmPendingStateDisposable)
+        @subscriptions.add(onDidConfirmPendingStateDisposable)
+      else
+        console.warn "::onDidConfirmPendingState does not return a valid Disposable!", @item

     if typeof @item.onDidChangeTitle is 'function'
       onDidChangeTitleDisposable = @item.onDidChangeTitle(titleChangedHandler)


Use skinny arrow for rootView.eachEditor callback

--- src/packages/wrap-guide/lib/wrap-guide-view.coffee
+++ src/packages/wrap-guide/lib/wrap-guide-view.coffee
@@ -5,7 +5,7 @@
 module.exports =
 class WrapGuideView extends View
   @activate: ->
-    rootView.eachEditor (editor) =>
+    rootView.eachEditor (editor) ->
       editor.underlayer.append(new WrapGuideView(editor)) if editor.attached

   @content: ->


:new: Validate filePath attribute properly

--- lib/validate.coffee
+++ lib/validate.coffee
@@ -41,9 +41,12 @@
         throw new Error 'Invalid class field on Linter Response' if typeof result.class isnt 'string'
       else
         result.class = result.type.toLowerCase().replace(' ', '-')
+      if result.filePath
+        throw new Error('Invalid filePath field on Linter response') if typeof result.filePath isnt 'string'
+      else
+        result.filePath = null
       result.range = Range.fromObject result.range if result.range?
       result.key = JSON.stringify(result)
       result.linter = linter.name
-      result.scope = linter.scope
       Validate.messages(result.trace, linter) if result.trace and result.trace.length
     return undefined


fix bug preventing column widths being set properly without paging turned on

--- coffee/directives/fixtable.coffee
+++ coffee/directives/fixtable.coffee
@@ -12,9 +12,9 @@
 			# update table data & calculated styles when source data changes
 			scope.$parent.$watchCollection scope.options.data, (newData) ->
 				scope.data = newData
-				for col, i in scope.options.columns
-					if col.width then fixtable._setColumnWidth i+1, col.width
 				$timeout ->
+					for col, i in scope.options.columns
+						if col.width then fixtable._setColumnWidth i+1, col.width
 					fixtable._setHeaderHeight()
 					fixtable._setFooterHeight()


Made the group classes support look more like the option classes support

--- coffee/lib/abstract-chosen.coffee
+++ coffee/lib/abstract-chosen.coffee
@@ -97,8 +97,12 @@
     return '' unless group.search_match || group.group_match
     return '' unless group.active_options > 0

+    classes = []
+    classes.push "group-result"
+    classes.push group.classes if group.classes
+
     group_el = document.createElement("li")
-    group_el.className = "group-result" + group.classes;
+    group_el.className = classes.join(" ")
     group_el.innerHTML = group.search_text

     this.outerHTML(group_el)