├── icon.png ├── logo.png ├── runtests ├── bower.json ├── version ├── browsertest ├── mocha-harness.coffee ├── browserify ├── mocha.runner.html └── jquery-tests.coffee ├── component.json ├── .gitignore ├── spec ├── CompatibilityTest.coffee ├── TickScheduler.coffee ├── Mock.coffee ├── PromiseSpec.coffee └── SpecHelper.coffee ├── test ├── testem.json ├── .travis.yml ├── CONTRIBUTING.md ├── grunt ├── .npmignore ├── update-cdnjs ├── release ├── LICENSE ├── nuspec └── Bacon.js.nuspec ├── Gruntfile.coffee ├── package.json ├── readme ├── doc.coffee ├── signature.pegjs ├── readme.coffee ├── common.coffee └── signature.js ├── examples ├── sliding_window.html └── examples.html ├── performance ├── PerformanceTest.coffee └── MemoryTest.coffee ├── RELEASE-NOTES.md ├── dist └── Bacon.min.js └── lib └── underscore.js /icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/markdalgleish/bacon.js/master/icon.png -------------------------------------------------------------------------------- /logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/markdalgleish/bacon.js/master/logo.png -------------------------------------------------------------------------------- /runtests: -------------------------------------------------------------------------------- 1 | ./node_modules/.bin/mocha --compilers coffee:coffee-script/register spec/*Spec.coffee 2 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bacon", 3 | "version": "0.7.20", 4 | "main": "dist/Bacon.js" 5 | } 6 | -------------------------------------------------------------------------------- /version: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash -e 2 | 3 | cat package.json|grep version|sed 's/.*:.*"\(.*\)".*/\1/' 4 | -------------------------------------------------------------------------------- /browsertest/mocha-harness.coffee: -------------------------------------------------------------------------------- 1 | # insert test files here 2 | require '../spec/BaconSpec' 3 | require '../spec/PromiseSpec' 4 | require './jquery-tests' 5 | -------------------------------------------------------------------------------- /component.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bacon", 3 | "version": "0.7.20", 4 | "main": "dist/Bacon.js", 5 | "repo": "raimohanska/bacon.js", 6 | "scripts": ["dist/Bacon.js"] 7 | } 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules* 2 | npm-debug.log 3 | **/.DS_Store 4 | .DS_Store 5 | .idea/ 6 | *.iml 7 | *.exe 8 | *.nupkg 9 | browsertest/bundle.js 10 | src/Testing* 11 | testing 12 | -------------------------------------------------------------------------------- /spec/CompatibilityTest.coffee: -------------------------------------------------------------------------------- 1 | # A pruned version of BaconSpec, just to see if it runs nicer in Testling.CI 2 | describe "Just testing", -> 3 | it "should always work", -> 4 | console.log "hooray" 5 | -------------------------------------------------------------------------------- /test: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash -e 2 | 3 | npm install --silent 4 | 5 | PATH=node_modules/.bin:$PATH 6 | 7 | ./node_modules/.bin/coffee src/*.coffee 8 | 9 | export grep=$1 10 | 11 | ./runtests 12 | -------------------------------------------------------------------------------- /browsertest/browserify: -------------------------------------------------------------------------------- 1 | # Creates a bundle.js file from node project, so that you can run tests 2 | # in the browser using mocha.runner.html 3 | 4 | node_modules/.bin/browserify browsertest/mocha-harness.coffee > browsertest/bundle.js 5 | -------------------------------------------------------------------------------- /testem.json: -------------------------------------------------------------------------------- 1 | { 2 | "before_tests": "browsertest/browserify", 3 | "test_page": "browsertest/mocha.runner.html", 4 | "framework": "mocha", 5 | "src_files": [ 6 | "src/*.coffee", 7 | "spec/*.coffee" 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | 3 | node_js: 4 | - "0.10" 5 | 6 | before_install: npm conf set strict-ssl false 7 | 8 | after_install: 9 | - sudo npm install -g phantomjs 10 | 11 | before_script: 12 | - phantomjs --version 13 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Use [GitHub issues](https://github.com/baconjs/bacon.js/issues) and [Pull Requests](https://github.com/baconjs/bacon.js/pulls). 2 | 3 | Note: the `README.md` generated from `readme-src.coffee`. After updating the src file, run `grunt readme`. 4 | -------------------------------------------------------------------------------- /grunt: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # Wrapper script for locally installed grunt-cli (https://github.com/gruntjs/grunt-cli). 4 | # With this there's no need to install the tool globally. 5 | # 6 | 7 | set -eu 8 | 9 | node_modules/.bin/grunt $@ 10 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .npmignore 2 | .travis.yml 3 | bower.json 4 | browsertest 5 | component.json 6 | examples 7 | grunt 8 | Gruntfile.coffee 9 | icon.png 10 | lib 11 | logo.png 12 | nuspec 13 | performance 14 | readme 15 | readme-src.coffee 16 | RELEASE-NOTES.md 17 | release 18 | runtests 19 | spec 20 | src 21 | test 22 | testem.json 23 | update-cdnjs 24 | version 25 | -------------------------------------------------------------------------------- /update-cdnjs: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash -e 2 | 3 | if [ -z $1 ]; then 4 | echo "usage: update-cdnjs " 5 | exit 1 6 | fi 7 | 8 | version=$1 9 | dir=../cdnjs/ajax/libs/bacon.js 10 | 11 | if [[ ! -d "$dir/$version" ]]; then 12 | mkdir $dir/$version 13 | fi 14 | 15 | sed -i "" 's/\("version".*:.*\)".*"/\1"'$version'"/' $dir/package.json 16 | 17 | git checkout -q $version 18 | 19 | cp dist/Bacon.js $dir/$version/bacon.js 20 | cp dist/Bacon.min.js $dir/$version/bacon.min.js 21 | 22 | git checkout -q master 23 | -------------------------------------------------------------------------------- /release: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash -e 2 | 3 | if [ -z $1 ]; then 4 | echo "usage: release " 5 | exit 1 6 | fi 7 | version=$1 8 | files="component.json package.json bower.json" 9 | echo "Releasing with version $version" 10 | 11 | echo "Pulling from origin" 12 | git pull --rebase 13 | 14 | echo "Building" 15 | npm install 16 | npm test 17 | ./grunt 18 | 19 | echo "Updating files" 20 | sed -i "" 's/\("version".*:.*\)".*"/\1"'$version'"/' $files 21 | sed -i "" 's//'$version'/' dist/*.js 22 | 23 | echo "Commit and tag" 24 | git add . 25 | git commit -m "release $version" 26 | git tag $version 27 | 28 | echo "Push to origin/master" 29 | git push 30 | git push --tags origin 31 | 32 | echo "Publish to npm" 33 | npm publish 34 | 35 | echo "DONE!" 36 | -------------------------------------------------------------------------------- /browsertest/mocha.runner.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Mocha Tests 5 | 6 | 7 | 8 | 11 | 15 | 16 | 25 | 26 | 27 |
28 |
29 |
30 | 31 | 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2014 Juha Paananen 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /nuspec/Bacon.js.nuspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Bacon.js 5 | Bacon.js - Functional Reactive Programming library for JavaScript 6 | 0.7.16 7 | Bacon.js Organization 8 | 9 | Functional reactive programming lib for JavaScript. Turns your event spaghetti into clean and declarative feng shui bacon, by switching from imperative to functional. 10 | 11 | http://baconjs.github.io 12 | https://github.com/baconjs/bacon.js/raw/master/icon.png 13 | false 14 | http://bit.ly/mit-license 15 | en-US 16 | Bacon Bacon.js Reactive Functional Observable FRP Event 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /spec/TickScheduler.coffee: -------------------------------------------------------------------------------- 1 | Bacon = (require "../src/Bacon").Bacon 2 | _ = Bacon._ 3 | 4 | exports.TickScheduler = -> 5 | counter = 1 6 | currentTick = 0 7 | schedule = {} 8 | toRemove = [] 9 | nextId = -> counter++ 10 | running = false 11 | 12 | add = (delay, entry) -> 13 | tick = currentTick + delay 14 | entry.id = nextId() if not entry.id 15 | schedule[tick] = [] if not schedule[tick] 16 | schedule[tick].push entry 17 | entry.id 18 | boot = (id) -> 19 | if not running 20 | running = true 21 | setTimeout run, 0 22 | id 23 | run = -> 24 | while Object.keys(schedule).length 25 | while schedule[currentTick]?.length 26 | forNow = schedule[currentTick].splice(0) 27 | for entry in forNow 28 | if _.contains(toRemove, entry.id) 29 | _.remove(entry.id, toRemove) 30 | else 31 | try 32 | entry.fn() 33 | catch e 34 | console.log("TickScheduler caught", e) unless e == "testing" 35 | add entry.recur, entry if entry.recur 36 | delete schedule[currentTick] 37 | currentTick++ 38 | running = false 39 | { 40 | setTimeout: (fn, delay) -> boot(add delay, { fn }) 41 | setInterval: (fn, recur) -> boot(add recur, { fn, recur }) 42 | clearTimeout: (id) -> toRemove.push(id) 43 | clearInterval: (id) -> toRemove.push(id) 44 | now: -> currentTick 45 | } 46 | -------------------------------------------------------------------------------- /browsertest/jquery-tests.coffee: -------------------------------------------------------------------------------- 1 | sinon = require 'sinon' 2 | Bacon = require '../src/Bacon' 3 | expect = require('chai').expect 4 | $.fn.asEventStream = Bacon.$.asEventStream 5 | 6 | elemName = (event) -> 7 | event.target.localName 8 | 9 | describe 'asEventStream', -> 10 | it 'supports simple format', -> 11 | mock = sinon.spy() 12 | $('body').asEventStream('click').map(elemName).take(1).onValue(mock) 13 | $('body').click() 14 | expect(mock.callCount).to.equal(1) 15 | expect(mock.firstCall.args[0]).to.equal('body') 16 | it 'supports jQuery live selector format', -> 17 | mock = sinon.spy() 18 | $('html').asEventStream('click', 'body').map(elemName).take(1).onValue(mock) 19 | $('body').click() 20 | expect(mock.callCount).to.equal(1) 21 | expect(mock.firstCall.args[0]).to.equal('body') 22 | it 'supports optional eventTransformer, with jQuery live selector', -> 23 | mock = sinon.spy() 24 | $('html').asEventStream('click', 'body', elemName).take(1).onValue(mock) 25 | $('body').click() 26 | expect(mock.callCount).to.equal(1) 27 | expect(mock.firstCall.args[0]).to.equal('body') 28 | it 'supports optional eventTransformer, without jQuery live selector', -> 29 | mock = sinon.spy() 30 | $('body').asEventStream('click', elemName).take(1).onValue(mock) 31 | $('body').click() 32 | expect(mock.callCount).to.equal(1) 33 | expect(mock.firstCall.args[0]).to.equal('body') 34 | 35 | -------------------------------------------------------------------------------- /Gruntfile.coffee: -------------------------------------------------------------------------------- 1 | module.exports = (grunt) -> 2 | grunt.initConfig 3 | clean: 4 | dist: ['dist/'] 5 | coffee: ['dist/*.coffee'] 6 | 7 | coffee: 8 | compile: 9 | expand: true 10 | files: [ 11 | 'dist/Bacon.js': 'dist/Bacon.coffee' 12 | 'dist/Bacon.min.js': 'dist/Bacon.noAssert.coffee' 13 | ] 14 | 15 | uglify: 16 | dist: 17 | files: 18 | 'dist/Bacon.min.js': 'dist/Bacon.min.js' 19 | 20 | copy: 21 | dist: 22 | expand:true 23 | files:[ 24 | 'dist/Bacon.coffee': 'src/Bacon.coffee' 25 | ] 26 | replace: 27 | asserts: 28 | dest: 'dist/Bacon.noAssert.coffee' 29 | src: ['dist/Bacon.coffee'] 30 | replacements: [ 31 | from: /assert.*/g 32 | to: '' 33 | ] 34 | watch: 35 | coffee: 36 | files: 'src/*.coffee' 37 | tasks: 'build' 38 | 39 | 40 | grunt.loadNpmTasks 'grunt-contrib-coffee' 41 | grunt.loadNpmTasks 'grunt-contrib-clean' 42 | grunt.loadNpmTasks 'grunt-contrib-copy' 43 | grunt.loadNpmTasks 'grunt-contrib-uglify' 44 | grunt.loadNpmTasks 'grunt-contrib-watch' 45 | grunt.loadNpmTasks 'grunt-text-replace' 46 | 47 | grunt.registerTask 'build', ['clean:dist', 'copy', 'replace:asserts', 'coffee', 'uglify', 'clean:coffee'] 48 | grunt.registerTask 'default', ['build'] 49 | 50 | grunt.registerTask 'readme', 'Generate README.md', -> 51 | fs = require 'fs' 52 | readmedoc = require './readme-src.coffee' 53 | readmegen = require './readme/readme.coffee' 54 | fs.writeFileSync('README.md', readmegen readmedoc) 55 | -------------------------------------------------------------------------------- /spec/Mock.coffee: -------------------------------------------------------------------------------- 1 | expect = require("chai").expect 2 | 3 | class Mock 4 | constructor: (methodNames...) -> 5 | for name in methodNames 6 | this[name] = mockFunction(name) 7 | this.verify = () => 8 | verifier = {} 9 | for name in methodNames 10 | verifier[name] = this[name].verify 11 | verifier 12 | this.when = () => 13 | returner = {} 14 | assign = (name) => 15 | returner[name] = (args...) => 16 | { 17 | thenReturn: (returnValue) => 18 | this[name].doReturn(returnValue).when(args...) 19 | } 20 | for name in methodNames 21 | assign(name) 22 | returner 23 | 24 | mockFunction = (name) -> 25 | calls = [] 26 | returns = [] 27 | method = (args...) => 28 | calls.push(args) 29 | for returnCombo in returns 30 | #console.log("check #{args} against #{name}(#{returnCombo.args})") 31 | if eq(returnCombo.args, args) 32 | #console.log("match => #{returnCombo.returnValue}") 33 | return returnCombo.returnValue 34 | method.verify = (args...) -> 35 | if !calls 36 | throw "not called: #{name}" 37 | actualCall = calls[0] 38 | calls.splice(0,1) 39 | expect(actualCall).to.deep.equal(args) 40 | method.doReturn = (returnValue) -> 41 | { 42 | when: (args...) -> 43 | #console.log("#{name}(#{args}) => #{returnValue}") 44 | returns.push({ args: args, returnValue: returnValue}) 45 | } 46 | method 47 | eq = (xs, ys) -> 48 | return false if (xs.length != ys.length) 49 | for x, i in xs 50 | return false if (x != ys[i]) 51 | true 52 | @mock = (methodNames...) -> new Mock(methodNames...) 53 | @mockFunction = mockFunction 54 | -------------------------------------------------------------------------------- /spec/PromiseSpec.coffee: -------------------------------------------------------------------------------- 1 | expect = require("chai").expect 2 | Bacon = (require "../src/Bacon").Bacon 3 | 4 | success = undefined 5 | fail = undefined 6 | calls = 0 7 | promise = { 8 | then: (s, f) -> 9 | success = s 10 | fail = f 11 | calls = calls + 1 12 | } 13 | _ = Bacon._ 14 | nop = -> 15 | 16 | describe "Bacon.fromPromise", -> 17 | it "should produce value and end on success", -> 18 | events = [] 19 | Bacon.fromPromise(promise).subscribe( (e) => events.push(e)) 20 | success("a") 21 | expect(_.map(((e) -> e.toString()), events)).to.deep.equal(["a", ""]) 22 | 23 | it "should produce error and end on error", -> 24 | events = [] 25 | Bacon.fromPromise(promise).subscribe( (e) => events.push(e)) 26 | fail("a") 27 | expect(events.map((e) -> e.toString())).to.deep.equal([" a", ""]) 28 | 29 | it "should respect unsubscription", -> 30 | events = [] 31 | dispose = Bacon.fromPromise(promise).subscribe( (e) => events.push(e)) 32 | dispose() 33 | success("a") 34 | expect(events).to.deep.equal([]) 35 | 36 | it "should abort ajax promise on unsub, if abort flag is set", -> 37 | isAborted = false 38 | promise.abort = -> 39 | isAborted = true 40 | dispose = Bacon.fromPromise(promise, true).subscribe(nop) 41 | dispose() 42 | delete promise.abort 43 | expect(isAborted).to.deep.equal(true) 44 | 45 | it "should not abort ajax promise on unsub, if abort flag is not set", -> 46 | isAborted = false 47 | promise.abort = -> 48 | isAborted = true 49 | dispose = Bacon.fromPromise(promise).subscribe(nop) 50 | dispose() 51 | delete promise.abort 52 | expect(isAborted).to.deep.equal(false) 53 | 54 | it "should not abort non-ajax promise", -> 55 | isAborted = false 56 | dispose = Bacon.fromPromise(promise).subscribe(nop) 57 | dispose() 58 | expect(isAborted).to.deep.equal(false) 59 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "baconjs", 3 | "description": "A small functional reactive programming lib for JavaScript.", 4 | "version": "0.7.20", 5 | "keywords": [ 6 | "bacon.js", "bacon", "frp", "functional", "reactive", "programming", 7 | "stream", "streams", "EventStream", "Rx", "RxJs", "Observable" 8 | ], 9 | "author": { 10 | "name": "Juha Paananen", 11 | "email": "juha.paananen@gmail.com", 12 | "url": "https://twitter.com/raimohanska" 13 | }, 14 | "contributors": [ 15 | { 16 | "name": "Daniel K", 17 | "email": "x.flowwolf@gmail.com", 18 | "url": "https://twitter.com/xflowwolf" 19 | } 20 | ], 21 | "licenses": [ 22 | "MIT" 23 | ], 24 | "dependencies": {}, 25 | "devDependencies": { 26 | "jasmine-node": "~1.13.1", 27 | "coffee-script": "~1.7.1", 28 | "grunt": "0.4", 29 | "grunt-cli": "0.1.13", 30 | "grunt-contrib-coffee": "~0.10.0", 31 | "grunt-contrib-clean": "~0.5.0", 32 | "grunt-contrib-copy": "~0.5.0", 33 | "grunt-contrib-watch": "~0.5", 34 | "grunt-text-replace": "~0.3.10", 35 | "benchmark": "1.0.0", 36 | "browserify": "~3.28.1", 37 | "sinon": "~1.8.1", 38 | "grunt-contrib-uglify": "~0.3.2", 39 | "chai": "1.9.0", 40 | "mocha": "1.17.1", 41 | "lodash": "~2.4.1" 42 | }, 43 | "repository": { 44 | "type": "git", 45 | "url": "https://github.com/baconjs/bacon.js.git" 46 | }, 47 | "homepage": "https://github.com/baconjs/bacon.js", 48 | "testling": { 49 | "browsers": { 50 | "ie": [ 6, 7, 8, 9, 10 ], 51 | "ff": [ 12, 17, 22 ], 52 | "chrome": [ 22 ], 53 | "opera": [ 12 ], 54 | "safari": [ 5.1, 6 ], 55 | "iphone": [ 6 ], 56 | "ipad": [ 6 ] 57 | }, 58 | "harness": "mocha", 59 | "files": "spec/CompatibilityTest.coffee" 60 | }, 61 | "scripts": { 62 | "test": "./runtests", 63 | "pre-publish": "grunt" 64 | }, 65 | "main": "dist/Bacon.js" 66 | } 67 | -------------------------------------------------------------------------------- /readme/doc.coffee: -------------------------------------------------------------------------------- 1 | _ = require "lodash" 2 | assert = require "assert" 3 | common = require "./common.coffee" 4 | signature = require "./signature.js" 5 | 6 | signatureParse = (s) -> 7 | try 8 | signature.parse s 9 | catch e 10 | console.error "Can't parse type signature:", s, e.message 11 | undefined 12 | 13 | # Sections and sub(sub) sections are virtually the same 14 | SECTION_SPEC = 15 | params: ["name"] 16 | process: (element) -> 17 | element.anchorName = common.anchorName element.name 18 | element 19 | 20 | # our doc DSL specification 21 | DOC_SPEC = 22 | logo: 23 | params: [] 24 | 25 | toc: 26 | params: [] 27 | 28 | section: SECTION_SPEC 29 | subsection: SECTION_SPEC 30 | subsubsection: SECTION_SPEC 31 | 32 | text: 33 | params: ["content"] 34 | 35 | fn: 36 | params: ["signature", "content"] 37 | process: (element) -> 38 | element.parsedSignature = signatureParse element.signature 39 | element.anchorName = common.functionAnchorName element.parsedSignature 40 | element 41 | 42 | fnOverload: 43 | params: ["signature", "anchorSuffix", "content"] 44 | process: (element) -> 45 | element.type = "fn" 46 | element.parsedSignature = signatureParse element.signature 47 | element.anchorName = common.functionAnchorName(element.parsedSignature) + "-" + element.anchorSuffix 48 | element 49 | 50 | class Marble 51 | constructor: -> 52 | @i = [] 53 | @o = undefined 54 | @type = "marble" 55 | 56 | input: (v) -> 57 | @i.push(v) 58 | @ 59 | 60 | output: (v) -> 61 | @o = v 62 | 63 | # DSL helper class 64 | class Doc 65 | constructor: -> 66 | @elements = [] 67 | 68 | push: (element) -> 69 | @elements.push element 70 | 71 | dump: -> 72 | console.log @elements 73 | 74 | marble: () -> 75 | m = new Marble 76 | @elements.push m 77 | m 78 | 79 | _.each DOC_SPEC, (spec, name) -> 80 | Doc::[name] = () -> 81 | assert.equal arguments.length, spec.params.length, 82 | "There should be same amount of params as spec'd" 83 | 84 | element = 85 | type: name 86 | 87 | for index in [0...spec.params.length] 88 | element[spec.params[index]] = arguments[index] 89 | 90 | if spec.process? 91 | element = spec.process element 92 | 93 | @push element 94 | 95 | module.exports = Doc 96 | 97 | -------------------------------------------------------------------------------- /readme/signature.pegjs: -------------------------------------------------------------------------------- 1 | functionsignature = n:new? c:identifier m:method? p:params? r:typeDefinition? { 2 | return { 3 | n: !!n, 4 | namespace: c, 5 | method: m, 6 | params: p, 7 | result: r, 8 | }; 9 | } 10 | 11 | // tokens 12 | whitespace = [ \t]+ 13 | identifier = x:[a-zA-Z_$] xs:[a-zA-Z0-9_$]* { return [x].concat(xs).join(""); } 14 | ob = "(" whitespace? 15 | cb = ")" whitespace? 16 | os = "[" whitespace? 17 | cs = "]" whitespace? 18 | 19 | // Method signature 20 | method = "." fn:identifier { return fn; } 21 | 22 | new = "new" whitespace { return "new" } 23 | 24 | params 25 | = ob cb { return []; } 26 | / ob "[" whitespace? p:param cs cb { return [p]; } 27 | / "(" p:firstparam r:rparams cb { return [p].concat(r); } 28 | 29 | rparams = restparam* 30 | 31 | restparam = restparamMandatory / restparamOptional 32 | restparamOptional = "[" whitespace? p:restparamMandatory cs { 33 | p.optional = true; 34 | return p; 35 | } 36 | restparamMandatory = "," whitespace? p:param { return p; } 37 | 38 | firstparam = param / thisparam 39 | 40 | param = i:identifier whitespace? t:typeDefinition? r:("...")? whitespace? { 41 | return { 42 | name: i, 43 | type: t, 44 | splat: !!r, 45 | }; 46 | } 47 | 48 | thisparam = "@" whitespace? t:typeDefinition { 49 | return { 50 | name: "@", 51 | type: t, 52 | }; 53 | } 54 | 55 | // Type 56 | typeDefinition = ":" whitespace? t:type { return t; } 57 | 58 | type = functionType / scalarType / unitType 59 | 60 | unitType = "()" { 61 | return { 62 | type: "unit", 63 | }; 64 | } 65 | 66 | scalarType = alternativeType / scalarFunction 67 | scalarFunction = ob t:functionType cb { return t; } 68 | 69 | alternativeType 70 | = x:variableType xs:alternativeRest { 71 | if (xs.length === 0) { 72 | return x; 73 | } else { 74 | return { 75 | type: "sum", 76 | options: [x].concat(xs), 77 | }; 78 | } 79 | } 80 | 81 | alternativeRest = ("|" whitespace? v:variableType { return v; })* 82 | 83 | variableType = i:identifier whitespace? p:typeParameters? { 84 | return { 85 | type: "name", 86 | name: i, 87 | params: p ? [p] : [] 88 | }; 89 | } 90 | 91 | functionType 92 | = x:scalarType? xs:("->" whitespace? y:scalarType? { return y; })+ { return { 93 | type: "function", 94 | ret: xs.slice(-1)[0] 95 | };} 96 | 97 | typeParameters = os t:type cs { return t; } 98 | -------------------------------------------------------------------------------- /examples/sliding_window.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Sliding Windows Example 5 | 6 | 7 | 13 | 14 | 15 | 88 | 89 | 90 | -------------------------------------------------------------------------------- /performance/PerformanceTest.coffee: -------------------------------------------------------------------------------- 1 | Benchmark = require('benchmark') 2 | Bacon = (require "../src/Bacon").Bacon 3 | 4 | _ = Bacon._ 5 | f = 6 | generator: -> 7 | streams = [] 8 | { 9 | stream: -> 10 | counter = 0 11 | bus = new Bacon.Bus() 12 | bus.tick = -> @push(counter = counter + 1) 13 | streams.push(bus) 14 | bus 15 | tick: -> 16 | s.tick() for s in streams 17 | ticks: (count) -> 18 | @tick() for i in [1..count] 19 | } 20 | everyNth: (n, stream) -> 21 | stream.filter (x) -> x % n == 0 22 | withGenerator: (fun, rounds=100) -> 23 | gen = f.generator() 24 | fun(gen).onValue((v) -> ) 25 | gen.ticks(rounds) 26 | combineTemplate: (gen, width, depth) -> 27 | if depth == 0 28 | gen.stream() 29 | else 30 | template = {} 31 | for i in [1..width] 32 | template[i] = f.combineTemplate gen, width, depth-1 33 | Bacon.combineTemplate(template) 34 | diamond: (src, width, depth) -> 35 | if depth == 0 36 | src 37 | else 38 | branches = (f.diamond(src.map(->), width, depth-1) for s in [1..width]) 39 | Bacon.combineAsArray branches 40 | 41 | zip: (gen) -> 42 | gen.stream().zip(gen.stream()) 43 | 44 | suite = new Benchmark.Suite 45 | 46 | suite.add 'diamond', -> 47 | f.withGenerator(((gen) -> 48 | s = f.diamond(gen.stream(), 3, 5) 49 | s.onValue -> 50 | s 51 | ),1) 52 | suite.add 'combo', -> 53 | f.withGenerator(((gen) -> 54 | s = f.combineTemplate(gen, 4, 4) 55 | s.onValue -> 56 | s), 1) 57 | suite.add 'zip', -> 58 | f.withGenerator (gen) -> 59 | f.zip(gen) 60 | suite.add 'flatMap', -> 61 | f.withGenerator (gen) -> 62 | gen.stream().flatMap (x) -> 63 | gen.stream().take(3) 64 | suite.add 'Bacon.combineTemplate.sample', -> 65 | f.withGenerator (gen) -> 66 | f.combineTemplate(gen, 5, 1) 67 | .sampledBy(f.everyNth(10, gen.stream())) 68 | suite.add 'Bacon.combineTemplate (deep)', -> 69 | f.withGenerator (gen) -> 70 | f.combineTemplate(gen, 3, 3) 71 | suite.add 'Bacon.combineTemplate', -> 72 | f.withGenerator (gen) -> 73 | f.combineTemplate(gen, 5, 1) 74 | suite.add 'EventStream.map', -> 75 | f.withGenerator (gen) -> 76 | gen.stream().map((x) -> x * 2) 77 | suite.add 'EventStream.scan', -> 78 | f.withGenerator (gen) -> 79 | gen.stream().scan(0, (x,y) -> x+y) 80 | suite.add 'EventStream.toProperty', -> 81 | f.withGenerator (gen) -> 82 | gen.stream().toProperty() 83 | suite.on 'cycle', (event) -> 84 | console.log(String(event.target)) 85 | .on "error", (error) -> 86 | console.log(error) 87 | .run({ 'async': false }) 88 | -------------------------------------------------------------------------------- /readme/readme.coffee: -------------------------------------------------------------------------------- 1 | common = require "./common.coffee" 2 | _ = require "lodash" 3 | 4 | renderToc = (elements) -> 5 | toc = "" 6 | _.each elements, (element) -> 7 | switch element.type 8 | when "toc" 9 | toc += '- [Table of contents](#table-of-contents)\n' 10 | when "section" 11 | toc += '- [' + element.name + '](#' + common.anchorName(element.name) + ')' + '\n' 12 | when "subsection" 13 | toc += ' - [' + element.name + '](#' + common.anchorName(element.name) + ')' + '\n' 14 | when "subsubsection" 15 | toc += ' - [' + element.name + '](#' + common.anchorName(element.name) + ')' + '\n' 16 | 17 | _.flatten _.map elements, (element) -> 18 | switch element.type 19 | when "toc" 20 | title = 21 | type: "section" 22 | name: "Table of contents" 23 | content = 24 | type: "text" 25 | content: toc 26 | [title, content] 27 | else 28 | [element] 29 | 30 | renderSignature = (parsedSignature) -> 31 | n = if parsedSignature.n then "new " else "" 32 | o = parsedSignature.namespace 33 | m = parsedSignature.method 34 | 35 | name = (n + o + "." + m) 36 | 37 | params = parsedSignature.params?.filter (p) -> 38 | p.name != "@" 39 | params = params?.map (p, i) -> 40 | r = p.name 41 | if i != 0 42 | r = ", " + r 43 | 44 | if p.splat 45 | r = r + "..." 46 | 47 | if p.optional 48 | r = "[" + r + "]" 49 | if i != 0 50 | r = " " + r 51 | 52 | r 53 | 54 | if params 55 | name + "(" + params.join("") + ")" 56 | else 57 | name 58 | 59 | renderElement = (element) -> 60 | switch element.type 61 | when "text" 62 | element.content 63 | when "section" 64 | element.name + "\n" + common.repeatString "=", element.name.length 65 | when "subsection" 66 | element.name + "\n" + common.repeatString "-", element.name.length 67 | when "subsubsection" 68 | '### ' + element.name 69 | when "fn" 70 | anchor = '' 71 | anchor + "\n[`" + renderSignature(element.parsedSignature) + '`](#' + element.anchorName + ' "' + element.signature + '") ' + element.content 72 | when "logo" 73 | """""" 74 | when "marble" 75 | undefined # filter marbles from README.md 76 | else 77 | throw new Error("Unknown token type: " + element.type) 78 | 79 | render = (doc) -> 80 | elements = _.cloneDeep doc.elements 81 | 82 | elements = common.preprocess elements 83 | 84 | elements = renderToc elements 85 | 86 | _.chain(elements) 87 | .map(renderElement) 88 | .compact() 89 | .join("\n\n") 90 | .value() + "\n" 91 | 92 | module.exports = render 93 | -------------------------------------------------------------------------------- /readme/common.coffee: -------------------------------------------------------------------------------- 1 | _ = require "lodash" 2 | 3 | repeatString = (string, n) -> 4 | (string for num in [1..n]).join("") 5 | 6 | anchorName = (str) -> 7 | str.toLowerCase().replace(/[^\w ]/g, "").replace /(.)/g, (c) -> 8 | if (c == " ") 9 | "-" 10 | else 11 | c 12 | 13 | functionAnchorName = (parsedSignature) -> 14 | if parsedSignature 15 | n = if parsedSignature.n then "new-" else "" 16 | o = parsedSignature.namespace 17 | m = parsedSignature.method 18 | 19 | (n + o + "-" + m).toLowerCase() 20 | 21 | checkDuplicateAnchors = (anchors) -> 22 | _.each anchors, (anchor) -> 23 | f = _.filter anchors, (a) -> a == anchor 24 | if (f.length > 1) 25 | console.error("Duplicate anchor name: " + anchor) 26 | 27 | checkLinks = (elements, anchors) -> 28 | textElements = _.filter elements, (element) -> 29 | element.type == "text" || element.type == "fn" 30 | 31 | _.each textElements, (element) -> 32 | ms = element.content.match(/\(#[a-z\-]+\)/g) 33 | if ms? 34 | ms = _.map ms, (m) -> m[2..m.length-2] 35 | diff = _.difference ms, anchors 36 | if diff.length > 0 37 | console.error("Undefined anchors: " + diff.join(", ")) 38 | 39 | strEndsWith = (name, suffix) -> 40 | name[-suffix.length..] == suffix 41 | 42 | findFunctionAnchor = (text, anchors) -> 43 | m = text.toLowerCase().match /^(\w+)\.(\w+)/ 44 | if m? 45 | fnAnchor = m[1] + "-" + m[2] 46 | if fnAnchor? && _.contains anchors, fnAnchor 47 | fnAnchor 48 | 49 | findAnchorByNeedle = (text, anchors) -> 50 | anchorNeedle = text.toLowerCase().replace(".", "-").replace(/[^a-z\-]/g, "") 51 | 52 | if _.contains anchors, anchorNeedle 53 | anchorNeedle 54 | else if _.contains ["subscribe", "f"], anchorNeedle 55 | undefined 56 | else 57 | possibleAnchors = _.filter anchors, (anchor) -> 58 | strEndsWith anchor, "-" + anchorNeedle 59 | 60 | if possibleAnchors.length == 1 61 | possibleAnchors[0] 62 | else if possibleAnchors.length > 1 63 | console.warn "Multiple matches:", text, possibleAnchors 64 | possibleAnchors[0] 65 | else 66 | undefined 67 | 68 | findAnchor = (text, anchors) -> 69 | findFunctionAnchor(text, anchors) || findAnchorByNeedle(text, anchors) 70 | 71 | linkifyCodeblocks = (elements, anchors) -> 72 | _.map elements, (element) -> 73 | if element.type != "text" && element.type != "fn" 74 | element 75 | else 76 | newContent = element.content.replace /(^|[^`\[])`([a-zA-Z\.\(\)]+?)`([^`\]]|$)/g , (all, before, code, after) -> 77 | anchor = findAnchor code, anchors 78 | if anchor? 79 | before + '[`' + code + '`](#' + anchor + ')' + after 80 | else 81 | all 82 | 83 | _.extend {}, element, 84 | content: newContent 85 | 86 | preprocess = (elements) -> 87 | anchors = _.compact _.map elements, (element) -> element.anchorName 88 | 89 | # Few checks 90 | checkDuplicateAnchors anchors 91 | checkLinks elements, anchors 92 | 93 | # Automatic `foo.bar` -> [`foo.bar`](#foo-bar) 94 | elements = linkifyCodeblocks elements, anchors 95 | 96 | elements 97 | 98 | module.exports = 99 | repeatString: repeatString 100 | anchorName: anchorName 101 | functionAnchorName: functionAnchorName 102 | preprocess: preprocess 103 | -------------------------------------------------------------------------------- /examples/examples.html: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 88 | 89 | 90 |

