Growl notification close should be defined as an action.

--- app/scripts/components/GrowlNotifications.coffee
+++ app/scripts/components/GrowlNotifications.coffee
@@ -82,13 +82,6 @@
         willDestroyElement: ->
             $(window).unbind('resize', @get('_recomputeStyle'))

-        close: ->
-            @set('isOpaque', false)
-            setTimeout (->
-                @set "content.closed", true
-                clearTimeout @get("timeoutId")
-            ).bind(@), 300
-
         style: (->
             notifications = @get('parentView.content').rejectProperty('closed', true)
             index = notifications.indexOf(@get('content'))
@@ -119,6 +112,14 @@
                 'danger': 'glyphicon-remove'
             hash[type] || ''
         ).property('content.type')
+
+        actions:
+            close: ->
+                @set('isOpaque', false)
+                setTimeout (->
+                    @set "content.closed", true
+                    clearTimeout @get("timeoutId")
+                ).bind(@), 300
     )
 )


Consistency in logging patterns

Consistency in logging patterns

--- realtime.coffee
+++ realtime.coffee
@@ -14,15 +14,15 @@
 ]

 userCreated = (user) ->
-  console.log "[REALTIME]: user created"
+  console.log "[REALTIME] user created"

 userDeleted = (user) ->
-  console.log "[REALTIME]: user deleted"
+  console.log "[REALTIME] user deleted"

 competitionStarted = (competition) ->
-  console.log "[REALTIME]: competition started"
+  console.log "[REALTIME] competition started"

 competitionEnded = (competition) ->
-  console.log "[REALTIME]: competition ended"
+  console.log "[REALTIME] competition ended"

 module.exports = {init}


Fix double popup appearance.

Fix double popup appearance.

--- src/popup.coffee
+++ src/popup.coffee
@@ -19,6 +19,7 @@
     add: (name, popup)->
       @popups[name] = popup
     show: (name, target)->
+      @hide()
       popupContent = @popups[name]
       return unless popupContent?
       attachTo = target
@@ -35,6 +36,7 @@
     hide: ->
       $document.unbind('click', documentClickBind)
       currentPopup?.remove()
+      currentPopup = attachTo = undefined
       return
   $rootScope.popup = popupManager
 ])


Fix clear button

Fix clear button

--- client/formFieldTemplates.coffee
+++ client/formFieldTemplates.coffee
@@ -26,6 +26,16 @@

   if this.inputType is 'staticDate' or this.inputType is 'dateTime'
     this.value = moment.unix(this.value).format("DD MMM YYYY HH:mm")
+
+  if this.inputType is 'dateTime'
+    _events = {}
+    templateEvent = "click .btn-#{this.name}-clear" #"click .close-form-#{data.single}"
+    if not DMUtils.find(Template.dateTimeFieldTemplate._events, 'selector', ".btn-#{this.name}-clear")
+      _events[templateEvent] = (event,template) ->
+        element = template.find("input")
+        element.value = ""
+        return false
+    Template.dateTimeFieldTemplate.events(_events)

   if this.inputType is 'selectState'
     states = States.find({}).fetch()
@@ -87,18 +97,7 @@
   if this.renderIfEmpty or this.value isnt null
     return Template[template]

-  if this.inputType is 'dateTime'
-    _events = {}
-    templateEvent = "click .btn-#{this.name}-clear" #"click .close-form-#{data.single}"
-    if not DMUtils.find(Template.dateTimeFieldTemplate._events, 'selector', ".btn-#{this.name}-clear")
-      _events[templateEvent] = (event,template) ->
-        element = template.find("input")
-        element.value("")
-        return false
-    Template.dateTimeFieldTemplate.events(_events)
-
 Template.dateTimeFieldTemplate.rendered = ->
-  console.log 'dateTimeFieldTemplate', this
   $("##{this.data.name}-picker").datetimepicker()
   $("##{this.data.name}-picker").data('DateTimePicker').setDate(moment());
   return


Add the proper return value for cancel

Add the proper return value for cancel

--- script.coffee
+++ script.coffee
@@ -24,7 +24,7 @@
     $('#forecastAPIKey').val(localStorage['forecastAPIKey'])

   $('#cancelButton').click ->
-    document.location = 'pebblejs://close'
+    document.location = getQueryParam('return_to', 'pebblejs://close')
   $('#submitButton').click ->
     return_to = getQueryParam('return_to', 'pebblejs://close#')
     return_val = encodeURIComponent(JSON.stringify(getConfigData()))


Added accessors for x and y coordinates of a position

--- position.coffee
+++ position.coffee
@@ -1,6 +1,7 @@
 class Position

-  constructor: (pos, @direction, @time) ->
-    @pos = new paper.Point(pos)
+  constructor: (@pos, @direction, @time) ->
+    @x = pos[0]
+    @y = pos[1]

 window.Position = Position


Perform an initial load of the quotefile.

--- src/quote.coffee
+++ src/quote.coffee
@@ -21,7 +21,7 @@
 module.exports = (robot) ->

   # Global state.
-  quotes = []
+  quotes = null

   # Read configuration from the environment.
   quotefilePath = process.env.HUBOT_QUOTEFILE_PATH
@@ -38,7 +38,19 @@
       quotes = _.filter quotes, (quote) -> quote.length > 1
       callback(null)

+  isLoaded = (msg) ->
+    if quotes?
+      true
+    else
+      msg.reply "Just a moment, the quotes aren't loaded yet."
+      false
+
+  # Perform the initial load.
+  reloadThen ->
+
   robot.respond /quote(.*)/i, (msg) ->
+    return unless isLoaded(msg)
+
     query = msg.match[1].trim().split /\s+/
     query = _.filter query, (part) -> part.length > 0


Start the "top quoted" list.

Start the "top quoted" list.

--- src/quote.coffee
+++ src/quote.coffee
@@ -128,3 +128,95 @@
           msg.send err.stack
         else
           msg.send "#{quotes.length} quotes reloaded successfully."
+
+  robot.respond /top quotes$/i, (msg) ->
+    return unless isLoaded(msg)
+
+    # usernames = (u.name for u in robot.brain.users)
+    usernames = [
+      "smashwilson"
+      "jlitzwilson"
+      "jrhusel"
+      "reostra"
+      "purr-purr"
+      "hdcunni"
+      "fenris"
+      "k0d3k1ttn"
+      "zmbusse"
+      "femshep"
+      "aschwa2"
+      "phbarna"
+      "blackbeardtron"
+      "iguanaditty"
+      "wrbritt"
+      "frey"
+      "clamsey"
+      "roastin41"
+      "puppetwrangler"
+      "pusscat"
+      "llynmir"
+      "gmhowar"
+      "susan"
+      "spartacus"
+      "belle.carrell"
+    ]
+    results = {}
+
+    incrementResultFor = (username, kind) ->
+      r = results[username] ?=
+        spoke: 0
+        mentioned: 0
+      r[kind] += 1
+
+    for quote in quotes
+      [speakers, mentioned] = [[], []]
+      mentionMatcher = new RegExp('(' + usernames.join('|') + ')', 'g')
+      mentionsFrom = (str) ->
+        mention = null
+        while mention?
+          mention = mentionMatcher.exec(str)
+          mentioned.push mention if mention?
+      console.log "Matcher is: #{mentionMatcher.toString()}"
+
+      for line in quote.split(/\n/)
+        m = line.match(/^\[[^\]]+\] @?([^:]+): (.*)$/)
+        if m?
+          [x, speaker, rest] = m
+          console.log "Speaker: #{speaker}, rest: #{rest}"
+          speakers.push speaker
+          mentionsFrom(rest)
+        else
+          console.log "No match in #{line}"
+          mentionsFrom(line)
+
+      incrementResultFor(u, 'spoke') for u in _.uniq speakers
+      incrementResultFor(u, 'mentioned') for u in _.uniq mentioned
+
+    # Report the results.
+    transformed = _.map usernames, (u) ->
+      r = results[u]
+      r ?=
+        spoke: 0
+        mentioned: 0
+
+      { username: u, spoke: r.spoke.toString(), mentioned: r.mentioned.toString() }
+    ordered = _.sortBy transformed, (r) -> parseInt(r.spoke) * -1
+
+    justify = (str, width) ->
+      if str.length >= width
+        str[0..(width - 3)] + '...'
+      else
+        justified = str
+        for i in [0..(width - str.length)]
+          justified += ' '
+        justified
+
+    lines = [
+      "```"
+      "#{justify 'Username', 20}| #{justify 'Spoke', 10}| #{justify 'Mentioned', 10}"
+    ]
+    for row in ordered
+      lines.push "#{justify row.username, 20}| #{justify row.spoke, 10}| #{justify row.mentioned, 10}"
+    lines.push "```"
+
+    msg.send lines.join("\n")


stub test for valid XML with jQuery

stub test for valid XML with jQuery


:art:

:art:

--- src/range.coffee
+++ src/range.coffee
@@ -194,8 +194,6 @@
   traverse: (delta) ->
     new @constructor(@start.traverse(delta), @end.traverse(delta))

-
-
   ###
   Section: Comparison
   ###


Remove sniff

Remove sniff

--- src/range.coffee
+++ src/range.coffee
@@ -8,28 +8,6 @@
 TEXT_NODE = 3

 Range = {}
-
-# Public: Determines the type of Range of the provided object and returns
-# a suitable Range instance.
-#
-# r - A range Object.
-#
-# Examples
-#
-#   selection = window.getSelection()
-#   Range.sniff(selection.getRangeAt(0))
-#   # => Returns a BrowserRange instance.
-#
-# Returns a Range object or false.
-Range.sniff = (r) ->
-  if r.commonAncestorContainer?
-    new Range.BrowserRange(r)
-  else if typeof r.start is "string"
-    new Range.SerializedRange(r)
-  else if r.start and typeof r.start is "object"
-    new Range.NormalizedRange(r)
-  else
-    false

 class Range.RangeError
   constructor: (@type, @message, @parent = null) ->


Fix broken case for generated explicit indentation.

--- rewriter.coffee
+++ rewriter.coffee
@@ -194,7 +194,13 @@
   # Add location data to all tokens generated by the rewriter.
   addLocationDataToGeneratedTokens: ->
     @scanTokens (token, i, tokens) ->
-      if token.generated and not token.locationData
+      tag = token[0]
+      # Some generated indentation isn't '.generated'.  We want to treet these and generated
+      # anyways, since everything needs a location data.  We don't want to just blindly add
+      # location data, though, since we want to find bugs.  :)
+      generated = token.generated or (token.explicit and (tag is 'INDENT' or tag is 'OUTDENT'))
+
+      if generated and not token.locationData
         if i > 0
           prevToken = tokens[i-1]
           token.locationData =
@@ -271,6 +277,7 @@
     indent  = ['INDENT', 2, token[2]]
     outdent = ['OUTDENT', 2, token[2]]
     indent.generated = outdent.generated = yes if implicit
+    indent.explicit = outdent.explicit = yes if not implicit
     [indent, outdent]

   # Create a generated token: one that exists due to a use of implicit syntax.


Fixing issue #751 ... implicit number arguments within implicit object literals.

--- rewriter.coffee
+++ rewriter.coffee
@@ -128,7 +128,7 @@
       [one, two, three] = @tokens.slice i + 1, i + 4
       [tag] = token
       tag in ['TERMINATOR', 'OUTDENT'] and not (two?[0] is ':' or one?[0] is '@' and three?[0] is ':') or
-      tag is ',' and one?[0] not in ['IDENTIFIER', 'STRING', '@', 'TERMINATOR', 'OUTDENT']
+      tag is ',' and one?[0] not in ['IDENTIFIER', 'NUMBER', 'STRING', '@', 'TERMINATOR', 'OUTDENT']
     action = (token, i) -> @tokens.splice i, 0, ['}', '}', token[2]]
     @scanTokens (token, i, tokens) ->
       if include EXPRESSION_START, tag = token[0]


Add Test for select* commands

Add Test for select* commands

--- spec/bookmarks-view-spec.coffee
+++ spec/bookmarks-view-spec.coffee
@@ -448,3 +448,33 @@

       expect(bookmarkedRangesForEditor(editor)).toEqual []
       expect(bookmarkedRangesForEditor(editor2)).toEqual []
+
+  describe "selecting bookmarks", ->
+
+    it "doesnt die when no bookmarks", ->
+      editor.setCursorBufferPosition([5, 10])
+
+      atom.commands.dispatch editorElement, 'bookmarks:select-to-next-bookmark'
+      expect(editor.getLastCursor().getBufferPosition()).toEqual [5, 10]
+      expect(atom.beep.callCount).toBe 1
+
+      atom.commands.dispatch editorElement, 'bookmarks:select-to-previous-bookmark'
+      expect(editor.getLastCursor().getBufferPosition()).toEqual [5, 10]
+      expect(atom.beep.callCount).toBe 2
+
+    describe "with one bookmark", ->
+      beforeEach ->
+        editor.setCursorBufferPosition([2, 0])
+        atom.commands.dispatch editorElement, 'bookmarks:toggle-bookmark'
+
+      it "select-to-next-bookmark selects to the right place", ->
+        editor.setCursorBufferPosition([0, 0])
+
+        atom.commands.dispatch editorElement, 'bookmarks:select-to-next-bookmark'
+        expect(editor.getSelectedBufferRange()).toEqual([[0, 0], [2, 0]])
+
+      it "select-to-previous-bookmark selects to the right place", ->
+        editor.setCursorBufferPosition([4, 0])
+
+        atom.commands.dispatch editorElement, 'bookmarks:select-to-previous-bookmark'
+        expect(editor.getSelectedBufferRange()).toEqual([[4, 0], [2, 0]])


