Remove modal for furatto

Remove modal for furatto

--- coffee/furatto-modal.coffee
+++ coffee/furatto-modal.coffee
@@ -1,65 +0,0 @@
-(($, window) ->
-
-  class Modal
-    constructor: (el, options) ->
-      @options = $.extend {}, @defaults, options
-      @$el = $(el)
-      @body = $('body')
-      @isShown = false
-
-    toggle: ->
-      @[(if not @isShown then @.show() else @.hide())]
-
-    onDocumentKeyup: (event) =>
-      if event.keyCode is 27
-        @.hide()
-
-    onDocumentClick: (event) =>
-      if $(event.target).is('.modal-overlay') || $(event.target).parent().is('[data-toggle="close"]')
-        @.hide()
-
-    show: ->
-      @isShown = true
-
-      setTimeout (=>
-        @body.addClass('modal-active')
-      ), 100
-
-      @body.append "
#{@$el.html()}
"
-
-      @body.bind 'keyup', @.onDocumentKeyup
-      @body.bind 'click', @.onDocumentClick
-
-    hide: ->
-      @isShown = false
-      @body.unbind 'keyup', @.onDocumentKeyup
-      @body.unbind 'click', @.onDocumentClick
-
-      @body.removeClass 'modal-active'
-      $('.modal-overlay').remove()
-
-      setTimeout (->
-        $('.modal-popin').remove()
-      ), 500
-
-  $.fn.extend modal: (option, args...) ->
-    @each ->
-      $this = $(this)
-      data = $this.data('modal')
-
-      if !data
-        $this.data 'modal', (data = new Modal(this, option))
-      if typeof option == 'string'
-        data[option].apply(data, args)
-
-      $('body').addClass('modal-ready')
-      $('body').append('

')
-
-  $(document).on 'click', '[data-furatto="modal"]', (e) ->
-    $this = $(this)
-    $target = $($this.data('target'))
-    $target.modal('toggle')
-
-    e.preventDefault()
-
-) window.jQuery, window


Fixes a merger conflict

Fixes a merger conflict

--- collections/articles.coffee
+++ collections/articles.coffee
@@ -80,13 +80,3 @@
       Articles.remove(id)
       Meteor.call("updateUserEventLastModified", removed.userEventId)
       Meteor.call("updateUserEventArticleCount", removed.userEventId, -1)
-<<<<<<< HEAD
-=======
-
-  curateArticle: (id, reviewed) ->
-    if Roles.userIsInRole(Meteor.userId(), ['curator', 'admin'])
-      Articles.update({_id: id}, {
-        $set:
-          reviewed: reviewed
-      })
->>>>>>> refs/remotes/origin/master


cook() should return a boolean value.

cook() should return a boolean value.

--- rime/recipe.coffee
+++ rime/recipe.coffee
@@ -9,6 +9,8 @@
     recipe.props.setup.call recipe
   catch e
     console.error 'error cooking recipe: ' + e
+    return false
+  return true

 exports.Recipe = class Recipe


snap: fix bug where my previous refactor made it actually not take any snapshots at all.  ugh.

--- salvus/snap.coffee
+++ salvus/snap.coffee
@@ -25,6 +25,7 @@

 secret_key_length             = 128
 registration_interval_seconds = 15
+snapshot_interval_seconds     = 10

 net       = require 'net'
 winston   = require 'winston'
@@ -804,6 +805,7 @@
                     timeout : 30  # should be very fast no matter what.
                     bash    : true
                     env     : {BUP_DIR : bup_active}
+                    err_on_exit : false  # if a grep along the way above is empty -- i.e., no file changes, then get a 1 exit code.  STUPID, but yep.
                     cb      : (err, output) ->
                         if err
                             winston.debug("SERIOUS BUG issue: error determining number of modified files for #{project_id}: #{err}")
@@ -1353,7 +1355,7 @@
 # Write entry to the database periodicially (with ttl) that this
 # snap server is up and running, and provide the key.
 size_of_bup_archive = undefined
-register_with_database = (cb) ->
+register_with_database = () ->
     database?.update
         table : 'snap_servers'
         where : {id : server_id, dummy:true}
@@ -1365,7 +1367,6 @@
             else
                 winston.info("successfully registered with database")

-setInterval(register_with_database, 1000*registration_interval_seconds)

 # For each commit in each project, re-enter a record in the database.

@@ -1453,8 +1454,6 @@
         (cb) ->
             connect_to_database(cb)
         (cb) ->
-            register_with_database(cb)
-        (cb) ->
             if program.resend_all_commits
                 resend_all_commits(cb)
             else
@@ -1463,7 +1462,9 @@
             monitor_snapshot_queue()
             cb()
         (cb) ->
-            setInterval(snapshot_active_projects, 10000)
+            setInterval(register_with_database, 1000*registration_interval_seconds)
+            setInterval(snapshot_active_projects, 1000*snapshot_interval_seconds)
+            cb()
         #(cb) ->
         #    ensure_all_projects_have_a_snapshot(cb)
         #(cb) ->


pjcbot is a diva. Cleaned up pjc.coffee.

--- scripts/pjc.coffee
+++ scripts/pjc.coffee
@@ -4,12 +4,16 @@
   robot.respond /lulz/i, (res) ->
     res.send res.random lulz

+  robot.hear /^hubot:? (.+)/i, (res) ->
+    response = "Sorry, I'm a diva and only respond to #{robot.name}"
+    response += " or #{robot.alias}" if robot.alias
+    res.reply response
+    return
+
   robot.listenerMiddleware (context, next, done) ->
-    # console.log context.response.message
     if ~context.response.message.text.indexOf('pjcbot help')
       context.response.send context.response.random lulz
       context.response.send "I'm sorry, @#{context.response.message.user.name}, I can't let you do that."
       done()
     else
-      # This is not a restricted command; allow everyone
       next()


Use environment debug setting

Use environment debug setting

--- source/File.coffee
+++ source/File.coffee
@@ -62,7 +62,7 @@
 restler = require 'restler'
 vm      = require 'vm'

-DEBUG = true # process.env.DEBUG
+DEBUG = process.env.DEBUG

 vmRequire = (path, sandbox, res) ->
     fs.readFile require.resolve(path), (err, code) ->


Fixing existential chaining issue

Fixing existential chaining issue

--- source/coffee/generator.coffee
+++ source/coffee/generator.coffee
@@ -168,8 +168,8 @@

     last_accessor = @accessors[@accessors.length-1]
     for accessor in @accessors
+      existence_qualifiers.push accessor.js_existence rv, undefined_unary, last_accessor.invert
       rv += accessor.js()
-      existence_qualifiers.push accessor.js_existence rv, undefined_unary, last_accessor.invert
       undefined_unary = no # only possible for the first accessor

     existence_check = (eq for eq in existence_qualifiers when eq isnt "").join(' && ')


Documentation

Documentation

--- src/bounded.coffee
+++ src/bounded.coffee
@@ -13,7 +13,7 @@
      more: "properties"

    player.position()
-=> Uncaught TypeError: Object # <Object> has no method 'position'
+=> Uncaught TypeError: Object has no method 'position'

    player.include(Bounded)


move title drawing into the ajax callback

--- source/javascripts/grid.coffee
+++ source/javascripts/grid.coffee
@@ -18,9 +18,12 @@
   path = d3.geo.path().projection(projection)

   d3.json 'data/pdx.json', (pdx) ->
-    neighborhoods = topojson.feature pdx, pdx.objects.neighborhoods
-    blockgroups   = topojson.feature pdx, pdx.objects.pdx
-    water         = topojson.feature pdx, pdx.objects.water
+    items.append('h3')
+      .attr('class', 'title')
+      .text((d) -> d.key)
+
+    blockgroups = topojson.feature pdx, pdx.objects.pdx
+    water       = topojson.feature pdx, pdx.objects.water
     parks       = topojson.feature pdx, pdx.objects.parks

     projection.scale(1).translate([0, 0])


Exit with an error code 1 if could not write a compiled JavaScript file or a source map

Currently CoffeeScript reports a success even if it failed to write a
compiled file to disk. This behavior confuses automated tools such as
test runners which may return false positives if a test failed to
compile because of a file lock.

--- src/command.coffee
+++ src/command.coffee
@@ -65,7 +65,7 @@
 # `--` will be passed verbatim to your script as arguments in `process.argv`
 exports.run = ->
   parseOptions()
-  # Make the REPL *CLI* use the global context so as to (a) be consistent with the
+  # Make the REPL *CLI* use the global context so as to (a) be consistent with the
   # `node` REPL CLI and, therefore, (b) make packages that modify native prototypes
   # (such as 'colors' and 'sugar') work as expected.
   replCliOpts = useGlobal: yes
@@ -346,12 +346,14 @@
       fs.writeFile jsPath, js, (err) ->
         if err
           printLine err.message
+          process.exit 1
         else if opts.compile and opts.watch
           timeLog "compiled #{sourcePath}"
     if generatedSourceMap
       fs.writeFile sourceMapPath, generatedSourceMap, (err) ->
         if err
           printLine "Could not write source map: #{err.message}"
+          process.exit 1
   fs.exists jsDir, (itExists) ->
     if itExists then compile() else mkdirp jsDir, compile


Fix verifyRightmostScreenPosition

Fix verifyRightmostScreenPosition

...as it caused false negatives when undefined was in the buffer text

--- spec/display-layer-spec.coffee
+++ spec/display-layer-spec.coffee
@@ -703,8 +703,8 @@
   longestScreenRows = new Set
   for screenLine, row in screenLines
     screenLineLength = screenLine.length
-    screenLineLength -= 1 if screenLine.indexOf(displayLayer.invisibles.cr) isnt -1
-    screenLineLength -= 1 if screenLine.indexOf(displayLayer.invisibles.eol) isnt -1
+    screenLineLength -= 1 if displayLayer.invisibles.cr? and screenLine.indexOf(displayLayer.invisibles.cr) isnt -1
+    screenLineLength -= 1 if displayLayer.invisibles.eol? and screenLine.indexOf(displayLayer.invisibles.eol) isnt -1

     expect(displayLayer.lineLengthForScreenRow(row)).toBe(screenLineLength)


Fix pylama spec

Fix pylama spec

--- spec/linter-pylama-spec.coffee
+++ spec/linter-pylama-spec.coffee
@@ -47,7 +47,7 @@
         lint(editor).then (results) ->
           messages = results
       runs ->
-        expect(messages.length).toEqual 8
+        expect(messages.length).toEqual 7

     it 'finds the right things to complain about', ->
       messages = null
@@ -57,12 +57,11 @@
       runs ->
         err0 = "D100 Missing docstring in public module [pep257]"
         err1 = "E0100 SyntaxError: invalid syntax [pylama]"
-        err2 = "E701 multiple statements on one line (colon) [pep8]"
-        err3 = "E203 whitespace before ':' [pep8]"
-        err4 = "E231 missing whitespace after ':' [pep8]"
-        err5 = "E225 missing whitespace around operator [pep8]"
-        err6 = "E302 expected 2 blank lines, found 1 [pep8]"
-        err7 = "D101 Missing docstring in public class [pep257]"
+        err2 = "E203 whitespace before ':' [pep8]"
+        err3 = "E231 missing whitespace after ':' [pep8]"
+        err4 = "E225 missing whitespace around operator [pep8]"
+        err5 = "E302 expected 2 blank lines, found 1 [pep8]"
+        err6 = "D101 Missing docstring in public class [pep257]"

         expect(messages[0].text).toBe(err0)
         expect(messages[0].range).toEqual([[0,0], [0,1]])
@@ -75,32 +74,27 @@
         expect(messages[1].filePath).toMatch(badPath)

         expect(messages[2].text).toBe(err2)
-        expect(messages[2].range).toEqual([[2,2], [2,3]])
+        expect(messages[2].range).toEqual([[2,1], [2,4]])
         expect(messages[2].type).toBe('Error')
         expect(messages[2].filePath).toMatch(badPath)

         expect(messages[3].text).toBe(err3)
-        expect(messages[3].range).toEqual([[2,1], [2,4]])
+        expect(messages[3].range).toEqual([[2,2], [2,3]])
         expect(messages[3].type).toBe('Error')
         expect(messages[3].filePath).toMatch(badPath)

         expect(messages[4].text).toBe(err4)
-        expect(messages[4].range).toEqual([[2,2], [2,3]])
+        expect(messages[4].range).toEqual([[2,3], [2,3]])
         expect(messages[4].type).toBe('Error')
         expect(messages[4].filePath).toMatch(badPath)

         expect(messages[5].text).toBe(err5)
-        expect(messages[5].range).toEqual([[2,3], [2,3]])
+        expect(messages[5].range).toEqual([[4,0], [4,5]])
         expect(messages[5].type).toBe('Error')
         expect(messages[5].filePath).toMatch(badPath)

         expect(messages[6].text).toBe(err6)
         expect(messages[6].range).toEqual([[4,0], [4,5]])
-        expect(messages[6].type).toBe('Error')
+        expect(messages[6].type).toBe('Warning')
         expect(messages[6].filePath).toMatch(badPath)

-        expect(messages[7].text).toBe(err7)
-        expect(messages[7].range).toEqual([[4,0], [4,5]])
-        expect(messages[7].type).toBe('Warning')
-        expect(messages[7].filePath).toMatch(badPath)
-


refactor: Clearly distinguish what's API Elements element instance

--- src/compile.coffee
+++ src/compile.coffee
@@ -62,8 +62,8 @@
   # This gets deleted once we're fully on minim
   refractTransitions = children(refract, {element: 'transition'})

-  apiElements.findRecursive('transition').forEach((transition, transitionNo) ->
-    transitionNo = transitionNo.toValue()
+  apiElements.findRecursive('transition').forEach((transitionElement, transitionNoElement) ->
+    transitionNo = transitionNoElement.toValue()

     # This gets deleted once we're fully on minim
     refractTransition = refractTransitions[transitionNo]
@@ -79,33 +79,33 @@
       # of Dredd. There's a plan to migrate to so-called "transaction paths"
       # in the future (apiaryio/dredd#227), which won't use the concept
       # of transaction examples anymore.
-      exampleNumbersPerTransaction = detectExampleNumbersPerTransaction(transition)
+      exampleNumbersPerTransaction = detectExampleNumbersPerTransaction(transitionElement)
       hasMoreExamples = Math.max(exampleNumbersPerTransaction...) > 1

       # Dredd supports only testing of the first request-response pair within
       # each transaction example. We iterate over available transactions and
       # skip those, which are not first within a particular example.
       exampleNo = 0
-      transition.transactions.forEach((httpTransaction, httpTransactionNo) ->
-        httpTransactionNo = httpTransactionNo.toValue()
+      transitionElement.transactions.forEach((httpTransactionElement, httpTransactionNoElement) ->
+        httpTransactionNo = httpTransactionNoElement.toValue()
         httpTransactionExampleNo = exampleNumbersPerTransaction[httpTransactionNo]

-        transactionInfo =
+        relevantTransaction =
           refract: refractHttpTransactions[httpTransactionNo]
-          apiElements: httpTransaction
+          apiElements: httpTransactionElement
           exampleNo: if hasMoreExamples then httpTransactionExampleNo else null

         if httpTransactionExampleNo isnt exampleNo
-          relevantTransactions.push(transactionInfo)
+          relevantTransactions.push(relevantTransaction)

         exampleNo = httpTransactionExampleNo
       )
     else
       # All other formats then API Blueprint
-      transition.transactions.forEach((httpTransaction, httpTransactionNo) ->
+      transitionElement.transactions.forEach((httpTransactionElement, httpTransactionNoElement) ->
         relevantTransactions.push(
-          refract: refractHttpTransactions[httpTransactionNo.toValue()]
-          apiElements: httpTransaction
+          refract: refractHttpTransactions[httpTransactionNoElement.toValue()]
+          apiElements: httpTransactionElement
         )
       )
   )
@@ -117,8 +117,8 @@
 #
 # Returns an array of numbers, where indexes correspond to HTTP transactions
 # within the transition and values represent the example numbers.
-detectExampleNumbersPerTransaction = (transition) ->
-  tempRefractTransition = apiElementsToRefract(transition)
+detectExampleNumbersPerTransaction = (transitionElement) ->
+  tempRefractTransition = apiElementsToRefract(transitionElement)
   tempRefractHttpTransactions = children(tempRefractTransition, {element: 'httpTransaction'})

   detectTransactionExamples(tempRefractTransition)


Test Exceptions

Test Exceptions

--- spec/notifications-spec.coffee
+++ spec/notifications-spec.coffee
@@ -56,3 +56,23 @@

         advanceClock(NotificationElement::animationDuration)
         expect(notificationContainer.childNodes.length).toBe 0
+
+    describe "when an exception is thrown", ->
+      beforeEach ->
+        try
+          a + 1
+        catch e
+          window.onerror.call(window, e.toString(), 'abc', 2, 3, e)
+
+      it "displays a fatal error", ->
+        notificationContainer = workspaceElement.querySelector('atom-notifications')
+        fatalError = notificationContainer.querySelector('atom-notification.fatal')
+        expect(notificationContainer.childNodes.length).toBe 1
+        expect(fatalError).toBeDefined()
+        expect(fatalError).toHaveClass 'has-close'
+        expect(fatalError.innerHTML).toContain 'ReferenceError: a is not defined'
+
+        issueBody = fatalError.getIssueBody()
+        expect(issueBody).toMatch /Atom Version: [0-9].[0-9]+.[0-9]+/ig
+        expect(issueBody).not.toMatch /Unknown/ig
+        expect(issueBody).toContain 'ReferenceError: a is not defined'


[GH-16] allow use of . for cwd at the start of a relative string

--- src/decking.coffee
+++ src/decking.coffee
@@ -547,6 +547,11 @@
     when "mount"
       for v in val
         [host, remote] = v.split ":"
+        matches = host.match /^\.(.*)$/
+        if matches
+          path = require "path"
+          host = path.join process.cwd(), matches[1]
+
         host = process.cwd() if host is "."
         arg = [].concat arg, ["-v #{host}:#{remote}"]


Revert "Fix Win32 compilation error"

Revert "Fix Win32 compilation error"

This reverts commit 58f301ee3829d9711cd5a20880adc273804cec61.

--- src/helpers.coffee
+++ src/helpers.coffee
@@ -83,8 +83,6 @@

     # Modify order.
     Object.keys(order).forEach (orderKey) ->
-      if typeof lang.order[orderKey] == 'string'
-        lang.order[orderKey] = [ lang.order[orderKey] ]
       lang.order[orderKey] = lang.order[orderKey].map(replaceSlashes)

     # Modify join configuration.


reddit.music.player.il.ly support

reddit.music.player.il.ly support

--- src/background/mappings.coffee
+++ src/background/mappings.coffee
@@ -64,3 +64,4 @@
   'themusicninja': 'TheMusicNinjaController.js'
   'tunein': 'TuneinController.js'
   'one.npr': 'NPROneController.js'
+  'reddit.music.player.il.ly': 'RedditplayerController.js'


Añadido operadores - y / a la gramatica

Añadido operadores - y / a la gramatica

--- public/scripts/coffee/main.coffee
+++ public/scripts/coffee/main.coffee
@@ -1,34 +1,56 @@
-main = ()->
+main = undefined
+parse = undefined
+main = ->
+  result = undefined
+  source = undefined
   source = INPUT.value
-
-  try
+  try
     result = JSON.stringify(parse(source), null, 2)
-  catch result
-    result = """
#{result}
"""
-
+  catch _error
+    result = _error
+    result = "
" + result + "
"
   OUTPUT.innerHTML = result

-window.onload = ()->
+window.onload = ->
   PARSE.onclick = main

 Object.constructor::error = (message, t) ->
   t = t or this
   t.name = "SyntaxError"
   t.message = message
-  throw treturn
+  throw treturnreturn

 RegExp::bexec = (str) ->
+  i = undefined
+  m = undefined
   i = @lastIndex
   m = @exec(str)
   return m  if m and m.index is i
   null

 String::tokens = ->
-  from = undefined # The index of the start of the token.
-  i = 0 # The index of the current character.
-  n = undefined # The number value.
-  m = undefined # Matching
-  result = [] # An array to hold the results.
+  ID = undefined
+  MULTIPLELINECOMMENT = undefined
+  NUM = undefined
+  ONECHAROPERATORS = undefined
+  ONELINECOMMENT = undefined
+  RESERVED_WORD = undefined
+  STRING = undefined
+  WHITES = undefined
+  from = undefined
+  getTok = undefined
+  i = undefined
+  m = undefined
+  make = undefined
+  n = undefined
+  result = undefined
+  rw = undefined
+  tokens = undefined
+  from = undefined
+  i = 0
+  n = undefined
+  m = undefined
+  result = []
   WHITES = /\s+/g
   ID = /[a-zA-Z_]\w*/g
   NUM = /\b\d+(\.\d*)?([eE][+-]?\d+)?\b/g
@@ -46,8 +68,6 @@
     ONECHAROPERATORS
   ]
   RESERVED_WORD = p: "P"
-
-  # Make a token object.
   make = (type, value) ->
     type: type
     value: value
@@ -55,77 +75,75 @@
     to: i

   getTok = ->
+    str = undefined
     str = m[0]
-    i += str.length # Warning! side effect on i
+    i += str.length
     str

-
-  # Begin tokenization. If the source string is empty, return nothing.
   return  unless this
-
-  # Loop through this text
   while i < @length
-    tokens.forEach (t) -> # Only ECMAScript5
+    tokens.forEach (t) ->
       t.lastIndex = i
       return

     from = i
-
-    # Ignore whitespace and comments
-    if m = WHITES.bexec(this) or
-           (m = ONELINECOMMENT.bexec(this)) or
-           (m = MULTIPLELINECOMMENT.bexec(this))
+    if m = WHITES.bexec(this) or (m = ONELINECOMMENT.bexec(this)) or (m = MULTIPLELINECOMMENT.bexec(this))
       getTok()
-
-    # name.
     else if m = ID.bexec(this)
       rw = RESERVED_WORD[m[0]]
       if rw
         result.push make(rw, getTok())
       else
         result.push make("ID", getTok())
-
-    # number.
     else if m = NUM.bexec(this)
       n = +getTok()
       if isFinite(n)
         result.push make("NUM", n)
       else
         make("NUM", m[0]).error "Bad number"
-
-    # string
     else if m = STRING.bexec(this)
-      result.push make("STRING",
-                        getTok().replace(/^["']|["']$/g, ""))
-
-    # single-character operator
+      result.push make("STRING", getTok().replace(/^["']|["']$/g, ""))
     else if m = ONECHAROPERATORS.bexec(this)
       result.push make(m[0], getTok())
     else
-      throw "Syntax error near '#{@substr(i)}'"
+      throw "Syntax error near '" + (@substr(i)) + "'"
   result

 parse = (input) ->
+  expression = undefined
+  factor = undefined
+  lookahead = undefined
+  match = undefined
+  statement = undefined
+  statements = undefined
+  term = undefined
+  tokens = undefined
+  tree = undefined
   tokens = input.tokens()
   lookahead = tokens.shift()
   match = (t) ->
     if lookahead.type is t
       lookahead = tokens.shift()
       lookahead = null  if typeof lookahead is "undefined"
-    else # Error. Throw exception
-      throw "Syntax Error. Expected #{t} found '" +
-            lookahead.value + "' near '" +
-            input.substr(lookahead.from) + "'"
+    else
+      throw ("Syntax Error. Expected " + t + " found '") + lookahead.value + "' near '" + input.substr(lookahead.from) + "'"
     return

   statements = ->
+    result = undefined
     result = [statement()]
     while lookahead and lookahead.type is ";"
       match ";"
       result.push statement()
-    (if result.length is 1 then result[0] else result)
+    if result.length is 1
+      result[0]
+    else
+      result

   statement = ->
+    left = undefined
+    result = undefined
+    right = undefined
     result = null
     if lookahead and lookahead.type is "ID"
       left =
@@ -145,13 +163,13 @@
       result =
         type: "P"
         value: right
-    else # Error!
-      throw "Syntax Error. Expected identifier but found " +
-        (if lookahead then lookahead.value else "end of input") +
-        " near '#{input.substr(lookahead.from)}'"
+    else
+      throw "Syntax Error. Expected identifier but found " + ((if lookahead then lookahead.value else "end of input")) + (" near '" + (input.substr(lookahead.from)) + "'")
     result

   expression = ->
+    result = undefined
+    right = undefined
     result = term()
     if lookahead and lookahead.type is "+"
       match "+"
@@ -160,9 +178,18 @@
         type: "+"
         left: result
         right: right
+    else if lookahead and lookahead.type is "-"
+      match "-"
+      right = expression()
+      result =
+        type: "-"
+        left: result
+        right: right
     result

   term = ->
+    result = undefined
+    right = undefined
     result = factor()
     if lookahead and lookahead.type is "*"
       match "*"
@@ -171,9 +198,17 @@
         type: "*"
         left: result
         right: right
+    else if lookahead and lookahead.type is "/"
+      match "/"
+      right = term()
+      result =
+        type: "/"
+        left: result
+        right: right
     result

   factor = ->
+    result = undefined
     result = null
     if lookahead.type is "NUM"
       result =
@@ -191,15 +226,10 @@
       match "("
       result = expression()
       match ")"
-    else # Throw exception
-      throw "Syntax Error. Expected number or identifier or '(' but found " +
-        (if lookahead then lookahead.value else "end of input") +
-        " near '" + input.substr(lookahead.from) + "'"
+    else
+      throw "Syntax Error. Expected number or identifier or '(' but found " + ((if lookahead then lookahead.value else "end of input")) + " near '" + input.substr(lookahead.from) + "'"
     result

   tree = statements(input)
-  if lookahead?
-    throw "Syntax Error parsing statements. " +
-      "Expected 'end of input' and found '" +
-      input.substr(lookahead.from) + "'"
-  tree
+  throw "Syntax Error parsing statements. " + "Expected 'end of input' and found '" + input.substr(lookahead.from) + "'"  if lookahead?
+  tree


adds in scrolling to the top when going back and forth in tasks

--- src/components/task/index.cjsx
+++ src/components/task/index.cjsx
@@ -38,6 +38,8 @@

   contextTypes:
     router: React.PropTypes.func
+
+  scrollingTargetDOM: -> window.document

   setStepKey: ->
     {stepIndex} = @context.router.getCurrentParams()
@@ -184,6 +186,7 @@
       @context.router.replaceWith('viewTaskStep', params)
       true
     else
+      @scrollToTop()
       @context.router.transitionTo('viewTaskStep', params)
       true


add some comments

add some comments

--- src/marktab.coffee
+++ src/marktab.coffee
@@ -30,6 +30,7 @@
 	variableNamePattern = /[\w0-9\-]+/

 	constructor: (@customVars = {}, @lines = [], @stringNames = stringNameDefaults, @lineBreak = 80) ->
+		# @customVars contains variables defined by the user (like: "myRiff: [1:2 2:2]")
 		# @lines contains an array of parsed lines, ready to be output
 		this

@@ -216,7 +217,7 @@
 	# example: parseRiff("[1:1 2 h 3 r 7 p 5]") => { 1:[1, 2, 'h', 3, 'r', 7, 'p', 5] }
 	parseRiff: (riff) ->
 		riffLine = riff.substr(1, riff.length-2)# remove brackets
-		m = new Marktab(@customVars)
+		m = new Marktab(@customVars)# recurse, keeping any user-defined variables
 		m.parse(riffLine)

 	# sets marktab variables


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/metered.coffee
+++ src/metered.coffee
@@ -101,7 +101,7 @@

   player = GameObject

-  player.include(Bounded)
+  player.include(Me)

   player.position()
   # => {x: 50, y: 40}


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/metered.coffee
+++ src/metered.coffee
@@ -12,7 +12,7 @@
 enemy = GameObject
   health: 500

-enemy.incl
+enemy.include Metered

 player.meter 'health'
 # =>


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/metered.coffee
+++ src/metered.coffee
@@ -173,7 +173,7 @@
   player.meter 'health'
     show: false

-  # enables visibility for the 'health' meter
+  # enables visibility for the meter named 'health'
   player.showMeter 'health'
   </pre></code>


oops, camelCase

oops, camelCase

--- src/methods.coffee
+++ src/methods.coffee
@@ -441,7 +441,7 @@
       # XXX: missing checkedExceptions, annotations, parameterAnnotations, annotationDefault
       clazz: rs.init_class_object c2t @class_name
       name: rs.init_string @name, true
-      parameter_types: rs.set_obj "[Ljava/lang/Class;", (rs.init_class_object f.type for f in @param_types)
+      parameterTypes: rs.set_obj "[Ljava/lang/Class;", (rs.init_class_object f.type for f in @param_types)
       returnType: rs.init_class_object @return_type
       modifiers: @access_byte
       slot: parseInt((i for i,v of rs.class_lookup(@class_name).methods when v is @)[0])


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/movable.coffee
+++ src/movable.coffee
@@ -43,7 +43,7 @@
   I.velocity = Point(I.velocity.x, I.velocity.y)

   self.bind 'update', ->
-    I.velocity = I.velocity.add(I.acceleration)
+    I.velocity.add$(I.acceleration)

     if I.maxSpeed?
       currentSpeed = I.velocity.magnitude()


rm console.log

rm console.log

--- src/observe.coffee
+++ src/observe.coffee
@@ -13,9 +13,7 @@
   this._set._pending = promise

   promise.then (val) =>
-    console.log 'PROMISE RESOLVED', val
     if this._set._pending is promise
-      console.log 'SETTING VALUE'
       this._set val

   for key in Object.keys promise


debugging commit

debugging commit

--- scripts/bungie-data-helper.coffee
+++ scripts/bungie-data-helper.coffee
@@ -13,7 +13,7 @@
     itemDefs = response.definitions.items[hash]

     # some weapons return an empty hash for definitions.damageTypes
-    if Object.keys(response.definitions.damageTypes).length is not 0
+    if Object.keys(response.definitions.damageTypes).length isnt 0
       damageTypeName = response.definitions.damageTypes[item.damageTypeHash].damageTypeName
     else
       damageTypeName = 'Kinetic'


remove unnecessary logs

remove unnecessary logs

--- servers/lib/server/helpers.coffee
+++ servers/lib/server/helpers.coffee
@@ -96,9 +96,7 @@
       if not account or err
         callback("couldnt find account #{kodingKey.owner}", null)
         return
-      console.log 'account ====================='
-      console.log account
-      console.log '======== account'
+
       req.account = account
       callback(err, account)


fix arc bounding box going wrong is some edge cases

--- src/plotter.coffee
+++ src/plotter.coffee
@@ -8,6 +8,11 @@
 Macro = require './macro-tool'
 # standard tool functions
 tool = require './standard-tool'
+
+# constants
+HALF_PI = Math.PI/2
+THREEHALF_PI = 3*HALF_PI
+TWO_PI = 2*Math.PI

 # parse a aperture definition command and return the object
 parseAD = (block) ->
@@ -85,19 +90,19 @@
   # get the quadrant we're in
   theta = Math.atan2 end.y-start.y, end.x - start.x
   # quadrant I
-  if 0 <= theta < Math.PI/2
+  if 0 <= theta < HALF_PI
     "M#{sxm} #{sym}L#{sxp} #{sym}L#{exp} #{eym}L#{exp} #{eyp}L#{exm} #{eyp}
      L#{sxm} #{syp}Z"
   # quadrant II
-  else if Math.PI/2 <= theta < Math.PI
+  else if HALF_PI <= theta < Math.PI
     "M#{sxm} #{sym}L#{sxp} #{sym}L#{sxp} #{syp}L#{exp} #{eyp}L#{exm} #{eyp}
      L#{exm} #{eym}Z"
   # quadrant III
-  else if -Math.PI <= theta < -Math.PI/2
+  else if -Math.PI <= theta < -HALF_PI
     "M#{sxp} #{sym}L#{sxp} #{syp}L#{sxm} #{syp}L#{exm} #{eyp}L#{exm} #{eym}
      L#{exp} #{eym}Z"
   # quadrant IV
-  else if -Math.PI/2 <= theta < 0
+  else if -HALF_PI <= theta < 0
     "M#{sxm} #{sym}L#{exm} #{eym}L#{exp} #{eym}L#{exp} #{eyp}L#{sxp} #{syp}
      L#{sxm} #{syp}Z"

@@ -406,40 +411,45 @@
           # check the points to make sure we have a valid arc
           for c in cen
             thetaE = Math.atan2 end.y-c.y, end.x-c.x
-            if thetaE < 0 then thetaE += 2*Math.PI
+            if thetaE < 0 then thetaE += TWO_PI
             thetaS = Math.atan2 start.y-c.y, start.x-c.x
-            if thetaS < 0 then thetaS += 2*Math.PI
+            if thetaS < 0 then thetaS += TWO_PI
             # adjust angles so math comes out right
-            if @mode is 'cw' and thetaS < thetaE then thetaS+=2*Math.PI
-            else if @mode is 'ccw' and thetaE < thetaS then thetaE+=2*Math.PI
+            if @mode is 'cw' and thetaS < thetaE then thetaS+=TWO_PI
+            else if @mode is 'ccw' and thetaE < thetaS then thetaE+=TWO_PI
             # take it if it's less than 90
             theta = Math.abs(thetaE - thetaS)
-            if @quad is 's' and Math.abs(thetaE - thetaS) > Math.PI/2
+            if @quad is 's' and Math.abs(thetaE - thetaS) > HALF_PI
               continue
             else
              if @quad is 'm' and theta >= Math.PI then large = 1
              cen = { x: c.x, y: c.y }
              break
-          # bounding box calculations
           rTool = if @trace.region then 0 else @tools[@currentTool].bbox().xMax
-          # minimum x is either at 180 degress or an endpoint
-          if thetaS <= Math.PI <= thetaE or thetaS >= Math.PI >= thetaE
+          # switch calculations to CCW to make things easier
+          if @mode is 'cw' then [thetaE, thetaS] = [thetaS, thetaE]
+          # maxima targets
+          xp = if thetaS > 0 then TWO_PI else 0
+          yp = HALF_PI + (if thetaS > HALF_PI then TWO_PI else 0)
+          xn = Math.PI + (if thetaS > Math.PI then TWO_PI else 0)
+          yn = THREEHALF_PI + (if thetaS > THREEHALF_PI then TWO_PI else 0)
+          # minimum x is either at the negative x axis or an endpoint
+          if thetaS <= xn <= thetaE
             xMin = cen.x - r - rTool
           else
             xMin = (Math.min start.x, end.x) - rTool
-          # max x is going to be at 2*pi or 0
-          if thetaS <= 2*Math.PI <= thetaE or thetaS >= 2*Math.PI >= thetaE or
-          thetaS <= 0 <= thetaE or thetaS >= 0 >= thetaE
+          # max x is going to be at positive x or endpoint
+          if thetaS <= xp <= thetaE
             xMax = cen.x + r + rTool
           else
             xMax = (Math.max start.x, end.x) + rTool
-          # minimum y is either at 270 degress or an endpoint
-          if thetaS <= 3*Math.PI/2 <= thetaE or thetaS >= 3*Math.PI/2 >= thetaE
+          # minimum y is either at negative y axis or an endpoint
+          if thetaS <= yn <=thetaE
             yMin = cen.y - r - rTool
           else
             yMin = (Math.min start.y, end.y) - rTool
-          # max y is going to be at pi/2
-          if thetaS <= Math.PI/2 <= thetaE or thetaS >= Math.PI/2 >= thetaE
+          # max y is going to be at positive y or endpoint
+          if thetaS <= yp <= thetaE
             yMax = cen.y + r + rTool
           else
             yMax = (Math.max start.y, end.y) + rTool


remove log messages

remove log messages

--- shared/ironTableController.coffee
+++ shared/ironTableController.coffee
@@ -212,6 +212,7 @@

   waitOn: ->
+    #console.log('waitOn')
     @subscribe()

@@ -388,7 +389,6 @@
         else if type is 'update' and col.onUpdate?
           rec[dataKey] = col.onUpdate()
       catch error
-        console.log("checkFields error", error)
         @errorMessage = ':' + error.reason or error
         return false
     true


use Array.prototype.some where corey wanted to use _.detect

--- src/require.coffee
+++ src/require.coffee
@@ -59,16 +59,11 @@
     throw "require: ../ prefix not yet implemented"

   if file[0] isnt '/'
-    # I want to use _.detect, but we don't have that at this point
-    # or break would do, but coffeescript doesn't have that
-    expandedPath = null
-    paths.forEach (path) ->
+    paths.some (path) ->
       if /\.(.+)$/.test(file) and __exists "#{path}/#{file}"
-        expandedPath ?= "#{path}/#{file}"
-      else
-        expandedPath ?= expandPath("#{path}/#{file}")
-
-      file = expandedPath if expandedPath?
+        file = "#{path}/#{file}"
+      else if expanded = expandPath "#{path}/#{file}"
+        file = expanded
   else
     file = expandPath(file) or file


add .coffee support to require()

add .coffee support to require()

--- src/require.coffee
+++ src/require.coffee
@@ -26,10 +26,12 @@
     exports

 exts =
-  js:  (file) ->
-    code = __read file
+  js: (file, code) ->
+    code or= __read file
     __jsc__.evalJSString_withScriptPath code, file
     defines.pop()?.call()
+  coffee: (file) ->
+    exts.js file, CoffeeScript.compile __read file

 resolve = (file) ->
   if /!/.test file


Remove old addHandler/removeHandler and use EventEmitter::on/EventEmitter::off

--- src/observable/property.coffee
+++ src/observable/property.coffee
@@ -118,9 +118,9 @@
   updateSourcesFromTracker: ->
     newSources = @constructor.popSourceTracker()
     handler = @sourceChangeHandler()
-    source?.event('change').removeHandler(handler) for source in @sources if @sources
+    source?.off('change', handler) for source in @sources if @sources
     @sources = newSources
-    source?.event('change').addHandler(handler) for source in @sources if @sources
+    source?.on('change', handler) for source in @sources if @sources

   getValue: ->
     @registerAsMutableSource()
@@ -206,7 +206,7 @@

   _removeHandlers: ->
     handler = @sourceChangeHandler()
-    source.event('change').removeHandler(handler) for source in @sources if @sources
+    source.off('change', handler) for source in @sources if @sources
     delete @sources
     @changeEvent().clearHandlers()


repository: implement getTeams

repository: implement getTeams

--- src/octonode/repository.coffee
+++ src/octonode/repository.coffee
@@ -41,5 +41,10 @@
   getContributors: (cb) ->
     @_invokeGet "/repos/#{@name}/contributors",200,"getContributors",cb

+  # Get the teams for a repository
+  # '/repos/pkumar/hub/teams' GEt
+  getTeams: (cb) ->
+    @_invokeGet "/repos/#{@name}/teams",200,"getTeams",cb
+
 # Export module
 module.exports = Repository


tattle on safety check failures

tattle on safety check failures

--- src/uapanfw.coffee
+++ src/uapanfw.coffee
@@ -90,11 +90,13 @@
     notifySubscribers msg
     fs.writeFileSync fwdata_file, JSON.stringify(fwdata), 'utf-8'

+
 notifySubscribers = (msg, current_un = false) ->
   console.error 'bad robotRef' unless robotRef
   for un in fwdata.notify
     robotRef.send { room: un }, msg unless current_un && un == current_un

+
 addListEntry = (robot, msg) ->
   fullcmd = String(msg.match.shift())
   who = msg.envelope.user.name
@@ -118,6 +120,7 @@
     if entry.val.match /^(?:137\.229\.|199\.165\.)/
       usermsg = "Blocking CIDRs begining with 137.229. or 199.165. not allowed. "+
         " If this is time critical ask a human for help; otherwise open a ticket."
+      notifySubscribers "Request by #{who} failed safety check: #{fullcmd}\nReason: #{usermsg}"
       return msg.reply usermsg

   else if extra = l_val.match /^([a-zA-Z][a-zA-Z0-9\.]+)$/
@@ -128,6 +131,7 @@
     if entry.val.toLowerCase().match /(?:alaska|uaf)\.edu$/
       usermsg = "Blocking alaska.edu or uaf.edu not allowed. "+
         " If this is time critical ask a human for help; otherwise open a ticket."
+      notifySubscribers "Request by #{who} failed safety check: #{fullcmd}\nReason: #{usermsg}"
       return msg.reply usermsg

   else
@@ -143,6 +147,7 @@
     if entry.val.toLowerCase().match /[^\/]+(?:alaska|uaf)\.edu/
       usermsg = "Blocking alaska.edu or uaf.edu not allowed. "+
         " If this is time critical ask a human for help; otherwise open a ticket."
+      notifySubscribers "Request by #{who} failed safety check: #{fullcmd}\nReason: #{usermsg}"
       return msg.reply usermsg

   #console.log "#{l_val}: #{entry.type} => #{entry.val}"


FIX: Repl had a bad dependency

FIX: Repl had a bad dependency

--- source/Caffeine.Eight/Repl.coffee
+++ source/Caffeine.Eight/Repl.coffee
@@ -1,9 +1,9 @@
 {defineModule, formattedInspect, isClass, log} = require 'art-standard-lib'
-require 'color'

 defineModule module, class Repl
   @caffeineEightRepl: (parser) ->
     parser = new parser if isClass parser
+    try require 'colors'
     require('repl').start
       prompt: "#{parser.getClassName()}> ".grey
       eval: (command, context, filename, callback) ->


Friday changes

Friday changes

--- source/javascripts/some.js.coffee
+++ source/javascripts/some.js.coffee
@@ -34,34 +34,44 @@
 rect.noStroke()
 rect.custom = 0

+timeout = (index, transform) ->
+	setTimeout(transform, index * 1000)
+	console.log(index)
+	true
+
+foreach = (transform) ->
+	for i in [0...circles.length]
+		timeout(i, transform(circles[i]))
+	true
+
 keys = {
 	37: {
 		pressed: false
 		execute: () ->
-			for circle in circles
-				circle.rotation -= 0.05 * Math.PI
-			true
+			foreach((c) ->
+				c.rotation -= 0.1 * Math.PI
+			)
 	}
 	39: {
 		pressed: false
 		execute: () ->
-			for circle in circles
-				circle.rotation += 0.05 * Math.PI
-			true
+			foreach((c) ->
+				c.rotation += 0.1 * Math.PI
+			)
 	}
 	38: {
 		pressed: false
 		execute: () ->
-			for circle in circles
-				circle.acceleration += 0.5
-			true
+			foreach((c) ->
+				c.acceleration += 0.5
+			)
 	}
 	40: {
 		pressed: false
 		execute: () ->
-			for circle in circles
-				circle.acceleration -= 0.5
-			true
+			foreach((c) ->
+				c.acceleration -= 0.5
+			)
 	}
 	82: {
 		pressed: false


Turn off sorting if you move a task manually

--- source/scripts/utils/mouse.coffee
+++ source/scripts/utils/mouse.coffee
@@ -1,5 +1,6 @@
 Mouse = require 'mouse'
 List = require '../models/list'
+Pref = require '../models/pref'
 DestroyTask = require '../views/modal/destroy_task'

@@ -52,6 +53,8 @@
   for item in items by -1
     collection.move item.task, position

+  Pref.sort = false
+
 lists.on 'sort', (item, position) ->
   List.move item[0].list, position


Fix help text

Fix help text

--- src/scripts/singularity.coffee
+++ src/scripts/singularity.coffee
@@ -11,8 +11,8 @@
 #   hubot singularity show aliases
 #   hubot singularity add alias <alias> <url>
 #   hubot singularity clear alias <alias>
-#   hubot singularity show requests in <alias>
-#   hubot singularity bounce <request> in <alias>
+#   hubot singularity show requests on <alias>
+#   hubot singularity bounce <request> on <alias>
 #
 # Author:
 #   Arca Artem - <aartem@opentable.com>
@@ -66,8 +66,8 @@
     msg.send "hubot singularity show aliases"
     msg.send "hubot singularity add alias <alias> <url>"
     msg.send "hubot singularity clear alias <alias>"
-    msg.send "hubot singularity show requests in <alias>"
-    msg.send "hubot singularity bounce <request> in <alias>"
+    msg.send "hubot singularity show requests on <alias>"
+    msg.send "hubot singularity bounce <request> on <alias>"

   bounceRequest = (msg, alias, request) ->
     if not _singularityAliases[alias]?


Removed unused file

Removed unused file


Fixed specs

Fixed specs

--- spec/editor/navigator_spec.coffee
+++ spec/editor/navigator_spec.coffee
@@ -40,9 +40,9 @@

   it 'refreshes specified page', ->
     expect(navigator.dom.children[2].className).toBe('class_c')
-    doc = pageAt[2].className = 'changed'
+    doc.pageAt(2).className = 'changed'
     expect(navigator.dom.children[2].className).toBe('class_c')
-    navigator.refresh(2)
+    navigator.refreshPage(2)
     expect(navigator.dom.children[2].className).toBe('changed')

   it 'moves a page into specified position', ->
@@ -55,7 +55,7 @@
     expect(navigator.dom.children[2].className).toBe('class_c')
     expect(navigator.dom.children[3].className).toBe('class_d')

-    navigator.move(2,0)
+    navigator.movePage(2,0)
     expect(doc.pageAt(0).id).toBe('id_c')
     expect(doc.pageAt(1).id).toBe('id_a')
     expect(doc.pageAt(2).id).toBe('id_b')


statistics: changed error rate to 10% and tau for faster results

--- src/services/statistics.coffee
+++ src/services/statistics.coffee
@@ -1,8 +1,8 @@
 _ = require 'lodash'
 log = require 'loglevel'

-ERROR_RATE = 0.05
-TAU = 0.5 # ???????? - free variable in significance
+ERROR_RATE = 0.10
+TAU = 0.01 # ???????? - free variable in significance
 FDR_THRESHOLD = 0.10
 Z_MAX = 6 # Maxium +/- z value


top-down-react rewrite refactoring fallout -- reference to alert_message that is no longer imported

--- src/smc-webapp/init_app.coffee
+++ src/smc-webapp/init_app.coffee
@@ -1,9 +1,12 @@
 {Actions, Store, redux} = require('./smc-react')
-{salvus_client} = require('./salvus_client')
-misc = require('smc-util/misc')
-
-{set_url} = require('./history')
-{set_window_title} = require('./browser')
+{salvus_client}         = require('./salvus_client')
+misc                    = require('smc-util/misc')
+
+{set_url}               = require('./history')
+{set_window_title}      = require('./browser')
+
+{alert_message}         = require('./alerts')
+
 ###
 # Page Redux
 ###


tasks: selecting in a task should NOT switch to edit mode.

--- src/smc-webapp/tasks/desc.cjsx
+++ src/smc-webapp/tasks/desc.cjsx
@@ -39,7 +39,16 @@
                @props.selected_hashtags != next.selected_hashtags

     edit: ->
+        cur_sel = window?.getSelection()?.toString()
+        if cur_sel and @_sel != cur_sel
+            # clicking to select something, so do NOT switch to edit.
+            # Do it via comparing selection, in case of clicking into a partial selection.
+            return
         @props.actions.edit_desc(@props.task_id)
+
+    mouse_down: ->
+        @_sel = window?.getSelection()?.toString()
+        console.log 'mouse down', @_sel

     stop_editing: ->
         @props.actions.stop_editing_desc(@props.task_id)
@@ -63,7 +72,10 @@
     render_desc: ->
         if @props.editing
             return
-        <div onClick={@edit}>
+        <div
+            onClick     = {@edit}
+            onMouseDown = {@mouse_down}
+        >
             <DescriptionRendered
                 actions           = {@props.actions}
                 task_id           = {@props.task_id}


Removed unused template helper

Removed unused template helper

--- core/app/backbone/views/users/user_statistics_view.coffee
+++ core/app/backbone/views/users/user_statistics_view.coffee
@@ -2,8 +2,5 @@
   className: 'statistics'
   template: 'users/statistics'

-  templateHelpers: =>
-    topic: @model.user_topics().first()?.toJSON()
-
   initialize: ->
     @listenTo @model, 'change', @render


Update socks.coffee

Update socks.coffee

@el_outer should have closing </div>, not </page>

--- src/Socks.net/tools/socks.coffee
+++ src/Socks.net/tools/socks.coffee
@@ -21,7 +21,7 @@
   constructor: ->
     @first_elm = true
     @el = $(empty_page).clone()
-    @el_outer = $("
</page>")
+    @el_outer = $("

")
     @el_outer.append @el
     $("body").append @el_outer
     @load_settings()


Fix coffeelint

Fix coffeelint

--- src/key-binding.coffee
+++ src/key-binding.coffee
@@ -2,29 +2,33 @@

 module.exports =
 class KeyBinding
-  @currentIndex: 1
+  KeyBinding.currentIndex = 1

   enabled: true

-  constructor: (@source, @command, @keystrokes, selector, @priority) ->
-    @keystrokeArray = @keystrokes.split(' ')
-    @keystrokeCount = @keystrokeArray.length
-    @selector = selector.replace(/!important/g, '')
-    @specificity = calculateSpecificity(selector)
-    @index = @constructor.currentIndex++
+  constructor: (source, command, keystrokes, selector, priority) ->
+    this.source = source
+    this.command = command
+    this.keystrokes = keystrokes
+    this.priority = priority
+    this.keystrokeArray = this.keystrokes.split(' ')
+    this.keystrokeCount = this.keystrokeArray.length
+    this.selector = selector.replace(/!important/g, '')
+    this.specificity = calculateSpecificity(selector)
+    this.index = this.constructor.currentIndex++

   matches: (keystroke) ->
     multiKeystroke = /\s/.test keystroke
     if multiKeystroke
-      keystroke == @keystroke
+      keystroke is this.keystroke
     else
-      keystroke.split(' ')[0] == @keystroke.split(' ')[0]
+      keystroke.split(' ')[0] is this.keystroke.split(' ')[0]

   compare: (keyBinding) ->
-    if keyBinding.priority is @priority
-      if keyBinding.specificity is @specificity
-        keyBinding.index - @index
+    if keyBinding.priority is this.priority
+      if keyBinding.specificity is this.specificity
+        keyBinding.index - this.index
       else
-        keyBinding.specificity - @specificity
+        keyBinding.specificity - this.specificity
     else
-      keyBinding.priority - @priority
+      keyBinding.priority - this.priority


Remove deprecated options from docs

Remove deprecated options from docs

--- src/lib/Helpers.coffee
+++ src/lib/Helpers.coffee
@@ -14,35 +14,18 @@
 # proc = (combinedInputData, inputGroups, outputPorts, asyncCallback) ->
 # ```
 #
-# With `config.group = true` it checks incoming group IPs and collates
-# data with matching group IPs. By default this kind of grouping is `false`.
-# Set `config.group` to a RegExp object to correlate inputs only if the
-# group matches the expression (e.g. `^req_`). For non-matching groups
-# the component will act normally.
-#
-# With `config.field = 'fieldName' it collates incoming data by specified
-# field. The component's proc function is passed a combined object with
-# port names used as keys. This kind of grouping is disabled by default.
-#
 # With `config.forwardGroups = true` it would forward group IPs from
 # inputs to the output sending them along with the data. This option also
 # accepts string or array values, if you want to forward groups from specific
 # port(s) only. By default group forwarding is `false`.
 #
-# `config.receiveStreams = [portNames]` feature makes the component expect
-# substreams on specific inports instead of separate IPs (brackets and data).
-# It makes select inports emit `Substream` objects on `data` event
-# and silences `beginGroup` and `endGroup` events.
-#
-# `config.sendStreams = [portNames]` feature makes the component emit entire
-# substreams of packets atomically to the outport. Atomically means that a
 # substream cannot be interrupted by other packets, which is important when
 # doing asynchronous processing. In fact, `sendStreams` is enabled by default
 # on all outports when `config.async` is `true`.
 #
-# WirePattern supports both sync and async `proc` handlers. In latter case
-# pass `config.async = true` and make sure that `proc` accepts callback as
-# 4th parameter and calls it when async operation completes or fails.
+# WirePattern supports async `proc` handlers. Set `config.async = true` and
+# make sure that `proc` accepts callback as 4th parameter and calls it when
+# async operation completes or fails.
 exports.WirePattern = (component, config, proc) ->
   # In ports
   inPorts = if 'in' of config then config.in else 'in'


More docco in pattern.coffee.

More docco in pattern.coffee.

--- src/lib/pattern.coffee
+++ src/lib/pattern.coffee
@@ -191,9 +191,9 @@
     f.exploded  = f.signed or f.bytes > 8 or "ha".indexOf(f.type) != -1

-    # Check for bit backing. The intense rest pattern allows us to skip over a
-    # nested padding specifier in the bit packing pattern, nested curly brace
-    # matching for a depth of one.
+    # Check for bit backing. The intense rest pattern in the regex allows us to
+    # skip over a nested padding specifier in the bit packing pattern, nested
+    # curly brace matching for a depth of one.
     pack = /^{((?:-b|b|x).+)}(\s*,.*|\s*)$/.exec(rest)
     if pack
       index++
@@ -224,6 +224,8 @@
       rest        = pack[2]
       index      += pack[1].length + 1

+      # Check that the packed bits sum up to the size of the field into which
+      # they are packed.
       sum = 0
       for pack in f.packing
         sum += pack.bits
@@ -240,20 +242,33 @@
       read          = alternation[1]
       rest          = alternation[2]
       write         = null
+
+      # See if there is a full write pattern. If not, then the pattern will be
+      # the same for reads and writes, but with possible different conditions
+      # for write to match an alternate.
       if alternation = /^(\s*\/\s*)\(([^)]+)\)(.*)$/.exec(rest)
         slash         = alternation[1]
         write         = alternation[2]
         rest          = alternation[3]
+
+      # Parse the primary alternation pattern.
       index += 1
       alternates pattern, f.alternation = [], read, "read", "write", not write, index
       index += read.length + 1
+
+      # Parse the full write alternation pattern, if we have one.
       if write
         index += slash.length + 1
         alternates pattern, f.alternation, write, "write", "read", false, index
         index += write.length
+
+      # This condition will catch all, and let us know that no condition
+      # matched.
       f.alternation.push {
         read: always(), write: always(), failed: true
       }
+
+    # Neither bit packing nor alternation.
     else
       # Check if this is a length encoding.
       length = /^\/(.*)$/.exec(rest)


#510 - Display import error message to user

--- dkobo/koboform/static/js/xlform/XLF.view.surveyApp.coffee
+++ dkobo/koboform/static/js/xlform/XLF.view.surveyApp.coffee
@@ -137,7 +137,7 @@

   onEscapeKeydown: -> #noop. to be overridden
   previewButtonClick: (evt)->
-    if evt.shiftKey and evt.altKey
+    if evt.shiftKey #and evt.altKey
       evt.preventDefault()
       viewUtils.debugFrame @survey.toCSV()
       @onEscapeKeydown = viewUtils.debugFrame.close
@@ -145,10 +145,15 @@
       XLF.enketoIframe.fromCsv @survey.toCSV(),
         previewServer: "http://kfdev.kobotoolbox.org"
         onSuccess: => @onEscapeKeydown = XLF.enketoIframe.close
-        onError: (errArgs...)->
-          console?.error.apply(console, errArgs)
+        onError: (errArgs...)=>
+          @alert errArgs
     ``

+  alert: (message) ->
+      $('.alert-modal').text(message).dialog('option', {
+          title: 'Error'
+          width: 500
+      }).dialog 'open'
   downloadButtonClick: (evt)->
     # Download = save a CSV file to the disk
     surveyCsv = @survey.toCSV()


fix ReferenceError: dot is undefined

fix ReferenceError: dot is undefined

this fixes the about tab

--- src/coffee/content/profile.coffee
+++ src/coffee/content/profile.coffee
@@ -216,6 +216,7 @@
         # and $elem if he does

         $root = $ '#' + oid + '-about-page'
+        dot = module.doOnce.timestamp()

         # own profile page uses slightly different IDs
         if $root.length is 0
@@ -298,7 +299,6 @@

     module.checkProfile = ->
         $tabs = $ '#contentPane div[role="tabpanel"]'
-        dot = module.doOnce.timestamp()

         if $tabs.length is 0
             # not a profile!


lint line that is too long

lint line that is too long

--- src/components/exercise/mode.cjsx
+++ src/components/exercise/mode.cjsx
@@ -80,7 +80,8 @@

     answerKeySet = null unless choicesEnabled

-    questionProps = _.pick(@props, 'processHtmlAndMath', 'choicesEnabled', 'correct_answer_id', 'feedback_html', 'type', 'questionNumber')
+    questionProperties = ['processHtmlAndMath', 'choicesEnabled', 'correct_answer_id', 'feedback_html', 'type', 'questionNumber']
+    questionProps = _.pick(@props, questionProperties)
     if mode is 'multiple-choice'
       changeProps =
         onChange: @onAnswerChanged


mess with some force layout constants

mess with some force layout constants

--- VisualGraph.coffee
+++ VisualGraph.coffee
@@ -70,9 +70,10 @@

   createForceLayout: ->
     d3.layout.force()
-      .charge(-120)
-      .linkDistance(50)
+      .charge(-300)
+      .linkDistance(100)
       .size([800, 400])
+      .friction(0.4)

   createInitialState: ->
     nodeCount = @initParams.nodesLength
@@ -94,7 +95,7 @@
       .links(@data.links)
       .start()

-    for x in [0...10]
+    for x in [0...20]
       force.resume()
       while force.alpha() > 0
         force.tick()


deliveries

deliveries

--- src/coffee/sphere_service.coffee
+++ src/coffee/sphere_service.coffee
@@ -298,6 +298,31 @@

     @_post "/orders/#{order.id}", json

+  addDelivery: (order, deliveryItems) ->
+    action =
+      action: 'addDelivery'
+      items: deliveryItems
+
+    json =
+      version: order.version
+      actions: [action]
+
+    @_post "/orders/#{order.id}", json
+
+  addParcel: (order, deliveryId, measurements, trackingData) ->
+    action =
+      action: 'addParcelToDelivery'
+      deliveryId: deliveryId
+
+    action.measurements = measurements if measurements?
+    action.trackingData = trackingData if trackingData?
+
+    json =
+      version: order.version
+      actions: [action]
+
+    @_post "/orders/#{order.id}", json
+
   _refCacheKey: (ref) ->
     ref.typeId + "-" + ref.id


Disable touch icons always on

Disable touch icons always on

--- js-library/app/js/jail_iframe/classes/icon_buttons.coffee
+++ js-library/app/js/jail_iframe/classes/icon_buttons.coffee
@@ -152,7 +152,7 @@

     FactlinkJailRoot.on 'factlink.factsLoaded factlinkAdded', @_destroyUnlessValid

-    if FactlinkJailRoot.isTouchDevice()
+    if FactlinkJailRoot.isTouchDevice() && false # quick disable until it is more acceptable to publishers
       @_iconButton.fadeIn()
     else
       @_attentionSpan = new FactlinkJailRoot.AttentionSpan


Make out an arrayport

Make out an arrayport

--- src/components/SplitArray.coffee
+++ src/components/SplitArray.coffee
@@ -5,7 +5,7 @@
         @inPorts =
             in: new noflo.Port()
         @outPorts =
-            out: new noflo.Port()
+            out: new noflo.ArrayPort()

         @inPorts.in.on "begingroup", (group) =>
             @outPorts.out.beginGroup group


docs: update scrolling to not push url state

--- src/components/docs/index.coffee
+++ src/components/docs/index.coffee
@@ -52,8 +52,8 @@

 isNodeVisible = ($el) ->
   bounds = $el.getBoundingClientRect()
-  # 10 px buffer, prevents off-by-one scrolling to next anchor
-  bounds.top >= -10 and bounds.bottom <= window.innerHeight
+  # 20 px buffer, prevents off-by-one scrolling to next anchor
+  bounds.top >= -20 and bounds.bottom <= window.innerHeight

 module.exports = class Docs
   constructor: ({@scrollToSubject}) ->
@@ -121,9 +121,8 @@
             if key
               path += '/' + key
           @scrollToSubject.onNext {section, key}
-          z.router.go path, {
-            shouldSkipScroll: true
-          }
+          # FIXME: should be handled by zorium router?
+          window.history?.replaceState? null, null, path
           break
     window.addEventListener 'scroll', @scrollListener


Use "." as separator in properties names

event.project.name = "project.name"

--- plugins/xmatters/index.coffee
+++ plugins/xmatters/index.coffee
@@ -10,7 +10,7 @@
           continue	# nested objects like stackTrace array are not supported
         flatten(dstObj, srcObj[property], property)
       else if prefix
-        dstObj[ prefix + "_" + property] = srcObj[property]
+        dstObj[ prefix + "." + property] = srcObj[property]

     dstObj


bug with input textfield no errasing message

--- app/assets/javascripts/landing.js.coffee
+++ app/assets/javascripts/landing.js.coffee
@@ -17,6 +17,7 @@
       success: (response)->
         alertify.success "Your sadness is now global :("
         $("#loading").hide()
+        $("input#sad-message").val("")

       error: (xhr,ajaxOptions, error)->
         alertify.error "We have problems. Please Try Again! "
@@ -40,5 +41,5 @@
     if e.which == 13
       e.preventDefault()
       getIpAndSendFeeling(true)
-
+
   loadData()


dumb turbolinks

dumb turbolinks

--- app/assets/javascripts/refills.js.coffee
+++ app/assets/javascripts/refills.js.coffee
@@ -1,7 +1,6 @@
-$(document).ready ->
+$(document).on 'page:load', ->
   $('.js-accordion-trigger').bind 'click', (e) ->
     jQuery(this).parent().find('.submenu').slideToggle 'fast'
     jQuery(this).parent().toggleClass 'is-expanded'
     e.preventDefault()
     return
-


Fucking docs man

Fucking docs man

--- app/frontend/scripts/behaviors.js.coffee
+++ app/frontend/scripts/behaviors.js.coffee
@@ -5,6 +5,7 @@

 $(document).ready () ->

+  # Refreshes the Queue.
   $('.queue').click () ->
     play.renderQueue()
     false


HTML-escape username when storing in Meteor Errors

--- packages/rocketchat-lib/server/methods/setUsername.coffee
+++ packages/rocketchat-lib/server/methods/setUsername.coffee
@@ -20,15 +20,15 @@
 			nameValidation = new RegExp '^[0-9a-zA-Z-_.]+$'

 		if not nameValidation.test username
-			throw new Meteor.Error 'username-invalid', "#{username} is not a valid username, use only letters, numbers, dots, hyphens and underscores"
+			throw new Meteor.Error 'username-invalid', "#{_.escape(username)} is not a valid username, use only letters, numbers, dots, hyphens and underscores"

 		if user.username != undefined
 			if not username.toLowerCase() == user.username.toLowerCase()
 				if not  RocketChat.checkUsernameAvailability username
-					throw new Meteor.Error 'error-field-unavailable', "" + username + " is already in use :(", { method: 'setUsername', field: username }
+					throw new Meteor.Error 'error-field-unavailable', "" + _.escape(username) + " is already in use :(", { method: 'setUsername', field: username }
 		else
 			if not  RocketChat.checkUsernameAvailability username
-				throw new Meteor.Error 'error-field-unavailable', "" + username + " is already in use :(", { method: 'setUsername', field: username }
+				throw new Meteor.Error 'error-field-unavailable', "" + _.escape(username) + " is already in use :(", { method: 'setUsername', field: username }

 		unless RocketChat.setUsername user._id, username
 			throw new Meteor.Error 'error-could-not-change-username', "Could not change username", { method: 'setUsername' }


Prevent duplicate workflow api requests

Prevent duplicate workflow api requests

--- app/pages/project/classify.cjsx
+++ app/pages/project/classify.cjsx
@@ -78,33 +78,30 @@
     query: 'loadAppropriateClassification'

   componentDidMount: () ->
-    @getCurrentWorkflowID().then (id) =>
-      @getWorkflow @props.project, id
-        .then (workflow) =>
-          @setState
-            workflow: workflow
+    @getCurrentWorkflow().then (workflow) =>
+      @setState {workflow}

   loadAppropriateClassification: (_, props = @props) ->
     # To load the right classification, we'll need to know which workflow the user expects.
     # console.log 'Loading appropriate classification'
-    @promiseToSetState classification: @getCurrentWorkflowID(props).then (workflowID) =>
-      # console.log 'Loading classification for workflow', workflowID
+    @promiseToSetState classification: @getCurrentWorkflow(props).then (workflow) =>
+      # console.log 'Loading classification for workflow', workflow.id
       # Create a classification if it doesn't exist for the chosen workflow, then resolve our state with it.
-      currentClassifications.forWorkflow[workflowID] ?= @createNewClassification props.project, workflowID
-      currentClassifications.forWorkflow[workflowID]
-
-  getCurrentWorkflowID: (props = @props) ->
-    getWorkflowID = if props.location.query?.workflow?
+      currentClassifications.forWorkflow[workflow.id] ?= @createNewClassification props.project, workflow
+      currentClassifications.forWorkflow[workflow.id]
+
+  getCurrentWorkflow: (props = @props) ->
+    if props.location.query?.workflow?
       # console.log 'Workflow specified as', props.query.workflow
       # Prefer the workflow specified in the query.
-      Promise.resolve props.location.query.workflow
+      @getWorkflow props.project, props.location.query.workflow
     else
       # If no workflow is specified, pick a random one and record it for later.
       # When we send this classification, we'll clear this value to select a new random workflow.
-      currentWorkflowForProject[props.project.id] ?= @getRandomWorkflowID props.project
+      currentWorkflowForProject[props.project.id] ?= @getRandomWorkflow props.project
       currentWorkflowForProject[props.project.id]

-  getRandomWorkflowID: (project) ->
+  getRandomWorkflow: (project) ->
     project.get('workflows', active: true).then (workflows) =>
       if workflows.length is 0
         throw new Error "No workflows for project #{project.id}"
@@ -114,24 +111,22 @@
         @setState {projectIsComplete}
         randomIndex = Math.floor Math.random() * workflows.length
         # console.log 'Chose random workflow', workflows[randomIndex].id
-        workflows[randomIndex].id
-
-  createNewClassification: (project, workflowID) ->
-    workflow = @getWorkflow project, workflowID
-    subject = workflow.then (workflow) =>
-      @setState {workflow}
-      # A subject set is only specified if the workflow is grouped.
-      getSubjectSet = if workflow.grouped
-        workflow.get('subject_sets').then (subjectSets) =>
-          randomIndex = Math.floor Math.random() * subjectSets.length
-          subjectSets[randomIndex]
-      else
-        Promise.resolve()
-
-      getSubjectSet.then (subjectSet) =>
-        @getNextSubject project, workflow, subjectSet
-
-    Promise.all([workflow, subject]).then ([workflow, subject]) =>
+        workflows[randomIndex]
+
+  createNewClassification: (project, workflow) ->
+    @setState {workflow}
+    # A subject set is only specified if the workflow is grouped.
+    getSubjectSet = if workflow.grouped
+      workflow.get('subject_sets').then (subjectSets) =>
+        randomIndex = Math.floor Math.random() * subjectSets.length
+        subjectSets[randomIndex]
+    else
+      Promise.resolve()
+
+    loadSubject = getSubjectSet.then (subjectSet) =>
+      @getNextSubject project, workflow, subjectSet
+
+    loadSubject.then (subject) =>
       # console.log 'Creating a new classification'
       classification = apiClient.type('classifications').create
         annotations: []
@@ -157,8 +152,7 @@
   getWorkflow: (project, workflowID) ->
     # console.log 'Getting workflow', workflowID
     # We could just get the workflow directly, but this way we ensure the workflow belongs to the project.
-    project.get('workflows').then (workflows) ->
-      workflow = (workflow for workflow in workflows when workflow.id is workflowID)[0]
+    project.get('workflows', id: workflowID).then ([workflow]) ->
       unless workflow?
         throw new Error "No workflow #{workflowID} for project #{project.id}"
       workflow
@@ -316,9 +310,9 @@
         </SignInPrompt>

   loadAnotherSubject: ->
-    @getCurrentWorkflowID(@props).then (workflowID) =>
+    @getCurrentWorkflow(@props).then (workflow) =>
       # Forget the old classification so a new one will load.
-      currentClassifications.forWorkflow[workflowID] = null
+      currentClassifications.forWorkflow[workflow.id] = null
       # Forget the old workflow, unless it was specified, so we'll get a random one next time.
       unless @props.location.query?.workflow?
         currentWorkflowForProject[@props.project.id] = null


Clean up a few map()s.

Clean up a few map()s.

--- bokehjs/src/coffee/common/ticking.coffee
+++ bokehjs/src/coffee/common/ticking.coffee
@@ -335,7 +335,7 @@
       start_factor = Math.floor(data_low / interval)
       end_factor   = Math.ceil(data_high / interval)
       factors = arange(start_factor, end_factor + 1)
-      ticks = factors.map((f) -> return f * interval)
+      ticks = (factor * interval for factor in factors)
       return ticks

     toString: () ->
@@ -362,8 +362,8 @@
       super()

       # FIXME Validate that the scales don't overlap.
-      @min_intervals = @scales.map((s) -> s.get_min_interval())
-      @max_intervals = @scales.map((s) -> s.get_max_interval())
+      @min_intervals = _.invoke(@scales, 'get_min_interval')
+      @max_intervals = _.invoke(@scales, 'get_max_interval')

     get_min_interval: () ->
       return @min_intervals[0]
@@ -524,8 +524,7 @@
           month_date.setUTCMonth(month)
           return month_date)

-      # FIXME Use a list comprehension?
-      month_dates = _.flatten(year_dates.map((date) -> months_of_year(date)))
+      month_dates = _.flatten(months_of_year(date) for date in year_dates)

       all_ticks = _.invoke(month_dates, 'getTime')
       ticks_in_range = _.filter(all_ticks,
@@ -546,7 +545,6 @@
     get_ticks_for_range: (data_low, data_high) ->
       month_dates = date_range_by_month(data_low, data_high)

-      # FIXME Is there a better way to deal with this?
       days = @days
       typical_interval = @typical_interval
       days_of_month = (month_date) ->
@@ -561,12 +559,10 @@
           # make sure we have a bit of room before we include a day.
           future_date = new Date(day_date.getTime() + (typical_interval / 2))
           if future_date.getUTCMonth() == month_date.getUTCMonth()
-#             console.log("  push #{month_date} / #{month_date.toUTCString()} ~ (#{month_date.getUTCDate()} -> #{day}) = #{day_date}")
             dates.push(day_date)
         return dates

-      # FIXME Use a list comprehension?
-      day_dates = _.flatten(month_dates.map((date) -> days_of_month(date)))
+      day_dates = _.flatten(days_of_month(date) for date in month_dates)

       all_ticks = _.invoke(day_dates, 'getTime')
       # FIXME Since the ticks are sorted, this could be done more efficiently.
@@ -574,11 +570,6 @@
                                 ((tick) -> data_low <= tick <= data_high))

       return ticks_in_range
-
-  class SimpleScale extends CompositeScale
-    constructor: (intervals) ->
-      super(intervals.map((interval) ->
-        return new SingleIntervalScale(interval)))

   HUNDRED_MILLIS = 100.0
   ONE_SECOND = 1000.0
@@ -825,6 +816,7 @@
       # Our resolution boundaries are not round numbers, because we want them
       # to fall between the possible tick intervals (which are round numbers,
       # as we've worked hard to ensure).
+      # FIXME Maybe make the adjustments explicit?
       r = resolution
       span = interval
       if r < 6e-4


Turns out bisect_right() already implemented in _.

(Actually _.sortedIndex() is bisect_left(), but it'll do.)

--- bokehjs/src/coffee/common/ticking.coffee
+++ bokehjs/src/coffee/common/ticking.coffee
@@ -277,13 +277,6 @@
     ret = _.min(indices(arr), ((i) -> return arr[i]))
     return ret

-  # FIXME Optimize this.
-  bisect_right = (xs, x) ->
-    for i in [0..xs.length]
-      if xs[i] > x
-        return i
-    return xs.length
-
   clamp = (x, min_val, max_val) ->
     return Math.max(min_val, Math.min(max_val, x))

@@ -348,8 +341,8 @@
       data_range = data_high - data_low
       ideal_interval = @get_ideal_interval(data_low, data_high)
       scale_ndxs = [
-        bisect_right(@min_intervals, ideal_interval) - 1,
-        bisect_right(@max_intervals, ideal_interval)
+        _.sortedIndex(@min_intervals, ideal_interval) - 1,
+        _.sortedIndex(@max_intervals, ideal_interval)
       ]
       intervals = [@min_intervals[scale_ndxs[0]],
                    @max_intervals[scale_ndxs[1]]]
@@ -401,7 +394,7 @@
       ideal_mantissa = ideal_interval / ideal_magnitude

       # An untested optimization.
-#       index = bisect_right(@allowed_mantissas, ideal_mantissa)
+#       index = _.sortedIndex(@allowed_mantissas, ideal_mantissa)
 #       candidate_mantissas = @allowed_mantissas[index..index + 1]
       candidate_mantissas = @allowed_mantissas


minor change in the doc

minor change in the doc

--- library/exposure/pinpoint.coffee
+++ library/exposure/pinpoint.coffee
@@ -83,7 +83,7 @@
         this.emit "mutation-summary", selector, callback
         return creator.call this, callback or _.noop

-    # Pinpoint when the specified selector vanishes (is reparented or
+    # Pinpoint when the specified selector vanishes (is parented or
     # moved) and then invoke the supplied rendering function, which
     # will receive the newly pinpointed node as its first argument.
     # If multiple nodes with this selector vanished, then renderer


Unroll and unoptimize impls

Unroll and unoptimize impls

--- library/membrane/validate.coffee
+++ library/membrane/validate.coffee
@@ -108,7 +108,7 @@
         assert _.isFunction(continuation), notContinuation
         assert _.isObject(response), notResponse
         assert _.isObject(request), notRequest
-        vcontexts = request.vcontexts or {}
+        vcontexts = request.params.vcontexts or {}
         transformed =  _.map _.values(vcontexts), transformer
         transformed = _.object _.keys(vcontexts), transformed
         async.parallel transformed, (error, results) =>
@@ -128,7 +128,72 @@
         context = @constructor.validationContext?()
         context = Primitive unless _.isObject context
         assert request; value = request.params?[name]
-        vcontexts = (request.vcontexts ?= {})
+        vcontexts = request.params.vcontexts ?= {}
+        return obtain if obtain = vcontexts[name]
+        created = new context value, message
+        vcontexts[name] = created; created
+
+# This is an ABC service intended to be used only as a compund. It
+# provides a complete validation solution for request headers. The
+# important difference is this validation system supports asynchronous
+# validators which is what differs it from existent solutions. This
+# validation system is a one-stop-shop for checking all the inputs!
+module.exports.HValidator = class HValidator extends Validator
+
+    # This is a marker that indicates to some internal subsystems
+    # that this class has to be considered abstract and therefore
+    # can not be treated as a complete class implementation. This
+    # mainly is used to exclude or account for abstract classes.
+    @abstract yes
+
+    # This method is a default implementation of the renderer that
+    # will be called when the validation has failed. You can easily
+    # override it in either your service or in an external compound.
+    # By default it renders JSON object with errors mapped to params.
+    renderHeaderValidation: (results, request, response) ->
+        notRequest = "a #{request} is not a request"
+        notResponse = "a #{response} is not a respnonse"
+        assert _.isObject(response), notResponse
+        assert _.isObject(request), notRequest
+        message = (error) -> error.message
+        strings = _.map results, message
+        response.statusCode = 400
+        map = _.object _.keys(results), strings
+        return @reject response, headers: map
+
+    # Given the request with possible validation contexts appended
+    # run all the validator contexts in parallel and wait for the
+    # completion. If no validation mistakes found, run continuation.
+    # If some mistakes are found, however, run `@renderValidation`.
+    validateHeaders: (request, response, continuation) ->
+        notRequest = "a #{request} is not a request"
+        notResponse = "a #{response} is not a response"
+        notContinuation = "a #{continuation} is not function"
+        transformer = (o) -> (c) -> o.run (e) -> c null, e
+        assert _.isFunction(continuation), notContinuation
+        assert _.isObject(response), notResponse
+        assert _.isObject(request), notRequest
+        vcontexts = request.headers.vcontexts or {}
+        transformed =  _.map _.values(vcontexts), transformer
+        transformed = _.object _.keys(vcontexts), transformed
+        async.parallel transformed, (error, results) =>
+            assert not error, "internal valdation error"
+            errors = _.any _.values(results), _.isObject
+            hasRender = _.isFunction @renderHeaderValidation
+            assert hasRender, "no method to render validation"
+            params = [results, request, response, continuation]
+            return @renderHeaderValidation params... if errors
+        return continuation.bind(this)()
+
+    # Create a validation context for the parameter designated by
+    # the `name` and add it to the current request. If the `message`
+    # is supplied then it will be forced as an error messages. Use
+    # this method to automatically obtain contex for the parameter.
+    header: (request, name, message) ->
+        context = @constructor.validationContext?()
+        context = Primitive unless _.isObject context
+        assert request; value = request.headers?[name]
+        vcontexts = request.headers.vcontexts ?= {}
         return obtain if obtain = vcontexts[name]
         created = new context value, message
         vcontexts[name] = created; created


Changes here and there in the validation arch

--- library/membrane/validate.coffee
+++ library/membrane/validate.coffee
@@ -138,15 +138,15 @@
         assert _.isObject(response), notResponse
         assert _.isObject(request), notRequest
         @validateValues request.params, (failure, results) ->
-            signature = [results, request, response, continuation]
-            return @renderParamValidation signature... if failure
+            signature = [results, request, response]
+            return @parametersFailed signature... if failure
             return continuation.bind(this) failure, results

     # This method is a default implementation of the renderer that
     # will be called when the validation has failed. You can easily
     # override it in either your service or in an external compound.
     # By default it renders JSON object with errors mapped to params.
-    renderParamValidation: (results, request, response) ->
+    parametersFailed: (results, request, response) ->
         notRequest = "a #{request} is not a request"
         notResponse = "a #{response} is not a respnonse"
         assert _.isObject(response), notResponse
@@ -192,15 +192,15 @@
         assert _.isObject(response), notResponse
         assert _.isObject(request), notRequest
         @validateValues request.headers, (failure, results) ->
-            signature = [results, request, response, continuation]
-            return @renderHeaderValidation signature... if failure
+            signature = [results, request, response]
+            return @headersFailed signature... if failure
             return continuation.bind(this) failure, results

     # This method is a default implementation of the renderer that
     # will be called when the validation has failed. You can easily
     # override it in either your service or in an external compound.
     # By default it renders JSON object with errors mapped to headers.
-    renderHeaderValidation: (results, request, response) ->
+    headersFailed: (results, request, response) ->
         notRequest = "a #{request} is not a request"
         notResponse = "a #{response} is not a respnonse"
         assert _.isObject(response), notResponse


element.css on window element fix

element.css on window element fix

--- src/scripts/jqlite-extras.coffee
+++ src/scripts/jqlite-extras.coffee
@@ -3,5 +3,11 @@
 		'$log', '$window'
 		(console, window) ->
 			console.log 'config'
-			console.log window.jQuery
+			unless window.jQuery
+				console.log angular.element.prototype.html
+				console.log angular.element.prototype.height
+				css = angular.element.prototype.css
+				angular.element.prototype.css = (elem, name, value) ->
+					css(elem, name, value) unless !elem || elem.nodeType == 3 || elem.nodeType == 8 || !elem.style
+
 ]


Decouple pending logical scroll positions

--- src/text-editor-presenter.coffee
+++ src/text-editor-presenter.coffee
@@ -70,7 +70,14 @@

     @updateContentDimensions()
     @updateScrollbarDimensions()
-    @updateScrollPosition()
+
+    @restoreScrollPosition()
+    @commitPendingLogicalScrollTopPosition()
+    @commitPendingScrollTopPosition()
+    @commitPendingLogicalScrollLeftPosition()
+    @commitPendingScrollLeftPosition()
+    @clearPendingScrollPosition()
+
     @updateStartRow()
     @updateEndRow()
     @updateCommonGutterState()
@@ -826,10 +833,10 @@

       @emitDidUpdateState()

-  setScrollTop: (scrollTop) ->
+  setScrollTop: (scrollTop, overrideScroll=true) ->
     return unless scrollTop?

-    @pendingScrollLogicalPosition = null
+    @pendingScrollLogicalPosition = null if overrideScroll
     @pendingScrollTop = scrollTop

     @shouldUpdateVerticalScrollState = true
@@ -867,10 +874,10 @@

     @emitDidUpdateState()

-  setScrollLeft: (scrollLeft) ->
+  setScrollLeft: (scrollLeft, overrideScroll=true) ->
     return unless scrollLeft?

-    @pendingScrollLogicalPosition = null
+    @pendingScrollLogicalPosition = null if overrideScroll
     @pendingScrollLeft = scrollLeft

     @shouldUpdateHorizontalScrollState = true
@@ -901,13 +908,13 @@
       @contentFrameWidth - @verticalScrollbarWidth

   getScrollBottom: -> @getScrollTop() + @getClientHeight()
-  setScrollBottom: (scrollBottom) ->
-    @setScrollTop(scrollBottom - @getClientHeight())
+  setScrollBottom: (scrollBottom, overrideScroll) ->
+    @setScrollTop(scrollBottom - @getClientHeight(), overrideScroll)
     @getScrollBottom()

   getScrollRight: -> @getScrollLeft() + @getClientWidth()
-  setScrollRight: (scrollRight) ->
-    @setScrollLeft(scrollRight - @getClientWidth())
+  setScrollRight: (scrollRight, overrideScroll) ->
+    @setScrollLeft(scrollRight - @getClientWidth(), overrideScroll)
     @getScrollRight()

   getScrollHeight: ->
@@ -1520,23 +1527,15 @@
   getHorizontalScrollbarHeight: ->
     @horizontalScrollbarHeight

-  commitPendingLogicalScrollPosition: ->
+  commitPendingLogicalScrollTopPosition: ->
     return unless @pendingScrollLogicalPosition?

     {screenRange, options} = @pendingScrollLogicalPosition

     verticalScrollMarginInPixels = @getVerticalScrollMarginInPixels()
-    horizontalScrollMarginInPixels = @getHorizontalScrollMarginInPixels()
-
-    {top, left} = @pixelRectForScreenRange(new Range(screenRange.start, screenRange.start))
-    {top: endTop, left: endLeft, height: endHeight} = @pixelRectForScreenRange(new Range(screenRange.end, screenRange.end))
-    bottom = endTop + endHeight
-    right = endLeft
-
-    top += @scrollTop
-    bottom += @scrollTop
-    left += @scrollLeft
-    right += @scrollLeft
+
+    top = screenRange.start.row * @lineHeight
+    bottom = (screenRange.end.row + 1) * @lineHeight

     if options?.center
       desiredScrollCenter = (top + bottom) / 2
@@ -1547,31 +1546,43 @@
       desiredScrollTop = top - verticalScrollMarginInPixels
       desiredScrollBottom = bottom + verticalScrollMarginInPixels

+    if options?.reversed ? true
+      if desiredScrollBottom > @getScrollBottom()
+        @setScrollBottom(desiredScrollBottom, false)
+      if desiredScrollTop < @getScrollTop()
+        @setScrollTop(desiredScrollTop, false)
+    else
+      if desiredScrollTop < @getScrollTop()
+        @setScrollTop(desiredScrollTop, false)
+      if desiredScrollBottom > @getScrollBottom()
+        @setScrollBottom(desiredScrollBottom, false)
+
+  commitPendingLogicalScrollLeftPosition: ->
+    return unless @pendingScrollLogicalPosition?
+
+    {screenRange, options} = @pendingScrollLogicalPosition
+
+    horizontalScrollMarginInPixels = @getHorizontalScrollMarginInPixels()
+
+    {left} = @pixelRectForScreenRange(new Range(screenRange.start, screenRange.start))
+    {left: right} = @pixelRectForScreenRange(new Range(screenRange.end, screenRange.end))
+
+    left += @scrollLeft
+    right += @scrollLeft
+
     desiredScrollLeft = left - horizontalScrollMarginInPixels
     desiredScrollRight = right + horizontalScrollMarginInPixels

     if options?.reversed ? true
-      if desiredScrollBottom > @getScrollBottom()
-        @setScrollBottom(desiredScrollBottom)
-      if desiredScrollTop < @getScrollTop()
-        @setScrollTop(desiredScrollTop)
-
       if desiredScrollRight > @getScrollRight()
-        @setScrollRight(desiredScrollRight)
+        @setScrollRight(desiredScrollRight, false)
       if desiredScrollLeft < @getScrollLeft()
-        @setScrollLeft(desiredScrollLeft)
+        @setScrollLeft(desiredScrollLeft, false)
     else
-      if desiredScrollTop < @getScrollTop()
-        @setScrollTop(desiredScrollTop)
-      if desiredScrollBottom > @getScrollBottom()
-        @setScrollBottom(desiredScrollBottom)
-
       if desiredScrollLeft < @getScrollLeft()
-        @setScrollLeft(desiredScrollLeft)
+        @setScrollLeft(desiredScrollLeft, false)
       if desiredScrollRight > @getScrollRight()
-        @setScrollRight(desiredScrollRight)
-
-    @pendingScrollLogicalPosition = null
+        @setScrollRight(desiredScrollRight, false)

   commitPendingScrollLeftPosition: ->
     return unless @pendingScrollLeft?
@@ -1585,8 +1596,6 @@

       @emitter.emit 'did-change-scroll-left', @scrollLeft

-    @pendingScrollLeft = null
-
   commitPendingScrollTopPosition: ->
     return unless @pendingScrollTop?

@@ -1600,8 +1609,6 @@
       @didStartScrolling()
       @emitter.emit 'did-change-scroll-top', @scrollTop

-    @pendingScrollTop = null
-
   restoreScrollPosition: ->
     return if @hasRestoredScrollPosition or not @hasPixelPositionRequirements()

@@ -1610,11 +1617,10 @@

     @hasRestoredScrollPosition = true

-  updateScrollPosition: ->
-    @restoreScrollPosition()
-    @commitPendingLogicalScrollPosition()
-    @commitPendingScrollLeftPosition()
-    @commitPendingScrollTopPosition()
+  clearPendingScrollPosition: ->
+    @pendingScrollLogicalPosition = null
+    @pendingScrollTop = null
+    @pendingScrollLeft = null

   canScrollLeftTo: (scrollLeft) ->
     @scrollLeft isnt @constrainScrollLeft(scrollLeft)


Unify decoration observation

Unify decoration observation

--- src/text-editor-presenter.coffee
+++ src/text-editor-presenter.coffee
@@ -47,10 +47,7 @@
       @updateLineNumbersState()
     @disposables.add @model.onDidAddDecoration(@didAddDecoration.bind(this))
     @disposables.add @model.onDidAddCursor(@didAddCursor.bind(this))
-    @observeLineDecoration(decoration) for decoration in @model.getLineDecorations()
-    @observeLineNumberDecoration(decoration) for decoration in @model.getLineNumberDecorations()
-    @observeHighlightDecoration(decoration) for decoration in @model.getHighlightDecorations()
-    @observeOverlayDecoration(decoration) for decoration in @model.getOverlayDecorations()
+    @observeDecoration(decoration) for decoration in @model.getDecorations()
     @observeCursor(cursor) for cursor in @model.getCursors()

   observeConfig: ->
@@ -606,31 +603,36 @@

     {top, left, width, height}

-  observeLineDecoration: (decoration) ->
+  observeDecoration: (decoration) ->
     decorationDisposables = new CompositeDisposable
-    decorationDisposables.add decoration.getMarker().onDidChange(@updateLinesState.bind(this))
+    decorationDisposables.add decoration.getMarker().onDidChange(@decorationMarkerDidChange.bind(this, decoration))
+    if decoration.isType('highlight')
+      decorationDisposables.add decoration.onDidChangeProperties(@updateHighlightsState.bind(this))
+      decorationDisposables.add decoration.onDidFlash(@highlightDidFlash.bind(this, decoration))
     decorationDisposables.add decoration.onDidDestroy =>
       @disposables.remove(decorationDisposables)
-      @updateLinesState()
+      @didDestroyDecoration(decoration)
     @disposables.add(decorationDisposables)

-  observeLineNumberDecoration: (decoration) ->
-    decorationDisposables = new CompositeDisposable
-    decorationDisposables.add decoration.getMarker().onDidChange(@updateLineNumbersState.bind(this))
-    decorationDisposables.add decoration.onDidDestroy =>
-      @disposables.remove(decorationDisposables)
-      @updateLineNumbersState()
-    @disposables.add(decorationDisposables)
-
-  observeHighlightDecoration: (decoration) ->
-    decorationDisposables = new CompositeDisposable
-    decorationDisposables.add decoration.getMarker().onDidChange(@updateHighlightsState.bind(this))
-    decorationDisposables.add decoration.onDidChangeProperties(@updateHighlightsState.bind(this))
-    decorationDisposables.add decoration.onDidFlash(@highlightDidFlash.bind(this, decoration))
-    decorationDisposables.add decoration.onDidDestroy =>
-      @disposables.remove(decorationDisposables)
+  decorationMarkerDidChange: (decoration) ->
+    if decoration.isType('line')
+      @updateLinesState()
+    if decoration.isType('line-number')
+      @updateLineNumbersState()
+    if decoration.isType('highlight')
       @updateHighlightsState()
-    @disposables.add(decorationDisposables)
+    if decoration.isType('overlay')
+      @updateOverlaysState()
+
+  didDestroyDecoration: (decoration) ->
+    if decoration.isType('line')
+      @updateLinesState()
+    if decoration.isType('line-number')
+      @updateLineNumbersState()
+    if decoration.isType('highlight')
+      @updateHighlightsState()
+    if decoration.isType('overlay')
+      @updateOverlaysState()

   highlightDidFlash: (decoration) ->
     flash = decoration.consumeNextFlash()
@@ -640,27 +642,16 @@
       decorationState.flashDuration = flash.duration
       @emitter.emit "did-update-state"

-  observeOverlayDecoration: (decoration) ->
-    decorationDisposables = new CompositeDisposable
-    decorationDisposables.add decoration.getMarker().onDidChange(@updateOverlaysState.bind(this))
-    decorationDisposables.add decoration.onDidChangeProperties(@updateOverlaysState.bind(this))
-    decorationDisposables.add decoration.onDidDestroy =>
-      @disposables.remove(decorationDisposables)
-      @updateOverlaysState()
-    @disposables.add(decorationDisposables)
-
   didAddDecoration: (decoration) ->
+    @observeDecoration(decoration)
+
     if decoration.isType('line')
-      @observeLineDecoration(decoration)
       @updateLinesState()
     if decoration.isType('line-number')
-      @observeLineNumberDecoration(decoration)
       @updateLineNumbersState()
     else if decoration.isType('highlight')
-      @observeHighlightDecoration(decoration)
       @updateHighlightsState()
     else if decoration.isType('overlay')
-      @observeOverlayDecoration(decoration)
       @updateOverlaysState()

   observeCursor: (cursor) ->


use expressionParser name

use expressionParser name

--- static/coffee/completions.coffee
+++ static/coffee/completions.coffee
@@ -3,17 +3,17 @@

 window.autoCompleteUtils = {}

-autoCompleteUtils.excellentParser = new window.excellent.Parser('@', ['channel', 'contact', 'date', 'extra', 'flow', 'step']);
+autoCompleteUtils.expressionParser = new window.excellent.Parser('@', ['channel', 'contact', 'date', 'extra', 'flow', 'step']);

 autoCompleteUtils.matcher = (flag, subtext) ->
-  autoCompleteUtils.excellentParser.expressionContext(subtext)
+  autoCompleteUtils.expressionParser.expressionContext(subtext)

 autoCompleteUtils.findContextQuery = (query) ->

   if not query
     return query

-  autoCompleteUtils.excellentParser.autoCompleteContext(query) or ''
+  autoCompleteUtils.expressionParser.autoCompleteContext(query) or ''

 autoCompleteUtils.findMatches = (query, data, start, lastIdx, prependChar = undefined ) ->


Updated test case descriptions

Updated test case descriptions

--- test/scenario-based-tests.coffee
+++ test/scenario-based-tests.coffee
@@ -123,7 +123,7 @@

-  describe 'scenario-4-comment-insertion (Async)', ->
+  describe 'Scenario 4a: Comment Insertion (Async)', ->

     it 'test', (done)->


modify

modify

--- test/test-npm-module.spec.coffee
+++ test/test-npm-module.spec.coffee
@@ -10,5 +10,5 @@

 describe 'test-npm-module', ->
   it 'works', ->
-    actual = testNpmModule 'World4'
+    actual = testNpmModule 'World5'
     expect(actual).to.eql 'Hello World4'


Added tests for lists of complex shapes to AWS.XMLParser.

--- test/unit/xml_parser.spec.coffee
+++ test/unit/xml_parser.spec.coffee
@@ -72,7 +72,31 @@
       parse xml, rules, (data) ->
         expect(data).toEqual({items:['abc','xyz']})

-    # TODO : test lists of structures (with rename)
+    it 'can parse lists of strucures', ->
+      xml = """
+      <xml>
+        <People>
+          <member><Name>abc</Name></member>>
+          <member><Name>xyz</Name></member>>
+        </People>
+      </xml>
+      """
+      rules = {People:{t:'a',m:{t:'o',m:{Name:{t:'s'}}}}}
+      parse xml, rules, (data) ->
+        expect(data).toEqual({People:[{Name:'abc'},{Name:'xyz'}]})
+
+    it 'can parse lists of strucures with renames', ->
+      xml = """
+      <xml>
+        <People>
+          <Person><Name>abc</Name></Person>>
+          <Person><Name>xyz</Name></Person>>
+        </People>
+      </xml>
+      """
+      rules = {People:{t:'a',m:{t:'o',n:'Person',m:{Name:{t:'s'}}}}}
+      parse xml, rules, (data) ->
+        expect(data).toEqual({People:[{Name:'abc'},{Name:'xyz'}]})

   describe 'flattened lists', ->

@@ -114,6 +138,18 @@
       parse xml, rules, (data) ->
         expect(data).toEqual(values)

+    it 'can parse flattened lists of complex objects', ->
+      xml = """
+      <xml>
+        <Count>2</Count>
+        <Person><Name>abc</Name></Person>
+        <Person><Name>xyz</Name></Person>
+      </xml>
+      """
+      rules = {Count:{t:'i'},Person:{t:'a',f:1,n:'People',m:{t:'o',n:'Person'}}}
+      parse xml, rules, (data) ->
+        expect(data).toEqual({Count:2,People:[{Name:'abc'},{Name:'xyz'}]})
+
   describe 'maps', ->

   describe 'booleans', ->


Social: fix lint errors

Social: fix lint errors

--- workers/social/lib/social/models/socialapi/channel.coffee
+++ workers/social/lib/social/models/socialapi/channel.coffee
@@ -50,7 +50,7 @@
     fetchGroup client, (err, group)->
       return callback err if err
       {connection:{delegate}} = client
-      delegate.createSocialApiId (err, socialApiId)=>
+      delegate.createSocialApiId (err, socialApiId)->
         return callback err if err

         data =
@@ -73,7 +73,7 @@
     fetchGroup client, (err, group)->
       return callback err if err
       {connection:{delegate}} = client
-      delegate.createSocialApiId (err, socialApiId)=>
+      delegate.createSocialApiId (err, socialApiId)->
         return callback err if err

         data =
@@ -86,7 +86,7 @@
   @listPinnedMessages = permit 'pin posts',
     success:  (client, data, callback)->
       {connection:{delegate}, context} = client
-      delegate.createSocialApiId (err, socialApiId)=>
+      delegate.createSocialApiId (err, socialApiId)->
         return callback err if err
         data.accountId = socialApiId
         data.groupName = context.group
@@ -96,10 +96,10 @@
   @pinMessage = permit 'pin posts',
     success:  (client, data, callback)->
       {connection:{delegate}, context} = client
-      delegate.createSocialApiId (err, socialApiId)=>
+      delegate.createSocialApiId (err, socialApiId)->
         return callback err if err
         unless data.messageId
-          return callback { message: "Message id is not set for pinning requests "}
+          return callback {message: "Message id is not set for pinning "}

         data.groupName = context.group
         data.accountId = socialApiId
@@ -109,10 +109,10 @@
   @unpinMessage = permit 'like posts',
     success:  (client, data, callback)->
       {connection:{delegate}, context} = client
-      delegate.createSocialApiId (err, socialApiId)=>
+      delegate.createSocialApiId (err, socialApiId)->
         return callback err if err
         unless data.messageId
-          return callback { message: "Message id is not set for un-pinning requests "}
+          return callback {message: "Message id is not set for un-pinning "}

         data.groupName = context.group
         data.accountId = socialApiId


Update redis_singleton

Update redis_singleton

--- src/models/redis_singleton.coffee
+++ src/models/redis_singleton.coffee
@@ -1,6 +1,6 @@
-Singleton = require './singleton'
-Config = require './config'
-Logger = require './logger'
+Singleton = require '../singleton'
+Config = require '../config'
+Logger = require '../logger'
 redis = require 'redis'

 ###*


put the ctrl-c keybinding in the right place

Oh yeah, this is for insert mode to active command mode. Now it works.

--- keymaps/vim-mode.cson
+++ keymaps/vim-mode.cson
@@ -3,6 +3,7 @@

 '.vim-mode.insert-mode:not(.mini)':
   'escape': 'vim-mode:activate-command-mode'
+  'ctrl-c': 'vim-mode:activate-command-mode'

 '.vim-mode.command-mode:not(.mini)':
   '" a': 'vim-mode:register-prefix'


Fix for auto autocomplete app like textExpander

--- src/index.coffee
+++ src/index.coffee
@@ -480,7 +480,7 @@
     QJ.on el, 'keypress', restrictCardNumber
     QJ.on el, 'keypress', formatCardNumber
     QJ.on el, 'keydown', formatBackCardNumber
-    QJ.on el, 'keyup', setCardType
+    QJ.on el, 'keyup blur', setCardType
     QJ.on el, 'paste', reFormatCardNumber
     el
   @getCardArray: -> return cards


Returns "this" for chaining

Returns "this" for chaining

--- src/revisionist.coffee
+++ src/revisionist.coffee
@@ -146,6 +146,9 @@
     # Reset the version pointer
     _currentVersion = 0

+    # Chain 'this' through
+    return @
+
 # ## Simple Plugin
 #
 # This is a reference implementation for a plugin.


:memo: Add TODOs.

:memo: Add TODOs.

--- lib/jumpy-view.coffee
+++ lib/jumpy-view.coffee
@@ -1,3 +1,13 @@
+#TODO: Do a final test through of all features with shadow turned on and off
+# before launching.
+# Currently shadow dom mode works!
+
+# MAJOR Before 2.0
+# TODO: Status bar code
+# TODO: Beacon code
+# TODO: Merge in @willdady's code for better accuracy.
+# TODO: Investigate using markers (if time permitting)
+
 {View, $} = require 'space-pen'
 _ = require 'lodash'

@@ -42,7 +52,6 @@
         @backedUpKeyBindings = _.clone atom.keymap.keyBindings

         @workspaceElement = atom.views.getView(atom.workspace)
-        # TODO: Fix status bar code!
         @workspaceElement.statusBar?.prependLeft(
             '

')


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/color2.coffee
+++ test/color2.coffee
@@ -269,12 +269,19 @@
   color1 = Color2(50, 50, 50)
   color2 = Color2(10, 10, 10)

-  color3 = Color2.mix(color1, color2)
-
-  equals color3.r, 30
-  equals color3.g, 30
-  equals color3.b, 30
-  equals color3.a, 1.0
+  mixedColor = Color2.mix(color1, color2)
+
+  equals mixedColor.r, 30
+  equals mixedColor.g, 30
+  equals mixedColor.b, 30
+  equals mixedColor.a, 1.0
+
+  weightedMixedColor = Color2.mix(color1, color2, 0.1)
+
+  equals mixedColor2.r(), (3.3 + 45).round()
+  equals mixedColor2.g(), (5.5 + 81).round()
+  equals mixedColor2.b(), (10 + 180).round()
+  equals mixedColor2.a(), 0.1 + (0.5 * 0.9)

 test "accepts named colors", ->
   white = Color2("white")


Track collection and page loading

Track collection and page loading

--- src/scripts/models/content.coffee
+++ src/scripts/models/content.coffee
@@ -37,9 +37,12 @@
       toc.reset()
       @set('toc', toc)

+      @set('loaded', false)
       @fetch
         reset: true
-        success: () => @load(options.page)
+        success: () =>
+          @set('loaded', true)
+          @load(options.page)

     parse: (response) ->
       response = @parseInfo(response)
@@ -91,7 +94,7 @@
       page = @get('currentPage')
       page.fetch
         success: () =>
-          page.loaded = true
+          page.set('loaded', true)
           @trigger('changePage')

     setPage: (num) ->
@@ -106,7 +109,7 @@
       page.set('active', true)
       @trigger('changePage')

-      if not page.loaded
+      if not page.get('loaded')
         @fetchPage()

     getNextPage: () ->


Derive content in the correct position in its book or section

--- src/scripts/models/content.coffee
+++ src/scripts/models/content.coffee
@@ -151,14 +151,15 @@

       if @isBook()
         page = @get('currentPage')
+        parent = page.get('_parent')
         title = page.get('title')
         id = page.id
-        index = @getPageNumber(page)
+        index = parent.get('contents').indexOf(page)

         options = _.extend({at: index}, options)

         @get('contents').remove(page)
-        @create({title: title, derivedFrom: id}, options)
+        parent.create({title: title, derivedFrom: id}, options)
       else
         @derive(options)