Sort disabled packages

Sort disabled packages

--- atom/config.cson
+++ atom/config.cson
@@ -6,44 +6,44 @@
   core:
     disabledPackages: [
       "adwaita-ui"
+      "atom-color-highlight"
+      "atom-ctags"
+      "atom-json-escape"
+      "atom-terminal"
+      "atom-ungit"
       "atomatigit"
-      "command-toolbar"
-      "ctags-status"
-      "recent-files"
-      "tomorrow"
-      "atom-ctags"
-      "git-projects"
-      "welcome"
-      "minimap-highlight-selected"
-      "minimap-selection"
-      "minimap"
       "autocomplete-plus-python-jedi"
-      "atom-color-highlight"
-      "atom-terminal"
-      "linter-jshint"
-      "sublime-tabs"
-      "linter-clang"
-      "tree-view-open-files"
-      "fixmyjs"
-      "linter-flow"
-      "node-debugger"
-      "atom-json-escape"
-      "atom-ungit"
       "build-tools"
       "color-picker"
+      "command-toolbar"
       "copy-improved"
+      "ctags-status"
       "dbus"
+      "fixmyjs"
+      "git-projects"
+      "linter-clang"
+      "linter-coffeelint"
+      "linter-coffeescript"
       "linter-flake8"
+      "linter-flow"
+      "linter-jshint"
       "linter-pep8"
       "linter-pyflakes"
       "linter-pylint"
       "linter-rust"
-      "linter-coffeescript"
-      "linter-coffeelint"
       "linter-scss-lint"
       "linter-stylint"
+      "minimap-highlight-selected"
+      "minimap-selection"
+      "minimap"
+      "node-debugger"
       "recent-files-fuzzy-finder"
+      "recent-files"
+      "sublime-tabs"
       "switch-header-source"
+      "tomorrow"
+      "tree-view-open-files"
+      "welcome"
       "you-complete-me"
     ]
     themes: [


Update config.cson for Atom

Update config.cson for Atom

--- atom/config.cson
+++ atom/config.cson
@@ -45,6 +45,8 @@
     maxDepth: 1
   "outdoor-theme":
     lightLevelToSwitch: 5904360
+  "notational-velocity":
+    directory: "/Users/j/.notes"
 ".git-commit.text":
   editor:
     preferredLineLength: 72


enable linter-ruby again

enable linter-ruby again

--- atom/config.cson
+++ atom/config.cson
@@ -11,9 +11,6 @@
     themes: [
       "one-dark-ui"
       "gruvbox"
-    ]
-    disabledPackages: [
-      "linter-ruby"
     ]
   editor:
     invisibles: {}


atom keymap cleanup fixed buggy shortcuts

--- atom/keymap.cson
+++ atom/keymap.cson
@@ -1,12 +1,13 @@
 '.platform-win32, .platform-linux':
-  'cmd-shift-F': 'project-find:show'
+  'cmd-shift-F': 'project-find:show',
+  'cmd-\\': 'tree-view:toggle'
 'atom-workspace, atom-workspace atom-text-editor':
   'cmd-shift-M': 'markdown-preview:toggle'
-'atom-workspace atom-text-editor:not([mini])':
-  'cmd-/': 'editor:toggle-line-comments'
-'.platform-win32, .platform-linux':
-  'cmd-\\': 'tree-view:toggle'
 '.platform-linux, .platform-linux .command-palette atom-text-editor':
   'cmd-shift-P': 'command-palette:toggle'
 '.platform-win32 atom-text-editor, .platform-linux atom-text-editor':
   'cmd-e': 'find-and-replace:use-selection-as-find-pattern'
+'atom-workspace atom-text-editor:not([mini])':
+  'cmd-/': 'editor:toggle-line-comments',
+  'cmd-[': 'editor:outdent-selected-rows',
+  'cmd-\]': 'editor:indent-selected-rows'


Add snippet for wrapping selection with Emmet abbrev

--- atom/keymap.cson
+++ atom/keymap.cson
@@ -80,6 +80,8 @@

   # Expands Emmet HTML abbreviations
   'cmd-e': 'emmet:expand-abbreviation'
+  # Wrap selection in Emmet HTML abbreviation
+  'shift-cmd-e': 'emmet:wrap-with-abbreviation'

   # Opens inline URL in default web browser if cursor is within URL
   'alt-cmd-o': 'link:open'


Fix keymap selectors for Atom

Fix keymap selectors for Atom

- Override Nuclide define for C-h
- Override C-d from vim-mode-plus

--- atom/keymap.cson
+++ atom/keymap.cson
@@ -1,6 +1,6 @@
 # all
 # ----------------------------------------
-'body':
+'atom-workspace':
     # tab navigation
     'ctrl-d': 'core:close'
     'ctrl-h': 'pane:show-previous-item'
@@ -13,6 +13,8 @@
 # editor
 # ----------------------------------------
 'atom-text-editor.vim-mode-plus':
+    # tab navigation
+    'ctrl-d': 'core:close'

     # mode switch
     'ctrl-space': 'vim-mode-plus:reset-normal-mode'


Improve dynamic Angular Bootstrapping

Improve dynamic Angular Bootstrapping

--- app/assets/javascripts/darkswarm/all.js.coffee
+++ app/assets/javascripts/darkswarm/all.js.coffee
@@ -56,7 +56,8 @@
 #= require_tree .

 document.addEventListener "turbo:load", ->
-  window.injector = angular.bootstrap document.body, ["Darkswarm"]
+  try
+    window.injector = angular.bootstrap document.body, ["Darkswarm"]
   true

 document.addEventListener "turbo:before-render", ->


No UTC here

No UTC here

--- app/assets/javascripts/faye_listener.js.coffee
+++ app/assets/javascripts/faye_listener.js.coffee
@@ -6,7 +6,7 @@
       fayeClient.subscribe "/temperature/#{sensorId}", (message) ->
         message = $.parseJSON(message)
         element.find('.temperature').html(parseFloat(message[0]).toFixed(1))
-        element.find('.when').html(moment(message[1] * 1e3).utc().format("llll"))
+        element.find('.when').html(moment(message[1] * 1e3).format("llll"))

     if $('.activity-feed').size() > 0
       fayeClient.subscribe "/pour/complete", (message) ->


Project::open -> Workspace::open

Project::open -> Workspace::open

--- spec/toggle-quotes-spec.coffee
+++ spec/toggle-quotes-spec.coffee
@@ -11,8 +11,11 @@
       waitsForPromise ->
         atom.packages.activatePackage('language-json')

+      waitsForPromise ->
+        atom.workspace.open()
+
       runs ->
-        editor = atom.project.openSync()
+        editor = atom.workspace.getActiveTextEditor()
         editor.setText """
           console.log("Hello World");
           console.log('Hello World');
@@ -107,8 +110,11 @@
       waitsForPromise ->
         atom.packages.activatePackage('language-python')

+      waitsForPromise ->
+        atom.workspace.open()
+
       runs ->
-        editor = atom.project.openSync()
+        editor = atom.workspace.getActiveTextEditor()
         editor.setText """
           print(u"Hello World")
           print(r'')


select links w/ real import kind for import-graph

We split the kind "import_or_view" into
"import" and "view". The display of those
links the graphs view needed to be adjusted to
account for this change.

--- app/assets/javascripts/plugins/graph.js.coffee
+++ app/assets/javascripts/plugins/graph.js.coffee
@@ -98,7 +98,7 @@
     else if the_mode == "import"
       import_edges = []
       for import_edge in edges
-        if import_edge.info.kind == "import_or_view"
+        if import_edge.info.kind == "import"
           import_edges.push(import_edge)
       import_edges


remove fdescribe

remove fdescribe

--- spec/bookmarks-view-spec.coffee
+++ spec/bookmarks-view-spec.coffee
@@ -1,7 +1,7 @@
 RootView = require 'root-view'
 _ = require 'underscore'

-fdescribe "Bookmarks package", ->
+describe "Bookmarks package", ->
   editor = null
   editSession = null
   displayBuffer = null


Default matches in settings tests.

Default matches in settings tests.

- Disabled the camel case tests, they're not ready but custom matches
  feature is ready.

--- spec/jumpy-settings-spec.coffee
+++ spec/jumpy-settings-spec.coffee
@@ -5,12 +5,14 @@
 NUM_ALPHA_TEST_WORDS = 26 * 3
 NUM_ENGLISH_TEXT = 8 - 2 #For a's that are only 1 character.  *'s don't count.
 NUM_COLLAPSIBLE_WORDS = 19
-NUM_CAMEL_MATCHES = 15 # 5 + 6 + 4 (camel matches)
+NUM_CAMEL_WORDS = 3
 NUM_TOTAL_WORDS =
     NUM_ALPHA_TEST_WORDS +
     NUM_ENGLISH_TEXT +
     NUM_COLLAPSIBLE_WORDS +
-    NUM_CAMEL_MATCHES
+    NUM_CAMEL_WORDS
+
+NUM_CAMEL_SPECIFIC_MATCHES = 4 + 5 + 3

 describe "Jumpy with non default settings on", ->
     [editorView, editor, jumpyPromise] = []
@@ -21,6 +23,8 @@
         atom.config.set 'jumpy.highContrast', true
         atom.config.set 'jumpy.fontSize', .50
         atom.config.set 'jumpy.useHomingBeaconEffectOnJumps', false
+        atom.config.set 'jumpy.matchPattern', '([\\w]){2,}' # also the default
+        #atom.config.set 'jumpy.matchPattern', '[A-Z]+[0-9a-z]|([a-z])*' # NOT YET DONE.

         waitsForPromise ->
             atom.workspace.open 'test_text.md'
@@ -53,3 +57,29 @@
             editorView.trigger 'jumpy:c'
             expect(editorView.find('.cursors .cursor')[0].classList
                 .contains 'beacon').toBe false
+
+    describe "when a custom match (jumpy default) is used", ->
+        it "draws correct labels", ->
+            expect(editorView.find('.jumpy.labels')).toExist()
+            labels = editorView.find('.jumpy.label')
+            expect(labels.length)
+                .toBe NUM_TOTAL_WORDS
+            expect(labels[0].innerHTML).toBe 'aa'
+            expect(labels[1].innerHTML).toBe 'ab'
+            expect(labels[82].innerHTML).toBe 'de'
+            expect(labels[83].innerHTML).toBe 'df'
+
+    # Do not have the regex to make this work yet.
+    xdescribe "when a custom match is used (camel case)", ->
+        it "draws correct labels", ->
+            expect(editorView.find('.jumpy.labels')).toExist()
+            labels = editorView.find('.jumpy.label')
+            expect(labels.length)
+                .toBe NUM_TOTAL_WORDS + NUM_CAMEL_SPECIFIC_MATCHES
+            # BASE CASE WORDS:
+            expect(labels[0].innerHTML).toBe 'aa'
+            expect(labels[1].innerHTML).toBe 'ab'
+            expect(labels[82].innerHTML).toBe 'de'
+            expect(labels[83].innerHTML).toBe 'df'
+
+            #CAMELS:


Doubt it is this but let us see

Doubt it is this but let us see

--- test/models/article.coffee
+++ test/models/article.coffee
@@ -22,7 +22,7 @@
       Backbone.sync.args[0][2].success _.extend {}, fixtures.article, title: 'Moo'
       Backbone.sync.args[1][2].success [fixtures.article]

-    it 'gets the slideshow artworks', (done) ->
+    xit 'gets the slideshow artworks', (done) ->
       @article.fetchWithRelated success: (data) ->
         # It looks like there's a very tight race condition here between
         # the success callback and the promise resolution.


columns: test false

columns: test false

--- test/option.columns.coffee
+++ test/option.columns.coffee
@@ -54,6 +54,17 @@
           "FIELD_6":"2050-11-27"
         ] unless err
         next err
+
+    it 'disabled if false', (next) ->
+      parse """
+      a,b,c
+      d,e,f
+      """, columns: false, (err, data) ->
+        data.should.eql [
+          ['a', 'b', 'c']
+          ['d', 'e', 'f']
+        ] unless err
+        next err

   describe 'boolean array', ->


remove function revealLetter