Adding better stash output

Adding better stash output

--- src/sensu.coffee
+++ src/sensu.coffee
@@ -145,7 +145,7 @@
     data = {}
     data['content'] = {}
     data['content']['timestamp'] = moment().unix()
-    data['content']['by'] = msg.message.user.name
+    data['content']['reason'] = msg.message.user.name + ' silenced'
     data['expire'] = expiration
     data['path'] = 'silence/' + path


adding some help text

adding some help text

--- src/giphy.coffee
+++ src/giphy.coffee
@@ -63,6 +63,18 @@

     @api = api
     @defaultEndpoint = process.env.HUBOT_GIPHY_DEFAULT_ENDPOINT or Giphy.SearchEndpointName
+    @helpText = "
+giphy [endpoint] [options...] [args]
+
+endpoints: search, id, translate, random, trending
+options: rating, limit, offset, api
+
+default endpoint is '#{@defaultEndpoint}' if none is specified
+options can be specified using /option:value
+
+Example:
+  giphy search /limit:100 /offset:50 /rating:pg something to search for
+".trim()

   ### istanbul ignore next ###
   log: ->
@@ -178,6 +190,7 @@

   getHelp: (state) ->
     @log 'getHelp:', state
+    @sendMessage state.msg, @helpText

   getUri: (state) ->
     @log 'getUri:', state


allow options to be passed to the toolbar of choice

--- src/hallo.coffee
+++ src/hallo.coffee
@@ -99,6 +99,7 @@
       buttonCssClass: null
       toolbarCssClass: null
       toolbarPositionAbove: false
+      toolbarOptions: {}
       placeholder: ''
       forceStructured: true
       checkTouch: true