bacon.js example page

91 |
92 |

Simplest example:

93 | click me 94 |
95 |
96 |

Combinators example:

97 | SPACEBAR value= 98 |
99 |
100 |

Enable/disable example:

101 | 102 | 103 |
104 |
105 |

Echo example:

106 | 107 | 108 |
109 |
110 |

Combination lock:

111 | 123 | 135 | 147 | 148 | Hint: Try combo 180 149 | 153 |
154 |
155 |

Batter up! (take/skip example):

156 | 157 | 158 |

♪♫ ...for it's one, two, three strikes you're out at the old ball game ♪♫

159 |
160 | 161 | 162 | -------------------------------------------------------------------------------- /performance/MemoryTest.coffee: -------------------------------------------------------------------------------- 1 | Bacon = (require "../src/Bacon").Bacon 2 | toKb = (x) -> (x / 1024).toFixed(2) + ' KiB' 3 | toMb = (x) -> (x / 1024 / 1024).toFixed(2) + ' MiB' 4 | 5 | byteFormat = (bytes, comparison) -> 6 | if Math.abs(comparison || bytes) > 512 * 1024 7 | toMb(bytes) 8 | else 9 | toKb(bytes) 10 | 11 | lpad = (string, length = 12) -> 12 | while(string.length < length) 13 | string = " #{string}" 14 | string 15 | 16 | rpad = (string, length = 12) -> 17 | while(string.length < length) 18 | string = "#{string} " 19 | string 20 | measure = (fun) -> 21 | global.gc() 22 | lastMemoryUsage = process.memoryUsage().heapUsed 23 | startTime = Date.now() 24 | fun() 25 | duration = Date.now() - startTime 26 | global.gc() 27 | [process.memoryUsage().heapUsed - lastMemoryUsage, duration] 28 | 29 | sum = (xs) -> xs.reduce (sum, x) -> sum + x 30 | mean = (xs) -> sum(xs) / xs.length 31 | stddev = (xs) -> 32 | avg = mean(xs) 33 | Math.pow(mean(Math.pow((x - avg), 2) for x in xs), 0.5) 34 | 35 | processResults = (results, i) -> 36 | values = (x[i] for x in results) 37 | 38 | mean: mean(values) 39 | stddev: stddev(values) 40 | 41 | printResult = (label, result, forcePrefix = false) -> 42 | prefix = if prefix && result.mean > 0 then '+' else '' 43 | console.log(" #{rpad(label, 20)}", lpad(prefix + byteFormat(result.mean), 12), '\u00b1', byteFormat(result.stddev, result.mean)) 44 | createNObservable = (count, generator) -> 45 | n = Math.floor(count / 10) 46 | m = 10 47 | 48 | results = for i in [0...m] 49 | global.gc() 50 | objects = new Array(n) # Preallocate array of n elements 51 | unsubscribers = new Array(n) 52 | subscribe = -> 53 | for i in [0...objects.length] 54 | unsubscribers[i] = objects[i].onValue(noop) 55 | unsubscribe = -> 56 | for i in [0...objects.length] 57 | unsubscribers[i]() 58 | unsubscribers[i] = null 59 | 60 | global.gc() 61 | withoutSubscriber = measure -> 62 | objects[i] = generator(i) for i in [0...objects.length] 63 | 64 | withSubscriber = measure subscribe 65 | afterCleanup = measure unsubscribe 66 | reSubscribe = measure subscribe 67 | afterReCleanup = measure unsubscribe 68 | 69 | objects = null 70 | unsubscribers = null 71 | [withoutSubscriber[0]/n, withSubscriber[0]/n, afterCleanup[0]/n, reSubscribe[0]/n, afterReCleanup[0]/n] 72 | 73 | withoutSubscriber = processResults(results, 0) 74 | withSubscriber = processResults(results, 1) 75 | afterCleanup = processResults(results, 2) 76 | reSubscribe = processResults(results, 3) 77 | afterReCleanup = processResults(results, 4) 78 | 79 | printResult('w/o subscription', withoutSubscriber) 80 | printResult('with subscription', withSubscriber, true) 81 | printResult('unsubscribe', afterCleanup, true) 82 | printResult('subscribe again', reSubscribe, true) 83 | printResult('unsubscribe again', afterReCleanup, true) 84 | 85 | title = (text) -> console.log('\n' + text) 86 | noop = -> 87 | 88 | 89 | # Keep reference to listeners during test run 90 | fakeSource = 91 | listeners: [] 92 | subscribe: (listener) -> @listeners.push(listener) 93 | unsubscribe: (listener) -> 94 | index = @listeners.indexOf(listener) 95 | @listeners.splice(index, 1) if index != -1 96 | 97 | 98 | eventStream = -> 99 | new Bacon.EventStream (sink) -> 100 | fakeSource.subscribe(sink) 101 | -> fakeSource.unsubscribe(sink) 102 | 103 | diamond = (src, width, depth) -> 104 | if depth == 0 105 | src 106 | else 107 | branches = (diamond(src.map(->), width, depth-1) for s in [1..width]) 108 | Bacon.combineAsArray branches 109 | 110 | combineTemplate = (gen, width, depth) -> 111 | if depth == 0 112 | gen() 113 | else 114 | template = {} 115 | for i in [1..width] 116 | template[i] = combineTemplate gen, width, depth-1 117 | Bacon.combineTemplate(template) 118 | 119 | title "new EventStream" 120 | createNObservable 700, eventStream 121 | 122 | title "new Bus()" 123 | createNObservable 700, -> new Bacon.Bus() 124 | 125 | 126 | title "Bacon.once(1)" 127 | createNObservable 700, (i) -> 128 | Bacon.once(i) 129 | 130 | title "Bacon.never()" 131 | createNObservable 700, -> 132 | Bacon.never() 133 | 134 | title "EventStream::toProperty(1)" 135 | createNObservable 700, -> 136 | eventStream().toProperty(1) 137 | 138 | title "EventStream::toProperty(1).changes()" 139 | createNObservable 700, -> 140 | eventStream().toProperty(1).changes() 141 | 142 | title "EventStream::map(noop)" 143 | createNObservable 700, -> 144 | eventStream().map(noop) 145 | 146 | title "EventStream::filter(noop)" 147 | createNObservable 700, -> 148 | eventStream().filter(noop) 149 | 150 | title "EventStream::scan(0, noop)" 151 | createNObservable 700, -> 152 | eventStream().scan(0, noop) 153 | 154 | title "Bacon.sequentially(0, [1, 2])" 155 | createNObservable 700, -> 156 | Bacon.sequentially(0, [1, 2]) 157 | 158 | title "EventStream::take(5)" 159 | createNObservable 700, -> 160 | eventStream().take(5) 161 | 162 | title "EventStream::flatMap(noop)" 163 | createNObservable 700, -> 164 | eventStream().flatMap(noop) 165 | 166 | title "EventStream::combine(stream, noop)" 167 | createNObservable 700, -> 168 | eventStream().combine(eventStream(), noop) 169 | 170 | title "EventStream::combineAsArray(stream1, stream2, stream3, stream4)" 171 | createNObservable 500, -> 172 | Bacon.combineAsArray(eventStream(), eventStream(), eventStream(), eventStream()) 173 | 174 | title "EventStream::mergeAll(stream1, stream2, stream3, stream4)" 175 | createNObservable 500, -> 176 | Bacon.mergeAll(eventStream(), eventStream(), eventStream(), eventStream()) 177 | 178 | title "Diamond-shaped Property graph" 179 | createNObservable 100, -> 180 | diamond(eventStream(), 3, 5) 181 | 182 | title "Bacon.combineTemplate" 183 | createNObservable 100, -> 184 | combineTemplate(eventStream, 4, 4) 185 | -------------------------------------------------------------------------------- /spec/SpecHelper.coffee: -------------------------------------------------------------------------------- 1 | expect = require("chai").expect 2 | Bacon = (require "../src/Bacon").Bacon 3 | _ = Bacon._ 4 | 5 | t = @t = (time) -> time 6 | seqs = [] 7 | waitMs = 100 8 | 9 | browser = (typeof window == "object") 10 | if browser 11 | console.log("Running in browser, narrowing test set") 12 | 13 | grep = process.env.grep 14 | if grep 15 | console.log("running with grep:", grep) 16 | origDescribe = describe 17 | match = false 18 | global.describe = (desc, f) -> 19 | if desc.indexOf(grep) >= 0 20 | match = true 21 | origDescribe(desc, f) 22 | match = false 23 | else if match 24 | origDescribe(desc, f) 25 | 26 | @error = (msg) -> new Bacon.Error(msg) 27 | @soon = (f) -> setTimeout f, t(1) 28 | @series = (interval, values) -> 29 | Bacon.sequentially(t(interval), values) 30 | @repeat = (interval, values) -> 31 | source = Bacon.repeatedly(t(interval), values) 32 | seqs.push({ values : values, source : source }) 33 | source 34 | @atGivenTimes = (timesAndValues) -> 35 | streams = for tv in timesAndValues 36 | Bacon.later(t(tv[0]), tv[1]) 37 | Bacon.mergeAll(streams) 38 | 39 | @expectStreamTimings = (src, expectedEventsAndTimings, options) -> 40 | srcWithRelativeTime = () -> 41 | now = Bacon.scheduler.now 42 | t0 = now() 43 | relativeTime = () -> 44 | Math.floor(now() - t0) 45 | withRelativeTime = (x) -> [relativeTime(), x] 46 | src().flatMap(withRelativeTime) 47 | @expectStreamEvents(srcWithRelativeTime, expectedEventsAndTimings, options) 48 | 49 | @expectStreamEvents = (src, expectedEvents, {unstable} = {}) -> 50 | verifySingleSubscriber src, expectedEvents 51 | verifyLateEval src, expectedEvents 52 | if not unstable 53 | verifySwitching src, expectedEvents unless browser 54 | verifySwitchingWithUnsub src, expectedEvents unless browser 55 | verifySwitchingAggressively src, expectedEvents 56 | 57 | @expectPropertyEvents = (src, expectedEvents, {unstable, extraCheck} = {}) -> 58 | expect(expectedEvents.length > 0).to.deep.equal(true) 59 | verifyPSingleSubscriber src, expectedEvents, extraCheck 60 | verifyPLateEval src, expectedEvents 61 | if not unstable 62 | verifyPSwitching src, justValues(expectedEvents) 63 | 64 | verifyPSingleSubscriber = (srcF, expectedEvents, extraCheck) -> 65 | verifyPropertyWith "(single subscriber)", srcF, expectedEvents, ((src, events, done) -> 66 | src.subscribe (event) -> 67 | if event.isEnd() 68 | done() 69 | else 70 | events.push(toValue(event))), extraCheck 71 | 72 | verifyPLateEval = (srcF, expectedEvents) -> 73 | verifyPropertyWith "(single subscriber)", srcF, expectedEvents, (src, events, done) -> 74 | src.subscribe (event) -> 75 | if event.isEnd() 76 | done() 77 | else 78 | events.push(event) 79 | 80 | verifyPSwitching = (srcF, expectedEvents) -> 81 | verifyPropertyWith "(switching subscribers)", srcF, expectedEvents, (src, events, done) -> 82 | src.subscribe (event) -> 83 | if event.isEnd() 84 | done() 85 | else 86 | if event.hasValue() 87 | src.subscribe (event) -> 88 | if event.isInitial() 89 | events.push(event.value()) 90 | Bacon.noMore 91 | 92 | verifyPropertyWith = (description, srcF, expectedEvents, collectF, extraCheck) -> 93 | describe description, -> 94 | src = null 95 | events = [] 96 | before -> 97 | src = srcF() 98 | before (done) -> 99 | collectF(src, events, done) 100 | it "is a Property", -> 101 | expect(src instanceof Bacon.Property).to.deep.equal(true) 102 | it "outputs expected events in order", -> 103 | expect(toValues(events)).to.deep.equal(toValues(expectedEvents)) 104 | it "has correct final state", -> 105 | verifyFinalState(src, lastNonError(expectedEvents)) 106 | it "cleans up observers", verifyCleanup 107 | if (extraCheck) 108 | extraCheck() 109 | 110 | verifyLateEval = (srcF, expectedEvents) -> 111 | verifyStreamWith "(late eval)", srcF, expectedEvents, (src, events, done) -> 112 | src.subscribe (event) -> 113 | if event.isEnd() 114 | done() 115 | else 116 | expect(event instanceof Bacon.Initial).to.deep.equal(false) 117 | events.push(event) 118 | 119 | 120 | verifySingleSubscriber = (srcF, expectedEvents) -> 121 | verifyStreamWith "(single subscriber)", srcF, expectedEvents, (src, events, done) -> 122 | src.subscribe (event) -> 123 | if event.isEnd() 124 | done() 125 | else 126 | expect(event instanceof Bacon.Initial).to.deep.equal(false) 127 | events.push(toValue(event)) 128 | 129 | # get each event with new subscriber 130 | verifySwitching = (srcF, expectedEvents, done) -> 131 | verifyStreamWith "(switching subscribers)", srcF, expectedEvents, (src, events, done) -> 132 | newSink = -> 133 | (event) -> 134 | if event.isEnd() 135 | done() 136 | else 137 | expect(event instanceof Bacon.Initial).to.deep.equal(false) 138 | events.push(toValue(event)) 139 | src.subscribe(newSink()) 140 | Bacon.noMore 141 | src.subscribe(newSink()) 142 | 143 | # get each event with new subscriber. Unsub using the unsub function 144 | # instead of Bacon.noMore 145 | verifySwitchingWithUnsub = (srcF, expectedEvents, done) -> 146 | verifyStreamWith "(switching subscribers with unsub)", srcF, expectedEvents, (src, events, done) -> 147 | globalEnded = false 148 | subNext = -> 149 | unsub = null 150 | newSink = -> 151 | ended = false 152 | noMoreExpected = false 153 | usedUnsub = false 154 | (event) -> 155 | if noMoreExpected 156 | console.log "got unexp", event.toString(), "usedUnsub", usedUnsub 157 | if event.isEnd() 158 | if ended 159 | console.log("one stream, two ends") 160 | else if globalEnded 161 | console.log("two ends") 162 | globalEnded = true 163 | ended = true 164 | done() 165 | else 166 | expect(event instanceof Bacon.Initial).to.deep.equal(false) 167 | events.push(toValue(event)) 168 | prevUnsub = unsub 169 | noMoreExpected = true 170 | subNext() 171 | if unsub? 172 | usedUnsub = true 173 | unsub() 174 | else 175 | Bacon.noMore 176 | unsub = src.subscribe(newSink()) 177 | subNext() 178 | 179 | verifyStreamWith = (description, srcF, expectedEvents, collectF) -> 180 | describe description, -> 181 | src = null 182 | events = [] 183 | before -> 184 | src = srcF() 185 | expect(src instanceof Bacon.EventStream).to.equal(true) 186 | before (done) -> 187 | collectF(src, events, done) 188 | it "outputs expected value in order", -> 189 | expect(toValues(events)).to.deep.equal(toValues(expectedEvents)) 190 | it "the stream is exhausted", -> 191 | verifyExhausted src 192 | it "cleans up observers", verifyCleanup 193 | 194 | verifySwitchingAggressively = (srcF, expectedEvents, done) -> 195 | describe "(switching aggressively)", -> 196 | src = null 197 | events = [] 198 | before -> 199 | src = srcF() 200 | expect(src instanceof Bacon.EventStream).to.equal(true) 201 | before (done) -> 202 | newSink = -> 203 | unsub = null 204 | (event) -> 205 | if event.isEnd() 206 | done() 207 | else 208 | expect(event instanceof Bacon.Initial).to.deep.equal(false) 209 | events.push(toValue(event)) 210 | unsub() if unsub? 211 | unsub = src.subscribe(newSink()) 212 | Bacon.noMore 213 | unsub = src.subscribe(newSink()) 214 | it "outputs expected value in order", -> 215 | expect(events).to.deep.equal(toValues(expectedEvents)) 216 | it "the stream is exhausted", -> 217 | verifyExhausted src 218 | it "cleans up observers", verifyCleanup 219 | 220 | verifyExhausted = (src) -> 221 | events = [] 222 | src.subscribe (event) -> 223 | events.push(event) 224 | expect(events[0].isEnd()).to.deep.equal(true) 225 | 226 | lastNonError = (events) -> 227 | _.last(_.filter(((e) -> toValue(e) != ""), events)) 228 | 229 | verifyFinalState = (property, value) -> 230 | events = [] 231 | property.subscribe (event) -> 232 | events.push(event) 233 | expect(toValues(events)).to.deep.equal(toValues([value, ""])) 234 | 235 | verifyCleanup = @verifyCleanup = -> 236 | for seq in seqs 237 | expect(seq.source.hasSubscribers()).to.deep.equal(false) 238 | seqs = [] 239 | 240 | toValues = (xs) -> 241 | values = [] 242 | for x in xs 243 | values.push(toValue(x)) 244 | values 245 | toValue = (x) -> 246 | switch true 247 | when !x?.isEvent?() then x 248 | when x.isError() then "" 249 | when x.isEnd() then "" 250 | else x.value() 251 | 252 | justValues = (xs) -> 253 | _.filter hasValue, xs 254 | hasValue = (x) -> 255 | toValue(x) != "" 256 | 257 | @toValues = toValues 258 | 259 | Bacon.Observable.prototype.onUnsub = (f) -> 260 | self = this; 261 | ended = false 262 | return new Bacon.EventStream (sink) -> 263 | unsub = self.subscribe sink 264 | -> 265 | f() 266 | unsub() 267 | -------------------------------------------------------------------------------- /RELEASE-NOTES.md: -------------------------------------------------------------------------------- 1 | ## 0.7.20 2 | 3 | - Fix #399 (double evaluation of lazies with Bus) 4 | - Fix #407 (event duplication in a specific scenario) 5 | 6 | ## 0.7.19 7 | 8 | - Performance and memory footprint improvements 9 | 10 | ## 0.7.18 11 | 12 | - Fix #397 (include minified version in npm) 13 | 14 | ## 0.7.17 15 | 16 | - Fix #394 (never call unbinder twice in fromBinder) 17 | 18 | ## 0.7.16 19 | 20 | - Fix #353 (flatMapLatest glitch) 21 | 22 | ## 0.7.15 23 | 24 | - Fix #363 (glitchfree-algorithm bug) 25 | 26 | ## 0.7.14 27 | 28 | - Fix Bacon.retry (#380) 29 | 30 | ## 0.7.13 31 | 32 | - Add flatMapWithConcurrencyLimit, flatMapConcat, bufferingThrottle, holdWhen (#324) 33 | 34 | ## 0.7.12 35 | 36 | - Add Bacon.retry, flatMapError, Bacon.error by @mileskin (#310) 37 | - Improve mergeAll performance by removing recursion (#373) 38 | 39 | ## 0.7.11 40 | 41 | - Optimizations by @lautis (#370) 42 | 43 | ## 0.7.9 44 | 45 | - Fix #348: toString problems with jQuery 46 | - Make .log() log the actual values instead of strings, again 47 | 48 | ## 0.7.8 49 | 50 | - Fix #347: bug related to evaluating (lazy) event values later 51 | 52 | ## 0.7.7 53 | 54 | - Fix #345: stack overflow with circular setups with Bus 55 | 56 | ## 0.7.6 57 | 58 | - Fix #338: event duplication bug in a specific scenario 59 | 60 | ## 0.7.5 61 | 62 | - Call external subscriber callbacks after event transaction is complete 63 | - Remove Bacon.afterTransaction 64 | 65 | ## 0.7.4 66 | 67 | - Add Bacon.afterTransaction for coordinating timings, in e.g. Bacon.Model 68 | 69 | ## 0.7.3 70 | 71 | - Fix #331: combineTemplate when Date or RegEx objects are involved 72 | 73 | ## 0.7.2 74 | 75 | - Fix #315: _.toString for objects that contain enumerable but innaccessible properties (like the HTML hidden field) 76 | - Fix #320, #321: errors thrown when Array.prototype contains extra junk 77 | 78 | ## 0.7.1 79 | 80 | - Support function construction rules in flatMap(Latest|First) (#306) 81 | 82 | ## 0.7.0 83 | 84 | - Glitch-free updates (#272) 85 | - toString/inspect implementation (#265) 86 | - Published Bacon._ API including helpers like Bacon._.indexOf 87 | 88 | ## 0.6.22 89 | 90 | - Include Bacon.version="0.6.22" in dist/Bacon.js (generated in release 91 | script) 92 | - Stabilize fromArray in case of subscribe unsubscribing in the middle of the array 93 | 94 | ## 0.6.21 95 | 96 | - Fix bug in concat with synchronous left stream (#262) 97 | 98 | ## 0.6.20 99 | 100 | - Fix Property.zip (#260) 101 | 102 | ## 0.6.19 103 | 104 | - Fix AMD support that was partially broken in 0.6.14 105 | 106 | ## 0.6.18 107 | 108 | - Allow (object, methodName, args...) syntax in from(Node)Callback 109 | 110 | ## 0.6.17 111 | 112 | - Allow predicate function in Observable.endOnError 113 | 114 | ## 0.6.16 115 | 116 | - Add EventStream.sampledBy 117 | 118 | ## 0.6.15 119 | 120 | - Add Property.startWith 121 | 122 | ## 0.6.14 123 | 124 | - Simplifications and cleanup 125 | 126 | ## 0.6.13 127 | 128 | - Fix #240: call accumulator function once per event in `scan` 129 | 130 | ## 0.6.12 131 | 132 | - Fix mergeAll, zipAsArray, zipWith with 0 sources, n-ary syntax 133 | 134 | ## 0.6.11 135 | 136 | - Fix Bacon.zipAsArray with 0 or 1 sources 137 | - Fix Bacon.when with 0 sources and 0-length patterns 138 | 139 | ## 0.6.9 140 | 141 | - Fix Bacon.when with Properties (#232) 142 | - Fix laziness of evaluation. Added tests to prevent further regression. 143 | - Re-implemented `combineAsArray` and `sampledBy` using Bacon.when 144 | 145 | ## 0.6.8 146 | 147 | - Fix skipWhile in case of Error events before first match (refix #218) 148 | 149 | ## 0.6.7 150 | 151 | - Fix #218: EventStream.skipWhile with synchronous sources 152 | 153 | ## 0.6.6 154 | 155 | - Fix #217: EventStream.scan for synchronous streams 156 | - Simplify skipUntil implementation 157 | 158 | ## 0.6.5 159 | 160 | - Fix `takeUntil` to work correctly with never-ending stopper (#214) 161 | 162 | ## 0.6.4 163 | 164 | - Generalize `awaiting` to Properties too 165 | 166 | ## 0.6.3 167 | 168 | - Fix Property.skipDuplicates: don't ever skip an Initial event (#211) 169 | 170 | ## 0.6.2 171 | 172 | - Fix sampledBy with stream of functions (#208) 173 | 174 | ## 0.6.1 175 | 176 | - Fix #206: takeUntil should work with Property as stopper 177 | 178 | ## 0.6.0 179 | 180 | - Add EventStream.skipWhile, support "Property Bool" instead of predicate function in takeWhile (#204) 181 | skipWhile 182 | - Fix Property.take(n) etc in case of a never-ending Property (#205) 183 | - Fix EventStream.skipUntil in case of self-derived stopper 184 | - Re-implement EventStream.takeUntil 185 | - Switch test framework from Jasmine to Mocha 186 | 187 | ## 0.5.1 188 | 189 | - Fix bug in Bacon.update (#199) 190 | 191 | ## 0.5.0 192 | 193 | - Added Join Patterns, i.e. Bacon.when and Bacon.update (#167) 194 | - Corrected the name to "bacon" instead of "baconjs" in bower.json, 195 | component.json (#197) 196 | 197 | ## 0.4.10 198 | 199 | - Fix #195: Bacon.mergeAll for empty list should return Bacon.never() 200 | 201 | ## 0.4.9 202 | 203 | - (#192) Throw runtime error if trying to merge Properties 204 | 205 | ## 0.4.8 206 | 207 | - Add EventStream.skipUntil (#194) 208 | 209 | ## 0.4.7 210 | 211 | - Fix #191: End properly even if an exception is thrown (sequentially, take) 212 | 213 | ## 0.4.6 214 | 215 | - Support n-ary syntax in Bacon.mergeAll and Bacon.zipWith 216 | 217 | ## 0.4.5 218 | 219 | - Add bower.json (component.json deprecated by bower) 220 | 221 | ## 0.4.3 222 | 223 | - Generalize `decode` to all Observables (#189) 224 | 225 | ## 0.4.2 226 | 227 | - Add bufferWithTimeOrCount (#181) 228 | 229 | ## 0.4.1 230 | 231 | - Add flatMapFirst and debounceImmediate (#132) 232 | - Add reduce/fold (#158) 233 | 234 | ## 0.4.0 235 | 236 | - Fix #85 Make Property updates atomic (with known limitations) 237 | 238 | ## 0.3.15 239 | 240 | - Add support for "min" parameter in slidingWindow. 241 | 242 | ## 0.3.14 243 | 244 | - Fix #163: add support for nulls etc in combineTemplate 245 | 246 | ## 0.3.13 247 | 248 | - Fix #162: Call Promise.abort() only if the "abort" flag is used 249 | 250 | ## 0.3.12 251 | 252 | - Support Bacon.Error in Bacon.once, Bacon.fromArray 253 | 254 | ## 0.3.11 255 | 256 | - Fix #160: Property.scan/debounce/throttle/delay bug with sources like 257 | Bacon.once() and Bacon.constant() 258 | 259 | ## 0.3.10 260 | 261 | - Add AMD support (#154) 262 | 263 | ## 0.3.9 264 | 265 | - Fix #152: Observable.skip now accepts zero and negative values 266 | 267 | ## 0.3.8 268 | 269 | - Fix #153: Remove dep on "global" to fix in browsers. 270 | 271 | ## 0.3.7 272 | 273 | - Fix #151: take(N) should work with N = 0 274 | 275 | ## 0.3.6 276 | 277 | - generalize combine method to all Observables 278 | - Fix #147: Prevent sending duplicate Errors in case the same Error is routed thru multiple paths. 279 | - Internal change: PropertyDispatcher now used in Property constructor 280 | as a default. 281 | 282 | ## 0.3.5 283 | 284 | - Support constant values to be returned from function f in flatMap 285 | - Fix #148: Handle same subscriber added twice just like two separate subscribers 286 | 287 | ## 0.3.4 288 | 289 | - Add Bacon.onValues shorthand function 290 | 291 | ## 0.3.3 292 | 293 | - Fix #146: Avoid catch-rethrow to preserve original stack trace 294 | 295 | ## 0.3.2 296 | 297 | - #133: Support EventStreams and Properties as arguments of fromCallback, 298 | fromNodeCallback 299 | 300 | ## 0.3.1 301 | 302 | - Fix #142: map(".foo.bar") failed if "foo" was null 303 | 304 | ## 0.3.0 305 | 306 | - #124: Change combineWith behavior to combine n Observables using n-ary function 307 | - Remove combineAll which had a confusing name and no known uses 308 | - Support constants instead of Observables as arguments of 309 | combineAsArray, combineWith 310 | 311 | ## 0.2.8 312 | 313 | - Add EventStream.zip, Bacon.zipWith, Bacon.zipAsArray 314 | 315 | ## 0.2.7 316 | 317 | - Removed exception-catching in fromBinder 318 | 319 | ## 0.2.6 320 | 321 | - Fix #129: error handling bug in fromBinder 322 | 323 | ## 0.2.5 324 | 325 | - log() does not crash if console or console.log is undefined 326 | 327 | ## 0.2.4 328 | 329 | - Support Node.js style callbacks using Bacon.fromNodeCallback 330 | 331 | ## 0.2.3 332 | 333 | - Fix #120: take(n) may be triggered more than n times if event emitter is triggered in onValue callback 334 | - Generally more reliable implementation in case of recursively 335 | generated events 336 | 337 | ## 0.2.2 338 | 339 | - Fix #118: Bus drops all inputs after last consumer of its events is dropped; impossible to re-subscribe 340 | 341 | ## 0.2.1 342 | 343 | - fromPromise calls .abort() on unsubscribe 344 | 345 | ## 0.2.0 346 | 347 | - Rename throttle to debounce 348 | - Rename throttle2 to throttle 349 | 350 | ## 0.1.10 351 | 352 | - Support usage of an EventStream/Property instead of a function as a parameter to flatMap/Latest 353 | - Support optional arguments to Observable.log 354 | 355 | ## 0.1.9 356 | 357 | - Performance improvements 358 | - #106: Fix stream ending in case an exception is thrown 359 | - #105: Rewrite binder stream factory to compose all others 360 | 361 | ## 0.1.8 362 | 363 | - add Property.filter(property) 364 | - reject arguments in Property.toProperty 365 | - add Property.sampledBy(property) 366 | - generalize mapEnd to all Observables 367 | - fix bufferWithTime behaviour: output at steady rate as long as there's anything to output 368 | - add throttle2 369 | 370 | ## 0.1.7 371 | 372 | - Fixed tests too on IE6+ 373 | 374 | ## 0.1.6 375 | 376 | - Fix property extraction (".field") syntax compatibility with IE6-7 377 | 378 | ## 0.1.5 379 | 380 | - Bus.plug returns function for unplugging 381 | - Fixes possible memory leak in Bus, related to ended streams 382 | 383 | ## 0.1.4 384 | 385 | - Add `EventStream.awaiting` (ported from Bacon.UI) 386 | - [#80: Fix handling of `undefined` in `skipDuplicates`](https://github.com/raimohanska/bacon.js/pull/80) 387 | - Documented Bacon.fromCallback 388 | 389 | ## 0.1.2 390 | 391 | - Fix bug in case stream values are functions 392 | 393 | ## 0.1.0 394 | 395 | - API change: event.value is now a function. Allows internal 396 | optimization. 397 | - API changes: drop methods switch (use flatMapLatest), do (use doAction), distinctUntilChanged (use skipDuplicates), decorateWith, latestValue 398 | 399 | ## 0.0.12 400 | 401 | - No external changes. Built with grunt. 402 | 403 | ## 0.0.11 404 | 405 | - Fix IE8 compatibility issue (Array.prototype.indexOf) 406 | 407 | ## 0.0.10 408 | 409 | - Fix flatMap/flatMapLatest in case the function returns a Property 410 | - Allow .fromEventTarget to define a custom mapping function 411 | - Add Observable.diff 412 | 413 | ## 0.0.9 414 | 415 | - Apply correct context in method calls using the dot-methodname form 416 | 417 | ## 0.0.8 418 | 419 | - Support arrays in combineTemplate 420 | - Add Observable.slidingWindow 421 | - Add Property.scan 422 | - Add optional isEqual argument to skipDuplicates 423 | 424 | ## 0.0.7 425 | 426 | - Make log() chainable by returning this instead of undefined 427 | - Add Property.throttle, Property.delay 428 | 429 | ## 0.0.6 430 | 431 | - rename switch->flatMapLatest, do->doAction 432 | -------------------------------------------------------------------------------- /dist/Bacon.min.js: -------------------------------------------------------------------------------- 1 | (function(){var a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,$,_,ab,bb,cb,db,eb,fb,gb,hb=[].slice,ib={}.hasOwnProperty,jb=function(a,b){function c(){this.constructor=a}for(var d in b)ib.call(b,d)&&(a[d]=b[d]);return c.prototype=b.prototype,a.prototype=new c,a.__super__=b.prototype,a},kb=function(a,b){return function(){return a.apply(b,arguments)}};a={toString:function(){return"Bacon"}},a.version="0.7.20",a.fromBinder=function(b,c){return null==c&&(c=fb.id),new l(A(a,"fromBinder",b,c),function(d){var e,f,g;return g=!1,e=function(){return"undefined"!=typeof f&&null!==f?(g||f(),g=!0):void 0},f=b(function(){var b,g,h,i,j,l;for(b=1<=arguments.length?hb.call(arguments,0):[],i=c.apply(null,b),I(i)&&fb.last(i)instanceof k||(i=[i]),h=a.more,j=0,l=i.length;l>j;j++)if(g=i[j],h=d(g=$(g)),h===a.noMore||g.isEnd())return null!=f?e():a.scheduler.setTimeout(e,0),h;return h}),e})},a.$={asEventStream:function(b,c,d){var e;return K(c)&&(e=[c,null],d=e[0],c=e[1]),db(this.selector||this,"asEventStream",b,a.fromBinder(function(a){return function(d){return a.on(b,c,d),function(){return a.off(b,c,d)}}}(this),d))}},null!=(gb="undefined"!=typeof jQuery&&null!==jQuery?jQuery:"undefined"!=typeof Zepto&&null!==Zepto?Zepto:null)&&(gb.fn.asEventStream=a.$.asEventStream),a.fromEventTarget=function(b,c,d){var e,f,g,h,i,j;return e=null!=(g=b.addEventListener)?g:null!=(h=b.addListener)?h:b.bind,f=null!=(i=b.removeEventListener)?i:null!=(j=b.removeListener)?j:b.unbind,db(a,"fromEventTarget",b,c,a.fromBinder(function(a){return e.call(b,c,a),function(){return f.call(b,c,a)}},d))},a.fromPromise=function(b,c){return db(a,"fromPromise",b,a.fromBinder(function(a){return b.then(a,function(b){return a(new j(b))}),function(){return c?"function"==typeof b.abort?b.abort():void 0:void 0}},function(a){return[a,B()]}))},a.noMore=[""],a.more=[""],a.later=function(b,c){return db(a,"later",b,c,a.sequentially(b,[c]))},a.sequentially=function(b,c){var d;return d=0,db(a,"sequentially",b,c,a.fromPoll(b,function(){var a;return a=c[d++],dc;c++)b=Y[c],e.push(b(a));return e}finally{delete X.running}},eb=function(a){return function(){var b,c,d,e;return d=arguments[0],b=2<=arguments.length?hb.call(arguments,1):[],"object"==typeof d&&b.length&&(c=d,e=b[0],d=function(){return c[e].apply(c,arguments)},b=b.slice(1)),a.apply(null,[d].concat(hb.call(b)))}},N=function(b,c){return eb(function(){var d,e,f;return e=arguments[0],d=2<=arguments.length?hb.call(arguments,1):[],f=V(c,[function(a,b){return e.apply(null,hb.call(a).concat([b]))}]),db.apply(null,[a,b,e].concat(hb.call(d),[a.combineAsArray(d).flatMap(f)]))})},a.fromCallback=N("fromCallback",function(){var b,c;return c=arguments[0],b=2<=arguments.length?hb.call(arguments,1):[],a.fromBinder(function(a){return O(c,b)(a),U},function(a){return[a,B()]})}),a.fromNodeCallback=N("fromNodeCallback",function(){var b,c;return c=arguments[0],b=2<=arguments.length?hb.call(arguments,1):[],a.fromBinder(function(a){return O(c,b)(a),U},function(a,b){return a?[new j(a),B()]:[b,B()]})}),a.fromPoll=function(b,c){return db(a,"fromPoll",b,c,a.fromBinder(function(c){var d;return d=a.scheduler.setInterval(c,b),function(){return a.scheduler.clearInterval(d)}},c))},a.interval=function(b,c){return null==c&&(c={}),db(a,"interval",b,c,a.fromPoll(b,function(){return T(c)}))},a.constant=function(b){return new q(A(a,"constant",b),function(a){return a(H(b)),a(B()),U})},a.never=function(){return db(a,"never",a.fromArray([]))},a.once=function(b){return db(a,"once",b,a.fromArray([b]))},a.fromArray=function(b){return b=w(b),new l(A(a,"fromArray",b),function(c){var d,e,f;for(e=!1,d=a.more;d!==a.noMore&&!e;)fb.empty(b)?(c(B()),d=a.noMore):(f=b.shift(),d=c($(f)));return function(){return e=!0}})},a.mergeAll=function(){var b;return b=1<=arguments.length?hb.call(arguments,0):[],I(b[0])&&(b=b[0]),b.length?new l(A.apply(null,[a,"mergeAll"].concat(hb.call(b))),function(c){var d,e,f;return d=0,f=function(e){return function(f){return e.subscribeInternal(function(e){var g;return e.isEnd()?(d++,d===b.length?c(B()):a.more):(g=c(e),g===a.noMore&&f(),g)})}},e=fb.map(f,b),x.apply(null,e)}):a.never()},a.zipAsArray=function(){var b;return b=1<=arguments.length?hb.call(arguments,0):[],I(b[0])&&(b=b[0]),db.apply(null,[a,"zipAsArray"].concat(hb.call(b),[a.zipWith(b,function(){var a;return a=1<=arguments.length?hb.call(arguments,0):[]})]))},a.zipWith=function(){var b,c,d;return b=arguments[0],c=2<=arguments.length?hb.call(arguments,1):[],K(b)||(d=[b,c[0]],c=d[0],b=d[1]),c=fb.map(function(a){return a.toEventStream()},c),db.apply(null,[a,"zipWith",b].concat(hb.call(c),[a.when(c,b)]))},a.groupSimultaneous=function(){var c,d,e;return e=1<=arguments.length?hb.call(arguments,0):[],1===e.length&&I(e[0])&&(e=e[0]),d=function(){var a,d,f;for(f=[],a=0,d=e.length;d>a;a++)c=e[a],f.push(new b(c));return f}(),db.apply(null,[a,"groupSimultaneous"].concat(hb.call(e),[a.when(d,function(){var a;return a=1<=arguments.length?hb.call(arguments,0):[]})]))},a.combineAsArray=function(){var b,c,d,e,f,g,h;for(f=1<=arguments.length?hb.call(arguments,0):[],1===f.length&&I(f[0])&&(f=f[0]),b=g=0,h=f.length;h>g;b=++g)e=f[b],L(e)||(f[b]=a.constant(e));return f.length?(d=function(){var a,b,d;for(d=[],a=0,b=f.length;b>a;a++)c=f[a],d.push(new t(c,!0,c.subscribeInternal));return d}(),db.apply(null,[a,"combineAsArray"].concat(hb.call(f),[a.when(d,function(){var a;return a=1<=arguments.length?hb.call(arguments,0):[]}).toProperty()]))):a.constant([])},a.onValues=function(){var b,c,d;return c=2<=arguments.length?hb.call(arguments,0,d=arguments.length-1):(d=0,[]),b=arguments[d++],a.combineAsArray(c).onValues(b)},a.combineWith=function(){var b,c;return b=arguments[0],c=2<=arguments.length?hb.call(arguments,1):[],db.apply(null,[a,"combineWith",b].concat(hb.call(c),[a.combineAsArray(c).map(function(a){return b.apply(null,a)})]))},a.combineTemplate=function(b){var c,d,e,f,g,h,i,j,k,l;return i=[],l=[],h=function(a){return a[a.length-1]},k=function(a,b,c){return h(a)[b]=c},c=function(a,b){return function(c,d){return k(c,a,d[b])}},g=function(a,b){return function(c){return k(c,a,b)}},j=function(a){return I(a)?[]:{}},e=function(a,b){var d,e;return L(b)?(l.push(b),i.push(c(a,l.length-1))):b!==Object(b)||"function"==typeof b||b instanceof RegExp||b instanceof Date?i.push(g(a,b)):(e=function(a){return function(c){var d;return d=j(b),k(c,a,d),c.push(d)}},d=function(a){return a.pop()},i.push(e(a)),f(b),i.push(d))},f=function(a){return fb.each(a,e)},f(b),d=function(a){var c,d,e,f,g;for(e=j(b),c=[e],f=0,g=i.length;g>f;f++)(d=i[f])(c,a);return e},db(a,"combineTemplate",b,a.combineAsArray(l).map(d))},a.retry=function(b){var c,d,e,f,g,h;if(!K(b.source))throw"'source' option has to be a function";return h=b.source,f=b.retries||0,e=b.maxRetries||f,c=b.delay||function(){return 0},d=b.isRetryable||function(){return!0},g=function(b){var g,i;return i={source:h,retries:f-1,maxRetries:e,delay:c,isRetryable:d},g=function(){return a.retry(i)},a.later(c(b)).filter(!1).concat(a.once().flatMap(g))},db(a,"retry",b,h().flatMapError(function(b){return d(b)&&f>0?g({error:b,retriesDone:e-f}):a.once(new a.Error(b))}))},C=0,k=function(){function a(){this.id=++C}return a.prototype.isEvent=function(){return!0},a.prototype.isEnd=function(){return!1},a.prototype.isInitial=function(){return!1},a.prototype.isNext=function(){return!1},a.prototype.isError=function(){return!1},a.prototype.hasValue=function(){return!1},a.prototype.filter=function(){return!0},a.prototype.inspect=function(){return this.toString()},a.prototype.log=function(){return this.toString()},a}(),n=function(a){function b(a){b.__super__.constructor.call(this),this.value=K(a)?fb.cached(a):fb.always(a)}return jb(b,a),b.prototype.isNext=function(){return!0},b.prototype.hasValue=function(){return!0},b.prototype.fmap=function(a){var b;return b=this.value,this.apply(function(){return a(b())})},b.prototype.apply=function(a){return new b(a)},b.prototype.filter=function(a){return a(this.value())},b.prototype.toString=function(){return fb.toString(this.value())},b.prototype.log=function(){return this.value()},b}(k),m=function(a){function b(){return b.__super__.constructor.apply(this,arguments)}return jb(b,a),b.prototype.isInitial=function(){return!0},b.prototype.isNext=function(){return!1},b.prototype.apply=function(a){return new b(a)},b.prototype.toNext=function(){return new n(this.value)},b}(n),i=function(a){function b(){return b.__super__.constructor.apply(this,arguments)}return jb(b,a),b.prototype.isEnd=function(){return!0},b.prototype.fmap=function(){return this},b.prototype.apply=function(){return this},b.prototype.toString=function(){return""},b}(k),j=function(a){function b(a){this.error=a}return jb(b,a),b.prototype.isError=function(){return!0},b.prototype.fmap=function(){return this},b.prototype.apply=function(){return this},b.prototype.toString=function(){return" "+fb.toString(this.error)},b}(k),G=0,p=function(){function b(a){this.flatMapError=kb(this.flatMapError,this),this.id=++G,db(a,this),this.initialDesc=this.desc}return b.prototype.onValue=function(){var a;return a=P(arguments),this.subscribe(function(b){return b.hasValue()?a(b.value()):void 0})},b.prototype.onValues=function(a){return this.onValue(function(b){return a.apply(null,b)})},b.prototype.onError=function(){var a;return a=P(arguments),this.subscribe(function(b){return b.isError()?a(b.error):void 0})},b.prototype.onEnd=function(){var a;return a=P(arguments),this.subscribe(function(b){return b.isEnd()?a():void 0})},b.prototype.errors=function(){return db(this,"errors",this.filter(function(){return!1}))},b.prototype.filter=function(){var b,c;return c=arguments[0],b=2<=arguments.length?hb.call(arguments,1):[],z(this,c,b,function(b){return db(this,"filter",b,this.withHandler(function(c){return c.filter(b)?this.push(c):a.more}))})},b.prototype.takeWhile=function(){var b,c;return c=arguments[0],b=2<=arguments.length?hb.call(arguments,1):[],z(this,c,b,function(b){return db(this,"takeWhile",b,this.withHandler(function(c){return c.filter(b)?this.push(c):(this.push(B()),a.noMore)}))})},b.prototype.endOnError=function(){var a,b;return b=arguments[0],a=2<=arguments.length?hb.call(arguments,1):[],null==b&&(b=!0),z(this,b,a,function(a){return db(this,"endOnError",this.withHandler(function(b){return b.isError()&&a(b.error)?(this.push(b),this.push(B())):this.push(b)}))})},b.prototype.take=function(b){return 0>=b?a.never():db(this,"take",b,this.withHandler(function(c){return c.hasValue()?(b--,b>0?this.push(c):(0===b&&this.push(c),this.push(B()),a.noMore)):this.push(c)}))},b.prototype.map=function(){var a,b;return b=arguments[0],a=2<=arguments.length?hb.call(arguments,1):[],b instanceof q?b.sampledBy(this,F):z(this,b,a,function(a){return db(this,"map",a,this.withHandler(function(b){return this.push(b.fmap(a))}))})},b.prototype.mapError=function(){var a;return a=P(arguments),db(this,"mapError",a,this.withHandler(function(b){return this.push(b.isError()?T(a(b.error)):b)}))},b.prototype.mapEnd=function(){var b;return b=P(arguments),db(this,"mapEnd",b,this.withHandler(function(c){return c.isEnd()?(this.push(T(b(c))),this.push(B()),a.noMore):this.push(c)}))},b.prototype.doAction=function(){var a;return a=P(arguments),db(this,"doAction",a,this.withHandler(function(b){return b.hasValue()&&a(b.value()),this.push(b)}))},b.prototype.skip=function(b){return db(this,"skip",b,this.withHandler(function(c){return c.hasValue()?b>0?(b--,a.more):this.push(c):this.push(c)}))},b.prototype.skipDuplicates=function(a){return null==a&&(a=function(a,b){return a===b}),db(this,"skipDuplicates",this.withStateMachine(o,function(b,c){return c.hasValue()?c.isInitial()||b===o||!a(b.get(),c.value())?[new s(c.value()),[c]]:[b,[]]:[b,[c]]}))},b.prototype.skipErrors=function(){return db(this,"skipErrors",this.withHandler(function(b){return b.isError()?a.more:this.push(b)}))},b.prototype.withStateMachine=function(b,c){var d;return d=b,db(this,"withStateMachine",b,c,this.withHandler(function(b){var e,f,g,h,i,j,k;for(e=c(d,b),f=e[0],h=e[1],d=f,i=a.more,j=0,k=h.length;k>j;j++)if(g=h[j],i=this.push(g),i===a.noMore)return i;return i}))},b.prototype.scan=function(b,c,d){var e,f,g,h;return null==d&&(d={}),f=Z(c),c=d.lazyF?f:function(a,b){return f(a(),b())},e=bb(b).map(function(a){return fb.always(a)}),h=function(b){return function(f){var h,i,j,k;return h=!1,k=U,i=a.more,j=function(){return h?void 0:e.forEach(function(b){return h=!0,i=f(new m(b)),i===a.noMore?(k(),k=U):void 0})},k=b.subscribeInternal(function(b){var g,k;return b.hasValue()?h&&b.isInitial()?a.more:(b.isInitial()||j(),h=!0,k=e.getOrElse(function(){return void 0}),g=fb.cached(function(){return c(k,b.value)}),e=new s(g),d.eager&&g(),f(b.apply(g))):(b.isEnd()&&(i=j()),i!==a.noMore?f(b):void 0)}),u.whenDoneWith(g,j),k}}(this),g=new q(A(this,"scan",b,c),h)},b.prototype.fold=function(a,b,c){return db(this,"fold",a,b,this.scan(a,b,c).sampledBy(this.filter(!1).mapEnd().toProperty()))},b.prototype.zip=function(b,c){return null==c&&(c=Array),db(this,"zip",b,a.zipWith([this,b],c))},b.prototype.diff=function(a,b){return b=Z(b),db(this,"diff",a,b,this.scan([a],function(a,c){return[c,b(a[0],c)]}).filter(function(a){return 2===a.length}).map(function(a){return a[1]}))},b.prototype.flatMap=function(){return E(this,S(arguments))},b.prototype.flatMapFirst=function(){return E(this,S(arguments),!0)},b.prototype.flatMapWithConcurrencyLimit=function(){var a,b;return b=arguments[0],a=2<=arguments.length?hb.call(arguments,1):[],db.apply(null,[this,"flatMapWithConcurrencyLimit",b].concat(hb.call(a),[E(this,S(a),!1,b)]))},b.prototype.flatMapLatest=function(){var a,b;return a=S(arguments),b=this.toEventStream(),db(this,"flatMapLatest",a,b.flatMap(function(c){return R(a(c)).takeUntil(b)}))},b.prototype.flatMapError=function(b){return db(this,"flatMapError",b,this.mapError(function(b){return new a.Error(b)}).flatMap(function(c){return c instanceof a.Error?b(c.error):a.once(c)}))},b.prototype.flatMapConcat=function(){return db.apply(null,[this,"flatMapConcat"].concat(hb.call(arguments),[this.flatMapWithConcurrencyLimit.apply(this,[1].concat(hb.call(arguments)))]))},b.prototype.bufferingThrottle=function(b){return db(this,"bufferingThrottle",b,this.flatMapConcat(function(c){return a.once(c).concat(a.later(b).filter(!1))}))},b.prototype.not=function(){return db(this,"not",this.map(function(a){return!a}))},b.prototype.log=function(){var a;return a=1<=arguments.length?hb.call(arguments,0):[],this.subscribe(function(b){return"undefined"!=typeof console&&null!==console?"function"==typeof console.log?console.log.apply(console,hb.call(a).concat([b.log()])):void 0:void 0}),this},b.prototype.slidingWindow=function(a,b){return null==b&&(b=0),db(this,"slidingWindow",a,b,this.scan([],function(b,c){return b.concat([c]).slice(-a)}).filter(function(a){return a.length>=b}))},b.prototype.combine=function(b,c){var d;return d=Z(c),db(this,"combine",b,c,a.combineAsArray(this,b).map(function(a){return d(a[0],a[1])}))},b.prototype.decode=function(b){return db(this,"decode",b,this.combine(a.combineTemplate(b),function(a,b){return b[a]}))},b.prototype.awaiting=function(b){return db(this,"awaiting",b,a.groupSimultaneous(this,b).map(function(a){var b,c;return b=a[0],c=a[1],0===c.length}).toProperty(!1).skipDuplicates())},b.prototype.name=function(a){return this._name=a,this},b.prototype.withDescription=function(){return A.apply(null,arguments).apply(this)},b.prototype.dependsOn=function(a){return f.dependsOn(this,a)},b.prototype.toString=function(){return this._name?this._name:this.desc.toString()},b.prototype.internalDeps=function(){return this.initialDesc.deps()},b}(),p.prototype.reduce=p.prototype.fold,p.prototype.assign=p.prototype.onValue,p.prototype.inspect=p.prototype.toString,E=function(b,c,e,g){var h,i;return h=[b],i=new l(A(b,"flatMap"+(e?"First":""),c),function(i){var j,k,l,n,o;return l=new d,n=[],o=function(b){var d;return d=R(c(b.value())),h.push(d),f.invalidate(),l.add(function(b,c){return d.subscribeInternal(function(e){var g;return e.isEnd()?(fb.remove(d,h),f.invalidate(),k(),j(c),a.noMore):(e instanceof m&&(e=e.toNext()),g=i(e),g===a.noMore&&b(),g)})})},k=function(){var a;return a=n.shift(),a?o(a):void 0},j=function(a){return a(),l.empty()?i(B()):void 0},l.add(function(c,d){return b.subscribeInternal(function(b){return b.isEnd()?j(d):b.isError()?i(b):e&&l.count()>1?a.more:l.unsubscribed?a.noMore:g&&l.count()>g?n.push(b):o(b)})}),l.unsubscribe}),i.internalDeps=function(){return h},i},l=function(b){function c(a,b){var d;K(a)&&(b=a,a=[]),c.__super__.constructor.call(this,a),d=new h(b),this.subscribeInternal=d.subscribe,this.subscribe=u.wrappedSubscribe(this),this.hasSubscribers=d.hasSubscribers,X(this)}return jb(c,b),c.prototype.delay=function(b){return db(this,"delay",b,this.flatMap(function(c){return a.later(b,c)}))},c.prototype.debounce=function(b){return db(this,"debounce",b,this.flatMapLatest(function(c){return a.later(b,c)}))},c.prototype.debounceImmediate=function(b){return db(this,"debounceImmediate",b,this.flatMapFirst(function(c){return a.once(c).concat(a.later(b).filter(!1))}))},c.prototype.throttle=function(a){return db(this,"throttle",a,this.bufferWithTime(a).map(function(a){return a[a.length-1]}))},c.prototype.bufferWithTime=function(a){return db(this,"bufferWithTime",a,this.bufferWithTimeOrCount(a,Number.MAX_VALUE))},c.prototype.bufferWithCount=function(a){return db(this,"bufferWithCount",a,this.bufferWithTimeOrCount(void 0,a))},c.prototype.bufferWithTimeOrCount=function(a,b){var c;return c=function(c){return c.values.length===b?c.flush():void 0!==a?c.schedule():void 0},db(this,"bufferWithTimeOrCount",a,b,this.buffer(a,c,c))},c.prototype.buffer=function(b,c,d){var e,f,g;return null==c&&(c=function(){}),null==d&&(d=function(){}),e={scheduled:!1,end:null,values:[],flush:function(){var b;if(this.scheduled=!1,this.values.length>0){if(b=this.push(T(this.values)),this.values=[],null!=this.end)return this.push(this.end);if(b!==a.noMore)return d(this)}else if(null!=this.end)return this.push(this.end)},schedule:function(){return this.scheduled?void 0:(this.scheduled=!0,b(function(a){return function(){return a.flush()}}(this)))}},g=a.more,K(b)||(f=b,b=function(b){return a.scheduler.setTimeout(b,f)}),db(this,"buffer",this.withHandler(function(a){return e.push=this.push,a.isError()?g=this.push(a):a.isEnd()?(e.end=a,e.scheduled||e.flush()):(e.values.push(a.value()),c(e)),g}))},c.prototype.merge=function(b){var c;return c=this,db(c,"merge",b,a.mergeAll(this,b))},c.prototype.toProperty=function(a){return 0===arguments.length&&(a=o),db(this,"toProperty",a,this.scan(a,M,{lazyF:!0}))},c.prototype.toEventStream=function(){return this},c.prototype.sampledBy=function(a,b){return db(this,"sampledBy",a,b,this.toProperty().sampledBy(a,b))},c.prototype.concat=function(a){var b;return b=this,new c(A(b,"concat",a),function(c){var d,e;return e=U,d=b.subscribeInternal(function(b){return b.isEnd()?e=a.subscribeInternal(c):c(b)}),function(){return d(),e()}})},c.prototype.takeUntil=function(b){var c;return c={},db(this,"takeUntil",b,a.groupSimultaneous(this.mapEnd(c),b.skipErrors()).withHandler(function(d){var e,f,g,h,i,j;if(d.hasValue()){if(j=d.value(),e=j[0],b=j[1],b.length)return this.push(B());for(f=a.more,h=0,i=e.length;i>h;h++)g=e[h],f=this.push(g===c?B():T(g));return f}return this.push(d)}))},c.prototype.skipUntil=function(a){var b;return b=a.take(1).map(!0).toProperty(!1),db(this,"skipUntil",a,this.filter(b))},c.prototype.skipWhile=function(){var b,c,d;return c=arguments[0],b=2<=arguments.length?hb.call(arguments,1):[],d=!1,z(this,c,b,function(b){return db(this,"skipWhile",b,this.withHandler(function(c){return!d&&c.hasValue()&&b(c.value())?a.more:(c.hasValue()&&(d=!0),this.push(c))}))})},c.prototype.holdWhen=function(b){var c,d,e;return e=b.startWith(!1),d=e.filter(function(a){return!a}),c=e.filter(fb.id),db(this,"holdWhen",b,this.filter(!1).merge(e.flatMapConcat(function(b){return function(e){return e?b.scan([],function(a,b){return a.concat(b)},{eager:!0}).sampledBy(d).take(1).flatMap(a.fromArray):b.takeUntil(c)}}(this))))},c.prototype.startWith=function(b){return db(this,"startWith",b,a.once(b).concat(this))},c.prototype.withHandler=function(a){var b;return b=new h(this.subscribeInternal,a),new c(A(this,"withHandler",a),b.subscribe)},c}(p),q=function(b){function c(a,b,d){K(a)&&(d=b,b=a,a=[]),c.__super__.constructor.call(this,a),this.subscribeInternal=d===!0?b:new r(this,b,d).subscribe,this.subscribe=u.wrappedSubscribe(this),X(this)}return jb(c,b),c.prototype.sampledBy=function(b,d){var e,f,g,h,i;return null!=d?d=Z(d):(e=!0,d=function(a){return a()}),i=new t(this,!1,this.subscribeInternal,e),g=new t(b,!0,b.subscribeInternal,e),h=a.when([i,g],d),f=b instanceof c?h.toProperty():h,db(this,"sampledBy",b,d,f)},c.prototype.sample=function(b){return db(this,"sample",b,this.sampledBy(a.interval(b,{})))},c.prototype.changes=function(){return new l(A(this,"changes"),function(a){return function(b){return a.subscribeInternal(function(a){return a.isInitial()?void 0:b(a)})}}(this))},c.prototype.withHandler=function(a){return new c(A(this,"withHandler",a),this.subscribeInternal,a)},c.prototype.toProperty=function(){return this},c.prototype.toEventStream=function(){return new l(A(this,"toEventStream"),function(a){return function(b){return a.subscribeInternal(function(a){return a.isInitial()&&(a=a.toNext()),b(a)})}}(this))},c.prototype.and=function(a){return db(this,"and",a,this.combine(a,function(a,b){return a&&b}))},c.prototype.or=function(a){return db(this,"or",a,this.combine(a,function(a,b){return a||b}))},c.prototype.delay=function(a){return this.delayChanges("delay",a,function(b){return b.delay(a)})},c.prototype.debounce=function(a){return this.delayChanges("debounce",a,function(b){return b.debounce(a)})},c.prototype.throttle=function(a){return this.delayChanges("throttle",a,function(b){return b.throttle(a)})},c.prototype.delayChanges=function(){var a,b,c;return a=2<=arguments.length?hb.call(arguments,0,c=arguments.length-1):(c=0,[]),b=arguments[c++],db.apply(null,[this].concat(hb.call(a),[v(this,b(this.changes()))]))},c.prototype.takeUntil=function(a){var b;return b=this.changes().takeUntil(a),db(this,"takeUntil",a,v(this,b))},c.prototype.startWith=function(a){return db(this,"startWith",a,this.scan(a,function(a,b){return b}))},c.prototype.bufferingThrottle=function(){var a;return(a=c.__super__.bufferingThrottle.apply(this,arguments)).bufferingThrottle.apply(a,arguments).toProperty()},c}(p),z=function(a,b,c,d){var e;return b instanceof q?(e=b.sampledBy(a,function(a,b){return[a,b]}),d.apply(e,[function(a){var b,c;return b=a[0],c=a[1],b}]).map(function(a){var b,c;return b=a[0],c=a[1]})):(b=O(b,c),d.apply(a,[b]))},v=function(b,c){var d;return d=new l(A(b,"justInitValue"),function(c){var e,f;return f=null,e=b.subscribeInternal(function(b){return b.hasValue()&&(f=b),a.noMore}),u.whenDoneWith(d,function(){return null!=f&&c(f),c(B())}),e}),d.concat(c).toProperty()},h=function(){function b(b,c){var d,e,f,g,h,i,j,k,l,m;null==b&&(b=function(){return U}),k=[],i=[],h=!1,e=!1,this.hasSubscribers=function(){return k.length>0},f=null,l=U,j=function(a){return k=fb.without(a,k)},m=null,d=function(){var a,b,c,d,e;if(null!=m){for(b=m,m=null,e=[],c=0,d=b.length;d>c;c++)a=b[c],e.push(a());return e}},g=function(b){var c,e,g,m,n,o;if(h)return i.push(b),a.more;if(b!==f){b.isError()&&(f=b),g=!1;try{for(h=!0,m=k,n=0,o=m.length;o>n;n++)e=m[n],c=e.sink(b),(c===a.noMore||b.isEnd())&&j(e);g=!0}finally{h=!1,g||(i=[])}for(g=!0;i.length;)b=i.shift(),this.push(b);return d(b),this.hasSubscribers()?a.more:(l(),a.noMore)}},this.push=function(a){return function(b){return u.inTransaction(b,a,g,[b])}}(this),null==c&&(c=function(a){return this.push(a)}),this.handleEvent=function(a){return function(b){return b.isEnd()&&(e=!0),c.apply(a,[b])}}(this),this.subscribe=function(a){return function(c){var d,f;return e?(c(B()),U):(d={sink:c},k.push(d),1===k.length&&(f=b(a.handleEvent),l=function(){return f(),l=U}),function(){return j(d),a.hasSubscribers()?void 0:l()})}}(this)}return b}(),r=function(b){function c(b,d,e){var f,g,h,i;c.__super__.constructor.call(this,d,e),f=o,g=void 0,i=this.push,d=this.subscribe,h=!1,this.push=function(a){return function(b){return b.isEnd()&&(h=!0),b.hasValue()&&(f=new s(b),g=u.currentEventId()),i.apply(a,[b])}}(this),this.subscribe=function(c){return function(e){var i,j,k,l,m;return j=!1,l=a.more,k=function(){return l===a.noMore?U:h?(e(B()),U):d.apply(this,[e])},f.isDefined&&(c.hasSubscribers()||h)?(i=u.currentEventId(),m=g,!h&&m&&i&&i!==m?(u.whenDoneWith(b,function(){return g===m?e(H(f.get().value())):void 0}),k()):(u.inTransaction(void 0,c,function(){return l=e(H(f.get().value()))},[]),k())):k()}}(this)}return jb(c,b),c}(h),c=function(b){function c(){var b,d,e,f,g,h,i,k;e=void 0,h=[],b=!1,d=function(){return function(b){return function(c){return c.isEnd()?(k(b),a.noMore):e(c)}}}(this),i=function(){var a,b,c,d;for(d=[],b=0,c=h.length;c>b;b++)a=h[b],d.push("function"==typeof a.unsub?a.unsub():void 0);return d},g=function(a){return a.unsub=a.input.subscribeInternal(d(a.input))},k=function(a){var b,c,d,e;for(b=d=0,e=h.length;e>d;b=++d)if(c=h[b],c.input===a)return"function"==typeof c.unsub&&c.unsub(),void h.splice(b,1)},f=function(){return function(a){var b,c,d,f;for(e=a,f=w(h),c=0,d=f.length;d>c;c++)b=f[c],g(b);return i}}(this),c.__super__.constructor.call(this,A(a,"Bus"),f),this.plug=function(){return function(a){var c;if(!b)return c={input:a},h.push(c),null!=e&&g(c),function(){return k(a)}}}(this),this.push=function(){return function(a){return"function"==typeof e?e(T(a)):void 0}}(this),this.error=function(){return function(a){return"function"==typeof e?e(new j(a)):void 0}}(this),this.end=function(){return function(){return b=!0,i(),"function"==typeof e?e(B()):void 0}}(this)}return jb(c,b),c}(l),t=function(){function a(a,b,c,d){this.obs=a,this.sync=b,this.subscribe=c,this.lazy=null!=d?d:!1,this.queue=[],null==this.subscribe&&(this.subscribe=this.obs.subscribeInternal)}return a.prototype.toString=function(){return this.obs.toString.call(this)},a.prototype.markEnded=function(){return this.ended=!0},a.prototype.consume=function(){return this.lazy?fb.always(this.queue[0]):this.queue[0]},a.prototype.push=function(a){return this.queue=[a]},a.prototype.mayHave=function(){return!0},a.prototype.hasAtLeast=function(){return this.queue.length},a.prototype.flatten=!0,a}(),e=function(a){function b(){return b.__super__.constructor.apply(this,arguments)}return jb(b,a),b.prototype.consume=function(){return this.queue.shift()},b.prototype.push=function(a){return this.queue.push(a)},b.prototype.mayHave=function(a){return!this.ended||this.queue.length>=a},b.prototype.hasAtLeast=function(a){return this.queue.length>=a},b.prototype.flatten=!1,b}(t),b=function(a){function b(a){this.obs=a,b.__super__.constructor.call(this,this.obs,!0,this.obs.subscribeInternal)}return jb(b,a),b.prototype.consume=function(){var a;return a=this.queue,this.queue=[],function(){return a}},b.prototype.push=function(a){return this.queue.push(a())},b.prototype.hasAtLeast=function(){return!0},b}(t),t.isTrigger=function(a){return a instanceof t?a.sync:a instanceof l},t.fromObservable=function(a){return a instanceof t?a:a instanceof q?new t(a,!1):new e(a,!0)},A=function(){var a,b,c;return b=arguments[0],c=arguments[1],a=3<=arguments.length?hb.call(arguments,2):[],(b||c)instanceof g?b||c:new g(b,c,a)},D=function(a){return I(a)?fb.flatMap(D,a):L(a)?[a]:a instanceof t?[a.obs]:[]},g=function(){function a(a,b,c){this.context=a,this.method=b,this.args=c,this.cached=null}return a.prototype.deps=function(){return this.cached||(this.cached=D([this.context].concat(this.args)))},a.prototype.apply=function(a){return a.desc=this,a},a.prototype.toString=function(){return fb.toString(this.context)+"."+fb.toString(this.method)+"("+fb.map(fb.toString,this.args)+")"},a}(),db=function(){var a,b,c;return a=2<=arguments.length?hb.call(arguments,0,c=arguments.length-1):(c=0,[]),b=arguments[c++],A.apply(null,a).apply(b)},a.when=function(){var b,c,d,e,f,g,h,i,j,k,m,n,o,p,q,r,s,v,w,z;if(k=1<=arguments.length?hb.call(arguments,0):[],0===k.length)return a.never();for(f=k.length,q="when: expecting arguments in the form (Observable+,function)+",o=[],j=[],c=0;f>c;){for(i=fb.toArray(k[c]),b=k[c+1],h={f:K(b)?b:function(){return b},ixs:[]},p=!1,r=0,v=i.length;v>r;r++){for(n=i[r],d=fb.indexOf(o,n),p||(p=t.isTrigger(n)),0>d&&(o.push(n),d=o.length-1),z=h.ixs,s=0,w=z.length;w>s;s++)e=z[s],e.index===d&&e.count++;h.ixs.push({index:d,count:1})}i.length>0&&j.push(h),c+=2}return o.length?(o=fb.map(t.fromObservable,o),g=fb.any(o,function(a){return a.flatten})&&y(fb.map(function(a){return a.obs},o)),m=new l(A.apply(null,[a,"when"].concat(hb.call(k))),function(b){var d,e,f,h,i,k,l;return l=[],f=!1,h=function(a){var b,d,e;for(e=a.ixs,b=0,d=e.length;d>b;b++)if(c=e[b],!o[c.index].hasAtLeast(c.count))return!1;return!0},e=function(a){return!a.sync||a.ended},d=function(a){var b,d,e;for(e=a.ixs,b=0,d=e.length;d>b;b++)if(c=e[b],!o[c.index].mayHave(c.count))return!0},i=function(a){return!a.source.flatten},k=function(k){return function(n){var p,q,r;return q=function(){return u.whenDoneWith(m,p)},r=function(){var d,e,f,g,k,m;if(!(l.length>0))return a.more;for(f=a.more,g=l.pop(),k=0,m=j.length;m>k;k++)if(e=j[k],h(e))return d=function(){var a,b,d,f;for(d=e.ixs,f=[],a=0,b=d.length;b>a;a++)c=d[a],f.push(o[c.index].consume());return f}(),f=b(g.e.apply(function(){var a,b;return b=function(){var b,c,e;for(e=[],b=0,c=d.length;c>b;b++)a=d[b],e.push(a());return e}(),e.f.apply(e,b)})),l.length&&(l=fb.filter(i,l)),f===a.noMore?f:r()},p=function(){var c;return c=r(),f&&(f=!1,(fb.all(o,e)||fb.all(j,d))&&(c=a.noMore,b(B()))),c===a.noMore&&n(),c},k.subscribe(function(c){var d;return c.isEnd()?(f=!0,k.markEnded(),q()):c.isError()?d=b(c):(k.push(c.value),k.sync&&(l.push({source:k,e:c}),g||u.hasWaiters()?q():p())),d===a.noMore&&n(),d||a.more})}},x.apply(null,function(){var a,b,c;for(c=[],a=0,b=o.length;b>a;a++)n=o[a],c.push(k(n));return c}())})):a.never()},y=function(b,c){var d;return null==c&&(c=[]),d=function(b){var e;return a._.contains(c,b)?!0:(e=b.internalDeps(),e.length?(c.push(b),a._.any(e,d)):(c.push(b),!1))},a._.any(b,d)},a.update=function(){var b,c,d,e;for(c=arguments[0],e=2<=arguments.length?hb.call(arguments,1):[],d=function(a){return function(){var b;return b=1<=arguments.length?hb.call(arguments,0):[],function(c){return a.apply(null,[c].concat(b))}}},b=e.length-1;b>0;)e[b]instanceof Function||(e[b]=function(a){return function(){return a}}(e[b])),e[b]=d(e[b]),b-=2;return db.apply(null,[a,"update",c].concat(hb.call(e),[a.when.apply(a,e).scan(c,function(a,b){return b(a)})]))},x=function(){var a;return a=1<=arguments.length?hb.call(arguments,0):[],new d(a).unsubscribe},d=function(){function a(a){var b,c,d;for(null==a&&(a=[]),this.unsubscribe=kb(this.unsubscribe,this),this.unsubscribed=!1,this.subscriptions=[],this.starting=[],c=0,d=a.length;d>c;c++)b=a[c],this.add(b)}return a.prototype.add=function(a){var b,c,d;if(!this.unsubscribed)return b=!1,c=U,this.starting.push(a),d=function(d){return function(){return d.unsubscribed?void 0:(b=!0,d.remove(c),fb.remove(a,d.starting))}}(this),c=a(this.unsubscribe,d),this.unsubscribed||b||this.subscriptions.push(c),fb.remove(a,this.starting),c},a.prototype.remove=function(a){return this.unsubscribed?void 0:void 0!==fb.remove(a,this.subscriptions)?a():void 0},a.prototype.unsubscribe=function(){var a,b,c,d;if(!this.unsubscribed){for(this.unsubscribed=!0,d=this.subscriptions,b=0,c=d.length;c>b;b++)(a=d[b])();return this.subscriptions=[],this.starting=[]}},a.prototype.count=function(){return this.unsubscribed?0:this.subscriptions.length+this.starting.length 2 | },a.prototype.empty=function(){return 0===this.count()},a}(),a.CompositeUnsubscribe=d,s=function(){function a(a){this.value=a}return a.prototype.getOrElse=function(){return this.value},a.prototype.get=function(){return this.value},a.prototype.filter=function(b){return b(this.value)?new a(this.value):o},a.prototype.map=function(b){return new a(b(this.value))},a.prototype.forEach=function(a){return a(this.value)},a.prototype.isDefined=!0,a.prototype.toArray=function(){return[this.value]},a.prototype.inspect=function(){return"Some("+this.value+")"},a.prototype.toString=function(){return this.inspect()},a}(),o={getOrElse:function(a){return a},filter:function(){return o},map:function(){return o},forEach:function(){},isDefined:!1,toArray:function(){return[]},inspect:function(){return"None"},toString:function(){return this.inspect()}},f=function(){var a,b,c,d;return c={},b=function(b,d){var e;return e=c[b.id],e||(e=c[b.id]={},a(b,b)),e[d.id]},a=function(b,d){var e,f,g,h,i;for(h=d.internalDeps(),i=[],f=0,g=h.length;g>f;f++)e=h[f],c[b.id][e.id]=!0,i.push(a(b,e));return i},d=function(){return c={}},{invalidate:d,dependsOn:b}}(),u=function(){var b,c,d,e,g,h,i,j,k,l,m,n,o;return l=void 0,m=[],c=[],b=function(a){return l?c.push(a):a()},j=function(a){return!fb.any(m,function(b){return f.dependsOn(a.obs,b.obs)})},n=function(a,b){return l?m.push({obs:a,f:b}):b()},e=function(){for(;!j(m[0]);)m.push(m.shift());return m.shift()},g=function(){var a;for(a=[];m.length;)a.push(e().f());return a},k=f.invalidate,i=function(a,b,d,e){var f,h,i,j;if(l)return d.apply(b,e);l=a;try{f=d.apply(b,e),g()}finally{for(l=void 0;c.length;)for(h=c,c=[],i=0,j=h.length;j>i;i++)(d=h[i])();k()}return f},d=function(){return l?l.id:void 0},o=function(c){return function(d){var e,f,g;return g=!1,e=function(){},f=function(){return g=!0,e()},e=c.subscribeInternal(function(c){return b(function(){var b;return g||(b=d(c),b!==a.noMore)?void 0:f()})}),f}},h=function(){return m.length>0},{whenDoneWith:n,hasWaiters:h,inTransaction:i,currentEventId:d,wrappedSubscribe:o}}(),a.EventStream=l,a.Property=q,a.Observable=p,a.Bus=c,a.Initial=m,a.Next=n,a.End=i,a.Error=j,U=function(){},M=function(a,b){return b()},F=function(a){return a},H=function(a){return new m(fb.always(a))},T=function(a){return new n(fb.always(a))},B=function(){return new i},$=function(a){return a instanceof k?a:T(a)},w=function(a){return a.slice(0)},K=function(a){return"function"==typeof a},I=function(a){return a instanceof Array},L=function(a){return a instanceof p},V=function(a,b){return function(){var c;return c=1<=arguments.length?hb.call(arguments,0):[],a.apply(null,b.concat(c))}},S=function(a){return 1===a.length&&L(a[0])?fb.always(a[0]):P(a)},P=function(a){return a=Array.prototype.slice.call(a),Q.apply(null,a)},Q=eb(function(){var a,b;return b=arguments[0],a=2<=arguments.length?hb.call(arguments,1):[],K(b)?a.length?V(b,a):b:J(b)?_(b,a):fb.always(b)}),O=function(a,b){return Q.apply(null,[a].concat(hb.call(b)))},R=function(b){return L(b)?b:a.once(b)},J=function(a){return"string"==typeof a&&a.length>1&&"."===a.charAt(0)},a.isFieldKey=J,_=function(a,b){var c,d;return d=a.slice(1).split("."),c=fb.map(cb(b),d),function(b){var d,e;for(d=0,e=c.length;e>d;d++)a=c[d],b=a(b);return b}},cb=function(a){return function(b){return function(c){var d;return null==c?void 0:(d=c[b],K(d)?d.apply(c,a):d)}}},ab=function(a){return a.slice(1)},Z=function(a){var b;return K(a)?a:J(a)?(b=ab(a),function(a,c){return a[b](c)}):void 0},bb=function(a){return a instanceof s||a===o?a:new s(a)},fb={indexOf:Array.prototype.indexOf?function(a,b){return a.indexOf(b)}:function(a,b){var c,d,e,f;for(c=e=0,f=a.length;f>e;c=++e)if(d=a[c],b===d)return c;return-1},indexWhere:function(a,b){var c,d,e,f;for(c=e=0,f=a.length;f>e;c=++e)if(d=a[c],b(d))return c;return-1},head:function(a){return a[0]},always:function(a){return function(){return a}},negate:function(a){return function(b){return!a(b)}},empty:function(a){return 0===a.length},tail:function(a){return a.slice(1,a.length)},filter:function(a,b){var c,d,e,f;for(c=[],e=0,f=b.length;f>e;e++)d=b[e],a(d)&&c.push(d);return c},map:function(a,b){var c,d,e,f;for(f=[],d=0,e=b.length;e>d;d++)c=b[d],f.push(a(c));return f},each:function(a,b){var c,d,e;e=[];for(c in a)d=a[c],e.push(b(c,d));return e},toArray:function(a){return I(a)?a:[a]},contains:function(a,b){return-1!==fb.indexOf(a,b)},id:function(a){return a},last:function(a){return a[a.length-1]},all:function(a,b){var c,d,e;for(null==b&&(b=fb.id),d=0,e=a.length;e>d;d++)if(c=a[d],!b(c))return!1;return!0},any:function(a,b){var c,d,e;for(null==b&&(b=fb.id),d=0,e=a.length;e>d;d++)if(c=a[d],b(c))return!0;return!1},without:function(a,b){return fb.filter(function(b){return b!==a},b)},remove:function(a,b){var c;return c=fb.indexOf(b,a),c>=0?b.splice(c,1):void 0},fold:function(a,b,c){var d,e,f;for(e=0,f=a.length;f>e;e++)d=a[e],b=c(b,d);return b},flatMap:function(a,b){return fb.fold(b,[],function(b,c){return b.concat(a(c))})},cached:function(a){var b;return b=o,function(){return b===o&&(b=a(),a=null),b}},toString:function(a){var b,c,d,e;try{return W++,null==a?"undefined":K(a)?"function":I(a)?W>5?"[..]":"["+fb.map(fb.toString,a).toString()+"]":null!=(null!=a?a.toString:void 0)&&a.toString!==Object.prototype.toString?a.toString():"object"==typeof a?W>5?"{..}":(c=function(){var c;c=[];for(d in a)ib.call(a,d)&&(e=function(){try{return a[d]}catch(c){return b=c}}(),c.push(fb.toString(d)+":"+fb.toString(e)));return c}(),"{"+c+"}"):a}finally{W--}}},W=0,a._=fb,a.scheduler={setTimeout:function(a,b){return setTimeout(a,b)},setInterval:function(a,b){return setInterval(a,b)},clearInterval:function(a){return clearInterval(a)},now:function(){return(new Date).getTime()}},"undefined"!=typeof define&&null!==define&&null!=define.amd?(define([],function(){return a}),this.Bacon=a):"undefined"!=typeof module&&null!==module?(module.exports=a,a.Bacon=a):this.Bacon=a}).call(this); 3 | -------------------------------------------------------------------------------- /readme/signature.js: -------------------------------------------------------------------------------- 1 | module.exports = (function() { 2 | /* 3 | * Generated by PEG.js 0.8.0. 4 | * 5 | * http://pegjs.majda.cz/ 6 | */ 7 | 8 | function peg$subclass(child, parent) { 9 | function ctor() { this.constructor = child; } 10 | ctor.prototype = parent.prototype; 11 | child.prototype = new ctor(); 12 | } 13 | 14 | function SyntaxError(message, expected, found, offset, line, column) { 15 | this.message = message; 16 | this.expected = expected; 17 | this.found = found; 18 | this.offset = offset; 19 | this.line = line; 20 | this.column = column; 21 | 22 | this.name = "SyntaxError"; 23 | } 24 | 25 | peg$subclass(SyntaxError, Error); 26 | 27 | function parse(input) { 28 | var options = arguments.length > 1 ? arguments[1] : {}, 29 | 30 | peg$FAILED = {}, 31 | 32 | peg$startRuleFunctions = { functionsignature: peg$parsefunctionsignature }, 33 | peg$startRuleFunction = peg$parsefunctionsignature, 34 | 35 | peg$c0 = peg$FAILED, 36 | peg$c1 = null, 37 | peg$c2 = function(n, c, m, p, r) { 38 | return { 39 | n: !!n, 40 | namespace: c, 41 | method: m, 42 | params: p, 43 | result: r, 44 | }; 45 | }, 46 | peg$c3 = [], 47 | peg$c4 = /^[ \t]/, 48 | peg$c5 = { type: "class", value: "[ \\t]", description: "[ \\t]" }, 49 | peg$c6 = /^[a-zA-Z_$]/, 50 | peg$c7 = { type: "class", value: "[a-zA-Z_$]", description: "[a-zA-Z_$]" }, 51 | peg$c8 = /^[a-zA-Z0-9_$]/, 52 | peg$c9 = { type: "class", value: "[a-zA-Z0-9_$]", description: "[a-zA-Z0-9_$]" }, 53 | peg$c10 = function(x, xs) { return [x].concat(xs).join(""); }, 54 | peg$c11 = "(", 55 | peg$c12 = { type: "literal", value: "(", description: "\"(\"" }, 56 | peg$c13 = ")", 57 | peg$c14 = { type: "literal", value: ")", description: "\")\"" }, 58 | peg$c15 = "[", 59 | peg$c16 = { type: "literal", value: "[", description: "\"[\"" }, 60 | peg$c17 = "]", 61 | peg$c18 = { type: "literal", value: "]", description: "\"]\"" }, 62 | peg$c19 = ".", 63 | peg$c20 = { type: "literal", value: ".", description: "\".\"" }, 64 | peg$c21 = function(fn) { return fn; }, 65 | peg$c22 = "new", 66 | peg$c23 = { type: "literal", value: "new", description: "\"new\"" }, 67 | peg$c24 = function() { return "new" }, 68 | peg$c25 = function() { return []; }, 69 | peg$c26 = function(p) { return [p]; }, 70 | peg$c27 = function(p, r) { return [p].concat(r); }, 71 | peg$c28 = function(p) { 72 | p.optional = true; 73 | return p; 74 | }, 75 | peg$c29 = ",", 76 | peg$c30 = { type: "literal", value: ",", description: "\",\"" }, 77 | peg$c31 = function(p) { return p; }, 78 | peg$c32 = "...", 79 | peg$c33 = { type: "literal", value: "...", description: "\"...\"" }, 80 | peg$c34 = function(i, t, r) { 81 | return { 82 | name: i, 83 | type: t, 84 | splat: !!r, 85 | }; 86 | }, 87 | peg$c35 = "@", 88 | peg$c36 = { type: "literal", value: "@", description: "\"@\"" }, 89 | peg$c37 = function(t) { 90 | return { 91 | name: "@", 92 | type: t, 93 | }; 94 | }, 95 | peg$c38 = ":", 96 | peg$c39 = { type: "literal", value: ":", description: "\":\"" }, 97 | peg$c40 = function(t) { return t; }, 98 | peg$c41 = "()", 99 | peg$c42 = { type: "literal", value: "()", description: "\"()\"" }, 100 | peg$c43 = function() { 101 | return { 102 | type: "unit", 103 | }; 104 | }, 105 | peg$c44 = function(x, xs) { 106 | if (xs.length === 0) { 107 | return x; 108 | } else { 109 | return { 110 | type: "sum", 111 | options: [x].concat(xs), 112 | }; 113 | } 114 | }, 115 | peg$c45 = "|", 116 | peg$c46 = { type: "literal", value: "|", description: "\"|\"" }, 117 | peg$c47 = function(v) { return v; }, 118 | peg$c48 = function(i, p) { 119 | return { 120 | type: "name", 121 | name: i, 122 | params: p ? [p] : [] 123 | }; 124 | }, 125 | peg$c49 = "->", 126 | peg$c50 = { type: "literal", value: "->", description: "\"->\"" }, 127 | peg$c51 = function(y) { return y; }, 128 | peg$c52 = function(x, xs) { return { 129 | type: "function", 130 | ret: xs.slice(-1)[0] 131 | };}, 132 | 133 | peg$currPos = 0, 134 | peg$reportedPos = 0, 135 | peg$cachedPos = 0, 136 | peg$cachedPosDetails = { line: 1, column: 1, seenCR: false }, 137 | peg$maxFailPos = 0, 138 | peg$maxFailExpected = [], 139 | peg$silentFails = 0, 140 | 141 | peg$result; 142 | 143 | if ("startRule" in options) { 144 | if (!(options.startRule in peg$startRuleFunctions)) { 145 | throw new Error("Can't start parsing from rule \"" + options.startRule + "\"."); 146 | } 147 | 148 | peg$startRuleFunction = peg$startRuleFunctions[options.startRule]; 149 | } 150 | 151 | function text() { 152 | return input.substring(peg$reportedPos, peg$currPos); 153 | } 154 | 155 | function offset() { 156 | return peg$reportedPos; 157 | } 158 | 159 | function line() { 160 | return peg$computePosDetails(peg$reportedPos).line; 161 | } 162 | 163 | function column() { 164 | return peg$computePosDetails(peg$reportedPos).column; 165 | } 166 | 167 | function expected(description) { 168 | throw peg$buildException( 169 | null, 170 | [{ type: "other", description: description }], 171 | peg$reportedPos 172 | ); 173 | } 174 | 175 | function error(message) { 176 | throw peg$buildException(message, null, peg$reportedPos); 177 | } 178 | 179 | function peg$computePosDetails(pos) { 180 | function advance(details, startPos, endPos) { 181 | var p, ch; 182 | 183 | for (p = startPos; p < endPos; p++) { 184 | ch = input.charAt(p); 185 | if (ch === "\n") { 186 | if (!details.seenCR) { details.line++; } 187 | details.column = 1; 188 | details.seenCR = false; 189 | } else if (ch === "\r" || ch === "\u2028" || ch === "\u2029") { 190 | details.line++; 191 | details.column = 1; 192 | details.seenCR = true; 193 | } else { 194 | details.column++; 195 | details.seenCR = false; 196 | } 197 | } 198 | } 199 | 200 | if (peg$cachedPos !== pos) { 201 | if (peg$cachedPos > pos) { 202 | peg$cachedPos = 0; 203 | peg$cachedPosDetails = { line: 1, column: 1, seenCR: false }; 204 | } 205 | advance(peg$cachedPosDetails, peg$cachedPos, pos); 206 | peg$cachedPos = pos; 207 | } 208 | 209 | return peg$cachedPosDetails; 210 | } 211 | 212 | function peg$fail(expected) { 213 | if (peg$currPos < peg$maxFailPos) { return; } 214 | 215 | if (peg$currPos > peg$maxFailPos) { 216 | peg$maxFailPos = peg$currPos; 217 | peg$maxFailExpected = []; 218 | } 219 | 220 | peg$maxFailExpected.push(expected); 221 | } 222 | 223 | function peg$buildException(message, expected, pos) { 224 | function cleanupExpected(expected) { 225 | var i = 1; 226 | 227 | expected.sort(function(a, b) { 228 | if (a.description < b.description) { 229 | return -1; 230 | } else if (a.description > b.description) { 231 | return 1; 232 | } else { 233 | return 0; 234 | } 235 | }); 236 | 237 | while (i < expected.length) { 238 | if (expected[i - 1] === expected[i]) { 239 | expected.splice(i, 1); 240 | } else { 241 | i++; 242 | } 243 | } 244 | } 245 | 246 | function buildMessage(expected, found) { 247 | function stringEscape(s) { 248 | function hex(ch) { return ch.charCodeAt(0).toString(16).toUpperCase(); } 249 | 250 | return s 251 | .replace(/\\/g, '\\\\') 252 | .replace(/"/g, '\\"') 253 | .replace(/\x08/g, '\\b') 254 | .replace(/\t/g, '\\t') 255 | .replace(/\n/g, '\\n') 256 | .replace(/\f/g, '\\f') 257 | .replace(/\r/g, '\\r') 258 | .replace(/[\x00-\x07\x0B\x0E\x0F]/g, function(ch) { return '\\x0' + hex(ch); }) 259 | .replace(/[\x10-\x1F\x80-\xFF]/g, function(ch) { return '\\x' + hex(ch); }) 260 | .replace(/[\u0180-\u0FFF]/g, function(ch) { return '\\u0' + hex(ch); }) 261 | .replace(/[\u1080-\uFFFF]/g, function(ch) { return '\\u' + hex(ch); }); 262 | } 263 | 264 | var expectedDescs = new Array(expected.length), 265 | expectedDesc, foundDesc, i; 266 | 267 | for (i = 0; i < expected.length; i++) { 268 | expectedDescs[i] = expected[i].description; 269 | } 270 | 271 | expectedDesc = expected.length > 1 272 | ? expectedDescs.slice(0, -1).join(", ") 273 | + " or " 274 | + expectedDescs[expected.length - 1] 275 | : expectedDescs[0]; 276 | 277 | foundDesc = found ? "\"" + stringEscape(found) + "\"" : "end of input"; 278 | 279 | return "Expected " + expectedDesc + " but " + foundDesc + " found."; 280 | } 281 | 282 | var posDetails = peg$computePosDetails(pos), 283 | found = pos < input.length ? input.charAt(pos) : null; 284 | 285 | if (expected !== null) { 286 | cleanupExpected(expected); 287 | } 288 | 289 | return new SyntaxError( 290 | message !== null ? message : buildMessage(expected, found), 291 | expected, 292 | found, 293 | pos, 294 | posDetails.line, 295 | posDetails.column 296 | ); 297 | } 298 | 299 | function peg$parsefunctionsignature() { 300 | var s0, s1, s2, s3, s4, s5; 301 | 302 | s0 = peg$currPos; 303 | s1 = peg$parsenew(); 304 | if (s1 === peg$FAILED) { 305 | s1 = peg$c1; 306 | } 307 | if (s1 !== peg$FAILED) { 308 | s2 = peg$parseidentifier(); 309 | if (s2 !== peg$FAILED) { 310 | s3 = peg$parsemethod(); 311 | if (s3 === peg$FAILED) { 312 | s3 = peg$c1; 313 | } 314 | if (s3 !== peg$FAILED) { 315 | s4 = peg$parseparams(); 316 | if (s4 === peg$FAILED) { 317 | s4 = peg$c1; 318 | } 319 | if (s4 !== peg$FAILED) { 320 | s5 = peg$parsetypeDefinition(); 321 | if (s5 === peg$FAILED) { 322 | s5 = peg$c1; 323 | } 324 | if (s5 !== peg$FAILED) { 325 | peg$reportedPos = s0; 326 | s1 = peg$c2(s1, s2, s3, s4, s5); 327 | s0 = s1; 328 | } else { 329 | peg$currPos = s0; 330 | s0 = peg$c0; 331 | } 332 | } else { 333 | peg$currPos = s0; 334 | s0 = peg$c0; 335 | } 336 | } else { 337 | peg$currPos = s0; 338 | s0 = peg$c0; 339 | } 340 | } else { 341 | peg$currPos = s0; 342 | s0 = peg$c0; 343 | } 344 | } else { 345 | peg$currPos = s0; 346 | s0 = peg$c0; 347 | } 348 | 349 | return s0; 350 | } 351 | 352 | function peg$parsewhitespace() { 353 | var s0, s1; 354 | 355 | s0 = []; 356 | if (peg$c4.test(input.charAt(peg$currPos))) { 357 | s1 = input.charAt(peg$currPos); 358 | peg$currPos++; 359 | } else { 360 | s1 = peg$FAILED; 361 | if (peg$silentFails === 0) { peg$fail(peg$c5); } 362 | } 363 | if (s1 !== peg$FAILED) { 364 | while (s1 !== peg$FAILED) { 365 | s0.push(s1); 366 | if (peg$c4.test(input.charAt(peg$currPos))) { 367 | s1 = input.charAt(peg$currPos); 368 | peg$currPos++; 369 | } else { 370 | s1 = peg$FAILED; 371 | if (peg$silentFails === 0) { peg$fail(peg$c5); } 372 | } 373 | } 374 | } else { 375 | s0 = peg$c0; 376 | } 377 | 378 | return s0; 379 | } 380 | 381 | function peg$parseidentifier() { 382 | var s0, s1, s2, s3; 383 | 384 | s0 = peg$currPos; 385 | if (peg$c6.test(input.charAt(peg$currPos))) { 386 | s1 = input.charAt(peg$currPos); 387 | peg$currPos++; 388 | } else { 389 | s1 = peg$FAILED; 390 | if (peg$silentFails === 0) { peg$fail(peg$c7); } 391 | } 392 | if (s1 !== peg$FAILED) { 393 | s2 = []; 394 | if (peg$c8.test(input.charAt(peg$currPos))) { 395 | s3 = input.charAt(peg$currPos); 396 | peg$currPos++; 397 | } else { 398 | s3 = peg$FAILED; 399 | if (peg$silentFails === 0) { peg$fail(peg$c9); } 400 | } 401 | while (s3 !== peg$FAILED) { 402 | s2.push(s3); 403 | if (peg$c8.test(input.charAt(peg$currPos))) { 404 | s3 = input.charAt(peg$currPos); 405 | peg$currPos++; 406 | } else { 407 | s3 = peg$FAILED; 408 | if (peg$silentFails === 0) { peg$fail(peg$c9); } 409 | } 410 | } 411 | if (s2 !== peg$FAILED) { 412 | peg$reportedPos = s0; 413 | s1 = peg$c10(s1, s2); 414 | s0 = s1; 415 | } else { 416 | peg$currPos = s0; 417 | s0 = peg$c0; 418 | } 419 | } else { 420 | peg$currPos = s0; 421 | s0 = peg$c0; 422 | } 423 | 424 | return s0; 425 | } 426 | 427 | function peg$parseob() { 428 | var s0, s1, s2; 429 | 430 | s0 = peg$currPos; 431 | if (input.charCodeAt(peg$currPos) === 40) { 432 | s1 = peg$c11; 433 | peg$currPos++; 434 | } else { 435 | s1 = peg$FAILED; 436 | if (peg$silentFails === 0) { peg$fail(peg$c12); } 437 | } 438 | if (s1 !== peg$FAILED) { 439 | s2 = peg$parsewhitespace(); 440 | if (s2 === peg$FAILED) { 441 | s2 = peg$c1; 442 | } 443 | if (s2 !== peg$FAILED) { 444 | s1 = [s1, s2]; 445 | s0 = s1; 446 | } else { 447 | peg$currPos = s0; 448 | s0 = peg$c0; 449 | } 450 | } else { 451 | peg$currPos = s0; 452 | s0 = peg$c0; 453 | } 454 | 455 | return s0; 456 | } 457 | 458 | function peg$parsecb() { 459 | var s0, s1, s2; 460 | 461 | s0 = peg$currPos; 462 | if (input.charCodeAt(peg$currPos) === 41) { 463 | s1 = peg$c13; 464 | peg$currPos++; 465 | } else { 466 | s1 = peg$FAILED; 467 | if (peg$silentFails === 0) { peg$fail(peg$c14); } 468 | } 469 | if (s1 !== peg$FAILED) { 470 | s2 = peg$parsewhitespace(); 471 | if (s2 === peg$FAILED) { 472 | s2 = peg$c1; 473 | } 474 | if (s2 !== peg$FAILED) { 475 | s1 = [s1, s2]; 476 | s0 = s1; 477 | } else { 478 | peg$currPos = s0; 479 | s0 = peg$c0; 480 | } 481 | } else { 482 | peg$currPos = s0; 483 | s0 = peg$c0; 484 | } 485 | 486 | return s0; 487 | } 488 | 489 | function peg$parseos() { 490 | var s0, s1, s2; 491 | 492 | s0 = peg$currPos; 493 | if (input.charCodeAt(peg$currPos) === 91) { 494 | s1 = peg$c15; 495 | peg$currPos++; 496 | } else { 497 | s1 = peg$FAILED; 498 | if (peg$silentFails === 0) { peg$fail(peg$c16); } 499 | } 500 | if (s1 !== peg$FAILED) { 501 | s2 = peg$parsewhitespace(); 502 | if (s2 === peg$FAILED) { 503 | s2 = peg$c1; 504 | } 505 | if (s2 !== peg$FAILED) { 506 | s1 = [s1, s2]; 507 | s0 = s1; 508 | } else { 509 | peg$currPos = s0; 510 | s0 = peg$c0; 511 | } 512 | } else { 513 | peg$currPos = s0; 514 | s0 = peg$c0; 515 | } 516 | 517 | return s0; 518 | } 519 | 520 | function peg$parsecs() { 521 | var s0, s1, s2; 522 | 523 | s0 = peg$currPos; 524 | if (input.charCodeAt(peg$currPos) === 93) { 525 | s1 = peg$c17; 526 | peg$currPos++; 527 | } else { 528 | s1 = peg$FAILED; 529 | if (peg$silentFails === 0) { peg$fail(peg$c18); } 530 | } 531 | if (s1 !== peg$FAILED) { 532 | s2 = peg$parsewhitespace(); 533 | if (s2 === peg$FAILED) { 534 | s2 = peg$c1; 535 | } 536 | if (s2 !== peg$FAILED) { 537 | s1 = [s1, s2]; 538 | s0 = s1; 539 | } else { 540 | peg$currPos = s0; 541 | s0 = peg$c0; 542 | } 543 | } else { 544 | peg$currPos = s0; 545 | s0 = peg$c0; 546 | } 547 | 548 | return s0; 549 | } 550 | 551 | function peg$parsemethod() { 552 | var s0, s1, s2; 553 | 554 | s0 = peg$currPos; 555 | if (input.charCodeAt(peg$currPos) === 46) { 556 | s1 = peg$c19; 557 | peg$currPos++; 558 | } else { 559 | s1 = peg$FAILED; 560 | if (peg$silentFails === 0) { peg$fail(peg$c20); } 561 | } 562 | if (s1 !== peg$FAILED) { 563 | s2 = peg$parseidentifier(); 564 | if (s2 !== peg$FAILED) { 565 | peg$reportedPos = s0; 566 | s1 = peg$c21(s2); 567 | s0 = s1; 568 | } else { 569 | peg$currPos = s0; 570 | s0 = peg$c0; 571 | } 572 | } else { 573 | peg$currPos = s0; 574 | s0 = peg$c0; 575 | } 576 | 577 | return s0; 578 | } 579 | 580 | function peg$parsenew() { 581 | var s0, s1, s2; 582 | 583 | s0 = peg$currPos; 584 | if (input.substr(peg$currPos, 3) === peg$c22) { 585 | s1 = peg$c22; 586 | peg$currPos += 3; 587 | } else { 588 | s1 = peg$FAILED; 589 | if (peg$silentFails === 0) { peg$fail(peg$c23); } 590 | } 591 | if (s1 !== peg$FAILED) { 592 | s2 = peg$parsewhitespace(); 593 | if (s2 !== peg$FAILED) { 594 | peg$reportedPos = s0; 595 | s1 = peg$c24(); 596 | s0 = s1; 597 | } else { 598 | peg$currPos = s0; 599 | s0 = peg$c0; 600 | } 601 | } else { 602 | peg$currPos = s0; 603 | s0 = peg$c0; 604 | } 605 | 606 | return s0; 607 | } 608 | 609 | function peg$parseparams() { 610 | var s0, s1, s2, s3, s4, s5, s6; 611 | 612 | s0 = peg$currPos; 613 | s1 = peg$parseob(); 614 | if (s1 !== peg$FAILED) { 615 | s2 = peg$parsecb(); 616 | if (s2 !== peg$FAILED) { 617 | peg$reportedPos = s0; 618 | s1 = peg$c25(); 619 | s0 = s1; 620 | } else { 621 | peg$currPos = s0; 622 | s0 = peg$c0; 623 | } 624 | } else { 625 | peg$currPos = s0; 626 | s0 = peg$c0; 627 | } 628 | if (s0 === peg$FAILED) { 629 | s0 = peg$currPos; 630 | s1 = peg$parseob(); 631 | if (s1 !== peg$FAILED) { 632 | if (input.charCodeAt(peg$currPos) === 91) { 633 | s2 = peg$c15; 634 | peg$currPos++; 635 | } else { 636 | s2 = peg$FAILED; 637 | if (peg$silentFails === 0) { peg$fail(peg$c16); } 638 | } 639 | if (s2 !== peg$FAILED) { 640 | s3 = peg$parsewhitespace(); 641 | if (s3 === peg$FAILED) { 642 | s3 = peg$c1; 643 | } 644 | if (s3 !== peg$FAILED) { 645 | s4 = peg$parseparam(); 646 | if (s4 !== peg$FAILED) { 647 | s5 = peg$parsecs(); 648 | if (s5 !== peg$FAILED) { 649 | s6 = peg$parsecb(); 650 | if (s6 !== peg$FAILED) { 651 | peg$reportedPos = s0; 652 | s1 = peg$c26(s4); 653 | s0 = s1; 654 | } else { 655 | peg$currPos = s0; 656 | s0 = peg$c0; 657 | } 658 | } else { 659 | peg$currPos = s0; 660 | s0 = peg$c0; 661 | } 662 | } else { 663 | peg$currPos = s0; 664 | s0 = peg$c0; 665 | } 666 | } else { 667 | peg$currPos = s0; 668 | s0 = peg$c0; 669 | } 670 | } else { 671 | peg$currPos = s0; 672 | s0 = peg$c0; 673 | } 674 | } else { 675 | peg$currPos = s0; 676 | s0 = peg$c0; 677 | } 678 | if (s0 === peg$FAILED) { 679 | s0 = peg$currPos; 680 | if (input.charCodeAt(peg$currPos) === 40) { 681 | s1 = peg$c11; 682 | peg$currPos++; 683 | } else { 684 | s1 = peg$FAILED; 685 | if (peg$silentFails === 0) { peg$fail(peg$c12); } 686 | } 687 | if (s1 !== peg$FAILED) { 688 | s2 = peg$parsefirstparam(); 689 | if (s2 !== peg$FAILED) { 690 | s3 = peg$parserparams(); 691 | if (s3 !== peg$FAILED) { 692 | s4 = peg$parsecb(); 693 | if (s4 !== peg$FAILED) { 694 | peg$reportedPos = s0; 695 | s1 = peg$c27(s2, s3); 696 | s0 = s1; 697 | } else { 698 | peg$currPos = s0; 699 | s0 = peg$c0; 700 | } 701 | } else { 702 | peg$currPos = s0; 703 | s0 = peg$c0; 704 | } 705 | } else { 706 | peg$currPos = s0; 707 | s0 = peg$c0; 708 | } 709 | } else { 710 | peg$currPos = s0; 711 | s0 = peg$c0; 712 | } 713 | } 714 | } 715 | 716 | return s0; 717 | } 718 | 719 | function peg$parserparams() { 720 | var s0, s1; 721 | 722 | s0 = []; 723 | s1 = peg$parserestparam(); 724 | while (s1 !== peg$FAILED) { 725 | s0.push(s1); 726 | s1 = peg$parserestparam(); 727 | } 728 | 729 | return s0; 730 | } 731 | 732 | function peg$parserestparam() { 733 | var s0; 734 | 735 | s0 = peg$parserestparamMandatory(); 736 | if (s0 === peg$FAILED) { 737 | s0 = peg$parserestparamOptional(); 738 | } 739 | 740 | return s0; 741 | } 742 | 743 | function peg$parserestparamOptional() { 744 | var s0, s1, s2, s3, s4; 745 | 746 | s0 = peg$currPos; 747 | if (input.charCodeAt(peg$currPos) === 91) { 748 | s1 = peg$c15; 749 | peg$currPos++; 750 | } else { 751 | s1 = peg$FAILED; 752 | if (peg$silentFails === 0) { peg$fail(peg$c16); } 753 | } 754 | if (s1 !== peg$FAILED) { 755 | s2 = peg$parsewhitespace(); 756 | if (s2 === peg$FAILED) { 757 | s2 = peg$c1; 758 | } 759 | if (s2 !== peg$FAILED) { 760 | s3 = peg$parserestparamMandatory(); 761 | if (s3 !== peg$FAILED) { 762 | s4 = peg$parsecs(); 763 | if (s4 !== peg$FAILED) { 764 | peg$reportedPos = s0; 765 | s1 = peg$c28(s3); 766 | s0 = s1; 767 | } else { 768 | peg$currPos = s0; 769 | s0 = peg$c0; 770 | } 771 | } else { 772 | peg$currPos = s0; 773 | s0 = peg$c0; 774 | } 775 | } else { 776 | peg$currPos = s0; 777 | s0 = peg$c0; 778 | } 779 | } else { 780 | peg$currPos = s0; 781 | s0 = peg$c0; 782 | } 783 | 784 | return s0; 785 | } 786 | 787 | function peg$parserestparamMandatory() { 788 | var s0, s1, s2, s3; 789 | 790 | s0 = peg$currPos; 791 | if (input.charCodeAt(peg$currPos) === 44) { 792 | s1 = peg$c29; 793 | peg$currPos++; 794 | } else { 795 | s1 = peg$FAILED; 796 | if (peg$silentFails === 0) { peg$fail(peg$c30); } 797 | } 798 | if (s1 !== peg$FAILED) { 799 | s2 = peg$parsewhitespace(); 800 | if (s2 === peg$FAILED) { 801 | s2 = peg$c1; 802 | } 803 | if (s2 !== peg$FAILED) { 804 | s3 = peg$parseparam(); 805 | if (s3 !== peg$FAILED) { 806 | peg$reportedPos = s0; 807 | s1 = peg$c31(s3); 808 | s0 = s1; 809 | } else { 810 | peg$currPos = s0; 811 | s0 = peg$c0; 812 | } 813 | } else { 814 | peg$currPos = s0; 815 | s0 = peg$c0; 816 | } 817 | } else { 818 | peg$currPos = s0; 819 | s0 = peg$c0; 820 | } 821 | 822 | return s0; 823 | } 824 | 825 | function peg$parsefirstparam() { 826 | var s0; 827 | 828 | s0 = peg$parseparam(); 829 | if (s0 === peg$FAILED) { 830 | s0 = peg$parsethisparam(); 831 | } 832 | 833 | return s0; 834 | } 835 | 836 | function peg$parseparam() { 837 | var s0, s1, s2, s3, s4, s5; 838 | 839 | s0 = peg$currPos; 840 | s1 = peg$parseidentifier(); 841 | if (s1 !== peg$FAILED) { 842 | s2 = peg$parsewhitespace(); 843 | if (s2 === peg$FAILED) { 844 | s2 = peg$c1; 845 | } 846 | if (s2 !== peg$FAILED) { 847 | s3 = peg$parsetypeDefinition(); 848 | if (s3 === peg$FAILED) { 849 | s3 = peg$c1; 850 | } 851 | if (s3 !== peg$FAILED) { 852 | if (input.substr(peg$currPos, 3) === peg$c32) { 853 | s4 = peg$c32; 854 | peg$currPos += 3; 855 | } else { 856 | s4 = peg$FAILED; 857 | if (peg$silentFails === 0) { peg$fail(peg$c33); } 858 | } 859 | if (s4 === peg$FAILED) { 860 | s4 = peg$c1; 861 | } 862 | if (s4 !== peg$FAILED) { 863 | s5 = peg$parsewhitespace(); 864 | if (s5 === peg$FAILED) { 865 | s5 = peg$c1; 866 | } 867 | if (s5 !== peg$FAILED) { 868 | peg$reportedPos = s0; 869 | s1 = peg$c34(s1, s3, s4); 870 | s0 = s1; 871 | } else { 872 | peg$currPos = s0; 873 | s0 = peg$c0; 874 | } 875 | } else { 876 | peg$currPos = s0; 877 | s0 = peg$c0; 878 | } 879 | } else { 880 | peg$currPos = s0; 881 | s0 = peg$c0; 882 | } 883 | } else { 884 | peg$currPos = s0; 885 | s0 = peg$c0; 886 | } 887 | } else { 888 | peg$currPos = s0; 889 | s0 = peg$c0; 890 | } 891 | 892 | return s0; 893 | } 894 | 895 | function peg$parsethisparam() { 896 | var s0, s1, s2, s3; 897 | 898 | s0 = peg$currPos; 899 | if (input.charCodeAt(peg$currPos) === 64) { 900 | s1 = peg$c35; 901 | peg$currPos++; 902 | } else { 903 | s1 = peg$FAILED; 904 | if (peg$silentFails === 0) { peg$fail(peg$c36); } 905 | } 906 | if (s1 !== peg$FAILED) { 907 | s2 = peg$parsewhitespace(); 908 | if (s2 === peg$FAILED) { 909 | s2 = peg$c1; 910 | } 911 | if (s2 !== peg$FAILED) { 912 | s3 = peg$parsetypeDefinition(); 913 | if (s3 !== peg$FAILED) { 914 | peg$reportedPos = s0; 915 | s1 = peg$c37(s3); 916 | s0 = s1; 917 | } else { 918 | peg$currPos = s0; 919 | s0 = peg$c0; 920 | } 921 | } else { 922 | peg$currPos = s0; 923 | s0 = peg$c0; 924 | } 925 | } else { 926 | peg$currPos = s0; 927 | s0 = peg$c0; 928 | } 929 | 930 | return s0; 931 | } 932 | 933 | function peg$parsetypeDefinition() { 934 | var s0, s1, s2, s3; 935 | 936 | s0 = peg$currPos; 937 | if (input.charCodeAt(peg$currPos) === 58) { 938 | s1 = peg$c38; 939 | peg$currPos++; 940 | } else { 941 | s1 = peg$FAILED; 942 | if (peg$silentFails === 0) { peg$fail(peg$c39); } 943 | } 944 | if (s1 !== peg$FAILED) { 945 | s2 = peg$parsewhitespace(); 946 | if (s2 === peg$FAILED) { 947 | s2 = peg$c1; 948 | } 949 | if (s2 !== peg$FAILED) { 950 | s3 = peg$parsetype(); 951 | if (s3 !== peg$FAILED) { 952 | peg$reportedPos = s0; 953 | s1 = peg$c40(s3); 954 | s0 = s1; 955 | } else { 956 | peg$currPos = s0; 957 | s0 = peg$c0; 958 | } 959 | } else { 960 | peg$currPos = s0; 961 | s0 = peg$c0; 962 | } 963 | } else { 964 | peg$currPos = s0; 965 | s0 = peg$c0; 966 | } 967 | 968 | return s0; 969 | } 970 | 971 | function peg$parsetype() { 972 | var s0; 973 | 974 | s0 = peg$parsefunctionType(); 975 | if (s0 === peg$FAILED) { 976 | s0 = peg$parsescalarType(); 977 | if (s0 === peg$FAILED) { 978 | s0 = peg$parseunitType(); 979 | } 980 | } 981 | 982 | return s0; 983 | } 984 | 985 | function peg$parseunitType() { 986 | var s0, s1; 987 | 988 | s0 = peg$currPos; 989 | if (input.substr(peg$currPos, 2) === peg$c41) { 990 | s1 = peg$c41; 991 | peg$currPos += 2; 992 | } else { 993 | s1 = peg$FAILED; 994 | if (peg$silentFails === 0) { peg$fail(peg$c42); } 995 | } 996 | if (s1 !== peg$FAILED) { 997 | peg$reportedPos = s0; 998 | s1 = peg$c43(); 999 | } 1000 | s0 = s1; 1001 | 1002 | return s0; 1003 | } 1004 | 1005 | function peg$parsescalarType() { 1006 | var s0; 1007 | 1008 | s0 = peg$parsealternativeType(); 1009 | if (s0 === peg$FAILED) { 1010 | s0 = peg$parsescalarFunction(); 1011 | } 1012 | 1013 | return s0; 1014 | } 1015 | 1016 | function peg$parsescalarFunction() { 1017 | var s0, s1, s2, s3; 1018 | 1019 | s0 = peg$currPos; 1020 | s1 = peg$parseob(); 1021 | if (s1 !== peg$FAILED) { 1022 | s2 = peg$parsefunctionType(); 1023 | if (s2 !== peg$FAILED) { 1024 | s3 = peg$parsecb(); 1025 | if (s3 !== peg$FAILED) { 1026 | peg$reportedPos = s0; 1027 | s1 = peg$c40(s2); 1028 | s0 = s1; 1029 | } else { 1030 | peg$currPos = s0; 1031 | s0 = peg$c0; 1032 | } 1033 | } else { 1034 | peg$currPos = s0; 1035 | s0 = peg$c0; 1036 | } 1037 | } else { 1038 | peg$currPos = s0; 1039 | s0 = peg$c0; 1040 | } 1041 | 1042 | return s0; 1043 | } 1044 | 1045 | function peg$parsealternativeType() { 1046 | var s0, s1, s2; 1047 | 1048 | s0 = peg$currPos; 1049 | s1 = peg$parsevariableType(); 1050 | if (s1 !== peg$FAILED) { 1051 | s2 = peg$parsealternativeRest(); 1052 | if (s2 !== peg$FAILED) { 1053 | peg$reportedPos = s0; 1054 | s1 = peg$c44(s1, s2); 1055 | s0 = s1; 1056 | } else { 1057 | peg$currPos = s0; 1058 | s0 = peg$c0; 1059 | } 1060 | } else { 1061 | peg$currPos = s0; 1062 | s0 = peg$c0; 1063 | } 1064 | 1065 | return s0; 1066 | } 1067 | 1068 | function peg$parsealternativeRest() { 1069 | var s0, s1, s2, s3, s4; 1070 | 1071 | s0 = []; 1072 | s1 = peg$currPos; 1073 | if (input.charCodeAt(peg$currPos) === 124) { 1074 | s2 = peg$c45; 1075 | peg$currPos++; 1076 | } else { 1077 | s2 = peg$FAILED; 1078 | if (peg$silentFails === 0) { peg$fail(peg$c46); } 1079 | } 1080 | if (s2 !== peg$FAILED) { 1081 | s3 = peg$parsewhitespace(); 1082 | if (s3 === peg$FAILED) { 1083 | s3 = peg$c1; 1084 | } 1085 | if (s3 !== peg$FAILED) { 1086 | s4 = peg$parsevariableType(); 1087 | if (s4 !== peg$FAILED) { 1088 | peg$reportedPos = s1; 1089 | s2 = peg$c47(s4); 1090 | s1 = s2; 1091 | } else { 1092 | peg$currPos = s1; 1093 | s1 = peg$c0; 1094 | } 1095 | } else { 1096 | peg$currPos = s1; 1097 | s1 = peg$c0; 1098 | } 1099 | } else { 1100 | peg$currPos = s1; 1101 | s1 = peg$c0; 1102 | } 1103 | while (s1 !== peg$FAILED) { 1104 | s0.push(s1); 1105 | s1 = peg$currPos; 1106 | if (input.charCodeAt(peg$currPos) === 124) { 1107 | s2 = peg$c45; 1108 | peg$currPos++; 1109 | } else { 1110 | s2 = peg$FAILED; 1111 | if (peg$silentFails === 0) { peg$fail(peg$c46); } 1112 | } 1113 | if (s2 !== peg$FAILED) { 1114 | s3 = peg$parsewhitespace(); 1115 | if (s3 === peg$FAILED) { 1116 | s3 = peg$c1; 1117 | } 1118 | if (s3 !== peg$FAILED) { 1119 | s4 = peg$parsevariableType(); 1120 | if (s4 !== peg$FAILED) { 1121 | peg$reportedPos = s1; 1122 | s2 = peg$c47(s4); 1123 | s1 = s2; 1124 | } else { 1125 | peg$currPos = s1; 1126 | s1 = peg$c0; 1127 | } 1128 | } else { 1129 | peg$currPos = s1; 1130 | s1 = peg$c0; 1131 | } 1132 | } else { 1133 | peg$currPos = s1; 1134 | s1 = peg$c0; 1135 | } 1136 | } 1137 | 1138 | return s0; 1139 | } 1140 | 1141 | function peg$parsevariableType() { 1142 | var s0, s1, s2, s3; 1143 | 1144 | s0 = peg$currPos; 1145 | s1 = peg$parseidentifier(); 1146 | if (s1 !== peg$FAILED) { 1147 | s2 = peg$parsewhitespace(); 1148 | if (s2 === peg$FAILED) { 1149 | s2 = peg$c1; 1150 | } 1151 | if (s2 !== peg$FAILED) { 1152 | s3 = peg$parsetypeParameters(); 1153 | if (s3 === peg$FAILED) { 1154 | s3 = peg$c1; 1155 | } 1156 | if (s3 !== peg$FAILED) { 1157 | peg$reportedPos = s0; 1158 | s1 = peg$c48(s1, s3); 1159 | s0 = s1; 1160 | } else { 1161 | peg$currPos = s0; 1162 | s0 = peg$c0; 1163 | } 1164 | } else { 1165 | peg$currPos = s0; 1166 | s0 = peg$c0; 1167 | } 1168 | } else { 1169 | peg$currPos = s0; 1170 | s0 = peg$c0; 1171 | } 1172 | 1173 | return s0; 1174 | } 1175 | 1176 | function peg$parsefunctionType() { 1177 | var s0, s1, s2, s3, s4, s5, s6; 1178 | 1179 | s0 = peg$currPos; 1180 | s1 = peg$parsescalarType(); 1181 | if (s1 === peg$FAILED) { 1182 | s1 = peg$c1; 1183 | } 1184 | if (s1 !== peg$FAILED) { 1185 | s2 = []; 1186 | s3 = peg$currPos; 1187 | if (input.substr(peg$currPos, 2) === peg$c49) { 1188 | s4 = peg$c49; 1189 | peg$currPos += 2; 1190 | } else { 1191 | s4 = peg$FAILED; 1192 | if (peg$silentFails === 0) { peg$fail(peg$c50); } 1193 | } 1194 | if (s4 !== peg$FAILED) { 1195 | s5 = peg$parsewhitespace(); 1196 | if (s5 === peg$FAILED) { 1197 | s5 = peg$c1; 1198 | } 1199 | if (s5 !== peg$FAILED) { 1200 | s6 = peg$parsescalarType(); 1201 | if (s6 === peg$FAILED) { 1202 | s6 = peg$c1; 1203 | } 1204 | if (s6 !== peg$FAILED) { 1205 | peg$reportedPos = s3; 1206 | s4 = peg$c51(s6); 1207 | s3 = s4; 1208 | } else { 1209 | peg$currPos = s3; 1210 | s3 = peg$c0; 1211 | } 1212 | } else { 1213 | peg$currPos = s3; 1214 | s3 = peg$c0; 1215 | } 1216 | } else { 1217 | peg$currPos = s3; 1218 | s3 = peg$c0; 1219 | } 1220 | if (s3 !== peg$FAILED) { 1221 | while (s3 !== peg$FAILED) { 1222 | s2.push(s3); 1223 | s3 = peg$currPos; 1224 | if (input.substr(peg$currPos, 2) === peg$c49) { 1225 | s4 = peg$c49; 1226 | peg$currPos += 2; 1227 | } else { 1228 | s4 = peg$FAILED; 1229 | if (peg$silentFails === 0) { peg$fail(peg$c50); } 1230 | } 1231 | if (s4 !== peg$FAILED) { 1232 | s5 = peg$parsewhitespace(); 1233 | if (s5 === peg$FAILED) { 1234 | s5 = peg$c1; 1235 | } 1236 | if (s5 !== peg$FAILED) { 1237 | s6 = peg$parsescalarType(); 1238 | if (s6 === peg$FAILED) { 1239 | s6 = peg$c1; 1240 | } 1241 | if (s6 !== peg$FAILED) { 1242 | peg$reportedPos = s3; 1243 | s4 = peg$c51(s6); 1244 | s3 = s4; 1245 | } else { 1246 | peg$currPos = s3; 1247 | s3 = peg$c0; 1248 | } 1249 | } else { 1250 | peg$currPos = s3; 1251 | s3 = peg$c0; 1252 | } 1253 | } else { 1254 | peg$currPos = s3; 1255 | s3 = peg$c0; 1256 | } 1257 | } 1258 | } else { 1259 | s2 = peg$c0; 1260 | } 1261 | if (s2 !== peg$FAILED) { 1262 | peg$reportedPos = s0; 1263 | s1 = peg$c52(s1, s2); 1264 | s0 = s1; 1265 | } else { 1266 | peg$currPos = s0; 1267 | s0 = peg$c0; 1268 | } 1269 | } else { 1270 | peg$currPos = s0; 1271 | s0 = peg$c0; 1272 | } 1273 | 1274 | return s0; 1275 | } 1276 | 1277 | function peg$parsetypeParameters() { 1278 | var s0, s1, s2, s3; 1279 | 1280 | s0 = peg$currPos; 1281 | s1 = peg$parseos(); 1282 | if (s1 !== peg$FAILED) { 1283 | s2 = peg$parsetype(); 1284 | if (s2 !== peg$FAILED) { 1285 | s3 = peg$parsecs(); 1286 | if (s3 !== peg$FAILED) { 1287 | peg$reportedPos = s0; 1288 | s1 = peg$c40(s2); 1289 | s0 = s1; 1290 | } else { 1291 | peg$currPos = s0; 1292 | s0 = peg$c0; 1293 | } 1294 | } else { 1295 | peg$currPos = s0; 1296 | s0 = peg$c0; 1297 | } 1298 | } else { 1299 | peg$currPos = s0; 1300 | s0 = peg$c0; 1301 | } 1302 | 1303 | return s0; 1304 | } 1305 | 1306 | peg$result = peg$startRuleFunction(); 1307 | 1308 | if (peg$result !== peg$FAILED && peg$currPos === input.length) { 1309 | return peg$result; 1310 | } else { 1311 | if (peg$result !== peg$FAILED && peg$currPos < input.length) { 1312 | peg$fail({ type: "end", description: "end of input" }); 1313 | } 1314 | 1315 | throw peg$buildException(null, peg$maxFailExpected, peg$maxFailPos); 1316 | } 1317 | } 1318 | 1319 | return { 1320 | SyntaxError: SyntaxError, 1321 | parse: parse 1322 | }; 1323 | })(); 1324 | -------------------------------------------------------------------------------- /lib/underscore.js: -------------------------------------------------------------------------------- 1 | // Underscore.js 1.5.2 2 | // http://underscorejs.org 3 | // (c) 2009-2013 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors 4 | // Underscore may be freely distributed under the MIT license. 5 | 6 | (function() { 7 | 8 | // Baseline setup 9 | // -------------- 10 | 11 | // Establish the root object, `window` in the browser, or `exports` on the server. 12 | var root = this; 13 | 14 | // Save the previous value of the `_` variable. 15 | var previousUnderscore = root._; 16 | 17 | // Establish the object that gets returned to break out of a loop iteration. 18 | var breaker = {}; 19 | 20 | // Save bytes in the minified (but not gzipped) version: 21 | var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = Function.prototype; 22 | 23 | // Create quick reference variables for speed access to core prototypes. 24 | var 25 | push = ArrayProto.push, 26 | slice = ArrayProto.slice, 27 | concat = ArrayProto.concat, 28 | toString = ObjProto.toString, 29 | hasOwnProperty = ObjProto.hasOwnProperty; 30 | 31 | // All **ECMAScript 5** native function implementations that we hope to use 32 | // are declared here. 33 | var 34 | nativeForEach = ArrayProto.forEach, 35 | nativeMap = ArrayProto.map, 36 | nativeReduce = ArrayProto.reduce, 37 | nativeReduceRight = ArrayProto.reduceRight, 38 | nativeFilter = ArrayProto.filter, 39 | nativeEvery = ArrayProto.every, 40 | nativeSome = ArrayProto.some, 41 | nativeIndexOf = ArrayProto.indexOf, 42 | nativeLastIndexOf = ArrayProto.lastIndexOf, 43 | nativeIsArray = Array.isArray, 44 | nativeKeys = Object.keys, 45 | nativeBind = FuncProto.bind; 46 | 47 | // Create a safe reference to the Underscore object for use below. 48 | var _ = function(obj) { 49 | if (obj instanceof _) return obj; 50 | if (!(this instanceof _)) return new _(obj); 51 | this._wrapped = obj; 52 | }; 53 | 54 | // Export the Underscore object for **Node.js**, with 55 | // backwards-compatibility for the old `require()` API. If we're in 56 | // the browser, add `_` as a global object via a string identifier, 57 | // for Closure Compiler "advanced" mode. 58 | if (typeof exports !== 'undefined') { 59 | if (typeof module !== 'undefined' && module.exports) { 60 | exports = module.exports = _; 61 | } 62 | exports._ = _; 63 | } else { 64 | root._ = _; 65 | } 66 | 67 | // Current version. 68 | _.VERSION = '1.5.2'; 69 | 70 | // Collection Functions 71 | // -------------------- 72 | 73 | // The cornerstone, an `each` implementation, aka `forEach`. 74 | // Handles objects with the built-in `forEach`, arrays, and raw objects. 75 | // Delegates to **ECMAScript 5**'s native `forEach` if available. 76 | var each = _.each = _.forEach = function(obj, iterator, context) { 77 | if (obj == null) return; 78 | if (nativeForEach && obj.forEach === nativeForEach) { 79 | obj.forEach(iterator, context); 80 | } else if (obj.length === +obj.length) { 81 | for (var i = 0, length = obj.length; i < length; i++) { 82 | if (iterator.call(context, obj[i], i, obj) === breaker) return; 83 | } 84 | } else { 85 | var keys = _.keys(obj); 86 | for (var i = 0, length = keys.length; i < length; i++) { 87 | if (iterator.call(context, obj[keys[i]], keys[i], obj) === breaker) return; 88 | } 89 | } 90 | }; 91 | 92 | // Return the results of applying the iterator to each element. 93 | // Delegates to **ECMAScript 5**'s native `map` if available. 94 | _.map = _.collect = function(obj, iterator, context) { 95 | var results = []; 96 | if (obj == null) return results; 97 | if (nativeMap && obj.map === nativeMap) return obj.map(iterator, context); 98 | each(obj, function(value, index, list) { 99 | results.push(iterator.call(context, value, index, list)); 100 | }); 101 | return results; 102 | }; 103 | 104 | var reduceError = 'Reduce of empty array with no initial value'; 105 | 106 | // **Reduce** builds up a single result from a list of values, aka `inject`, 107 | // or `foldl`. Delegates to **ECMAScript 5**'s native `reduce` if available. 108 | _.reduce = _.foldl = _.inject = function(obj, iterator, memo, context) { 109 | var initial = arguments.length > 2; 110 | if (obj == null) obj = []; 111 | if (nativeReduce && obj.reduce === nativeReduce) { 112 | if (context) iterator = _.bind(iterator, context); 113 | return initial ? obj.reduce(iterator, memo) : obj.reduce(iterator); 114 | } 115 | each(obj, function(value, index, list) { 116 | if (!initial) { 117 | memo = value; 118 | initial = true; 119 | } else { 120 | memo = iterator.call(context, memo, value, index, list); 121 | } 122 | }); 123 | if (!initial) throw new TypeError(reduceError); 124 | return memo; 125 | }; 126 | 127 | // The right-associative version of reduce, also known as `foldr`. 128 | // Delegates to **ECMAScript 5**'s native `reduceRight` if available. 129 | _.reduceRight = _.foldr = function(obj, iterator, memo, context) { 130 | var initial = arguments.length > 2; 131 | if (obj == null) obj = []; 132 | if (nativeReduceRight && obj.reduceRight === nativeReduceRight) { 133 | if (context) iterator = _.bind(iterator, context); 134 | return initial ? obj.reduceRight(iterator, memo) : obj.reduceRight(iterator); 135 | } 136 | var length = obj.length; 137 | if (length !== +length) { 138 | var keys = _.keys(obj); 139 | length = keys.length; 140 | } 141 | each(obj, function(value, index, list) { 142 | index = keys ? keys[--length] : --length; 143 | if (!initial) { 144 | memo = obj[index]; 145 | initial = true; 146 | } else { 147 | memo = iterator.call(context, memo, obj[index], index, list); 148 | } 149 | }); 150 | if (!initial) throw new TypeError(reduceError); 151 | return memo; 152 | }; 153 | 154 | // Return the first value which passes a truth test. Aliased as `detect`. 155 | _.find = _.detect = function(obj, iterator, context) { 156 | var result; 157 | any(obj, function(value, index, list) { 158 | if (iterator.call(context, value, index, list)) { 159 | result = value; 160 | return true; 161 | } 162 | }); 163 | return result; 164 | }; 165 | 166 | // Return all the elements that pass a truth test. 167 | // Delegates to **ECMAScript 5**'s native `filter` if available. 168 | // Aliased as `select`. 169 | _.filter = _.select = function(obj, iterator, context) { 170 | var results = []; 171 | if (obj == null) return results; 172 | if (nativeFilter && obj.filter === nativeFilter) return obj.filter(iterator, context); 173 | each(obj, function(value, index, list) { 174 | if (iterator.call(context, value, index, list)) results.push(value); 175 | }); 176 | return results; 177 | }; 178 | 179 | // Return all the elements for which a truth test fails. 180 | _.reject = function(obj, iterator, context) { 181 | return _.filter(obj, function(value, index, list) { 182 | return !iterator.call(context, value, index, list); 183 | }, context); 184 | }; 185 | 186 | // Determine whether all of the elements match a truth test. 187 | // Delegates to **ECMAScript 5**'s native `every` if available. 188 | // Aliased as `all`. 189 | _.every = _.all = function(obj, iterator, context) { 190 | iterator || (iterator = _.identity); 191 | var result = true; 192 | if (obj == null) return result; 193 | if (nativeEvery && obj.every === nativeEvery) return obj.every(iterator, context); 194 | each(obj, function(value, index, list) { 195 | if (!(result = result && iterator.call(context, value, index, list))) return breaker; 196 | }); 197 | return !!result; 198 | }; 199 | 200 | // Determine if at least one element in the object matches a truth test. 201 | // Delegates to **ECMAScript 5**'s native `some` if available. 202 | // Aliased as `any`. 203 | var any = _.some = _.any = function(obj, iterator, context) { 204 | iterator || (iterator = _.identity); 205 | var result = false; 206 | if (obj == null) return result; 207 | if (nativeSome && obj.some === nativeSome) return obj.some(iterator, context); 208 | each(obj, function(value, index, list) { 209 | if (result || (result = iterator.call(context, value, index, list))) return breaker; 210 | }); 211 | return !!result; 212 | }; 213 | 214 | // Determine if the array or object contains a given value (using `===`). 215 | // Aliased as `include`. 216 | _.contains = _.include = function(obj, target) { 217 | if (obj == null) return false; 218 | if (nativeIndexOf && obj.indexOf === nativeIndexOf) return obj.indexOf(target) != -1; 219 | return any(obj, function(value) { 220 | return value === target; 221 | }); 222 | }; 223 | 224 | // Invoke a method (with arguments) on every item in a collection. 225 | _.invoke = function(obj, method) { 226 | var args = slice.call(arguments, 2); 227 | var isFunc = _.isFunction(method); 228 | return _.map(obj, function(value) { 229 | return (isFunc ? method : value[method]).apply(value, args); 230 | }); 231 | }; 232 | 233 | // Convenience version of a common use case of `map`: fetching a property. 234 | _.pluck = function(obj, key) { 235 | return _.map(obj, function(value){ return value[key]; }); 236 | }; 237 | 238 | // Convenience version of a common use case of `filter`: selecting only objects 239 | // containing specific `key:value` pairs. 240 | _.where = function(obj, attrs, first) { 241 | if (_.isEmpty(attrs)) return first ? void 0 : []; 242 | return _[first ? 'find' : 'filter'](obj, function(value) { 243 | for (var key in attrs) { 244 | if (attrs[key] !== value[key]) return false; 245 | } 246 | return true; 247 | }); 248 | }; 249 | 250 | // Convenience version of a common use case of `find`: getting the first object 251 | // containing specific `key:value` pairs. 252 | _.findWhere = function(obj, attrs) { 253 | return _.where(obj, attrs, true); 254 | }; 255 | 256 | // Return the maximum element or (element-based computation). 257 | // Can't optimize arrays of integers longer than 65,535 elements. 258 | // See [WebKit Bug 80797](https://bugs.webkit.org/show_bug.cgi?id=80797) 259 | _.max = function(obj, iterator, context) { 260 | if (!iterator && _.isArray(obj) && obj[0] === +obj[0] && obj.length < 65535) { 261 | return Math.max.apply(Math, obj); 262 | } 263 | if (!iterator && _.isEmpty(obj)) return -Infinity; 264 | var result = {computed : -Infinity, value: -Infinity}; 265 | each(obj, function(value, index, list) { 266 | var computed = iterator ? iterator.call(context, value, index, list) : value; 267 | computed > result.computed && (result = {value : value, computed : computed}); 268 | }); 269 | return result.value; 270 | }; 271 | 272 | // Return the minimum element (or element-based computation). 273 | _.min = function(obj, iterator, context) { 274 | if (!iterator && _.isArray(obj) && obj[0] === +obj[0] && obj.length < 65535) { 275 | return Math.min.apply(Math, obj); 276 | } 277 | if (!iterator && _.isEmpty(obj)) return Infinity; 278 | var result = {computed : Infinity, value: Infinity}; 279 | each(obj, function(value, index, list) { 280 | var computed = iterator ? iterator.call(context, value, index, list) : value; 281 | computed < result.computed && (result = {value : value, computed : computed}); 282 | }); 283 | return result.value; 284 | }; 285 | 286 | // Shuffle an array, using the modern version of the 287 | // [Fisher-Yates shuffle](http://en.wikipedia.org/wiki/Fisher–Yates_shuffle). 288 | _.shuffle = function(obj) { 289 | var rand; 290 | var index = 0; 291 | var shuffled = []; 292 | each(obj, function(value) { 293 | rand = _.random(index++); 294 | shuffled[index - 1] = shuffled[rand]; 295 | shuffled[rand] = value; 296 | }); 297 | return shuffled; 298 | }; 299 | 300 | // Sample **n** random values from an array. 301 | // If **n** is not specified, returns a single random element from the array. 302 | // The internal `guard` argument allows it to work with `map`. 303 | _.sample = function(obj, n, guard) { 304 | if (arguments.length < 2 || guard) { 305 | return obj[_.random(obj.length - 1)]; 306 | } 307 | return _.shuffle(obj).slice(0, Math.max(0, n)); 308 | }; 309 | 310 | // An internal function to generate lookup iterators. 311 | var lookupIterator = function(value) { 312 | return _.isFunction(value) ? value : function(obj){ return obj[value]; }; 313 | }; 314 | 315 | // Sort the object's values by a criterion produced by an iterator. 316 | _.sortBy = function(obj, value, context) { 317 | var iterator = lookupIterator(value); 318 | return _.pluck(_.map(obj, function(value, index, list) { 319 | return { 320 | value: value, 321 | index: index, 322 | criteria: iterator.call(context, value, index, list) 323 | }; 324 | }).sort(function(left, right) { 325 | var a = left.criteria; 326 | var b = right.criteria; 327 | if (a !== b) { 328 | if (a > b || a === void 0) return 1; 329 | if (a < b || b === void 0) return -1; 330 | } 331 | return left.index - right.index; 332 | }), 'value'); 333 | }; 334 | 335 | // An internal function used for aggregate "group by" operations. 336 | var group = function(behavior) { 337 | return function(obj, value, context) { 338 | var result = {}; 339 | var iterator = value == null ? _.identity : lookupIterator(value); 340 | each(obj, function(value, index) { 341 | var key = iterator.call(context, value, index, obj); 342 | behavior(result, key, value); 343 | }); 344 | return result; 345 | }; 346 | }; 347 | 348 | // Groups the object's values by a criterion. Pass either a string attribute 349 | // to group by, or a function that returns the criterion. 350 | _.groupBy = group(function(result, key, value) { 351 | (_.has(result, key) ? result[key] : (result[key] = [])).push(value); 352 | }); 353 | 354 | // Indexes the object's values by a criterion, similar to `groupBy`, but for 355 | // when you know that your index values will be unique. 356 | _.indexBy = group(function(result, key, value) { 357 | result[key] = value; 358 | }); 359 | 360 | // Counts instances of an object that group by a certain criterion. Pass 361 | // either a string attribute to count by, or a function that returns the 362 | // criterion. 363 | _.countBy = group(function(result, key) { 364 | _.has(result, key) ? result[key]++ : result[key] = 1; 365 | }); 366 | 367 | // Use a comparator function to figure out the smallest index at which 368 | // an object should be inserted so as to maintain order. Uses binary search. 369 | _.sortedIndex = function(array, obj, iterator, context) { 370 | iterator = iterator == null ? _.identity : lookupIterator(iterator); 371 | var value = iterator.call(context, obj); 372 | var low = 0, high = array.length; 373 | while (low < high) { 374 | var mid = (low + high) >>> 1; 375 | iterator.call(context, array[mid]) < value ? low = mid + 1 : high = mid; 376 | } 377 | return low; 378 | }; 379 | 380 | // Safely create a real, live array from anything iterable. 381 | _.toArray = function(obj) { 382 | if (!obj) return []; 383 | if (_.isArray(obj)) return slice.call(obj); 384 | if (obj.length === +obj.length) return _.map(obj, _.identity); 385 | return _.values(obj); 386 | }; 387 | 388 | // Return the number of elements in an object. 389 | _.size = function(obj) { 390 | if (obj == null) return 0; 391 | return (obj.length === +obj.length) ? obj.length : _.keys(obj).length; 392 | }; 393 | 394 | // Array Functions 395 | // --------------- 396 | 397 | // Get the first element of an array. Passing **n** will return the first N 398 | // values in the array. Aliased as `head` and `take`. The **guard** check 399 | // allows it to work with `_.map`. 400 | _.first = _.head = _.take = function(array, n, guard) { 401 | if (array == null) return void 0; 402 | return (n == null) || guard ? array[0] : slice.call(array, 0, n); 403 | }; 404 | 405 | // Returns everything but the last entry of the array. Especially useful on 406 | // the arguments object. Passing **n** will return all the values in 407 | // the array, excluding the last N. The **guard** check allows it to work with 408 | // `_.map`. 409 | _.initial = function(array, n, guard) { 410 | return slice.call(array, 0, array.length - ((n == null) || guard ? 1 : n)); 411 | }; 412 | 413 | // Get the last element of an array. Passing **n** will return the last N 414 | // values in the array. The **guard** check allows it to work with `_.map`. 415 | _.last = function(array, n, guard) { 416 | if (array == null) return void 0; 417 | if ((n == null) || guard) { 418 | return array[array.length - 1]; 419 | } else { 420 | return slice.call(array, Math.max(array.length - n, 0)); 421 | } 422 | }; 423 | 424 | // Returns everything but the first entry of the array. Aliased as `tail` and `drop`. 425 | // Especially useful on the arguments object. Passing an **n** will return 426 | // the rest N values in the array. The **guard** 427 | // check allows it to work with `_.map`. 428 | _.rest = _.tail = _.drop = function(array, n, guard) { 429 | return slice.call(array, (n == null) || guard ? 1 : n); 430 | }; 431 | 432 | // Trim out all falsy values from an array. 433 | _.compact = function(array) { 434 | return _.filter(array, _.identity); 435 | }; 436 | 437 | // Internal implementation of a recursive `flatten` function. 438 | var flatten = function(input, shallow, output) { 439 | if (shallow && _.every(input, _.isArray)) { 440 | return concat.apply(output, input); 441 | } 442 | each(input, function(value) { 443 | if (_.isArray(value) || _.isArguments(value)) { 444 | shallow ? push.apply(output, value) : flatten(value, shallow, output); 445 | } else { 446 | output.push(value); 447 | } 448 | }); 449 | return output; 450 | }; 451 | 452 | // Flatten out an array, either recursively (by default), or just one level. 453 | _.flatten = function(array, shallow) { 454 | return flatten(array, shallow, []); 455 | }; 456 | 457 | // Return a version of the array that does not contain the specified value(s). 458 | _.without = function(array) { 459 | return _.difference(array, slice.call(arguments, 1)); 460 | }; 461 | 462 | // Produce a duplicate-free version of the array. If the array has already 463 | // been sorted, you have the option of using a faster algorithm. 464 | // Aliased as `unique`. 465 | _.uniq = _.unique = function(array, isSorted, iterator, context) { 466 | if (_.isFunction(isSorted)) { 467 | context = iterator; 468 | iterator = isSorted; 469 | isSorted = false; 470 | } 471 | var initial = iterator ? _.map(array, iterator, context) : array; 472 | var results = []; 473 | var seen = []; 474 | each(initial, function(value, index) { 475 | if (isSorted ? (!index || seen[seen.length - 1] !== value) : !_.contains(seen, value)) { 476 | seen.push(value); 477 | results.push(array[index]); 478 | } 479 | }); 480 | return results; 481 | }; 482 | 483 | // Produce an array that contains the union: each distinct element from all of 484 | // the passed-in arrays. 485 | _.union = function() { 486 | return _.uniq(_.flatten(arguments, true)); 487 | }; 488 | 489 | // Produce an array that contains every item shared between all the 490 | // passed-in arrays. 491 | _.intersection = function(array) { 492 | var rest = slice.call(arguments, 1); 493 | return _.filter(_.uniq(array), function(item) { 494 | return _.every(rest, function(other) { 495 | return _.indexOf(other, item) >= 0; 496 | }); 497 | }); 498 | }; 499 | 500 | // Take the difference between one array and a number of other arrays. 501 | // Only the elements present in just the first array will remain. 502 | _.difference = function(array) { 503 | var rest = concat.apply(ArrayProto, slice.call(arguments, 1)); 504 | return _.filter(array, function(value){ return !_.contains(rest, value); }); 505 | }; 506 | 507 | // Zip together multiple lists into a single array -- elements that share 508 | // an index go together. 509 | _.zip = function() { 510 | var length = _.max(_.pluck(arguments, "length").concat(0)); 511 | var results = new Array(length); 512 | for (var i = 0; i < length; i++) { 513 | results[i] = _.pluck(arguments, '' + i); 514 | } 515 | return results; 516 | }; 517 | 518 | // Converts lists into objects. Pass either a single array of `[key, value]` 519 | // pairs, or two parallel arrays of the same length -- one of keys, and one of 520 | // the corresponding values. 521 | _.object = function(list, values) { 522 | if (list == null) return {}; 523 | var result = {}; 524 | for (var i = 0, length = list.length; i < length; i++) { 525 | if (values) { 526 | result[list[i]] = values[i]; 527 | } else { 528 | result[list[i][0]] = list[i][1]; 529 | } 530 | } 531 | return result; 532 | }; 533 | 534 | // If the browser doesn't supply us with indexOf (I'm looking at you, **MSIE**), 535 | // we need this function. Return the position of the first occurrence of an 536 | // item in an array, or -1 if the item is not included in the array. 537 | // Delegates to **ECMAScript 5**'s native `indexOf` if available. 538 | // If the array is large and already in sort order, pass `true` 539 | // for **isSorted** to use binary search. 540 | _.indexOf = function(array, item, isSorted) { 541 | if (array == null) return -1; 542 | var i = 0, length = array.length; 543 | if (isSorted) { 544 | if (typeof isSorted == 'number') { 545 | i = (isSorted < 0 ? Math.max(0, length + isSorted) : isSorted); 546 | } else { 547 | i = _.sortedIndex(array, item); 548 | return array[i] === item ? i : -1; 549 | } 550 | } 551 | if (nativeIndexOf && array.indexOf === nativeIndexOf) return array.indexOf(item, isSorted); 552 | for (; i < length; i++) if (array[i] === item) return i; 553 | return -1; 554 | }; 555 | 556 | // Delegates to **ECMAScript 5**'s native `lastIndexOf` if available. 557 | _.lastIndexOf = function(array, item, from) { 558 | if (array == null) return -1; 559 | var hasIndex = from != null; 560 | if (nativeLastIndexOf && array.lastIndexOf === nativeLastIndexOf) { 561 | return hasIndex ? array.lastIndexOf(item, from) : array.lastIndexOf(item); 562 | } 563 | var i = (hasIndex ? from : array.length); 564 | while (i--) if (array[i] === item) return i; 565 | return -1; 566 | }; 567 | 568 | // Generate an integer Array containing an arithmetic progression. A port of 569 | // the native Python `range()` function. See 570 | // [the Python documentation](http://docs.python.org/library/functions.html#range). 571 | _.range = function(start, stop, step) { 572 | if (arguments.length <= 1) { 573 | stop = start || 0; 574 | start = 0; 575 | } 576 | step = arguments[2] || 1; 577 | 578 | var length = Math.max(Math.ceil((stop - start) / step), 0); 579 | var idx = 0; 580 | var range = new Array(length); 581 | 582 | while(idx < length) { 583 | range[idx++] = start; 584 | start += step; 585 | } 586 | 587 | return range; 588 | }; 589 | 590 | // Function (ahem) Functions 591 | // ------------------ 592 | 593 | // Reusable constructor function for prototype setting. 594 | var ctor = function(){}; 595 | 596 | // Create a function bound to a given object (assigning `this`, and arguments, 597 | // optionally). Delegates to **ECMAScript 5**'s native `Function.bind` if 598 | // available. 599 | _.bind = function(func, context) { 600 | var args, bound; 601 | if (nativeBind && func.bind === nativeBind) return nativeBind.apply(func, slice.call(arguments, 1)); 602 | if (!_.isFunction(func)) throw new TypeError; 603 | args = slice.call(arguments, 2); 604 | return bound = function() { 605 | if (!(this instanceof bound)) return func.apply(context, args.concat(slice.call(arguments))); 606 | ctor.prototype = func.prototype; 607 | var self = new ctor; 608 | ctor.prototype = null; 609 | var result = func.apply(self, args.concat(slice.call(arguments))); 610 | if (Object(result) === result) return result; 611 | return self; 612 | }; 613 | }; 614 | 615 | // Partially apply a function by creating a version that has had some of its 616 | // arguments pre-filled, without changing its dynamic `this` context. 617 | _.partial = function(func) { 618 | var args = slice.call(arguments, 1); 619 | return function() { 620 | return func.apply(this, args.concat(slice.call(arguments))); 621 | }; 622 | }; 623 | 624 | // Bind all of an object's methods to that object. Useful for ensuring that 625 | // all callbacks defined on an object belong to it. 626 | _.bindAll = function(obj) { 627 | var funcs = slice.call(arguments, 1); 628 | if (funcs.length === 0) throw new Error("bindAll must be passed function names"); 629 | each(funcs, function(f) { obj[f] = _.bind(obj[f], obj); }); 630 | return obj; 631 | }; 632 | 633 | // Memoize an expensive function by storing its results. 634 | _.memoize = function(func, hasher) { 635 | var memo = {}; 636 | hasher || (hasher = _.identity); 637 | return function() { 638 | var key = hasher.apply(this, arguments); 639 | return _.has(memo, key) ? memo[key] : (memo[key] = func.apply(this, arguments)); 640 | }; 641 | }; 642 | 643 | // Delays a function for the given number of milliseconds, and then calls 644 | // it with the arguments supplied. 645 | _.delay = function(func, wait) { 646 | var args = slice.call(arguments, 2); 647 | return setTimeout(function(){ return func.apply(null, args); }, wait); 648 | }; 649 | 650 | // Defers a function, scheduling it to run after the current call stack has 651 | // cleared. 652 | _.defer = function(func) { 653 | return _.delay.apply(_, [func, 1].concat(slice.call(arguments, 1))); 654 | }; 655 | 656 | // Returns a function, that, when invoked, will only be triggered at most once 657 | // during a given window of time. Normally, the throttled function will run 658 | // as much as it can, without ever going more than once per `wait` duration; 659 | // but if you'd like to disable the execution on the leading edge, pass 660 | // `{leading: false}`. To disable execution on the trailing edge, ditto. 661 | _.throttle = function(func, wait, options) { 662 | var context, args, result; 663 | var timeout = null; 664 | var previous = 0; 665 | options || (options = {}); 666 | var later = function() { 667 | previous = options.leading === false ? 0 : new Date; 668 | timeout = null; 669 | result = func.apply(context, args); 670 | }; 671 | return function() { 672 | var now = new Date; 673 | if (!previous && options.leading === false) previous = now; 674 | var remaining = wait - (now - previous); 675 | context = this; 676 | args = arguments; 677 | if (remaining <= 0) { 678 | clearTimeout(timeout); 679 | timeout = null; 680 | previous = now; 681 | result = func.apply(context, args); 682 | } else if (!timeout && options.trailing !== false) { 683 | timeout = setTimeout(later, remaining); 684 | } 685 | return result; 686 | }; 687 | }; 688 | 689 | // Returns a function, that, as long as it continues to be invoked, will not 690 | // be triggered. The function will be called after it stops being called for 691 | // N milliseconds. If `immediate` is passed, trigger the function on the 692 | // leading edge, instead of the trailing. 693 | _.debounce = function(func, wait, immediate) { 694 | var timeout, args, context, timestamp, result; 695 | return function() { 696 | context = this; 697 | args = arguments; 698 | timestamp = new Date(); 699 | var later = function() { 700 | var last = (new Date()) - timestamp; 701 | if (last < wait) { 702 | timeout = setTimeout(later, wait - last); 703 | } else { 704 | timeout = null; 705 | if (!immediate) result = func.apply(context, args); 706 | } 707 | }; 708 | var callNow = immediate && !timeout; 709 | if (!timeout) { 710 | timeout = setTimeout(later, wait); 711 | } 712 | if (callNow) result = func.apply(context, args); 713 | return result; 714 | }; 715 | }; 716 | 717 | // Returns a function that will be executed at most one time, no matter how 718 | // often you call it. Useful for lazy initialization. 719 | _.once = function(func) { 720 | var ran = false, memo; 721 | return function() { 722 | if (ran) return memo; 723 | ran = true; 724 | memo = func.apply(this, arguments); 725 | func = null; 726 | return memo; 727 | }; 728 | }; 729 | 730 | // Returns the first function passed as an argument to the second, 731 | // allowing you to adjust arguments, run code before and after, and 732 | // conditionally execute the original function. 733 | _.wrap = function(func, wrapper) { 734 | return function() { 735 | var args = [func]; 736 | push.apply(args, arguments); 737 | return wrapper.apply(this, args); 738 | }; 739 | }; 740 | 741 | // Returns a function that is the composition of a list of functions, each 742 | // consuming the return value of the function that follows. 743 | _.compose = function() { 744 | var funcs = arguments; 745 | return function() { 746 | var args = arguments; 747 | for (var i = funcs.length - 1; i >= 0; i--) { 748 | args = [funcs[i].apply(this, args)]; 749 | } 750 | return args[0]; 751 | }; 752 | }; 753 | 754 | // Returns a function that will only be executed after being called N times. 755 | _.after = function(times, func) { 756 | return function() { 757 | if (--times < 1) { 758 | return func.apply(this, arguments); 759 | } 760 | }; 761 | }; 762 | 763 | // Object Functions 764 | // ---------------- 765 | 766 | // Retrieve the names of an object's properties. 767 | // Delegates to **ECMAScript 5**'s native `Object.keys` 768 | _.keys = nativeKeys || function(obj) { 769 | if (obj !== Object(obj)) throw new TypeError('Invalid object'); 770 | var keys = []; 771 | for (var key in obj) if (_.has(obj, key)) keys.push(key); 772 | return keys; 773 | }; 774 | 775 | // Retrieve the values of an object's properties. 776 | _.values = function(obj) { 777 | var keys = _.keys(obj); 778 | var length = keys.length; 779 | var values = new Array(length); 780 | for (var i = 0; i < length; i++) { 781 | values[i] = obj[keys[i]]; 782 | } 783 | return values; 784 | }; 785 | 786 | // Convert an object into a list of `[key, value]` pairs. 787 | _.pairs = function(obj) { 788 | var keys = _.keys(obj); 789 | var length = keys.length; 790 | var pairs = new Array(length); 791 | for (var i = 0; i < length; i++) { 792 | pairs[i] = [keys[i], obj[keys[i]]]; 793 | } 794 | return pairs; 795 | }; 796 | 797 | // Invert the keys and values of an object. The values must be serializable. 798 | _.invert = function(obj) { 799 | var result = {}; 800 | var keys = _.keys(obj); 801 | for (var i = 0, length = keys.length; i < length; i++) { 802 | result[obj[keys[i]]] = keys[i]; 803 | } 804 | return result; 805 | }; 806 | 807 | // Return a sorted list of the function names available on the object. 808 | // Aliased as `methods` 809 | _.functions = _.methods = function(obj) { 810 | var names = []; 811 | for (var key in obj) { 812 | if (_.isFunction(obj[key])) names.push(key); 813 | } 814 | return names.sort(); 815 | }; 816 | 817 | // Extend a given object with all the properties in passed-in object(s). 818 | _.extend = function(obj) { 819 | each(slice.call(arguments, 1), function(source) { 820 | if (source) { 821 | for (var prop in source) { 822 | obj[prop] = source[prop]; 823 | } 824 | } 825 | }); 826 | return obj; 827 | }; 828 | 829 | // Return a copy of the object only containing the whitelisted properties. 830 | _.pick = function(obj) { 831 | var copy = {}; 832 | var keys = concat.apply(ArrayProto, slice.call(arguments, 1)); 833 | each(keys, function(key) { 834 | if (key in obj) copy[key] = obj[key]; 835 | }); 836 | return copy; 837 | }; 838 | 839 | // Return a copy of the object without the blacklisted properties. 840 | _.omit = function(obj) { 841 | var copy = {}; 842 | var keys = concat.apply(ArrayProto, slice.call(arguments, 1)); 843 | for (var key in obj) { 844 | if (!_.contains(keys, key)) copy[key] = obj[key]; 845 | } 846 | return copy; 847 | }; 848 | 849 | // Fill in a given object with default properties. 850 | _.defaults = function(obj) { 851 | each(slice.call(arguments, 1), function(source) { 852 | if (source) { 853 | for (var prop in source) { 854 | if (obj[prop] === void 0) obj[prop] = source[prop]; 855 | } 856 | } 857 | }); 858 | return obj; 859 | }; 860 | 861 | // Create a (shallow-cloned) duplicate of an object. 862 | _.clone = function(obj) { 863 | if (!_.isObject(obj)) return obj; 864 | return _.isArray(obj) ? obj.slice() : _.extend({}, obj); 865 | }; 866 | 867 | // Invokes interceptor with the obj, and then returns obj. 868 | // The primary purpose of this method is to "tap into" a method chain, in 869 | // order to perform operations on intermediate results within the chain. 870 | _.tap = function(obj, interceptor) { 871 | interceptor(obj); 872 | return obj; 873 | }; 874 | 875 | // Internal recursive comparison function for `isEqual`. 876 | var eq = function(a, b, aStack, bStack) { 877 | // Identical objects are equal. `0 === -0`, but they aren't identical. 878 | // See the [Harmony `egal` proposal](http://wiki.ecmascript.org/doku.php?id=harmony:egal). 879 | if (a === b) return a !== 0 || 1 / a == 1 / b; 880 | // A strict comparison is necessary because `null == undefined`. 881 | if (a == null || b == null) return a === b; 882 | // Unwrap any wrapped objects. 883 | if (a instanceof _) a = a._wrapped; 884 | if (b instanceof _) b = b._wrapped; 885 | // Compare `[[Class]]` names. 886 | var className = toString.call(a); 887 | if (className != toString.call(b)) return false; 888 | switch (className) { 889 | // Strings, numbers, dates, and booleans are compared by value. 890 | case '[object String]': 891 | // Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is 892 | // equivalent to `new String("5")`. 893 | return a == String(b); 894 | case '[object Number]': 895 | // `NaN`s are equivalent, but non-reflexive. An `egal` comparison is performed for 896 | // other numeric values. 897 | return a != +a ? b != +b : (a == 0 ? 1 / a == 1 / b : a == +b); 898 | case '[object Date]': 899 | case '[object Boolean]': 900 | // Coerce dates and booleans to numeric primitive values. Dates are compared by their 901 | // millisecond representations. Note that invalid dates with millisecond representations 902 | // of `NaN` are not equivalent. 903 | return +a == +b; 904 | // RegExps are compared by their source patterns and flags. 905 | case '[object RegExp]': 906 | return a.source == b.source && 907 | a.global == b.global && 908 | a.multiline == b.multiline && 909 | a.ignoreCase == b.ignoreCase; 910 | } 911 | if (typeof a != 'object' || typeof b != 'object') return false; 912 | // Assume equality for cyclic structures. The algorithm for detecting cyclic 913 | // structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`. 914 | var length = aStack.length; 915 | while (length--) { 916 | // Linear search. Performance is inversely proportional to the number of 917 | // unique nested structures. 918 | if (aStack[length] == a) return bStack[length] == b; 919 | } 920 | // Objects with different constructors are not equivalent, but `Object`s 921 | // from different frames are. 922 | var aCtor = a.constructor, bCtor = b.constructor; 923 | if (aCtor !== bCtor && !(_.isFunction(aCtor) && (aCtor instanceof aCtor) && 924 | _.isFunction(bCtor) && (bCtor instanceof bCtor))) { 925 | return false; 926 | } 927 | // Add the first object to the stack of traversed objects. 928 | aStack.push(a); 929 | bStack.push(b); 930 | var size = 0, result = true; 931 | // Recursively compare objects and arrays. 932 | if (className == '[object Array]') { 933 | // Compare array lengths to determine if a deep comparison is necessary. 934 | size = a.length; 935 | result = size == b.length; 936 | if (result) { 937 | // Deep compare the contents, ignoring non-numeric properties. 938 | while (size--) { 939 | if (!(result = eq(a[size], b[size], aStack, bStack))) break; 940 | } 941 | } 942 | } else { 943 | // Deep compare objects. 944 | for (var key in a) { 945 | if (_.has(a, key)) { 946 | // Count the expected number of properties. 947 | size++; 948 | // Deep compare each member. 949 | if (!(result = _.has(b, key) && eq(a[key], b[key], aStack, bStack))) break; 950 | } 951 | } 952 | // Ensure that both objects contain the same number of properties. 953 | if (result) { 954 | for (key in b) { 955 | if (_.has(b, key) && !(size--)) break; 956 | } 957 | result = !size; 958 | } 959 | } 960 | // Remove the first object from the stack of traversed objects. 961 | aStack.pop(); 962 | bStack.pop(); 963 | return result; 964 | }; 965 | 966 | // Perform a deep comparison to check if two objects are equal. 967 | _.isEqual = function(a, b) { 968 | return eq(a, b, [], []); 969 | }; 970 | 971 | // Is a given array, string, or object empty? 972 | // An "empty" object has no enumerable own-properties. 973 | _.isEmpty = function(obj) { 974 | if (obj == null) return true; 975 | if (_.isArray(obj) || _.isString(obj)) return obj.length === 0; 976 | for (var key in obj) if (_.has(obj, key)) return false; 977 | return true; 978 | }; 979 | 980 | // Is a given value a DOM element? 981 | _.isElement = function(obj) { 982 | return !!(obj && obj.nodeType === 1); 983 | }; 984 | 985 | // Is a given value an array? 986 | // Delegates to ECMA5's native Array.isArray 987 | _.isArray = nativeIsArray || function(obj) { 988 | return toString.call(obj) == '[object Array]'; 989 | }; 990 | 991 | // Is a given variable an object? 992 | _.isObject = function(obj) { 993 | return obj === Object(obj); 994 | }; 995 | 996 | // Add some isType methods: isArguments, isFunction, isString, isNumber, isDate, isRegExp. 997 | each(['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp'], function(name) { 998 | _['is' + name] = function(obj) { 999 | return toString.call(obj) == '[object ' + name + ']'; 1000 | }; 1001 | }); 1002 | 1003 | // Define a fallback version of the method in browsers (ahem, IE), where 1004 | // there isn't any inspectable "Arguments" type. 1005 | if (!_.isArguments(arguments)) { 1006 | _.isArguments = function(obj) { 1007 | return !!(obj && _.has(obj, 'callee')); 1008 | }; 1009 | } 1010 | 1011 | // Optimize `isFunction` if appropriate. 1012 | if (typeof (/./) !== 'function') { 1013 | _.isFunction = function(obj) { 1014 | return typeof obj === 'function'; 1015 | }; 1016 | } 1017 | 1018 | // Is a given object a finite number? 1019 | _.isFinite = function(obj) { 1020 | return isFinite(obj) && !isNaN(parseFloat(obj)); 1021 | }; 1022 | 1023 | // Is the given value `NaN`? (NaN is the only number which does not equal itself). 1024 | _.isNaN = function(obj) { 1025 | return _.isNumber(obj) && obj != +obj; 1026 | }; 1027 | 1028 | // Is a given value a boolean? 1029 | _.isBoolean = function(obj) { 1030 | return obj === true || obj === false || toString.call(obj) == '[object Boolean]'; 1031 | }; 1032 | 1033 | // Is a given value equal to null? 1034 | _.isNull = function(obj) { 1035 | return obj === null; 1036 | }; 1037 | 1038 | // Is a given variable undefined? 1039 | _.isUndefined = function(obj) { 1040 | return obj === void 0; 1041 | }; 1042 | 1043 | // Shortcut function for checking if an object has a given property directly 1044 | // on itself (in other words, not on a prototype). 1045 | _.has = function(obj, key) { 1046 | return hasOwnProperty.call(obj, key); 1047 | }; 1048 | 1049 | // Utility Functions 1050 | // ----------------- 1051 | 1052 | // Run Underscore.js in *noConflict* mode, returning the `_` variable to its 1053 | // previous owner. Returns a reference to the Underscore object. 1054 | _.noConflict = function() { 1055 | root._ = previousUnderscore; 1056 | return this; 1057 | }; 1058 | 1059 | // Keep the identity function around for default iterators. 1060 | _.identity = function(value) { 1061 | return value; 1062 | }; 1063 | 1064 | // Run a function **n** times. 1065 | _.times = function(n, iterator, context) { 1066 | var accum = Array(Math.max(0, n)); 1067 | for (var i = 0; i < n; i++) accum[i] = iterator.call(context, i); 1068 | return accum; 1069 | }; 1070 | 1071 | // Return a random integer between min and max (inclusive). 1072 | _.random = function(min, max) { 1073 | if (max == null) { 1074 | max = min; 1075 | min = 0; 1076 | } 1077 | return min + Math.floor(Math.random() * (max - min + 1)); 1078 | }; 1079 | 1080 | // List of HTML entities for escaping. 1081 | var entityMap = { 1082 | escape: { 1083 | '&': '&', 1084 | '<': '<', 1085 | '>': '>', 1086 | '"': '"', 1087 | "'": ''' 1088 | } 1089 | }; 1090 | entityMap.unescape = _.invert(entityMap.escape); 1091 | 1092 | // Regexes containing the keys and values listed immediately above. 1093 | var entityRegexes = { 1094 | escape: new RegExp('[' + _.keys(entityMap.escape).join('') + ']', 'g'), 1095 | unescape: new RegExp('(' + _.keys(entityMap.unescape).join('|') + ')', 'g') 1096 | }; 1097 | 1098 | // Functions for escaping and unescaping strings to/from HTML interpolation. 1099 | _.each(['escape', 'unescape'], function(method) { 1100 | _[method] = function(string) { 1101 | if (string == null) return ''; 1102 | return ('' + string).replace(entityRegexes[method], function(match) { 1103 | return entityMap[method][match]; 1104 | }); 1105 | }; 1106 | }); 1107 | 1108 | // If the value of the named `property` is a function then invoke it with the 1109 | // `object` as context; otherwise, return it. 1110 | _.result = function(object, property) { 1111 | if (object == null) return void 0; 1112 | var value = object[property]; 1113 | return _.isFunction(value) ? value.call(object) : value; 1114 | }; 1115 | 1116 | // Add your own custom functions to the Underscore object. 1117 | _.mixin = function(obj) { 1118 | each(_.functions(obj), function(name) { 1119 | var func = _[name] = obj[name]; 1120 | _.prototype[name] = function() { 1121 | var args = [this._wrapped]; 1122 | push.apply(args, arguments); 1123 | return result.call(this, func.apply(_, args)); 1124 | }; 1125 | }); 1126 | }; 1127 | 1128 | // Generate a unique integer id (unique within the entire client session). 1129 | // Useful for temporary DOM ids. 1130 | var idCounter = 0; 1131 | _.uniqueId = function(prefix) { 1132 | var id = ++idCounter + ''; 1133 | return prefix ? prefix + id : id; 1134 | }; 1135 | 1136 | // By default, Underscore uses ERB-style template delimiters, change the 1137 | // following template settings to use alternative delimiters. 1138 | _.templateSettings = { 1139 | evaluate : /<%([\s\S]+?)%>/g, 1140 | interpolate : /<%=([\s\S]+?)%>/g, 1141 | escape : /<%-([\s\S]+?)%>/g 1142 | }; 1143 | 1144 | // When customizing `templateSettings`, if you don't want to define an 1145 | // interpolation, evaluation or escaping regex, we need one that is 1146 | // guaranteed not to match. 1147 | var noMatch = /(.)^/; 1148 | 1149 | // Certain characters need to be escaped so that they can be put into a 1150 | // string literal. 1151 | var escapes = { 1152 | "'": "'", 1153 | '\\': '\\', 1154 | '\r': 'r', 1155 | '\n': 'n', 1156 | '\t': 't', 1157 | '\u2028': 'u2028', 1158 | '\u2029': 'u2029' 1159 | }; 1160 | 1161 | var escaper = /\\|'|\r|\n|\t|\u2028|\u2029/g; 1162 | 1163 | // JavaScript micro-templating, similar to John Resig's implementation. 1164 | // Underscore templating handles arbitrary delimiters, preserves whitespace, 1165 | // and correctly escapes quotes within interpolated code. 1166 | _.template = function(text, data, settings) { 1167 | var render; 1168 | settings = _.defaults({}, settings, _.templateSettings); 1169 | 1170 | // Combine delimiters into one regular expression via alternation. 1171 | var matcher = new RegExp([ 1172 | (settings.escape || noMatch).source, 1173 | (settings.interpolate || noMatch).source, 1174 | (settings.evaluate || noMatch).source 1175 | ].join('|') + '|$', 'g'); 1176 | 1177 | // Compile the template source, escaping string literals appropriately. 1178 | var index = 0; 1179 | var source = "__p+='"; 1180 | text.replace(matcher, function(match, escape, interpolate, evaluate, offset) { 1181 | source += text.slice(index, offset) 1182 | .replace(escaper, function(match) { return '\\' + escapes[match]; }); 1183 | 1184 | if (escape) { 1185 | source += "'+\n((__t=(" + escape + "))==null?'':_.escape(__t))+\n'"; 1186 | } 1187 | if (interpolate) { 1188 | source += "'+\n((__t=(" + interpolate + "))==null?'':__t)+\n'"; 1189 | } 1190 | if (evaluate) { 1191 | source += "';\n" + evaluate + "\n__p+='"; 1192 | } 1193 | index = offset + match.length; 1194 | return match; 1195 | }); 1196 | source += "';\n"; 1197 | 1198 | // If a variable is not specified, place data values in local scope. 1199 | if (!settings.variable) source = 'with(obj||{}){\n' + source + '}\n'; 1200 | 1201 | source = "var __t,__p='',__j=Array.prototype.join," + 1202 | "print=function(){__p+=__j.call(arguments,'');};\n" + 1203 | source + "return __p;\n"; 1204 | 1205 | try { 1206 | render = new Function(settings.variable || 'obj', '_', source); 1207 | } catch (e) { 1208 | e.source = source; 1209 | throw e; 1210 | } 1211 | 1212 | if (data) return render(data, _); 1213 | var template = function(data) { 1214 | return render.call(this, data, _); 1215 | }; 1216 | 1217 | // Provide the compiled function source as a convenience for precompilation. 1218 | template.source = 'function(' + (settings.variable || 'obj') + '){\n' + source + '}'; 1219 | 1220 | return template; 1221 | }; 1222 | 1223 | // Add a "chain" function, which will delegate to the wrapper. 1224 | _.chain = function(obj) { 1225 | return _(obj).chain(); 1226 | }; 1227 | 1228 | // OOP 1229 | // --------------- 1230 | // If Underscore is called as a function, it returns a wrapped object that 1231 | // can be used OO-style. This wrapper holds altered versions of all the 1232 | // underscore functions. Wrapped objects may be chained. 1233 | 1234 | // Helper function to continue chaining intermediate results. 1235 | var result = function(obj) { 1236 | return this._chain ? _(obj).chain() : obj; 1237 | }; 1238 | 1239 | // Add all of the Underscore functions to the wrapper object. 1240 | _.mixin(_); 1241 | 1242 | // Add all mutator Array functions to the wrapper. 1243 | each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) { 1244 | var method = ArrayProto[name]; 1245 | _.prototype[name] = function() { 1246 | var obj = this._wrapped; 1247 | method.apply(obj, arguments); 1248 | if ((name == 'shift' || name == 'splice') && obj.length === 0) delete obj[0]; 1249 | return result.call(this, obj); 1250 | }; 1251 | }); 1252 | 1253 | // Add all accessor Array functions to the wrapper. 1254 | each(['concat', 'join', 'slice'], function(name) { 1255 | var method = ArrayProto[name]; 1256 | _.prototype[name] = function() { 1257 | return result.call(this, method.apply(this._wrapped, arguments)); 1258 | }; 1259 | }); 1260 | 1261 | _.extend(_.prototype, { 1262 | 1263 | // Start chaining a wrapped Underscore object. 1264 | chain: function() { 1265 | this._chain = true; 1266 | return this; 1267 | }, 1268 | 1269 | // Extracts the result from a wrapped and chained object. 1270 | value: function() { 1271 | return this._wrapped; 1272 | } 1273 | 1274 | }); 1275 | 1276 | }).call(this); 1277 | --------------------------------------------------------------------------------