Repository: 4commerce-technologies-AG/meteor
Branch: release-1.3.4.1-universal
Commit: ffdadacb6138
Files: 2037
Total size: 12.0 MB
Directory structure:
gitextract_hcijcecp/
├── .arcconfig
├── .eslintignore
├── .github/
│ ├── ISSUE_TEMPLATE.md
│ └── PULL_REQUEST_TEMPLATE.md
├── .gitignore
├── .mailmap
├── .reviewboardrc
├── .travis.yml
├── Contributing.md
├── History.md
├── IssueTriage.md
├── LICENSE.txt
├── LICENSES/
│ ├── Apache.txt
│ ├── Artistic.txt
│ ├── BSD.txt
│ ├── CDDL.txt
│ ├── DWTFYWT.txt
│ ├── Eclipse.txt
│ ├── ISC.txt
│ ├── IntelHAXM.txt
│ ├── MIT.txt
│ ├── MongoDB.txt
│ ├── Node.txt
│ ├── PublicDomain.txt
│ ├── andris9.txt
│ ├── browserify.txt
│ ├── commonmark.txt
│ ├── heapdump.txt
│ ├── ieee754.txt
│ ├── jsdoc.txt
│ ├── libuv.txt
│ ├── nan.txt
│ ├── through2.txt
│ ├── tough-cookie.txt
│ └── unorm.txt
├── README.md
├── Roadmap.md
├── circle.yml
├── examples/
│ ├── .gitignore
│ ├── other/
│ │ ├── benchmark/
│ │ │ └── .meteor/
│ │ │ └── release
│ │ ├── client-info/
│ │ │ ├── .meteor/
│ │ │ │ ├── .gitignore
│ │ │ │ ├── packages
│ │ │ │ └── release
│ │ │ ├── client-info.html
│ │ │ └── client-info.js
│ │ ├── controllers-demo/
│ │ │ └── .meteor/
│ │ │ └── release
│ │ ├── defer-in-inactive-tab/
│ │ │ ├── .meteor/
│ │ │ │ ├── .gitignore
│ │ │ │ └── packages
│ │ │ ├── README.md
│ │ │ ├── test.html
│ │ │ └── test.js
│ │ ├── domrange-grid/
│ │ │ ├── .meteor/
│ │ │ │ ├── .gitignore
│ │ │ │ ├── packages
│ │ │ │ └── release
│ │ │ ├── domrange-grid.css
│ │ │ ├── domrange-grid.html
│ │ │ └── domrange-grid.js
│ │ ├── login-demo/
│ │ │ ├── .meteor/
│ │ │ │ ├── .gitignore
│ │ │ │ ├── packages
│ │ │ │ └── release
│ │ │ ├── login-demo.css
│ │ │ ├── login-demo.html
│ │ │ └── login-demo.js
│ │ ├── parties/
│ │ │ ├── .meteor/
│ │ │ │ ├── .finished-upgraders
│ │ │ │ ├── .gitignore
│ │ │ │ ├── packages
│ │ │ │ ├── platforms
│ │ │ │ ├── release
│ │ │ │ └── versions
│ │ │ ├── client/
│ │ │ │ ├── client.js
│ │ │ │ ├── parties.css
│ │ │ │ └── parties.html
│ │ │ ├── model.js
│ │ │ └── server/
│ │ │ └── server.js
│ │ ├── quiescence/
│ │ │ ├── .meteor/
│ │ │ │ ├── .gitignore
│ │ │ │ ├── packages
│ │ │ │ └── release
│ │ │ ├── quiescence.html
│ │ │ └── quiescence.js
│ │ ├── template-demo/
│ │ │ ├── .meteor/
│ │ │ │ ├── .gitignore
│ │ │ │ ├── packages
│ │ │ │ └── release
│ │ │ ├── client/
│ │ │ │ ├── d3.v2.js
│ │ │ │ ├── template-demo.css
│ │ │ │ ├── template-demo.html
│ │ │ │ └── template-demo.js
│ │ │ └── model.js
│ │ └── wordplay/
│ │ ├── .meteor/
│ │ │ ├── .finished-upgraders
│ │ │ ├── .gitignore
│ │ │ ├── packages
│ │ │ ├── platforms
│ │ │ ├── release
│ │ │ └── versions
│ │ ├── TODO
│ │ ├── client/
│ │ │ ├── wordplay.css
│ │ │ ├── wordplay.html
│ │ │ └── wordplay.js
│ │ ├── model.js
│ │ ├── private/
│ │ │ └── enable2k.txt
│ │ └── server/
│ │ ├── game.js
│ │ └── make-boggle-dict.js.noload
│ └── unfinished/
│ ├── accounts-ui-viewer/
│ │ ├── .meteor/
│ │ │ ├── .gitignore
│ │ │ ├── packages
│ │ │ └── release
│ │ ├── accounts-ui-viewer.html
│ │ ├── accounts-ui-viewer.js
│ │ └── accounts-ui-viewer.less
│ ├── atoms/
│ │ ├── .meteor/
│ │ │ ├── .gitignore
│ │ │ ├── packages
│ │ │ └── release
│ │ ├── atoms.css
│ │ ├── atoms.html
│ │ └── atoms.js
│ ├── azrael/
│ │ ├── .meteor/
│ │ │ ├── .gitignore
│ │ │ ├── packages
│ │ │ └── release
│ │ ├── client/
│ │ │ ├── azrael.css
│ │ │ ├── azrael.html
│ │ │ └── azrael.js
│ │ └── model.js
│ ├── benchmark/
│ │ ├── .meteor/
│ │ │ ├── .gitignore
│ │ │ ├── packages
│ │ │ └── release
│ │ ├── benchmark.css
│ │ ├── benchmark.html
│ │ ├── benchmark.js
│ │ ├── run-local.sh
│ │ └── scenarios/
│ │ ├── README.md
│ │ ├── default.json
│ │ ├── fast.json
│ │ ├── nodata.json
│ │ ├── scale10.json
│ │ ├── scale100.json
│ │ ├── scale20.json
│ │ ├── scale40.json
│ │ └── scale50.json
│ ├── blaze-test/
│ │ ├── .meteor/
│ │ │ ├── .gitignore
│ │ │ ├── packages
│ │ │ └── release
│ │ └── client/
│ │ ├── blaze-test.css
│ │ ├── blaze-test.html
│ │ └── blaze-test.js
│ ├── chat-benchmark/
│ │ ├── .meteor/
│ │ │ ├── .gitignore
│ │ │ ├── packages
│ │ │ └── release
│ │ ├── benchmark.css
│ │ ├── benchmark.html
│ │ ├── benchmark.js
│ │ ├── run-local.sh
│ │ └── scenarios/
│ │ ├── README.md
│ │ └── default.json
│ ├── coffeeless/
│ │ ├── .meteor/
│ │ │ ├── .gitignore
│ │ │ ├── packages
│ │ │ └── release
│ │ ├── client/
│ │ │ ├── coffeeless.coffee
│ │ │ ├── coffeeless.html
│ │ │ └── coffeeless.less
│ │ └── model.coffee
│ ├── controls/
│ │ ├── .meteor/
│ │ │ ├── .gitignore
│ │ │ ├── packages
│ │ │ └── release
│ │ ├── client/
│ │ │ └── controls.js
│ │ ├── controls.css
│ │ └── controls.html
│ ├── jsparse-docs/
│ │ ├── .meteor/
│ │ │ ├── .gitignore
│ │ │ ├── packages
│ │ │ └── release
│ │ ├── jsparse-docs.css
│ │ ├── jsparse-docs.html
│ │ └── jsparse-docs.js
│ ├── leaderboard-remote/
│ │ ├── .meteor/
│ │ │ ├── .gitignore
│ │ │ ├── packages
│ │ │ └── release
│ │ └── client/
│ │ ├── leaderboard-remote.css
│ │ ├── leaderboard-remote.html
│ │ └── leaderboard-remote.js
│ ├── movers/
│ │ ├── .meteor/
│ │ │ ├── .gitignore
│ │ │ ├── packages
│ │ │ └── release
│ │ ├── client/
│ │ │ └── jquery-ui-sortable.js
│ │ ├── movers.html
│ │ ├── movers.js
│ │ └── movers.less
│ ├── parse-inspector/
│ │ ├── .meteor/
│ │ │ ├── .gitignore
│ │ │ ├── packages
│ │ │ └── release
│ │ ├── parse-inspector.css
│ │ ├── parse-inspector.html
│ │ └── parse-inspector.js
│ ├── python-ddp-client/
│ │ ├── README
│ │ ├── ddp-client.py
│ │ └── test_input
│ ├── reorderable-list/
│ │ ├── .meteor/
│ │ │ ├── .gitignore
│ │ │ ├── packages
│ │ │ └── release
│ │ ├── client/
│ │ │ ├── jquery-ui-sortable.js
│ │ │ ├── shark.css
│ │ │ ├── shark.html
│ │ │ └── shark.js
│ │ └── lib/
│ │ └── items.js
│ ├── todos-backbone/
│ │ ├── .meteor/
│ │ │ ├── .gitignore
│ │ │ ├── packages
│ │ │ └── release
│ │ ├── body.html
│ │ ├── client/
│ │ │ └── todos.js
│ │ ├── common.js
│ │ └── todos.css
│ └── todos-underscore/
│ ├── .meteor/
│ │ ├── .gitignore
│ │ ├── packages
│ │ └── release
│ ├── body.html
│ ├── client/
│ │ └── client.js
│ ├── common.js
│ ├── main.css
│ ├── reset.css
│ └── server/
│ └── bootstrap.js
├── meteor
├── meteor.bat
├── packages/
│ ├── .gitignore
│ ├── accounts-base/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── accounts_client.js
│ │ ├── accounts_common.js
│ │ ├── accounts_rate_limit.js
│ │ ├── accounts_reconnect_tests.js
│ │ ├── accounts_server.js
│ │ ├── accounts_tests.js
│ │ ├── accounts_url_tests.js
│ │ ├── client_main.js
│ │ ├── client_tests.js
│ │ ├── localstorage_token.js
│ │ ├── package.js
│ │ ├── server_main.js
│ │ ├── server_tests.js
│ │ ├── url_client.js
│ │ └── url_server.js
│ ├── accounts-facebook/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── facebook.js
│ │ ├── facebook_login_button.css
│ │ └── package.js
│ ├── accounts-github/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── github.js
│ │ ├── github_login_button.css
│ │ └── package.js
│ ├── accounts-google/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── google.js
│ │ ├── google_login_button.css
│ │ └── package.js
│ ├── accounts-meetup/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── meetup.js
│ │ ├── meetup_login_button.css
│ │ └── package.js
│ ├── accounts-meteor-developer/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── meteor-developer-login-button.css
│ │ ├── meteor-developer.js
│ │ └── package.js
│ ├── accounts-oauth/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── oauth_client.js
│ │ ├── oauth_common.js
│ │ ├── oauth_server.js
│ │ ├── oauth_tests.js
│ │ └── package.js
│ ├── accounts-password/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── email_templates.js
│ │ ├── email_tests.js
│ │ ├── email_tests_setup.js
│ │ ├── package.js
│ │ ├── password_client.js
│ │ ├── password_server.js
│ │ ├── password_tests.js
│ │ └── password_tests_setup.js
│ ├── accounts-twitter/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── package.js
│ │ ├── twitter.js
│ │ └── twitter_login_button.css
│ ├── accounts-ui/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── login_buttons.less
│ │ └── package.js
│ ├── accounts-ui-unstyled/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── accounts_ui.js
│ │ ├── accounts_ui_tests.js
│ │ ├── login_buttons.html
│ │ ├── login_buttons.import.less
│ │ ├── login_buttons.js
│ │ ├── login_buttons_dialogs.html
│ │ ├── login_buttons_dialogs.js
│ │ ├── login_buttons_dropdown.html
│ │ ├── login_buttons_dropdown.js
│ │ ├── login_buttons_session.js
│ │ ├── login_buttons_single.html
│ │ ├── login_buttons_single.js
│ │ └── package.js
│ ├── accounts-weibo/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── package.js
│ │ ├── weibo.js
│ │ └── weibo_login_button.css
│ ├── allow-deny/
│ │ ├── README.md
│ │ ├── allow-deny-tests.js
│ │ ├── allow-deny.js
│ │ └── package.js
│ ├── appcache/
│ │ ├── .gitignore
│ │ ├── QA.md
│ │ ├── README.md
│ │ ├── appcache-client.js
│ │ ├── appcache-server.js
│ │ ├── appcache_tests-client.js
│ │ ├── appcache_tests-server.js
│ │ └── package.js
│ ├── audit-argument-checks/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ └── package.js
│ ├── autopublish/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ └── package.js
│ ├── autoupdate/
│ │ ├── .gitignore
│ │ ├── QA.md
│ │ ├── README.md
│ │ ├── autoupdate_client.js
│ │ ├── autoupdate_cordova.js
│ │ ├── autoupdate_server.js
│ │ └── package.js
│ ├── babel-compiler/
│ │ ├── .npm/
│ │ │ └── package/
│ │ │ ├── .gitignore
│ │ │ ├── README
│ │ │ └── npm-shrinkwrap.json
│ │ ├── README.md
│ │ ├── babel-compiler.js
│ │ ├── babel.js
│ │ └── package.js
│ ├── babel-runtime/
│ │ ├── .npm/
│ │ │ └── package/
│ │ │ ├── .gitignore
│ │ │ ├── README
│ │ │ └── npm-shrinkwrap.json
│ │ ├── README.md
│ │ ├── babel-runtime.js
│ │ └── package.js
│ ├── base64/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── base64.js
│ │ ├── base64_test.js
│ │ └── package.js
│ ├── binary-heap/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── binary-heap-tests.js
│ │ ├── max-heap.js
│ │ ├── min-heap.js
│ │ ├── min-max-heap.js
│ │ └── package.js
│ ├── blaze/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── attrs.js
│ │ ├── backcompat.js
│ │ ├── builtins.js
│ │ ├── dombackend.js
│ │ ├── domrange.js
│ │ ├── events.js
│ │ ├── exceptions.js
│ │ ├── lookup.js
│ │ ├── materializer.js
│ │ ├── microscore.js
│ │ ├── package.js
│ │ ├── preamble.js
│ │ ├── render_tests.js
│ │ ├── template.js
│ │ ├── view.js
│ │ └── view_tests.js
│ ├── blaze-html-templates/
│ │ ├── README.md
│ │ └── package.js
│ ├── blaze-tools/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── package.js
│ │ ├── preamble.js
│ │ ├── tojs.js
│ │ ├── token_tests.js
│ │ └── tokens.js
│ ├── boilerplate-generator/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── boilerplate-generator.js
│ │ ├── boilerplate_web.browser.html
│ │ ├── boilerplate_web.cordova.html
│ │ └── package.js
│ ├── browser-policy/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── browser-policy-test.js
│ │ └── package.js
│ ├── browser-policy-common/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── browser-policy-common.js
│ │ └── package.js
│ ├── browser-policy-content/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── browser-policy-content.js
│ │ └── package.js
│ ├── browser-policy-framing/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── browser-policy-framing.js
│ │ └── package.js
│ ├── caching-compiler/
│ │ ├── .npm/
│ │ │ └── package/
│ │ │ ├── .gitignore
│ │ │ ├── README
│ │ │ └── npm-shrinkwrap.json
│ │ ├── README.md
│ │ ├── caching-compiler.js
│ │ ├── multi-file-caching-compiler.js
│ │ └── package.js
│ ├── caching-html-compiler/
│ │ ├── README.md
│ │ ├── caching-html-compiler.js
│ │ └── package.js
│ ├── callback-hook/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── hook.js
│ │ ├── hook_tests.js
│ │ └── package.js
│ ├── check/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── isPlainObject.js
│ │ ├── match.js
│ │ ├── match_test.js
│ │ └── package.js
│ ├── code-prettify/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── lang-apollo.js
│ │ ├── lang-clj.js
│ │ ├── lang-css.js
│ │ ├── lang-go.js
│ │ ├── lang-hs.js
│ │ ├── lang-lisp.js
│ │ ├── lang-lua.js
│ │ ├── lang-ml.js
│ │ ├── lang-n.js
│ │ ├── lang-proto.js
│ │ ├── lang-scala.js
│ │ ├── lang-sql.js
│ │ ├── lang-tex.js
│ │ ├── lang-vhdl.js
│ │ ├── lang-wiki.js
│ │ ├── lang-xq.js
│ │ ├── lang-yaml.js
│ │ ├── package.js
│ │ ├── prettify.css
│ │ ├── prettify.js
│ │ └── styles/
│ │ ├── desert.css
│ │ └── sunburst.css
│ ├── coffeescript/
│ │ ├── .gitignore
│ │ ├── .npm/
│ │ │ └── plugin/
│ │ │ └── compileCoffeescript/
│ │ │ ├── .gitignore
│ │ │ ├── README
│ │ │ └── npm-shrinkwrap.json
│ │ ├── README.md
│ │ ├── package.js
│ │ ├── plugin/
│ │ │ └── compile-coffeescript.js
│ │ └── tests/
│ │ ├── bare_test_setup.coffee
│ │ ├── bare_tests.js
│ │ ├── coffeescript_strict_tests.coffee
│ │ ├── coffeescript_test_setup.js
│ │ ├── coffeescript_tests.coffee
│ │ ├── coffeescript_tests.js
│ │ ├── es2015_module.js
│ │ ├── litcoffeescript_tests.coffee.md
│ │ └── litcoffeescript_tests.litcoffee
│ ├── coffeescript-test-helper/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── exporting.coffee
│ │ └── package.js
│ ├── constraint-solver/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── benchmark-tests.js
│ │ ├── catalog-cache-tests.js
│ │ ├── catalog-cache.js
│ │ ├── catalog-loader.js
│ │ ├── constraint-solver-input.js
│ │ ├── constraint-solver-tests.js
│ │ ├── constraint-solver.js
│ │ ├── datatypes-tests.js
│ │ ├── datatypes.js
│ │ ├── gem-test-data.js
│ │ ├── input-tests.js
│ │ ├── package.js
│ │ ├── slow-test-data.js
│ │ ├── solver.js
│ │ ├── stack-overflow-bug-test-data.js
│ │ ├── version-pricer-tests.js
│ │ └── version-pricer.js
│ ├── crosswalk/
│ │ └── package.js
│ ├── ddp/
│ │ ├── .gitignore
│ │ ├── DDP.md
│ │ ├── README.md
│ │ └── package.js
│ ├── ddp-client/
│ │ ├── .npm/
│ │ │ └── package/
│ │ │ ├── .gitignore
│ │ │ ├── README
│ │ │ └── npm-shrinkwrap.json
│ │ ├── client_convenience.js
│ │ ├── id_map.js
│ │ ├── livedata_common.js
│ │ ├── livedata_connection.js
│ │ ├── livedata_connection_tests.js
│ │ ├── livedata_test_service.js
│ │ ├── livedata_tests.js
│ │ ├── namespace.js
│ │ ├── package.js
│ │ ├── random_stream.js
│ │ ├── random_stream_tests.js
│ │ ├── sockjs-0.3.4.js
│ │ ├── stream_client_common.js
│ │ ├── stream_client_nodejs.js
│ │ ├── stream_client_sockjs.js
│ │ ├── stream_client_tests.js
│ │ ├── stream_tests.js
│ │ └── stub_stream.js
│ ├── ddp-common/
│ │ ├── heartbeat.js
│ │ ├── method_invocation.js
│ │ ├── namespace.js
│ │ ├── package.js
│ │ ├── random_stream.js
│ │ └── utils.js
│ ├── ddp-rate-limiter/
│ │ ├── README.md
│ │ ├── ddp-rate-limiter-test-service.js
│ │ ├── ddp-rate-limiter-tests-common.js
│ │ ├── ddp-rate-limiter-tests.js
│ │ ├── ddp-rate-limiter.js
│ │ └── package.js
│ ├── ddp-server/
│ │ ├── .npm/
│ │ │ └── package/
│ │ │ ├── .gitignore
│ │ │ ├── README
│ │ │ └── npm-shrinkwrap.json
│ │ ├── crossbar.js
│ │ ├── crossbar_tests.js
│ │ ├── livedata_server.js
│ │ ├── livedata_server_tests.js
│ │ ├── package.js
│ │ ├── server_convenience.js
│ │ ├── session_view_tests.js
│ │ ├── stream_server.js
│ │ └── writefence.js
│ ├── deprecated/
│ │ ├── README
│ │ ├── amplify/
│ │ │ ├── .gitignore
│ │ │ ├── amplify.js
│ │ │ └── package.js
│ │ ├── backbone/
│ │ │ ├── .gitignore
│ │ │ ├── backbone.js
│ │ │ └── package.js
│ │ ├── bootstrap/
│ │ │ ├── .gitignore
│ │ │ ├── css/
│ │ │ │ ├── bootstrap-override.css
│ │ │ │ ├── bootstrap-responsive.css
│ │ │ │ └── bootstrap.css
│ │ │ ├── js/
│ │ │ │ └── bootstrap.js
│ │ │ └── package.js
│ │ └── d3/
│ │ ├── .gitignore
│ │ ├── d3.v3.js
│ │ └── package.js
│ ├── deps/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ └── package.js
│ ├── diff-sequence/
│ │ ├── diff.js
│ │ ├── package.js
│ │ └── tests.js
│ ├── disable-oplog/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ └── package.js
│ ├── ecmascript/
│ │ ├── README.md
│ │ ├── bare-test-file.js
│ │ ├── bare-test.js
│ │ ├── ecmascript.js
│ │ ├── package.js
│ │ ├── plugin.js
│ │ ├── runtime-tests.js
│ │ └── transpilation-tests.js
│ ├── ecmascript-runtime/
│ │ ├── .npm/
│ │ │ └── package/
│ │ │ ├── .gitignore
│ │ │ ├── README
│ │ │ └── npm-shrinkwrap.json
│ │ ├── README.md
│ │ ├── package.js
│ │ ├── runtime-tests.js
│ │ └── runtime.js
│ ├── ejson/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── custom_models_for_tests.js
│ │ ├── ejson.js
│ │ ├── ejson_test.js
│ │ ├── package.js
│ │ └── stringify.js
│ ├── email/
│ │ ├── .gitignore
│ │ ├── .npm/
│ │ │ └── package/
│ │ │ ├── .gitignore
│ │ │ ├── README
│ │ │ └── npm-shrinkwrap.json
│ │ ├── README.md
│ │ ├── email.js
│ │ ├── email_tests.js
│ │ └── package.js
│ ├── es5-shim/
│ │ ├── .npm/
│ │ │ └── package/
│ │ │ ├── .gitignore
│ │ │ ├── README
│ │ │ └── npm-shrinkwrap.json
│ │ ├── README.md
│ │ ├── client.js
│ │ ├── console.js
│ │ ├── export_globals.js
│ │ ├── import_globals.js
│ │ ├── package.js
│ │ └── server.js
│ ├── facebook/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── facebook_client.js
│ │ ├── facebook_configure.html
│ │ ├── facebook_configure.js
│ │ ├── facebook_server.js
│ │ └── package.js
│ ├── facts/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── facts.html
│ │ ├── facts.js
│ │ └── package.js
│ ├── fastclick/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── fastclick.js
│ │ ├── package.js
│ │ ├── post.js
│ │ └── pre.js
│ ├── force-ssl/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── force_ssl_common.js
│ │ ├── force_ssl_server.js
│ │ └── package.js
│ ├── geojson-utils/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── geojson-utils.js
│ │ ├── geojson-utils.tests.js
│ │ ├── main.js
│ │ └── package.js
│ ├── github/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── github_client.js
│ │ ├── github_configure.html
│ │ ├── github_configure.js
│ │ ├── github_server.js
│ │ └── package.js
│ ├── google/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── google_client.js
│ │ ├── google_configure.html
│ │ ├── google_configure.js
│ │ ├── google_server.js
│ │ └── package.js
│ ├── handlebars/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ └── package.js
│ ├── hot-code-push/
│ │ ├── README.md
│ │ ├── hot-code-push-tests.js
│ │ ├── hot-code-push.js
│ │ └── package.js
│ ├── html-tools/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── charref.js
│ │ ├── charref_tests.js
│ │ ├── package.js
│ │ ├── parse.js
│ │ ├── parse_tests.js
│ │ ├── scanner.js
│ │ ├── templatetag.js
│ │ ├── tokenize.js
│ │ ├── tokenize_tests.js
│ │ └── utils.js
│ ├── htmljs/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── html.js
│ │ ├── htmljs_test.js
│ │ ├── package.js
│ │ ├── preamble.js
│ │ └── visitors.js
│ ├── http/
│ │ ├── .gitignore
│ │ ├── .npm/
│ │ │ └── package/
│ │ │ ├── .gitignore
│ │ │ ├── README
│ │ │ └── npm-shrinkwrap.json
│ │ ├── README.md
│ │ ├── deprecated.js
│ │ ├── httpcall_client.js
│ │ ├── httpcall_common.js
│ │ ├── httpcall_server.js
│ │ ├── httpcall_tests.js
│ │ ├── package.js
│ │ ├── test_responder.js
│ │ └── test_static.serveme
│ ├── id-map/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── id-map.js
│ │ └── package.js
│ ├── insecure/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ └── package.js
│ ├── jquery/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── jquery.js
│ │ ├── main.js
│ │ └── package.js
│ ├── jquery-waypoints/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── package.js
│ │ └── waypoints.coffee
│ ├── jshint/
│ │ ├── .npm/
│ │ │ └── plugin/
│ │ │ └── lintJshint/
│ │ │ ├── .gitignore
│ │ │ ├── README
│ │ │ └── npm-shrinkwrap.json
│ │ ├── README.md
│ │ ├── package.js
│ │ └── plugin/
│ │ └── lint-jshint.js
│ ├── jsparse/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── lexer.js
│ │ ├── package.js
│ │ ├── parser.js
│ │ ├── parser_tests.js
│ │ ├── parserlib.js
│ │ └── stringify.js
│ ├── launch-screen/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── default-behavior.js
│ │ ├── mobile-launch-screen.js
│ │ └── package.js
│ ├── less/
│ │ ├── .gitignore
│ │ ├── .npm/
│ │ │ └── plugin/
│ │ │ └── compileLessBatch/
│ │ │ ├── .gitignore
│ │ │ ├── README
│ │ │ └── npm-shrinkwrap.json
│ │ ├── README.md
│ │ ├── less_tests.js
│ │ ├── package.js
│ │ ├── plugin/
│ │ │ └── compile-less.js
│ │ └── tests/
│ │ ├── dir/
│ │ │ ├── in-dir.import.less
│ │ │ ├── in-dir2.import.less
│ │ │ ├── root.less
│ │ │ └── subdir/
│ │ │ └── in-subdir.import.less
│ │ ├── imports/
│ │ │ └── not-included.less
│ │ ├── top.import.less
│ │ ├── top2.less
│ │ └── top3.import.less
│ ├── livedata/
│ │ ├── .gitignore
│ │ ├── DDP.md
│ │ ├── README.md
│ │ └── package.js
│ ├── localstorage/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── localstorage.js
│ │ ├── localstorage_tests.js
│ │ └── package.js
│ ├── logging/
│ │ ├── .gitignore
│ │ ├── .npm/
│ │ │ └── package/
│ │ │ ├── .gitignore
│ │ │ ├── README
│ │ │ └── npm-shrinkwrap.json
│ │ ├── README.md
│ │ ├── logging.js
│ │ ├── logging_cordova.js
│ │ ├── logging_test.js
│ │ └── package.js
│ ├── logic-solver/
│ │ ├── README.md
│ │ ├── logic.js
│ │ ├── logic_tests.js
│ │ ├── minisat.js
│ │ ├── minisat_wrapper.js
│ │ ├── optimize.js
│ │ ├── package.js
│ │ └── types.js
│ ├── markdown/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── license.txt
│ │ ├── package.js
│ │ ├── showdown.js
│ │ └── template-integration.js
│ ├── meetup/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── meetup_client.js
│ │ ├── meetup_configure.html
│ │ ├── meetup_configure.js
│ │ ├── meetup_server.js
│ │ └── package.js
│ ├── meteor/
│ │ ├── .gitignore
│ │ ├── .npm/
│ │ │ └── package/
│ │ │ ├── .gitignore
│ │ │ ├── README
│ │ │ └── npm-shrinkwrap.json
│ │ ├── README.md
│ │ ├── bare_test_setup.js
│ │ ├── bare_tests.js
│ │ ├── browser_environment_test.js
│ │ ├── client_environment.js
│ │ ├── client_environment_test.js
│ │ ├── cordova_environment.js
│ │ ├── cordova_environment_test.js
│ │ ├── debug.js
│ │ ├── debug_test.js
│ │ ├── dynamics_browser.js
│ │ ├── dynamics_nodejs.js
│ │ ├── dynamics_test.js
│ │ ├── errors.js
│ │ ├── fiber_helpers.js
│ │ ├── fiber_helpers_test.js
│ │ ├── fiber_stubs_client.js
│ │ ├── flush-buffers-on-exit-in-windows.js
│ │ ├── global.js
│ │ ├── helpers.js
│ │ ├── helpers_test.js
│ │ ├── package.js
│ │ ├── plugin/
│ │ │ └── basic-file-types.js
│ │ ├── server_environment.js
│ │ ├── server_environment_test.js
│ │ ├── setimmediate.js
│ │ ├── startup_client.js
│ │ ├── startup_server.js
│ │ ├── string_utils.js
│ │ ├── test_environment.js
│ │ ├── timers.js
│ │ ├── timers_tests.js
│ │ ├── url_common.js
│ │ ├── url_server.js
│ │ ├── url_tests.js
│ │ └── wrapasync_test.js
│ ├── meteor-base/
│ │ ├── README.md
│ │ └── package.js
│ ├── meteor-developer/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── meteor_developer_client.js
│ │ ├── meteor_developer_common.js
│ │ ├── meteor_developer_configure.html
│ │ ├── meteor_developer_configure.js
│ │ ├── meteor_developer_server.js
│ │ └── package.js
│ ├── meteor-platform/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ └── package.js
│ ├── meteor-tool/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ └── package.js
│ ├── meyerweb-reset/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── package.js
│ │ └── reset.css
│ ├── minifier-css/
│ │ ├── .gitignore
│ │ ├── .npm/
│ │ │ └── package/
│ │ │ ├── .gitignore
│ │ │ ├── README
│ │ │ └── npm-shrinkwrap.json
│ │ ├── README.md
│ │ ├── minification.js
│ │ ├── minifier-tests.js
│ │ ├── minifier.js
│ │ ├── package.js
│ │ └── urlrewriting-tests.js
│ ├── minifier-js/
│ │ ├── .gitignore
│ │ ├── .npm/
│ │ │ └── package/
│ │ │ ├── .gitignore
│ │ │ ├── README
│ │ │ └── npm-shrinkwrap.json
│ │ ├── README.md
│ │ ├── beautify-tests.js
│ │ ├── minifier.js
│ │ └── package.js
│ ├── minimongo/
│ │ ├── .gitignore
│ │ ├── NOTES
│ │ ├── README.md
│ │ ├── diff.js
│ │ ├── helpers.js
│ │ ├── id_map.js
│ │ ├── minimongo.js
│ │ ├── minimongo_server_tests.js
│ │ ├── minimongo_tests.js
│ │ ├── modify.js
│ │ ├── objectid.js
│ │ ├── observe.js
│ │ ├── package.js
│ │ ├── projection.js
│ │ ├── selector.js
│ │ ├── selector_modifier.js
│ │ ├── selector_projection.js
│ │ ├── sort.js
│ │ ├── sorter_projection.js
│ │ ├── wrap_transform.js
│ │ └── wrap_transform_tests.js
│ ├── mobile-experience/
│ │ ├── README.md
│ │ └── package.js
│ ├── mobile-status-bar/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ └── package.js
│ ├── modules/
│ │ ├── .npm/
│ │ │ └── package/
│ │ │ ├── .gitignore
│ │ │ ├── README
│ │ │ └── npm-shrinkwrap.json
│ │ ├── README.md
│ │ ├── buffer.js
│ │ ├── client.js
│ │ ├── css.js
│ │ ├── install-packages.js
│ │ ├── package.js
│ │ ├── process.js
│ │ ├── server.js
│ │ └── stubs.js
│ ├── modules-runtime/
│ │ ├── .npm/
│ │ │ └── package/
│ │ │ ├── .gitignore
│ │ │ ├── README
│ │ │ └── npm-shrinkwrap.json
│ │ ├── README.md
│ │ ├── modules-runtime-tests.js
│ │ ├── modules-runtime.js
│ │ └── package.js
│ ├── mongo/
│ │ ├── .gitignore
│ │ ├── .npm/
│ │ │ └── package/
│ │ │ ├── .gitignore
│ │ │ ├── README
│ │ │ └── npm-shrinkwrap.json
│ │ ├── README.md
│ │ ├── allow_tests.js
│ │ ├── collection.js
│ │ ├── collection_tests.js
│ │ ├── doc_fetcher.js
│ │ ├── doc_fetcher_tests.js
│ │ ├── local_collection_driver.js
│ │ ├── mongo_driver.js
│ │ ├── mongo_livedata_tests.js
│ │ ├── observe_changes_tests.js
│ │ ├── observe_multiplex.js
│ │ ├── oplog_observe_driver.js
│ │ ├── oplog_tailing.js
│ │ ├── oplog_tests.js
│ │ ├── package.js
│ │ ├── polling_observe_driver.js
│ │ └── remote_collection_driver.js
│ ├── mongo-id/
│ │ ├── id.js
│ │ └── package.js
│ ├── mongo-livedata/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ └── package.js
│ ├── non-core/
│ │ ├── README
│ │ ├── jquery-history/
│ │ │ ├── .gitignore
│ │ │ ├── history.adapter.jquery.js
│ │ │ ├── history.html4.js
│ │ │ ├── history.js
│ │ │ └── package.js
│ │ ├── jquery-layout/
│ │ │ ├── .gitignore
│ │ │ ├── jquery.layout.js
│ │ │ └── package.js
│ │ ├── npm-bcrypt/
│ │ │ ├── .gitignore
│ │ │ ├── .npm/
│ │ │ │ └── package/
│ │ │ │ ├── .gitignore
│ │ │ │ ├── README
│ │ │ │ └── npm-shrinkwrap.json
│ │ │ ├── .versions
│ │ │ ├── package.js
│ │ │ └── wrapper.js
│ │ └── npm-node-aes-gcm/
│ │ ├── .gitignore
│ │ ├── .npm/
│ │ │ └── package/
│ │ │ ├── .gitignore
│ │ │ ├── README
│ │ │ └── npm-shrinkwrap.json
│ │ ├── .versions
│ │ ├── README.md
│ │ ├── package.js
│ │ └── wrapper.js
│ ├── npm-mongo/
│ │ ├── .npm/
│ │ │ └── package/
│ │ │ ├── .gitignore
│ │ │ ├── README
│ │ │ └── npm-shrinkwrap.json
│ │ ├── package.js
│ │ └── wrapper.js
│ ├── oauth/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── deprecated.js
│ │ ├── end_of_popup_response.html
│ │ ├── end_of_popup_response.js
│ │ ├── end_of_redirect_response.html
│ │ ├── end_of_redirect_response.js
│ │ ├── oauth_browser.js
│ │ ├── oauth_client.js
│ │ ├── oauth_common.js
│ │ ├── oauth_cordova.js
│ │ ├── oauth_server.js
│ │ ├── oauth_tests.js
│ │ ├── package.js
│ │ └── pending_credentials.js
│ ├── oauth-encryption/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── encrypt.js
│ │ ├── encrypt_tests.js
│ │ └── package.js
│ ├── oauth1/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── oauth1_binding.js
│ │ ├── oauth1_pending_request_tokens.js
│ │ ├── oauth1_server.js
│ │ ├── oauth1_tests.js
│ │ └── package.js
│ ├── oauth2/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── oauth2_server.js
│ │ ├── oauth2_tests.js
│ │ └── package.js
│ ├── observe-sequence/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── observe_sequence.js
│ │ ├── observe_sequence_tests.js
│ │ └── package.js
│ ├── ordered-dict/
│ │ ├── .gitignore
│ │ ├── ordered_dict.js
│ │ └── package.js
│ ├── package-stats-opt-out/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ └── package.js
│ ├── package-version-parser/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── package-version-parser-tests.js
│ │ ├── package-version-parser.js
│ │ ├── package.js
│ │ └── semver410.js
│ ├── preserve-inputs/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── deprecated.js
│ │ └── package.js
│ ├── promise/
│ │ ├── .npm/
│ │ │ └── package/
│ │ │ ├── .gitignore
│ │ │ ├── README
│ │ │ └── npm-shrinkwrap.json
│ │ ├── README.md
│ │ ├── client.js
│ │ ├── common.js
│ │ ├── package.js
│ │ ├── promise-tests.js
│ │ └── server.js
│ ├── random/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── deprecated.js
│ │ ├── package.js
│ │ ├── random.js
│ │ └── random_tests.js
│ ├── rate-limit/
│ │ ├── README.md
│ │ ├── package.js
│ │ ├── rate-limit-tests.js
│ │ └── rate-limit.js
│ ├── reactive-dict/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── migration.js
│ │ ├── package.js
│ │ ├── reactive-dict-tests.js
│ │ └── reactive-dict.js
│ ├── reactive-var/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── package.js
│ │ └── reactive-var.js
│ ├── reload/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── deprecated.js
│ │ ├── package.js
│ │ ├── reload.js
│ │ └── reload_tests.js
│ ├── reload-safetybelt/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── package.js
│ │ ├── reload-safety-belt-tests.js
│ │ ├── reload-safety-belt.js
│ │ └── safetybelt.js
│ ├── retry/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── package.js
│ │ └── retry.js
│ ├── routepolicy/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── package.js
│ │ ├── routepolicy.js
│ │ └── routepolicy_tests.js
│ ├── service-configuration/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── package.js
│ │ ├── service_configuration_common.js
│ │ └── service_configuration_server.js
│ ├── session/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── package.js
│ │ ├── session.js
│ │ └── session_tests.js
│ ├── sha/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── package.js
│ │ └── sha256.js
│ ├── showdown/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ └── package.js
│ ├── spacebars/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── package.js
│ │ └── spacebars-runtime.js
│ ├── spacebars-compiler/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── codegen.js
│ │ ├── compile_tests.js
│ │ ├── compiler.js
│ │ ├── compiler_output_tests.coffee
│ │ ├── optimizer.js
│ │ ├── package.js
│ │ ├── react.js
│ │ ├── spacebars_tests.js
│ │ └── templatetag.js
│ ├── spacebars-tests/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── assets/
│ │ │ ├── markdown_basic.html
│ │ │ ├── markdown_each1.html
│ │ │ ├── markdown_each2.html
│ │ │ ├── markdown_if1.html
│ │ │ └── markdown_if2.html
│ │ ├── old_templates.js
│ │ ├── old_templates_tests.js
│ │ ├── package.js
│ │ ├── template_tests.html
│ │ ├── template_tests.js
│ │ ├── template_tests_server.js
│ │ ├── templating_tests.html
│ │ └── templating_tests.js
│ ├── spiderable/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── package.js
│ │ ├── phantom_script.js
│ │ ├── spiderable.html
│ │ ├── spiderable.js
│ │ ├── spiderable_client.js
│ │ ├── spiderable_client_tests.js
│ │ ├── spiderable_server.js
│ │ └── spiderable_server_tests.js
│ ├── srp/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── biginteger.js
│ │ ├── package.js
│ │ ├── srp.js
│ │ └── srp_tests.js
│ ├── standard-app-packages/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ └── package.js
│ ├── standard-minifier-css/
│ │ ├── .npm/
│ │ │ └── plugin/
│ │ │ ├── minifyStd/
│ │ │ │ ├── .gitignore
│ │ │ │ ├── README
│ │ │ │ └── npm-shrinkwrap.json
│ │ │ └── minifyStdCSS/
│ │ │ ├── .gitignore
│ │ │ ├── README
│ │ │ └── npm-shrinkwrap.json
│ │ ├── README.md
│ │ ├── package.js
│ │ └── plugin/
│ │ └── minify-css.js
│ ├── standard-minifier-js/
│ │ ├── .npm/
│ │ │ └── plugin/
│ │ │ └── minifyStd/
│ │ │ ├── .gitignore
│ │ │ ├── README
│ │ │ └── npm-shrinkwrap.json
│ │ ├── README.md
│ │ ├── package.js
│ │ └── plugin/
│ │ └── minify-js.js
│ ├── standard-minifiers/
│ │ ├── README.md
│ │ └── package.js
│ ├── startup/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ └── package.js
│ ├── static-html/
│ │ ├── README.md
│ │ ├── package.js
│ │ └── static-html.js
│ ├── stylus/
│ │ ├── .gitignore
│ │ ├── .npm/
│ │ │ └── plugin/
│ │ │ ├── compileStylus/
│ │ │ │ ├── .gitignore
│ │ │ │ ├── README
│ │ │ │ └── npm-shrinkwrap.json
│ │ │ └── compileStylusBatch/
│ │ │ ├── .gitignore
│ │ │ ├── README
│ │ │ └── npm-shrinkwrap.json
│ │ ├── README.md
│ │ ├── package.js
│ │ ├── plugin/
│ │ │ └── compile-stylus.js
│ │ ├── stylus_tests.html
│ │ ├── stylus_tests.import.styl
│ │ ├── stylus_tests.js
│ │ └── stylus_tests.styl
│ ├── templating/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── dynamic.html
│ │ ├── dynamic.js
│ │ ├── dynamic_tests.html
│ │ ├── dynamic_tests.js
│ │ ├── package.js
│ │ ├── plugin/
│ │ │ └── compile-templates.js
│ │ └── templating.js
│ ├── templating-tools/
│ │ ├── README.md
│ │ ├── code-generation.js
│ │ ├── compile-tags-with-spacebars.js
│ │ ├── html-scanner-tests.js
│ │ ├── html-scanner.js
│ │ ├── package.js
│ │ ├── templating-tools.js
│ │ └── throw-compile-error.js
│ ├── test-helpers/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── async_multi.js
│ │ ├── callback_logger.js
│ │ ├── canonicalize_html.js
│ │ ├── connection.js
│ │ ├── current_style.js
│ │ ├── domutils.js
│ │ ├── event_simulation.js
│ │ ├── package.js
│ │ ├── render_div.js
│ │ ├── seeded_random.js
│ │ ├── seeded_random_test.js
│ │ ├── try_all_permutations.js
│ │ └── try_all_permutations_test.js
│ ├── test-in-browser/
│ │ ├── .gitignore
│ │ ├── autoupdate.js
│ │ ├── diff_match_patch_uncompressed.js
│ │ ├── driver.css
│ │ ├── driver.html
│ │ ├── driver.js
│ │ └── package.js
│ ├── test-in-console/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── driver.js
│ │ ├── package.js
│ │ ├── reporter.js
│ │ ├── run.sh
│ │ └── runner.js
│ ├── test-server-tests-in-console-once/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── package.js
│ │ └── server.js
│ ├── tinytest/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── model.js
│ │ ├── package.js
│ │ ├── tinytest.js
│ │ ├── tinytest_client.js
│ │ └── tinytest_server.js
│ ├── tinytest-harness/
│ │ ├── README.md
│ │ └── package.js
│ ├── tracker/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── deprecated.js
│ │ ├── package.js
│ │ ├── tracker.js
│ │ └── tracker_tests.js
│ ├── twitter/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── package.js
│ │ ├── twitter_client.js
│ │ ├── twitter_configure.html
│ │ ├── twitter_configure.js
│ │ └── twitter_server.js
│ ├── ui/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ └── package.js
│ ├── underscore/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── package.js
│ │ ├── post.js
│ │ ├── pre.js
│ │ └── underscore.js
│ ├── underscore-tests/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── each_test.js
│ │ └── package.js
│ ├── url/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── package.js
│ │ ├── url_client.js
│ │ ├── url_common.js
│ │ └── url_server.js
│ ├── webapp/
│ │ ├── .gitignore
│ │ ├── .npm/
│ │ │ └── package/
│ │ │ ├── .gitignore
│ │ │ ├── README
│ │ │ └── npm-shrinkwrap.json
│ │ ├── README.md
│ │ ├── package.js
│ │ ├── webapp_client.js
│ │ ├── webapp_client_tests.js
│ │ ├── webapp_cordova.js
│ │ ├── webapp_server.js
│ │ └── webapp_tests.js
│ ├── webapp-hashing/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── package.js
│ │ └── webapp-hashing.js
│ ├── weibo/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── package.js
│ │ ├── weibo_client.js
│ │ ├── weibo_configure.html
│ │ ├── weibo_configure.js
│ │ └── weibo_server.js
│ └── xmlbuilder/
│ ├── .gitignore
│ ├── .npm/
│ │ └── package/
│ │ ├── .gitignore
│ │ ├── README
│ │ └── npm-shrinkwrap.json
│ ├── README.md
│ ├── package.js
│ └── xmlbuilder.js
├── scripts/
│ ├── admin/
│ │ ├── banners-oldcore.json
│ │ ├── banners.json
│ │ ├── bump-all-version-numbers.js
│ │ ├── check-package-dependencies.rb
│ │ ├── copy-bootstrap-tarballs-from-jenkins.sh
│ │ ├── copy-dev-bundle-from-jenkins.sh
│ │ ├── copy-windows-installer-from-jenkins.sh
│ │ ├── eslint/
│ │ │ ├── .eslintrc
│ │ │ ├── .gitignore
│ │ │ ├── eslint.sh
│ │ │ └── package.json
│ │ ├── find-author-github.sh
│ │ ├── find-new-npm-versions.sh
│ │ ├── git-hooks/
│ │ │ └── pre-commit
│ │ ├── install-from-bootstrap.sh
│ │ ├── jsdoc/
│ │ │ ├── .gitignore
│ │ │ ├── docdata-jsdoc-template/
│ │ │ │ └── publish.js
│ │ │ ├── jsdoc-conf.json
│ │ │ ├── jsdoc.sh
│ │ │ └── package.json
│ │ ├── launch-meteor
│ │ ├── manifest.json
│ │ ├── meteor-release-experimental.json
│ │ ├── meteor-release-official.json
│ │ ├── old-banner.txt
│ │ ├── publish-meteor-tool-on-all-platforms.sh
│ │ ├── publish-meteor-tool-on-arch.sh
│ │ ├── publish-meteor-tool.bat
│ │ └── test-packages-with-mongo-versions.rb
│ ├── benchmarks/
│ │ └── initial-start-time.sh
│ ├── build-dev-bundle-common.sh
│ ├── build-mongo-for-dev-bundle.sh
│ ├── build-node-for-dev-bundle.sh
│ ├── ci.sh
│ ├── dev-bundle-server-package.js
│ ├── dev-bundle-tool-package.js
│ ├── doctool.js
│ ├── doctool.md
│ ├── doctool.md.md
│ ├── generate-dev-bundle.ps1
│ ├── generate-dev-bundle.sh
│ ├── node.sh
│ ├── npm.cmd
│ └── windows/
│ ├── .gitignore
│ ├── README
│ ├── build-installer.ps1
│ ├── check-dev-bundle.ps1
│ ├── download-dev-bundle.ps1
│ └── installer/
│ ├── .gitignore
│ ├── MeteorSetup.sln
│ ├── WiXBalExtension/
│ │ ├── .gitignore
│ │ ├── BalExtensionExt.sln
│ │ ├── LICENSE.TXT
│ │ ├── build.bat
│ │ ├── inc/
│ │ │ ├── .gitignore
│ │ │ ├── Version.proj
│ │ │ ├── WixDistribution.cs
│ │ │ ├── WixDistribution.h
│ │ │ └── wix.rc
│ │ ├── wixext/
│ │ │ ├── BalCompiler.cs
│ │ │ ├── BalExtension.cs
│ │ │ ├── BalPreprocessorExtension.cs
│ │ │ ├── Properties/
│ │ │ │ └── AssemblyInfo.cs
│ │ │ ├── WixBalExtensionExt.csproj
│ │ │ ├── Xsd/
│ │ │ │ └── bal.xsd
│ │ │ └── data/
│ │ │ ├── messages.xml
│ │ │ └── tables.xml
│ │ ├── wixlib/
│ │ │ ├── BalExtension.wixproj
│ │ │ ├── BalExtension.wxs
│ │ │ ├── wixstdba.wxs
│ │ │ ├── wixstdba_platform.wxi
│ │ │ └── wixstdba_x86.wxs
│ │ └── wixstdba/
│ │ ├── JSON.cpp
│ │ ├── JSON.h
│ │ ├── JSONValue.cpp
│ │ ├── JSONValue.h
│ │ ├── Resources/
│ │ │ ├── Hyperlink2Theme.xml
│ │ │ ├── HyperlinkTheme.wxl
│ │ │ ├── HyperlinkTheme.xml
│ │ │ ├── LoremIpsumLicense.rtf
│ │ │ ├── RtfTheme.wxl
│ │ │ └── RtfTheme.xml
│ │ ├── WixStandardBootstrapperApplication.cpp
│ │ ├── precomp.h
│ │ ├── resource.h
│ │ ├── wixstdba.cpp
│ │ ├── wixstdba.def
│ │ ├── wixstdba.rc
│ │ ├── wixstdba.vcxproj
│ │ └── wixstdba.vcxproj.filters
│ ├── WiXHelper/
│ │ ├── .gitignore
│ │ ├── CustomAction.cpp
│ │ ├── CustomAction.def
│ │ ├── WiXHelper.vcxproj
│ │ ├── WiXHelper.vcxproj.filters
│ │ ├── stdafx.cpp
│ │ ├── stdafx.h
│ │ └── targetver.h
│ ├── WiXInstaller/
│ │ ├── .gitignore
│ │ ├── Configuration.wxi
│ │ ├── MSIPackage.wixproj
│ │ ├── Meteor_Bundle.wxs
│ │ ├── Meteor_Product.wxs
│ │ ├── Resources/
│ │ │ ├── License.htm
│ │ │ ├── License.rtf
│ │ │ ├── Theme_Meteor.wxl
│ │ │ ├── Theme_Meteor.xml
│ │ │ └── dummy.file
│ │ └── SetupPackage.wixproj
│ ├── WiXSDK/
│ │ ├── inc/
│ │ │ ├── BalBaseBootstrapperApplication.h
│ │ │ ├── IBootstrapperApplication.h
│ │ │ ├── IBootstrapperBAFunction.h
│ │ │ ├── IBootstrapperEngine.h
│ │ │ ├── aclutil.h
│ │ │ ├── apuputil.h
│ │ │ ├── atomutil.h
│ │ │ ├── balcondition.h
│ │ │ ├── balinfo.h
│ │ │ ├── balretry.h
│ │ │ ├── balutil.h
│ │ │ ├── buffutil.h
│ │ │ ├── cabcutil.h
│ │ │ ├── cabutil.h
│ │ │ ├── certutil.h
│ │ │ ├── conutil.h
│ │ │ ├── cryputil.h
│ │ │ ├── custommsierrors.h
│ │ │ ├── dictutil.h
│ │ │ ├── dirutil.h
│ │ │ ├── dutil.h
│ │ │ ├── eseutil.h
│ │ │ ├── fileutil.h
│ │ │ ├── gdiputil.h
│ │ │ ├── iis7util.h
│ │ │ ├── inetutil.h
│ │ │ ├── iniutil.h
│ │ │ ├── jsonutil.h
│ │ │ ├── locutil.h
│ │ │ ├── logutil.h
│ │ │ ├── memutil.h
│ │ │ ├── metautil.h
│ │ │ ├── osutil.h
│ │ │ ├── pathutil.h
│ │ │ ├── perfutil.h
│ │ │ ├── polcutil.h
│ │ │ ├── procutil.h
│ │ │ ├── regutil.h
│ │ │ ├── resrutil.h
│ │ │ ├── reswutil.h
│ │ │ ├── rexutil.h
│ │ │ ├── rmutil.h
│ │ │ ├── rssutil.h
│ │ │ ├── sceutil.h
│ │ │ ├── shelutil.h
│ │ │ ├── sqlutil.h
│ │ │ ├── srputil.h
│ │ │ ├── strutil.h
│ │ │ ├── svcutil.h
│ │ │ ├── thmutil.h
│ │ │ ├── timeutil.h
│ │ │ ├── uriutil.h
│ │ │ ├── userutil.h
│ │ │ ├── wcalog.h
│ │ │ ├── wcautil.h
│ │ │ ├── wcawow64.h
│ │ │ ├── wcawrapquery.h
│ │ │ ├── wiutil.h
│ │ │ ├── wuautil.h
│ │ │ └── xmlutil.h
│ │ ├── vs2010/
│ │ │ └── lib/
│ │ │ └── x86/
│ │ │ ├── balutil.lib
│ │ │ ├── deputil.lib
│ │ │ ├── dutil.lib
│ │ │ └── wcautil.lib
│ │ ├── vs2012/
│ │ │ └── lib/
│ │ │ └── x86/
│ │ │ ├── balutil.lib
│ │ │ ├── deputil.lib
│ │ │ ├── dutil.lib
│ │ │ └── wcautil.lib
│ │ └── vs2013/
│ │ └── lib/
│ │ └── x86/
│ │ ├── balutil.lib
│ │ ├── deputil.lib
│ │ ├── dutil.lib
│ │ └── wcautil.lib
│ └── build.bat
└── tools/
├── PERFORMANCE.md
├── README.md
├── cli/
│ ├── README.md
│ ├── commands-aliases.js
│ ├── commands-cordova.js
│ ├── commands-packages-query.js
│ ├── commands-packages.js
│ ├── commands.js
│ ├── dev-bundle-bin-commands.js
│ ├── dev-bundle-bin-helpers.js
│ ├── example-repositories.js
│ ├── flush-buffers-on-exit-in-windows.js
│ ├── help.txt
│ └── main.js
├── console/
│ ├── README.md
│ ├── console.js
│ └── progress.js
├── cordova/
│ ├── README.md
│ ├── builder.js
│ ├── index.js
│ ├── project.js
│ ├── protect-string-proto.js
│ ├── run-targets.js
│ └── runner.js
├── fs/
│ ├── README.md
│ ├── files.js
│ ├── mini-files.js
│ ├── safe-pathwatcher.js
│ └── watch.js
├── index.js
├── inspector.js
├── isobuild/
│ ├── README.md
│ ├── build-plugin.js
│ ├── builder.js
│ ├── bundler.js
│ ├── compiler-deprecated-compile-step.js
│ ├── compiler-plugin.js
│ ├── compiler.js
│ ├── css-modules.js
│ ├── import-scanner.js
│ ├── isopack-cache.js
│ ├── isopack.js
│ ├── js-analyze.js
│ ├── linker.js
│ ├── linter-plugin.js
│ ├── meteor-npm-userconfig
│ ├── meteor-npm.js
│ ├── minifier-plugin.js
│ ├── npm-discards.js
│ ├── package-api.js
│ ├── package-source.js
│ ├── resolver.js
│ ├── source-arch.js
│ └── test-files.js
├── meteor-services/
│ ├── README.md
│ ├── auth-client.js
│ ├── auth.js
│ ├── config.js
│ ├── deploy.js
│ ├── service-connection.js
│ └── stats.js
├── packaging/
│ ├── README.md
│ ├── catalog/
│ │ ├── catalog-local.js
│ │ ├── catalog-remote.js
│ │ ├── catalog-utils.js
│ │ └── catalog.js
│ ├── package-client.js
│ ├── package-map.js
│ ├── package-version-parser.js
│ ├── release.js
│ ├── tropohouse.js
│ ├── updater.js
│ └── warehouse.js
├── project-context.js
├── runners/
│ ├── README.md
│ ├── run-all.js
│ ├── run-app.js
│ ├── run-log.js
│ ├── run-mongo.js
│ ├── run-proxy.js
│ ├── run-selenium.js
│ └── run-updater.js
├── shell-client.js
├── static-assets/
│ ├── README.md
│ ├── server/
│ │ ├── boot-utils.js
│ │ ├── boot.js
│ │ ├── mini-files.js
│ │ ├── npm-rebuild.js
│ │ ├── npm-require.js
│ │ ├── server-json.js
│ │ └── shell-server.js
│ ├── skel/
│ │ ├── .gitignore
│ │ ├── .meteor/
│ │ │ ├── .gitignore
│ │ │ ├── packages
│ │ │ └── platforms
│ │ ├── client/
│ │ │ ├── main.css
│ │ │ ├── main.html
│ │ │ └── main.js
│ │ ├── package.json
│ │ └── server/
│ │ └── main.js
│ └── skel-pack/
│ ├── README.md
│ ├── package.js
│ ├── ~fs-name~-tests.js
│ └── ~fs-name~.js
├── tests/
│ ├── apps/
│ │ ├── app-prints-pid/
│ │ │ ├── .meteor/
│ │ │ │ ├── .gitignore
│ │ │ │ ├── packages
│ │ │ │ └── release
│ │ │ └── print.js
│ │ ├── app-throws-error/
│ │ │ ├── .meteor/
│ │ │ │ ├── .gitignore
│ │ │ │ ├── packages
│ │ │ │ └── release
│ │ │ ├── packages/
│ │ │ │ └── throwing-package/
│ │ │ │ ├── package.js
│ │ │ │ └── thrower.js
│ │ │ └── throw.js
│ │ ├── app-using-stylus/
│ │ │ ├── .meteor/
│ │ │ │ ├── .gitignore
│ │ │ │ ├── packages
│ │ │ │ └── release
│ │ │ ├── body.html
│ │ │ ├── client/
│ │ │ │ ├── app-export.import.styl
│ │ │ │ └── app.styl
│ │ │ ├── main.js
│ │ │ └── packages/
│ │ │ └── my-package/
│ │ │ ├── package-export.styl
│ │ │ ├── package-file.main.styl
│ │ │ ├── package-local-export.styl
│ │ │ └── package.js
│ │ ├── app-with-atmosphere-package/
│ │ │ └── .meteor/
│ │ │ ├── .gitignore
│ │ │ ├── packages
│ │ │ ├── release
│ │ │ └── versions
│ │ ├── app-with-unimported-lazy-file/
│ │ │ ├── .meteor/
│ │ │ │ ├── .gitignore
│ │ │ │ ├── .id
│ │ │ │ ├── packages
│ │ │ │ ├── platforms
│ │ │ │ ├── release
│ │ │ │ └── versions
│ │ │ ├── imports/
│ │ │ │ └── file.js
│ │ │ └── main.js
│ │ ├── build-errors/
│ │ │ ├── .meteor/
│ │ │ │ ├── .gitignore
│ │ │ │ ├── packages
│ │ │ │ └── release
│ │ │ ├── foo.awesome
│ │ │ └── packages/
│ │ │ └── with-colon-plugin/
│ │ │ ├── .gitignore
│ │ │ ├── package.js
│ │ │ └── plugin.js
│ │ ├── build-plugin-throws-error/
│ │ │ ├── .meteor/
│ │ │ │ ├── .gitignore
│ │ │ │ ├── packages
│ │ │ │ └── release
│ │ │ ├── file.extension
│ │ │ └── packages/
│ │ │ └── build-plugin/
│ │ │ ├── README.md
│ │ │ ├── build-plugin.js
│ │ │ └── package.js
│ │ ├── caching-coffee/
│ │ │ ├── .meteor/
│ │ │ │ ├── .gitignore
│ │ │ │ ├── packages
│ │ │ │ └── release
│ │ │ ├── f1.coffee
│ │ │ ├── f2.coffee
│ │ │ ├── f3.coffee
│ │ │ └── packages/
│ │ │ └── local-pack/
│ │ │ ├── p.coffee
│ │ │ └── package.js
│ │ ├── caching-less/
│ │ │ ├── .meteor/
│ │ │ │ ├── .gitignore
│ │ │ │ ├── packages
│ │ │ │ └── release
│ │ │ ├── imports/
│ │ │ │ └── dotdot.less
│ │ │ ├── output.js
│ │ │ ├── packages/
│ │ │ │ └── local-pack/
│ │ │ │ ├── p.less
│ │ │ │ └── package.js
│ │ │ ├── subdir/
│ │ │ │ ├── foo.import.less
│ │ │ │ └── nested-root.less
│ │ │ └── top.less
│ │ ├── caching-stylus/
│ │ │ ├── .meteor/
│ │ │ │ ├── .gitignore
│ │ │ │ ├── packages
│ │ │ │ └── release
│ │ │ ├── imports/
│ │ │ │ └── dotdot.styl
│ │ │ ├── output.js
│ │ │ ├── packages/
│ │ │ │ └── local-pack/
│ │ │ │ ├── p.styl
│ │ │ │ └── package.js
│ │ │ ├── subdir/
│ │ │ │ ├── foo.import.styl
│ │ │ │ └── nested-root.styl
│ │ │ └── top.styl
│ │ ├── circular-deps/
│ │ │ ├── .meteor/
│ │ │ │ ├── .gitignore
│ │ │ │ ├── packages
│ │ │ │ └── release
│ │ │ ├── packages/
│ │ │ │ ├── first/
│ │ │ │ │ └── package.js
│ │ │ │ ├── first-imply/
│ │ │ │ │ └── package.js
│ │ │ │ ├── first-unordered/
│ │ │ │ │ └── package.js
│ │ │ │ ├── first-weak/
│ │ │ │ │ └── package.js
│ │ │ │ ├── second/
│ │ │ │ │ └── package.js
│ │ │ │ ├── second-imply/
│ │ │ │ │ └── package.js
│ │ │ │ ├── second-unordered/
│ │ │ │ │ └── package.js
│ │ │ │ └── second-weak/
│ │ │ │ └── package.js
│ │ │ └── server/
│ │ │ └── exit123.js
│ │ ├── compiler-plugin-add-asset/
│ │ │ ├── .meteor/
│ │ │ │ ├── .gitignore
│ │ │ │ ├── packages
│ │ │ │ └── release
│ │ │ ├── foo.printme
│ │ │ ├── packages/
│ │ │ │ └── local-plugin/
│ │ │ │ ├── package.js
│ │ │ │ └── plugin.js
│ │ │ └── use-asset.js
│ │ ├── compiler-plugin-asset-and-source/
│ │ │ ├── .meteor/
│ │ │ │ ├── .gitignore
│ │ │ │ ├── packages
│ │ │ │ └── release
│ │ │ └── packages/
│ │ │ └── asset-and-source/
│ │ │ ├── asset-and-source.js
│ │ │ └── package.js
│ │ ├── compiler-plugin-static-html/
│ │ │ ├── .meteor/
│ │ │ │ ├── .gitignore
│ │ │ │ ├── .id
│ │ │ │ ├── packages
│ │ │ │ ├── platforms
│ │ │ │ ├── release
│ │ │ │ └── versions
│ │ │ └── static.html
│ │ ├── compiler-plugin-static-html-error/
│ │ │ ├── .meteor/
│ │ │ │ ├── .gitignore
│ │ │ │ ├── .id
│ │ │ │ ├── packages
│ │ │ │ ├── platforms
│ │ │ │ ├── release
│ │ │ │ └── versions
│ │ │ └── static.html
│ │ ├── compiler-plugin-throws-on-instantiate/
│ │ │ ├── .meteor/
│ │ │ │ ├── .gitignore
│ │ │ │ ├── packages
│ │ │ │ └── release
│ │ │ ├── foo.printme
│ │ │ └── packages/
│ │ │ └── local-plugin/
│ │ │ ├── package.js
│ │ │ └── plugin.js
│ │ ├── css-injection-test/
│ │ │ ├── .meteor/
│ │ │ │ ├── .gitignore
│ │ │ │ ├── packages
│ │ │ │ └── release
│ │ │ ├── css-injection-test.js
│ │ │ └── packages/
│ │ │ └── my-package/
│ │ │ ├── foo.css
│ │ │ └── package.js
│ │ ├── custom-minifier/
│ │ │ ├── .meteor/
│ │ │ │ ├── .gitignore
│ │ │ │ ├── packages
│ │ │ │ └── release
│ │ │ ├── body.html
│ │ │ ├── code.js
│ │ │ ├── packages/
│ │ │ │ └── custom-minifier/
│ │ │ │ ├── package.js
│ │ │ │ └── plugin/
│ │ │ │ └── minify.js
│ │ │ └── styles.css
│ │ ├── ddp-heartbeat/
│ │ │ ├── .meteor/
│ │ │ │ ├── .gitignore
│ │ │ │ ├── packages
│ │ │ │ └── release
│ │ │ └── server/
│ │ │ └── heartbeat_test.js
│ │ ├── debug-only-test/
│ │ │ ├── .meteor/
│ │ │ │ ├── packages
│ │ │ │ └── release
│ │ │ └── packages/
│ │ │ └── debug-only-prod-only/
│ │ │ └── package.js
│ │ ├── dev-bundle-bin-commands/
│ │ │ ├── .meteor/
│ │ │ │ ├── .gitignore
│ │ │ │ ├── packages
│ │ │ │ └── release
│ │ │ └── package.json
│ │ ├── duplicate-compiler-extensions/
│ │ │ ├── .meteor/
│ │ │ │ ├── .gitignore
│ │ │ │ ├── packages
│ │ │ │ └── release
│ │ │ ├── bar.printme
│ │ │ ├── foo.printme
│ │ │ └── packages/
│ │ │ ├── another-local-plugin/
│ │ │ │ ├── package.js
│ │ │ │ └── plugin.js
│ │ │ └── local-plugin/
│ │ │ ├── package.js
│ │ │ └── plugin.js
│ │ ├── empty/
│ │ │ └── .meteor/
│ │ │ ├── .gitignore
│ │ │ ├── packages
│ │ │ └── release
│ │ ├── failover-test/
│ │ │ ├── .meteor/
│ │ │ │ ├── .gitignore
│ │ │ │ ├── packages
│ │ │ │ └── release
│ │ │ └── server/
│ │ │ └── failover-test.js
│ │ ├── hot-code-push-test/
│ │ │ ├── .meteor/
│ │ │ │ ├── .gitignore
│ │ │ │ ├── packages
│ │ │ │ └── release
│ │ │ ├── hot-code-push-test.html
│ │ │ ├── hot-code-push-test.js
│ │ │ └── packages/
│ │ │ └── my-package/
│ │ │ ├── foo.js
│ │ │ └── package.js
│ │ ├── lint-on-publish/
│ │ │ ├── .meteor/
│ │ │ │ ├── .gitignore
│ │ │ │ ├── .id
│ │ │ │ ├── packages
│ │ │ │ ├── platforms
│ │ │ │ └── release
│ │ │ ├── a.js
│ │ │ └── packages/
│ │ │ ├── dep-package/
│ │ │ │ ├── dep-package.js
│ │ │ │ └── package.js
│ │ │ └── my-package/
│ │ │ ├── .versions
│ │ │ ├── my-package.js
│ │ │ └── package.js
│ │ ├── linting-app/
│ │ │ ├── .jshintrc
│ │ │ ├── .meteor/
│ │ │ │ ├── .gitignore
│ │ │ │ ├── packages
│ │ │ │ └── release
│ │ │ ├── a.html
│ │ │ ├── client/
│ │ │ │ └── client.js
│ │ │ ├── packages/
│ │ │ │ └── my-package/
│ │ │ │ ├── .jshintrc
│ │ │ │ ├── .versions
│ │ │ │ ├── package-client.js
│ │ │ │ ├── package-server.js
│ │ │ │ └── package.js
│ │ │ └── server/
│ │ │ └── server.js
│ │ ├── local-compiler-plugin/
│ │ │ ├── .meteor/
│ │ │ │ ├── .gitignore
│ │ │ │ ├── packages
│ │ │ │ └── release
│ │ │ ├── bar.printme
│ │ │ ├── foo.printme
│ │ │ └── packages/
│ │ │ └── local-plugin/
│ │ │ ├── package.js
│ │ │ └── plugin.js
│ │ ├── minification-css-splitting/
│ │ │ ├── .meteor/
│ │ │ │ ├── .gitignore
│ │ │ │ ├── packages
│ │ │ │ └── release
│ │ │ ├── body.html
│ │ │ ├── client/
│ │ │ │ └── lots-of-styles.main.styl
│ │ │ └── code.js
│ │ ├── minifier-plugin-bad-extension/
│ │ │ ├── .meteor/
│ │ │ │ ├── .gitignore
│ │ │ │ ├── packages
│ │ │ │ └── release
│ │ │ └── packages/
│ │ │ └── local-plugin/
│ │ │ ├── package.js
│ │ │ └── plugin.js
│ │ ├── minifier-plugin-multiple-minifiers-for-js/
│ │ │ ├── .meteor/
│ │ │ │ ├── .gitignore
│ │ │ │ ├── packages
│ │ │ │ └── release
│ │ │ └── packages/
│ │ │ ├── local-plugin/
│ │ │ │ ├── package.js
│ │ │ │ └── plugin.js
│ │ │ └── local-plugin-2/
│ │ │ ├── package.js
│ │ │ └── plugin.js
│ │ ├── mobile-platforms/
│ │ │ └── .meteor/
│ │ │ ├── .gitignore
│ │ │ ├── packages
│ │ │ ├── platforms
│ │ │ └── release
│ │ ├── modules/
│ │ │ ├── .gitignore
│ │ │ ├── .meteor/
│ │ │ │ ├── .finished-upgraders
│ │ │ │ ├── .gitignore
│ │ │ │ ├── .id
│ │ │ │ ├── packages
│ │ │ │ ├── platforms
│ │ │ │ ├── release
│ │ │ │ └── versions
│ │ │ ├── README.md
│ │ │ ├── client/
│ │ │ │ ├── compatibility/
│ │ │ │ │ └── bare.js
│ │ │ │ └── only.js
│ │ │ ├── eager-coffee.coffee
│ │ │ ├── eager-jsx.jsx
│ │ │ ├── eager.css
│ │ │ ├── imports/
│ │ │ │ ├── dir/
│ │ │ │ │ ├── index.js
│ │ │ │ │ └── package.json
│ │ │ │ ├── lazy.css
│ │ │ │ ├── lazy.html
│ │ │ │ ├── lazy1.js
│ │ │ │ ├── lazy2.js
│ │ │ │ ├── plain.es5.js
│ │ │ │ ├── return.js
│ │ │ │ └── shared.js
│ │ │ ├── lazy-css.html
│ │ │ ├── lib/
│ │ │ │ ├── a/
│ │ │ │ │ ├── index.js
│ │ │ │ │ └── package.json
│ │ │ │ ├── b/
│ │ │ │ │ └── package.json
│ │ │ │ └── index.js
│ │ │ ├── package.json
│ │ │ ├── packages/
│ │ │ │ ├── client-only-ecmascript/
│ │ │ │ │ ├── README.md
│ │ │ │ │ ├── client.js
│ │ │ │ │ ├── imported.js
│ │ │ │ │ ├── package.js
│ │ │ │ │ └── server.js
│ │ │ │ ├── modules-test-package/
│ │ │ │ │ ├── README.md
│ │ │ │ │ ├── client.js
│ │ │ │ │ ├── common.js
│ │ │ │ │ ├── css/
│ │ │ │ │ │ ├── imported.css
│ │ │ │ │ │ └── not-imported.css
│ │ │ │ │ ├── illegal.html
│ │ │ │ │ ├── os-stub.js
│ │ │ │ │ ├── package.js
│ │ │ │ │ └── server.js
│ │ │ │ └── modules-test-plugin/
│ │ │ │ ├── .babelrc
│ │ │ │ ├── README.md
│ │ │ │ ├── array.arson
│ │ │ │ ├── modules-test-plugin.js
│ │ │ │ ├── one.arson
│ │ │ │ ├── oyez-transform.js
│ │ │ │ ├── package.js
│ │ │ │ └── plugin.js
│ │ │ ├── server/
│ │ │ │ └── only.js
│ │ │ └── tests.js
│ │ ├── npmtest/
│ │ │ ├── .meteor/
│ │ │ │ ├── .gitignore
│ │ │ │ ├── packages
│ │ │ │ └── release
│ │ │ └── packages/
│ │ │ └── npm-test/
│ │ │ ├── npmtest.js
│ │ │ └── package.js
│ │ ├── once/
│ │ │ ├── .meteor/
│ │ │ │ ├── .gitignore
│ │ │ │ ├── packages
│ │ │ │ └── release
│ │ │ └── once.js
│ │ ├── package-stats-tests/
│ │ │ ├── .meteor/
│ │ │ │ ├── .gitignore
│ │ │ │ ├── packages
│ │ │ │ └── release
│ │ │ ├── package-stats-tests.js
│ │ │ └── packages/
│ │ │ ├── local-package/
│ │ │ │ ├── blah.js
│ │ │ │ └── package.js
│ │ │ └── package-stats-opt-out/
│ │ │ └── package.js
│ │ ├── package-tests/
│ │ │ ├── .meteor/
│ │ │ │ ├── .gitignore
│ │ │ │ ├── identifier
│ │ │ │ ├── packages
│ │ │ │ └── release
│ │ │ ├── mobile-config.js
│ │ │ └── packages/
│ │ │ ├── contains-camera-cordova-plugin/
│ │ │ │ └── package.js
│ │ │ ├── contains-cordova-plugin/
│ │ │ │ ├── package.js
│ │ │ │ ├── package2.js
│ │ │ │ └── package3.js
│ │ │ ├── contains-old-cordova-plugin/
│ │ │ │ └── package.js
│ │ │ ├── contains-plugin/
│ │ │ │ ├── .gitignore
│ │ │ │ ├── .npm/
│ │ │ │ │ └── plugin/
│ │ │ │ │ └── compileCoffeescript/
│ │ │ │ │ ├── .gitignore
│ │ │ │ │ ├── README
│ │ │ │ │ └── npm-shrinkwrap.json
│ │ │ │ ├── package.js
│ │ │ │ ├── package2.js
│ │ │ │ └── plugin/
│ │ │ │ └── plugin.js
│ │ │ ├── debug-only/
│ │ │ │ ├── debug-only.js
│ │ │ │ └── package.js
│ │ │ ├── depends-on-plugin/
│ │ │ │ ├── .gitignore
│ │ │ │ ├── foo.js
│ │ │ │ └── package.js
│ │ │ ├── empty-cordova-plugin/
│ │ │ │ ├── package.js
│ │ │ │ └── plugin/
│ │ │ │ ├── plugin.xml
│ │ │ │ ├── src/
│ │ │ │ │ └── android/
│ │ │ │ │ ├── Empty.java
│ │ │ │ │ └── Empty_changed.java
│ │ │ │ └── www/
│ │ │ │ └── Empty.js
│ │ │ ├── no-description/
│ │ │ │ └── package.js
│ │ │ ├── prod-only/
│ │ │ │ ├── package.js
│ │ │ │ └── prod-only.js
│ │ │ ├── say-something/
│ │ │ │ ├── .gitignore
│ │ │ │ ├── foo.js
│ │ │ │ └── package.js
│ │ │ ├── say-something-client-targets/
│ │ │ │ ├── .gitignore
│ │ │ │ ├── all-clients.js
│ │ │ │ ├── browser-client.js
│ │ │ │ ├── cordova-client.js
│ │ │ │ ├── package.js
│ │ │ │ └── server.js
│ │ │ ├── with-add-files/
│ │ │ │ ├── package.js
│ │ │ │ └── with-add-files.js
│ │ │ └── with-main-module/
│ │ │ ├── package.js
│ │ │ └── with-main-module.js
│ │ ├── packageless/
│ │ │ ├── .meteor/
│ │ │ │ ├── .gitignore
│ │ │ │ ├── .id
│ │ │ │ ├── packages
│ │ │ │ ├── platforms
│ │ │ │ └── release
│ │ │ └── packageless.js
│ │ ├── simple-app/
│ │ │ ├── .meteor/
│ │ │ │ ├── .gitignore
│ │ │ │ ├── packages
│ │ │ │ └── release
│ │ │ └── simple.html
│ │ ├── standard-app/
│ │ │ └── .meteor/
│ │ │ ├── .gitignore
│ │ │ ├── packages
│ │ │ └── release
│ │ └── uses-published-package-with-inactive-source/
│ │ ├── .meteor/
│ │ │ ├── .gitignore
│ │ │ ├── packages
│ │ │ └── release
│ │ └── packages/
│ │ └── local-plugin/
│ │ └── package.js
│ ├── autoupdate.js
│ ├── boot-tests.js
│ ├── build-errors.js
│ ├── built-packages/
│ │ └── has-colons.tgz
│ ├── bundle.js
│ ├── colon-converter-tests.js
│ ├── command-line.js
│ ├── compiler-plugins.js
│ ├── constraint-solver.js
│ ├── cordova-builds.js
│ ├── cordova-hcp.js
│ ├── cordova-platforms.js
│ ├── cordova-plugins.js
│ ├── cordova-run.js
│ ├── create.js
│ ├── custom-minifier.js
│ ├── ddp-heartbeat.js
│ ├── dev-bundle-bin-commands.js
│ ├── fake-mongod/
│ │ ├── fake-mongod
│ │ ├── fake-mongod.bat
│ │ └── fake-mongod.js
│ ├── galaxy.js
│ ├── help.js
│ ├── hot-code-push.js
│ ├── js-analyze.js
│ ├── linter-plugins.js
│ ├── login.js
│ ├── meteor-script-link-tests.js
│ ├── minifier-bad-plugins.js
│ ├── modules.js
│ ├── mongo.js
│ ├── npm.js
│ ├── old/
│ │ ├── app-with-package/
│ │ │ ├── .meteor/
│ │ │ │ ├── .gitignore
│ │ │ │ ├── .id
│ │ │ │ └── packages
│ │ │ └── packages/
│ │ │ └── test-package/
│ │ │ ├── dummy.js
│ │ │ └── package.js
│ │ ├── app-with-private/
│ │ │ ├── .meteor/
│ │ │ │ ├── .gitignore
│ │ │ │ ├── .id
│ │ │ │ ├── packages
│ │ │ │ └── release
│ │ │ ├── packages/
│ │ │ │ └── test-package/
│ │ │ │ ├── .gitignore
│ │ │ │ ├── package.js
│ │ │ │ ├── plugin.js
│ │ │ │ ├── test-package.js
│ │ │ │ ├── test-package.txt
│ │ │ │ └── test.notregistered
│ │ │ ├── private/
│ │ │ │ ├── nested/
│ │ │ │ │ └── test.txt
│ │ │ │ └── test.txt
│ │ │ └── test.js
│ │ ├── app-with-public/
│ │ │ ├── .meteor/
│ │ │ │ ├── .gitignore
│ │ │ │ ├── .id
│ │ │ │ ├── packages
│ │ │ │ └── release
│ │ │ └── public/
│ │ │ ├── nested/
│ │ │ │ └── nested.txt
│ │ │ └── test.txt
│ │ ├── cli-test.sh
│ │ ├── empty-app/
│ │ │ └── .meteor/
│ │ │ ├── .gitignore
│ │ │ ├── .id
│ │ │ └── packages
│ │ ├── test-bundler-assets.js
│ │ ├── test-bundler-npm.js
│ │ ├── test-bundler-options.js
│ │ └── test-watch.js
│ ├── old.js
│ ├── organizations.js
│ ├── package-tests.js
│ ├── packages/
│ │ ├── fake-accounts-base/
│ │ │ ├── fake-accounts-base-tests.js
│ │ │ ├── fake-accounts-base.js
│ │ │ └── package.js
│ │ ├── has-colons.tgz
│ │ ├── package-for-show/
│ │ │ ├── README.md
│ │ │ ├── package-completely-empty.js
│ │ │ ├── package-customizable.js
│ │ │ ├── package-rc-version.js
│ │ │ ├── package-with-deps.js
│ │ │ ├── package-with-exports.js
│ │ │ ├── package-with-git.js
│ │ │ ├── package-with-implies.js
│ │ │ └── package.js
│ │ ├── package-of-two-versions/
│ │ │ ├── package-version.js
│ │ │ └── package.js
│ │ ├── package-with-colons/
│ │ │ └── package.js
│ │ └── package-with-npm/
│ │ └── package.js
│ ├── parse-stack-test.js
│ ├── releases.js
│ ├── run.js
│ ├── source-maps.js
│ ├── standard-minification.js
│ ├── static-html.js
│ ├── stylus-cross-packages.js
│ ├── tarball.js
│ ├── test-modes.js
│ ├── utils-tests.js
│ ├── version-parser.js
│ └── wipe-all-packages.js
├── tool-env/
│ ├── README.md
│ ├── cleanup.js
│ ├── install-babel.js
│ ├── install-git-hooks.js
│ ├── install-runtime.js
│ ├── isopackets.js
│ ├── profile-require.js
│ ├── profile.js
│ └── source-map-retriever-stack.js
├── tool-testing/
│ ├── README.md
│ ├── galaxy-utils.js
│ ├── phantom/
│ │ └── open-url.js
│ ├── selftest.js
│ └── test-utils.js
├── upgraders.js
└── utils/
├── archinfo.js
├── buildmessage.js
├── buildmessage.md
├── colon-converter.js
├── fiber-helpers.js
├── func-utils.js
├── http-helpers.js
├── mongo-exit-codes.js
├── parse-stack.js
├── processes.js
└── utils.js
================================================
FILE CONTENTS
================================================
================================================
FILE: .arcconfig
================================================
{
"project_id" : "Meteor framework",
"conduit_uri" : "https://phabricator.meteor.io/",
"immutable_history" : false
}
================================================
FILE: .eslintignore
================================================
android_bundle/
dev_bundle/
docs/
examples/
packages/
scripts/
tools/
!tools/*.js
!tools/isobuild/*.js
!tools/catalog/*.js
!tools/packaging/*.js
!tools/cli/*.js
!tools/runners/*.js
!tools/tool-env/*.js
!tools/fs/*.js
# Below, files that have yet to be converted to match the linter
tools/archinfo.js
tools/auth-client.js
tools/auth.js
tools/buildmessage.js
tools/cleanup.js
tools/colon-converter.js
tools/config.js
tools/console.js
tools/deploy.js
tools/fiber-helpers.js
tools/fs/files.js
tools/fs/mini-files.js
tools/func-utils.js
tools/http-helpers.js
tools/inspector.js
tools/index.js
tools/mongo-exit-codes.js
tools/processes.js
tools/progress.js
tools/project-context.js
tools/runners/run-log.js
tools/fs/safe-pathwatcher.js
tools/selftest.js
tools/service-connection.js
tools/shell-client.js
tools/stats.js
tools/test-utils.js
tools/upgraders.js
tools/utils/utils.js
tools/fs/watch.js
tools/catalog/catalog-local.js
tools/catalog/catalog-remote.js
tools/catalog/catalog.js
tools/catalog/catalog-utils.js
tools/cli/commands-cordova.js
tools/cli/commands-packages-query.js
tools/cli/commands-packages.js
tools/cli/commands.js
tools/cli/main.js
tools/tool-env/flush-buffers-on-exit-in-windows.js
tools/tool-env/install-babel.js
tools/tool-env/isopackets.js
tools/tool-env/profile-require.js
tools/tool-env/profile.js
tools/runners/run-all.js
tools/runners/run-app.js
tools/runners/run-mongo.js
tools/runners/run-proxy.js
tools/runners/run-selenium.js
tools/runners/run-updater.js
tools/packaging/package-client.js
tools/packaging/package-map.js
tools/packaging/package-version-parser.js
tools/packaging/release.js
tools/packaging/tropohouse.js
tools/packaging/updater.js
tools/packaging/warehouse.js
tools/isobuild/build-plugin.js
tools/isobuild/builder.js
tools/isobuild/bundler.js
tools/isobuild/compiler-deprecated-compile-step.js
tools/isobuild/compiler-plugin.js
tools/isobuild/compiler.js
tools/isobuild/import-scanner.js
tools/isobuild/isopack-cache.js
tools/isobuild/isopack.js
tools/isobuild/js-analyze.js
tools/isobuild/linker.js
tools/isobuild/linter-plugin.js
tools/isobuild/meteor-npm.js
tools/isobuild/npm-discards.js
tools/isobuild/package-api.js
tools/isobuild/package-source.js
tools/isobuild/source-arch.js
================================================
FILE: .github/ISSUE_TEMPLATE.md
================================================
## README and "Good to know" section
Please make sure that you carefully have read the [README](https://github.com/4commerce-technologies-AG/meteor) and especially had a look to its "Good to know" section before filing a new issue.
## Guide and tutorial issues
Some of the already known issues may also help as a guide or tutorial. Please check out our [_guide flagged issues_](https://github.com/4commerce-technologies-AG/meteor/issues?q=+label%3Aflag%3Aguide+) if your question may already be answered.
Thanks for supporting this project
================================================
FILE: .github/PULL_REQUEST_TEMPLATE.md
================================================
Describe the big picture of your changes here to communicate to the maintainers why we should accept this pull request. If it fixes a bug or resolves a feature request, be sure to link to that issue.
Note that we are unlikely to accept pull requests that add features without prior discussion. The best way to propose a feature is to open an issue first and discuss your ideas there before implementing them.
================================================
FILE: .gitignore
================================================
.DS_Store
/.meteor
*~
/dev_bundle
/dev_bundle*.tar.gz
/android_bundle
/android_bundle*.tar.gz
/node_*.tar.gz
/mongo_*.tar.gz
/dist
\#*\#
.\#*
.idea
*.iml
*.sublime-project
*.sublime-workspace
TAGS
*.log
*.out
npm-debug.log
universe
.babel-cache
mongo-test-output
================================================
FILE: .mailmap
================================================
# The presence of this file makes it easier to find GitHub usernames for
# History.md.
#
# This is a git dotfile that affects the output of 'git shortlog'. eg, run:
# git shortlog -s release/METEOR@1.0.1..HEAD
# to get a sorted list of all committers to revisions in HEAD but not
# in 1.0.1. To get the list including email addresses (useful for input
# to the script below) include --email as well.
#
# For any emails that show up in the shortlog that aren't in one of
# these lists, figure out their GitHub username and add them.
#
# A command-line way to get the GitHub username for an author:
# scripts/admin/find-author-github.sh 'User Name
### README and "Good to know" section
Please make sure that you carefully have read the [README](https://github.com/4commerce-technologies-AG/meteor) and especially had a look to its "Good to know" section.
### Guide and tutorial issues
Some of the already known issues may also help as a guide or tutorial. Please check out our [_guide flagged issues_](https://github.com/4commerce-technologies-AG/meteor/issues?q=+label%3Aflag%3Aguide+) if your question may already be answered.
### Reporting a bug in Meteor universal fork
We welcome bug reports. If you've found a new bug in running Meteor universal fork please file a report in [our issue tracker](https://github.com/4commerce-technologies-AG/meteor/issues). Before you file your issue, please look twice to see if it has already been reported or solved. If so, comment, up-vote or +1 the existing, even closed, issue to show that it's affecting multiple people.
# Contributing to official Meteor project
Please check contribution for Meteor at the official project and
[read about their rules](https://github.com/meteor/meteor/blob/devel/Contributing.md)
================================================
FILE: History.md
================================================
## v.NEXT
## v1.3.4.1
* Increased the default HTTP timeout for requests made by the `meteor`
command-line tool to 60 seconds (previously 30), and [disabled the
timeout completely for Galaxy
deploys](https://forums.meteor.com/t/1-3-4-breaks-galaxy-deployment-etimedout/25383/).
* Minor bug fixes: [#7281](https://github.com/meteor/meteor/pull/7281)
[#7276](https://github.com/meteor/meteor/pull/7276)
## v1.3.4
* The version of `npm` used by `meteor npm` and when installing
`Npm.depends` dependencies of Meteor packages has been upgraded from
2.15.1 to **3.9.6**, which should lead to much flatter node_modules
dependency trees.
* The `meteor-babel` npm package has been upgraded to 0.11.6, and is now
installed using `npm@3.9.6`, fixing bugs arising from Windows path
limits, such as [#7247](https://github.com/meteor/meteor/issues/7247).
* The `reify` npm package has been upgraded to 0.3.4, fixing
[#7250](https://github.com/meteor/meteor/issues/7250).
* Thanks to caching improvements for the
`files.{stat,lstat,readdir,realpath}` methods and
`PackageSource#_findSources`, development server restart times are no
longer proportional to the number of files in `node_modules`
directories. [#7253](https://github.com/meteor/meteor/issues/7253)
[#7008](https://github.com/meteor/meteor/issues/7008)
* When installed via `InstallMeteor.exe` on Windows, Meteor can now be
easily uninstalled through the "Programs and Features" control panel.
* HTTP requests made by the `meteor` command-line tool now have a timeout
of 30 seconds, which can be adjusted by the `$TIMEOUT_SCALE_FACTOR`
environment variable. [#7143](https://github.com/meteor/meteor/pull/7143)
* The `request` npm dependency of the `http` package has been upgraded
from 2.53.0 to 2.72.0.
* The `--headless` option is now supported by `meteor test` and
`meteor test-packages`, in addition to `meteor self-test`.
[#7245](https://github.com/meteor/meteor/pull/7245)
* Miscellaneous fixed bugs:
[#7255](https://github.com/meteor/meteor/pull/7255)
[#7239](https://github.com/meteor/meteor/pull/7239)
## v1.3.3.1
* Fixed bugs:
[#7226](https://github.com/meteor/meteor/pull/7226)
[#7181](https://github.com/meteor/meteor/pull/7181)
[#7221](https://github.com/meteor/meteor/pull/7221)
[#7215](https://github.com/meteor/meteor/pull/7215)
[#7217](https://github.com/meteor/meteor/pull/7217)
* The `node-aes-gcm` npm package used by `oauth-encryption` has been
upgraded to 0.1.5. [#7217](https://github.com/meteor/meteor/issues/7217)
* The `reify` module compiler has been upgraded to 0.3.3.
* The `meteor-babel` package has been upgraded to 0.11.4.
* The `pathwatcher` npm package has been upgraded to 6.7.0.
* In CoffeeScript files with raw JavaScript enclosed by backticks, the
compiled JS will no longer contain `require` calls inserted by Babel.
[#7226](https://github.com/meteor/meteor/issues/7226)
* Code related to the Velocity testing system has been removed.
[#7235](https://github.com/meteor/meteor/pull/7235)
* Allow smtps:// in MAIL_URL [#7043](https://github.com/meteor/meteor/pull/7043)
* Adds `Accounts.onLogout()` a hook directly analogous to `Accounts.onLogin()`. [PR #6889](https://github.com/meteor/meteor/pull/6889)
## v1.3.3
* Node has been upgraded from 0.10.43 to 0.10.45.
* `npm` has been upgraded from 2.14.22 to 2.15.1.
* The `fibers` package has been upgraded to 1.0.13.
* The `meteor-babel` package has been upgraded to 0.10.9.
* The `meteor-promise` package has been upgraded to 0.7.1, a breaking
change for code that uses `Promise.denodeify`, `Promise.nodeify`,
`Function.prototype.async`, or `Function.prototype.asyncApply`, since
those APIs have been removed.
* Meteor packages with binary npm dependencies are now automatically
rebuilt using `npm rebuild` whenever the version of Node or V8 changes,
making it much simpler to use Meteor with different versions of Node.
5dc51d39ecc9e8e342884f3b4f8a489f734b4352
* `*.min.js` files are no longer minified during the build process.
[PR #6986](https://github.com/meteor/meteor/pull/6986) [Issue #5363](https://github.com/meteor/meteor/issues/5363)
* You can now pick where the `.meteor/local` directory is created by setting the `METEOR_LOCAL_DIR` environment variable. This lets you run multiple instances of the same Meteor app.
[PR #6760](https://github.com/meteor/meteor/pull/6760) [Issue #6532](https://github.com/meteor/meteor/issues/6532)
* Allow using authType in Facebook login [PR #5694](https://github.com/meteor/meteor/pull/5694)
* Adds flush() method to Tracker to force recomputation [PR #4710](https://github.com/meteor/meteor/pull/4710)
* Adds `defineMutationMethods` option (default: true) to `new Mongo.Collection` to override default behavior that sets up mutation methods (/collection/[insert|update...]) [PR #5778](https://github.com/meteor/meteor/pull/5778)
* Allow overridding the default warehouse url by specifying `METEOR_WAREHOUSE_URLBASE` [PR #7054](https://github.com/meteor/meteor/pull/7054)
* Allow `_id` in `$setOnInsert` in Minimongo: https://github.com/meteor/meteor/pull/7066
* Added support for `$eq` to Minimongo: https://github.com/meteor/meteor/pull/4235
* Insert a `Date` header into emails by default: https://github.com/meteor/meteor/pull/6916/files
* `meteor test` now supports setting the bind address using `--port IP:PORT` the same as `meteor run` [PR #6964](https://github.com/meteor/meteor/pull/6964) [Issue #6961](https://github.com/meteor/meteor/issues/6961)
* `Meteor.apply` now takes a `noRetry` option to opt-out of automatically retrying non-idempotent methods on connection blips: [PR #6180](https://github.com/meteor/meteor/pull/6180)
* DDP callbacks are now batched on the client side. This means that after a DDP message arrives, the local DDP client will batch changes for a minimum of 5ms (configurable via `bufferedWritesInterval`) and a maximum of 500ms (configurable via `bufferedWritesMaxAge`) before calling any callbacks (such as cursor observe callbacks).
* PhantomJS is no longer included in the Meteor dev bundle (#6905). If you
previously relied on PhantomJS for local testing, the `spiderable`
package, Velocity tests, or testing Meteor from a checkout, you should
now install PhantomJS yourself, by running the following commmand:
`meteor npm install -g phantomjs-prebuilt`
* The `babel-compiler` package now looks for `.babelrc` files and
`package.json` files with a "babel" section. If found, these files may
contribute additional Babel transforms that run before the usual
`babel-preset-meteor` set of transforms. In other words, if you don't
like the way `babel-preset-meteor` handles a particular kind of syntax,
you can add your preferred transform plugins to the "presets" or
"plugins" section of your `.babelrc` or `package.json` file. #6351
* When `BabelCompiler` cannot resolve a Babel plugin or preset package in
`.babelrc` or `package.json`, it now merely warns instead of
crashing. #7179
* Compiler plugins can now import npm packages that are visible to their
input files using `inputFile.require(id)`. b16e8d50194b37d3511889b316345f31d689b020
* `import` statements in application modules now declare normal variables
for the symbols that are imported, making it significantly easier to
inspect imported variables when debugging in the browser console or in
`meteor shell`.
* `import` statements in application modules are no longer restricted to
the top level, and may now appear inside conditional statements
(e.g. `if (Meteor.isServer) { import ... }`) or in nested scopes.
* `import` statements now work as expected in `meteor shell`. #6271
* Commands installed in `dev_bundle/lib/node_modules/.bin` (such as
`node-gyp` and `node-pre-gyp`) are now available to scripts run by
`meteor npm`. e95dfe410e1b43e8131bc2df9d2c29decdd1eaf6
* When building an application using `meteor build`, "devDependencies"
listed in `package.json` are no longer copied into the bundle. #6750
* Packages tested with `meteor test-packages` now have access to local
`node_modules` directories installed in the parent application or in the
package directory itself. #6827
* You no longer need to specify `DEPLOY_HOSTNAME=galaxy.meteor.com` to run
`meteor deploy` (and similar commands) against Galaxy. The AWS us-east-1
Galaxy is now the default for `DEPLOY_HOSTNAME`. If your app's DNS points to
another Galaxy region, `meteor deploy` will detect that automatically as
well. #7055
* The `coffeescript` plugin now passes raw JavaScript code enclosed by
back-ticks to `BabelCompiler`, enabling all ECMAScript features
(including `import` and `export`) within CoffeeScript. #6000 #6691
* The `coffeescript` package now implies the same runtime environment as
`ecmascript` (`ecmascript-runtime`, `babel-runtime`, and `promise`, but
not `modules`). #7184
* When Meteor packages install `npm` dependencies, the
`process.env.NPM_CONFIG_REGISTRY` environment variable is now
respected. #7162
* `files.rename` now always executes synchronously. 9856d1d418a4d19c0adf22ec9a92f7ce81a23b05
* "Bare" files contained by `client/compatibility/` directories or added
with `api.addFiles(path, ..., { bare: true })` are no longer compiled by
Babel. https://github.com/meteor/meteor/pull/7033#issuecomment-225126778
* Miscellaneous fixed bugs: #6877 #6843 #6881
## v1.3.2.4
> Meteor 1.3.2.4 was published because publishing 1.3.2.3 failed in an
unrecoverable way. Meteor 1.3.2.4 contains no additional changes beyond
the changes in 1.3.2.3.
## v1.3.2.3
* Reverted accidental changes included in 1.3.2.1 and 1.3.2.2 that
improved DDP performance by batching updates, but broke some packages
that relied on private methods of the DDP client Connection class. See
https://github.com/meteor/meteor/pull/5680 for more details. These
changes will be reinstated in 1.3.3.
## v1.3.2.2
* Fixed bugs #6819 and #6831.
## v1.3.2.1
* Fixed faulty comparison of `.sourcePath` and `.targetPath` properties of
files scanned by the `ImportScanner`, which caused problems for apps
using the `tap:i18n` package. 6e792a7cf25847b8cd5d5664a0ff45c9fffd9e57
## v1.3.2
* The `meteor/meteor` repository now includes a `Roadmap.md` file:
https://github.com/meteor/meteor/blob/devel/Roadmap.md
* Running `npm install` in `bundle/programs/server` when deploying an app
also rebuilds any binary npm dependencies, fixing #6537. Set
METEOR_SKIP_NPM_REBUILD=1 to disable this behavior if necessary.
* Non-.js(on) files in `node_modules` (such as `.less` and `.scss`) are
now processed by compiler plugins and may be imported by JS. #6037
* The `jquery` package can now be completely removed from any app (#6563),
and uses `
To get started, create a new todo list in the left sidebar by typing its name in the text box. Select a list by clicking on its name, and rename by double clicking. The active list appears in the main window pane. You can do the usual here: add items, check them off as completed, and destroy items. You can also tag items with one or more tags, by clicking the blue Add new tag button to the right. All your in-use tags appear at the top. You can filter the list items by selecting a tag, or click the leftmost button to return to the full list.
Inspired by Backbone's Todo Demo, with credit to Jérôme Gravel-Niquet.
================================================ FILE: examples/unfinished/todos-backbone/client/todos.js ================================================ // An example Backbone application contributed by // [Jérôme Gravel-Niquet](http://jgn.me/). This demo uses a simple // [LocalStorage adapter](backbone-localstorage.html) // to persist Backbone models within your browser. // Load the application once the DOM is ready, using `jQuery.ready`: $(function(){ // ask for all the todos in my cache Meteor.subscribe('todos'); // helper functions function all () { return Todos.find(); }; function all_done () { return Todos.find({done: true}); }; function all_remaining () { return Todos.find({done: false}); }; function nextOrder () { var todos = Todos.find({}, {sort: {order: -1}, limit: 1}); return todos[0] ? todos[0].order + 1 : 1; }; // Todo Item View // -------------- // The DOM element for a todo item... window.TodoView = Backbone.View.extend({ //... is a list tag. tagName: "li", // Cache the template function for a single item. template: _.template($('#item-template').html()), // The DOM events specific to an item. events: { "click .check" : "toggleDone", "dblclick div.todo-text" : "edit", "click span.todo-destroy" : "clear", "keypress .todo-input" : "updateOnEnter" }, // Re-render the contents of the todo item. render: function() { $(this.el).html(this.template(this.model)); this.setText(); return this; }, // To avoid XSS (not that it would be harmful in this particular app), // we use `jQuery.text` to set the contents of the todo item. setText: function() { this.$('.todo-text').text(this.model.text); this.input = this.$('.todo-input'); this.input.bind('blur', _.bind(this.close, this)).val(this.model.text); }, // Toggle the `"done"` state of the object. toggleDone: function() { Todos.update(this.model._id, {$set: {done: !this.model.done}}); }, // Switch this view into `"editing"` mode, displaying the input field. edit: function() { $(this.el).addClass("editing"); this.input.focus(); }, // Close the `"editing"` mode, saving changes to the todo. // findLive callback will update this view. close: function() { Todos.update(this.model._id, {$set: {text: this.input.val()}}); $(this.el).removeClass("editing"); }, // If you hit `enter`, we're through editing the item. updateOnEnter: function(e) { if (e.keyCode == 13) this.close(); }, // Remove this view from the DOM. remove: function() { $(this.el).remove(); }, // destroy the todo object. the findLive callback will g/c this view. clear: function() { Todos.remove(this.model._id); } }); // The Application // --------------- // Our overall **AppView** is the top-level piece of UI. window.AppView = Backbone.View.extend({ // Instead of generating a new element, bind to the existing skeleton of // the App already present in the HTML. el: $("#todoapp"), // Our template for the line of statistics at the bottom of the app. statsTemplate: _.template($('#stats-template').html()), // Delegated events for creating new items, and clearing done ones. events: { "keypress #new-todo": "createOnEnter", "keyup #new-todo": "showTooltip", "click .todo-clear a": "clearCompleted" }, todos: [], // At initialization we bind to the relevant events on the `Todos` // collection, when items are added or changed. Kick things off by // loading any preexisting todos that might be saved in *localStorage*. initialize: function() { var self = this; this.input = this.$("#new-todo"); // spin up the live query. ignore the return value since we never // stop the query. Todos.findLive({}, { added: function (obj, before_idx) { // add a view node to the DOM var view = new TodoView({model: obj}); self.todos.splice(before_idx, 0, view); self.$("#todo-list").append(view.render().el); self.render(); }, removed: function (obj, at_idx) { // remove the view node from the DOM var view = self.todos.splice(at_idx, 1); view[0].remove(); self.render(); }, changed: function (obj, at_idx) { // update obj in existing view and rerender self.todos[at_idx].model = obj; self.todos[at_idx].render(); self.render(); }, moved: function (old_idx, new_idx) { // unimplemented -- items don't ever move }, sort: {'order': 1} }); }, // Re-rendering the App just means refreshing the statistics -- the rest // of the app doesn't change. render: function() { console.log("RENDER", all().length, all_done().length, all_remaining().length); this.$('#todo-stats').html(this.statsTemplate({ total: all().length, done: all_done().length, remaining: all_remaining().length })); }, // If you hit return in the main input field, and there is text to save, // create new **Todo** model. createOnEnter: function(e) { var text = this.input.val(); if (!text || e.keyCode != 13) return; Todos.insert({text: text, done: false, order: nextOrder()}); this.input.val(''); }, // Clear all done todo items, destroying their models. clearCompleted: function() { _.each(all_done(), function (todo) { Todos.remove(todo._id); }); return false; }, // Lazily show the tooltip that tells you to press `enter` to save // a new todo item, after one second. showTooltip: function(e) { var tooltip = this.$(".ui-tooltip-top"); var val = this.input.val(); tooltip.fadeOut(); if (this.tooltipTimeout) clearTimeout(this.tooltipTimeout); if (val == '' || val == this.input.attr('placeholder')) return; var show = function(){ tooltip.show().fadeIn(); }; this.tooltipTimeout = _.delay(show, 1000); } }); // Finally, we kick things off by creating the **App**. window.App = new AppView; }); ================================================ FILE: examples/unfinished/todos-backbone/common.js ================================================ Todos = new Mongo.Collection("todos"); //Todos.schema({text: String, done: Boolean, order: Number}); if (Meteor.isServer) { Meteor.publish('todos', function () { return Todos.find(); }); } ================================================ FILE: examples/unfinished/todos-backbone/todos.css ================================================ html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, font, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td { margin: 0; padding: 0; border: 0; outline: 0; font-weight: inherit; font-style: inherit; font-size: 100%; font-family: inherit; vertical-align: baseline; } body { line-height: 1; color: black; background: white; } ol, ul { list-style: none; } a img { border: none; } html { background: #eeeeee; } body { font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; font-size: 14px; line-height: 1.4em; background: #eeeeee; color: #333333; } #todoapp { width: 480px; margin: 0 auto 40px; background: white; padding: 20px; -moz-box-shadow: rgba(0, 0, 0, 0.2) 0 5px 6px 0; -webkit-box-shadow: rgba(0, 0, 0, 0.2) 0 5px 6px 0; -o-box-shadow: rgba(0, 0, 0, 0.2) 0 5px 6px 0; box-shadow: rgba(0, 0, 0, 0.2) 0 5px 6px 0; } #todoapp h1 { font-size: 36px; font-weight: bold; text-align: center; padding: 20px 0 30px 0; line-height: 1; } #create-todo { position: relative; } #create-todo input { width: 466px; font-size: 24px; font-family: inherit; line-height: 1.4em; border: 0; outline: none; padding: 6px; border: 1px solid #999999; -moz-box-shadow: rgba(0, 0, 0, 0.2) 0 1px 2px 0 inset; -webkit-box-shadow: rgba(0, 0, 0, 0.2) 0 1px 2px 0 inset; -o-box-shadow: rgba(0, 0, 0, 0.2) 0 1px 2px 0 inset; box-shadow: rgba(0, 0, 0, 0.2) 0 1px 2px 0 inset; } #create-todo input::-webkit-input-placeholder { font-style: italic; } #create-todo span { position: absolute; z-index: 999; width: 170px; left: 50%; margin-left: -85px; } #todo-list { margin-top: 10px; } #todo-list li { padding: 12px 20px 11px 0; position: relative; font-size: 24px; line-height: 1.1em; border-bottom: 1px solid #cccccc; } #todo-list li:after { content: "\0020"; display: block; height: 0; clear: both; overflow: hidden; visibility: hidden; } #todo-list li.editing { padding: 0; border-bottom: 0; } #todo-list .editing .display, #todo-list .edit { display: none; } #todo-list .editing .edit { display: block; } #todo-list .editing input { width: 444px; font-size: 24px; font-family: inherit; margin: 0; line-height: 1.6em; border: 0; outline: none; padding: 10px 7px 0px 27px; border: 1px solid #999999; -moz-box-shadow: rgba(0, 0, 0, 0.2) 0 1px 2px 0 inset; -webkit-box-shadow: rgba(0, 0, 0, 0.2) 0 1px 2px 0 inset; -o-box-shadow: rgba(0, 0, 0, 0.2) 0 1px 2px 0 inset; box-shadow: rgba(0, 0, 0, 0.2) 0 1px 2px 0 inset; } #todo-list .check { position: relative; top: 9px; margin: 0 10px 0 7px; float: left; } #todo-list .done .todo-text { text-decoration: line-through; color: #777777; } #todo-list .todo-destroy { position: absolute; right: 5px; top: 14px; display: none; cursor: pointer; width: 20px; height: 20px; background: url(destroy.png) no-repeat 0 0; } #todo-list li:hover .todo-destroy { display: block; } #todo-list .todo-destroy:hover { background-position: 0 -20px; } #todo-stats { *zoom: 1; margin-top: 10px; color: #777777; } #todo-stats:after { content: "\0020"; display: block; height: 0; clear: both; overflow: hidden; visibility: hidden; } #todo-stats .todo-count { float: left; } #todo-stats .todo-count .number { font-weight: bold; color: #333333; } #todo-stats .todo-clear { float: right; } #todo-stats .todo-clear a { color: #777777; font-size: 12px; } #todo-stats .todo-clear a:visited { color: #777777; } #todo-stats .todo-clear a:hover { color: #336699; } #instructions { width: 520px; margin: 10px auto; color: #777777; text-shadow: rgba(255, 255, 255, 0.8) 0 1px 0; text-align: center; } #instructions a { color: #336699; } #credits { width: 520px; margin: 30px auto; color: #999; text-shadow: rgba(255, 255, 255, 0.8) 0 1px 0; text-align: center; } #credits a { color: #888; } /* * François 'cahnory' Germain */ .ui-tooltip, .ui-tooltip-top, .ui-tooltip-right, .ui-tooltip-bottom, .ui-tooltip-left { color:#ffffff; cursor:normal; display:-moz-inline-stack; display:inline-block; font-size:12px; font-family:arial; padding:.5em 1em; position:relative; text-align:center; text-shadow:0 -1px 1px #111111; -webkit-border-top-left-radius:4px ; -webkit-border-top-right-radius:4px ; -webkit-border-bottom-right-radius:4px ; -webkit-border-bottom-left-radius:4px ; -khtml-border-top-left-radius:4px ; -khtml-border-top-right-radius:4px ; -khtml-border-bottom-right-radius:4px ; -khtml-border-bottom-left-radius:4px ; -moz-border-radius-topleft:4px ; -moz-border-radius-topright:4px ; -moz-border-radius-bottomright:4px ; -moz-border-radius-bottomleft:4px ; border-top-left-radius:4px ; border-top-right-radius:4px ; border-bottom-right-radius:4px ; border-bottom-left-radius:4px ; -o-box-shadow:0 1px 2px #000000, inset 0 0 0 1px #222222, inset 0 2px #666666, inset 0 -2px 2px #444444; -moz-box-shadow:0 1px 2px #000000, inset 0 0 0 1px #222222, inset 0 2px #666666, inset 0 -2px 2px #444444; -khtml-box-shadow:0 1px 2px #000000, inset 0 0 0 1px #222222, inset 0 2px #666666, inset 0 -2px 2px #444444; -webkit-box-shadow:0 1px 2px #000000, inset 0 0 0 1px #222222, inset 0 2px #666666, inset 0 -2px 2px #444444; box-shadow:0 1px 2px #000000, inset 0 0 0 1px #222222, inset 0 2px #666666, inset 0 -2px 2px #444444; background-color:#3b3b3b; background-image:-moz-linear-gradient(top,#555555,#222222); background-image:-webkit-gradient(linear,left top,left bottom,color-stop(0,#555555),color-stop(1,#222222)); filter:progid:DXImageTransform.Microsoft.gradient(startColorStr=#555555,EndColorStr=#222222); -ms-filter:progid:DXImageTransform.Microsoft.gradient(startColorStr=#555555,EndColorStr=#222222); } .ui-tooltip:after, .ui-tooltip-top:after, .ui-tooltip-right:after, .ui-tooltip-bottom:after, .ui-tooltip-left:after { content:"\25B8"; display:block; font-size:2em; height:0; line-height:0; position:absolute; } .ui-tooltip:after, .ui-tooltip-bottom:after { color:#2a2a2a; bottom:0; left:1px; text-align:center; text-shadow:1px 0 2px #000000; -o-transform:rotate(90deg); -moz-transform:rotate(90deg); -khtml-transform:rotate(90deg); -webkit-transform:rotate(90deg); width:100%; } .ui-tooltip-top:after { bottom:auto; color:#4f4f4f; left:-2px; top:0; text-align:center; text-shadow:none; -o-transform:rotate(-90deg); -moz-transform:rotate(-90deg); -khtml-transform:rotate(-90deg); -webkit-transform:rotate(-90deg); width:100%; } .ui-tooltip-right:after { color:#222222; right:-0.375em; top:50%; margin-top:-.05em; text-shadow:0 1px 2px #000000; -o-transform:rotate(0); -moz-transform:rotate(0); -khtml-transform:rotate(0); -webkit-transform:rotate(0); } .ui-tooltip-left:after { color:#222222; left:-0.375em; top:50%; margin-top:.1em; text-shadow:0 -1px 2px #000000; -o-transform:rotate(180deg); -moz-transform:rotate(180deg); -khtml-transform:rotate(180deg); -webkit-transform:rotate(180deg); } ================================================ FILE: examples/unfinished/todos-underscore/.meteor/.gitignore ================================================ local ================================================ FILE: examples/unfinished/todos-underscore/.meteor/packages ================================================ # Meteor packages used by this project, one per line. # # 'meteor add' and 'meteor remove' will edit this file for you, # but you can also edit it by hand. jquery jquery-layout jquery-history standard-app-packages ================================================ FILE: examples/unfinished/todos-underscore/.meteor/release ================================================ 0.6.0 ================================================ FILE: examples/unfinished/todos-underscore/body.html ================================================
================================================
FILE: examples/unfinished/todos-underscore/client/client.js
================================================
// quick jquery extension to bind text inputs to blur and RET.
$.fn.onBlurOrEnter = function (callback) {
this.bind('blur', callback);
this.bind('keypress', function (evt) {
if (evt.keyCode === 13 && $(this).val())
callback.call(this, evt);
});
};
// everything else happens after DOM is ready
$(function () {
$('body').layout({north__minSize: 50,
spacing_open: 10,
north__fxSettings: { direction: "vertical" }});
// cache the template function for a single item.
var item_template = _.template($('#item-template').html());
// this render function could be replaced with a handlebars
// template. underscore template isn't safe for user-entered data
// like the item text (XSS).
function renderItem (obj) {
// generate template for todo
var elt = $(item_template(obj));
// set text through jquery for XSS protection
elt.find('.todo-text').text(obj.text);
// clicking the checkbox toggles done state
elt.find('.check').click(function () {
Todos.update(obj._id, {$set: {done: !obj.done}});
});
// clicking destroy button removes the item
elt.find('.destroy').click(function () {
Todos.remove(obj._id);
});
// wire up tag destruction links
elt.find('.tag .remove').click(function () {
var tag = $(this).attr('name');
$(this).parent().fadeOut(500, function () {
Todos.update(obj._id, {$pull: {tags: tag}});
});
});
// wire up add tag
elt.find('.addtag').click(function () {
$(this).hide();
elt.find('.edittag').show();
elt.find('.edittag input').focus();
});
// wire up edit tag
elt.find('.edittag input').onBlurOrEnter(function () {
elt.find('.edittag').hide();
elt.find('.addtag').show();
if ($(this).val() !== '')
Todos.update(obj._id, {$addToSet: {tags: $(this).val()}});
});
// doubleclick on todo text brings up the editor
elt.find('.todo-text').dblclick(function () {
elt.addClass('editing');
var input = elt.find('.todo-input');
input.val(obj.text);
input.focus();
input.select();
input.onBlurOrEnter(function () {
elt.removeClass('editing');
if ($(this).val() !== '')
Todos.update(obj._id, {$set: {text: elt.find('.todo-input').val()}});
});
});
return elt[0];
};
// construct new todo from text box
$('#new-todo').bind('keypress', function (evt) {
var list_id = Session.get('list_id');
var tag = Session.get('tag_filter');
// prevent creation of a new todo if nothing is selected
if (!list_id) return;
var text = $('#new-todo').val();
if (evt.keyCode === 13 && text) {
var obj = {text: text,
list_id: list_id,
done: false,
timestamp: (new Date()).getTime()};
if (tag) obj.tags = [tag];
Todos.insert(obj);
$('#new-todo').val('');
}
});
var current_list_stop;
function setCurrentList (list_id) {
Session.set('list_id', list_id);
$('#items-view').show();
// kill current findLive render
if (current_list_stop)
current_list_stop.stop();
var query = {list_id: list_id};
if (Session.get('tag_filter'))
query.tags = Session.get('tag_filter')
// render individual todo list, stash kill function
current_list_stop =
Meteor.ui.renderList(Todos, $('#item-list'), {
selector: query,
sort: {timestamp: 1},
render: renderItem,
events: {}
});
};
// render list of lists in the left sidebar.
Meteor.ui.renderList(Lists, $('#lists'), {
sort: {name: 1},
template: $('#list-template'),
events: {
'click': function (evt) {
window.History.pushState({list_id: this._id},
"Todos: " + this.name,
"/" + this._id);
},
'dblclick': function (evt) {
var list_elt = $(evt.currentTarget);
var input = list_elt.find('.list-name-input');
list_elt.addClass('editing');
input.val(this.name);
input.focus();
input.select();
var _id = this._id;
input.onBlurOrEnter(function () {
list_elt.removeClass('editing');
if (input.val() !== '')
Lists.update(_id, {$set: {name: input.val()}});
});
}
}
});
// construct new todo list from text box
$('#new-list').bind('keypress', function (evt) {
var text = $('#new-list').val();
if (evt.keyCode === 13 && text) {
var list = Lists.insert({name: text});
$('#new-list').val('');
window.History.pushState({list_id: list._id},
"Todos: " + list.name,
"/" + list._id);
}
});
// tags and filters
// the tag filter bar is easy to generate using a simple
// renderList() against a minimongo query. since minimongo doesn't
// support aggregate queries, construct a local collection to serve
// the same purpose, and drive the renderList() off of it.
var LocalTags = new Mongo.Collection;
(function () {
function updateLocalTags() {
var real = _(Todos.find()).chain().pluck('tags').compact().flatten().uniq().value();
real.unshift(null); // XXX fake tag
var computed = _(LocalTags.find()).pluck('tag');
_.each(_.difference(real, computed), function (new_tag) {
LocalTags.insert({tag: new_tag});
});
_.each(_.difference(computed, real), function (dead_tag) {
LocalTags.remove({tag: dead_tag});
});
};
Todos.findLive({}, {
added: function (obj, before_idx) { _.defer(updateLocalTags); },
removed: function (id, at_idx) { _.defer(updateLocalTags); },
changed: function (obj, at_idx) { _.defer(updateLocalTags); },
});
})();
// findLive() against the computed tag table. since we also want a
// show-all button, arrange for the computed table to always include
// a null placeholder tag, and for the template to render that as
// "Show all". always begin the user session with a null filter.
Session.set('tag_filter', null);
Meteor.ui.renderList(LocalTags, $('#tag-filter'), {
sort: {tag: 1},
template: $('#tag-filter-template'),
events: {
'click': function (evt) {
if (Session.equals('tag_filter', this.tag))
Session.set('tag_filter', null);
else
Session.set('tag_filter', this.tag);
setCurrentList(Session.get('list_id'));
}
}
});
// load list on statechange (which we drive from several places).
window.History.Adapter.bind(window, 'statechange', function () {
var state = window.History.getState();
var list = Lists.find(state.data.list_id);
setCurrentList(list._id);
});
// subscribe to all available todo lists. once the inital load
// completes, navigate to the list specified by URL, if any.
Meteor.subscribe('lists', function () {
var initial_list_id = window.location.pathname.split('/')[1];
var list;
if (initial_list_id) {
list = Lists.find(initial_list_id);
} else {
var lists = Lists.find({}, {sort: {name: 1}, limit: 1});
list = lists[0];
}
if (list) {
window.History.replaceState({list_id: list._id},
"Todos: " + list.name,
"/" + list._id);
// replaceState doesn't always trigger statechange on reload. if
// you last reloaded the same page and the state is the same, it
// won't fire. so call this here. double calling is not great, but
// OK.
setCurrentList(list._id);
}
});
// subscribe to all the items in each list. no need for a callback
// here: todo items are never queried using collection.find().
Meteor.subscribe('todos');
});
================================================
FILE: examples/unfinished/todos-underscore/common.js
================================================
Lists = new Mongo.Collection("lists");
Todos = new Mongo.Collection("todos");
/* Schema support coming soon!
Lists.schema({text: String});
Todos.schema({text: String,
done: Boolean,
tags: [String]});
*/
if (Meteor.isServer) {
Meteor.publish('lists', function () {
return Lists.find();
});
Meteor.publish('todos', function () {
return Todos.find();
});
}
================================================
FILE: examples/unfinished/todos-underscore/main.css
================================================
body {
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 14px;
line-height: 1.4em;
background: #eeeeee;
color: #333333;
}
.ui-layout-north {
background: #dddddd;
}
#tag-filter {
margin: 8px;
}
#items-view {
display: none;
margin: 10px;
}
#new-todo {
width: 466px;
font-size: 24px;
font-family: inherit;
line-height: 1.4em;
border: 0;
outline: none;
padding: 6px;
border: 1px solid #999999;
margin-left: 75px;
}
.ui-layout-west {
padding: 10px;
border-right: solid 1px #cccccc;
}
.ui-layout-south {
border-top: solid 1px black;
padding: 10px;
background: #cccccc;
}
#help p {
margin: 8px;
}
.ui-layout-center {
overflow: auto;
}
#lists .list {
margin: 2px;
font-weight: bold;
}
#lists .list-name .empty {
font-size: 0.9em;
font-style: italic;
}
#lists .editing .display,
#lists .edit {
display: none;
}
#lists .editing .edit {
display: block;
}
#lists .editing input {
font-family: inherit;
margin: 0;
line-height: 1.6em;
border: 0;
outline: none;
padding: 10px 7px 0px 27px;
border: 1px solid #999999;
}
#lists .selected {
background-color: lightblue;
}
/* todo items */
#item-list {
margin-top: 10px;
}
#item-list li {
margin: 12px;
font-size: 24px;
line-height: 1.1em;
border-bottom: 1px solid #cccccc;
height: 50px;
}
#item-list li:after {
content: "\0020";
display: block;
height: 0;
clear: both;
overflow: hidden;
visibility: hidden;
}
#item-list .destroy {
float: left;
width: 20px;
height: 20px;
cursor: pointer;
margin-top: 12px;
margin-left: 5px;
}
#item-list li:hover .destroy {
background: url('/destroy.png') no-repeat 0 0;
}
#item-list li .destroy:hover {
background-position: 0 -20px;
}
#item-list .display {
float: left;
margin: 9px;
}
#item-list .check {
float: left;
margin: 9px;
}
#item-list .edit {
float: left;
}
#item-list .todo-text {
float: left;
}
#item-list li.editing {
padding: 0;
}
#item-list .editing .display,
#item-list .edit {
display: none;
}
#item-list .editing .edit {
display: block;
}
#item-list .editing input {
width: 444px;
font-size: 24px;
font-family: inherit;
margin-left: 38px;
line-height: 1.6em;
border: 0;
outline: none;
border: 1px solid #999999;
}
#item-list .done .todo-text {
text-decoration: line-through;
color: #777777;
}
#item-list .item-tags {
float: right;
}
/* tags */
.tag {
float: left;
color: black;
background: #aaaaaa;
font-size: 16px;
font-weight: bold;
cursor: pointer;
border: 1px solid black;
border-radius: 2px;
-webkit-border-radius: 2px;
-moz-border-radius: 2px;
padding: 1px 3px 1px 3px;
margin: 4px;
}
.tag.addtag {
background: lightblue;
border: 1px dashed black;
}
.tag.edittag {
display: none;
}
.tag.selected {
background: lightblue;
}
.tag .name {
float: left;
}
.tag .remove {
margin-top: 5px;
margin-left: 5px;
float: left;
width: 16px;
height: 16px;
background-image: url("/close_16.png");
}
================================================
FILE: examples/unfinished/todos-underscore/reset.css
================================================
html, body, div, span, applet, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, big, cite, code,
del, dfn, em, font, img, ins, kbd, q, s, samp,
small, strike, strong, sub, sup, tt, var,
dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td {
margin: 0;
padding: 0;
border: 0;
outline: 0;
font-weight: inherit;
font-style: inherit;
font-size: 100%;
font-family: inherit;
vertical-align: baseline;
}
body {
line-height: 1;
color: black;
background: white;
}
ol, ul {
list-style: none;
}
a img {
border: none;
}
================================================
FILE: examples/unfinished/todos-underscore/server/bootstrap.js
================================================
// if the database is empty on server start, create some sample data.
Meteor.startup(function () {
if (Lists.find().length === 0) {
var list1 = Lists.insert({name: 'Things to do'});
Todos.insert({list_id: list1._id,
text: 'Write Meteor app', tags: ['fun']});
Todos.insert({list_id: list1._id,
text: 'Drink beer', tags: ['fun', 'yum']});
var list2 = Lists.insert({name: 'Places to see'});
Todos.insert({list_id: list2._id, text: 'San Francisco',
tags: ['yum']});
Todos.insert({list_id: list2._id, text: 'Paris',
tags: ['fun']});
Todos.insert({list_id: list2._id, text: 'Tokyo'});
var list3 = Lists.insert({name: 'People to meet'});
Todos.insert({list_id: list3._id,
text: 'All the cool kids'});
}
});
================================================
FILE: meteor
================================================
#!/usr/bin/env bash
BUNDLE_VERSION=0.6.15
# OS Check. Put here because here is where we download the precompiled
# bundles that are arch specific.
UNAME=$(uname)
if [ "$UNAME" != "Linux" -a "$UNAME" != "Darwin" ] ; then
if [ "$UNAME" != "FreeBSD" -a "$UNAME" != "OpenBSD" -a "$UNAME" != "NetBSD" ] ; then
echo "Sorry, this OS is not supported."
exit 1
fi
fi
# The METEOR_UNIVERSAL_FLAG will save the indicator how to handle unofficially
# support environments. For armvXl boards we are support pre built binaries from
# bintray. For all other systems we check, that there are system binries available
# for node and mongo. If METEOR_UNIVERSAL_FLAG is not set, then this runs as same
# as official meteor installer and starter
METEOR_UNIVERSAL_FLAG=
if [ "$UNAME" = "Darwin" ] ; then
if [ "i386" != "$(uname -p)" -o "1" != "$(sysctl -n hw.cpu64bit_capable 2>/dev/null || echo 0)" ] ; then
# Can't just test uname -m = x86_64, because Snow Leopard can
# return other values.
echo "Only 64-bit Intel processors are supported at this time."
exit 1
fi
ARCH="x86_64"
elif [ "$UNAME" = "Linux" ] ; then
ARCH="$(uname -m)"
if [ "$ARCH" != "i686" -a "$ARCH" != "x86_64" ] ; then
if [ "$ARCH" != "armv6l" -a "$ARCH" != "armv7l" ] ; then
# set flag that we are in universal system environment support mode
METEOR_UNIVERSAL_FLAG="env"
else
# set flag that we are in unofficial ARM support mode
METEOR_UNIVERSAL_FLAG="arm"
fi
fi
elif [ "$UNAME" = "FreeBSD" -o "$UNAME" = "OpenBSD" -o "$UNAME" = "NetBSD" ] ; then
ARCH="$(uname -m)"
if [ "$ARCH" != "i686" -a "$ARCH" != "x86_64" -a "$ARCH" != "amd64" ] ; then
# set flag that we are in universal system environment support mode
METEOR_UNIVERSAL_FLAG="env"
else
# set flag that we are in unofficial xBSD support mode
METEOR_UNIVERSAL_FLAG="bsd"
fi
fi
PLATFORM="${UNAME}_${ARCH}"
# Find the script dir, following symlinks. Note that symlink can be relative or
# absolute. Too bad 'readlink -f' and 'realpath' (the command-line program) are
# not portable. We don't stress about infinite loops or bad links, because the
# OS has already resolved this symlink chain once in order to actually run the
# shell script.
ORIG_DIR="$(pwd)"
SCRIPT="$0"
while true; do
# The symlink might be relative, so we have to actually cd to the right place
# each time in order to resolve it.
cd "$(dirname "$SCRIPT")"
if [ ! -L "$(basename "$SCRIPT")" ]; then
SCRIPT_DIR="$(pwd -P)"
break
fi
SCRIPT="$(readlink "$(basename "$SCRIPT")")"
done
cd "$ORIG_DIR"
function install_dev_bundle {
set -e
trap "echo Failed to install dependency kit." EXIT
TARBALL="dev_bundle_${PLATFORM}_${BUNDLE_VERSION}.tar.gz"
BUNDLE_TMPDIR="$SCRIPT_DIR/dev_bundle.xxx"
rm -rf "$BUNDLE_TMPDIR"
mkdir "$BUNDLE_TMPDIR"
# duplicated in scripts/windows/download-dev-bundle.ps1:
DEV_BUNDLE_URL_ROOT="https://d3sqy0vbqsdhku.cloudfront.net/"
# If you set $USE_TEST_DEV_BUNDLE_SERVER then we will download
# dev bundles copied by copy-dev-bundle-from-jenkins.sh without --prod.
# It still only does this if the version number has changed
# (setting it won't cause it to automatically delete a prod dev bundle).
if [ -n "$USE_TEST_DEV_BUNDLE_SERVER" ] ; then
DEV_BUNDLE_URL_ROOT="https://s3.amazonaws.com/com.meteor.static/test/"
fi
# test for unofficial supported architecture based dev_bundles
if [ -n "$METEOR_UNIVERSAL_FLAG" ] ; then
# Use bintray release downloads
DEV_BUNDLE_URL_ROOT="https://dl.bintray.com/4commerce-technologies-ag/meteor-universal/${METEOR_UNIVERSAL_FLAG}_dev_bundles/"
fi
# test if we can download the dev_bundle tarball
function __check_tarball_download() {
CHECK_TARBALL=$(curl --head --location --silent "$DEV_BUNDLE_URL_ROOT$TARBALL" --output /dev/null --write-out '%{http_code}')
# Write down a message instead doing a false download
if [ "$CHECK_TARBALL" != "200" ] ; then
if [ -n "$METEOR_UNIVERSAL_FLAG" ] ; then
# We have identified that there is no pre built dev_bundle available
# and we do not provide yet downloadable pre builts for that platform
echo ""
echo "You are trying to run Meteor on yet not official supported platform: ${PLATFORM}"
echo "There is currently no pre-built dev_bundle available for your system."
echo "Check https://github.com/4commerce-technologies-AG/meteor to get"
echo "the information how you may generate your own dev_bundle using"
echo "scripts/generate-dev-bundle.sh"
echo ""
fi
echo "Unable to download: $DEV_BUNDLE_URL_ROOT$TARBALL"
echo "Pre-built tarball is not available!"
echo ""
exit 1
fi
}
if [ -f "$SCRIPT_DIR/$TARBALL" ] ; then
echo "Skipping download and installing kit from $SCRIPT_DIR/$TARBALL" >&2
tar -xzf "$SCRIPT_DIR/$TARBALL" -C "$BUNDLE_TMPDIR"
elif [ -n "$SAVE_DEV_BUNDLE_TARBALL" ] ; then
# URL duplicated in tools/server/target.sh.in
__check_tarball_download
curl --location -# "$DEV_BUNDLE_URL_ROOT$TARBALL" >"$SCRIPT_DIR/$TARBALL"
tar -xzf "$SCRIPT_DIR/$TARBALL" -C "$BUNDLE_TMPDIR"
else
__check_tarball_download
curl --location -# "$DEV_BUNDLE_URL_ROOT$TARBALL" | tar -xzf - -C "$BUNDLE_TMPDIR"
fi
# In case that we have to use the universal system environment, make sure
# that at least the necessary linked binaries are available and linked to
# the correct system binaries. If detect a linked binary just set the link
# again to the installed system one after unpacking
if [ -L "$BUNDLE_TMPDIR/bin/node" ] ; then
# test for system installed binaries
if [ -z "$(which node 2>/dev/null)" -o -z "$(which npm 2>/dev/null)" ] ; then
echo "Meteor officially supports only Linux i686 and x86_64 and OSX architecture for now."
echo "Meteor universal unofficially supports arm, bsd and universal architectures as well."
echo "To use the universal system environment make sure that the necessary binaries are pre-installed."
echo "Please make sure that node (compatible to $NODE_VERSION) and npm is installed."
echo -e "\tnode version:" $(which node 2>/dev/null)
echo -e "\tnpm version:" $(which npm 2>/dev/null)
exit 1
fi
# remove the link from dev_bundle tgz and reset new to existing binary
rm -f "$BUNDLE_TMPDIR/bin/node"
rm -f "$BUNDLE_TMPDIR/bin/npm"
ln -s "$(which node)" "$BUNDLE_TMPDIR/bin/node"
ln -s "$(which npm)" "$BUNDLE_TMPDIR/bin/npm"
fi
if [ -L "$BUNDLE_TMPDIR/mongodb/bin/mongod" ] ; then
# test for system installed binaries
if [ -z "$(which mongo 2>/dev/null)" -o -z "$(which mongod 2>/dev/null)" ] ; then
echo "Meteor officially supports only Linux i686 and x86_64 and OSX architecture for now."
echo "Meteor universal unofficially supports arm, bsd and universal architectures as well."
echo "To use the universal system environment make sure that the necessary binaries are pre-installed."
echo "Please make sure that mongo and mongod is installed."
echo -e "\tmongo version:" $(which mongo 2>/dev/null)
echo -e "\tmongod version:" $(which mongod 2>/dev/null)
exit 1
fi
# remove the link from dev_bundle tgz and reset new to existing binary
rm -f "$BUNDLE_TMPDIR/mongodb/bin/mongo"
rm -f "$BUNDLE_TMPDIR/mongodb/bin/mongod"
ln -s "$(which mongo 2>/dev/null)" "$BUNDLE_TMPDIR/mongodb/bin/mongo"
ln -s "$(which mongod 2>/dev/null)" "$BUNDLE_TMPDIR/mongodb/bin/mongod"
fi
test -x "${BUNDLE_TMPDIR}/bin/node" # bomb out if it didn't work, eg no net
# Delete old dev bundle and rename the new one on top of it.
rm -rf "$SCRIPT_DIR/dev_bundle"
mv "$BUNDLE_TMPDIR" "$SCRIPT_DIR/dev_bundle"
echo "Installed dependency kit v${BUNDLE_VERSION} in dev_bundle." >&2
echo >&2
trap - EXIT
set +e
}
if [ -d "$SCRIPT_DIR/.git" ] || [ -f "$SCRIPT_DIR/.git" ]; then
# In a checkout.
if [ ! -d "$SCRIPT_DIR/dev_bundle" ] ; then
echo "It's the first time you've run Meteor from a git checkout." >&2
echo "I will download a kit containing all of Meteor's dependencies." >&2
install_dev_bundle
elif [ ! -f "$SCRIPT_DIR/dev_bundle/.bundle_version.txt" ] ||
grep -qvx "$BUNDLE_VERSION" "$SCRIPT_DIR/dev_bundle/.bundle_version.txt" ; then
echo "Your dependency kit is out of date. I will download the new one." >&2
install_dev_bundle
fi
export BABEL_CACHE_DIR="$SCRIPT_DIR/.babel-cache"
fi
DEV_BUNDLE="$SCRIPT_DIR/dev_bundle"
METEOR="$SCRIPT_DIR/tools/index.js"
# Bump our file descriptor ulimit as high as it will go. This is a
# temporary workaround for dependancy watching holding open too many
# files: https://app.asana.com/0/364581412985/472479912325
if [ "$(ulimit -n)" != "unlimited" ] ; then
ulimit -n 16384 > /dev/null 2>&1 || \
ulimit -n 8192 > /dev/null 2>&1 || \
ulimit -n 4096 > /dev/null 2>&1 || \
ulimit -n 2048 > /dev/null 2>&1 || \
ulimit -n 1024 > /dev/null 2>&1 || \
ulimit -n 512 > /dev/null 2>&1
fi
# set ENV VAR and include non-core packages per default if $PACKAGE_DIRS
# is not set already
if [ -z "$PACKAGE_DIRS" ] && [ -d "$SCRIPT_DIR/packages/non-core" ] ; then
export PACKAGE_DIRS=$SCRIPT_DIR/packages/non-core
fi
# We used to set $NODE_PATH here to include the node_modules from the dev
# bundle, but now we just get them from the symlink at tools/node_modules. This
# is better because node_modules directories found via the ancestor walk from
# the script take precedence over $NODE_PATH; it used to be that users would
# screw up their meteor installs by have a ~/node_modules
exec "$DEV_BUNDLE/bin/node" ${TOOL_NODE_FLAGS} "$METEOR" "$@"
================================================
FILE: meteor.bat
================================================
@echo off
SETLOCAL
rem only if we are running from a checkout
IF EXIST "%~dp0\.git" (
rem verify that we have 7zip in the path
7z.exe --help > nul
IF errorlevel 1 (
REM For some reason, without quotes this line causes an error
echo "Please install 7z.exe (7-Zip) and put it into your PATH"
exit /b 1
)
rem if dev_bundle is not present, get it
IF NOT EXIST "%~dp0\dev_bundle" (
REM need `< con` so that we can run this file from Node
REM (See http://stackoverflow.com/questions/9155289/calling-powershell-from-nodejs)
PowerShell.exe -executionpolicy ByPass -file "%~dp0\scripts\windows\download-dev-bundle.ps1" < con
)
rem if dev_bundle is the wrong version, remove it and get a new one
PowerShell.exe -executionpolicy ByPass -file "%~dp0\scripts\windows\check-dev-bundle.ps1" < con
IF errorlevel 1 (
rmdir /s /q "%~dp0\dev_bundle"
IF EXIST "%~dp0\dev_bundle" (
echo Couldn't delete old dependency kit. Please try again.
exit /b 1
)
PowerShell.exe -executionpolicy ByPass -file "%~dp0\scripts\windows\download-dev-bundle.ps1" < con
)
rem Only set this when we're in a checkout. When running from a release,
rem this is correctly set in the top-level `meteor.bat` file
SET METEOR_INSTALLATION=%~dp0
)
SET NODE_PATH=%~dp0\dev_bundle\lib\node_modules
SET BABEL_CACHE_DIR=%~dp0\.babel-cache
"%~dp0\dev_bundle\bin\node.exe" "%~dp0\tools\index.js" %*
ENDLOCAL
EXIT /b %ERRORLEVEL%
================================================
FILE: packages/.gitignore
================================================
.meteor
*/.meteor
================================================
FILE: packages/accounts-base/.gitignore
================================================
.build*
================================================
FILE: packages/accounts-base/README.md
================================================
# accounts-base
Meteor's user account system. This package implements the basic functions necessary for user accounts and lets other packages register login services. Some of these services are in the following packages:
- `accounts-password`
- `accounts-facebook`
- `accounts-google`
- `accounts-github`
- `accounts-twitter`
- `accounts-meetup`
- `accounts-weibo`
There are also login services available in community packages.
For more information, see the [Meteor docs](http://docs.meteor.com/#accounts_api) and the Meteor Accounts [project page](https://www.meteor.com/accounts).
================================================
FILE: packages/accounts-base/accounts_client.js
================================================
import {AccountsCommon} from "./accounts_common.js";
/**
* @summary Constructor for the `Accounts` object on the client.
* @locus Client
* @class AccountsClient
* @extends AccountsCommon
* @instancename accountsClient
* @param {Object} options an object with fields:
* @param {Object} options.connection Optional DDP connection to reuse.
* @param {String} options.ddpUrl Optional URL for creating a new DDP connection.
*/
export class AccountsClient extends AccountsCommon {
constructor(options) {
super(options);
this._loggingIn = false;
this._loggingInDeps = new Tracker.Dependency;
this._loginServicesHandle =
this.connection.subscribe("meteor.loginServiceConfiguration");
this._pageLoadLoginCallbacks = [];
this._pageLoadLoginAttemptInfo = null;
// Defined in url_client.js.
this._initUrlMatching();
// Defined in localstorage_token.js.
this._initLocalStorage();
}
///
/// CURRENT USER
///
// @override
userId() {
return this.connection.userId();
}
// This is mostly just called within this file, but Meteor.loginWithPassword
// also uses it to make loggingIn() be true during the beginPasswordExchange
// method call too.
_setLoggingIn(x) {
if (this._loggingIn !== x) {
this._loggingIn = x;
this._loggingInDeps.changed();
}
}
/**
* @summary True if a login method (such as `Meteor.loginWithPassword`, `Meteor.loginWithFacebook`, or `Accounts.createUser`) is currently in progress. A reactive data source.
* @locus Client
*/
loggingIn() {
this._loggingInDeps.depend();
return this._loggingIn;
}
/**
* @summary Log the user out.
* @locus Client
* @param {Function} [callback] Optional callback. Called with no arguments on success, or with a single `Error` argument on failure.
*/
logout(callback) {
var self = this;
self.connection.apply('logout', [], {
wait: true
}, function (error, result) {
if (error) {
callback && callback(error);
} else {
self.makeClientLoggedOut();
callback && callback();
}
});
}
/**
* @summary Log out other clients logged in as the current user, but does not log out the client that calls this function.
* @locus Client
* @param {Function} [callback] Optional callback. Called with no arguments on success, or with a single `Error` argument on failure.
*/
logoutOtherClients(callback) {
var self = this;
// We need to make two method calls: one to replace our current token,
// and another to remove all tokens except the current one. We want to
// call these two methods one after the other, without any other
// methods running between them. For example, we don't want `logout`
// to be called in between our two method calls (otherwise the second
// method call would return an error). Another example: we don't want
// logout to be called before the callback for `getNewToken`;
// otherwise we would momentarily log the user out and then write a
// new token to localStorage.
//
// To accomplish this, we make both calls as wait methods, and queue
// them one after the other, without spinning off the event loop in
// between. Even though we queue `removeOtherTokens` before
// `getNewToken`, we won't actually send the `removeOtherTokens` call
// until the `getNewToken` callback has finished running, because they
// are both wait methods.
self.connection.apply(
'getNewToken',
[],
{ wait: true },
function (err, result) {
if (! err) {
self._storeLoginToken(
self.userId(),
result.token,
result.tokenExpires
);
}
}
);
self.connection.apply(
'removeOtherTokens',
[],
{ wait: true },
function (err) {
callback && callback(err);
}
);
}
};
var Ap = AccountsClient.prototype;
/**
* @summary True if a login method (such as `Meteor.loginWithPassword`, `Meteor.loginWithFacebook`, or `Accounts.createUser`) is currently in progress. A reactive data source.
* @locus Client
* @importFromPackage meteor
*/
Meteor.loggingIn = function () {
return Accounts.loggingIn();
};
///
/// LOGIN METHODS
///
// Call a login method on the server.
//
// A login method is a method which on success calls `this.setUserId(id)` and
// `Accounts._setLoginToken` on the server and returns an object with fields
// 'id' (containing the user id), 'token' (containing a resume token), and
// optionally `tokenExpires`.
//
// This function takes care of:
// - Updating the Meteor.loggingIn() reactive data source
// - Calling the method in 'wait' mode
// - On success, saving the resume token to localStorage
// - On success, calling Accounts.connection.setUserId()
// - Setting up an onReconnect handler which logs in with
// the resume token
//
// Options:
// - methodName: The method to call (default 'login')
// - methodArguments: The arguments for the method
// - validateResult: If provided, will be called with the result of the
// method. If it throws, the client will not be logged in (and
// its error will be passed to the callback).
// - userCallback: Will be called with no arguments once the user is fully
// logged in, or with the error on error.
//
Ap.callLoginMethod = function (options) {
var self = this;
options = _.extend({
methodName: 'login',
methodArguments: [{}],
_suppressLoggingIn: false
}, options);
// Set defaults for callback arguments to no-op functions; make sure we
// override falsey values too.
_.each(['validateResult', 'userCallback'], function (f) {
if (!options[f])
options[f] = function () {};
});
// Prepare callbacks: user provided and onLogin/onLoginFailure hooks.
var loginCallbacks = _.once(function (error) {
if (!error) {
self._onLoginHook.each(function (callback) {
callback();
return true;
});
} else {
self._onLoginFailureHook.each(function (callback) {
callback();
return true;
});
}
options.userCallback.apply(this, arguments);
});
var reconnected = false;
// We want to set up onReconnect as soon as we get a result token back from
// the server, without having to wait for subscriptions to rerun. This is
// because if we disconnect and reconnect between getting the result and
// getting the results of subscription rerun, we WILL NOT re-send this
// method (because we never re-send methods whose results we've received)
// but we WILL call loggedInAndDataReadyCallback at "reconnect quiesce"
// time. This will lead to makeClientLoggedIn(result.id) even though we
// haven't actually sent a login method!
//
// But by making sure that we send this "resume" login in that case (and
// calling makeClientLoggedOut if it fails), we'll end up with an accurate
// client-side userId. (It's important that livedata_connection guarantees
// that the "reconnect quiesce"-time call to loggedInAndDataReadyCallback
// will occur before the callback from the resume login call.)
var onResultReceived = function (err, result) {
if (err || !result || !result.token) {
// Leave onReconnect alone if there was an error, so that if the user was
// already logged in they will still get logged in on reconnect.
// See issue #4970.
} else {
self.connection.onReconnect = function () {
reconnected = true;
// If our token was updated in storage, use the latest one.
var storedToken = self._storedLoginToken();
if (storedToken) {
result = {
token: storedToken,
tokenExpires: self._storedLoginTokenExpires()
};
}
if (! result.tokenExpires)
result.tokenExpires = self._tokenExpiration(new Date());
if (self._tokenExpiresSoon(result.tokenExpires)) {
self.makeClientLoggedOut();
} else {
self.callLoginMethod({
methodArguments: [{resume: result.token}],
// Reconnect quiescence ensures that the user doesn't see an
// intermediate state before the login method finishes. So we don't
// need to show a logging-in animation.
_suppressLoggingIn: true,
userCallback: function (error) {
var storedTokenNow = self._storedLoginToken();
if (error) {
// If we had a login error AND the current stored token is the
// one that we tried to log in with, then declare ourselves
// logged out. If there's a token in storage but it's not the
// token that we tried to log in with, we don't know anything
// about whether that token is valid or not, so do nothing. The
// periodic localStorage poll will decide if we are logged in or
// out with this token, if it hasn't already. Of course, even
// with this check, another tab could insert a new valid token
// immediately before we clear localStorage here, which would
// lead to both tabs being logged out, but by checking the token
// in storage right now we hope to make that unlikely to happen.
//
// If there is no token in storage right now, we don't have to
// do anything; whatever code removed the token from storage was
// responsible for calling `makeClientLoggedOut()`, or the
// periodic localStorage poll will call `makeClientLoggedOut`
// eventually if another tab wiped the token from storage.
if (storedTokenNow && storedTokenNow === result.token) {
self.makeClientLoggedOut();
}
}
// Possibly a weird callback to call, but better than nothing if
// there is a reconnect between "login result received" and "data
// ready".
loginCallbacks(error);
}});
}
};
}
};
// This callback is called once the local cache of the current-user
// subscription (and all subscriptions, in fact) are guaranteed to be up to
// date.
var loggedInAndDataReadyCallback = function (error, result) {
// If the login method returns its result but the connection is lost
// before the data is in the local cache, it'll set an onReconnect (see
// above). The onReconnect will try to log in using the token, and *it*
// will call userCallback via its own version of this
// loggedInAndDataReadyCallback. So we don't have to do anything here.
if (reconnected)
return;
// Note that we need to call this even if _suppressLoggingIn is true,
// because it could be matching a _setLoggingIn(true) from a
// half-completed pre-reconnect login method.
self._setLoggingIn(false);
if (error || !result) {
error = error || new Error(
"No result from call to " + options.methodName);
loginCallbacks(error);
return;
}
try {
options.validateResult(result);
} catch (e) {
loginCallbacks(e);
return;
}
// Make the client logged in. (The user data should already be loaded!)
self.makeClientLoggedIn(result.id, result.token, result.tokenExpires);
loginCallbacks();
};
if (!options._suppressLoggingIn)
self._setLoggingIn(true);
self.connection.apply(
options.methodName,
options.methodArguments,
{wait: true, onResultReceived: onResultReceived},
loggedInAndDataReadyCallback);
};
Ap.makeClientLoggedOut = function () {
// Ensure client was successfully logged in before running logout hooks.
if (this.connection._userId) {
this._onLogoutHook.each(function (callback) {
callback();
return true;
});
}
this._unstoreLoginToken();
this.connection.setUserId(null);
this.connection.onReconnect = null;
};
Ap.makeClientLoggedIn = function (userId, token, tokenExpires) {
this._storeLoginToken(userId, token, tokenExpires);
this.connection.setUserId(userId);
};
/**
* @summary Log the user out.
* @locus Client
* @param {Function} [callback] Optional callback. Called with no arguments on success, or with a single `Error` argument on failure.
* @importFromPackage meteor
*/
Meteor.logout = function (callback) {
return Accounts.logout(callback);
};
/**
* @summary Log out other clients logged in as the current user, but does not log out the client that calls this function.
* @locus Client
* @param {Function} [callback] Optional callback. Called with no arguments on success, or with a single `Error` argument on failure.
* @importFromPackage meteor
*/
Meteor.logoutOtherClients = function (callback) {
return Accounts.logoutOtherClients(callback);
};
///
/// LOGIN SERVICES
///
// A reactive function returning whether the loginServiceConfiguration
// subscription is ready. Used by accounts-ui to hide the login button
// until we have all the configuration loaded
//
Ap.loginServicesConfigured = function () {
return this._loginServicesHandle.ready();
};
// Some login services such as the redirect login flow or the resume
// login handler can log the user in at page load time. The
// Meteor.loginWithX functions have a callback argument, but the
// callback function instance won't be in memory any longer if the
// page was reloaded. The `onPageLoadLogin` function allows a
// callback to be registered for the case where the login was
// initiated in a previous VM, and we now have the result of the login
// attempt in a new VM.
// Register a callback to be called if we have information about a
// login attempt at page load time. Call the callback immediately if
// we already have the page load login attempt info, otherwise stash
// the callback to be called if and when we do get the attempt info.
//
Ap.onPageLoadLogin = function (f) {
if (this._pageLoadLoginAttemptInfo) {
f(this._pageLoadLoginAttemptInfo);
} else {
this._pageLoadLoginCallbacks.push(f);
}
};
// Receive the information about the login attempt at page load time.
// Call registered callbacks, and also record the info in case
// someone's callback hasn't been registered yet.
//
Ap._pageLoadLogin = function (attemptInfo) {
if (this._pageLoadLoginAttemptInfo) {
Meteor._debug("Ignoring unexpected duplicate page load login attempt info");
return;
}
_.each(this._pageLoadLoginCallbacks, function (callback) {
callback(attemptInfo);
});
this._pageLoadLoginCallbacks = [];
this._pageLoadLoginAttemptInfo = attemptInfo;
};
///
/// HANDLEBARS HELPERS
///
// If our app has a Blaze, register the {{currentUser}} and {{loggingIn}}
// global helpers.
if (Package.blaze) {
/**
* @global
* @name currentUser
* @isHelper true
* @summary Calls [Meteor.user()](#meteor_user). Use `{{#if currentUser}}` to check whether the user is logged in.
*/
Package.blaze.Blaze.Template.registerHelper('currentUser', function () {
return Meteor.user();
});
/**
* @global
* @name loggingIn
* @isHelper true
* @summary Calls [Meteor.loggingIn()](#meteor_loggingin).
*/
Package.blaze.Blaze.Template.registerHelper('loggingIn', function () {
return Meteor.loggingIn();
});
}
================================================
FILE: packages/accounts-base/accounts_common.js
================================================
/**
* @summary Super-constructor for AccountsClient and AccountsServer.
* @locus Anywhere
* @class AccountsCommon
* @instancename accountsClientOrServer
* @param options {Object} an object with fields:
* - connection {Object} Optional DDP connection to reuse.
* - ddpUrl {String} Optional URL for creating a new DDP connection.
*/
export class AccountsCommon {
constructor(options) {
// Currently this is read directly by packages like accounts-password
// and accounts-ui-unstyled.
this._options = {};
// Note that setting this.connection = null causes this.users to be a
// LocalCollection, which is not what we want.
this.connection = undefined;
this._initConnection(options || {});
// There is an allow call in accounts_server.js that restricts writes to
// this collection.
this.users = new Mongo.Collection("users", {
_preventAutopublish: true,
connection: this.connection
});
// Callback exceptions are printed with Meteor._debug and ignored.
this._onLoginHook = new Hook({
bindEnvironment: false,
debugPrintExceptions: "onLogin callback"
});
this._onLoginFailureHook = new Hook({
bindEnvironment: false,
debugPrintExceptions: "onLoginFailure callback"
});
this._onLogoutHook = new Hook({
bindEnvironment: false,
debugPrintExceptions: "onLogout callback"
});
}
/**
* @summary Get the current user id, or `null` if no user is logged in. A reactive data source.
* @locus Anywhere but publish functions
*/
userId() {
throw new Error("userId method not implemented");
}
/**
* @summary Get the current user record, or `null` if no user is logged in. A reactive data source.
* @locus Anywhere but publish functions
*/
user() {
var userId = this.userId();
return userId ? this.users.findOne(userId) : null;
}
// Set up config for the accounts system. Call this on both the client
// and the server.
//
// Note that this method gets overridden on AccountsServer.prototype, but
// the overriding method calls the overridden method.
//
// XXX we should add some enforcement that this is called on both the
// client and the server. Otherwise, a user can
// 'forbidClientAccountCreation' only on the client and while it looks
// like their app is secure, the server will still accept createUser
// calls. https://github.com/meteor/meteor/issues/828
//
// @param options {Object} an object with fields:
// - sendVerificationEmail {Boolean}
// Send email address verification emails to new users created from
// client signups.
// - forbidClientAccountCreation {Boolean}
// Do not allow clients to create accounts directly.
// - restrictCreationByEmailDomain {Function or String}
// Require created users to have an email matching the function or
// having the string as domain.
// - loginExpirationInDays {Number}
// Number of days since login until a user is logged out (login token
// expires).
/**
* @summary Set global accounts options.
* @locus Anywhere
* @param {Object} options
* @param {Boolean} options.sendVerificationEmail New users with an email address will receive an address verification email.
* @param {Boolean} options.forbidClientAccountCreation Calls to [`createUser`](#accounts_createuser) from the client will be rejected. In addition, if you are using [accounts-ui](#accountsui), the "Create account" link will not be available.
* @param {String | Function} options.restrictCreationByEmailDomain If set to a string, only allows new users if the domain part of their email address matches the string. If set to a function, only allows new users if the function returns true. The function is passed the full email address of the proposed new user. Works with password-based sign-in and external services that expose email addresses (Google, Facebook, GitHub). All existing users still can log in after enabling this option. Example: `Accounts.config({ restrictCreationByEmailDomain: 'school.edu' })`.
* @param {Number} options.loginExpirationInDays The number of days from when a user logs in until their token expires and they are logged out. Defaults to 90. Set to `null` to disable login expiration.
* @param {String} options.oauthSecretKey When using the `oauth-encryption` package, the 16 byte key using to encrypt sensitive account credentials in the database, encoded in base64. This option may only be specifed on the server. See packages/oauth-encryption/README.md for details.
*/
config(options) {
var self = this;
// We don't want users to accidentally only call Accounts.config on the
// client, where some of the options will have partial effects (eg removing
// the "create account" button from accounts-ui if forbidClientAccountCreation
// is set, or redirecting Google login to a specific-domain page) without
// having their full effects.
if (Meteor.isServer) {
__meteor_runtime_config__.accountsConfigCalled = true;
} else if (!__meteor_runtime_config__.accountsConfigCalled) {
// XXX would be nice to "crash" the client and replace the UI with an error
// message, but there's no trivial way to do this.
Meteor._debug("Accounts.config was called on the client but not on the " +
"server; some configuration options may not take effect.");
}
// We need to validate the oauthSecretKey option at the time
// Accounts.config is called. We also deliberately don't store the
// oauthSecretKey in Accounts._options.
if (_.has(options, "oauthSecretKey")) {
if (Meteor.isClient)
throw new Error("The oauthSecretKey option may only be specified on the server");
if (! Package["oauth-encryption"])
throw new Error("The oauth-encryption package must be loaded to set oauthSecretKey");
Package["oauth-encryption"].OAuthEncryption.loadKey(options.oauthSecretKey);
options = _.omit(options, "oauthSecretKey");
}
// validate option keys
var VALID_KEYS = ["sendVerificationEmail", "forbidClientAccountCreation",
"restrictCreationByEmailDomain", "loginExpirationInDays"];
_.each(_.keys(options), function (key) {
if (!_.contains(VALID_KEYS, key)) {
throw new Error("Accounts.config: Invalid key: " + key);
}
});
// set values in Accounts._options
_.each(VALID_KEYS, function (key) {
if (key in options) {
if (key in self._options) {
throw new Error("Can't set `" + key + "` more than once");
}
self._options[key] = options[key];
}
});
}
/**
* @summary Register a callback to be called after a login attempt succeeds.
* @locus Anywhere
* @param {Function} func The callback to be called when login is successful.
*/
onLogin(func) {
return this._onLoginHook.register(func);
}
/**
* @summary Register a callback to be called after a login attempt fails.
* @locus Anywhere
* @param {Function} func The callback to be called after the login has failed.
*/
onLoginFailure(func) {
return this._onLoginFailureHook.register(func);
}
/**
* @summary Register a callback to be called after a logout attempt succeeds.
* @locus Anywhere
* @param {Function} func The callback to be called when logout is successful.
*/
onLogout(func) {
return this._onLogoutHook.register(func);
}
_initConnection(options) {
if (! Meteor.isClient) {
return;
}
// The connection used by the Accounts system. This is the connection
// that will get logged in by Meteor.login(), and this is the
// connection whose login state will be reflected by Meteor.userId().
//
// It would be much preferable for this to be in accounts_client.js,
// but it has to be here because it's needed to create the
// Meteor.users collection.
if (options.connection) {
this.connection = options.connection;
} else if (options.ddpUrl) {
this.connection = DDP.connect(options.ddpUrl);
} else if (typeof __meteor_runtime_config__ !== "undefined" &&
__meteor_runtime_config__.ACCOUNTS_CONNECTION_URL) {
// Temporary, internal hook to allow the server to point the client
// to a different authentication server. This is for a very
// particular use case that comes up when implementing a oauth
// server. Unsupported and may go away at any point in time.
//
// We will eventually provide a general way to use account-base
// against any DDP connection, not just one special one.
this.connection =
DDP.connect(__meteor_runtime_config__.ACCOUNTS_CONNECTION_URL);
} else {
this.connection = Meteor.connection;
}
}
_getTokenLifetimeMs() {
return (this._options.loginExpirationInDays ||
DEFAULT_LOGIN_EXPIRATION_DAYS) * 24 * 60 * 60 * 1000;
}
_tokenExpiration(when) {
// We pass when through the Date constructor for backwards compatibility;
// `when` used to be a number.
return new Date((new Date(when)).getTime() + this._getTokenLifetimeMs());
}
_tokenExpiresSoon(when) {
var minLifetimeMs = .1 * this._getTokenLifetimeMs();
var minLifetimeCapMs = MIN_TOKEN_LIFETIME_CAP_SECS * 1000;
if (minLifetimeMs > minLifetimeCapMs)
minLifetimeMs = minLifetimeCapMs;
return new Date() > (new Date(when) - minLifetimeMs);
}
}
var Ap = AccountsCommon.prototype;
// Note that Accounts is defined separately in accounts_client.js and
// accounts_server.js.
/**
* @summary Get the current user id, or `null` if no user is logged in. A reactive data source.
* @locus Anywhere but publish functions
* @importFromPackage meteor
*/
Meteor.userId = function () {
return Accounts.userId();
};
/**
* @summary Get the current user record, or `null` if no user is logged in. A reactive data source.
* @locus Anywhere but publish functions
* @importFromPackage meteor
*/
Meteor.user = function () {
return Accounts.user();
};
// how long (in days) until a login token expires
var DEFAULT_LOGIN_EXPIRATION_DAYS = 90;
// Clients don't try to auto-login with a token that is going to expire within
// .1 * DEFAULT_LOGIN_EXPIRATION_DAYS, capped at MIN_TOKEN_LIFETIME_CAP_SECS.
// Tries to avoid abrupt disconnects from expiring tokens.
var MIN_TOKEN_LIFETIME_CAP_SECS = 3600; // one hour
// how often (in milliseconds) we check for expired tokens
EXPIRE_TOKENS_INTERVAL_MS = 600 * 1000; // 10 minutes
// how long we wait before logging out clients when Meteor.logoutOtherClients is
// called
CONNECTION_CLOSE_DELAY_MS = 10 * 1000;
// loginServiceConfiguration and ConfigError are maintained for backwards compatibility
Meteor.startup(function () {
var ServiceConfiguration =
Package['service-configuration'].ServiceConfiguration;
Ap.loginServiceConfiguration = ServiceConfiguration.configurations;
Ap.ConfigError = ServiceConfiguration.ConfigError;
});
// Thrown when the user cancels the login process (eg, closes an oauth
// popup, declines retina scan, etc)
var lceName = 'Accounts.LoginCancelledError';
Ap.LoginCancelledError = Meteor.makeErrorType(
lceName,
function (description) {
this.message = description;
}
);
Ap.LoginCancelledError.prototype.name = lceName;
// This is used to transmit specific subclass errors over the wire. We should
// come up with a more generic way to do this (eg, with some sort of symbolic
// error code rather than a number).
Ap.LoginCancelledError.numericError = 0x8acdc2f;
================================================
FILE: packages/accounts-base/accounts_rate_limit.js
================================================
import {AccountsCommon} from "./accounts_common.js";
var Ap = AccountsCommon.prototype;
var defaultRateLimiterRuleId;
// Removes default rate limiting rule
Ap.removeDefaultRateLimit = function () {
const resp = DDPRateLimiter.removeRule(defaultRateLimiterRuleId);
defaultRateLimiterRuleId = null;
return resp;
};
// Add a default rule of limiting logins, creating new users and password reset
// to 5 times every 10 seconds per connection.
Ap.addDefaultRateLimit = function () {
if (!defaultRateLimiterRuleId) {
defaultRateLimiterRuleId = DDPRateLimiter.addRule({
userId: null,
clientAddress: null,
type: 'method',
name: function (name) {
return _.contains(['login', 'createUser', 'resetPassword',
'forgotPassword'], name);
},
connectionId: function (connectionId) {
return true;
}
}, 5, 10000);
}
};
Ap.addDefaultRateLimit();
================================================
FILE: packages/accounts-base/accounts_reconnect_tests.js
================================================
if (Meteor.isServer) {
Meteor.methods({
getConnectionUserId: function() {
return this.userId;
}
});
}
if (Meteor.isClient) {
Tinytest.addAsync('accounts - reconnect auto-login', function(test, done) {
var username1 = 'testuser1-' + Random.id();
var username2 = 'testuser2-' + Random.id();
var password1 = 'password1-' + Random.id();
var password2 = 'password2-' + Random.id();
var timeoutHandle;
var onLoginStopper;
loginAsUser1();
function loginAsUser1() {
Accounts.createUser({
username: username1,
password: password1
}, onUser1LoggedIn);
}
function onUser1LoggedIn(err) {
test.isUndefined(err, 'Unexpected error logging in as user1');
Accounts.createUser({
username: username2,
password: password2
}, onUser2LoggedIn);
}
function onUser2LoggedIn(err) {
test.isUndefined(err, 'Unexpected error logging in as user2');
onLoginStopper = Accounts.onLogin(onUser2LoggedInAfterReconnect);
Meteor.disconnect();
Meteor.reconnect();
}
function onUser2LoggedInAfterReconnect() {
onLoginStopper.stop();
Meteor.loginWithPassword('non-existent-user', 'or-wrong-password',
onFailedLogin);
}
function onFailedLogin(err) {
test.instanceOf(err, Meteor.Error, 'No Meteor.Error on login failure');
onLoginStopper = Accounts.onLogin(onUser2LoggedInAfterReconnectAfterFailedLogin);
Meteor.disconnect();
Meteor.reconnect();
timeoutHandle = Meteor.setTimeout(failTest, 1000);
}
function failTest() {
onLoginStopper.stop();
test.fail('Issue #4970 has occured.');
Meteor.call('getConnectionUserId', checkFinalState);
}
function onUser2LoggedInAfterReconnectAfterFailedLogin() {
onLoginStopper.stop();
Meteor.clearTimeout(timeoutHandle);
Meteor.call('getConnectionUserId', checkFinalState);
}
function checkFinalState(err, connectionUserId) {
test.isUndefined(err, 'Unexpected error calling getConnectionUserId');
test.equal(connectionUserId, Meteor.userId(),
'userId is different on client and server');
done();
}
});
}
================================================
FILE: packages/accounts-base/accounts_server.js
================================================
var crypto = Npm.require('crypto');
import {AccountsCommon} from "./accounts_common.js";
/**
* @summary Constructor for the `Accounts` namespace on the server.
* @locus Server
* @class AccountsServer
* @extends AccountsCommon
* @instancename accountsServer
* @param {Object} server A server object such as `Meteor.server`.
*/
export class AccountsServer extends AccountsCommon {
// Note that this constructor is less likely to be instantiated multiple
// times than the `AccountsClient` constructor, because a single server
// can provide only one set of methods.
constructor(server) {
super();
this._server = server || Meteor.server;
// Set up the server's methods, as if by calling Meteor.methods.
this._initServerMethods();
this._initAccountDataHooks();
// If autopublish is on, publish these user fields. Login service
// packages (eg accounts-google) add to these by calling
// addAutopublishFields. Notably, this isn't implemented with multiple
// publishes since DDP only merges only across top-level fields, not
// subfields (such as 'services.facebook.accessToken')
this._autopublishFields = {
loggedInUser: ['profile', 'username', 'emails'],
otherUsers: ['profile', 'username']
};
this._initServerPublications();
// connectionId -> {connection, loginToken}
this._accountData = {};
// connection id -> observe handle for the login token that this connection is
// currently associated with, or a number. The number indicates that we are in
// the process of setting up the observe (using a number instead of a single
// sentinel allows multiple attempts to set up the observe to identify which
// one was theirs).
this._userObservesForConnections = {};
this._nextUserObserveNumber = 1; // for the number described above.
// list of all registered handlers.
this._loginHandlers = [];
setupUsersCollection(this.users);
setupDefaultLoginHandlers(this);
setExpireTokensInterval(this);
this._validateLoginHook = new Hook({ bindEnvironment: false });
this._validateNewUserHooks = [
defaultValidateNewUserHook.bind(this)
];
this._deleteSavedTokensForAllUsersOnStartup();
this._skipCaseInsensitiveChecksForTest = {};
}
///
/// CURRENT USER
///
// @override of "abstract" non-implementation in accounts_common.js
userId() {
// This function only works if called inside a method. In theory, it
// could also be called from publish statements, since they also
// have a userId associated with them. However, given that publish
// functions aren't reactive, using any of the infomation from
// Meteor.user() in a publish function will always use the value
// from when the function first runs. This is likely not what the
// user expects. The way to make this work in a publish is to do
// Meteor.find(this.userId).observe and recompute when the user
// record changes.
var currentInvocation = DDP._CurrentInvocation.get();
if (!currentInvocation)
throw new Error("Meteor.userId can only be invoked in method calls. Use this.userId in publish functions.");
return currentInvocation.userId;
}
///
/// LOGIN HOOKS
///
/**
* @summary Validate login attempts.
* @locus Server
* @param {Function} func Called whenever a login is attempted (either successful or unsuccessful). A login can be aborted by returning a falsy value or throwing an exception.
*/
validateLoginAttempt(func) {
// Exceptions inside the hook callback are passed up to us.
return this._validateLoginHook.register(func);
}
/**
* @summary Set restrictions on new user creation.
* @locus Server
* @param {Function} func Called whenever a new user is created. Takes the new user object, and returns true to allow the creation or false to abort.
*/
validateNewUser(func) {
this._validateNewUserHooks.push(func);
}
///
/// CREATE USER HOOKS
///
/**
* @summary Customize new user creation.
* @locus Server
* @param {Function} func Called whenever a new user is created. Return the new user object, or throw an `Error` to abort the creation.
*/
onCreateUser(func) {
if (this._onCreateUserHook) {
throw new Error("Can only call onCreateUser once");
}
this._onCreateUserHook = func;
}
};
var Ap = AccountsServer.prototype;
// Give each login hook callback a fresh cloned copy of the attempt
// object, but don't clone the connection.
//
function cloneAttemptWithConnection(connection, attempt) {
var clonedAttempt = EJSON.clone(attempt);
clonedAttempt.connection = connection;
return clonedAttempt;
}
Ap._validateLogin = function (connection, attempt) {
this._validateLoginHook.each(function (callback) {
var ret;
try {
ret = callback(cloneAttemptWithConnection(connection, attempt));
}
catch (e) {
attempt.allowed = false;
// XXX this means the last thrown error overrides previous error
// messages. Maybe this is surprising to users and we should make
// overriding errors more explicit. (see
// https://github.com/meteor/meteor/issues/1960)
attempt.error = e;
return true;
}
if (! ret) {
attempt.allowed = false;
// don't override a specific error provided by a previous
// validator or the initial attempt (eg "incorrect password").
if (!attempt.error)
attempt.error = new Meteor.Error(403, "Login forbidden");
}
return true;
});
};
Ap._successfulLogin = function (connection, attempt) {
this._onLoginHook.each(function (callback) {
callback(cloneAttemptWithConnection(connection, attempt));
return true;
});
};
Ap._failedLogin = function (connection, attempt) {
this._onLoginFailureHook.each(function (callback) {
callback(cloneAttemptWithConnection(connection, attempt));
return true;
});
};
Ap._successfulLogout = function () {
this._onLogoutHook.each(function (callback) {
callback();
return true;
});
};
///
/// LOGIN METHODS
///
// Login methods return to the client an object containing these
// fields when the user was logged in successfully:
//
// id: userId
// token: *
// tokenExpires: *
//
// tokenExpires is optional and intends to provide a hint to the
// client as to when the token will expire. If not provided, the
// client will call Accounts._tokenExpiration, passing it the date
// that it received the token.
//
// The login method will throw an error back to the client if the user
// failed to log in.
//
//
// Login handlers and service specific login methods such as
// `createUser` internally return a `result` object containing these
// fields:
//
// type:
// optional string; the service name, overrides the handler
// default if present.
//
// error:
// exception; if the user is not allowed to login, the reason why.
//
// userId:
// string; the user id of the user attempting to login (if
// known), required for an allowed login.
//
// options:
// optional object merged into the result returned by the login
// method; used by HAMK from SRP.
//
// stampedLoginToken:
// optional object with `token` and `when` indicating the login
// token is already present in the database, returned by the
// "resume" login handler.
//
// For convenience, login methods can also throw an exception, which
// is converted into an {error} result. However, if the id of the
// user attempting the login is known, a {userId, error} result should
// be returned instead since the user id is not captured when an
// exception is thrown.
//
// This internal `result` object is automatically converted into the
// public {id, token, tokenExpires} object returned to the client.
// Try a login method, converting thrown exceptions into an {error}
// result. The `type` argument is a default, inserted into the result
// object if not explicitly returned.
//
var tryLoginMethod = function (type, fn) {
var result;
try {
result = fn();
}
catch (e) {
result = {error: e};
}
if (result && !result.type && type)
result.type = type;
return result;
};
// Log in a user on a connection.
//
// We use the method invocation to set the user id on the connection,
// not the connection object directly. setUserId is tied to methods to
// enforce clear ordering of method application (using wait methods on
// the client, and a no setUserId after unblock restriction on the
// server)
//
// The `stampedLoginToken` parameter is optional. When present, it
// indicates that the login token has already been inserted into the
// database and doesn't need to be inserted again. (It's used by the
// "resume" login handler).
Ap._loginUser = function (methodInvocation, userId, stampedLoginToken) {
var self = this;
if (! stampedLoginToken) {
stampedLoginToken = self._generateStampedLoginToken();
self._insertLoginToken(userId, stampedLoginToken);
}
// This order (and the avoidance of yields) is important to make
// sure that when publish functions are rerun, they see a
// consistent view of the world: the userId is set and matches
// the login token on the connection (not that there is
// currently a public API for reading the login token on a
// connection).
Meteor._noYieldsAllowed(function () {
self._setLoginToken(
userId,
methodInvocation.connection,
self._hashLoginToken(stampedLoginToken.token)
);
});
methodInvocation.setUserId(userId);
return {
id: userId,
token: stampedLoginToken.token,
tokenExpires: self._tokenExpiration(stampedLoginToken.when)
};
};
// After a login method has completed, call the login hooks. Note
// that `attemptLogin` is called for *all* login attempts, even ones
// which aren't successful (such as an invalid password, etc).
//
// If the login is allowed and isn't aborted by a validate login hook
// callback, log in the user.
//
Ap._attemptLogin = function (
methodInvocation,
methodName,
methodArgs,
result
) {
if (!result)
throw new Error("result is required");
// XXX A programming error in a login handler can lead to this occuring, and
// then we don't call onLogin or onLoginFailure callbacks. Should
// tryLoginMethod catch this case and turn it into an error?
if (!result.userId && !result.error)
throw new Error("A login method must specify a userId or an error");
var user;
if (result.userId)
user = this.users.findOne(result.userId);
var attempt = {
type: result.type || "unknown",
allowed: !! (result.userId && !result.error),
methodName: methodName,
methodArguments: _.toArray(methodArgs)
};
if (result.error)
attempt.error = result.error;
if (user)
attempt.user = user;
// _validateLogin may mutate `attempt` by adding an error and changing allowed
// to false, but that's the only change it can make (and the user's callbacks
// only get a clone of `attempt`).
this._validateLogin(methodInvocation.connection, attempt);
if (attempt.allowed) {
var ret = _.extend(
this._loginUser(
methodInvocation,
result.userId,
result.stampedLoginToken
),
result.options || {}
);
this._successfulLogin(methodInvocation.connection, attempt);
return ret;
}
else {
this._failedLogin(methodInvocation.connection, attempt);
throw attempt.error;
}
};
// All service specific login methods should go through this function.
// Ensure that thrown exceptions are caught and that login hook
// callbacks are still called.
//
Ap._loginMethod = function (
methodInvocation,
methodName,
methodArgs,
type,
fn
) {
return this._attemptLogin(
methodInvocation,
methodName,
methodArgs,
tryLoginMethod(type, fn)
);
};
// Report a login attempt failed outside the context of a normal login
// method. This is for use in the case where there is a multi-step login
// procedure (eg SRP based password login). If a method early in the
// chain fails, it should call this function to report a failure. There
// is no corresponding method for a successful login; methods that can
// succeed at logging a user in should always be actual login methods
// (using either Accounts._loginMethod or Accounts.registerLoginHandler).
Ap._reportLoginFailure = function (
methodInvocation,
methodName,
methodArgs,
result
) {
var attempt = {
type: result.type || "unknown",
allowed: false,
error: result.error,
methodName: methodName,
methodArguments: _.toArray(methodArgs)
};
if (result.userId) {
attempt.user = this.users.findOne(result.userId);
}
this._validateLogin(methodInvocation.connection, attempt);
this._failedLogin(methodInvocation.connection, attempt);
// _validateLogin may mutate attempt to set a new error message. Return
// the modified version.
return attempt;
};
///
/// LOGIN HANDLERS
///
// The main entry point for auth packages to hook in to login.
//
// A login handler is a login method which can return `undefined` to
// indicate that the login request is not handled by this handler.
//
// @param name {String} Optional. The service name, used by default
// if a specific service name isn't returned in the result.
//
// @param handler {Function} A function that receives an options object
// (as passed as an argument to the `login` method) and returns one of:
// - `undefined`, meaning don't handle;
// - a login method result object
Ap.registerLoginHandler = function (name, handler) {
if (! handler) {
handler = name;
name = null;
}
this._loginHandlers.push({
name: name,
handler: handler
});
};
// Checks a user's credentials against all the registered login
// handlers, and returns a login token if the credentials are valid. It
// is like the login method, except that it doesn't set the logged-in
// user on the connection. Throws a Meteor.Error if logging in fails,
// including the case where none of the login handlers handled the login
// request. Otherwise, returns {id: userId, token: *, tokenExpires: *}.
//
// For example, if you want to login with a plaintext password, `options` could be
// { user: { username:
Now, copy over some details.
Choose the login style:
Please configure login on a desktop browser.
Hello Hello \ud835\udd6b 𝕫 Hello Hello Hello Hello Hello World Hello World Two Two Hello Hello World Hello World Hello World Goodbye World Hello World World "));
succeed('', SCRIPT("a&b"));
succeed('', SCRIPT("\n', SCRIPT("\n"));
succeed('', SCRIPT(""));
succeed('', SCRIPT("asdf"));
fatal('&davidgreenspan;', SCRIPT("&davidgreenspan;"));
succeed('', SCRIPT("&"));
succeed('asdf',
[SCRIPT("asdf', STYLE("asdf"));
succeed('', STYLE({x: "y"}, "asdf"));
succeed('', STYLE(" "));
succeed('', STYLE("a&b"));
succeed('', STYLE("\n', STYLE("\n"));
succeed('', STYLE(""));
succeed('', STYLE("asdf"));
fatal('&davidgreenspan;', STYLE("&davidgreenspan;"));
succeed('', STYLE("&"));
succeed('asdf',
[STYLE(" Hello x xA x y
{{/each}}
If you'd prefer not to move your files, you can use the file names
themselves as the URL prefix:
Meteor.AppCache.config({
onlineOnly: [
'/bigimage.jpg',
'/largedata.json'
]
});
though keep in mind that since the exclusion is by prefix (this is a
limitation of the application cache manifest), excluding
`/largedata.json` will also exclude such URLs as
`/largedata.json.orig` and `/largedata.json/file1`.
For more information about how Meteor interacts with the application
cache, see the
[AppCache page](https://github.com/meteor/meteor/wiki/AppCache)
in the Meteor wiki.
================================================
FILE: packages/appcache/appcache-client.js
================================================
if (window.applicationCache) {
var appCacheStatuses = [
'uncached',
'idle',
'checking',
'downloading',
'updateready',
'obsolete'
];
var updatingAppcache = false;
var reloadRetry = null;
var appcacheUpdated = false;
Reload._onMigrate('appcache', function (retry) {
if (appcacheUpdated)
return [true];
// An uncached application (one that does not have a manifest) cannot
// be updated.
if (window.applicationCache.status === window.applicationCache.UNCACHED)
return [true];
if (!updatingAppcache) {
try {
window.applicationCache.update();
} catch (e) {
Meteor._debug('applicationCache update error', e);
// There's no point in delaying the reload if we can't update the cache.
return [true];
}
updatingAppcache = true;
}
// Delay migration until the app cache has been updated.
reloadRetry = retry;
return false;
});
// If we're migrating and the app cache is now up to date, signal that
// we're now ready to migrate.
var cacheIsNowUpToDate = function () {
if (!updatingAppcache)
return;
appcacheUpdated = true;
reloadRetry();
};
window.applicationCache.addEventListener('updateready', cacheIsNowUpToDate, false);
window.applicationCache.addEventListener('noupdate', cacheIsNowUpToDate, false);
// We'll get the obsolete event on a 404 fetching the app.manifest:
// we had previously been running with an app cache, but the app
// cache has now been disabled or the appcache package removed.
// Reload to get the new non-cached code.
window.applicationCache.addEventListener('obsolete', (function () {
if (reloadRetry) {
cacheIsNowUpToDate();
} else {
appcacheUpdated = true;
Reload._reload();
}
}), false);
} // if window.applicationCache
================================================
FILE: packages/appcache/appcache-server.js
================================================
var crypto = Npm.require('crypto');
var fs = Npm.require('fs');
var path = Npm.require('path');
var _disableSizeCheck = false;
Meteor.AppCache = {
config: function (options) {
_.each(options, function (value, option) {
if (option === 'browsers') {
disabledBrowsers = {};
_.each(value, function (browser) {
disabledBrowsers[browser] = false;
});
}
else if (option === 'onlineOnly') {
_.each(value, function (urlPrefix) {
RoutePolicy.declare(urlPrefix, 'static-online');
});
}
// option to suppress warnings for tests.
else if (option === '_disableSizeCheck') {
_disableSizeCheck = value;
}
else if (value === false) {
disabledBrowsers[option] = true;
}
else if (value === true) {
disabledBrowsers[option] = false;
} else {
throw new Error('Invalid AppCache config option: ' + option);
}
});
}
};
var disabledBrowsers = {};
var browserDisabled = function (request) {
return disabledBrowsers[request.browser.name];
};
WebApp.addHtmlAttributeHook(function (request) {
if (browserDisabled(request))
return null;
else
return { manifest: "/app.manifest" };
});
WebApp.connectHandlers.use(function (req, res, next) {
if (req.url !== '/app.manifest') {
return next();
}
// Browsers will get confused if we unconditionally serve the
// manifest and then disable the app cache for that browser. If
// the app cache had previously been enabled for a browser, it
// will continue to fetch the manifest as long as it's available,
// even if we now are not including the manifest attribute in the
// app HTML. (Firefox for example will continue to display "this
// website is asking to store data on your computer for offline
// use"). Returning a 404 gets the browser to really turn off the
// app cache.
if (browserDisabled(WebApp.categorizeRequest(req))) {
res.writeHead(404);
res.end();
return;
}
var manifest = "CACHE MANIFEST\n\n";
// After the browser has downloaded the app files from the server and
// has populated the browser's application cache, the browser will
// *only* connect to the server and reload the application if the
// *contents* of the app manifest file has changed.
//
// So to ensure that the client updates if client resources change,
// include a hash of client resources in the manifest.
manifest += "# " + WebApp.clientHash() + "\n";
// When using the autoupdate package, also include
// AUTOUPDATE_VERSION. Otherwise the client will get into an
// infinite loop of reloads when the browser doesn't fetch the new
// app HTML which contains the new version, and autoupdate will
// reload again trying to get the new code.
if (Package.autoupdate) {
var version = Package.autoupdate.Autoupdate.autoupdateVersion;
if (version !== WebApp.clientHash())
manifest += "# " + version + "\n";
}
manifest += "\n";
manifest += "CACHE:" + "\n";
manifest += "/" + "\n";
_.each(WebApp.clientPrograms[WebApp.defaultArch].manifest, function (resource) {
if (resource.where === 'client' &&
! RoutePolicy.classify(resource.url)) {
manifest += resource.url;
// If the resource is not already cacheable (has a query
// parameter, presumably with a hash or version of some sort),
// put a version with a hash in the cache.
//
// Avoid putting a non-cacheable asset into the cache, otherwise
// the user can't modify the asset until the cache headers
// expire.
if (!resource.cacheable)
manifest += "?" + resource.hash;
manifest += "\n";
}
});
manifest += "\n";
manifest += "FALLBACK:\n";
manifest += "/ /" + "\n";
// Add a fallback entry for each uncacheable asset we added above.
//
// This means requests for the bare url (/image.png instead of
// /image.png?hash) will work offline. Online, however, the browser
// will send a request to the server. Users can remove this extra
// request to the server and have the asset served from cache by
// specifying the full URL with hash in their code (manually, with
// some sort of URL rewriting helper)
_.each(WebApp.clientPrograms[WebApp.defaultArch].manifest, function (resource) {
if (resource.where === 'client' &&
! RoutePolicy.classify(resource.url) &&
!resource.cacheable) {
manifest += resource.url + " " + resource.url +
"?" + resource.hash + "\n";
}
});
manifest += "\n";
manifest += "NETWORK:\n";
// TODO adding the manifest file to NETWORK should be unnecessary?
// Want more testing to be sure.
manifest += "/app.manifest" + "\n";
_.each(
[].concat(
RoutePolicy.urlPrefixesFor('network'),
RoutePolicy.urlPrefixesFor('static-online')
),
function (urlPrefix) {
manifest += urlPrefix + "\n";
}
);
manifest += "*" + "\n";
// content length needs to be based on bytes
var body = new Buffer(manifest);
res.setHeader('Content-Type', 'text/cache-manifest');
res.setHeader('Content-Length', body.length);
return res.end(body);
});
var sizeCheck = function () {
var totalSize = 0;
_.each(WebApp.clientPrograms[WebApp.defaultArch].manifest, function (resource) {
if (resource.where === 'client' &&
! RoutePolicy.classify(resource.url)) {
totalSize += resource.size;
}
});
if (totalSize > 5 * 1024 * 1024) {
Meteor._debug(
"** You are using the appcache package but the total size of the\n" +
"** cached resources is " +
(totalSize / 1024 / 1024).toFixed(1) + "MB.\n" +
"**\n" +
"** This is over the recommended maximum of 5 MB and may break your\n" +
"** app in some browsers! See http://docs.meteor.com/#appcache\n" +
"** for more information and fixes.\n"
);
}
};
// Run the size check after user code has had a chance to run. That way,
// the size check can take into account files that the user does not
// want cached. Otherwise, the size check warning will still print even
// if the user excludes their large files with
// `Meteor.AppCache.config({onlineOnly: files})`.
Meteor.startup(function () {
if (! _disableSizeCheck)
sizeCheck();
});
================================================
FILE: packages/appcache/appcache_tests-client.js
================================================
var manifestUrl = '/app.manifest';
var appcacheTest = function (name, cb) {
Tinytest.addAsync('appcache - ' + name, function (test, next) {
HTTP.get(manifestUrl, function (err, res) {
if (err) {
test.fail(err);
} else {
cb(test, res);
}
next();
});
});
};
// Verify that the code status of the HTTP response is "OK"
appcacheTest('presence', function (test, manifest) {
test.equal(manifest.statusCode, 200, 'manifest not served');
});
// Verify the content-type HTTP header
appcacheTest('content type', function (test, manifest) {
test.equal(manifest.headers['content-type'], 'text/cache-manifest');
});
// Verify that each section header is only set once.
appcacheTest('sections uniqueness', function (test, manifest) {
var content = manifest.content;
var mandatorySectionHeaders = ['CACHE:', 'NETWORK:', 'FALLBACK:'];
var optionalSectionHeaders = ['SETTINGS'];
_.each(_.union(mandatorySectionHeaders, optionalSectionHeaders),
function (sectionHeader) {
var globalSearch = new RegExp(sectionHeader, "g");
var matches = content.match(globalSearch) || [];
test.isTrue(matches.length <= 1, sectionHeader + ' is set twice');
if (_.contains(mandatorySectionHeaders, sectionHeader)) {
test.isTrue(matches.length == 1, sectionHeader + ' is not set');
}
});
});
// Verify the content of the header and of each section of the manifest using
// regular expressions. Regular expressions matches malformed URIs but that's
// not what we're trying to catch here (the user is free to add its own content
// in the manifest -- even malformed).
appcacheTest('sections validity', function (test, manifest) {
var lines = manifest.content.split('\n');
var i = 0;
var currentRegex = null, line = null;
var nextLine = function () {
return lines[i++];
};
var eof = function () {
return i >= lines.length;
};
var nextLineMatches = function (expected, n) {
n = n || 1;
_.times(n, function () {
var testFunc = _.isRegExp(expected) ? 'matches' : 'equal';
test[testFunc](nextLine(), expected);
});
};
// Verify header validity
nextLineMatches('CACHE MANIFEST');
nextLineMatches('');
nextLineMatches(/^# [a-z0-9]+$/i, 2);
// Verify body validity
while (! eof()) {
line = nextLine();
// There are three distinct sections: 'CACHE', 'FALLBACK', and 'NETWORK'.
// A section start with its name suffixed by a colon. When we read a new
// section header, we update the currentRegex expression for the next lines
// of the section.
// XXX There is also a 'SETTINGS' section, not used by this package. If this
// section is used, the test will fail.
if (line === 'CACHE:' || line === 'NETWORK:')
currentRegex = /^\S+$/;
else if (line === 'FALLBACK:')
currentRegex = /^\S+ \S+$/;
// Blank lines and lines starting with a `#` (comments) are valid
else if (line == '' || line.match(/^#.+/))
continue;
// Outside sections, only blanks lines and comments are valid
else if (currentRegex === null)
test.fail('Invalid line ' + i + ': ' + line);
// Inside a section, a star is a valid expression
else if (line === '*')
continue;
// If it is not a blank line, not a comment, and not a header it must
// match the current section format
else
test.matches(line, currentRegex, 'line ' + i);
}
});
// Verify that resources declared on the server with the `onlineOnly` parameter
// are present in the network section of the manifest. The `appcache` package
// also automatically add the manifest (`app.manifest`) add the star symbol to
// this list and therefore we also check the presence of these two elements.
appcacheTest('network section content', function (test, manifest) {
var shouldBePresentInNetworkSection = [
"/app.manifest",
"/online/",
"/bigimage.jpg",
"/largedata.json",
"*"
];
var lines = manifest.content.split('\n');
var startNetworkSection = lines.indexOf('NETWORK:');
// We search the end of the 'NETWORK:' section by looking at the beginning
// of any potential other section. By default we set this value to
// `lines.length - 1` which is the index of the last line.
var otherSections = ['CACHE:', 'FALLBACK:', 'SETTINGS'];
var endNetworkSection = _.reduce(otherSections, function (min, sectionName) {
var position = lines.indexOf(sectionName);
return position > startNetworkSection && position < min ? position : min;
}, lines.length - 1);
// We remove the first line because it's the 'NETWORK:' header line.
var networkLines = lines.slice(startNetworkSection + 1, endNetworkSection);
_.each(shouldBePresentInNetworkSection, function (item) {
test.include(networkLines, item);
});
});
================================================
FILE: packages/appcache/appcache_tests-server.js
================================================
// For our testing purpose, we don't want the cache manifest to be
// active, all we want is the cache manifest to be manually request-able
// so we can read it and verify its validity from the client. This is
// because caching the test files would make tests non deterministic
// depending on the state of the browser cache. To do that we disable
// the "manifest" attribute of the tag. This runs after appcache
// registers its hook, so this hook overrides the return value of the
// real hook. We point to a non-existent file to clear the appcache in
// case there was previously a site running with appcache on
// localhost:3000.
WebApp.addHtmlAttributeHook(function (request) {
return { manifest: "/no-such-file" };
});
// Let's add some resources in the 'NETWORK' section
Meteor.AppCache.config({
onlineOnly: [
'/online/',
'/bigimage.jpg',
'/largedata.json'
],
_disableSizeCheck: true // don't print warnings
});
================================================
FILE: packages/appcache/package.js
================================================
Package.describe({
summary: "Enable the application cache in the browser",
version: "1.0.11"
});
Package.onUse(function (api) {
api.use('webapp', 'server');
api.use('reload', 'client');
api.use('routepolicy', 'server');
api.use('underscore', 'server');
api.use('autoupdate', 'server', {weak: true});
api.addFiles('appcache-client.js', 'client');
api.addFiles('appcache-server.js', 'server');
});
Package.onTest(function (api) {
api.use('tinytest');
api.use('appcache');
api.use('http', 'client');
api.use('underscore', 'client');
api.use('webapp', 'server');
api.addFiles('appcache_tests-server.js', 'server');
api.addFiles('appcache_tests-client.js', 'client');
});
================================================
FILE: packages/audit-argument-checks/.gitignore
================================================
.build*
================================================
FILE: packages/audit-argument-checks/README.md
================================================
# Audit-Argument-Checks
This is an internal Meteor package.
================================================
FILE: packages/audit-argument-checks/package.js
================================================
Package.describe({
summary: "Try to detect inadequate input sanitization",
version: '1.0.7'
});
// This package is empty; its presence is detected by livedata.
================================================
FILE: packages/autopublish/.gitignore
================================================
.build*
================================================
FILE: packages/autopublish/README.md
================================================
# autopublish
Publish all server collections to the client. This package is useful for prototyping an app without worrying about which clients have access to certain data, but should be removed as soon as the app needs to restrict which data is seen by the client.
The `autopublish` package is automatically added to every Meteor app by `meteor create`.
================================================
FILE: packages/autopublish/package.js
================================================
Package.describe({
summary: "(For prototyping only) Publish the entire database to all clients",
version: '1.0.7'
});
// This package is empty; its presence is detected by several other packages
// (such as ddp-server and mongo) which check for the presence of
// Package.autopublish.
================================================
FILE: packages/autoupdate/.gitignore
================================================
.build*
================================================
FILE: packages/autoupdate/QA.md
================================================
# QA Notes
## Hot Code Push Reload
Run the leaderboard example, and click on one of the names. Make a
change to the leaderboard.html file, see the client reload, and see
that the name is still selected.
## AUTOUPDATE_VERSION
Set the `AUTOUPDATE_VERSION` environment variable when running the
application:
$ AUTOUPDATE_VERSION=abc meteor
Now when you make an HTML change, it won't appear in the client
automatically. (Note the leader list flickers when the server
subscription restarts, but that's not a window reload).
Conversely, you can force a client reload (even without making any
client code changes) by restarting the server with a new value for
`AUTOUPDATE_VERSION`.
## No Client Reload on Server-only Change
Revert previous changes and run the example without setting
AUTOUPDATE_VERSION.
Note that it might look like the browser is reloading because the page
content in the leaderboard example will flicker when the server
restarts because the example is using autopublish, but that the window
won't actually be reloading.
In the browser console, assign a variable such as `a = true` so that
you can easily verify that the client hasn't reloaded.
In the leaderboard example directory, create the `server` directory
and add `foo.js`. See in the browser console that `a` is still
defined, indicating the browser hasn't reloaded.
## Test with the appcache
Add the appcache package:
$ meteor add appcache
And do the above tests again.
Note that if 1) AUTOUPDATE_VERSION is set so the client doesn't
automatically reload, 2) you make a client change, and 3) you manually
reload the browser page, you usually *won't* see the updated HTML the
*first* time you reload (unless the browser happened to check the app
cache manifest between steps 2 and 3). This is normal browser app
cache behavior: the browser populates the app cache in the background,
so it doesn't wait for new files to download before displaying the web
page.
## Autoupdate.newClientAvailable
Undo previous changes made, such as by using `git checkout .` Reload
the client, which will cause the browser to stop using the app cache.
It's hard to see the `newClientAvailable` reactive variable when the
client automatically reloads. Remove the `hot-code-push` package so you can
see the variable without having the client also reload.
$ meteor remove meteor-base
$ meteor add meteor webapp ddp autoupdate
Add to leaderboard.js:
Template.leaderboard.helpers({
available: function () {
return Autoupdate.newClientAvailable().toString();
}
});
And add `{{available}}` to the leaderboard template in
leaderboard.html.
Initially you'll see `false`, and then when you make a change to the
leaderboard HTML you'll see the variable change to `true`. (You won't
see the new HTML on the client because you disabled reload).
Amusingly, you can undo the addition you made to the HTML and the "new
client available" variable will go back to `false` (you now don't have
client code available on the server different than what's running in
the browser), because by default the client version is based on a hash
of the client files.
## DDP Version Negotiation Failure
A quick way to test DDP version negotiation failure is to force the
client to use the wrong DDP version. At the top of
livedata_connection.js:
var Connection = function (url, options) {
var self = this;
+ options.supportedDDPVersions = ['abc'];
You will see the client reload (in the hope that new client code will
be available that can successfully negotiation the DDP version). Each
reload takes longer than the one before, using an exponential backoff.
If you remove the `options.supportedDDPVersions` line and allow the
client to connect (or manually reload the browser page so you don't
have to wait), this will reset the exponential backoff counter.
You can verify the counter was reset by adding the line back in a
second time, and you'll see the reload cycle start over again with
first reloading quickly, and then again taking longer between tries.
================================================
FILE: packages/autoupdate/README.md
================================================
# autoupdate
This package is the heart of Meteor's Hot Code Push functionality. It has a
client component and a server component component. The client component uses a
DDP API provided by the server to subscribe to the version ID of the most recent
build of the app's client. When it sees that a new version is available, it uses
the [reload](https://atmospherejs.com/meteor/reload) package (if included in the
app) to gracefully save the app's state and reload it in place.
`autoupdate` is part of the [Webapp](https://www.meteor.com/webapp)
project.
================================================
FILE: packages/autoupdate/autoupdate_client.js
================================================
// Subscribe to the `meteor_autoupdate_clientVersions` collection,
// which contains the set of acceptable client versions.
//
// A "hard code push" occurs when the running client version is not in
// the set of acceptable client versions (or the server updates the
// collection, there is a published client version marked `current` and
// the running client version is no longer in the set).
//
// When the `reload` package is loaded, a hard code push causes
// the browser to reload, so that it will load the latest client
// version from the server.
//
// A "soft code push" represents the situation when the running client
// version is in the set of acceptable versions, but there is a newer
// version available on the server.
//
// `Autoupdate.newClientAvailable` is a reactive data source which
// becomes `true` if there is a new version of the client is available on
// the server.
//
// This package doesn't implement a soft code reload process itself,
// but `newClientAvailable` could be used for example to display a
// "click to reload" link to the user.
// The client version of the client code currently running in the
// browser.
var autoupdateVersion = __meteor_runtime_config__.autoupdateVersion || "unknown";
var autoupdateVersionRefreshable =
__meteor_runtime_config__.autoupdateVersionRefreshable || "unknown";
// The collection of acceptable client versions.
ClientVersions = new Mongo.Collection("meteor_autoupdate_clientVersions");
Autoupdate = {};
Autoupdate.newClientAvailable = function () {
return !! ClientVersions.findOne({
_id: "version",
version: {$ne: autoupdateVersion} }) ||
!! ClientVersions.findOne({
_id: "version-refreshable",
version: {$ne: autoupdateVersionRefreshable} });
};
Autoupdate._ClientVersions = ClientVersions; // Used by a self-test
var knownToSupportCssOnLoad = false;
var retry = new Retry({
// Unlike the stream reconnect use of Retry, which we want to be instant
// in normal operation, this is a wacky failure. We don't want to retry
// right away, we can start slowly.
//
// A better way than timeconstants here might be to use the knowledge
// of when we reconnect to help trigger these retries. Typically, the
// server fixing code will result in a restart and reconnect, but
// potentially the subscription could have a transient error.
minCount: 0, // don't do any immediate retries
baseTimeout: 30*1000 // start with 30s
});
var failures = 0;
Autoupdate._retrySubscription = function () {
Meteor.subscribe("meteor_autoupdate_clientVersions", {
onError: function (error) {
Meteor._debug("autoupdate subscription failed:", error);
failures++;
retry.retryLater(failures, function () {
// Just retry making the subscription, don't reload the whole
// page. While reloading would catch more cases (for example,
// the server went back a version and is now doing old-style hot
// code push), it would also be more prone to reload loops,
// which look really bad to the user. Just retrying the
// subscription over DDP means it is at least possible to fix by
// updating the server.
Autoupdate._retrySubscription();
});
},
onReady: function () {
if (Package.reload) {
var checkNewVersionDocument = function (doc) {
var self = this;
if (doc._id === 'version-refreshable' &&
doc.version !== autoupdateVersionRefreshable) {
autoupdateVersionRefreshable = doc.version;
// Switch out old css links for the new css links. Inspired by:
// https://github.com/guard/guard-livereload/blob/master/js/livereload.js#L710
var newCss = (doc.assets && doc.assets.allCss) || [];
var oldLinks = [];
_.each(document.getElementsByTagName('link'), function (link) {
if (link.className === '__meteor-css__') {
oldLinks.push(link);
}
});
var waitUntilCssLoads = function (link, callback) {
var executeCallback = _.once(callback);
link.onload = function () {
knownToSupportCssOnLoad = true;
executeCallback();
};
if (! knownToSupportCssOnLoad) {
var id = Meteor.setInterval(function () {
if (link.sheet) {
executeCallback();
Meteor.clearInterval(id);
}
}, 50);
}
};
var removeOldLinks = _.after(newCss.length, function () {
_.each(oldLinks, function (oldLink) {
oldLink.parentNode.removeChild(oldLink);
});
});
var attachStylesheetLink = function (newLink) {
document.getElementsByTagName("head").item(0).appendChild(newLink);
waitUntilCssLoads(newLink, function () {
Meteor.setTimeout(removeOldLinks, 200);
});
};
if (newCss.length !== 0) {
_.each(newCss, function (css) {
var newLink = document.createElement("link");
newLink.setAttribute("rel", "stylesheet");
newLink.setAttribute("type", "text/css");
newLink.setAttribute("class", "__meteor-css__");
newLink.setAttribute("href", Meteor._relativeToSiteRootUrl(css.url));
attachStylesheetLink(newLink);
});
} else {
removeOldLinks();
}
}
else if (doc._id === 'version' && doc.version !== autoupdateVersion) {
handle && handle.stop();
if (Package.reload) {
Package.reload.Reload._reload();
}
}
};
var handle = ClientVersions.find().observe({
added: checkNewVersionDocument,
changed: checkNewVersionDocument
});
}
}
});
};
Autoupdate._retrySubscription();
================================================
FILE: packages/autoupdate/autoupdate_cordova.js
================================================
var autoupdateVersionCordova = __meteor_runtime_config__.autoupdateVersionCordova || "unknown";
// The collection of acceptable client versions.
ClientVersions = new Mongo.Collection("meteor_autoupdate_clientVersions");
Autoupdate = {};
Autoupdate.newClientAvailable = function() {
return !! ClientVersions.findOne({
_id: 'version-cordova',
version: {$ne: autoupdateVersionCordova}
});
};
var retry = new Retry({
// Unlike the stream reconnect use of Retry, which we want to be instant
// in normal operation, this is a wacky failure. We don't want to retry
// right away, we can start slowly.
//
// A better way than timeconstants here might be to use the knowledge
// of when we reconnect to help trigger these retries. Typically, the
// server fixing code will result in a restart and reconnect, but
// potentially the subscription could have a transient error.
minCount: 0, // don't do any immediate retries
baseTimeout: 30*1000 // start with 30s
});
var failures = 0;
Autoupdate._retrySubscription = function() {
var appId = __meteor_runtime_config__.appId;
Meteor.subscribe("meteor_autoupdate_clientVersions", appId, {
onError: function(error) {
console.log("autoupdate subscription failed:", error);
failures++;
retry.retryLater(failures, function() {
// Just retry making the subscription, don't reload the whole
// page. While reloading would catch more cases (for example,
// the server went back a version and is now doing old-style hot
// code push), it would also be more prone to reload loops,
// which look really bad to the user. Just retrying the
// subscription over DDP means it is at least possible to fix by
// updating the server.
Autoupdate._retrySubscription();
});
},
onReady: function() {
if (Package.reload) {
var checkNewVersionDocument = function(doc) {
var self = this;
if (doc.version !== autoupdateVersionCordova) {
newVersionAvailable();
}
};
var handle = ClientVersions.find({_id: 'version-cordova'}).observe({
added: checkNewVersionDocument,
changed: checkNewVersionDocument
});
}
}
});
};
Meteor.startup(function() {
WebAppLocalServer.onNewVersionReady(function() {
if (Package.reload) {
Package.reload.Reload._reload();
}
});
Autoupdate._retrySubscription();
});
var newVersionAvailable = function() {
WebAppLocalServer.checkForUpdates();
}
================================================
FILE: packages/autoupdate/autoupdate_server.js
================================================
// Publish the current client versions to the client. When a client
// sees the subscription change and that there is a new version of the
// client available on the server, it can reload.
//
// By default there are two current client versions. The refreshable client
// version is identified by a hash of the client resources seen by the browser
// that are refreshable, such as CSS, while the non refreshable client version
// is identified by a hash of the rest of the client assets
// (the HTML, code, and static files in the `public` directory).
//
// If the environment variable `AUTOUPDATE_VERSION` is set it will be
// used as the client id instead. You can use this to control when
// the client reloads. For example, if you want to only force a
// reload on major changes, you can use a custom AUTOUPDATE_VERSION
// which you only change when something worth pushing to clients
// immediately happens.
//
// The server publishes a `meteor_autoupdate_clientVersions`
// collection. There are two documents in this collection, a document
// with _id 'version' which represents the non refreshable client assets,
// and a document with _id 'version-refreshable' which represents the
// refreshable client assets. Each document has a 'version' field
// which is equivalent to the hash of the relevant assets. The refreshable
// document also contains a list of the refreshable assets, so that the client
// can swap in the new assets without forcing a page refresh. Clients can
// observe changes on these documents to detect when there is a new
// version available.
//
// In this implementation only two documents are present in the collection
// the current refreshable client version and the current nonRefreshable client
// version. Developers can easily experiment with different versioning and
// updating models by forking this package.
var Future = Npm.require("fibers/future");
Autoupdate = {};
// The collection of acceptable client versions.
ClientVersions = new Mongo.Collection("meteor_autoupdate_clientVersions",
{ connection: null });
// The client hash includes __meteor_runtime_config__, so wait until
// all packages have loaded and have had a chance to populate the
// runtime config before using the client hash as our default auto
// update version id.
// Note: Tests allow people to override Autoupdate.autoupdateVersion before
// startup.
Autoupdate.autoupdateVersion = null;
Autoupdate.autoupdateVersionRefreshable = null;
Autoupdate.autoupdateVersionCordova = null;
Autoupdate.appId = __meteor_runtime_config__.appId = process.env.APP_ID;
var syncQueue = new Meteor._SynchronousQueue();
// updateVersions can only be called after the server has fully loaded.
var updateVersions = function (shouldReloadClientProgram) {
// Step 1: load the current client program on the server and update the
// hash values in __meteor_runtime_config__.
if (shouldReloadClientProgram) {
WebAppInternals.reloadClientPrograms();
}
// If we just re-read the client program, or if we don't have an autoupdate
// version, calculate it.
if (shouldReloadClientProgram || Autoupdate.autoupdateVersion === null) {
Autoupdate.autoupdateVersion =
process.env.AUTOUPDATE_VERSION ||
WebApp.calculateClientHashNonRefreshable();
}
// If we just recalculated it OR if it was set by (eg) test-in-browser,
// ensure it ends up in __meteor_runtime_config__.
__meteor_runtime_config__.autoupdateVersion =
Autoupdate.autoupdateVersion;
Autoupdate.autoupdateVersionRefreshable =
__meteor_runtime_config__.autoupdateVersionRefreshable =
process.env.AUTOUPDATE_VERSION ||
WebApp.calculateClientHashRefreshable();
Autoupdate.autoupdateVersionCordova =
__meteor_runtime_config__.autoupdateVersionCordova =
process.env.AUTOUPDATE_VERSION ||
WebApp.calculateClientHashCordova();
// Step 2: form the new client boilerplate which contains the updated
// assets and __meteor_runtime_config__.
if (shouldReloadClientProgram) {
WebAppInternals.generateBoilerplate();
}
// XXX COMPAT WITH 0.8.3
if (! ClientVersions.findOne({current: true})) {
// To ensure apps with version of Meteor prior to 0.9.0 (in
// which the structure of documents in `ClientVersions` was
// different) also reload.
ClientVersions.insert({current: true});
}
if (! ClientVersions.findOne({_id: "version"})) {
ClientVersions.insert({
_id: "version",
version: Autoupdate.autoupdateVersion
});
} else {
ClientVersions.update("version", { $set: {
version: Autoupdate.autoupdateVersion
}});
}
if (! ClientVersions.findOne({_id: "version-cordova"})) {
ClientVersions.insert({
_id: "version-cordova",
version: Autoupdate.autoupdateVersionCordova,
refreshable: false
});
} else {
ClientVersions.update("version-cordova", { $set: {
version: Autoupdate.autoupdateVersionCordova
}});
}
// Use `onListening` here because we need to use
// `WebAppInternals.refreshableAssets`, which is only set after
// `WebApp.generateBoilerplate` is called by `main` in webapp.
WebApp.onListening(function () {
if (! ClientVersions.findOne({_id: "version-refreshable"})) {
ClientVersions.insert({
_id: "version-refreshable",
version: Autoupdate.autoupdateVersionRefreshable,
assets: WebAppInternals.refreshableAssets
});
} else {
ClientVersions.update("version-refreshable", { $set: {
version: Autoupdate.autoupdateVersionRefreshable,
assets: WebAppInternals.refreshableAssets
}});
}
});
};
Meteor.publish(
"meteor_autoupdate_clientVersions",
function (appId) {
// `null` happens when a client doesn't have an appId and passes
// `undefined` to `Meteor.subscribe`. `undefined` is translated to
// `null` as JSON doesn't have `undefined.
check(appId, Match.OneOf(String, undefined, null));
// Don't notify clients using wrong appId such as mobile apps built with a
// different server but pointing at the same local url
if (Autoupdate.appId && appId && Autoupdate.appId !== appId)
return [];
return ClientVersions.find();
},
{is_auto: true}
);
Meteor.startup(function () {
updateVersions(false);
});
var fut = new Future();
// We only want 'refresh' to trigger 'updateVersions' AFTER onListen,
// so we add a queued task that waits for onListen before 'refresh' can queue
// tasks. Note that the `onListening` callbacks do not fire until after
// Meteor.startup, so there is no concern that the 'updateVersions' calls from
// 'refresh' will overlap with the `updateVersions` call from Meteor.startup.
syncQueue.queueTask(function () {
fut.wait();
});
WebApp.onListening(function () {
fut.return();
});
var enqueueVersionsRefresh = function () {
syncQueue.queueTask(function () {
updateVersions(true);
});
};
// Listen for the special {refresh: 'client'} message, which signals that a
// client asset has changed.
process.on('message', Meteor.bindEnvironment(function (m) {
if (m && m.refresh === 'client') {
enqueueVersionsRefresh();
}
}, "handling client refresh message"));
// Another way to tell the process to refresh: send SIGHUP signal
process.on('SIGHUP', Meteor.bindEnvironment(function () {
enqueueVersionsRefresh();
}, "handling SIGHUP signal for refresh"));
================================================
FILE: packages/autoupdate/package.js
================================================
Package.describe({
summary: "Update the client when new client code is available",
version: '1.2.10'
});
Package.onUse(function (api) {
api.use([
'webapp',
'check'
], 'server');
api.use([
'tracker',
'retry'
], 'client');
api.use([
'ddp',
'mongo',
'underscore'
], ['client', 'server']);
api.use(['http', 'random'], 'web.cordova');
api.addFiles('autoupdate_server.js', 'server');
api.addFiles('autoupdate_client.js', 'web.browser');
api.addFiles('autoupdate_cordova.js', 'web.cordova');
api.export('Autoupdate');
});
================================================
FILE: packages/babel-compiler/.npm/package/.gitignore
================================================
node_modules
================================================
FILE: packages/babel-compiler/.npm/package/README
================================================
This directory and the files immediately inside it are automatically generated
when you change this package's NPM dependencies. Commit the files in this
directory (npm-shrinkwrap.json, .gitignore, and this README) to source control
so that others run the same versions of sub-dependencies.
You should NOT check in the node_modules directory that Meteor automatically
creates; if you are using git, the .gitignore file tells git to ignore it.
================================================
FILE: packages/babel-compiler/.npm/package/npm-shrinkwrap.json
================================================
{
"dependencies": {
"acorn": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-3.2.0.tgz",
"from": "acorn@>=3.2.0 <3.3.0"
},
"amdefine": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.0.tgz",
"from": "amdefine@>=0.0.4"
},
"ansi-regex": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.0.0.tgz",
"from": "ansi-regex@>=2.0.0 <3.0.0"
},
"ansi-styles": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
"from": "ansi-styles@>=2.2.1 <3.0.0"
},
"ast-types": {
"version": "0.8.16",
"resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.8.16.tgz",
"from": "ast-types@>=0.8.16 <0.9.0"
},
"babel-code-frame": {
"version": "6.8.0",
"resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.8.0.tgz",
"from": "babel-code-frame@>=6.8.0 <7.0.0"
},
"babel-core": {
"version": "6.9.1",
"resolved": "https://registry.npmjs.org/babel-core/-/babel-core-6.9.1.tgz",
"from": "babel-core@>=6.9.1 <6.10.0"
},
"babel-generator": {
"version": "6.10.2",
"resolved": "https://registry.npmjs.org/babel-generator/-/babel-generator-6.10.2.tgz",
"from": "babel-generator@>=6.9.0 <7.0.0"
},
"babel-helper-builder-react-jsx": {
"version": "6.9.0",
"resolved": "https://registry.npmjs.org/babel-helper-builder-react-jsx/-/babel-helper-builder-react-jsx-6.9.0.tgz",
"from": "babel-helper-builder-react-jsx@>=6.8.0 <7.0.0"
},
"babel-helper-call-delegate": {
"version": "6.8.0",
"resolved": "https://registry.npmjs.org/babel-helper-call-delegate/-/babel-helper-call-delegate-6.8.0.tgz",
"from": "babel-helper-call-delegate@>=6.8.0 <7.0.0"
},
"babel-helper-define-map": {
"version": "6.9.0",
"resolved": "https://registry.npmjs.org/babel-helper-define-map/-/babel-helper-define-map-6.9.0.tgz",
"from": "babel-helper-define-map@>=6.9.0 <7.0.0"
},
"babel-helper-function-name": {
"version": "6.8.0",
"resolved": "https://registry.npmjs.org/babel-helper-function-name/-/babel-helper-function-name-6.8.0.tgz",
"from": "babel-helper-function-name@>=6.8.0 <7.0.0"
},
"babel-helper-get-function-arity": {
"version": "6.8.0",
"resolved": "https://registry.npmjs.org/babel-helper-get-function-arity/-/babel-helper-get-function-arity-6.8.0.tgz",
"from": "babel-helper-get-function-arity@>=6.8.0 <7.0.0"
},
"babel-helper-hoist-variables": {
"version": "6.8.0",
"resolved": "https://registry.npmjs.org/babel-helper-hoist-variables/-/babel-helper-hoist-variables-6.8.0.tgz",
"from": "babel-helper-hoist-variables@>=6.8.0 <7.0.0"
},
"babel-helper-optimise-call-expression": {
"version": "6.8.0",
"resolved": "https://registry.npmjs.org/babel-helper-optimise-call-expression/-/babel-helper-optimise-call-expression-6.8.0.tgz",
"from": "babel-helper-optimise-call-expression@>=6.8.0 <7.0.0"
},
"babel-helper-regex": {
"version": "6.9.0",
"resolved": "https://registry.npmjs.org/babel-helper-regex/-/babel-helper-regex-6.9.0.tgz",
"from": "babel-helper-regex@>=6.8.0 <7.0.0"
},
"babel-helper-replace-supers": {
"version": "6.8.0",
"resolved": "https://registry.npmjs.org/babel-helper-replace-supers/-/babel-helper-replace-supers-6.8.0.tgz",
"from": "babel-helper-replace-supers@>=6.8.0 <7.0.0"
},
"babel-helpers": {
"version": "6.8.0",
"resolved": "https://registry.npmjs.org/babel-helpers/-/babel-helpers-6.8.0.tgz",
"from": "babel-helpers@>=6.8.0 <7.0.0"
},
"babel-messages": {
"version": "6.8.0",
"resolved": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.8.0.tgz",
"from": "babel-messages@>=6.8.0 <7.0.0"
},
"babel-plugin-check-es2015-constants": {
"version": "6.8.0",
"resolved": "https://registry.npmjs.org/babel-plugin-check-es2015-constants/-/babel-plugin-check-es2015-constants-6.8.0.tgz",
"from": "babel-plugin-check-es2015-constants@>=6.8.0 <7.0.0"
},
"babel-plugin-syntax-async-functions": {
"version": "6.8.0",
"resolved": "https://registry.npmjs.org/babel-plugin-syntax-async-functions/-/babel-plugin-syntax-async-functions-6.8.0.tgz",
"from": "babel-plugin-syntax-async-functions@>=6.8.0 <7.0.0"
},
"babel-plugin-syntax-async-generators": {
"version": "6.8.0",
"resolved": "https://registry.npmjs.org/babel-plugin-syntax-async-generators/-/babel-plugin-syntax-async-generators-6.8.0.tgz",
"from": "babel-plugin-syntax-async-generators@>=6.8.0 <7.0.0"
},
"babel-plugin-syntax-flow": {
"version": "6.8.0",
"resolved": "https://registry.npmjs.org/babel-plugin-syntax-flow/-/babel-plugin-syntax-flow-6.8.0.tgz",
"from": "babel-plugin-syntax-flow@>=6.8.0 <7.0.0"
},
"babel-plugin-syntax-jsx": {
"version": "6.8.0",
"resolved": "https://registry.npmjs.org/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.8.0.tgz",
"from": "babel-plugin-syntax-jsx@>=6.3.13 <7.0.0"
},
"babel-plugin-syntax-object-rest-spread": {
"version": "6.8.0",
"resolved": "https://registry.npmjs.org/babel-plugin-syntax-object-rest-spread/-/babel-plugin-syntax-object-rest-spread-6.8.0.tgz",
"from": "babel-plugin-syntax-object-rest-spread@>=6.8.0 <7.0.0"
},
"babel-plugin-syntax-trailing-function-commas": {
"version": "6.8.0",
"resolved": "https://registry.npmjs.org/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-6.8.0.tgz",
"from": "babel-plugin-syntax-trailing-function-commas@>=6.8.0 <7.0.0"
},
"babel-plugin-transform-es2015-arrow-functions": {
"version": "6.8.0",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-arrow-functions/-/babel-plugin-transform-es2015-arrow-functions-6.8.0.tgz",
"from": "babel-plugin-transform-es2015-arrow-functions@>=6.8.0 <7.0.0"
},
"babel-plugin-transform-es2015-block-scoped-functions": {
"version": "6.8.0",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-block-scoped-functions/-/babel-plugin-transform-es2015-block-scoped-functions-6.8.0.tgz",
"from": "babel-plugin-transform-es2015-block-scoped-functions@>=6.8.0 <7.0.0"
},
"babel-plugin-transform-es2015-block-scoping": {
"version": "6.10.1",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-block-scoping/-/babel-plugin-transform-es2015-block-scoping-6.10.1.tgz",
"from": "babel-plugin-transform-es2015-block-scoping@>=6.10.1 <7.0.0"
},
"babel-plugin-transform-es2015-classes": {
"version": "6.9.0",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-classes/-/babel-plugin-transform-es2015-classes-6.9.0.tgz",
"from": "babel-plugin-transform-es2015-classes@>=6.9.0 <7.0.0"
},
"babel-plugin-transform-es2015-computed-properties": {
"version": "6.8.0",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-computed-properties/-/babel-plugin-transform-es2015-computed-properties-6.8.0.tgz",
"from": "babel-plugin-transform-es2015-computed-properties@>=6.8.0 <7.0.0"
},
"babel-plugin-transform-es2015-destructuring": {
"version": "6.9.0",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-destructuring/-/babel-plugin-transform-es2015-destructuring-6.9.0.tgz",
"from": "babel-plugin-transform-es2015-destructuring@>=6.9.0 <7.0.0"
},
"babel-plugin-transform-es2015-for-of": {
"version": "6.8.0",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-for-of/-/babel-plugin-transform-es2015-for-of-6.8.0.tgz",
"from": "babel-plugin-transform-es2015-for-of@>=6.8.0 <7.0.0"
},
"babel-plugin-transform-es2015-function-name": {
"version": "6.9.0",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-function-name/-/babel-plugin-transform-es2015-function-name-6.9.0.tgz",
"from": "babel-plugin-transform-es2015-function-name@>=6.9.0 <7.0.0"
},
"babel-plugin-transform-es2015-literals": {
"version": "6.8.0",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-literals/-/babel-plugin-transform-es2015-literals-6.8.0.tgz",
"from": "babel-plugin-transform-es2015-literals@>=6.8.0 <7.0.0"
},
"babel-plugin-transform-es2015-modules-commonjs": {
"version": "6.8.0",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.8.0.tgz",
"from": "babel-plugin-transform-es2015-modules-commonjs@>=6.8.0 <6.9.0"
},
"babel-plugin-transform-es2015-object-super": {
"version": "6.8.0",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-object-super/-/babel-plugin-transform-es2015-object-super-6.8.0.tgz",
"from": "babel-plugin-transform-es2015-object-super@>=6.8.0 <7.0.0"
},
"babel-plugin-transform-es2015-parameters": {
"version": "6.9.0",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-parameters/-/babel-plugin-transform-es2015-parameters-6.9.0.tgz",
"from": "babel-plugin-transform-es2015-parameters@>=6.9.0 <7.0.0"
},
"babel-plugin-transform-es2015-shorthand-properties": {
"version": "6.8.0",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-shorthand-properties/-/babel-plugin-transform-es2015-shorthand-properties-6.8.0.tgz",
"from": "babel-plugin-transform-es2015-shorthand-properties@>=6.8.0 <7.0.0"
},
"babel-plugin-transform-es2015-spread": {
"version": "6.8.0",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-spread/-/babel-plugin-transform-es2015-spread-6.8.0.tgz",
"from": "babel-plugin-transform-es2015-spread@>=6.8.0 <7.0.0"
},
"babel-plugin-transform-es2015-sticky-regex": {
"version": "6.8.0",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-sticky-regex/-/babel-plugin-transform-es2015-sticky-regex-6.8.0.tgz",
"from": "babel-plugin-transform-es2015-sticky-regex@>=6.8.0 <7.0.0"
},
"babel-plugin-transform-es2015-template-literals": {
"version": "6.8.0",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-template-literals/-/babel-plugin-transform-es2015-template-literals-6.8.0.tgz",
"from": "babel-plugin-transform-es2015-template-literals@>=6.8.0 <7.0.0"
},
"babel-plugin-transform-es2015-typeof-symbol": {
"version": "6.8.0",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-typeof-symbol/-/babel-plugin-transform-es2015-typeof-symbol-6.8.0.tgz",
"from": "babel-plugin-transform-es2015-typeof-symbol@>=6.8.0 <7.0.0"
},
"babel-plugin-transform-es2015-unicode-regex": {
"version": "6.8.0",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-unicode-regex/-/babel-plugin-transform-es2015-unicode-regex-6.8.0.tgz",
"from": "babel-plugin-transform-es2015-unicode-regex@>=6.8.0 <7.0.0"
},
"babel-plugin-transform-es3-member-expression-literals": {
"version": "6.8.0",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-es3-member-expression-literals/-/babel-plugin-transform-es3-member-expression-literals-6.8.0.tgz",
"from": "babel-plugin-transform-es3-member-expression-literals@>=6.8.0 <7.0.0"
},
"babel-plugin-transform-es3-property-literals": {
"version": "6.8.0",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-es3-property-literals/-/babel-plugin-transform-es3-property-literals-6.8.0.tgz",
"from": "babel-plugin-transform-es3-property-literals@>=6.8.0 <7.0.0"
},
"babel-plugin-transform-flow-strip-types": {
"version": "6.8.0",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-flow-strip-types/-/babel-plugin-transform-flow-strip-types-6.8.0.tgz",
"from": "babel-plugin-transform-flow-strip-types@>=6.8.0 <7.0.0"
},
"babel-plugin-transform-object-rest-spread": {
"version": "6.8.0",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-object-rest-spread/-/babel-plugin-transform-object-rest-spread-6.8.0.tgz",
"from": "babel-plugin-transform-object-rest-spread@>=6.8.0 <7.0.0"
},
"babel-plugin-transform-react-display-name": {
"version": "6.8.0",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-react-display-name/-/babel-plugin-transform-react-display-name-6.8.0.tgz",
"from": "babel-plugin-transform-react-display-name@>=6.3.13 <7.0.0"
},
"babel-plugin-transform-react-jsx": {
"version": "6.8.0",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-react-jsx/-/babel-plugin-transform-react-jsx-6.8.0.tgz",
"from": "babel-plugin-transform-react-jsx@>=6.3.13 <7.0.0"
},
"babel-plugin-transform-react-jsx-source": {
"version": "6.9.0",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-react-jsx-source/-/babel-plugin-transform-react-jsx-source-6.9.0.tgz",
"from": "babel-plugin-transform-react-jsx-source@>=6.3.13 <7.0.0"
},
"babel-plugin-transform-regenerator": {
"version": "6.9.0",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-regenerator/-/babel-plugin-transform-regenerator-6.9.0.tgz",
"from": "babel-plugin-transform-regenerator@>=6.9.0 <7.0.0"
},
"babel-plugin-transform-runtime": {
"version": "6.9.0",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-runtime/-/babel-plugin-transform-runtime-6.9.0.tgz",
"from": "babel-plugin-transform-runtime@>=6.9.0 <6.10.0"
},
"babel-plugin-transform-strict-mode": {
"version": "6.8.0",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-strict-mode/-/babel-plugin-transform-strict-mode-6.8.0.tgz",
"from": "babel-plugin-transform-strict-mode@>=6.8.0 <7.0.0"
},
"babel-preset-meteor": {
"version": "6.11.0",
"resolved": "https://registry.npmjs.org/babel-preset-meteor/-/babel-preset-meteor-6.11.0.tgz",
"from": "babel-preset-meteor@>=6.11.0 <6.12.0"
},
"babel-preset-react": {
"version": "6.5.0",
"resolved": "https://registry.npmjs.org/babel-preset-react/-/babel-preset-react-6.5.0.tgz",
"from": "babel-preset-react@>=6.5.0 <6.6.0"
},
"babel-register": {
"version": "6.9.0",
"resolved": "https://registry.npmjs.org/babel-register/-/babel-register-6.9.0.tgz",
"from": "babel-register@>=6.9.0 <7.0.0"
},
"babel-runtime": {
"version": "6.9.2",
"resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.9.2.tgz",
"from": "babel-runtime@>=6.9.2 <6.10.0"
},
"babel-template": {
"version": "6.9.0",
"resolved": "https://registry.npmjs.org/babel-template/-/babel-template-6.9.0.tgz",
"from": "babel-template@>=6.9.0 <6.10.0"
},
"babel-traverse": {
"version": "6.9.0",
"resolved": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.9.0.tgz",
"from": "babel-traverse@>=6.9.0 <6.10.0"
},
"babel-types": {
"version": "6.10.2",
"resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.10.2.tgz",
"from": "babel-types@>=6.10.0 <6.11.0"
},
"babylon": {
"version": "6.8.1",
"resolved": "https://registry.npmjs.org/babylon/-/babylon-6.8.1.tgz",
"from": "babylon@>=6.8.1 <6.9.0"
},
"balanced-match": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.1.tgz",
"from": "balanced-match@>=0.4.1 <0.5.0"
},
"brace-expansion": {
"version": "1.1.5",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.5.tgz",
"from": "brace-expansion@>=1.0.0 <2.0.0"
},
"chalk": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
"from": "chalk@>=1.1.0 <2.0.0"
},
"concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
"from": "concat-map@0.0.1"
},
"convert-source-map": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.2.0.tgz",
"from": "convert-source-map@>=1.2.0 <1.3.0"
},
"core-js": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/core-js/-/core-js-2.4.0.tgz",
"from": "core-js@>=2.4.0 <3.0.0"
},
"debug": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz",
"from": "debug@>=2.1.1 <3.0.0"
},
"detect-indent": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-3.0.1.tgz",
"from": "detect-indent@>=3.0.1 <4.0.0"
},
"escape-string-regexp": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
"from": "escape-string-regexp@>=1.0.2 <2.0.0"
},
"esutils": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz",
"from": "esutils@>=2.0.2 <3.0.0"
},
"get-stdin": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz",
"from": "get-stdin@>=4.0.1 <5.0.0"
},
"globals": {
"version": "8.18.0",
"resolved": "https://registry.npmjs.org/globals/-/globals-8.18.0.tgz",
"from": "globals@>=8.3.0 <9.0.0"
},
"has-ansi": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz",
"from": "has-ansi@>=2.0.0 <3.0.0"
},
"home-or-tmp": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/home-or-tmp/-/home-or-tmp-1.0.0.tgz",
"from": "home-or-tmp@>=1.0.0 <2.0.0"
},
"invariant": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.1.tgz",
"from": "invariant@>=2.2.0 <3.0.0"
},
"is-finite": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.1.tgz",
"from": "is-finite@>=1.0.0 <2.0.0"
},
"js-tokens": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-1.0.3.tgz",
"from": "js-tokens@>=1.0.2 <2.0.0"
},
"jsesc": {
"version": "0.5.0",
"resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz",
"from": "jsesc@>=0.5.0 <0.6.0"
},
"json5": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/json5/-/json5-0.4.0.tgz",
"from": "json5@>=0.4.0 <0.5.0"
},
"lodash": {
"version": "4.13.1",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.13.1.tgz",
"from": "lodash@>=4.13.1 <4.14.0"
},
"loose-envify": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.2.0.tgz",
"from": "loose-envify@>=1.0.0 <2.0.0"
},
"magic-string": {
"version": "0.15.1",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.15.1.tgz",
"from": "magic-string@>=0.15.0 <0.16.0"
},
"meteor-babel": {
"version": "0.11.6",
"resolved": "https://registry.npmjs.org/meteor-babel/-/meteor-babel-0.11.6.tgz",
"from": "meteor-babel@0.11.6"
},
"meteor-babel-helpers": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/meteor-babel-helpers/-/meteor-babel-helpers-0.0.3.tgz",
"from": "meteor-babel-helpers@0.0.3"
},
"minimatch": {
"version": "2.0.10",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-2.0.10.tgz",
"from": "minimatch@>=2.0.3 <3.0.0"
},
"minimist": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
"from": "minimist@>=1.1.0 <2.0.0"
},
"mkdirp": {
"version": "0.5.1",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
"from": "mkdirp@>=0.5.1 <0.6.0",
"dependencies": {
"minimist": {
"version": "0.0.8",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
"from": "minimist@0.0.8"
}
}
},
"ms": {
"version": "0.7.1",
"resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz",
"from": "ms@0.7.1"
},
"number-is-nan": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.0.tgz",
"from": "number-is-nan@>=1.0.0 <2.0.0"
},
"os-tmpdir": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.1.tgz",
"from": "os-tmpdir@>=1.0.1 <2.0.0"
},
"path-exists": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-1.0.0.tgz",
"from": "path-exists@>=1.0.0 <2.0.0"
},
"path-is-absolute": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.0.tgz",
"from": "path-is-absolute@>=1.0.0 <2.0.0"
},
"private": {
"version": "0.1.6",
"resolved": "https://registry.npmjs.org/private/-/private-0.1.6.tgz",
"from": "private@>=0.1.6 <0.2.0"
},
"regenerate": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.3.1.tgz",
"from": "regenerate@>=1.2.1 <2.0.0"
},
"regenerator-runtime": {
"version": "0.9.5",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.9.5.tgz",
"from": "regenerator-runtime@>=0.9.5 <0.10.0"
},
"regexpu-core": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-1.0.0.tgz",
"from": "regexpu-core@>=1.0.0 <2.0.0"
},
"regjsgen": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.2.0.tgz",
"from": "regjsgen@>=0.2.0 <0.3.0"
},
"regjsparser": {
"version": "0.1.5",
"resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.1.5.tgz",
"from": "regjsparser@>=0.1.4 <0.2.0"
},
"reify": {
"version": "0.3.4",
"resolved": "https://registry.npmjs.org/reify/-/reify-0.3.4.tgz",
"from": "reify@>=0.3.4 <0.4.0"
},
"repeating": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/repeating/-/repeating-1.1.3.tgz",
"from": "repeating@>=1.1.0 <2.0.0"
},
"shebang-regex": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz",
"from": "shebang-regex@>=1.0.0 <2.0.0"
},
"slash": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz",
"from": "slash@>=1.0.0 <2.0.0"
},
"source-map": {
"version": "0.5.6",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz",
"from": "source-map@>=0.5.0 <0.6.0"
},
"source-map-support": {
"version": "0.2.10",
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.2.10.tgz",
"from": "source-map-support@>=0.2.10 <0.3.0",
"dependencies": {
"source-map": {
"version": "0.1.32",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.32.tgz",
"from": "source-map@0.1.32"
}
}
},
"strip-ansi": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
"from": "strip-ansi@>=3.0.0 <4.0.0"
},
"supports-color": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
"from": "supports-color@>=2.0.0 <3.0.0"
},
"to-fast-properties": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.2.tgz",
"from": "to-fast-properties@>=1.0.1 <2.0.0"
},
"user-home": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/user-home/-/user-home-1.1.1.tgz",
"from": "user-home@>=1.1.1 <2.0.0"
},
"vlq": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/vlq/-/vlq-0.2.1.tgz",
"from": "vlq@>=0.2.1 <0.3.0"
}
}
}
================================================
FILE: packages/babel-compiler/README.md
================================================
[Babel](http://babeljs.io/) is a parser and transpiler for ECMAScript 2015
syntax and beyond, which enables some upcoming JavaScript syntax features
to be used in today's browsers and runtimes.
Meteor's Babel support consists of the following core packages:
* `babel-compiler` - Exposes the [Babel API](https://babeljs.io/docs/usage/api/)
on the symbol `Babel`. For example, `Babel.compile(source, options)`.
* `babel-runtime` - Meteor versions of the external helpers used by
Babel-generated code. Meteor's core packages must run on IE 8 without
polyfills, so these helpers cannot assume the existence of
`Object.defineProperty`, `Object.freeze`, and so on.
### Babel API
The `babel-compiler` package exports the `Babel` symbol, which exposes
functionality provided by the
[`meteor-babel`](https://www.npmjs.com/package/meteor-babel) NPM package,
which is in turn implmented using the
[`babel-core`](https://www.npmjs.com/package/babel-core) NPM package.
Note that you can only use the `babel-compiler` package on the server.
Example:
```js
var babelOptions = Babel.getDefaultOptions();
// Modify the default options, if necessary:
babelOptions.whitelist = [
"es6.blockScoping", // For `let`
"es6.arrowFunctions" // For `=>`
];
var result = Babel.compile(
"let square = (x) => x*x;",
babelOptions
);
// result.code will be something like
// "var square = function (x) {\n return x * x;\n};"
```
Use `Babel.compile(source)` to transpile code using a set of default
options that work well for Meteor code.
### `.babelrc` configuration files
Like other Babel-compiled projects, a Meteor project that uses the
`ecmascript` package can specify custom Babel plugins and presets (which
are just groups of plugins) in JSON files named `.babelrc`.
For example, to enable the Babel
[transform](https://www.npmjs.com/package/babel-plugin-transform-class-properties)
that supports [class
properties](https://github.com/jeffmo/es-class-fields-and-static-properties),
you should
1. run `meteor npm install --save-dev babel-plugin-transform-class-properties`
2. put the following in a `.babelrc` file in the root of your project:
```js
{
"plugins": ["transform-class-properties"]
}
```
If you want to include all Stage 1 transforms (including the class
properties plugin), you could use a preset instead:
```sh
meteor npm install --save-dev babel-preset-stage-1
```
and then (in your `.babelrc` file):
```js
{
"presets": ["stage-1"]
}
```
Note that you should never need to include the `es2015` or `react`
transforms, as that functionality is already provided by the default
`babel-preset-meteor` preset.
Any plugins and transforms that you list in your `.babelrc` file will be
included after `babel-preset-meteor`.
To be considered by the `babel-compiler` package, `.babelrc` files must be
contained within your root application directory.
### Resources:
* [API docs](https://babeljs.io/docs/usage/api/)
* [List of transformers](https://babeljs.io/docs/usage/transformers/)
================================================
FILE: packages/babel-compiler/babel-compiler.js
================================================
/**
* A compiler that can be instantiated with features and used inside
* Plugin.registerCompiler
* @param {Object} extraFeatures The same object that getDefaultOptions takes
*/
BabelCompiler = function BabelCompiler(extraFeatures) {
this.extraFeatures = extraFeatures;
this._babelrcCache = null;
this._babelrcWarnings = Object.create(null);
};
var BCp = BabelCompiler.prototype;
var excludedFileExtensionPattern = /\.es5\.js$/i;
var hasOwn = Object.prototype.hasOwnProperty;
BCp.processFilesForTarget = function (inputFiles) {
// Reset this cache for each batch processed.
this._babelrcCache = null;
inputFiles.forEach(function (inputFile) {
var toBeAdded = this.processOneFileForTarget(inputFile);
if (toBeAdded) {
inputFile.addJavaScript(toBeAdded);
}
}, this);
};
// Returns an object suitable for passing to inputFile.addJavaScript, or
// null to indicate there was an error, and nothing should be added.
BCp.processOneFileForTarget = function (inputFile, source) {
this._babelrcCache = this._babelrcCache || Object.create(null);
if (typeof source !== "string") {
// Other compiler plugins can call processOneFileForTarget with a
// source string that's different from inputFile.getContentsAsString()
// if they've already done some processing.
source = inputFile.getContentsAsString();
}
var packageName = inputFile.getPackageName();
var inputFilePath = inputFile.getPathInPackage();
var outputFilePath = inputFilePath;
var fileOptions = inputFile.getFileOptions();
var toBeAdded = {
sourcePath: inputFilePath,
path: outputFilePath,
data: source,
hash: inputFile.getSourceHash(),
sourceMap: null,
bare: !! fileOptions.bare
};
var cacheDeps = {
sourceHash: toBeAdded.hash
};
// If you need to exclude a specific file within a package from Babel
// compilation, pass the { transpile: false } options to api.addFiles
// when you add that file.
if (fileOptions.transpile !== false &&
// Bare files should not be transpiled by Babel, because they do not
// have access to CommonJS APIs like `require`, `module`, `exports`.
! toBeAdded.bare &&
// If you need to exclude a specific file within an app from Babel
// compilation, give it the following file extension: .es5.js
! excludedFileExtensionPattern.test(inputFilePath)) {
var targetCouldBeInternetExplorer8 =
inputFile.getArch() === "web.browser";
var extraFeatures = Object.assign({}, this.extraFeatures);
if (! extraFeatures.hasOwnProperty("jscript")) {
// Perform some additional transformations to improve compatibility
// in older browsers (e.g. wrapping named function expressions, per
// http://kiro.me/blog/nfe_dilemma.html).
extraFeatures.jscript = targetCouldBeInternetExplorer8;
}
if (inputFile.isPackageFile()) {
// When compiling package files, handle import/export syntax using
// the official Babel plugin, so that package authors won't publish
// code that relies on module.import and module.export, because such
// code would fail on Meteor versions before 1.3.3. When compiling
// application files, however, it's fine to rely on module.import
// and module.export, and the developer experience will be much
// better for it: faster compilation, real variables, import
// statements inside conditional statements, etc.
//
// TODO Remove this once we are confident enough developers have
// updated to a version of Meteor that supports module.import and
// module.export.
extraFeatures.legacyModules = true;
}
var babelOptions = Babel.getDefaultOptions(extraFeatures);
this.inferExtraBabelOptions(inputFile, babelOptions, cacheDeps);
babelOptions.sourceMap = true;
babelOptions.filename =
babelOptions.sourceFileName = packageName
? "/packages/" + packageName + "/" + inputFilePath
: "/" + inputFilePath;
babelOptions.sourceMapTarget = babelOptions.filename + ".map";
try {
var result = profile('Babel.compile', function () {
return Babel.compile(source, babelOptions, cacheDeps);
});
} catch (e) {
if (e.loc) {
inputFile.error({
message: e.message,
line: e.loc.line,
column: e.loc.column,
});
return null;
}
throw e;
}
toBeAdded.data = result.code;
toBeAdded.hash = result.hash;
toBeAdded.sourceMap = result.map;
}
return toBeAdded;
};
BCp.setDiskCacheDirectory = function (cacheDir) {
Babel.setCacheDir(cacheDir);
};
function profile(name, func) {
if (typeof Profile !== 'undefined') {
return Profile.time(name, func);
} else {
return func();
}
};
BCp.inferExtraBabelOptions = function (inputFile, babelOptions, cacheDeps) {
if (! inputFile.require ||
! inputFile.findControlFile ||
! inputFile.readAndWatchFile) {
return false;
}
return (
// If a .babelrc exists, it takes precedence over package.json.
this._inferFromBabelRc(inputFile, babelOptions, cacheDeps) ||
this._inferFromPackageJson(inputFile, babelOptions, cacheDeps)
);
};
BCp._inferFromBabelRc = function (inputFile, babelOptions, cacheDeps) {
var babelrcPath = inputFile.findControlFile(".babelrc");
if (babelrcPath) {
if (! hasOwn.call(this._babelrcCache, babelrcPath)) {
this._babelrcCache[babelrcPath] =
JSON.parse(inputFile.readAndWatchFile(babelrcPath));
}
return this._inferHelper(
inputFile,
babelOptions,
babelrcPath,
this._babelrcCache[babelrcPath],
cacheDeps
);
}
};
BCp._inferFromPackageJson = function (inputFile, babelOptions, cacheDeps) {
var pkgJsonPath = inputFile.findControlFile("package.json");
if (pkgJsonPath) {
if (! hasOwn.call(this._babelrcCache, pkgJsonPath)) {
this._babelrcCache[pkgJsonPath] = JSON.parse(
inputFile.readAndWatchFile(pkgJsonPath)
).babel || null;
}
return this._inferHelper(
inputFile,
babelOptions,
pkgJsonPath,
this._babelrcCache[pkgJsonPath],
cacheDeps
);
}
};
BCp._inferHelper = function (
inputFile,
babelOptions,
controlFilePath,
babelrc,
cacheDeps
) {
if (! babelrc) {
return false;
}
var compiler = this;
var inferredPresets = [];
var result;
function infer(listName, prefix) {
var list = babelrc[listName];
if (! Array.isArray(list) || list.length === 0) {
return;
}
function req(id) {
try {
return reqMightThrow(id);
} catch (e) {
if (e.code !== "MODULE_NOT_FOUND") {
throw e;
}
if (! hasOwn.call(compiler._babelrcWarnings, id)) {
compiler._babelrcWarnings[id] = controlFilePath;
console.error(
"Warning: unable to resolve " +
JSON.stringify(id) +
" in " + listName +
" of " + controlFilePath
);
}
return null;
}
}
function reqMightThrow(id) {
var isTopLevel = "./".indexOf(id.charAt(0)) < 0;
var presetOrPlugin;
var presetOrPluginMeta;
if (isTopLevel) {
try {
// If the identifier is top-level, try to prefix it with
// "babel-plugin-" or "babel-preset-".
presetOrPlugin = inputFile.require(prefix + id);
presetOrPluginMeta = inputFile.require(
packageNameFromTopLevelModuleId(prefix + id) + '/package.json');
} catch (e) {
if (e.code !== "MODULE_NOT_FOUND") {
throw e;
}
// Fall back to requiring the plugin as-is if the prefix failed.
presetOrPlugin = inputFile.require(id);
presetOrPluginMeta = inputFile.require(
packageNameFromTopLevelModuleId(id) + '/package.json');
}
} else {
// If the identifier is not top-level, but relative or absolute,
// then it will be required as-is, so that you can implement your
// own Babel plugins locally, rather than always using plugins
// installed from npm.
presetOrPlugin = inputFile.require(id, controlFilePath);
// Note that inputFile.readAndWatchFileWithHash converts module
// identifers to OS-specific paths if necessary.
var absId = inputFile.resolve(id, controlFilePath);
var info = inputFile.readAndWatchFileWithHash(absId);
presetOrPluginMeta = {
name: absId,
version: info.hash
};
}
return {
name: presetOrPluginMeta.name,
version: presetOrPluginMeta.version,
module: presetOrPlugin.__esModule
? presetOrPlugin.default
: presetOrPlugin
};
}
var filtered = [];
list.forEach(function (item, i) {
if (typeof item === "string") {
result = req(item);
if (! result) return;
item = result.module;
cacheDeps[result.name] = result.version;
} else if (Array.isArray(item) &&
typeof item[0] === "string") {
item = item.slice(); // defensive copy
result = req(item[0]);
if (! result) return;
item[0] = result.module;
cacheDeps[result.name] = result.version;
}
// else, an object { presets: [], plugins: [] } from meteorBabel, whose
// version is used for the cache hash internally.
filtered.push(item);
});
if (listName === "plugins") {
// Turn any additional plugins into their own preset, so that they
// can come before babel-preset-meteor.
inferredPresets.push({ plugins: filtered });
} else if (listName === "presets") {
inferredPresets.push.apply(inferredPresets, filtered);
}
}
infer("presets", "babel-preset-");
infer("plugins", "babel-plugin-");
if (inferredPresets.length > 0) {
babelOptions.presets.push.apply(
babelOptions.presets,
inferredPresets
);
return true;
}
return false;
};
// 'react-hot-loader/babel' => 'react-hot-loader'
function packageNameFromTopLevelModuleId(id) {
return id.split("/", 1)[0];
}
================================================
FILE: packages/babel-compiler/babel.js
================================================
var meteorBabel = Npm.require('meteor-babel');
/**
* Returns a new object containing default options appropriate for
*/
function getDefaultOptions(extraFeatures) {
// See https://github.com/meteor/babel/blob/master/options.js for more
// information about what the default options are.
var options = meteorBabel.getDefaultOptions(extraFeatures);
// The sourceMap option should probably be removed from the default
// options returned by meteorBabel.getDefaultOptions.
delete options.sourceMap;
return options;
}
Babel = {
getDefaultOptions: getDefaultOptions,
// Deprecated, now a no-op.
validateExtraFeatures: Function.prototype,
compile: function (source, options) {
options = options || getDefaultOptions();
return meteorBabel.compile(source, options);
},
setCacheDir: function (cacheDir) {
meteorBabel.setCacheDir(cacheDir);
}
};
================================================
FILE: packages/babel-compiler/package.js
================================================
Package.describe({
name: "babel-compiler",
summary: "Parser/transpiler for ECMAScript 2015+ syntax",
// Tracks the npm version below. Use wrap numbers to increment
// without incrementing the npm version. Hmm-- Apparently this
// isn't possible because you can't publish a non-recommended
// release with package versions that don't have a pre-release
// identifier at the end (eg, -dev)
version: '6.8.3'
});
Npm.depends({
'meteor-babel': '0.11.6',
});
Package.onUse(function (api) {
api.use('ecmascript-runtime');
api.addFiles([
'babel.js',
'babel-compiler.js'
], 'server');
api.export('Babel', 'server');
api.export('BabelCompiler', 'server');
});
================================================
FILE: packages/babel-runtime/.npm/package/.gitignore
================================================
node_modules
================================================
FILE: packages/babel-runtime/.npm/package/README
================================================
This directory and the files immediately inside it are automatically generated
when you change this package's NPM dependencies. Commit the files in this
directory (npm-shrinkwrap.json, .gitignore, and this README) to source control
so that others run the same versions of sub-dependencies.
You should NOT check in the node_modules directory that Meteor automatically
creates; if you are using git, the .gitignore file tells git to ignore it.
================================================
FILE: packages/babel-runtime/.npm/package/npm-shrinkwrap.json
================================================
{
"dependencies": {
"meteor-babel-helpers": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/meteor-babel-helpers/-/meteor-babel-helpers-0.0.3.tgz",
"from": "https://registry.npmjs.org/meteor-babel-helpers/-/meteor-babel-helpers-0.0.3.tgz"
},
"regenerator-runtime": {
"version": "0.9.5",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.9.5.tgz",
"from": "regenerator-runtime@0.9.5"
}
}
}
================================================
FILE: packages/babel-runtime/README.md
================================================
# babel-runtime
Meteor maintains a version of the runtime helpers needed by Babel-transpiled code.
In most cases, the code is copied from Babel's helper implementations, though we
have also made some changes.
Benefits of maintaining our own package include:
* IE 8 support. Babel's helpers target IE 9 and do not work in IE 8, but generally
IE 8 support can be achieved with only minor changes.
* Backwards-compatibility. When the Babel compiler changes, the helpers sometimes
change. Our Babel package can keep old helpers for back-compat. (If we change
over to publishing original ES6 code in packages instead of transpiled code, this
becomes less important.)
* Client-side code size. We've made the helpers file as small as possible.
## Helpers
Helpers needed for each transform **as of [Babel v5.6.15](https://github.com/babel/babel/tree/a1a46882fddc596a47e0e29017c5440ab6d7d9c0/src/babel/transformation/transformers)**:
* es3.propertyLiterals: None
* es3.memberExpressionLiterals: None
* es6.arrowFunctions: None
* es6.templateLiterals
* `taggedTemplateLiteralLoose`
* es6.classes
* `inherits`
* `classCallCheck`
* `createClass` (only for getter/setters)
* Excluded because only for decorator support(2): `createDecoratedClass`, `defineDecoratedPropertyDescriptor`
* es6.constants: None
* es6.blockScoping: None
* Excluded because only for spec mode(1): `temporalUndefined`, `temporalAssertDefined`
* es6.properties.shorthand: None
* es6.properties.computed: None
* Excluded because only for non-loose mode(1): `defineProperty`
* es6.parameters: None
* es6.spread
* `bind` (for `new A(...b)`)
* es6.forOf: None
* es7.objectRestSpread
* `_extends`
* Everything in es6.destructuring
* es6.destructuring
* `objectWithoutProperties`
* `objectDestructuringEmpty`
* es7.trailingFunctionCommas: None
* flow: None
Footnotes:
1. A transform can be run in "loose," normal, or "spec" mode, with "loose" providing
the fastest, most lightweight, and usually most browser-compatible transpilation,
while "spec" mode tries extra hard to be spec-compliant at the expense of those
things. We've found that "loose" mode is the best mode for production code for
every transform we've looked at.
2. Decorators are still a Stage 1 proposal and are only implemented in Babel as
an experiment.
================================================
FILE: packages/babel-runtime/babel-runtime.js
================================================
var hasOwn = Object.prototype.hasOwnProperty;
var S = typeof Symbol === "function" ? Symbol : {};
var iteratorSymbol = S.iterator || "@@iterator";
meteorBabelHelpers = require("meteor-babel-helpers");
var BabelRuntime = {
// es6.templateLiterals
// Constructs the object passed to the tag function in a tagged
// template literal.
taggedTemplateLiteralLoose: function (strings, raw) {
// Babel's own version of this calls Object.freeze on `strings` and
// `strings.raw`, but it doesn't seem worth the compatibility and
// performance concerns. If you're writing code against this helper,
// don't add properties to these objects.
strings.raw = raw;
return strings;
},
// es6.classes
// Checks that a class constructor is being called with `new`, and throws
// an error if it is not.
classCallCheck: function (instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
},
// es6.classes
inherits: function (subClass, superClass) {
if (typeof superClass !== "function" && superClass !== null) {
throw new TypeError("Super expression must either be null or a function, not " + typeof superClass);
}
if (superClass) {
if (Object.create) {
// All but IE 8
subClass.prototype = Object.create(superClass.prototype, {
constructor: {
value: subClass,
enumerable: false,
writable: true,
configurable: true
}
});
} else {
// IE 8 path. Slightly worse for modern browsers, because `constructor`
// is enumerable and shows up in the inspector unnecessarily.
// It's not an "own" property of any instance though.
//
// For correctness when writing code,
// don't enumerate all the own-and-inherited properties of an instance
// of a class and expect not to find `constructor` (but who does that?).
var F = function () {
this.constructor = subClass;
};
F.prototype = superClass.prototype;
subClass.prototype = new F();
}
// For modern browsers, this would be `subClass.__proto__ = superClass`,
// but IE <=10 don't support `__proto__`, and in this case the difference
// would be detectable; code that works in modern browsers could easily
// fail on IE 8 if we ever used the `__proto__` trick.
//
// There's no perfect way to make static methods inherited if they are
// assigned after declaration of the classes. The best we can do is
// to copy them. In other words, when you write `class Foo
// extends Bar`, we copy the static methods from Bar onto Foo, but future
// ones are not copied.
//
// For correctness when writing code, don't add static methods to a class
// after you subclass it.
// The ecmascript-runtime package provides adequate polyfills for
// all of these Object.* functions (and Array#forEach), and anyone
// using babel-runtime is almost certainly using it because of the
// ecmascript package, which also implies ecmascript-runtime.
Object.getOwnPropertyNames(superClass).forEach(function (k) {
// This property descriptor dance preserves getter/setter behavior
// in browsers that support accessor properties (all except
// IE8). In IE8, the superClass can't have accessor properties
// anyway, so this code is still safe.
var descriptor = Object.getOwnPropertyDescriptor(superClass, k);
if (descriptor && typeof descriptor === "object") {
if (Object.getOwnPropertyDescriptor(subClass, k)) {
// If subClass already has a property by this name, then it
// would not be inherited, so it should not be copied. This
// notably excludes properties like .prototype and .name.
return;
}
Object.defineProperty(subClass, k, descriptor);
}
});
}
},
createClass: (function () {
var hasDefineProperty = false;
try {
// IE 8 has a broken Object.defineProperty, so feature-test by
// trying to call it.
Object.defineProperty({}, 'x', {});
hasDefineProperty = true;
} catch (e) {}
function defineProperties(target, props) {
for (var i = 0; i < props.length; i++) {
var descriptor = props[i];
descriptor.enumerable = descriptor.enumerable || false;
descriptor.configurable = true;
if ("value" in descriptor) descriptor.writable = true;
Object.defineProperty(target, descriptor.key, descriptor);
}
}
return function (Constructor, protoProps, staticProps) {
if (! hasDefineProperty) {
// e.g. `class Foo { get bar() {} }`. If you try to use getters and
// setters in IE 8, you will get a big nasty error, with or without
// Babel. I don't know of any other syntax features besides getters
// and setters that will trigger this error.
throw new Error(
"Your browser does not support this type of class property. " +
"For example, Internet Explorer 8 does not support getters and " +
"setters.");
}
if (protoProps) defineProperties(Constructor.prototype, protoProps);
if (staticProps) defineProperties(Constructor, staticProps);
return Constructor;
};
})(),
"typeof": function (obj) {
return obj && obj.constructor === Symbol ? "symbol" : typeof obj;
},
possibleConstructorReturn: function (self, call) {
if (! self) {
throw new ReferenceError(
"this hasn't been initialised - super() hasn't been called"
);
}
var callType = typeof call;
if (call &&
callType === "function" ||
callType === "object") {
return call;
}
return self;
},
interopRequireDefault: function (obj) {
return obj && obj.__esModule ? obj : { 'default': obj };
},
interopRequireWildcard: function (obj) {
if (obj && obj.__esModule) {
return obj;
}
var newObj = {};
if (obj != null) {
for (var key in obj) {
if (hasOwn.call(obj, key)) {
newObj[key] = obj[key];
}
}
}
newObj["default"] = obj;
return newObj;
},
interopExportWildcard: function (obj, defaults) {
var newObj = defaults({}, obj);
delete newObj["default"];
return newObj;
},
defaults: function (obj, defaults) {
Object.getOwnPropertyNames(defaults).forEach(function (key) {
var desc = Object.getOwnPropertyDescriptor(defaults, key);
if (desc && desc.configurable && typeof obj[key] === "undefined") {
Object.defineProperty(obj, key, desc);
}
});
return obj;
},
// es7.objectRestSpread and react (JSX)
"extends": Object.assign || (function (target) {
for (var i = 1; i < arguments.length; i++) {
var source = arguments[i];
for (var key in source) {
if (hasOwn.call(source, key)) {
target[key] = source[key];
}
}
}
return target;
}),
// es6.destructuring
objectWithoutProperties: function (obj, keys) {
var target = {};
outer: for (var i in obj) {
if (! hasOwn.call(obj, i)) continue;
for (var j = 0; j < keys.length; j++) {
if (keys[j] === i) continue outer;
}
target[i] = obj[i];
}
return target;
},
// es6.destructuring
objectDestructuringEmpty: function (obj) {
if (obj == null) throw new TypeError("Cannot destructure undefined");
},
// es6.spread
bind: Function.prototype.bind || (function () {
var isCallable = function (value) { return typeof value === 'function'; };
var $Object = Object;
var to_string = Object.prototype.toString;
var array_slice = Array.prototype.slice;
var array_concat = Array.prototype.concat;
var array_push = Array.prototype.push;
var max = Math.max;
var Empty = function Empty() {};
// Copied from es5-shim.js (3ac7942). See original for more comments.
return function bind(that) {
var target = this;
if (!isCallable(target)) {
throw new TypeError('Function.prototype.bind called on incompatible ' + target);
}
var args = array_slice.call(arguments, 1);
var bound;
var binder = function () {
if (this instanceof bound) {
var result = target.apply(
this,
array_concat.call(args, array_slice.call(arguments))
);
if ($Object(result) === result) {
return result;
}
return this;
} else {
return target.apply(
that,
array_concat.call(args, array_slice.call(arguments))
);
}
};
var boundLength = max(0, target.length - args.length);
var boundArgs = [];
for (var i = 0; i < boundLength; i++) {
array_push.call(boundArgs, '$' + i);
}
// Create a Function from source code so that it has the right `.length`.
// Probably not important for Babel. This code violates CSPs that ban
// `eval`, but the browsers that need this polyfill don't have CSP!
bound = Function('binder', 'return function (' + boundArgs.join(',') + '){ return binder.apply(this, arguments); }')(binder);
if (target.prototype) {
Empty.prototype = target.prototype;
bound.prototype = new Empty();
Empty.prototype = null;
}
return bound;
};
})(),
toConsumableArray: function (arr) {
if (Array.isArray(arr)) {
for (var i = arr.length - 1, arr2 = Array(i + 1); i >= 0; --i) {
arr2[i] = arr[i];
}
return arr2;
}
return Array.from(arr);
},
toArray: function (arr) {
return Array.isArray(arr) ? arr : Array.from(arr);
},
slicedToArray: function (iterable, limit) {
if (Array.isArray(iterable)) {
return iterable;
}
if (iterable) {
var it = iterable[iteratorSymbol]();
var result = [];
var info;
if (typeof limit !== "number") {
limit = Infinity;
}
while (result.length < limit &&
! (info = it.next()).done) {
result.push(info.value);
}
return result;
}
throw new TypeError(
"Invalid attempt to destructure non-iterable instance"
);
},
slice: Array.prototype.slice
};
// Use meteorInstall to install all of the above helper functions within
// node_modules/babel-runtime/helpers.
Object.keys(BabelRuntime).forEach(function (helperName) {
var helpers = {};
helpers[helperName + ".js"] = function (require, exports, module) {
module.exports = BabelRuntime[helperName];
};
meteorInstall({
node_modules: {
"babel-runtime": {
helpers: helpers
}
}
});
});
// Use meteorInstall to install the regenerator runtime at
// node_modules/babel-runtime/regenerator.
meteorInstall({
node_modules: {
"babel-runtime": {
"regenerator.js": function (r, e, module) {
// Note that we use the require function provided to the
// babel-runtime.js file, not the one named 'r' above.
var runtime = require("regenerator-runtime");
// If Promise.asyncApply is defined, use it to wrap calls to
// runtime.async so that the entire async function will run in its
// own Fiber, not just the code that comes after the first await.
if (typeof Promise === "function" &&
typeof Promise.asyncApply === "function") {
var realAsync = runtime.async;
runtime.async = function () {
return Promise.asyncApply(realAsync, runtime, arguments);
};
}
module.exports = runtime;
}
}
}
});
================================================
FILE: packages/babel-runtime/package.js
================================================
Package.describe({
name: "babel-runtime",
summary: "Runtime support for output of Babel transpiler",
version: '0.1.9_1',
documentation: 'README.md'
});
Npm.depends({
"regenerator-runtime": "0.9.5",
"meteor-babel-helpers": "0.0.3"
});
Package.onUse(function (api) {
// If the es5-shim package is installed, make sure it loads before
// babel-runtime, since babel-runtime uses some ES5 APIs like
// Object.defineProperties that are buggy in older browsers.
api.use("es5-shim", { weak: true });
api.use("modules");
api.use("promise"); // Needed by Regenerator.
api.addFiles("babel-runtime.js");
api.export("meteorBabelHelpers");
});
================================================
FILE: packages/base64/.gitignore
================================================
.build*
================================================
FILE: packages/base64/README.md
================================================
This is an internal Meteor package.
================================================
FILE: packages/base64/base64.js
================================================
// Base 64 encoding
var BASE_64_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
var BASE_64_VALS = {};
for (var i = 0; i < BASE_64_CHARS.length; i++) {
BASE_64_VALS[BASE_64_CHARS.charAt(i)] = i;
};
Base64 = {};
Base64.encode = function (array) {
if (typeof array === "string") {
var str = array;
array = Base64.newBinary(str.length);
for (var i = 0; i < str.length; i++) {
var ch = str.charCodeAt(i);
if (ch > 0xFF) {
throw new Error(
"Not ascii. Base64.encode can only take ascii strings.");
}
array[i] = ch;
}
}
var answer = [];
var a = null;
var b = null;
var c = null;
var d = null;
for (var i = 0; i < array.length; i++) {
switch (i % 3) {
case 0:
a = (array[i] >> 2) & 0x3F;
b = (array[i] & 0x03) << 4;
break;
case 1:
b = b | (array[i] >> 4) & 0xF;
c = (array[i] & 0xF) << 2;
break;
case 2:
c = c | (array[i] >> 6) & 0x03;
d = array[i] & 0x3F;
answer.push(getChar(a));
answer.push(getChar(b));
answer.push(getChar(c));
answer.push(getChar(d));
a = null;
b = null;
c = null;
d = null;
break;
}
}
if (a != null) {
answer.push(getChar(a));
answer.push(getChar(b));
if (c == null)
answer.push('=');
else
answer.push(getChar(c));
if (d == null)
answer.push('=');
}
return answer.join("");
};
var getChar = function (val) {
return BASE_64_CHARS.charAt(val);
};
var getVal = function (ch) {
if (ch === '=') {
return -1;
}
return BASE_64_VALS[ch];
};
// XXX This is a weird place for this to live, but it's used both by
// this package and 'ejson', and we can't put it in 'ejson' without
// introducing a circular dependency. It should probably be in its own
// package or as a helper in a package that both 'base64' and 'ejson'
// use.
Base64.newBinary = function (len) {
if (typeof Uint8Array === 'undefined' || typeof ArrayBuffer === 'undefined') {
var ret = [];
for (var i = 0; i < len; i++) {
ret.push(0);
}
ret.$Uint8ArrayPolyfill = true;
return ret;
}
return new Uint8Array(new ArrayBuffer(len));
};
Base64.decode = function (str) {
var len = Math.floor((str.length*3)/4);
if (str.charAt(str.length - 1) == '=') {
len--;
if (str.charAt(str.length - 2) == '=')
len--;
}
var arr = Base64.newBinary(len);
var one = null;
var two = null;
var three = null;
var j = 0;
for (var i = 0; i < str.length; i++) {
var c = str.charAt(i);
var v = getVal(c);
switch (i % 4) {
case 0:
if (v < 0)
throw new Error('invalid base64 string');
one = v << 2;
break;
case 1:
if (v < 0)
throw new Error('invalid base64 string');
one = one | (v >> 4);
arr[j++] = one;
two = (v & 0x0F) << 4;
break;
case 2:
if (v >= 0) {
two = two | (v >> 2);
arr[j++] = two;
three = (v & 0x03) << 6;
}
break;
case 3:
if (v >= 0) {
arr[j++] = three | v;
}
break;
}
}
return arr;
};
================================================
FILE: packages/base64/base64_test.js
================================================
var asciiToArray = function (str) {
var arr = Base64.newBinary(str.length);
for (var i = 0; i < str.length; i++) {
var c = str.charCodeAt(i);
if (c > 0xFF) {
throw new Error("Not ascii");
}
arr[i] = c;
}
return arr;
};
var arrayToAscii = function (arr) {
var res = [];
for (var i = 0; i < arr.length; i++) {
res.push(String.fromCharCode(arr[i]));
}
return res.join("");
};
Tinytest.add("base64 - testing the test", function (test) {
test.equal(arrayToAscii(asciiToArray("The quick brown fox jumps over the lazy dog")),
"The quick brown fox jumps over the lazy dog");
});
Tinytest.add("base64 - empty", function (test) {
test.equal(Base64.encode(EJSON.newBinary(0)), "");
test.equal(Base64.decode(""), EJSON.newBinary(0));
});
Tinytest.add("base64 - wikipedia examples", function (test) {
var tests = [
{txt: "pleasure.", res: "cGxlYXN1cmUu"},
{txt: "leasure.", res: "bGVhc3VyZS4="},
{txt: "easure.", res: "ZWFzdXJlLg=="},
{txt: "asure.", res: "YXN1cmUu"},
{txt: "sure.", res: "c3VyZS4="}
];
_.each(tests, function(t) {
test.equal(Base64.encode(asciiToArray(t.txt)), t.res);
test.equal(arrayToAscii(Base64.decode(t.res)), t.txt);
});
});
Tinytest.add("base64 - non-text examples", function (test) {
var tests = [
{array: [0, 0, 0], b64: "AAAA"},
{array: [0, 0, 1], b64: "AAAB"}
];
_.each(tests, function(t) {
test.equal(Base64.encode(t.array), t.b64);
var expectedAsBinary = EJSON.newBinary(t.array.length);
_.each(t.array, function (val, i) {
expectedAsBinary[i] = val;
});
test.equal(Base64.decode(t.b64), expectedAsBinary);
});
});
================================================
FILE: packages/base64/package.js
================================================
Package.describe({
summary: "Base64 encoding and decoding",
version: '1.0.9'
});
Package.onUse(function (api) {
api.export('Base64');
api.addFiles('base64.js', ['client', 'server']);
});
Package.onTest(function (api) {
api.use('base64', ['client', 'server']);
api.use(['tinytest', 'underscore', 'ejson']);
api.addFiles('base64_test.js', ['client', 'server']);
});
================================================
FILE: packages/binary-heap/.gitignore
================================================
.build*
================================================
FILE: packages/binary-heap/README.md
================================================
This is an internal Meteor package.
================================================
FILE: packages/binary-heap/binary-heap-tests.js
================================================
Tinytest.add("binary-heap - simple max-heap tests", function (test) {
var h = new MaxHeap(function (a, b) { return a-b; });
h.set("a", 1);
h.set("b", 233);
h.set("c", -122);
h.set("d", 0);
h.set("e", 0);
test.equal(h.size(), 5);
test.equal(h.maxElementId(), "b");
test.equal(h.get("b"), 233);
h.remove("b");
test.equal(h.size(), 4);
test.equal(h.maxElementId(), "a");
h.set("e", 44);
test.equal(h.maxElementId(), "e");
test.equal(h.get("b"), null);
test.isTrue(h.has("a"));
test.isFalse(h.has("dd"));
h.clear();
test.isFalse(h.has("a"));
test.equal(h.size(), 0);
test.equal(h.setDefault("a", 12345), 12345);
test.equal(h.setDefault("a", 55555), 12345);
test.equal(h.size(), 1);
test.equal(h.maxElementId(), "a");
});
Tinytest.add("binary-heap - big test for max-heap", function (test) {
var positiveNumbers = _.shuffle(_.range(1, 41));
var negativeNumbers = _.shuffle(_.range(-1, -41, -1));
var allNumbers = negativeNumbers.concat(positiveNumbers);
var heap = new MaxHeap(function (a, b) { return a-b; });
var output = [];
_.each(allNumbers, function (n) { heap.set(n, n); });
_.times(positiveNumbers.length + negativeNumbers.length, function () {
var maxId = heap.maxElementId();
output.push(heap.get(maxId));
heap.remove(maxId);
});
allNumbers.sort(function (a, b) { return b-a; });
test.equal(output, allNumbers);
});
Tinytest.add("binary-heap - min-max heap tests", function (test) {
var h = new MinMaxHeap(function (a, b) { return a-b; });
h.set("a", 1);
h.set("b", 233);
h.set("c", -122);
h.set("d", 0);
h.set("e", 0);
test.equal(h.size(), 5);
test.equal(h.maxElementId(), "b");
test.equal(h.get("b"), 233);
test.equal(h.minElementId(), "c");
h.remove("b");
test.equal(h.size(), 4);
test.equal(h.minElementId(), "c");
h.set("e", -123);
test.equal(h.minElementId(), "e");
test.equal(h.get("b"), null);
test.isTrue(h.has("a"));
test.isFalse(h.has("dd"));
h.clear();
test.isFalse(h.has("a"));
test.equal(h.size(), 0);
test.equal(h.setDefault("a", 12345), 12345);
test.equal(h.setDefault("a", 55555), 12345);
test.equal(h.size(), 1);
test.equal(h.maxElementId(), "a");
test.equal(h.minElementId(), "a");
});
Tinytest.add("binary-heap - big test for min-max-heap", function (test) {
var N = 500;
var positiveNumbers = _.shuffle(_.range(1, N + 1));
var negativeNumbers = _.shuffle(_.range(-1, -N - 1, -1));
var allNumbers = positiveNumbers.concat(negativeNumbers);
var heap = new MinMaxHeap(function (a, b) { return a-b; });
var output = [];
var initialSets = _.clone(allNumbers);
_.each(allNumbers, function (n) {
heap.set(n, n);
heap._selfCheck();
heap._minHeap._selfCheck();
});
allNumbers = _.shuffle(allNumbers);
var secondarySets = _.clone(allNumbers);
_.each(allNumbers, function (n) {
heap.set(-n, n);
heap._selfCheck();
heap._minHeap._selfCheck();
});
_.times(positiveNumbers.length + negativeNumbers.length, function () {
var minId = heap.minElementId();
output.push(heap.get(minId));
heap.remove(minId);
heap._selfCheck(); heap._minHeap._selfCheck();
});
test.equal(heap.size(), 0);
allNumbers.sort(function (a, b) { return a-b; });
var initialTestText = "initial sets: " + initialSets.toString() +
"; secondary sets: " + secondarySets.toString();
test.equal(output, allNumbers, initialTestText);
_.each(initialSets, function (n) { heap.set(n, n); })
_.each(secondarySets, function (n) { heap.set(-n, n); });
allNumbers.sort(function (a, b) { return b-a; });
output = [];
_.times(positiveNumbers.length + negativeNumbers.length, function () {
var maxId = heap.maxElementId();
output.push(heap.get(maxId));
heap.remove(maxId);
heap._selfCheck(); heap._minHeap._selfCheck();
});
test.equal(output, allNumbers, initialTestText);
});
================================================
FILE: packages/binary-heap/max-heap.js
================================================
// Constructor of Heap
// - comparator - Function - given two items returns a number
// - options:
// - initData - Array - Optional - the initial data in a format:
// Object:
// - id - String - unique id of the item
// - value - Any - the data value
// each value is retained
// - IdMap - Constructor - Optional - custom IdMap class to store id->index
// mappings internally. Standard IdMap is used by default.
MaxHeap = function (comparator, options) {
if (! _.isFunction(comparator))
throw new Error('Passed comparator is invalid, should be a comparison function');
var self = this;
// a C-style comparator that is given two values and returns a number,
// negative if the first value is less than the second, positive if the second
// value is greater than the first and zero if they are equal.
self._comparator = comparator;
options = _.defaults(options || {}, { IdMap: IdMap });
// _heapIdx maps an id to an index in the Heap array the corresponding value
// is located on.
self._heapIdx = new options.IdMap;
// The Heap data-structure implemented as a 0-based contiguous array where
// every item on index idx is a node in a complete binary tree. Every node can
// have children on indexes idx*2+1 and idx*2+2, except for the leaves. Every
// node has a parent on index (idx-1)/2;
self._heap = [];
// If the initial array is passed, we can build the heap in linear time
// complexity (O(N)) compared to linearithmic time complexity (O(nlogn)) if
// we push elements one by one.
if (_.isArray(options.initData))
self._initFromData(options.initData);
};
_.extend(MaxHeap.prototype, {
// Builds a new heap in-place in linear time based on passed data
_initFromData: function (data) {
var self = this;
self._heap = _.map(data, function (o) {
return { id: o.id, value: o.value };
});
_.each(data, function (o, i) {
self._heapIdx.set(o.id, i);
});
if (! data.length)
return;
// start from the first non-leaf - the parent of the last leaf
for (var i = parentIdx(data.length - 1); i >= 0; i--)
self._downHeap(i);
},
_downHeap: function (idx) {
var self = this;
while (leftChildIdx(idx) < self.size()) {
var left = leftChildIdx(idx);
var right = rightChildIdx(idx);
var largest = idx;
if (left < self.size()) {
largest = self._maxIndex(largest, left);
}
if (right < self.size()) {
largest = self._maxIndex(largest, right);
}
if (largest === idx)
break;
self._swap(largest, idx);
idx = largest;
}
},
_upHeap: function (idx) {
var self = this;
while (idx > 0) {
var parent = parentIdx(idx);
if (self._maxIndex(parent, idx) === idx) {
self._swap(parent, idx)
idx = parent;
} else {
break;
}
}
},
_maxIndex: function (idxA, idxB) {
var self = this;
var valueA = self._get(idxA);
var valueB = self._get(idxB);
return self._comparator(valueA, valueB) >= 0 ? idxA : idxB;
},
// Internal: gets raw data object placed on idxth place in heap
_get: function (idx) {
var self = this;
return self._heap[idx].value;
},
_swap: function (idxA, idxB) {
var self = this;
var recA = self._heap[idxA];
var recB = self._heap[idxB];
self._heapIdx.set(recA.id, idxB);
self._heapIdx.set(recB.id, idxA);
self._heap[idxA] = recB;
self._heap[idxB] = recA;
},
get: function (id) {
var self = this;
if (! self.has(id))
return null;
return self._get(self._heapIdx.get(id));
},
set: function (id, value) {
var self = this;
if (self.has(id)) {
if (self.get(id) === value)
return;
var idx = self._heapIdx.get(id);
self._heap[idx].value = value;
// Fix the new value's position
// Either bubble new value up if it is greater than its parent
self._upHeap(idx);
// or bubble it down if it is smaller than one of its children
self._downHeap(idx);
} else {
self._heapIdx.set(id, self._heap.length);
self._heap.push({ id: id, value: value });
self._upHeap(self._heap.length - 1);
}
},
remove: function (id) {
var self = this;
if (self.has(id)) {
var last = self._heap.length - 1;
var idx = self._heapIdx.get(id);
if (idx !== last) {
self._swap(idx, last);
self._heap.pop();
self._heapIdx.remove(id);
// Fix the swapped value's position
self._upHeap(idx);
self._downHeap(idx);
} else {
self._heap.pop();
self._heapIdx.remove(id);
}
}
},
has: function (id) {
var self = this;
return self._heapIdx.has(id);
},
empty: function () {
var self = this;
return !self.size();
},
clear: function () {
var self = this;
self._heap = [];
self._heapIdx.clear();
},
// iterate over values in no particular order
forEach: function (iterator) {
var self = this;
_.each(self._heap, function (obj) {
return iterator(obj.value, obj.id);
});
},
size: function () {
var self = this;
return self._heap.length;
},
setDefault: function (id, def) {
var self = this;
if (self.has(id))
return self.get(id);
self.set(id, def);
return def;
},
clone: function () {
var self = this;
var clone = new MaxHeap(self._comparator, self._heap);
return clone;
},
maxElementId: function () {
var self = this;
return self.size() ? self._heap[0].id : null;
},
_selfCheck: function () {
var self = this;
for (var i = 1; i < self._heap.length; i++)
if (self._maxIndex(parentIdx(i), i) !== parentIdx(i))
throw new Error("An item with id " + self._heap[i].id +
" has a parent younger than it: " +
self._heap[parentIdx(i)].id);
}
});
function leftChildIdx (i) { return i * 2 + 1; }
function rightChildIdx (i) { return i * 2 + 2; }
function parentIdx (i) { return (i - 1) >> 1; }
================================================
FILE: packages/binary-heap/min-heap.js
================================================
MinHeap = function (comparator, options) {
var self = this;
MaxHeap.call(self, function (a, b) {
return -comparator(a, b);
}, options);
};
Meteor._inherits(MinHeap, MaxHeap);
_.extend(MinHeap.prototype, {
maxElementId: function () {
throw new Error("Cannot call maxElementId on MinHeap");
},
minElementId: function () {
var self = this;
return MaxHeap.prototype.maxElementId.call(self);
}
});
================================================
FILE: packages/binary-heap/min-max-heap.js
================================================
// This implementation of Min/Max-Heap is just a subclass of Max-Heap
// with a Min-Heap as an encapsulated property.
//
// Most of the operations are just proxy methods to call the same method on both
// heaps.
//
// This implementation takes 2*N memory but is fairly simple to write and
// understand. And the constant factor of a simple Heap is usually smaller
// compared to other two-way priority queues like Min/Max Heaps
// (http://www.cs.otago.ac.nz/staffpriv/mike/Papers/MinMaxHeaps/MinMaxHeaps.pdf)
// and Interval Heaps
// (http://www.cise.ufl.edu/~sahni/dsaac/enrich/c13/double.htm)
MinMaxHeap = function (comparator, options) {
var self = this;
MaxHeap.call(self, comparator, options);
self._minHeap = new MinHeap(comparator, options);
};
Meteor._inherits(MinMaxHeap, MaxHeap);
_.extend(MinMaxHeap.prototype, {
set: function (id, value) {
var self = this;
MaxHeap.prototype.set.apply(self, arguments);
self._minHeap.set(id, value);
},
remove: function (id) {
var self = this;
MaxHeap.prototype.remove.apply(self, arguments);
self._minHeap.remove(id);
},
clear: function () {
var self = this;
MaxHeap.prototype.clear.apply(self, arguments);
self._minHeap.clear();
},
setDefault: function (id, def) {
var self = this;
MaxHeap.prototype.setDefault.apply(self, arguments);
return self._minHeap.setDefault(id, def);
},
clone: function () {
var self = this;
var clone = new MinMaxHeap(self._comparator, self._heap);
return clone;
},
minElementId: function () {
var self = this;
return self._minHeap.minElementId();
}
});
================================================
FILE: packages/binary-heap/package.js
================================================
Package.describe({
summary: "Binary Heap datastructure implementation",
version: '1.0.9'
});
Package.onUse(function (api) {
api.export('MaxHeap');
api.export('MinHeap');
api.export('MinMaxHeap');
api.use(['underscore', 'id-map']);
api.addFiles(['max-heap.js', 'min-heap.js', 'min-max-heap.js']);
});
Package.onTest(function (api) {
api.use([
'tinytest',
'underscore',
'binary-heap'
]);
api.addFiles('binary-heap-tests.js');
});
================================================
FILE: packages/blaze/.gitignore
================================================
.build*
================================================
FILE: packages/blaze/README.md
================================================
# Blaze
Blaze is a powerful library for creating user interfaces by writing
reactive HTML templates. Compared to using a combination of
traditional templates and jQuery, Blaze eliminates the need for all
the "update logic" in your app that listens for data changes and
manipulates the DOM. Instead, familiar template directives like
`{{#if}}` and `{{#each}}` integrate with
[Tracker's](https://meteor.com/tracker) "transparent reactivity" and
[Minimongo's](https://meteor.com/mini-databases) database cursors so
that the DOM updates automatically.
Read more on the Blaze [project page](http://www.meteor.com/blaze).
## Details
Blaze has two major parts:
* A template compiler that compiles template files into JavaScript
code that runs against the Blaze runtime library. Moreover, Blaze
provides a compiler toolchain (think LLVM) that can be used to
support arbitrary template syntaxes. The flagship template syntax
is Spacebars, a variant of Handlebars, but a community alternative
based on Jade is already in use by many apps.
* A reactive DOM engine that builds and manages the DOM at runtime,
invoked via templates or directly from the app, which features
reactively updating regions, lists, and attributes; event
delegation; and many callbacks and hooks to aid the app developer.
Blaze is sometimes compared to frameworks like React, Angular, Ember,
Polymer, Knockout, and others by virtue of its advanced templating
system. What sets Blaze apart is a relentless focus on the developer
experience, using templating, transparent reactivity, and
interoperability with existing libraries to create a gentle learning
curve while enabling you to build world-class apps.
## Examples
Here are two Spacebars templates from an example app called
"Leaderboard" which displays a sorted list of top players and their
scores:
```html
{{#each players}}
{{> player}}
{{/each}}
Three
Three
',
'
',
'HTML.BR({a: [[""]]})');
run(BR({
x: function () { return Blaze.View(function () { return Blaze.View(function () { return []; }); }); },
a: function () { return Blaze.View(function () { return Blaze.View(function () { return ''; }); }); }}),
'
',
'
');
});
// test that we correctly update the 'value' property on input fields
// rather than the 'value' attribute. the 'value' attribute only sets
// the initial value.
Tinytest.add("blaze - render - input - value", function (test) {
var R = ReactiveVar("hello");
var div = document.createElement("DIV");
materialize(INPUT({value: function () { return R.get(); }}), div);
var inputEl = div.querySelector('input');
test.equal(inputEl.value, "hello");
inputEl.value = "goodbye";
R.set("hola");
Tracker.flush();
test.equal(inputEl.value, "hola");
});
// test that we correctly update the 'checked' property rather than
// the 'checked' attribute on input fields of type 'checkbox'. the
// 'checked' attribute only sets the initial value.
Tinytest.add("blaze - render - input - checked", function (test) {
var R = ReactiveVar(null);
var div = document.createElement("DIV");
materialize(INPUT({type: "checkbox", checked: function () { return R.get(); }}), div);
var inputEl = div.querySelector('input');
test.equal(inputEl.checked, false);
inputEl.checked = true;
R.set("checked");
Tracker.flush();
R.set(null);
Tracker.flush();
test.equal(inputEl.checked, false);
});
Tinytest.add("blaze - render - textarea", function (test) {
var run = function (optNode, text, html, code) {
if (typeof optNode === 'string') {
// called with args (text, html, code)
code = html;
html = text;
text = optNode;
optNode = null;
}
var div = document.createElement("DIV");
var node = TEXTAREA({value: optNode || text});
materialize(node, div);
var value = div.querySelector('textarea').value;
value = value.replace(/\r\n/g, "\n"); // IE8 substitutes \n with \r\n
test.equal(value, text);
test.equal(toHTML(node), html);
if (typeof code === 'string')
test.equal(toCode(node), code);
};
run('Hello',
'',
'HTML.TEXTAREA({value: "Hello"})');
run('\nHello',
'',
'HTML.TEXTAREA({value: "\\nHello"})');
run('',
'',
'HTML.TEXTAREA({value: ""})');
run(CharRef({html: '&', str: '&'}),
'&',
'',
'HTML.TEXTAREA({value: HTML.CharRef({html: "&", str: "&"})})');
run(function () {
return ['a', Blaze.View(function () { return 'b'; }), 'c'];
},
'abc',
'');
// test that reactivity of textarea "value" attribute works...
(function () {
var R = ReactiveVar('one');
var div = document.createElement("DIV");
var node = TEXTAREA({value: function () {
return Blaze.View(function () {
return R.get();
});
}});
materialize(node, div);
var textarea = div.querySelector('textarea');
test.equal(textarea.value, 'one');
R.set('two');
Tracker.flush();
test.equal(textarea.value, 'two');
})();
// ... while "content" reactivity simply doesn't update
// (but doesn't throw either)
(function () {
var R = ReactiveVar('one');
var div = document.createElement("DIV");
var node = TEXTAREA([Blaze.View(function () {
return R.get();
})]);
materialize(node, div);
var textarea = div.querySelector('textarea');
test.equal(textarea.value, 'one');
R.set('two');
Tracker.flush({_throwFirstError: true});
test.equal(textarea.value, 'one');
})();
});
Tinytest.add("blaze - render - view isolation", function (test) {
// Reactively change a text node
(function () {
var R = ReactiveVar('Hello');
var test1 = function () {
return P(Blaze.View(function () { return R.get(); }));
};
test.equal(toHTML(test1()), '
');
buf.length = 0;
$(div).remove();
buf.sort();
test.equal(buf, ['destroyed 1', 'destroyed 2', 'destroyed 3']);
// Now use toHTML. Should still get most of the callbacks (not `rendered`).
buf.length = 0;
counter = 1;
var html = Blaze.toHTML(makeView());
test.equal(buf, ['created 1',
'parent of 2 is 1',
'created 2',
'parent of 3 is 2',
'created 3',
'destroyed 3',
'destroyed 2',
'destroyed 1']);
test.equal(html, '123
');
})();
});
Tinytest.add("blaze - render - findAll", function (test) {
var found = null;
var $found = null;
var myTemplate = new Template(
'findAllTest',
function() {
return DIV([P('first'), P('second')]);
});
myTemplate.rendered = function() {
found = this.findAll('p');
$found = this.$('p');
};
var div = document.createElement("DIV");
Blaze.render(myTemplate, div);
Tracker.flush();
test.equal(_.isArray(found), true);
test.equal(_.isArray($found), false);
test.equal(found.length, 2);
test.equal($found.length, 2);
});
Tinytest.add("blaze - render - reactive attributes 2", function (test) {
var R1 = ReactiveVar(['foo']);
var R2 = ReactiveVar(['bar']);
var spanFunc = function () {
return SPAN(HTML.Attrs(
{ blah: function () { return R1.get(); } },
function () { return { blah: R2.get() }; }));
};
var div = document.createElement("DIV");
Blaze.render(spanFunc, div);
var check = function (expected) {
test.equal(Blaze.toHTML(spanFunc()), expected);
test.equal(canonicalizeHtml(div.innerHTML), expected);
};
check('');
test.equal(R1._numListeners(), 1);
test.equal(R2._numListeners(), 1);
R2.set([[]]);
Tracker.flush();
// We combine `['foo']` with what evaluates to `[[[]]]`, which is nully.
check('');
R2.set([['']]);
Tracker.flush();
// We combine `['foo']` with what evaluates to `[[['']]]`, which is non-nully.
check('');
R2.set(null);
Tracker.flush();
// We combine `['foo']` with `[null]`, which is nully.
check('');
R1.set([[], []]);
Tracker.flush();
// We combine two nully values.
check('');
R1.set([[], ['foo']]);
Tracker.flush();
check('');
// clean up
$(div).remove();
test.equal(R1._numListeners(), 0);
test.equal(R2._numListeners(), 0);
});
Tinytest.add("blaze - render - SVG", function (test) {
if (! document.createElementNS) {
// IE 8
return;
}
var fillColor = ReactiveVar('red');
var classes = ReactiveVar('one two');
var content = DIV({'class': 'container'}, HTML.SVG(
{width: 100, height: 100},
HTML.CIRCLE({cx: 50, cy: 50, r: 40,
stroke: 'black', 'stroke-width': 3,
'class': function () { return classes.get(); },
fill: function () { return fillColor.get(); }})));
var div = document.createElement("DIV");
materialize(content, div);
var circle = div.querySelector('.container > svg > circle');
test.equal(circle.getAttribute('fill'), 'red');
test.equal(circle.className.baseVal, 'one two');
fillColor.set('green');
classes.set('two three');
Tracker.flush();
test.equal(circle.getAttribute('fill'), 'green');
test.equal(circle.className.baseVal, 'two three');
test.equal(circle.nodeName, 'circle');
test.equal(circle.namespaceURI, "http://www.w3.org/2000/svg");
test.equal(circle.parentNode.namespaceURI, "http://www.w3.org/2000/svg");
});
Tinytest.add("ui - attributes", function (test) {
var SPAN = HTML.SPAN;
var amp = HTML.CharRef({html: '&', str: '&'});
test.equal(HTML.toHTML(SPAN({title: ['M', amp, 'Ms']}, 'M', amp, 'M candies')),
'M&M candies');
});
================================================
FILE: packages/blaze/template.js
================================================
// [new] Blaze.Template([viewName], renderFunction)
//
// `Blaze.Template` is the class of templates, like `Template.foo` in
// Meteor, which is `instanceof Template`.
//
// `viewKind` is a string that looks like "Template.foo" for templates
// defined by the compiler.
/**
* @class
* @summary Constructor for a Template, which is used to construct Views with particular name and content.
* @locus Client
* @param {String} [viewName] Optional. A name for Views constructed by this Template. See [`view.name`](#view_name).
* @param {Function} renderFunction A function that returns [*renderable content*](#renderable_content). This function is used as the `renderFunction` for Views constructed by this Template.
*/
Blaze.Template = function (viewName, renderFunction) {
if (! (this instanceof Blaze.Template))
// called without `new`
return new Blaze.Template(viewName, renderFunction);
if (typeof viewName === 'function') {
// omitted "viewName" argument
renderFunction = viewName;
viewName = '';
}
if (typeof viewName !== 'string')
throw new Error("viewName must be a String (or omitted)");
if (typeof renderFunction !== 'function')
throw new Error("renderFunction must be a function");
this.viewName = viewName;
this.renderFunction = renderFunction;
this.__helpers = new HelperMap;
this.__eventMaps = [];
this._callbacks = {
created: [],
rendered: [],
destroyed: []
};
};
var Template = Blaze.Template;
var HelperMap = function () {};
HelperMap.prototype.get = function (name) {
return this[' '+name];
};
HelperMap.prototype.set = function (name, helper) {
this[' '+name] = helper;
};
HelperMap.prototype.has = function (name) {
return (' '+name) in this;
};
/**
* @summary Returns true if `value` is a template object like `Template.myTemplate`.
* @locus Client
* @param {Any} value The value to test.
*/
Blaze.isTemplate = function (t) {
return (t instanceof Blaze.Template);
};
/**
* @name onCreated
* @instance
* @memberOf Template
* @summary Register a function to be called when an instance of this template is created.
* @param {Function} callback A function to be added as a callback.
* @locus Client
* @importFromPackage templating
*/
Template.prototype.onCreated = function (cb) {
this._callbacks.created.push(cb);
};
/**
* @name onRendered
* @instance
* @memberOf Template
* @summary Register a function to be called when an instance of this template is inserted into the DOM.
* @param {Function} callback A function to be added as a callback.
* @locus Client
* @importFromPackage templating
*/
Template.prototype.onRendered = function (cb) {
this._callbacks.rendered.push(cb);
};
/**
* @name onDestroyed
* @instance
* @memberOf Template
* @summary Register a function to be called when an instance of this template is removed from the DOM and destroyed.
* @param {Function} callback A function to be added as a callback.
* @locus Client
* @importFromPackage templating
*/
Template.prototype.onDestroyed = function (cb) {
this._callbacks.destroyed.push(cb);
};
Template.prototype._getCallbacks = function (which) {
var self = this;
var callbacks = self[which] ? [self[which]] : [];
// Fire all callbacks added with the new API (Template.onRendered())
// as well as the old-style callback (e.g. Template.rendered) for
// backwards-compatibility.
callbacks = callbacks.concat(self._callbacks[which]);
return callbacks;
};
var fireCallbacks = function (callbacks, template) {
Template._withTemplateInstanceFunc(
function () { return template; },
function () {
for (var i = 0, N = callbacks.length; i < N; i++) {
callbacks[i].call(template);
}
});
};
Template.prototype.constructView = function (contentFunc, elseFunc) {
var self = this;
var view = Blaze.View(self.viewName, self.renderFunction);
view.template = self;
view.templateContentBlock = (
contentFunc ? new Template('(contentBlock)', contentFunc) : null);
view.templateElseBlock = (
elseFunc ? new Template('(elseBlock)', elseFunc) : null);
if (self.__eventMaps || typeof self.events === 'object') {
view._onViewRendered(function () {
if (view.renderCount !== 1)
return;
if (! self.__eventMaps.length && typeof self.events === "object") {
// Provide limited back-compat support for `.events = {...}`
// syntax. Pass `template.events` to the original `.events(...)`
// function. This code must run only once per template, in
// order to not bind the handlers more than once, which is
// ensured by the fact that we only do this when `__eventMaps`
// is falsy, and we cause it to be set now.
Template.prototype.events.call(self, self.events);
}
_.each(self.__eventMaps, function (m) {
Blaze._addEventMap(view, m, view);
});
});
}
view._templateInstance = new Blaze.TemplateInstance(view);
view.templateInstance = function () {
// Update data, firstNode, and lastNode, and return the TemplateInstance
// object.
var inst = view._templateInstance;
/**
* @instance
* @memberOf Blaze.TemplateInstance
* @name data
* @summary The data context of this instance's latest invocation.
* @locus Client
*/
inst.data = Blaze.getData(view);
if (view._domrange && !view.isDestroyed) {
inst.firstNode = view._domrange.firstNode();
inst.lastNode = view._domrange.lastNode();
} else {
// on 'created' or 'destroyed' callbacks we don't have a DomRange
inst.firstNode = null;
inst.lastNode = null;
}
return inst;
};
/**
* @name created
* @instance
* @memberOf Template
* @summary Provide a callback when an instance of a template is created.
* @locus Client
* @deprecated in 1.1
*/
// To avoid situations when new callbacks are added in between view
// instantiation and event being fired, decide on all callbacks to fire
// immediately and then fire them on the event.
var createdCallbacks = self._getCallbacks('created');
view.onViewCreated(function () {
fireCallbacks(createdCallbacks, view.templateInstance());
});
/**
* @name rendered
* @instance
* @memberOf Template
* @summary Provide a callback when an instance of a template is rendered.
* @locus Client
* @deprecated in 1.1
*/
var renderedCallbacks = self._getCallbacks('rendered');
view.onViewReady(function () {
fireCallbacks(renderedCallbacks, view.templateInstance());
});
/**
* @name destroyed
* @instance
* @memberOf Template
* @summary Provide a callback when an instance of a template is destroyed.
* @locus Client
* @deprecated in 1.1
*/
var destroyedCallbacks = self._getCallbacks('destroyed');
view.onViewDestroyed(function () {
fireCallbacks(destroyedCallbacks, view.templateInstance());
});
return view;
};
/**
* @class
* @summary The class for template instances
* @param {Blaze.View} view
* @instanceName template
*/
Blaze.TemplateInstance = function (view) {
if (! (this instanceof Blaze.TemplateInstance))
// called without `new`
return new Blaze.TemplateInstance(view);
if (! (view instanceof Blaze.View))
throw new Error("View required");
view._templateInstance = this;
/**
* @name view
* @memberOf Blaze.TemplateInstance
* @instance
* @summary The [View](#blaze_view) object for this invocation of the template.
* @locus Client
* @type {Blaze.View}
*/
this.view = view;
this.data = null;
/**
* @name firstNode
* @memberOf Blaze.TemplateInstance
* @instance
* @summary The first top-level DOM node in this template instance.
* @locus Client
* @type {DOMNode}
*/
this.firstNode = null;
/**
* @name lastNode
* @memberOf Blaze.TemplateInstance
* @instance
* @summary The last top-level DOM node in this template instance.
* @locus Client
* @type {DOMNode}
*/
this.lastNode = null;
// This dependency is used to identify state transitions in
// _subscriptionHandles which could cause the result of
// TemplateInstance#subscriptionsReady to change. Basically this is triggered
// whenever a new subscription handle is added or when a subscription handle
// is removed and they are not ready.
this._allSubsReadyDep = new Tracker.Dependency();
this._allSubsReady = false;
this._subscriptionHandles = {};
};
/**
* @summary Find all elements matching `selector` in this template instance, and return them as a JQuery object.
* @locus Client
* @param {String} selector The CSS selector to match, scoped to the template contents.
* @returns {DOMNode[]}
*/
Blaze.TemplateInstance.prototype.$ = function (selector) {
var view = this.view;
if (! view._domrange)
throw new Error("Can't use $ on template instance with no DOM");
return view._domrange.$(selector);
};
/**
* @summary Find all elements matching `selector` in this template instance.
* @locus Client
* @param {String} selector The CSS selector to match, scoped to the template contents.
* @returns {DOMElement[]}
*/
Blaze.TemplateInstance.prototype.findAll = function (selector) {
return Array.prototype.slice.call(this.$(selector));
};
/**
* @summary Find one element matching `selector` in this template instance.
* @locus Client
* @param {String} selector The CSS selector to match, scoped to the template contents.
* @returns {DOMElement}
*/
Blaze.TemplateInstance.prototype.find = function (selector) {
var result = this.$(selector);
return result[0] || null;
};
/**
* @summary A version of [Tracker.autorun](#tracker_autorun) that is stopped when the template is destroyed.
* @locus Client
* @param {Function} runFunc The function to run. It receives one argument: a Tracker.Computation object.
*/
Blaze.TemplateInstance.prototype.autorun = function (f) {
return this.view.autorun(f);
};
/**
* @summary A version of [Meteor.subscribe](#meteor_subscribe) that is stopped
* when the template is destroyed.
* @return {SubscriptionHandle} The subscription handle to the newly made
* subscription. Call `handle.stop()` to manually stop the subscription, or
* `handle.ready()` to find out if this particular subscription has loaded all
* of its inital data.
* @locus Client
* @param {String} name Name of the subscription. Matches the name of the
* server's `publish()` call.
* @param {Any} [arg1,arg2...] Optional arguments passed to publisher function
* on server.
* @param {Function|Object} [options] If a function is passed instead of an
* object, it is interpreted as an `onReady` callback.
* @param {Function} [options.onReady] Passed to [`Meteor.subscribe`](#meteor_subscribe).
* @param {Function} [options.onStop] Passed to [`Meteor.subscribe`](#meteor_subscribe).
* @param {DDP.Connection} [options.connection] The connection on which to make the
* subscription.
*/
Blaze.TemplateInstance.prototype.subscribe = function (/* arguments */) {
var self = this;
var subHandles = self._subscriptionHandles;
var args = _.toArray(arguments);
// Duplicate logic from Meteor.subscribe
var options = {};
if (args.length) {
var lastParam = _.last(args);
// Match pattern to check if the last arg is an options argument
var lastParamOptionsPattern = {
onReady: Match.Optional(Function),
// XXX COMPAT WITH 1.0.3.1 onError used to exist, but now we use
// onStop with an error callback instead.
onError: Match.Optional(Function),
onStop: Match.Optional(Function),
connection: Match.Optional(Match.Any)
};
if (_.isFunction(lastParam)) {
options.onReady = args.pop();
} else if (lastParam && ! _.isEmpty(lastParam) && Match.test(lastParam, lastParamOptionsPattern)) {
options = args.pop();
}
}
var subHandle;
var oldStopped = options.onStop;
options.onStop = function (error) {
// When the subscription is stopped, remove it from the set of tracked
// subscriptions to avoid this list growing without bound
delete subHandles[subHandle.subscriptionId];
// Removing a subscription can only change the result of subscriptionsReady
// if we are not ready (that subscription could be the one blocking us being
// ready).
if (! self._allSubsReady) {
self._allSubsReadyDep.changed();
}
if (oldStopped) {
oldStopped(error);
}
};
var connection = options.connection;
var callbacks = _.pick(options, ["onReady", "onError", "onStop"]);
// The callbacks are passed as the last item in the arguments array passed to
// View#subscribe
args.push(callbacks);
// View#subscribe takes the connection as one of the options in the last
// argument
subHandle = self.view.subscribe.call(self.view, args, {
connection: connection
});
if (! _.has(subHandles, subHandle.subscriptionId)) {
subHandles[subHandle.subscriptionId] = subHandle;
// Adding a new subscription will always cause us to transition from ready
// to not ready, but if we are already not ready then this can't make us
// ready.
if (self._allSubsReady) {
self._allSubsReadyDep.changed();
}
}
return subHandle;
};
/**
* @summary A reactive function that returns true when all of the subscriptions
* called with [this.subscribe](#TemplateInstance-subscribe) are ready.
* @return {Boolean} True if all subscriptions on this template instance are
* ready.
*/
Blaze.TemplateInstance.prototype.subscriptionsReady = function () {
this._allSubsReadyDep.depend();
this._allSubsReady = _.all(this._subscriptionHandles, function (handle) {
return handle.ready();
});
return this._allSubsReady;
};
/**
* @summary Specify template helpers available to this template.
* @locus Client
* @param {Object} helpers Dictionary of helper functions by name.
* @importFromPackage templating
*/
Template.prototype.helpers = function (dict) {
if (! _.isObject(dict)) {
throw new Error("Helpers dictionary has to be an object");
}
for (var k in dict)
this.__helpers.set(k, dict[k]);
};
// Kind of like Blaze.currentView but for the template instance.
// This is a function, not a value -- so that not all helpers
// are implicitly dependent on the current template instance's `data` property,
// which would make them dependenct on the data context of the template
// inclusion.
Template._currentTemplateInstanceFunc = null;
Template._withTemplateInstanceFunc = function (templateInstanceFunc, func) {
if (typeof func !== 'function')
throw new Error("Expected function, got: " + func);
var oldTmplInstanceFunc = Template._currentTemplateInstanceFunc;
try {
Template._currentTemplateInstanceFunc = templateInstanceFunc;
return func();
} finally {
Template._currentTemplateInstanceFunc = oldTmplInstanceFunc;
}
};
/**
* @summary Specify event handlers for this template.
* @locus Client
* @param {EventMap} eventMap Event handlers to associate with this template.
* @importFromPackage templating
*/
Template.prototype.events = function (eventMap) {
if (! _.isObject(eventMap)) {
throw new Error("Event map has to be an object");
}
var template = this;
var eventMap2 = {};
for (var k in eventMap) {
eventMap2[k] = (function (k, v) {
return function (event/*, ...*/) {
var view = this; // passed by EventAugmenter
var data = Blaze.getData(event.currentTarget);
if (data == null)
data = {};
var args = Array.prototype.slice.call(arguments);
var tmplInstanceFunc = _.bind(view.templateInstance, view);
args.splice(1, 0, tmplInstanceFunc());
return Template._withTemplateInstanceFunc(tmplInstanceFunc, function () {
return v.apply(data, args);
});
};
})(k, eventMap[k]);
}
template.__eventMaps.push(eventMap2);
};
/**
* @function
* @name instance
* @memberOf Template
* @summary The [template instance](#template_inst) corresponding to the current template helper, event handler, callback, or autorun. If there isn't one, `null`.
* @locus Client
* @returns {Blaze.TemplateInstance}
* @importFromPackage templating
*/
Template.instance = function () {
return Template._currentTemplateInstanceFunc
&& Template._currentTemplateInstanceFunc();
};
// Note: Template.currentData() is documented to take zero arguments,
// while Blaze.getData takes up to one.
/**
* @summary
*
* - Inside an `onCreated`, `onRendered`, or `onDestroyed` callback, returns
* the data context of the template.
* - Inside an event handler, returns the data context of the template on which
* this event handler was defined.
* - Inside a helper, returns the data context of the DOM node where the helper
* was used.
*
* Establishes a reactive dependency on the result.
* @locus Client
* @function
* @importFromPackage templating
*/
Template.currentData = Blaze.getData;
/**
* @summary Accesses other data contexts that enclose the current data context.
* @locus Client
* @function
* @param {Integer} [numLevels] The number of levels beyond the current data context to look. Defaults to 1.
* @importFromPackage templating
*/
Template.parentData = Blaze._parentData;
/**
* @summary Defines a [helper function](#template_helpers) which can be used from all templates.
* @locus Client
* @function
* @param {String} name The name of the helper function you are defining.
* @param {Function} function The helper function itself.
* @importFromPackage templating
*/
Template.registerHelper = Blaze.registerHelper;
/**
* @summary Removes a global [helper function](#template_helpers).
* @locus Client
* @function
* @param {String} name The name of the helper function you are defining.
* @importFromPackage templating
*/
Template.deregisterHelper = Blaze.deregisterHelper;
================================================
FILE: packages/blaze/view.js
================================================
/// [new] Blaze.View([name], renderMethod)
///
/// Blaze.View is the building block of reactive DOM. Views have
/// the following features:
///
/// * lifecycle callbacks - Views are created, rendered, and destroyed,
/// and callbacks can be registered to fire when these things happen.
///
/// * parent pointer - A View points to its parentView, which is the
/// View that caused it to be rendered. These pointers form a
/// hierarchy or tree of Views.
///
/// * render() method - A View's render() method specifies the DOM
/// (or HTML) content of the View. If the method establishes
/// reactive dependencies, it may be re-run.
///
/// * a DOMRange - If a View is rendered to DOM, its position and
/// extent in the DOM are tracked using a DOMRange object.
///
/// When a View is constructed by calling Blaze.View, the View is
/// not yet considered "created." It doesn't have a parentView yet,
/// and no logic has been run to initialize the View. All real
/// work is deferred until at least creation time, when the onViewCreated
/// callbacks are fired, which happens when the View is "used" in
/// some way that requires it to be rendered.
///
/// ...more lifecycle stuff
///
/// `name` is an optional string tag identifying the View. The only
/// time it's used is when looking in the View tree for a View of a
/// particular name; for example, data contexts are stored on Views
/// of name "with". Names are also useful when debugging, so in
/// general it's good for functions that create Views to set the name.
/// Views associated with templates have names of the form "Template.foo".
/**
* @class
* @summary Constructor for a View, which represents a reactive region of DOM.
* @locus Client
* @param {String} [name] Optional. A name for this type of View. See [`view.name`](#view_name).
* @param {Function} renderFunction A function that returns [*renderable content*](#renderable_content). In this function, `this` is bound to the View.
*/
Blaze.View = function (name, render) {
if (! (this instanceof Blaze.View))
// called without `new`
return new Blaze.View(name, render);
if (typeof name === 'function') {
// omitted "name" argument
render = name;
name = '';
}
this.name = name;
this._render = render;
this._callbacks = {
created: null,
rendered: null,
destroyed: null
};
// Setting all properties here is good for readability,
// and also may help Chrome optimize the code by keeping
// the View object from changing shape too much.
this.isCreated = false;
this._isCreatedForExpansion = false;
this.isRendered = false;
this._isAttached = false;
this.isDestroyed = false;
this._isInRender = false;
this.parentView = null;
this._domrange = null;
// This flag is normally set to false except for the cases when view's parent
// was generated as part of expanding some syntactic sugar expressions or
// methods.
// Ex.: Blaze.renderWithData is an equivalent to creating a view with regular
// Blaze.render and wrapping it into {{#with data}}{{/with}} view. Since the
// users don't know anything about these generated parent views, Blaze needs
// this information to be available on views to make smarter decisions. For
// example: removing the generated parent view with the view on Blaze.remove.
this._hasGeneratedParent = false;
// Bindings accessible to children views (via view.lookup('name')) within the
// closest template view.
this._scopeBindings = {};
this.renderCount = 0;
};
Blaze.View.prototype._render = function () { return null; };
Blaze.View.prototype.onViewCreated = function (cb) {
this._callbacks.created = this._callbacks.created || [];
this._callbacks.created.push(cb);
};
Blaze.View.prototype._onViewRendered = function (cb) {
this._callbacks.rendered = this._callbacks.rendered || [];
this._callbacks.rendered.push(cb);
};
Blaze.View.prototype.onViewReady = function (cb) {
var self = this;
var fire = function () {
Tracker.afterFlush(function () {
if (! self.isDestroyed) {
Blaze._withCurrentView(self, function () {
cb.call(self);
});
}
});
};
self._onViewRendered(function onViewRendered() {
if (self.isDestroyed)
return;
if (! self._domrange.attached)
self._domrange.onAttached(fire);
else
fire();
});
};
Blaze.View.prototype.onViewDestroyed = function (cb) {
this._callbacks.destroyed = this._callbacks.destroyed || [];
this._callbacks.destroyed.push(cb);
};
Blaze.View.prototype.removeViewDestroyedListener = function (cb) {
var destroyed = this._callbacks.destroyed;
if (! destroyed)
return;
var index = _.lastIndexOf(destroyed, cb);
if (index !== -1) {
// XXX You'd think the right thing to do would be splice, but _fireCallbacks
// gets sad if you remove callbacks while iterating over the list. Should
// change this to use callback-hook or EventEmitter or something else that
// properly supports removal.
destroyed[index] = null;
}
};
/// View#autorun(func)
///
/// Sets up a Tracker autorun that is "scoped" to this View in two
/// important ways: 1) Blaze.currentView is automatically set
/// on every re-run, and 2) the autorun is stopped when the
/// View is destroyed. As with Tracker.autorun, the first run of
/// the function is immediate, and a Computation object that can
/// be used to stop the autorun is returned.
///
/// View#autorun is meant to be called from View callbacks like
/// onViewCreated, or from outside the rendering process. It may not
/// be called before the onViewCreated callbacks are fired (too early),
/// or from a render() method (too confusing).
///
/// Typically, autoruns that update the state
/// of the View (as in Blaze.With) should be started from an onViewCreated
/// callback. Autoruns that update the DOM should be started
/// from either onViewCreated (guarded against the absence of
/// view._domrange), or onViewReady.
Blaze.View.prototype.autorun = function (f, _inViewScope, displayName) {
var self = this;
// The restrictions on when View#autorun can be called are in order
// to avoid bad patterns, like creating a Blaze.View and immediately
// calling autorun on it. A freshly created View is not ready to
// have logic run on it; it doesn't have a parentView, for example.
// It's when the View is materialized or expanded that the onViewCreated
// handlers are fired and the View starts up.
//
// Letting the render() method call `this.autorun()` is problematic
// because of re-render. The best we can do is to stop the old
// autorun and start a new one for each render, but that's a pattern
// we try to avoid internally because it leads to helpers being
// called extra times, in the case where the autorun causes the
// view to re-render (and thus the autorun to be torn down and a
// new one established).
//
// We could lift these restrictions in various ways. One interesting
// idea is to allow you to call `view.autorun` after instantiating
// `view`, and automatically wrap it in `view.onViewCreated`, deferring
// the autorun so that it starts at an appropriate time. However,
// then we can't return the Computation object to the caller, because
// it doesn't exist yet.
if (! self.isCreated) {
throw new Error("View#autorun must be called from the created callback at the earliest");
}
if (this._isInRender) {
throw new Error("Can't call View#autorun from inside render(); try calling it from the created or rendered callback");
}
if (Tracker.active) {
throw new Error("Can't call View#autorun from a Tracker Computation; try calling it from the created or rendered callback");
}
var templateInstanceFunc = Blaze.Template._currentTemplateInstanceFunc;
var func = function viewAutorun(c) {
return Blaze._withCurrentView(_inViewScope || self, function () {
return Blaze.Template._withTemplateInstanceFunc(
templateInstanceFunc, function () {
return f.call(self, c);
});
});
};
// Give the autorun function a better name for debugging and profiling.
// The `displayName` property is not part of the spec but browsers like Chrome
// and Firefox prefer it in debuggers over the name function was declared by.
func.displayName =
(self.name || 'anonymous') + ':' + (displayName || 'anonymous');
var comp = Tracker.autorun(func);
var stopComputation = function () { comp.stop(); };
self.onViewDestroyed(stopComputation);
comp.onStop(function () {
self.removeViewDestroyedListener(stopComputation);
});
return comp;
};
Blaze.View.prototype._errorIfShouldntCallSubscribe = function () {
var self = this;
if (! self.isCreated) {
throw new Error("View#subscribe must be called from the created callback at the earliest");
}
if (self._isInRender) {
throw new Error("Can't call View#subscribe from inside render(); try calling it from the created or rendered callback");
}
if (self.isDestroyed) {
throw new Error("Can't call View#subscribe from inside the destroyed callback, try calling it inside created or rendered.");
}
};
/**
* Just like Blaze.View#autorun, but with Meteor.subscribe instead of
* Tracker.autorun. Stop the subscription when the view is destroyed.
* @return {SubscriptionHandle} A handle to the subscription so that you can
* see if it is ready, or stop it manually
*/
Blaze.View.prototype.subscribe = function (args, options) {
var self = this;
options = options || {};
self._errorIfShouldntCallSubscribe();
var subHandle;
if (options.connection) {
subHandle = options.connection.subscribe.apply(options.connection, args);
} else {
subHandle = Meteor.subscribe.apply(Meteor, args);
}
self.onViewDestroyed(function () {
subHandle.stop();
});
return subHandle;
};
Blaze.View.prototype.firstNode = function () {
if (! this._isAttached)
throw new Error("View must be attached before accessing its DOM");
return this._domrange.firstNode();
};
Blaze.View.prototype.lastNode = function () {
if (! this._isAttached)
throw new Error("View must be attached before accessing its DOM");
return this._domrange.lastNode();
};
Blaze._fireCallbacks = function (view, which) {
Blaze._withCurrentView(view, function () {
Tracker.nonreactive(function fireCallbacks() {
var cbs = view._callbacks[which];
for (var i = 0, N = (cbs && cbs.length); i < N; i++)
cbs[i] && cbs[i].call(view);
});
});
};
Blaze._createView = function (view, parentView, forExpansion) {
if (view.isCreated)
throw new Error("Can't render the same View twice");
view.parentView = (parentView || null);
view.isCreated = true;
if (forExpansion)
view._isCreatedForExpansion = true;
Blaze._fireCallbacks(view, 'created');
};
var doFirstRender = function (view, initialContent) {
var domrange = new Blaze._DOMRange(initialContent);
view._domrange = domrange;
domrange.view = view;
view.isRendered = true;
Blaze._fireCallbacks(view, 'rendered');
var teardownHook = null;
domrange.onAttached(function attached(range, element) {
view._isAttached = true;
teardownHook = Blaze._DOMBackend.Teardown.onElementTeardown(
element, function teardown() {
Blaze._destroyView(view, true /* _skipNodes */);
});
});
// tear down the teardown hook
view.onViewDestroyed(function () {
teardownHook && teardownHook.stop();
teardownHook = null;
});
return domrange;
};
// Take an uncreated View `view` and create and render it to DOM,
// setting up the autorun that updates the View. Returns a new
// DOMRange, which has been associated with the View.
//
// The private arguments `_workStack` and `_intoArray` are passed in
// by Blaze._materializeDOM and are only present for recursive calls
// (when there is some other _materializeView on the stack). If
// provided, then we avoid the mutual recursion of calling back into
// Blaze._materializeDOM so that deep View hierarchies don't blow the
// stack. Instead, we push tasks onto workStack for the initial
// rendering and subsequent setup of the View, and they are done after
// we return. When there is a _workStack, we do not return the new
// DOMRange, but instead push it into _intoArray from a _workStack
// task.
Blaze._materializeView = function (view, parentView, _workStack, _intoArray) {
Blaze._createView(view, parentView);
var domrange;
var lastHtmljs;
// We don't expect to be called in a Computation, but just in case,
// wrap in Tracker.nonreactive.
Tracker.nonreactive(function () {
view.autorun(function doRender(c) {
// `view.autorun` sets the current view.
view.renderCount++;
view._isInRender = true;
// Any dependencies that should invalidate this Computation come
// from this line:
var htmljs = view._render();
view._isInRender = false;
if (! c.firstRun) {
Tracker.nonreactive(function doMaterialize() {
// re-render
var rangesAndNodes = Blaze._materializeDOM(htmljs, [], view);
if (! Blaze._isContentEqual(lastHtmljs, htmljs)) {
domrange.setMembers(rangesAndNodes);
Blaze._fireCallbacks(view, 'rendered');
}
});
}
lastHtmljs = htmljs;
// Causes any nested views to stop immediately, not when we call
// `setMembers` the next time around the autorun. Otherwise,
// helpers in the DOM tree to be replaced might be scheduled
// to re-run before we have a chance to stop them.
Tracker.onInvalidate(function () {
if (domrange) {
domrange.destroyMembers();
}
});
}, undefined, 'materialize');
// first render. lastHtmljs is the first htmljs.
var initialContents;
if (! _workStack) {
initialContents = Blaze._materializeDOM(lastHtmljs, [], view);
domrange = doFirstRender(view, initialContents);
initialContents = null; // help GC because we close over this scope a lot
} else {
// We're being called from Blaze._materializeDOM, so to avoid
// recursion and save stack space, provide a description of the
// work to be done instead of doing it. Tasks pushed onto
// _workStack will be done in LIFO order after we return.
// The work will still be done within a Tracker.nonreactive,
// because it will be done by some call to Blaze._materializeDOM
// (which is always called in a Tracker.nonreactive).
initialContents = [];
// push this function first so that it happens last
_workStack.push(function () {
domrange = doFirstRender(view, initialContents);
initialContents = null; // help GC because of all the closures here
_intoArray.push(domrange);
});
// now push the task that calculates initialContents
_workStack.push(_.bind(Blaze._materializeDOM, null,
lastHtmljs, initialContents, view, _workStack));
}
});
if (! _workStack) {
return domrange;
} else {
return null;
}
};
// Expands a View to HTMLjs, calling `render` recursively on all
// Views and evaluating any dynamic attributes. Calls the `created`
// callback, but not the `materialized` or `rendered` callbacks.
// Destroys the view immediately, unless called in a Tracker Computation,
// in which case the view will be destroyed when the Computation is
// invalidated. If called in a Tracker Computation, the result is a
// reactive string; that is, the Computation will be invalidated
// if any changes are made to the view or subviews that might affect
// the HTML.
Blaze._expandView = function (view, parentView) {
Blaze._createView(view, parentView, true /*forExpansion*/);
view._isInRender = true;
var htmljs = Blaze._withCurrentView(view, function () {
return view._render();
});
view._isInRender = false;
var result = Blaze._expand(htmljs, view);
if (Tracker.active) {
Tracker.onInvalidate(function () {
Blaze._destroyView(view);
});
} else {
Blaze._destroyView(view);
}
return result;
};
// Options: `parentView`
Blaze._HTMLJSExpander = HTML.TransformingVisitor.extend();
Blaze._HTMLJSExpander.def({
visitObject: function (x) {
if (x instanceof Blaze.Template)
x = x.constructView();
if (x instanceof Blaze.View)
return Blaze._expandView(x, this.parentView);
// this will throw an error; other objects are not allowed!
return HTML.TransformingVisitor.prototype.visitObject.call(this, x);
},
visitAttributes: function (attrs) {
// expand dynamic attributes
if (typeof attrs === 'function')
attrs = Blaze._withCurrentView(this.parentView, attrs);
// call super (e.g. for case where `attrs` is an array)
return HTML.TransformingVisitor.prototype.visitAttributes.call(this, attrs);
},
visitAttribute: function (name, value, tag) {
// expand attribute values that are functions. Any attribute value
// that contains Views must be wrapped in a function.
if (typeof value === 'function')
value = Blaze._withCurrentView(this.parentView, value);
return HTML.TransformingVisitor.prototype.visitAttribute.call(
this, name, value, tag);
}
});
// Return Blaze.currentView, but only if it is being rendered
// (i.e. we are in its render() method).
var currentViewIfRendering = function () {
var view = Blaze.currentView;
return (view && view._isInRender) ? view : null;
};
Blaze._expand = function (htmljs, parentView) {
parentView = parentView || currentViewIfRendering();
return (new Blaze._HTMLJSExpander(
{parentView: parentView})).visit(htmljs);
};
Blaze._expandAttributes = function (attrs, parentView) {
parentView = parentView || currentViewIfRendering();
return (new Blaze._HTMLJSExpander(
{parentView: parentView})).visitAttributes(attrs);
};
Blaze._destroyView = function (view, _skipNodes) {
if (view.isDestroyed)
return;
view.isDestroyed = true;
Blaze._fireCallbacks(view, 'destroyed');
// Destroy views and elements recursively. If _skipNodes,
// only recurse up to views, not elements, for the case where
// the backend (jQuery) is recursing over the elements already.
if (view._domrange)
view._domrange.destroyMembers(_skipNodes);
};
Blaze._destroyNode = function (node) {
if (node.nodeType === 1)
Blaze._DOMBackend.Teardown.tearDownElement(node);
};
// Are the HTMLjs entities `a` and `b` the same? We could be
// more elaborate here but the point is to catch the most basic
// cases.
Blaze._isContentEqual = function (a, b) {
if (a instanceof HTML.Raw) {
return (b instanceof HTML.Raw) && (a.value === b.value);
} else if (a == null) {
return (b == null);
} else {
return (a === b) &&
((typeof a === 'number') || (typeof a === 'boolean') ||
(typeof a === 'string'));
}
};
/**
* @summary The View corresponding to the current template helper, event handler, callback, or autorun. If there isn't one, `null`.
* @locus Client
* @type {Blaze.View}
*/
Blaze.currentView = null;
Blaze._withCurrentView = function (view, func) {
var oldView = Blaze.currentView;
try {
Blaze.currentView = view;
return func();
} finally {
Blaze.currentView = oldView;
}
};
// Blaze.render publicly takes a View or a Template.
// Privately, it takes any HTMLJS (extended with Views and Templates)
// except null or undefined, or a function that returns any extended
// HTMLJS.
var checkRenderContent = function (content) {
if (content === null)
throw new Error("Can't render null");
if (typeof content === 'undefined')
throw new Error("Can't render undefined");
if ((content instanceof Blaze.View) ||
(content instanceof Blaze.Template) ||
(typeof content === 'function'))
return;
try {
// Throw if content doesn't look like HTMLJS at the top level
// (i.e. verify that this is an HTML.Tag, or an array,
// or a primitive, etc.)
(new HTML.Visitor).visit(content);
} catch (e) {
// Make error message suitable for public API
throw new Error("Expected Template or View");
}
};
// For Blaze.render and Blaze.toHTML, take content and
// wrap it in a View, unless it's a single View or
// Template already.
var contentAsView = function (content) {
checkRenderContent(content);
if (content instanceof Blaze.Template) {
return content.constructView();
} else if (content instanceof Blaze.View) {
return content;
} else {
var func = content;
if (typeof func !== 'function') {
func = function () {
return content;
};
}
return Blaze.View('render', func);
}
};
// For Blaze.renderWithData and Blaze.toHTMLWithData, wrap content
// in a function, if necessary, so it can be a content arg to
// a Blaze.With.
var contentAsFunc = function (content) {
checkRenderContent(content);
if (typeof content !== 'function') {
return function () {
return content;
};
} else {
return content;
}
};
/**
* @summary Renders a template or View to DOM nodes and inserts it into the DOM, returning a rendered [View](#blaze_view) which can be passed to [`Blaze.remove`](#blaze_remove).
* @locus Client
* @param {Template|Blaze.View} templateOrView The template (e.g. `Template.myTemplate`) or View object to render. If a template, a View object is [constructed](#template_constructview). If a View, it must be an unrendered View, which becomes a rendered View and is returned.
* @param {DOMNode} parentNode The node that will be the parent of the rendered template. It must be an Element node.
* @param {DOMNode} [nextNode] Optional. If provided, must be a child of parentNode; the template will be inserted before this node. If not provided, the template will be inserted as the last child of parentNode.
* @param {Blaze.View} [parentView] Optional. If provided, it will be set as the rendered View's [`parentView`](#view_parentview).
*/
Blaze.render = function (content, parentElement, nextNode, parentView) {
if (! parentElement) {
Blaze._warn("Blaze.render without a parent element is deprecated. " +
"You must specify where to insert the rendered content.");
}
if (nextNode instanceof Blaze.View) {
// handle omitted nextNode
parentView = nextNode;
nextNode = null;
}
// parentElement must be a DOM node. in particular, can't be the
// result of a call to `$`. Can't check if `parentElement instanceof
// Node` since 'Node' is undefined in IE8.
if (parentElement && typeof parentElement.nodeType !== 'number')
throw new Error("'parentElement' must be a DOM node");
if (nextNode && typeof nextNode.nodeType !== 'number') // 'nextNode' is optional
throw new Error("'nextNode' must be a DOM node");
parentView = parentView || currentViewIfRendering();
var view = contentAsView(content);
Blaze._materializeView(view, parentView);
if (parentElement) {
view._domrange.attach(parentElement, nextNode);
}
return view;
};
Blaze.insert = function (view, parentElement, nextNode) {
Blaze._warn("Blaze.insert has been deprecated. Specify where to insert the " +
"rendered content in the call to Blaze.render.");
if (! (view && (view._domrange instanceof Blaze._DOMRange)))
throw new Error("Expected template rendered with Blaze.render");
view._domrange.attach(parentElement, nextNode);
};
/**
* @summary Renders a template or View to DOM nodes with a data context. Otherwise identical to `Blaze.render`.
* @locus Client
* @param {Template|Blaze.View} templateOrView The template (e.g. `Template.myTemplate`) or View object to render.
* @param {Object|Function} data The data context to use, or a function returning a data context. If a function is provided, it will be reactively re-run.
* @param {DOMNode} parentNode The node that will be the parent of the rendered template. It must be an Element node.
* @param {DOMNode} [nextNode] Optional. If provided, must be a child of parentNode; the template will be inserted before this node. If not provided, the template will be inserted as the last child of parentNode.
* @param {Blaze.View} [parentView] Optional. If provided, it will be set as the rendered View's [`parentView`](#view_parentview).
*/
Blaze.renderWithData = function (content, data, parentElement, nextNode, parentView) {
// We defer the handling of optional arguments to Blaze.render. At this point,
// `nextNode` may actually be `parentView`.
return Blaze.render(Blaze._TemplateWith(data, contentAsFunc(content)),
parentElement, nextNode, parentView);
};
/**
* @summary Removes a rendered View from the DOM, stopping all reactive updates and event listeners on it. Also destroys the Blaze.Template instance associated with the view.
* @locus Client
* @param {Blaze.View} renderedView The return value from `Blaze.render` or `Blaze.renderWithData`, or the `view` property of a Blaze.Template instance. Calling `Blaze.remove(Template.instance().view)` from within a template event handler will destroy the view as well as that template and trigger the template's `onDestroyed` handlers.
*/
Blaze.remove = function (view) {
if (! (view && (view._domrange instanceof Blaze._DOMRange)))
throw new Error("Expected template rendered with Blaze.render");
while (view) {
if (! view.isDestroyed) {
var range = view._domrange;
if (range.attached && ! range.parentRange)
range.detach();
range.destroy();
}
view = view._hasGeneratedParent && view.parentView;
}
};
/**
* @summary Renders a template or View to a string of HTML.
* @locus Client
* @param {Template|Blaze.View} templateOrView The template (e.g. `Template.myTemplate`) or View object from which to generate HTML.
*/
Blaze.toHTML = function (content, parentView) {
parentView = parentView || currentViewIfRendering();
return HTML.toHTML(Blaze._expandView(contentAsView(content), parentView));
};
/**
* @summary Renders a template or View to HTML with a data context. Otherwise identical to `Blaze.toHTML`.
* @locus Client
* @param {Template|Blaze.View} templateOrView The template (e.g. `Template.myTemplate`) or View object from which to generate HTML.
* @param {Object|Function} data The data context to use, or a function returning a data context.
*/
Blaze.toHTMLWithData = function (content, data, parentView) {
parentView = parentView || currentViewIfRendering();
return HTML.toHTML(Blaze._expandView(Blaze._TemplateWith(
data, contentAsFunc(content)), parentView));
};
Blaze._toText = function (htmljs, parentView, textMode) {
if (typeof htmljs === 'function')
throw new Error("Blaze._toText doesn't take a function, just HTMLjs");
if ((parentView != null) && ! (parentView instanceof Blaze.View)) {
// omitted parentView argument
textMode = parentView;
parentView = null;
}
parentView = parentView || currentViewIfRendering();
if (! textMode)
throw new Error("textMode required");
if (! (textMode === HTML.TEXTMODE.STRING ||
textMode === HTML.TEXTMODE.RCDATA ||
textMode === HTML.TEXTMODE.ATTRIBUTE))
throw new Error("Unknown textMode: " + textMode);
return HTML.toText(Blaze._expand(htmljs, parentView), textMode);
};
/**
* @summary Returns the current data context, or the data context that was used when rendering a particular DOM element or View from a Meteor template.
* @locus Client
* @param {DOMElement|Blaze.View} [elementOrView] Optional. An element that was rendered by a Meteor, or a View.
*/
Blaze.getData = function (elementOrView) {
var theWith;
if (! elementOrView) {
theWith = Blaze.getView('with');
} else if (elementOrView instanceof Blaze.View) {
var view = elementOrView;
theWith = (view.name === 'with' ? view :
Blaze.getView(view, 'with'));
} else if (typeof elementOrView.nodeType === 'number') {
if (elementOrView.nodeType !== 1)
throw new Error("Expected DOM element");
theWith = Blaze.getView(elementOrView, 'with');
} else {
throw new Error("Expected DOM element or View");
}
return theWith ? theWith.dataVar.get() : null;
};
// For back-compat
Blaze.getElementData = function (element) {
Blaze._warn("Blaze.getElementData has been deprecated. Use " +
"Blaze.getData(element) instead.");
if (element.nodeType !== 1)
throw new Error("Expected DOM element");
return Blaze.getData(element);
};
// Both arguments are optional.
/**
* @summary Gets either the current View, or the View enclosing the given DOM element.
* @locus Client
* @param {DOMElement} [element] Optional. If specified, the View enclosing `element` is returned.
*/
Blaze.getView = function (elementOrView, _viewName) {
var viewName = _viewName;
if ((typeof elementOrView) === 'string') {
// omitted elementOrView; viewName present
viewName = elementOrView;
elementOrView = null;
}
// We could eventually shorten the code by folding the logic
// from the other methods into this method.
if (! elementOrView) {
return Blaze._getCurrentView(viewName);
} else if (elementOrView instanceof Blaze.View) {
return Blaze._getParentView(elementOrView, viewName);
} else if (typeof elementOrView.nodeType === 'number') {
return Blaze._getElementView(elementOrView, viewName);
} else {
throw new Error("Expected DOM element or View");
}
};
// Gets the current view or its nearest ancestor of name
// `name`.
Blaze._getCurrentView = function (name) {
var view = Blaze.currentView;
// Better to fail in cases where it doesn't make sense
// to use Blaze._getCurrentView(). There will be a current
// view anywhere it does. You can check Blaze.currentView
// if you want to know whether there is one or not.
if (! view)
throw new Error("There is no current view");
if (name) {
while (view && view.name !== name)
view = view.parentView;
return view || null;
} else {
// Blaze._getCurrentView() with no arguments just returns
// Blaze.currentView.
return view;
}
};
Blaze._getParentView = function (view, name) {
var v = view.parentView;
if (name) {
while (v && v.name !== name)
v = v.parentView;
}
return v || null;
};
Blaze._getElementView = function (elem, name) {
var range = Blaze._DOMRange.forElement(elem);
var view = null;
while (range && ! view) {
view = (range.view || null);
if (! view) {
if (range.parentRange)
range = range.parentRange;
else
range = Blaze._DOMRange.forElement(range.parentElement);
}
}
if (name) {
while (view && view.name !== name)
view = view.parentView;
return view || null;
} else {
return view;
}
};
Blaze._addEventMap = function (view, eventMap, thisInHandler) {
thisInHandler = (thisInHandler || null);
var handles = [];
if (! view._domrange)
throw new Error("View must have a DOMRange");
view._domrange.onAttached(function attached_eventMaps(range, element) {
_.each(eventMap, function (handler, spec) {
var clauses = spec.split(/,\s+/);
// iterate over clauses of spec, e.g. ['click .foo', 'click .bar']
_.each(clauses, function (clause) {
var parts = clause.split(/\s+/);
if (parts.length === 0)
return;
var newEvents = parts.shift();
var selector = parts.join(' ');
handles.push(Blaze._EventSupport.listen(
element, newEvents, selector,
function (evt) {
if (! range.containsElement(evt.currentTarget))
return null;
var handlerThis = thisInHandler || this;
var handlerArgs = arguments;
return Blaze._withCurrentView(view, function () {
return handler.apply(handlerThis, handlerArgs);
});
},
range, function (r) {
return r.parentRange;
}));
});
});
});
view.onViewDestroyed(function () {
_.each(handles, function (h) {
h.stop();
});
handles.length = 0;
});
};
================================================
FILE: packages/blaze/view_tests.js
================================================
if (Meteor.isClient) {
Tinytest.add("blaze - view - callbacks", function (test) {
var R = ReactiveVar('foo');
var buf = '';
var v = Blaze.View(function () {
return R.get();
});
v.onViewCreated(function () {
buf += 'c' + v.renderCount;
});
v._onViewRendered(function () {
buf += 'r' + v.renderCount;
});
v.onViewReady(function () {
buf += 'y' + v.renderCount;
});
v.onViewDestroyed(function () {
buf += 'd' + v.renderCount;
});
test.equal(buf, '');
var div = document.createElement("DIV");
test.isFalse(v.isRendered);
test.isFalse(v._isAttached);
test.equal(canonicalizeHtml(div.innerHTML), "");
test.throws(function () { v.firstNode(); }, /View must be attached/);
test.throws(function () { v.lastNode(); }, /View must be attached/);
Blaze.render(v, div);
test.equal(buf, 'c0r1');
test.equal(typeof (v.firstNode().nodeType), "number");
test.equal(typeof (v.lastNode().nodeType), "number");
test.isTrue(v.isRendered);
test.isTrue(v._isAttached);
test.equal(buf, 'c0r1');
test.equal(canonicalizeHtml(div.innerHTML), "foo");
Tracker.flush();
test.equal(buf, 'c0r1y1');
R.set("bar");
Tracker.flush();
test.equal(buf, 'c0r1y1r2y2');
test.equal(canonicalizeHtml(div.innerHTML), "bar");
Blaze.remove(v);
test.equal(buf, 'c0r1y1r2y2d2');
test.equal(canonicalizeHtml(div.innerHTML), "");
buf = "";
R.set("baz");
Tracker.flush();
test.equal(buf, "");
});
}
================================================
FILE: packages/blaze-html-templates/README.md
================================================
# blaze-html-templates
A meta-package that includes everything you need to compile and run Meteor templates with Spacebars and Blaze.
For more details, see the documentation of the component packages:
- [templating](https://atmospherejs.com/meteor/templating): compiles `.html` files
- [blaze](https://atmospherejs.com/meteor/blaze): the runtime library
- [spacebars](https://atmospherejs.com/meteor/spacebars): the templating language
================================================
FILE: packages/blaze-html-templates/package.js
================================================
Package.describe({
name: 'blaze-html-templates',
version: '1.0.4',
// Brief, one-line summary of the package.
summary: 'Compile HTML templates into reactive UI with Meteor Blaze',
// By default, Meteor will default to using README.md for documentation.
// To avoid submitting documentation, set this field to null.
documentation: 'README.md'
});
Package.onUse(function(api) {
api.imply([
// A library for reactive user interfaces
'blaze',
// The following packages are basically empty shells that just exist to
// satisfy code checking for the existence of a package. Rest assured that
// they are not adding any bloat to your bundle.
'ui', // XXX COMPAT WITH PACKAGES BUILT FOR 0.9.0.
'spacebars', // XXX COMPAT WITH PACKAGES BUILT FOR 0.9.0
// Compile .html files into Blaze reactive views
'templating'
]);
});
================================================
FILE: packages/blaze-tools/.gitignore
================================================
.build*
================================================
FILE: packages/blaze-tools/README.md
================================================
# blaze-tools
Compile-time utilities that are likely to be useful to any package
that compiles templates for Blaze.
================================================
FILE: packages/blaze-tools/package.js
================================================
Package.describe({
summary: "Compile-time tools for Blaze",
version: '1.0.9'
});
Package.onUse(function (api) {
api.export('BlazeTools');
api.use('htmljs');
api.use('underscore');
api.addFiles(['preamble.js',
'tokens.js',
'tojs.js']);
});
Package.onTest(function (api) {
api.use('blaze-tools');
api.use('tinytest');
api.use('underscore');
api.use('html-tools');
api.addFiles(['token_tests.js']);
});
================================================
FILE: packages/blaze-tools/preamble.js
================================================
BlazeTools = {};
================================================
FILE: packages/blaze-tools/tojs.js
================================================
BlazeTools.EmitCode = function (value) {
if (! (this instanceof BlazeTools.EmitCode))
// called without `new`
return new BlazeTools.EmitCode(value);
if (typeof value !== 'string')
throw new Error('BlazeTools.EmitCode must be constructed with a string');
this.value = value;
};
BlazeTools.EmitCode.prototype.toJS = function (visitor) {
return this.value;
};
// Turns any JSONable value into a JavaScript literal.
toJSLiteral = function (obj) {
// See
', BR({x:'{'}));
succeed('
', BR({x:'{foo}'}));
succeed('
', BR(Attrs(TemplateTag({stuff: 'x'}))));
succeed('
', BR(Attrs(TemplateTag({stuff: 'x'}),
TemplateTag({stuff: 'y'}))));
succeed('
', BR(Attrs({y: ''}, TemplateTag({stuff: 'x'}))));
fatal('
');
fatal('
');
succeed('
', BR({x: TemplateTag({stuff: 'y'}), z: ''}));
succeed('
', BR({x: ['y', TemplateTag({stuff: 'z'}), 'w']}));
succeed('
', BR({x: ['y', TemplateTag({stuff: 'z'}), 'w']}));
succeed('
', BR({x: ['y ', TemplateTag({stuff: 'z'}),
TemplateTag({stuff: 'w'}), ' v']}));
// Slash is parsed as part of unquoted attribute! This is consistent with
// the HTML tokenization spec. It seems odd for some inputs but is probably
// for cases like `` or ``.
succeed('
', BR({x: [TemplateTag({stuff: 'y'}), '/']}));
succeed('
', BR({x: [TemplateTag({stuff: 'z'}),
TemplateTag({stuff: 'w'})]}));
fatal('
');
succeed('
', BR({x:CharRef({html: '&', str: '&'})}));
// check tokenization of stache tags with spaces
succeed('
', BR(Attrs(TemplateTag({stuff: 'x 1'}))));
succeed('
', BR(Attrs(TemplateTag({stuff: 'x 1'}),
TemplateTag({stuff: 'y 2'}))));
succeed('
', BR(Attrs({y:''}, TemplateTag({stuff: 'x 1'}))));
fatal('
');
fatal('
');
succeed('
', BR({x: TemplateTag({stuff: 'y 2'}), z: ''}));
succeed('
', BR({x: ['y', TemplateTag({stuff: 'z 3'}), 'w']}));
succeed('
', BR({x: ['y', TemplateTag({stuff: 'z 3'}), 'w']}));
succeed('
', BR({x: ['y ', TemplateTag({stuff: 'z 3'}),
TemplateTag({stuff: 'w 4'}), ' v']}));
succeed('
', BR({x: [TemplateTag({stuff: 'y 2'}), '/']}));
succeed('
', BR({x: [TemplateTag({stuff: 'z 3'}),
TemplateTag({stuff: 'w 4'})]}));
succeed('', P());
succeed('x{{foo}}{{bar}}y', ['x', TemplateTag({stuff: 'foo'}),
TemplateTag({stuff: 'bar'}), 'y']);
succeed('x{{!foo}}{{!bar}}y', 'xy');
succeed('x{{!foo}}{{bar}}y', ['x', TemplateTag({stuff: 'bar'}), 'y']);
succeed('x{{foo}}{{!bar}}y', ['x', TemplateTag({stuff: 'foo'}), 'y']);
succeed('
{{!bar}}
{{!bar}}