@@ -293,11 +294,15 @@
     _prepareToolbar: ->
       @toolbar = jQuery('

').hide()
       @toolbar.addClass @options.toolbarCssClass if @options.toolbarCssClass
-      @element[@options.toolbar]
+
+      defaultToolbarOptions =
         editable: @
         parentElement: @options.parentElement
         toolbar: @toolbar
         positionAbove: @options.toolbarPositionAbove
+
+      toolbarOptions = $.extend({}, defaultToolbarOptions, @options.toolbarOptions)
+      @element[@options.toolbar] toolbarOptions

       for plugin of @options.plugins
         populate = jQuery(@element).data(plugin).populateToolbar


Removed unused state parameter from activate function

--- lib/levels-debugger-ruby.coffee
+++ lib/levels-debugger-ruby.coffee
@@ -7,7 +7,7 @@
 DebuggerView              = require './views/debugger-view'

 module.exports =
-  activate: (state) ->
+  activate: ->
     packageDeps.install('levels-debugger-ruby').then console.log 'All dependencies installed, good to go!'

     @incomingMessageDispatcher = new IncomingMessageDispatcher


+ set autoMigrate promise to loopback-component-auto-migrate

--- src/index.coffee
+++ src/index.coffee
@@ -9,8 +9,19 @@
     throw new Error('loopback-component-auto-migrate requires loopback 2.0 or newer')

   if !options or options.enabled isnt false
-    autoMigrate = (options and options.migration) or 'auto-update'
-    autoMigrate = require './' + autoMigrate
-    app.autoMigrateDone = autoMigrate(app, options).then -> debug "Done"
+    migration = (options and options.migration) or 'auto-update'
+    autoMigrate = require './' + migration
+    raiseError = (options and options.migration)
+    app.set('loopback-component-auto-migrate-status', 'loaded')
+    app.set('loopback-component-auto-migrate', autoMigrate)
+    autoMigrate(app, options)
+    .asCallback (err)->
+      if err
+        app.set('loopback-component-auto-migrate-error', err)
+        app.set('loopback-component-auto-migrate-status', 'failed')
+        debug migration + ' failed: %O', err
+        throw err if raiseError
+      else
+        app.set('loopback-component-auto-migrate-status', 'done')
   else
     debug 'component not enabled'


add in mathjax start up

add in mathjax start up

--- src/index.coffee
+++ src/index.coffee
@@ -3,6 +3,9 @@
 {AnswerActions, AnswerStore} = require './stores/answer'
 {QuestionActions, QuestionStore} = require './stores/answer'
 {ExerciseActions, ExerciseStore} = require './stores/exercise'
+
+MathJaxHelper =  require 'openstax-react-components/src/helpers/mathjax'
+
 Exercise = require './components/exercise'
 api = require './api'

@@ -20,6 +23,7 @@
 loadApp = ->
   api.start()

+  MathJaxHelper.startMathJax()
   root = document.getElementById('exercise')

   if not root


Avoid dom interfence in React updates

Avoid dom interfence in React updates

--- src/index.coffee
+++ src/index.coffee
@@ -1,19 +1,26 @@
 mdast = require 'mdast'
 uuid = require 'uuid'
-# {DOMParser} = require 'xmldom'

 $ = React.createElement
-HTMLWrapper = React.createClass
+defaultHTMLWrapper = React.createClass
   _update: ->
     current = @props.html
     if @_lastHtml isnt current
       @_lastHtml = current
       node = @refs.htmlWrapper.getDOMNode()
-      node.innerHTML = @props.html
+      node.contentDocument.body.innerHTML = @props.html
+      node.style.height = node.contentWindow.document.body.scrollHeight + 'px'
+      node.style.width  = node.contentWindow.document.body.scrollWidth  + 'px'

   componentDidUpdate: -> @_update()
   componentDidMount: -> @_update()
-  render: -> $ 'div', ref: 'htmlWrapper'
+
+  render: ->
+    $ 'iframe',
+      ref: 'htmlWrapper'
+      html: @props.html
+      style:
+        border: 'none'

 toChildren = (node, parentKey, tableAlign = []) ->
   return (for child, i in node.children
@@ -100,11 +107,13 @@
           dompurify.sanitize(node.value)
         else
           node.value
-      $ HTMLWrapper, key: key, html: value
+      $ htmlWrapperComponent, key: key, html: value
     else
       throw node.type + ' is unsuppoted node type. report to https://github.com/mizchi/md2react/issues'

+htmlWrapperComponent = null
 module.exports = (raw, options = {}) ->
+  htmlWrapperComponent = options.htmlWrapperComponent ? defaultHTMLWrapper
   sanitize = options.sanitize ? true
   ast = mdast.parse raw, options
   compile(ast)


Fix lowercase S in removed[S]electedNode

--- src/index.coffee
+++ src/index.coffee
@@ -111,9 +111,9 @@
           removedSelectedNodes.push selectedNode

       while removedSelectedNodes.length > 0
-        removedselectedNode = removedSelectedNodes[removedSelectedNodes.length - 1]
-        @selectedNodes.splice @selectedNodes.indexOf(removedselectedNode), 1
-        if @_firstSelectedNode == removedselectedNode
+        removedSelectedNode = removedSelectedNodes[removedSelectedNodes.length - 1]
+        @selectedNodes.splice @selectedNodes.indexOf(removedSelectedNode), 1
+        if @_firstSelectedNode == removedSelectedNode
           @_firstSelectedNode = @selectedNodes[0]

       element.parentElement.removeChild childrenElement


explicit return

explicit return

--- src/index.coffee
+++ src/index.coffee
@@ -85,27 +85,27 @@
       return item.value

     keys: ->
-      ret = []
+      keys = []
       item = head
       while item?
-        ret.push item.key
+        keys.push item.key
         item = item.next
-      ret
+      return keys

     forEach: (cb) ->
       item = head
       while item?
         cb item.key, item.value
         item = item.next
-      undefined
+      return undefined

-    has: (key) -> instance.get(key)?
+    has: (key) -> return instance.get(key)?

-    size: -> size
+    size: -> return size

-    tail: -> tail
-
-    head: -> head
+    tail: -> return tail
+
+    head: -> return head

     at: (pos) ->
       i = 0
@@ -120,7 +120,7 @@
       tail = null
       storage = Object.create null
       size = 0
-      undefined
+      return undefined

   return instance


testing

testing

--- src/index.coffee
+++ src/index.coffee
@@ -38,10 +38,14 @@
       #   console.log "Inside initial valid until block: #{@validUntil} and meetingStartTime: #{meetingStartTime}"

       noShowLimit = moment(meetingStartTime).utc().add(1, 'minute')
-      console.log 'No show limit : ', noShowLimit.toISOString()
-      if moment(@validUntil).isBefore(moment().utc() && moment().isAfter(noShowLimit))
+      if moment().isBefore(noShowLimit)
+        @validUntil = noShowLimit
+        console.log "Initializing valid until for: #{@validUntil}"
+
+      if (@validUntil.isBefore(moment().utc() && moment().utc().isAfter(noShowLimit)))
         meetingId = _.get currentMeeting, 'meetingId'
         console.log "====================================="
+        console.log "meetingStartTime: #{meetingStartTime} and No show limit :#{noShowLimit.toISOString()}"
         console.log "Ending the meeting because : \n validUntil:#{@validUntil} and current time: #{moment()}\n"
         @endMeeting meetingId


remove unused var

remove unused var

--- src/index.coffee
+++ src/index.coffee
@@ -13,8 +13,6 @@

 createTrustProxy = require "./utils/createTrustProxy"
 etag = require "./utils/etag"
-
-__DEV__ = process.env.NODE_ENV isnt "production"

 optionTypes = valido
   port: "number?"


Add support for ES6 `import ... from ...` and shebang scripts.

--- src/index.coffee
+++ src/index.coffee
@@ -9,17 +9,46 @@
 # Returns a list of requires in source file by naive regex scan of requires
 # and grunt's loadNpmTasks.
 requires = (src) ->
-  re = /(require|loadNpmTasks)[\( ]+['"]([^\.][^'"]+)/g
+  re = /(import.*?from|require|loadNpmTasks)[\( ]+['"]([^\.][^'"]+)/g
   r = []
   while m = re.exec src
     r.push m[2].split('/')[0] # First component only ('foo/bar' -> 'foo')
   r

+# We want to be sync so we don't use it, maybe later.
+# asyncHead = (at, done) ->
+#   r = ''
+#   stream = fs.createReadStream at, { start: 0, end: 256 }
+#   stream.on 'data', (data) -> r += data
+#   stream.on 'error', (err) ->
+#     done err
+#   stream.on 'end', ->
+#     done null, r
+
+# Reads "head" of file (first ~256 bytes).
+# TODO: Don't read it twice if start/end doesn't work for readFileSync like it does for createReadStream.
+head = (at) ->
+  fs.readFileSync(at, { encoding: 'utf8', start: 0, end: 256 })[0...256]
+
+# Check if file is exec on unixy sys, on windows it's always exec.
+isExec = (at, stat = null) ->
+  if /^win/.test process.platform
+    true
+  else
+    (if stat? then stat else fs.statSync(at)).mode | 0o111
+
+# Returns true if file looks like node/coffee script.
+isNodeish = (at, stat) ->
+  if endsWithAny(at, [ '.coffee', '.js', '.es', '.es6' ])
+    true
+  else
+    isExec(at, stat) and /node|coffee/.test head(at)
+
 # Recursively scan all coffee and js files returning used requires.
 scanRequires = (at = '.', map = {}, root = true) ->
   stat = fs.statSync at

-  if stat.isFile() and endsWithAny at, [ '.coffee', '.js' ]
+  if stat.isFile() and isNodeish(at, stat)
     for e in requires fs.readFileSync(at, { encoding: 'utf8' })
       map[e] = true

@@ -57,9 +86,10 @@
   ra

 module.exports = {
+  deps
   endsWithAny
+  isNodeish
   requires
   scanRequires
-  deps
   unused
 }


* [Value] should pass the aOptions to toValue method

--- src/index.coffee
+++ src/index.coffee
@@ -51,7 +51,7 @@
     if aValue instanceof Value
       aValue = aValue.valueOf()
     else if vType.toValue
-      aValue = vType.toValue aValue
+      aValue = vType.toValue aValue, aOptions
     @$type.validate(aValue, checkValidity) if checkValidity isnt false
     @_assign aValue
     @


Added linus taunt as proposed on HN

Added linus taunt as proposed on HN

--- server.coffee
+++ server.coffee
@@ -71,6 +71,11 @@
   subtitle = "- #{req.params.from}"
   dooutput(res, message, subtitle)

+app.get '/linus/:name/:from', (req, res) ->
+  message = "#{req.params.name}, there aren't enough swear-words in the English language, so now I'll have to call you perkeleen vittupää just to express my disgust and frustration with this crap."
+  subtitle = "- #{req.params.from}"
+  dooutput(res, message, subtitle)
+
 port = process.env.PORT || 5000
 app.listen port
-console.log "FOAAS Started on port #{port}"+console.log "FOAAS Started on port #{port}"


remove comment

remove comment

--- server.coffee
+++ server.coffee
@@ -138,15 +138,7 @@

 io.sockets.on 'connection', (socket) =>
     console.log 'hallo'
-
-###
-io.sockets.on('connection', function(socket) {
-
-    function log(eventStr) {
-        console.log("Event: " + eventStr + " from " + users[socket.id] + " (" + socket.id + ")")
-    }
-    log("connection");
-###
+

 console.log "http server running on port " + config.server_port


little binding magic to get rid of the Server.<function> calls inside the server

--- server.coffee
+++ server.coffee
@@ -1,5 +1,7 @@
 class Server

+  @recommendables = []
+
   http = require 'http'

   # helper function that responds to the client
@@ -10,35 +12,37 @@
       res.write data
       res.end()

-  @setup: ->
-    @server = http.createServer (request, response) ->
+  @setup: =>
+    @server = http.createServer (request, response) =>
       console.log "Request: #{request.method} #{request.url}"
-      data = Server.testJSON()
+      data = @testJSON()
       request.on 'data', (chunk) -> data += chunk
-      request.on 'end', () ->
+      request.on 'end', () =>
         console.log "Data: #{data}"
         requestObject = JSON.parse(decodeURIComponent(data.trim()))
         console.log requestObject
         switch requestObject.msg
           when 'feedback'
-            Server.processFeedback(requestObject)
+            @processFeedback(requestObject)
           when 'impression'
-            Server.processImpression(requestObject)
+            @processImpression(requestObject)
           when 'error'
-            Server.processError(requestObject)
+            @processError(requestObject)
           else
             console.log 'lol?'
-        Server.respond response, 200, 'text/plain', 'Hi'
+        @respond response, 200, 'text/plain', 'Hi'

-  @processFeedback: (requestObject) ->    console.log 'Got feedback'
+  @processFeedback: (requestObject) ->
+    console.log 'Got feedback'

   @processImpression: (requestObject) ->
-    Server.saveItem(requestObject.item) if requestObject.item.recommendable
-    Server.makeRecommendation(requestObject) if requestObject.config.recommend
+    @saveItem(requestObject.item) if requestObject.item.recommendable
+    @makeRecommendation(requestObject) if requestObject.config.recommend

   @saveItem: (item) ->
     console.log 'Save the item'
+    @recommendables.push item

   @makeRecommendation: (requestObject) ->
     console.log "Let's recommend something"


Require http using Node require()

Require http using Node require()

--- server.coffee
+++ server.coffee
@@ -1,5 +1,6 @@
 requirejs = require("requirejs")
 express = require("express")
+http = require("http")

 requirejs.config({
     baseUrl: './src',
@@ -24,7 +25,7 @@

-requirejs ['game', 'synchronizedtime', 'http'], (Game, SynchronizedTime, http) ->
+requirejs ['game', 'synchronizedtime'], (Game, SynchronizedTime) ->

   app = express()


More checking for object.

More checking for object.

--- server.coffee
+++ server.coffee
@@ -2,7 +2,7 @@

 checkNames = (publish, collectionNames, computation, result) ->
   if result and _.isArray result
-    resultNames = (cursor._getCollectionName() for cursor in result when '_getCollectionName' of cursor)
+    resultNames = (cursor._getCollectionName() for cursor in result when _.isObject cursor and '_getCollectionName' of cursor)
   else if result and _.isObject result and '_getCollectionName' of result
     resultNames = [result._getCollectionName()]
   else


tweaked impression handling, placed http variable into correct context, renamed method, tweaked response messages

--- server.coffee
+++ server.coffee
@@ -3,9 +3,8 @@
   set = require './Set'
   @recommendables = set.createSet()

-  http = require 'http'
-
   @setup: =>
+    http = require 'http'
     @server = http.createServer (request, response) =>
       #console.log "Request: #{request.method} #{request.url}"
       data = @testJSON()
@@ -35,17 +34,23 @@

   @processImpression: (requestObject) ->
     @saveItem(requestObject.item) if requestObject.item.recommendable
-    recommendations = @makeRecommendation(requestObject) if requestObject.config.recommend
-    content =
-      code: 200
-      data: recommendations
+    if requestObject.config.recommend
+      recommendations = @findRecommendations(requestObject)
+      content =
+        code: 200
+        data: recommendations
+    else
+      console.log 'Received impression, but nothing has to be recommended'
+      content =
+        code: 200
+        phrase: 'OK'

   @saveItem: (item) ->
     console.log 'Save the item. Recommendable items now are:'
     @recommendables.add item
     console.log @recommendables['hash']

-  @makeRecommendation: (requestObject) ->
+  @findRecommendations: (requestObject) ->
     console.log "Let's recommend something"
     #TODO find real recommendations, for now they are 1,2,3 and 4 ;)
     recommendations =
@@ -67,7 +72,7 @@
   @respond: (res, content) ->
       data = JSON.stringify(
         content['data'] ?
-        {error: content['phrase'], code: content['code']}
+        passphrase: content['phrase'], code: content['code']
       )

       res.writeHead content['code'],


dead code

dead code

--- src/skuld.coffee
+++ src/skuld.coffee
@@ -100,13 +100,4 @@
   }
   , TConsensus List norns

-    #@bind (norns) ->
-    #  new Consensus
-    #    norns.concat Trait.create
-    #      mark : @norns.length
-    #    TNorn
-    #    mark : -1
-    #    verdandi : TSkuld
-    #  }, TNorn(norn)
-
 exports.createConsensus = Consensus


Added handling of rate limiting errors

Added handling of rate limiting errors

--- src/slack.coffee
+++ src/slack.coffee
@@ -44,6 +44,8 @@
     @client.login()

   error: (error) =>
+    return @robot.logger.warning "Received rate limiting error #{JSON.stringify error}" if error.code == -1
+
     @robot.logger.error "Received error #{JSON.stringify error}"
     @robot.logger.error error.stack
     @robot.logger.error "Exiting in 1 second"


Memory is now correctly defined in `xo` object.

--- src/spec.coffee
+++ src/spec.coffee
@@ -77,14 +77,16 @@
               exit: (VM) -> @field -= VM.CPUs.length

           $memory: @dynamic { usage: 0, size: 0 },
-            host:
+            host_metrics:
               # No `update`: `exit` then `enter` will be called instead.
-              enter: (host) ->
-                @field.usage += host.memory.usage
-                @field.size += host.memory.size
-              exit: (host) ->
-                @field.usage -= host.memory.usage
-                @field.size -= host.memory.size
+              enter: (metrics) ->
+                {memory_free, memory_total} = metrics
+                @field.usage += +memory_total - memory_free
+                @field.size += +memory_total
+              exit: (metrics) ->
+                {memory_free, memory_total} = metrics
+                @field.usage -= +memory_total - memory_free
+                @field.size -= +memory_total

       pool:

@@ -207,8 +209,8 @@
                 metrics_UUID = refsToUUIDs[@generator.metrics]
                 return if UUID isnt metrics_UUID
                 {memory_free, memory_total} = metrics
-                @field.usage = memory_total - memory_free
-                @field.size = memory_total
+                @field.usage = +memory_total - memory_free
+                @field.size = +memory_total

           power_state: 'Running' # TODO

@@ -261,14 +263,14 @@
               update: (metrics, UUID) ->
                 return if UUID isnt refsToUUIDs[@generator.metrics]

-                @field.size = metrics.memory_actual
+                @field.size = +metrics.memory_actual
             }
             VM_guest_metrics: {
               update: (metrics, UUID) ->
                 return if UUID isnt refsToUUIDs[@generator.metrics]

-                @field.size = metrics.memory.total
-                @field.usage = metrics.memory.used
+                @field.size = +metrics.memory.total
+                @field.usage = +metrics.memory.used
             }
           }


:memo: Document Task.cancel

:memo: Document Task.cancel

--- src/task.coffee
+++ src/task.coffee
@@ -155,6 +155,9 @@

     true

+  # Public: Cancel the running task and emit an event if it was canceled.
+  #
+  # Returns a {Boolean} indicating whether the task was terminated.
   cancel: ->
     didForcefullyTerminate = @terminate()
     if didForcefullyTerminate


Escape backslashes in task bootstrap on all platforms

--- src/task.coffee
+++ src/task.coffee
@@ -51,10 +51,10 @@
       #{coffeeCacheRequire}
       #{taskBootstrapRequire}
     """
-    bootstrap = bootstrap.replace(/\\/g, "\\\\") if process.platform == 'win32'
+    bootstrap = bootstrap.replace(/\\/g, "\\\\")

     taskPath = require.resolve(taskPath)
-    taskPath = taskPath.replace(/\\/g, "\\\\") if process.platform == 'win32'
+    taskPath = taskPath.replace(/\\/g, "\\\\")

     env = _.extend({}, process.env, {taskPath, userAgent: navigator.userAgent})
     args = [bootstrap, '--harmony_collections']


re-enable tests

re-enable tests

--- src/test.coffee
+++ src/test.coffee
@@ -11,9 +11,9 @@

 run_tests = (rx) ->
   test_god(test, rx)
-  #test_world(test, rx)
+  test_world(test, rx)
   test_config(test, rx)
-  #test_draw(test, rx)
+  test_draw(test, rx)

 exports.test = (rx) ->
   console.log "Running tests..."


Fix evaluating args with scheduler

Fix evaluating args with scheduler

--- assets/javascripts/evaluator/evaluator.coffee
+++ assets/javascripts/evaluator/evaluator.coffee
@@ -1,12 +1,22 @@
 V = Visualizer
 N = V.ReactNodes
+
+getArgsWithScheduler = ({input, getDefaultArgs, useScheduler}) ->
+  if getDefaultArgs && useScheduler
+    "#{input}, scheduler"
+  else if useScheduler
+    "scheduler"
+  else if getDefaultArgs
+    input
+  else
+    ""

 rootEvaluators = R.mapObjIndexed( ({useScheduler, getDefaultArgs}, key) ->
   (input) ->
     R.always(
       "Rx.Observable.#{key}(" +
-        (if getDefaultArgs then input else '') +
-        (if useScheduler then (if getDefaultArgs then ', scheduler)' else 'scheduler)') else ')')
+      getArgsWithScheduler({input, useScheduler, getDefaultArgs}) +
+      ")"
     )
   )(N.Roots)

@@ -19,7 +29,9 @@
           when "observable" then ".#{key}(#{innerObservable})"
           when "observableWithSelector" then ".#{key}(#{innerObservable}, #{input})"
     else
-      R.always(".#{key}(#{input || ''}#{useScheduler && ', scheduler' || ''})")
+      R.always(
+        ".#{key}(#{getArgsWithScheduler({input, useScheduler, getDefaultArgs})})"
+      )
   )(N.Operators)

 evalRoot = ({id, type, args}) ->


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/tween.coffee
+++ src/tween.coffee
@@ -1,7 +1,7 @@
 ###*
-The Tween module provides a method to tween objec.
+The Tween module provides a method to tween object properties.

-@name Flickerable
+@name Tween
 @module
 @constructor
 @param {Object} I Instance variables


Fixed deepExtend when value may also accept arrays

--- src/utils.coffee
+++ src/utils.coffee
@@ -23,7 +23,7 @@
   return {} if not object?
   for other in extenders
     for own key, val of other
-      if not object[key]? or typeof val isnt "object"
+      if not object[key]? or typeof val isnt "object" or 'array' is toType val
         object[key] = val
       else
         object[key] = deepExtend object[key], val


Added support for an "onCreateNode" callback.

--- src/tree.coffee
+++ src/tree.coffee
@@ -236,6 +236,9 @@
         _.each getNode(template, id).children, (childNode) ->
           # Check all children and trigger a change event so it's recursive.
           getCheckbox(template, childNode.id).prop('checked', isChecked).trigger('change')
+  onCreateNode = settings.onCreateNode
+  if onCreateNode
+    onCreateNode.apply(@, arguments)

   $selectRow = $('

')
   $('.jqtree-element', $em).append($selectRow)


fix map resize

fix map resize

--- src/views.coffee
+++ src/views.coffee
@@ -237,6 +237,12 @@
     'click .fr_map_cover': 'enable'
     'click [data-js-clear]': 'disable'

+  initialize: ->
+    FormRenderer.Views.ResponseField::initialize.apply @, arguments
+
+    @on 'shown', ->
+      @map?._onResize()
+
   render: ->
     FormRenderer.Views.ResponseField::render.apply @, arguments
     @$cover = @$el.find('.fr_map_cover')


Changed implementation of toBase64. Now using Uint8Array to store char code

This avoids constantly resizing a list of characters as it was done
previously.

TODO: Need to test String.fromCharCode.apply for large sequences

--- src/util.coffee
+++ src/util.coffee
@@ -18,6 +18,15 @@
 ###
 base64Code = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='

+###
+Char codes for base64Code string
+###
+base64CharCode = new Uint8Array(base64Code.length)
+
+# get char codes from base64Code
+for i in [0...base64Code.length]
+    base64CharCode[i] = base64Code.charCodeAt(i)
+

 ###
 Encodes and ArrayBuffer object to base 64.
@@ -31,8 +40,6 @@
     if !buffer instanceof ArrayBuffer
         throw 'argument is not an ArrayBuffer object'

-    code = []
-
     # creates a uint8 view of the buffer
     buffer_u8 = new Uint8Array(buffer)

@@ -41,19 +48,25 @@
     mod = buffer.byteLength % 3
     N = (buffer.byteLength) - mod

+    # size of the encoded array, including padding
+    S = 4*(N/3) + (if mod != 0 then 4 else 0)
+
+    # stores the char codes for the string
+    codeBuffer = new Uint8Array(S)
+
+    # index to scan codeBuffer
+    s = -1
+
     # encode the first N bytes of buffer
     for n in [0...N] by 3
-        console.log('n: ' + n)
-
         # read next three bytes to encode
         word = (buffer_u8[n] << 16) | (buffer_u8[n+1] << 8) | buffer_u8[n+2]

-        # get the character indices from word and
-        # transform it to text
-        code.push(base64Code[(word & 0xFC0000) >> 18])
-        code.push(base64Code[(word & 0x03F000) >> 12])
-        code.push(base64Code[(word & 0x03F000) >> 12])
-        code.push(base64Code[(word & 0x00003F)])
+        # get the character code for each element of word
+        codeBuffer[++s] = base64CharCode[(word & 0xFC0000) >> 18]
+        codeBuffer[++s] = base64CharCode[(word & 0x03F000) >> 12]
+        codeBuffer[++s] = base64CharCode[(word & 0x000FC0) >> 6]
+        codeBuffer[++s] = base64CharCode[(word & 0x00003F)]

     # encode the last bytes of buffer
@@ -66,22 +79,28 @@
             break

         when 1
+            # read one byte only
             word = (buffer_u8[N] << 16)

-            code.push(base64Code[(word & 0xFC0000) >> 18])
-            code.push(base64Code[(word & 0x03F000) >> 12])
-            code.push(base64Code[64])   # padding
-            code.push(base64Code[64])   # padding
+            # get the character code for each element of word
+            codeBuffer[++s] = base64CharCode[(word & 0xFC0000) >> 18]
+            codeBuffer[++s] = base64CharCode[(word & 0x03F000) >> 12]
+            codeBuffer[++s] = base64CharCode[64]    # padding
+            codeBuffer[++s] = base64CharCode[64]    # padding

         when 2
+            # read two bytes only
             word = (buffer_u8[N] << 16) | (buffer_u8[N+1] << 8)

-            code.push(base64Code[(word & 0xFC0000) >> 18])
-            code.push(base64Code[(word & 0x03F000) >> 12])
-            code.push(base64Code[(word & 0x000FC0) >> 6])
-            code.push(base64Code[64])   # padding
+            # get the character code for each element of word
+            codeBuffer[++s] = base64CharCode[(word & 0xFC0000) >> 18]
+            codeBuffer[++s] = base64CharCode[(word & 0x03F000) >> 12]
+            codeBuffer[++s] = base64CharCode[(word & 0x000FC0) >> 6]
+            codeBuffer[++s] = base64CharCode[64]     # padding

-    return code.join('')
+
+    str = String.fromCharCode.apply(null, codeBuffer)
+    return str

 ###


Fix isDirectory()

Fix isDirectory()

--- src/util.coffee
+++ src/util.coffee
@@ -1,7 +1,19 @@
 {Directory, File} = require 'atom'
+fs = require 'fs'
 HS = -> require '../hs/HaskellCabal.min.js'

 module.exports = Util =
+  isDirectory: (dir) ->
+    switch
+      when dir instanceof Directory
+        return fs.statSync(dir.getPath()).isDirectory()
+      when dir instanceof File
+        return fs.statSync(dir.getPath()).isDirectory()
+      when typeof dir is 'string'
+        return fs.statSync(dir).isDirectory()
+      else
+        return false
+
   getRootDirFallback: (file) ->
     [dir] = atom.project.getDirectories().filter (dir) ->
       dir.contains(file?.getPath?())
@@ -9,7 +21,7 @@
       dir = atom.project.getDirectories()[0]
     if dir?.getPath?() is 'atom://config'
       dir = null
-    unless dir?.isDirectory?()
+    unless Util.isDirectory(dir)
       dir = file?.getParent?() ? new Directory '.'
     dir

@@ -27,15 +39,16 @@
         d = d?.getParent?()
       d if check d
     file =
-      if bufferOrFileOrString.file?
-        bufferOrFileOrString.file?
-      else if bufferOrFileOrString instanceof File
-        bufferOrFileOrString
-      else if typeof bufferOrFileOrString is 'string'
-        new File(bufferOrFileOrString)
+      switch
+        when bufferOrFileOrString.file?
+          bufferOrFileOrString.file?
+        when bufferOrFileOrString instanceof File
+          bufferOrFileOrString
+        when typeof bufferOrFileOrString is 'string'
+          new File(bufferOrFileOrString)
     dir = file?.getParent?() ? Util.getRootDirFallback file
     dir = findProjectRoot(dir, dirHasCabalFile) ? findProjectRoot(dir, dirHasSandboxFile)
-    unless dir?.isDirectory?()
+    unless Util.isDirectory(dir)
       dir = Util.getRootDirFallback file
     return dir


fix typo]

fix typo]

--- src/vent.coffee
+++ src/vent.coffee
@@ -32,7 +32,7 @@
         details.message = message if message

         throw new Error("a topic is required for publish") unless details.topic
-        throw new Error("a message is required for pulish") unless details.message
+        throw new Error("a message is required for publish") unless details.message

         details.channel ?= @setup._default_channel


codename is gone

codename is gone

--- src/zappa.coffee
+++ src/zappa.coffee
@@ -5,7 +5,6 @@

 zappa = version: '0.4.22'

-codename = 'You can\'t do that on stage anymore'

 log = console.log
 fs = require 'fs'
@@ -738,7 +737,7 @@
   express_ready = ->
     log 'Express server listening on port %d in %s mode',
       zapp.server.address()?.port, app.settings.env
-    log "Zappa #{zappa.version} \"#{codename}\" orchestrating the show"
+    log "Zappa #{zappa.version} orchestrating the show"

   if host
     zapp.server.listen port, host, express_ready


fix blind text name for module

fix blind text name for module

--- src/louie.coffee
+++ src/louie.coffee
@@ -1,4 +1,4 @@
-class Test
+class Louie
 	constructor: ->
 		@_tasks = []
 		@_timeout = null
@@ -37,4 +37,4 @@
 			return timeout
 		1000

-module.exports = Test
+module.exports = Louie


fix(model): setting relation id as editable correctly

--- src/model.coffee
+++ src/model.coffee
@@ -71,11 +71,12 @@
         if typeof fn != 'function'
           throw new Error 'No model for has_one/belongs_to relation found'

-        if typeof self[name + '_id'] != 'function' && name not in options.belongs_to && name + '_id' not in options.editable
+        if typeof self[name + '_id'] != 'function' && name not in options.belongs_to
           unless options.editable?
             options.editable = []

-          options.editable.push name + '_id'
+          if name + '_id' not in options.editable
+            options.editable.push name + '_id'

         if self[name]?
           Observable self, name, new fn(self[name])
@@ -144,7 +145,6 @@
     # Set editable fields
     #
     if options.editable?
-      console.log options.editable
       for index, name of options.editable
         Editable self, name, DelayedSave.apply(self, [options, self])


Settings tab doesn't replace temporary tab

--- lib/sublime-tab-bar-view.coffee
+++ lib/sublime-tab-bar-view.coffee
@@ -31,8 +31,9 @@
       false

   addTabForItem: (item, index) ->
-    for tab in @getTabs()
-      @closeTab(tab) if tab.is('.temp')
+    if item.activePanelName != "Settings"
+        for tab in @getTabs()
+          @closeTab(tab) if tab.is('.temp')

     tabView = new SublimeTabView(item, @pane, @openPermanent, @considerTemp)
     @insertTabAtIndex(tabView, index)


First appendTo() to get dimensions, then render

--- bokehjs/src/coffee/widget/cell_editors.coffee
+++ bokehjs/src/coffee/widget/cell_editors.coffee
@@ -32,11 +32,11 @@
       @render()

     render: () ->
+      @$el.appendTo(@args.container)
       @$input = $(@input)
       @$el.append(@$input)
       @renderEditor()
       @disableNavigation()
-      @$el.appendTo(@args.container)

     renderEditor: () ->


enable spectate in one team ladder (#6162)

Co-authored-by: smallst_wsl <6c42689ff77371401bf43b7d56b93ac1ca3e92d3@163.com>

--- app/views/ladder/LadderTabView.coffee
+++ app/views/ladder/LadderTabView.coffee
@@ -40,6 +40,7 @@
     @refreshLadder()

     @capitalize = _.string.capitalize
+    @selectedTeam = 'humans'

     # Trying not loading the FP/G+ stuff for now to see if anyone complains they were using it so we can have just two columns.
     #@socialNetworkRes = @supermodel.addSomethingResource('social_network_apis', 0)
@@ -318,13 +319,28 @@
     row = cell.parent()
     table = row.closest('table')
     wasSelected = cell.hasClass 'selected'
-    table.find('.spectate-cell.selected').removeClass 'selected'
-    cell = $(e.target).closest('.spectate-cell').toggleClass 'selected', not wasSelected
-    sessionID = row.data 'session-id'
-    teamID = table.data 'team'
-    @spectateTargets ?= {}
-    @spectateTargets[teamID] = if wasSelected then null else sessionID
-    console.log @spectateTargets, cell, row, table
+    if @teams.length == 2
+      table.find('.spectate-cell.selected').removeClass 'selected'
+      cell = $(e.target).closest('.spectate-cell').toggleClass 'selected', not wasSelected
+      sessionID = row.data 'session-id'
+      teamID = table.data 'team'
+      @spectateTargets ?= {}
+      @spectateTargets[teamID] = if wasSelected then null else sessionID
+      console.log @spectateTargets, cell, row, table
+    else
+      if wasSelected
+        removeClass = if cell.hasClass('selected-humans') then 'selected-humans' else 'selected-ogres'
+        cell = $(e.target).closest('.spectate-cell').removeClass ('selected '+removeClass)
+        teamID = @selectedTeam = if removeClass is 'selected-humans' then 'humans' else 'ogres'
+      else
+        table.find('.spectate-cell.selected.selected-' + @selectedTeam).removeClass ('selected selected-'+@selectedTeam)
+        cell = $(e.target).closest('.spectate-cell').addClass 'selected selected-'+@selectedTeam
+        teamID = @selectedTeam
+        @selectedTeam = if @selectedTeam is 'humans' then 'ogres' else 'humans'
+      sessionID = row.data 'session-id'
+      @spectateTargets ?= {}
+      @spectateTargets[teamID] = if wasSelected then null else sessionID
+      console.log @spectateTargets, cell, row, table

   onLoadMoreLadderEntries: (e) ->
     @ladderLimit ?= 100


minor change in the doc

minor change in the doc

--- library/nucleus/cakefile.coffee
+++ library/nucleus/cakefile.coffee
@@ -62,7 +62,7 @@
     option "-d", "--documents [PATH]", "Path to the documents directory"
     option "-s", "--scoping [SCOPE]", "The name of the scope to boot kernel"
     option "-i", "--logging [LEVEL]", "The level to use for the logging output"
-    option "-w", "--watch", "Watch the library sources and recompile"
+    option "-w", "--watch", "Watch the library sources and recompile it"
     option "-g", "--git-hub-pages", "Publish documents to GitHub pages"

     # This is one of the major tasks in this Cakefile, it implements


actually support overriding with options (cherry picked from commit efb393d482f1b534f49db7c042b63e19507460ef)

--- src/models/views/WebView.coffee
+++ src/models/views/WebView.coffee
@@ -22,7 +22,7 @@
   preload: (options={}, callbacks={}) ->
     steroids.debug "preload called for WebView #{JSON.stringify @}"

-    proposedId = @location || options.id
+    proposedId = options.id || @location

     setIdOnSuccess = () =>
       steroids.debug "preload success: setting id"
@@ -32,7 +32,7 @@
       method: "preloadLayer"
       parameters:
         id: proposedId
-        url: @location || options.location
+        url: options.location || @location
       successCallbacks: [setIdOnSuccess, callbacks.onSuccess]
       failureCallbacks: [callbacks.onFailure]


unnecessary double clear

unnecessary double clear

--- src/modules/undo-manager.coffee
+++ src/modules/undo-manager.coffee
@@ -30,9 +30,7 @@
       )
     )
     @quill.on(@quill.constructor.events.TEXT_CHANGE, (delta, origin) =>
-      if _.isEqual(delta, @emittedDelta)
-        @emittedDelta = null
-        return
+      return if _.isEqual(delta, @emittedDelta)
       this.record(delta, @oldDelta)
       @oldDelta = @quill.getContents()
     )


googledrive: get rid of defunct html5hack

--- src/provider/googledrive.coffee
+++ src/provider/googledrive.coffee
@@ -7,7 +7,6 @@
 Media = require '../media'
 { ITAG_QMAP, ITAG_CMAP } = require '../util/itag'

-HTML5_HACK_ENABLED = false
 LOGGER = require('@calzoneman/jsli')('mediaquery/googledrive')

 extractHexId = (url) ->
@@ -16,9 +15,6 @@
         return m[1]
     else
         return null
-
-exports.setHTML5HackEnabled = (enabled) ->
-    HTML5_HACK_ENABLED = enabled

 fetchAndParse = (id, options = {}) ->
     url = "https://docs.google.com/get_video_info?authuser=&docid=#{id}&sle=true&hl=en"
@@ -80,24 +76,7 @@
     )

 exports.lookup = lookup = (id) ->
-    if not HTML5_HACK_ENABLED
-        return fetchAndParse(id, fetchSubtitles: true)
-
-    return fetchAndParse(id,
-        fetchSubtitles: true
-        family: 6
-    ).then((media6) ->
-        return fetchAndParse(id,
-            family: 4
-        ).then((media4) ->
-            media6.meta.html5hack = true
-            for quality in [1080, 720, 480, 360]
-                media6.meta.direct[quality] = media6.meta.direct[quality].concat(
-                    media4.meta.direct[quality]
-                )
-            return media6
-        )
-    )
+    return fetchAndParse(id, fetchSubtitles: true)

 exports.getSubtitles = (id, vid) ->
     params =


Unit tests fixed

Unit tests fixed

--- test.spec.coffee
+++ test.spec.coffee
@@ -38,7 +38,7 @@

 	it 'removes key', ->
 		dict = Dict a: 1
-		expect(dict.pop 'a').toBe dict
+		dict.pop 'a'
 		expect(dict.get 'a').toBe undefined

 describe 'keys()', ->
@@ -90,7 +90,7 @@
 		expect(dict2.items()).toEqual dict.items()
 		expect(dict2).toEqual jasmine.any Dict

-describe 'onChanged signal', ->
+describe 'onChange signal', ->

 	dict = listener = null
 	ok = false
@@ -108,59 +108,25 @@

 	afterEach ->
 		expect(ok).toBeTruthy()
-		dict.onChanged.disconnect listener
+		dict.onChange.disconnect listener

 	it 'works with set() on new item', ->
 		dict = Dict()
-		dict.onChanged listener
+		dict.onChange listener
 		dict.set 'a', 1
 		expect(args).toEqual [['a', undefined]]
 		expect(items).toEqual [['a', 1]]

 	it 'works with set() on item change', ->
 		dict = Dict a: 1
-		dict.onChanged listener
+		dict.onChange listener
 		dict.set 'a', 2
 		expect(args).toEqual [['a', 1]]
 		expect(items).toEqual [['a', 2]]

 	it 'works with pop()', ->
 		dict = Dict a: 1
-		dict.onChanged listener
+		dict.onChange listener
 		dict.pop 'a'
 		expect(args).toEqual [['a', 1]]
 		expect(items).toEqual []
-
-describe 'properties', ->
-
-	it 'getter/setter works', ->
-		NAME = 'abc'
-		NAME2 = '123'
-
-		dict = Dict()
-		Dict.defineProperty dict, 'name'
-
-		expect(dict.name).toBe undefined
-		dict.name = NAME
-		expect(dict.name).toBe NAME
-		expect(dict.get 'name').toBe NAME
-
-		dict.set 'name', NAME2
-		expect(dict.name).toBe NAME2
-		expect(dict.get 'name').toBe NAME2
-
-	it 'custom signals are calling', ->
-		dict = Dict()
-		Dict.defineProperty dict, 'name'
-
-		i = 0
-		args = []
-		dict.onNameChanged ->
-			args.push [arguments...]
-			i++
-
-		dict.name = 2
-		dict.name = 25
-
-		expect(i).toBe 2
-		expect(args).toEqual [[undefined], [2]]


sample test

sample test

--- test/main.coffee
+++ test/main.coffee
@@ -0,0 +1,10 @@
+APPNAME = require '../'
+should = require 'should'
+require 'mocha'
+
+describe 'FUNCTIONTYPE', ->
+  describe 'FUNCTIONNAME()', ->
+    it 'should TASKNAME', (done) ->
+      should.exist true
+      true.should.equal.true
+      done()


Changed pane indexes after a sort is done.

--- components/tabs/KDTabView.coffee
+++ components/tabs/KDTabView.coffee
@@ -82,10 +82,13 @@
       methodName  = 'insertBefore'
       targetIndex = index - 1
     @handles[index].$()[methodName] @handles[targetIndex].$()
-
-    spliced  = @handles.splice index, 1
-    newIndex = if dir is 'left' then index - 1 else index + 1
-    @handles.splice newIndex, 0, spliced[0]
+
+    newIndex       = if dir is 'left' then index - 1 else index + 1
+    splicedHandle  = @handles.splice index, 1
+    splicedPane    = @panes.splice index, 1
+
+    @handles.splice newIndex, 0, splicedHandle[0]
+    @panes.splice   newIndex, 0, splicedPane[0]

   removePane:(pane)->
     pane.emit "KDTabPaneDestroy"


Implement PatchIterator::splice

Implement PatchIterator::splice

Signed-off-by: Nathan Sobo <2e8aa918660411855c6d44d5bb2da677aa033255@github.com>

--- src/patch.coffee
+++ src/patch.coffee
@@ -10,45 +10,9 @@
       @inputExtent = @inputExtent.traverse(child.inputExtent)
       @outputExtent = @outputExtent.traverse(child.outputExtent)

-  splice: (spliceOutputStart, oldOutputExtent, newOutputExtent, content) ->
-    spliceOldOutputEnd = spliceOutputStart.traverse(oldOutputExtent)
-
-    i = 0
-    childOutputEnd = Point.zero()
-    while i < @children.length
-      child = @children[i]
-      childOutputStart = childOutputEnd
-      childOutputEnd = childOutputStart.traverse(child.outputExtent)
-
-      if extentToCut?
-        newExtentToCut = extentToCut.traversalFrom(child.outputExtent)
-        if newExtentToCut.isPositive()
-          @children.splice(i, 1)
-          extentToCut = newExtentToCut
-        else
-          child.cut(extentToCut)
-          break
-      else
-        if childOutputEnd.compare(spliceOutputStart) > 0
-          relativeSpliceOutputStart = spliceOutputStart.traversalFrom(childOutputStart)
-          extentToCut = spliceOldOutputEnd.traversalFrom(childOutputEnd)
-          if splitNodes = child.splice(relativeSpliceOutputStart, oldOutputExtent, newOutputExtent, content)
-            @children.splice(i, 1, splitNodes...)
-            i += splitNodes.length
-          else
-            i++
-        else
-          i++
-
-    if @children.length > BRANCHING_THRESHOLD
-      splitIndex = Math.ceil(@children.length / BRANCHING_THRESHOLD)
-      [new Node(@children.slice(0, splitIndex)), new Node(@children.slice(splitIndex))]
-    else
-      null
-
   cut: (extentToCut) ->
-    @inputExtent = Point.max(Point.zero(), @inputExtent.traversalFrom(extent))
-    @outputExtent = @outputExtent.traversalFrom(extent)
+    @inputExtent = Point.max(Point.zero(), @inputExtent.traversalFrom(extentToCut))
+    @outputExtent = @outputExtent.traversalFrom(extentToCut)

     i = 0
     while i < @children.length
@@ -61,13 +25,6 @@
       else
         child.cut(extentToCut)
         break
-
-  mergeChildrenIfNeeded: (i) ->
-    if @children[i]?.shouldMergeWith(@children[i + 1])
-      @children.splice(i, 2, @children[i].merge(@children[i + 1]))
-      true
-    else
-      false

   getHunks: ->
     result = []
@@ -126,24 +83,22 @@
       "#{indent}Leaf #{@inputExtent} #{@outputExtent}"

 class PatchIterator
-  constructor: (@node) ->
+  constructor: (@patch) ->
     @inputPosition = Point.zero()
     @outputPosition = Point.zero()
     @nodeStack = []
-    @descend()
+    @descend(@patch.rootNode)

   next: ->
-    if @node?
-      value = @node.content?.slice(@leafOffset.column) ? null
-      @inputPosition = @inputPosition.traverse(Point.max(Point.zero(), @node.inputExtent.traversalFrom(@leafOffset)))
-      @outputPosition = @outputPosition.traverse(@node.outputExtent.traversalFrom(@leafOffset))
+    if @leaf?
+      value = @leaf.content?.slice(@leafOffset.column) ? null
+      @inputPosition = @inputPosition.traverse(Point.max(Point.zero(), @leaf.inputExtent.traversalFrom(@leafOffset)))
+      @outputPosition = @outputPosition.traverse(@leaf.outputExtent.traversalFrom(@leafOffset))

-      @node = null
       while parent = @nodeStack[@nodeStack.length - 1]
-        parent.index++
-        if nextChild = parent.node.children[parent.index]
-          @node = nextChild
-          @descend()
+        parent.childIndex++
+        if nextChild = parent.node.children[parent.childIndex]
+          @descend(nextChild)
           return {value, done: false}
         else
           @nodeStack.pop()
@@ -154,27 +109,55 @@
     childInputStart = Point.zero()
     childOutputStart = Point.zero()

-    if @nodeStack.length > 0
-      @node = @nodeStack[0].node
-      @nodeStack.length = 0
+    node = @patch.rootNode
+    @nodeStack.length = 0

-      while @node.children?
-        for child, index in @node.children
-          childInputEnd = childInputStart.traverse(child.inputExtent)
-          childOutputEnd = childOutputStart.traverse(child.outputExtent)
+    while node.children?
+      for child, childIndex in node.children
+        childInputEnd = childInputStart.traverse(child.inputExtent)
+        childOutputEnd = childOutputStart.traverse(child.outputExtent)

-          if childOutputEnd.compare(outputPosition) > 0
-            foundChild = true
-            @nodeStack.push({@node, index})
-            @node = child
-            break
+        if childOutputEnd.compare(outputPosition) > 0
+          foundChild = true
+          @nodeStack.push({node, childIndex})
+          node = child
+          break

-          childOutputStart = childOutputEnd
-          childInputStart = childInputEnd
+        childOutputStart = childOutputEnd
+        childInputStart = childInputEnd

+    @leaf = node
     @leafOffset = outputPosition.traversalFrom(childOutputStart)
-    @inputPosition = childInputStart.traverse(Point.min(@leafOffset, @node.inputExtent))
+    @inputPosition = childInputStart.traverse(Point.min(@leafOffset, @leaf.inputExtent))
     @outputPosition = outputPosition.copy()
+
+  splice: (oldOutputExtent, newOutputExtent, content) ->
+    extentToCut = oldOutputExtent.traversalFrom(@leaf.outputExtent.traversalFrom(@leafOffset))
+    splitNodes = @leaf.splice(@leafOffset, oldOutputExtent, newOutputExtent, content)
+
+    for {node, childIndex} in @nodeStack by -1
+      if splitNodes?
+        node.children.splice(childIndex, 1, splitNodes...)
+        childIndex += splitNodes.length
+        splitNodes = null
+      else
+        childIndex++
+
+      while extentToCut.isPositive() and childIndex < node.children.length
+        child = node.children[childIndex]
+        newExtentToCut = extentToCut.traversalFrom(child.outputExtent)
+        if newExtentToCut.isPositive()
+          node.children.splice(childIndex, 1)
+        else
+          child.cut(extentToCut)
+        extentToCut = newExtentToCut
+
+      if node.children.length > BRANCHING_THRESHOLD
+        splitIndex = Math.ceil(node.children.length / BRANCHING_THRESHOLD)
+        splitNodes = [new Node(node.children.slice(0, splitIndex)), new Node(node.children.slice(splitIndex))]
+
+    if splitNodes?
+      @patch.rootNode = new Node(splitNodes)

   getPosition: ->
     @outputPosition.copy()
@@ -182,23 +165,25 @@
   getSourcePosition: ->
     @inputPosition.copy()

-  descend: ->
+  descend: (node) ->
+    while node.children?
+      @nodeStack.push({node, childIndex: 0})
+      node = node.children[0]
+    @leaf = node
     @leafOffset = Point.zero()
-    while @node.children?
-      @nodeStack.push({@node, index: 0})
-      @node = @node.children[0]

 module.exports =
 class Patch
   constructor: ->
     @rootNode = new Leaf(Point.infinity(), Point.infinity(), null)

-  splice: (outputPosition, oldOutputExtent, newOutputExtent, content) ->
-    if splitNodes = @rootNode.splice(outputPosition, oldOutputExtent, newOutputExtent, content)
-      @rootNode = new Node(splitNodes)
+  splice: (spliceOutputStart, oldOutputExtent, newOutputExtent, content) ->
+    iterator = @buildIterator()
+    iterator.seek(spliceOutputStart)
+    iterator.splice(oldOutputExtent, newOutputExtent, content)

   buildIterator: ->
-    new PatchIterator(@rootNode)
+    new PatchIterator(this)

   toInputPosition: (outputPosition) ->


collision

collision

--- src/collision.coffee
+++ src/collision.coffee
@@ -91,8 +91,8 @@
   @name rayCircle
   @methodOf Collision

-  @param {Point} source A point
-  @param {Number} direction The direction from the point
+  @param {Point} source The starting position
+  @param {Point} direction A vector from the point
   @param {Object} target The circle

   @returns {Boolean} true if the line intersects the circle, false otherwise
@@ -128,9 +128,9 @@
   @name rayRectangle
   @methodOf Collision

-  @param source
-  @param direction
-  @param target
+  @param {Point} source The starting position
+  @param {Point} direction A vector from the point
+  @param {Object} target The rectangle

   @returns {Boolean} true if the line intersects the rectangle, false otherwise
   ###


things I learned when merging with gh-pages

--- src/communist.coffee
+++ src/communist.coffee
@@ -33,7 +33,7 @@
 		window.URL = window.URL or window.webkiURL
 		if fun
 			fun = fun.toString()
-			body = "self._add=function(name, func){		_db[name]=eval('('+atob(func)+')');		return true;	};	self._rm=function(name){		delete _db[name];		return true;	};var _f =  #{ fun };self.addEventListener('message', function(_e) {self.send = function(data){self.postMessage({messege:data,cb:_e.data.cb})};try{self.postMessage({body:_f.apply(null, _e.data.body),cb:_e.data.cb})}catch(_err){self.postMessage({error:_err,cb:_e.data.cb})}})"
+			body = "var send;var _f =  #{ fun };self.addEventListener('message', function(_e) {send = function(data){self.postMessage({message:data,cb:_e.data.cb})};self.postMessage({body:_f.apply(null, _e.data.body),cb:_e.data.cb})})"
 		else
 			body = defaultFunc
 		blob = new Blob [body],
@@ -50,9 +50,8 @@
 			if  e.data.body
 				@CBs[e.data.cb] null, e.data.body
 				delete @CBs[e.data.cb]
-			else if e.data.error
-				@CBs[e.data.cb] e.data.error
-				delete @CBs[e.data.cb]
+			else if e.data.message
+				@CBs[e.data.cb] null,e.data.message
 			true
 		@_worker.onerror = (e) ->
 			cb e


I don't think it was possible for this to work before...

--- src/quest.coffee
+++ src/quest.coffee
@@ -237,7 +237,7 @@
     @emit 'retryLoopFail', opts, error
     if opts.silent
       @silentErrors = true
-      console.error e.message.red.underline
+      console.error error.message.red.underline
       return
     else
       throw error


added ENV Var COUCHSVR

added ENV Var COUCHSVR

--- src/queue.coffee
+++ src/queue.coffee
@@ -2,7 +2,7 @@
 _ = require 'underscore'

 cradle.setup
-  host: 'localhost'
+  host: process.env.COUCHSVR or 'localhost'
   port: 5984
   cache: false
   raw: false


Only support verbatim and slackapp for now.

--- src/quote.coffee
+++ src/quote.coffee
@@ -98,7 +98,7 @@
       else
         msg.send "#{quotes.length} quotes loaded successfully."

-  robot.respond /(create|verbatim|slackapp|slackweb) quote:\s*([^]+)/i, (msg) ->
+  robot.respond /(verbatim|slackapp) quote:\s*([^]+)/i, (msg) ->
     unless robot.auth.hasRole(msg.message.user, CREATOR_ROLE)
       msg.reply [
         "You can't do that! You're not a *#{CREATOR_ROLE}*."
@@ -107,7 +107,7 @@
       return

     pname = msg.match[1]
-    pname = 'identity' if pname is 'create' or pname is 'verbatim'
+    pname = 'identity' if pname is 'verbatim'

     parse = parsers[pname]
     unless parse?


Implement "buffer quote".

Implement "buffer quote".

--- src/quote.coffee
+++ src/quote.coffee
@@ -128,6 +128,35 @@
         "```\n#{e.stack}\n```"
       ].join("\n")
       return
+
+    fs.appendFile quotefilePath, quote, ->
+      msg.reply "Quote added."
+      reloadThen (err) ->
+        if err?
+          msg.reply "AAAAAAAH"
+          msg.send err.stack
+        else
+          msg.send "#{quotes.length} quotes reloaded successfully."
+
+  robot.respond /buffer quote/i, (msg) ->
+    unless robot.bufferForUserName?
+      msg.reply "Buffer package not available."
+      return
+
+    buffer = robot.bufferForUserName msg.message.user.name
+    lines = buffer.commit()
+
+    unless lines.length > 0
+      msg.reply "Your buffer is empty."
+      return
+
+    processed = for line in lines
+      if line.isRaw()
+        line.text
+      else
+        ts = moment(line.timestamp).format('h:mm A D MMM YYYY')
+        "[#{ts}] #{line.speaker}: #{line.text}"
+    quote = "\n" + processed.join("\n") + "\n"

     fs.appendFile quotefilePath, quote, ->
       msg.reply "Quote added."


Update robot.router so we can log routes

--- src/robot.coffee
+++ src/robot.coffee
@@ -159,8 +159,23 @@

     @connect.use Connect.bodyParser()
     @connect.use Connect.router (app) =>
-      @router = app
-
+
+      @router =
+        get: (route, callback) =>
+          @logger.debug "Registered route: GET #{route}"
+          app.get route, callback
+
+        post: (route, callback) =>
+          @logger.debug "Registered route: POST #{route}"
+          app.post route, callback
+
+        put: (route, callback) =>
+          @logger.debug "Registered route: PUT #{route}"
+          app.put route, callback
+
+        delete: (route, callback) =>
+          @logger.debug "Registered route: DELETE #{route}"
+          app.delete route, callback

     @connect.listen process.env.PORT || 8080


Fucking indentation, honestly

Fucking indentation, honestly

--- src/sensu.coffee
+++ src/sensu.coffee
@@ -150,7 +150,7 @@
     data['expire'] = expiration
     data['path'] = 'silence/' + path

-      credential = createCredential()
+    credential = createCredential()
     req = robot.http(config.sensu_api + '/stashes')
     if credential
       req = req.headers(Authorization: credential)
@@ -174,7 +174,7 @@
     unless stash.match /^silence\/(.*)\//
       stash = addClientDomain(stash)

-     credential = createCredential()
+    credential = createCredential()
     req = robot.http(config.sensu_api + '/stashes')
     if credential
       req = req.headers(Authorization: credential)
@@ -217,7 +217,6 @@
   robot.respond /sensu client (.*)( history)/i, (msg) ->
     validateVars
     client = addClientDomain(msg.match[1])
-

     credential = createCredential()
     req = robot.http(config.sensu_api + '/clients/' + client + '/history')
@@ -250,8 +249,7 @@
     validateVars
     client = addClientDomain(msg.match[1])

-
-     credential = createCredential()
+    credential = createCredential()
     req = robot.http(config.sensu_api + '/clients/' + client)
     if credential
       req = req.headers(Authorization: credential)
@@ -296,7 +294,7 @@
       client = ''

-     credential = createCredential()
+    credential = createCredential()
     req = robot.http(config.sensu_api + '/events' + client)
     if credential
       req = req.headers(Authorization: credential)
@@ -329,7 +327,7 @@
     data['check'] = msg.match[2]

-     credential = createCredential()
+    credential = createCredential()
     req = robot.http(config.sensu_api + '/resolve')
     if credential
       req = req.headers(Authorization: credential)


setup: Allow to specify --timeout

setup: Allow to specify --timeout

--- src/setup.coffee
+++ src/setup.coffee
@@ -152,7 +152,7 @@
   return callback null, bindings

 # callbacks with bindings
-discoverParticipantQueues = (broker, graph, callback) ->
+discoverParticipantQueues = (broker, graph, options, callback) ->
   foundParticipantsByRole = {}

   broker = transport.getBroker broker if typeof broker == 'string'
@@ -188,7 +188,7 @@
       callback new Error 'setup: Participant discovery timed out'
       callback = null
       return
-    timeout = setTimeout onTimeout, 10000
+    timeout = setTimeout onTimeout, options.timeout

     broker.subscribeParticipantChange (msg) =>
       data = msg.data
@@ -255,7 +255,7 @@
   if options.discover
     # wait for FBP discovery messsages, use queues from there
     getBindings = (broker, graph, cb) ->
-      discoverParticipantQueues options.broker, graph, (err, defs) ->
+      discoverParticipantQueues options.broker, graph, options, (err, defs) ->
         return cb err if err
         #console.log 'got defs', definitions
         bindings = bindingsFromDefinitions graph, defs
@@ -297,6 +297,7 @@
     .option('--library <FILE.json>', 'Library definition to use', String, 'package.json')
     .option('--forward [stderr,stdout]', 'Forward child process stdout and/or stderr', String, '')
     .option('--discover [BOOL]', 'Whether to wait for FBP discovery messages for queue info', Boolean, false)
+    .option('--timeout <SECONDS>', 'How long to wait for discovery messages', Number, 30)
     .option('--forever [BOOL]', 'Keep running until killed by signal', Boolean, false)
     .option('--shell [shell]', 'Run participant commands in a shell', String, '')
     .action (gr, env) ->
@@ -305,6 +306,7 @@

   program.libraryfile = program.library
   program.graphfile = graph
+  program.timeout = program.timeout*1000
   return program

 exports.prettyFormatBindings = pretty = (bindings) ->


Update: 起動後のホワイトアウト時間の減少

Update: 起動後のホワイトアウト時間の減少

--- src/core/core.coffee
+++ src/core/core.coffee
@@ -20,6 +20,10 @@
     title: "BlitzModderPC",
     icon: path.join(app.getAppPath(), "gui/img/image.jpg"),
     autoHideMenuBar: true
+  )
+  mainWindow.once("ready-to-show", ->
+    win.show()
+    return
   )
   mainWindow.loadURL("file://#{app.getAppPath()}/gui/index.html")
   mainWindow.webContents.openDevTools()


test in dev mode should load correct swf

--- test/test.coffee
+++ test/test.coffee
@@ -23,7 +23,7 @@
         SC.initialize
           client_id: "YOUR_CLIENT_ID"
           baseUrl: "../vendor"
-        SC._recorderSwfPath = "/recorder.js/recorder.swf"
+        SC._recorderSwfPath = "/recorder.js/flash/bin-release/SoundcloudRecorder.swf"
         SC._soundmanagerPath = "/soundmanager2/swf/"
         SC._soundmanagerScriptPath = "../script/soundmanager2-nodebug-jsmin.js"
         @loadCoffeescripts @tests


more tests

more tests

--- test/test.coffee
+++ test/test.coffee
@@ -33,7 +33,7 @@

 {{#each items}}
 {{name}}
-{{/items}}
+{{/each}}

 """
 		vm =
@@ -70,3 +70,19 @@

 		run false
 		run true
+
+	it 'should process {{#with expr}}', ->
+		template """
+

+{{#with item}}
+{{name}}
+{{/with}}
+

+"""
+		vm =
+			item: {
+				name: 'test'
+			}
+		ko.applyBindings(vm)
+
+		$('.item').text().should.eql('test')


fix lines exceeding 80 char

fix lines exceeding 80 char

--- test/test.coffee
+++ test/test.coffee
@@ -54,14 +54,15 @@
   it 'should throw an error without content type id', ->
     compile_fixture.call(@, 'missing_config').should.be.rejected

-  it 'allows the content type name to be set through a k/v object config', (done) ->
-    compile_fixture.call(@, 'alt-content-type-config')
-      .with(@)
-      .then ->
-        p = path.join(@public, 'index.html')
-        h.file.contains(p, @title).should.be.true
-        h.file.contains(p, @body).should.be.true
-      .then(-> done()).catch(done)
+  it 'allows the content type name to be set through a k/v object config',
+    (done) ->
+      compile_fixture.call(@, 'alt-content-type-config')
+        .with(@)
+        .then ->
+          p = path.join(@public, 'index.html')
+          h.file.contains(p, @title).should.be.true
+          h.file.contains(p, @body).should.be.true
+        .then(-> done()).catch(done)

   after -> unmock_contentful()

@@ -114,8 +115,14 @@
   describe 'sort', ->
     before (done) ->
       @titles = ['Title C', 'Title B', 'Title A']
-      @bodies = ['Rich Boy selling crick', 'Something else', 'Nothing interesting']
-      @entries = ({fields: {title: @titles[index], body: @bodies[index]}} for index in [0..2])
+      @bodies = [
+        'Rich Boy selling crick',
+        'Something else',
+        'Nothing interesting'
+      ]
+      @entries = for index in [0..2]
+        {fields: {title: @titles[index], body: @bodies[index]}}
+
       mock_contentful(entries: @entries)
       compile_fixture.call(@, 'sort').then(-> done()).catch(done)

@@ -126,7 +133,11 @@
     it 'orders data correctly for the project', ->
       p = path.join(@public, 'index.html')
       # Titles should be order A before B before C
-      h.file.contains_match(p, '^.*(Title A)[/<>\\w\\s]*(Title B)[/<>\\w\\s]*(Title C).*$').should.be.true
+      h.file.contains_match(
+        p,
+        '^.*(Title A)[/<>\\w\\s]*(Title B)[/<>\\w\\s]*(Title C).*$'
+      ).should.be.true
+
       for body in @bodies
         h.file.contains(p, body).should.be.true

@@ -140,8 +151,14 @@
   describe 'transform', ->
     before (done) ->
       @titles = ['Title C', 'Title B', 'Title A']
-      @bodies = ['Rich Boy selling crick', 'Something else', 'Nothing interesting']
-      @entries = ({fields: {title: @titles[index], body: @bodies[index]}} for index in [0..2])
+      @bodies = [
+        'Rich Boy selling crick',
+        'Something else',
+        'Nothing interesting'
+      ]
+      @entries = for index in [0..2]
+        {fields: {title: @titles[index], body: @bodies[index]}}
+
       mock_contentful(entries: @entries)
       compile_fixture.call(@, 'transform').then(-> done()).catch(done)

@@ -152,7 +169,10 @@
     it 'does not reorder data', ->
       p = path.join(@public, 'index.html')
       # Titles should be order C before B before A
-      h.file.contains_match(p, '^.*(Title C)[/<>\\w\\s]*(Title B)[/<>\\w\\s]*(Title A).*$').should.be.true
+      h.file.contains_match(
+        p,
+        '^.*(Title C)[/<>\\w\\s]*(Title B)[/<>\\w\\s]*(Title A).*$'
+      ).should.be.true

     it 'has manipulated data correctly for the project', ->
       p = path.join(@public, 'index.html')
@@ -223,7 +243,7 @@

     after -> unmock_contentful()

-    it 'should not have the first entry\'s content in the second entries single view', ->
+    it 'should not have first entry\'s content in second entries single view', ->
       p = path.join(@public, "blog_posts/#{S(@title_2).slugify().s}.html")
       h.file.contains(p, @body).should.not.be.true

@@ -271,11 +291,12 @@
           h.file.contains(p, @body[i]).should.be.true
           h.file.contains(p, "<p>#{output}</p>").should.be.true

-    it 'sets a _urls attribute listing the paths to all of the entry\'s compiled files', ->
+    it 'sets _urls attribute to all of the entry\'s compiled files', ->
       p = path.join(@public, 'index.html')
       for lang in ['en', 'fr']
         for i in [0, 1]
-          h.file.contains(p, "/#{lang}/#{S(@title[i]).slugify().s}.html").should.be.true
+          h.file.contains(p, "/#{lang}/#{S(@title[i]).slugify().s}.html")
+            .should.be.true

     after -> unmock_contentful()


attempt to fix timeing issues

attempt to fix timeing issues

--- test/test.coffee
+++ test/test.coffee
@@ -4,15 +4,18 @@
   $("#content").html(content)
   # ActionCable.bindings.refresh()

-test "it should create subscritions for action-cable-subscription elements in the dom", ->
+test "it should create subscritions for action-cable-subscription elements in the dom", 2, (assert)->
   withContent """
     <action-cable-subscription channel="ChatChannel"></action-cable-subscription>
   """
-
   equal window.cable.subscriptions.findAll("{\"channel\":\"ChatChannel\"}").length, 1

   withContent ""
-  equal window.cable.subscriptions.findAll("{\"channel\":\"ChatChannel\"}").length, 0
+  done = assert.async()
+  requestAnimationFrame(->
+    equal window.cable.subscriptions.findAll("{\"channel\":\"ChatChannel\"}").length, 0
+    done()
+  ,100)

 test "it should trigger events on the elements for messages received through the channel", ->
   withContent """
@@ -36,7 +39,8 @@

   $('[data-cable-action]').click()

-test "it should perform actions when 'cable:perform' events are triggered on the element", ->
+test "it should perform actions when 'cable:perform' events are triggered on the element", (assert)->
+  done = assert.async()
   withContent """
     <action-cable-subscription id='action-test' channel='ChatChannel'>
     </action-cable-subscription>
@@ -45,6 +49,7 @@
   subscription = window.cable.subscriptions.findAll("{\"channel\":\"ChatChannel\"}")[0]
   subscription.perform = (action, data)->
     equal action, 'doAction'
+    done()

   $('#action-test').trigger('cable:perform', 'doAction')


add change for current py file

add change for current py file


Change supported minimap version

Change supported minimap version


Minor defineProperty fix

Minor defineProperty fix

--- src/access/active-record.coffee
+++ src/access/active-record.coffee
@@ -85,9 +85,9 @@
         @_asyncProperties[key] = property

         Object.defineProperty(@, key, {
-            configurable: property.configurable?
-            enumerable: property.enumerable?
-            writable: property.writable?
+            configurable: property.configurable ? false
+            enumerable: property.enumerable ? false
+            writable: property.writable ? false

             value: (values..., callback) ->
                 if _.isEmpty(values)


Prefix e2e test case names with function name

--- tests/e2e.coffee
+++ tests/e2e.coffee
@@ -8,7 +8,7 @@
 RANDOM1 = path.join(__dirname, 'images', '1.random')
 RANDOM2 = path.join(__dirname, 'images', '2.random')

-wary.it 'should be able to burn data to a file',
+wary.it 'write() should be able to burn data to a file',
 	random1: RANDOM1
 	random2: RANDOM2
 , (images) ->
@@ -25,7 +25,7 @@
 		.then (results) ->
 			m.chai.expect(results.random1).to.deep.equal(results.random2)

-wary.it 'should be rejected if the stream has no length',
+wary.it 'write() should be rejected if the stream has no length',
 	random1: RANDOM1
 	random2: RANDOM2
 , (images) ->


added goog analytics

added goog analytics

--- src/layouts/default.html.coffee
+++ src/layouts/default.html.coffee
@@ -71,11 +71,15 @@

     script type: "text/javascript", ->
-      _gaq = _gaq || []
-      _gaq.push(['_setAccount', 'UA-xxxxx-x'])
-      _gaq.push(['_setDomainName', 'csell.net'])
-      _gaq.push(['_trackPageview'])
-      ->
-        ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true
-        ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js'
-        s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s)
+      '''
+        var _gaq = _gaq || [];
+        _gaq.push(['_setAccount', 'UA-21705613-2']);
+        _gaq.push(['_setDomainName', 'csell.net']);
+        _gaq.push(['_trackPageview']);
+        (function() {
+          var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
+          ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
+          var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
+        })();
+      '''
+


ontext

ontext

--- src/dt-jquery.coffee
+++ src/dt-jquery.coffee
@@ -21,11 +21,26 @@
                 el._jquery.append child
             delete el._children

+        if el._jquery_delay?
+            for delay in el._jquery_delay
+                delay?()
+            delete el._jquery_delay
+
         el.on 'newListener', (type) ->
             return if el._events?[type]?.length # dont bind two times for the same type
             el._jquery.bind type, ->
                 el.emit type, this, arguments...
                 return # dont return emit result (which is either true or false)
+
+    tpl.on 'text', (el, text) ->
+        return unless text?.length
+        callback = ->
+            el._jquery?.text(text)
+        # only when tag is ready
+        if el._jquery?
+            callback()
+        else
+            (el._jquery_delay ?= []).push callback

     tpl.on 'attr', (el, key, value) ->
         el._jquery?.attr key, value


I'm afraid of commitment

I'm afraid of commitment

--- tentacle.coffee
+++ tentacle.coffee
@@ -21,9 +21,13 @@
     @tentacleConn.pipe through(@onMicrobluData)

   listenToMeshbluMessages: =>
+    return if @alreadyListening
+
     @meshbluConn.on 'ready',  @onMeshbluReady
     @meshbluConn.on 'message', @onMeshbluMessage
     @meshbluConn.on 'config', @onMeshbluConfig
+
+    @alreadyListening = true

   onMeshbluReady: =>
     console.log "I'm ready!"
@@ -68,7 +72,7 @@
     console.log "I have a connection, so I'm sending it"
     @meshbluConn.message '*', payload: msg

-  authenticateWithMeshblu: (credentials)=>
+  authenticateWithMeshblu: (credentials) =>
       try
         console.log "authenticating with credentials: #{JSON.stringify(credentials)}"
         @meshbluConn = meshblu.createConnection(


evaluate: if func undefined, check data

evaluate: if func undefined, check data

--- src/evaluator.coffee
+++ src/evaluator.coffee
@@ -14,7 +14,7 @@
   visitFunctionCall: (node) ->
     vals = (param.visit @ for param in node.parameters)
     func = funcs[node.name]
-    func vals
+    if func? then func vals else @data["#{node.name}()"]

 getOperator = (operator) ->
   switch operator


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/expirable.coffee
+++ src/expirable.coffee
@@ -2,6 +2,8 @@
   Object.reverseMerge I,
     duration: -1
     alpha: 1
+
+  startingAlph

   self.bind "update", ->
     if I.fadeOut


removed logging

removed logging

--- src/generator.coffee
+++ src/generator.coffee
@@ -165,8 +165,6 @@

     includeNamespaces = []

-    log fileNamesToUpperCamelCaseNames:fileNamesToUpperCamelCaseNames
-
     for name in subdirs.sort()
       unless upperCamelCaseNames[upperCamelCase name]
         if name.match /^[_a-z0-9]/i
@@ -202,7 +200,6 @@

     dontAdd = fileName.match /^-/

-    console.log nameSpaceName
     contents = [
       """
       #{Generator.generatedByString}


stream.pipe() instead of util.pump() (deprecated)

--- src/generator.coffee
+++ src/generator.coffee
@@ -57,7 +57,9 @@
             return callback(err) if err
             ins = fs.createReadStream src
             outs = fs.createWriteStream dest
-            util.pump ins, outs, callback
+            stream = ins.pipe outs
+            stream.on 'end', callback
+            stream.on 'error', callback

     # Public: Generates an index file containing a list of all manifests
     # ordered by category. Default category is "All Projects"


TAP5-2224: t5/core/ajax should return a RequestWrapper

--- tapestry-core/src/main/preprocessed-coffeescript/org/apache/tapestry5/t5-core-dom.coffee
+++ tapestry-core/src/main/preprocessed-coffeescript/org/apache/tapestry5/t5-core-dom.coffee
@@ -620,6 +620,25 @@

 #if jquery
   # Wrapper around the `jqXHR` object
+  class RequestWrapper
+
+    constructor: (@jqxhr) ->
+
+    # Abort a running ajax request
+    abort: -> @jqxhr.abort()
+#elseif prototype
+  # Wrapper around the Prototype `Ajax.Request` object
+  class RequestWrapper
+
+    constructor: (@req) ->
+
+    # Abort a running ajax request
+    abort: -> throw "Cannot abort Ajax request when using Prototype."
+#endif
+
+
+#if jquery
+  # Wrapper around the `jqXHR` object
   class ResponseWrapper

     constructor: (@jqxhr, data) ->
@@ -668,7 +687,7 @@
   # Returns the module's exports
   ajaxRequest = (url, options = {}) ->
 #if jquery
-    $.ajax
+    jqxhr = $.ajax
       url: url
       type: options.method?.toUpperCase() or "POST"
       contentType: options.contentType
@@ -676,6 +695,7 @@
       data: options.data
       # jQuery doesn't have the equivalent of Protoype's onException
       error: (jqXHR, textStatus, errorThrown) ->
+        return if textStatus is "abort"
         message = "Request to #{url} failed with status #{textStatus}"
         text = jqXHR.statusText
         if not _.isEmpty text
@@ -693,6 +713,7 @@

         options.success and options.success(new ResponseWrapper jqXHR, data)
         return
+    new RequestWrapper jqxhr
 #elseif prototype
     finalOptions =
       method: options.method or "post"
@@ -733,10 +754,8 @@
         options.success and options.success(new ResponseWrapper response)
         return

-    new Ajax.Request(url, finalOptions)
-#endif
-
-    return exports
+    new RequestWrapper (new Ajax.Request url, finalOptions)
+#endif

   # The main export is a function that wraps a DOM element as an ElementWrapper; additional functions are attached as
   # properties.
@@ -833,4 +852,4 @@
     # it is always safe to get the body.
     body: wrapElement document.body

-  return exports+  return exports


Fixed code duplication

Fixed code duplication

--- src/io.coffee
+++ src/io.coffee
@@ -103,19 +103,20 @@
       @_cleanup '_tmpOutname'
     bindCallback promise, next

+  _stream: (source) ->
+    {resolve, reject, promise} = When.defer()
+    res = @_write source, '-'
+    res.promise.otherwise reject
+    res.stdout.on 'readable', ->
+      resolve res.stdout
+    promise
+
   stream: (next) ->
-    {resolve, reject, promise} = When.defer()
-    if @_stdin
-      res = @_write @source, '-'
-      res.promise.otherwise reject
-      res.stdout.on 'readable', ->
-        resolve res.stdout
+    promise = if @_stdin
+      @_stream @source
     else
       When(@_fileSource()).then (filename) =>
-        res = @_write filename, '-'
-        res.promise.otherwise reject
-        res.stdout.on 'readable', ->
-          resolve res.stdout
-        res.promise.ensure =>
-          @_cleanup '_tmpFilename'
+        @_stream filename
+      .ensure =>
+        @_cleanup '_tmpFilename'
     bindCallback promise, next


Use `res.statusCode` instead of parsing internal property

--- src/webjs.coffee
+++ src/webjs.coffee
@@ -120,8 +120,7 @@
     log_request: (req, res, data) ->
         td = (new Date()) - req.start_date
         @log('info', req.method + ' ' + req.url + ' ' + td + 'ms ' +
-                (if res.finished and res._header then res._header.split('\r')[0].split(' ')[1] \
-                                  else '(unfinished)'))
+                (if res.finished then res.statusCode else '(unfinished)'))
         return data

     log: (severity, line) ->


change the way previously commited -> accept just options instead of the whole server

--- src/zappa.coffee
+++ src/zappa.coffee
@@ -69,7 +69,7 @@
   fs.readFileSync p, 'utf8'

 # Takes in a function and builds express/socket.io apps based on the rules contained in it.
-zappa.app = (func, extserv) ->
+zappa.app = (func, options={}) ->
   context = {id: uuid(), zappa, express}

   context.root = path.dirname(module.parent.filename)
@@ -79,10 +79,7 @@
   ws_handlers = {}
   helpers = {}
   postrenders = {}
-  if extserv
-    app = context.app = extserv
-  else
-    app = context.app = express.createServer()
+  app = context.app = express.createServer options
   io = context.io = socketio.listen(app)

   # Tracks if the zappa middleware is already mounted (`@use 'zappa'`).


Add support for newer versions of `less`.

--- src/zappa.coffee
+++ src/zappa.coffee
@@ -234,6 +234,7 @@
             for k, v of obj
               module.render v, filename: k, (err, css) ->
                 throw err if err
+                css = css.css if css.css? and typeof css.css is 'string' # less
                 route verb: 'get', path: k, handler: css, type: 'css'
             return
         return


my.extend

my.extend

--- src/my.coffee
+++ src/my.coffee
@@ -43,9 +43,11 @@

 exports.my = {
   test: is_local
-  online: if _ then true else false
+  online: _?
   inspect: inspect
   assert: assert
+  extend: $.extend
+  dup: (x) -> $.extend({}, x)
   page_dimensions: PAGE
   column_1_width: HALF
   column_2_width: HALF


add confirmation

add confirmation

--- src/reactGUI/ClearButton.coffee
+++ src/reactGUI/ClearButton.coffee
@@ -18,7 +18,8 @@
       'toolbar-button': true
       'fat-button': true
       'disabled': not @state.isEnabled
-    onClick = if lc.canUndo() then (=> lc.clear()) else ->
+    # onClick = if lc.canUndo() then (=> lc.clear()) else ->
+    onClick = if lc.canUndo() then (=> confirmWindow = confirm("Are you sure you want to clear your drawing?"); confirmWindow &&lc.clear()) else ->

     (div {className, onClick}, _('Clear'))


Add join/part convenience functions.

Add join/part convenience functions.

--- src/hubot/irc.coffee
+++ src/hubot/irc.coffee
@@ -15,8 +15,18 @@
     for str in strings
       @send user, "#{user.name}: #{str}"

+  join: (channel) ->
+    self = @
+    @bot.join channel, () ->
+      console.log('joined %s', channel)
+
+  part: (channel) ->
+    @bot.part channel, () ->
+      console.log('left %s', channel)
+
   run: ->
     self = @
+
     options =
       nick:     process.env.HUBOT_IRC_NICK
       port:     process.env.HUBOT_IRC_PORT
@@ -47,8 +57,7 @@
           bot.say 'NickServ', "identify #{options.nickpass}"
         else if options.nickpass and from is 'NickServ' and text.indexOf('now identified') isnt -1
           for room in options.rooms
-            bot.join room, ->
-              console.log('%s has joined %s', options.nick, room)
+            @join room

     bot.addListener 'message', (from, to, message) ->
       console.log "From #{from} to #{to}: #{message}"


Focus on the next input field

Focus on the next input field

--- app/assets/javascripts/erd/erd.js.coffee
+++ app/assets/javascripts/erd/erd.js.coffee
@@ -288,7 +288,7 @@
     ev.preventDefault()
     target = $(ev.currentTarget)

-    target.parent().siblings('table').append('
<input type="text" />	:	<input type="text" value="string" />
')
+    target.parent().siblings('table').append('
<input type="text" />	:	<input type="text" value="string" />
').find('tr:last > td > input:first').focus()

   handle_open_migration_click: (ev) =>


hide cursor on drag

hide cursor on drag

--- app/assets/javascripts/libs/mouse.coffee
+++ app/assets/javascripts/libs/mouse.coffee
@@ -57,10 +57,12 @@
 				y : evt.pageY

 		mouseDown : =>
+			$(@target).css("cursor", "none")
 			@buttonDown = true

 		mouseUp : =>
 			@buttonDown = false
+			$(@target).css("cursor", "auto")

 		attachEventHandler : (type, func) ->
 			if @target.addEventListener


Refactor queries to be easier to add more of

Signed-off-by: Ian Macalinao <08370e385f4778cad8fe504ec5445edd3d45bd9a@gmail.com>

--- src/items.coffee
+++ src/items.coffee
@@ -125,20 +125,15 @@
           return false if item[paramName] isnt param

         # It's a query!
-        if param.eq
-          return false unless item[paramName] is param.eq
+        for handlerName, handler of lol.items._queryHandlers
+          if param[handlerName]
+            # Check if the handler is of the right type
+            if handler.types
+              for type in handler.types
+                return false if typeof param[handlerName] isnt type

-        if param.gte
-          return false if typeof param.gte isnt "number" or not (item[paramName] >= param.gte)
-
-        if param.gt
-          return false if typeof param.gt isnt "number" or not (item[paramName] > param.gt)
-
-        if param.lte
-          return false if typeof param.lte isnt "number" or not (item[paramName] <= param.lte)
-
-        if param.lt
-          return false if typeof param.lt isnt "number" or not (item[paramName] < param.lt)
+            # Does not match filter unless the handler is triggered correctly
+            return false unless handler.handler item[paramName], param[handlerName]

       return true

@@ -165,6 +160,29 @@

       if asc then pa - pb else pb - pa

+lol.items._queryHandlers =
+  eq:
+    handler: (property, val) -> property is val
+
+  neq:
+    handler: (property, val) -> property isnt val
+
+  gte:
+    types: ["number"]
+    handler: (property, val) -> property >= val
+
+  gt:
+    types: ["number"]
+    handler: (property, val) -> property > val
+
+  lte:
+    types: ["number"]
+    handler: (property, val) -> property <= val
+
+  lt:
+    types: ["number"]
+    handler: (property, val) -> property < val
+
 ##
 # Defines a new item.
 #


Style.

Style.

--- client/publication.coffee
+++ client/publication.coffee
@@ -675,7 +675,7 @@

     return # Make sure CoffeeScript does not return anything

-# We do conversion of local annotation already in mousedown so that
+  # We do conversion of local annotation already in mousedown so that
   # we are before mousedown on document which deselects highlights
   'mousedown': (e, template) =>
     # TODO: Get rid of this. Annotation invites are being replaced.


Take advantage of CoffeeScript's syntax

Take advantage of CoffeeScript's syntax

- Use a default parameter value instead of explicitly
  setting the default value
- Use @prop instead of this.prop
- Use `if var in [a, b]` instead of long form
- Use `var or= other` instead of `var = var or other`
- Use the null-safe navigation operator instead of explicitly
  checking for null before navigating properties
- Use an array comprehension instead of explicitly calling
  Array.prototype.push to build an array

--- src/jsonp.coffee
+++ src/jsonp.coffee
@@ -2,9 +2,8 @@
 encode = window.encodeURIComponent
 random = Math.random

-JSONP = (options) ->
+JSONP = (options = {}) ->

-    options = if options then options else {}
     params =
         data: options.data or {}
         error: options.error or noop
@@ -39,21 +38,22 @@
             params.complete { url: script.src, event: evt }, params

         script.onload = script.onreadystatechange = ->
-            if not done and (not this.readyState or this.readyState is 'loaded' or this.readyState is 'complete')
-                done = true
+            return if done or !@readyState or @readyState in ['loaded', 'complete']
+            done = true
+            if script
                 script.onload = script.onreadystatechange = null
-                script.parentNode.removeChild script if script and script.parentNode
+                script.parentNode?.removeChild script
                 script = null
-        head = head or window.document.getElementsByTagName('head')[0] or window.document.documentElement
+        head or= window.document.getElementsByTagName('head')[0] or window.document.documentElement
         # (see jQuery bugs #2709 and #4378)
         head.insertBefore script, head.firstChild

     abort: ->
         window[callback] = -> window[callback] = null
         done = true
-        if script and script.parentNode
+        if script?.parentNode
             script.onload = script.onreadystatechange = null
-            script.parentNode.removeChild script if script and script.parentNode
+            script.parentNode.removeChild script
             script = null

 noop = -> undefined
@@ -70,10 +70,9 @@
     return str

 objectToURI = (obj) ->
-    data = []
-    data.push encode(key) + '=' + encode value for key, value of obj
+    data = [encode(key) + '=' + encode value for key, value of obj]
     return data.join '&'

-if define? and define.amd then define -> JSONP
-else if module? and module.exports then module.exports = JSONP
-else this.JSONP = JSONP
+if define?.amd then define -> JSONP
+else if module?.exports then module.exports = JSONP
+else @JSONP = JSONP


Extra newlines are extra

Extra newlines are extra

--- src/scripts/code-name-generator.coffee
+++ src/scripts/code-name-generator.coffee
@@ -19,7 +19,7 @@
     robot.http("https://gist.github.com/sroberts/6529712/raw/1e979071f6a9e8747d2c44cf5af7c4998e068d49/wordlist")
       .get() (err, res, body) ->
         wordlist = body.split(",")
-        msg.send """Codewords: \n
+        msg.send """Codewords:
         - #{msg.random wordlist} #{msg.random wordlist}
         - #{msg.random wordlist} #{msg.random wordlist}
         - #{msg.random wordlist} #{msg.random wordlist}


fixed a whoopsie

fixed a whoopsie

--- src/scripts/which-do-you-prefer.coffee
+++ src/scripts/which-do-you-prefer.coffee
@@ -20,4 +20,4 @@

     if thing[thing.length-1] == '?' then thing = thing[0..thing.length-2]

-    msg.send("I #{msg.match[1]} #{thing}.")
+    msg.send("I #{msg.match[2]} #{thing}.")


:green_heart: Fix failing containsRange test


directives work

directives work

--- src/lexer.coffee
+++ src/lexer.coffee
@@ -34,7 +34,7 @@
   #
   # Before returning the token stream, run it through the [Rewriter](rewriter.html)
   # unless explicitly asked not to.
-  tokenize: (code, opts = {}) ->
+  doTokenize: (code, opts = {}) ->
     @literate   = opts.literate  # Are we lexing literate CoffeeScript?
     @indent     = 0              # The current indentation level.
     @baseIndent = 0              # The overall minimum indentation level
@@ -78,6 +78,45 @@
     @error "missing #{tag}" if tag = @ends.pop()
     return @tokens if opts.rewrite is off
     (new Rewriter).rewrite @tokens
+
+  CompilerRepresentation: class
+
+  # A tokenize method that processes lexer and compiler directives before compiling
+  tokenize: (code, opts)->
+    compiler = @compiler ?= new @CompilerRepresentation
+    compiler.lexer = @
+    compiler.code = code
+    compiler.tokenizeOptions = opts
+    code.processDirectives(code)
+    @applyReify compiler.lexer, -> @doTokenize(code, opts)
+
+  # Run cb with 'this' set to val
+  # If val is a promise, run cb with the fulfilled value
+  applyReify: (val, cb)->
+    if val? and typeof val in [ 'object', 'function' ] and (th = val.then)? and typeof th is 'function'
+      then.call val, (res)-> cb(res)
+    else
+      cb(val)
+
+  # Run directives to be executed before lexing
+  processDirectives: (code)->
+    code = code.replace ///
+    (^|\n)
+      @@@@
+    (?:
+        \W*\r?\n((\r?\n\W+.*)+)
+      |
+        ( .* )
+    ) ///g, (m, start, multiline, singleline)->
+      compiler = new @compiler.constructor
+      if multiline?
+        compiler.eval(multiline).call @compiler
+        start + multiline.replace /.+/g, ''
+      else
+        singleline = singleline.replace /^\w/, (m)-> '@lexer.' + m
+        compiler.eval(singleline).call @compiler.lexer
+        start + "# Pre-lexer directive: #{ singleline }"
+    code

   # Preprocess the code to remove leading and trailing whitespace, carriage
   # returns, etc. If we're lexing literate CoffeeScript, strip external Markdown