remove function revealLetter

--- cf/spell.coffee
+++ cf/spell.coffee
@@ -43,11 +43,7 @@
     n = Math.floor(Math.random * @current.length)
     if @reveal[n] == "_"
       @reveal[n] = @current[n]
-    else
-      randomLetter()
-
-  revealLetter = () ->
-    i = randomLetter()
+      return

   update_keyHandler = () ->
     $("#input_answer").off()


one day I'll learn to pay attention to errors

--- test/reactions_test.coffee
+++ test/reactions_test.coffee
@@ -21,7 +21,7 @@
     expect(@robot.respond).to.have.been.calledWith(/react (fangirling)/i)

   it 'registers a slowclap responder', ->
-    expect(@robot.respond).to.have.been.calledWith(/react (slowclap|clap|applause|slow clap/i)
+    expect(@robot.respond).to.have.been.calledWith(/react (slowclap|clap|applause|slow clap)/i)

   it 'registers a success responder', ->
     expect(@robot.respond).to.have.been.calledWith(/react (success|awesome)/i)


add spec for the Shortcuts component and targetNodeSelector prop

--- test/shortcuts.spec.coffee
+++ test/shortcuts.spec.coffee
@@ -154,19 +154,19 @@

     expect(wrapper.props().preventDefault).to.be.equal(true)

-  it 'should not have targetNode prop by default', ->
-    shortcutComponent = React.createElement(Shortcuts, baseProps)
-    wrapper = enzyme.mount(shortcutComponent, { context: baseContext })
-
-    expect(wrapper.props().targetNode).to.be.equal(null)
+  it 'should not have targetNodeSelector prop by default', ->
+    shortcutComponent = React.createElement(Shortcuts, baseProps)
+    wrapper = enzyme.mount(shortcutComponent, { context: baseContext })
+
+    expect(wrapper.props().targetNodeSelector).to.be.equal(null)

   it 'should have targetNode prop', ->
     props = _.assign {}, baseProps,
-      targetNode: document.body
-    shortcutComponent = React.createElement(Shortcuts, props)
-    wrapper = enzyme.mount(shortcutComponent, { context: baseContext })
-
-    expect(wrapper.props().targetNode).to.be.equal(document.body)
+      targetNodeSelector: 'body'
+    shortcutComponent = React.createElement(Shortcuts, props)
+    wrapper = enzyme.mount(shortcutComponent, { context: baseContext })
+
+    expect(wrapper.props().targetNodeSelector).to.be.equal('body')

   it 'should have global prop set to false by default', ->
     shortcutComponent = React.createElement(Shortcuts, baseProps)
@@ -279,6 +279,27 @@
     simulant.fire(node, 'keydown', { keyCode: enter, key: 'Enter' })

     expect(wrapper.props().handler).to.have.been.called
+
+  it 'should fire the handler when using targetNodeSelector', ->
+    props = _.assign {}, baseProps,
+      targetNodeSelector: 'body'
+    shortcutComponent = React.createElement(Shortcuts, props)
+    wrapper = enzyme.mount(shortcutComponent, { context: baseContext })
+
+    enter = 13
+    simulant.fire(document.body, 'keydown', { keyCode: enter, key: 'Enter' })
+
+    expect(wrapper.props().handler).to.have.been.called
+
+  it 'should throw and error if targetNodeSelector is not found', ->
+    props = _.assign {}, baseProps,
+      targetNodeSelector: 'non-existing'
+    shortcutComponent = React.createElement(Shortcuts, props)
+
+    try
+      enzyme.mount(shortcutComponent, { context: baseContext })
+    catch err
+      expect(err).to.match(/Node selector 'non-existing'  was not found/)

   describe 'Shortcuts component inside Shortcuts component:', ->


Fix "value.format is not a function" bug

Fixes #513

The value passed to isValid is a momentjs instance, which
can't be compared using > < operators.  This switches to
the isBefore and isAfer methods with the granularity set to day.

Also if false was returned from the date picker, the min property
would be used.  However if the min property is a plain date object
it would lack the .format method which was later called on it.

--- src/components/tutor-input.cjsx
+++ src/components/tutor-input.cjsx
@@ -100,15 +100,15 @@

   isValid: (value) ->
     valid = true
-    valid = false if (@props.min and value < @props.min)
-    valid = false if (@props.max and value > @props.max)
+    valid = false if (@props.min and value.isBefore(@props.min, 'day'))
+    valid = false if (@props.max and value.isAfter(@props.max, 'day'))
     valid

   dateSelected: (value) ->
     valid = @isValid(value)

     if (not valid)
-      value = @props.min or null
+      value = moment(@props.min) or null

     date = value.format(TutorDateFormat)
     @props.onChange(date)


initial validation for tutor inputs so that is required error will show on invalidation

--- src/components/tutor-input.cjsx
+++ src/components/tutor-input.cjsx
@@ -22,7 +22,8 @@
       return ['required'] unless (inputValue? and inputValue.length > 0)

   getInitialState: ->
-    errors: []
+    errors = @props.validate(@props.value)
+    errors: errors or []

   onChange: (event) ->
     @props.onChange(event.target?.value, event.target)


Conditionally include deprecations in ContextMenuManager

--- src/context-menu-manager.coffee
+++ src/context-menu-manager.coffee
@@ -101,25 +101,26 @@
   #     with the following argument:
   #     * `event` The click event that deployed the context menu.
   add: (itemsBySelector) ->
-    # Detect deprecated file path as first argument
-    if itemsBySelector? and typeof itemsBySelector isnt 'object'
-      Grim.deprecate """
-        ContextMenuManager::add has changed to take a single object as its
-        argument. Please see
-        https://atom.io/docs/api/latest/ContextMenuManager for more info.
-      """
-      itemsBySelector = arguments[1]
-      devMode = arguments[2]?.devMode
-
-    # Detect deprecated format for items object
-    for key, value of itemsBySelector
-      unless _.isArray(value)
+    if Grim.includeDeprecations
+      # Detect deprecated file path as first argument
+      if itemsBySelector? and typeof itemsBySelector isnt 'object'
         Grim.deprecate """
           ContextMenuManager::add has changed to take a single object as its
           argument. Please see
           https://atom.io/docs/api/latest/ContextMenuManager for more info.
         """
-        itemsBySelector = @convertLegacyItemsBySelector(itemsBySelector, devMode)
+        itemsBySelector = arguments[1]
+        devMode = arguments[2]?.devMode
+
+      # Detect deprecated format for items object
+      for key, value of itemsBySelector
+        unless _.isArray(value)
+          Grim.deprecate """
+            ContextMenuManager::add has changed to take a single object as its
+            argument. Please see
+            https://atom.io/docs/api/latest/ContextMenuManager for more info.
+          """
+          itemsBySelector = @convertLegacyItemsBySelector(itemsBySelector, devMode)

     addedItemSets = []


Beautify Swig templates with handlebars instead of markup

--- src/beautifiers/prettydiff.coffee
+++ src/beautifiers/prettydiff.coffee
@@ -63,9 +63,9 @@
           lang = "ejs"
         when "ERB"
           lang = "html_ruby"
-        when "Handlebars", "Mustache", "Spacebars"
+        when "Handlebars", "Mustache", "Spacebars", "Swig"
           lang = "handlebars"
-        when "SGML", "Swig"
+        when "SGML"
           lang = "markup"
         when "XML", "Visualforce", "SVG"
           lang = "xml"


Reference test: use test repo

Reference test: use test repo

--- test/test.reference.coffee
+++ test/test.reference.coffee
@@ -5,7 +5,7 @@
 utils = require "./utils"
 fixtures = require "./fixtures"

-{secondCommit} = fixtures.projectRepo
+secondCommit = fixtures.testRepo.secondCommit

 describe "Reference", ->
 	repo = null
@@ -14,7 +14,7 @@

 	describe "Using the project repo...", ->
 		it "can find the HEAD sym reference", (done) ->
-			gitteh.openRepository fixtures.projectRepo.path, (err, _repo) ->
+			gitteh.openRepository fixtures.testRepo.path, (err, _repo) ->
 				repo = _repo

 				# We'll need this later.
@@ -62,4 +62,4 @@
 			repo.createReference "refs/heads/testref", secondCommit.id, (err, _ref) ->
 				should.exist err
 				err.should.be.an.instanceof Error
-				done()+				done()


Terminal: when terminal closed session list is updated

--- client/Terminal/sessionstackview.coffee
+++ client/Terminal/sessionstackview.coffee
@@ -15,6 +15,7 @@
       tagName : "ul"

     delegate.on "WebTermConnected", @bound "updateSessions"
+    delegate.on "TerminalClosed",   @bound "updateSessions"

     @updateSessions()


Terminal: initial state implemented

Terminal: initial state implemented

--- client/Terminal/terminalstarttab.coffee
+++ client/Terminal/terminalstarttab.coffee
@@ -5,6 +5,11 @@
     super

     @vmWrapper = new KDCustomHTMLView tagName : 'ul'
+
+
+  viewAppended:->
+
+    super

     @fetchVMs()

@@ -21,6 +26,10 @@
       vms.sort (a,b)-> a.hostnameAlias > b.hostnameAlias

       @listVMs vms
+
+      for own alias, kite of vmController.kites
+        if kite.recentState
+          @vmWrapper[alias].handleVMInfo kite.recentState

     vmController.on 'vm.start.progress', (alias, update) => @vmWrapper[alias].handleVMStart update
     vmController.on 'vm.stop.progress',  (alias, update) => @vmWrapper[alias].handleVMStop update


Accepting --no-wrap, with a warning

Accepting --no-wrap, with a warning

--- command.coffee
+++ command.coffee
@@ -42,6 +42,13 @@
   ['-v', '--version',         'display CoffeeScript version']
   ['-h', '--help',            'display this help message']
 ]
+
+# Switches that are still supported, but will cause a warning message.
+DEPRECATED_SWITCHES = [
+  ['--no-wrap',               'compile without the top-level function wrapper']
+]
+
+ALL_SWITCHES = SWITCHES.concat DEPRECATED_SWITCHES

 # Top-level objects shared by all the functions.
 opts         = {}
@@ -173,19 +180,22 @@
 # Use the [OptionParser module](optparse.html) to extract all options from
 # `process.argv` that are specified in `SWITCHES`.
 parseOptions = ->
-  optionParser  = new optparse.OptionParser SWITCHES, BANNER
+  optionParser  = new optparse.OptionParser ALL_SWITCHES, BANNER
   o = opts      = optionParser.parse process.argv.slice 2
   o.compile     or=  !!o.output
   o.run         = not (o.compile or o.print or o.lint)
   o.print       = !!  (o.print or (o.eval or o.stdio and o.compile))
   sources       = o.arguments
+  if opts['no-wrap']
+    console.log '--no-wrap is deprecated; please use --bare instead.\n'

 # The compile-time options to pass to the CoffeeScript compiler.
-compileOptions = (fileName) -> {fileName, bare: opts.bare}
-
-# Print the `--help` usage message and exit.
+compileOptions = (fileName) -> {fileName, bare: opts.bare or opts['no-wrap']}
+
+# Print the `--help` usage message and exit. Deprecated switches are not
+# shown.
 usage = ->
-  console.log optionParser.help()
+  console.log (new optparse.OptionParser SWITCHES).help()
   process.exit 0

 # Print the `--version` message and exit.


