├── Cakefile ├── README.md ├── lib ├── coffee_machine.js └── state_machine.js ├── node_modules ├── .bin │ ├── cake │ ├── coffee │ └── vows ├── coffee-script │ ├── .npmignore │ ├── LICENSE │ ├── README │ ├── Rakefile │ ├── bin │ │ ├── cake │ │ └── coffee │ ├── lib │ │ ├── browser.js │ │ ├── cake.js │ │ ├── coffee-script.js │ │ ├── command.js │ │ ├── grammar.js │ │ ├── helpers.js │ │ ├── index.js │ │ ├── lexer.js │ │ ├── nodes.js │ │ ├── optparse.js │ │ ├── parser.js │ │ ├── repl.js │ │ ├── rewriter.js │ │ └── scope.js │ └── package.json └── vows │ ├── LICENSE │ ├── Makefile │ ├── README.md │ ├── bin │ └── vows │ ├── lib │ ├── assert │ │ ├── error.js │ │ └── macros.js │ ├── vows.js │ └── vows │ │ ├── console.js │ │ ├── context.js │ │ ├── extras.js │ │ ├── reporters │ │ ├── dot-matrix.js │ │ ├── json.js │ │ ├── silent.js │ │ ├── spec.js │ │ ├── watch.js │ │ └── xunit.js │ │ └── suite.js │ ├── node_modules │ └── eyes │ │ ├── LICENSE │ │ ├── Makefile │ │ ├── README.md │ │ ├── lib │ │ └── eyes.js │ │ ├── package.json │ │ └── test │ │ └── eyes-test.js │ ├── package.json │ └── test │ ├── assert-test.js │ └── vows-test.js ├── package.json ├── src └── coffee_machine.coffee └── test └── test_coffee_machine.coffee /Cakefile: -------------------------------------------------------------------------------- 1 | {exec} = require 'child_process' 2 | 3 | task 'build', 'Build project from src/*.coffee to lib/*.js', -> 4 | exec 'coffee --compile --output lib/ src/', (error, stdout, stderr) -> 5 | if error 6 | console.log "build failed: #{error}" 7 | throw error 8 | console.log "build complete. #{stdout} #{stderr}" 9 | 10 | task 'test', 'Runs vowsjs test suite', -> 11 | exec './node_modules/vows/bin/vows test/test_coffee_machine.coffee --spec', (error, stdout, stderr) -> 12 | console.log stdout -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Description: 2 | ------------ 3 | 4 | A simple state machine written in CoffeeScript. 5 | 6 | Installation: 7 | ------------- 8 | 9 | npm install coffee-machine 10 | 11 | Usage: 12 | ------ 13 | 14 | A "CoffeeMachine" class is provided that can be used as the basis of your state machine implementation. 15 | The object passed in to the constructor has an expected format that will define the state machine. 16 | The sample stuff below will use a chess game as a basic example. 17 | 18 | Step one will always be to require the state machine: 19 | 20 | {CoffeeMachine} = require 'coffee_machine' 21 | 22 | The CoffeeMachine class' constructor takes in an object that defines the entire state machine. 23 | Here's what it looks like: 24 | 25 | states: 26 | stateName: 27 | active: true/false (optional, the 1st state defaults to true) 28 | onEnter: enterMethod (called when successfully entering the state) 29 | onExit: exitMethod (called when successfully exiting the state) 30 | guard: guardMethod (stops the change to this state if returns false) 31 | stateName2: etc... 32 | events: 33 | eventName: 34 | from: fromState (should be a defined state, or "any") 35 | to: toState (should be a defined state) 36 | eventName2: etc... 37 | onStateChange: changeMethod (called on any state change) 38 | 39 | If you don't need anything fancy on the states, then you can use a basic Array setup: 40 | 41 | game = new CoffeeMachine states: ['whiteToMove', 'blackToMove'] 42 | 43 | game.availableStates() 44 | # outputs: [ 'whiteToMove', 'blackToMove' ] 45 | game.currentState() 46 | # outputs: 'whiteToMove' 47 | 48 | But, you should really define some *events* that will trigger state changes. Each 49 | defined event gives you a method you can call to trigger the state change. 50 | 51 | class ChessGame extends CoffeeMachine 52 | switchSides: -> 53 | # ... 54 | console.log "switchSides called." 55 | 56 | game = new ChessGame 57 | states: 58 | whiteToMove: 59 | onEnter: -> this.switchSides() 60 | blackToMove: 61 | onEnter: -> this.switchSides() 62 | events: 63 | whiteMoved: {from:'whiteToMove', to:'blackToMove'} 64 | blackMoved: {from:'blackToMove', to:'whiteToMove'} 65 | 66 | game.whiteMoved() 67 | # outputs: switchSides called. 68 | 69 | You can also pass the states definition to the defineCoffeeMachine method. So, a more custom 70 | and comprehensive implementation may look like: 71 | 72 | class ChessGame extends CoffeeMachine 73 | constructor: (@board, @pieces) -> 74 | @defineStateMachine 75 | states: 76 | whiteToMove: 77 | # If black was in check, sides can't switch unless they're now not in check 78 | guard: (args) -> not (args.from is 'blackInCheck' and this.blackKingInCheck()) 79 | onEnter: -> this.deliverMessage('white', 'Your move.') 80 | blackToMove: 81 | guard: (args) -> not (args.from is 'whiteInCheck' and this.whiteKingInCheck()) 82 | onEnter: -> this.deliverMessage('black', 'Your move.') 83 | whiteInCheck: 84 | onEnter: -> this.deliverMessage('white', 'Check!') 85 | onExit: -> this.deliverMessage('white', 'Check escaped.') 86 | blackInCheck: 87 | onEnter: -> this.deliverMessage('black', 'Check!') 88 | onExit: -> this.deliverMessage('black', 'Check escaped.') 89 | whiteCheckmated: 90 | onEnter: -> 91 | this.deliverMessage('white', 'Checkmate, you lose :-(') 92 | this.deliverMessage('black', 'Checkmate, you win!') 93 | blackCheckmated: 94 | onEnter: -> 95 | this.deliverMessage('black', 'Checkmate, you lose :-(') 96 | this.deliverMessage('white', 'Checkmate, you win!') 97 | events: 98 | whiteMoved: { from: 'whiteToMove', to: 'blackToMove' } 99 | whiteChecked: { from: ['blackToMove', 'blackInCheck'], to: 'whiteInCheck' } 100 | whiteCheckMated: { from: ['blackToMove', 'blackInCheck'], to: 'whiteCheckmated' } 101 | blackMoved: { from: 'blackToMove', to: 'whiteToMove' } 102 | blackChecked: { from: ['whiteToMove', 'whiteInCheck'], to: 'blackInCheck' } 103 | blackCheckMated: { from: ['whitetoMove', 'whiteInCheck'], to: 'blackCheckmated' } 104 | onStateChange: (args) -> this.logActivity(args.from, args.to, args.event) 105 | 106 | blackKingInCheck: -> 107 | # ... 108 | 109 | whiteKingInCheck: -> 110 | # ... 111 | 112 | deliverMessage: (playerColor, message) -> 113 | console.log "[Message to #{playerColor}] #{message}" 114 | 115 | logActivity: (from, to, event) -> 116 | console.log "Activity: from => #{from}, to => #{to}, event => #{event}" 117 | 118 | ################################## 119 | 120 | game = new ChessGame 121 | 122 | game.whiteMoved() 123 | # outputs: 124 | # [Message to black] Your move. 125 | # Activity: from => whiteToMove, to => blackToMove, event => whiteMoved 126 | 127 | game.blackMoved() 128 | # outputs: 129 | # [Message to white] Your move. 130 | # Activity: from => blackToMove, to => whiteToMove, event => blackMoved 131 | 132 | game.blackChecked() 133 | # outputs: 134 | # [Message to black] Check! 135 | # Activity: from => whiteToMove, to => blackInCheck, event => blackChecked 136 | 137 | game.whiteCheckMated() 138 | # outputs: 139 | # [Message to black] Check escaped. 140 | # [Message to white] Checkmate, you lose :-( 141 | # [Message to black] Checkmate, you win! 142 | # Activity: from => blackInCheck, to => whiteCheckmated, event => whiteCheckMated 143 | 144 | try 145 | game.blackMoved() 146 | catch error 147 | console.log error 148 | # outputs: 149 | # Cannot change from state 'blackToMove'; it is not the active state! 150 | 151 | 152 | Note that each callback method (onEnter, onExit, guard, and onStateChange) gets passed an args object that 153 | has a "from", "to", and "event" key, providing the previous state, new state, and the 154 | event that triggered the state change. 155 | 156 | Tests 157 | ------ 158 | cake test 159 | -------------------------------------------------------------------------------- /lib/coffee_machine.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | var CoffeeMachine, root; 3 | var __hasProp = Object.prototype.hasOwnProperty, __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; 4 | root = typeof exports !== "undefined" && exports !== null ? exports : window; 5 | root.CoffeeMachine = CoffeeMachine = (function() { 6 | function CoffeeMachine(stateMachine) { 7 | this.stateMachine = stateMachine != null ? stateMachine : { 8 | states: {}, 9 | events: {} 10 | }; 11 | this.defineStateMachine(this.stateMachine); 12 | } 13 | CoffeeMachine.prototype.defineStateMachine = function(stateMachine) { 14 | var activeStates, event, eventDef, state, stateDef, states, _i, _j, _len, _len2, _ref, _ref2, _results; 15 | this.stateMachine = stateMachine != null ? stateMachine : { 16 | states: {}, 17 | events: {} 18 | }; 19 | if (this.stateMachine.states.constructor.toString().indexOf('Array') !== -1) { 20 | states = this.stateMachine.states; 21 | this.stateMachine.states = {}; 22 | for (_i = 0, _len = states.length; _i < _len; _i++) { 23 | state = states[_i]; 24 | this.stateMachine.states[state] = { 25 | active: state === states[0] 26 | }; 27 | } 28 | } 29 | activeStates = (function() { 30 | var _ref, _results; 31 | _ref = this.stateMachine.states; 32 | _results = []; 33 | for (state in _ref) { 34 | if (!__hasProp.call(_ref, state)) continue; 35 | stateDef = _ref[state]; 36 | if (stateDef.active) { 37 | _results.push(state); 38 | } 39 | } 40 | return _results; 41 | }).call(this); 42 | if (activeStates.length === 0) { 43 | _ref = this.stateMachine.states; 44 | for (state in _ref) { 45 | if (!__hasProp.call(_ref, state)) continue; 46 | stateDef = _ref[state]; 47 | stateDef.active = true; 48 | break; 49 | } 50 | } else if (activeStates.length > 1) { 51 | for (_j = 0, _len2 = activeStates.length; _j < _len2; _j++) { 52 | state = activeStates[_j]; 53 | if (state === activeStates[0]) { 54 | continue; 55 | } 56 | stateDef.active = false; 57 | } 58 | } 59 | _ref2 = this.stateMachine.events; 60 | _results = []; 61 | for (event in _ref2) { 62 | eventDef = _ref2[event]; 63 | _results.push(__bind(function(event, eventDef) { 64 | return this[event] = function() { 65 | return this.changeState(eventDef.from, eventDef.to, event); 66 | }; 67 | }, this)(event, eventDef)); 68 | } 69 | return _results; 70 | }; 71 | CoffeeMachine.prototype.currentState = function() { 72 | var state, stateDef; 73 | return ((function() { 74 | var _ref, _results; 75 | _ref = this.stateMachine.states; 76 | _results = []; 77 | for (state in _ref) { 78 | if (!__hasProp.call(_ref, state)) continue; 79 | stateDef = _ref[state]; 80 | if (stateDef.active) { 81 | _results.push(state); 82 | } 83 | } 84 | return _results; 85 | }).call(this))[0]; 86 | }; 87 | CoffeeMachine.prototype.availableStates = function() { 88 | var state, _ref, _results; 89 | _ref = this.stateMachine.states; 90 | _results = []; 91 | for (state in _ref) { 92 | if (!__hasProp.call(_ref, state)) continue; 93 | _results.push(state); 94 | } 95 | return _results; 96 | }; 97 | CoffeeMachine.prototype.availableEvents = function() { 98 | var event, _ref, _results; 99 | _ref = this.stateMachine.events; 100 | _results = []; 101 | for (event in _ref) { 102 | if (!__hasProp.call(_ref, event)) continue; 103 | _results.push(event); 104 | } 105 | return _results; 106 | }; 107 | CoffeeMachine.prototype.changeState = function(from, to, event) { 108 | var args, enterMethod, exitMethod, fromStateDef, guardMethod, toStateDef; 109 | if (event == null) { 110 | event = null; 111 | } 112 | if (from.constructor.toString().indexOf('Array') !== -1) { 113 | if (from.indexOf(this.currentState()) !== -1) { 114 | from = this.currentState(); 115 | } else { 116 | throw "Cannot change from states " + (from.join(' or ')) + "; none are the active state!"; 117 | } 118 | } 119 | fromStateDef = this.stateMachine.states[from]; 120 | toStateDef = this.stateMachine.states[to]; 121 | if (toStateDef === void 0) { 122 | throw "Cannot change to state '" + to + "'; it is undefined!"; 123 | } 124 | enterMethod = toStateDef.onEnter, guardMethod = toStateDef.guard; 125 | if (from !== 'any') { 126 | if (fromStateDef === void 0) { 127 | throw "Cannot change from state '" + from + "'; it is undefined!"; 128 | } 129 | if (fromStateDef.active !== true) { 130 | throw "Cannot change from state '" + from + "'; it is not the active state!"; 131 | } 132 | } 133 | if (from === 'any') { 134 | fromStateDef = this.stateMachine.states[this.currentState()]; 135 | } 136 | exitMethod = fromStateDef.onExit; 137 | args = { 138 | from: from, 139 | to: to, 140 | event: event 141 | }; 142 | if (guardMethod !== void 0 && guardMethod.call(this, args) === false) { 143 | return false; 144 | } 145 | if (exitMethod !== void 0) { 146 | exitMethod.call(this, args); 147 | } 148 | if (enterMethod !== void 0) { 149 | enterMethod.call(this, args); 150 | } 151 | if (this.stateMachine.onStateChange !== void 0) { 152 | this.stateMachine.onStateChange.call(this, args); 153 | } 154 | fromStateDef.active = false; 155 | return toStateDef.active = true; 156 | }; 157 | return CoffeeMachine; 158 | })(); 159 | }).call(this); 160 | -------------------------------------------------------------------------------- /lib/state_machine.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | var StateMachine, root; 3 | var __hasProp = Object.prototype.hasOwnProperty, __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; 4 | root = typeof exports !== "undefined" && exports !== null ? exports : window; 5 | root.StateMachine = StateMachine = (function() { 6 | function StateMachine(stateMachine) { 7 | this.stateMachine = stateMachine != null ? stateMachine : { 8 | states: {}, 9 | events: {} 10 | }; 11 | this.defineStateMachine(this.stateMachine); 12 | } 13 | StateMachine.prototype.defineStateMachine = function(stateMachine) { 14 | var activeStates, event, eventDef, state, stateDef, states, _i, _j, _len, _len2, _ref, _ref2, _results; 15 | this.stateMachine = stateMachine != null ? stateMachine : { 16 | states: {}, 17 | events: {} 18 | }; 19 | if (this.stateMachine.states.constructor.toString().indexOf('Array') !== -1) { 20 | states = this.stateMachine.states; 21 | this.stateMachine.states = {}; 22 | for (_i = 0, _len = states.length; _i < _len; _i++) { 23 | state = states[_i]; 24 | this.stateMachine.states[state] = { 25 | active: state === states[0] 26 | }; 27 | } 28 | } 29 | activeStates = (function() { 30 | var _ref, _results; 31 | _ref = this.stateMachine.states; 32 | _results = []; 33 | for (state in _ref) { 34 | if (!__hasProp.call(_ref, state)) continue; 35 | stateDef = _ref[state]; 36 | if (stateDef.active) { 37 | _results.push(state); 38 | } 39 | } 40 | return _results; 41 | }).call(this); 42 | if (activeStates.length === 0) { 43 | _ref = this.stateMachine.states; 44 | for (state in _ref) { 45 | if (!__hasProp.call(_ref, state)) continue; 46 | stateDef = _ref[state]; 47 | stateDef.active = true; 48 | break; 49 | } 50 | } else if (activeStates.length > 1) { 51 | for (_j = 0, _len2 = activeStates.length; _j < _len2; _j++) { 52 | state = activeStates[_j]; 53 | if (state === activeStates[0]) { 54 | continue; 55 | } 56 | stateDef.active = false; 57 | } 58 | } 59 | _ref2 = this.stateMachine.events; 60 | _results = []; 61 | for (event in _ref2) { 62 | eventDef = _ref2[event]; 63 | _results.push(__bind(function(event, eventDef) { 64 | return this[event] = function() { 65 | return this.changeState(eventDef.from, eventDef.to, event); 66 | }; 67 | }, this)(event, eventDef)); 68 | } 69 | return _results; 70 | }; 71 | StateMachine.prototype.currentState = function() { 72 | var state, stateDef; 73 | return ((function() { 74 | var _ref, _results; 75 | _ref = this.stateMachine.states; 76 | _results = []; 77 | for (state in _ref) { 78 | if (!__hasProp.call(_ref, state)) continue; 79 | stateDef = _ref[state]; 80 | if (stateDef.active) { 81 | _results.push(state); 82 | } 83 | } 84 | return _results; 85 | }).call(this))[0]; 86 | }; 87 | StateMachine.prototype.availableStates = function() { 88 | var state, _ref, _results; 89 | _ref = this.stateMachine.states; 90 | _results = []; 91 | for (state in _ref) { 92 | if (!__hasProp.call(_ref, state)) continue; 93 | _results.push(state); 94 | } 95 | return _results; 96 | }; 97 | StateMachine.prototype.availableEvents = function() { 98 | var event, _ref, _results; 99 | _ref = this.stateMachine.events; 100 | _results = []; 101 | for (event in _ref) { 102 | if (!__hasProp.call(_ref, event)) continue; 103 | _results.push(event); 104 | } 105 | return _results; 106 | }; 107 | StateMachine.prototype.changeState = function(from, to, event) { 108 | var args, enterMethod, exitMethod, fromStateDef, guardMethod, toStateDef; 109 | if (event == null) { 110 | event = null; 111 | } 112 | if (from.constructor.toString().indexOf('Array') !== -1) { 113 | if (from.indexOf(this.currentState()) !== -1) { 114 | from = this.currentState(); 115 | } else { 116 | throw "Cannot change from states " + (from.join(' or ')) + "; none are the active state!"; 117 | } 118 | } 119 | fromStateDef = this.stateMachine.states[from]; 120 | toStateDef = this.stateMachine.states[to]; 121 | if (toStateDef === void 0) { 122 | throw "Cannot change to state '" + to + "'; it is undefined!"; 123 | } 124 | enterMethod = toStateDef.onEnter, guardMethod = toStateDef.guard; 125 | if (from !== 'any') { 126 | if (fromStateDef === void 0) { 127 | throw "Cannot change from state '" + from + "'; it is undefined!"; 128 | } 129 | if (fromStateDef.active !== true) { 130 | throw "Cannot change from state '" + from + "'; it is not the active state!"; 131 | } 132 | } 133 | if (from === 'any') { 134 | fromStateDef = this.stateMachine.states[this.currentState()]; 135 | } 136 | exitMethod = fromStateDef.onExit; 137 | args = { 138 | from: from, 139 | to: to, 140 | event: event 141 | }; 142 | if (guardMethod !== void 0 && guardMethod.call(this, args) === false) { 143 | return false; 144 | } 145 | if (exitMethod !== void 0) { 146 | exitMethod.call(this, args); 147 | } 148 | if (enterMethod !== void 0) { 149 | enterMethod.call(this, args); 150 | } 151 | if (this.stateMachine.onStateChange !== void 0) { 152 | this.stateMachine.onStateChange.call(this, args); 153 | } 154 | fromStateDef.active = false; 155 | return toStateDef.active = true; 156 | }; 157 | return StateMachine; 158 | })(); 159 | }).call(this); 160 | -------------------------------------------------------------------------------- /node_modules/.bin/cake: -------------------------------------------------------------------------------- 1 | ../coffee-script/bin/cake -------------------------------------------------------------------------------- /node_modules/.bin/coffee: -------------------------------------------------------------------------------- 1 | ../coffee-script/bin/coffee -------------------------------------------------------------------------------- /node_modules/.bin/vows: -------------------------------------------------------------------------------- 1 | ../vows/bin/vows -------------------------------------------------------------------------------- /node_modules/coffee-script/.npmignore: -------------------------------------------------------------------------------- 1 | *.coffee 2 | *.html 3 | .DS_Store 4 | .git* 5 | Cakefile 6 | documentation/ 7 | examples/ 8 | extras/ 9 | raw/ 10 | src/ 11 | test/ 12 | -------------------------------------------------------------------------------- /node_modules/coffee-script/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011 Jeremy Ashkenas 2 | 3 | Permission is hereby granted, free of charge, to any person 4 | obtaining a copy of this software and associated documentation 5 | files (the "Software"), to deal in the Software without 6 | restriction, including without limitation the rights to use, 7 | copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the 9 | Software is furnished to do so, subject to the following 10 | conditions: 11 | 12 | The above copyright notice and this permission notice shall be 13 | included in all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 17 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 19 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 20 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /node_modules/coffee-script/README: -------------------------------------------------------------------------------- 1 | = 2 | { 3 | } } { 4 | { { } } 5 | } }{ { 6 | { }{ } } _____ __ __ 7 | ( }{ }{ { ) / ____| / _|/ _| 8 | .- { { } { }} -. | | ___ | |_| |_ ___ ___ 9 | ( ( } { } { } } ) | | / _ \| _| _/ _ \/ _ \ 10 | |`-..________ ..-'| | |___| (_) | | | || __/ __/ 11 | | | \_____\___/|_| |_| \___|\___| 12 | | ;--. 13 | | (__ \ _____ _ _ 14 | | | ) ) / ____| (_) | | 15 | | |/ / | (___ ___ _ __ _ _ __ | |_ 16 | | ( / \___ \ / __| '__| | '_ \| __| 17 | | |/ ____) | (__| | | | |_) | |_ 18 | | | |_____/ \___|_| |_| .__/ \__| 19 | `-.._________..-' | | 20 | |_| 21 | 22 | 23 | CoffeeScript is a little language that compiles into JavaScript. 24 | 25 | Install Node.js, and then the CoffeeScript compiler: 26 | sudo bin/cake install 27 | 28 | Or, if you have the Node Package Manager installed: 29 | npm install -g coffee-script 30 | (Leave off the -g if you don't wish to install globally.) 31 | 32 | Compile a script: 33 | coffee /path/to/script.coffee 34 | 35 | For documentation, usage, and examples, see: 36 | http://coffeescript.org/ 37 | 38 | To suggest a feature, report a bug, or general discussion: 39 | http://github.com/jashkenas/coffee-script/issues/ 40 | 41 | If you'd like to chat, drop by #coffeescript on Freenode IRC, 42 | or on webchat.freenode.net. 43 | 44 | The source repository: 45 | git://github.com/jashkenas/coffee-script.git 46 | 47 | All contributors are listed here: 48 | http://github.com/jashkenas/coffee-script/contributors 49 | -------------------------------------------------------------------------------- /node_modules/coffee-script/Rakefile: -------------------------------------------------------------------------------- 1 | require 'rubygems' 2 | require 'erb' 3 | require 'fileutils' 4 | require 'rake/testtask' 5 | require 'json' 6 | 7 | desc "Build the documentation page" 8 | task :doc do 9 | source = 'documentation/index.html.erb' 10 | child = fork { exec "bin/coffee -bcw -o documentation/js documentation/coffee/*.coffee" } 11 | at_exit { Process.kill("INT", child) } 12 | Signal.trap("INT") { exit } 13 | loop do 14 | mtime = File.stat(source).mtime 15 | if !@mtime || mtime > @mtime 16 | rendered = ERB.new(File.read(source)).result(binding) 17 | File.open('index.html', 'w+') {|f| f.write(rendered) } 18 | end 19 | @mtime = mtime 20 | sleep 1 21 | end 22 | end 23 | 24 | desc "Build coffee-script-source gem" 25 | task :gem do 26 | require 'rubygems' 27 | require 'rubygems/package' 28 | 29 | gemspec = Gem::Specification.new do |s| 30 | s.name = 'coffee-script-source' 31 | s.version = JSON.parse(File.read('package.json'))["version"] 32 | s.date = Time.now.strftime("%Y-%m-%d") 33 | 34 | s.homepage = "http://jashkenas.github.com/coffee-script/" 35 | s.summary = "The CoffeeScript Compiler" 36 | s.description = <<-EOS 37 | CoffeeScript is a little language that compiles into JavaScript. 38 | Underneath all of those embarrassing braces and semicolons, 39 | JavaScript has always had a gorgeous object model at its heart. 40 | CoffeeScript is an attempt to expose the good parts of JavaScript 41 | in a simple way. 42 | EOS 43 | 44 | s.files = [ 45 | 'lib/coffee_script/coffee-script.js', 46 | 'lib/coffee_script/source.rb' 47 | ] 48 | 49 | s.authors = ['Jeremy Ashkenas'] 50 | s.email = 'jashkenas@gmail.com' 51 | s.rubyforge_project = 'coffee-script-source' 52 | end 53 | 54 | file = File.open("coffee-script-source.gem", "w") 55 | Gem::Package.open(file, 'w') do |pkg| 56 | pkg.metadata = gemspec.to_yaml 57 | 58 | path = "lib/coffee_script/source.rb" 59 | contents = <<-ERUBY 60 | module CoffeeScript 61 | module Source 62 | def self.bundled_path 63 | File.expand_path("../coffee-script.js", __FILE__) 64 | end 65 | end 66 | end 67 | ERUBY 68 | pkg.add_file_simple(path, 0644, contents.size) do |tar_io| 69 | tar_io.write(contents) 70 | end 71 | 72 | contents = File.read("extras/coffee-script.js") 73 | path = "lib/coffee_script/coffee-script.js" 74 | pkg.add_file_simple(path, 0644, contents.size) do |tar_io| 75 | tar_io.write(contents) 76 | end 77 | end 78 | end 79 | -------------------------------------------------------------------------------- /node_modules/coffee-script/bin/cake: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var path = require('path'); 4 | var fs = require('fs'); 5 | var lib = path.join(path.dirname(fs.realpathSync(__filename)), '../lib'); 6 | 7 | require(lib + '/cake').run(); 8 | -------------------------------------------------------------------------------- /node_modules/coffee-script/bin/coffee: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var path = require('path'); 4 | var fs = require('fs'); 5 | var lib = path.join(path.dirname(fs.realpathSync(__filename)), '../lib'); 6 | 7 | require(lib + '/command').run(); 8 | -------------------------------------------------------------------------------- /node_modules/coffee-script/lib/browser.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | var CoffeeScript, runScripts; 3 | CoffeeScript = require('./coffee-script'); 4 | CoffeeScript.require = require; 5 | CoffeeScript.eval = function(code, options) { 6 | return eval(CoffeeScript.compile(code, options)); 7 | }; 8 | CoffeeScript.run = function(code, options) { 9 | if (options == null) { 10 | options = {}; 11 | } 12 | options.bare = true; 13 | return Function(CoffeeScript.compile(code, options))(); 14 | }; 15 | if (typeof window === "undefined" || window === null) { 16 | return; 17 | } 18 | CoffeeScript.load = function(url, callback) { 19 | var xhr; 20 | xhr = new (window.ActiveXObject || XMLHttpRequest)('Microsoft.XMLHTTP'); 21 | xhr.open('GET', url, true); 22 | if ('overrideMimeType' in xhr) { 23 | xhr.overrideMimeType('text/plain'); 24 | } 25 | xhr.onreadystatechange = function() { 26 | var _ref; 27 | if (xhr.readyState === 4) { 28 | if ((_ref = xhr.status) === 0 || _ref === 200) { 29 | CoffeeScript.run(xhr.responseText); 30 | } else { 31 | throw new Error("Could not load " + url); 32 | } 33 | if (callback) { 34 | return callback(); 35 | } 36 | } 37 | }; 38 | return xhr.send(null); 39 | }; 40 | runScripts = function() { 41 | var coffees, execute, index, length, s, scripts; 42 | scripts = document.getElementsByTagName('script'); 43 | coffees = (function() { 44 | var _i, _len, _results; 45 | _results = []; 46 | for (_i = 0, _len = scripts.length; _i < _len; _i++) { 47 | s = scripts[_i]; 48 | if (s.type === 'text/coffeescript') { 49 | _results.push(s); 50 | } 51 | } 52 | return _results; 53 | })(); 54 | index = 0; 55 | length = coffees.length; 56 | (execute = function() { 57 | var script; 58 | script = coffees[index++]; 59 | if ((script != null ? script.type : void 0) === 'text/coffeescript') { 60 | if (script.src) { 61 | return CoffeeScript.load(script.src, execute); 62 | } else { 63 | CoffeeScript.run(script.innerHTML); 64 | return execute(); 65 | } 66 | } 67 | })(); 68 | return null; 69 | }; 70 | if (window.addEventListener) { 71 | addEventListener('DOMContentLoaded', runScripts, false); 72 | } else { 73 | attachEvent('onload', runScripts); 74 | } 75 | }).call(this); 76 | -------------------------------------------------------------------------------- /node_modules/coffee-script/lib/cake.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | var CoffeeScript, fs, helpers, missingTask, oparse, options, optparse, path, printTasks, switches, tasks; 3 | fs = require('fs'); 4 | path = require('path'); 5 | helpers = require('./helpers'); 6 | optparse = require('./optparse'); 7 | CoffeeScript = require('./coffee-script'); 8 | tasks = {}; 9 | options = {}; 10 | switches = []; 11 | oparse = null; 12 | helpers.extend(global, { 13 | task: function(name, description, action) { 14 | var _ref; 15 | if (!action) { 16 | _ref = [description, action], action = _ref[0], description = _ref[1]; 17 | } 18 | return tasks[name] = { 19 | name: name, 20 | description: description, 21 | action: action 22 | }; 23 | }, 24 | option: function(letter, flag, description) { 25 | return switches.push([letter, flag, description]); 26 | }, 27 | invoke: function(name) { 28 | if (!tasks[name]) { 29 | missingTask(name); 30 | } 31 | return tasks[name].action(options); 32 | } 33 | }); 34 | exports.run = function() { 35 | return path.exists('Cakefile', function(exists) { 36 | var arg, args, _i, _len, _ref, _results; 37 | if (!exists) { 38 | throw new Error("Cakefile not found in " + (process.cwd())); 39 | } 40 | args = process.argv.slice(2); 41 | CoffeeScript.run(fs.readFileSync('Cakefile').toString(), { 42 | filename: 'Cakefile' 43 | }); 44 | oparse = new optparse.OptionParser(switches); 45 | if (!args.length) { 46 | return printTasks(); 47 | } 48 | options = oparse.parse(args); 49 | _ref = options.arguments; 50 | _results = []; 51 | for (_i = 0, _len = _ref.length; _i < _len; _i++) { 52 | arg = _ref[_i]; 53 | _results.push(invoke(arg)); 54 | } 55 | return _results; 56 | }); 57 | }; 58 | printTasks = function() { 59 | var desc, name, spaces, task; 60 | console.log(''); 61 | for (name in tasks) { 62 | task = tasks[name]; 63 | spaces = 20 - name.length; 64 | spaces = spaces > 0 ? Array(spaces + 1).join(' ') : ''; 65 | desc = task.description ? "# " + task.description : ''; 66 | console.log("cake " + name + spaces + " " + desc); 67 | } 68 | if (switches.length) { 69 | return console.log(oparse.help()); 70 | } 71 | }; 72 | missingTask = function(task) { 73 | console.log("No such task: \"" + task + "\""); 74 | return process.exit(1); 75 | }; 76 | }).call(this); 77 | -------------------------------------------------------------------------------- /node_modules/coffee-script/lib/coffee-script.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | var Lexer, RESERVED, compile, fs, lexer, parser, path, vm, _ref; 3 | fs = require('fs'); 4 | path = require('path'); 5 | vm = require('vm'); 6 | _ref = require('./lexer'), Lexer = _ref.Lexer, RESERVED = _ref.RESERVED; 7 | parser = require('./parser').parser; 8 | if (require.extensions) { 9 | require.extensions['.coffee'] = function(module, filename) { 10 | var content; 11 | content = compile(fs.readFileSync(filename, 'utf8'), { 12 | filename: filename 13 | }); 14 | return module._compile(content, filename); 15 | }; 16 | } else if (require.registerExtension) { 17 | require.registerExtension('.coffee', function(content) { 18 | return compile(content); 19 | }); 20 | } 21 | exports.VERSION = '1.1.1'; 22 | exports.RESERVED = RESERVED; 23 | exports.helpers = require('./helpers'); 24 | exports.compile = compile = function(code, options) { 25 | if (options == null) { 26 | options = {}; 27 | } 28 | try { 29 | return (parser.parse(lexer.tokenize(code))).compile(options); 30 | } catch (err) { 31 | if (options.filename) { 32 | err.message = "In " + options.filename + ", " + err.message; 33 | } 34 | throw err; 35 | } 36 | }; 37 | exports.tokens = function(code, options) { 38 | return lexer.tokenize(code, options); 39 | }; 40 | exports.nodes = function(source, options) { 41 | if (typeof source === 'string') { 42 | return parser.parse(lexer.tokenize(source, options)); 43 | } else { 44 | return parser.parse(source); 45 | } 46 | }; 47 | exports.run = function(code, options) { 48 | var Module, root; 49 | root = module; 50 | while (root.parent) { 51 | root = root.parent; 52 | } 53 | root.filename = process.argv[1] = options.filename ? fs.realpathSync(options.filename) : '.'; 54 | if (root.moduleCache) { 55 | root.moduleCache = {}; 56 | } 57 | if (process.binding('natives').module) { 58 | Module = require('module').Module; 59 | root.paths = Module._nodeModulePaths(path.dirname(options.filename)); 60 | } 61 | if (path.extname(root.filename) !== '.coffee' || require.extensions) { 62 | return root._compile(compile(code, options), root.filename); 63 | } else { 64 | return root._compile(code, root.filename); 65 | } 66 | }; 67 | exports.eval = function(code, options) { 68 | var g, js, sandbox; 69 | if (options == null) { 70 | options = {}; 71 | } 72 | sandbox = options.sandbox; 73 | if (!sandbox) { 74 | sandbox = { 75 | require: require, 76 | module: { 77 | exports: {} 78 | } 79 | }; 80 | for (g in global) { 81 | sandbox[g] = global[g]; 82 | } 83 | sandbox.global = sandbox; 84 | sandbox.global.global = sandbox.global.root = sandbox.global.GLOBAL = sandbox; 85 | } 86 | sandbox.__filename = options.filename || 'eval'; 87 | sandbox.__dirname = path.dirname(sandbox.__filename); 88 | js = compile("_=(" + (code.trim()) + ")", options); 89 | return vm.runInNewContext(js, sandbox, sandbox.__filename); 90 | }; 91 | lexer = new Lexer; 92 | parser.lexer = { 93 | lex: function() { 94 | var tag, _ref2; 95 | _ref2 = this.tokens[this.pos++] || [''], tag = _ref2[0], this.yytext = _ref2[1], this.yylineno = _ref2[2]; 96 | return tag; 97 | }, 98 | setInput: function(tokens) { 99 | this.tokens = tokens; 100 | return this.pos = 0; 101 | }, 102 | upcomingInput: function() { 103 | return ""; 104 | } 105 | }; 106 | parser.yy = require('./nodes'); 107 | }).call(this); 108 | -------------------------------------------------------------------------------- /node_modules/coffee-script/lib/command.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | var BANNER, CoffeeScript, EventEmitter, SWITCHES, compileJoin, compileOptions, compileScript, compileScripts, compileStdio, contents, exec, forkNode, fs, helpers, lint, loadRequires, optionParser, optparse, opts, parseOptions, path, printLine, printTokens, printWarn, sources, spawn, usage, version, watch, writeJs, _ref; 3 | fs = require('fs'); 4 | path = require('path'); 5 | helpers = require('./helpers'); 6 | optparse = require('./optparse'); 7 | CoffeeScript = require('./coffee-script'); 8 | _ref = require('child_process'), spawn = _ref.spawn, exec = _ref.exec; 9 | EventEmitter = require('events').EventEmitter; 10 | helpers.extend(CoffeeScript, new EventEmitter); 11 | printLine = function(line) { 12 | return process.stdout.write(line + '\n'); 13 | }; 14 | printWarn = function(line) { 15 | return process.binding('stdio').writeError(line + '\n'); 16 | }; 17 | BANNER = 'Usage: coffee [options] path/to/script.coffee'; 18 | SWITCHES = [['-c', '--compile', 'compile to JavaScript and save as .js files'], ['-i', '--interactive', 'run an interactive CoffeeScript REPL'], ['-o', '--output [DIR]', 'set the directory for compiled JavaScript'], ['-j', '--join [FILE]', 'concatenate the scripts before compiling'], ['-w', '--watch', 'watch scripts for changes, and recompile'], ['-p', '--print', 'print the compiled JavaScript to stdout'], ['-l', '--lint', 'pipe the compiled JavaScript through JavaScript Lint'], ['-s', '--stdio', 'listen for and compile scripts over stdio'], ['-e', '--eval', 'compile a string from the command line'], ['-r', '--require [FILE*]', 'require a library before executing your script'], ['-b', '--bare', 'compile without the top-level function wrapper'], ['-t', '--tokens', 'print the tokens that the lexer produces'], ['-n', '--nodes', 'print the parse tree that Jison produces'], ['--nodejs [ARGS]', 'pass options through to the "node" binary'], ['-v', '--version', 'display CoffeeScript version'], ['-h', '--help', 'display this help message']]; 19 | opts = {}; 20 | sources = []; 21 | contents = []; 22 | optionParser = null; 23 | exports.run = function() { 24 | parseOptions(); 25 | if (opts.nodejs) { 26 | return forkNode(); 27 | } 28 | if (opts.help) { 29 | return usage(); 30 | } 31 | if (opts.version) { 32 | return version(); 33 | } 34 | if (opts.require) { 35 | loadRequires(); 36 | } 37 | if (opts.interactive) { 38 | return require('./repl'); 39 | } 40 | if (opts.stdio) { 41 | return compileStdio(); 42 | } 43 | if (opts.eval) { 44 | return compileScript(null, sources[0]); 45 | } 46 | if (!sources.length) { 47 | return require('./repl'); 48 | } 49 | if (opts.run) { 50 | opts.literals = sources.splice(1).concat(opts.literals); 51 | } 52 | process.ARGV = process.argv = process.argv.slice(0, 2).concat(opts.literals); 53 | process.argv[0] = 'coffee'; 54 | process.execPath = require.main.filename; 55 | return compileScripts(); 56 | }; 57 | compileScripts = function() { 58 | var base, compile, source, _i, _len, _results; 59 | _results = []; 60 | for (_i = 0, _len = sources.length; _i < _len; _i++) { 61 | source = sources[_i]; 62 | base = path.join(source); 63 | compile = function(source, topLevel) { 64 | return path.exists(source, function(exists) { 65 | if (topLevel && !exists) { 66 | throw new Error("File not found: " + source); 67 | } 68 | return fs.stat(source, function(err, stats) { 69 | if (err) { 70 | throw err; 71 | } 72 | if (stats.isDirectory()) { 73 | return fs.readdir(source, function(err, files) { 74 | var file, _j, _len2, _results2; 75 | _results2 = []; 76 | for (_j = 0, _len2 = files.length; _j < _len2; _j++) { 77 | file = files[_j]; 78 | _results2.push(compile(path.join(source, file))); 79 | } 80 | return _results2; 81 | }); 82 | } else if (topLevel || path.extname(source) === '.coffee') { 83 | fs.readFile(source, function(err, code) { 84 | if (opts.join) { 85 | contents[sources.indexOf(source)] = code.toString(); 86 | if (helpers.compact(contents).length > 0) { 87 | return compileJoin(); 88 | } 89 | } else { 90 | return compileScript(source, code.toString(), base); 91 | } 92 | }); 93 | if (opts.watch && !opts.join) { 94 | return watch(source, base); 95 | } 96 | } 97 | }); 98 | }); 99 | }; 100 | _results.push(compile(source, true)); 101 | } 102 | return _results; 103 | }; 104 | compileScript = function(file, input, base) { 105 | var o, options, t, task; 106 | o = opts; 107 | options = compileOptions(file); 108 | try { 109 | t = task = { 110 | file: file, 111 | input: input, 112 | options: options 113 | }; 114 | CoffeeScript.emit('compile', task); 115 | if (o.tokens) { 116 | return printTokens(CoffeeScript.tokens(t.input)); 117 | } else if (o.nodes) { 118 | return printLine(CoffeeScript.nodes(t.input).toString().trim()); 119 | } else if (o.run) { 120 | return CoffeeScript.run(t.input, t.options); 121 | } else { 122 | t.output = CoffeeScript.compile(t.input, t.options); 123 | CoffeeScript.emit('success', task); 124 | if (o.print) { 125 | return printLine(t.output.trim()); 126 | } else if (o.compile) { 127 | return writeJs(t.file, t.output, base); 128 | } else if (o.lint) { 129 | return lint(t.file, t.output); 130 | } 131 | } 132 | } catch (err) { 133 | CoffeeScript.emit('failure', err, task); 134 | if (CoffeeScript.listeners('failure').length) { 135 | return; 136 | } 137 | if (o.watch) { 138 | return printLine(err.message); 139 | } 140 | printWarn(err.stack); 141 | return process.exit(1); 142 | } 143 | }; 144 | compileStdio = function() { 145 | var code, stdin; 146 | code = ''; 147 | stdin = process.openStdin(); 148 | stdin.on('data', function(buffer) { 149 | if (buffer) { 150 | return code += buffer.toString(); 151 | } 152 | }); 153 | return stdin.on('end', function() { 154 | return compileScript(null, code); 155 | }); 156 | }; 157 | compileJoin = function() { 158 | var code; 159 | code = contents.join('\n'); 160 | return compileScript(opts.join, code, opts.join); 161 | }; 162 | loadRequires = function() { 163 | var realFilename, req, _i, _len, _ref2; 164 | realFilename = module.filename; 165 | module.filename = '.'; 166 | _ref2 = opts.require; 167 | for (_i = 0, _len = _ref2.length; _i < _len; _i++) { 168 | req = _ref2[_i]; 169 | require(req); 170 | } 171 | return module.filename = realFilename; 172 | }; 173 | watch = function(source, base) { 174 | return fs.watchFile(source, { 175 | persistent: true, 176 | interval: 500 177 | }, function(curr, prev) { 178 | if (curr.size === prev.size && curr.mtime.getTime() === prev.mtime.getTime()) { 179 | return; 180 | } 181 | return fs.readFile(source, function(err, code) { 182 | if (err) { 183 | throw err; 184 | } 185 | return compileScript(source, code.toString(), base); 186 | }); 187 | }); 188 | }; 189 | writeJs = function(source, js, base) { 190 | var baseDir, compile, dir, filename, jsPath, srcDir; 191 | filename = path.basename(source, path.extname(source)) + '.js'; 192 | srcDir = path.dirname(source); 193 | baseDir = base === '.' ? srcDir : srcDir.substring(base.length); 194 | dir = opts.output ? path.join(opts.output, baseDir) : srcDir; 195 | jsPath = path.join(dir, filename); 196 | compile = function() { 197 | if (js.length <= 0) { 198 | js = ' '; 199 | } 200 | return fs.writeFile(jsPath, js, function(err) { 201 | if (err) { 202 | return printLine(err.message); 203 | } else if (opts.compile && opts.watch) { 204 | return console.log("" + ((new Date).toLocaleTimeString()) + " - compiled " + source); 205 | } 206 | }); 207 | }; 208 | return path.exists(dir, function(exists) { 209 | if (exists) { 210 | return compile(); 211 | } else { 212 | return exec("mkdir -p " + dir, compile); 213 | } 214 | }); 215 | }; 216 | lint = function(file, js) { 217 | var conf, jsl, printIt; 218 | printIt = function(buffer) { 219 | return printLine(file + ':\t' + buffer.toString().trim()); 220 | }; 221 | conf = __dirname + '/../extras/jsl.conf'; 222 | jsl = spawn('jsl', ['-nologo', '-stdin', '-conf', conf]); 223 | jsl.stdout.on('data', printIt); 224 | jsl.stderr.on('data', printIt); 225 | jsl.stdin.write(js); 226 | return jsl.stdin.end(); 227 | }; 228 | printTokens = function(tokens) { 229 | var strings, tag, token, value; 230 | strings = (function() { 231 | var _i, _len, _ref2, _results; 232 | _results = []; 233 | for (_i = 0, _len = tokens.length; _i < _len; _i++) { 234 | token = tokens[_i]; 235 | _ref2 = [token[0], token[1].toString().replace(/\n/, '\\n')], tag = _ref2[0], value = _ref2[1]; 236 | _results.push("[" + tag + " " + value + "]"); 237 | } 238 | return _results; 239 | })(); 240 | return printLine(strings.join(' ')); 241 | }; 242 | parseOptions = function() { 243 | var o; 244 | optionParser = new optparse.OptionParser(SWITCHES, BANNER); 245 | o = opts = optionParser.parse(process.argv.slice(2)); 246 | o.compile || (o.compile = !!o.output); 247 | o.run = !(o.compile || o.print || o.lint); 248 | o.print = !!(o.print || (o.eval || o.stdio && o.compile)); 249 | return sources = o.arguments; 250 | }; 251 | compileOptions = function(filename) { 252 | return { 253 | filename: filename, 254 | bare: opts.bare 255 | }; 256 | }; 257 | forkNode = function() { 258 | var args, nodeArgs; 259 | nodeArgs = opts.nodejs.split(/\s+/); 260 | args = process.argv.slice(1); 261 | args.splice(args.indexOf('--nodejs'), 2); 262 | return spawn(process.execPath, nodeArgs.concat(args), { 263 | cwd: process.cwd(), 264 | env: process.env, 265 | customFds: [0, 1, 2] 266 | }); 267 | }; 268 | usage = function() { 269 | return printLine((new optparse.OptionParser(SWITCHES, BANNER)).help()); 270 | }; 271 | version = function() { 272 | return printLine("CoffeeScript version " + CoffeeScript.VERSION); 273 | }; 274 | }).call(this); 275 | -------------------------------------------------------------------------------- /node_modules/coffee-script/lib/grammar.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | var Parser, alt, alternatives, grammar, name, o, operators, token, tokens, unwrap; 3 | Parser = require('jison').Parser; 4 | unwrap = /^function\s*\(\)\s*\{\s*return\s*([\s\S]*);\s*\}/; 5 | o = function(patternString, action, options) { 6 | var match; 7 | patternString = patternString.replace(/\s{2,}/g, ' '); 8 | if (!action) { 9 | return [patternString, '$$ = $1;', options]; 10 | } 11 | action = (match = unwrap.exec(action)) ? match[1] : "(" + action + "())"; 12 | action = action.replace(/\bnew /g, '$&yy.'); 13 | action = action.replace(/\b(?:Block\.wrap|extend)\b/g, 'yy.$&'); 14 | return [patternString, "$$ = " + action + ";", options]; 15 | }; 16 | grammar = { 17 | Root: [ 18 | o('', function() { 19 | return new Block; 20 | }), o('Body'), o('Block TERMINATOR') 21 | ], 22 | Body: [ 23 | o('Line', function() { 24 | return Block.wrap([$1]); 25 | }), o('Body TERMINATOR Line', function() { 26 | return $1.push($3); 27 | }), o('Body TERMINATOR') 28 | ], 29 | Line: [o('Expression'), o('Statement')], 30 | Statement: [ 31 | o('Return'), o('Throw'), o('Comment'), o('STATEMENT', function() { 32 | return new Literal($1); 33 | }) 34 | ], 35 | Expression: [o('Value'), o('Invocation'), o('Code'), o('Operation'), o('Assign'), o('If'), o('Try'), o('While'), o('For'), o('Switch'), o('Class')], 36 | Block: [ 37 | o('INDENT OUTDENT', function() { 38 | return new Block; 39 | }), o('INDENT Body OUTDENT', function() { 40 | return $2; 41 | }) 42 | ], 43 | Identifier: [ 44 | o('IDENTIFIER', function() { 45 | return new Literal($1); 46 | }) 47 | ], 48 | AlphaNumeric: [ 49 | o('NUMBER', function() { 50 | return new Literal($1); 51 | }), o('STRING', function() { 52 | return new Literal($1); 53 | }) 54 | ], 55 | Literal: [ 56 | o('AlphaNumeric'), o('JS', function() { 57 | return new Literal($1); 58 | }), o('REGEX', function() { 59 | return new Literal($1); 60 | }), o('BOOL', function() { 61 | var val; 62 | val = new Literal($1); 63 | if ($1 === 'undefined') { 64 | val.isUndefined = true; 65 | } 66 | return val; 67 | }) 68 | ], 69 | Assign: [ 70 | o('Assignable = Expression', function() { 71 | return new Assign($1, $3); 72 | }), o('Assignable = INDENT Expression OUTDENT', function() { 73 | return new Assign($1, $4); 74 | }) 75 | ], 76 | AssignObj: [ 77 | o('ObjAssignable', function() { 78 | return new Value($1); 79 | }), o('ObjAssignable : Expression', function() { 80 | return new Assign(new Value($1), $3, 'object'); 81 | }), o('ObjAssignable :\ 82 | INDENT Expression OUTDENT', function() { 83 | return new Assign(new Value($1), $4, 'object'); 84 | }), o('Comment') 85 | ], 86 | ObjAssignable: [o('Identifier'), o('AlphaNumeric'), o('ThisProperty')], 87 | Return: [ 88 | o('RETURN Expression', function() { 89 | return new Return($2); 90 | }), o('RETURN', function() { 91 | return new Return; 92 | }) 93 | ], 94 | Comment: [ 95 | o('HERECOMMENT', function() { 96 | return new Comment($1); 97 | }) 98 | ], 99 | Code: [ 100 | o('PARAM_START ParamList PARAM_END FuncGlyph Block', function() { 101 | return new Code($2, $5, $4); 102 | }), o('FuncGlyph Block', function() { 103 | return new Code([], $2, $1); 104 | }) 105 | ], 106 | FuncGlyph: [ 107 | o('->', function() { 108 | return 'func'; 109 | }), o('=>', function() { 110 | return 'boundfunc'; 111 | }) 112 | ], 113 | OptComma: [o(''), o(',')], 114 | ParamList: [ 115 | o('', function() { 116 | return []; 117 | }), o('Param', function() { 118 | return [$1]; 119 | }), o('ParamList , Param', function() { 120 | return $1.concat($3); 121 | }) 122 | ], 123 | Param: [ 124 | o('ParamVar', function() { 125 | return new Param($1); 126 | }), o('ParamVar ...', function() { 127 | return new Param($1, null, true); 128 | }), o('ParamVar = Expression', function() { 129 | return new Param($1, $3); 130 | }) 131 | ], 132 | ParamVar: [o('Identifier'), o('ThisProperty'), o('Array'), o('Object')], 133 | Splat: [ 134 | o('Expression ...', function() { 135 | return new Splat($1); 136 | }) 137 | ], 138 | SimpleAssignable: [ 139 | o('Identifier', function() { 140 | return new Value($1); 141 | }), o('Value Accessor', function() { 142 | return $1.push($2); 143 | }), o('Invocation Accessor', function() { 144 | return new Value($1, [$2]); 145 | }), o('ThisProperty') 146 | ], 147 | Assignable: [ 148 | o('SimpleAssignable'), o('Array', function() { 149 | return new Value($1); 150 | }), o('Object', function() { 151 | return new Value($1); 152 | }) 153 | ], 154 | Value: [ 155 | o('Assignable'), o('Literal', function() { 156 | return new Value($1); 157 | }), o('Parenthetical', function() { 158 | return new Value($1); 159 | }), o('Range', function() { 160 | return new Value($1); 161 | }), o('This') 162 | ], 163 | Accessor: [ 164 | o('. Identifier', function() { 165 | return new Access($2); 166 | }), o('?. Identifier', function() { 167 | return new Access($2, 'soak'); 168 | }), o(':: Identifier', function() { 169 | return new Access($2, 'proto'); 170 | }), o('::', function() { 171 | return new Access(new Literal('prototype')); 172 | }), o('Index') 173 | ], 174 | Index: [ 175 | o('INDEX_START IndexValue INDEX_END', function() { 176 | return $2; 177 | }), o('INDEX_SOAK Index', function() { 178 | return extend($2, { 179 | soak: true 180 | }); 181 | }), o('INDEX_PROTO Index', function() { 182 | return extend($2, { 183 | proto: true 184 | }); 185 | }) 186 | ], 187 | IndexValue: [ 188 | o('Expression', function() { 189 | return new Index($1); 190 | }), o('Slice', function() { 191 | return new Slice($1); 192 | }) 193 | ], 194 | Object: [ 195 | o('{ AssignList OptComma }', function() { 196 | return new Obj($2, $1.generated); 197 | }) 198 | ], 199 | AssignList: [ 200 | o('', function() { 201 | return []; 202 | }), o('AssignObj', function() { 203 | return [$1]; 204 | }), o('AssignList , AssignObj', function() { 205 | return $1.concat($3); 206 | }), o('AssignList OptComma TERMINATOR AssignObj', function() { 207 | return $1.concat($4); 208 | }), o('AssignList OptComma INDENT AssignList OptComma OUTDENT', function() { 209 | return $1.concat($4); 210 | }) 211 | ], 212 | Class: [ 213 | o('CLASS', function() { 214 | return new Class; 215 | }), o('CLASS Block', function() { 216 | return new Class(null, null, $2); 217 | }), o('CLASS EXTENDS Value', function() { 218 | return new Class(null, $3); 219 | }), o('CLASS EXTENDS Value Block', function() { 220 | return new Class(null, $3, $4); 221 | }), o('CLASS SimpleAssignable', function() { 222 | return new Class($2); 223 | }), o('CLASS SimpleAssignable Block', function() { 224 | return new Class($2, null, $3); 225 | }), o('CLASS SimpleAssignable EXTENDS Value', function() { 226 | return new Class($2, $4); 227 | }), o('CLASS SimpleAssignable EXTENDS Value Block', function() { 228 | return new Class($2, $4, $5); 229 | }) 230 | ], 231 | Invocation: [ 232 | o('Value OptFuncExist Arguments', function() { 233 | return new Call($1, $3, $2); 234 | }), o('Invocation OptFuncExist Arguments', function() { 235 | return new Call($1, $3, $2); 236 | }), o('SUPER', function() { 237 | return new Call('super', [new Splat(new Literal('arguments'))]); 238 | }), o('SUPER Arguments', function() { 239 | return new Call('super', $2); 240 | }) 241 | ], 242 | OptFuncExist: [ 243 | o('', function() { 244 | return false; 245 | }), o('FUNC_EXIST', function() { 246 | return true; 247 | }) 248 | ], 249 | Arguments: [ 250 | o('CALL_START CALL_END', function() { 251 | return []; 252 | }), o('CALL_START ArgList OptComma CALL_END', function() { 253 | return $2; 254 | }) 255 | ], 256 | This: [ 257 | o('THIS', function() { 258 | return new Value(new Literal('this')); 259 | }), o('@', function() { 260 | return new Value(new Literal('this')); 261 | }) 262 | ], 263 | ThisProperty: [ 264 | o('@ Identifier', function() { 265 | return new Value(new Literal('this'), [new Access($2)], 'this'); 266 | }) 267 | ], 268 | Array: [ 269 | o('[ ]', function() { 270 | return new Arr([]); 271 | }), o('[ ArgList OptComma ]', function() { 272 | return new Arr($2); 273 | }) 274 | ], 275 | RangeDots: [ 276 | o('..', function() { 277 | return 'inclusive'; 278 | }), o('...', function() { 279 | return 'exclusive'; 280 | }) 281 | ], 282 | Range: [ 283 | o('[ Expression RangeDots Expression ]', function() { 284 | return new Range($2, $4, $3); 285 | }) 286 | ], 287 | Slice: [ 288 | o('Expression RangeDots Expression', function() { 289 | return new Range($1, $3, $2); 290 | }), o('Expression RangeDots', function() { 291 | return new Range($1, null, $2); 292 | }), o('RangeDots Expression', function() { 293 | return new Range(null, $2, $1); 294 | }) 295 | ], 296 | ArgList: [ 297 | o('Arg', function() { 298 | return [$1]; 299 | }), o('ArgList , Arg', function() { 300 | return $1.concat($3); 301 | }), o('ArgList OptComma TERMINATOR Arg', function() { 302 | return $1.concat($4); 303 | }), o('INDENT ArgList OptComma OUTDENT', function() { 304 | return $2; 305 | }), o('ArgList OptComma INDENT ArgList OptComma OUTDENT', function() { 306 | return $1.concat($4); 307 | }) 308 | ], 309 | Arg: [o('Expression'), o('Splat')], 310 | SimpleArgs: [ 311 | o('Expression'), o('SimpleArgs , Expression', function() { 312 | return [].concat($1, $3); 313 | }) 314 | ], 315 | Try: [ 316 | o('TRY Block', function() { 317 | return new Try($2); 318 | }), o('TRY Block Catch', function() { 319 | return new Try($2, $3[0], $3[1]); 320 | }), o('TRY Block FINALLY Block', function() { 321 | return new Try($2, null, null, $4); 322 | }), o('TRY Block Catch FINALLY Block', function() { 323 | return new Try($2, $3[0], $3[1], $5); 324 | }) 325 | ], 326 | Catch: [ 327 | o('CATCH Identifier Block', function() { 328 | return [$2, $3]; 329 | }) 330 | ], 331 | Throw: [ 332 | o('THROW Expression', function() { 333 | return new Throw($2); 334 | }) 335 | ], 336 | Parenthetical: [ 337 | o('( Body )', function() { 338 | return new Parens($2); 339 | }), o('( INDENT Body OUTDENT )', function() { 340 | return new Parens($3); 341 | }) 342 | ], 343 | WhileSource: [ 344 | o('WHILE Expression', function() { 345 | return new While($2); 346 | }), o('WHILE Expression WHEN Expression', function() { 347 | return new While($2, { 348 | guard: $4 349 | }); 350 | }), o('UNTIL Expression', function() { 351 | return new While($2, { 352 | invert: true 353 | }); 354 | }), o('UNTIL Expression WHEN Expression', function() { 355 | return new While($2, { 356 | invert: true, 357 | guard: $4 358 | }); 359 | }) 360 | ], 361 | While: [ 362 | o('WhileSource Block', function() { 363 | return $1.addBody($2); 364 | }), o('Statement WhileSource', function() { 365 | return $2.addBody(Block.wrap([$1])); 366 | }), o('Expression WhileSource', function() { 367 | return $2.addBody(Block.wrap([$1])); 368 | }), o('Loop', function() { 369 | return $1; 370 | }) 371 | ], 372 | Loop: [ 373 | o('LOOP Block', function() { 374 | return new While(new Literal('true')).addBody($2); 375 | }), o('LOOP Expression', function() { 376 | return new While(new Literal('true')).addBody(Block.wrap([$2])); 377 | }) 378 | ], 379 | For: [ 380 | o('Statement ForBody', function() { 381 | return new For($1, $2); 382 | }), o('Expression ForBody', function() { 383 | return new For($1, $2); 384 | }), o('ForBody Block', function() { 385 | return new For($2, $1); 386 | }) 387 | ], 388 | ForBody: [ 389 | o('FOR Range', function() { 390 | return { 391 | source: new Value($2) 392 | }; 393 | }), o('ForStart ForSource', function() { 394 | $2.own = $1.own; 395 | $2.name = $1[0]; 396 | $2.index = $1[1]; 397 | return $2; 398 | }) 399 | ], 400 | ForStart: [ 401 | o('FOR ForVariables', function() { 402 | return $2; 403 | }), o('FOR OWN ForVariables', function() { 404 | $3.own = true; 405 | return $3; 406 | }) 407 | ], 408 | ForValue: [ 409 | o('Identifier'), o('Array', function() { 410 | return new Value($1); 411 | }), o('Object', function() { 412 | return new Value($1); 413 | }) 414 | ], 415 | ForVariables: [ 416 | o('ForValue', function() { 417 | return [$1]; 418 | }), o('ForValue , ForValue', function() { 419 | return [$1, $3]; 420 | }) 421 | ], 422 | ForSource: [ 423 | o('FORIN Expression', function() { 424 | return { 425 | source: $2 426 | }; 427 | }), o('FOROF Expression', function() { 428 | return { 429 | source: $2, 430 | object: true 431 | }; 432 | }), o('FORIN Expression WHEN Expression', function() { 433 | return { 434 | source: $2, 435 | guard: $4 436 | }; 437 | }), o('FOROF Expression WHEN Expression', function() { 438 | return { 439 | source: $2, 440 | guard: $4, 441 | object: true 442 | }; 443 | }), o('FORIN Expression BY Expression', function() { 444 | return { 445 | source: $2, 446 | step: $4 447 | }; 448 | }), o('FORIN Expression WHEN Expression BY Expression', function() { 449 | return { 450 | source: $2, 451 | guard: $4, 452 | step: $6 453 | }; 454 | }), o('FORIN Expression BY Expression WHEN Expression', function() { 455 | return { 456 | source: $2, 457 | step: $4, 458 | guard: $6 459 | }; 460 | }) 461 | ], 462 | Switch: [ 463 | o('SWITCH Expression INDENT Whens OUTDENT', function() { 464 | return new Switch($2, $4); 465 | }), o('SWITCH Expression INDENT Whens ELSE Block OUTDENT', function() { 466 | return new Switch($2, $4, $6); 467 | }), o('SWITCH INDENT Whens OUTDENT', function() { 468 | return new Switch(null, $3); 469 | }), o('SWITCH INDENT Whens ELSE Block OUTDENT', function() { 470 | return new Switch(null, $3, $5); 471 | }) 472 | ], 473 | Whens: [ 474 | o('When'), o('Whens When', function() { 475 | return $1.concat($2); 476 | }) 477 | ], 478 | When: [ 479 | o('LEADING_WHEN SimpleArgs Block', function() { 480 | return [[$2, $3]]; 481 | }), o('LEADING_WHEN SimpleArgs Block TERMINATOR', function() { 482 | return [[$2, $3]]; 483 | }) 484 | ], 485 | IfBlock: [ 486 | o('IF Expression Block', function() { 487 | return new If($2, $3, { 488 | type: $1 489 | }); 490 | }), o('IfBlock ELSE IF Expression Block', function() { 491 | return $1.addElse(new If($4, $5, { 492 | type: $3 493 | })); 494 | }) 495 | ], 496 | If: [ 497 | o('IfBlock'), o('IfBlock ELSE Block', function() { 498 | return $1.addElse($3); 499 | }), o('Statement POST_IF Expression', function() { 500 | return new If($3, Block.wrap([$1]), { 501 | type: $2, 502 | statement: true 503 | }); 504 | }), o('Expression POST_IF Expression', function() { 505 | return new If($3, Block.wrap([$1]), { 506 | type: $2, 507 | statement: true 508 | }); 509 | }) 510 | ], 511 | Operation: [ 512 | o('UNARY Expression', function() { 513 | return new Op($1, $2); 514 | }), o('- Expression', (function() { 515 | return new Op('-', $2); 516 | }), { 517 | prec: 'UNARY' 518 | }), o('+ Expression', (function() { 519 | return new Op('+', $2); 520 | }), { 521 | prec: 'UNARY' 522 | }), o('-- SimpleAssignable', function() { 523 | return new Op('--', $2); 524 | }), o('++ SimpleAssignable', function() { 525 | return new Op('++', $2); 526 | }), o('SimpleAssignable --', function() { 527 | return new Op('--', $1, null, true); 528 | }), o('SimpleAssignable ++', function() { 529 | return new Op('++', $1, null, true); 530 | }), o('Expression ?', function() { 531 | return new Existence($1); 532 | }), o('Expression + Expression', function() { 533 | return new Op('+', $1, $3); 534 | }), o('Expression - Expression', function() { 535 | return new Op('-', $1, $3); 536 | }), o('Expression MATH Expression', function() { 537 | return new Op($2, $1, $3); 538 | }), o('Expression SHIFT Expression', function() { 539 | return new Op($2, $1, $3); 540 | }), o('Expression COMPARE Expression', function() { 541 | return new Op($2, $1, $3); 542 | }), o('Expression LOGIC Expression', function() { 543 | return new Op($2, $1, $3); 544 | }), o('Expression RELATION Expression', function() { 545 | if ($2.charAt(0) === '!') { 546 | return new Op($2.slice(1), $1, $3).invert(); 547 | } else { 548 | return new Op($2, $1, $3); 549 | } 550 | }), o('SimpleAssignable COMPOUND_ASSIGN\ 551 | Expression', function() { 552 | return new Assign($1, $3, $2); 553 | }), o('SimpleAssignable COMPOUND_ASSIGN\ 554 | INDENT Expression OUTDENT', function() { 555 | return new Assign($1, $4, $2); 556 | }), o('SimpleAssignable EXTENDS Expression', function() { 557 | return new Extends($1, $3); 558 | }) 559 | ] 560 | }; 561 | operators = [['left', '.', '?.', '::'], ['left', 'CALL_START', 'CALL_END'], ['nonassoc', '++', '--'], ['left', '?'], ['right', 'UNARY'], ['left', 'MATH'], ['left', '+', '-'], ['left', 'SHIFT'], ['left', 'RELATION'], ['left', 'COMPARE'], ['left', 'LOGIC'], ['nonassoc', 'INDENT', 'OUTDENT'], ['right', '=', ':', 'COMPOUND_ASSIGN', 'RETURN', 'THROW', 'EXTENDS'], ['right', 'FORIN', 'FOROF', 'BY', 'WHEN'], ['right', 'IF', 'ELSE', 'FOR', 'DO', 'WHILE', 'UNTIL', 'LOOP', 'SUPER', 'CLASS'], ['right', 'POST_IF']]; 562 | tokens = []; 563 | for (name in grammar) { 564 | alternatives = grammar[name]; 565 | grammar[name] = (function() { 566 | var _i, _j, _len, _len2, _ref, _results; 567 | _results = []; 568 | for (_i = 0, _len = alternatives.length; _i < _len; _i++) { 569 | alt = alternatives[_i]; 570 | _ref = alt[0].split(' '); 571 | for (_j = 0, _len2 = _ref.length; _j < _len2; _j++) { 572 | token = _ref[_j]; 573 | if (!grammar[token]) { 574 | tokens.push(token); 575 | } 576 | } 577 | if (name === 'Root') { 578 | alt[1] = "return " + alt[1]; 579 | } 580 | _results.push(alt); 581 | } 582 | return _results; 583 | })(); 584 | } 585 | exports.parser = new Parser({ 586 | tokens: tokens.join(' '), 587 | bnf: grammar, 588 | operators: operators.reverse(), 589 | startSymbol: 'Root' 590 | }); 591 | }).call(this); 592 | -------------------------------------------------------------------------------- /node_modules/coffee-script/lib/helpers.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | var extend, flatten; 3 | exports.starts = function(string, literal, start) { 4 | return literal === string.substr(start, literal.length); 5 | }; 6 | exports.ends = function(string, literal, back) { 7 | var len; 8 | len = literal.length; 9 | return literal === string.substr(string.length - len - (back || 0), len); 10 | }; 11 | exports.compact = function(array) { 12 | var item, _i, _len, _results; 13 | _results = []; 14 | for (_i = 0, _len = array.length; _i < _len; _i++) { 15 | item = array[_i]; 16 | if (item) { 17 | _results.push(item); 18 | } 19 | } 20 | return _results; 21 | }; 22 | exports.count = function(string, substr) { 23 | var num, pos; 24 | num = pos = 0; 25 | if (!substr.length) { 26 | return 1 / 0; 27 | } 28 | while (pos = 1 + string.indexOf(substr, pos)) { 29 | num++; 30 | } 31 | return num; 32 | }; 33 | exports.merge = function(options, overrides) { 34 | return extend(extend({}, options), overrides); 35 | }; 36 | extend = exports.extend = function(object, properties) { 37 | var key, val; 38 | for (key in properties) { 39 | val = properties[key]; 40 | object[key] = val; 41 | } 42 | return object; 43 | }; 44 | exports.flatten = flatten = function(array) { 45 | var element, flattened, _i, _len; 46 | flattened = []; 47 | for (_i = 0, _len = array.length; _i < _len; _i++) { 48 | element = array[_i]; 49 | if (element instanceof Array) { 50 | flattened = flattened.concat(flatten(element)); 51 | } else { 52 | flattened.push(element); 53 | } 54 | } 55 | return flattened; 56 | }; 57 | exports.del = function(obj, key) { 58 | var val; 59 | val = obj[key]; 60 | delete obj[key]; 61 | return val; 62 | }; 63 | exports.last = function(array, back) { 64 | return array[array.length - (back || 0) - 1]; 65 | }; 66 | }).call(this); 67 | -------------------------------------------------------------------------------- /node_modules/coffee-script/lib/index.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | var key, val, _ref; 3 | _ref = require('./coffee-script'); 4 | for (key in _ref) { 5 | val = _ref[key]; 6 | exports[key] = val; 7 | } 8 | }).call(this); 9 | -------------------------------------------------------------------------------- /node_modules/coffee-script/lib/lexer.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | var ASSIGNED, BOOL, CALLABLE, CODE, COFFEE_ALIASES, COFFEE_ALIAS_MAP, COFFEE_KEYWORDS, COMMENT, COMPARE, COMPOUND_ASSIGN, HEREDOC, HEREDOC_ILLEGAL, HEREDOC_INDENT, HEREGEX, HEREGEX_OMIT, IDENTIFIER, INDEXABLE, JSTOKEN, JS_FORBIDDEN, JS_KEYWORDS, LINE_BREAK, LINE_CONTINUER, LOGIC, Lexer, MATH, MULTILINER, MULTI_DENT, NOT_REGEX, NOT_SPACED_REGEX, NO_NEWLINE, NUMBER, OPERATOR, REGEX, RELATION, RESERVED, Rewriter, SHIFT, SIMPLESTR, TRAILING_SPACES, UNARY, WHITESPACE, compact, count, key, last, starts, _ref; 3 | var __indexOf = Array.prototype.indexOf || function(item) { 4 | for (var i = 0, l = this.length; i < l; i++) { 5 | if (this[i] === item) return i; 6 | } 7 | return -1; 8 | }; 9 | Rewriter = require('./rewriter').Rewriter; 10 | _ref = require('./helpers'), count = _ref.count, starts = _ref.starts, compact = _ref.compact, last = _ref.last; 11 | exports.Lexer = Lexer = (function() { 12 | function Lexer() {} 13 | Lexer.prototype.tokenize = function(code, opts) { 14 | var i; 15 | if (opts == null) { 16 | opts = {}; 17 | } 18 | if (WHITESPACE.test(code)) { 19 | code = "\n" + code; 20 | } 21 | code = code.replace(/\r/g, '').replace(TRAILING_SPACES, ''); 22 | this.code = code; 23 | this.line = opts.line || 0; 24 | this.indent = 0; 25 | this.indebt = 0; 26 | this.outdebt = 0; 27 | this.indents = []; 28 | this.tokens = []; 29 | i = 0; 30 | while (this.chunk = code.slice(i)) { 31 | i += this.identifierToken() || this.commentToken() || this.whitespaceToken() || this.lineToken() || this.heredocToken() || this.stringToken() || this.numberToken() || this.regexToken() || this.jsToken() || this.literalToken(); 32 | } 33 | this.closeIndentation(); 34 | if (opts.rewrite === false) { 35 | return this.tokens; 36 | } 37 | return (new Rewriter).rewrite(this.tokens); 38 | }; 39 | Lexer.prototype.identifierToken = function() { 40 | var colon, forcedIdentifier, id, input, match, prev, tag, _ref2, _ref3; 41 | if (!(match = IDENTIFIER.exec(this.chunk))) { 42 | return 0; 43 | } 44 | input = match[0], id = match[1], colon = match[2]; 45 | if (id === 'own' && this.tag() === 'FOR') { 46 | this.token('OWN', id); 47 | return id.length; 48 | } 49 | forcedIdentifier = colon || (prev = last(this.tokens)) && (((_ref2 = prev[0]) === '.' || _ref2 === '?.' || _ref2 === '::') || !prev.spaced && prev[0] === '@'); 50 | tag = 'IDENTIFIER'; 51 | if (__indexOf.call(JS_KEYWORDS, id) >= 0 || !forcedIdentifier && __indexOf.call(COFFEE_KEYWORDS, id) >= 0) { 52 | tag = id.toUpperCase(); 53 | if (tag === 'WHEN' && (_ref3 = this.tag(), __indexOf.call(LINE_BREAK, _ref3) >= 0)) { 54 | tag = 'LEADING_WHEN'; 55 | } else if (tag === 'FOR') { 56 | this.seenFor = true; 57 | } else if (tag === 'UNLESS') { 58 | tag = 'IF'; 59 | } else if (__indexOf.call(UNARY, tag) >= 0) { 60 | tag = 'UNARY'; 61 | } else if (__indexOf.call(RELATION, tag) >= 0) { 62 | if (tag !== 'INSTANCEOF' && this.seenFor) { 63 | tag = 'FOR' + tag; 64 | this.seenFor = false; 65 | } else { 66 | tag = 'RELATION'; 67 | if (this.value() === '!') { 68 | this.tokens.pop(); 69 | id = '!' + id; 70 | } 71 | } 72 | } 73 | } 74 | if (__indexOf.call(JS_FORBIDDEN, id) >= 0) { 75 | if (forcedIdentifier) { 76 | tag = 'IDENTIFIER'; 77 | id = new String(id); 78 | id.reserved = true; 79 | } else if (__indexOf.call(RESERVED, id) >= 0) { 80 | this.identifierError(id); 81 | } 82 | } 83 | if (!forcedIdentifier) { 84 | if (__indexOf.call(COFFEE_ALIASES, id) >= 0) { 85 | id = COFFEE_ALIAS_MAP[id]; 86 | } 87 | tag = (function() { 88 | switch (id) { 89 | case '!': 90 | return 'UNARY'; 91 | case '==': 92 | case '!=': 93 | return 'COMPARE'; 94 | case '&&': 95 | case '||': 96 | return 'LOGIC'; 97 | case 'true': 98 | case 'false': 99 | case 'null': 100 | case 'undefined': 101 | return 'BOOL'; 102 | case 'break': 103 | case 'continue': 104 | case 'debugger': 105 | return 'STATEMENT'; 106 | default: 107 | return tag; 108 | } 109 | })(); 110 | } 111 | this.token(tag, id); 112 | if (colon) { 113 | this.token(':', ':'); 114 | } 115 | return input.length; 116 | }; 117 | Lexer.prototype.numberToken = function() { 118 | var match, number; 119 | if (!(match = NUMBER.exec(this.chunk))) { 120 | return 0; 121 | } 122 | number = match[0]; 123 | this.token('NUMBER', number); 124 | return number.length; 125 | }; 126 | Lexer.prototype.stringToken = function() { 127 | var match, string; 128 | switch (this.chunk.charAt(0)) { 129 | case "'": 130 | if (!(match = SIMPLESTR.exec(this.chunk))) { 131 | return 0; 132 | } 133 | this.token('STRING', (string = match[0]).replace(MULTILINER, '\\\n')); 134 | break; 135 | case '"': 136 | if (!(string = this.balancedString(this.chunk, '"'))) { 137 | return 0; 138 | } 139 | if (0 < string.indexOf('#{', 1)) { 140 | this.interpolateString(string.slice(1, -1)); 141 | } else { 142 | this.token('STRING', this.escapeLines(string)); 143 | } 144 | break; 145 | default: 146 | return 0; 147 | } 148 | this.line += count(string, '\n'); 149 | return string.length; 150 | }; 151 | Lexer.prototype.heredocToken = function() { 152 | var doc, heredoc, match, quote; 153 | if (!(match = HEREDOC.exec(this.chunk))) { 154 | return 0; 155 | } 156 | heredoc = match[0]; 157 | quote = heredoc.charAt(0); 158 | doc = this.sanitizeHeredoc(match[2], { 159 | quote: quote, 160 | indent: null 161 | }); 162 | if (quote === '"' && 0 <= doc.indexOf('#{')) { 163 | this.interpolateString(doc, { 164 | heredoc: true 165 | }); 166 | } else { 167 | this.token('STRING', this.makeString(doc, quote, true)); 168 | } 169 | this.line += count(heredoc, '\n'); 170 | return heredoc.length; 171 | }; 172 | Lexer.prototype.commentToken = function() { 173 | var comment, here, match; 174 | if (!(match = this.chunk.match(COMMENT))) { 175 | return 0; 176 | } 177 | comment = match[0], here = match[1]; 178 | if (here) { 179 | this.token('HERECOMMENT', this.sanitizeHeredoc(here, { 180 | herecomment: true, 181 | indent: Array(this.indent + 1).join(' ') 182 | })); 183 | this.token('TERMINATOR', '\n'); 184 | } 185 | this.line += count(comment, '\n'); 186 | return comment.length; 187 | }; 188 | Lexer.prototype.jsToken = function() { 189 | var match, script; 190 | if (!(this.chunk.charAt(0) === '`' && (match = JSTOKEN.exec(this.chunk)))) { 191 | return 0; 192 | } 193 | this.token('JS', (script = match[0]).slice(1, -1)); 194 | return script.length; 195 | }; 196 | Lexer.prototype.regexToken = function() { 197 | var match, prev, regex, _ref2; 198 | if (this.chunk.charAt(0) !== '/') { 199 | return 0; 200 | } 201 | if (match = HEREGEX.exec(this.chunk)) { 202 | return this.heregexToken(match); 203 | } 204 | prev = last(this.tokens); 205 | if (prev && (_ref2 = prev[0], __indexOf.call((prev.spaced ? NOT_REGEX : NOT_SPACED_REGEX), _ref2) >= 0)) { 206 | return 0; 207 | } 208 | if (!(match = REGEX.exec(this.chunk))) { 209 | return 0; 210 | } 211 | regex = match[0]; 212 | this.token('REGEX', regex === '//' ? '/(?:)/' : regex); 213 | return regex.length; 214 | }; 215 | Lexer.prototype.heregexToken = function(match) { 216 | var body, flags, heregex, re, tag, tokens, value, _i, _len, _ref2, _ref3, _ref4, _ref5; 217 | heregex = match[0], body = match[1], flags = match[2]; 218 | if (0 > body.indexOf('#{')) { 219 | re = body.replace(HEREGEX_OMIT, '').replace(/\//g, '\\/'); 220 | this.token('REGEX', "/" + (re || '(?:)') + "/" + flags); 221 | return heregex.length; 222 | } 223 | this.token('IDENTIFIER', 'RegExp'); 224 | this.tokens.push(['CALL_START', '(']); 225 | tokens = []; 226 | _ref2 = this.interpolateString(body, { 227 | regex: true 228 | }); 229 | for (_i = 0, _len = _ref2.length; _i < _len; _i++) { 230 | _ref3 = _ref2[_i], tag = _ref3[0], value = _ref3[1]; 231 | if (tag === 'TOKENS') { 232 | tokens.push.apply(tokens, value); 233 | } else { 234 | if (!(value = value.replace(HEREGEX_OMIT, ''))) { 235 | continue; 236 | } 237 | value = value.replace(/\\/g, '\\\\'); 238 | tokens.push(['STRING', this.makeString(value, '"', true)]); 239 | } 240 | tokens.push(['+', '+']); 241 | } 242 | tokens.pop(); 243 | if (((_ref4 = tokens[0]) != null ? _ref4[0] : void 0) !== 'STRING') { 244 | this.tokens.push(['STRING', '""'], ['+', '+']); 245 | } 246 | (_ref5 = this.tokens).push.apply(_ref5, tokens); 247 | if (flags) { 248 | this.tokens.push([',', ','], ['STRING', '"' + flags + '"']); 249 | } 250 | this.token(')', ')'); 251 | return heregex.length; 252 | }; 253 | Lexer.prototype.lineToken = function() { 254 | var diff, indent, match, noNewlines, prev, size; 255 | if (!(match = MULTI_DENT.exec(this.chunk))) { 256 | return 0; 257 | } 258 | indent = match[0]; 259 | this.line += count(indent, '\n'); 260 | prev = last(this.tokens, 1); 261 | size = indent.length - 1 - indent.lastIndexOf('\n'); 262 | noNewlines = this.unfinished(); 263 | if (size - this.indebt === this.indent) { 264 | if (noNewlines) { 265 | this.suppressNewlines(); 266 | } else { 267 | this.newlineToken(); 268 | } 269 | return indent.length; 270 | } 271 | if (size > this.indent) { 272 | if (noNewlines) { 273 | this.indebt = size - this.indent; 274 | this.suppressNewlines(); 275 | return indent.length; 276 | } 277 | diff = size - this.indent + this.outdebt; 278 | this.token('INDENT', diff); 279 | this.indents.push(diff); 280 | this.outdebt = this.indebt = 0; 281 | } else { 282 | this.indebt = 0; 283 | this.outdentToken(this.indent - size, noNewlines); 284 | } 285 | this.indent = size; 286 | return indent.length; 287 | }; 288 | Lexer.prototype.outdentToken = function(moveOut, noNewlines, close) { 289 | var dent, len; 290 | while (moveOut > 0) { 291 | len = this.indents.length - 1; 292 | if (this.indents[len] === void 0) { 293 | moveOut = 0; 294 | } else if (this.indents[len] === this.outdebt) { 295 | moveOut -= this.outdebt; 296 | this.outdebt = 0; 297 | } else if (this.indents[len] < this.outdebt) { 298 | this.outdebt -= this.indents[len]; 299 | moveOut -= this.indents[len]; 300 | } else { 301 | dent = this.indents.pop() - this.outdebt; 302 | moveOut -= dent; 303 | this.outdebt = 0; 304 | this.token('OUTDENT', dent); 305 | } 306 | } 307 | if (dent) { 308 | this.outdebt -= moveOut; 309 | } 310 | if (!(this.tag() === 'TERMINATOR' || noNewlines)) { 311 | this.token('TERMINATOR', '\n'); 312 | } 313 | return this; 314 | }; 315 | Lexer.prototype.whitespaceToken = function() { 316 | var match, nline, prev; 317 | if (!((match = WHITESPACE.exec(this.chunk)) || (nline = this.chunk.charAt(0) === '\n'))) { 318 | return 0; 319 | } 320 | prev = last(this.tokens); 321 | if (prev) { 322 | prev[match ? 'spaced' : 'newLine'] = true; 323 | } 324 | if (match) { 325 | return match[0].length; 326 | } else { 327 | return 0; 328 | } 329 | }; 330 | Lexer.prototype.newlineToken = function() { 331 | if (this.tag() !== 'TERMINATOR') { 332 | this.token('TERMINATOR', '\n'); 333 | } 334 | return this; 335 | }; 336 | Lexer.prototype.suppressNewlines = function() { 337 | if (this.value() === '\\') { 338 | this.tokens.pop(); 339 | } 340 | return this; 341 | }; 342 | Lexer.prototype.literalToken = function() { 343 | var match, prev, tag, value, _ref2, _ref3, _ref4, _ref5; 344 | if (match = OPERATOR.exec(this.chunk)) { 345 | value = match[0]; 346 | if (CODE.test(value)) { 347 | this.tagParameters(); 348 | } 349 | } else { 350 | value = this.chunk.charAt(0); 351 | } 352 | tag = value; 353 | prev = last(this.tokens); 354 | if (value === '=' && prev) { 355 | if (!prev[1].reserved && (_ref2 = prev[1], __indexOf.call(JS_FORBIDDEN, _ref2) >= 0)) { 356 | this.assignmentError(); 357 | } 358 | if ((_ref3 = prev[1]) === '||' || _ref3 === '&&') { 359 | prev[0] = 'COMPOUND_ASSIGN'; 360 | prev[1] += '='; 361 | return value.length; 362 | } 363 | } 364 | if (value === ';') { 365 | tag = 'TERMINATOR'; 366 | } else if (__indexOf.call(MATH, value) >= 0) { 367 | tag = 'MATH'; 368 | } else if (__indexOf.call(COMPARE, value) >= 0) { 369 | tag = 'COMPARE'; 370 | } else if (__indexOf.call(COMPOUND_ASSIGN, value) >= 0) { 371 | tag = 'COMPOUND_ASSIGN'; 372 | } else if (__indexOf.call(UNARY, value) >= 0) { 373 | tag = 'UNARY'; 374 | } else if (__indexOf.call(SHIFT, value) >= 0) { 375 | tag = 'SHIFT'; 376 | } else if (__indexOf.call(LOGIC, value) >= 0 || value === '?' && (prev != null ? prev.spaced : void 0)) { 377 | tag = 'LOGIC'; 378 | } else if (prev && !prev.spaced) { 379 | if (value === '(' && (_ref4 = prev[0], __indexOf.call(CALLABLE, _ref4) >= 0)) { 380 | if (prev[0] === '?') { 381 | prev[0] = 'FUNC_EXIST'; 382 | } 383 | tag = 'CALL_START'; 384 | } else if (value === '[' && (_ref5 = prev[0], __indexOf.call(INDEXABLE, _ref5) >= 0)) { 385 | tag = 'INDEX_START'; 386 | switch (prev[0]) { 387 | case '?': 388 | prev[0] = 'INDEX_SOAK'; 389 | break; 390 | case '::': 391 | prev[0] = 'INDEX_PROTO'; 392 | } 393 | } 394 | } 395 | this.token(tag, value); 396 | return value.length; 397 | }; 398 | Lexer.prototype.sanitizeHeredoc = function(doc, options) { 399 | var attempt, herecomment, indent, match, _ref2; 400 | indent = options.indent, herecomment = options.herecomment; 401 | if (herecomment) { 402 | if (HEREDOC_ILLEGAL.test(doc)) { 403 | throw new Error("block comment cannot contain \"*/\", starting on line " + (this.line + 1)); 404 | } 405 | if (doc.indexOf('\n') <= 0) { 406 | return doc; 407 | } 408 | } else { 409 | while (match = HEREDOC_INDENT.exec(doc)) { 410 | attempt = match[1]; 411 | if (indent === null || (0 < (_ref2 = attempt.length) && _ref2 < indent.length)) { 412 | indent = attempt; 413 | } 414 | } 415 | } 416 | if (indent) { 417 | doc = doc.replace(RegExp("\\n" + indent, "g"), '\n'); 418 | } 419 | if (!herecomment) { 420 | doc = doc.replace(/^\n/, ''); 421 | } 422 | return doc; 423 | }; 424 | Lexer.prototype.tagParameters = function() { 425 | var i, stack, tok, tokens; 426 | if (this.tag() !== ')') { 427 | return this; 428 | } 429 | stack = []; 430 | tokens = this.tokens; 431 | i = tokens.length; 432 | tokens[--i][0] = 'PARAM_END'; 433 | while (tok = tokens[--i]) { 434 | switch (tok[0]) { 435 | case ')': 436 | stack.push(tok); 437 | break; 438 | case '(': 439 | case 'CALL_START': 440 | if (stack.length) { 441 | stack.pop(); 442 | } else if (tok[0] === '(') { 443 | tok[0] = 'PARAM_START'; 444 | return this; 445 | } 446 | } 447 | } 448 | return this; 449 | }; 450 | Lexer.prototype.closeIndentation = function() { 451 | return this.outdentToken(this.indent); 452 | }; 453 | Lexer.prototype.identifierError = function(word) { 454 | throw SyntaxError("Reserved word \"" + word + "\" on line " + (this.line + 1)); 455 | }; 456 | Lexer.prototype.assignmentError = function() { 457 | throw SyntaxError("Reserved word \"" + (this.value()) + "\" on line " + (this.line + 1) + " can't be assigned"); 458 | }; 459 | Lexer.prototype.balancedString = function(str, end) { 460 | var i, letter, prev, stack, _ref2; 461 | stack = [end]; 462 | for (i = 1, _ref2 = str.length; 1 <= _ref2 ? i < _ref2 : i > _ref2; 1 <= _ref2 ? i++ : i--) { 463 | switch (letter = str.charAt(i)) { 464 | case '\\': 465 | i++; 466 | continue; 467 | case end: 468 | stack.pop(); 469 | if (!stack.length) { 470 | return str.slice(0, i + 1); 471 | } 472 | end = stack[stack.length - 1]; 473 | continue; 474 | } 475 | if (end === '}' && (letter === '"' || letter === "'")) { 476 | stack.push(end = letter); 477 | } else if (end === '}' && letter === '{') { 478 | stack.push(end = '}'); 479 | } else if (end === '"' && prev === '#' && letter === '{') { 480 | stack.push(end = '}'); 481 | } 482 | prev = letter; 483 | } 484 | throw new Error("missing " + (stack.pop()) + ", starting on line " + (this.line + 1)); 485 | }; 486 | Lexer.prototype.interpolateString = function(str, options) { 487 | var expr, heredoc, i, inner, interpolated, len, letter, nested, pi, regex, tag, tokens, value, _len, _ref2, _ref3, _ref4; 488 | if (options == null) { 489 | options = {}; 490 | } 491 | heredoc = options.heredoc, regex = options.regex; 492 | tokens = []; 493 | pi = 0; 494 | i = -1; 495 | while (letter = str.charAt(i += 1)) { 496 | if (letter === '\\') { 497 | i += 1; 498 | continue; 499 | } 500 | if (!(letter === '#' && str.charAt(i + 1) === '{' && (expr = this.balancedString(str.slice(i + 1), '}')))) { 501 | continue; 502 | } 503 | if (pi < i) { 504 | tokens.push(['NEOSTRING', str.slice(pi, i)]); 505 | } 506 | inner = expr.slice(1, -1); 507 | if (inner.length) { 508 | nested = new Lexer().tokenize(inner, { 509 | line: this.line, 510 | rewrite: false 511 | }); 512 | nested.pop(); 513 | if (((_ref2 = nested[0]) != null ? _ref2[0] : void 0) === 'TERMINATOR') { 514 | nested.shift(); 515 | } 516 | if (len = nested.length) { 517 | if (len > 1) { 518 | nested.unshift(['(', '(']); 519 | nested.push([')', ')']); 520 | } 521 | tokens.push(['TOKENS', nested]); 522 | } 523 | } 524 | i += expr.length; 525 | pi = i + 1; 526 | } 527 | if ((i > pi && pi < str.length)) { 528 | tokens.push(['NEOSTRING', str.slice(pi)]); 529 | } 530 | if (regex) { 531 | return tokens; 532 | } 533 | if (!tokens.length) { 534 | return this.token('STRING', '""'); 535 | } 536 | if (tokens[0][0] !== 'NEOSTRING') { 537 | tokens.unshift(['', '']); 538 | } 539 | if (interpolated = tokens.length > 1) { 540 | this.token('(', '('); 541 | } 542 | for (i = 0, _len = tokens.length; i < _len; i++) { 543 | _ref3 = tokens[i], tag = _ref3[0], value = _ref3[1]; 544 | if (i) { 545 | this.token('+', '+'); 546 | } 547 | if (tag === 'TOKENS') { 548 | (_ref4 = this.tokens).push.apply(_ref4, value); 549 | } else { 550 | this.token('STRING', this.makeString(value, '"', heredoc)); 551 | } 552 | } 553 | if (interpolated) { 554 | this.token(')', ')'); 555 | } 556 | return tokens; 557 | }; 558 | Lexer.prototype.token = function(tag, value) { 559 | return this.tokens.push([tag, value, this.line]); 560 | }; 561 | Lexer.prototype.tag = function(index, tag) { 562 | var tok; 563 | return (tok = last(this.tokens, index)) && (tag ? tok[0] = tag : tok[0]); 564 | }; 565 | Lexer.prototype.value = function(index, val) { 566 | var tok; 567 | return (tok = last(this.tokens, index)) && (val ? tok[1] = val : tok[1]); 568 | }; 569 | Lexer.prototype.unfinished = function() { 570 | var prev, value; 571 | return LINE_CONTINUER.test(this.chunk) || (prev = last(this.tokens, 1)) && prev[0] !== '.' && (value = this.value()) && !value.reserved && NO_NEWLINE.test(value) && !CODE.test(value) && !ASSIGNED.test(this.chunk); 572 | }; 573 | Lexer.prototype.escapeLines = function(str, heredoc) { 574 | return str.replace(MULTILINER, heredoc ? '\\n' : ''); 575 | }; 576 | Lexer.prototype.makeString = function(body, quote, heredoc) { 577 | if (!body) { 578 | return quote + quote; 579 | } 580 | body = body.replace(/\\([\s\S])/g, function(match, contents) { 581 | if (contents === '\n' || contents === quote) { 582 | return contents; 583 | } else { 584 | return match; 585 | } 586 | }); 587 | body = body.replace(RegExp("" + quote, "g"), '\\$&'); 588 | return quote + this.escapeLines(body, heredoc) + quote; 589 | }; 590 | return Lexer; 591 | })(); 592 | JS_KEYWORDS = ['true', 'false', 'null', 'this', 'new', 'delete', 'typeof', 'in', 'instanceof', 'return', 'throw', 'break', 'continue', 'debugger', 'if', 'else', 'switch', 'for', 'while', 'do', 'try', 'catch', 'finally', 'class', 'extends', 'super']; 593 | COFFEE_KEYWORDS = ['undefined', 'then', 'unless', 'until', 'loop', 'of', 'by', 'when']; 594 | COFFEE_ALIAS_MAP = { 595 | and: '&&', 596 | or: '||', 597 | is: '==', 598 | isnt: '!=', 599 | not: '!', 600 | yes: 'true', 601 | no: 'false', 602 | on: 'true', 603 | off: 'false' 604 | }; 605 | COFFEE_ALIASES = (function() { 606 | var _results; 607 | _results = []; 608 | for (key in COFFEE_ALIAS_MAP) { 609 | _results.push(key); 610 | } 611 | return _results; 612 | })(); 613 | COFFEE_KEYWORDS = COFFEE_KEYWORDS.concat(COFFEE_ALIASES); 614 | RESERVED = ['case', 'default', 'function', 'var', 'void', 'with', 'const', 'let', 'enum', 'export', 'import', 'native', '__hasProp', '__extends', '__slice', '__bind', '__indexOf']; 615 | JS_FORBIDDEN = JS_KEYWORDS.concat(RESERVED); 616 | exports.RESERVED = RESERVED.concat(JS_KEYWORDS).concat(COFFEE_KEYWORDS); 617 | IDENTIFIER = /^([$A-Za-z_\x7f-\uffff][$\w\x7f-\uffff]*)([^\n\S]*:(?!:))?/; 618 | NUMBER = /^0x[\da-f]+|^(?:\d+(\.\d+)?|\.\d+)(?:e[+-]?\d+)?/i; 619 | HEREDOC = /^("""|''')([\s\S]*?)(?:\n[^\n\S]*)?\1/; 620 | OPERATOR = /^(?:[-=]>|[-+*\/%<>&|^!?=]=|>>>=?|([-+:])\1|([&|<>])\2=?|\?\.|\.{2,3})/; 621 | WHITESPACE = /^[^\n\S]+/; 622 | COMMENT = /^###([^#][\s\S]*?)(?:###[^\n\S]*|(?:###)?$)|^(?:\s*#(?!##[^#]).*)+/; 623 | CODE = /^[-=]>/; 624 | MULTI_DENT = /^(?:\n[^\n\S]*)+/; 625 | SIMPLESTR = /^'[^\\']*(?:\\.[^\\']*)*'/; 626 | JSTOKEN = /^`[^\\`]*(?:\\.[^\\`]*)*`/; 627 | REGEX = /^\/(?![\s=])[^[\/\n\\]*(?:(?:\\[\s\S]|\[[^\]\n\\]*(?:\\[\s\S][^\]\n\\]*)*])[^[\/\n\\]*)*\/[imgy]{0,4}(?!\w)/; 628 | HEREGEX = /^\/{3}([\s\S]+?)\/{3}([imgy]{0,4})(?!\w)/; 629 | HEREGEX_OMIT = /\s+(?:#.*)?/g; 630 | MULTILINER = /\n/g; 631 | HEREDOC_INDENT = /\n+([^\n\S]*)/g; 632 | HEREDOC_ILLEGAL = /\*\//; 633 | ASSIGNED = /^\s*@?([$A-Za-z_][$\w\x7f-\uffff]*|['"].*['"])[^\n\S]*?[:=][^:=>]/; 634 | LINE_CONTINUER = /^\s*(?:,|\??\.(?![.\d])|::)/; 635 | TRAILING_SPACES = /\s+$/; 636 | NO_NEWLINE = /^(?:[-+*&|\/%=<>!.\\][<>=&|]*|and|or|is(?:nt)?|n(?:ot|ew)|delete|typeof|instanceof)$/; 637 | COMPOUND_ASSIGN = ['-=', '+=', '/=', '*=', '%=', '||=', '&&=', '?=', '<<=', '>>=', '>>>=', '&=', '^=', '|=']; 638 | UNARY = ['!', '~', 'NEW', 'TYPEOF', 'DELETE', 'DO']; 639 | LOGIC = ['&&', '||', '&', '|', '^']; 640 | SHIFT = ['<<', '>>', '>>>']; 641 | COMPARE = ['==', '!=', '<', '>', '<=', '>=']; 642 | MATH = ['*', '/', '%']; 643 | RELATION = ['IN', 'OF', 'INSTANCEOF']; 644 | BOOL = ['TRUE', 'FALSE', 'NULL', 'UNDEFINED']; 645 | NOT_REGEX = ['NUMBER', 'REGEX', 'BOOL', '++', '--', ']']; 646 | NOT_SPACED_REGEX = NOT_REGEX.concat(')', '}', 'THIS', 'IDENTIFIER', 'STRING'); 647 | CALLABLE = ['IDENTIFIER', 'STRING', 'REGEX', ')', ']', '}', '?', '::', '@', 'THIS', 'SUPER']; 648 | INDEXABLE = CALLABLE.concat('NUMBER', 'BOOL'); 649 | LINE_BREAK = ['INDENT', 'OUTDENT', 'TERMINATOR']; 650 | }).call(this); 651 | -------------------------------------------------------------------------------- /node_modules/coffee-script/lib/optparse.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | var LONG_FLAG, MULTI_FLAG, OPTIONAL, OptionParser, SHORT_FLAG, buildRule, buildRules, normalizeArguments; 3 | exports.OptionParser = OptionParser = (function() { 4 | function OptionParser(rules, banner) { 5 | this.banner = banner; 6 | this.rules = buildRules(rules); 7 | } 8 | OptionParser.prototype.parse = function(args) { 9 | var arg, i, isOption, matchedRule, options, rule, value, _i, _len, _len2, _ref; 10 | options = { 11 | arguments: [], 12 | literals: [] 13 | }; 14 | args = normalizeArguments(args); 15 | for (i = 0, _len = args.length; i < _len; i++) { 16 | arg = args[i]; 17 | if (arg === '--') { 18 | options.literals = args.slice(i + 1); 19 | break; 20 | } 21 | isOption = !!(arg.match(LONG_FLAG) || arg.match(SHORT_FLAG)); 22 | matchedRule = false; 23 | _ref = this.rules; 24 | for (_i = 0, _len2 = _ref.length; _i < _len2; _i++) { 25 | rule = _ref[_i]; 26 | if (rule.shortFlag === arg || rule.longFlag === arg) { 27 | value = rule.hasArgument ? args[i += 1] : true; 28 | options[rule.name] = rule.isList ? (options[rule.name] || []).concat(value) : value; 29 | matchedRule = true; 30 | break; 31 | } 32 | } 33 | if (isOption && !matchedRule) { 34 | throw new Error("unrecognized option: " + arg); 35 | } 36 | if (!isOption) { 37 | options.arguments = args.slice(i); 38 | break; 39 | } 40 | } 41 | return options; 42 | }; 43 | OptionParser.prototype.help = function() { 44 | var letPart, lines, rule, spaces, _i, _len, _ref; 45 | lines = []; 46 | if (this.banner) { 47 | lines.unshift("" + this.banner + "\n"); 48 | } 49 | _ref = this.rules; 50 | for (_i = 0, _len = _ref.length; _i < _len; _i++) { 51 | rule = _ref[_i]; 52 | spaces = 15 - rule.longFlag.length; 53 | spaces = spaces > 0 ? Array(spaces + 1).join(' ') : ''; 54 | letPart = rule.shortFlag ? rule.shortFlag + ', ' : ' '; 55 | lines.push(' ' + letPart + rule.longFlag + spaces + rule.description); 56 | } 57 | return "\n" + (lines.join('\n')) + "\n"; 58 | }; 59 | return OptionParser; 60 | })(); 61 | LONG_FLAG = /^(--\w[\w\-]+)/; 62 | SHORT_FLAG = /^(-\w)/; 63 | MULTI_FLAG = /^-(\w{2,})/; 64 | OPTIONAL = /\[(\w+(\*?))\]/; 65 | buildRules = function(rules) { 66 | var tuple, _i, _len, _results; 67 | _results = []; 68 | for (_i = 0, _len = rules.length; _i < _len; _i++) { 69 | tuple = rules[_i]; 70 | if (tuple.length < 3) { 71 | tuple.unshift(null); 72 | } 73 | _results.push(buildRule.apply(null, tuple)); 74 | } 75 | return _results; 76 | }; 77 | buildRule = function(shortFlag, longFlag, description, options) { 78 | var match; 79 | if (options == null) { 80 | options = {}; 81 | } 82 | match = longFlag.match(OPTIONAL); 83 | longFlag = longFlag.match(LONG_FLAG)[1]; 84 | return { 85 | name: longFlag.substr(2), 86 | shortFlag: shortFlag, 87 | longFlag: longFlag, 88 | description: description, 89 | hasArgument: !!(match && match[1]), 90 | isList: !!(match && match[2]) 91 | }; 92 | }; 93 | normalizeArguments = function(args) { 94 | var arg, l, match, result, _i, _j, _len, _len2, _ref; 95 | args = args.slice(0); 96 | result = []; 97 | for (_i = 0, _len = args.length; _i < _len; _i++) { 98 | arg = args[_i]; 99 | if (match = arg.match(MULTI_FLAG)) { 100 | _ref = match[1].split(''); 101 | for (_j = 0, _len2 = _ref.length; _j < _len2; _j++) { 102 | l = _ref[_j]; 103 | result.push('-' + l); 104 | } 105 | } else { 106 | result.push(arg); 107 | } 108 | } 109 | return result; 110 | }; 111 | }).call(this); 112 | -------------------------------------------------------------------------------- /node_modules/coffee-script/lib/repl.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | var ACCESSOR, CoffeeScript, SIMPLEVAR, Script, autocomplete, backlog, completeAttribute, completeVariable, enableColours, error, getCompletions, getPropertyNames, inspect, readline, repl, run, stdin, stdout; 3 | var __hasProp = Object.prototype.hasOwnProperty; 4 | CoffeeScript = require('./coffee-script'); 5 | readline = require('readline'); 6 | inspect = require('util').inspect; 7 | Script = require('vm').Script; 8 | enableColours = false; 9 | if (process.platform !== 'win32') { 10 | enableColours = !process.env.NODE_DISABLE_COLORS; 11 | } 12 | stdin = process.openStdin(); 13 | stdout = process.stdout; 14 | error = function(err) { 15 | return stdout.write((err.stack || err.toString()) + '\n\n'); 16 | }; 17 | backlog = ''; 18 | run = (function() { 19 | var g, sandbox; 20 | sandbox = { 21 | require: require, 22 | module: { 23 | exports: {} 24 | } 25 | }; 26 | for (g in global) { 27 | sandbox[g] = global[g]; 28 | } 29 | sandbox.global = sandbox; 30 | sandbox.global.global = sandbox.global.root = sandbox.global.GLOBAL = sandbox; 31 | return function(buffer) { 32 | var code, val; 33 | code = backlog += '\n' + buffer.toString(); 34 | if (code[code.length - 1] === '\\') { 35 | return backlog = backlog.slice(0, backlog.length - 1); 36 | } 37 | backlog = ''; 38 | try { 39 | val = CoffeeScript.eval(code, { 40 | sandbox: sandbox, 41 | bare: true, 42 | filename: 'repl' 43 | }); 44 | if (val !== void 0) { 45 | process.stdout.write(inspect(val, false, 2, enableColours) + '\n'); 46 | } 47 | } catch (err) { 48 | error(err); 49 | } 50 | return repl.prompt(); 51 | }; 52 | })(); 53 | ACCESSOR = /\s*([\w\.]+)(?:\.(\w*))$/; 54 | SIMPLEVAR = /\s*(\w*)$/i; 55 | autocomplete = function(text) { 56 | return completeAttribute(text) || completeVariable(text) || [[], text]; 57 | }; 58 | completeAttribute = function(text) { 59 | var all, completions, match, obj, prefix, val; 60 | if (match = text.match(ACCESSOR)) { 61 | all = match[0], obj = match[1], prefix = match[2]; 62 | try { 63 | val = Script.runInThisContext(obj); 64 | } catch (error) { 65 | return [[], text]; 66 | } 67 | completions = getCompletions(prefix, getPropertyNames(val)); 68 | return [completions, prefix]; 69 | } 70 | }; 71 | completeVariable = function(text) { 72 | var completions, free, scope, _ref; 73 | if (free = (_ref = text.match(SIMPLEVAR)) != null ? _ref[1] : void 0) { 74 | scope = Script.runInThisContext('this'); 75 | completions = getCompletions(free, CoffeeScript.RESERVED.concat(getPropertyNames(scope))); 76 | return [completions, free]; 77 | } 78 | }; 79 | getCompletions = function(prefix, candidates) { 80 | var el, _i, _len, _results; 81 | _results = []; 82 | for (_i = 0, _len = candidates.length; _i < _len; _i++) { 83 | el = candidates[_i]; 84 | if (el.indexOf(prefix) === 0) { 85 | _results.push(el); 86 | } 87 | } 88 | return _results; 89 | }; 90 | getPropertyNames = function(obj) { 91 | var name, _results; 92 | _results = []; 93 | for (name in obj) { 94 | if (!__hasProp.call(obj, name)) continue; 95 | _results.push(name); 96 | } 97 | return _results; 98 | }; 99 | process.on('uncaughtException', error); 100 | if (readline.createInterface.length < 3) { 101 | repl = readline.createInterface(stdin, autocomplete); 102 | stdin.on('data', function(buffer) { 103 | return repl.write(buffer); 104 | }); 105 | } else { 106 | repl = readline.createInterface(stdin, stdout, autocomplete); 107 | } 108 | repl.setPrompt('coffee> '); 109 | repl.on('close', function() { 110 | return stdin.destroy(); 111 | }); 112 | repl.on('line', run); 113 | repl.prompt(); 114 | }).call(this); 115 | -------------------------------------------------------------------------------- /node_modules/coffee-script/lib/rewriter.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | var BALANCED_PAIRS, EXPRESSION_CLOSE, EXPRESSION_END, EXPRESSION_START, IMPLICIT_BLOCK, IMPLICIT_CALL, IMPLICIT_END, IMPLICIT_FUNC, IMPLICIT_UNSPACED_CALL, INVERSES, LINEBREAKS, SINGLE_CLOSERS, SINGLE_LINERS, left, rite, _i, _len, _ref; 3 | var __indexOf = Array.prototype.indexOf || function(item) { 4 | for (var i = 0, l = this.length; i < l; i++) { 5 | if (this[i] === item) return i; 6 | } 7 | return -1; 8 | }, __slice = Array.prototype.slice; 9 | exports.Rewriter = (function() { 10 | function Rewriter() {} 11 | Rewriter.prototype.rewrite = function(tokens) { 12 | this.tokens = tokens; 13 | this.removeLeadingNewlines(); 14 | this.removeMidExpressionNewlines(); 15 | this.closeOpenCalls(); 16 | this.closeOpenIndexes(); 17 | this.addImplicitIndentation(); 18 | this.tagPostfixConditionals(); 19 | this.addImplicitBraces(); 20 | this.addImplicitParentheses(); 21 | this.ensureBalance(BALANCED_PAIRS); 22 | this.rewriteClosingParens(); 23 | return this.tokens; 24 | }; 25 | Rewriter.prototype.scanTokens = function(block) { 26 | var i, token, tokens; 27 | tokens = this.tokens; 28 | i = 0; 29 | while (token = tokens[i]) { 30 | i += block.call(this, token, i, tokens); 31 | } 32 | return true; 33 | }; 34 | Rewriter.prototype.detectEnd = function(i, condition, action) { 35 | var levels, token, tokens, _ref, _ref2; 36 | tokens = this.tokens; 37 | levels = 0; 38 | while (token = tokens[i]) { 39 | if (levels === 0 && condition.call(this, token, i)) { 40 | return action.call(this, token, i); 41 | } 42 | if (!token || levels < 0) { 43 | return action.call(this, token, i - 1); 44 | } 45 | if (_ref = token[0], __indexOf.call(EXPRESSION_START, _ref) >= 0) { 46 | levels += 1; 47 | } else if (_ref2 = token[0], __indexOf.call(EXPRESSION_END, _ref2) >= 0) { 48 | levels -= 1; 49 | } 50 | i += 1; 51 | } 52 | return i - 1; 53 | }; 54 | Rewriter.prototype.removeLeadingNewlines = function() { 55 | var i, tag, _len, _ref; 56 | _ref = this.tokens; 57 | for (i = 0, _len = _ref.length; i < _len; i++) { 58 | tag = _ref[i][0]; 59 | if (tag !== 'TERMINATOR') { 60 | break; 61 | } 62 | } 63 | if (i) { 64 | return this.tokens.splice(0, i); 65 | } 66 | }; 67 | Rewriter.prototype.removeMidExpressionNewlines = function() { 68 | return this.scanTokens(function(token, i, tokens) { 69 | var _ref; 70 | if (!(token[0] === 'TERMINATOR' && (_ref = this.tag(i + 1), __indexOf.call(EXPRESSION_CLOSE, _ref) >= 0))) { 71 | return 1; 72 | } 73 | tokens.splice(i, 1); 74 | return 0; 75 | }); 76 | }; 77 | Rewriter.prototype.closeOpenCalls = function() { 78 | var action, condition; 79 | condition = function(token, i) { 80 | var _ref; 81 | return ((_ref = token[0]) === ')' || _ref === 'CALL_END') || token[0] === 'OUTDENT' && this.tag(i - 1) === ')'; 82 | }; 83 | action = function(token, i) { 84 | return this.tokens[token[0] === 'OUTDENT' ? i - 1 : i][0] = 'CALL_END'; 85 | }; 86 | return this.scanTokens(function(token, i) { 87 | if (token[0] === 'CALL_START') { 88 | this.detectEnd(i + 1, condition, action); 89 | } 90 | return 1; 91 | }); 92 | }; 93 | Rewriter.prototype.closeOpenIndexes = function() { 94 | var action, condition; 95 | condition = function(token, i) { 96 | var _ref; 97 | return (_ref = token[0]) === ']' || _ref === 'INDEX_END'; 98 | }; 99 | action = function(token, i) { 100 | return token[0] = 'INDEX_END'; 101 | }; 102 | return this.scanTokens(function(token, i) { 103 | if (token[0] === 'INDEX_START') { 104 | this.detectEnd(i + 1, condition, action); 105 | } 106 | return 1; 107 | }); 108 | }; 109 | Rewriter.prototype.addImplicitBraces = function() { 110 | var action, condition, stack, start, startIndent; 111 | stack = []; 112 | start = null; 113 | startIndent = 0; 114 | condition = function(token, i) { 115 | var one, tag, three, two, _ref, _ref2; 116 | _ref = this.tokens.slice(i + 1, (i + 3 + 1) || 9e9), one = _ref[0], two = _ref[1], three = _ref[2]; 117 | if ('HERECOMMENT' === (one != null ? one[0] : void 0)) { 118 | return false; 119 | } 120 | tag = token[0]; 121 | return ((tag === 'TERMINATOR' || tag === 'OUTDENT') && !((two != null ? two[0] : void 0) === ':' || (one != null ? one[0] : void 0) === '@' && (three != null ? three[0] : void 0) === ':')) || (tag === ',' && one && ((_ref2 = one[0]) !== 'IDENTIFIER' && _ref2 !== 'NUMBER' && _ref2 !== 'STRING' && _ref2 !== '@' && _ref2 !== 'TERMINATOR' && _ref2 !== 'OUTDENT')); 122 | }; 123 | action = function(token, i) { 124 | var tok; 125 | tok = ['}', '}', token[2]]; 126 | tok.generated = true; 127 | return this.tokens.splice(i, 0, tok); 128 | }; 129 | return this.scanTokens(function(token, i, tokens) { 130 | var ago, idx, tag, tok, value, _ref, _ref2; 131 | if (_ref = (tag = token[0]), __indexOf.call(EXPRESSION_START, _ref) >= 0) { 132 | stack.push([(tag === 'INDENT' && this.tag(i - 1) === '{' ? '{' : tag), i]); 133 | return 1; 134 | } 135 | if (__indexOf.call(EXPRESSION_END, tag) >= 0) { 136 | start = stack.pop(); 137 | return 1; 138 | } 139 | if (!(tag === ':' && ((ago = this.tag(i - 2)) === ':' || ((_ref2 = stack[stack.length - 1]) != null ? _ref2[0] : void 0) !== '{'))) { 140 | return 1; 141 | } 142 | stack.push(['{']); 143 | idx = ago === '@' ? i - 2 : i - 1; 144 | while (this.tag(idx - 2) === 'HERECOMMENT') { 145 | idx -= 2; 146 | } 147 | value = new String('{'); 148 | value.generated = true; 149 | tok = ['{', value, token[2]]; 150 | tok.generated = true; 151 | tokens.splice(idx, 0, tok); 152 | this.detectEnd(i + 2, condition, action); 153 | return 2; 154 | }); 155 | }; 156 | Rewriter.prototype.addImplicitParentheses = function() { 157 | var action, noCall; 158 | noCall = false; 159 | action = function(token, i) { 160 | var idx; 161 | idx = token[0] === 'OUTDENT' ? i + 1 : i; 162 | return this.tokens.splice(idx, 0, ['CALL_END', ')', token[2]]); 163 | }; 164 | return this.scanTokens(function(token, i, tokens) { 165 | var callObject, current, next, prev, seenSingle, tag, _ref, _ref2, _ref3; 166 | tag = token[0]; 167 | if (tag === 'CLASS' || tag === 'IF') { 168 | noCall = true; 169 | } 170 | _ref = tokens.slice(i - 1, (i + 1 + 1) || 9e9), prev = _ref[0], current = _ref[1], next = _ref[2]; 171 | callObject = !noCall && tag === 'INDENT' && next && next.generated && next[0] === '{' && prev && (_ref2 = prev[0], __indexOf.call(IMPLICIT_FUNC, _ref2) >= 0); 172 | seenSingle = false; 173 | if (__indexOf.call(LINEBREAKS, tag) >= 0) { 174 | noCall = false; 175 | } 176 | if (prev && !prev.spaced && tag === '?') { 177 | token.call = true; 178 | } 179 | if (token.fromThen) { 180 | return 1; 181 | } 182 | if (!(callObject || (prev != null ? prev.spaced : void 0) && (prev.call || (_ref3 = prev[0], __indexOf.call(IMPLICIT_FUNC, _ref3) >= 0)) && (__indexOf.call(IMPLICIT_CALL, tag) >= 0 || !(token.spaced || token.newLine) && __indexOf.call(IMPLICIT_UNSPACED_CALL, tag) >= 0))) { 183 | return 1; 184 | } 185 | tokens.splice(i, 0, ['CALL_START', '(', token[2]]); 186 | this.detectEnd(i + 1, function(token, i) { 187 | var post, _ref4; 188 | tag = token[0]; 189 | if (!seenSingle && token.fromThen) { 190 | return true; 191 | } 192 | if (tag === 'IF' || tag === 'ELSE' || tag === '->' || tag === '=>') { 193 | seenSingle = true; 194 | } 195 | if ((tag === '.' || tag === '?.' || tag === '::') && this.tag(i - 1) === 'OUTDENT') { 196 | return true; 197 | } 198 | return !token.generated && this.tag(i - 1) !== ',' && __indexOf.call(IMPLICIT_END, tag) >= 0 && (tag !== 'INDENT' || (this.tag(i - 2) !== 'CLASS' && (_ref4 = this.tag(i - 1), __indexOf.call(IMPLICIT_BLOCK, _ref4) < 0) && !((post = this.tokens[i + 1]) && post.generated && post[0] === '{'))); 199 | }, action); 200 | if (prev[0] === '?') { 201 | prev[0] = 'FUNC_EXIST'; 202 | } 203 | return 2; 204 | }); 205 | }; 206 | Rewriter.prototype.addImplicitIndentation = function() { 207 | return this.scanTokens(function(token, i, tokens) { 208 | var action, condition, indent, outdent, starter, tag, _ref, _ref2; 209 | tag = token[0]; 210 | if (tag === 'TERMINATOR' && this.tag(i + 1) === 'THEN') { 211 | tokens.splice(i, 1); 212 | return 0; 213 | } 214 | if (tag === 'ELSE' && this.tag(i - 1) !== 'OUTDENT') { 215 | tokens.splice.apply(tokens, [i, 0].concat(__slice.call(this.indentation(token)))); 216 | return 2; 217 | } 218 | if (tag === 'CATCH' && ((_ref = this.tag(i + 2)) === 'OUTDENT' || _ref === 'TERMINATOR' || _ref === 'FINALLY')) { 219 | tokens.splice.apply(tokens, [i + 2, 0].concat(__slice.call(this.indentation(token)))); 220 | return 4; 221 | } 222 | if (__indexOf.call(SINGLE_LINERS, tag) >= 0 && this.tag(i + 1) !== 'INDENT' && !(tag === 'ELSE' && this.tag(i + 1) === 'IF')) { 223 | starter = tag; 224 | _ref2 = this.indentation(token), indent = _ref2[0], outdent = _ref2[1]; 225 | if (starter === 'THEN') { 226 | indent.fromThen = true; 227 | } 228 | indent.generated = outdent.generated = true; 229 | tokens.splice(i + 1, 0, indent); 230 | condition = function(token, i) { 231 | var _ref3; 232 | return token[1] !== ';' && (_ref3 = token[0], __indexOf.call(SINGLE_CLOSERS, _ref3) >= 0) && !(token[0] === 'ELSE' && (starter !== 'IF' && starter !== 'THEN')); 233 | }; 234 | action = function(token, i) { 235 | return this.tokens.splice((this.tag(i - 1) === ',' ? i - 1 : i), 0, outdent); 236 | }; 237 | this.detectEnd(i + 2, condition, action); 238 | if (tag === 'THEN') { 239 | tokens.splice(i, 1); 240 | } 241 | return 1; 242 | } 243 | return 1; 244 | }); 245 | }; 246 | Rewriter.prototype.tagPostfixConditionals = function() { 247 | var condition; 248 | condition = function(token, i) { 249 | var _ref; 250 | return (_ref = token[0]) === 'TERMINATOR' || _ref === 'INDENT'; 251 | }; 252 | return this.scanTokens(function(token, i) { 253 | var original; 254 | if (token[0] !== 'IF') { 255 | return 1; 256 | } 257 | original = token; 258 | this.detectEnd(i + 1, condition, function(token, i) { 259 | if (token[0] !== 'INDENT') { 260 | return original[0] = 'POST_' + original[0]; 261 | } 262 | }); 263 | return 1; 264 | }); 265 | }; 266 | Rewriter.prototype.ensureBalance = function(pairs) { 267 | var close, level, levels, open, openLine, tag, token, _i, _j, _len, _len2, _ref, _ref2; 268 | levels = {}; 269 | openLine = {}; 270 | _ref = this.tokens; 271 | for (_i = 0, _len = _ref.length; _i < _len; _i++) { 272 | token = _ref[_i]; 273 | tag = token[0]; 274 | for (_j = 0, _len2 = pairs.length; _j < _len2; _j++) { 275 | _ref2 = pairs[_j], open = _ref2[0], close = _ref2[1]; 276 | levels[open] |= 0; 277 | if (tag === open) { 278 | if (levels[open]++ === 0) { 279 | openLine[open] = token[2]; 280 | } 281 | } else if (tag === close && --levels[open] < 0) { 282 | throw Error("too many " + token[1] + " on line " + (token[2] + 1)); 283 | } 284 | } 285 | } 286 | for (open in levels) { 287 | level = levels[open]; 288 | if (level > 0) { 289 | throw Error("unclosed " + open + " on line " + (openLine[open] + 1)); 290 | } 291 | } 292 | return this; 293 | }; 294 | Rewriter.prototype.rewriteClosingParens = function() { 295 | var debt, key, stack; 296 | stack = []; 297 | debt = {}; 298 | for (key in INVERSES) { 299 | debt[key] = 0; 300 | } 301 | return this.scanTokens(function(token, i, tokens) { 302 | var inv, match, mtag, oppos, tag, val, _ref; 303 | if (_ref = (tag = token[0]), __indexOf.call(EXPRESSION_START, _ref) >= 0) { 304 | stack.push(token); 305 | return 1; 306 | } 307 | if (__indexOf.call(EXPRESSION_END, tag) < 0) { 308 | return 1; 309 | } 310 | if (debt[inv = INVERSES[tag]] > 0) { 311 | debt[inv] -= 1; 312 | tokens.splice(i, 1); 313 | return 0; 314 | } 315 | match = stack.pop(); 316 | mtag = match[0]; 317 | oppos = INVERSES[mtag]; 318 | if (tag === oppos) { 319 | return 1; 320 | } 321 | debt[mtag] += 1; 322 | val = [oppos, mtag === 'INDENT' ? match[1] : oppos]; 323 | if (this.tag(i + 2) === mtag) { 324 | tokens.splice(i + 3, 0, val); 325 | stack.push(match); 326 | } else { 327 | tokens.splice(i, 0, val); 328 | } 329 | return 1; 330 | }); 331 | }; 332 | Rewriter.prototype.indentation = function(token) { 333 | return [['INDENT', 2, token[2]], ['OUTDENT', 2, token[2]]]; 334 | }; 335 | Rewriter.prototype.tag = function(i) { 336 | var _ref; 337 | return (_ref = this.tokens[i]) != null ? _ref[0] : void 0; 338 | }; 339 | return Rewriter; 340 | })(); 341 | BALANCED_PAIRS = [['(', ')'], ['[', ']'], ['{', '}'], ['INDENT', 'OUTDENT'], ['CALL_START', 'CALL_END'], ['PARAM_START', 'PARAM_END'], ['INDEX_START', 'INDEX_END']]; 342 | INVERSES = {}; 343 | EXPRESSION_START = []; 344 | EXPRESSION_END = []; 345 | for (_i = 0, _len = BALANCED_PAIRS.length; _i < _len; _i++) { 346 | _ref = BALANCED_PAIRS[_i], left = _ref[0], rite = _ref[1]; 347 | EXPRESSION_START.push(INVERSES[rite] = left); 348 | EXPRESSION_END.push(INVERSES[left] = rite); 349 | } 350 | EXPRESSION_CLOSE = ['CATCH', 'WHEN', 'ELSE', 'FINALLY'].concat(EXPRESSION_END); 351 | IMPLICIT_FUNC = ['IDENTIFIER', 'SUPER', ')', 'CALL_END', ']', 'INDEX_END', '@', 'THIS']; 352 | IMPLICIT_CALL = ['IDENTIFIER', 'NUMBER', 'STRING', 'JS', 'REGEX', 'NEW', 'PARAM_START', 'CLASS', 'IF', 'TRY', 'SWITCH', 'THIS', 'BOOL', 'UNARY', 'SUPER', '@', '->', '=>', '[', '(', '{', '--', '++']; 353 | IMPLICIT_UNSPACED_CALL = ['+', '-']; 354 | IMPLICIT_BLOCK = ['->', '=>', '{', '[', ',']; 355 | IMPLICIT_END = ['POST_IF', 'FOR', 'WHILE', 'UNTIL', 'WHEN', 'BY', 'LOOP', 'TERMINATOR', 'INDENT']; 356 | SINGLE_LINERS = ['ELSE', '->', '=>', 'TRY', 'FINALLY', 'THEN']; 357 | SINGLE_CLOSERS = ['TERMINATOR', 'CATCH', 'FINALLY', 'ELSE', 'OUTDENT', 'LEADING_WHEN']; 358 | LINEBREAKS = ['TERMINATOR', 'INDENT', 'OUTDENT']; 359 | }).call(this); 360 | -------------------------------------------------------------------------------- /node_modules/coffee-script/lib/scope.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | var Scope, extend, last, _ref; 3 | _ref = require('./helpers'), extend = _ref.extend, last = _ref.last; 4 | exports.Scope = Scope = (function() { 5 | Scope.root = null; 6 | function Scope(parent, expressions, method) { 7 | this.parent = parent; 8 | this.expressions = expressions; 9 | this.method = method; 10 | this.variables = [ 11 | { 12 | name: 'arguments', 13 | type: 'arguments' 14 | } 15 | ]; 16 | this.positions = {}; 17 | if (!this.parent) { 18 | Scope.root = this; 19 | } 20 | } 21 | Scope.prototype.add = function(name, type, immediate) { 22 | var pos; 23 | if (this.shared && !immediate) { 24 | return this.parent.add(name, type, immediate); 25 | } 26 | if (typeof (pos = this.positions[name]) === 'number') { 27 | return this.variables[pos].type = type; 28 | } else { 29 | return this.positions[name] = this.variables.push({ 30 | name: name, 31 | type: type 32 | }) - 1; 33 | } 34 | }; 35 | Scope.prototype.find = function(name, options) { 36 | if (this.check(name, options)) { 37 | return true; 38 | } 39 | this.add(name, 'var'); 40 | return false; 41 | }; 42 | Scope.prototype.parameter = function(name) { 43 | if (this.shared && this.parent.check(name, true)) { 44 | return; 45 | } 46 | return this.add(name, 'param'); 47 | }; 48 | Scope.prototype.check = function(name, immediate) { 49 | var found, _ref2; 50 | found = !!this.type(name); 51 | if (found || immediate) { 52 | return found; 53 | } 54 | return !!((_ref2 = this.parent) != null ? _ref2.check(name) : void 0); 55 | }; 56 | Scope.prototype.temporary = function(name, index) { 57 | if (name.length > 1) { 58 | return '_' + name + (index > 1 ? index : ''); 59 | } else { 60 | return '_' + (index + parseInt(name, 36)).toString(36).replace(/\d/g, 'a'); 61 | } 62 | }; 63 | Scope.prototype.type = function(name) { 64 | var v, _i, _len, _ref2; 65 | _ref2 = this.variables; 66 | for (_i = 0, _len = _ref2.length; _i < _len; _i++) { 67 | v = _ref2[_i]; 68 | if (v.name === name) { 69 | return v.type; 70 | } 71 | } 72 | return null; 73 | }; 74 | Scope.prototype.freeVariable = function(type) { 75 | var index, temp; 76 | index = 0; 77 | while (this.check((temp = this.temporary(type, index)))) { 78 | index++; 79 | } 80 | this.add(temp, 'var', true); 81 | return temp; 82 | }; 83 | Scope.prototype.assign = function(name, value) { 84 | this.add(name, { 85 | value: value, 86 | assigned: true 87 | }); 88 | return this.hasAssignments = true; 89 | }; 90 | Scope.prototype.hasDeclarations = function() { 91 | return !!this.declaredVariables().length; 92 | }; 93 | Scope.prototype.declaredVariables = function() { 94 | var realVars, tempVars, v, _i, _len, _ref2; 95 | realVars = []; 96 | tempVars = []; 97 | _ref2 = this.variables; 98 | for (_i = 0, _len = _ref2.length; _i < _len; _i++) { 99 | v = _ref2[_i]; 100 | if (v.type === 'var') { 101 | (v.name.charAt(0) === '_' ? tempVars : realVars).push(v.name); 102 | } 103 | } 104 | return realVars.sort().concat(tempVars.sort()); 105 | }; 106 | Scope.prototype.assignedVariables = function() { 107 | var v, _i, _len, _ref2, _results; 108 | _ref2 = this.variables; 109 | _results = []; 110 | for (_i = 0, _len = _ref2.length; _i < _len; _i++) { 111 | v = _ref2[_i]; 112 | if (v.type.assigned) { 113 | _results.push("" + v.name + " = " + v.type.value); 114 | } 115 | } 116 | return _results; 117 | }; 118 | return Scope; 119 | })(); 120 | }).call(this); 121 | -------------------------------------------------------------------------------- /node_modules/coffee-script/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "coffee-script", 3 | "description": "Unfancy JavaScript", 4 | "keywords": ["javascript", "language", "coffeescript", "compiler"], 5 | "author": "Jeremy Ashkenas", 6 | "version": "1.1.1", 7 | "licenses": [{ 8 | "type": "MIT", 9 | "url": "http://github.com/jashkenas/coffee-script/raw/master/LICENSE" 10 | }], 11 | "engines": { 12 | "node": ">=0.2.5" 13 | }, 14 | "directories" : { 15 | "lib" : "./lib" 16 | }, 17 | "main" : "./lib/coffee-script", 18 | "bin": { 19 | "coffee": "./bin/coffee", 20 | "cake": "./bin/cake" 21 | }, 22 | "homepage": "http://coffeescript.org", 23 | "repository": { 24 | "type": "git", 25 | "url": "git://github.com/jashkenas/coffee-script.git" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /node_modules/vows/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2009 cloudhead 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /node_modules/vows/Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # Run all tests 3 | # 4 | test: 5 | @@bin/vows test/* 6 | 7 | .PHONY: test install 8 | -------------------------------------------------------------------------------- /node_modules/vows/README.md: -------------------------------------------------------------------------------- 1 | Vows 2 | ==== 3 | 4 | > Asynchronous BDD & continuous integration for node.js 5 | 6 | #### # 7 | 8 | introduction 9 | ------------ 10 | There are two reasons why we might want asynchronous testing. The first, and obvious reason is that node.js is asynchronous, and therefore our tests need to be. The second reason is to make test suites which target I/O libraries run much faster. 11 | 12 | _Vows_ is an experiment in making this possible, while adding a minimum of overhead. 13 | 14 | synopsis 15 | -------- 16 | 17 | var vows = require('vows'), 18 | assert = require('assert'); 19 | 20 | vows.describe('Deep Thought').addBatch({ 21 | 'An instance of DeepThought': { 22 | topic: new DeepThought, 23 | 24 | 'should know the answer to the ultimate question of life': function (deepThought) { 25 | assert.equal (deepThought.question('what is the answer to the universe?'), 42); 26 | } 27 | } 28 | }); 29 | 30 | installation 31 | ------------ 32 | 33 | $ npm install vows 34 | 35 | documentation 36 | ------------- 37 | 38 | Head over to 39 | 40 | -------------------------------------------------------------------------------- /node_modules/vows/bin/vows: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var path = require('path'), 4 | fs = require('fs'), 5 | util = require('util'), 6 | events = require('events'); 7 | 8 | // 9 | // Attempt to load Coffee-Script. If it's not available, continue on our 10 | // merry way, if it is available, set it up so we can include `*.coffee` 11 | // scripts and start searching for them. 12 | // 13 | var fileExt, specFileExt; 14 | 15 | try { 16 | var coffee = require('coffee-script'); 17 | if (require.extensions) { 18 | require.extensions['.coffee'] = function (module, filename) { 19 | var content = coffee.compile(fs.readFileSync(filename, 'utf8')); 20 | return module._compile(content, filename); 21 | }; 22 | } else { 23 | require.registerExtension('.coffee', function (content) { return coffee.compile(content) }); 24 | } 25 | fileExt = /\.(js|coffee)$/; 26 | specFileExt = /[.-](test|spec)\.(js|coffee)$/; 27 | } catch (_) { 28 | fileExt = /\.js$/; 29 | specFileExt = /[.-](test|spec)\.js$/; 30 | } 31 | 32 | var inspect = require('eyes').inspector({ 33 | stream: null, 34 | styles: { string: 'grey', regexp: 'grey' } 35 | }); 36 | 37 | require.paths.unshift(path.join(__dirname, '..', 'lib')); 38 | 39 | var vows = require('vows'); 40 | var cutils = require('vows/console'); 41 | var stylize = require('vows/console').stylize; 42 | var _reporter = require('vows/reporters/dot-matrix'), reporter = { 43 | name: _reporter.name, 44 | }; 45 | 46 | var help = [ 47 | "usage: vows [FILE, ...] [options]", 48 | "", 49 | "options:", 50 | " -v, --verbose Enable verbose output", 51 | " -w, --watch Watch mode", 52 | " -s, --silent Don't report", 53 | " -m PATTERN Only run tests matching the PATTERN string", 54 | " -r PATTERN Only run tests matching the PATTERN regexp", 55 | " --json Use JSON reporter", 56 | " --spec Use Spec reporter", 57 | " --dot-matrix Use Dot-Matrix reporter", 58 | " --xunit Use xUnit reporter", 59 | //" --no-color Don't use terminal colors", 60 | " --version Show version", 61 | " -h, --help You're staring at it" 62 | ].join('\n'); 63 | 64 | var options = { 65 | reporter: reporter, 66 | matcher: /.*/, 67 | watch: false 68 | }; 69 | 70 | var files = []; 71 | 72 | // Get rid of process runner 73 | // ('node' in most cases) 74 | var arg, args = [], argv = process.argv.slice(2); 75 | 76 | // Current directory index, 77 | // and path of test folder. 78 | var root, testFolder; 79 | 80 | // 81 | // Parse command-line parameters 82 | // 83 | while (arg = argv.shift()) { 84 | if (arg === __filename) { continue } 85 | 86 | if (arg[0] !== '-') { 87 | args.push(arg); 88 | } else { 89 | arg = arg.match(/^--?(.+)/)[1]; 90 | 91 | if (arg[0] === 'r') { 92 | options.matcher = new(RegExp)(argv.shift()); 93 | } else if (arg[0] === 'm') { 94 | options.matcher = (function (str) { // Create an escaped RegExp 95 | var specials = '. * + ? | ( ) [ ] { } \\ ^ ? ! = : $'.split(' ').join('|\\'), 96 | regex = new(RegExp)('(\\' + specials + ')', 'g'); 97 | return new(RegExp)(str.replace(regex, '\\$1')); 98 | })(argv.shift()); 99 | } else if (arg in options) { 100 | options[arg] = true; 101 | } else { 102 | switch (arg) { 103 | case 'json': 104 | _reporter = require('vows/reporters/json'); 105 | break; 106 | case 'spec': 107 | _reporter = require('vows/reporters/spec'); 108 | break; 109 | case 'dot-matrix': 110 | _reporter = require('vows/reporters/dot-matrix'); 111 | break; 112 | case 'silent': 113 | case 's': 114 | _reporter = require('vows/reporters/silent'); 115 | break; 116 | case 'xunit': 117 | _reporter = require('vows/reporters/xunit'); 118 | break; 119 | case 'verbose': 120 | case 'v': 121 | options.verbose = true; 122 | break; 123 | case 'watch': 124 | case 'w': 125 | options.watch = true; 126 | break; 127 | case 'no-color': 128 | options.nocolor = true; 129 | break; 130 | case 'no-error': 131 | options.error = false; 132 | break; 133 | case 'version': 134 | console.log('vows ' + vows.version); 135 | process.exit(0); 136 | case 'help': 137 | case 'h': 138 | console.log(help); 139 | process.exit(0); 140 | break; 141 | } 142 | } 143 | } 144 | } 145 | 146 | if (options.watch) { 147 | options.reporter = reporter = require('vows/reporters/watch'); 148 | } 149 | 150 | msg('bin', 'argv', args); 151 | msg('bin', 'options', { reporter: options.reporter.name, matcher: options.matcher }); 152 | 153 | if (args.length === 0 || options.watch) { 154 | msg('bin', 'discovering', 'folder structure'); 155 | root = fs.readdirSync('.'); 156 | 157 | if (root.indexOf('test') !== -1) { 158 | testFolder = 'test'; 159 | } else if (root.indexOf('spec') !== -1) { 160 | testFolder = 'spec'; 161 | } else { 162 | abort("runner", "couldn't find test folder"); 163 | } 164 | msg('bin', 'discovered', "./" + testFolder); 165 | 166 | if (args.length === 0) { 167 | args = paths(testFolder).filter(function (f) { 168 | return new(RegExp)('-' + testFolder + '.(js|coffee)$').test(f); 169 | }); 170 | 171 | if (options.watch) { 172 | args = args.concat(paths('lib'), 173 | paths('src')); 174 | } 175 | } 176 | } 177 | 178 | if (! options.watch) { 179 | reporter.report = function (data, filename) { 180 | switch (data[0]) { 181 | case 'subject': 182 | case 'vow': 183 | case 'context': 184 | case 'error': 185 | _reporter.report(data, filename); 186 | break; 187 | case 'end': 188 | (options.verbose || _reporter.name === 'json') && _reporter.report(data); 189 | break; 190 | case 'finish': 191 | options.verbose ? _reporter.print('\n') : _reporter.print(' '); 192 | break; 193 | } 194 | }; 195 | reporter.reset = function () { _reporter.reset && _reporter.reset() }; 196 | reporter.print = _reporter.print; 197 | 198 | files = args.map(function (a) { 199 | return path.join(process.cwd(), a.replace(fileExt, '')); 200 | }); 201 | 202 | runSuites(importSuites(files), function (results) { 203 | var status = results.errored ? 2 : (results.broken ? 1 : 0); 204 | 205 | !options.verbose && _reporter.print('\n'); 206 | msg('runner', 'finish'); 207 | _reporter.report(['finish', results], { 208 | write: function (str) { 209 | util.print(str.replace(/^\n\n/, '\n')); 210 | } 211 | }); 212 | if (process.stdout.write('')) { // Check if stdout is drained 213 | process.exit(status); 214 | } else { 215 | process.stdout.on('drain', function () { 216 | process.exit(status); 217 | }); 218 | } 219 | }); 220 | } else { 221 | // 222 | // Watch mode 223 | // 224 | (function () { 225 | var pendulum = [ 226 | '. ', '.. ', '... ', ' ...', 227 | ' ..', ' .', ' .', ' ..', 228 | '... ', '.. ', '. ' 229 | ]; 230 | var strobe = ['.', ' ']; 231 | var status, 232 | cue, 233 | current = 0, 234 | running = 0, 235 | lastRun, 236 | colors = ['32m', '33m', '31m'], 237 | timer = setInterval(tick, 100); 238 | 239 | process.on('uncaughtException', cleanup); 240 | process.on('exit', cleanup); 241 | process.on('SIGINT', function () { 242 | process.exit(0); 243 | }); 244 | process.on('SIGQUIT', function () { 245 | changed(); 246 | }); 247 | 248 | cursorHide(); 249 | 250 | // Run every 100ms 251 | function tick() { 252 | if (running && (cue !== strobe)) { 253 | cue = strobe, current = 0; 254 | } else if (!running && (cue !== pendulum)) { 255 | cue = pendulum, current = 0; 256 | } 257 | 258 | eraseLine(); 259 | lastRun && !running && esc(colors[status.errored ? 2 : (status.broken ? 1 : 0)]); 260 | print(cue[current]); 261 | 262 | if (current == cue.length - 1) { current = -1 } 263 | 264 | current ++; 265 | esc('39m'); 266 | cursorRestore(); 267 | } 268 | 269 | // 270 | // Utility functions 271 | // 272 | function print(str) { util.print(str) } 273 | function esc(str) { print("\x1b[" + str) } 274 | function eraseLine() { esc("0K") } 275 | function cursorRestore() { esc("0G") } 276 | function cursorHide() { esc("?25l") } 277 | function cursorShow() { esc("?25h") } 278 | function cleanup() { eraseLine(), cursorShow(), clearInterval(timer), print('\n') } 279 | 280 | // 281 | // Called when a file has been modified. 282 | // Run the matching tests and change the status. 283 | // 284 | function changed(file) { 285 | status = { honored: 0, broken: 0, errored: 0, pending: 0 }; 286 | 287 | msg('watcher', 'detected change in', file); 288 | 289 | file = (specFileExt.test(file) ? path.join(testFolder, file) 290 | : path.join(testFolder, file + '-' + testFolder)); 291 | 292 | try { 293 | fs.statSync(file); 294 | } catch (e) { 295 | msg('watcher', 'no equivalence found, running all tests.'); 296 | file = null; 297 | } 298 | 299 | var files = (specFileExt.test(file) ? [file] : paths(testFolder)).map(function (p) { 300 | return path.join(process.cwd(), p); 301 | }).map(function (p) { 302 | var cache = require.main.moduleCache || require.cache; 303 | if (cache[p]) { delete(cache[p]) } 304 | return p; 305 | }).map(function (p) { 306 | return p.replace(fileExt, ''); 307 | }); 308 | 309 | running ++; 310 | 311 | runSuites(importSuites(files), function (results) { 312 | delete(results.time); 313 | print(cutils.result(results).join('') + '\n\n'); 314 | lastRun = new(Date); 315 | status = results; 316 | running --; 317 | }); 318 | } 319 | 320 | msg('watcher', 'watching', args); 321 | 322 | // 323 | // Watch all relevant files, 324 | // and call `changed()` on change. 325 | // 326 | args.forEach(function (p) { 327 | fs.watchFile(p, function (current, previous) { 328 | if (new(Date)(current.mtime).valueOf() === 329 | new(Date)(previous.mtime).valueOf()) { return } 330 | else { 331 | changed(p); 332 | } 333 | }); 334 | }); 335 | })(); 336 | } 337 | 338 | function runSuites(suites, callback) { 339 | var results = { 340 | honored: 0, 341 | broken: 0, 342 | errored: 0, 343 | pending: 0, 344 | total: 0, 345 | time: 0 346 | }; 347 | reporter.reset(); 348 | 349 | (function run(suites, callback) { 350 | var suite = suites.shift(); 351 | if (suite) { 352 | msg('runner', "running", suite.subject + ' ', options.watch ? false : true); 353 | suite.run(options, function (result) { 354 | Object.keys(result).forEach(function (k) { 355 | results[k] += result[k]; 356 | }); 357 | run(suites, callback); 358 | }); 359 | } else { 360 | callback(results); 361 | } 362 | })(suites, callback); 363 | } 364 | 365 | function importSuites(files) { 366 | msg(options.watcher ? 'watcher' : 'runner', 'loading', files); 367 | 368 | return files.reduce(function (suites, f) { 369 | var obj = require(f); 370 | return suites.concat(Object.keys(obj).map(function (s) { 371 | obj[s]._filename = f.replace(process.cwd() + '/', '') + '.js'; 372 | return obj[s]; 373 | })); 374 | }, []) 375 | } 376 | 377 | // 378 | // Recursively traverse a hierarchy, returning 379 | // a list of all relevant .js files. 380 | // 381 | function paths(dir) { 382 | var paths = []; 383 | 384 | try { fs.statSync(dir) } 385 | catch (e) { return [] } 386 | 387 | (function traverse(dir, stack) { 388 | stack.push(dir); 389 | fs.readdirSync(stack.join('/')).forEach(function (file) { 390 | var path = stack.concat([file]).join('/'), 391 | stat = fs.statSync(path); 392 | 393 | if (file[0] == '.' || file === 'vendor') { 394 | return; 395 | } else if (stat.isFile() && fileExt.test(file)) { 396 | paths.push(path); 397 | } else if (stat.isDirectory()) { 398 | traverse(file, stack); 399 | } 400 | }); 401 | stack.pop(); 402 | })(dir || '.', []); 403 | 404 | return paths; 405 | } 406 | 407 | function msg(cmd, subject, str, p) { 408 | if (options.verbose) { 409 | util[p ? 'print' : 'puts']( stylize('vows ', 'green') 410 | + stylize(cmd, 'bold') 411 | + ' ' + subject + ' ' 412 | + (str ? (typeof(str) === 'string' ? str : inspect(str)) : '') 413 | ); 414 | } 415 | } 416 | 417 | function abort(cmd, str) { 418 | console.log(stylize('vows ', 'red') + stylize(cmd, 'bold') + ' ' + str); 419 | console.log(stylize('vows ', 'red') + stylize(cmd, 'bold') + ' exiting'); 420 | process.exit(-1); 421 | } 422 | -------------------------------------------------------------------------------- /node_modules/vows/lib/assert/error.js: -------------------------------------------------------------------------------- 1 | var stylize = require('vows/console').stylize; 2 | var inspect = require('vows/console').inspect; 3 | 4 | require('assert').AssertionError.prototype.toString = function () { 5 | var that = this, 6 | source = this.stack.match(/([a-zA-Z0-9._-]+\.js)(:\d+):\d+/); 7 | 8 | function parse(str) { 9 | return str.replace(/{actual}/g, inspect(that.actual)). 10 | replace(/{operator}/g, stylize(that.operator, 'bold')). 11 | replace(/{expected}/g, (that.expected instanceof Function) 12 | ? that.expected.name 13 | : inspect(that.expected)); 14 | } 15 | 16 | if (this.message) { 17 | return stylize(parse(this.message), 'yellow') + 18 | stylize(' // ' + source[1] + source[2], 'grey'); 19 | } else { 20 | return stylize([ 21 | this.expected, 22 | this.operator, 23 | this.actual 24 | ].join(' '), 'yellow'); 25 | } 26 | }; 27 | 28 | -------------------------------------------------------------------------------- /node_modules/vows/lib/assert/macros.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'); 2 | 3 | var messages = { 4 | 'equal' : "expected {expected},\n\tgot\t {actual} ({operator})", 5 | 'notEqual' : "didn't expect {actual} ({operator})" 6 | }; 7 | messages['strictEqual'] = messages['deepEqual'] = messages['equal']; 8 | messages['notStrictEqual'] = messages['notDeepEqual'] = messages['notEqual']; 9 | 10 | for (var key in messages) { 11 | assert[key] = (function (key, callback) { 12 | return function (actual, expected, message) { 13 | callback(actual, expected, message || messages[key]); 14 | }; 15 | })(key, assert[key]); 16 | } 17 | 18 | assert.ok = (function (callback) { 19 | return function (actual, message) { 20 | callback(actual, message || "expected expression to evaluate to {expected}, but was {actual}"); 21 | }; 22 | })(assert.ok); 23 | 24 | assert.match = function (actual, expected, message) { 25 | if (! expected.test(actual)) { 26 | assert.fail(actual, expected, message || "expected {actual} to match {expected}", "match", assert.match); 27 | } 28 | }; 29 | assert.matches = assert.match; 30 | 31 | assert.isTrue = function (actual, message) { 32 | if (actual !== true) { 33 | assert.fail(actual, true, message || "expected {expected}, got {actual}", "===", assert.isTrue); 34 | } 35 | }; 36 | assert.isFalse = function (actual, message) { 37 | if (actual !== false) { 38 | assert.fail(actual, false, message || "expected {expected}, got {actual}", "===", assert.isFalse); 39 | } 40 | }; 41 | assert.isZero = function (actual, message) { 42 | if (actual !== 0) { 43 | assert.fail(actual, 0, message || "expected {expected}, got {actual}", "===", assert.isZero); 44 | } 45 | }; 46 | assert.isNotZero = function (actual, message) { 47 | if (actual === 0) { 48 | assert.fail(actual, 0, message || "expected non-zero value, got {actual}", "===", assert.isNotZero); 49 | } 50 | }; 51 | 52 | assert.greater = function (actual, expected, message) { 53 | if (actual <= expected) { 54 | assert.fail(actual, expected, message || "expected {actual} to be greater than {expected}", ">", assert.greater); 55 | } 56 | }; 57 | assert.lesser = function (actual, expected, message) { 58 | if (actual >= expected) { 59 | assert.fail(actual, expected, message || "expected {actual} to be lesser than {expected}", "<", assert.lesser); 60 | } 61 | }; 62 | 63 | // 64 | // Inclusion 65 | // 66 | assert.include = function (actual, expected, message) { 67 | if ((function (obj) { 68 | if (isArray(obj) || isString(obj)) { 69 | return obj.indexOf(expected) === -1; 70 | } else if (isObject(actual)) { 71 | return ! obj.hasOwnProperty(expected); 72 | } 73 | return false; 74 | })(actual)) { 75 | assert.fail(actual, expected, message || "expected {actual} to include {expected}", "include", assert.include); 76 | } 77 | }; 78 | assert.includes = assert.include; 79 | 80 | // 81 | // Length 82 | // 83 | assert.isEmpty = function (actual, message) { 84 | if ((isObject(actual) && Object.keys(actual).length > 0) || actual.length > 0) { 85 | assert.fail(actual, 0, message || "expected {actual} to be empty", "length", assert.isEmpty); 86 | } 87 | }; 88 | 89 | assert.length = function (actual, expected, message) { 90 | if (actual.length !== expected) { 91 | assert.fail(actual, expected, message || "expected {actual} to have {expected} element(s)", "length", assert.length); 92 | } 93 | }; 94 | 95 | // 96 | // Type 97 | // 98 | assert.isArray = function (actual, message) { 99 | assertTypeOf(actual, 'array', message || "expected {actual} to be an Array", assert.isArray); 100 | }; 101 | assert.isObject = function (actual, message) { 102 | assertTypeOf(actual, 'object', message || "expected {actual} to be an Object", assert.isObject); 103 | }; 104 | assert.isNumber = function (actual, message) { 105 | if (isNaN(actual)) { 106 | assert.fail(actual, 'number', message || "expected {actual} to be of type {expected}", "isNaN", assert.isNumber); 107 | } else { 108 | assertTypeOf(actual, 'number', message || "expected {actual} to be a Number", assert.isNumber); 109 | } 110 | }; 111 | assert.isNaN = function (actual, message) { 112 | if (actual === actual) { 113 | assert.fail(actual, 'NaN', message || "expected {actual} to be NaN", "===", assert.isNaN); 114 | } 115 | }; 116 | assert.isNull = function (actual, message) { 117 | if (actual !== null) { 118 | assert.fail(actual, null, message || "expected {expected}, got {actual}", "===", assert.isNull); 119 | } 120 | }; 121 | assert.isNotNull = function (actual, message) { 122 | if (actual === null) { 123 | assert.fail(actual, null, message || "expected non-null value, got {actual}", "===", assert.isNotNull); 124 | } 125 | }; 126 | assert.isUndefined = function (actual, message) { 127 | if (actual !== undefined) { 128 | assert.fail(actual, undefined, message || "expected {actual} to be {expected}", "===", assert.isUndefined); 129 | } 130 | }; 131 | assert.isString = function (actual, message) { 132 | assertTypeOf(actual, 'string', message || "expected {actual} to be a String", assert.isString); 133 | }; 134 | assert.isFunction = function (actual, message) { 135 | assertTypeOf(actual, 'function', message || "expected {actual} to be a Function", assert.isFunction); 136 | }; 137 | assert.typeOf = function (actual, expected, message) { 138 | assertTypeOf(actual, expected, message, assert.typeOf); 139 | }; 140 | assert.instanceOf = function (actual, expected, message) { 141 | if (! (actual instanceof expected)) { 142 | assert.fail(actual, expected, message || "expected {actual} to be an instance of {expected}", "instanceof", assert.instanceOf); 143 | } 144 | }; 145 | 146 | // 147 | // Utility functions 148 | // 149 | function assertTypeOf(actual, expected, message, caller) { 150 | if (typeOf(actual) !== expected) { 151 | assert.fail(actual, expected, message || "expected {actual} to be of type {expected}", "typeOf", caller); 152 | } 153 | }; 154 | 155 | function isArray (obj) { 156 | return Array.isArray(obj); 157 | } 158 | 159 | function isString (obj) { 160 | return typeof(obj) === 'string' || obj instanceof String; 161 | } 162 | 163 | function isObject (obj) { 164 | return typeof(obj) === 'object' && obj && !isArray(obj); 165 | } 166 | 167 | // A better `typeof` 168 | function typeOf(value) { 169 | var s = typeof(value), 170 | types = [Object, Array, String, RegExp, Number, Function, Boolean, Date]; 171 | 172 | if (s === 'object' || s === 'function') { 173 | if (value) { 174 | types.forEach(function (t) { 175 | if (value instanceof t) { s = t.name.toLowerCase() } 176 | }); 177 | } else { s = 'null' } 178 | } 179 | return s; 180 | } 181 | -------------------------------------------------------------------------------- /node_modules/vows/lib/vows.js: -------------------------------------------------------------------------------- 1 | // 2 | // Vows.js - asynchronous event-based BDD for node.js 3 | // 4 | // usage: 5 | // 6 | // var vows = require('vows'); 7 | // 8 | // vows.describe('Deep Thought').addBatch({ 9 | // "An instance of DeepThought": { 10 | // topic: new DeepThought, 11 | // 12 | // "should know the answer to the ultimate question of life": function (deepThought) { 13 | // assert.equal (deepThought.question('what is the answer to the universe?'), 42); 14 | // } 15 | // } 16 | // }).run(); 17 | // 18 | var sys = require('sys'), 19 | path = require('path'), 20 | events = require('events'), 21 | vows = exports; 22 | 23 | require.paths.unshift(__dirname); 24 | 25 | // Options 26 | vows.options = { 27 | Emitter: events.EventEmitter, 28 | reporter: require('vows/reporters/dot-matrix'), 29 | matcher: /.*/, 30 | error: true // Handle "error" event 31 | }; 32 | 33 | vows.__defineGetter__('reporter', function () { 34 | return vows.options.reporter; 35 | }); 36 | 37 | var stylize = require('vows/console').stylize; 38 | var console = require('vows/console'); 39 | 40 | vows.inspect = require('vows/console').inspect; 41 | vows.prepare = require('vows/extras').prepare; 42 | vows.tryEnd = require('vows/suite').tryEnd; 43 | 44 | // 45 | // Assertion Macros & Extensions 46 | // 47 | require('./assert/error'); 48 | require('./assert/macros'); 49 | 50 | // 51 | // Suite constructor 52 | // 53 | var Suite = require('vows/suite').Suite; 54 | 55 | // 56 | // This function gets added to events.EventEmitter.prototype, by default. 57 | // It's essentially a wrapper around `on`, which adds all the specification 58 | // goodness. 59 | // 60 | function addVow(vow) { 61 | var batch = vow.batch; 62 | 63 | batch.total ++; 64 | batch.vows.push(vow); 65 | 66 | return this.on("success", function () { 67 | var args = Array.prototype.slice.call(arguments); 68 | // If the callback is expecting two or more arguments, 69 | // pass the error as the first (null) and the result after. 70 | if (vow.callback.length >= 2 && batch.suite.options.error) { 71 | args.unshift(null); 72 | } 73 | runTest(args, this.ctx); 74 | vows.tryEnd(batch); 75 | 76 | }).on("error", function (err) { 77 | if (vow.callback.length >= 2 || !batch.suite.options.error) { 78 | runTest([err], this.ctx); 79 | } else { 80 | output('errored', { type: 'promise', error: err.stack || err.message || JSON.stringify(err) }); 81 | } 82 | vows.tryEnd(batch); 83 | }); 84 | 85 | function runTest(args, ctx) { 86 | var topic, status; 87 | 88 | if (vow.callback instanceof String) { 89 | return output('pending'); 90 | } 91 | 92 | // Run the test, and try to catch `AssertionError`s and other exceptions; 93 | // increment counters accordingly. 94 | try { 95 | vow.callback.apply(ctx === global || !ctx ? vow.binding : ctx, args); 96 | output('honored'); 97 | } catch (e) { 98 | if (e.name && e.name.match(/AssertionError/)) { 99 | output('broken', e.toString()); 100 | } else { 101 | output('errored', e.stack || e.message || e); 102 | } 103 | } 104 | } 105 | 106 | function output(status, exception) { 107 | batch[status] ++; 108 | vow.status = status; 109 | 110 | if (vow.context && batch.lastContext !== vow.context) { 111 | batch.lastContext = vow.context; 112 | batch.suite.report(['context', vow.context]); 113 | } 114 | batch.suite.report(['vow', { 115 | title: vow.description, 116 | context: vow.context, 117 | status: status, 118 | exception: exception || null 119 | }]); 120 | } 121 | }; 122 | 123 | // 124 | // On exit, check that all promises have been fired. 125 | // If not, report an error message. 126 | // 127 | process.on('exit', function () { 128 | var results = { honored: 0, broken: 0, errored: 0, pending: 0, total: 0 }, failure; 129 | 130 | vows.suites.forEach(function (s) { 131 | if ((s.results.total > 0) && (s.results.time === null)) { 132 | s.reporter.print('\n\n'); 133 | s.reporter.report(['error', { error: "Asynchronous Error", suite: s }]); 134 | } 135 | s.batches.forEach(function (b) { 136 | var unFired = []; 137 | 138 | b.vows.forEach(function (vow) { 139 | if (! vow.status) { 140 | if (unFired.indexOf(vow.context) === -1) { 141 | unFired.push(vow.context); 142 | } 143 | } 144 | }); 145 | 146 | if (unFired.length > 0) { sys.print('\n') } 147 | 148 | unFired.forEach(function (title) { 149 | s.reporter.report(['error', { 150 | error: "callback not fired", 151 | context: title, 152 | batch: b, 153 | suite: s 154 | }]); 155 | }); 156 | 157 | if (b.status === 'begin') { 158 | failure = true; 159 | results.errored ++; 160 | results.total ++; 161 | } 162 | Object.keys(results).forEach(function (k) { results[k] += b[k] }); 163 | }); 164 | }); 165 | if (failure) { 166 | sys.puts(console.result(results)); 167 | } 168 | }); 169 | 170 | vows.suites = []; 171 | 172 | // 173 | // Create a new test suite 174 | // 175 | vows.describe = function (subject) { 176 | var suite = new(Suite)(subject); 177 | 178 | this.options.Emitter.prototype.addVow = addVow; 179 | this.suites.push(suite); 180 | 181 | // 182 | // Add any additional arguments as batches if they're present 183 | // 184 | if (arguments.length > 1) { 185 | for (var i = 1, l = arguments.length; i < l; ++i) { 186 | suite.addBatch(arguments[i]); 187 | } 188 | } 189 | 190 | return suite; 191 | }; 192 | 193 | 194 | vows.version = require('fs').readFileSync(path.join(__dirname, '..', 'package.json')) 195 | .toString().match(/"version"\s*:\s*"([\d.]+)"/)[1]; 196 | -------------------------------------------------------------------------------- /node_modules/vows/lib/vows/console.js: -------------------------------------------------------------------------------- 1 | var eyes = require('eyes').inspector({ stream: null, styles: false }); 2 | 3 | // Stylize a string 4 | this.stylize = function stylize(str, style) { 5 | var styles = { 6 | 'bold' : [1, 22], 7 | 'italic' : [3, 23], 8 | 'underline' : [4, 24], 9 | 'cyan' : [96, 39], 10 | 'yellow' : [33, 39], 11 | 'green' : [32, 39], 12 | 'red' : [31, 39], 13 | 'grey' : [90, 39], 14 | 'green-hi' : [92, 32], 15 | }; 16 | return '\033[' + styles[style][0] + 'm' + str + 17 | '\033[' + styles[style][1] + 'm'; 18 | }; 19 | 20 | var $ = this.$ = function (str) { 21 | str = new(String)(str); 22 | 23 | ['bold', 'grey', 'yellow', 'red', 'green', 'white', 'cyan', 'italic'].forEach(function (style) { 24 | Object.defineProperty(str, style, { 25 | get: function () { 26 | return exports.$(exports.stylize(this, style)); 27 | } 28 | }); 29 | }); 30 | return str; 31 | }; 32 | 33 | this.puts = function (options) { 34 | var stylize = exports.stylize; 35 | return function (args) { 36 | args = Array.prototype.slice.call(arguments).map(function (a) { 37 | return a.replace(/`([^`]+)`/g, function (_, capture) { return stylize(capture, 'italic') }) 38 | .replace(/\*([^*]+)\*/g, function (_, capture) { return stylize(capture, 'bold') }); 39 | }); 40 | return options.stream.write(args.join('\n') + '\n'); 41 | }; 42 | }; 43 | 44 | this.result = function (event) { 45 | var result = [], buffer = [], time = '', header; 46 | var complete = event.honored + event.pending + event.errored + event.broken; 47 | var status = (event.errored && 'errored') || (event.broken && 'broken') || 48 | (event.honored && 'honored') || (event.pending && 'pending'); 49 | 50 | if (event.total === 0) { 51 | return [$("Could not find any tests to run.").bold.red]; 52 | } 53 | 54 | event.honored && result.push($(event.honored).bold + " honored"); 55 | event.broken && result.push($(event.broken).bold + " broken"); 56 | event.errored && result.push($(event.errored).bold + " errored"); 57 | event.pending && result.push($(event.pending).bold + " pending"); 58 | 59 | if (complete < event.total) { 60 | result.push($(event.total - complete).bold + " dropped"); 61 | } 62 | 63 | result = result.join(' ∙ '); 64 | 65 | header = { 66 | honored: '✓ ' + $('OK').bold.green, 67 | broken: '✗ ' + $('Broken').bold.yellow, 68 | errored: '✗ ' + $('Errored').bold.red, 69 | pending: '- ' + $('Pending').bold.cyan 70 | }[status] + ' » '; 71 | 72 | if (typeof(event.time) === 'number') { 73 | time = ' (' + event.time.toFixed(3) + 's)'; 74 | time = this.stylize(time, 'grey'); 75 | } 76 | buffer.push(header + result + time); 77 | 78 | return buffer; 79 | }; 80 | 81 | this.inspect = function inspect(val) { 82 | return '\033[1m' + eyes(val) + '\033[22m'; 83 | }; 84 | 85 | this.error = function (obj) { 86 | var string = '✗ ' + $('Errored ').red + '» '; 87 | string += $(obj.error).red.bold + '\n'; 88 | string += (obj.context ? ' in ' + $(obj.context).red + '\n': ''); 89 | string += ' in ' + $(obj.suite.subject).red + '\n'; 90 | string += ' in ' + $(obj.suite._filename).red; 91 | 92 | return string; 93 | }; 94 | -------------------------------------------------------------------------------- /node_modules/vows/lib/vows/context.js: -------------------------------------------------------------------------------- 1 | 2 | this.Context = function (vow, ctx, env) { 3 | var that = this; 4 | 5 | this.tests = vow.callback; 6 | this.topics = (ctx.topics || []).slice(0); 7 | this.emitter = null; 8 | this.env = env || {}; 9 | this.env.context = this; 10 | 11 | this.env.callback = function (/* arguments */) { 12 | var ctx = this; 13 | var args = Array.prototype.slice.call(arguments); 14 | 15 | var emit = (function (args) { 16 | // 17 | // Convert callback-style results into events. 18 | // 19 | if (vow.batch.suite.options.error) { 20 | return function () { 21 | var e = args.shift(); 22 | that.emitter.ctx = ctx; 23 | // We handle a special case, where the first argument is a 24 | // boolean, in which case we treat it as a result, and not 25 | // an error. This is useful for `path.exists` and other 26 | // functions like it, which only pass a single boolean 27 | // parameter instead of the more common (error, result) pair. 28 | if (typeof(e) === 'boolean' && args.length === 0) { 29 | that.emitter.emit.call(that.emitter, 'success', e); 30 | } else { 31 | if (e) { that.emitter.emit.call(that.emitter, 'error', e) } 32 | else { that.emitter.emit.apply(that.emitter, ['success'].concat(args)) } 33 | } 34 | }; 35 | } else { 36 | return function () { 37 | that.emitter.ctx = ctx; 38 | that.emitter.emit.apply(that.emitter, ['success'].concat(args)); 39 | }; 40 | } 41 | })(args.slice(0)); 42 | // If `this.callback` is called synchronously, 43 | // the emitter will not have been set yet, 44 | // so we defer the emition, that way it'll behave 45 | // asynchronously. 46 | if (that.emitter) { emit() } 47 | else { process.nextTick(emit) } 48 | }; 49 | this.name = vow.description; 50 | this.title = [ 51 | ctx.title || '', 52 | vow.description || '' 53 | ].join(/^[#.:]/.test(vow.description) ? '' : ' ').trim(); 54 | }; 55 | 56 | -------------------------------------------------------------------------------- /node_modules/vows/lib/vows/extras.js: -------------------------------------------------------------------------------- 1 | var events = require('events'); 2 | // 3 | // Wrap a Node.js style async function into an EventEmmitter 4 | // 5 | this.prepare = function (obj, targets) { 6 | targets.forEach(function (target) { 7 | if (target in obj) { 8 | obj[target] = (function (fun) { 9 | return function () { 10 | var args = Array.prototype.slice.call(arguments); 11 | var ee = new(events.EventEmitter); 12 | 13 | args.push(function (err /* [, data] */) { 14 | var args = Array.prototype.slice.call(arguments, 1); 15 | 16 | if (err) { ee.emit('error', err) } 17 | else { ee.emit.apply(ee, ['success'].concat(args)) } 18 | }); 19 | fun.apply(obj, args); 20 | 21 | return ee; 22 | }; 23 | })(obj[target]); 24 | } 25 | }); 26 | return obj; 27 | }; 28 | 29 | -------------------------------------------------------------------------------- /node_modules/vows/lib/vows/reporters/dot-matrix.js: -------------------------------------------------------------------------------- 1 | 2 | var sys = require('sys'); 3 | 4 | var options = {}; 5 | var console = require('vows/console'); 6 | var spec = require('vows/reporters/spec'); 7 | var stylize = console.stylize, 8 | puts = console.puts(options); 9 | // 10 | // Console reporter 11 | // 12 | var stream, messages = [], lastContext; 13 | 14 | this.name = 'dot-matrix'; 15 | this.reset = function () { 16 | messages = []; 17 | lastContext = null; 18 | }; 19 | this.report = function (data, s) { 20 | var event = data[1]; 21 | 22 | options.stream = typeof(s) === 'object' ? s : process.stdout; 23 | 24 | switch (data[0]) { 25 | case 'subject': 26 | // messages.push(stylize(event, 'underline') + '\n'); 27 | break; 28 | case 'context': 29 | break; 30 | case 'vow': 31 | if (event.status === 'honored') { 32 | sys.print(stylize('·', 'green')); 33 | } else if (event.status === 'pending') { 34 | sys.print(stylize('-', 'cyan')); 35 | } else { 36 | if (lastContext !== event.context) { 37 | lastContext = event.context; 38 | messages.push(spec.contextText(event.context)); 39 | } 40 | if (event.status === 'broken') { 41 | sys.print(stylize('✗', 'yellow')); 42 | messages.push(spec.vowText(event)); 43 | } else if (event.status === 'errored') { 44 | sys.print(stylize('✗', 'red')); 45 | messages.push(spec.vowText(event)); 46 | } 47 | messages.push(''); 48 | } 49 | break; 50 | case 'end': 51 | sys.print(' '); 52 | break; 53 | case 'finish': 54 | if (messages.length) { 55 | puts('\n\n' + messages.join('\n')); 56 | } else { 57 | sys.print('\n'); 58 | } 59 | puts(console.result(event).join('\n')); 60 | break; 61 | case 'error': 62 | puts(console.error(event)); 63 | break; 64 | } 65 | }; 66 | 67 | this.print = function (str) { 68 | sys.print(str); 69 | }; 70 | -------------------------------------------------------------------------------- /node_modules/vows/lib/vows/reporters/json.js: -------------------------------------------------------------------------------- 1 | var sys = require('sys'); 2 | // 3 | // Console JSON reporter 4 | // 5 | this.name = 'json'; 6 | this.report = function (obj) { 7 | sys.puts(JSON.stringify(obj)); 8 | }; 9 | 10 | this.print = function (str) {}; 11 | -------------------------------------------------------------------------------- /node_modules/vows/lib/vows/reporters/silent.js: -------------------------------------------------------------------------------- 1 | // 2 | // Silent reporter - "Shhh" 3 | // 4 | this.name = 'silent'; 5 | this.reset = function () {}; 6 | this.report = function () {}; 7 | this.print = function () {}; 8 | 9 | -------------------------------------------------------------------------------- /node_modules/vows/lib/vows/reporters/spec.js: -------------------------------------------------------------------------------- 1 | var sys = require('sys'); 2 | 3 | var options = {}; 4 | var console = require('vows/console'); 5 | var stylize = console.stylize, 6 | puts = console.puts(options); 7 | // 8 | // Console reporter 9 | // 10 | 11 | this.name = 'spec'; 12 | this.report = function (data, s) { 13 | var event = data[1]; 14 | 15 | options.stream = typeof(s) === 'object' ? s : process.stdout; 16 | buffer = []; 17 | 18 | switch (data[0]) { 19 | case 'subject': 20 | puts('\n♢ ' + stylize(event, 'bold') + '\n'); 21 | break; 22 | case 'context': 23 | puts(this.contextText(event)); 24 | break; 25 | case 'vow': 26 | puts(this.vowText(event)); 27 | break; 28 | case 'end': 29 | sys.print('\n'); 30 | break; 31 | case 'finish': 32 | puts(console.result(event).join('\n')); 33 | break; 34 | case 'error': 35 | puts(console.error(event)); 36 | break; 37 | } 38 | }; 39 | 40 | this.contextText = function (event) { 41 | return ' ' + event; 42 | }; 43 | 44 | this.vowText = function (event) { 45 | var buffer = []; 46 | 47 | buffer.push(' ' + { 48 | honored: ' ✓ ', 49 | broken: ' ✗ ', 50 | errored: ' ✗ ', 51 | pending: ' - ' 52 | }[event.status] + stylize(event.title, ({ 53 | honored: 'green', 54 | broken: 'yellow', 55 | errored: 'red', 56 | pending: 'cyan' 57 | })[event.status])); 58 | 59 | if (event.status === 'broken') { 60 | buffer.push(' » ' + event.exception); 61 | } else if (event.status === 'errored') { 62 | if (event.exception.type === 'promise') { 63 | buffer.push(' » ' + stylize("An unexpected error was caught: " + 64 | stylize(event.exception.error, 'bold'), 'red')); 65 | } else { 66 | buffer.push(' ' + stylize(event.exception, 'red')); 67 | } 68 | } 69 | return buffer.join('\n'); 70 | }; 71 | 72 | this.print = function (str) { 73 | sys.print(str); 74 | }; 75 | -------------------------------------------------------------------------------- /node_modules/vows/lib/vows/reporters/watch.js: -------------------------------------------------------------------------------- 1 | var sys = require('sys'); 2 | 3 | var options = {}; 4 | var console = require('vows/console'); 5 | var spec = require('vows/reporters/spec'); 6 | var stylize = console.stylize, 7 | puts = console.puts(options); 8 | // 9 | // Console reporter 10 | // 11 | var lastContext; 12 | 13 | this.name = 'watch'; 14 | this.reset = function () { 15 | lastContext = null; 16 | }; 17 | this.report = function (data) { 18 | var event = data[1]; 19 | 20 | options.stream = process.stdout; 21 | 22 | switch (data[0]) { 23 | case 'vow': 24 | if (['honored', 'pending'].indexOf(event.status) === -1) { 25 | if (lastContext !== event.context) { 26 | lastContext = event.context; 27 | puts(spec.contextText(event.context)); 28 | } 29 | puts(spec.vowText(event)); 30 | puts(''); 31 | } 32 | break; 33 | case 'error': 34 | puts(console.error(event)); 35 | break; 36 | } 37 | }; 38 | this.print = function (str) {}; 39 | -------------------------------------------------------------------------------- /node_modules/vows/lib/vows/reporters/xunit.js: -------------------------------------------------------------------------------- 1 | // xunit outoput for vows, so we can run things under hudson 2 | // 3 | // The translation to xunit is simple. Most likely more tags/attributes can be 4 | // added, see: http://ant.1045680.n5.nabble.com/schema-for-junit-xml-output-td1375274.html 5 | // 6 | 7 | var puts = require('util').puts; 8 | 9 | var buffer = [], 10 | curSubject = null; 11 | 12 | function xmlEnc(value) { 13 | return !value ? value : String(value).replace(/&/g, "&") 14 | .replace(/>/g, ">") 15 | .replace(/'; 43 | } 44 | 45 | function cdata(data) { 46 | return ''; 47 | } 48 | 49 | this.name = 'xunit'; 50 | this.report = function (data) { 51 | var event = data[1]; 52 | 53 | switch (data[0]) { 54 | case 'subject': 55 | curSubject = event; 56 | break; 57 | case 'context': 58 | break; 59 | case 'vow': 60 | switch (event.status) { 61 | case 'honored': 62 | buffer.push(tag('testcase', {classname: curSubject, name: event.context + ': ' + event.title}, true)); 63 | break; 64 | case 'broken': 65 | var err = tag('error', {type: 'vows.event.broken', message: 'Broken test'}, false, cdata(event.exception)); 66 | buffer.push(tag('testcase', {classname: curSubject, name: event.context + ': ' + event.title}, false, err)); 67 | break; 68 | case 'errored': 69 | var skip = tag('skipped', {type: 'vows.event.errored', message: 'Errored test'}, false, cdata(event.exception)); 70 | buffer.push(tag('testcase', {classname: curSubject, name: event.context + ': ' + event.title}, false, skip)); 71 | break; 72 | case 'pending': 73 | // nop 74 | break; 75 | } 76 | break; 77 | case 'end': 78 | buffer.push(end('testcase')); 79 | break; 80 | case 'finish': 81 | buffer.unshift(tag('testsuite', {name: 'Vows test', tests: event.total, timestamp: (new Date()).toUTCString(), errors: event.errored, failures: event.broken, skip: event.pending, time: event.time})); 82 | buffer.push(end('testsuite')); 83 | puts(buffer.join('\n')); 84 | break; 85 | case 'error': 86 | break; 87 | } 88 | }; 89 | 90 | this.print = function (str) { }; 91 | -------------------------------------------------------------------------------- /node_modules/vows/lib/vows/suite.js: -------------------------------------------------------------------------------- 1 | var events = require('events'), 2 | path = require('path'); 3 | 4 | require.paths.unshift(path.join(__dirname, '..')); 5 | 6 | var vows = require('vows'); 7 | var Context = require('vows/context').Context; 8 | 9 | this.Suite = function (subject) { 10 | this.subject = subject; 11 | this.matcher = /.*/; 12 | this.reporter = require('vows/reporters/dot-matrix'); 13 | this.batches = []; 14 | this.options = { error: true }; 15 | this.reset(); 16 | }; 17 | 18 | this.Suite.prototype = new(function () { 19 | this.reset = function () { 20 | this.results = { 21 | honored: 0, 22 | broken: 0, 23 | errored: 0, 24 | pending: 0, 25 | total: 0, 26 | time: null 27 | }; 28 | this.batches.forEach(function (b) { 29 | b.lastContext = null; 30 | b.remaining = b._remaining; 31 | b.honored = b.broken = b.errored = b.total = b.pending = 0; 32 | b.vows.forEach(function (vow) { vow.status = null }); 33 | b.teardowns = []; 34 | }); 35 | }; 36 | 37 | this.addBatch = function (tests) { 38 | this.batches.push({ 39 | tests: tests, 40 | suite: this, 41 | vows: [], 42 | remaining: 0, 43 | _remaining: 0, 44 | honored: 0, 45 | broken: 0, 46 | errored: 0, 47 | pending: 0, 48 | total: 0, 49 | teardowns: [] 50 | }); 51 | return this; 52 | }; 53 | this.addVows = this.addBatch; 54 | 55 | this.parseBatch = function (batch, matcher) { 56 | var tests = batch.tests; 57 | 58 | if ('topic' in tests) { 59 | throw new(Error)("missing top-level context."); 60 | } 61 | // Count the number of vows/promises expected to fire, 62 | // so we know when the tests are over. 63 | // We match the keys against `matcher`, to decide 64 | // whether or not they should be included in the test. 65 | // Any key, including assertion function keys can be matched. 66 | // If a child matches, then the n parent topics must not be skipped. 67 | (function count(tests, _match) { 68 | var match = false; 69 | 70 | var keys = Object.keys(tests).filter(function (k) { 71 | return k !== 'topic' && k !== 'teardown'; 72 | }); 73 | 74 | for (var i = 0, key; i < keys.length; i++) { 75 | key = keys[i]; 76 | 77 | // If the parent node, or this one matches. 78 | match = _match || matcher.test(key); 79 | 80 | if (typeof(tests[key]) === 'object') { 81 | match = count(tests[key], match); 82 | } else { 83 | if (typeof(tests[key]) === 'string') { 84 | tests[key] = new(String)(tests[key]); 85 | } 86 | if (! match) { 87 | tests[key]._skip = true; 88 | } 89 | } 90 | } 91 | 92 | // If any of the children matched, 93 | // don't skip this node. 94 | for (var i = 0; i < keys.length; i++) { 95 | if (! tests[keys[i]]._skip) { match = true } 96 | } 97 | 98 | if (match) { batch.remaining ++ } 99 | else { tests._skip = true } 100 | 101 | return match; 102 | })(tests, false); 103 | 104 | batch._remaining = batch.remaining; 105 | }; 106 | 107 | this.runBatch = function (batch) { 108 | var topic, 109 | tests = batch.tests, 110 | promise = batch.promise = new(events.EventEmitter); 111 | 112 | var that = this; 113 | 114 | batch.status = 'begin'; 115 | 116 | // The test runner, it calls itself recursively, passing the 117 | // previous context to the inner contexts. This is so the `topic` 118 | // functions have access to all the previous context topics in their 119 | // arguments list. 120 | // It is defined and invoked at the same time. 121 | // If it encounters a `topic` function, it waits for the returned 122 | // promise to emit (the topic), at which point it runs the functions under it, 123 | // passing the topic as an argument. 124 | (function run(ctx, lastTopic) { 125 | var old = false; 126 | topic = ctx.tests.topic; 127 | 128 | if (typeof(topic) === 'function') { 129 | // Run the topic, passing the previous context topics 130 | topic = topic.apply(ctx.env, ctx.topics); 131 | 132 | if (typeof(topic) === 'undefined') { ctx._callback = true } 133 | } 134 | 135 | // If this context has a topic, store it in `lastTopic`, 136 | // if not, use the last topic, passed down by a parent 137 | // context. 138 | if (typeof(topic) !== 'undefined' || ctx._callback) { 139 | lastTopic = topic; 140 | } else { 141 | old = true; 142 | topic = lastTopic; 143 | } 144 | 145 | // If the topic doesn't return an event emitter (such as a promise), 146 | // we create it ourselves, and emit the value on the next tick. 147 | if (! (topic && topic.constructor === events.EventEmitter)) { 148 | ctx.emitter = new(events.EventEmitter); 149 | 150 | if (! ctx._callback) { 151 | process.nextTick(function (val) { 152 | return function () { ctx.emitter.emit("success", val) }; 153 | }(topic)); 154 | } 155 | topic = ctx.emitter; 156 | } 157 | 158 | topic.on('success', function (val) { 159 | // Once the topic fires, add the return value 160 | // to the beginning of the topics list, so it 161 | // becomes the first argument for the next topic. 162 | // If we're using the parent topic, no need to 163 | // prepend it to the topics list, or we'll get 164 | // duplicates. 165 | if (! old) Array.prototype.unshift.apply(ctx.topics, arguments); 166 | }); 167 | if (topic.setMaxListeners) { topic.setMaxListeners(Infinity) } 168 | 169 | // Now run the tests, or sub-contexts 170 | Object.keys(ctx.tests).filter(function (k) { 171 | return ctx.tests[k] && k !== 'topic' && 172 | k !== 'teardown' && !ctx.tests[k]._skip; 173 | }).forEach(function (item) { 174 | // Create a new evaluation context, 175 | // inheriting from the parent one. 176 | var env = Object.create(ctx.env); 177 | env.suite = that; 178 | 179 | // Holds the current test or context 180 | var vow = Object.create({ 181 | callback: ctx.tests[item], 182 | context: ctx.title, 183 | description: item, 184 | binding: ctx.env, 185 | status: null, 186 | batch: batch 187 | }); 188 | 189 | // If we encounter a function, add it to the callbacks 190 | // of the `topic` function, so it'll get called once the 191 | // topic fires. 192 | // If we encounter an object literal, we recurse, sending it 193 | // our current context. 194 | if ((typeof(vow.callback) === 'function') || (vow.callback instanceof String)) { 195 | topic.addVow(vow); 196 | } else if (typeof(vow.callback) === 'object') { 197 | // If there's a setup stage, we have to wait for it to fire, 198 | // before calling the inner context. Else, just run the inner context 199 | // synchronously. 200 | if (topic) { 201 | topic.on("success", function (ctx) { 202 | return function (val) { 203 | return run(new(Context)(vow, ctx, env), lastTopic); 204 | }; 205 | }(ctx)); 206 | } else { 207 | run(new(Context)(vow, ctx, env), lastTopic); 208 | } 209 | } 210 | }); 211 | // Teardown 212 | if (ctx.tests.teardown) { 213 | batch.teardowns.push(ctx); 214 | } 215 | if (! ctx.tests._skip) { 216 | batch.remaining --; 217 | } 218 | // Check if we're done running the tests 219 | exports.tryEnd(batch); 220 | // This is our initial, empty context 221 | })(new(Context)({ callback: tests, context: null, description: null }, {})); 222 | return promise; 223 | }; 224 | 225 | this.report = function () { 226 | return this.reporter.report.apply(this.reporter, arguments); 227 | }; 228 | 229 | this.run = function (options, callback) { 230 | var that = this, start; 231 | 232 | options = options || {}; 233 | 234 | for (var k in options) { this.options[k] = options[k] } 235 | 236 | this.matcher = this.options.matcher || this.matcher; 237 | this.reporter = this.options.reporter || this.reporter; 238 | 239 | this.batches.forEach(function (batch) { 240 | that.parseBatch(batch, that.matcher); 241 | }); 242 | 243 | this.reset(); 244 | 245 | start = new(Date); 246 | 247 | if (this.batches.filter(function (b) { return b.remaining > 0 }).length) { 248 | this.report(['subject', this.subject]); 249 | } 250 | 251 | return (function run(batches) { 252 | var batch = batches.shift(); 253 | 254 | if (batch) { 255 | // If the batch has no vows to run, 256 | // go to the next one. 257 | if (batch.remaining === 0) { 258 | run(batches); 259 | } else { 260 | that.runBatch(batch).on('end', function () { 261 | run(batches); 262 | }); 263 | } 264 | } else { 265 | that.results.time = (new(Date) - start) / 1000; 266 | that.report(['finish', that.results]); 267 | 268 | if (callback) { callback(that.results) } 269 | 270 | if (that.results.honored + that.results.pending === that.results.total) { 271 | return 0; 272 | } else { 273 | return 1; 274 | } 275 | } 276 | })(this.batches.slice(0)); 277 | }; 278 | 279 | this.runParallel = function () {}; 280 | 281 | this.export = function (module, options) { 282 | for (var k in (options || {})) { this.options[k] = options[k] } 283 | 284 | if (require.main === module) { 285 | return this.run(); 286 | } else { 287 | return module.exports[this.subject] = this; 288 | } 289 | }; 290 | this.exportTo = function (module, options) { // Alias, for JSLint 291 | return this.export(module, options); 292 | }; 293 | }); 294 | 295 | // 296 | // Checks if all the tests in the batch have been run, 297 | // and triggers the next batch (if any), by emitting the 'end' event. 298 | // 299 | this.tryEnd = function (batch) { 300 | var result, style, time; 301 | 302 | if (batch.honored + batch.broken + batch.errored + batch.pending === batch.total && 303 | batch.remaining === 0) { 304 | 305 | Object.keys(batch).forEach(function (k) { 306 | (k in batch.suite.results) && (batch.suite.results[k] += batch[k]); 307 | }); 308 | 309 | // Run teardowns 310 | if (batch.teardowns) { 311 | for (var i = batch.teardowns.length - 1, ctx; i >= 0; i--) { 312 | ctx = batch.teardowns[i]; 313 | ctx.tests.teardown.apply(ctx.env, ctx.topics); 314 | } 315 | } 316 | 317 | batch.status = 'end'; 318 | batch.suite.report(['end']); 319 | batch.promise.emit('end', batch.honored, batch.broken, batch.errored, batch.pending); 320 | } 321 | }; 322 | -------------------------------------------------------------------------------- /node_modules/vows/node_modules/eyes/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2009 cloudhead 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /node_modules/vows/node_modules/eyes/Makefile: -------------------------------------------------------------------------------- 1 | test: 2 | @@node test/eyes-test.js 3 | 4 | .PHONY: test 5 | -------------------------------------------------------------------------------- /node_modules/vows/node_modules/eyes/README.md: -------------------------------------------------------------------------------- 1 | eyes 2 | ==== 3 | 4 | a customizable value inspector for Node.js 5 | 6 | synopsis 7 | -------- 8 | 9 | I was tired of looking at cluttered output in the console -- something needed to be done, 10 | `sys.inspect()` didn't display regexps correctly, and was too verbose, and I had an hour or two to spare. 11 | So I decided to have some fun. _eyes_ were born. 12 | 13 | ![eyes-ss](http://dl.dropbox.com/u/251849/eyes-js-ss.gif) 14 | 15 | _example of the output of a user-customized eyes.js inspector_ 16 | 17 | *eyes* also deals with circular objects in an intelligent way, and can pretty-print object literals. 18 | 19 | usage 20 | ----- 21 | 22 | var inspect = require('eyes').inspector({styles: {all: 'magenta'}}); 23 | 24 | inspect(something); // inspect with the settings passed to `inspector` 25 | 26 | or 27 | 28 | var eyes = require('eyes'); 29 | 30 | eyes.inspect(something); // inspect with the default settings 31 | 32 | you can pass a _label_ to `inspect()`, to keep track of your inspections: 33 | 34 | eyes.inspect(something, "a random value"); 35 | 36 | If you want to return the output of eyes without printing it, you can set it up this way: 37 | 38 | var inspect = require('eyes').inspector({ stream: null }); 39 | 40 | sys.puts(inspect({ something: 42 })); 41 | 42 | customization 43 | ------------- 44 | 45 | These are the default styles and settings used by _eyes_. 46 | styles: { // Styles applied to stdout 47 | all: 'cyan', // Overall style applied to everything 48 | label: 'underline', // Inspection labels, like 'array' in `array: [1, 2, 3]` 49 | other: 'inverted', // Objects which don't have a literal representation, such as functions 50 | key: 'bold', // The keys in object literals, like 'a' in `{a: 1}` 51 | 52 | special: 'grey', // null, undefined... 53 | string: 'green', 54 | number: 'magenta', 55 | bool: 'blue', // true false 56 | regexp: 'green', // /\d+/ 57 | }, 58 | pretty: true, // Indent object literals 59 | hideFunctions: false, // Don't output functions at all 60 | stream: process.stdout, // Stream to write to, or null 61 | maxLength: 2048 // Truncate output if longer 62 | 63 | You can overwrite them with your own, by passing a similar object to `inspector()` or `inspect()`. 64 | 65 | var inspect = require('eyes').inspector({ 66 | styles: { 67 | all: 'magenta', 68 | special: 'bold' 69 | }, 70 | maxLength: 512 71 | }); 72 | 73 | -------------------------------------------------------------------------------- /node_modules/vows/node_modules/eyes/lib/eyes.js: -------------------------------------------------------------------------------- 1 | // 2 | // Eyes.js - a customizable value inspector for Node.js 3 | // 4 | // usage: 5 | // 6 | // var inspect = require('eyes').inspector({styles: {all: 'magenta'}}); 7 | // inspect(something); // inspect with the settings passed to `inspector` 8 | // 9 | // or 10 | // 11 | // var eyes = require('eyes'); 12 | // eyes.inspect(something); // inspect with the default settings 13 | // 14 | var eyes = exports, 15 | stack = []; 16 | 17 | eyes.defaults = { 18 | styles: { // Styles applied to stdout 19 | all: 'cyan', // Overall style applied to everything 20 | label: 'underline', // Inspection labels, like 'array' in `array: [1, 2, 3]` 21 | other: 'inverted', // Objects which don't have a literal representation, such as functions 22 | key: 'bold', // The keys in object literals, like 'a' in `{a: 1}` 23 | special: 'grey', // null, undefined... 24 | string: 'green', 25 | number: 'magenta', 26 | bool: 'blue', // true false 27 | regexp: 'green', // /\d+/ 28 | }, 29 | pretty: true, // Indent object literals 30 | hideFunctions: false, 31 | stream: process.stdout, 32 | maxLength: 2048 // Truncate output if longer 33 | }; 34 | 35 | // Return a curried inspect() function, with the `options` argument filled in. 36 | eyes.inspector = function (options) { 37 | var that = this; 38 | return function (obj, label, opts) { 39 | return that.inspect.call(that, obj, label, 40 | merge(options || {}, opts || {})); 41 | }; 42 | }; 43 | 44 | // If we have a `stream` defined, use it to print a styled string, 45 | // if not, we just return the stringified object. 46 | eyes.inspect = function (obj, label, options) { 47 | options = merge(this.defaults, options || {}); 48 | 49 | if (options.stream) { 50 | return this.print(stringify(obj, options), label, options); 51 | } else { 52 | return stringify(obj, options) + (options.styles ? '\033[39m' : ''); 53 | } 54 | }; 55 | 56 | // Output using the 'stream', and an optional label 57 | // Loop through `str`, and truncate it after `options.maxLength` has been reached. 58 | // Because escape sequences are, at this point embeded within 59 | // the output string, we can't measure the length of the string 60 | // in a useful way, without separating what is an escape sequence, 61 | // versus a printable character (`c`). So we resort to counting the 62 | // length manually. 63 | eyes.print = function (str, label, options) { 64 | for (var c = 0, i = 0; i < str.length; i++) { 65 | if (str.charAt(i) === '\033') { i += 4 } // `4` because '\033[25m'.length + 1 == 5 66 | else if (c === options.maxLength) { 67 | str = str.slice(0, i - 1) + '…'; 68 | break; 69 | } else { c++ } 70 | } 71 | return options.stream.write.call(options.stream, (label ? 72 | this.stylize(label, options.styles.label, options.styles) + ': ' : '') + 73 | this.stylize(str, options.styles.all, options.styles) + '\033[0m' + "\n"); 74 | }; 75 | 76 | // Apply a style to a string, eventually, 77 | // I'd like this to support passing multiple 78 | // styles. 79 | eyes.stylize = function (str, style, styles) { 80 | var codes = { 81 | 'bold' : [1, 22], 82 | 'underline' : [4, 24], 83 | 'inverse' : [7, 27], 84 | 'cyan' : [36, 39], 85 | 'magenta' : [35, 39], 86 | 'blue' : [34, 39], 87 | 'yellow' : [33, 39], 88 | 'green' : [32, 39], 89 | 'red' : [31, 39], 90 | 'grey' : [90, 39] 91 | }, endCode; 92 | 93 | if (style && codes[style]) { 94 | endCode = (codes[style][1] === 39 && styles.all) ? codes[styles.all][0] 95 | : codes[style][1]; 96 | return '\033[' + codes[style][0] + 'm' + str + 97 | '\033[' + endCode + 'm'; 98 | } else { return str } 99 | }; 100 | 101 | // Convert any object to a string, ready for output. 102 | // When an 'array' or an 'object' are encountered, they are 103 | // passed to specialized functions, which can then recursively call 104 | // stringify(). 105 | function stringify(obj, options) { 106 | var that = this, stylize = function (str, style) { 107 | return eyes.stylize(str, options.styles[style], options.styles) 108 | }, index, result; 109 | 110 | if ((index = stack.indexOf(obj)) !== -1) { 111 | return stylize(new(Array)(stack.length - index + 1).join('.'), 'special'); 112 | } 113 | stack.push(obj); 114 | 115 | result = (function (obj) { 116 | switch (typeOf(obj)) { 117 | case "string" : obj = stringifyString(obj.indexOf("'") === -1 ? "'" + obj + "'" 118 | : '"' + obj + '"'); 119 | return stylize(obj, 'string'); 120 | case "regexp" : return stylize('/' + obj.source + '/', 'regexp'); 121 | case "number" : return stylize(obj + '', 'number'); 122 | case "function" : return options.stream ? stylize("Function", 'other') : '[Function]'; 123 | case "null" : return stylize("null", 'special'); 124 | case "undefined": return stylize("undefined", 'special'); 125 | case "boolean" : return stylize(obj + '', 'bool'); 126 | case "date" : return stylize(obj.toUTCString()); 127 | case "array" : return stringifyArray(obj, options, stack.length); 128 | case "object" : return stringifyObject(obj, options, stack.length); 129 | } 130 | })(obj); 131 | 132 | stack.pop(); 133 | return result; 134 | }; 135 | 136 | // Escape invisible characters in a string 137 | function stringifyString (str, options) { 138 | return str.replace(/\\/g, '\\\\') 139 | .replace(/\n/g, '\\n') 140 | .replace(/[\u0001-\u001F]/g, function (match) { 141 | return '\\0' + match[0].charCodeAt(0).toString(8); 142 | }); 143 | } 144 | 145 | // Convert an array to a string, such as [1, 2, 3]. 146 | // This function calls stringify() for each of the elements 147 | // in the array. 148 | function stringifyArray(ary, options, level) { 149 | var out = []; 150 | var pretty = options.pretty && (ary.length > 4 || ary.some(function (o) { 151 | return (typeof(o) === 'object' && Object.keys(o).length > 0) || 152 | (Array.isArray(o) && o.length > 0); 153 | })); 154 | var ws = pretty ? '\n' + new(Array)(level * 4 + 1).join(' ') : ' '; 155 | 156 | for (var i = 0; i < ary.length; i++) { 157 | out.push(stringify(ary[i], options)); 158 | } 159 | 160 | if (out.length === 0) { 161 | return '[]'; 162 | } else { 163 | return '[' + ws 164 | + out.join(',' + (pretty ? ws : ' ')) 165 | + (pretty ? ws.slice(0, -4) : ws) + 166 | ']'; 167 | } 168 | }; 169 | 170 | // Convert an object to a string, such as {a: 1}. 171 | // This function calls stringify() for each of its values, 172 | // and does not output functions or prototype values. 173 | function stringifyObject(obj, options, level) { 174 | var out = []; 175 | var pretty = options.pretty && (Object.keys(obj).length > 2 || 176 | Object.keys(obj).some(function (k) { return typeof(obj[k]) === 'object' })); 177 | var ws = pretty ? '\n' + new(Array)(level * 4 + 1).join(' ') : ' '; 178 | 179 | Object.keys(obj).forEach(function (k) { 180 | if (obj.hasOwnProperty(k) && !(obj[k] instanceof Function && options.hideFunctions)) { 181 | out.push(eyes.stylize(k, options.styles.key, options.styles) + ': ' + 182 | stringify(obj[k], options)); 183 | } 184 | }); 185 | 186 | if (out.length === 0) { 187 | return '{}'; 188 | } else { 189 | return "{" + ws 190 | + out.join(',' + (pretty ? ws : ' ')) 191 | + (pretty ? ws.slice(0, -4) : ws) + 192 | "}"; 193 | } 194 | }; 195 | 196 | // A better `typeof` 197 | function typeOf(value) { 198 | var s = typeof(value), 199 | types = [Object, Array, String, RegExp, Number, Function, Boolean, Date]; 200 | 201 | if (s === 'object' || s === 'function') { 202 | if (value) { 203 | types.forEach(function (t) { 204 | if (value instanceof t) { s = t.name.toLowerCase() } 205 | }); 206 | } else { s = 'null' } 207 | } 208 | return s; 209 | } 210 | 211 | function merge(/* variable args */) { 212 | var objs = Array.prototype.slice.call(arguments); 213 | var target = {}; 214 | 215 | objs.forEach(function (o) { 216 | Object.keys(o).forEach(function (k) { 217 | if (k === 'styles') { 218 | if (! o.styles) { 219 | target.styles = false; 220 | } else { 221 | target.styles = {} 222 | for (var s in o.styles) { 223 | target.styles[s] = o.styles[s]; 224 | } 225 | } 226 | } else { 227 | target[k] = o[k]; 228 | } 229 | }); 230 | }); 231 | return target; 232 | } 233 | 234 | -------------------------------------------------------------------------------- /node_modules/vows/node_modules/eyes/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name" : "eyes", 3 | "description" : "a customizable value inspector", 4 | "url" : "http://github.com/cloudhead/eyes.js", 5 | "keywords" : ["inspector", "debug", "inspect", "print"], 6 | "author" : "Alexis Sellier ", 7 | "contributors" : [], 8 | "licenses" : ["MIT"], 9 | "dependencies" : [], 10 | "main" : "./lib/eyes", 11 | "version" : "0.1.6", 12 | "directories" : { "lib": "./lib", "test": "./test" }, 13 | "engines" : { "node": "> 0.1.90" } 14 | } 15 | -------------------------------------------------------------------------------- /node_modules/vows/node_modules/eyes/test/eyes-test.js: -------------------------------------------------------------------------------- 1 | var sys = require('sys'); 2 | var eyes = require('../lib/eyes'); 3 | 4 | eyes.inspect({ 5 | number: 42, 6 | string: "John Galt", 7 | regexp: /[a-z]+/, 8 | array: [99, 168, 'x', {}], 9 | func: function () {}, 10 | bool: false, 11 | nil: null, 12 | undef: undefined, 13 | object: {attr: []} 14 | }, "native types"); 15 | 16 | eyes.inspect({ 17 | number: new(Number)(42), 18 | string: new(String)("John Galt"), 19 | regexp: new(RegExp)(/[a-z]+/), 20 | array: new(Array)(99, 168, 'x', {}), 21 | bool: new(Boolean)(false), 22 | object: new(Object)({attr: []}), 23 | date: new(Date) 24 | }, "wrapped types"); 25 | 26 | var obj = {}; 27 | obj.that = { self: obj }; 28 | obj.self = obj; 29 | 30 | eyes.inspect(obj, "circular object"); 31 | eyes.inspect({hello: 'moto'}, "small object"); 32 | eyes.inspect({hello: new(Array)(6) }, "big object"); 33 | eyes.inspect(["hello 'world'", 'hello "world"'], "quotes"); 34 | eyes.inspect({ 35 | recommendations: [{ 36 | id: 'a7a6576c2c822c8e2bd81a27e41437d8', 37 | key: [ 'spree', 3.764316258020699 ], 38 | value: { 39 | _id: 'a7a6576c2c822c8e2bd81a27e41437d8', 40 | _rev: '1-2e2d2f7fd858c4a5984bcf809d22ed98', 41 | type: 'domain', 42 | domain: 'spree', 43 | weight: 3.764316258020699, 44 | product_id: 30 45 | } 46 | }] 47 | }, 'complex'); 48 | 49 | 50 | var inspect = eyes.inspector({ stream: null }); 51 | 52 | sys.puts(inspect('something', "something")); 53 | sys.puts(inspect("something else")); 54 | 55 | sys.puts(inspect(["no color"], null, { styles: false })); 56 | -------------------------------------------------------------------------------- /node_modules/vows/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name" : "vows", 3 | "description" : "Asynchronous BDD & continuous integration for node.js", 4 | "url" : "http://vowsjs.org", 5 | "keywords" : ["testing", "spec", "test", "BDD"], 6 | "author" : "Alexis Sellier ", 7 | "contributors" : [], 8 | "dependencies" : {"eyes": ">=0.1.6"}, 9 | "main" : "./lib/vows", 10 | "bin" : { "vows": "./bin/vows" }, 11 | "directories" : { "test": "./test" }, 12 | "version" : "0.5.8", 13 | "engines" : {"node": ">=0.2.6"} 14 | } 15 | -------------------------------------------------------------------------------- /node_modules/vows/test/assert-test.js: -------------------------------------------------------------------------------- 1 | var vows = require('vows'); 2 | var assert = require('assert'); 3 | 4 | vows.describe('vows/assert').addBatch({ 5 | "The Assertion module": { 6 | topic: require('assert'), 7 | 8 | "`equal`": function (assert) { 9 | assert.equal("hello world", "hello world"); 10 | assert.equal(1, true); 11 | }, 12 | "`match`": function (assert) { 13 | assert.match("hello world", /^[a-z]+ [a-z]+$/); 14 | }, 15 | "`length`": function (assert) { 16 | assert.length("hello world", 11); 17 | assert.length([1, 2, 3], 3); 18 | }, 19 | "`include`": function (assert) { 20 | assert.include("hello world", "world"); 21 | assert.include([0, 42, 0], 42); 22 | assert.include({goo:true}, 'goo'); 23 | }, 24 | "`typeOf`": function (assert) { 25 | assert.typeOf('goo', 'string'); 26 | assert.typeOf(42, 'number'); 27 | assert.typeOf([], 'array'); 28 | assert.typeOf({}, 'object'); 29 | assert.typeOf(false, 'boolean'); 30 | }, 31 | "`instanceOf`": function (assert) { 32 | assert.instanceOf([], Array); 33 | assert.instanceOf(function () {}, Function); 34 | }, 35 | "`isArray`": function (assert) { 36 | assert.isArray([]); 37 | assertError(assert.isArray, {}); 38 | }, 39 | "`isString`": function (assert) { 40 | assert.isString(""); 41 | }, 42 | "`isObject`": function (assert) { 43 | assert.isObject({}); 44 | assertError(assert.isObject, []); 45 | }, 46 | "`isNumber`": function (assert) { 47 | assert.isNumber(0); 48 | }, 49 | "`isNan`": function (assert) { 50 | assert.isNaN(0/0); 51 | }, 52 | "`isTrue`": function (assert) { 53 | assert.isTrue(true); 54 | assertError(assert.isTrue, 1); 55 | }, 56 | "`isFalse`": function (assert) { 57 | assert.isFalse(false); 58 | assertError(assert.isFalse, 0); 59 | }, 60 | "`isZero`": function (assert) { 61 | assert.isZero(0); 62 | assertError(assert.isZero, null); 63 | }, 64 | "`isNotZero`": function (assert) { 65 | assert.isNotZero(1); 66 | }, 67 | "`isUndefined`": function (assert) { 68 | assert.isUndefined(undefined); 69 | assertError(assert.isUndefined, null); 70 | }, 71 | "`isNull`": function (assert) { 72 | assert.isNull(null); 73 | assertError(assert.isNull, 0); 74 | assertError(assert.isNull, undefined); 75 | }, 76 | "`isNotNull`": function (assert) { 77 | assert.isNotNull(0); 78 | }, 79 | "`greater` and `lesser`": function (assert) { 80 | assert.greater(5, 4); 81 | assert.lesser(4, 5); 82 | }, 83 | "`isEmpty`": function (assert) { 84 | assert.isEmpty({}); 85 | assert.isEmpty([]); 86 | assert.isEmpty(""); 87 | } 88 | } 89 | }).export(module); 90 | 91 | function assertError(assertion, value, fail) { 92 | try { 93 | assertion(value); 94 | fail = true; 95 | } catch (e) {/* Success */} 96 | 97 | fail && assert.fail(value, assert.AssertionError, 98 | "expected an AssertionError for {actual}", 99 | "assertError", assertError); 100 | } 101 | 102 | -------------------------------------------------------------------------------- /node_modules/vows/test/vows-test.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | 3 | require.paths.unshift(path.join(__dirname, '..', 'lib')); 4 | 5 | var events = require('events'), 6 | assert = require('assert'), 7 | fs = require('fs'); 8 | 9 | var vows = require('vows'); 10 | 11 | var api = vows.prepare({ 12 | get: function (id, callback) { 13 | process.nextTick(function () { callback(null, id) }); 14 | }, 15 | version: function () { return '1.0' } 16 | }, ['get']); 17 | 18 | var promiser = function (val) { 19 | return function () { 20 | var promise = new(events.EventEmitter); 21 | process.nextTick(function () { promise.emit('success', val) }); 22 | return promise; 23 | } 24 | }; 25 | 26 | vows.describe("Vows").addBatch({ 27 | "A context": { 28 | topic: promiser("hello world"), 29 | 30 | "with a nested context": { 31 | topic: function (parent) { 32 | this.state = 42; 33 | return promiser(parent)(); 34 | }, 35 | "has access to the environment": function () { 36 | assert.equal(this.state, 42); 37 | }, 38 | "and a sub nested context": { 39 | topic: function () { 40 | return this.state; 41 | }, 42 | "has access to the parent environment": function (r) { 43 | assert.equal(r, 42); 44 | assert.equal(this.state, 42); 45 | }, 46 | "has access to the parent context object": function (r) { 47 | assert.ok(Array.isArray(this.context.topics)); 48 | assert.include(this.context.topics, "hello world"); 49 | } 50 | } 51 | } 52 | }, 53 | "A nested context": { 54 | topic: promiser(1), 55 | 56 | ".": { 57 | topic: function (a) { return promiser(2)() }, 58 | 59 | ".": { 60 | topic: function (b, a) { return promiser(3)() }, 61 | 62 | ".": { 63 | topic: function (c, b, a) { return promiser([4, c, b, a])() }, 64 | 65 | "should have access to the parent topics": function (topics) { 66 | assert.equal(topics.join(), [4, 3, 2, 1].join()); 67 | } 68 | }, 69 | 70 | "from": { 71 | topic: function (c, b, a) { return promiser([4, c, b, a])() }, 72 | 73 | "the parent topics": function(topics) { 74 | assert.equal(topics.join(), [4, 3, 2, 1].join()); 75 | } 76 | } 77 | } 78 | } 79 | }, 80 | "Nested contexts with callback-style async": { 81 | topic: function () { 82 | fs.stat(__dirname + '/vows-test.js', this.callback); 83 | }, 84 | 'after a successful `fs.stat`': { 85 | topic: function (stat) { 86 | fs.open(__dirname + '/vows-test.js', "r", stat.mode, this.callback); 87 | }, 88 | 'after a successful `fs.open`': { 89 | topic: function (fd, stat) { 90 | fs.read(fd, stat.size, 0, "utf8", this.callback); 91 | }, 92 | 'after a successful `fs.read`': function (data) { 93 | assert.match (data, /after a successful `fs.read`/); 94 | } 95 | } 96 | } 97 | }, 98 | "A nested context with no topics": { 99 | topic: 45, 100 | ".": { 101 | ".": { 102 | "should pass the value down": function (topic) { 103 | assert.equal(topic, 45); 104 | } 105 | } 106 | } 107 | }, 108 | "A Nested context with topic gaps": { 109 | topic: 45, 110 | ".": { 111 | ".": { 112 | topic: 101, 113 | ".": { 114 | ".": { 115 | topic: function (prev, prev2) { 116 | return this.context.topics.slice(0); 117 | }, 118 | "should pass the topics down": function (topics) { 119 | assert.length(topics, 2); 120 | assert.equal(topics[0], 101); 121 | assert.equal(topics[1], 45); 122 | } 123 | } 124 | } 125 | } 126 | } 127 | }, 128 | "A non-promise return value": { 129 | topic: function () { return 1 }, 130 | "should be converted to a promise": function (val) { 131 | assert.equal(val, 1); 132 | } 133 | }, 134 | "A 'prepared' interface": { 135 | "with a wrapped function": { 136 | topic: function () { return api.get(42) }, 137 | "should work as expected": function (val) { 138 | assert.equal(val, 42); 139 | } 140 | }, 141 | "with a non-wrapped function": { 142 | topic: function () { return api.version() }, 143 | "should work as expected": function (val) { 144 | assert.equal(val, '1.0'); 145 | } 146 | } 147 | }, 148 | "A non-function topic": { 149 | topic: 45, 150 | 151 | "should work as expected": function (topic) { 152 | assert.equal(topic, 45); 153 | } 154 | }, 155 | "A non-function topic with a falsy value": { 156 | topic: 0, 157 | 158 | "should work as expected": function (topic) { 159 | assert.equal(topic, 0); 160 | } 161 | }, 162 | "A topic returning a function": { 163 | topic: function () { 164 | return function () { return 42 }; 165 | }, 166 | 167 | "should work as expected": function (topic) { 168 | assert.isFunction(topic); 169 | assert.equal(topic(), 42); 170 | }, 171 | "in a sub-context": { 172 | "should work as expected": function (topic) { 173 | assert.isFunction(topic); 174 | assert.equal(topic(), 42); 175 | }, 176 | } 177 | }, 178 | "A topic emitting an error": { 179 | topic: function () { 180 | var promise = new(events.EventEmitter); 181 | process.nextTick(function () { 182 | promise.emit("error", 404); 183 | }); 184 | return promise; 185 | }, 186 | "shouldn't raise an exception if the test expects it": function (e, res) { 187 | assert.equal(e, 404); 188 | assert.ok(! res); 189 | } 190 | }, 191 | "A topic not emitting an error": { 192 | topic: function () { 193 | var promise = new(events.EventEmitter); 194 | process.nextTick(function () { 195 | promise.emit("success", true); 196 | }); 197 | return promise; 198 | }, 199 | "should pass `null` as first argument, if the test is expecting an error": function (e, res) { 200 | assert.strictEqual(e, null); 201 | assert.equal(res, true); 202 | }, 203 | "should pass the result as first argument if the test isn't expecting an error": function (res) { 204 | assert.equal(res, true); 205 | } 206 | }, 207 | "A topic with callback-style async": { 208 | "when successful": { 209 | topic: function () { 210 | var that = this; 211 | process.nextTick(function () { 212 | that.callback(null, "OK"); 213 | }); 214 | }, 215 | "should work like an event-emitter": function (res) { 216 | assert.equal(res, "OK"); 217 | }, 218 | "should assign `null` to the error argument": function (e, res) { 219 | assert.strictEqual(e, null); 220 | assert.equal(res, "OK"); 221 | } 222 | }, 223 | "when unsuccessful": { 224 | topic: function () { 225 | function async(callback) { 226 | process.nextTick(function () { 227 | callback("ERROR"); 228 | }); 229 | } 230 | async(this.callback); 231 | }, 232 | "should have a non-null error value": function (e, res) { 233 | assert.equal(e, "ERROR"); 234 | }, 235 | "should work like an event-emitter": function (e, res) { 236 | assert.equal(res, undefined); 237 | } 238 | }, 239 | "using this.callback synchronously": { 240 | topic: function () { 241 | this.callback(null, 'hello'); 242 | }, 243 | "should work the same as returning a value": function (res) { 244 | assert.equal(res, 'hello'); 245 | } 246 | }, 247 | "using this.callback with a user context": { 248 | topic: function () { 249 | this.callback.call({ boo: true }, null, 'hello'); 250 | }, 251 | "should give access to the user context": function (res) { 252 | assert.isTrue(this.boo); 253 | } 254 | }, 255 | "passing this.callback to a function": { 256 | topic: function () { 257 | this.boo = true; 258 | var async = function (callback) { 259 | callback(null); 260 | }; 261 | async(this.callback); 262 | }, 263 | "should give access to the topic context": function () { 264 | assert.isTrue(this.boo); 265 | } 266 | }, 267 | "with multiple arguments": { 268 | topic: function () { 269 | this.callback(null, 1, 2, 3); 270 | }, 271 | "should pass them to the vow": function (e, a, b, c) { 272 | assert.strictEqual(e, null); 273 | assert.strictEqual(a, 1); 274 | assert.strictEqual(b, 2); 275 | assert.strictEqual(c, 3); 276 | }, 277 | "and a sub-topic": { 278 | topic: function (a, b, c) { 279 | return [a, b, c]; 280 | }, 281 | "should receive them too": function (val) { 282 | assert.deepEqual(val, [1, 2, 3]); 283 | } 284 | } 285 | } 286 | } 287 | }).addBatch({ 288 | "A Sibling context": { 289 | "'A', with `this.foo = true`": { 290 | topic: function () { 291 | this.foo = true; 292 | return this; 293 | }, 294 | "should have `this.foo` set to true": function (res) { 295 | assert.equal(res.foo, true); 296 | } 297 | }, 298 | "'B', with nothing set": { 299 | topic: function () { 300 | return this; 301 | }, 302 | "shouldn't have access to `this.foo`": function (e, res) { 303 | assert.isUndefined(res.foo); 304 | } 305 | } 306 | } 307 | }).addBatch({ 308 | "A 2nd batch": { 309 | topic: function () { 310 | var p = new(events.EventEmitter); 311 | setTimeout(function () { 312 | p.emit("success"); 313 | }, 100); 314 | return p; 315 | }, 316 | "should run after the first": function () {} 317 | } 318 | }).addBatch({ 319 | "A 3rd batch": { 320 | topic: true, "should run last": function () {} 321 | } 322 | }).addBatch({}).export(module); 323 | 324 | vows.describe("Vows with a single batch", { 325 | "This is a batch that's added as the optional parameter to describe()": { 326 | topic: true, 327 | "And a vow": function () {} 328 | } 329 | }).export(module); 330 | 331 | vows.describe("Vows with multiple batches added as optional parameters", { 332 | "First batch": { 333 | topic: true, 334 | "should be run first": function () {} 335 | } 336 | }, { 337 | "Second batch": { 338 | topic: true, 339 | "should be run second": function () {} 340 | } 341 | }).export(module); 342 | 343 | vows.describe("Vows with teardowns").addBatch({ 344 | "A context": { 345 | topic: function () { 346 | return { flag: true }; 347 | }, 348 | "And a vow": function (topic) { 349 | assert.isTrue(topic.flag); 350 | }, 351 | "And another vow": function (topic) { 352 | assert.isTrue(topic.flag); 353 | }, 354 | "And a final vow": function (topic) { 355 | assert.isTrue(topic.flag); 356 | }, 357 | teardown: function (topic) { 358 | topic.flag = false; 359 | }, 360 | "with a subcontext" : { 361 | topic: function (topic) { 362 | var that = this; 363 | process.nextTick(function () { 364 | that.callback(null, topic); 365 | }); 366 | }, 367 | "Waits for the subcontext before teardown" : function(topic) { 368 | assert.isTrue(topic.flag); 369 | } 370 | } 371 | } 372 | }).export(module); 373 | 374 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "author": "Stephen Blankenship", 3 | "name": "coffee-machine", 4 | "description": "A simple state machine written in CoffeeScript.", 5 | "version": "0.0.3", 6 | "repository": { 7 | "type": "git", 8 | "url": "git://github.com/stephenb/coffee-machine.git" 9 | }, 10 | "main": "./lib/coffee_machine", 11 | "scripts": { 12 | "test": "cake test" 13 | }, 14 | "keywords": ["state machine", "statemachine", "coffee script", "coffeescript"], 15 | "engines": { 16 | "node": ">=v0.4.7" 17 | }, 18 | "dependencies": {}, 19 | "devDependencies": { 20 | "vows": ">=v0.5.8", 21 | "coffee-script": ">=v1.1.1" 22 | } 23 | } -------------------------------------------------------------------------------- /src/coffee_machine.coffee: -------------------------------------------------------------------------------- 1 | root = exports ? window 2 | 3 | # new CoffeeMachine 4 | # states: 5 | # stateName: 6 | # active: true/false (optional, the 1st state defaults to true) 7 | # onEnter: enterMethod (called when successfully entering the state) 8 | # onExit: exitMethod (called when successfully exiting the state) 9 | # guard: guardMethod (stops the change to this state if returns false) 10 | # events: 11 | # eventName: 12 | # from: fromState (should be a defined state, array of defined states, or "any") 13 | # to: toState (should be a defined state) 14 | # onStateChange: changeMethod (called on any state change) 15 | # 16 | root.CoffeeMachine = class CoffeeMachine 17 | 18 | constructor: (@stateMachine = {states:{}, events:{}}) -> 19 | this.defineStateMachine(@stateMachine) 20 | 21 | defineStateMachine: (@stateMachine = {states:{}, events:{}}) -> 22 | # If array setup was used, translate it into the object setup 23 | if @stateMachine.states.constructor.toString().indexOf('Array') isnt -1 24 | states = @stateMachine.states 25 | @stateMachine.states = {} 26 | for state in states 27 | @stateMachine.states[state] = { active: (state is states[0]) } 28 | # Make sure an active state is properly set 29 | activeStates = (state for own state, stateDef of @stateMachine.states when stateDef.active) 30 | if activeStates.length is 0 31 | # Set the 1st state to active 32 | for own state, stateDef of @stateMachine.states 33 | stateDef.active = true 34 | break 35 | else if activeStates.length > 1 36 | # Set only the 1st active state to active 37 | for own state in activeStates 38 | continue if state is activeStates[0] 39 | stateDef.active = false 40 | # Define the event methods 41 | for event, eventDef of @stateMachine.events 42 | do(event, eventDef) => 43 | this[event] = -> this.changeState(eventDef.from, eventDef.to, event) 44 | 45 | currentState: -> 46 | (state for own state, stateDef of @stateMachine.states when stateDef.active)[0] 47 | 48 | availableStates: -> 49 | state for own state of @stateMachine.states 50 | 51 | availableEvents: -> 52 | event for own event of @stateMachine.events 53 | 54 | changeState: (from, to, event=null) -> 55 | # If from is an array, and it contains the currentState, set from to currentState 56 | if from.constructor.toString().indexOf('Array') isnt -1 57 | if from.indexOf(this.currentState()) isnt -1 58 | from = this.currentState() 59 | else 60 | throw "Cannot change from states #{from.join(' or ')}; none are the active state!" 61 | # If using 'any', then set the from to whatever the current state is 62 | if from is 'any' then from = this.currentState() 63 | 64 | fromStateDef = @stateMachine.states[from] 65 | toStateDef = @stateMachine.states[to] 66 | 67 | throw "Cannot change to state '#{to}'; it is undefined!" if toStateDef is undefined 68 | throw "Cannot change from state '#{from}'; it is undefined!" if fromStateDef is undefined 69 | throw "Cannot change from state '#{from}'; it is not the active state!" if fromStateDef.active isnt true 70 | 71 | {onEnter: enterMethod, guard: guardMethod} = toStateDef 72 | {onExit: exitMethod} = fromStateDef 73 | 74 | args = {from: from, to: to, event: event} 75 | return false if guardMethod isnt undefined and guardMethod.call(this, args) is false 76 | exitMethod.call(this, args) if exitMethod isnt undefined 77 | enterMethod.call(this, args) if enterMethod isnt undefined 78 | @stateMachine.onStateChange.call(this, args) if @stateMachine.onStateChange isnt undefined 79 | fromStateDef.active = false 80 | toStateDef.active = true 81 | -------------------------------------------------------------------------------- /test/test_coffee_machine.coffee: -------------------------------------------------------------------------------- 1 | vows = require 'vows' 2 | assert = require 'assert' 3 | {CoffeeMachine} = require '../src/coffee_machine' 4 | 5 | vow = vows.describe('CoffeeMachine') 6 | vow.addBatch 7 | 'State setup using an array': 8 | topic: new CoffeeMachine { 9 | states: ['state1', 'state2', 'state3'] 10 | } 11 | 12 | 'should have available states': (topic) -> 13 | assert.deepEqual topic.availableStates(), ['state1', 'state2', 'state3'] 14 | 15 | 'currentState should be the 1st state': (topic) -> 16 | assert.equal topic.currentState(), 'state1' 17 | 18 | 'changeState should work': (topic) -> 19 | topic.changeState('state1','state2') 20 | assert.equal topic.currentState(), 'state2' 21 | 22 | 'changeState should throw error if trying to change from inactive state': (topic) -> 23 | try 24 | topic.changeState('state1','state3') 25 | catch error 26 | assert.equal topic.currentState(), 'state2' 27 | 28 | vow.addBatch 29 | 'State setup using full object': 30 | topic: new CoffeeMachine 31 | states: 32 | state1: 33 | onEnter: -> 'onEnter state1' 34 | guard: -> 'guard state1' 35 | state2: 36 | onExit: -> 'onExit state2' 37 | state3: 38 | active: true 39 | onEnter: -> 'onEnter state3' 40 | events: 41 | event1: 42 | from: 'state1' 43 | to: 'state2' 44 | 45 | 'should have available states': (topic) -> 46 | assert.deepEqual topic.availableStates(), ['state1', 'state2', 'state3'] 47 | 48 | 'currentState should be state3': (topic) -> 49 | assert.equal topic.currentState(), 'state3' 50 | 51 | 'changeState should work': (topic) -> 52 | topic.changeState('state3','state2') 53 | assert.equal topic.currentState(), 'state2' 54 | 55 | 'changeState should throw error if trying to change from inactive state': (topic) -> 56 | try 57 | topic.changeState('state1','state3') 58 | catch error 59 | assert.equal topic.currentState(), 'state2' 60 | 61 | vow.addBatch 62 | 'onExit': 63 | topic: -> 64 | new CoffeeMachine 65 | states: 66 | state1: 67 | onExit: -> throw 'onExitCalled' 68 | state2: {} 69 | 70 | 'should be setup properly': (topic) -> 71 | assert.isFunction topic.stateMachine.states.state1.onExit 72 | 73 | 'onExit should get called on state change': (topic) -> 74 | try 75 | topic.changeState('state1', 'state2') 76 | catch e 77 | assert.equal e, 'onExitCalled' 78 | 79 | 'onEnter': 80 | topic: -> 81 | new CoffeeMachine 82 | states: 83 | state1: {} 84 | state2: 85 | onEnter: -> throw 'onEnterCalled' 86 | 87 | 'should be setup properly': (topic) -> 88 | assert.isFunction topic.stateMachine.states.state2.onEnter 89 | 90 | 'onEnter should get called on state change': (topic) -> 91 | try 92 | topic.changeState('state1', 'state2') 93 | catch e 94 | assert.equal e, 'onEnterCalled' 95 | 96 | 'guard': 97 | topic: -> 98 | new CoffeeMachine 99 | states: 100 | state1: {} 101 | state2: 102 | guard: -> false 103 | 104 | 'should be setup properly': (topic) -> 105 | assert.isFunction topic.stateMachine.states.state2.guard 106 | 107 | 'state should not change when guard returns false': (topic) -> 108 | topic.changeState('state1', 'state2') 109 | assert.equal topic.currentState(), 'state1' 110 | 111 | 'onStatechange': 112 | topic: -> 113 | new CoffeeMachine 114 | states: 115 | state1: {} 116 | state2: {} 117 | onStateChange: -> throw 'onStateChangeCalled' 118 | 119 | 'should be setup properly': (topic) -> 120 | assert.isFunction topic.stateMachine.onStateChange 121 | 122 | 'onStateChange should get called on state change': (topic) -> 123 | try 124 | topic.changeState('state1', 'state2') 125 | catch e 126 | assert.equal e, 'onStateChangeCalled' 127 | 128 | 'Callbacks should contain state and event info': 129 | topic: -> 130 | new CoffeeMachine 131 | states: 132 | state1: 133 | onExit: (args) -> this.returnedArgs = args 134 | state2: {} 135 | state3: 136 | onEnter: (args) -> this.returnedArgs = args 137 | state4: 138 | guard: (args) -> this.returnedArgs = args 139 | events: 140 | state2to3: {from: 'state2', to: 'state3'} 141 | 142 | 'onExit': (topic) -> 143 | topic.changeState('state1', 'state2') 144 | assert.equal topic.returnedArgs.from, 'state1' 145 | assert.equal topic.returnedArgs.to, 'state2' 146 | 147 | 'onEnter': (topic) -> 148 | topic.state2to3() 149 | assert.equal topic.returnedArgs.from, 'state2' 150 | assert.equal topic.returnedArgs.to, 'state3' 151 | assert.equal topic.returnedArgs.event, 'state2to3' 152 | 153 | 'guard': (topic) -> 154 | topic.changeState('state3', 'state4') 155 | assert.equal topic.returnedArgs.from, 'state3' 156 | assert.equal topic.returnedArgs.to, 'state4' 157 | assert.equal topic.returnedArgs.event, null 158 | 159 | vow.addBatch 160 | 'Events': 161 | topic: -> 162 | new CoffeeMachine 163 | states: ['state1', 'state2', 'state3'] 164 | events: 165 | state1to2: {from:'state1', to:'state2'} 166 | state2to1: {from:'state2', to:'state1'} 167 | anyToState3: {from:'any', to:'state3'} 168 | state2or3toState1: {from:['state2', 'state3'], to:'state1'} 169 | 170 | 'should properly change state': (topic) -> 171 | topic.state1to2() 172 | assert.equal topic.currentState(), 'state2' 173 | topic.state2to1() 174 | assert.equal topic.currentState(), 'state1' 175 | 176 | 'should not change state if the from is different than the defition': (topic) -> 177 | try 178 | topic.state2to1() 179 | assert.equal 1, 2 # hrmmm... don't know how to test the error well 180 | catch e 181 | assert.equal e, "Cannot change from state 'state2'; it is not the active state!" 182 | 183 | 'should change from any state if "any" is the from key': (topic) -> 184 | assert.equal topic.currentState(), 'state1' # We're in state1 185 | topic.anyToState3() 186 | assert.equal topic.currentState(), 'state3' # should change to state3 187 | topic.changeState('state3', 'state2') 188 | assert.equal topic.currentState(), 'state2' # We're now in state2 189 | topic.anyToState3() 190 | assert.equal topic.currentState(), 'state3' # should change to state3 191 | 192 | 'should support an array in the from key': (topic) -> 193 | assert.equal topic.currentState(), 'state3' # We're in state3 194 | topic.state2or3toState1() 195 | assert.equal topic.currentState(), 'state1' 196 | topic.state1to2() 197 | assert.equal topic.currentState(), 'state2' # We're in state2 198 | topic.state2or3toState1() 199 | assert.equal topic.currentState(), 'state1' # We're in state1 200 | try 201 | topic.state2or3toState1() 202 | assert.equal 1, 2 # hrmmm... don't know how to test the error well 203 | catch e 204 | assert.equal e, "Cannot change from states state2 or state3; none are the active state!" 205 | 206 | exports.test_utils = vow --------------------------------------------------------------------------------