Removing rewatchTimer (see discussion at #1964)

--- command.coffee
+++ command.coffee
@@ -174,19 +174,16 @@

   prevStats = null
   compileTimeout = null
-  rewatchTimeout = null

   watchErr = (e) ->
     if e.code is 'ENOENT'
       return if sources.indexOf(source) is -1
-      clearTimeout rewatchTimeout
-      rewatchTimeout = wait 25, ->
-        try
-          rewatch()
-          compile()
-        catch e
-          removeSource source, base, yes
-          compileJoin()
+      try
+        rewatch()
+        compile()
+      catch e
+        removeSource source, base, yes
+        compileJoin()
     else throw e

   rewatch = ->


Fixing typo

Fixing typo

--- source/core/class/atom.coffee
+++ source/core/class/atom.coffee
@@ -13,7 +13,7 @@
   @include Atoms.Core.EventEmitter
   @include Atoms.Core.Output

-  @events = do @evts...
+  @events = (@evts...) -> @

   constructor: (@attributes) ->
     super


add activation

add activation

--- source/javascripts/all.coffee
+++ source/javascripts/all.coffee
@@ -24,7 +24,7 @@
       wall : new shapeConfig(1, 1, 0, 0.8)
       floor : new shapeConfig(1, 1, 0, 0.8)
       ceil : new shapeConfig(1, 1, 0, 0.8)
-      shoot: new shapeConfig(0.5, 1, 0, 0.8)
+      shoot: new shapeConfig(0.5, 8, 0, 0.8)
       shape: [
         [
           new shapeConfig(1, 1, 0.5, 0.8)
@@ -75,7 +75,7 @@
           # circle
           fixture.shape = new b2CircleShape
           fixture.shape.SetRadius(config.size)
-
+      #fixture.filter.maskBits = 2
       body = new b2BodyDef
       body.angle = angle
       body.type = b2Body.b2_kinematicBody
@@ -157,6 +157,17 @@
         b.SetPosition(new b2Vec2(pos.x, pos.y + 0.01)) # should be customizable
       b = b.GetNext()

+    c = iris.field.world.GetContactList()
+    while(c)
+      b1 = c.GetFixtureA().GetBody()
+      b2 = c.GetFixtureB().GetBody()
+      if c.IsTouching()
+        if b1.GetType() == 1
+          b1.SetType(2)
+        if b2.GetType() == 1
+          b2.SetType(2)
+      c = c.GetNext()
+
     @field.world.DrawDebugData()
     # grid
     @s.lineWidth = 1


Removes some whitespace

Removes some whitespace

--- source/javascripts/all.coffee
+++ source/javascripts/all.coffee
@@ -11,5 +11,3 @@
       return
     return
   return
-
-


comment all this out for now

comment all this out for now

--- source/javascripts/all.coffee
+++ source/javascripts/all.coffee
@@ -1,175 +1,175 @@
 #= require master-map
 render = ->
-  width       = $('#master-map').width()
-  height      = 1200
-  projection  = d3.geo.albersUsa().scale(1).translate [ 0, 0 ]
-  path        = d3.geo.path().projection(projection)
-  fill        = d3.scale.log().clamp(true).range ['#111', '#00ff00']
-  starCount   = 'total'
-  centered    = null
-  geometries  = null
-  colors      = ['#a634f4', '#f1f42f', '#bcf020', '#eeb016', '#ec180c']
+#   width       = $('#master-map').width()
+#   height      = 1200
+#   projection  = d3.geo.albersUsa().scale(1).translate [ 0, 0 ]
+#   path        = d3.geo.path().projection(projection)
+#   fill        = d3.scale.log().clamp(true).range ['#111', '#00ff00']
+#   starCount   = 'total'
+#   centered    = null
+#   geometries  = null
+#   colors      = ['#a634f4', '#f1f42f', '#bcf020', '#eeb016', '#ec180c']

-  map = d3.select('#master-map').append('svg')
-    .attr('width', width)
-    .attr('height', height)
+#   map = d3.select('#master-map').append('svg')
+#     .attr('width', width)
+#     .attr('height', height)

-  tip = d3.tip().attr('class', 'd3-tip').offset([-10, 0]).html (d) ->
-    if d.properties?
-      p = d.properties
-      name = if /county/i.test(p.name) then p.name else "#{p.name} County"
-      "
#{name}

#{p[starCount] || 0} athletes.
"
-    else if d.weight?
-      "
#{d.stars} ☆ #{d.name} - #{d.year}

#{d.weight}lb #{d.position} from #{d.school} in #{d.location}"
-    else
-      "
#{d.team}

#{d.stadium} in #{d.city}, #{d.state}
"
+#   tip = d3.tip().attr('class', 'd3-tip').offset([-10, 0]).html (d) ->
+#     if d.properties?
+#       p = d.properties
+#       name = if /county/i.test(p.name) then p.name else "#{p.name} County"
+#       "
#{name}

#{p[starCount] || 0} athletes.
"
+#     else if d.weight?
+#       "
#{d.stars} ☆ #{d.name} - #{d.year}

#{d.weight}lb #{d.position} from #{d.school} in #{d.location}"
+#     else
+#       "
#{d.team}

#{d.stadium} in #{d.city}, #{d.state}
"

-  map.call(tip)
+#   map.call(tip)

-  onCountyClick = (d) ->
-    if d && centered != d
-      [x,y] = path.centroid(d)
-      centered = d
-      k = 4
-    else
-      x = width / 2
-      y = height / 2
-      centered = null
-      k = 1
+#   onCountyClick = (d) ->
+#     if d && centered != d
+#       [x,y] = path.centroid(d)
+#       centered = d
+#       k = 4
+#     else
+#       x = width / 2
+#       y = height / 2
+#       centered = null
+#       k = 1

-    geometries.selectAll('path')
-      .classed('active', centered && ((d) -> d == centered))
+#     geometries.selectAll('path')
+#       .classed('active', centered && ((d) -> d == centered))

-    translate = " translate(#{width / 2},#{height / 2})
-        scale(#{k})
-        translate({#{-x},#{-y})"
+#     translate = " translate(#{width / 2},#{height / 2})
+#         scale(#{k})
+#         translate({#{-x},#{-y})"

-    geometries.transition()
-      .duration(750)
-      .attr('transform', "
-        translate(#{width / 2},#{height / 2})
-        scale(#{k})
-        translate(#{-x},#{-y})")
-      .style('stroke-width', "#{1.5 / k}px")
+#     geometries.transition()
+#       .duration(750)
+#       .attr('transform', "
+#         translate(#{width / 2},#{height / 2})
+#         scale(#{k})
+#         translate(#{-x},#{-y})")
+#       .style('stroke-width', "#{1.5 / k}px")

-  $.when($.ajax('/data/counties.json'),
-         $.ajax('/data/schools.csv'),
-         $.ajax('/data/recruits.csv')).then (r1, r2, r3) ->
+#   $.when($.ajax('/data/counties.json'),
+#          $.ajax('/data/schools.csv'),
+#          $.ajax('/data/recruits.csv')).then (r1, r2, r3) ->

-    usa      = r1[0]
-    schools = d3.csv.parse(r2[0])
-    recruits = d3.csv.parse(r3[0])
+#     usa      = r1[0]
+#     schools = d3.csv.parse(r2[0])
+#     recruits = d3.csv.parse(r3[0])

-    # Convert to GeoJSON
-    states   = topojson.mesh usa, usa.objects.states, (a, b) -> a.id != b.id
-    counties = topojson.feature usa, usa.objects.counties
-    nation   = topojson.mesh usa, usa.objects.nation
+#     # Convert to GeoJSON
+#     states   = topojson.mesh usa, usa.objects.states, (a, b) -> a.id != b.id
+#     counties = topojson.feature usa, usa.objects.counties
+#     nation   = topojson.mesh usa, usa.objects.nation

-    fill.domain [0.1, d3.max(counties.features, (d) -> d.properties[starCount])]
+#     fill.domain [0.1, d3.max(counties.features, (d) -> d.properties[starCount])]

-    # Auto scale map based on bounds
-    projection.scale(1).translate([0, 0])
-    b = path.bounds(nation)
-    s = 1 / Math.max((b[1][0] - b[0][0]) / width, (b[1][1] - b[0][1]) / height)
-    t = [(width - s * (b[1][0] + b[0][0])) / 2, (height - s * (b[1][1] + b[0][1])) / 2]
-    projection.scale(s).translate(t)
+#     # Auto scale map based on bounds
+#     projection.scale(1).translate([0, 0])
+#     b = path.bounds(nation)
+#     s = 1 / Math.max((b[1][0] - b[0][0]) / width, (b[1][1] - b[0][1]) / height)
+#     t = [(width - s * (b[1][0] + b[0][0])) / 2, (height - s * (b[1][1] + b[0][1])) / 2]
+#     projection.scale(s).translate(t)

-    geometries = map.append('g')
+#     geometries = map.append('g')

-    # Add counties
-    geometries.append('g')
-      .attr('class', 'counties')
-    .selectAll('path.county')
-      .data(counties.features)
-    .enter().append('path')
-      .attr('class', 'county')
-      .style('fill', (d) -> fill(d.properties[starCount] || 0))
-      .attr('d', path)
-      .on('mouseover', tip.show)
-      .on('mouseout', tip.hide)
-      .on('click', onCountyClick)
-      .style('stroke', (d) ->
-        stars = d.properties[starCount] || 0
-        if stars > 0 then fill(stars) else '#333')
+#     # Add counties
+#     geometries.append('g')
+#       .attr('class', 'counties')
+#     .selectAll('path.county')
+#       .data(counties.features)
+#     .enter().append('path')
+#       .attr('class', 'county')
+#       .style('fill', (d) -> fill(d.properties[starCount] || 0))
+#       .attr('d', path)
+#       .on('mouseover', tip.show)
+#       .on('mouseout', tip.hide)
+#       .on('click', onCountyClick)
+#       .style('stroke', (d) ->
+#         stars = d.properties[starCount] || 0
+#         if stars > 0 then fill(stars) else '#333')

-    # Add states mesh
-    geometries.append('path')
-      .datum(states)
-      .attr('class', 'states')
-      .attr('d', path)
+#     # Add states mesh
+#     geometries.append('path')
+#       .datum(states)
+#       .attr('class', 'states')
+#       .attr('d', path)

-    # Add nation mesh
-    geometries.append('path')
-      .datum(nation)
-      .attr('class', 'nation')
-      .attr('d', path)
+#     # Add nation mesh
+#     geometries.append('path')
+#       .datum(nation)
+#       .attr('class', 'nation')
+#       .attr('d', path)

-    for recruit in recruits
-      recruit.coordinates = projection [recruit.lat, recruit.lon]
+#     for recruit in recruits
+#       recruit.coordinates = projection [recruit.lat, recruit.lon]

-    # recgroup = geometries.append('g')
-    #   .attr('class', 'recruits')
+#     # recgroup = geometries.append('g')
+#     #   .attr('class', 'recruits')

-    # recs = recgroup.selectAll('circle.recruit')
-    #   .data(recruits)
-    # .enter().append('circle')
-    #   .attr('cx', (d) -> d.position[0])
-    #   .attr('cy', (d) -> d.position[1])
-    #   .attr('r', 1)
-    #   .style('fill', '#ff00ff')
-    #   .style('fill-opacity', 0)
-    #   .attr('class', (d) -> d.school.toLowerCase().replace(' ', '-'))
+#     # recs = recgroup.selectAll('circle.recruit')
+#     #   .data(recruits)
+#     # .enter().append('circle')
+#     #   .attr('cx', (d) -> d.position[0])
+#     #   .attr('cy', (d) -> d.position[1])
+#     #   .attr('r', 1)
+#     #   .style('fill', '#ff00ff')
+#     #   .style('fill-opacity', 0)
+#     #   .attr('class', (d) -> d.school.toLowerCase().replace(' ', '-'))

-# svg.append("path")
-#     .datum({type: "LineString", coordinates: [[-77.05, 38.91], [116.35, 39.91]]})
-#     .attr("class", "arc")
-#     .attr("d", path)
+# # svg.append("path")
+# #     .datum({type: "LineString", coordinates: [[-77.05, 38.91], [116.35, 39.91]]})
+# #     .attr("class", "arc")
+# #     .attr("d", path)

-    for school in schools
-      school.position = projection [school.lat, school.lon]
+#     for school in schools
+#       school.position = projection [school.lat, school.lon]

-    geometries.selectAll('.schools')
-      .data(schools)
-    .enter().append('circle')
-      .attr('cx', (d) -> d.position[0])
-      .attr('cy', (d) -> d.position[1])
-      .attr('r', 4)
-      .attr('class', 'school')
-      .classed('gt', (d) -> d.name == 'Georgia Tech')
-      .on('mouseover', tip.show)
-      .on('mouseout', tip.hide)
-      .on('click', (d) ->
-        players = recruits.filter (r) -> r.institution in [d.team, d.alt]
-        features = players.map (p) ->
-          type: "LineString"
-          coordinates: [[d.lat, d.lon], [p.lat, p.lon]]
-          properties: p
+#     geometries.selectAll('.schools')
+#       .data(schools)
+#     .enter().append('circle')
+#       .attr('cx', (d) -> d.position[0])
+#       .attr('cy', (d) -> d.position[1])
+#       .attr('r', 4)
+#       .attr('class', 'school')
+#       .classed('gt', (d) -> d.name == 'Georgia Tech')
+#       .on('mouseover', tip.show)
+#       .on('mouseout', tip.hide)
+#       .on('click', (d) ->
+#         players = recruits.filter (r) -> r.institution in [d.team, d.alt]
+#         features = players.map (p) ->
+#           type: "LineString"
+#           coordinates: [[d.lat, d.lon], [p.lat, p.lon]]
+#           properties: p

-        connections = geometries.selectAll('.connection')
-          .data(features, (d) -> "#{d.properties.name}:#{d.properties.school}")
+#         connections = geometries.selectAll('.connection')
+#           .data(features, (d) -> "#{d.properties.name}:#{d.properties.school}")

-        connections.enter().append('path')
-          .attr('class', 'connection')
-          .attr('d', path)
-          .style('stroke', (d) -> colors[d.properties.stars - 1])
+#         connections.enter().append('path')
+#           .attr('class', 'connection')
+#           .attr('d', path)
+#           .style('stroke', (d) -> colors[d.properties.stars - 1])

-        connections.exit().remove()
+#         connections.exit().remove()

-        recs = geometries.selectAll('.recruit')
-          .data(players, (d) -> "#{d.name}:#{d.school}")
+#         recs = geometries.selectAll('.recruit')
+#           .data(players, (d) -> "#{d.name}:#{d.school}")

-        recs.enter().append('circle')
-          .attr('cx', (d) -> d.coordinates[0])
-          .attr('cy', (d) -> d.coordinates[1])
-          .attr('r', 2)
-          .style('fill', (d) -> console.log d.stars; colors[d.stars - 1])
-          .attr('class', 'recruit')
-          .on('mouseover', tip.show)
-          .on('mouseout', tip.hide)
+#         recs.enter().append('circle')
+#           .attr('cx', (d) -> d.coordinates[0])
+#           .attr('cy', (d) -> d.coordinates[1])
+#           .attr('r', 2)
+#           .style('fill', (d) -> console.log d.stars; colors[d.stars - 1])
+#           .attr('class', 'recruit')
+#           .on('mouseover', tip.show)
+#           .on('mouseout', tip.hide)

-        recs.exit().remove()
+#         recs.exit().remove()

-      )
+#       )

 $(render)


Sprockets 3 somehow wants to include the coffee script version and chokes

--- vendor/assets/components/Keypress/keypress.coffee
+++ vendor/assets/components/Keypress/keypress.coffee
@@ -1,921 +0,0 @@
-###
-Copyright 2014 David Mauro
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-
-Keypress is a robust keyboard input capturing Javascript utility
-focused on input for games.
-
-version 2.1.0
-###
-
-###
-Combo options available and their defaults:
-    keys            : []            - An array of the keys pressed together to activate combo.
-    count           : 0             - The number of times a counting combo has been pressed. Reset on release.
-    is_unordered    : false         - Unless this is set to true, the keys can be pressed down in any order.
-    is_counting     : false         - Makes this a counting combo (see documentation).
-    is_exclusive    : false         - This combo will replace other exclusive combos when true.
-    is_solitary     : false         - This combo will only fire if ONLY it's keys are pressed down.
-    is_sequence     : false         - Rather than a key combo, this is an ordered key sequence.
-    prevent_default : false         - Prevent default behavior for all component key keypresses.
-    prevent_repeat  : false         - Prevent the combo from repeating when keydown is held.
-    on_keydown      : null          - A function that is called when the combo is pressed.
-    on_keyup        : null          - A function that is called when the combo is released.
-    on_release      : null          - A function that is called when all keys in the combo are released.
-    this            : undefined     - Defines the scope for your callback functions.
-###
-
-###########
-# Constants
-###########
-
-_factory_defaults =
-    is_unordered    : false
-    is_counting     : false
-    is_exclusive    : false
-    is_solitary     : false
-    prevent_default : false
-    prevent_repeat  : false
-
-_modifier_keys = ["meta", "alt", "option", "ctrl", "shift", "cmd"]
-
-_metakey = "ctrl"
-
-###########################
-# Public object and Classes
-###########################
-
-keypress = {}
-
-keypress.debug = false
-
-class Combo
-    constructor: (dictionary) ->
-        # Copy over any non-false values
-        for own property, value of dictionary
-            @[property] = value if value != false
-
-        # Standard Defaults
-        @keys = @keys or []
-        @count = @count or 0
-
-    allows_key_repeat: ->
-        # Combos with keydown functions should be able to rapid fire
-        # when holding down the key for an extended period
-        return not @prevent_repeat and typeof @on_keydown is "function"
-
-    reset: ->
-        @count = 0
-        @keyup_fired = null
-
-class keypress.Listener
-    constructor:(element, defaults) ->
-        # Public properties
-        @should_suppress_event_defaults = false
-        @should_force_event_defaults = false
-        @sequence_delay = 800
-
-        # Private properties
-        @_registered_combos = []
-        @_keys_down = []
-        @_active_combos = []
-        @_sequence = []
-        @_sequence_timer = null
-        @_prevent_capture = false
-        @_defaults = defaults or {}
-        for own property, value of _factory_defaults
-            @_defaults[property] = @_defaults[property] or value
-
-        # Attach handlers to element
-        @element = element or document.body
-
-        attach_handler = (target, event, handler) ->
-            if target.addEventListener
-                target.addEventListener event, handler
-            else if target.attachEvent
-                target.attachEvent "on#{event}", handler
-
-            return handler
-
-        @keydown_event = attach_handler @element, "keydown", (e) =>
-            e = e or window.event
-            @_receive_input e, true
-            @_bug_catcher e
-
-        @keyup_event = attach_handler @element, "keyup", (e) =>
-            e = e or window.event
-            @_receive_input e, false
-
-        @blur_event = attach_handler window, "blur", =>
-            # Assume all keys are released when we can't catch key events
-            # This prevents alt+tab conflicts
-            for key in @_keys_down
-                @_key_up key, {}
-            @_keys_down = []
-
-    destroy: () ->
-        remove_handler = (target, event, handler) ->
-            if target.removeEventListener?
-                target.removeEventListener event, handler
-            else if target.removeEvent?
-                target.removeEvent "on#{event}", handler
-
-        remove_handler @element, "keydown", @keydown_event
-        remove_handler @element, "keyup", @keyup_event
-        remove_handler window, "blur", @blur_event
-
-    # Helper Methods
-
-    _bug_catcher: (e) ->
-        # This seems to be Mac specific weirdness, so we'll target "cmd" as metaKey
-        # Force a keyup for non-modifier keys when command is held because they don't fire
-        if _metakey is "cmd" and "cmd" in @_keys_down and _convert_key_to_readable(e.keyCode) not in ["cmd", "shift", "alt", "caps", "tab"]
-            @_receive_input e, false
-        # Note: we're currently ignoring the fact that this doesn't catch the bug that a keyup
-        # will not fire if you keydown a combo, then press and hold cmd, then keyup the combo.
-        # Perhaps we should fire keyup on all active combos when we press cmd?
-
-    _cmd_bug_check: (combo_keys) ->
-        # We don't want to allow combos to activate if the cmd key
-        # is pressed, but cmd isn't in them. This is so they don't
-        # accidentally rapid fire due to our hack-around for the cmd
-        # key bug and having to fake keyups.
-        if _metakey is "cmd" and "cmd" in @_keys_down and "cmd" not in combo_keys
-            return false
-        return true
-
-    _prevent_default: (e, should_prevent) ->
-        # If we've pressed a combo, or if we are working towards
-        # one, we should prevent the default keydown event.
-        if (should_prevent or @should_suppress_event_defaults) and not @should_force_event_defaults
-            if e.preventDefault then e.preventDefault() else e.returnValue = false
-            e.stopPropagation() if e.stopPropagation
-
-    # Tracking Combos
-
-    _get_active_combos: (key) ->
-        # Based on the keys_down and the key just pressed or released
-        # (which should not be in keys_down), we determine if any
-        # combo in registered_combos could be considered active.
-        # This will return an array of active combos
-
-        active_combos = []
-
-        # First check that every key in keys_down maps to a combo
-        keys_down = _filter_array @_keys_down, (down_key) ->
-            down_key isnt key
-        keys_down.push key
-
-        # Get perfect matches
-        @_match_combo_arrays keys_down, (match) =>
-            active_combos.push(match) if @_cmd_bug_check match.keys
-
-        # Get fuzzy matches
-        @_fuzzy_match_combo_arrays keys_down, (match) =>
-            return if match in active_combos
-            active_combos.push(match) unless match.is_solitary or not @_cmd_bug_check match.keys
-
-        return active_combos
-
-    _get_potential_combos: (key) ->
-        # Check if we are working towards pressing a combo.
-        # Used for preventing default on keys that might match
-        # to a combo in the future.
-        potentials = []
-        for combo in @_registered_combos
-            continue if combo.is_sequence
-            potentials.push(combo) if key in combo.keys and @_cmd_bug_check combo.keys
-        return potentials
-
-    _add_to_active_combos: (combo) ->
-        should_replace = false
-        should_prepend = true
-        already_replaced = false
-        # An active combo is any combo which the user has already entered.
-        # We use this to track when a user has released the last key of a
-        # combo for on_release, and to keep combos from 'overlapping'.
-        if combo in @_active_combos
-            return true
-        else if @_active_combos.length
-            # We have to check if we're replacing another active combo
-            # So compare the combo.keys to all active combos' keys.
-            for i in [0...@_active_combos.length]
-                active_combo = @_active_combos[i]
-                continue unless active_combo and active_combo.is_exclusive and combo.is_exclusive
-                active_keys = active_combo.keys
-                unless should_replace
-                    for active_key in active_keys
-                        should_replace = true
-                        unless active_key in combo.keys
-                            should_replace = false
-                            break
-
-                if should_prepend and not should_replace
-                    for combo_key in combo.keys
-                        should_prepend = false
-                        unless combo_key in active_keys
-                            should_prepend = true
-                            break
-
-                if should_replace
-                    if already_replaced
-                        active_combo = @_active_combos.splice(i, 1)[0]
-                        active_combo.reset() if active_combo?
-                    else
-                        active_combo =  @_active_combos.splice(i, 1, combo)[0]
-                        active_combo.reset() if active_combo?
-                        already_replaced = true
-                    should_prepend = false
-        if should_prepend
-            @_active_combos.unshift combo
-
-        return should_replace or should_prepend
-
-    _remove_from_active_combos: (combo) ->
-        for i in [0...@_active_combos.length]
-            active_combo = @_active_combos[i]
-            if active_combo is combo
-                combo = @_active_combos.splice(i, 1)[0]
-                combo.reset()
-                break
-        return
-
-    # Sequence Methods
-
-    _get_possible_sequences: ->
-        # Determine what if any sequences we're working towards.
-        # We will consider any which any part of the end of the sequence
-        # matches and return all of them.
-        matches = []
-        for combo in @_registered_combos
-            for j in [1..@_sequence.length]
-                sequence = @_sequence.slice -j
-                continue unless combo.is_sequence
-                unless "shift" in combo.keys
-                    sequence = _filter_array sequence, (key) ->
-                        return key isnt "shift"
-                    continue unless sequence.length
-                for i in [0...sequence.length]
-                    if combo.keys[i] is sequence[i]
-                        match = true
-                    else
-                        match = false
-                        break
-                matches.push(combo) if match
-        return matches
-
-    _add_key_to_sequence: (key, e) ->
-        @_sequence.push key
-        # Now check if they're working towards a sequence
-        sequence_combos = @_get_possible_sequences()
-        if sequence_combos.length
-            for combo in sequence_combos
-                @_prevent_default e, combo.prevent_default
-            # If we're working towards one, give them more time to keep going
-            clearTimeout(@_sequence_timer) if @_sequence_timer
-            if @sequence_delay > -1
-                @_sequence_timer = setTimeout ->
-                    @_sequence = []
-                , @sequence_delay
-        else
-            # If we're not working towards something, just clear it out
-            @_sequence = []
-        return
-
-    _get_sequence: (key) ->
-        # Compare _sequence to all combos
-        for combo in @_registered_combos
-            continue unless combo.is_sequence
-            for j in [1..@_sequence.length]
-                # As we are traversing backwards through the sequence keys,
-                # Take out any shift keys, unless shift is in the combo.
-                sequence = (_filter_array @_sequence, (seq_key) ->
-                    return true if "shift" in combo.keys
-                    return seq_key isnt "shift"
-                ).slice -j
-                continue unless combo.keys.length is sequence.length
-                for i in [0...sequence.length]
-                    seq_key = sequence[i]
-                    # Special case for shift. Ignore shift keys, unless the sequence explicitly uses them
-                    continue if seq_key is "shift" unless "shift" in combo.keys
-                    # Don't select this combo if we're pressing shift and shift isn't in it
-                    continue if key is "shift" and "shift" not in combo.keys
-                    if combo.keys[i] is seq_key
-                        match = true
-                    else
-                        match = false
-                        break
-            return combo if match
-        return false
-
-    # Catching Combos
-
-    _receive_input: (e, is_keydown) ->
-        # If we're not capturing input, we should
-        # clear out _keys_down for good measure
-        if @_prevent_capture
-            @_keys_down = [] if @_keys_down.length
-            return
-        key = _convert_key_to_readable e.keyCode
-        # Catch tabbing out of a non-capturing state
-        if !is_keydown and !@_keys_down.length and key in ["alt", _metakey]
-            return
-        return unless key
-        if is_keydown
-            @_key_down key, e
-        else
-            @_key_up key, e
-
-    _fire: (event, combo, key_event, is_autorepeat) ->
-        # Only fire this event if the function is defined
-        if typeof combo["on_" + event] is "function"
-            @_prevent_default key_event, (combo["on_" + event].call(combo.this, key_event, combo.count, is_autorepeat) isnt true)
-        # We need to mark that keyup has already happened
-        if event is "release"
-            combo.count = 0
-        if event is "keyup"
-            combo.keyup_fired = true
-
-    _match_combo_arrays: (potential_match, match_handler) ->
-        # This will return all combos that match
-        for source_combo in @_registered_combos
-            if (not source_combo.is_unordered and _compare_arrays_sorted(potential_match, source_combo.keys)) or (source_combo.is_unordered and _compare_arrays(potential_match, source_combo.keys))
-                match_handler source_combo
-        return
-
-    _fuzzy_match_combo_arrays: (potential_match, match_handler) ->
-        # This will return combos that match even if other keys are pressed
-        for source_combo in @_registered_combos
-            if (not source_combo.is_unordered and _is_array_in_array_sorted(source_combo.keys, potential_match)) or (source_combo.is_unordered and _is_array_in_array(source_combo.keys, potential_match))
-                match_handler source_combo
-        return
-
-    _keys_remain: (combo) ->
-        for key in combo.keys
-            if key in @_keys_down
-                keys_remain = true
-                break
-        return keys_remain
-
-    _key_down: (key, e) ->
-        # Check if we're holding shift
-        shifted_key = _convert_to_shifted_key key, e
-        key = shifted_key if shifted_key
-
-        # Add the key to sequences
-        @_add_key_to_sequence key, e
-        sequence_combo = @_get_sequence key
-        @_fire("keydown", sequence_combo, e) if sequence_combo
-
-        # We might have modifier keys down when coming back to
-        # this window and they might not be in _keys_down, so
-        # we're doing a check to make sure we put it back in.
-        # This only works for explicit modifier keys.
-        for mod, event_mod of _modifier_event_mapping
-            continue unless e[event_mod]
-            continue if mod is key or mod in @_keys_down
-            @_keys_down.push mod
-        # Alternatively, we might not have modifier keys down
-        # that we think are, so we should catch those too
-        for mod, event_mod of _modifier_event_mapping
-            continue if mod is key
-            if mod in @_keys_down and not e[event_mod]
-                # The Windows key will think it is the cmd key, but won't trigger the event mod
-                continue if mod is "cmd" and _metakey isnt "cmd"
-                for i in [0...@_keys_down.length]
-                    @_keys_down.splice(i, 1) if @_keys_down[i] is mod
-
-        # Find which combos we have pressed or might be working towards, and prevent default
-        combos = @_get_active_combos key
-        potential_combos = @_get_potential_combos key
-        for combo in combos
-            @_handle_combo_down combo, potential_combos, key, e
-        if potential_combos.length
-            for potential in potential_combos
-                @_prevent_default e, potential.prevent_default
-
-        if key not in @_keys_down
-            @_keys_down.push key
-        return
-
-    _handle_combo_down: (combo, potential_combos, key, e) ->
-        # Make sure we're not trying to fire for a combo that already fired
-        return false unless key in combo.keys
-
-        @_prevent_default e, (combo and combo.prevent_default)
-
-        is_autorepeat = false
-        # If we've already pressed this key, check that we want to fire
-        # again, otherwise just add it to the keys_down list.
-        if key in @_keys_down
-            is_autorepeat = true
-            return false unless combo.allows_key_repeat()
-
-        # Now we add this combo or replace it in _active_combos
-        result = @_add_to_active_combos combo, key
-
-        # We reset the keyup_fired property because you should be
-        # able to fire that again, if you've pressed the key down again
-        combo.keyup_fired = false
-
-        # Now we fire the keydown event unless there is a larger exclusive potential combo
-        is_other_exclusive = false
-        if combo.is_exclusive
-            for potential_combo in potential_combos
-                if potential_combo.is_exclusive and potential_combo.keys.length > combo.keys.length
-                    is_other_exclusive = true
-                    break
-
-        unless is_other_exclusive
-            if combo.is_counting and typeof combo.on_keydown is "function"
-                combo.count += 1
-
-            # Only fire keydown if we added it
-            if result
-                @_fire "keydown", combo, e, is_autorepeat
-
-    _key_up: (key, e) ->
-        # Check if we're holding shift
-        unshifted_key = key
-        shifted_key = _convert_to_shifted_key key, e
-        key = shifted_key if shifted_key
-        shifted_key = _keycode_shifted_keys[unshifted_key]
-        # We have to make sure the key matches to what we had in _keys_down
-        if e.shiftKey
-            key = unshifted_key unless shifted_key and shifted_key in @_keys_down
-        else
-            key = shifted_key unless unshifted_key and unshifted_key in @_keys_down
-
-        # Check if we have a keyup firing
-        sequence_combo = @_get_sequence key
-        @_fire("keyup", sequence_combo, e) if sequence_combo
-
-        # Remove from the list
-        return false unless key in @_keys_down
-        for i in [0...@_keys_down.length]
-            if @_keys_down[i] in [key, shifted_key, unshifted_key]
-                @_keys_down.splice i, 1
-                break
-
-        # Store this for later cleanup
-        active_combos_length = @_active_combos.length
-
-        # When releasing we should only check if we
-        # match from _active_combos so that we don't
-        # accidentally fire for a combo that was a
-        # smaller part of the one we actually wanted.
-        combos = []
-        for active_combo in @_active_combos
-            if key in active_combo.keys
-                combos.push active_combo
-        for combo in combos
-            @_handle_combo_up combo, e, key
-
-        # We also need to check other combos that might still be in active_combos
-        # and needs to be removed from it.
-        if active_combos_length > 1
-            for active_combo in @_active_combos
-                continue if active_combo is undefined or active_combo in combos
-                unless @_keys_remain active_combo
-                    @_remove_from_active_combos active_combo
-        return
-
-    _handle_combo_up: (combo, e, key) ->
-        @_prevent_default e, (combo and combo.prevent_default)
-
-        # Check if any keys from this combo are still being held.
-        keys_remaining = @_keys_remain combo
-
-        # Any unactivated combos will fire
-        if !combo.keyup_fired
-            # And we should not fire it if it is a solitary combo and something else is pressed
-            keys_down = @_keys_down.slice()
-            keys_down.push key
-            if not combo.is_solitary or _compare_arrays keys_down, combo.keys
-                @_fire "keyup", combo, e
-                # Dont' add to the count unless we only have a keyup callback
-                if combo.is_counting and typeof combo.on_keyup is "function" and typeof combo.on_keydown isnt "function"
-                    combo.count += 1
-
-        # If this was the last key released of the combo, clean up.
-        unless keys_remaining
-            @_fire "release", combo, e
-            @_remove_from_active_combos combo
-        return
-
-    # Public Registration Methods
-
-    simple_combo: (keys, callback) ->
-        # Shortcut for simple combos.
-        @register_combo(
-            keys            : keys
-            on_keydown      : callback
-        )
-
-    counting_combo: (keys, count_callback) ->
-        # Shortcut for counting combos
-        @register_combo(
-            keys            : keys
-            is_counting     : true
-            is_unordered    : false
-            on_keydown      : count_callback
-        )
-
-    sequence_combo: (keys, callback) ->
-        @register_combo(
-            keys            : keys
-            on_keydown      : callback
-            is_sequence     : true
-        )
-
-    register_combo: (combo_dictionary) ->
-        # Allow a space dilineated string instead of array
-        if typeof combo_dictionary["keys"] is "string"
-            combo_dictionary["keys"] = combo_dictionary["keys"].split " "
-        for own property, value of @_defaults
-            if combo_dictionary[property] is undefined
-                combo_dictionary[property] = value
-        combo = new Combo combo_dictionary
-
-        if _validate_combo combo
-            @_registered_combos.push combo
-            return combo
-
-    register_many: (combo_array) ->
-        # Will return an array of the combos actually registered
-        @register_combo(combo) for combo in combo_array
-
-    unregister_combo: (keys_or_combo) ->
-        return false unless keys_or_combo
-
-        unregister_combo = (combo) =>
-            for i in [0...@_registered_combos.length]
-                if combo is @_registered_combos[i]
-                    @_registered_combos.splice i, 1
-                    break
-
-        if keys_or_combo instanceof Combo
-            unregister_combo keys_or_combo
-        else
-            if typeof keys_or_combo is "string"
-                keys_or_combo = keys_or_combo.split " "
-            for combo in @_registered_combos
-                continue unless combo?
-                if (combo.is_unordered and _compare_arrays(keys_or_combo, combo.keys)) or (not combo.is_unordered and _compare_arrays_sorted(keys_or_combo, combo.keys))
-                    unregister_combo combo
-
-    unregister_many: (combo_array) ->
-        for combo in combo_array
-            @unregister_combo combo
-
-    # Other public methods
-
-    get_registered_combos: ->
-        return @_registered_combos
-
-    reset: ->
-        @_registered_combos = []
-
-    listen: ->
-        @_prevent_capture = false
-
-    stop_listening: ->
-        @_prevent_capture = true
-
-    get_meta_key: ->
-        # Helpful for debugging purposes
-        return _metakey
-
-##################
-# Helper Functions
-##################
-
-_decide_meta_key = ->
-    # If the useragent reports Mac OS X, assume cmd is metakey
-    if navigator.userAgent.indexOf("Mac OS X") != -1
-        _metakey = "cmd"
-    return
-
-_change_keycodes_by_browser = ->
-    if navigator.userAgent.indexOf("Opera") != -1
-        # Opera does weird stuff with command and control keys, let's fix that.
-        # Note: Opera cannot override meta + s browser default of save page.
-        # Note: Opera does some really strange stuff when cmd+alt+shift
-        # are held and a non-modifier key is pressed.
-        _keycode_dictionary["17"] = "cmd"
-    return
-
-_convert_key_to_readable = (k) ->
-    return _keycode_dictionary[k]
-
-_filter_array = (array, callback) ->
-  if array.filter
-    return array.filter(callback)
-  else
-    # For browsers without Array.prototype.filter like IE<9:
-    return (element for element in array when callback(element))
-
-_compare_arrays = (a1, a2) ->
-    # This will ignore the ordering of the arrays
-    # and simply check if they have the same contents.
-    return false unless a1.length is a2.length
-    for item in a1
-        continue if item in a2
-        return false
-    return true
-
-_compare_arrays_sorted = (a1, a2) ->
-    return false unless a1.length is a2.length
-    for i in [0...a1.length]
-        return false unless a1[i] is a2[i]
-    return true
-
-_is_array_in_array = (a1, a2) ->
-    # Returns true only if all of the contents of
-    # a1 are included in a2
-    for item in a1
-        return false unless item in a2
-    return true
-
-_index_of_in_array = Array.prototype.indexOf or (a, item) ->
-    for i in [0..a.length]
-        return i if a[i] is item
-    return -1
-
-_is_array_in_array_sorted = (a1, a2) ->
-    # Return true only if all of the contents of
-    # a1 are include in a2 and they appear in the
-    # same order in both.
-    prev = 0
-    for item in a1
-        index = _index_of_in_array.call a2, item
-        if index >= prev
-            prev = index
-        else
-            return false
-    return true
-
-_log_error = () ->
-    console.log arguments... if keypress.debug
-
-_key_is_valid = (key) ->
-    valid = false
-    for _, valid_key of _keycode_dictionary
-        if key is valid_key
-            valid = true
-            break
-    unless valid
-        for _, valid_key of _keycode_shifted_keys
-            if key is valid_key
-                valid = true
-                break
-    return valid
-
-_validate_combo = (combo) ->
-    validated = true
-
-    # Warn for lack of keys
-    unless combo.keys.length
-        _log_error "You're trying to bind a combo with no keys:", combo
-
-    # Convert "meta" to either "ctrl" or "cmd"
-    # Don't explicity use the command key, it breaks
-    # because it is the windows key in Windows, and
-    # cannot be hijacked.
-    for i in [0...combo.keys.length]
-        key = combo.keys[i]
-        # Check the name and replace if needed
-        alt_name = _keycode_alternate_names[key]
-        key = combo.keys[i] = alt_name if alt_name
-        if key is "meta"
-            combo.keys.splice i, 1, _metakey
-        if key is "cmd"
-            _log_error "Warning: use the \"meta\" key rather than \"cmd\" for Windows compatibility"
-
-    # Check that all keys in the combo are valid
-    for key in combo.keys
-        unless _key_is_valid key
-            _log_error "Do not recognize the key \"#{key}\""
-            validated = false
-
-    # We can only allow a single non-modifier key
-    # in combos that include the command key (this
-    # includes 'meta') because of the keyup bug.
-    if "meta" in combo.keys or "cmd" in combo.keys
-        non_modifier_keys = combo.keys.slice()
-        for mod_key in _modifier_keys
-            if (i = _index_of_in_array.call(non_modifier_keys, mod_key)) > -1
-                non_modifier_keys.splice(i, 1)
-        if non_modifier_keys.length > 1
-            _log_error "META and CMD key combos cannot have more than 1 non-modifier keys", combo, non_modifier_keys
-            validated = false
-
-    # Tell the user if they are trying to use any
-    # combo properties that don't actually exist,
-    # but allow the combo
-    for property, value of combo
-        if _factory_defaults[property] is "undefined"
-            _log_error "The property #{property} is not a valid combo property. Your combo has still been registered."
-
-    return validated
-
-_convert_to_shifted_key = (key, e) ->
-    return false unless e.shiftKey
-    k = _keycode_shifted_keys[key]
-    return k if k?
-    return false
-
-##########################
-# Key Mapping Dictionaries
-##########################
-
-_modifier_event_mapping =
-    "cmd"   : "metaKey"
-    "ctrl"  : "ctrlKey"
-    "shift" : "shiftKey"
-    "alt"   : "altKey"
-
-_keycode_alternate_names =
-    "escape"        : "esc"
-    "control"       : "ctrl"
-    "command"       : "cmd"
-    "break"         : "pause"
-    "windows"       : "cmd"
-    "option"        : "alt"
-    "caps_lock"     : "caps"
-    "apostrophe"    : "\'"
-    "semicolon"     : ";"
-    "tilde"         : "~"
-    "accent"        : "`"
-    "scroll_lock"   : "scroll"
-    "num_lock"      : "num"
-
-_keycode_shifted_keys =
-    "/"     : "?"
-    "."     : ">"
-    ","     : "<"
-    "\'"    : "\""
-    ";"     : ":"
-    "["     : "{"
-    "]"     : "}"
-    "\\"    : "|"
-    "`"     : "~"
-    "="     : "+"
-    "-"     : "_"
-    "1"     : "!"
-    "2"     : "@"
-    "3"     : "#"
-    "4"     : "$"
-    "5"     : "%"
-    "6"     : "^"
-    "7"     : "&"
-    "8"     : "*"
-    "9"     : "("
-    "0"     : ")"
-
-_keycode_dictionary =
-    0   : "\\"          # Firefox reports this keyCode when shift is held
-    8   : "backspace"
-    9   : "tab"
-    12  : "num"
-    13  : "enter"
-    16  : "shift"
-    17  : "ctrl"
-    18  : "alt"
-    19  : "pause"
-    20  : "caps"
-    27  : "esc"
-    32  : "space"
-    33  : "pageup"
-    34  : "pagedown"
-    35  : "end"
-    36  : "home"
-    37  : "left"
-    38  : "up"
-    39  : "right"
-    40  : "down"
-    44  : "print"
-    45  : "insert"
-    46  : "delete"
-    48  : "0"
-    49  : "1"
-    50  : "2"
-    51  : "3"
-    52  : "4"
-    53  : "5"
-    54  : "6"
-    55  : "7"
-    56  : "8"
-    57  : "9"
-    65  : "a"
-    66  : "b"
-    67  : "c"
-    68  : "d"
-    69  : "e"
-    70  : "f"
-    71  : "g"
-    72  : "h"
-    73  : "i"
-    74  : "j"
-    75  : "k"
-    76  : "l"
-    77  : "m"
-    78  : "n"
-    79  : "o"
-    80  : "p"
-    81  : "q"
-    82  : "r"
-    83  : "s"
-    84  : "t"
-    85  : "u"
-    86  : "v"
-    87  : "w"
-    88  : "x"
-    89  : "y"
-    90  : "z"
-    91  : "cmd"
-    92  : "cmd"
-    93  : "cmd"
-    96  : "num_0"
-    97  : "num_1"
-    98  : "num_2"
-    99  : "num_3"
-    100 : "num_4"
-    101 : "num_5"
-    102 : "num_6"
-    103 : "num_7"
-    104 : "num_8"
-    105 : "num_9"
-    106 : "num_multiply"
-    107 : "num_add"
-    108 : "num_enter"
-    109 : "num_subtract"
-    110 : "num_decimal"
-    111 : "num_divide"
-    112 : "f1"
-    113 : "f2"
-    114 : "f3"
-    115 : "f4"
-    116 : "f5"
-    117 : "f6"
-    118 : "f7"
-    119 : "f8"
-    120 : "f9"
-    121 : "f10"
-    122 : "f11"
-    123 : "f12"
-    124 : "print"
-    144 : "num"
-    145 : "scroll"
-    186 : ";"
-    187 : "="
-    188 : ","
-    189 : "-"
-    190 : "."
-    191 : "/"
-    192 : "`"
-    219 : "["
-    220 : "\\"
-    221 : "]"
-    222 : "\'"
-    223 : "`"
-    224 : "cmd"
-    225 : "alt"
-    # Opera weirdness
-    57392   : "ctrl"
-    63289   : "num"
-    # Firefox weirdness
-    59 : ";"
-    61 : "-"
-    173 : "="
-
-# For testing only:
-keypress._keycode_dictionary = _keycode_dictionary
-keypress._is_array_in_array_sorted = _is_array_in_array_sorted
-
-############
-# Initialize
-############
-
-_decide_meta_key()
-_change_keycodes_by_browser()
-
-# Anonymous Module Definition
-if typeof define is "function" and define.amd
-    define [], ->
-        return keypress
-else if exports?
-    exports.keypress = keypress
-else
-    window.keypress = keypress


Backbone.offline 0.4.0

Backbone.offline 0.4.0

--- vendor/assets/javascripts/backbone_offline.coffee
+++ vendor/assets/javascripts/backbone_offline.coffee
@@ -5,7 +5,7 @@
 #    May be freely distributed according to MIT license.

 window.Offline =
-  VERSION: '0.3.0.beta'
+  VERSION: '0.4.0.alfa'

   # This is a method for CRUD operations with localStorage.
   # Delegates to 'Offline.Storage' and works as ‘Backbone.sync’ alternative
@@ -57,7 +57,7 @@

   # Name of storage and collection link are required params
   constructor: (@name, @collection, options = {}) ->
-    @support = this.isLocalStorageSupport()
+    @support = @isLocalStorageSupport()
     @allIds = new Offline.Index(@name, this)
     @destroyIds = new Offline.Index("#{@name}-destroy", this)
     @sync = new Offline.Sync(@collection, this)
@@ -104,62 +104,62 @@
   # Set a sync's attributes updated_at, dirty and add
   create: (model, options = {}) ->
     options.regenerateId = true
-    this.save(model, options)
+    @save(model, options)

   # Update a model into the set. Set a sync's attributes update_at and dirty.
   update: (model, options = {}) ->
-    this.save(model, options)
+    @save(model, options)

   # Delete a model from the storage
   destroy: (model, options = {}) ->
     @destroyIds.add(sid) unless options.local or (sid = model.get('sid')) is 'new'
-    this.remove(model)
+    @remove(model, options)

   find: (model, options = {}) ->
-    JSON.parse this.getItem("#{@name}-#{model.id}")
+    JSON.parse @getItem("#{@name}-#{model.id}")

   # Returns the array of all models currently in the storage.
   # And refreshes the storage into background
   findAll: (options = {}) ->
     unless options.local
-      if this.isEmpty() then @sync.full() else @sync.incremental()
-    JSON.parse(this.getItem("#{@name}-#{id}")) for id in @allIds.values
+      if @isEmpty() then @sync.full() else @sync.incremental()
+    JSON.parse(@getItem("#{@name}-#{id}")) for id in @allIds.values

   s4: ->
     (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1)

   guid: ->
-    this.s4() + this.s4() + '-' + this.s4() + '-' + this.s4() + '-' + this.s4() + '-' + this.s4() + this.s4() + this.s4()
+    @s4() + @s4() + '-' + @s4() + '-' + @s4() + '-' + @s4() + '-' + @s4() + @s4() + @s4()

   save: (item, options = {}) ->
-    item.set(sid: item.attributes?.sid || item.attributes?.id || 'new', id: this.guid()) if options.regenerateId
+    item.set(sid: item.attributes?.sid || item.attributes?.id || 'new', id: @guid()) if options.regenerateId
     item.set(updated_at: (new Date()).toJSON(), dirty: true) unless options.local

-    this.replaceKeyFields(item, 'local')
-    this.setItem "#{@name}-#{item.id}", JSON.stringify(item)
+    @replaceKeyFields(item, 'local')
+    @setItem "#{@name}-#{item.id}", JSON.stringify(item)
     @allIds.add(item.id)

     @sync.pushItem(item) if @autoPush and !options.local
-    return item
-
-  remove: (item) ->
-    this.removeItem "#{@name}-#{item.id}"
+
+    item
+
+  remove: (item, options = {}) ->
+    @removeItem "#{@name}-#{item.id}"
     @allIds.remove(item.id)

     sid = item.get('sid')
-    @sync.flushItem(sid) if @autoPush and sid isnt 'new'
-
-    return item
+    @sync.flushItem(sid) if @autoPush and sid isnt 'new' and !options.local
+
+    item

   isEmpty: ->
-    this.getItem(@name) is null
+    @getItem(@name) is null

   # Clears the current storage
   clear: ->
     keys = Object.keys(localStorage)
     collectionKeys = _.filter keys, (key) => (new RegExp @name).test(key)
-    this.removeItem(key) for key in collectionKeys
-    this.setItem(@name, '')
+    @removeItem(key) for key in collectionKeys
     record.reset() for record in [@allIds, @destroyIds]

   # Replaces local-keys to server-keys based on options.keys.
@@ -177,7 +177,7 @@
           else
             collection.get(replacedField)?.get('sid')
           item[field] = newValue unless _.isUndefined(newValue)
-    return item
+    item

 # Sync collection with a server. All server requests delegated to 'Backbone.sync'
 # It provides a backward-compability. If your application is working with 'Backbone.sync'
@@ -192,7 +192,7 @@

   ajax: (method, model, options) ->
     if Offline.onLine()
-      this.prepareOptions(options)
+      @prepareOptions(options)
       Backbone.ajaxSync(method, model, options)
     else
       @storage.setItem('offline', 'true')
@@ -201,7 +201,7 @@
   # 1. clear collection and store
   # 2. load new data
   full: (options = {}) ->
-    this.ajax 'read', @collection.items, success: (response, status, xhr) =>
+    @ajax 'read', @collection.items, success: (response, status, xhr) =>
       @storage.clear()
       @collection.items.reset([], silent: true)
       @collection.items.create(item, silent: true, local: true, regenerateId: true) for item in response
@@ -212,7 +212,7 @@
   # 1. pull() - request data from server
   # 2. push() - send modified data to server
   incremental: ->
-    this.pull success: => this.push()
+    @pull success: => @push()

   # Runs incremental sync when storage was offline
   # after current request therefore don't duplicate requests
@@ -222,7 +222,7 @@
       success = options.success
       options.success = (response, status, xhr) =>
         success(response, status, xhr)
-        this.incremental()
+        @incremental()

   # Requests data from the server and merges it with a collection.
   # It's useful when you want to refresh your collection and don't want to reload it completely.
@@ -231,17 +231,17 @@
   #
   # @storage.sync.pull()
   pull: (options = {}) ->
-    this.ajax 'read', @collection.items, success: (response, status, xhr) =>
+    @ajax 'read', @collection.items, success: (response, status, xhr) =>
       @collection.destroyDiff(response)
-      this.pullItem(item) for item in response
+      @pullItem(item) for item in response
       options.success() if options.success

   pullItem: (item) ->
     local = @collection.get(item.id)
     if local
-      this.updateItem(item, local)
+      @updateItem(item, local)
     else
-      this.createItem(item)
+      @createItem(item)

   createItem: (item) ->
     unless _.include(@storage.destroyIds.values, item.id.toString())
@@ -263,8 +263,8 @@
   # and sends every object to server using 'Backbone.sync' method
   # after that it sends deleted objects to the server
   push: ->
-    this.pushItem(item) for item in @collection.dirty()
-    this.flushItem(sid) for sid in @storage.destroyIds.values
+    @pushItem(item) for item in @collection.dirty()
+    @flushItem(sid) for sid in @storage.destroyIds.values

   pushItem: (item) ->
     @storage.replaceKeyFields(item, 'server')
@@ -272,7 +272,7 @@
     delete item.attributes.id
     [method, item.id] = if item.get('sid') is 'new' then ['create', null] else ['update', item.attributes.sid]

-    this.ajax method, item, success: (response, status, xhr) =>
+    @ajax method, item, success: (response, status, xhr) =>
       item.set(sid: response.id) if method is 'create'
       item.save {dirty: false}, {local: true}

@@ -280,7 +280,7 @@

   flushItem: (sid) ->
     model = @collection.fakeModel(sid)
-    this.ajax 'delete', model, success: (response, status, xhr) =>
+    @ajax 'delete', model, success: (response, status, xhr) =>
       @storage.destroyIds.remove(sid)

 # Manage indexes storing to localStorage.
@@ -303,7 +303,7 @@
   add: (itemId) ->
     unless _.include(@values, itemId.toString())
       @values.push itemId.toString()
-    this.save()
+    @save()

   # Remove element from a list of values
   # records.remove '3'
@@ -311,10 +311,14 @@
   # => ['1', '2', '4', '5']
   remove: (itemId) ->
     @values = _.without @values, itemId.toString()
-    this.save()
-
-  save: -> @storage.setItem @name, @values.join(',')
-  reset: -> @values = []; this.save()
+    @save()
+
+  save: ->
+    @storage.setItem @name, @values.join(',')
+
+  reset: ->
+    @values = []
+    @storage.removeItem @name

 # Use as wrapper for 'Backbone.Collection'
 class Offline.Collection
@@ -334,12 +338,10 @@
   # destory old models from the collection which have not marked as "new"
   destroyDiff: (response) ->
     diff = _.difference(_.without(@items.pluck('sid'), 'new'), _.pluck(response, 'id'))
-    this.get(sid)?.destroy(local: true) for sid in diff
+    @get(sid)?.destroy(local: true) for sid in diff

   # Use to create a fake model for the set
   fakeModel: (sid) ->
-    model = new Backbone.Model()
-    model.id = sid
+    model = new Backbone.Model(id: sid)
     model.urlRoot = @items.url
-
-    return model
+    model


Add a new spec for testing getCode()

Add a new spec for testing getCode()

--- spec/code-context-spec.coffee
+++ spec/code-context-spec.coffee
@@ -1,10 +1,7 @@
-{WorkspaceView} = require 'atom'
 CodeContext = require '../lib/code-context'

 describe 'CodeContext', ->
   beforeEach ->
-    atom.workspaceView = new WorkspaceView
-    textSource = atom.workspace.getActiveEditor()
     @codeContext = new CodeContext('test.txt', '/tmp/test.txt', null)

   describe 'fileColonLine when lineNumber is not set', ->
@@ -28,3 +25,14 @@
   describe 'getCode', ->
     it 'returns undefined if no textSource is available', ->
       expect(@codeContext.getCode()).toBe(undefined)
+
+    it 'returns the text from the textSource when available', ->
+      # TODO: Test using an actual editor or a selection?
+      dummyTextSource = {}
+      dummyTextSource.getText = ->
+        "print 'hello world!'"
+      @codeContext.textSource = dummyTextSource
+      code = @codeContext.getCode()
+
+      expect(typeof code).toEqual('string')
+      expect(code).toMatch("print 'hello world!'")


fixed coffeelint errors on workers/social/lib/social/models/kodingkey.coffee

--- workers/social/lib/social/models/kodingkey.coffee
+++ workers/social/lib/social/models/kodingkey.coffee
@@ -1,12 +1,13 @@
+# coffeelint: disable=no_implicit_braces
 jraphical = require 'jraphical'
 JAccount  = require './account'
 KodingError = require '../error'

 module.exports = class JKodingKey extends jraphical.Module

-  {Relationship} = jraphical
+  { Relationship } = jraphical

-  {Base, secure, race, signature} = require 'bongo'
+  { Base, secure, race, signature } = require 'bongo'

   @share()

@@ -36,45 +37,45 @@
       hostname        : String
       owner           : String

-  @create = secure (client, data, callback)->
-    {delegate} = client.connection
+  @create = secure (client, data, callback) ->
+    { delegate } = client.connection
     key = new JKodingKey
       key     : data.key
       hostname: data.hostname
       owner   : delegate._id
-    key.save (err)->
+    key.save (err) ->
       if err
         callback err
       else
         callback null, key

-  @fetchAll = secure ({connection:{delegate}}, options, callback)->
+  @fetchAll = secure ({ connection:{ delegate } }, options, callback) ->
     JKodingKey.all
       owner : delegate.getId()
-    , (err, keys)->
+    , (err, keys) ->
       callback err, keys

-  @fetchByKey = secure ({connection:{delegate}}, options, callback)->
+  @fetchByKey = secure ({ connection:{ delegate } }, options, callback) ->
     JKodingKey.all
       owner : delegate.getId()
       key   : options.key
-    , (err, keys)->
+    , (err, keys) ->
       callback err, keys

-  @checkKey = (options, callback)->
-    {key} = options
-    return new KodingError "Key is not valid" unless key and @authCheckKey key
+  @checkKey = (options, callback) ->
+    { key } = options
+    return new KodingError 'Key is not valid' unless key and @authCheckKey key
     JKodingKey.one
       key   : options.key
-    , (err, key)->
+    , (err, key) ->
       return callback err, no if err
       return callback null, no unless key
       callback null, yes

-  @createKeyByUser = (options, callback)->
+  @createKeyByUser = (options, callback) ->
     JAccount.one
       'profile.nickname': options.username
-    , (err, account)->
+    , (err, account) ->
       if err then callback err
       else if not account
         callback null, null
@@ -83,43 +84,43 @@
           key     : options.key
           hostname: options.hostname
           owner   : account._id
-        key.save (err)->
+        key.save (err) ->
           if err
             callback err
           else
             callback null, key

-  revoke: secure ({connection:{delegate}}, callback)->
+  revoke: secure ({ connection:{ delegate } }, callback) ->
     JKodingKey.one
       owner : delegate.getId()
       _id    : @getId()
-    , (err, key)->
+    , (err, key) ->
       key.remove callback

-  @authCheckKey = (key)->
+  @authCheckKey = (key) ->
     return false if not key
-    return false if typeof key isnt "string"
+    return false if typeof key isnt 'string'

     key = decodeURIComponent key
     return false if key.length isnt 64
     return true

-  @registerHostnameAndKey$ = secure (client, options, callback)->
-    {connection:{delegate}} = client
-    errMessage = "You need to be registered/loggedin in order to register your Kite"
-    return callback new KodingError errMessage if delegate.type isnt "registered"
+  @registerHostnameAndKey$ = secure (client, options, callback) ->
+    { connection:{ delegate } } = client
+    errMessage = 'You need to be registered/loggedin in order to register your Kite'
+    return callback new KodingError errMessage if delegate.type isnt 'registered'
     # set username
     options.username = delegate.profile.nickname
     @registerHostnameAndKey options, callback

-  @registerHostnameAndKey = ({username, key, hostname}, callback)->
-    return new KodingError "Key is not valid" unless @authCheckKey key
-    return new KodingError "Data is not valid" unless username and hostname
+  @registerHostnameAndKey = ({ username, key, hostname }, callback) ->
+    return new KodingError 'Key is not valid' unless @authCheckKey key
+    return new KodingError 'Data is not valid' unless username and hostname

-    JKodingKey.one {key}, (err, kodingKey)->
+    JKodingKey.one { key }, (err, kodingKey) ->
       if err
         console.warn "Error occurred while fetching koding-key username: #{username}, key: #{key}, err :#{err}"
-        return callback new KodingError "There is a problem with your key"
+        return callback new KodingError 'There is a problem with your key'

       if kodingKey
         errMessage = "Authentication already established with #{kodingKey.hostname}!"
@@ -128,8 +129,10 @@
         return callback error

       # if not created before, create here
-      JKodingKey.createKeyByUser {username, hostname, key}, (err, data)->
+      JKodingKey.createKeyByUser { username, hostname, key }, (err, data) ->
         if err
           console.warn "Error occurred while creating koding key - Err: #{err}, "
-          return callback new KodingError "An error occurred while saving your key, please try again later"
+          return callback new KodingError 'An error occurred while saving your key, please try again later'
         return callback null, "Authentication is successfull! Using id: #{data.hostname}"
+
+


An even dumber way

An even dumber way

--- spec/spellchecker-spec.coffee
+++ spec/spellchecker-spec.coffee
@@ -134,9 +134,11 @@
       @fixture.setDictionary defaultLanguage, dictionaryDirectory

     it "returns an array of possible corrections", ->
-      corrections = @fixture.getCorrectionsForMisspelling('wordd')
+      correction = if process.platform is "darwin" then "world" else "word"
+
+      corrections = @fixture.getCorrectionsForMisspelling('worrd')
       expect(corrections.length).toBeGreaterThan 0
-      expect(corrections.indexOf('words')).toBeGreaterThan -1
+      expect(corrections.indexOf(correction)).toBeGreaterThan -1

     it "throws an exception when no word specified", ->
       expect(-> @fixture.getCorrectionsForMisspelling()).toThrow()


Cosmetic changes to payment method list items.

--- client/landing/app/Applications/Account.kdapplication/account/accSettingsPaymentMethods.coffee
+++ client/landing/app/Applications/Account.kdapplication/account/accSettingsPaymentMethods.coffee
@@ -113,7 +113,7 @@
             type         : res.cardType
             cardNumber   : res.cardNumber
             cardExpiry   : res.cardMonth + '/' + res.cardYear
-            cardAddress  : res.address1 + '
' + res.address2
+            cardAddress  : res.address1 + ' ' + res.address2
             cardCity     : res.city
             cardState    : res.state
             cardZip      : res.zip
@@ -287,7 +287,7 @@
   partial:(data)->
     """

-
#{data.type}

+
#{data.title} - #{data.type}

#{data.cardNumber} - #{data.cardExpiry}

           #{data.cardAddress}


Show x installed y activities

Show x installed y activities

--- client/landing/app/Applications/Activity.kdapplication/ListItems/ActivityListItemFollow.coffee
+++ client/landing/app/Applications/Activity.kdapplication/ListItems/ActivityListItemFollow.coffee
@@ -3,21 +3,26 @@
   cssClassMap = ->
     JTag      : "topic"
     JAccount  : "account"
+    JApp      : "application" # We can use this in style

   constructor:(options,data)->
     options = $.extend options,
       cssClass : "follow bucket #{cssClassMap()[data.sourceName]}"
     super options,data

-    @anchor = new ProfileLinkView {
-      origin: data.anchor
-    }
-
-    @group = new LinkGroup {
-      group         : data.group
-      itemsToShow   : 3
-      subItemClass  : options.subItemLinkClass
-    }
+    @action = "followed"
+    if data.anchor?.constructorName is "JApp"
+      @anchor = new ProfileLinkView
+        origin: data.group[0]
+      @group = new AppLinkView origin: data.anchor
+      @action = "installed"
+    else
+      @anchor = new ProfileLinkView
+        origin: data.anchor
+      @group = new LinkGroup
+        group         : data.group
+        itemsToShow   : 3
+        subItemClass  : options.subItemLinkClass

     #@getData().on 'ItemWasAdded', -> log 'heres the event, sinan', arguments

@@ -25,7 +30,7 @@
     """

     {{> @anchor}}
-    followed
+    #{@action}
     {{> @group}}
     """

@@ -62,12 +67,14 @@
     became a member.
     """

+
 class AccountFollowBucketItemView extends FollowBucketItemView

   constructor:(options, data)->
     options.subItemLinkClass or= ProfileLinkView
     options.subItemCssClass or= 'profile'
     super
+

 class TagFollowBucketItemView extends FollowBucketItemView

@@ -76,6 +83,7 @@
     options.subItemCssClass or= 'topic'
     super

+
 class AppFollowBucketItemView extends FollowBucketItemView

   constructor:(options, data)->


Abstract out core Control methods and properties

This enables 'mixing-in' these methods to other view types
rather than assuming a Layout class via Control.

--- coffee/cilantro/ui/controls/base.coffee
+++ coffee/cilantro/ui/controls/base.coffee
@@ -7,36 +7,26 @@

     controlOptions = ['attrNames', 'dataAttrs', 'dataSelectors', 'regions',
-        'regionViews', 'attrGetters', 'attrSetters']
+        'regionViews', 'regionOptions', 'attrGetters', 'attrSetters']

-    # Control interface, this should not be used directly
-    class Control extends c.Marionette.Layout
-        className: 'control'
-
+    # Mixin for view prototypes. This can be applied using `_.extend`
+    # (override) or `_.defaults` (supplement) on the prototype, e.g.
+    #
+    #    c._.defaults MyView::, ControlViewMixin
+    #
+    # The constructor on the view itself should call the `mergeOptions`
+    # passing the options followed by the `bindContext` method passing
+    # in @context object passed in.
+    #
+    #   class MyView extends c.Backbone.View
+    #       constructor: ->
+    #           super
+    #           @mergeOptions(@options)
+    #           @bindContext(@options.context)
+    #
+    ControlViewMixin =
         attrNames: ['field', 'operator', 'value', 'nulls']
-
-        regions:
-            field: '.control-field'
-            operator: '.control-operator'
-            value: '.control-value'
-            nulls: '.control-nulls'
-
-        regionViews: {}
-
-        regionOptions: {}
-
-        dataAttrs:
-            field: 'data-field'
-            operator: 'data-operator'
-            value: 'data-value'
-            nulls: 'data-nulls'
-
-        dataSelectors:
-            field: '[data-field]'
-            operator: '[data-operator]'
-            value: '[data-value]'
-            nulls: '[data-nulls]'

         attrGetters:
             field: 'getField'
@@ -50,12 +40,10 @@
             value: 'setValue'
             nulls: 'setNulls'

-        constructor: ->
-            super
-            @bindContext(@options.context)
-
+        # Merge options into local references
+        mergeOptions: (options) ->
             for optionKey in controlOptions
-                if (option = @options[optionKey])?
+                if (option = c._.result(options, optionKey))?
                     if c._.isArray(option)
                         @[optionKey] = option
                     else if c._.isObject(option)
@@ -80,16 +68,6 @@
             @stopListening(@context)

         onRender: ->
-            for key, klass of c._.result @, 'regionViews'
-                inputAttrs = {}
-                inputAttrs[@dataAttrs[key]] = ''
-
-                options = c._.extend {}, c._.result(@regionOptions, key),
-                    inputAttrs: inputAttrs
-                    model: @model
-
-                @[key].show new klass(options)
-
             @set(@context)

         _get: (key, options) ->
@@ -146,10 +124,9 @@

         # Triggered any time the control contents have changed. Upstream, the
         # context can be bound to this event and update itself as changes
-        # occur. The small timer is prevent chatty typing
-        change: (event) =>
+        # occur.
+        change: (event) ->
             @trigger 'change', @, @get()
-

         getField: ->
         getOperator: ->
@@ -162,4 +139,52 @@
         setNulls: ->

-    { Control }
+    # Control interface, this should not be used directly
+    class Control extends c.Marionette.Layout
+        className: 'control'
+
+        regions:
+            field: '.control-field'
+            operator: '.control-operator'
+            value: '.control-value'
+            nulls: '.control-nulls'
+
+        regionViews: {}
+
+        regionOptions: {}
+
+        dataAttrs:
+            field: 'data-field'
+            operator: 'data-operator'
+            value: 'data-value'
+            nulls: 'data-nulls'
+
+        dataSelectors:
+            field: '[data-field]'
+            operator: '[data-operator]'
+            value: '[data-value]'
+            nulls: '[data-nulls]'
+
+        constructor: ->
+            super
+            @mergeOptions(@options)
+            @bindContext(@options.context)
+
+        onRender: ->
+            for key, klass of c._.result @, 'regionViews'
+                inputAttrs = {}
+                inputAttrs[@dataAttrs[key]] = ''
+
+                options = c._.extend {}, c._.result(@regionOptions, key),
+                    inputAttrs: inputAttrs
+                    model: @model
+
+                @[key].show new klass(options)
+
+            @set(@context)
+
+
+    c._.defaults Control::, ControlViewMixin
+
+
+    { Control, ControlViewMixin }


Refactor app banner naming: skip when already _dismissed_, not seen

--- components/app_banner/app_banner.coffee
+++ components/app_banner/app_banner.coffee
@@ -54,10 +54,10 @@
     @$el.remove()
     @$content.css marginTop: 0

-  seen: ->
+  dismissed: ->
     Cookies.set @cookie, yes, expires: (60 * 60 * 24 * 365)

-  @hasSeen: ->
+  @hasDismissed: ->
     Cookies.get(@::cookie)?

   @isEigen: ->
@@ -65,6 +65,6 @@

   @shouldDisplay: ->
     USER_AGENT?.match(/(Chrome)/)? and
-    not @hasSeen() and
+    not @hasDismissed() and
     not @isEigen() and
     not excluded.check()


Log an error for my sanity

Log an error for my sanity

--- components/branching_state/index.coffee
+++ components/branching_state/index.coffee
@@ -56,4 +56,7 @@

   view: ->
     views = @get('views')
-    new views[@current()] arguments...
+    if (View = views[@current()])?
+      new View arguments...
+    else
+      console.error "view for #{@current()} is not defined"


MachineSettingsCommonView: Make view creation a method to override.

--- client/app/lib/providers/machinesettingscommonview.coffee
+++ client/app/lib/providers/machinesettingscommonview.coffee
@@ -26,10 +26,15 @@

     @machine = @getData()

+    @createElements()
+    @initList()
+
+
+  createElements: ->
+
     @createHeader()
     @createAddView()
     @createListView()
-    @initList()

   createHeader: ->


Main: removed top navigation

Main: removed top navigation

--- client/landing/site.hackathon/coffee/core/mainview.coffee
+++ client/landing/site.hackathon/coffee/core/mainview.coffee
@@ -1,4 +1,3 @@
-TopNavigation  = require './topnavigation'
 CustomLinkView = require './customlinkview'
 MainTabView    = require './maintabview'

@@ -18,29 +17,10 @@

     {mainController} = KD.singletons

-    @createHeader()
-
     @createPanelWrapper()
     @createMainTabView()

     @emit 'ready'
-
-
-  createHeader:->
-
-    @addSubView @header = new KDView
-      tagName   : 'header'
-      domId     : 'main-header'
-
-    @header.addSubView new TopNavigation
-
-    @header.addSubView @logo = new KDCustomHTMLView
-      tagName   : 'a'
-      domId     : 'koding-logo'
-      partial   : '<cite></cite>'
-      click     : (event) =>
-        KD.utils.stopDOMEvent event
-        KD.singletons.router.clear()

   createPanelWrapper:->


Visual/edit modes: WIP, scrolling content editable.

--- content_scripts/mode_visual_edit.coffee
+++ content_scripts/mode_visual_edit.coffee
@@ -426,11 +426,10 @@
             # WIP...
             elementWithFocus = DomUtils.getElementWithFocus @selection, @getDirection() == backward
             console.log elementWithFocus.innerHTML
-            position = elementWithFocus.getClientRects()[0].top - @element.getClientRects()[0].top
-            console.log "top", position
-            Scroller.scrollToPosition @element, position, 0
-            position = elementWithFocus.getClientRects()[0].bottom - @element.getClientRects()[0].top
-            console.log "bottom", position
+            # position = @element.getClientRects()[0].top - elementWithFocus.getClientRects()[0].top
+            # console.log "top", position
+            # Scroller.scrollToPosition @element, position, 0
+            position = elementWithFocus.getClientRects()[0].bottom - @element.getClientRects()[0].top - @element.clientHeight + @element.scrollTop
             Scroller.scrollToPosition @element, position, 0
           else
             position = if @getDirection() == backward then @element.selectionStart else @element.selectionEnd


fix ebook cover path error

fix ebook cover path error

--- lib/markdown-preview-enhanced-view.coffee
+++ lib/markdown-preview-enhanced-view.coffee
@@ -952,9 +952,18 @@
   generateEbook: (dist)->
     {html, ebookConfig} = @parseMD(this, {isSavingToHTML: false, isForPreview: false, isForEbook: true})
     if !ebookConfig.isEbook
-      return atom.notifications.addError('book config not found', detail: 'please insert <!-- book --> to your markdown file')
+      return atom.notifications.addError('ebook config not found', detail: 'please insert <!-- ebook --> to your markdown file')
     else
       atom.notifications.addInfo('Your document is being prepared', detail: ':)')
+
+      if ebookConfig.cover # change cover to absolute path if necessary
+        cover = ebookConfig.cover
+        if cover.startsWith('./') or cover.startsWith('../')
+          cover = path.resolve(@rootDirectoryPath, cover)
+          ebookConfig.cover = cover
+        else if cover.startsWith('/')
+          cover = path.resolve(@projectDirectoryPath, '.'+cover)
+          ebookConfig.cover = cover

       div = document.createElement('div')
       div.innerHTML = html
@@ -974,6 +983,7 @@
           headingOffset += 1

           a.href = '#'+id # change id
+          a.classList.add('ebook-heading')

           if li.childElementCount > 1
             getStructure(li.children[1], level+1)