diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000000..a0767ce460 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "packages/non-core/blaze"] + path = packages/non-core/blaze + url = https://github.com/meteor/blaze.git diff --git a/.travis.yml b/.travis.yml index 5ec40272b9..e943bf20c2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,10 +3,8 @@ node_js: - "4.0" cache: directories: - - "dev_bundle" - ".meteor" - ".babel-cache" -install: ./meteor --get-ready script: TEST_PACKAGES_EXCLUDE="less" ./packages/test-in-console/run.sh sudo: false env: diff --git a/Contributing.md b/Contributing.md index 547278dc69..38fa0880ad 100644 --- a/Contributing.md +++ b/Contributing.md @@ -1,10 +1,84 @@ # Contributing to Meteor -Thank you for contributing to the Meteor project! Please read the guidelines below or it might be -hard for the community to help you with your issue or pull request. +We are excited to have your help building Meteor — both the platform and the community behind it. Please read the project overview and guidelines for contributing bug reports and new code, or it might be hard for the community to help you with your issue or pull request. -We are excited to have your help building Meteor — both the platform and the -community behind it. Here's how you can help with bug reports and new code. +

Project overview

+ +Before we jump into detailed guidelines for opening and triaging issues and submitting pull requests, here is some information about how our project is structured and resources you should refer to as you start contributing. + +### Ways to contribute + +There are many ways to contribute to the Meteor Project. Here’s a list of technical contributions with increasing levels of involvement and required knowledge of Meteor’s code and operations. +- [Reporting a bug](https://github.com/meteor/meteor/blob/devel/Contributing.md#reporting-a-bug-in-meteor) +- [Triaging issues](https://github.com/meteor/meteor/blob/devel/IssueTriage.md) +- [Contributing to documentation](https://github.com/meteor/docs/blob/master/Contributing.md) +- [Submitting pull requests](https://github.com/meteor/meteor/blob/devel/Contributing.md#making-changes-to-meteor-core) +- [Reviewing pull requests](https://github.com/meteor/meteor/blob/devel/Contributing.md#reviewer) +- [Maintaining a community package](https://github.com/meteor/meteor/blob/devel/Contributing.md#community-package-maintainer) + +There are also several ways to contribute to the Meteor Project outside of GitHub, like organizing or speaking at [Meetups](https://www.meetup.com/topics/meteor/) and events and helping to moderate our [forums](https://forums.meteor.com/). Stay tuned for more documentation around non-code contributions. + +If you can think of any changes to the project or documentation that would improve the contributor experience, let us know by opening an issue! + +### Project roles + +We’ve just begun to create more defined project roles for Meteor. Here are descriptions of the existing project roles, along with the current contributors taking on those roles today. + +#### Issue Triager + +Issue Triagers are members of the community that meet with us weekly to help triage Meteor’s open issues and bug reports. Once you’ve begun triaging issues regularly on your own, we will invite you to join our dedicated Slack channel to participate in these regular coordination sessions. + +Current Issue Triagers: +- [@hwillson](https://github.com/hwillson) +- [@laosb](https://github.com/laosb) +- [@abernix](https://github.com/abernix) +- [@lorensr](https://github.com/lorensr) + +#### Reviewer + +Our most regular and experienced Issue Triagers sometimes move on to doing code reviews for pull requests, and have input into which pull requests should be merged. + +Current Reviewers: +- [@lorensr](https://github.com/lorensr) +- [@abernix](https://github.com/abernix) + +#### Core Committer + +For now, the only contributors with commit access to meteor/meteor are employees of Meteor Development Group, the company that sponsors the Meteor project. We're actively exploring adding non-MDG core committers who have distinguished themselves in other contribution areas. + +Project Lead: [@benjamn](https://github.com/benjamn) + +Current Core Committers: +- [@tmeasday](https://github.com/tmeasday) +- [@zol](https://github.com/zol) +- [@glasser](https://github.com/glasser) +- [@stubailo](https://github.com/stubailo) + +#### Documentation Maintainer + +Documentation Maintainers are regular documentation contributors that have been given the ability to merge docs changes on [meteor/docs](https://github.com/meteor/docs). + +Current Documentation Maintainers: +- [@tmeasday](https://github.com/tmeasday) +- [@lorensr](https://github.com/lorensr) + +#### Community Package Maintainer: + +Community package maintainers are community members who maintain packages outside of Meteor core. This requires code to be extracted from meteor/meteor, and entails a high level of responsibility. For this reason, community maintainers generally (and currently) must first become an advanced contributor to Meteor core and have 4-5 non-trivial pull requests merged that went through the proper contribution workflow. At that point, core contributors may make the case for breaking out a particular core package, and assist in the technical process around doing so. + +Current Community Package Maintainers: +- [@mitar](https://github.com/mitar) for [Blaze](https://github.com/meteor/blaze) + +#### Community Manager + +The community manager helps to coordinate resources, documentation, events, and other supportive work needed to ensure the health of the Meteor project. + +Current Community Manager: +- [@theadactyl](https://github.com/theadactyl) + +### Tracking project work + +Right now, the best place to track the work being done on Meteor is to take a look at the latest release milestone [here](https://github.com/meteor/meteor/milestones). We also curate specific issues that would make great pull requests for community contributors with the [pull requests encouraged tag](https://github.com/meteor/meteor/issues?q=is%3Aopen+is%3Aissue+label%3Apull-requests-encouraged).

Reporting a bug in Meteor

@@ -61,9 +135,8 @@ for more details on proposing changes to core code. As of May 2016, we use GitHub to track feature requests. Feature request issues get the `feature` label, as well as a label corresponding to the Meteor subproject that they are a part of. -Meteor is a big project with [many](https://www.meteor.com/projects) -[subprojects](https://github.com/meteor/meteor/labels). Right now, the project -doesn't have as many +Meteor is a big project with [many subprojects](https://github.com/meteor/meteor/tree/devel/packages). +Right now, the project doesn't have as many [core developers (we're hiring!)](https://www.meteor.com/jobs/core-developer) as subprojects, so we're not able to work on every single subproject every month. We use our [roadmap](Roadmap.md) to communicate the high level features we're prioritizing over the near and medium term. @@ -157,7 +230,7 @@ these guidelines: * Include tests that prove your code works. * Follow the - [MDG style guide](https://github.com/meteor/meteor/wiki/Meteor-Style-Guide) + [MDG style guide](https://guide.meteor.com/code-style.html#javascript) for code and commit messages. * Be sure your author field in git is properly filled out with your full name diff --git a/History.md b/History.md index 84f0b7520f..b3a36ad74a 100644 --- a/History.md +++ b/History.md @@ -1,5 +1,350 @@ ## v.NEXT +* The `npm` npm package has been upgraded to version 4.0.2. + +* The `facebook` package has been split into: + - `facebook-oauth` (the part that allows oauth w/ FB directly) and + - `facebook-config-ui` (the Blaze configuration templates for accounts-ui) + + This means you can now use `[accounts-]facebook` without needing Blaze. + + If you are using `accounts-ui` and `accounts-facebook`, you will probably + need to install the `facebook-config-ui` package if you want to configure it + using the Accounts UI. + + [Issue #7715](https://github.com/meteor/meteor/issues/7715) + [PR #7728](https://github.com/meteor/meteor/pull/7728) + +* The `meteor-babel` npm package has been upgraded to version 0.14.3, + fixing [#8021](https://github.com/meteor/meteor/issues/8021) and + [#7662](https://github.com/meteor/meteor/issues/7662). + +* Added support for frame-ancestors CSP option in browser-policy. + [#7970](https://github.com/meteor/meteor/pull/7970) + +* You can now use autoprefixer with stylus files added via packages [#7727](https://github.com/meteor/meteor/pull/7727) + +## v1.4.2.3 + +* Style improvements for `meteor create --full`. + [#8045](https://github.com/meteor/meteor/pull/8045) + +> Note: Meteor 1.4.2.2 was finalized before + [#8045](https://github.com/meteor/meteor/pull/8045) was merged, but + those changes were [deemed important + enough](https://github.com/meteor/meteor/pull/8044#issuecomment-260913739) + to skip recommending 1.4.2.2 and instead immediately release 1.4.2.3. + +## v1.4.2.2 + +* Node has been upgraded to version 4.6.2. + +* `meteor create` now has a new `--full` option, which generates an larger app, + demonstrating development techniques highlighted in the + [Meteor Guide](http://guide.meteor.com) + + [Issue #6974](https://github.com/meteor/meteor/issues/6974) + [PR #7807](https://github.com/meteor/meteor/pull/7807) + +* Minimongo now supports `$min`, `$max` and partially supports `$currentDate`. + + [Issue #7857](https://github.com/meteor/meteor/issues/7857) + [PR #7858](https://github.com/meteor/meteor/pull/7858) + +* Fix for [Issue #5676](https://github.com/meteor/meteor/issues/5676) + [PR #7968](https://github.com/meteor/meteor/pull/7968) + +* It is now possible for packages to specify a *lazy* main module: + ```js + Package.onUse(function (api) { + api.mainModule("client.js", "client", { lazy: true }); + }); + ``` + This means the `client.js` module will not be evaluated during app + startup unless/until another module imports it, and will not even be + included in the client bundle if no importing code is found. **Note 1:** + packages with lazy main modules cannot use `api.export` to export global + symbols to other packages/apps. **Note 2:** packages with lazy main + modules should be restricted to Meteor 1.4.2.2 or later via + `api.versionsFrom("1.4.2.2")`, since older versions of Meteor cannot + import lazy main modules using `import "meteor/"` but must + explicitly name the module: `import "meteor//client.js"`. + +## v1.4.2.1 + +* Installing the `babel-runtime` npm package in your application + `node_modules` directory is now required for most Babel-transformed code + to work, as the Meteor `babel-runtime` package no longer attempts to + provide custom implementations of Babel helper functions. To install + the `babel-runtime` package, simply run the command + ```sh + meteor npm install --save babel-runtime + ``` + in any Meteor application directory. The Meteor `babel-runtime` package + version has been bumped to 1.0.0 to reflect this major change. + [#7995](https://github.com/meteor/meteor/pull/7995) + +* File system operations performed by the command-line tool no longer use + fibers unless the `METEOR_DISABLE_FS_FIBERS` environment variable is + explicitly set to a falsy value. For larger apps, this change results in + significant build performance improvements due to the creation of fewer + fibers and the avoidance of unnecessary asyncronous delays. + https://github.com/meteor/meteor/pull/7975/commits/ca4baed90ae0675e55c93976411d4ed91f12dd63 + +* Running Meteor as `root` is still discouraged, and results in a fatal + error by default, but the `--allow-superuser` flag now works as claimed. + [#7959](https://github.com/meteor/meteor/issues/7959) + +* The `dev_bundle\python\python.exe` executable has been restored to the + Windows dev bundle, which may help with `meteor npm rebuild` commands. + [#7960](https://github.com/meteor/meteor/issues/7960) + +* Changes within linked npm packages now trigger a partial rebuild, + whereas previously (in 1.4.2) they were ignored. + [#7978](https://github.com/meteor/meteor/issues/7978) + +* Miscellaneous fixed bugs: + [#2876](https://github.com/meteor/meteor/issues/2876) + [#7154](https://github.com/meteor/meteor/issues/7154) + [#7956](https://github.com/meteor/meteor/issues/7956) + [#7974](https://github.com/meteor/meteor/issues/7974) + [#7999](https://github.com/meteor/meteor/issues/7999) + [#8005](https://github.com/meteor/meteor/issues/8005) + [#8007](https://github.com/meteor/meteor/issues/8007) + +## v1.4.2 + +* This release implements a number of rebuild performance optimizations. + As you edit files in development, the server should restart and rebuild + much more quickly, especially if you have many `node_modules` files. + See https://github.com/meteor/meteor/pull/7668 for more details. + +> Note: the `METEOR_PROFILE` environment variable now provides data for + server startup time as well as build time, which should make it easier + to tell which of your packages are responsible for slow startup times. + Please include the output of `METEOR_PROFILE=10 meteor run` with any + GitHub issue about rebuild performance. + +* `npm` has been upgraded to version 3.10.9. + +* The `cordova-lib` npm package has been updated to 6.3.1, along with + cordova-android (5.2.2) and cordova-ios (4.2.1), and various plugins. + +* The `node-pre-gyp` npm package has been updated to 0.6.30. + +* The `lru-cache` npm package has been updated to 4.0.1. + +* The `meteor-promise` npm package has been updated to 0.8.0 for better + asynchronous stack traces. + +* The `meteor` tool is now prevented from running as `root` as this is + not recommended and can cause issues with permissions. In some environments, + (e.g. Docker), it may still be desired to run as `root` and this can be + permitted by passing `--unsafe-perm` to the `meteor` command. + [#7821](https://github.com/meteor/meteor/pull/7821) + +* Blaze-related packages have been extracted to + [`meteor/blaze`](https://github.com/meteor/blaze), and the main + [`meteor/meteor`](https://github.com/meteor/meteor) repository now + refers to them via git submodules (see + [#7633](https://github.com/meteor/meteor/pull/7633)). + When running `meteor` from a checkout, you must now update these + submodules by running + ```sh + git submodule update --init --recursive + ``` + in the root directory of your `meteor` checkout. + +* Accounts.forgotPassword and .verifyEmail no longer throw errors if callback is provided. [Issue #5664](https://github.com/meteor/meteor/issues/5664) [Origin PR #5681](https://github.com/meteor/meteor/pull/5681) [Merged PR](https://github.com/meteor/meteor/pull/7117) + +* The default content security policy (CSP) for Cordova now includes `ws:` + and `wss:` WebSocket protocols. + [#7774](https://github.com/meteor/meteor/pull/7774) + +* `meteor npm` commands are now configured to use `dev_bundle/.npm` as the + npm cache directory by default, which should make npm commands less + sensitive to non-reproducible factors in the external environment. + https://github.com/meteor/meteor/pull/7668/commits/3313180a6ff33ee63602f7592a9506012029e919 + +* The `meteor test` command now supports the `--no-release-check` flag. + https://github.com/meteor/meteor/pull/7668/commits/7097f78926f331fb9e70a06300ce1711adae2850 + +* JavaScript module bundles on the server no longer include transitive + `node_modules` dependencies, since those dependencies can be evaluated + directly by Node. This optimization should improve server rebuild times + for apps and packages with large `node_modules` directories. + https://github.com/meteor/meteor/pull/7668/commits/03c5346873849151cecc3e00606c6e5aa13b3bbc + +* The `standard-minifier-css` package now does basic caching for the + expensive `mergeCss` function. + https://github.com/meteor/meteor/pull/7668/commits/bfa67337dda1e90610830611fd99dcb1bd44846a + +* The `coffeescript` package now natively supports `import` and `export` + declarations. [#7818](https://github.com/meteor/meteor/pull/7818) + +## v1.4.1.3 + +* Node has been updated to version 4.6.1: + https://nodejs.org/en/blog/release/v4.6.1/ + +* The `mongodb` npm package used by the `npm-mongo` Meteor package has + been updated to version 2.2.11. + [#7780](https://github.com/meteor/meteor/pull/7780) + +* The `fibers` npm package has been upgraded to version 1.0.15. + +* Running Meteor with a different `--port` will now automatically + reconfigure the Mongo replica set when using the WiredTiger storage + engine, instead of failing to start Mongo. + [#7840](https://github.com/meteor/meteor/pull/7840). + +* When the Meteor development server shuts down, it now attempts to kill + the `mongod` process it spawned, in addition to killing any running + `mongod` processes when the server first starts up. + https://github.com/meteor/meteor/pull/7668/commits/295d3d5678228f06ee0ab6c0d60139849a0ea192 + +* The `meteor ...` syntax will now work for any command + installed in `dev_bundle/bin`, except for Meteor's own commands. + +* Incomplete package downloads will now fail (and be retried several + times) instead of silently succeeding, which was the cause of the + dreaded `Error: ENOENT: no such file or directory, open... os.json` + error. [#7806](https://github.com/meteor/meteor/issues/7806) + +## v1.4.1.2 + +* Node has been upgraded to version 4.6.0, a recommended security release: + https://nodejs.org/en/blog/release/v4.6.0/ + +* `npm` has been upgraded to version 3.10.8. + +## v1.4.1.1 + +* Update the version of our Node MongoDB driver to 2.2.8 to fix a bug in + reconnection logic, leading to some `update` and `remove` commands being + treated as `insert`s. [#7594](https://github.com/meteor/meteor/issues/7594) + +## v1.4.1 + +* Node has been upgraded to 4.5.0. + +* `npm` has been upgraded to 3.10.6. + +* The `meteor publish-for-arch` command is no longer necessary when + publishing Meteor packages with binary npm dependencies. Instead, binary + dependencies will be rebuilt automatically on the installation side. + Meteor package authors are not responsible for failures due to compiler + toolchain misconfiguration, and any compilation problems with the + underlying npm packages should be taken up with the authors of those + packages. That said, if a Meteor package author really needs or wants to + continue using `meteor publish-for-arch`, she should publish her package + using an older release: e.g. `meteor --release 1.4 publish`. + [#7608](https://github.com/meteor/meteor/pull/7608) + +* The `.meteor-last-rebuild-version.json` files that determine if a binary + npm package needs to be rebuilt now include more information from the + `process` object, namely `process.{platform,arch,versions}` instead of + just `process.versions`. Note also that the comparison of versions now + ignores differences in patch versions, to avoid needless rebuilds. + +* The `npm-bcrypt` package now uses a pure-JavaScript implementation by + default, but will prefer the native `bcrypt` implementation if it is + installed in the application's `node_modules` directory. In other words, + run `meteor install --save bcrypt` in your application if you need or + want to use the native implementation of `bcrypt`. + [#7595](https://github.com/meteor/meteor/pull/7595) + +* After Meteor packages are downloaded from Atmosphere, they will now be + extracted using native `tar` or `7z.exe` on Windows, instead of the + https://www.npmjs.com/package/tar library, for a significant performance + improvement. [#7457](https://github.com/meteor/meteor/pull/7457) + +* The npm `tar` package has been upgraded to 2.2.1, though it is now only + used as a fallback after native `tar` and/or `7z.exe`. + +* The progress indicator now distinguishes between downloading, + extracting, and loading newly-installed Meteor packages, instead of + lumping all of that work into a "downloading" status message. + +* Background Meteor updates will no longer modify the `~/.meteor/meteor` + symbolic link (or `AppData\Local\.meteor\meteor.bat` on Windows). + Instead, developers must explicitly type `meteor update` to begin using + a new version of the `meteor` script. + +* Password Reset tokens now expire (after 3 days by default -- can be modified via `Accounts.config({ passwordResetTokenExpirationInDays: ...}`). [PR #7534](https://github.com/meteor/meteor/pull/7534) + +* The `google` package now uses the `email` scope as a mandatory field instead + of the `profile` scope. The `profile` scope is still added by default if the + `requestPermissions` option is not specified to maintain backward + compatibility, but it is now possible to pass an empty array to + `requestPermissions` in order to only request the `email` scope, which + reduces the amount of permissions requested from the user in the Google + popup. [PR #6975](https://github.com/meteor/meteor/pull/6975) + +* Added `Facebook.handleAuthFromAccessToken` in the case where you get the FB + accessToken in some out-of-band way. [PR #7550](https://github.com/meteor/meteor/pull/7550) + +* `Accounts.onLogout` gets `{ user, connection }` context in a similar fashion + to `Accounts.onLogin`. [Issue #7397](https://github.com/meteor/meteor/issues/7397) [PR #7433](https://github.com/meteor/meteor/pull/7433) + +* The `node-gyp` and `node-pre-gyp` tools will now be installed in + `bundle/programs/server/node_modules`, to assist with rebuilding binary + npm packages when deploying an app to Galaxy or elsewhere. + [#7571](https://github.com/meteor/meteor/pull/7571) + +* The `standard-minifier-{js,css}` packages no longer minify .js or .css + files on the server. [#7572](https://github.com/meteor/meteor/pull/7572) + +* Multi-line input to `meteor shell`, which was broken by changes to the + `repl` module in Node 4, works again. + [#7562](https://github.com/meteor/meteor/pull/7562) + +* The implementation of the command-line `meteor` tool now forbids + misbehaving polyfill libraries from overwriting `global.Promise`. + [#7569](https://github.com/meteor/meteor/pull/7569) + +* The `oauth-encryption` package no longer depends on the + `npm-node-aes-gcm` package (or any special npm packages), because the + Node 4 `crypto` library natively supports the `aes-128-gcm` algorithm. + [#7548](https://github.com/meteor/meteor/pull/7548) + +* The server-side component of the `meteor shell` command has been moved + into a Meteor package, so that it can be developed independently from + the Meteor release process, thanks to version unpinning. + [#7624](https://github.com/meteor/meteor/pull/7624) + +* The `meteor shell` command now works when running `meteor test`. + +* The `meteor debug` command no longer pauses at the first statement + in the Node process, yet still reliably stops at custom breakpoints + it encounters later. + +* The `meteor-babel` package has been upgraded to 0.12.0. + +* The `meteor-ecmascript-runtime` package has been upgraded to 0.2.9, to + support several additional [stage 4 + proposals](https://github.com/meteor/ecmascript-runtime/pull/4). + +* A bug that prevented @-scoped npm packages from getting bundled for + deployed apps has been fixed. + [#7609](https://github.com/meteor/meteor/pull/7609). + +* The `meteor update` command now supports an `--all-packages` flag to + update all packages (including indirect dependencies) to their latest + compatible versions, similar to passing the names of all your packages + to the `meteor update` command. + [#7653](https://github.com/meteor/meteor/pull/7653) + +* Background release updates can now be disabled by invoking either + `meteor --no-release-check` or `METEOR_NO_RELEASE_CHECK=1 meteor`. + [#7445](https://github.com/meteor/meteor/pull/7445) + +## v1.4.0.1 + +* Fix issue with the 1.4 tool springboarding to older releases (see [Issue #7491](https://github.com/meteor/meteor/issues/7491)) + +* Fix issue with running in development on Linux 32bit [Issue #7511](https://github.com/meteor/meteor/issues/7511) + ## v1.4 * Node has been upgraded to 4.4.7. @@ -29,6 +374,10 @@ 2.6 up. Mongo 2.4 has now reached end-of-life (https://www.mongodb.com/support-policy), and is no longer supported. + If you are setting `MONGO_OPLOG_URL`, especially in production, ensure you are + passing in the `replicaSet` argument (see [#7450] + (https://github.com/meteor/meteor/issues/7450)) + * Custom Mongo options can now be specified using the `Mongo.setConnectionOptions(options)` API. [#7277](https://github.com/meteor/meteor/pull/7277) @@ -46,7 +395,7 @@ * This release fixed a small bug in 1.3.5 that prevented updating apps whose `.meteor/release` files refer to releases no longer installed in - `~/.meteor/packages/meteor-tool`. 576468eae8d8dd7c1fe2fa381ac51dee5cb792cd + `~/.meteor/packages/meteor-tool`. [576468eae8d8dd7c1fe2fa381ac51dee5cb792cd](https://github.com/meteor/meteor/commit/576468eae8d8dd7c1fe2fa381ac51dee5cb792cd) ## v1.3.5 @@ -57,7 +406,7 @@ * If an app has no `package.json` file, all packages in `node_modules` will be built into the production bundle. In other words, make sure you have a `package.json` file if you want to benefit from `devDependencies` - pruning. 7b2193188fc9e297eefc841ce6035825164f0684 + pruning. [7b2193188fc9e297eefc841ce6035825164f0684](https://github.com/meteor/meteor/commit/7b2193188fc9e297eefc841ce6035825164f0684) * Binary npm dependencies of compiler plugins are now automatically rebuilt when Node/V8 versions change. @@ -92,7 +441,7 @@ `Npm.depends`) has been set to "error" instead of "warn". Note that this change does not affect `meteor npm ...` commands, which can be easily configured using `.npmrc` files or command-line flags. - https://github.com/meteor/meteor/commit/0689cae25a3e0da3615a402cdd0bec94ce8455c8 + [0689cae25a3e0da3615a402cdd0bec94ce8455c8](https://github.com/meteor/meteor/commit/0689cae25a3e0da3615a402cdd0bec94ce8455c8) ## v1.3.4.3 @@ -123,7 +472,7 @@ invoke. Note that you must `meteor update` to 1.3.4.2 before this logic will take effect, but it will work in all app directories after updating, even those pinned to older versions. - [#7338](https://github.com/meteor/meteor/issue/7338) + [#7338](https://github.com/meteor/meteor/issues/7338) * The Meteor installer now has the ability to resume downloads, so installing Meteor on a spotty internet connection should be more diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000000..f3dea8371d --- /dev/null +++ b/LICENSE @@ -0,0 +1,28 @@ +The MIT License (MIT) + +Copyright (c) 2011 - 2016 Meteor Development Group, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +==================================================================== +This license applies to all code in Meteor that is not an externally +maintained library. Externally maintained libraries have their own +licenses, included in the LICENSES directory. +==================================================================== diff --git a/LICENSE.txt b/LICENSE.txt deleted file mode 100644 index cbd814c046..0000000000 --- a/LICENSE.txt +++ /dev/null @@ -1,18 +0,0 @@ -======================================== -Meteor is licensed under the MIT License -======================================== - -Copyright (C) 2011--2016 Meteor Development Group - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - -==================================================================== -This license applies to all code in Meteor that is not an externally -maintained library. Externally maintained libraries have their own -licenses, included in the LICENSES directory. -==================================================================== diff --git a/README.md b/README.md index 021c811f0d..9c471c747c 100644 --- a/README.md +++ b/README.md @@ -18,10 +18,12 @@ Next, read the [guide](http://guide.meteor.com) or the reference documentation a ## Quick Start -Install Meteor: +On Windows, simply go to https://www.meteor.com/install and use the Windows installer. + +On Linux/macOS, use this line: ```bash -curl https://install.meteor.com | /bin/sh +curl https://install.meteor.com/ | sh ``` Create a project: @@ -39,47 +41,56 @@ meteor ## Slow Start (for developers) -If you want to run on the bleeding edge, or help develop Meteor, you -can run Meteor directly from a git checkout. +If you want to run on the bleeding edge, or [help contribute to Meteor](Contributing.md), you +can run Meteor directly from a Git checkout using these steps: -```bash -git clone git://github.com/meteor/meteor.git -cd meteor -``` +0. **Clone from GitHub** -If you're the sort of person who likes to build everything from scratch, -you can build all the Meteor dependencies (node.js, npm, mongodb, etc) -with the provided script. This requires git, a C and C++ compiler, -autotools, and scons. If you do not run this script, Meteor will -automatically download pre-compiled binaries when you first run it. + ```sh + $ git clone --recursive https://github.com/meteor/meteor.git + $ cd meteor + ``` -```bash -# OPTIONAL -./scripts/generate-dev-bundle.sh -``` + > ##### Important note about Git submodules! + > + > This repository uses Git submodules. If you clone without the `--recursive` flag, + > re-fetch with `git pull` or experience "`Depending on unknown package`" errors, + > run the following in the repository root to sync things up again: + > + > $ git submodule update --init --recursive -Now you can run meteor directly from the checkout (if you did not -build the dependency bundle above, this will take a few moments to -download a pre-build version). +0. **_(Optional)_ Compile dependencies** -```bash -./meteor --help -``` + > This optional step requires a C and C++ compiler, autotools, and scons. + > If this step is skipped, Meteor will simply download pre-built binaries. -From your checkout, you can read the docs locally. The `/docs` directory is a -meteor application, so simply change into the `/docs` directory and launch -the app: + To build everything from scratch (`node`, `npm`, `mongodb`, etc.) run the following: -```bash -cd docs/ -../meteor -``` + ```sh + $ ./scripts/generate-dev-bundle.sh # OPTIONAL! + ``` -You'll then be able to read the docs locally in your browser at -`http://localhost:3000/`. +0. **Run a Meteor command to install dependencies** -Note that if you run Meteor from a git checkout, you cannot pin apps to specific -Meteor releases or run using different Meteor releases using `--release`. + > If you did not compile dependencies above, this will also download the binaries. + + + ```sh + $ ./meteor --help + ``` + +0. **Ready to Go!** + + Your local Meteor checkout is now ready to use! You can use this `./meteor` + anywhere you would normally call the system `meteor`. For example,: + + ```sh + $ cd my-app/ + $ /path/to/meteor-checkout/meteor run + ``` + + > _Note:_ When running from a `git` checkout, you cannot pin apps to specific + > Meteor releases or change the release using `--release`. ## Uninstalling Meteor @@ -91,6 +102,8 @@ rm -rf ~/.meteor/ sudo rm /usr/local/bin/meteor ``` +On Windows, just run the uninstaller from your Control Panel. + ## Developer Resources Building an application with Meteor? diff --git a/Roadmap.md b/Roadmap.md index 892fd7c6e3..94f3978295 100644 --- a/Roadmap.md +++ b/Roadmap.md @@ -1,42 +1,99 @@ -Meteor Roadmap -============== -This document describes the high level features the project has decided to prioritize in the near to medium term future. A large fraction of the MDG core engineering team's time will be dedicated to working on the features described here. -Contributors are encouraged to focus their efforts on work that aligns with the roadmap as maintainers will prioritize their time around these contributions. This however does not mean that PR's against other features and bugs will be automatically rejected. +# **Meteor Roadmap** -For the meantime, MDG won't be accepting PR's for changes to the roadmap. We hope to change this in the future as we figure out strategies to decentralize Meteor development. +**Last updated December 13, 2016** -## MongoDB updates +This document describes the high level features the Meteor project maintainers have decided to prioritize in the near- to medium-term future. A large fraction of the maintainers’ time will be dedicated to working on the features described here. As with any roadmap, this is a living document that will evolve as priorities and dependencies shift; we aim to update the roadmap with any changes or status updates on a monthly basis. -The mongo driver that currently ships with Meteor is old and doesn’t reliably work with connecting to MongoDB 3.2 databases (e.g [#6258](https://github.com/meteor/meteor/issues/6258)). We want to update to the latest driver [#5763](https://github.com/meteor/meteor/issues/5763). +Contributors are encouraged to focus their efforts on work that aligns with the roadmap as maintainers will prioritize their time spent reviewing PRs and issues around these contributions. This however does not mean that PRs against other features and bugs will automatically be rejected. -In addition, we'd like to update the dev bundle to ship with the latest stable version of MongoDB (3.2) [#5809](https://github.com/meteor/meteor/issues/5809) as MongoDB 2.6 will be officially sunsetted at the end of October, 2016. +Items can be added to this roadmap by first getting design approval for a solution to an open issue, as outlined by our [contributing guidelines](https://github.com/meteor/meteor/blob/devel/Contributing.md). Then, when a contributor has committed to solving the issue in the short to medium term, they can submit a PR to add that work to the roadmap. All other PRs to the roadmap will be rejected. -## Support for Node 4 and beyond -We want to be able to update the version of Node that ships with Meteor to 4 and eventually 6 [#5124](https://github.com/meteor/meteor/issues/5124). [#6537](https://github.com/meteor/meteor/issues/6537) lays the groundwork to overcome the main blocker for updating to Node 4, that is, needing to rebuild all existing Meteor packages that contain binary dependencies. +## Upgrade to Node 6 + +Tracking pull request: https://github.com/meteor/meteor/pull/6923 + + +## Page load performance improvements + +*Status: In progress* + +Fast initial page load times are somewhat less important for single-page reactive web apps than for other kinds of websites, but large Meteor apps with lots of packages tend to load quite a bit of JavaScript, and the cost of all that network traffic, parsing, and evaluation definitely adds up. + +Speeding up page load times will require a combination of new tools for asynchronous JavaScript delivery (code splitting), dead code elimination, deferred evaluation of JavaScript modules, and performance profiling (so that developers can identify expensive packages). + + +## Build system improvements + +*Status: In progress* + +Meteor 1.4.2 addressed a number of critical performance problems with the Meteor build system, but there are still more areas left for improvement. For example, Meteor could take better advantage of native support for new ECMAScript features in Node 4 (and eventually Node 6) on the server. + ## Full transition to npm +*Status: We encourage publishing Meteor-related packages to npm where possible. We are investigating ways to make it possible to move some parts of Meteor core onto npm without making the installation process more complex.* + The community has rallied around npm as the de-facto standard package manager for JavaScript. We believe committing fully to npm will strengthen the entire JavaScript ecosystem by removing fragmentation, will benefit existing Meteor developers by making it seamless to use any JavaScript package and increase Meteor adoption by aligning it with JavaScript best practices. 1.3 introduced `npm install` support along with ES2015 modules. In the future, we would like to transition the Meteor package ecosystem over entirely from Atmosphere to npm. We are still in the early conceptual design phase and expect to update the roadmap once we have a design in place that will underpin further development. -## Testing updates +## GraphQL Data: SQL, REST, performance -Meteor 1.3 shipped with two new testing modes and many developers are writing test suites and building test drivers as part of migrating to 1.3. We’re looking at improving the feature in upcoming releases, especially our support for continuous integration mode (see [#6755](https://github.com/meteor/meteor/issues/6755)). +*Status: In progress at [http://dev.apollodata.com/](http://dev.apollodata.com/)* -## Project Governance/Community Contribution +We're building a next generation GraphQL-focused data stack for modern applications called Apollo. We believe that this spiritual successor to the data part of the Meteor stack is impactful enough to deserve it's own fully fledged project that is compatible with other languages and frameworks. Apollo is born from the Meteor project and works perfectly with Meteor today. -Currently, it’s difficult for external developers to make meaningful contributions to Meteor as there is no clear guidance on what to work on, how best to do that work and signals around what will/won’t get merged. We plan on fixing this by creating tight documentation around how the project is developed (e.g the [Swift contribution guidelines](https://swift.org/contributing/)) and giving contributors a path towards earning increased privileges that culminate in full commit access. We’re also aiming to move more sub-projects into their own repositories that can be released on their own release schedule and more easily maintained by the community. +Apollo is our approach to giving Meteor developers SQL and other database support, the ability to choose between realtime and static data, and improved performance analysis. We are working on providing better tools and documentation for Meteor developers to integrate Apollo into their apps, and welcome contributions in this area. The next priority for Apollo and Meteor is enabling Meteor developers to choose to replace MongoDB entirely with GraphQL on top of other storage engines. -## Data (SQL, REST, Performance) +Even though Apollo could eventually be a complete replacement for Meteor’s included Mongo/DDP data stack, you should feel good about Meteor’s existing data system. We are currently open to ideas around performance and stability improvements. -We're building the next generation data stack for modern applications called [Apollo](https://github.com/apollostack/apollo). We believe the data part of the Meteor stack is impactful enough to deserve it's own fully fledged sub-project that will be compatible with other languages and frameworks. Apollo is born from the Meteor project and is naturally designed to work perfectly with Meteor. It is GraphQL based and will work with multiple data sources starting with REST. Apollo is our answer to bringing SQL and improved performance analysis to Meteor. + + +# **Recently completed** + + + +## Rebuild performance improvements + +*Status: Shipped in 1.4.2* + +Rebuild performance refers to the length of time between changing a file in development and being able to reload your app in a browser. After extensive profiling to identify performance hot-spots, and with careful caching of previously completed work, Meteor 1.4.2 takes substantially less time to rebuild most apps, especially larger apps. + + +## MongoDB updates + +*Status: Shipped in 1.4* + +The mongo driver that currently ships with Meteor is old and doesn’t reliably work with connecting to MongoDB 3.2 databases (e.g [#6258](https://github.com/meteor/meteor/issues/6258)). We want to update to the latest driver [#5763](https://github.com/meteor/meteor/issues/5763). +In addition, we'd like to update the dev bundle to ship with the latest stable version of MongoDB (3.2) [#5809](https://github.com/meteor/meteor/issues/5809) as MongoDB 2.6 will be officially sunsetted at the end of October, 2016. + + +## Support for Node 4 and beyond + +*Status: Shipped in 1.4* + +We want to be able to update the version of Node that ships with Meteor to 4 and eventually 6 [#5124](https://github.com/meteor/meteor/issues/5124). [#6537](https://github.com/meteor/meteor/issues/6537) lays the groundwork to overcome the main blocker for updating to Node 4, that is, needing to rebuild all existing Meteor packages that contain binary dependencies. ## View Layer +*Status: Blaze split into new repository and can be published independently as of 1.4.2* + Our plans around the view layer are to maintain strong integrations (along with guidance) with React, Angular and Blaze. We'll make essential fixes to Blaze but most likely won't be adding new features ourselves. We encourage you to help build the features you need at the [meteor/blaze](https://github.com/meteor/blaze) repository. + + +## Project Governance/Community Contribution + +*Status: Since this topic was added to the roadmap, we have introduced [completely new contribution guidelines](https://github.com/meteor/meteor/blob/devel/Contributing.md) that outline exactly how to contribute to Meteor in several ways, including triaging issues, improving documentation, submitting designs for new features, and submitting PRs for bug fixes and improvements. We encourage proposals about how to make the process better via new GitHub issues.* + +Currently, it’s difficult for external developers to make meaningful contributions to Meteor as there is no clear guidance on what to work on, how best to do that work and signals around what will/won’t get merged. We plan on fixing this by creating tight documentation around how the project is developed (e.g the [Swift contribution guidelines](https://swift.org/contributing/)) and giving contributors a path towards earning increased privileges that culminate in full commit access. We’re also aiming to move more sub-projects into their own repositories that can be released on their own release schedule and more easily maintained by the community. + + +## Other + +For more completed items, refer to the project history here: https://github.com/meteor/meteor/blob/devel/History.md + diff --git a/circle.yml b/circle.yml index f8fd56b333..393d52c371 100644 --- a/circle.yml +++ b/circle.yml @@ -1,3 +1,7 @@ +checkout: + post: + - git submodule update --init + dependencies: pre: # https://github.com/meteor/docs/blob/version-NEXT/long-form/file-change-watcher-efficiency.md @@ -7,7 +11,9 @@ dependencies: - ".meteor" - ".babel-cache" override: - - ./meteor --get-ready + # shouldn't take longer than 10 minutes + - METEOR_PRETTY_OUTPUT=0 ./meteor --get-ready: + timeout: 600 test: override: diff --git a/meteor b/meteor index 0ab07e0073..f260611874 100755 --- a/meteor +++ b/meteor @@ -1,6 +1,6 @@ #!/usr/bin/env bash -BUNDLE_VERSION=4.1.4 +BUNDLE_VERSION=4.7.5 # OS Check. Put here because here is where we download the precompiled # bundles that are arch specific. @@ -113,17 +113,17 @@ fi DEV_BUNDLE="$SCRIPT_DIR/dev_bundle" METEOR="$SCRIPT_DIR/tools/index.js" +# Set the nofile ulimit as high as permitted by the hard-limit/kernel +if [ "$(ulimit -Sn)" != "unlimited" ]; then + if [ "$(uname -s)" = "Darwin" ]; then + maxfilesuse="$(sysctl -n kern.maxfilesperproc)" + else + maxfilesuse="$(ulimit -Hn)" + fi -# 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 + if [ -n "${maxfilesuse}" ] && [ "${maxfilesuse}" != "unlimited" ]; then + ulimit -Sn ${maxfilesuse} > /dev/null 2>&1 + fi fi # We used to set $NODE_PATH here to include the node_modules from the dev diff --git a/packages/.gitignore b/packages/.gitignore index cf6595efb8..5a8f31f2d0 100644 --- a/packages/.gitignore +++ b/packages/.gitignore @@ -1,2 +1,2 @@ .meteor -*/.meteor \ No newline at end of file +*/.meteor diff --git a/packages/accounts-base/README.md b/packages/accounts-base/README.md index c3cfa43e25..1a94a410ef 100644 --- a/packages/accounts-base/README.md +++ b/packages/accounts-base/README.md @@ -1,4 +1,6 @@ # accounts-base +[Source code of released version](https://github.com/meteor/meteor/tree/master/packages/accounts-base) | [Source code of development version](https://github.com/meteor/meteor/tree/devel/packages/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: diff --git a/packages/accounts-base/accounts_client.js b/packages/accounts-base/accounts_client.js index a7e83902f5..b4c1debf29 100644 --- a/packages/accounts-base/accounts_client.js +++ b/packages/accounts-base/accounts_client.js @@ -191,7 +191,7 @@ Ap.callLoginMethod = function (options) { }); } else { self._onLoginFailureHook.each(function (callback) { - callback(); + callback({ error }); return true; }); } diff --git a/packages/accounts-base/accounts_common.js b/packages/accounts-base/accounts_common.js index 8282789cfa..e968ec2e5e 100644 --- a/packages/accounts-base/accounts_common.js +++ b/packages/accounts-base/accounts_common.js @@ -83,6 +83,9 @@ export class AccountsCommon { // - loginExpirationInDays {Number} // Number of days since login until a user is logged out (login token // expires). + // - passwordResetTokenExpirationInDays {Number} + // Number of days since password reset token creation until the + // token cannt be used any longer (password reset token expires). /** * @summary Set global accounts options. @@ -93,6 +96,8 @@ export class AccountsCommon { * @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. + * @param {Number} options.passwordResetTokenExpirationInDays The number of days from when a link to reset password is sent until token expires and user can't reset password with the link anymore. Defaults to 3. + * @param {Number} options.passwordEnrollTokenExpirationInDays The number of days from when a link to set inital password is sent until token expires and user can't set password with the link anymore. Defaults to 30. */ config(options) { var self = this; @@ -124,8 +129,8 @@ export class AccountsCommon { } // validate option keys - var VALID_KEYS = ["sendVerificationEmail", "forbidClientAccountCreation", - "restrictCreationByEmailDomain", "loginExpirationInDays"]; + var VALID_KEYS = ["sendVerificationEmail", "forbidClientAccountCreation", "passwordEnrollTokenExpirationInDays", + "restrictCreationByEmailDomain", "loginExpirationInDays", "passwordResetTokenExpirationInDays"]; _.each(_.keys(options), function (key) { if (!_.contains(VALID_KEYS, key)) { throw new Error("Accounts.config: Invalid key: " + key); @@ -208,6 +213,16 @@ export class AccountsCommon { DEFAULT_LOGIN_EXPIRATION_DAYS) * 24 * 60 * 60 * 1000; } + _getPasswordResetTokenLifetimeMs() { + return (this._options.passwordResetTokenExpirationInDays || + DEFAULT_PASSWORD_RESET_TOKEN_EXPIRATION_DAYS) * 24 * 60 * 60 * 1000; + } + + _getPasswordEnrollTokenLifetimeMs() { + return (this._options.passwordEnrollTokenExpirationInDays || + DEFAULT_PASSWORD_ENROLL_TOKEN_EXPIRATION_DAYS) * 24 * 60 * 60 * 1000; + } + _tokenExpiration(when) { // We pass when through the Date constructor for backwards compatibility; // `when` used to be a number. @@ -248,6 +263,10 @@ Meteor.user = function () { // how long (in days) until a login token expires var DEFAULT_LOGIN_EXPIRATION_DAYS = 90; +// how long (in days) until reset password token expires +var DEFAULT_PASSWORD_RESET_TOKEN_EXPIRATION_DAYS = 3; +// how long (in days) until enrol password token expires +var DEFAULT_PASSWORD_ENROLL_TOKEN_EXPIRATION_DAYS = 30; // 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. diff --git a/packages/accounts-base/accounts_server.js b/packages/accounts-base/accounts_server.js index b81dd40482..57ea4eb6e0 100644 --- a/packages/accounts-base/accounts_server.js +++ b/packages/accounts-base/accounts_server.js @@ -176,9 +176,10 @@ Ap._failedLogin = function (connection, attempt) { }); }; -Ap._successfulLogout = function () { +Ap._successfulLogout = function (connection, userId) { + const user = userId && this.users.findOne(userId); this._onLogoutHook.each(function (callback) { - callback(); + callback({ user, connection }); return true; }); }; @@ -537,8 +538,8 @@ Ap._initServerMethods = function () { accounts._setLoginToken(this.userId, this.connection, null); if (token && this.userId) accounts.destroyToken(this.userId, token); + accounts._successfulLogout(this.connection, this.userId); this.setUserId(null); - accounts._successfulLogout(); }; // Delete all the current user's tokens and close all open connections logged @@ -1074,6 +1075,21 @@ Ap._generateStampedLoginToken = function () { /// TOKEN EXPIRATION /// +function expirePasswordToken(accounts, oldestValidDate, tokenFilter, userId) { + var userFilter = userId ? {_id: userId} : {}; + + accounts.users.update(_.extend(userFilter, tokenFilter, { + $or: [ + { "services.password.reset.when": { $lt: oldestValidDate } }, + { "services.password.reset.when": { $lt: +oldestValidDate } } + ] + }), { + $unset: { + "services.password.reset": "" + } + }, { multi: true }); +} + // Deletes expired tokens from the database and closes all open connections // associated with these tokens. // @@ -1115,6 +1131,57 @@ Ap._expireTokens = function (oldestValidDate, userId) { // expired tokens. }; +// Deletes expired password reset tokens from the database. +// +// Exported for tests. Also, the arguments are only used by +// tests. oldestValidDate is simulate expiring tokens without waiting +// for them to actually expire. userId is used by tests to only expire +// tokens for the test user. +Ap._expirePasswordResetTokens = function (oldestValidDate, userId) { + var tokenLifetimeMs = this._getPasswordResetTokenLifetimeMs(); + + // when calling from a test with extra arguments, you must specify both! + if ((oldestValidDate && !userId) || (!oldestValidDate && userId)) { + throw new Error("Bad test. Must specify both oldestValidDate and userId."); + } + + oldestValidDate = oldestValidDate || + (new Date(new Date() - tokenLifetimeMs)); + + var tokenFilter = { + $or: [ + { "services.password.reset.reason": "reset"}, + { "services.password.reset.reason": {$exists: false}} + ] + }; + + expirePasswordToken(this, oldestValidDate, tokenFilter, userId); +} + +// Deletes expired password enroll tokens from the database. +// +// Exported for tests. Also, the arguments are only used by +// tests. oldestValidDate is simulate expiring tokens without waiting +// for them to actually expire. userId is used by tests to only expire +// tokens for the test user. +Ap._expirePasswordEnrollTokens = function (oldestValidDate, userId) { + var tokenLifetimeMs = this._getPasswordEnrollTokenLifetimeMs(); + + // when calling from a test with extra arguments, you must specify both! + if ((oldestValidDate && !userId) || (!oldestValidDate && userId)) { + throw new Error("Bad test. Must specify both oldestValidDate and userId."); + } + + oldestValidDate = oldestValidDate || + (new Date(new Date() - tokenLifetimeMs)); + + var tokenFilter = { + "services.password.reset.reason": "enroll" + }; + + expirePasswordToken(this, oldestValidDate, tokenFilter, userId); +} + // @override from accounts_common.js Ap.config = function (options) { // Call the overridden implementation of the method. @@ -1135,6 +1202,8 @@ Ap.config = function (options) { function setExpireTokensInterval(accounts) { accounts.expireTokenInterval = Meteor.setInterval(function () { accounts._expireTokens(); + accounts._expirePasswordResetTokens(); + accounts._expirePasswordEnrollTokens(); }, EXPIRE_TOKENS_INTERVAL_MS); } diff --git a/packages/accounts-base/accounts_tests.js b/packages/accounts-base/accounts_tests.js index bb8774b7b1..5857597221 100644 --- a/packages/accounts-base/accounts_tests.js +++ b/packages/accounts-base/accounts_tests.js @@ -388,6 +388,10 @@ Tinytest.add( var onLoginStopper = Accounts.onLogin(function(attempt) { test.equal(Meteor.userId(), onLoginExpectedUserId, "onLogin"); }); + var onLogoutStopper = Accounts.onLogout(function(logoutContext) { + test.equal(logoutContext.user._id, onLogoutExpectedUserId, "onLogout"); + test.instanceOf(logoutContext.connection, Object); + }); var onLoginFailureStopper = Accounts.onLoginFailure(function(attempt) { test.equal(Meteor.userId(), onLoginFailureExpectedUserId, "onLoginFailure"); }); @@ -408,9 +412,14 @@ Tinytest.add( var onLoginFailureExpectedUserId = userId; test.throws(function() { conn.call('login', { resume: "bogus" }) }, '403'); + // Trigger onLogout callbacks + var onLogoutExpectedUserId = userId; + conn.call('logout'); + conn.disconnect(); validateStopper.stop(); onLoginStopper.stop(); + onLogoutStopper.stop(); onLoginFailureStopper.stop(); } ); diff --git a/packages/accounts-base/package.js b/packages/accounts-base/package.js index a64b9a6611..5f33d2e408 100644 --- a/packages/accounts-base/package.js +++ b/packages/accounts-base/package.js @@ -1,6 +1,6 @@ Package.describe({ summary: "A user account system", - version: "1.2.9" + version: "1.2.14" }); Package.onUse(function (api) { diff --git a/packages/accounts-facebook/README.md b/packages/accounts-facebook/README.md index 67c49e430c..b4b54902a2 100644 --- a/packages/accounts-facebook/README.md +++ b/packages/accounts-facebook/README.md @@ -1,3 +1,5 @@ # accounts-facebook +[Source code of released version](https://github.com/meteor/meteor/tree/master/packages/accounts-facebook) | [Source code of development version](https://github.com/meteor/meteor/tree/devel/packages/accounts-facebook) +*** A login service for Facebook. See the [project page](https://www.meteor.com/accounts) on Meteor Accounts for more details. \ No newline at end of file diff --git a/packages/accounts-facebook/notice.js b/packages/accounts-facebook/notice.js new file mode 100644 index 0000000000..7532f84619 --- /dev/null +++ b/packages/accounts-facebook/notice.js @@ -0,0 +1,5 @@ +if (Package['accounts-ui'] && (Package['facebook-config-ui'] === undefined)) { + console.warn("Note: You're using accounts-ui and accounts-facebook, but didn't"); + console.warn("install the configuration UI for Facebook OAuth."); + console.warn("You can install it with `meteor add facebook-config-ui`."); +} diff --git a/packages/accounts-facebook/package.js b/packages/accounts-facebook/package.js index 80e0c52044..2ce5d5feb2 100644 --- a/packages/accounts-facebook/package.js +++ b/packages/accounts-facebook/package.js @@ -1,6 +1,6 @@ Package.describe({ summary: "Login service for Facebook accounts", - version: "1.0.10" + version: "1.1.0-beta.1" }); Package.onUse(function(api) { @@ -8,9 +8,12 @@ Package.onUse(function(api) { // Export Accounts (etc) to packages using this one. api.imply('accounts-base', ['client', 'server']); api.use('accounts-oauth', ['client', 'server']); - api.use('facebook', ['client', 'server']); + api.use('facebook-oauth'); + api.imply('facebook-oauth'); - api.addFiles('facebook_login_button.css', 'client'); + // If users use accounts-ui but not facebook-config-ui, give them a tip. + api.use(['accounts-ui', 'facebook-config-ui'], ['client', 'server'], { weak: true }); + api.addFiles("notice.js"); api.addFiles("facebook.js"); }); diff --git a/packages/accounts-github/README.md b/packages/accounts-github/README.md index fb1e200ab9..1e8d550aa1 100644 --- a/packages/accounts-github/README.md +++ b/packages/accounts-github/README.md @@ -1,3 +1,5 @@ # accounts-github +[Source code of released version](https://github.com/meteor/meteor/tree/master/packages/accounts-github) | [Source code of development version](https://github.com/meteor/meteor/tree/devel/packages/accounts-github) +*** A login service for GitHub. See the [project page](https://www.meteor.com/accounts) on Meteor Accounts for more details. \ No newline at end of file diff --git a/packages/accounts-github/package.js b/packages/accounts-github/package.js index 761e4921ba..7646ea1108 100644 --- a/packages/accounts-github/package.js +++ b/packages/accounts-github/package.js @@ -1,6 +1,6 @@ Package.describe({ summary: "Login service for Github accounts", - version: "1.0.10" + version: "1.0.11" }); Package.onUse(function(api) { diff --git a/packages/accounts-google/README.md b/packages/accounts-google/README.md index 8976bfcc2d..a7821adbe2 100644 --- a/packages/accounts-google/README.md +++ b/packages/accounts-google/README.md @@ -1,3 +1,5 @@ # accounts-google +[Source code of released version](https://github.com/meteor/meteor/tree/master/packages/accounts-google) | [Source code of development version](https://github.com/meteor/meteor/tree/devel/packages/accounts-google) +*** A login service for Google. See the [project page](https://www.meteor.com/accounts) on Meteor Accounts for more details. \ No newline at end of file diff --git a/packages/accounts-google/package.js b/packages/accounts-google/package.js index 474b051f8a..1f71d41315 100644 --- a/packages/accounts-google/package.js +++ b/packages/accounts-google/package.js @@ -1,6 +1,6 @@ Package.describe({ summary: "Login service for Google accounts", - version: "1.0.10" + version: "1.0.11" }); Package.onUse(function(api) { diff --git a/packages/accounts-meetup/README.md b/packages/accounts-meetup/README.md index 8b4928d31c..4a8c87f201 100644 --- a/packages/accounts-meetup/README.md +++ b/packages/accounts-meetup/README.md @@ -1,3 +1,5 @@ # accounts-meetup +[Source code of released version](https://github.com/meteor/meteor/tree/master/packages/accounts-meetup) | [Source code of development version](https://github.com/meteor/meteor/tree/devel/packages/accounts-meetup) +*** A login service for Meetup. See the [project page](https://www.meteor.com/accounts) on Meteor Accounts for more details. \ No newline at end of file diff --git a/packages/accounts-meetup/package.js b/packages/accounts-meetup/package.js index 5162c639ea..637ab50659 100644 --- a/packages/accounts-meetup/package.js +++ b/packages/accounts-meetup/package.js @@ -1,6 +1,6 @@ Package.describe({ summary: "Login service for Meetup accounts", - version: "1.0.10" + version: "1.0.11" }); Package.onUse(function(api) { diff --git a/packages/accounts-meteor-developer/README.md b/packages/accounts-meteor-developer/README.md index 1f01e02e97..a49afa5eb6 100644 --- a/packages/accounts-meteor-developer/README.md +++ b/packages/accounts-meteor-developer/README.md @@ -1,4 +1,6 @@ # accounts-meteor-developer +[Source code of released version](https://github.com/meteor/meteor/tree/master/packages/accounts-meteor-developer) | [Source code of development version](https://github.com/meteor/meteor/tree/devel/packages/accounts-meteor-developer) +*** A login service for Meteor developer accounts. See the project page on [Meteor Accounts](https://www.meteor.com/accounts) and Meteor [Developer diff --git a/packages/accounts-meteor-developer/package.js b/packages/accounts-meteor-developer/package.js index 63662662f7..d15392912e 100644 --- a/packages/accounts-meteor-developer/package.js +++ b/packages/accounts-meteor-developer/package.js @@ -1,6 +1,6 @@ Package.describe({ summary: "Login service for Meteor developer accounts", - version: "1.0.10" + version: "1.0.11" }); Package.onUse(function (api) { diff --git a/packages/accounts-oauth/README.md b/packages/accounts-oauth/README.md index 7c90af10b9..6cc54cb069 100644 --- a/packages/accounts-oauth/README.md +++ b/packages/accounts-oauth/README.md @@ -1,3 +1,5 @@ # accounts-oauth +[Source code of released version](https://github.com/meteor/meteor/tree/master/packages/accounts-oauth) | [Source code of development version](https://github.com/meteor/meteor/tree/devel/packages/accounts-oauth) +*** Common functionality for OAuth-based login services. See the [project page](https://www.meteor.com/accounts) on Meteor Accounts for more details. \ No newline at end of file diff --git a/packages/accounts-oauth/oauth_client.js b/packages/accounts-oauth/oauth_client.js index a419b2003a..7290158083 100644 --- a/packages/accounts-oauth/oauth_client.js +++ b/packages/accounts-oauth/oauth_client.js @@ -9,7 +9,7 @@ * @param {Object} [options] * @param {String[]} options.requestPermissions A list of permissions to request from the user. * @param {Boolean} options.requestOfflineToken If true, asks the user for permission to act on their behalf when offline. This stores an additional offline token in the `services` field of the user document. Currently only supported with Google. - * @param {Object} options.loginUrlParameters Provide additional parameters to the authentication uri. Currently only supported with Google {@url https://developers.google.com/identity/protocols/OpenIDConnect#authenticationuriparameters}. + * @param {Object} options.loginUrlParameters Provide additional parameters to the authentication URI. Currently only supported with Google. See [Google Identity Platform documentation](https://developers.google.com/identity/protocols/OpenIDConnect#authenticationuriparameters). * @param {String} options.loginHint An email address that the external service will use to pre-fill the login prompt. Currently only supported with Meteor developer accounts and Google accounts. If used with Google, the Google User ID can also be passed. * @param {String} options.loginStyle Login style ("popup" or "redirect", defaults to the login service configuration). The "popup" style opens the login page in a separate popup window, which is generally preferred because the Meteor application doesn't need to be reloaded. The "redirect" style redirects the Meteor application's window to the login page, and the login service provider redirects back to the Meteor application which is then reloaded. The "redirect" style can be used in situations where a popup window can't be opened, such as in a mobile UIWebView. The "redirect" style however relies on session storage which isn't available in Safari private mode, so the "popup" style will be forced if session storage can't be used. * @param {String} options.redirectUrl If using "redirect" login style, the user will be returned to this URL after authorisation has been completed. diff --git a/packages/accounts-oauth/package.js b/packages/accounts-oauth/package.js index c0830a3b4e..41e5f9d9e4 100644 --- a/packages/accounts-oauth/package.js +++ b/packages/accounts-oauth/package.js @@ -1,6 +1,6 @@ Package.describe({ summary: "Common code for OAuth-based login services", - version: "1.1.13" + version: "1.1.15" }); Package.onUse(function (api) { diff --git a/packages/accounts-password/README.md b/packages/accounts-password/README.md index dd0af41f38..9c1d88e4a0 100644 --- a/packages/accounts-password/README.md +++ b/packages/accounts-password/README.md @@ -1,3 +1,5 @@ # accounts-password +[Source code of released version](https://github.com/meteor/meteor/tree/master/packages/accounts-password) | [Source code of development version](https://github.com/meteor/meteor/tree/devel/packages/accounts-password) +*** A login service that enables secure password-based login. See the [project page](https://www.meteor.com/accounts) on Meteor Accounts for more details. diff --git a/packages/accounts-password/email_templates.js b/packages/accounts-password/email_templates.js index ce517c38d3..a2116998c8 100644 --- a/packages/accounts-password/email_templates.js +++ b/packages/accounts-password/email_templates.js @@ -26,18 +26,7 @@ Accounts.emailTemplates = { subject: function(user) { return "How to reset your password on " + Accounts.emailTemplates.siteName; }, - text: function(user, url) { - var greeting = (user.profile && user.profile.name) ? - ("Hello " + user.profile.name + ",") : "Hello,"; - return `${greeting} - -To reset your password, simply click the link below. - -${url} - -Thanks. -`; - } + text: greet("To reset your password") }, verifyEmail: { subject: function(user) { diff --git a/packages/accounts-password/package.js b/packages/accounts-password/package.js index 01ea4278df..d9cf68e575 100644 --- a/packages/accounts-password/package.js +++ b/packages/accounts-password/package.js @@ -1,10 +1,10 @@ Package.describe({ summary: "Password support for accounts", - version: "1.2.12" + version: "1.3.3" }); Package.onUse(function(api) { - api.use('npm-bcrypt@=0.8.7'); + api.use('npm-bcrypt', 'server'); api.use([ 'accounts-base', diff --git a/packages/accounts-password/password_client.js b/packages/accounts-password/password_client.js index 83f1161984..28e2787c4f 100644 --- a/packages/accounts-password/password_client.js +++ b/packages/accounts-password/password_client.js @@ -1,3 +1,12 @@ +// Used in the various functions below to handle errors consistently +function reportError(error, callback) { + if (callback) { + callback(error); + } else { + throw error; + } +}; + // Attempt to log in with a password. // // @param selector {String|Object} One of the following: @@ -55,7 +64,7 @@ Meteor.loginWithPassword = function (selector, password, callback) { }, callback); } else if (error) { - callback && callback(error); + reportError(error, callback); } else { callback && callback(); } @@ -83,9 +92,9 @@ var srpUpgradePath = function (options, callback) { details = EJSON.parse(options.upgradeError.details); } catch (e) {} if (!(details && details.format === 'srp')) { - callback && callback( + reportError( new Meteor.Error(400, "Password is old. Please reset your " + - "password.")); + "password."), callback); } else { Accounts.callLoginMethod({ methodArguments: [{ @@ -98,7 +107,6 @@ var srpUpgradePath = function (options, callback) { } }; - // Attempt to log in as a new user. /** @@ -118,8 +126,7 @@ Accounts.createUser = function (options, callback) { if (typeof options.password !== 'string') throw new Error("options.password must be a string"); if (!options.password) { - callback(new Meteor.Error(400, "Password may not be empty")); - return; + return reportError(new Meteor.Error(400, "Password may not be empty"), callback); } // Replace password with the hashed password. @@ -150,14 +157,12 @@ Accounts.createUser = function (options, callback) { */ Accounts.changePassword = function (oldPassword, newPassword, callback) { if (!Meteor.user()) { - callback && callback(new Error("Must be logged in to change password.")); - return; + return reportError(new Error("Must be logged in to change password."), callback); } check(newPassword, String); if (!newPassword) { - callback(new Meteor.Error(400, "Password may not be empty")); - return; + return reportError(new Meteor.Error(400, "Password may not be empty"), callback); } Accounts.connection.apply( @@ -177,7 +182,7 @@ Accounts.changePassword = function (oldPassword, newPassword, callback) { plaintextPassword: oldPassword }, function (err) { if (err) { - callback && callback(err); + reportError(err, callback); } else { // Now that we've successfully migrated from srp to // bcrypt, try changing the password again. @@ -186,8 +191,8 @@ Accounts.changePassword = function (oldPassword, newPassword, callback) { }); } else { // A normal error, not an error telling us to upgrade to bcrypt - callback && callback( - error || new Error("No result from changePassword.")); + reportError( + error || new Error("No result from changePassword."), callback); } } else { callback && callback(); @@ -212,9 +217,15 @@ Accounts.changePassword = function (oldPassword, newPassword, callback) { * @importFromPackage accounts-base */ Accounts.forgotPassword = function(options, callback) { - if (!options.email) - throw new Error("Must pass options.email"); - Accounts.connection.call("forgotPassword", options, callback); + if (!options.email) { + return reportError(new Meteor.Error(400, "Must pass options.email"), callback); + } + + if (callback) { + Accounts.connection.call("forgotPassword", options, callback); + } else { + Accounts.connection.call("forgotPassword", options); + } }; // Resets a password based on a token originally created by @@ -237,8 +248,7 @@ Accounts.resetPassword = function(token, newPassword, callback) { check(newPassword, String); if (!newPassword) { - callback(new Meteor.Error(400, "Password may not be empty")); - return; + return reportError(new Meteor.Error(400, "Password may not be empty"), callback); } Accounts.callLoginMethod({ @@ -261,8 +271,9 @@ Accounts.resetPassword = function(token, newPassword, callback) { * @importFromPackage accounts-base */ Accounts.verifyEmail = function(token, callback) { - if (!token) - throw new Error("Need to pass token"); + if (!token) { + return reportError(new Meteor.Error(400, "Need to pass token"), callback); + } Accounts.callLoginMethod({ methodName: 'verifyEmail', diff --git a/packages/accounts-password/password_server.js b/packages/accounts-password/password_server.js index 0262386488..7333493c73 100644 --- a/packages/accounts-password/password_server.js +++ b/packages/accounts-password/password_server.js @@ -543,7 +543,8 @@ Accounts.sendResetPasswordEmail = function (userId, email) { var tokenRecord = { token: token, email: email, - when: when + when: when, + reason: 'reset' }; Meteor.users.update(userId, {$set: { "services.password.reset": tokenRecord @@ -611,7 +612,8 @@ Accounts.sendEnrollmentEmail = function (userId, email) { var tokenRecord = { token: token, email: email, - when: when + when: when, + reason: 'enroll' }; Meteor.users.update(userId, {$set: { "services.password.reset": tokenRecord @@ -664,6 +666,15 @@ Meteor.methods({resetPassword: function (token, newPassword) { "services.password.reset.token": token}); if (!user) throw new Meteor.Error(403, "Token expired"); + var when = user.services.password.reset.when; + var reason = user.services.password.reset.reason; + var tokenLifetimeMs = Accounts._getPasswordResetTokenLifetimeMs(); + if (reason === "enroll") { + tokenLifetimeMs = Accounts._getPasswordEnrollTokenLifetimeMs(); + } + var currentTimeMs = Date.now(); + if ((currentTimeMs - when) > tokenLifetimeMs) + throw new Meteor.Error(403, "Token expired"); var email = user.services.password.reset.email; if (!_.include(_.pluck(user.emails || [], 'address'), email)) return { diff --git a/packages/accounts-password/password_tests.js b/packages/accounts-password/password_tests.js index b6f831d6f6..3a3e564f21 100644 --- a/packages/accounts-password/password_tests.js +++ b/packages/accounts-password/password_tests.js @@ -2,7 +2,7 @@ Accounts._noConnectionCloseDelayForTest = true; if (Meteor.isServer) { Accounts.removeDefaultRateLimit(); - + Meteor.methods({ getResetToken: function () { var token = Meteor.users.findOne(this.userId).services.password.reset; @@ -410,6 +410,13 @@ if (Meteor.isClient) (function () { "email", [ createUserStep, logoutStep, + // Create user error without callback should throw error + function (test, expect) { + this.newUsername = 'adalovelace' + this.randomSuffix; + test.throws(function(){ + Accounts.createUser({ username: this.newUsername, password: '' }); + }, /Password may not be empty/); + }, // Attempting to create another user with an email that only differs in // case should fail function (test, expect) { @@ -468,6 +475,12 @@ if (Meteor.isClient) (function () { test.equal(Meteor.user().username, self.username); })); }, + // change password with blank new password + function (test, expect) { + test.throws(function(){ + Accounts.changePassword(this.password, ''); + }, /Password may not be empty/); + }, // change password with good old password. function (test, expect) { Accounts.changePassword(this.password, this.password2, @@ -546,6 +559,109 @@ if (Meteor.isClient) (function () { ]); + testAsyncMulti("passwords - forgotPassword client return error when empty email", [ + function (test, expect) { + // setup + this.email = ''; + }, + // forgotPassword called on client with blank email + function (test, expect) { + Accounts.forgotPassword( + { email: this.email }, expect(function (error) { + test.isTrue(error); + })); + }, + // forgotPassword called on client with blank email and no callback. + function (test, expect) { + test.throws(function(){ + Accounts.forgotPassword({ email: this.email }); + }, /Must pass options\.email/); + }, + ]); + + Tinytest.add( + 'passwords - forgotPassword only passes callback value to forgotPassword ' + + 'Method if callback is defined (to address issue #5676)', + function (test) { + let methodCallArgumentCount = 0; + const originalMethodCall = Accounts.connection.call; + const stubMethodCall = (...args) => { + methodCallArgumentCount = args.length; + } + Accounts.connection.call = stubMethodCall; + + Accounts.forgotPassword({ email: 'test@meteor.com' }); + test.equal( + methodCallArgumentCount, + 2, + 'Method call should have 2 arguments since no callback is passed in' + ); + + Accounts.forgotPassword({ email: 'test@meteor.com' }, () => {}); + test.equal( + methodCallArgumentCount, + 3, + 'Method call should have 3 arguments since a callback is passed in' + ); + + Accounts.connection.call = originalMethodCall; + } + ); + + testAsyncMulti("passwords - verifyEmail client return error when empty token", [ + function (test, expect) { + // setup + this.token = ''; + }, + // verifyEmail called on client with blank token + function (test, expect) { + Accounts.verifyEmail( + this.token, expect(function (error) { + test.isTrue(error); + })); + }, + // verifyEmail called on client with blank token and no callback. + function (test, expect) { + test.throws(function(){ + Accounts.verifyEmail(this.token); + }, /Need to pass token/); + }, + ]); + + testAsyncMulti("passwords - resetPassword errors", [ + function (test, expect) { + // setup + this.token = ''; + this.newPassword = 'nonblankpassword'; + }, + // resetPassword called on client with blank token + function (test, expect) { + Accounts.resetPassword( + this.token, this.newPassword, expect(function (error) { + test.isTrue(error); + })); + }, + function (test, expect) { + // setup + this.token = 'nonblank-token'; + this.newPassword = ''; + }, + // resetPassword called on client with blank password + function (test, expect) { + Accounts.resetPassword( + this.token, this.newPassword, expect(function (error) { + test.isTrue(error); + })); + }, + // resetPassword called on client with blank password and no callback. + function (test, expect) { + test.throws(function(){ + Accounts.resetPassword(this.token, this.newPassword); + }, /Match error: Expected string, got undefined/); + }, + ]); + + testAsyncMulti("passwords - new user hooks", [ function (test, expect) { // setup @@ -1392,6 +1508,195 @@ if (Meteor.isServer) (function () { }, /Incorrect password/); }); + Tinytest.addAsync( + 'passwords - reset password should work when token is not expired', + function (test, onComplete) { + var username = Random.id(); + var email = username + '-intercept@example.com'; + + var userId = Accounts.createUser({ + username: username, + email: email, + password: "old-password" + }); + + var user = Meteor.users.findOne(userId); + + Accounts.sendResetPasswordEmail(userId, email); + + var resetPasswordEmailOptions = + Meteor.call("getInterceptedEmails", email)[0]; + + var re = new RegExp(Meteor.absoluteUrl() + "#/reset-password/(\\S*)"); + var match = resetPasswordEmailOptions.text.match(re); + test.isTrue(match); + var resetPasswordToken = match[1]; + + makeTestConnection( + test, + function (clientConn) { + test.isTrue(clientConn.call( + "resetPassword", + resetPasswordToken, + "new-password" + )); + + test.isTrue(clientConn.call("login", { + user: { username }, + password: "new-password" + })); + + onComplete(); + }); + }); + + Tinytest.add( + 'passwords - reset password should not work when token is expired', + function (test) { + var username = Random.id(); + var email = username + '-intercept@example.com'; + + var userId = Accounts.createUser({ + username: username, + email: email, + password: "old-password" + }); + + var user = Meteor.users.findOne(userId); + + Accounts.sendResetPasswordEmail(userId, email); + + var resetPasswordEmailOptions = + Meteor.call("getInterceptedEmails", email)[0]; + + var re = new RegExp(Meteor.absoluteUrl() + "#/reset-password/(\\S*)"); + var match = resetPasswordEmailOptions.text.match(re); + test.isTrue(match); + var resetPasswordToken = match[1]; + + Meteor.users.update(userId, {$set: {"services.password.reset.when": new Date(Date.now() + -5 * 24 * 3600 * 1000) }}); + + test.throws(function () { + Meteor.call("resetPassword", resetPasswordToken, "new-password"); + }, /Token expired/); + test.throws(function () { + Meteor.call("login", {user: {username: username}, password: "new-password"}); + }, /Incorrect password/); + }); + + Tinytest.add( + 'passwords - reset tokens with reasons get cleaned up', + function (test) { + var email = test.id + '-intercept@example.com'; + var userId = Accounts.createUser({email: email, password: 'password'}); + Accounts.sendResetPasswordEmail(userId, email); + test.isTrue(!!Meteor.users.findOne(userId).services.password.reset); + + Accounts._expirePasswordResetTokens(new Date(), userId); + + test.isUndefined(Meteor.users.findOne(userId).services.password.reset); + }); + + Tinytest.add( + 'passwords - reset tokens without reasons get cleaned up', + function (test) { + var email = test.id + '-intercept@example.com'; + var userId = Accounts.createUser({email: email, password: 'password'}); + Accounts.sendResetPasswordEmail(userId, email); + Meteor.users.update({_id: userId}, {$unset: {"services.password.reset.reason": 1}}); + test.isTrue(!!Meteor.users.findOne(userId).services.password.reset); + test.isUndefined(Meteor.users.findOne(userId).services.password.reset.reason); + + Accounts._expirePasswordResetTokens(new Date(), userId); + + test.isUndefined(Meteor.users.findOne(userId).services.password.reset); + }); + + Tinytest.addAsync( + 'passwords - enroll password should work when token is not expired', + function (test, onComplete) { + var username = Random.id(); + var email = username + '-intercept@example.com'; + + var userId = Accounts.createUser({ + username: username, + email: email + }); + + var user = Meteor.users.findOne(userId); + + Accounts.sendEnrollmentEmail(userId, email); + + var enrollPasswordEmailOptions = + Meteor.call("getInterceptedEmails", email)[0]; + + var re = new RegExp(Meteor.absoluteUrl() + "#/enroll-account/(\\S*)"); + var match = enrollPasswordEmailOptions.text.match(re); + test.isTrue(match); + var enrollPasswordToken = match[1]; + + makeTestConnection( + test, + function (clientConn) { + test.isTrue(clientConn.call( + "resetPassword", + enrollPasswordToken, + "new-password" + )); + + test.isTrue(clientConn.call("login", { + user: { username }, + password: "new-password" + })); + + onComplete(); + }); + }); + + Tinytest.add( + 'passwords - enroll password should not work when token is expired', + function (test) { + var username = Random.id(); + var email = username + '-intercept@example.com'; + + var userId = Accounts.createUser({ + username: username, + email: email + }); + + var user = Meteor.users.findOne(userId); + + Accounts.sendEnrollmentEmail(userId, email); + + var enrollPasswordEmailOptions = + Meteor.call("getInterceptedEmails", email)[0]; + + var re = new RegExp(Meteor.absoluteUrl() + "#/enroll-account/(\\S*)"); + var match = enrollPasswordEmailOptions.text.match(re); + test.isTrue(match); + var enrollPasswordToken = match[1]; + + Meteor.users.update(userId, {$set: {"services.password.reset.when": new Date(Date.now() + -35 * 24 * 3600 * 1000) }}); + + test.throws(function () { + Meteor.call("resetPassword", enrollPasswordToken, "new-password"); + }, /Token expired/); + }); + + Tinytest.add( + 'passwords - enroll tokens get cleaned up', + function (test) { + var email = test.id + '-intercept@example.com'; + var userId = Accounts.createUser({email: email, password: 'password'}); + Accounts.sendEnrollmentEmail(userId, email); + test.isTrue(!!Meteor.users.findOne(userId).services.password.reset); + + Accounts._expirePasswordEnrollTokens(new Date(), userId); + + test.isUndefined(Meteor.users.findOne(userId).services.password.reset); + } + ) + // We should be able to change the username Tinytest.add("passwords - change username", function (test) { var username = Random.id(); diff --git a/packages/accounts-twitter/README.md b/packages/accounts-twitter/README.md index cd8c70e11c..3f8ac0cdc5 100644 --- a/packages/accounts-twitter/README.md +++ b/packages/accounts-twitter/README.md @@ -1,3 +1,5 @@ # accounts-twitter +[Source code of released version](https://github.com/meteor/meteor/tree/master/packages/accounts-twitter) | [Source code of development version](https://github.com/meteor/meteor/tree/devel/packages/accounts-twitter) +*** A login service for Twitter. See the [project page](https://www.meteor.com/accounts) on Meteor Accounts for more details. diff --git a/packages/accounts-twitter/package.js b/packages/accounts-twitter/package.js index 63bb4aa397..82ca9e64c8 100644 --- a/packages/accounts-twitter/package.js +++ b/packages/accounts-twitter/package.js @@ -1,6 +1,6 @@ Package.describe({ summary: "Login service for Twitter accounts", - version: "1.1.11" + version: "1.1.12" }); Package.onUse(function(api) { diff --git a/packages/accounts-ui-unstyled/README.md b/packages/accounts-ui-unstyled/README.md index afefda9352..e5f2dec21d 100644 --- a/packages/accounts-ui-unstyled/README.md +++ b/packages/accounts-ui-unstyled/README.md @@ -1,4 +1,6 @@ # accounts-ui-unstyled +[Source code of released version](https://github.com/meteor/meteor/tree/master/packages/accounts-ui-unstyled) | [Source code of development version](https://github.com/meteor/meteor/tree/devel/packages/accounts-ui-unstyled) +*** A version of `accounts-ui` without the CSS, so that you can add your own styling. See the [`accounts-ui` diff --git a/packages/accounts-ui-unstyled/login_buttons_dialogs.js b/packages/accounts-ui-unstyled/login_buttons_dialogs.js index 9f6de4261a..d1c5473c3d 100644 --- a/packages/accounts-ui-unstyled/login_buttons_dialogs.js +++ b/packages/accounts-ui-unstyled/login_buttons_dialogs.js @@ -237,8 +237,8 @@ var updateSaveDisabled = function () { // Returns the appropriate template for this login service. This // template should be defined in the service's package -var configureLoginServiceDialogTemplateForService = function () { - var serviceName = loginButtonsSession.get('configureLoginServiceDialogServiceName'); +Template._configureLoginServiceDialog.templateForService = function(serviceName) { + serviceName = serviceName || loginButtonsSession.get('configureLoginServiceDialogServiceName'); // XXX Service providers should be able to specify their configuration // template name. return Template['configureLoginServiceDialogFor' + @@ -248,7 +248,7 @@ var configureLoginServiceDialogTemplateForService = function () { }; var configurationFields = function () { - var template = configureLoginServiceDialogTemplateForService(); + var template = Template._configureLoginServiceDialog.templateForService(); return template.fields(); }; @@ -261,7 +261,7 @@ Template._configureLoginServiceDialog.helpers({ }, configurationSteps: function () { // renders the appropriate template - return configureLoginServiceDialogTemplateForService(); + return Template._configureLoginServiceDialog.templateForService(); }, saveDisabled: function () { return loginButtonsSession.get('configureLoginServiceDialogSaveDisabled'); diff --git a/packages/accounts-ui-unstyled/login_buttons_single.html b/packages/accounts-ui-unstyled/login_buttons_single.html index e83d484116..d3a13d5236 100644 --- a/packages/accounts-ui-unstyled/login_buttons_single.html +++ b/packages/accounts-ui-unstyled/login_buttons_single.html @@ -2,11 +2,15 @@ @@ -26,4 +30,3 @@ - diff --git a/packages/accounts-ui-unstyled/login_buttons_single.js b/packages/accounts-ui-unstyled/login_buttons_single.js index e7a97d36f3..3749a8f3c4 100644 --- a/packages/accounts-ui-unstyled/login_buttons_single.js +++ b/packages/accounts-ui-unstyled/login_buttons_single.js @@ -8,7 +8,15 @@ var loginResultCallback = function (serviceName, err) { } else if (err instanceof Accounts.LoginCancelledError) { // do nothing } else if (err instanceof ServiceConfiguration.ConfigError) { - loginButtonsSession.configureService(serviceName); + if (Template._configureLoginServiceDialog.templateForService(serviceName)) { + loginButtonsSession.configureService(serviceName); + } else { + loginButtonsSession.errorMessage( + "No configuration for " + capitalize(serviceName) + ".\n" + + "Use `ServiceConfiguration` to configure it or " + + "install the `" +serviceName + "-config-ui` package." + ); + } } else { loginButtonsSession.errorMessage(err.reason || "Unknown error"); } @@ -55,6 +63,11 @@ Template._loginButtonsLoggedOutSingleLoginButton.events({ }); Template._loginButtonsLoggedOutSingleLoginButton.helpers({ + // not configured and has no config UI + cannotConfigure: function() { + return !ServiceConfiguration.configurations.findOne({service: this.name}) + && !Template._configureLoginServiceDialog.templateForService(this.name); + }, configured: function () { return !!ServiceConfiguration.configurations.findOne({service: this.name}); }, diff --git a/packages/accounts-ui-unstyled/package.js b/packages/accounts-ui-unstyled/package.js index 6ee319ea71..9171eb192b 100644 --- a/packages/accounts-ui-unstyled/package.js +++ b/packages/accounts-ui-unstyled/package.js @@ -1,11 +1,11 @@ Package.describe({ summary: "Unstyled version of login widgets", - version: "1.1.12" + version: "1.2.0-beta.1" }); Package.onUse(function (api) { api.use(['tracker', 'service-configuration', 'accounts-base', - 'underscore', 'templating', 'session', 'jquery'], 'client'); + 'underscore', 'templating@1.2.13', 'session', 'jquery'], 'client'); // Export Accounts (etc) to packages using this one. api.imply('accounts-base', ['client', 'server']); diff --git a/packages/accounts-ui/README.md b/packages/accounts-ui/README.md index fd90096d81..95bf5d63c8 100644 --- a/packages/accounts-ui/README.md +++ b/packages/accounts-ui/README.md @@ -1,4 +1,6 @@ # accounts-ui +[Source code of released version](https://github.com/meteor/meteor/tree/master/packages/accounts-ui) | [Source code of development version](https://github.com/meteor/meteor/tree/devel/packages/accounts-ui) +*** A turn-key user interface for Meteor Accounts. diff --git a/packages/accounts-weibo/README.md b/packages/accounts-weibo/README.md index 217e5ce76a..8ff27cd47f 100644 --- a/packages/accounts-weibo/README.md +++ b/packages/accounts-weibo/README.md @@ -1,3 +1,5 @@ # accounts-weibo +[Source code of released version](https://github.com/meteor/meteor/tree/master/packages/accounts-weibo) | [Source code of development version](https://github.com/meteor/meteor/tree/devel/packages/accounts-weibo) +*** A login service for Weibo. See the [project page](https://www.meteor.com/accounts) on Meteor Accounts for more details. \ No newline at end of file diff --git a/packages/accounts-weibo/package.js b/packages/accounts-weibo/package.js index 7ece3606fc..830c9a5748 100644 --- a/packages/accounts-weibo/package.js +++ b/packages/accounts-weibo/package.js @@ -1,6 +1,6 @@ Package.describe({ summary: "Login service for Sina Weibo accounts", - version: "1.0.10" + version: "1.0.11" }); Package.onUse(function(api) { diff --git a/packages/allow-deny/README.md b/packages/allow-deny/README.md index e69de29bb2..6783b449d6 100644 --- a/packages/allow-deny/README.md +++ b/packages/allow-deny/README.md @@ -0,0 +1,4 @@ +# allow-deny +[Source code of released version](https://github.com/meteor/meteor/tree/master/packages/allow-deny) | [Source code of development version](https://github.com/meteor/meteor/tree/devel/packages/allow-deny) +*** + diff --git a/packages/appcache/README.md b/packages/appcache/README.md index 342db9ef40..e74d791bff 100644 --- a/packages/appcache/README.md +++ b/packages/appcache/README.md @@ -1,4 +1,6 @@ # appcache +[Source code of released version](https://github.com/meteor/meteor/tree/master/packages/appcache) | [Source code of development version](https://github.com/meteor/meteor/tree/devel/packages/appcache) +*** The `appcache` package, part of [Webapp](https://www.meteor.com/webapp), stores the static parts of a diff --git a/packages/appcache/package.js b/packages/appcache/package.js index bc53628346..78c0652a10 100644 --- a/packages/appcache/package.js +++ b/packages/appcache/package.js @@ -1,6 +1,6 @@ Package.describe({ summary: "Enable the application cache in the browser", - version: "1.0.11" + version: "1.0.12" }); Package.onUse(function (api) { diff --git a/packages/audit-argument-checks/README.md b/packages/audit-argument-checks/README.md index e26fc2ada7..b5b9fefb0c 100644 --- a/packages/audit-argument-checks/README.md +++ b/packages/audit-argument-checks/README.md @@ -1,3 +1,5 @@ # Audit-Argument-Checks +[Source code of released version](https://github.com/meteor/meteor/tree/master/packages/audit-argument-checks) | [Source code of development version](https://github.com/meteor/meteor/tree/devel/packages/audit-argument-checks) +*** This is an internal Meteor package. \ No newline at end of file diff --git a/packages/autopublish/README.md b/packages/autopublish/README.md index 0dc6c7afa4..d26a14f8e1 100644 --- a/packages/autopublish/README.md +++ b/packages/autopublish/README.md @@ -1,4 +1,6 @@ # autopublish +[Source code of released version](https://github.com/meteor/meteor/tree/master/packages/autopublish) | [Source code of development version](https://github.com/meteor/meteor/tree/devel/packages/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. diff --git a/packages/autoupdate/README.md b/packages/autoupdate/README.md index 87e4354676..26b7c0c2a4 100644 --- a/packages/autoupdate/README.md +++ b/packages/autoupdate/README.md @@ -1,4 +1,6 @@ # autoupdate +[Source code of released version](https://github.com/meteor/meteor/tree/master/packages/autoupdate) | [Source code of development version](https://github.com/meteor/meteor/tree/devel/packages/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 diff --git a/packages/autoupdate/package.js b/packages/autoupdate/package.js index c4b13bde2d..642f3c2c1c 100644 --- a/packages/autoupdate/package.js +++ b/packages/autoupdate/package.js @@ -1,6 +1,6 @@ Package.describe({ summary: "Update the client when new client code is available", - version: '1.3.11' + version: '1.3.12' }); Package.onUse(function (api) { diff --git a/packages/babel-compiler/.npm/package/npm-shrinkwrap.json b/packages/babel-compiler/.npm/package/npm-shrinkwrap.json index 7ca8edd8db..21ecc62663 100644 --- a/packages/babel-compiler/.npm/package/npm-shrinkwrap.json +++ b/packages/babel-compiler/.npm/package/npm-shrinkwrap.json @@ -1,14 +1,14 @@ { "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" + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz", + "from": "acorn@>=3.3.0 <3.4.0" }, - "amdefine": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.0.tgz", - "from": "amdefine@>=0.0.4" + "acorn-es7-plugin": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/acorn-es7-plugin/-/acorn-es7-plugin-1.1.3.tgz", + "from": "acorn-es7-plugin@>=1.1.0 <1.2.0" }, "ansi-regex": { "version": "2.0.0", @@ -21,74 +21,74 @@ "from": "ansi-styles@>=2.2.1 <3.0.0" }, "ast-types": { - "version": "0.8.17", - "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.8.17.tgz", - "from": "ast-types@>=0.8.16 <0.9.0" + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.9.2.tgz", + "from": "ast-types@>=0.9.0 <0.10.0" }, "babel-code-frame": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.11.0.tgz", - "from": "babel-code-frame@>=6.8.0 <7.0.0" + "version": "6.16.0", + "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.16.0.tgz", + "from": "babel-code-frame@>=6.16.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" + "version": "6.18.2", + "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-6.18.2.tgz", + "from": "babel-core@>=6.18.2 <7.0.0" }, "babel-generator": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/babel-generator/-/babel-generator-6.11.0.tgz", - "from": "babel-generator@>=6.9.0 <7.0.0" + "version": "6.19.0", + "resolved": "https://registry.npmjs.org/babel-generator/-/babel-generator-6.19.0.tgz", + "from": "babel-generator@>=6.18.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", + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/babel-helper-builder-react-jsx/-/babel-helper-builder-react-jsx-6.18.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" + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/babel-helper-call-delegate/-/babel-helper-call-delegate-6.18.0.tgz", + "from": "babel-helper-call-delegate@>=6.18.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" + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/babel-helper-define-map/-/babel-helper-define-map-6.18.0.tgz", + "from": "babel-helper-define-map@>=6.18.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" + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/babel-helper-function-name/-/babel-helper-function-name-6.18.0.tgz", + "from": "babel-helper-function-name@>=6.18.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" + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/babel-helper-get-function-arity/-/babel-helper-get-function-arity-6.18.0.tgz", + "from": "babel-helper-get-function-arity@>=6.18.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" + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/babel-helper-hoist-variables/-/babel-helper-hoist-variables-6.18.0.tgz", + "from": "babel-helper-hoist-variables@>=6.18.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" + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/babel-helper-optimise-call-expression/-/babel-helper-optimise-call-expression-6.18.0.tgz", + "from": "babel-helper-optimise-call-expression@>=6.18.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", + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/babel-helper-regex/-/babel-helper-regex-6.18.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" + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/babel-helper-replace-supers/-/babel-helper-replace-supers-6.18.0.tgz", + "from": "babel-helper-replace-supers@>=6.18.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" + "version": "6.16.0", + "resolved": "https://registry.npmjs.org/babel-helpers/-/babel-helpers-6.16.0.tgz", + "from": "babel-helpers@>=6.16.0 <7.0.0" }, "babel-messages": { "version": "6.8.0", @@ -101,34 +101,34 @@ "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" + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-async-functions/-/babel-plugin-syntax-async-functions-6.13.0.tgz", + "from": "babel-plugin-syntax-async-functions@>=6.13.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" + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-async-generators/-/babel-plugin-syntax-async-generators-6.13.0.tgz", + "from": "babel-plugin-syntax-async-generators@>=6.13.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" + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-flow/-/babel-plugin-syntax-flow-6.18.0.tgz", + "from": "babel-plugin-syntax-flow@>=6.13.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", + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.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" + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-object-rest-spread/-/babel-plugin-syntax-object-rest-spread-6.13.0.tgz", + "from": "babel-plugin-syntax-object-rest-spread@>=6.13.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" + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-6.13.0.tgz", + "from": "babel-plugin-syntax-trailing-function-commas@>=6.13.0 <7.0.0" }, "babel-plugin-transform-es2015-arrow-functions": { "version": "6.8.0", @@ -141,14 +141,14 @@ "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" + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-block-scoping/-/babel-plugin-transform-es2015-block-scoping-6.18.0.tgz", + "from": "babel-plugin-transform-es2015-block-scoping@>=6.15.0 <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" + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-classes/-/babel-plugin-transform-es2015-classes-6.18.0.tgz", + "from": "babel-plugin-transform-es2015-classes@>=6.14.0 <7.0.0" }, "babel-plugin-transform-es2015-computed-properties": { "version": "6.8.0", @@ -156,29 +156,24 @@ "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" + "version": "6.19.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-destructuring/-/babel-plugin-transform-es2015-destructuring-6.19.0.tgz", + "from": "babel-plugin-transform-es2015-destructuring@>=6.16.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", + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-for-of/-/babel-plugin-transform-es2015-for-of-6.18.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" + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.18.0.tgz", + "from": "babel-plugin-transform-es2015-modules-commonjs@>=6.18.0 <7.0.0" }, "babel-plugin-transform-es2015-object-super": { "version": "6.8.0", @@ -186,13 +181,13 @@ "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" + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-parameters/-/babel-plugin-transform-es2015-parameters-6.18.0.tgz", + "from": "babel-plugin-transform-es2015-parameters@>=6.17.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", + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-shorthand-properties/-/babel-plugin-transform-es2015-shorthand-properties-6.18.0.tgz", "from": "babel-plugin-transform-es2015-shorthand-properties@>=6.8.0 <7.0.0" }, "babel-plugin-transform-es2015-spread": { @@ -211,19 +206,14 @@ "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", + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-typeof-symbol/-/babel-plugin-transform-es2015-typeof-symbol-6.18.0.tgz", "from": "babel-plugin-transform-es2015-typeof-symbol@>=6.8.0 <7.0.0" }, "babel-plugin-transform-es2015-unicode-regex": { "version": "6.11.0", "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-unicode-regex/-/babel-plugin-transform-es2015-unicode-regex-6.11.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" + "from": "babel-plugin-transform-es2015-unicode-regex@>=6.11.0 <7.0.0" }, "babel-plugin-transform-es3-property-literals": { "version": "6.8.0", @@ -231,14 +221,14 @@ "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" + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-flow-strip-types/-/babel-plugin-transform-flow-strip-types-6.18.0.tgz", + "from": "babel-plugin-transform-flow-strip-types@>=6.14.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" + "version": "6.19.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-object-rest-spread/-/babel-plugin-transform-object-rest-spread-6.19.0.tgz", + "from": "babel-plugin-transform-object-rest-spread@>=6.16.0 <7.0.0" }, "babel-plugin-transform-react-display-name": { "version": "6.8.0", @@ -250,74 +240,79 @@ "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-self": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-react-jsx-self/-/babel-plugin-transform-react-jsx-self-6.11.0.tgz", + "from": "babel-plugin-transform-react-jsx-self@>=6.11.0 <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" + "version": "6.16.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-regenerator/-/babel-plugin-transform-regenerator-6.16.1.tgz", + "from": "babel-plugin-transform-regenerator@>=6.16.1 <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" + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-runtime/-/babel-plugin-transform-runtime-6.15.0.tgz", + "from": "babel-plugin-transform-runtime@>=6.15.0 <7.0.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" + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-strict-mode/-/babel-plugin-transform-strict-mode-6.18.0.tgz", + "from": "babel-plugin-transform-strict-mode@>=6.18.0 <7.0.0" }, "babel-preset-meteor": { - "version": "6.11.1", - "resolved": "https://registry.npmjs.org/babel-preset-meteor/-/babel-preset-meteor-6.11.1.tgz", - "from": "babel-preset-meteor@6.11.1" + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/babel-preset-meteor/-/babel-preset-meteor-6.14.0.tgz", + "from": "babel-preset-meteor@6.14.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" + "version": "6.16.0", + "resolved": "https://registry.npmjs.org/babel-preset-react/-/babel-preset-react-6.16.0.tgz", + "from": "babel-preset-react@>=6.16.0 <7.0.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" + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/babel-register/-/babel-register-6.18.0.tgz", + "from": "babel-register@>=6.18.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" + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.18.0.tgz", + "from": "babel-runtime@>=6.18.0 <7.0.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" + "version": "6.16.0", + "resolved": "https://registry.npmjs.org/babel-template/-/babel-template-6.16.0.tgz", + "from": "babel-template@>=6.16.0 <7.0.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" + "version": "6.19.0", + "resolved": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.19.0.tgz", + "from": "babel-traverse@>=6.18.0 <7.0.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" + "version": "6.19.0", + "resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.19.0.tgz", + "from": "babel-types@>=6.18.0 <7.0.0" }, "babylon": { - "version": "6.8.1", - "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.8.1.tgz", - "from": "babylon@6.8.1" + "version": "6.14.1", + "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.14.1.tgz", + "from": "babylon@>=6.13.1 <7.0.0" }, "balanced-match": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.1.tgz", + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.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", + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.6.tgz", "from": "brace-expansion@>=1.0.0 <2.0.0" }, "chalk": { @@ -331,24 +326,24 @@ "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" + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.3.0.tgz", + "from": "convert-source-map@>=1.3.0 <2.0.0" }, "core-js": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.4.0.tgz", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.4.1.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", + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.3.3.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" + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-4.0.0.tgz", + "from": "detect-indent@>=4.0.0 <5.0.0" }, "escape-string-regexp": { "version": "1.0.5", @@ -360,15 +355,10 @@ "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" + "version": "9.14.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-9.14.0.tgz", + "from": "globals@>=9.0.0 <10.0.0" }, "has-ansi": { "version": "2.0.0", @@ -376,18 +366,18 @@ "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" + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/home-or-tmp/-/home-or-tmp-2.0.0.tgz", + "from": "home-or-tmp@>=2.0.0 <3.0.0" }, "invariant": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.1.tgz", + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.2.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", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz", "from": "is-finite@>=1.0.0 <2.0.0" }, "js-tokens": { @@ -396,41 +386,34 @@ "from": "js-tokens@>=2.0.0 <3.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" + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-1.3.0.tgz", + "from": "jsesc@>=1.3.0 <2.0.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" + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", + "from": "json5@>=0.5.0 <0.6.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" + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.2.tgz", + "from": "lodash@>=4.16.4 <5.0.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", - "dependencies": { - "js-tokens": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-1.0.3.tgz", - "from": "js-tokens@>=1.0.1 <2.0.0" - } - } + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.3.0.tgz", + "from": "loose-envify@>=1.0.0 <2.0.0" }, "magic-string": { - "version": "0.15.2", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.15.2.tgz", - "from": "magic-string@>=0.15.0 <0.16.0" + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.16.0.tgz", + "from": "magic-string@>=0.16.0 <0.17.0" }, "meteor-babel": { - "version": "0.11.7", - "resolved": "https://registry.npmjs.org/meteor-babel/-/meteor-babel-0.11.7.tgz", - "from": "meteor-babel@0.11.7" + "version": "0.14.3", + "resolved": "https://registry.npmjs.org/meteor-babel/-/meteor-babel-0.14.3.tgz", + "from": "meteor-babel@0.14.3" }, "meteor-babel-helpers": { "version": "0.0.3", @@ -438,50 +421,43 @@ "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" + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.3.tgz", + "from": "minimatch@>=3.0.2 <4.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" + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "from": "minimist@0.0.8" }, "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" - } - } + "from": "mkdirp@>=0.5.1 <0.6.0" }, "ms": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz", - "from": "ms@0.7.1" + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz", + "from": "ms@0.7.2" }, "number-is-nan": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.0.tgz", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", "from": "number-is-nan@>=1.0.0 <2.0.0" }, + "os-homedir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "from": "os-homedir@>=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", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.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", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "from": "path-is-absolute@>=1.0.0 <2.0.0" }, "private": { @@ -490,13 +466,13 @@ "from": "private@>=0.1.6 <0.2.0" }, "regenerate": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.3.1.tgz", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.3.2.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", + "version": "0.9.6", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.9.6.tgz", "from": "regenerator-runtime@>=0.9.5 <0.10.0" }, "regexpu-core": { @@ -512,22 +488,24 @@ "regjsparser": { "version": "0.1.5", "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.1.5.tgz", - "from": "regjsparser@>=0.1.4 <0.2.0" + "from": "regjsparser@>=0.1.4 <0.2.0", + "dependencies": { + "jsesc": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "from": "jsesc@>=0.5.0 <0.6.0" + } + } }, "reify": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/reify/-/reify-0.3.6.tgz", - "from": "reify@>=0.3.6 <0.4.0" + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/reify/-/reify-0.4.4.tgz", + "from": "reify@>=0.4.0 <0.5.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" + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", + "from": "repeating@>=2.0.0 <3.0.0" }, "slash": { "version": "1.0.0", @@ -540,16 +518,9 @@ "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" - } - } + "version": "0.4.6", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.6.tgz", + "from": "source-map-support@>=0.4.2 <0.5.0" }, "strip-ansi": { "version": "3.0.1", @@ -566,11 +537,6 @@ "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", diff --git a/packages/babel-compiler/README.md b/packages/babel-compiler/README.md index 84a248dd60..cf7031e241 100644 --- a/packages/babel-compiler/README.md +++ b/packages/babel-compiler/README.md @@ -1,3 +1,7 @@ +# babel-compiler +[Source code of released version](https://github.com/meteor/meteor/tree/master/packages/babel-compiler) | [Source code of development version](https://github.com/meteor/meteor/tree/devel/packages/babel-compiler) +*** + [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. diff --git a/packages/babel-compiler/babel-compiler.js b/packages/babel-compiler/babel-compiler.js index 485ecb8aa9..97169aa21a 100644 --- a/packages/babel-compiler/babel-compiler.js +++ b/packages/babel-compiler/babel-compiler.js @@ -64,16 +64,13 @@ BCp.processOneFileForTarget = function (inputFile, source) { // 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; + extraFeatures.jscript = true; } var babelOptions = Babel.getDefaultOptions(extraFeatures); diff --git a/packages/babel-compiler/babel.js b/packages/babel-compiler/babel.js index 8212945501..fc20af1d6e 100644 --- a/packages/babel-compiler/babel.js +++ b/packages/babel-compiler/babel.js @@ -1,9 +1,9 @@ -var meteorBabel = Npm.require('meteor-babel'); - /** * Returns a new object containing default options appropriate for */ function getDefaultOptions(extraFeatures) { + var meteorBabel = Npm.require('meteor-babel'); + // See https://github.com/meteor/babel/blob/master/options.js for more // information about what the default options are. var options = meteorBabel.getDefaultOptions(extraFeatures); @@ -22,11 +22,12 @@ Babel = { validateExtraFeatures: Function.prototype, compile: function (source, options) { + var meteorBabel = Npm.require('meteor-babel'); options = options || getDefaultOptions(); return meteorBabel.compile(source, options); }, setCacheDir: function (cacheDir) { - meteorBabel.setCacheDir(cacheDir); + Npm.require('meteor-babel').setCacheDir(cacheDir); } }; diff --git a/packages/babel-compiler/package.js b/packages/babel-compiler/package.js index 1a6c4306d2..7ea36403c4 100644 --- a/packages/babel-compiler/package.js +++ b/packages/babel-compiler/package.js @@ -6,11 +6,11 @@ Package.describe({ // 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.9.0' + version: '6.14.0-beta.1' }); Npm.depends({ - 'meteor-babel': '0.11.7', + 'meteor-babel': '0.14.3' }); Package.onUse(function (api) { diff --git a/packages/babel-runtime/.npm/package/npm-shrinkwrap.json b/packages/babel-runtime/.npm/package/npm-shrinkwrap.json index f37acb615f..bfa3d317c7 100644 --- a/packages/babel-runtime/.npm/package/npm-shrinkwrap.json +++ b/packages/babel-runtime/.npm/package/npm-shrinkwrap.json @@ -3,12 +3,7 @@ "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" + "from": "meteor-babel-helpers@0.0.3" } } } diff --git a/packages/babel-runtime/README.md b/packages/babel-runtime/README.md index fea7b52648..26fab8e202 100644 --- a/packages/babel-runtime/README.md +++ b/packages/babel-runtime/README.md @@ -1,4 +1,6 @@ # babel-runtime +[Source code of released version](https://github.com/meteor/meteor/tree/master/packages/babel-runtime) | [Source code of development version](https://github.com/meteor/meteor/tree/devel/packages/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 diff --git a/packages/babel-runtime/babel-runtime.js b/packages/babel-runtime/babel-runtime.js index c0314c6fec..d1e41db490 100644 --- a/packages/babel-runtime/babel-runtime.js +++ b/packages/babel-runtime/babel-runtime.js @@ -1,380 +1,34 @@ -var hasOwn = Object.prototype.hasOwnProperty; -var S = typeof Symbol === "function" ? Symbol : {}; -var iteratorSymbol = S.iterator || "@@iterator"; +exports.meteorBabelHelpers = require("meteor-babel-helpers"); -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 +// Returns true if a given absolute identifier will be provided at runtime +// by the babel-runtime package. +exports.checkHelper = function checkHelper(id) { + // There used to be more complicated logic here, when the babel-runtime + // package provided helper implementations of its own, but now this + // function exists just for backwards compatibility. + return false; }; -// Use meteorInstall to install all of the above helper functions within -// node_modules/babel-runtime/helpers. -Object.keys(BabelRuntime).forEach(function (helperName) { - var helpers = {}; +try { + var regeneratorRuntime = require("babel-runtime/regenerator"); +} catch (e) { + throw new Error([ + "The babel-runtime npm package could not be found in your node_modules ", + "directory. Please run the following command to install it:", + "", + " meteor npm install --save babel-runtime", + "" + ].join("\n")); +} - helpers[helperName + ".js"] = function (require, exports, module) { - module.exports = BabelRuntime[helperName]; +if (regeneratorRuntime && + typeof Promise === "function" && + typeof Promise.asyncApply === "function") { + // 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. + var realAsync = regeneratorRuntime.async; + regeneratorRuntime.async = function () { + return Promise.asyncApply(realAsync, regeneratorRuntime, arguments); }; - - 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; - } - } - } -}); +} diff --git a/packages/babel-runtime/package.js b/packages/babel-runtime/package.js index 8f68486e18..62287ce5d1 100644 --- a/packages/babel-runtime/package.js +++ b/packages/babel-runtime/package.js @@ -1,12 +1,11 @@ Package.describe({ name: "babel-runtime", summary: "Runtime support for output of Babel transpiler", - version: '0.1.10', + version: '1.0.1', documentation: 'README.md' }); Npm.depends({ - "regenerator-runtime": "0.9.5", "meteor-babel-helpers": "0.0.3" }); @@ -17,6 +16,6 @@ Package.onUse(function (api) { api.use("es5-shim", { weak: true }); api.use("modules"); api.use("promise"); // Needed by Regenerator. - api.addFiles("babel-runtime.js"); + api.mainModule("babel-runtime.js"); api.export("meteorBabelHelpers"); }); diff --git a/packages/base64/README.md b/packages/base64/README.md index ec37b267c6..38fe8224ad 100644 --- a/packages/base64/README.md +++ b/packages/base64/README.md @@ -1 +1,5 @@ +# base64 +[Source code of released version](https://github.com/meteor/meteor/tree/master/packages/base64) | [Source code of development version](https://github.com/meteor/meteor/tree/devel/packages/base64) +*** + This is an internal Meteor package. \ No newline at end of file diff --git a/packages/base64/package.js b/packages/base64/package.js index 0cddfdd739..7d4742aea2 100644 --- a/packages/base64/package.js +++ b/packages/base64/package.js @@ -1,6 +1,6 @@ Package.describe({ summary: "Base64 encoding and decoding", - version: '1.0.9' + version: '1.0.10' }); Package.onUse(function (api) { diff --git a/packages/binary-heap/README.md b/packages/binary-heap/README.md index ec37b267c6..cc4d8be6a4 100644 --- a/packages/binary-heap/README.md +++ b/packages/binary-heap/README.md @@ -1 +1,5 @@ +# binary-heap +[Source code of released version](https://github.com/meteor/meteor/tree/master/packages/binary-heap) | [Source code of development version](https://github.com/meteor/meteor/tree/devel/packages/binary-heap) +*** + This is an internal Meteor package. \ No newline at end of file diff --git a/packages/binary-heap/package.js b/packages/binary-heap/package.js index 99c59d67f9..262bb06acd 100644 --- a/packages/binary-heap/package.js +++ b/packages/binary-heap/package.js @@ -1,6 +1,6 @@ Package.describe({ summary: "Binary Heap datastructure implementation", - version: '1.0.9' + version: '1.0.10' }); Package.onUse(function (api) { diff --git a/packages/blaze-html-templates/README.md b/packages/blaze-html-templates/README.md deleted file mode 100644 index 84ad905655..0000000000 --- a/packages/blaze-html-templates/README.md +++ /dev/null @@ -1,9 +0,0 @@ -# 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 diff --git a/packages/blaze-html-templates/package.js b/packages/blaze-html-templates/package.js deleted file mode 100644 index edc01346bc..0000000000 --- a/packages/blaze-html-templates/package.js +++ /dev/null @@ -1,25 +0,0 @@ -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' - ]); -}); diff --git a/packages/blaze-tools/README.md b/packages/blaze-tools/README.md deleted file mode 100644 index 4b92b835aa..0000000000 --- a/packages/blaze-tools/README.md +++ /dev/null @@ -1,4 +0,0 @@ -# blaze-tools - -Compile-time utilities that are likely to be useful to any package -that compiles templates for Blaze. diff --git a/packages/blaze-tools/package.js b/packages/blaze-tools/package.js deleted file mode 100644 index c6c90f3071..0000000000 --- a/packages/blaze-tools/package.js +++ /dev/null @@ -1,24 +0,0 @@ -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']); -}); diff --git a/packages/blaze-tools/preamble.js b/packages/blaze-tools/preamble.js deleted file mode 100644 index 2211e646ab..0000000000 --- a/packages/blaze-tools/preamble.js +++ /dev/null @@ -1 +0,0 @@ -BlazeTools = {}; diff --git a/packages/blaze-tools/tojs.js b/packages/blaze-tools/tojs.js deleted file mode 100644 index 730dc24d54..0000000000 --- a/packages/blaze-tools/tojs.js +++ /dev/null @@ -1,156 +0,0 @@ - -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 for `\u2028\u2029`. - // Also escape Unicode surrogates. - return (JSON.stringify(obj) - .replace(/[\u2028\u2029\ud800-\udfff]/g, function (c) { - return '\\u' + ('000' + c.charCodeAt(0).toString(16)).slice(-4); - })); -}; -BlazeTools.toJSLiteral = toJSLiteral; - - - -var jsReservedWordSet = (function (set) { - _.each("abstract else instanceof super boolean enum int switch break export interface synchronized byte extends let this case false long throw catch final native throws char finally new transient class float null true const for package try continue function private typeof debugger goto protected var default if public void delete implements return volatile do import short while double in static with".split(' '), function (w) { - set[w] = 1; - }); - return set; -})({}); - -toObjectLiteralKey = function (k) { - if (/^[a-zA-Z$_][a-zA-Z$0-9_]*$/.test(k) && jsReservedWordSet[k] !== 1) - return k; - return toJSLiteral(k); -}; -BlazeTools.toObjectLiteralKey = toObjectLiteralKey; - -var hasToJS = function (x) { - return x.toJS && (typeof (x.toJS) === 'function'); -}; - -ToJSVisitor = HTML.Visitor.extend(); -ToJSVisitor.def({ - visitNull: function (nullOrUndefined) { - return 'null'; - }, - visitPrimitive: function (stringBooleanOrNumber) { - return toJSLiteral(stringBooleanOrNumber); - }, - visitArray: function (array) { - var parts = []; - for (var i = 0; i < array.length; i++) - parts.push(this.visit(array[i])); - return '[' + parts.join(', ') + ']'; - }, - visitTag: function (tag) { - return this.generateCall(tag.tagName, tag.attrs, tag.children); - }, - visitComment: function (comment) { - return this.generateCall('HTML.Comment', null, [comment.value]); - }, - visitCharRef: function (charRef) { - return this.generateCall('HTML.CharRef', - {html: charRef.html, str: charRef.str}); - }, - visitRaw: function (raw) { - return this.generateCall('HTML.Raw', null, [raw.value]); - }, - visitObject: function (x) { - if (hasToJS(x)) { - return x.toJS(this); - } - - throw new Error("Unexpected object in HTMLjs in toJS: " + x); - }, - generateCall: function (name, attrs, children) { - var tagSymbol; - if (name.indexOf('.') >= 0) { - tagSymbol = name; - } else if (HTML.isTagEnsured(name)) { - tagSymbol = 'HTML.' + HTML.getSymbolName(name); - } else { - tagSymbol = 'HTML.getTag(' + toJSLiteral(name) + ')'; - } - - var attrsArray = null; - if (attrs) { - attrsArray = []; - var needsHTMLAttrs = false; - if (HTML.isArray(attrs)) { - var attrsArray = []; - for (var i = 0; i < attrs.length; i++) { - var a = attrs[i]; - if (hasToJS(a)) { - attrsArray.push(a.toJS(this)); - needsHTMLAttrs = true; - } else { - var attrsObjStr = this.generateAttrsDictionary(attrs[i]); - if (attrsObjStr !== null) - attrsArray.push(attrsObjStr); - } - } - } else if (hasToJS(attrs)) { - attrsArray.push(attrs.toJS(this)); - needsHTMLAttrs = true; - } else { - attrsArray.push(this.generateAttrsDictionary(attrs)); - } - } - var attrsStr = null; - if (attrsArray && attrsArray.length) { - if (attrsArray.length === 1 && ! needsHTMLAttrs) { - attrsStr = attrsArray[0]; - } else { - attrsStr = 'HTML.Attrs(' + attrsArray.join(', ') + ')'; - } - } - - var argStrs = []; - if (attrsStr !== null) - argStrs.push(attrsStr); - - if (children) { - for (var i = 0; i < children.length; i++) - argStrs.push(this.visit(children[i])); - } - - return tagSymbol + '(' + argStrs.join(', ') + ')'; - }, - generateAttrsDictionary: function (attrsDict) { - if (attrsDict.toJS && (typeof (attrsDict.toJS) === 'function')) { - // not an attrs dictionary, but something else! Like a template tag. - return attrsDict.toJS(this); - } - - var kvStrs = []; - for (var k in attrsDict) { - if (! HTML.isNully(attrsDict[k])) - kvStrs.push(toObjectLiteralKey(k) + ': ' + - this.visit(attrsDict[k])); - } - if (kvStrs.length) - return '{' + kvStrs.join(', ') + '}'; - return null; - } -}); -BlazeTools.ToJSVisitor = ToJSVisitor; - -BlazeTools.toJS = function (content) { - return (new ToJSVisitor).visit(content); -}; diff --git a/packages/blaze-tools/token_tests.js b/packages/blaze-tools/token_tests.js deleted file mode 100644 index 91aa8bd369..0000000000 --- a/packages/blaze-tools/token_tests.js +++ /dev/null @@ -1,78 +0,0 @@ -Tinytest.add("blaze-tools - token parsers", function (test) { - - var run = function (func, input, expected) { - var scanner = new HTMLTools.Scanner('z' + input); - // make sure the parse function respects `scanner.pos` - scanner.pos = 1; - var result = func(scanner); - if (expected === null) { - test.equal(scanner.pos, 1); - test.equal(result, null); - } else { - test.isTrue(scanner.isEOF()); - test.equal(result, expected); - } - }; - - var runValue = function (func, input, expectedValue) { - var expected; - if (expectedValue === null) - expected = null; - else - expected = { text: input, value: expectedValue }; - run(func, input, expected); - }; - - var parseNumber = BlazeTools.parseNumber; - var parseIdentifierName = BlazeTools.parseIdentifierName; - var parseExtendedIdentifierName = BlazeTools.parseExtendedIdentifierName; - var parseStringLiteral = BlazeTools.parseStringLiteral; - - runValue(parseNumber, "0", 0); - runValue(parseNumber, "-0", 0); - runValue(parseNumber, "-", null); - runValue(parseNumber, ".a", null); - runValue(parseNumber, ".1", 0.1); - runValue(parseNumber, "1.", 1); - runValue(parseNumber, "1.1", 1.1); - runValue(parseNumber, "0x", null); - runValue(parseNumber, "0xa", 10); - runValue(parseNumber, "-0xa", -10); - runValue(parseNumber, "1e+1", 10); - - _.each([parseIdentifierName, parseExtendedIdentifierName], function (f) { - run(f, "a", "a"); - run(f, "true", "true"); - run(f, "null", "null"); - run(f, "if", "if"); - run(f, "1", null); - run(f, "1a", null); - run(f, "+a", null); - run(f, "a1", "a1"); - run(f, "a1a", "a1a"); - run(f, "_a8f_f8d88_", "_a8f_f8d88_"); - }); - run(parseIdentifierName, "@index", null); - run(parseExtendedIdentifierName, "@index", "@index"); - run(parseExtendedIdentifierName, "@something", "@something"); - run(parseExtendedIdentifierName, "@", null); - - runValue(parseStringLiteral, '"a"', 'a'); - runValue(parseStringLiteral, '"\'"', "'"); - runValue(parseStringLiteral, '\'"\'', '"'); - runValue(parseStringLiteral, '"a\\\nb"', 'ab'); // line continuation - runValue(parseStringLiteral, '"a\u0062c"', 'abc'); - // Note: IE 8 doesn't correctly parse '\v' in JavaScript. - runValue(parseStringLiteral, '"\\0\\b\\f\\n\\r\\t\\v"', '\0\b\f\n\r\t\u000b'); - runValue(parseStringLiteral, '"\\x41"', 'A'); - runValue(parseStringLiteral, '"\\\\"', '\\'); - runValue(parseStringLiteral, '"\\\""', '\"'); - runValue(parseStringLiteral, '"\\\'"', '\''); - runValue(parseStringLiteral, "'\\\\'", '\\'); - runValue(parseStringLiteral, "'\\\"'", '\"'); - runValue(parseStringLiteral, "'\\\''", '\''); - - test.throws(function () { - run(parseStringLiteral, "'this is my string"); - }, /Unterminated string literal/); -}); diff --git a/packages/blaze-tools/tokens.js b/packages/blaze-tools/tokens.js deleted file mode 100644 index 441d070fb4..0000000000 --- a/packages/blaze-tools/tokens.js +++ /dev/null @@ -1,193 +0,0 @@ - -// Adapted from source code of http://xregexp.com/plugins/#unicode -var unicodeCategories = { - Ll: "0061-007A00B500DF-00F600F8-00FF01010103010501070109010B010D010F01110113011501170119011B011D011F01210123012501270129012B012D012F01310133013501370138013A013C013E014001420144014601480149014B014D014F01510153015501570159015B015D015F01610163016501670169016B016D016F0171017301750177017A017C017E-0180018301850188018C018D019201950199-019B019E01A101A301A501A801AA01AB01AD01B001B401B601B901BA01BD-01BF01C601C901CC01CE01D001D201D401D601D801DA01DC01DD01DF01E101E301E501E701E901EB01ED01EF01F001F301F501F901FB01FD01FF02010203020502070209020B020D020F02110213021502170219021B021D021F02210223022502270229022B022D022F02310233-0239023C023F0240024202470249024B024D024F-02930295-02AF037103730377037B-037D039003AC-03CE03D003D103D5-03D703D903DB03DD03DF03E103E303E503E703E903EB03ED03EF-03F303F503F803FB03FC0430-045F04610463046504670469046B046D046F04710473047504770479047B047D047F0481048B048D048F04910493049504970499049B049D049F04A104A304A504A704A904AB04AD04AF04B104B304B504B704B904BB04BD04BF04C204C404C604C804CA04CC04CE04CF04D104D304D504D704D904DB04DD04DF04E104E304E504E704E904EB04ED04EF04F104F304F504F704F904FB04FD04FF05010503050505070509050B050D050F05110513051505170519051B051D051F05210523052505270561-05871D00-1D2B1D6B-1D771D79-1D9A1E011E031E051E071E091E0B1E0D1E0F1E111E131E151E171E191E1B1E1D1E1F1E211E231E251E271E291E2B1E2D1E2F1E311E331E351E371E391E3B1E3D1E3F1E411E431E451E471E491E4B1E4D1E4F1E511E531E551E571E591E5B1E5D1E5F1E611E631E651E671E691E6B1E6D1E6F1E711E731E751E771E791E7B1E7D1E7F1E811E831E851E871E891E8B1E8D1E8F1E911E931E95-1E9D1E9F1EA11EA31EA51EA71EA91EAB1EAD1EAF1EB11EB31EB51EB71EB91EBB1EBD1EBF1EC11EC31EC51EC71EC91ECB1ECD1ECF1ED11ED31ED51ED71ED91EDB1EDD1EDF1EE11EE31EE51EE71EE91EEB1EED1EEF1EF11EF31EF51EF71EF91EFB1EFD1EFF-1F071F10-1F151F20-1F271F30-1F371F40-1F451F50-1F571F60-1F671F70-1F7D1F80-1F871F90-1F971FA0-1FA71FB0-1FB41FB61FB71FBE1FC2-1FC41FC61FC71FD0-1FD31FD61FD71FE0-1FE71FF2-1FF41FF61FF7210A210E210F2113212F21342139213C213D2146-2149214E21842C30-2C5E2C612C652C662C682C6A2C6C2C712C732C742C76-2C7B2C812C832C852C872C892C8B2C8D2C8F2C912C932C952C972C992C9B2C9D2C9F2CA12CA32CA52CA72CA92CAB2CAD2CAF2CB12CB32CB52CB72CB92CBB2CBD2CBF2CC12CC32CC52CC72CC92CCB2CCD2CCF2CD12CD32CD52CD72CD92CDB2CDD2CDF2CE12CE32CE42CEC2CEE2CF32D00-2D252D272D2DA641A643A645A647A649A64BA64DA64FA651A653A655A657A659A65BA65DA65FA661A663A665A667A669A66BA66DA681A683A685A687A689A68BA68DA68FA691A693A695A697A723A725A727A729A72BA72DA72F-A731A733A735A737A739A73BA73DA73FA741A743A745A747A749A74BA74DA74FA751A753A755A757A759A75BA75DA75FA761A763A765A767A769A76BA76DA76FA771-A778A77AA77CA77FA781A783A785A787A78CA78EA791A793A7A1A7A3A7A5A7A7A7A9A7FAFB00-FB06FB13-FB17FF41-FF5A", - Lm: "02B0-02C102C6-02D102E0-02E402EC02EE0374037A0559064006E506E607F407F507FA081A0824082809710E460EC610FC17D718431AA71C78-1C7D1D2C-1D6A1D781D9B-1DBF2071207F2090-209C2C7C2C7D2D6F2E2F30053031-3035303B309D309E30FC-30FEA015A4F8-A4FDA60CA67FA717-A71FA770A788A7F8A7F9A9CFAA70AADDAAF3AAF4FF70FF9EFF9F", - Lo: "00AA00BA01BB01C0-01C3029405D0-05EA05F0-05F20620-063F0641-064A066E066F0671-06D306D506EE06EF06FA-06FC06FF07100712-072F074D-07A507B107CA-07EA0800-08150840-085808A008A2-08AC0904-0939093D09500958-09610972-09770979-097F0985-098C098F09900993-09A809AA-09B009B209B6-09B909BD09CE09DC09DD09DF-09E109F009F10A05-0A0A0A0F0A100A13-0A280A2A-0A300A320A330A350A360A380A390A59-0A5C0A5E0A72-0A740A85-0A8D0A8F-0A910A93-0AA80AAA-0AB00AB20AB30AB5-0AB90ABD0AD00AE00AE10B05-0B0C0B0F0B100B13-0B280B2A-0B300B320B330B35-0B390B3D0B5C0B5D0B5F-0B610B710B830B85-0B8A0B8E-0B900B92-0B950B990B9A0B9C0B9E0B9F0BA30BA40BA8-0BAA0BAE-0BB90BD00C05-0C0C0C0E-0C100C12-0C280C2A-0C330C35-0C390C3D0C580C590C600C610C85-0C8C0C8E-0C900C92-0CA80CAA-0CB30CB5-0CB90CBD0CDE0CE00CE10CF10CF20D05-0D0C0D0E-0D100D12-0D3A0D3D0D4E0D600D610D7A-0D7F0D85-0D960D9A-0DB10DB3-0DBB0DBD0DC0-0DC60E01-0E300E320E330E40-0E450E810E820E840E870E880E8A0E8D0E94-0E970E99-0E9F0EA1-0EA30EA50EA70EAA0EAB0EAD-0EB00EB20EB30EBD0EC0-0EC40EDC-0EDF0F000F40-0F470F49-0F6C0F88-0F8C1000-102A103F1050-1055105A-105D106110651066106E-10701075-1081108E10D0-10FA10FD-1248124A-124D1250-12561258125A-125D1260-1288128A-128D1290-12B012B2-12B512B8-12BE12C012C2-12C512C8-12D612D8-13101312-13151318-135A1380-138F13A0-13F41401-166C166F-167F1681-169A16A0-16EA1700-170C170E-17111720-17311740-17511760-176C176E-17701780-17B317DC1820-18421844-18771880-18A818AA18B0-18F51900-191C1950-196D1970-19741980-19AB19C1-19C71A00-1A161A20-1A541B05-1B331B45-1B4B1B83-1BA01BAE1BAF1BBA-1BE51C00-1C231C4D-1C4F1C5A-1C771CE9-1CEC1CEE-1CF11CF51CF62135-21382D30-2D672D80-2D962DA0-2DA62DA8-2DAE2DB0-2DB62DB8-2DBE2DC0-2DC62DC8-2DCE2DD0-2DD62DD8-2DDE3006303C3041-3096309F30A1-30FA30FF3105-312D3131-318E31A0-31BA31F0-31FF3400-4DB54E00-9FCCA000-A014A016-A48CA4D0-A4F7A500-A60BA610-A61FA62AA62BA66EA6A0-A6E5A7FB-A801A803-A805A807-A80AA80C-A822A840-A873A882-A8B3A8F2-A8F7A8FBA90A-A925A930-A946A960-A97CA984-A9B2AA00-AA28AA40-AA42AA44-AA4BAA60-AA6FAA71-AA76AA7AAA80-AAAFAAB1AAB5AAB6AAB9-AABDAAC0AAC2AADBAADCAAE0-AAEAAAF2AB01-AB06AB09-AB0EAB11-AB16AB20-AB26AB28-AB2EABC0-ABE2AC00-D7A3D7B0-D7C6D7CB-D7FBF900-FA6DFA70-FAD9FB1DFB1F-FB28FB2A-FB36FB38-FB3CFB3EFB40FB41FB43FB44FB46-FBB1FBD3-FD3DFD50-FD8FFD92-FDC7FDF0-FDFBFE70-FE74FE76-FEFCFF66-FF6FFF71-FF9DFFA0-FFBEFFC2-FFC7FFCA-FFCFFFD2-FFD7FFDA-FFDC", - Lt: "01C501C801CB01F21F88-1F8F1F98-1F9F1FA8-1FAF1FBC1FCC1FFC", - Lu: "0041-005A00C0-00D600D8-00DE01000102010401060108010A010C010E01100112011401160118011A011C011E01200122012401260128012A012C012E01300132013401360139013B013D013F0141014301450147014A014C014E01500152015401560158015A015C015E01600162016401660168016A016C016E017001720174017601780179017B017D018101820184018601870189-018B018E-0191019301940196-0198019C019D019F01A001A201A401A601A701A901AC01AE01AF01B1-01B301B501B701B801BC01C401C701CA01CD01CF01D101D301D501D701D901DB01DE01E001E201E401E601E801EA01EC01EE01F101F401F6-01F801FA01FC01FE02000202020402060208020A020C020E02100212021402160218021A021C021E02200222022402260228022A022C022E02300232023A023B023D023E02410243-02460248024A024C024E03700372037603860388-038A038C038E038F0391-03A103A3-03AB03CF03D2-03D403D803DA03DC03DE03E003E203E403E603E803EA03EC03EE03F403F703F903FA03FD-042F04600462046404660468046A046C046E04700472047404760478047A047C047E0480048A048C048E04900492049404960498049A049C049E04A004A204A404A604A804AA04AC04AE04B004B204B404B604B804BA04BC04BE04C004C104C304C504C704C904CB04CD04D004D204D404D604D804DA04DC04DE04E004E204E404E604E804EA04EC04EE04F004F204F404F604F804FA04FC04FE05000502050405060508050A050C050E05100512051405160518051A051C051E05200522052405260531-055610A0-10C510C710CD1E001E021E041E061E081E0A1E0C1E0E1E101E121E141E161E181E1A1E1C1E1E1E201E221E241E261E281E2A1E2C1E2E1E301E321E341E361E381E3A1E3C1E3E1E401E421E441E461E481E4A1E4C1E4E1E501E521E541E561E581E5A1E5C1E5E1E601E621E641E661E681E6A1E6C1E6E1E701E721E741E761E781E7A1E7C1E7E1E801E821E841E861E881E8A1E8C1E8E1E901E921E941E9E1EA01EA21EA41EA61EA81EAA1EAC1EAE1EB01EB21EB41EB61EB81EBA1EBC1EBE1EC01EC21EC41EC61EC81ECA1ECC1ECE1ED01ED21ED41ED61ED81EDA1EDC1EDE1EE01EE21EE41EE61EE81EEA1EEC1EEE1EF01EF21EF41EF61EF81EFA1EFC1EFE1F08-1F0F1F18-1F1D1F28-1F2F1F38-1F3F1F48-1F4D1F591F5B1F5D1F5F1F68-1F6F1FB8-1FBB1FC8-1FCB1FD8-1FDB1FE8-1FEC1FF8-1FFB21022107210B-210D2110-211221152119-211D212421262128212A-212D2130-2133213E213F214521832C00-2C2E2C602C62-2C642C672C692C6B2C6D-2C702C722C752C7E-2C802C822C842C862C882C8A2C8C2C8E2C902C922C942C962C982C9A2C9C2C9E2CA02CA22CA42CA62CA82CAA2CAC2CAE2CB02CB22CB42CB62CB82CBA2CBC2CBE2CC02CC22CC42CC62CC82CCA2CCC2CCE2CD02CD22CD42CD62CD82CDA2CDC2CDE2CE02CE22CEB2CED2CF2A640A642A644A646A648A64AA64CA64EA650A652A654A656A658A65AA65CA65EA660A662A664A666A668A66AA66CA680A682A684A686A688A68AA68CA68EA690A692A694A696A722A724A726A728A72AA72CA72EA732A734A736A738A73AA73CA73EA740A742A744A746A748A74AA74CA74EA750A752A754A756A758A75AA75CA75EA760A762A764A766A768A76AA76CA76EA779A77BA77DA77EA780A782A784A786A78BA78DA790A792A7A0A7A2A7A4A7A6A7A8A7AAFF21-FF3A", - Mc: "0903093B093E-09400949-094C094E094F0982098309BE-09C009C709C809CB09CC09D70A030A3E-0A400A830ABE-0AC00AC90ACB0ACC0B020B030B3E0B400B470B480B4B0B4C0B570BBE0BBF0BC10BC20BC6-0BC80BCA-0BCC0BD70C01-0C030C41-0C440C820C830CBE0CC0-0CC40CC70CC80CCA0CCB0CD50CD60D020D030D3E-0D400D46-0D480D4A-0D4C0D570D820D830DCF-0DD10DD8-0DDF0DF20DF30F3E0F3F0F7F102B102C10311038103B103C105610571062-10641067-106D108310841087-108C108F109A-109C17B617BE-17C517C717C81923-19261929-192B193019311933-193819B0-19C019C819C91A19-1A1B1A551A571A611A631A641A6D-1A721B041B351B3B1B3D-1B411B431B441B821BA11BA61BA71BAA1BAC1BAD1BE71BEA-1BEC1BEE1BF21BF31C24-1C2B1C341C351CE11CF21CF3302E302FA823A824A827A880A881A8B4-A8C3A952A953A983A9B4A9B5A9BAA9BBA9BD-A9C0AA2FAA30AA33AA34AA4DAA7BAAEBAAEEAAEFAAF5ABE3ABE4ABE6ABE7ABE9ABEAABEC", - Mn: "0300-036F0483-04870591-05BD05BF05C105C205C405C505C70610-061A064B-065F067006D6-06DC06DF-06E406E706E806EA-06ED07110730-074A07A6-07B007EB-07F30816-0819081B-08230825-08270829-082D0859-085B08E4-08FE0900-0902093A093C0941-0948094D0951-095709620963098109BC09C1-09C409CD09E209E30A010A020A3C0A410A420A470A480A4B-0A4D0A510A700A710A750A810A820ABC0AC1-0AC50AC70AC80ACD0AE20AE30B010B3C0B3F0B41-0B440B4D0B560B620B630B820BC00BCD0C3E-0C400C46-0C480C4A-0C4D0C550C560C620C630CBC0CBF0CC60CCC0CCD0CE20CE30D41-0D440D4D0D620D630DCA0DD2-0DD40DD60E310E34-0E3A0E47-0E4E0EB10EB4-0EB90EBB0EBC0EC8-0ECD0F180F190F350F370F390F71-0F7E0F80-0F840F860F870F8D-0F970F99-0FBC0FC6102D-10301032-10371039103A103D103E10581059105E-10601071-1074108210851086108D109D135D-135F1712-17141732-1734175217531772177317B417B517B7-17BD17C617C9-17D317DD180B-180D18A91920-19221927192819321939-193B1A171A181A561A58-1A5E1A601A621A65-1A6C1A73-1A7C1A7F1B00-1B031B341B36-1B3A1B3C1B421B6B-1B731B801B811BA2-1BA51BA81BA91BAB1BE61BE81BE91BED1BEF-1BF11C2C-1C331C361C371CD0-1CD21CD4-1CE01CE2-1CE81CED1CF41DC0-1DE61DFC-1DFF20D0-20DC20E120E5-20F02CEF-2CF12D7F2DE0-2DFF302A-302D3099309AA66FA674-A67DA69FA6F0A6F1A802A806A80BA825A826A8C4A8E0-A8F1A926-A92DA947-A951A980-A982A9B3A9B6-A9B9A9BCAA29-AA2EAA31AA32AA35AA36AA43AA4CAAB0AAB2-AAB4AAB7AAB8AABEAABFAAC1AAECAAEDAAF6ABE5ABE8ABEDFB1EFE00-FE0FFE20-FE26", - Nd: "0030-00390660-066906F0-06F907C0-07C90966-096F09E6-09EF0A66-0A6F0AE6-0AEF0B66-0B6F0BE6-0BEF0C66-0C6F0CE6-0CEF0D66-0D6F0E50-0E590ED0-0ED90F20-0F291040-10491090-109917E0-17E91810-18191946-194F19D0-19D91A80-1A891A90-1A991B50-1B591BB0-1BB91C40-1C491C50-1C59A620-A629A8D0-A8D9A900-A909A9D0-A9D9AA50-AA59ABF0-ABF9FF10-FF19", - Nl: "16EE-16F02160-21822185-218830073021-30293038-303AA6E6-A6EF", - Pc: "005F203F20402054FE33FE34FE4D-FE4FFF3F" -}; - -var unicodeClass = function (abbrev) { - return '[' + - unicodeCategories[abbrev].replace(/[0-9A-F]{4}/ig, "\\u$&") + ']'; -}; - -// See ECMA-262 spec, 3rd edition, Section 7.6 -// Match one or more characters that can start an identifier. -// This is IdentifierStart+. -var rIdentifierPrefix = new RegExp( - "^([a-zA-Z$_]+|\\\\u[0-9a-fA-F]{4}|" + - [unicodeClass('Lu'), unicodeClass('Ll'), unicodeClass('Lt'), - unicodeClass('Lm'), unicodeClass('Lo'), unicodeClass('Nl')].join('|') + - ")+"); -// Match one or more characters that can continue an identifier. -// This is (IdentifierPart and not IdentifierStart)+. -// To match a full identifier, match rIdentifierPrefix, then -// match rIdentifierMiddle followed by rIdentifierPrefix until they both fail. -var rIdentifierMiddle = new RegExp( - "^([0-9]|" + [unicodeClass('Mn'), unicodeClass('Mc'), unicodeClass('Nd'), - unicodeClass('Pc')].join('|') + ")+"); - - -// See ECMA-262 spec, 3rd edition, Section 7.8.3 -var rHexLiteral = /^0[xX][0-9a-fA-F]+(?!\w)/; -var rDecLiteral = - /^(((0|[1-9][0-9]*)(\.[0-9]*)?)|\.[0-9]+)([Ee][+-]?[0-9]+)?(?!\w)/; - -// Section 7.8.4 -var rStringQuote = /^["']/; -// Match one or more characters besides quotes, backslashes, or line ends -var rStringMiddle = /^(?=.)[^"'\\]+?((?!.)|(?=["'\\]))/; -// Match one escape sequence, including the backslash. -var rEscapeSequence = - /^\\(['"\\bfnrtv]|0(?![0-9])|x[0-9a-fA-F]{2}|u[0-9a-fA-F]{4}|(?=.)[^ux0-9])/; -// Match one ES5 line continuation -var rLineContinuation = - /^\\(\r\n|[\u000A\u000D\u2028\u2029])/; - - -BlazeTools.parseNumber = function (scanner) { - var startPos = scanner.pos; - - var isNegative = false; - if (scanner.peek() === '-') { - scanner.pos++; - isNegative = true; - } - // Note that we allow `"-0xa"`, unlike `Number(...)`. - - var rest = scanner.rest(); - var match = rDecLiteral.exec(rest) || rHexLiteral.exec(rest); - if (! match) { - scanner.pos = startPos; - return null; - } - var matchText = match[0]; - scanner.pos += matchText.length; - - var text = (isNegative ? '-' : '') + matchText; - var value = Number(matchText); - value = (isNegative ? -value : value); - return { text: text, value: value }; -}; - -BlazeTools.parseIdentifierName = function (scanner) { - var startPos = scanner.pos; - var rest = scanner.rest(); - var match = rIdentifierPrefix.exec(rest); - if (! match) - return null; - scanner.pos += match[0].length; - rest = scanner.rest(); - var foundMore = true; - - while (foundMore) { - foundMore = false; - - match = rIdentifierMiddle.exec(rest); - if (match) { - foundMore = true; - scanner.pos += match[0].length; - rest = scanner.rest(); - } - - match = rIdentifierPrefix.exec(rest); - if (match) { - foundMore = true; - scanner.pos += match[0].length; - rest = scanner.rest(); - } - } - - return scanner.input.substring(startPos, scanner.pos); -}; - -BlazeTools.parseExtendedIdentifierName = function (scanner) { - // parse an identifier name optionally preceded by '@' - if (scanner.peek() === '@') { - scanner.pos++; - var afterAt = BlazeTools.parseIdentifierName(scanner); - if (afterAt) { - return '@' + afterAt; - } else { - scanner.pos--; - return null; - } - } else { - return BlazeTools.parseIdentifierName(scanner); - } -}; - -BlazeTools.parseStringLiteral = function (scanner) { - var startPos = scanner.pos; - var rest = scanner.rest(); - var match = rStringQuote.exec(rest); - if (! match) - return null; - - var quote = match[0]; - scanner.pos++; - rest = scanner.rest(); - - var jsonLiteral = '"'; - - while (match) { - match = rStringMiddle.exec(rest); - if (match) { - jsonLiteral += match[0]; - } else { - match = rEscapeSequence.exec(rest); - if (match) { - var esc = match[0]; - // Convert all string escapes to JSON-compatible string escapes, so we - // can use JSON.parse for some of the work. JSON strings are not the - // same as JS strings. They don't support `\0`, `\v`, `\'`, or hex - // escapes. - if (esc === '\\0') - jsonLiteral += '\\u0000'; - else if (esc === '\\v') - // Note: IE 8 doesn't correctly parse '\v' in JavaScript. - jsonLiteral += '\\u000b'; - else if (esc.charAt(1) === 'x') - jsonLiteral += '\\u00' + esc.slice(2); - else if (esc === '\\\'') - jsonLiteral += "'"; - else - jsonLiteral += esc; - } else { - match = rLineContinuation.exec(rest); - if (! match) { - match = rStringQuote.exec(rest); - if (match) { - var c = match[0]; - if (c !== quote) { - if (c === '"') - jsonLiteral += '\\'; - jsonLiteral += c; - } - } - } - } - } - if (match) { - scanner.pos += match[0].length; - rest = scanner.rest(); - if (match[0] === quote) - break; - } - } - - if (! match || match[0] !== quote) - scanner.fatal("Unterminated string literal"); - - jsonLiteral += '"'; - var text = scanner.input.substring(startPos, scanner.pos); - var value = JSON.parse(jsonLiteral); - return { text: text, value: value }; -}; diff --git a/packages/blaze/README.md b/packages/blaze/README.md deleted file mode 100644 index 81d8298630..0000000000 --- a/packages/blaze/README.md +++ /dev/null @@ -1,318 +0,0 @@ -# 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 - - - -``` - -The template tags `{{name}}` and `{{score}}` refer to properties of -the data context (the current player), while `players` and `selected` -refer to helper functions. Helper functions and event handlers are defined -in JavaScript: - -```javascript -Template.leaderboard.helpers({ - players: function () { - // Perform a reactive database query against minimongo - return Players.find({}, { sort: { score: -1, name: 1 } }); - } -}); - -Template.player.events({ - 'click': function () { - // click on a player to select it - Session.set("selectedPlayer", this._id); - } -}); - -Template.player.helpers({ - selected: function () { - return Session.equals("selectedPlayer", this._id) ? "selected" : ''; - } -}); -``` - -No additional UI code is necessary to ensure that the list of players -stays up-to-date, or that the "selected" class is added and removed -from the LI elements as appropriate when the user clicks on a player. - -Thanks to a powerful template language, it doesn't take much ceremony -to write a loop, include another template, or bind an attribute (or -part of an attribute). And thanks to Tracker's transparent -reactivity, there's no ceremony around depending on reactive data -sources like the database or Session; it just happens when you read -the value, and when the value changes, the DOM will be updated in a -fine-grained way. - -# Principles - -## Gentle Learning Curve - -To get started with Blaze, you don't have to learn a lot of concepts -or terminology. As web developers, we are already students of HTML, -CSS, and JavaScript, which are complex technologies described in thick -books. Blaze lets you apply your existing knowledge in exciting new -ways without having to read another book first. - -Many factors go into making Blaze easy to pick up and use, including -the other principles below. In general, we prefer APIs that lead to -simple and obvious-looking application code, and we recognize that -developers have limited time and energy to learn new and unfamiliar -terms and syntaxes. - -It may sound obvious to "keep it simple" and prioritize the developer -experience when creating a system for reactive HTML, but it's also -challenging, and we think it's not done often enough! We use feedback -from the Meteor community to ensure that Blaze's features stay simple, -understandable, and useful. - -## Transparent Reactivity - -Under the hood, Blaze uses the [Tracker](https://meteor.com/tracker) -library to automatically keep track of when to recalculate each -template helper. If a helper reads a value from the client-side -database, for example, the helper will automatically be recalculated -when the value changes. - -What this means for the developer is simple. You don't have to -explicitly declare when to update the DOM, or even perform any -explicit "data-binding." You don't have to know how Tracker works, or -even exactly what "reactivity" means, to benefit. The result is less -thinking and less typing than other approaches. - -## Clean Templates - -Blaze embraces popular template syntaxes such as Handlebars and Jade -which are clean, readable, and familiar to developers coming from -other frameworks. - -A good template language should clearly distinguish the special -"template directives" (often enclosed in curly braces) from the HTML, -and it should not obscure the structure of the resulting HTML. These -properties make templating an easy concept to learn after static HTML -(or alongside it), and make templates easy to read, easy to style with -CSS, and easy to relate to the DOM. - -In contrast, some newer frameworks try to remake templates as just -HTML (Angular, Polymer) or replace them with just JavaScript (React). -These approaches tend to obscure either the structure of the template, -or what is a real DOM element and what is not, or both. In addition, -since templates are generally precompiled anyway as a best practice, -it's really not important that raw template source code be -browser-parsable. Meanwhile, the developer experience of reading, -writing, and maintaining templates is hugely important. - -## Plugin Interoperability - -Web developers often share snippets of HTML, JavaScript, and CSS, or -publish them as libraries, widgets, or jQuery plugins. They want to -embed videos, maps, and other third-party content. - -Blaze doesn't assume it owns the whole DOM, and it tries to make as -few assumptions as possible about the DOM outside of its updates. -It hooks into jQuery's clean-up routines to prevent memory leaks, -and it preserves classes, attributes, and styles added to elements -by jQuery or any third-party library. - -While it's certainly possible for Blaze and jQuery to step on each -other's toes if you aren't careful, there are established patterns for -keeping the peace, and Meteor users rightfully expect to be able to -use the various widgets and enhancements cooked up by the broader web -community in their apps. - -# Comparisons to other libraries - -Compared to Backbone and other libraries that simply re-render -templates, Blaze does much less re-rendering and doesn't suffer from -the dreaded "nested view" problem, which is when two templates can't -be updated independently of each other because one is nested inside -the other. In addition, Blaze automatically determines when -re-rendering must occur, using Tracker. - -Compared to Ember, Blaze offers finer-grained, automatic DOM updates. -Because Blaze uses Tracker's transparent reactivity, you don't have to -perform explicit "data-binding" to get data into your template, or -declare the data dependencies of each template helper. - -Compared to Angular and Polymer, Blaze has a gentler learning curve, -simpler concepts, and nicer template syntax that cleanly separates -template directives and HTML. Also, Blaze is targeted at today's -browsers and not designed around a hypothetical "browser of the -future." - -Compared to React, Blaze emphasizes HTML templates rather than -JavaScript component classes. Templates are more approachable than -JavaScript code and easier to read, write, and style with CSS. -Instead of using Tracker, React relies on a combination of explicit -"setState" calls and data-model diffing in order to achieve efficient -rendering. - -# Future Work - -### Components - -Blaze will get better patterns for creating reusable UI components. -Templates already serve as reusable components, to a point. -Improvements will focus on: - -* Argument-handling -* Local reactive state -* "Methods" that are callable from other components and have side - effects, versus the current "helpers" which are called from the - template language and are "pure" -* Scoping and the lookup chain -* Inheritance and configuration - -### Forms - -Most applications have a lot of forms, where input fields and other -widgets are used to enter data, which must then be validated and -turned into database changes. Server-side frameworks like Rails and -Django have well-honed patterns for this, but client-side frameworks -are typically more lacking, perhaps because they are more estranged -from the database. - -Meteor developers have already found ways and built packages to deal -with forms and validation, but we think there's a great opportunity to -make this part of the core, out-of-the-box Meteor experience. - -### Mobile and Animation - -Blaze will cater to the needs of the mobile web, including enhanced -performance and patterns for touch and other mobile interaction. - -We'll also improve the ease with which developers can integrate -animated transitions into their apps. - -### JavaScript Expressions in Templates - -We plan to support JavaScript expressions in templates. This will -make templates more expressive, and it will further shorten -application code by eliminating the need for a certain class of -one-line helpers. - -The usual argument against allowing JavaScript expressions in a -template language is one of "separation of concerns" -- separating -business logic from presentation, so that the business logic may be -better organized, maintained, and tested independently. Meanwhile, -even "logicless" template languages often include some concessions in -the form of microsyntax for filtering, querying, and transforming data -before using it. This special syntax (and its extension mechanisms) -must then be learned. - -While keeping business logic out of templates is indeed good policy, -there is a large class of "presentation logic" that is not really -separable from the concerns of templates and HTML, such as the code to -calculate styles and classes to apply to HTML elements or to massage -data records into a better form for templating purposes. In many -cases where this code is short, it may be more convenient or more -readable to embed the code in the template, and it's certainly better -than evolving the template syntax in a direction that diverges from -JavaScript. - -Because templates are already precompiled to JavaScript code, there is -nothing fundamentally difficult or inelegant about allowing a large -subset of JavaScript to be used within templates (see e.g. the project -Ractive.js). - -### Other Template Enhancements - -Source maps for debugging templates. Imagine seeing your template -code in the browser's debugger! Pretty slick. - -True lexical scoping. - -Better support for pluggable template syntax (e.g. Jade-like -templates). There is already a Jade package in use, but we should -learn from it and clarify the abstraction boundary that authors of -template syntaxes are programming against. - -### Pluggable Backends (don't require jQuery) - -While Blaze currently requires jQuery, it is architected to run -against other "DOM backends" using a common adaptor interface. You -should be able to use Zepto, or some very small shim if browser -compatibility is not a big deal for your application for some reason. -At the moment, no such adaptors besides the jQuery one have been -written. - -The Blaze team experimented with dropping jQuery and talking directly -to "modern browsers," but it turns out there is about 5-10K of code at -the heart of jQuery that you can't throw out even if you don't care -about old browsers or supporting jQuery's app-facing API, which is -required just to bring browsers up to the modest expectations of web -developers. - -### Better Stand-alone Support - -Blaze will get better support for using it outside of Meteor, such as -regular stand-alone builds. - -# Resources - -* [Templates API](http://docs.meteor.com/#templates_api) -* [Blaze API](http://docs.meteor.com/#blaze) -* [Spacebars syntax](https://github.com/meteor/meteor/blob/devel/packages/spacebars/README.md) - -# Packages - -* blaze -* blaze-tools -* html-tools -* htmljs -* spacebars -* spacebars-compiler diff --git a/packages/blaze/attrs.js b/packages/blaze/attrs.js deleted file mode 100644 index 427f9cf8ec..0000000000 --- a/packages/blaze/attrs.js +++ /dev/null @@ -1,365 +0,0 @@ -var jsUrlsAllowed = false; -Blaze._allowJavascriptUrls = function () { - jsUrlsAllowed = true; -}; -Blaze._javascriptUrlsAllowed = function () { - return jsUrlsAllowed; -}; - -// An AttributeHandler object is responsible for updating a particular attribute -// of a particular element. AttributeHandler subclasses implement -// browser-specific logic for dealing with particular attributes across -// different browsers. -// -// To define a new type of AttributeHandler, use -// `var FooHandler = AttributeHandler.extend({ update: function ... })` -// where the `update` function takes arguments `(element, oldValue, value)`. -// The `element` argument is always the same between calls to `update` on -// the same instance. `oldValue` and `value` are each either `null` or -// a Unicode string of the type that might be passed to the value argument -// of `setAttribute` (i.e. not an HTML string with character references). -// When an AttributeHandler is installed, an initial call to `update` is -// always made with `oldValue = null`. The `update` method can access -// `this.name` if the AttributeHandler class is a generic one that applies -// to multiple attribute names. -// -// AttributeHandlers can store custom properties on `this`, as long as they -// don't use the names `element`, `name`, `value`, and `oldValue`. -// -// AttributeHandlers can't influence how attributes appear in rendered HTML, -// only how they are updated after materialization as DOM. - -AttributeHandler = function (name, value) { - this.name = name; - this.value = value; -}; -Blaze._AttributeHandler = AttributeHandler; - -AttributeHandler.prototype.update = function (element, oldValue, value) { - if (value === null) { - if (oldValue !== null) - element.removeAttribute(this.name); - } else { - element.setAttribute(this.name, value); - } -}; - -AttributeHandler.extend = function (options) { - var curType = this; - var subType = function AttributeHandlerSubtype(/*arguments*/) { - AttributeHandler.apply(this, arguments); - }; - subType.prototype = new curType; - subType.extend = curType.extend; - if (options) - _.extend(subType.prototype, options); - return subType; -}; - -/// Apply the diff between the attributes of "oldValue" and "value" to "element." -// -// Each subclass must implement a parseValue method which takes a string -// as an input and returns a dict of attributes. The keys of the dict -// are unique identifiers (ie. css properties in the case of styles), and the -// values are the entire attribute which will be injected into the element. -// -// Extended below to support classes, SVG elements and styles. - -var DiffingAttributeHandler = AttributeHandler.extend({ - update: function (element, oldValue, value) { - if (!this.getCurrentValue || !this.setValue || !this.parseValue) - throw new Error("Missing methods in subclass of 'DiffingAttributeHandler'"); - - var oldAttrsMap = oldValue ? this.parseValue(oldValue) : {}; - var newAttrsMap = value ? this.parseValue(value) : {}; - - // the current attributes on the element, which we will mutate. - - var attrString = this.getCurrentValue(element); - var attrsMap = attrString ? this.parseValue(attrString) : {}; - - _.each(_.keys(oldAttrsMap), function (t) { - if (! (t in newAttrsMap)) - delete attrsMap[t]; - }); - - _.each(_.keys(newAttrsMap), function (t) { - attrsMap[t] = newAttrsMap[t]; - }); - - this.setValue(element, _.values(attrsMap).join(' ')); - } -}); - -var ClassHandler = DiffingAttributeHandler.extend({ - // @param rawValue {String} - getCurrentValue: function (element) { - return element.className; - }, - setValue: function (element, className) { - element.className = className; - }, - parseValue: function (attrString) { - var tokens = {}; - - _.each(attrString.split(' '), function(token) { - if (token) - tokens[token] = token; - }); - return tokens; - } -}); - -var SVGClassHandler = ClassHandler.extend({ - getCurrentValue: function (element) { - return element.className.baseVal; - }, - setValue: function (element, className) { - element.setAttribute('class', className); - } -}); - -var StyleHandler = DiffingAttributeHandler.extend({ - getCurrentValue: function (element) { - return element.getAttribute('style'); - }, - setValue: function (element, style) { - if (style === '') { - element.removeAttribute('style'); - } else { - element.setAttribute('style', style); - } - }, - - // Parse a string to produce a map from property to attribute string. - // - // Example: - // "color:red; foo:12px" produces a token {color: "color:red", foo:"foo:12px"} - parseValue: function (attrString) { - var tokens = {}; - - // Regex for parsing a css attribute declaration, taken from css-parse: - // https://github.com/reworkcss/css-parse/blob/7cef3658d0bba872cde05a85339034b187cb3397/index.js#L219 - var regex = /(\*?[-#\/\*\\\w]+(?:\[[0-9a-z_-]+\])?)\s*:\s*(?:\'(?:\\\'|.)*?\'|"(?:\\"|.)*?"|\([^\)]*?\)|[^};])+[;\s]*/g; - var match = regex.exec(attrString); - while (match) { - // match[0] = entire matching string - // match[1] = css property - // Prefix the token to prevent conflicts with existing properties. - - // XXX No `String.trim` on Safari 4. Swap out $.trim if we want to - // remove strong dep on jquery. - tokens[' ' + match[1]] = match[0].trim ? - match[0].trim() : $.trim(match[0]); - - match = regex.exec(attrString); - } - - return tokens; - } -}); - -var BooleanHandler = AttributeHandler.extend({ - update: function (element, oldValue, value) { - var name = this.name; - if (value == null) { - if (oldValue != null) - element[name] = false; - } else { - element[name] = true; - } - } -}); - -var DOMPropertyHandler = AttributeHandler.extend({ - update: function (element, oldValue, value) { - var name = this.name; - if (value !== element[name]) - element[name] = value; - } -}); - -// attributes of the type 'xlink:something' should be set using -// the correct namespace in order to work -var XlinkHandler = AttributeHandler.extend({ - update: function(element, oldValue, value) { - var NS = 'http://www.w3.org/1999/xlink'; - if (value === null) { - if (oldValue !== null) - element.removeAttributeNS(NS, this.name); - } else { - element.setAttributeNS(NS, this.name, this.value); - } - } -}); - -// cross-browser version of `instanceof SVGElement` -var isSVGElement = function (elem) { - return 'ownerSVGElement' in elem; -}; - -var isUrlAttribute = function (tagName, attrName) { - // Compiled from http://www.w3.org/TR/REC-html40/index/attributes.html - // and - // http://www.w3.org/html/wg/drafts/html/master/index.html#attributes-1 - var urlAttrs = { - FORM: ['action'], - BODY: ['background'], - BLOCKQUOTE: ['cite'], - Q: ['cite'], - DEL: ['cite'], - INS: ['cite'], - OBJECT: ['classid', 'codebase', 'data', 'usemap'], - APPLET: ['codebase'], - A: ['href'], - AREA: ['href'], - LINK: ['href'], - BASE: ['href'], - IMG: ['longdesc', 'src', 'usemap'], - FRAME: ['longdesc', 'src'], - IFRAME: ['longdesc', 'src'], - HEAD: ['profile'], - SCRIPT: ['src'], - INPUT: ['src', 'usemap', 'formaction'], - BUTTON: ['formaction'], - BASE: ['href'], - MENUITEM: ['icon'], - HTML: ['manifest'], - VIDEO: ['poster'] - }; - - if (attrName === 'itemid') { - return true; - } - - var urlAttrNames = urlAttrs[tagName] || []; - return _.contains(urlAttrNames, attrName); -}; - -// To get the protocol for a URL, we let the browser normalize it for -// us, by setting it as the href for an anchor tag and then reading out -// the 'protocol' property. -if (Meteor.isClient) { - var anchorForNormalization = document.createElement('A'); -} - -var getUrlProtocol = function (url) { - if (Meteor.isClient) { - anchorForNormalization.href = url; - return (anchorForNormalization.protocol || "").toLowerCase(); - } else { - throw new Error('getUrlProtocol not implemented on the server'); - } -}; - -// UrlHandler is an attribute handler for all HTML attributes that take -// URL values. It disallows javascript: URLs, unless -// Blaze._allowJavascriptUrls() has been called. To detect javascript: -// urls, we set the attribute on a dummy anchor element and then read -// out the 'protocol' property of the attribute. -var origUpdate = AttributeHandler.prototype.update; -var UrlHandler = AttributeHandler.extend({ - update: function (element, oldValue, value) { - var self = this; - var args = arguments; - - if (Blaze._javascriptUrlsAllowed()) { - origUpdate.apply(self, args); - } else { - var isJavascriptProtocol = (getUrlProtocol(value) === "javascript:"); - var isVBScriptProtocol = (getUrlProtocol(value) === "vbscript:"); - if (isJavascriptProtocol || isVBScriptProtocol) { - Blaze._warn("URLs that use the 'javascript:' or 'vbscript:' protocol are not " + - "allowed in URL attribute values. " + - "Call Blaze._allowJavascriptUrls() " + - "to enable them."); - origUpdate.apply(self, [element, oldValue, null]); - } else { - origUpdate.apply(self, args); - } - } - } -}); - -// XXX make it possible for users to register attribute handlers! -makeAttributeHandler = function (elem, name, value) { - // generally, use setAttribute but certain attributes need to be set - // by directly setting a JavaScript property on the DOM element. - if (name === 'class') { - if (isSVGElement(elem)) { - return new SVGClassHandler(name, value); - } else { - return new ClassHandler(name, value); - } - } else if (name === 'style') { - return new StyleHandler(name, value); - } else if ((elem.tagName === 'OPTION' && name === 'selected') || - (elem.tagName === 'INPUT' && name === 'checked')) { - return new BooleanHandler(name, value); - } else if ((elem.tagName === 'TEXTAREA' || elem.tagName === 'INPUT') - && name === 'value') { - // internally, TEXTAREAs tracks their value in the 'value' - // attribute just like INPUTs. - return new DOMPropertyHandler(name, value); - } else if (name.substring(0,6) === 'xlink:') { - return new XlinkHandler(name.substring(6), value); - } else if (isUrlAttribute(elem.tagName, name)) { - return new UrlHandler(name, value); - } else { - return new AttributeHandler(name, value); - } - - // XXX will need one for 'style' on IE, though modern browsers - // seem to handle setAttribute ok. -}; - - -ElementAttributesUpdater = function (elem) { - this.elem = elem; - this.handlers = {}; -}; - -// Update attributes on `elem` to the dictionary `attrs`, whose -// values are strings. -ElementAttributesUpdater.prototype.update = function(newAttrs) { - var elem = this.elem; - var handlers = this.handlers; - - for (var k in handlers) { - if (! _.has(newAttrs, k)) { - // remove attributes (and handlers) for attribute names - // that don't exist as keys of `newAttrs` and so won't - // be visited when traversing it. (Attributes that - // exist in the `newAttrs` object but are `null` - // are handled later.) - var handler = handlers[k]; - var oldValue = handler.value; - handler.value = null; - handler.update(elem, oldValue, null); - delete handlers[k]; - } - } - - for (var k in newAttrs) { - var handler = null; - var oldValue; - var value = newAttrs[k]; - if (! _.has(handlers, k)) { - if (value !== null) { - // make new handler - handler = makeAttributeHandler(elem, k, value); - handlers[k] = handler; - oldValue = null; - } - } else { - handler = handlers[k]; - oldValue = handler.value; - } - if (oldValue !== value) { - handler.value = value; - handler.update(elem, oldValue, value); - if (value === null) - delete handlers[k]; - } - } -}; diff --git a/packages/blaze/backcompat.js b/packages/blaze/backcompat.js deleted file mode 100644 index d8c5d509d8..0000000000 --- a/packages/blaze/backcompat.js +++ /dev/null @@ -1,18 +0,0 @@ -UI = Blaze; - -Blaze.ReactiveVar = ReactiveVar; -UI._templateInstance = Blaze.Template.instance; - -Handlebars = {}; -Handlebars.registerHelper = Blaze.registerHelper; - -Handlebars._escape = Blaze._escape; - -// Return these from {{...}} helpers to achieve the same as returning -// strings from {{{...}}} helpers -Handlebars.SafeString = function(string) { - this.string = string; -}; -Handlebars.SafeString.prototype.toString = function() { - return this.string.toString(); -}; diff --git a/packages/blaze/builtins.js b/packages/blaze/builtins.js deleted file mode 100644 index 4d883bcd97..0000000000 --- a/packages/blaze/builtins.js +++ /dev/null @@ -1,354 +0,0 @@ -Blaze._calculateCondition = function (cond) { - if (cond instanceof Array && cond.length === 0) - cond = false; - return !! cond; -}; - -/** - * @summary Constructs a View that renders content with a data context. - * @locus Client - * @param {Object|Function} data An object to use as the data context, or a function returning such an object. If a function is provided, it will be reactively re-run. - * @param {Function} contentFunc A Function that returns [*renderable content*](#renderable_content). - */ -Blaze.With = function (data, contentFunc) { - var view = Blaze.View('with', contentFunc); - - view.dataVar = new ReactiveVar; - - view.onViewCreated(function () { - if (typeof data === 'function') { - // `data` is a reactive function - view.autorun(function () { - view.dataVar.set(data()); - }, view.parentView, 'setData'); - } else { - view.dataVar.set(data); - } - }); - - return view; -}; - -/** - * Attaches bindings to the instantiated view. - * @param {Object} bindings A dictionary of bindings, each binding name - * corresponds to a value or a function that will be reactively re-run. - * @param {View} view The target. - */ -Blaze._attachBindingsToView = function (bindings, view) { - view.onViewCreated(function () { - _.each(bindings, function (binding, name) { - view._scopeBindings[name] = new ReactiveVar; - if (typeof binding === 'function') { - view.autorun(function () { - view._scopeBindings[name].set(binding()); - }, view.parentView); - } else { - view._scopeBindings[name].set(binding); - } - }); - }); -}; - -/** - * @summary Constructs a View setting the local lexical scope in the block. - * @param {Function} bindings Dictionary mapping names of bindings to - * values or computations to reactively re-run. - * @param {Function} contentFunc A Function that returns [*renderable content*](#renderable_content). - */ -Blaze.Let = function (bindings, contentFunc) { - var view = Blaze.View('let', contentFunc); - Blaze._attachBindingsToView(bindings, view); - - return view; -}; - -/** - * @summary Constructs a View that renders content conditionally. - * @locus Client - * @param {Function} conditionFunc A function to reactively re-run. Whether the result is truthy or falsy determines whether `contentFunc` or `elseFunc` is shown. An empty array is considered falsy. - * @param {Function} contentFunc A Function that returns [*renderable content*](#renderable_content). - * @param {Function} [elseFunc] Optional. A Function that returns [*renderable content*](#renderable_content). If no `elseFunc` is supplied, no content is shown in the "else" case. - */ -Blaze.If = function (conditionFunc, contentFunc, elseFunc, _not) { - var conditionVar = new ReactiveVar; - - var view = Blaze.View(_not ? 'unless' : 'if', function () { - return conditionVar.get() ? contentFunc() : - (elseFunc ? elseFunc() : null); - }); - view.__conditionVar = conditionVar; - view.onViewCreated(function () { - this.autorun(function () { - var cond = Blaze._calculateCondition(conditionFunc()); - conditionVar.set(_not ? (! cond) : cond); - }, this.parentView, 'condition'); - }); - - return view; -}; - -/** - * @summary An inverted [`Blaze.If`](#blaze_if). - * @locus Client - * @param {Function} conditionFunc A function to reactively re-run. If the result is falsy, `contentFunc` is shown, otherwise `elseFunc` is shown. An empty array is considered falsy. - * @param {Function} contentFunc A Function that returns [*renderable content*](#renderable_content). - * @param {Function} [elseFunc] Optional. A Function that returns [*renderable content*](#renderable_content). If no `elseFunc` is supplied, no content is shown in the "else" case. - */ -Blaze.Unless = function (conditionFunc, contentFunc, elseFunc) { - return Blaze.If(conditionFunc, contentFunc, elseFunc, true /*_not*/); -}; - -/** - * @summary Constructs a View that renders `contentFunc` for each item in a sequence. - * @locus Client - * @param {Function} argFunc A function to reactively re-run. The function can - * return one of two options: - * - * 1. An object with two fields: '_variable' and '_sequence'. Each iterates over - * '_sequence', it may be a Cursor, an array, null, or undefined. Inside the - * Each body you will be able to get the current item from the sequence using - * the name specified in the '_variable' field. - * - * 2. Just a sequence (Cursor, array, null, or undefined) not wrapped into an - * object. Inside the Each body, the current item will be set as the data - * context. - * @param {Function} contentFunc A Function that returns [*renderable - * content*](#renderable_content). - * @param {Function} [elseFunc] A Function that returns [*renderable - * content*](#renderable_content) to display in the case when there are no items - * in the sequence. - */ -Blaze.Each = function (argFunc, contentFunc, elseFunc) { - var eachView = Blaze.View('each', function () { - var subviews = this.initialSubviews; - this.initialSubviews = null; - if (this._isCreatedForExpansion) { - this.expandedValueDep = new Tracker.Dependency; - this.expandedValueDep.depend(); - } - return subviews; - }); - eachView.initialSubviews = []; - eachView.numItems = 0; - eachView.inElseMode = false; - eachView.stopHandle = null; - eachView.contentFunc = contentFunc; - eachView.elseFunc = elseFunc; - eachView.argVar = new ReactiveVar; - eachView.variableName = null; - - // update the @index value in the scope of all subviews in the range - var updateIndices = function (from, to) { - if (to === undefined) { - to = eachView.numItems - 1; - } - - for (var i = from; i <= to; i++) { - var view = eachView._domrange.members[i].view; - view._scopeBindings['@index'].set(i); - } - }; - - eachView.onViewCreated(function () { - // We evaluate argFunc in an autorun to make sure - // Blaze.currentView is always set when it runs (rather than - // passing argFunc straight to ObserveSequence). - eachView.autorun(function () { - // argFunc can return either a sequence as is or a wrapper object with a - // _sequence and _variable fields set. - var arg = argFunc(); - if (_.isObject(arg) && _.has(arg, '_sequence')) { - eachView.variableName = arg._variable || null; - arg = arg._sequence; - } - - eachView.argVar.set(arg); - }, eachView.parentView, 'collection'); - - eachView.stopHandle = ObserveSequence.observe(function () { - return eachView.argVar.get(); - }, { - addedAt: function (id, item, index) { - Tracker.nonreactive(function () { - var newItemView; - if (eachView.variableName) { - // new-style #each (as in {{#each item in items}}) - // doesn't create a new data context - newItemView = Blaze.View('item', eachView.contentFunc); - } else { - newItemView = Blaze.With(item, eachView.contentFunc); - } - - eachView.numItems++; - - var bindings = {}; - bindings['@index'] = index; - if (eachView.variableName) { - bindings[eachView.variableName] = item; - } - Blaze._attachBindingsToView(bindings, newItemView); - - if (eachView.expandedValueDep) { - eachView.expandedValueDep.changed(); - } else if (eachView._domrange) { - if (eachView.inElseMode) { - eachView._domrange.removeMember(0); - eachView.inElseMode = false; - } - - var range = Blaze._materializeView(newItemView, eachView); - eachView._domrange.addMember(range, index); - updateIndices(index); - } else { - eachView.initialSubviews.splice(index, 0, newItemView); - } - }); - }, - removedAt: function (id, item, index) { - Tracker.nonreactive(function () { - eachView.numItems--; - if (eachView.expandedValueDep) { - eachView.expandedValueDep.changed(); - } else if (eachView._domrange) { - eachView._domrange.removeMember(index); - updateIndices(index); - if (eachView.elseFunc && eachView.numItems === 0) { - eachView.inElseMode = true; - eachView._domrange.addMember( - Blaze._materializeView( - Blaze.View('each_else',eachView.elseFunc), - eachView), 0); - } - } else { - eachView.initialSubviews.splice(index, 1); - } - }); - }, - changedAt: function (id, newItem, oldItem, index) { - Tracker.nonreactive(function () { - if (eachView.expandedValueDep) { - eachView.expandedValueDep.changed(); - } else { - var itemView; - if (eachView._domrange) { - itemView = eachView._domrange.getMember(index).view; - } else { - itemView = eachView.initialSubviews[index]; - } - if (eachView.variableName) { - itemView._scopeBindings[eachView.variableName].set(newItem); - } else { - itemView.dataVar.set(newItem); - } - } - }); - }, - movedTo: function (id, item, fromIndex, toIndex) { - Tracker.nonreactive(function () { - if (eachView.expandedValueDep) { - eachView.expandedValueDep.changed(); - } else if (eachView._domrange) { - eachView._domrange.moveMember(fromIndex, toIndex); - updateIndices( - Math.min(fromIndex, toIndex), Math.max(fromIndex, toIndex)); - } else { - var subviews = eachView.initialSubviews; - var itemView = subviews[fromIndex]; - subviews.splice(fromIndex, 1); - subviews.splice(toIndex, 0, itemView); - } - }); - } - }); - - if (eachView.elseFunc && eachView.numItems === 0) { - eachView.inElseMode = true; - eachView.initialSubviews[0] = - Blaze.View('each_else', eachView.elseFunc); - } - }); - - eachView.onViewDestroyed(function () { - if (eachView.stopHandle) - eachView.stopHandle.stop(); - }); - - return eachView; -}; - -Blaze._TemplateWith = function (arg, contentFunc) { - var w; - - var argFunc = arg; - if (typeof arg !== 'function') { - argFunc = function () { - return arg; - }; - } - - // This is a little messy. When we compile `{{> Template.contentBlock}}`, we - // wrap it in Blaze._InOuterTemplateScope in order to skip the intermediate - // parent Views in the current template. However, when there's an argument - // (`{{> Template.contentBlock arg}}`), the argument needs to be evaluated - // in the original scope. There's no good order to nest - // Blaze._InOuterTemplateScope and Spacebars.TemplateWith to achieve this, - // so we wrap argFunc to run it in the "original parentView" of the - // Blaze._InOuterTemplateScope. - // - // To make this better, reconsider _InOuterTemplateScope as a primitive. - // Longer term, evaluate expressions in the proper lexical scope. - var wrappedArgFunc = function () { - var viewToEvaluateArg = null; - if (w.parentView && w.parentView.name === 'InOuterTemplateScope') { - viewToEvaluateArg = w.parentView.originalParentView; - } - if (viewToEvaluateArg) { - return Blaze._withCurrentView(viewToEvaluateArg, argFunc); - } else { - return argFunc(); - } - }; - - var wrappedContentFunc = function () { - var content = contentFunc.call(this); - - // Since we are generating the Blaze._TemplateWith view for the - // user, set the flag on the child view. If `content` is a template, - // construct the View so that we can set the flag. - if (content instanceof Blaze.Template) { - content = content.constructView(); - } - if (content instanceof Blaze.View) { - content._hasGeneratedParent = true; - } - - return content; - }; - - w = Blaze.With(wrappedArgFunc, wrappedContentFunc); - w.__isTemplateWith = true; - return w; -}; - -Blaze._InOuterTemplateScope = function (templateView, contentFunc) { - var view = Blaze.View('InOuterTemplateScope', contentFunc); - var parentView = templateView.parentView; - - // Hack so that if you call `{{> foo bar}}` and it expands into - // `{{#with bar}}{{> foo}}{{/with}}`, and then `foo` is a template - // that inserts `{{> Template.contentBlock}}`, the data context for - // `Template.contentBlock` is not `bar` but the one enclosing that. - if (parentView.__isTemplateWith) - parentView = parentView.parentView; - - view.onViewCreated(function () { - this.originalParentView = this.parentView; - this.parentView = parentView; - this.__childDoesntStartNewLexicalScope = true; - }); - return view; -}; - -// XXX COMPAT WITH 0.9.0 -Blaze.InOuterTemplateScope = Blaze._InOuterTemplateScope; diff --git a/packages/blaze/dombackend.js b/packages/blaze/dombackend.js deleted file mode 100644 index 361e37ae5e..0000000000 --- a/packages/blaze/dombackend.js +++ /dev/null @@ -1,179 +0,0 @@ -var DOMBackend = {}; -Blaze._DOMBackend = DOMBackend; - -var $jq = (typeof jQuery !== 'undefined' ? jQuery : - (typeof Package !== 'undefined' ? - Package.jquery && Package.jquery.jQuery : null)); -if (! $jq) - throw new Error("jQuery not found"); - -DOMBackend._$jq = $jq; - -DOMBackend.parseHTML = function (html) { - // Return an array of nodes. - // - // jQuery does fancy stuff like creating an appropriate - // container element and setting innerHTML on it, as well - // as working around various IE quirks. - return $jq.parseHTML(html) || []; -}; - -DOMBackend.Events = { - // `selector` is non-null. `type` is one type (but - // may be in backend-specific form, e.g. have namespaces). - // Order fired must be order bound. - delegateEvents: function (elem, type, selector, handler) { - $jq(elem).on(type, selector, handler); - }, - - undelegateEvents: function (elem, type, handler) { - $jq(elem).off(type, '**', handler); - }, - - bindEventCapturer: function (elem, type, selector, handler) { - var $elem = $jq(elem); - - var wrapper = function (event) { - event = $jq.event.fix(event); - event.currentTarget = event.target; - - // Note: It might improve jQuery interop if we called into jQuery - // here somehow. Since we don't use jQuery to dispatch the event, - // we don't fire any of jQuery's event hooks or anything. However, - // since jQuery can't bind capturing handlers, it's not clear - // where we would hook in. Internal jQuery functions like `dispatch` - // are too high-level. - var $target = $jq(event.currentTarget); - if ($target.is($elem.find(selector))) - handler.call(elem, event); - }; - - handler._meteorui_wrapper = wrapper; - - type = DOMBackend.Events.parseEventType(type); - // add *capturing* event listener - elem.addEventListener(type, wrapper, true); - }, - - unbindEventCapturer: function (elem, type, handler) { - type = DOMBackend.Events.parseEventType(type); - elem.removeEventListener(type, handler._meteorui_wrapper, true); - }, - - parseEventType: function (type) { - // strip off namespaces - var dotLoc = type.indexOf('.'); - if (dotLoc >= 0) - return type.slice(0, dotLoc); - return type; - } -}; - - -///// Removal detection and interoperability. - -// For an explanation of this technique, see: -// http://bugs.jquery.com/ticket/12213#comment:23 . -// -// In short, an element is considered "removed" when jQuery -// cleans up its *private* userdata on the element, -// which we can detect using a custom event with a teardown -// hook. - -var NOOP = function () {}; - -// Circular doubly-linked list -var TeardownCallback = function (func) { - this.next = this; - this.prev = this; - this.func = func; -}; - -// Insert newElt before oldElt in the circular list -TeardownCallback.prototype.linkBefore = function(oldElt) { - this.prev = oldElt.prev; - this.next = oldElt; - oldElt.prev.next = this; - oldElt.prev = this; -}; - -TeardownCallback.prototype.unlink = function () { - this.prev.next = this.next; - this.next.prev = this.prev; -}; - -TeardownCallback.prototype.go = function () { - var func = this.func; - func && func(); -}; - -TeardownCallback.prototype.stop = TeardownCallback.prototype.unlink; - -DOMBackend.Teardown = { - _JQUERY_EVENT_NAME: 'blaze_teardown_watcher', - _CB_PROP: '$blaze_teardown_callbacks', - // Registers a callback function to be called when the given element or - // one of its ancestors is removed from the DOM via the backend library. - // The callback function is called at most once, and it receives the element - // in question as an argument. - onElementTeardown: function (elem, func) { - var elt = new TeardownCallback(func); - - var propName = DOMBackend.Teardown._CB_PROP; - if (! elem[propName]) { - // create an empty node that is never unlinked - elem[propName] = new TeardownCallback; - - // Set up the event, only the first time. - $jq(elem).on(DOMBackend.Teardown._JQUERY_EVENT_NAME, NOOP); - } - - elt.linkBefore(elem[propName]); - - return elt; // so caller can call stop() - }, - // Recursively call all teardown hooks, in the backend and registered - // through DOMBackend.onElementTeardown. - tearDownElement: function (elem) { - var elems = []; - // Array.prototype.slice.call doesn't work when given a NodeList in - // IE8 ("JScript object expected"). - var nodeList = elem.getElementsByTagName('*'); - for (var i = 0; i < nodeList.length; i++) { - elems.push(nodeList[i]); - } - elems.push(elem); - $jq.cleanData(elems); - } -}; - -$jq.event.special[DOMBackend.Teardown._JQUERY_EVENT_NAME] = { - setup: function () { - // This "setup" callback is important even though it is empty! - // Without it, jQuery will call addEventListener, which is a - // performance hit, especially with Chrome's async stack trace - // feature enabled. - }, - teardown: function() { - var elem = this; - var callbacks = elem[DOMBackend.Teardown._CB_PROP]; - if (callbacks) { - var elt = callbacks.next; - while (elt !== callbacks) { - elt.go(); - elt = elt.next; - } - callbacks.go(); - - elem[DOMBackend.Teardown._CB_PROP] = null; - } - } -}; - - -// Must use jQuery semantics for `context`, not -// querySelectorAll's. In other words, all the parts -// of `selector` must be found under `context`. -DOMBackend.findBySelector = function (selector, context) { - return $jq(selector, context); -}; diff --git a/packages/blaze/domrange.js b/packages/blaze/domrange.js deleted file mode 100644 index ca39ac459c..0000000000 --- a/packages/blaze/domrange.js +++ /dev/null @@ -1,485 +0,0 @@ - -// A constant empty array (frozen if the JS engine supports it). -var _emptyArray = Object.freeze ? Object.freeze([]) : []; - -// `[new] Blaze._DOMRange([nodeAndRangeArray])` -// -// A DOMRange consists of an array of consecutive nodes and DOMRanges, -// which may be replaced at any time with a new array. If the DOMRange -// has been attached to the DOM at some location, then updating -// the array will cause the DOM to be updated at that location. -Blaze._DOMRange = function (nodeAndRangeArray) { - if (! (this instanceof DOMRange)) - // called without `new` - return new DOMRange(nodeAndRangeArray); - - var members = (nodeAndRangeArray || _emptyArray); - if (! (members && (typeof members.length) === 'number')) - throw new Error("Expected array"); - - for (var i = 0; i < members.length; i++) - this._memberIn(members[i]); - - this.members = members; - this.emptyRangePlaceholder = null; - this.attached = false; - this.parentElement = null; - this.parentRange = null; - this.attachedCallbacks = _emptyArray; -}; -var DOMRange = Blaze._DOMRange; - -// In IE 8, don't use empty text nodes as placeholders -// in empty DOMRanges, use comment nodes instead. Using -// empty text nodes in modern browsers is great because -// it doesn't clutter the web inspector. In IE 8, however, -// it seems to lead in some roundabout way to the OAuth -// pop-up crashing the browser completely. In the past, -// we didn't use empty text nodes on IE 8 because they -// don't accept JS properties, so just use the same logic -// even though we don't need to set properties on the -// placeholder anymore. -DOMRange._USE_COMMENT_PLACEHOLDERS = (function () { - var result = false; - var textNode = document.createTextNode(""); - try { - textNode.someProp = true; - } catch (e) { - // IE 8 - result = true; - } - return result; -})(); - -// static methods -DOMRange._insert = function (rangeOrNode, parentElement, nextNode, _isMove) { - var m = rangeOrNode; - if (m instanceof DOMRange) { - m.attach(parentElement, nextNode, _isMove); - } else { - if (_isMove) - DOMRange._moveNodeWithHooks(m, parentElement, nextNode); - else - DOMRange._insertNodeWithHooks(m, parentElement, nextNode); - } -}; - -DOMRange._remove = function (rangeOrNode) { - var m = rangeOrNode; - if (m instanceof DOMRange) { - m.detach(); - } else { - DOMRange._removeNodeWithHooks(m); - } -}; - -DOMRange._removeNodeWithHooks = function (n) { - if (! n.parentNode) - return; - if (n.nodeType === 1 && - n.parentNode._uihooks && n.parentNode._uihooks.removeElement) { - n.parentNode._uihooks.removeElement(n); - } else { - n.parentNode.removeChild(n); - } -}; - -DOMRange._insertNodeWithHooks = function (n, parent, next) { - // `|| null` because IE throws an error if 'next' is undefined - next = next || null; - if (n.nodeType === 1 && - parent._uihooks && parent._uihooks.insertElement) { - parent._uihooks.insertElement(n, next); - } else { - parent.insertBefore(n, next); - } -}; - -DOMRange._moveNodeWithHooks = function (n, parent, next) { - if (n.parentNode !== parent) - return; - // `|| null` because IE throws an error if 'next' is undefined - next = next || null; - if (n.nodeType === 1 && - parent._uihooks && parent._uihooks.moveElement) { - parent._uihooks.moveElement(n, next); - } else { - parent.insertBefore(n, next); - } -}; - -DOMRange.forElement = function (elem) { - if (elem.nodeType !== 1) - throw new Error("Expected element, found: " + elem); - var range = null; - while (elem && ! range) { - range = (elem.$blaze_range || null); - if (! range) - elem = elem.parentNode; - } - return range; -}; - -DOMRange.prototype.attach = function (parentElement, nextNode, _isMove, _isReplace) { - // This method is called to insert the DOMRange into the DOM for - // the first time, but it's also used internally when - // updating the DOM. - // - // If _isMove is true, move this attached range to a different - // location under the same parentElement. - if (_isMove || _isReplace) { - if (! (this.parentElement === parentElement && - this.attached)) - throw new Error("Can only move or replace an attached DOMRange, and only under the same parent element"); - } - - var members = this.members; - if (members.length) { - this.emptyRangePlaceholder = null; - for (var i = 0; i < members.length; i++) { - DOMRange._insert(members[i], parentElement, nextNode, _isMove); - } - } else { - var placeholder = ( - DOMRange._USE_COMMENT_PLACEHOLDERS ? - document.createComment("") : - document.createTextNode("")); - this.emptyRangePlaceholder = placeholder; - parentElement.insertBefore(placeholder, nextNode || null); - } - this.attached = true; - this.parentElement = parentElement; - - if (! (_isMove || _isReplace)) { - for(var i = 0; i < this.attachedCallbacks.length; i++) { - var obj = this.attachedCallbacks[i]; - obj.attached && obj.attached(this, parentElement); - } - } -}; - -DOMRange.prototype.setMembers = function (newNodeAndRangeArray) { - var newMembers = newNodeAndRangeArray; - if (! (newMembers && (typeof newMembers.length) === 'number')) - throw new Error("Expected array"); - - var oldMembers = this.members; - - for (var i = 0; i < oldMembers.length; i++) - this._memberOut(oldMembers[i]); - for (var i = 0; i < newMembers.length; i++) - this._memberIn(newMembers[i]); - - if (! this.attached) { - this.members = newMembers; - } else { - // don't do anything if we're going from empty to empty - if (newMembers.length || oldMembers.length) { - // detach the old members and insert the new members - var nextNode = this.lastNode().nextSibling; - var parentElement = this.parentElement; - // Use detach/attach, but don't fire attached/detached hooks - this.detach(true /*_isReplace*/); - this.members = newMembers; - this.attach(parentElement, nextNode, false, true /*_isReplace*/); - } - } -}; - -DOMRange.prototype.firstNode = function () { - if (! this.attached) - throw new Error("Must be attached"); - - if (! this.members.length) - return this.emptyRangePlaceholder; - - var m = this.members[0]; - return (m instanceof DOMRange) ? m.firstNode() : m; -}; - -DOMRange.prototype.lastNode = function () { - if (! this.attached) - throw new Error("Must be attached"); - - if (! this.members.length) - return this.emptyRangePlaceholder; - - var m = this.members[this.members.length - 1]; - return (m instanceof DOMRange) ? m.lastNode() : m; -}; - -DOMRange.prototype.detach = function (_isReplace) { - if (! this.attached) - throw new Error("Must be attached"); - - var oldParentElement = this.parentElement; - var members = this.members; - if (members.length) { - for (var i = 0; i < members.length; i++) { - DOMRange._remove(members[i]); - } - } else { - var placeholder = this.emptyRangePlaceholder; - this.parentElement.removeChild(placeholder); - this.emptyRangePlaceholder = null; - } - - if (! _isReplace) { - this.attached = false; - this.parentElement = null; - - for(var i = 0; i < this.attachedCallbacks.length; i++) { - var obj = this.attachedCallbacks[i]; - obj.detached && obj.detached(this, oldParentElement); - } - } -}; - -DOMRange.prototype.addMember = function (newMember, atIndex, _isMove) { - var members = this.members; - if (! (atIndex >= 0 && atIndex <= members.length)) - throw new Error("Bad index in range.addMember: " + atIndex); - - if (! _isMove) - this._memberIn(newMember); - - if (! this.attached) { - // currently detached; just updated members - members.splice(atIndex, 0, newMember); - } else if (members.length === 0) { - // empty; use the empty-to-nonempty handling of setMembers - this.setMembers([newMember]); - } else { - var nextNode; - if (atIndex === members.length) { - // insert at end - nextNode = this.lastNode().nextSibling; - } else { - var m = members[atIndex]; - nextNode = (m instanceof DOMRange) ? m.firstNode() : m; - } - members.splice(atIndex, 0, newMember); - DOMRange._insert(newMember, this.parentElement, nextNode, _isMove); - } -}; - -DOMRange.prototype.removeMember = function (atIndex, _isMove) { - var members = this.members; - if (! (atIndex >= 0 && atIndex < members.length)) - throw new Error("Bad index in range.removeMember: " + atIndex); - - if (_isMove) { - members.splice(atIndex, 1); - } else { - var oldMember = members[atIndex]; - this._memberOut(oldMember); - - if (members.length === 1) { - // becoming empty; use the logic in setMembers - this.setMembers(_emptyArray); - } else { - members.splice(atIndex, 1); - if (this.attached) - DOMRange._remove(oldMember); - } - } -}; - -DOMRange.prototype.moveMember = function (oldIndex, newIndex) { - var member = this.members[oldIndex]; - this.removeMember(oldIndex, true /*_isMove*/); - this.addMember(member, newIndex, true /*_isMove*/); -}; - -DOMRange.prototype.getMember = function (atIndex) { - var members = this.members; - if (! (atIndex >= 0 && atIndex < members.length)) - throw new Error("Bad index in range.getMember: " + atIndex); - return this.members[atIndex]; -}; - -DOMRange.prototype._memberIn = function (m) { - if (m instanceof DOMRange) - m.parentRange = this; - else if (m.nodeType === 1) // DOM Element - m.$blaze_range = this; -}; - -DOMRange._destroy = function (m, _skipNodes) { - if (m instanceof DOMRange) { - if (m.view) - Blaze._destroyView(m.view, _skipNodes); - } else if ((! _skipNodes) && m.nodeType === 1) { - // DOM Element - if (m.$blaze_range) { - Blaze._destroyNode(m); - m.$blaze_range = null; - } - } -}; - -DOMRange.prototype._memberOut = DOMRange._destroy; - -// Tear down, but don't remove, the members. Used when chunks -// of DOM are being torn down or replaced. -DOMRange.prototype.destroyMembers = function (_skipNodes) { - var members = this.members; - for (var i = 0; i < members.length; i++) - this._memberOut(members[i], _skipNodes); -}; - -DOMRange.prototype.destroy = function (_skipNodes) { - DOMRange._destroy(this, _skipNodes); -}; - -DOMRange.prototype.containsElement = function (elem) { - if (! this.attached) - throw new Error("Must be attached"); - - // An element is contained in this DOMRange if it's possible to - // reach it by walking parent pointers, first through the DOM and - // then parentRange pointers. In other words, the element or some - // ancestor of it is at our level of the DOM (a child of our - // parentElement), and this element is one of our members or - // is a member of a descendant Range. - - // First check that elem is a descendant of this.parentElement, - // according to the DOM. - if (! Blaze._elementContains(this.parentElement, elem)) - return false; - - // If elem is not an immediate child of this.parentElement, - // walk up to its ancestor that is. - while (elem.parentNode !== this.parentElement) - elem = elem.parentNode; - - var range = elem.$blaze_range; - while (range && range !== this) - range = range.parentRange; - - return range === this; -}; - -DOMRange.prototype.containsRange = function (range) { - if (! this.attached) - throw new Error("Must be attached"); - - if (! range.attached) - return false; - - // A DOMRange is contained in this DOMRange if it's possible - // to reach this range by following parent pointers. If the - // DOMRange has the same parentElement, then it should be - // a member, or a member of a member etc. Otherwise, we must - // contain its parentElement. - - if (range.parentElement !== this.parentElement) - return this.containsElement(range.parentElement); - - if (range === this) - return false; // don't contain self - - while (range && range !== this) - range = range.parentRange; - - return range === this; -}; - -DOMRange.prototype.onAttached = function (attached) { - this.onAttachedDetached({ attached: attached }); -}; - -// callbacks are `attached(range, element)` and -// `detached(range, element)`, and they may -// access the `callbacks` object in `this`. -// The arguments to `detached` are the same -// range and element that were passed to `attached`. -DOMRange.prototype.onAttachedDetached = function (callbacks) { - if (this.attachedCallbacks === _emptyArray) - this.attachedCallbacks = []; - this.attachedCallbacks.push(callbacks); -}; - -DOMRange.prototype.$ = function (selector) { - var self = this; - - var parentNode = this.parentElement; - if (! parentNode) - throw new Error("Can't select in removed DomRange"); - - // Strategy: Find all selector matches under parentNode, - // then filter out the ones that aren't in this DomRange - // using `DOMRange#containsElement`. This is - // asymptotically slow in the presence of O(N) sibling - // content that is under parentNode but not in our range, - // so if performance is an issue, the selector should be - // run on a child element. - - // Since jQuery can't run selectors on a DocumentFragment, - // we don't expect findBySelector to work. - if (parentNode.nodeType === 11 /* DocumentFragment */) - throw new Error("Can't use $ on an offscreen range"); - - var results = Blaze._DOMBackend.findBySelector(selector, parentNode); - - // We don't assume `results` has jQuery API; a plain array - // should do just as well. However, if we do have a jQuery - // array, we want to end up with one also, so we use - // `.filter`. - - // Function that selects only elements that are actually - // in this DomRange, rather than simply descending from - // `parentNode`. - var filterFunc = function (elem) { - // handle jQuery's arguments to filter, where the node - // is in `this` and the index is the first argument. - if (typeof elem === 'number') - elem = this; - - return self.containsElement(elem); - }; - - if (! results.filter) { - // not a jQuery array, and not a browser with - // Array.prototype.filter (e.g. IE <9) - var newResults = []; - for (var i = 0; i < results.length; i++) { - var x = results[i]; - if (filterFunc(x)) - newResults.push(x); - } - results = newResults; - } else { - // `results.filter` is either jQuery's or ECMAScript's `filter` - results = results.filter(filterFunc); - } - - return results; -}; - -// Returns true if element a contains node b and is not node b. -// -// The restriction that `a` be an element (not a document fragment, -// say) is based on what's easy to implement cross-browser. -Blaze._elementContains = function (a, b) { - if (a.nodeType !== 1) // ELEMENT - return false; - if (a === b) - return false; - - if (a.compareDocumentPosition) { - return a.compareDocumentPosition(b) & 0x10; - } else { - // Should be only old IE and maybe other old browsers here. - // Modern Safari has both functions but seems to get contains() wrong. - // IE can't handle b being a text node. We work around this - // by doing a direct parent test now. - b = b.parentNode; - if (! (b && b.nodeType === 1)) // ELEMENT - return false; - if (a === b) - return true; - - return a.contains(b); - } -}; diff --git a/packages/blaze/events.js b/packages/blaze/events.js deleted file mode 100644 index 7b9faf10a1..0000000000 --- a/packages/blaze/events.js +++ /dev/null @@ -1,204 +0,0 @@ -var EventSupport = Blaze._EventSupport = {}; - -var DOMBackend = Blaze._DOMBackend; - -// List of events to always delegate, never capture. -// Since jQuery fakes bubbling for certain events in -// certain browsers (like `submit`), we don't want to -// get in its way. -// -// We could list all known bubbling -// events here to avoid creating speculative capturers -// for them, but it would only be an optimization. -var eventsToDelegate = EventSupport.eventsToDelegate = { - blur: 1, change: 1, click: 1, focus: 1, focusin: 1, - focusout: 1, reset: 1, submit: 1 -}; - -var EVENT_MODE = EventSupport.EVENT_MODE = { - TBD: 0, - BUBBLING: 1, - CAPTURING: 2 -}; - -var NEXT_HANDLERREC_ID = 1; - -var HandlerRec = function (elem, type, selector, handler, recipient) { - this.elem = elem; - this.type = type; - this.selector = selector; - this.handler = handler; - this.recipient = recipient; - this.id = (NEXT_HANDLERREC_ID++); - - this.mode = EVENT_MODE.TBD; - - // It's important that delegatedHandler be a different - // instance for each handlerRecord, because its identity - // is used to remove it. - // - // It's also important that the closure have access to - // `this` when it is not called with it set. - this.delegatedHandler = (function (h) { - return function (evt) { - if ((! h.selector) && evt.currentTarget !== evt.target) - // no selector means only fire on target - return; - return h.handler.apply(h.recipient, arguments); - }; - })(this); - - // WHY CAPTURE AND DELEGATE: jQuery can't delegate - // non-bubbling events, because - // event capture doesn't work in IE 8. However, there - // are all sorts of new-fangled non-bubbling events - // like "play" and "touchenter". We delegate these - // events using capture in all browsers except IE 8. - // IE 8 doesn't support these events anyway. - - var tryCapturing = elem.addEventListener && - (! _.has(eventsToDelegate, - DOMBackend.Events.parseEventType(type))); - - if (tryCapturing) { - this.capturingHandler = (function (h) { - return function (evt) { - if (h.mode === EVENT_MODE.TBD) { - // must be first time we're called. - if (evt.bubbles) { - // this type of event bubbles, so don't - // get called again. - h.mode = EVENT_MODE.BUBBLING; - DOMBackend.Events.unbindEventCapturer( - h.elem, h.type, h.capturingHandler); - return; - } else { - // this type of event doesn't bubble, - // so unbind the delegation, preventing - // it from ever firing. - h.mode = EVENT_MODE.CAPTURING; - DOMBackend.Events.undelegateEvents( - h.elem, h.type, h.delegatedHandler); - } - } - - h.delegatedHandler(evt); - }; - })(this); - - } else { - this.mode = EVENT_MODE.BUBBLING; - } -}; -EventSupport.HandlerRec = HandlerRec; - -HandlerRec.prototype.bind = function () { - // `this.mode` may be EVENT_MODE_TBD, in which case we bind both. in - // this case, 'capturingHandler' is in charge of detecting the - // correct mode and turning off one or the other handlers. - if (this.mode !== EVENT_MODE.BUBBLING) { - DOMBackend.Events.bindEventCapturer( - this.elem, this.type, this.selector || '*', - this.capturingHandler); - } - - if (this.mode !== EVENT_MODE.CAPTURING) - DOMBackend.Events.delegateEvents( - this.elem, this.type, - this.selector || '*', this.delegatedHandler); -}; - -HandlerRec.prototype.unbind = function () { - if (this.mode !== EVENT_MODE.BUBBLING) - DOMBackend.Events.unbindEventCapturer(this.elem, this.type, - this.capturingHandler); - - if (this.mode !== EVENT_MODE.CAPTURING) - DOMBackend.Events.undelegateEvents(this.elem, this.type, - this.delegatedHandler); -}; - -EventSupport.listen = function (element, events, selector, handler, recipient, getParentRecipient) { - - // Prevent this method from being JITed by Safari. Due to a - // presumed JIT bug in Safari -- observed in Version 7.0.6 - // (9537.78.2) -- this method may crash the Safari render process if - // it is JITed. - // Repro: https://github.com/dgreensp/public/tree/master/safari-crash - try { element = element; } finally {} - - var eventTypes = []; - events.replace(/[^ /]+/g, function (e) { - eventTypes.push(e); - }); - - var newHandlerRecs = []; - for (var i = 0, N = eventTypes.length; i < N; i++) { - var type = eventTypes[i]; - - var eventDict = element.$blaze_events; - if (! eventDict) - eventDict = (element.$blaze_events = {}); - - var info = eventDict[type]; - if (! info) { - info = eventDict[type] = {}; - info.handlers = []; - } - var handlerList = info.handlers; - var handlerRec = new HandlerRec( - element, type, selector, handler, recipient); - newHandlerRecs.push(handlerRec); - handlerRec.bind(); - handlerList.push(handlerRec); - // Move handlers of enclosing ranges to end, by unbinding and rebinding - // them. In jQuery (or other DOMBackend) this causes them to fire - // later when the backend dispatches event handlers. - if (getParentRecipient) { - for (var r = getParentRecipient(recipient); r; - r = getParentRecipient(r)) { - // r is an enclosing range (recipient) - for (var j = 0, Nj = handlerList.length; - j < Nj; j++) { - var h = handlerList[j]; - if (h.recipient === r) { - h.unbind(); - h.bind(); - handlerList.splice(j, 1); // remove handlerList[j] - handlerList.push(h); - j--; // account for removed handler - Nj--; // don't visit appended handlers - } - } - } - } - } - - return { - // closes over just `element` and `newHandlerRecs` - stop: function () { - var eventDict = element.$blaze_events; - if (! eventDict) - return; - // newHandlerRecs has only one item unless you specify multiple - // event types. If this code is slow, it's because we have to - // iterate over handlerList here. Clearing a whole handlerList - // via stop() methods is O(N^2) in the number of handlers on - // an element. - for (var i = 0; i < newHandlerRecs.length; i++) { - var handlerToRemove = newHandlerRecs[i]; - var info = eventDict[handlerToRemove.type]; - if (! info) - continue; - var handlerList = info.handlers; - for (var j = handlerList.length - 1; j >= 0; j--) { - if (handlerList[j] === handlerToRemove) { - handlerToRemove.unbind(); - handlerList.splice(j, 1); // remove handlerList[j] - } - } - } - newHandlerRecs.length = 0; - } - }; -}; diff --git a/packages/blaze/exceptions.js b/packages/blaze/exceptions.js deleted file mode 100644 index 429ac82f73..0000000000 --- a/packages/blaze/exceptions.js +++ /dev/null @@ -1,56 +0,0 @@ -var debugFunc; - -// We call into user code in many places, and it's nice to catch exceptions -// propagated from user code immediately so that the whole system doesn't just -// break. Catching exceptions is easy; reporting them is hard. This helper -// reports exceptions. -// -// Usage: -// -// ``` -// try { -// // ... someStuff ... -// } catch (e) { -// reportUIException(e); -// } -// ``` -// -// An optional second argument overrides the default message. - -// Set this to `true` to cause `reportException` to throw -// the next exception rather than reporting it. This is -// useful in unit tests that test error messages. -Blaze._throwNextException = false; - -Blaze._reportException = function (e, msg) { - if (Blaze._throwNextException) { - Blaze._throwNextException = false; - throw e; - } - - if (! debugFunc) - // adapted from Tracker - debugFunc = function () { - return (typeof Meteor !== "undefined" ? Meteor._debug : - ((typeof console !== "undefined") && console.log ? console.log : - function () {})); - }; - - // In Chrome, `e.stack` is a multiline string that starts with the message - // and contains a stack trace. Furthermore, `console.log` makes it clickable. - // `console.log` supplies the space between the two arguments. - debugFunc()(msg || 'Exception caught in template:', e.stack || e.message || e); -}; - -Blaze._wrapCatchingExceptions = function (f, where) { - if (typeof f !== 'function') - return f; - - return function () { - try { - return f.apply(this, arguments); - } catch (e) { - Blaze._reportException(e, 'Exception in ' + where + ':'); - } - }; -}; diff --git a/packages/blaze/lookup.js b/packages/blaze/lookup.js deleted file mode 100644 index 932b82054d..0000000000 --- a/packages/blaze/lookup.js +++ /dev/null @@ -1,238 +0,0 @@ -Blaze._globalHelpers = {}; - -// Documented as Template.registerHelper. -// This definition also provides back-compat for `UI.registerHelper`. -Blaze.registerHelper = function (name, func) { - Blaze._globalHelpers[name] = func; -}; - -// Also documented as Template.deregisterHelper -Blaze.deregisterHelper = function(name) { - delete Blaze._globalHelpers[name]; -} - -var bindIfIsFunction = function (x, target) { - if (typeof x !== 'function') - return x; - return _.bind(x, target); -}; - -// If `x` is a function, binds the value of `this` for that function -// to the current data context. -var bindDataContext = function (x) { - if (typeof x === 'function') { - return function () { - var data = Blaze.getData(); - if (data == null) - data = {}; - return x.apply(data, arguments); - }; - } - return x; -}; - -Blaze._OLDSTYLE_HELPER = {}; - -Blaze._getTemplateHelper = function (template, name, tmplInstanceFunc) { - // XXX COMPAT WITH 0.9.3 - var isKnownOldStyleHelper = false; - - if (template.__helpers.has(name)) { - var helper = template.__helpers.get(name); - if (helper === Blaze._OLDSTYLE_HELPER) { - isKnownOldStyleHelper = true; - } else if (helper != null) { - return wrapHelper(bindDataContext(helper), tmplInstanceFunc); - } else { - return null; - } - } - - // old-style helper - if (name in template) { - // Only warn once per helper - if (! isKnownOldStyleHelper) { - template.__helpers.set(name, Blaze._OLDSTYLE_HELPER); - if (! template._NOWARN_OLDSTYLE_HELPERS) { - Blaze._warn('Assigning helper with `' + template.viewName + '.' + - name + ' = ...` is deprecated. Use `' + template.viewName + - '.helpers(...)` instead.'); - } - } - if (template[name] != null) { - return wrapHelper(bindDataContext(template[name]), tmplInstanceFunc); - } - } - - return null; -}; - -var wrapHelper = function (f, templateFunc) { - if (typeof f !== "function") { - return f; - } - - return function () { - var self = this; - var args = arguments; - - return Blaze.Template._withTemplateInstanceFunc(templateFunc, function () { - return Blaze._wrapCatchingExceptions(f, 'template helper').apply(self, args); - }); - }; -}; - -Blaze._lexicalBindingLookup = function (view, name) { - var currentView = view; - var blockHelpersStack = []; - - // walk up the views stopping at a Spacebars.include or Template view that - // doesn't have an InOuterTemplateScope view as a parent - do { - // skip block helpers views - // if we found the binding on the scope, return it - if (_.has(currentView._scopeBindings, name)) { - var bindingReactiveVar = currentView._scopeBindings[name]; - return function () { - return bindingReactiveVar.get(); - }; - } - } while (! (currentView.__startsNewLexicalScope && - ! (currentView.parentView && - currentView.parentView.__childDoesntStartNewLexicalScope)) - && (currentView = currentView.parentView)); - - return null; -}; - -// templateInstance argument is provided to be available for possible -// alternative implementations of this function by 3rd party packages. -Blaze._getTemplate = function (name, templateInstance) { - if ((name in Blaze.Template) && (Blaze.Template[name] instanceof Blaze.Template)) { - return Blaze.Template[name]; - } - return null; -}; - -Blaze._getGlobalHelper = function (name, templateInstance) { - if (Blaze._globalHelpers[name] != null) { - return wrapHelper(bindDataContext(Blaze._globalHelpers[name]), templateInstance); - } - return null; -}; - -// Looks up a name, like "foo" or "..", as a helper of the -// current template; the name of a template; a global helper; -// or a property of the data context. Called on the View of -// a template (i.e. a View with a `.template` property, -// where the helpers are). Used for the first name in a -// "path" in a template tag, like "foo" in `{{foo.bar}}` or -// ".." in `{{frobulate ../blah}}`. -// -// Returns a function, a non-function value, or null. If -// a function is found, it is bound appropriately. -// -// NOTE: This function must not establish any reactive -// dependencies itself. If there is any reactivity in the -// value, lookup should return a function. -Blaze.View.prototype.lookup = function (name, _options) { - var template = this.template; - var lookupTemplate = _options && _options.template; - var helper; - var binding; - var boundTmplInstance; - var foundTemplate; - - if (this.templateInstance) { - boundTmplInstance = _.bind(this.templateInstance, this); - } - - // 0. looking up the parent data context with the special "../" syntax - if (/^\./.test(name)) { - // starts with a dot. must be a series of dots which maps to an - // ancestor of the appropriate height. - if (!/^(\.)+$/.test(name)) - throw new Error("id starting with dot must be a series of dots"); - - return Blaze._parentData(name.length - 1, true /*_functionWrapped*/); - - } - - // 1. look up a helper on the current template - if (template && ((helper = Blaze._getTemplateHelper(template, name, boundTmplInstance)) != null)) { - return helper; - } - - // 2. look up a binding by traversing the lexical view hierarchy inside the - // current template - if (template && (binding = Blaze._lexicalBindingLookup(Blaze.currentView, name)) != null) { - return binding; - } - - // 3. look up a template by name - if (lookupTemplate && ((foundTemplate = Blaze._getTemplate(name, boundTmplInstance)) != null)) { - return foundTemplate; - } - - // 4. look up a global helper - if ((helper = Blaze._getGlobalHelper(name, boundTmplInstance)) != null) { - return helper; - } - - // 5. look up in a data context - return function () { - var isCalledAsFunction = (arguments.length > 0); - var data = Blaze.getData(); - var x = data && data[name]; - if (! x) { - if (lookupTemplate) { - throw new Error("No such template: " + name); - } else if (isCalledAsFunction) { - throw new Error("No such function: " + name); - } else if (name.charAt(0) === '@' && ((x === null) || - (x === undefined))) { - // Throw an error if the user tries to use a `@directive` - // that doesn't exist. We don't implement all directives - // from Handlebars, so there's a potential for confusion - // if we fail silently. On the other hand, we want to - // throw late in case some app or package wants to provide - // a missing directive. - throw new Error("Unsupported directive: " + name); - } - } - if (! data) { - return null; - } - if (typeof x !== 'function') { - if (isCalledAsFunction) { - throw new Error("Can't call non-function: " + x); - } - return x; - } - return x.apply(data, arguments); - }; -}; - -// Implement Spacebars' {{../..}}. -// @param height {Number} The number of '..'s -Blaze._parentData = function (height, _functionWrapped) { - // If height is null or undefined, we default to 1, the first parent. - if (height == null) { - height = 1; - } - var theWith = Blaze.getView('with'); - for (var i = 0; (i < height) && theWith; i++) { - theWith = Blaze.getView(theWith, 'with'); - } - - if (! theWith) - return null; - if (_functionWrapped) - return function () { return theWith.dataVar.get(); }; - return theWith.dataVar.get(); -}; - - -Blaze.View.prototype.lookupTemplate = function (name) { - return this.lookup(name, {template:true}); -}; diff --git a/packages/blaze/materializer.js b/packages/blaze/materializer.js deleted file mode 100644 index 4986750e05..0000000000 --- a/packages/blaze/materializer.js +++ /dev/null @@ -1,191 +0,0 @@ -// Turns HTMLjs into DOM nodes and DOMRanges. -// -// - `htmljs`: the value to materialize, which may be any of the htmljs -// types (Tag, CharRef, Comment, Raw, array, string, boolean, number, -// null, or undefined) or a View or Template (which will be used to -// construct a View). -// - `intoArray`: the array of DOM nodes and DOMRanges to push the output -// into (required) -// - `parentView`: the View we are materializing content for (optional) -// - `_existingWorkStack`: optional argument, only used for recursive -// calls when there is some other _materializeDOM on the call stack. -// If _materializeDOM called your function and passed in a workStack, -// pass it back when you call _materializeDOM (such as from a workStack -// task). -// -// Returns `intoArray`, which is especially useful if you pass in `[]`. -Blaze._materializeDOM = function (htmljs, intoArray, parentView, - _existingWorkStack) { - // In order to use fewer stack frames, materializeDOMInner can push - // tasks onto `workStack`, and they will be popped off - // and run, last first, after materializeDOMInner returns. The - // reason we use a stack instead of a queue is so that we recurse - // depth-first, doing newer tasks first. - var workStack = (_existingWorkStack || []); - materializeDOMInner(htmljs, intoArray, parentView, workStack); - - if (! _existingWorkStack) { - // We created the work stack, so we are responsible for finishing - // the work. Call each "task" function, starting with the top - // of the stack. - while (workStack.length) { - // Note that running task() may push new items onto workStack. - var task = workStack.pop(); - task(); - } - } - - return intoArray; -}; - -var materializeDOMInner = function (htmljs, intoArray, parentView, workStack) { - if (htmljs == null) { - // null or undefined - return; - } - - switch (typeof htmljs) { - case 'string': case 'boolean': case 'number': - intoArray.push(document.createTextNode(String(htmljs))); - return; - case 'object': - if (htmljs.htmljsType) { - switch (htmljs.htmljsType) { - case HTML.Tag.htmljsType: - intoArray.push(materializeTag(htmljs, parentView, workStack)); - return; - case HTML.CharRef.htmljsType: - intoArray.push(document.createTextNode(htmljs.str)); - return; - case HTML.Comment.htmljsType: - intoArray.push(document.createComment(htmljs.sanitizedValue)); - return; - case HTML.Raw.htmljsType: - // Get an array of DOM nodes by using the browser's HTML parser - // (like innerHTML). - var nodes = Blaze._DOMBackend.parseHTML(htmljs.value); - for (var i = 0; i < nodes.length; i++) - intoArray.push(nodes[i]); - return; - } - } else if (HTML.isArray(htmljs)) { - for (var i = htmljs.length-1; i >= 0; i--) { - workStack.push(_.bind(Blaze._materializeDOM, null, - htmljs[i], intoArray, parentView, workStack)); - } - return; - } else { - if (htmljs instanceof Blaze.Template) { - htmljs = htmljs.constructView(); - // fall through to Blaze.View case below - } - if (htmljs instanceof Blaze.View) { - Blaze._materializeView(htmljs, parentView, workStack, intoArray); - return; - } - } - } - - throw new Error("Unexpected object in htmljs: " + htmljs); -}; - -var materializeTag = function (tag, parentView, workStack) { - var tagName = tag.tagName; - var elem; - if ((HTML.isKnownSVGElement(tagName) || isSVGAnchor(tag)) - && document.createElementNS) { - // inline SVG - elem = document.createElementNS('http://www.w3.org/2000/svg', tagName); - } else { - // normal elements - elem = document.createElement(tagName); - } - - var rawAttrs = tag.attrs; - var children = tag.children; - if (tagName === 'textarea' && tag.children.length && - ! (rawAttrs && ('value' in rawAttrs))) { - // Provide very limited support for TEXTAREA tags with children - // rather than a "value" attribute. - // Reactivity in the form of Views nested in the tag's children - // won't work. Compilers should compile textarea contents into - // the "value" attribute of the tag, wrapped in a function if there - // is reactivity. - if (typeof rawAttrs === 'function' || - HTML.isArray(rawAttrs)) { - throw new Error("Can't have reactive children of TEXTAREA node; " + - "use the 'value' attribute instead."); - } - rawAttrs = _.extend({}, rawAttrs || null); - rawAttrs.value = Blaze._expand(children, parentView); - children = []; - } - - if (rawAttrs) { - var attrUpdater = new ElementAttributesUpdater(elem); - var updateAttributes = function () { - var expandedAttrs = Blaze._expandAttributes(rawAttrs, parentView); - var flattenedAttrs = HTML.flattenAttributes(expandedAttrs); - var stringAttrs = {}; - for (var attrName in flattenedAttrs) { - stringAttrs[attrName] = Blaze._toText(flattenedAttrs[attrName], - parentView, - HTML.TEXTMODE.STRING); - } - attrUpdater.update(stringAttrs); - }; - var updaterComputation; - if (parentView) { - updaterComputation = - parentView.autorun(updateAttributes, undefined, 'updater'); - } else { - updaterComputation = Tracker.nonreactive(function () { - return Tracker.autorun(function () { - Tracker._withCurrentView(parentView, updateAttributes); - }); - }); - } - Blaze._DOMBackend.Teardown.onElementTeardown(elem, function attrTeardown() { - updaterComputation.stop(); - }); - } - - if (children.length) { - var childNodesAndRanges = []; - // push this function first so that it's done last - workStack.push(function () { - for (var i = 0; i < childNodesAndRanges.length; i++) { - var x = childNodesAndRanges[i]; - if (x instanceof Blaze._DOMRange) - x.attach(elem); - else - elem.appendChild(x); - } - }); - // now push the task that calculates childNodesAndRanges - workStack.push(_.bind(Blaze._materializeDOM, null, - children, childNodesAndRanges, parentView, - workStack)); - } - - return elem; -}; - - -var isSVGAnchor = function (node) { - // We generally aren't able to detect SVG elements because - // if "A" were in our list of known svg element names, then all - // nodes would be created using - // `document.createElementNS`. But in the special case of , we can at least detect that attribute and - // create an SVG tag in that case. - // - // However, we still have a general problem of knowing when to - // use document.createElementNS and when to use - // document.createElement; for example, font tags will always - // be created as SVG elements which can cause other - // problems. #1977 - return (node.tagName === "a" && - node.attrs && - node.attrs["xlink:href"] !== undefined); -}; diff --git a/packages/blaze/microscore.js b/packages/blaze/microscore.js deleted file mode 100644 index 56e78f6659..0000000000 --- a/packages/blaze/microscore.js +++ /dev/null @@ -1,116 +0,0 @@ -// Microscore is a partial polyfill for Underscore. It implements -// a subset of Underscore functions, and for some functions it -// implements a subset of the full functionality. -// -// Code written against Microscore should just work with Underscore. -// The reverse is not true, because Microscore doesn't support -// all features of every function. A list of known differences -// between Underscore and Microscore is given with each function. -// -// This file should be curated to keep it small, so that it doesn't -// grow into Underscore. -// -// In the future, we'll figure out something better, like package -// slices and dead code elimination. - -if (typeof _ !== 'undefined') - throw new Error("If you have Underscore, don't use Microscore"); - -_ = {}; - -var hasOwnProperty = Object.prototype.hasOwnProperty; -var objectToString = Object.prototype.toString; - -// Doesn't support more than two arguments (more than one "source" -// object). -_.extend = function (tgt, src) { - for (var k in src) { - if (hasOwnProperty.call(src, k)) - tgt[k] = src[k]; - } - return tgt; -}; - -_.has = function (obj, key) { - return hasOwnProperty.call(obj, key); -}; - -// Returns a copy of `array` with falsy elements removed. -_.compact = function (array) { - var result = []; - for (var i = 0; i < array.length; i++) { - var item = array[i]; - if (item) - result.push(item); - } - return result; -}; - -// Returns whether `array` contains an element that is -// `=== item`. -_.contains = function (array, item) { - for (var i = 0; i < array.length; i++) { - if (array[i] === item) - return true; - } - return false; -}; - -// Returns `array` filtered to exclude elements that are -// `=== item`. Similar to `_.without`. -_.without = function (array, item) { - var result = []; - for (var i = 0; i < array.length; i++) { - var x = array[i]; - if (x !== item) - result.push(x); - } - return result; -}; - -// Assembles an array by calling `func(oldElement, index)` -// on each element of `array`. Assumes argument is an array. -_.map = function (array, func) { - var result = new Array(array.length); - for (var i = 0; i < array.length; i++) { - result[i] = func(array[i], i); - } - return result; -}; - -// Given an array: Calls `func(element, index)` on each element of -// `array`. -// -// Given an object: Calls `func(value, key)` on each key/value of -// `obj`. -// -// Only REAL arrays are treated as arrays. No Arguments objects, jQuery -// objects, etc. This may be relaxed to the standard Meteor approach -// if it is too constraining. -// -// Doesn't accept `null` as first argument. Doesn't delegate to built-in -// `forEach` (which is generally not faster anyway because it calls -// across the C/JS boundary). Doesn't mess with JavaScript's built-in -// behavior if keys are added or removed during iteration (i.e. may -// or may not visit them). - -_.each = function (arrayOrObject, func) { - if (objectToString.call(arrayOrObject) === '[object Array]') { - var array = arrayOrObject; - for (var i = 0; i < array.length; i++) { - func(array[i], i); - } - } else { - var obj = arrayOrObject; - for (var key in obj) { - if (_.has(obj, key)) - func(obj[key], key); - } - } -}; - -_.bind = function (f, context) { - return function () { - return f.apply(target, context); - }; -}; diff --git a/packages/blaze/package.js b/packages/blaze/package.js deleted file mode 100644 index 9bcacbb5bd..0000000000 --- a/packages/blaze/package.js +++ /dev/null @@ -1,55 +0,0 @@ -Package.describe({ - summary: "Meteor Reactive Templating library", - version: '2.1.8' -}); - -Package.onUse(function (api) { - api.export(['Blaze', 'UI', 'Handlebars']); - api.use('jquery'); // should be a weak dep, by having multiple "DOM backends" - api.use('tracker'); - api.use('check'); - api.use('underscore'); // only the subset in microscore.js - api.use('htmljs'); - api.imply('htmljs'); - api.use('observe-sequence'); - api.use('reactive-var'); - - api.addFiles([ - 'preamble.js' - ]); - - // client-only files - api.addFiles([ - 'dombackend.js', - 'domrange.js', - 'events.js', - 'attrs.js', - 'materializer.js' - ], 'client'); - - // client and server - api.addFiles([ - 'exceptions.js', - 'view.js', - 'builtins.js', - 'lookup.js', - 'template.js', - 'backcompat.js' - ]); -}); - -Package.onTest(function (api) { - api.use('tinytest'); - api.use('jquery'); // strong dependency, for testing jQuery backend - api.use('blaze'); - api.use('test-helpers'); - api.use('underscore'); - api.use('blaze-tools'); // for BlazeTools.toJS - api.use('html-tools'); - api.use('reactive-var'); - api.use('tracker'); - api.use('templating'); - - api.addFiles('view_tests.js'); - api.addFiles('render_tests.js', 'client'); -}); diff --git a/packages/blaze/preamble.js b/packages/blaze/preamble.js deleted file mode 100644 index aba9384a76..0000000000 --- a/packages/blaze/preamble.js +++ /dev/null @@ -1,32 +0,0 @@ -/** - * @namespace Blaze - * @summary The namespace for all Blaze-related methods and classes. - */ -Blaze = {}; - -// Utility to HTML-escape a string. Included for legacy reasons. -Blaze._escape = (function() { - var escape_map = { - "<": "<", - ">": ">", - '"': """, - "'": "'", - "`": "`", /* IE allows backtick-delimited attributes?? */ - "&": "&" - }; - var escape_one = function(c) { - return escape_map[c]; - }; - - return function (x) { - return x.replace(/[&<>"'`]/g, escape_one); - }; -})(); - -Blaze._warn = function (msg) { - msg = 'Warning: ' + msg; - - if ((typeof console !== 'undefined') && console.warn) { - console.warn(msg); - } -}; diff --git a/packages/blaze/render_tests.js b/packages/blaze/render_tests.js deleted file mode 100644 index bccc001716..0000000000 --- a/packages/blaze/render_tests.js +++ /dev/null @@ -1,688 +0,0 @@ -var toCode = BlazeTools.toJS; - -var P = HTML.P; -var CharRef = HTML.CharRef; -var DIV = HTML.DIV; -var Comment = HTML.Comment; -var BR = HTML.BR; -var A = HTML.A; -var UL = HTML.UL; -var LI = HTML.LI; -var SPAN = HTML.SPAN; -var HR = HTML.HR; -var TEXTAREA = HTML.TEXTAREA; -var INPUT = HTML.INPUT; - -var materialize = function (content, parent) { - var func = content; - if (typeof content !== 'function') { - func = function () { - return content; - }; - } - Blaze.render(func, parent); -}; - -var toHTML = Blaze.toHTML; - -Tinytest.add("blaze - render - basic", function (test) { - var run = function (input, expectedInnerHTML, expectedHTML, expectedCode) { - var div = document.createElement("DIV"); - materialize(input, div); - test.equal(canonicalizeHtml(div.innerHTML), expectedInnerHTML); - test.equal(toHTML(input), expectedHTML); - if (typeof expectedCode !== 'undefined') - test.equal(toCode(input), expectedCode); - }; - - run(P('Hello'), - '

Hello

', - '

Hello

', - 'HTML.P("Hello")'); - - run([], '', '', '[]'); - run([null, null], '', '', '[null, null]'); - - // Test crazy character references - - // `𝕫` is "Mathematical double-struck small z" a.k.a. "open-face z" - run(P(CharRef({html: '𝕫', str: '\ud835\udd6b'})), - '

\ud835\udd6b

', - '

𝕫

', - 'HTML.P(HTML.CharRef({html: "𝕫", str: "\\ud835\\udd6b"}))'); - - run(P({id: CharRef({html: '𝕫', str: '\ud835\udd6b'})}, 'Hello'), - '

Hello

', - '

Hello

', - 'HTML.P({id: HTML.CharRef({html: "𝕫", str: "\\ud835\\udd6b"})}, "Hello")'); - - run(P({id: [CharRef({html: '𝕫', str: '\ud835\udd6b'}), '!']}, 'Hello'), - '

Hello

', - '

Hello

', - 'HTML.P({id: [HTML.CharRef({html: "𝕫", str: "\\ud835\\udd6b"}), "!"]}, "Hello")'); - - // Test comments - - run(DIV(Comment('Test')), - '
', // our innerHTML-canonicalization function kills comment contents - '
', - 'HTML.DIV(HTML.Comment("Test"))'); - - // Test arrays - - run([P('Hello'), P('World')], - '

Hello

World

', - '

Hello

World

', - '[HTML.P("Hello"), HTML.P("World")]'); - - // Test slightly more complicated structure - - run(DIV({'class': 'foo'}, UL(LI(P(A({href: '#one'}, 'One'))), - LI(P('Two', BR(), 'Three')))), - '
', - '
', - 'HTML.DIV({"class": "foo"}, HTML.UL(HTML.LI(HTML.P(HTML.A({href: "#one"}, "One"))), HTML.LI(HTML.P("Two", HTML.BR(), "Three"))))'); - - - // Test nully attributes - run(BR({x: null, - y: [[], []], - a: [['']]}), - '
', - '
', - '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()), '

Hello

'); - - var div = document.createElement("DIV"); - materialize(test1, div); - test.equal(canonicalizeHtml(div.innerHTML), "

Hello

"); - - R.set('World'); - Tracker.flush(); - test.equal(canonicalizeHtml(div.innerHTML), "

World

"); - })(); - - // Reactively change an array of text nodes - (function () { - var R = ReactiveVar(['Hello', ' World']); - var test1 = function () { - return P(Blaze.View(function () { return R.get(); })); - }; - - test.equal(toHTML(test1()), '

Hello World

'); - - var div = document.createElement("DIV"); - materialize(test1, div); - test.equal(canonicalizeHtml(div.innerHTML), "

Hello World

"); - - R.set(['Goodbye', ' World']); - Tracker.flush(); - test.equal(canonicalizeHtml(div.innerHTML), "

Goodbye World

"); - })(); - -}); - -// IE strips malformed styles like "bar::d" from the `style` -// attribute. We detect this to adjust expectations for the StyleHandler -// test below. -var malformedStylesAllowed = function () { - var div = document.createElement("div"); - div.setAttribute("style", "bar::d;"); - return (div.getAttribute("style") === "bar::d;"); -}; - -Tinytest.add("blaze - render - view GC", function (test) { - // test that removing parent element removes listeners and stops autoruns. - (function () { - var R = ReactiveVar('Hello'); - var test1 = P(Blaze.View(function () { return R.get(); })); - - var div = document.createElement("DIV"); - materialize(test1, div); - test.equal(canonicalizeHtml(div.innerHTML), "

Hello

"); - - R.set('World'); - Tracker.flush(); - test.equal(canonicalizeHtml(div.innerHTML), "

World

"); - - test.equal(R._numListeners(), 1); - - $(div).remove(); - - test.equal(R._numListeners(), 0); - - R.set('Steve'); - Tracker.flush(); - // should not have changed: - test.equal(canonicalizeHtml(div.innerHTML), "

World

"); - })(); - -}); - -Tinytest.add("blaze - render - reactive attributes", function (test) { - (function () { - var R = ReactiveVar({'class': ['david gre', CharRef({html: 'ë', str: '\u00eb'}), 'nspan'], - id: 'foo'}); - - var spanFunc = function () { - return SPAN(HTML.Attrs( - function () { return R.get(); })); - }; - - test.equal(Blaze.toHTML(spanFunc()), - ''); - - test.equal(R._numListeners(), 0); - - var div = document.createElement("DIV"); - Blaze.render(spanFunc, div); - test.equal(canonicalizeHtml(div.innerHTML), ''); - - test.equal(R._numListeners(), 1); - - var span = div.firstChild; - test.equal(span.nodeName, 'SPAN'); - span.className += ' blah'; // change the element's class outside of Blaze. this simulates what a jQuery could do - - R.set({'class': 'david smith', id: 'bar'}); - Tracker.flush(); - test.equal(canonicalizeHtml(div.innerHTML), ''); - test.equal(R._numListeners(), 1); - - R.set({}); - Tracker.flush(); - test.equal(canonicalizeHtml(div.innerHTML), ''); - test.equal(R._numListeners(), 1); - - $(div).remove(); - - test.equal(R._numListeners(), 0); - })(); - - // Test styles. - (function () { - // Test the case where there is a semicolon in the css attribute. - var R = ReactiveVar({'style': 'foo: "a;aa"; bar: b;', - id: 'foo'}); - - var spanFunc = function () { - return SPAN(HTML.Attrs(function () { return R.get(); })); - }; - - test.equal(Blaze.toHTML(spanFunc()), ''); - - test.equal(R._numListeners(), 0); - - var div = document.createElement("DIV"); - Blaze.render(spanFunc, div); - test.equal(canonicalizeHtml(div.innerHTML), ''); - - test.equal(R._numListeners(), 1); - var span = div.firstChild; - test.equal(span.nodeName, 'SPAN'); - - span.setAttribute('style', span.getAttribute('style') + '; jquery-style: hidden'); - - R.set({'style': 'foo: "a;zz;aa";', id: 'bar'}); - Tracker.flush(); - test.equal(canonicalizeHtml(div.innerHTML, true), ''); - test.equal(R._numListeners(), 1); - - R.set({}); - Tracker.flush(); - test.equal(canonicalizeHtml(div.innerHTML), ''); - test.equal(R._numListeners(), 1); - - $(div).remove(); - - test.equal(R._numListeners(), 0); - })(); - - // Test that identical styles are successfully overwritten. - (function () { - - var R = ReactiveVar({'style': 'foo: a;'}); - - var spanFunc = function () { - return SPAN(HTML.Attrs(function () { return R.get(); })); - }; - - var div = document.createElement("DIV"); - document.body.appendChild(div); - Blaze.render(spanFunc, div); - test.equal(canonicalizeHtml(div.innerHTML), ''); - - var span = div.firstChild; - test.equal(span.nodeName, 'SPAN'); - span.setAttribute("style", 'foo: b;'); - test.equal(canonicalizeHtml(div.innerHTML), ''); - - R.set({'style': 'foo: c;'}); - Tracker.flush(); - test.equal(canonicalizeHtml(div.innerHTML), ''); - - // test malformed styles - different expectations in IE (which - // strips malformed styles) from other browsers - R.set({'style': 'foo: a; bar::d;:e; baz: c;'}); - Tracker.flush(); - test.equal(canonicalizeHtml(div.innerHTML), - malformedStylesAllowed() ? - '' : - ''); - - // Test strange styles - R.set({'style': ' foo: c; constructor: a; __proto__: b;'}); - Tracker.flush(); - test.equal(canonicalizeHtml(div.innerHTML), ''); - - R.set({}); - Tracker.flush(); - test.equal(canonicalizeHtml(div.innerHTML), ''); - - R.set({'style': 'foo: bar;'}); - Tracker.flush(); - test.equal(canonicalizeHtml(div.innerHTML), ''); - })(); - - // Test `null`, `undefined`, and `[]` attributes - (function () { - var R = ReactiveVar({id: 'foo', - aaa: null, - bbb: undefined, - ccc: [], - ddd: [null], - eee: [undefined], - fff: [[]], - ggg: ['x', ['y', ['z']]]}); - - var spanFunc = function () { - return SPAN(HTML.Attrs( - function () { return R.get(); })); - }; - - test.equal(Blaze.toHTML(spanFunc()), ''); - test.equal(toCode(SPAN(R.get())), - 'HTML.SPAN({id: "foo", ggg: ["x", ["y", ["z"]]]})'); - - var div = document.createElement("DIV"); - Blaze.render(spanFunc, div); - var span = div.firstChild; - test.equal(span.nodeName, 'SPAN'); - - test.equal(canonicalizeHtml(div.innerHTML), ''); - R.set({id: 'foo', ggg: [[], [], []]}); - Tracker.flush(); - test.equal(canonicalizeHtml(div.innerHTML), ''); - - R.set({id: 'foo', ggg: null}); - Tracker.flush(); - test.equal(canonicalizeHtml(div.innerHTML), ''); - - R.set({id: 'foo', ggg: ''}); - Tracker.flush(); - test.equal(canonicalizeHtml(div.innerHTML), ''); - - $(div).remove(); - - test.equal(R._numListeners(), 0); - })(); -}); - -Tinytest.add("blaze - render - templates and views", function (test) { - (function () { - var counter = 1; - var buf = []; - - var myTemplate = Blaze.Template( - 'myTemplate', - function () { - return [String(this.number), - (this.number < 3 ? makeView() : HR())]; - }); - - myTemplate.constructView = function (number) { - var view = Template.prototype.constructView.call(this); - view.number = number; - return view; - }; - - myTemplate.created = function () { - test.isFalse(Tracker.active); - var view = this.view; - var parent = Blaze.getView(view, 'myTemplate'); - if (parent) { - buf.push('parent of ' + view.number + ' is ' + - parent.number); - } - - buf.push('created ' + Template.currentData()); - }; - - myTemplate.onRendered(function () { - test.isFalse(Tracker.active); - var nodeDescr = function (node) { - if (node.nodeType === 8) // comment - return ''; - if (node.nodeType === 3) // text - return node.nodeValue; - - return node.nodeName; - }; - - var view = this.view; - var start = view.firstNode(); - var end = view.lastNode(); - // skip marker nodes - while (start !== end && ! nodeDescr(start)) - start = start.nextSibling; - while (end !== start && ! nodeDescr(end)) - end = end.previousSibling; - - buf.push('dom-' + Template.currentData() + - ' is ' + nodeDescr(start) +'..' + - nodeDescr(end)); - }); - - myTemplate.onDestroyed(function () { - test.isFalse(Tracker.active); - buf.push('destroyed ' + Template.currentData()); - }); - - var makeView = function () { - var number = counter++; - return Blaze.With(number, function () { - return myTemplate.constructView(number); - }); - }; - - var div = document.createElement("DIV"); - - Blaze.render(makeView, div); - buf.push('---flush---'); - Tracker.flush(); - test.equal(buf, ['created 1', - 'parent of 2 is 1', - 'created 2', - 'parent of 3 is 2', - 'created 3', - '---flush---', - // (proper order for these has not be thought out:) - 'dom-3 is 3..HR', - 'dom-2 is 2..HR', - 'dom-1 is 1..HR']); - - test.equal(canonicalizeHtml(div.innerHTML), '123
'); - - 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'); -}); diff --git a/packages/blaze/template.js b/packages/blaze/template.js deleted file mode 100644 index 4a6b5842d8..0000000000 --- a/packages/blaze/template.js +++ /dev/null @@ -1,565 +0,0 @@ -// [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; diff --git a/packages/blaze/view.js b/packages/blaze/view.js deleted file mode 100644 index 3a17d3487d..0000000000 --- a/packages/blaze/view.js +++ /dev/null @@ -1,900 +0,0 @@ -/// [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; - }); -}; diff --git a/packages/blaze/view_tests.js b/packages/blaze/view_tests.js deleted file mode 100644 index c9f271723d..0000000000 --- a/packages/blaze/view_tests.js +++ /dev/null @@ -1,59 +0,0 @@ -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, ""); - }); - -} diff --git a/packages/boilerplate-generator/README.md b/packages/boilerplate-generator/README.md index ec37b267c6..8521a83c87 100644 --- a/packages/boilerplate-generator/README.md +++ b/packages/boilerplate-generator/README.md @@ -1 +1,5 @@ +# boilerplate-generator +[Source code of released version](https://github.com/meteor/meteor/tree/master/packages/boilerplate-generator) | [Source code of development version](https://github.com/meteor/meteor/tree/devel/packages/boilerplate-generator) +*** + This is an internal Meteor package. \ No newline at end of file diff --git a/packages/boilerplate-generator/boilerplate_web.cordova.html b/packages/boilerplate-generator/boilerplate_web.cordova.html index c2776afafe..5cb90f70f9 100644 --- a/packages/boilerplate-generator/boilerplate_web.cordova.html +++ b/packages/boilerplate-generator/boilerplate_web.cordova.html @@ -4,7 +4,7 @@ - + {{! We are explicitly not using bundledJsCssUrlRewriteHook: in cordova we serve assets up directly from disk, so rewriting the URL does not make sense }} diff --git a/packages/boilerplate-generator/package.js b/packages/boilerplate-generator/package.js index 9b4e1e08f1..357701a5a2 100644 --- a/packages/boilerplate-generator/package.js +++ b/packages/boilerplate-generator/package.js @@ -1,11 +1,16 @@ Package.describe({ summary: "Generates the boilerplate html from program's manifest", - version: '1.0.9' + version: '1.0.11' }); Package.onUse(function (api) { - api.use(['underscore', 'spacebars-compiler', - 'spacebars', 'htmljs', 'ui'], 'server'); + api.use([ + 'underscore@1.0.9', + 'spacebars-compiler@1.0.12', + 'spacebars@1.0.12', + 'htmljs@1.0.10', + 'ui@1.0.11', + ], 'server'); api.addFiles(['boilerplate-generator.js'], 'server'); api.export(['Boilerplate'], 'server'); // These are spacebars templates, but we process them manually with the diff --git a/packages/browser-policy-common/README.md b/packages/browser-policy-common/README.md index 37f8a88562..79dee7dd8c 100644 --- a/packages/browser-policy-common/README.md +++ b/packages/browser-policy-common/README.md @@ -1,4 +1,6 @@ # browser-policy-common +[Source code of released version](https://github.com/meteor/meteor/tree/master/packages/browser-policy-common) | [Source code of development version](https://github.com/meteor/meteor/tree/devel/packages/browser-policy-common) +*** This is one of a family of packages that can be used to easily configure an app's browser-side security policies. The documentation diff --git a/packages/browser-policy-common/package.js b/packages/browser-policy-common/package.js index d306854849..85c2554c3e 100644 --- a/packages/browser-policy-common/package.js +++ b/packages/browser-policy-common/package.js @@ -1,6 +1,6 @@ Package.describe({ summary: "Common code for browser-policy packages", - version: "1.0.10" + version: "1.0.11" }); Package.onUse(function (api) { diff --git a/packages/browser-policy-content/README.md b/packages/browser-policy-content/README.md index 864b2f283f..29a35c7ea6 100644 --- a/packages/browser-policy-content/README.md +++ b/packages/browser-policy-content/README.md @@ -1,4 +1,6 @@ # browser-policy-content +[Source code of released version](https://github.com/meteor/meteor/tree/master/packages/browser-policy-content) | [Source code of development version](https://github.com/meteor/meteor/tree/devel/packages/browser-policy-content) +*** This is one of a family of packages that can be used to easily configure an app's browser-side security policies. The documentation diff --git a/packages/browser-policy-content/browser-policy-content.js b/packages/browser-policy-content/browser-policy-content.js index 78738313e4..958fd18f7b 100644 --- a/packages/browser-policy-content/browser-policy-content.js +++ b/packages/browser-policy-content/browser-policy-content.js @@ -17,7 +17,7 @@ // disallowEval() // // For each type of content (script, object, image, media, font, connect, -// style), there are the following functions: +// style, frame, frame-ancestors), there are the following functions: // allowOrigin(origin): allows the type of content to be loaded // from the given origin // allowDataUrl(): allows the content to be loaded from data: URLs @@ -102,19 +102,26 @@ var addSourceForDirective = function (directive, src) { if (_.contains(_.values(keywords), src)) { cspSrcs[directive].push(src); } else { - src = src.toLowerCase(); - - // Trim trailing slashes. - src = src.replace(/\/+$/, ''); - var toAdd = []; - // If there is no protocol, add both http:// and https://. - if (! /^([a-z0-9.+-]+:)/.test(src)) { - toAdd.push("http://" + src); - toAdd.push("https://" + src); + + //Only add single quotes to CSP2 script digests + if (/^(sha(256|384|512)-)/i.test(src)) { + toAdd.push("'" + src + "'"); } else { - toAdd.push(src); + src = src.toLowerCase(); + + // Trim trailing slashes. + src = src.replace(/\/+$/, ''); + + // If there is no protocol, add both http:// and https://. + if (! /^([a-z0-9.+-]+:)/.test(src)) { + toAdd.push("http://" + src); + toAdd.push("https://" + src); + } else { + toAdd.push(src); + } } + _.each(toAdd, function (s) { cspSrcs[directive].push(s); }); @@ -241,53 +248,55 @@ _.extend(BrowserPolicy.content, { // allowOrigin, allowData, allowself, and // disallow methods for each type of resource. -_.each(["script", "object", "img", "media", - "font", "connect", "style", "frame"], - function (resource) { - var directive = resource + "-src"; - var methodResource; - if (resource !== "img") { - methodResource = resource.charAt(0).toUpperCase() + - resource.slice(1); - } else { - methodResource = "Image"; - } - var allowMethodName = "allow" + methodResource + "Origin"; - var disallowMethodName = "disallow" + methodResource; - var allowDataMethodName = "allow" + methodResource + "DataUrl"; - var allowBlobMethodName = "allow" + methodResource + "BlobUrl"; - var allowSelfMethodName = "allow" + methodResource + "SameOrigin"; +var resources = [ + { methodResource: "Script", directive: "script-src" }, + { methodResource: "Object", directive: "object-src" }, + { methodResource: "Image", directive: "img-src" }, + { methodResource: "Media", directive: "media-src" }, + { methodResource: "Font", directive: "font-src" }, + { methodResource: "Connect", directive: "connect-src" }, + { methodResource: "Style", directive: "style-src" }, + { methodResource: "Frame", directive: "frame-src" }, + { methodResource: "FrameAncestors", directive: "frame-ancestors" } +]; +_.each(resources,
 function (resource) { + var directive = resource.directive;
 + var methodResource = resource.methodResource;
 + var allowMethodName = "allow" + methodResource + "Origin"; + var disallowMethodName = "disallow" + methodResource; + var allowDataMethodName = "allow" + methodResource + "DataUrl";
 + var allowBlobMethodName = "allow" + methodResource + "BlobUrl"; + var allowSelfMethodName = "allow" + methodResource + "SameOrigin"; - var disallow = function () { - cachedCsp = null; - cspSrcs[directive] = []; - }; - - BrowserPolicy.content[allowMethodName] = function (src) { - prepareForCspDirective(directive); - addSourceForDirective(directive, src); - }; - if (resource === "script") { - BrowserPolicy.content[disallowMethodName] = function () { - disallow(); - setWebAppInlineScripts(false); - }; - } else { - BrowserPolicy.content[disallowMethodName] = disallow; - } - BrowserPolicy.content[allowDataMethodName] = function () { - prepareForCspDirective(directive); - cspSrcs[directive].push("data:"); - }; - BrowserPolicy.content[allowBlobMethodName] = function () { - prepareForCspDirective(directive); - cspSrcs[directive].push("blob:"); - }; - BrowserPolicy.content[allowSelfMethodName] = function () { - prepareForCspDirective(directive); - cspSrcs[directive].push(keywords.self); - }; - }); + var disallow = function () { + cachedCsp = null; + cspSrcs[directive] = []; + }; + BrowserPolicy.content[allowMethodName] = function (src) { + prepareForCspDirective(directive); + addSourceForDirective(directive, src); + }; + if (resource === "script") { + BrowserPolicy.content[disallowMethodName] = function () { + disallow(); + setWebAppInlineScripts(false); + }; + } else { + BrowserPolicy.content[disallowMethodName] = disallow; + } + BrowserPolicy.content[allowDataMethodName] = function () { + prepareForCspDirective(directive); + cspSrcs[directive].push("data:"); + }; + BrowserPolicy.content[allowBlobMethodName] = function () { + prepareForCspDirective(directive); + cspSrcs[directive].push("blob:"); + }; + BrowserPolicy.content[allowSelfMethodName] = function () { + prepareForCspDirective(directive); + cspSrcs[directive].push(keywords.self); + }; +}); setDefaultPolicy(); diff --git a/packages/browser-policy-content/package.js b/packages/browser-policy-content/package.js index 2f13f124eb..780c836df8 100644 --- a/packages/browser-policy-content/package.js +++ b/packages/browser-policy-content/package.js @@ -1,6 +1,6 @@ Package.describe({ summary: "Configure content security policies", - version: "1.0.11" + version: "1.0.13-beta.1" }); Package.onUse(function (api) { diff --git a/packages/browser-policy-framing/README.md b/packages/browser-policy-framing/README.md index bdf41b0189..0e1bae14ba 100644 --- a/packages/browser-policy-framing/README.md +++ b/packages/browser-policy-framing/README.md @@ -1,4 +1,6 @@ # browser-policy-framing +[Source code of released version](https://github.com/meteor/meteor/tree/master/packages/browser-policy-framing) | [Source code of development version](https://github.com/meteor/meteor/tree/devel/packages/browser-policy-framing) +*** This is one of a family of packages that can be used to easily configure an app's browser-side security policies. The documentation diff --git a/packages/browser-policy-framing/package.js b/packages/browser-policy-framing/package.js index e87937ae29..a9ebddde15 100644 --- a/packages/browser-policy-framing/package.js +++ b/packages/browser-policy-framing/package.js @@ -1,6 +1,6 @@ Package.describe({ summary: "Restrict which websites can frame your app", - version: "1.0.11" + version: "1.0.12" }); Package.onUse(function (api) { diff --git a/packages/browser-policy/README.md b/packages/browser-policy/README.md index 9525170ef1..53cb941e32 100644 --- a/packages/browser-policy/README.md +++ b/packages/browser-policy/README.md @@ -1,4 +1,6 @@ # browser-policy +[Source code of released version](https://github.com/meteor/meteor/tree/master/packages/browser-policy) | [Source code of development version](https://github.com/meteor/meteor/tree/devel/packages/browser-policy) +*** The `browser-policy` family of packages, part of [Webapp](https://www.meteor.com/webapp), lets you set security-related @@ -64,7 +66,10 @@ that are allowed to frame your app. (This is a limitation of the X-Frame-Options header.) Example values of origin include "http://example.com" and "https://foo.example.com". This value of the X-Frame-Options header is not yet supported in Chrome or Safari -and will be ignored in those browsers. +and will be ignored in those browsers. If you need Chrome and/or Safari +support, or need to allow multiple domains to frame your application, +you can use the frame-ancestors CSP option via the +BrowserPolicy.content.allowFrameAncestorsOrigin() function @@ -124,7 +129,7 @@ Disallows inline CSS. Finally, you can configure a whitelist of allowed requests that various types of content can make. The following functions are defined for the content types -script, object, image, media, font, frame, style, and connect. +script, object, image, media, font, frame, frame-ancestors, style, and connect.
diff --git a/packages/browser-policy/browser-policy-test.js b/packages/browser-policy/browser-policy-test.js index 1a2ed512bd..19ca95c292 100644 --- a/packages/browser-policy/browser-policy-test.js +++ b/packages/browser-policy/browser-policy-test.js @@ -136,6 +136,13 @@ Tinytest.add("browser-policy - csp", function (test) { test.isTrue(cspsEqual(BrowserPolicy.content._constructCsp(), "default-src 'none'; frame-src https://foo.com; " + "object-src http://foo.com https://foo.com;")); + + // Check that frame-ancestors property is set correctly.
 + BrowserPolicy.content.allowFrameAncestorsOrigin("https://foo.com/");
 + test.isTrue(cspsEqual(BrowserPolicy.content._constructCsp(),
 + "default-src 'none'; frame-src https://foo.com; " +
 + "object-src http://foo.com https://foo.com; " +
 + "frame-ancestors https://foo.com;")); }); Tinytest.add("browser-policy - x-frame-options", function (test) { diff --git a/packages/browser-policy/package.js b/packages/browser-policy/package.js index 87e01b6e21..32421434c2 100644 --- a/packages/browser-policy/package.js +++ b/packages/browser-policy/package.js @@ -1,6 +1,6 @@ Package.describe({ summary: "Configure security policies enforced by the browser", - version: "1.0.9" + version: "1.0.10-beta.1" }); Package.onUse(function (api) { diff --git a/packages/caching-compiler/README.md b/packages/caching-compiler/README.md index 222c8c2d99..a4afcf3af8 100644 --- a/packages/caching-compiler/README.md +++ b/packages/caching-compiler/README.md @@ -1,5 +1,8 @@ -# caching-compiler: caching superclasses for Plugin.registerCompiler +# caching-compiler +[Source code of released version](https://github.com/meteor/meteor/tree/master/packages/caching-compiler) | [Source code of development version](https://github.com/meteor/meteor/tree/devel/packages/caching-compiler) +*** +Caching superclasses for Plugin.registerCompiler. CachingCompiler and MultiFileCachingCompiler are classes designed to be used with Plugin.registerCompiler. They implement in-memory and on-disk caches for the files that they process. diff --git a/packages/caching-compiler/caching-compiler.js b/packages/caching-compiler/caching-compiler.js index 2149ddcea1..c200944091 100644 --- a/packages/caching-compiler/caching-compiler.js +++ b/packages/caching-compiler/caching-compiler.js @@ -163,15 +163,22 @@ CachingCompilerBase = class CachingCompilerBase { // processing on the file write. _writeFileAsync(filename, contents) { const tempFilename = filename + '.tmp.' + Random.id(); - fs.writeFile(tempFilename, contents, (err) => { - // ignore errors, it's just a cache - if (err) { - return; + if (this._cacheDebugEnabled) { + // Write cache file synchronously when cache debugging enabled. + try { + fs.writeFileSync(tempFilename, contents); + fs.renameSync(tempFilename, filename); + } catch (e) { + // ignore errors, it's just a cache } - fs.rename(tempFilename, filename, (err) => { - // ignore this error too. + } else { + fs.writeFile(tempFilename, contents, err => { + // ignore errors, it's just a cache + if (! err) { + fs.rename(tempFilename, filename, err => {}); + } }); - }); + } } // Helper function. Returns the body of the file as a string, or null if it diff --git a/packages/caching-compiler/package.js b/packages/caching-compiler/package.js index 06b1d6d4cd..470e4d04be 100644 --- a/packages/caching-compiler/package.js +++ b/packages/caching-compiler/package.js @@ -1,12 +1,14 @@ Package.describe({ name: 'caching-compiler', - version: '1.1.6', + version: '1.1.9', summary: 'An easy way to make compiler plugins cache', documentation: 'README.md' }); -Npm.depends({'lru-cache': '2.6.4', - 'async': '1.4.0'}); +Npm.depends({ + 'lru-cache': '2.6.4', + 'async': '1.4.0' +}); Package.onUse(function(api) { api.use(['ecmascript', 'random']); diff --git a/packages/caching-html-compiler/README.md b/packages/caching-html-compiler/README.md deleted file mode 100644 index d64298c59f..0000000000 --- a/packages/caching-html-compiler/README.md +++ /dev/null @@ -1,37 +0,0 @@ -# caching-html-compiler - -Provides a pluggable class used to compile HTML-style templates in Meteor build plugins. This abstracts out a lot of the functionality you would need to implement the following plugins: - -1. `templating` -2. `static-html` -3. `simple:markdown-templating` - -It provides automatic caching and handles communicating with the build plugin APIs. The actual functions that convert HTML into compiled form are passed in as arguments into the constructor, allowing those functions to be unit tested separately from the caching and file system functionality. - -------- - -### new CachingHtmlCompiler(name, tagScannerFunc, tagHandlerFunc) - -Constructs a new CachingHtmlCompiler that can be passed into `Plugin.registerCompiler`. - -#### Arguments - -1. `name` The name of the compiler, used when printing errors. Should probably be the same as the name of the build plugin and package it is used in. -2. `tagScannerFunc` A function that takes a string representing a template file as input, and returns an array of Tag objects. See the README for `templating-tools` for more information about the Tag object. -3. `tagHandlerFunc` A function that takes an array of Tag objects (the output of the previous argument) and returns an object with `js`, `body`, `head`, and `bodyAttr` properties, which will be added to the app through the build plugin API. - -#### Example - -Here is some example code from the `templating` package: - -```js -Plugin.registerCompiler({ - extensions: ['html'], - archMatching: 'web', - isTemplate: true -}, () => new CachingHtmlCompiler( - "templating", - TemplatingTools.scanHtmlForTags, - TemplatingTools.compileTagsWithSpacebars -)); -``` diff --git a/packages/caching-html-compiler/caching-html-compiler.js b/packages/caching-html-compiler/caching-html-compiler.js deleted file mode 100644 index 43e01ee3ef..0000000000 --- a/packages/caching-html-compiler/caching-html-compiler.js +++ /dev/null @@ -1,141 +0,0 @@ -const path = Plugin.path; - -// The CompileResult type for this CachingCompiler is the return value of -// htmlScanner.scan: a {js, head, body, bodyAttrs} object. -CachingHtmlCompiler = class CachingHtmlCompiler extends CachingCompiler { - /** - * Constructor for CachingHtmlCompiler - * @param {String} name The name of the compiler, printed in errors - - * should probably always be the same as the name of the build - * plugin/package - * @param {Function} tagScannerFunc Transforms a template file (commonly - * .html) into an array of Tags - * @param {Function} tagHandlerFunc Transforms an array of tags into a - * results object with js, body, head, and bodyAttrs properties - */ - constructor(name, tagScannerFunc, tagHandlerFunc) { - super({ - compilerName: name, - defaultCacheSize: 1024*1024*10, - }); - - this._bodyAttrInfo = null; - - this.tagScannerFunc = tagScannerFunc; - this.tagHandlerFunc = tagHandlerFunc; - } - - // Implements method from CachingCompilerBase - compileResultSize(compileResult) { - function lengthOrZero(field) { - return field ? field.length : 0; - } - return lengthOrZero(compileResult.head) + lengthOrZero(compileResult.body) + - lengthOrZero(compileResult.js); - } - - // Overrides method from CachingCompiler - processFilesForTarget(inputFiles) { - this._bodyAttrInfo = {}; - super.processFilesForTarget(inputFiles); - } - - // Implements method from CachingCompilerBase - getCacheKey(inputFile) { - // Note: the path is only used for errors, so it doesn't have to be part - // of the cache key. - return inputFile.getSourceHash(); - } - - // Implements method from CachingCompiler - compileOneFile(inputFile) { - const contents = inputFile.getContentsAsString(); - const inputPath = inputFile.getPathInPackage(); - try { - const tags = this.tagScannerFunc({ - sourceName: inputPath, - contents: contents, - tagNames: ["body", "head", "template"] - }); - - return this.tagHandlerFunc(tags); - } catch (e) { - if (e instanceof TemplatingTools.CompileError) { - inputFile.error({ - message: e.message, - line: e.line - }); - return null; - } else { - throw e; - } - } - } - - // Implements method from CachingCompilerBase - addCompileResult(inputFile, compileResult) { - let allJavaScript = ""; - - if (compileResult.head) { - inputFile.addHtml({ section: "head", data: compileResult.head }); - } - - if (compileResult.body) { - inputFile.addHtml({ section: "body", data: compileResult.body }); - } - - if (compileResult.js) { - allJavaScript += compileResult.js; - } - - if (! _.isEmpty(compileResult.bodyAttrs)) { - Object.keys(compileResult.bodyAttrs).forEach((attr) => { - const value = compileResult.bodyAttrs[attr]; - if (this._bodyAttrInfo.hasOwnProperty(attr) && - this._bodyAttrInfo[attr].value !== value) { - // two conflicting attributes on tags in two different template - // files - inputFile.error({ - message: - ` declarations have conflicting values for the '${ attr }' ` + - `attribute in the following files: ` + - this._bodyAttrInfo[attr].inputFile.getPathInPackage() + - `, ${ inputFile.getPathInPackage() }` - }); - } else { - this._bodyAttrInfo[attr] = {inputFile, value}; - } - }); - - // Add JavaScript code to set attributes on body - allJavaScript += -`Meteor.startup(function() { - var attrs = ${JSON.stringify(compileResult.bodyAttrs)}; - for (var prop in attrs) { - document.body.setAttribute(prop, attrs[prop]); - } -}); -`; - } - - - if (allJavaScript) { - const filePath = inputFile.getPathInPackage(); - // XXX this path manipulation may be unnecessarily complex - let pathPart = path.dirname(filePath); - if (pathPart === '.') - pathPart = ''; - if (pathPart.length && pathPart !== path.sep) - pathPart = pathPart + path.sep; - const ext = path.extname(filePath); - const basename = path.basename(filePath, ext); - - // XXX generate a source map - - inputFile.addJavaScript({ - path: path.join(pathPart, "template." + basename + ".js"), - data: allJavaScript - }); - } - } -} diff --git a/packages/caching-html-compiler/package.js b/packages/caching-html-compiler/package.js deleted file mode 100644 index df7d81a1d1..0000000000 --- a/packages/caching-html-compiler/package.js +++ /dev/null @@ -1,21 +0,0 @@ -Package.describe({ - version: '1.0.6', - // Brief, one-line summary of the package. - summary: 'Pluggable class for compiling HTML into templates', - // 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.use([ - 'underscore', - 'caching-compiler', - 'templating-tools', - 'ecmascript' - ]); - - api.addFiles('caching-html-compiler.js', 'server'); - - api.export("CachingHtmlCompiler", 'server'); -}); diff --git a/packages/callback-hook/README.md b/packages/callback-hook/README.md index ec37b267c6..73601a3161 100644 --- a/packages/callback-hook/README.md +++ b/packages/callback-hook/README.md @@ -1 +1,5 @@ +# callback-hook +[Source code of released version](https://github.com/meteor/meteor/tree/master/packages/callback-hook) | [Source code of development version](https://github.com/meteor/meteor/tree/devel/packages/callback-hook) +*** + This is an internal Meteor package. \ No newline at end of file diff --git a/packages/callback-hook/package.js b/packages/callback-hook/package.js index dcbed7a49e..ece4b39e27 100644 --- a/packages/callback-hook/package.js +++ b/packages/callback-hook/package.js @@ -1,6 +1,6 @@ Package.describe({ summary: "Register callbacks on a hook", - version: '1.0.9' + version: '1.0.10' }); Package.onUse(function (api) { diff --git a/packages/check/README.md b/packages/check/README.md index 19b5927d56..1c5d1c10a8 100644 --- a/packages/check/README.md +++ b/packages/check/README.md @@ -1,4 +1,6 @@ # check +[Source code of released version](https://github.com/meteor/meteor/tree/master/packages/check) | [Source code of development version](https://github.com/meteor/meteor/tree/devel/packages/check) +*** `check` is a lightweight package for argument checking and general pattern matching. Use it like this: diff --git a/packages/check/package.js b/packages/check/package.js index fef64a58e7..f279ac070f 100644 --- a/packages/check/package.js +++ b/packages/check/package.js @@ -1,6 +1,6 @@ Package.describe({ summary: "Check whether a value matches a pattern", - version: '1.2.3' + version: '1.2.4' }); Package.onUse(function (api) { diff --git a/packages/code-prettify/.gitignore b/packages/code-prettify/.gitignore index 5dc30d8d90..c765a6f21e 100644 --- a/packages/code-prettify/.gitignore +++ b/packages/code-prettify/.gitignore @@ -1,2 +1,2 @@ .build* -.!* \ No newline at end of file +.!* diff --git a/packages/code-prettify/README.md b/packages/code-prettify/README.md index ec37b267c6..cd76627a5f 100644 --- a/packages/code-prettify/README.md +++ b/packages/code-prettify/README.md @@ -1 +1,5 @@ +# code-prettify +[Source code of released version](https://github.com/meteor/meteor/tree/master/packages/code-prettify) | [Source code of development version](https://github.com/meteor/meteor/tree/devel/packages/code-prettify) +*** + This is an internal Meteor package. \ No newline at end of file diff --git a/packages/code-prettify/package.js b/packages/code-prettify/package.js index 872735ec48..abe0277061 100644 --- a/packages/code-prettify/package.js +++ b/packages/code-prettify/package.js @@ -8,7 +8,7 @@ var path = Npm.require('path'); Package.describe({ summary: "Syntax highlighting of code, from Google", - version: "1.0.10" + version: "1.0.11" }); // XXX this code dumps symbols into the global namespace (directly diff --git a/packages/coffeescript-test-helper/README.md b/packages/coffeescript-test-helper/README.md index ec37b267c6..61bdd71e5c 100644 --- a/packages/coffeescript-test-helper/README.md +++ b/packages/coffeescript-test-helper/README.md @@ -1 +1,5 @@ +# coffeescript-test-helper +[Source code of released version](https://github.com/meteor/meteor/tree/master/packages/coffeescript-test-helper) | [Source code of development version](https://github.com/meteor/meteor/tree/devel/packages/coffeescript-test-helper) +*** + This is an internal Meteor package. \ No newline at end of file diff --git a/packages/coffeescript-test-helper/exporting.coffee b/packages/coffeescript-test-helper/exporting.coffee index eb75de9f7d..93e4873187 100644 --- a/packages/coffeescript-test-helper/exporting.coffee +++ b/packages/coffeescript-test-helper/exporting.coffee @@ -1 +1,15 @@ COFFEESCRIPT_EXPORTED = 123 +COFFEESCRIPT_EXPORTED_ONE_MORE = 234 + +# Having backticks in the code is required to trigger Babel processing +# the file and re-wrapping the list of defined variables. +`COFFEESCRIPT_EXPORTED_WITH_BACKTICKS = 345` + +# Defining a class which extends a new class forces CoffeeScript +# to define an "extend" function, which then in turn forces Babel +# to re-wrap the list of defined variables so that each is defined +# in its own line. + +class TestClass + +class ClassExtending extends TestClass diff --git a/packages/coffeescript-test-helper/package.js b/packages/coffeescript-test-helper/package.js index ec5f594029..b79b10367e 100644 --- a/packages/coffeescript-test-helper/package.js +++ b/packages/coffeescript-test-helper/package.js @@ -1,10 +1,12 @@ Package.describe({ summary: "Used by the coffeescript package's tests", - version: "1.0.7" + version: "1.0.8" }); Package.onUse(function (api) { api.use('coffeescript', ['client', 'server']); api.export('COFFEESCRIPT_EXPORTED'); + api.export('COFFEESCRIPT_EXPORTED_ONE_MORE'); + api.export('COFFEESCRIPT_EXPORTED_WITH_BACKTICKS'); api.addFiles("exporting.coffee", ['client', 'server']); }); diff --git a/packages/coffeescript/.npm/plugin/compileCoffeescript/npm-shrinkwrap.json b/packages/coffeescript/.npm/plugin/compileCoffeescript/npm-shrinkwrap.json index 8a5954b454..3c9553c754 100644 --- a/packages/coffeescript/.npm/plugin/compileCoffeescript/npm-shrinkwrap.json +++ b/packages/coffeescript/.npm/plugin/compileCoffeescript/npm-shrinkwrap.json @@ -1,14 +1,14 @@ { "dependencies": { "coffee-script": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.10.0.tgz", - "from": "coffee-script@1.10.0" + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.12.0.tgz", + "from": "coffee-script@1.12.0" }, "source-map": { "version": "0.5.6", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz", - "from": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz" + "from": "source-map@0.5.6" } } } diff --git a/packages/coffeescript/README.md b/packages/coffeescript/README.md index 6d57948b34..711ff02abd 100644 --- a/packages/coffeescript/README.md +++ b/packages/coffeescript/README.md @@ -1,4 +1,6 @@ # coffeescript +[Source code of released version](https://github.com/meteor/meteor/tree/master/packages/coffeescript) | [Source code of development version](https://github.com/meteor/meteor/tree/devel/packages/coffeescript) +*** [CoffeeScript](http://coffeescript.org/) is a little language that compiles into JavaScript. It provides a simple syntax without lots of diff --git a/packages/coffeescript/package.js b/packages/coffeescript/package.js index 7f339ed356..7aa305b453 100644 --- a/packages/coffeescript/package.js +++ b/packages/coffeescript/package.js @@ -1,6 +1,6 @@ Package.describe({ summary: "Javascript dialect with fewer braces and semicolons", - version: "1.2.3" + version: "1.12.0_1" }); Package.registerBuildPlugin({ @@ -8,7 +8,7 @@ Package.registerBuildPlugin({ use: ['caching-compiler', 'ecmascript'], sources: ['plugin/compile-coffeescript.js'], npmDependencies: { - "coffee-script": "1.10.0", + "coffee-script": "1.12.0", "source-map": "0.5.6" } }); @@ -18,10 +18,10 @@ Package.onUse(function (api) { api.use('babel-compiler'); // Because the CoffeeScript plugin now calls - // BabelCompiler.prototype.processOneFileForTarget for any raw - // JavaScript enclosed by backticks, it must provide the same runtime - // environment that the 'ecmascript' package provides. The following - // api.imply calls should match those in ../ecmascript/package.js, + // BabelCompiler.prototype.processOneFileForTarget for any ES2015+ + // JavaScript or JavaScript enclosed by backticks, it must provide the + // same runtime environment that the 'ecmascript' package provides. + // The following api.imply calls should match those in ../ecmascript/package.js, // except that coffeescript does not api.imply('modules'). api.imply('ecmascript-runtime'); api.imply('babel-runtime'); @@ -37,6 +37,7 @@ Package.onTest(function (api) { 'tests/coffeescript_test_setup.js', 'tests/coffeescript_tests.coffee', 'tests/coffeescript_strict_tests.coffee', + 'tests/coffeescript_module.coffee', 'tests/es2015_module.js', 'tests/litcoffeescript_tests.litcoffee', 'tests/litcoffeescript_tests.coffee.md', diff --git a/packages/coffeescript/plugin/compile-coffeescript.js b/packages/coffeescript/plugin/compile-coffeescript.js index 68c6bc1363..c51b51b58b 100644 --- a/packages/coffeescript/plugin/compile-coffeescript.js +++ b/packages/coffeescript/plugin/compile-coffeescript.js @@ -65,6 +65,11 @@ export class CoffeeCompiler extends CachingCompiler { ]; } + setDiskCacheDirectory(cacheDir) { + this.babelCompiler.setDiskCacheDirectory(cacheDir); + return super.setDiskCacheDirectory(cacheDir); + } + compileOneFile(inputFile) { const source = inputFile.getContentsAsString(); const compileOptions = this._getCompileOptions(inputFile); @@ -83,35 +88,38 @@ export class CoffeeCompiler extends CachingCompiler { let sourceMap = JSON.parse(output.v3SourceMap); - if (source.indexOf('`') !== -1) { - // If source contains backticks, pass the coffee output through babel-compiler - const doubleRoastedCoffee = - this.babelCompiler.processOneFileForTarget(inputFile, output.js); + output.js = stripExportedVars( + output.js, + inputFile.getDeclaredExports().map(e => e.name) + ); - if (doubleRoastedCoffee != null && - doubleRoastedCoffee.data != null) { - output.js = doubleRoastedCoffee.data; + // CoffeeScript contains a handful of features that output as ES2015+, + // such as modules, generator functions, for…of, and tagged template + // literals. Because they’re too varied to detect, pass all CoffeeScript + // compiler output through the Babel compiler. + const doubleRoastedCoffee = + this.babelCompiler.processOneFileForTarget(inputFile, output.js); - if (doubleRoastedCoffee.sourceMap) { - // Combine the original CoffeeScript source map with the one - // produced by this.babelCompiler.processOneFileForTarget. - const smg = new SourceMapGenerator( - new SourceMapConsumer(doubleRoastedCoffee.sourceMap)); - smg.applySourceMap(new SourceMapConsumer(sourceMap)); - sourceMap = smg.toJSON(); - } else { - // If the .coffee file is contained by a node_modules directory, - // then BabelCompiler will not transpile it, and there will be - // no sourceMap, but that's fine because the original - // CoffeeScript sourceMap will still be valid. - } + if (doubleRoastedCoffee != null && + doubleRoastedCoffee.data != null) { + output.js = doubleRoastedCoffee.data; + + if (doubleRoastedCoffee.sourceMap) { + // Combine the original CoffeeScript source map with the one + // produced by this.babelCompiler.processOneFileForTarget. + const smg = new SourceMapGenerator( + new SourceMapConsumer(doubleRoastedCoffee.sourceMap)); + smg.applySourceMap(new SourceMapConsumer(sourceMap)); + sourceMap = smg.toJSON(); + } else { + // If the .coffee file is contained by a node_modules directory, + // then BabelCompiler will not transpile it, and there will be + // no sourceMap, but that's fine because the original + // CoffeeScript sourceMap will still be valid. } } - return addSharedHeader(stripExportedVars( - output.js, - inputFile.getDeclaredExports().map(e => e.name) - ), sourceMap); + return addSharedHeader(output.js, sourceMap); } addCompileResult(inputFile, sourceWithMap) { diff --git a/packages/coffeescript/tests/coffeescript_module.coffee b/packages/coffeescript/tests/coffeescript_module.coffee new file mode 100644 index 0000000000..953914deb5 --- /dev/null +++ b/packages/coffeescript/tests/coffeescript_module.coffee @@ -0,0 +1 @@ +export testingForNativeImportedModule123456789 = true diff --git a/packages/coffeescript/tests/coffeescript_tests.coffee b/packages/coffeescript/tests/coffeescript_tests.coffee index 5268f8e459..ef584dc8cd 100644 --- a/packages/coffeescript/tests/coffeescript_tests.coffee +++ b/packages/coffeescript/tests/coffeescript_tests.coffee @@ -7,10 +7,24 @@ Tinytest.add "coffeescript - compile", (test) -> test.isTrue true # import/export statements must be top-level -`import { Meteor as testingForImportedSymbol123456789 } from "meteor/meteor";` -Tinytest.add "coffeescript - import external package via backticks", (test) -> - test.isTrue testingForImportedSymbol123456789? +`import { Meteor as testingForBacktickedImportedSymbol } from "meteor/meteor";` +Tinytest.add "coffeescript - import external package via backticked import statement", (test) -> + test.isTrue testingForBacktickedImportedSymbol? `import { testingForImportedModule987654321 } from "./es2015_module.js";` -Tinytest.add "coffeescript - import local module via backticks", (test) -> +Tinytest.add "coffeescript - import local module via backticked import statement", (test) -> test.isTrue testingForImportedModule987654321? + + +import { Meteor as testingForNativeImportedSymbol } from "meteor/meteor" +Tinytest.add "coffeescript - import external package via native import statement", (test) -> + test.isTrue testingForNativeImportedSymbol? + +import { testingForImportedModule123456789 } from "./es2015_module.js"; +Tinytest.add "coffeescript - import local module via native import statement", (test) -> + test.isTrue testingForImportedModule123456789? + + +import { testingForNativeImportedModule123456789 } from "./coffeescript_module.coffee"; +Tinytest.add "coffeescript - import local module exported by a CoffeeScript native export statement, via native import statement", (test) -> + test.isTrue testingForNativeImportedModule123456789? diff --git a/packages/coffeescript/tests/coffeescript_tests.js b/packages/coffeescript/tests/coffeescript_tests.js index 033d7f9082..df127d2cc3 100644 --- a/packages/coffeescript/tests/coffeescript_tests.js +++ b/packages/coffeescript/tests/coffeescript_tests.js @@ -9,4 +9,8 @@ Tinytest.add("literate coffeescript - presence", function(test) { Tinytest.add("coffeescript - exported variable", function(test) { test.equal(COFFEESCRIPT_EXPORTED, 123); test.equal(Package['coffeescript-test-helper'].COFFEESCRIPT_EXPORTED, 123); + test.equal(COFFEESCRIPT_EXPORTED_ONE_MORE, 234); + test.equal(Package['coffeescript-test-helper'].COFFEESCRIPT_EXPORTED_ONE_MORE, 234); + test.equal(COFFEESCRIPT_EXPORTED_WITH_BACKTICKS, 345); + test.equal(Package['coffeescript-test-helper'].COFFEESCRIPT_EXPORTED_WITH_BACKTICKS, 345); }); diff --git a/packages/coffeescript/tests/es2015_module.js b/packages/coffeescript/tests/es2015_module.js index 9eb8c9f8c1..40cf34fbac 100644 --- a/packages/coffeescript/tests/es2015_module.js +++ b/packages/coffeescript/tests/es2015_module.js @@ -1 +1,2 @@ +export const testingForImportedModule123456789 = true; export const testingForImportedModule987654321 = true; diff --git a/packages/constraint-solver/README.md b/packages/constraint-solver/README.md index 0fbb94c27b..9e8976f273 100644 --- a/packages/constraint-solver/README.md +++ b/packages/constraint-solver/README.md @@ -1,4 +1,6 @@ # constraint-solver +[Source code of released version](https://github.com/meteor/meteor/tree/master/packages/constraint-solver) | [Source code of development version](https://github.com/meteor/meteor/tree/devel/packages/constraint-solver) +*** Meteor Version Solver is an optimizing constraint solver for package dependencies. It is used by the Meteor build tool (Isobuild) for apps, diff --git a/packages/constraint-solver/package.js b/packages/constraint-solver/package.js index f4655d7309..5f8f368bc9 100644 --- a/packages/constraint-solver/package.js +++ b/packages/constraint-solver/package.js @@ -1,6 +1,6 @@ Package.describe({ summary: "Given the set of the constraints, picks a satisfying configuration", - version: "1.1.0" + version: "1.1.1" }); Package.onUse(function (api) { diff --git a/packages/crosswalk/README.md b/packages/crosswalk/README.md new file mode 100644 index 0000000000..bbd54bd0b3 --- /dev/null +++ b/packages/crosswalk/README.md @@ -0,0 +1,4 @@ +# crosswalk +[Source code of released version](https://github.com/meteor/meteor/tree/master/packages/crosswalk) | [Source code of development version](https://github.com/meteor/meteor/tree/devel/packages/crosswalk) +*** + diff --git a/packages/crosswalk/package.js b/packages/crosswalk/package.js index 2af0fe2352..ea2a82118b 100644 --- a/packages/crosswalk/package.js +++ b/packages/crosswalk/package.js @@ -1,10 +1,10 @@ Package.describe({ summary: "Makes your Cordova application use the Crosswalk WebView \ instead of the System WebView on Android", - version: '1.6.2', + version: '1.7.0', documentation: null }); Cordova.depends({ - 'cordova-plugin-crosswalk-webview': '1.6.0' + 'cordova-plugin-crosswalk-webview': '2.1.0' }); diff --git a/packages/ddp-client/README.md b/packages/ddp-client/README.md new file mode 100644 index 0000000000..9daa0a6277 --- /dev/null +++ b/packages/ddp-client/README.md @@ -0,0 +1,4 @@ +# ddp-client +[Source code of released version](https://github.com/meteor/meteor/tree/master/packages/ddp-client) | [Source code of development version](https://github.com/meteor/meteor/tree/devel/packages/ddp-client) +*** + diff --git a/packages/ddp-client/package.js b/packages/ddp-client/package.js index cc0cb2a6d3..7816884263 100644 --- a/packages/ddp-client/package.js +++ b/packages/ddp-client/package.js @@ -1,6 +1,6 @@ Package.describe({ summary: "Meteor's latency-compensated distributed data client", - version: '1.3.0', + version: '1.3.2', documentation: null }); diff --git a/packages/ddp-common/README.md b/packages/ddp-common/README.md new file mode 100644 index 0000000000..d3bc884e06 --- /dev/null +++ b/packages/ddp-common/README.md @@ -0,0 +1,4 @@ +# ddp-common +[Source code of released version](https://github.com/meteor/meteor/tree/master/packages/ddp-common) | [Source code of development version](https://github.com/meteor/meteor/tree/devel/packages/ddp-common) +*** + diff --git a/packages/ddp-common/method_invocation.js b/packages/ddp-common/method_invocation.js index 4ec21dde09..d2b8cd510e 100644 --- a/packages/ddp-common/method_invocation.js +++ b/packages/ddp-common/method_invocation.js @@ -5,6 +5,7 @@ * inside a method definition. * @param {Object} options * @instanceName this + * @showInstanceName true */ DDPCommon.MethodInvocation = function (options) { var self = this; @@ -51,7 +52,7 @@ DDPCommon.MethodInvocation = function (options) { // On the server, the connection this method call came in on. /** - * @summary Access inside a method invocation. The [connection](#meteor_onconnection) that this method was received on. `null` if the method is not associated with a connection, eg. a server initiated method call. Calls to methods made from a server method which was in turn initiated from the client share the same `connection`. + * @summary Access inside a method invocation. The [connection](#meteor_onconnection) that this method was received on. `null` if the method is not associated with a connection, eg. a server initiated method call. Calls to methods made from a server method which was in turn initiated from the client share the same `connection`. * @locus Server * @name connection * @memberOf DDPCommon.MethodInvocation @@ -94,4 +95,3 @@ _.extend(DDPCommon.MethodInvocation.prototype, { self._setUserId(userId); } }); - diff --git a/packages/ddp-common/package.js b/packages/ddp-common/package.js index ca4d2eaf2f..42a831899b 100644 --- a/packages/ddp-common/package.js +++ b/packages/ddp-common/package.js @@ -1,6 +1,6 @@ Package.describe({ summary: "Code shared beween ddp-client and ddp-server", - version: '1.2.6', + version: '1.2.8', documentation: null }); diff --git a/packages/ddp-rate-limiter/README.md b/packages/ddp-rate-limiter/README.md index dc784441be..a54043d630 100644 --- a/packages/ddp-rate-limiter/README.md +++ b/packages/ddp-rate-limiter/README.md @@ -1,3 +1,7 @@ +# ddp-rate-limiter +[Source code of released version](https://github.com/meteor/meteor/tree/master/packages/ddp-rate-limiter) | [Source code of development version](https://github.com/meteor/meteor/tree/devel/packages/ddp-rate-limiter) +*** + DDP Rate Limiter package === diff --git a/packages/ddp-rate-limiter/package.js b/packages/ddp-rate-limiter/package.js index ef3d1902de..c442b116f9 100644 --- a/packages/ddp-rate-limiter/package.js +++ b/packages/ddp-rate-limiter/package.js @@ -1,6 +1,6 @@ Package.describe({ name: 'ddp-rate-limiter', - version: '1.0.5', + version: '1.0.6', // Brief, one-line summary of the package. summary: 'The DDPRateLimiter allows users to add rate limits to DDP' + ' methods and subscriptions.', diff --git a/packages/ddp-server/README.md b/packages/ddp-server/README.md new file mode 100644 index 0000000000..21d2ac4c84 --- /dev/null +++ b/packages/ddp-server/README.md @@ -0,0 +1,4 @@ +# ddp-server +[Source code of released version](https://github.com/meteor/meteor/tree/master/packages/ddp-server) | [Source code of development version](https://github.com/meteor/meteor/tree/devel/packages/ddp-server) +*** + diff --git a/packages/ddp-server/livedata_server.js b/packages/ddp-server/livedata_server.js index 2ac3aebea1..e16b8f6d25 100644 --- a/packages/ddp-server/livedata_server.js +++ b/packages/ddp-server/livedata_server.js @@ -302,6 +302,9 @@ var Session = function (server, version, socket, options) { }).run(); if (version !== 'pre1' && options.heartbeatInterval !== 0) { + // We no longer need the low level timeout because we have heartbeating. + socket.setWebsocketTimeout(0); + self.heartbeat = new DDPCommon.Heartbeat({ heartbeatInterval: options.heartbeatInterval, heartbeatTimeout: options.heartbeatTimeout, @@ -547,6 +550,11 @@ _.extend(Session.prototype, { processNext(); }; + self.server.onMessageHook.each(function (callback) { + callback(msg, self); + return true; + }); + if (_.has(self.protocol_handlers, msg.msg)) self.protocol_handlers[msg.msg].call(self, msg, unblock); else @@ -937,6 +945,7 @@ _.extend(Session.prototype, { * @summary The server's side of a subscription * @class Subscription * @instanceName this + * @showInstanceName true */ var Subscription = function ( session, handler, subscriptionId, params, name) { @@ -1320,6 +1329,11 @@ Server = function (options) { debugPrintExceptions: "onConnection callback" }); + // Map of callbacks to call when a new message comes in. + self.onMessageHook = new Hook({ + debugPrintExceptions: "onMessage callback" + }); + self.publish_handlers = {}; self.universal_publish_handlers = []; @@ -1403,6 +1417,18 @@ _.extend(Server.prototype, { return self.onConnectionHook.register(fn); }, + /** + * @summary Register a callback to be called when a new DDP message is received. + * @locus Server + * @param {function} callback The function to call when a new DDP message is received. + * @memberOf Meteor + * @importFromPackage meteor + */ + onMessage: function (fn) { + var self = this; + return self.onMessageHook.register(fn); + }, + _handleConnect: function (socket, msg) { var self = this; diff --git a/packages/ddp-server/livedata_server_tests.js b/packages/ddp-server/livedata_server_tests.js index 10203f319b..d24cfb0d06 100644 --- a/packages/ddp-server/livedata_server_tests.js +++ b/packages/ddp-server/livedata_server_tests.js @@ -82,7 +82,6 @@ testAsyncMulti( }] ); - Meteor.methods({ livedata_server_test_inner: function () { return this.connection.id; @@ -94,6 +93,28 @@ Meteor.methods({ }); +Tinytest.addAsync( + "livedata server - onMessage hook", + function (test, onComplete) { + + var cb = Meteor.onMessage(function (msg, session) { + test.equal(msg.method, 'livedata_server_test_inner'); + cb.stop(); + onComplete(); + }); + + makeTestConnection( + test, + function (clientConn, serverConn) { + clientConn.call('livedata_server_test_inner'); + clientConn.disconnect(); + }, + onComplete + ); + } +); + + Tinytest.addAsync( "livedata server - connection in method invocation", function (test, onComplete) { diff --git a/packages/ddp-server/package.js b/packages/ddp-server/package.js index c2a7731e23..06cdaf97c2 100644 --- a/packages/ddp-server/package.js +++ b/packages/ddp-server/package.js @@ -1,6 +1,6 @@ Package.describe({ summary: "Meteor's latency-compensated distributed data server", - version: '1.3.9', + version: '1.3.13-beta.1', documentation: null }); diff --git a/packages/ddp-server/server_convenience.js b/packages/ddp-server/server_convenience.js index 62384353fa..28fca40b2b 100755 --- a/packages/ddp-server/server_convenience.js +++ b/packages/ddp-server/server_convenience.js @@ -11,7 +11,7 @@ Meteor.refresh = function (notification) { // Proxy the public methods of Meteor.server so they can // be called directly on Meteor. -_.each(['publish', 'methods', 'call', 'apply', 'onConnection'], +_.each(['publish', 'methods', 'call', 'apply', 'onConnection', 'onMessage'], function (name) { Meteor[name] = _.bind(Meteor.server[name], Meteor.server); }); diff --git a/packages/ddp-server/stream_server.js b/packages/ddp-server/stream_server.js index e2d3771b2f..1996d9088c 100644 --- a/packages/ddp-server/stream_server.js +++ b/packages/ddp-server/stream_server.js @@ -86,6 +86,25 @@ StreamServer = function () { self._redirectWebsocketEndpoint(); self.server.on('connection', function (socket) { + // We want to make sure that if a client connects to us and does the initial + // Websocket handshake but never gets to the DDP handshake, that we + // eventually kill the socket. Once the DDP handshake happens, DDP + // heartbeating will work. And before the Websocket handshake, the timeouts + // we set at the server level in webapp_server.js will work. But + // faye-websocket calls setTimeout(0) on any socket it takes over, so there + // is an "in between" state where this doesn't happen. We work around this + // by explicitly setting the socket timeout to a relatively large time here, + // and setting it back to zero when we set up the heartbeat in + // livedata_server.js. + socket.setWebsocketTimeout = function (timeout) { + if ((socket.protocol === 'websocket' || + socket.protocol === 'websocket-raw') + && socket._session.recv) { + socket._session.recv.connection.setTimeout(timeout); + } + }; + socket.setWebsocketTimeout(45 * 1000); + socket.send = function (data) { socket.write(data); }; diff --git a/packages/ddp/README.md b/packages/ddp/README.md index b3785a95b5..f56c6cf13c 100644 --- a/packages/ddp/README.md +++ b/packages/ddp/README.md @@ -1,4 +1,6 @@ # ddp +[Source code of released version](https://github.com/meteor/meteor/tree/master/packages/ddp) | [Source code of development version](https://github.com/meteor/meteor/tree/devel/packages/ddp) +*** DDP (Distributed Data Protocol) is the stateful websocket protocol that Meteor uses to communicate between the client and the server. For diff --git a/packages/blaze-tools/.gitignore b/packages/deprecated/facebook/.gitignore similarity index 100% rename from packages/blaze-tools/.gitignore rename to packages/deprecated/facebook/.gitignore diff --git a/packages/deprecated/facebook/README.md b/packages/deprecated/facebook/README.md new file mode 100644 index 0000000000..ad16f40317 --- /dev/null +++ b/packages/deprecated/facebook/README.md @@ -0,0 +1,7 @@ +# facebook +[Source code of released version](https://github.com/meteor/meteor/tree/master/packages/facebook) | [Source code of development version](https://github.com/meteor/meteor/tree/devel/packages/facebook) +*** + +** Deprecated, use facebook-oauth instead** + +An implementation of the Facebook OAuth flow. See the [project page](https://www.meteor.com/accounts) on Meteor Accounts for more details. XXX link diff --git a/packages/deprecated/facebook/deprecation_notice.js b/packages/deprecated/facebook/deprecation_notice.js new file mode 100644 index 0000000000..8370a533f1 --- /dev/null +++ b/packages/deprecated/facebook/deprecation_notice.js @@ -0,0 +1,11 @@ +console.warn(); +console.warn('This package (facebook) has been deprecated.'); +console.warn( + "To use the `Facebook` symbol, use the `facebook-oauth` package " + + "and import from it." +); +console.warn( + "If you need the Blaze OAuth configuration UI, add " + + "`facebook-config-ui` alongside `accounts-ui`." +); +console.warn(); diff --git a/packages/deprecated/facebook/package.js b/packages/deprecated/facebook/package.js new file mode 100644 index 0000000000..6eeda137a4 --- /dev/null +++ b/packages/deprecated/facebook/package.js @@ -0,0 +1,13 @@ +Package.describe({ + summary: "DEPRECATED - Use facebook-oauth instead - Facebook OAuth flow", + version: "1.3.0" +}); + +Package.onUse(function(api) { + api.use('facebook-oauth'); + api.use('facebook-config-ui', 'client'); + + api.imply('facebook-oauth'); + + api.addFiles('deprecation_notice.js'); +}); diff --git a/packages/deps/README.md b/packages/deps/README.md index ec37b267c6..ee8dff27cf 100644 --- a/packages/deps/README.md +++ b/packages/deps/README.md @@ -1 +1,5 @@ +# deps +[Source code of released version](https://github.com/meteor/meteor/tree/master/packages/deps) | [Source code of development version](https://github.com/meteor/meteor/tree/devel/packages/deps) +*** + This is an internal Meteor package. \ No newline at end of file diff --git a/packages/diff-sequence/README.md b/packages/diff-sequence/README.md new file mode 100644 index 0000000000..708e14303c --- /dev/null +++ b/packages/diff-sequence/README.md @@ -0,0 +1,4 @@ +# diff-sequence +[Source code of released version](https://github.com/meteor/meteor/tree/master/packages/diff-sequence) | [Source code of development version](https://github.com/meteor/meteor/tree/devel/packages/diff-sequence) +*** + diff --git a/packages/diff-sequence/package.js b/packages/diff-sequence/package.js index b9237b882b..0725708bdc 100644 --- a/packages/diff-sequence/package.js +++ b/packages/diff-sequence/package.js @@ -1,6 +1,6 @@ Package.describe({ summary: "An implementation of a diff algorithm on arrays and objects.", - version: '1.0.6', + version: '1.0.7', documentation: null }); diff --git a/packages/disable-oplog/README.md b/packages/disable-oplog/README.md index ec37b267c6..ce382c61a0 100644 --- a/packages/disable-oplog/README.md +++ b/packages/disable-oplog/README.md @@ -1 +1,5 @@ +# disable-oplog +[Source code of released version](https://github.com/meteor/meteor/tree/master/packages/disable-oplog) | [Source code of development version](https://github.com/meteor/meteor/tree/devel/packages/disable-oplog) +*** + This is an internal Meteor package. \ No newline at end of file diff --git a/packages/ecmascript-runtime/.npm/package/npm-shrinkwrap.json b/packages/ecmascript-runtime/.npm/package/npm-shrinkwrap.json index b6abbda7be..2d4e33d663 100644 --- a/packages/ecmascript-runtime/.npm/package/npm-shrinkwrap.json +++ b/packages/ecmascript-runtime/.npm/package/npm-shrinkwrap.json @@ -1,16 +1,14 @@ { "dependencies": { + "core-js": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.4.1.tgz", + "from": "core-js@2.4.1" + }, "meteor-ecmascript-runtime": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/meteor-ecmascript-runtime/-/meteor-ecmascript-runtime-0.2.6.tgz", - "from": "https://registry.npmjs.org/meteor-ecmascript-runtime/-/meteor-ecmascript-runtime-0.2.6.tgz", - "dependencies": { - "core-js": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-1.2.1.tgz", - "from": "https://registry.npmjs.org/core-js/-/core-js-1.2.1.tgz" - } - } + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/meteor-ecmascript-runtime/-/meteor-ecmascript-runtime-0.2.9.tgz", + "from": "meteor-ecmascript-runtime@0.2.9" } } } diff --git a/packages/ecmascript-runtime/README.md b/packages/ecmascript-runtime/README.md index 3a44495c24..2fd1bfe192 100644 --- a/packages/ecmascript-runtime/README.md +++ b/packages/ecmascript-runtime/README.md @@ -1,3 +1,6 @@ -# ecmascript-runtime [![Build Status](https://travis-ci.org/meteor/ecmascript-runtime.svg?branch=master)](https://travis-ci.org/meteor/ecmascript-runtime) +# ecmascript-runtime +[Source code of released version](https://github.com/meteor/meteor/tree/master/packages/ecmascript-runtime) | [Source code of development version](https://github.com/meteor/meteor/tree/devel/packages/ecmascript-runtime) +*** +[![Build Status](https://travis-ci.org/meteor/ecmascript-runtime.svg?branch=master)](https://travis-ci.org/meteor/ecmascript-runtime) Polyfills for new ECMAScript 2015 APIs like Map and Set diff --git a/packages/ecmascript-runtime/package.js b/packages/ecmascript-runtime/package.js index b014f46c7d..78b193e14e 100644 --- a/packages/ecmascript-runtime/package.js +++ b/packages/ecmascript-runtime/package.js @@ -1,23 +1,23 @@ Package.describe({ name: "ecmascript-runtime", - version: "0.3.12", + version: "0.3.15", summary: "Polyfills for new ECMAScript 2015 APIs like Map and Set", git: "https://github.com/meteor/ecmascript-runtime", documentation: "README.md" }); Npm.depends({ - "meteor-ecmascript-runtime": "0.2.6", + "meteor-ecmascript-runtime": "0.2.9", }); Package.onUse(function(api) { // If the es5-shim package is installed, make sure it loads before // ecmascript-runtime, since ecmascript-runtime uses some ES5 APIs like // Object.defineProperties that are buggy in older browsers. - api.use("es5-shim", { weak: true }); + api.use("es5-shim@4.6.13", { weak: true }); - api.use("modules"); - api.use("promise"); + api.use("modules@0.7.5"); + api.use("promise@0.8.3"); api.mainModule("runtime.js"); diff --git a/packages/ecmascript/README.md b/packages/ecmascript/README.md index 38f3903b4b..e624886590 100644 --- a/packages/ecmascript/README.md +++ b/packages/ecmascript/README.md @@ -1,4 +1,6 @@ -# [ecmascript](https://github.com/meteor/meteor/tree/devel/packages/ecmascript) +# ecmascript +[Source code of released version](https://github.com/meteor/meteor/tree/master/packages/ecmascript) | [Source code of development version](https://github.com/meteor/meteor/tree/devel/packages/ecmascript) +*** This package lets you use new JavaScript language features that are part of the [ECMAScript 2015 diff --git a/packages/ecmascript/package.js b/packages/ecmascript/package.js index 252280c8be..5fcfc72c29 100644 --- a/packages/ecmascript/package.js +++ b/packages/ecmascript/package.js @@ -1,6 +1,6 @@ Package.describe({ name: 'ecmascript', - version: '0.5.7', + version: '0.6.2-beta.1', summary: 'Compiler plugin that supports ES2015+ in all .js files', documentation: 'README.md' }); diff --git a/packages/ejson/README.md b/packages/ejson/README.md index 3da8c169b2..ac017deebc 100644 --- a/packages/ejson/README.md +++ b/packages/ejson/README.md @@ -1,4 +1,6 @@ # ejson +[Source code of released version](https://github.com/meteor/meteor/tree/master/packages/ejson) | [Source code of development version](https://github.com/meteor/meteor/tree/devel/packages/ejson) +*** EJSON is an extension of JSON to support more types. It supports all JSON-safe types, as well as: diff --git a/packages/ejson/package.js b/packages/ejson/package.js index 4fef9e2748..27c712f5a3 100644 --- a/packages/ejson/package.js +++ b/packages/ejson/package.js @@ -1,6 +1,6 @@ Package.describe({ summary: "Extended and Extensible JSON library", - version: '1.0.12' + version: '1.0.13' }); Package.onUse(function (api) { diff --git a/packages/email/README.md b/packages/email/README.md index 8b49e4d9d2..6f6e0363cb 100644 --- a/packages/email/README.md +++ b/packages/email/README.md @@ -1,4 +1,6 @@ # email +[Source code of released version](https://github.com/meteor/meteor/tree/master/packages/email) | [Source code of development version](https://github.com/meteor/meteor/tree/devel/packages/email) +*** The `email` package allows sending email from a Meteor app. diff --git a/packages/email/package.js b/packages/email/package.js index ad1dc37e5d..dad8b2d8b3 100644 --- a/packages/email/package.js +++ b/packages/email/package.js @@ -1,6 +1,6 @@ Package.describe({ summary: "Send email messages", - version: "1.1.16" + version: "1.1.18" }); Npm.depends({ diff --git a/packages/es5-shim/.npm/package/npm-shrinkwrap.json b/packages/es5-shim/.npm/package/npm-shrinkwrap.json index 5e557de55d..cf6fd2e3a9 100644 --- a/packages/es5-shim/.npm/package/npm-shrinkwrap.json +++ b/packages/es5-shim/.npm/package/npm-shrinkwrap.json @@ -1,9 +1,9 @@ { "dependencies": { "es5-shim": { - "version": "4.5.7", - "resolved": "https://registry.npmjs.org/es5-shim/-/es5-shim-4.5.7.tgz", - "from": "https://registry.npmjs.org/es5-shim/-/es5-shim-4.5.7.tgz" + "version": "4.5.9", + "resolved": "https://registry.npmjs.org/es5-shim/-/es5-shim-4.5.9.tgz", + "from": "es5-shim@4.5.9" } } } diff --git a/packages/es5-shim/README.md b/packages/es5-shim/README.md index 8aac900c5e..db2c865add 100644 --- a/packages/es5-shim/README.md +++ b/packages/es5-shim/README.md @@ -1,4 +1,6 @@ # es5-shim +[Source code of released version](https://github.com/meteor/meteor/tree/master/packages/es5-shim) | [Source code of development version](https://github.com/meteor/meteor/tree/devel/packages/es5-shim) +*** This package improves ECMAScript 5 compliance in all browers, particularly older browsers, and especially Internet Explorer 8 (and earlier). diff --git a/packages/es5-shim/package.js b/packages/es5-shim/package.js index 30e5b6451a..e114a1b51a 100644 --- a/packages/es5-shim/package.js +++ b/packages/es5-shim/package.js @@ -1,12 +1,12 @@ Package.describe({ name: "es5-shim", - version: "4.6.13", + version: "4.6.15", summary: "Shims and polyfills to improve ECMAScript 5 support", documentation: "README.md" }); Npm.depends({ - "es5-shim": "4.5.7" + "es5-shim": "4.5.9" }); Package.onUse(function(api) { diff --git a/packages/facebook-config-ui/README.md b/packages/facebook-config-ui/README.md new file mode 100644 index 0000000000..11efb78d44 --- /dev/null +++ b/packages/facebook-config-ui/README.md @@ -0,0 +1,3 @@ +#facebook-config-ui + +This package contains Blaze configuration UI templates for Facebook OAuth. diff --git a/packages/facebook/facebook_configure.html b/packages/facebook-config-ui/facebook_configure.html similarity index 100% rename from packages/facebook/facebook_configure.html rename to packages/facebook-config-ui/facebook_configure.html diff --git a/packages/facebook/facebook_configure.js b/packages/facebook-config-ui/facebook_configure.js similarity index 100% rename from packages/facebook/facebook_configure.js rename to packages/facebook-config-ui/facebook_configure.js diff --git a/packages/accounts-facebook/facebook_login_button.css b/packages/facebook-config-ui/facebook_login_button.css similarity index 100% rename from packages/accounts-facebook/facebook_login_button.css rename to packages/facebook-config-ui/facebook_login_button.css diff --git a/packages/facebook-config-ui/package.js b/packages/facebook-config-ui/package.js new file mode 100644 index 0000000000..c580ad00de --- /dev/null +++ b/packages/facebook-config-ui/package.js @@ -0,0 +1,13 @@ +Package.describe({ + summary: "Blaze configuration templates for Facebook OAuth.", + version: "1.0.0-beta.1" +}); + +Package.onUse(function(api) { + api.use('templating@1.2.13', 'client'); + + api.addFiles('facebook_login_button.css', 'client'); + api.addFiles( + ['facebook_configure.html', 'facebook_configure.js'], + 'client'); +}); diff --git a/packages/blaze/.gitignore b/packages/facebook-oauth/.gitignore similarity index 100% rename from packages/blaze/.gitignore rename to packages/facebook-oauth/.gitignore diff --git a/packages/facebook-oauth/README.md b/packages/facebook-oauth/README.md new file mode 100644 index 0000000000..4070b88bc1 --- /dev/null +++ b/packages/facebook-oauth/README.md @@ -0,0 +1,3 @@ +# facebook-oauth + +An implementation of the Facebook OAuth flow. See the [Meteor Guide](https://guide.meteor.com/accounts.html) for more details. diff --git a/packages/facebook/facebook_client.js b/packages/facebook-oauth/facebook_client.js similarity index 94% rename from packages/facebook/facebook_client.js rename to packages/facebook-oauth/facebook_client.js index 6ad938b545..5cd457ee7f 100644 --- a/packages/facebook/facebook_client.js +++ b/packages/facebook-oauth/facebook_client.js @@ -37,8 +37,8 @@ Facebook.requestCredential = function (options, credentialRequestCompleteCallbac '&state=' + OAuth._stateParam(loginStyle, credentialToken, options && options.redirectUrl); // Handle authentication type (e.g. for force login you need authType: "reauthenticate") - if (options && options.authType) { - loginUrl += "&authType=" + encodeURIComponent(options.authType); + if (options && options.auth_type) { + loginUrl += "&auth_type=" + encodeURIComponent(options.auth_type); } OAuth.launchLogin({ diff --git a/packages/facebook/facebook_server.js b/packages/facebook-oauth/facebook_server.js similarity index 92% rename from packages/facebook/facebook_server.js rename to packages/facebook-oauth/facebook_server.js index 36ddb48b03..c52ab64ce9 100644 --- a/packages/facebook/facebook_server.js +++ b/packages/facebook-oauth/facebook_server.js @@ -2,12 +2,7 @@ Facebook = {}; var querystring = Npm.require('querystring'); - -OAuth.registerService('facebook', 2, null, function(query) { - - var response = getTokenResponse(query); - var accessToken = response.accessToken; - +Facebook.handleAuthFromAccessToken = function handleAuthFromAccessToken(accessToken, expiresAt) { // include all fields from facebook // http://developers.facebook.com/docs/reference/login/public-profile-and-friend-list/ var whitelisted = ['id', 'email', 'name', 'first_name', @@ -17,10 +12,9 @@ OAuth.registerService('facebook', 2, null, function(query) { var serviceData = { accessToken: accessToken, - expiresAt: (+new Date) + (1000 * response.expiresIn) + expiresAt: expiresAt }; - var fields = _.pick(identity, whitelisted); _.extend(serviceData, fields); @@ -28,6 +22,14 @@ OAuth.registerService('facebook', 2, null, function(query) { serviceData: serviceData, options: {profile: {name: identity.name}} }; +}; + +OAuth.registerService('facebook', 2, null, function(query) { + var response = getTokenResponse(query); + var accessToken = response.accessToken; + var expiresIn = response.expiresIn; + + return Facebook.handleAuthFromAccessToken(accessToken, (+new Date) + (1000 * expiresIn)); }); // checks whether a string parses as JSON diff --git a/packages/facebook/package.js b/packages/facebook-oauth/package.js similarity index 76% rename from packages/facebook/package.js rename to packages/facebook-oauth/package.js index 456e9e2525..cde5c54fbe 100644 --- a/packages/facebook/package.js +++ b/packages/facebook-oauth/package.js @@ -1,23 +1,18 @@ Package.describe({ summary: "Facebook OAuth flow", - version: "1.2.8" + version: "1.3.0-beta.1" }); Package.onUse(function(api) { api.use('oauth2', ['client', 'server']); api.use('oauth', ['client', 'server']); api.use('http', ['server']); - api.use('templating', 'client'); api.use('underscore', 'server'); api.use('random', 'client'); api.use('service-configuration', ['client', 'server']); - api.export('Facebook'); - - api.addFiles( - ['facebook_configure.html', 'facebook_configure.js'], - 'client'); - - api.addFiles('facebook_server.js', 'server'); api.addFiles('facebook_client.js', 'client'); + api.addFiles('facebook_server.js', 'server'); + + api.export('Facebook'); }); diff --git a/packages/facebook/README.md b/packages/facebook/README.md deleted file mode 100644 index f4c9f1c5fc..0000000000 --- a/packages/facebook/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# facebook - -An implementation of the Facebook OAuth flow. See the [project page](https://www.meteor.com/accounts) on Meteor Accounts for more details. XXX link \ No newline at end of file diff --git a/packages/facts/README.md b/packages/facts/README.md index ec37b267c6..15358322c6 100644 --- a/packages/facts/README.md +++ b/packages/facts/README.md @@ -1 +1,5 @@ +# facts +[Source code of released version](https://github.com/meteor/meteor/tree/master/packages/facts) | [Source code of development version](https://github.com/meteor/meteor/tree/devel/packages/facts) +*** + This is an internal Meteor package. \ No newline at end of file diff --git a/packages/facts/package.js b/packages/facts/package.js index 721cbe8af4..1c73053180 100644 --- a/packages/facts/package.js +++ b/packages/facts/package.js @@ -1,11 +1,11 @@ Package.describe({ summary: "Publish internal app statistics", - version: '1.0.8' + version: '1.0.9' }); Package.onUse(function (api) { api.use(['underscore'], ['client', 'server']); - api.use(['templating', 'mongo', 'ddp'], ['client']); + api.use(['templating@1.2.13', 'mongo', 'ddp'], ['client']); // Detect whether autopublish is used. api.use('autopublish', 'server', {weak: true}); diff --git a/packages/fastclick/README.md b/packages/fastclick/README.md index 6d2f3d0c3a..5d1f8e4ffa 100644 --- a/packages/fastclick/README.md +++ b/packages/fastclick/README.md @@ -1,4 +1,6 @@ # fastclick +[Source code of released version](https://github.com/meteor/meteor/tree/master/packages/fastclick) | [Source code of development version](https://github.com/meteor/meteor/tree/devel/packages/fastclick) +*** FastClick is a simple, easy-to-use library for eliminating the 300ms delay between a physical tap and the firing of a `click` event on mobile browsers. The diff --git a/packages/fastclick/package.js b/packages/fastclick/package.js index 319008989b..bef7ca2e6e 100755 --- a/packages/fastclick/package.js +++ b/packages/fastclick/package.js @@ -1,6 +1,6 @@ Package.describe({ summary: "Faster touch events on mobile", - version: '1.0.12' + version: '1.0.13' }); Package.onUse(function (api) { diff --git a/packages/force-ssl/README.md b/packages/force-ssl/README.md index 889c86886d..ddfe0bb9ea 100644 --- a/packages/force-ssl/README.md +++ b/packages/force-ssl/README.md @@ -1,4 +1,6 @@ # force-ssl +[Source code of released version](https://github.com/meteor/meteor/tree/master/packages/force-ssl) | [Source code of development version](https://github.com/meteor/meteor/tree/devel/packages/force-ssl) +*** This package, part of [Webapp](https://www.meteor.com/webapp), causes Meteor to redirect insecure connections (HTTP) to a secure URL diff --git a/packages/force-ssl/package.js b/packages/force-ssl/package.js index 7e9f371b58..76f2e6a095 100644 --- a/packages/force-ssl/package.js +++ b/packages/force-ssl/package.js @@ -1,6 +1,6 @@ Package.describe({ summary: "Require this application to use HTTPS", - version: "1.0.12", + version: "1.0.13", prodOnly: true }); diff --git a/packages/geojson-utils/README.md b/packages/geojson-utils/README.md index ec37b267c6..63f52701b4 100644 --- a/packages/geojson-utils/README.md +++ b/packages/geojson-utils/README.md @@ -1 +1,5 @@ +# geojson-utils +[Source code of released version](https://github.com/meteor/meteor/tree/master/packages/geojson-utils) | [Source code of development version](https://github.com/meteor/meteor/tree/devel/packages/geojson-utils) +*** + This is an internal Meteor package. \ No newline at end of file diff --git a/packages/geojson-utils/package.js b/packages/geojson-utils/package.js index ba4f6a9768..5ae045b046 100644 --- a/packages/geojson-utils/package.js +++ b/packages/geojson-utils/package.js @@ -1,6 +1,6 @@ Package.describe({ summary: 'GeoJSON utility functions (from https://github.com/maxogden/geojson-js-utils)', - version: '1.0.9' + version: '1.0.10' }); Package.onUse(function (api) { diff --git a/packages/github/README.md b/packages/github/README.md index 27a5e87808..bca8563ddb 100644 --- a/packages/github/README.md +++ b/packages/github/README.md @@ -1,3 +1,5 @@ # github +[Source code of released version](https://github.com/meteor/meteor/tree/master/packages/github) | [Source code of development version](https://github.com/meteor/meteor/tree/devel/packages/github) +*** An implementation of the GitHub OAuth flow. See the [project page](https://www.meteor.com/accounts) on Meteor Accounts for more details. XXX link \ No newline at end of file diff --git a/packages/github/package.js b/packages/github/package.js index ed9b6240fe..4481a19963 100644 --- a/packages/github/package.js +++ b/packages/github/package.js @@ -1,6 +1,6 @@ Package.describe({ summary: "Github OAuth flow", - version: "1.1.8" + version: "1.1.9" }); Package.onUse(function(api) { @@ -8,7 +8,7 @@ Package.onUse(function(api) { api.use('oauth', ['client', 'server']); api.use('http', ['server']); api.use('underscore', 'client'); - api.use('templating', 'client'); + api.use('templating@1.2.13', 'client'); api.use('random', 'client'); api.use('service-configuration', ['client', 'server']); diff --git a/packages/google/README.md b/packages/google/README.md index f232edd358..2271763f41 100644 --- a/packages/google/README.md +++ b/packages/google/README.md @@ -1,3 +1,5 @@ # google +[Source code of released version](https://github.com/meteor/meteor/tree/master/packages/google) | [Source code of development version](https://github.com/meteor/meteor/tree/devel/packages/google) +*** An implementation of the Google OAuth flow. See the [project page](https://www.meteor.com/accounts) on Meteor Accounts for more details. XXX link \ No newline at end of file diff --git a/packages/google/google_client.js b/packages/google/google_client.js index d8a9ea4e9a..b8af26fee4 100644 --- a/packages/google/google_client.js +++ b/packages/google/google_client.js @@ -23,9 +23,9 @@ Google.requestCredential = function (options, credentialRequestCompleteCallback) var credentialToken = Random.secret(); - // always need this to get user id from google. - var requiredScope = ['profile']; - var scope = ['email']; + // we need the email scope to get user id from google. + var requiredScope = ['email']; + var scope = ['profile']; if (options.requestPermissions) scope = options.requestPermissions; scope = _.union(scope, requiredScope); diff --git a/packages/google/package.js b/packages/google/package.js index 4dd3ec78a9..ef74f1aee7 100644 --- a/packages/google/package.js +++ b/packages/google/package.js @@ -1,6 +1,6 @@ Package.describe({ summary: "Google OAuth flow", - version: "1.1.13" + version: "1.1.15" }); Package.onUse(function(api) { @@ -8,7 +8,7 @@ Package.onUse(function(api) { api.use('oauth', ['client', 'server']); api.use('http', ['server']); api.use(['underscore', 'service-configuration'], ['client', 'server']); - api.use(['random', 'templating'], 'client'); + api.use(['random', 'templating@1.2.13'], 'client'); api.export('Google'); diff --git a/packages/handlebars/README.md b/packages/handlebars/README.md index ec37b267c6..b5883dc56f 100644 --- a/packages/handlebars/README.md +++ b/packages/handlebars/README.md @@ -1 +1,5 @@ +# handlebars +[Source code of released version](https://github.com/meteor/meteor/tree/master/packages/handlebars) | [Source code of development version](https://github.com/meteor/meteor/tree/devel/packages/handlebars) +*** + This is an internal Meteor package. \ No newline at end of file diff --git a/packages/hot-code-push/README.md b/packages/hot-code-push/README.md index 0f5cee0bd8..9d81f04a29 100644 --- a/packages/hot-code-push/README.md +++ b/packages/hot-code-push/README.md @@ -1,4 +1,6 @@ # hot-code-push +[Source code of released version](https://github.com/meteor/meteor/tree/master/packages/hot-code-push) | [Source code of development version](https://github.com/meteor/meteor/tree/devel/packages/hot-code-push) +*** Enables Meteor's hot code push functionality in development and production by including the `autoupdate` and `reload` packages in your app. diff --git a/packages/html-tools/.gitignore b/packages/html-tools/.gitignore deleted file mode 100644 index 677a6fc263..0000000000 --- a/packages/html-tools/.gitignore +++ /dev/null @@ -1 +0,0 @@ -.build* diff --git a/packages/html-tools/README.md b/packages/html-tools/README.md deleted file mode 100644 index 5b60bae263..0000000000 --- a/packages/html-tools/README.md +++ /dev/null @@ -1,139 +0,0 @@ -# html-tools - -A lightweight HTML tokenizer and parser which outputs to the HTMLjs -object representation. Special hooks allow the syntax to be extended -to parse an HTML-like template language like Spacebars. - -``` -HTMLTools.parseFragment("
Hello
World
") - -=> HTML.DIV({'class':'greeting'}, "Hello", HTML.BR(), "World")) -``` - -This package is used by the Spacebars compiler, which normally only -runs at bundle time but can also be used at runtime on the client or -server. - -## Invoking the Parser - -`HTMLTools.parseFragment(input, options)` - Takes an input string or Scanner object and returns HTMLjs. - -In the basic case, where no options are passed, `parseFragment` will consume the entire input (the full string or the rest of the Scanner). - -The options are as follows: - -#### getTemplateTag - -This option extends the HTML parser to parse template tags such as `{{foo}}`. - -`getTemplateTag: function (scanner, templateTagPosition) { ... }` - A function for the parser to call after every HTML token and at various positions within tags. If the function returns an instanceof `HTMLTools.TemplateTag`, it is inserted into the HTMLjs tree at the appropriate location. The constructor is `HTMLTools.TemplateTag(props)`, where props is an object whose properties are copied to the `TemplateTag` instance. You can also call the constructor with no arguments and assign whatever properties you want, or you can subclass `TemplateTag`. - -There are four possible outcomes when `getTemplateTag` is called: - -* Not a template tag - Leave the scanner as is, and return `null`. A quick peek at the next character should bail to this case if the start of a template tag is not seen. -* Bad template tag - Call `scanner.fatal`, which aborts parsing completely. Once the beginning of a template tag is seen, `getTemplateTag` will generally want to commit, and either succeed or fail trying). -* Good template tag - Advance the scanner to the end of the template tag and return an `HTMLTools.TemplateTag` object. -* Comment tag - Advance the scanner and return `null`. For example, a Spacebars comment is `{{! foo}}`. - -The `templateTagPosition` argument to `getTemplateTag` is one of: - -* `HTMLTools.TEMPLATE_TAG_POSITION.ELEMENT` - At "element level," meaning somewhere an HTML tag could be. -* `HTMLTools.TEMPLATE_TAG_POSITION.IN_START_TAG` - Inside a start tag, as in `
`, where you might otherwise find `name=value`. -* `HTMLTools.TEMPLATE_TAG_POSITION.IN_ATTRIBUTE` - Inside the value of an HTML attribute, as in `
`. -* `HTMLTools.TEMPLATE_TAG_POSITION.IN_RCDATA` - Inside a TEXTAREA or a block helper inside an attribute, where character references are allowed ("replaced character data") but not tags. -* `HTMLTools.TEMPLATE_TAG_POSITION.IN_RAWTEXT` - In a context where character references are not parsed, such as a script tag, style tag, or markdown helper. - -It's completely normal for `getTemplateTag` to invoke `HTMLTools.parseFragment` recursively on the same scanner (see `shouldStop`). If it does so, the same value of `getTemplateTag` must be passed to the second invocation. - -At the moment, template tags must begin with `{`. The parser does not try calling `getTemplateTag` for every character of an HTML document, only at token boundaries, and it knows to always end a token at `{`. - -#### textMode - -The `textMode` option, if present, causes the parser to parse text (such as the contents of a `', TEXTAREA({value: "asdf"})); - succeed('', TEXTAREA({x: "y", value: "asdf"})); - succeed('', TEXTAREA({value: "

"})); - succeed('', - TEXTAREA({value: ["a", CharRef({html: '&', str: '&'}), "b"]})); - succeed('', TEXTAREA({value: "\n', TEXTAREA()); - succeed('', TEXTAREA({value: "asdf"})); - succeed('', TEXTAREA({value: "\nasdf"})); - succeed('', TEXTAREA({value: "\n"})); - succeed('', TEXTAREA({value: "asdf\n"})); - succeed('', TEXTAREA({value: ""})); - succeed('', TEXTAREA({value: "asdf"})); - fatal(''); - succeed('', TEXTAREA({value: "&"})); - succeed('asdf', - [TEXTAREA({value: "x

', [DIV("x"), TEXTAREA()]); - - // CR/LF behavior - succeed('', BR({x:''})); - succeed('', BR({x:''})); - succeed('
', BR({x:'y'})); - succeed('
', BR({x:'y'})); - succeed('
', BR({x:'y'})); - succeed('
', BR({x:'y'})); - succeed('
', BR({x:'y'})); - succeed('', Comment('\n')); - succeed('', Comment('\n')); - succeed('', TEXTAREA({value: 'a\nb\nc'})); - succeed('', TEXTAREA({value: 'a\nb\nc'})); - succeed('
', BR({x:'\n\n'})); - succeed('
', BR({x:'\n\n'})); - succeed('
', BR({x:'y'})); - fatal('
'); - - succeed('',SCRIPT('var x="
";')); - succeed('',SCRIPT('var x=1 && 0;')); - - succeed('', SCRIPT("asdf")); - succeed('', SCRIPT({x: "y"}, "asdf")); - succeed('', SCRIPT("

")); - succeed('', SCRIPT("a&b")); - succeed('', SCRIPT("\n', SCRIPT("\n")); - succeed('', SCRIPT("")); - succeed('', SCRIPT("asdf")); - fatal('', 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('', STYLE("&davidgreenspan;")); - succeed('', STYLE("&")); - succeed('asdf', - [STYLE("

Hello

")), - BlazeTools.toJS(DIV(P({id:'foo'}, 'Hello')))); - - _.each(['asdf
', '{{!foo}}
', '{{!foo}}
', - 'asdf', '{{!foo}}', '{{!foo}} '], function (badFrag) { - test.throws(function() { - HTMLTools.parseFragment(badFrag); - }, /Unexpected HTML close tag/); - }); - - (function () { - var p = HTMLTools.parseFragment('

'); - test.equal(p.tagName, 'p'); - test.equal(p.attrs, null); - test.isTrue(p instanceof HTML.Tag); - test.equal(p.children.length, 0); - })(); - - (function () { - var p = HTMLTools.parseFragment('

x

'); - test.equal(p.tagName, 'p'); - test.equal(p.attrs, null); - test.isTrue(p instanceof HTML.Tag); - test.equal(p.children.length, 1); - test.equal(p.children[0], 'x'); - })(); - - (function () { - var p = HTMLTools.parseFragment('

xA

'); - test.equal(p.tagName, 'p'); - test.equal(p.attrs, null); - test.isTrue(p instanceof HTML.Tag); - test.equal(p.children.length, 2); - test.equal(p.children[0], 'x'); - - test.isTrue(p.children[1] instanceof HTML.CharRef); - test.equal(p.children[1].html, 'A'); - test.equal(p.children[1].str, 'A'); - })(); - - (function () { - var pp = HTMLTools.parseFragment('

x

y

'); - test.isTrue(pp instanceof Array); - test.equal(pp.length, 2); - - test.equal(pp[0].tagName, 'p'); - test.equal(pp[0].attrs, null); - test.isTrue(pp[0] instanceof HTML.Tag); - test.equal(pp[0].children.length, 1); - test.equal(pp[0].children[0], 'x'); - - test.equal(pp[1].tagName, 'p'); - test.equal(pp[1].attrs, null); - test.isTrue(pp[1] instanceof HTML.Tag); - test.equal(pp[1].children.length, 1); - test.equal(pp[1].children[0], 'y'); - })(); - - var scanner = new Scanner('asdf'); - scanner.pos = 1; - test.equal(HTMLTools.parseFragment(scanner), 'sdf'); - - test.throws(function () { - var scanner = new Scanner('asdf

'); - scanner.pos = 1; - HTMLTools.parseFragment(scanner); - }); -}); - -Tinytest.add("html-tools - getTemplateTag", function (test) { - - // match a simple tag consisting of `{{`, an optional `!`, one - // or more ASCII letters, spaces or html tags, and a closing `}}`. - var mustache = /^\{\{(!?[a-zA-Z 0-9]+)\}\}/; - - // This implementation of `getTemplateTag` looks for "{{" and if it - // finds it, it will match the regex above or fail fatally trying. - // The object it returns is opaque to the tokenizer/parser and can - // be anything we want. - var getTemplateTag = function (scanner, position) { - if (! (scanner.peek() === '{' && // one-char peek is just an optimization - scanner.rest().slice(0, 2) === '{{')) - return null; - - var match = mustache.exec(scanner.rest()); - if (! match) - scanner.fatal("Bad mustache"); - - scanner.pos += match[0].length; - - if (match[1].charAt(0) === '!') - return null; // `{{!foo}}` is like a comment - - return TemplateTag({ stuff: match[1] }); - }; - - - - var succeed = function (input, expected) { - var endPos = input.indexOf('^^^'); - if (endPos < 0) - endPos = input.length; - - var scanner = new Scanner(input.replace('^^^', '')); - scanner.getTemplateTag = getTemplateTag; - var result; - try { - result = getContent(scanner); - } catch (e) { - result = String(e); - } - test.equal(scanner.pos, endPos); - test.equal(BlazeTools.toJS(result), BlazeTools.toJS(expected)); - }; - - var fatal = function (input, messageContains) { - var scanner = new Scanner(input); - scanner.getTemplateTag = getTemplateTag; - var error; - try { - getContent(scanner); - } catch (e) { - error = e; - } - test.isTrue(error); - if (messageContains) - test.isTrue(messageContains && error.message.indexOf(messageContains) >= 0, error.message); - }; - - - succeed('{{foo}}', TemplateTag({stuff: 'foo'})); - - succeed('{{foo}}', - A({href: "http://www.apple.com/"}, TemplateTag({stuff: 'foo'}))); - - // tags not parsed in comments - succeed('', Comment("{{foo}}")); - succeed('', Comment("{{foo")); - - succeed('&am{{foo}}p;', ['&am', TemplateTag({stuff: 'foo'}), 'p;']); - - // can't start a mustache and not finish it - fatal('{{foo'); - fatal('{{'); - - // no mustache allowed in tag name - fatal('<{{a}}>'); - fatal('<{{a}}b>'); - fatal(''); - - // single curly brace is no biggie - succeed('a{b', 'a{b'); - succeed('
', 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('
{{!foo}}{{!bar}}
', DIV()); - succeed('
{{!foo}}
{{!bar}}
', DIV(BR())); - succeed('
{{!foo}} {{!bar}}
', DIV(" ")); - succeed('
{{!foo}}
{{!bar}}
', DIV(" ", BR(), " ")); - succeed('{{!
}}', null); - succeed('{{!
}}', null); - - succeed('', null); - succeed('{{!foo}}', null); - - succeed('', - TEXTAREA(Attrs({x:"1"}, TemplateTag({stuff: 'a'}), - TemplateTag({stuff: 'b'})))); - -}); diff --git a/packages/html-tools/scanner.js b/packages/html-tools/scanner.js deleted file mode 100644 index d110a8a814..0000000000 --- a/packages/html-tools/scanner.js +++ /dev/null @@ -1,82 +0,0 @@ -// This is a Scanner class suitable for any parser/lexer/tokenizer. -// -// A Scanner has an immutable source document (string) `input` and a current -// position `pos`, an index into the string, which can be set at will. -// -// * `new Scanner(input)` - constructs a Scanner with source string `input` -// * `scanner.rest()` - returns the rest of the input after `pos` -// * `scanner.peek()` - returns the character at `pos` -// * `scanner.isEOF()` - true if `pos` is at or beyond the end of `input` -// * `scanner.fatal(msg)` - throw an error indicating a problem at `pos` - -Scanner = HTMLTools.Scanner = function (input) { - this.input = input; // public, read-only - this.pos = 0; // public, read-write -}; - -Scanner.prototype.rest = function () { - // Slicing a string is O(1) in modern JavaScript VMs (including old IE). - return this.input.slice(this.pos); -}; - -Scanner.prototype.isEOF = function () { - return this.pos >= this.input.length; -}; - -Scanner.prototype.fatal = function (msg) { - // despite this default, you should always provide a message! - msg = (msg || "Parse error"); - - var CONTEXT_AMOUNT = 20; - - var input = this.input; - var pos = this.pos; - var pastInput = input.substring(pos - CONTEXT_AMOUNT - 1, pos); - if (pastInput.length > CONTEXT_AMOUNT) - pastInput = '...' + pastInput.substring(-CONTEXT_AMOUNT); - - var upcomingInput = input.substring(pos, pos + CONTEXT_AMOUNT + 1); - if (upcomingInput.length > CONTEXT_AMOUNT) - upcomingInput = upcomingInput.substring(0, CONTEXT_AMOUNT) + '...'; - - var positionDisplay = ((pastInput + upcomingInput).replace(/\n/g, ' ') + '\n' + - (new Array(pastInput.length + 1).join(' ')) + "^"); - - var e = new Error(msg + "\n" + positionDisplay); - - e.offset = pos; - var allPastInput = input.substring(0, pos); - e.line = (1 + (allPastInput.match(/\n/g) || []).length); - e.col = (1 + pos - allPastInput.lastIndexOf('\n')); - e.scanner = this; - - throw e; -}; - -// Peek at the next character. -// -// If `isEOF`, returns an empty string. -Scanner.prototype.peek = function () { - return this.input.charAt(this.pos); -}; - -// Constructs a `getFoo` function where `foo` is specified with a regex. -// The regex should start with `^`. The constructed function will return -// match group 1, if it exists and matches a non-empty string, or else -// the entire matched string (or null if there is no match). -// -// A `getFoo` function tries to match and consume a foo. If it succeeds, -// the current position of the scanner is advanced. If it fails, the -// current position is not advanced and a falsy value (typically null) -// is returned. -makeRegexMatcher = function (regex) { - return function (scanner) { - var match = regex.exec(scanner.rest()); - - if (! match) - return null; - - scanner.pos += match[0].length; - return match[1] || match[0]; - }; -}; diff --git a/packages/html-tools/templatetag.js b/packages/html-tools/templatetag.js deleted file mode 100644 index f94f9fd214..0000000000 --- a/packages/html-tools/templatetag.js +++ /dev/null @@ -1,29 +0,0 @@ -// _assign is like _.extend or the upcoming Object.assign. -// Copy src's own, enumerable properties onto tgt and return -// tgt. -var _hasOwnProperty = Object.prototype.hasOwnProperty; -var _assign = function (tgt, src) { - for (var k in src) { - if (_hasOwnProperty.call(src, k)) - tgt[k] = src[k]; - } - return tgt; -}; - - -HTMLTools.TemplateTag = function (props) { - if (! (this instanceof HTMLTools.TemplateTag)) - // called without `new` - return new HTMLTools.TemplateTag; - - if (props) - _assign(this, props); -}; - -_assign(HTMLTools.TemplateTag.prototype, { - constructorName: 'HTMLTools.TemplateTag', - toJS: function (visitor) { - return visitor.generateCall(this.constructorName, - _assign({}, this)); - } -}); diff --git a/packages/html-tools/tokenize.js b/packages/html-tools/tokenize.js deleted file mode 100644 index de0b9c0662..0000000000 --- a/packages/html-tools/tokenize.js +++ /dev/null @@ -1,513 +0,0 @@ -// Token types: -// -// { t: 'Doctype', -// v: String (entire Doctype declaration from the source), -// name: String, -// systemId: String (optional), -// publicId: String (optional) -// } -// -// { t: 'Comment', -// v: String (not including "") -// } -// -// { t: 'Chars', -// v: String (pure text like you might pass to document.createTextNode, -// no character references) -// } -// -// { t: 'Tag', -// isEnd: Boolean (optional), -// isSelfClosing: Boolean (optional), -// n: String (tag name, in lowercase or camel case), -// attrs: dictionary of { String: [tokens] } -// OR [{ String: [tokens] }, TemplateTag tokens...] -// (only for start tags; required) -// } -// -// { t: 'CharRef', -// v: String (entire character reference from the source, e.g. "&"), -// cp: [Integer] (array of Unicode code point numbers it expands to) -// } -// -// We keep around both the original form of the character reference and its -// expansion so that subsequent processing steps have the option to -// re-emit it (if they are generating HTML) or interpret it. Named and -// numerical code points may be more than 16 bits, in which case they -// need to passed through codePointToString to make a JavaScript string. -// Most named entities and all numeric character references are one codepoint -// (e.g. "&" is [38]), but a few are two codepoints. -// -// { t: 'TemplateTag', -// v: HTMLTools.TemplateTag -// } - -// The HTML tokenization spec says to preprocess the input stream to replace -// CR(LF)? with LF. However, preprocessing `scanner` would complicate things -// by making indexes not match the input (e.g. for error messages), so we just -// keep in mind as we go along that an LF might be represented by CRLF or CR. -// In most cases, it doesn't actually matter what combination of whitespace -// characters are present (e.g. inside tags). -var HTML_SPACE = /^[\f\n\r\t ]/; - -var convertCRLF = function (str) { - return str.replace(/\r\n?/g, '\n'); -}; - -getComment = HTMLTools.Parse.getComment = function (scanner) { - if (scanner.rest().slice(0, 4) !== ''); - if (closePos < 0) - scanner.fatal("Unclosed HTML comment"); - - var commentContents = rest.slice(0, closePos); - if (commentContents.slice(-1) === '-') - scanner.fatal("HTML comment must end at first `--`"); - if (commentContents.indexOf("--") >= 0) - scanner.fatal("HTML comment cannot contain `--` anywhere"); - if (commentContents.indexOf('\u0000') >= 0) - scanner.fatal("HTML comment cannot contain NULL"); - - scanner.pos += closePos + 3; - - return { t: 'Comment', - v: convertCRLF(commentContents) }; -}; - -var skipSpaces = function (scanner) { - while (HTML_SPACE.test(scanner.peek())) - scanner.pos++; -}; - -var requireSpaces = function (scanner) { - if (! HTML_SPACE.test(scanner.peek())) - scanner.fatal("Expected space"); - skipSpaces(scanner); -}; - -var getDoctypeQuotedString = function (scanner) { - var quote = scanner.peek(); - if (! (quote === '"' || quote === "'")) - scanner.fatal("Expected single or double quote in DOCTYPE"); - scanner.pos++; - - if (scanner.peek() === quote) - // prevent a falsy return value (empty string) - scanner.fatal("Malformed DOCTYPE"); - - var str = ''; - var ch; - while ((ch = scanner.peek()), ch !== quote) { - if ((! ch) || (ch === '\u0000') || (ch === '>')) - scanner.fatal("Malformed DOCTYPE"); - str += ch; - scanner.pos++; - } - - scanner.pos++; - - return str; -}; - -// See http://www.whatwg.org/specs/web-apps/current-work/multipage/syntax.html#the-doctype. -// -// If `getDocType` sees "') || (ch === '\u0000')) - scanner.fatal('Malformed DOCTYPE'); - var name = ch; - scanner.pos++; - - while ((ch = scanner.peek()), ! (HTML_SPACE.test(ch) || ch === '>')) { - if ((! ch) || (ch === '\u0000')) - scanner.fatal('Malformed DOCTYPE'); - name += ch; - scanner.pos++; - } - name = HTMLTools.asciiLowerCase(name); - - // Now we're looking at a space or a `>`. - skipSpaces(scanner); - - var systemId = null; - var publicId = null; - - if (scanner.peek() !== '>') { - // Now we're essentially in the "After DOCTYPE name state" of the tokenizer, - // but we're not looking at space or `>`. - - // this should be "public" or "system". - var publicOrSystem = HTMLTools.asciiLowerCase(scanner.rest().slice(0, 6)); - - if (publicOrSystem === 'system') { - scanner.pos += 6; - requireSpaces(scanner); - systemId = getDoctypeQuotedString(scanner); - skipSpaces(scanner); - if (scanner.peek() !== '>') - scanner.fatal("Malformed DOCTYPE"); - } else if (publicOrSystem === 'public') { - scanner.pos += 6; - requireSpaces(scanner); - publicId = getDoctypeQuotedString(scanner); - if (scanner.peek() !== '>') { - requireSpaces(scanner); - if (scanner.peek() !== '>') { - systemId = getDoctypeQuotedString(scanner); - skipSpaces(scanner); - if (scanner.peek() !== '>') - scanner.fatal("Malformed DOCTYPE"); - } - } - } else { - scanner.fatal("Expected PUBLIC or SYSTEM in DOCTYPE"); - } - } - - // looking at `>` - scanner.pos++; - var result = { t: 'Doctype', - v: scanner.input.slice(start, scanner.pos), - name: name }; - - if (systemId) - result.systemId = systemId; - if (publicId) - result.publicId = publicId; - - return result; -}; - -// The special character `{` is only allowed as the first character -// of a Chars, so that we have a chance to detect template tags. -var getChars = makeRegexMatcher(/^[^&<\u0000][^&<\u0000{]*/); - -var assertIsTemplateTag = function (x) { - if (! (x instanceof HTMLTools.TemplateTag)) - throw new Error("Expected an instance of HTMLTools.TemplateTag"); - return x; -}; - -// Returns the next HTML token, or `null` if we reach EOF. -// -// Note that if we have a `getTemplateTag` function that sometimes -// consumes characters and emits nothing (e.g. in the case of template -// comments), we may go from not-at-EOF to at-EOF and return `null`, -// while otherwise we always find some token to return. -getHTMLToken = HTMLTools.Parse.getHTMLToken = function (scanner, dataMode) { - var result = null; - if (scanner.getTemplateTag) { - // Try to parse a template tag by calling out to the provided - // `getTemplateTag` function. If the function returns `null` but - // consumes characters, it must have parsed a comment or something, - // so we loop and try it again. If it ever returns `null` without - // consuming anything, that means it didn't see anything interesting - // so we look for a normal token. If it returns a truthy value, - // the value must be instanceof HTMLTools.TemplateTag. We wrap it - // in a Special token. - var lastPos = scanner.pos; - result = scanner.getTemplateTag( - scanner, - (dataMode === 'rcdata' ? TEMPLATE_TAG_POSITION.IN_RCDATA : - (dataMode === 'rawtext' ? TEMPLATE_TAG_POSITION.IN_RAWTEXT : - TEMPLATE_TAG_POSITION.ELEMENT))); - - if (result) - return { t: 'TemplateTag', v: assertIsTemplateTag(result) }; - else if (scanner.pos > lastPos) - return null; - } - - var chars = getChars(scanner); - if (chars) - return { t: 'Chars', - v: convertCRLF(chars) }; - - var ch = scanner.peek(); - if (! ch) - return null; // EOF - - if (ch === '\u0000') - scanner.fatal("Illegal NULL character"); - - if (ch === '&') { - if (dataMode !== 'rawtext') { - var charRef = getCharacterReference(scanner); - if (charRef) - return charRef; - } - - scanner.pos++; - return { t: 'Chars', - v: '&' }; - } - - // If we're here, we're looking at `<`. - - if (scanner.peek() === '<' && dataMode) { - // don't interpret tags - scanner.pos++; - return { t: 'Chars', - v: '<' }; - } - - // `getTag` will claim anything starting with `<` not followed by `!`. - // `getComment` takes `")), - { t: 'Comment', v: ' hello ' }); - - ignore(""); - ignore("', 'Unclosed'); - fatal('', 'cannot contain'); - fatal('', 'must end at first'); - - fatal('', 'cannot contain'); - fatal('', 'cannot contain'); - - succeed('', ''); - succeed('', '-x'); - succeed('', 'x'); - succeed('', ' hello - - world '); -}); - -Tinytest.add("html-tools - doctype", function (test) { - var succeed = function (input, expectedProps) { - var scanner = new Scanner(input); - var result = getDoctype(scanner); - test.isTrue(result); - test.equal(scanner.pos, result.v.length); - test.equal(input.slice(0, result.v.length), result.v); - var actualProps = _.extend({}, result); - delete actualProps.t; - delete actualProps.v; - test.equal(actualProps, expectedProps); - }; - - var fatal = function (input, messageContains) { - var scanner = new Scanner(input); - var error; - try { - getDoctype(scanner); - } catch (e) { - error = e; - } - test.isTrue(error); - if (messageContains) - test.isTrue(error.message.indexOf(messageContains) >= 0, error.message); - }; - - test.equal(getDoctype(new Scanner("x")), - { t: 'Doctype', - v: '', - name: 'html' }); - - test.equal(getDoctype(new Scanner("x")), - { t: 'Doctype', - v: "", - name: 'html', - systemId: 'about:legacy-compat' }); - - test.equal(getDoctype(new Scanner("x")), - { t: 'Doctype', - v: "", - name: 'html', - publicId: '-//W3C//DTD HTML 4.0//EN' }); - - test.equal(getDoctype(new Scanner("x")), - { t: 'Doctype', - v: "", - name: 'html', - publicId: '-//W3C//DTD HTML 4.0//EN', - systemId: 'http://www.w3.org/TR/html4/strict.dtd' }); - - succeed('', {name: 'html'}); - succeed('', {name: 'html'}); - succeed('', {name: 'html'}); - succeed('', {name: 'html'}); - succeed('', {name: 'html'}); - succeed('', {name: 'html'}); - fatal('', 'Expected space'); - fatal('', 'Malformed DOCTYPE'); - fatal('', 'Malformed DOCTYPE'); - fatal('', {name: 'html', systemId: 'about:legacy-compat'}); - succeed('', {name: 'html', systemId: 'about:legacy-compat'}); - succeed("", {name: 'html', systemId: 'about:legacy-compat'}); - succeed("", {name: 'html', systemId: 'about:legacy-compat'}); - succeed('', {name: 'html', systemId: 'about:legacy-compat'}); - fatal('', 'Expected PUBLIC or SYSTEM'); - fatal('', 'Expected space'); - fatal(''); - fatal(''); - fatal('">'); - fatal(''); - fatal(''); - fatal(''); - fatal(''); - fatal(''); - - succeed('', - { name: 'html', - publicId: '-//W3C//DTD HTML 4.0//EN'}); - succeed('', - { name: 'html', - publicId: '-//W3C//DTD HTML 4.0//EN'}); - succeed('', - { name: 'html', - publicId: '-//W3C//DTD HTML 4.0//EN', - systemId: 'http://www.w3.org/TR/REC-html40/strict.dtd'}); - succeed('', - { name: 'html', - publicId: '-//W3C//DTD HTML 4.0//EN', - systemId: 'http://www.w3.org/TR/REC-html40/strict.dtd'}); - succeed('', - { name: 'html', - publicId: '-//W3C//DTD HTML 4.0//EN', - systemId: 'http://www.w3.org/TR/REC-html40/strict.dtd'}); - succeed('', - { name: 'html', - publicId: '-//W3C//DTD HTML 4.0//EN', - systemId: 'http://www.w3.org/TR/REC-html40/strict.dtd'}); - fatal(''); - fatal(''); -}); - -Tinytest.add("html-tools - tokenize", function (test) { - - var fatal = function (input, messageContains) { - var error; - try { - tokenize(input); - } catch (e) { - error = e; - } - test.isTrue(error); - if (messageContains) - test.isTrue(error.message.indexOf(messageContains) >= 0, error.message); - }; - - - test.equal(tokenize(''), []); - test.equal(tokenize('abc'), [{t: 'Chars', v: 'abc'}]); - test.equal(tokenize('&'), [{t: 'Chars', v: '&'}]); - test.equal(tokenize('&'), [{t: 'CharRef', v: '&', cp: [38]}]); - test.equal(tokenize('ok fine'), - [{t: 'Chars', v: 'ok'}, - {t: 'CharRef', v: ' ', cp: [32]}, - {t: 'Chars', v: 'fine'}]); - - test.equal(tokenize('ac'), - [{t: 'Chars', - v: 'a'}, - {t: 'Comment', - v: 'b'}, - {t: 'Chars', - v: 'c'}]); - - test.equal(tokenize('
'), [{t: 'Tag', n: 'a'}]); - - fatal('<'); - fatal(''), - [{t: 'Tag', n: 'a', - attrs: { b: [{t: 'Chars', v: 'c'}], - d: [{t: 'Chars', v: 'e'}], - f: [{t: 'Chars', v: 'g'}], - h: [] }}]); - - fatal(''); - fatal(''); - fatal(''); - - test.equal(tokenize(''), [{t: 'Tag', n: 'a', isSelfClosing: true}]); - - fatal(''); - fatal(''); - fatal(''); - fatal(''); - - test.equal(tokenize(''), - [{t: 'Tag', n: 'a#', - attrs: { b0: [{t: 'Chars', v: 'c@'}], - d1: [{t: 'Chars', v: 'e2'}], - 'f#': [{t: 'Chars', v: 'g '}], - h: [] }}]); - - test.equal(tokenize('
'), - [{t: 'Tag', n: 'div', attrs: { 'class': [] }}, - {t: 'Tag', n: 'div', isEnd: true}]); - - test.equal(tokenize('
'), - [{t: 'Tag', n: 'div', attrs: { 'class': [{t: 'Chars', v: '&'}] }}]); - test.equal(tokenize('
'), - [{t: 'Tag', n: 'div', attrs: { 'class': [{t: 'Chars', v: '&'}] }}]); - test.equal(tokenize('
'), - [{t: 'Tag', n: 'div', attrs: { 'class': [{t: 'CharRef', v: '&', cp: [38]}] }}]); - - test.equal(tokenize('
'), - [{t: 'Tag', n: 'div', attrs: { 'class': [ - {t: 'Chars', v: 'aa&'}, - {t: 'CharRef', v: '𝕫', cp: [120171]}, - {t: 'CharRef', v: '∾̳', cp: [8766, 819]}, - {t: 'Chars', v: '&bb'} - ] }}]); - - test.equal(tokenize('
'), - [{t: 'Tag', n: 'div', attrs: { 'class': [ - {t: 'Chars', v: 'aa &'}, - {t: 'CharRef', v: '𝕫', cp: [120171]}, - {t: 'CharRef', v: '∾̳', cp: [8766, 819]}, - {t: 'Chars', v: '& bb'} - ] }}]); - - test.equal(tokenize(''), - [{t: 'Tag', n: 'a', attrs: { b: [{t: 'Chars', v: '\'`<>&'}] }}]); - test.equal(tokenize('&\'>'), - [{t: 'Tag', n: 'a', attrs: { b: [{t: 'Chars', v: '"`<>&'}] }}]); - - fatal('>'); - fatal('>c'); - test.equal(tokenize(''), - [{t: 'Tag', n: 'a', attrs: { b: [{t: 'Chars', v: '>c' }] }}]); - test.equal(tokenize(''), - [{t: 'Tag', n: 'a', attrs: { b: [{t: 'Chars', v: '>c' }] }}]); - fatal(''); - fatal(''); - fatal(''); - - fatal(''); - - fatal(''); - fatal('<{{a}}>'); - fatal(''); // end tag can't have attributes - fatal(''); // end tag can't be self-closing - fatal(''); -}); diff --git a/packages/html-tools/utils.js b/packages/html-tools/utils.js deleted file mode 100644 index 934bc09e02..0000000000 --- a/packages/html-tools/utils.js +++ /dev/null @@ -1,50 +0,0 @@ - -HTMLTools = {}; -HTMLTools.Parse = {}; - -var asciiLowerCase = HTMLTools.asciiLowerCase = function (str) { - return str.replace(/[A-Z]/g, function (c) { - return String.fromCharCode(c.charCodeAt(0) + 32); - }); -}; - -var svgCamelCaseAttributes = 'attributeName attributeType baseFrequency baseProfile calcMode clipPathUnits contentScriptType contentStyleType diffuseConstant edgeMode externalResourcesRequired filterRes filterUnits glyphRef glyphRef gradientTransform gradientTransform gradientUnits gradientUnits kernelMatrix kernelUnitLength kernelUnitLength kernelUnitLength keyPoints keySplines keyTimes lengthAdjust limitingConeAngle markerHeight markerUnits markerWidth maskContentUnits maskUnits numOctaves pathLength patternContentUnits patternTransform patternUnits pointsAtX pointsAtY pointsAtZ preserveAlpha preserveAspectRatio primitiveUnits refX refY repeatCount repeatDur requiredExtensions requiredFeatures specularConstant specularExponent specularExponent spreadMethod spreadMethod startOffset stdDeviation stitchTiles surfaceScale surfaceScale systemLanguage tableValues targetX targetY textLength textLength viewBox viewTarget xChannelSelector yChannelSelector zoomAndPan'.split(' '); - -var properAttributeCaseMap = (function (map) { - for (var i = 0; i < svgCamelCaseAttributes.length; i++) { - var a = svgCamelCaseAttributes[i]; - map[asciiLowerCase(a)] = a; - } - return map; -})({}); - -var properTagCaseMap = (function (map) { - var knownElements = HTML.knownElementNames; - for (var i = 0; i < knownElements.length; i++) { - var a = knownElements[i]; - map[asciiLowerCase(a)] = a; - } - return map; -})({}); - -// Take a tag name in any case and make it the proper case for HTML. -// -// Modern browsers let you embed SVG in HTML, but SVG elements are special -// in that they have a case-sensitive DOM API (nodeName, getAttribute, -// setAttribute). For example, it has to be `setAttribute("viewBox")`, -// not `"viewbox"`. However, the browser's HTML parser is NOT case sensitive -// and will fix the case for you, so if you write `` -// you actually get a `"viewBox"` attribute. Any HTML-parsing toolchain -// must do the same. -HTMLTools.properCaseTagName = function (name) { - var lowered = asciiLowerCase(name); - return properTagCaseMap.hasOwnProperty(lowered) ? - properTagCaseMap[lowered] : lowered; -}; - -// See docs for properCaseTagName. -HTMLTools.properCaseAttributeName = function (name) { - var lowered = asciiLowerCase(name); - return properAttributeCaseMap.hasOwnProperty(lowered) ? - properAttributeCaseMap[lowered] : lowered; -}; diff --git a/packages/htmljs/.gitignore b/packages/htmljs/.gitignore deleted file mode 100644 index 677a6fc263..0000000000 --- a/packages/htmljs/.gitignore +++ /dev/null @@ -1 +0,0 @@ -.build* diff --git a/packages/htmljs/README.md b/packages/htmljs/README.md deleted file mode 100644 index d2b06c4b00..0000000000 --- a/packages/htmljs/README.md +++ /dev/null @@ -1,427 +0,0 @@ -# HTMLjs - -HTMLjs is a small library for expressing HTML trees with a concise -syntax. It is used to render content in Blaze and to represent -templates during compilation. - -``` -var UL = HTML.UL, LI = HTML.LI, B = HTML.B; - -HTML.toHTML( - UL({id: 'mylist'}, - LI({'class': 'item'}, "Hello ", B("world"), "!"), - LI({'class': 'item'}, "Goodbye, world"))) -``` - -``` -
    -
  • Hello world!
  • -
  • Goodbye, world
  • -
-``` - -The functions `UL`, `LI`, and `B` are constructors which -return instances of `HTML.Tag`. These tag objects can -then be converted to an HTML string or directly into DOM nodes. - -The flexible structure of HTMLjs allows different kinds of Blaze -directives to be embedded in the tree. HTMLjs does not know about -these directives, which are considered "foreign objects." - -# Built-in Types - -The following types are built into HTMLjs. Built-in methods like -`HTML.toHTML` require a tree consisting only of these types. - -* __`null`, `undefined`__ - Render to nothing. - -* __boolean, number__ - Render to the string form of the boolean or number. - -* __string__ - Renders to a text node (or part of an attribute value). All characters are safe, and no HTML injection is possible. The string `""` renders `<a>` in HTML, and `document.createTextNode("")` in DOM. - -* __Array__ - Renders to its elements in order. An array may be empty. Arrays are detected using `HTML.isArray(...)`. - -* __`HTML.Tag`__ - Renders to an HTML element (including start tag, contents, and end tag). - -* __`HTML.CharRef({html: ..., str: ...})`__ - Renders to a character reference (such as ` `) when generating HTML. - -* __`HTML.Comment(text)`__ - Renders to an HTML comment. - -* __`HTML.Raw(html)`__ - Renders to a string of HTML to include verbatim. - -The `new` keyword is not required before constructors of HTML object types. - -All objects and arrays should be considered immutable. Instance properties -are public, but they should only be read, not written. Arrays should not -be spliced in place. This convention allows for clean patterns of -processing and transforming HTMLjs trees. - - -## HTML.Tag - -An `HTML.Tag` is created using a tag-specific constructor, like -`HTML.P` for a `

` tag or `HTML.INPUT` for an `` tag. The -resulting object is `instanceof HTML.Tag`. (The `HTML.Tag` -constructor should not be called directly.) - -Tag constructors take an optional attributes dictionary followed -by zero or more children: - -``` -HTML.HR() - -HTML.DIV(HTML.P("First paragraph"), - HTML.P("Second paragraph")) - -HTML.INPUT({type: "text"}) - -HTML.SPAN({'class': "foo"}, "Some text") -``` - -### Instance properties - -Tags have the following properties: - -* `tagName` - The tag name in lowercase (or camelCase) -* `children` - An array of children (always present) -* `attrs` - An attributes dictionary, `null`, or an array (see below) - - -### Special forms of attributes - -The attributes of a Tag may be an array of dictionaries. In order -for a tag constructor to recognize an array as the attributes argument, -it must be written as `HTML.Attrs(attrs1, attrs2, ...)`, as in this -example: - -``` -var extraAttrs = {'class': "container"}; - -var div = HTML.DIV(HTML.Attrs({id: "main"}, extraAttrs), - "This is the content."); - -div.attrs // => [{id: "main"}, {'class': "container"}] -``` - -`HTML.Attrs` may also be used to pass a foreign object in place of -an attributes dictionary of a tag. - - - -### Normalized Case for Tag Names - -The `tagName` field is always in "normalized case," which is the -official case for that particular element name (usually lowercase). -For example, `HTML.DIV().tagName` is `"div"`. For some elements -used in inline SVG graphics, the correct case is "camelCase." For -example, there is an element named `clipPath`. - -Web browsers have a confusing policy about case. They perform case -normalization when parsing HTML, but not when creating SVG elements -at runtime; the correct case is required. - -Therefore, in order to avoid ever having to normalize case at -runtime, the policy of HTMLjs is to put the burden on the caller -of functions like `HTML.ensureTag` -- for example, a template -engine -- of supplying correct normalized case. - -Briefly put, normlized case is usually lowercase, except for certain -elements where it is camelCase. - - -### Known Elements - -HTMLjs comes preloaded with constructors for all "known" HTML and -SVG elements. You can use `HTML.P`, `HTML.DIV`, and so on out of -the box. If you want to create a tag like `` for some reason, -you have to tell HTMLjs to create the `HTML.FOO` constructor for you -using `HTML.ensureTag` or `HTML.getTag`. - -HTMLjs's lists of known elements are public because they are useful to -other packages that provide additional functions not found here, like -functions for normalizing case. - - - -## Foreign objects - -Arbitrary objects are allowed in HTMLjs trees, which is useful for -adapting HTMLjs to a wide variety of uses. Such objects are called -foreign objects. - -The one restriction on foreign objects is that they must be -instances of a class -- so-called "constructed objects" (see -`HTML.isConstructedObject`) -- so that they can be distinguished -from the vanilla JS objects that represent attributes dictionaries -when constructing Tags. - -Functions are also considered foreign objects. - -## HTML.getTag(tagName) - -* `tagName` - A string in normalized case - -Creates a tag constructor for `tagName`, assigns it to the `HTML` -namespace object, and returns it. - -For example, `HTML.getTag("p")` returns `HTML.P`. `HTML.getTag("foo")` -will create and return `HTML.FOO`. - -It's very important that `tagName` be in normalized case, or else -an incorrect tag constructor will be registered and used henceforth. - - -## HTML.ensureTag(tagName) - -* `tagName` - A string in normalized case - -Ensures that a tag constructor (like `HTML.FOO`) exists for a tag -name (like `"foo"`), creating it if necessary. Like `HTML.getTag` -but does not return the tag constructor. - - -## HTML.isTagEnsured(tagName) - -* `tagName` - A string in normalized case - -Returns whether a particular tag is guaranteed to be available on -the `HTML` object (under the name returned by `HTML.getSymbolName`). - -Useful for code generators. - - -## HTML.getSymbolName(tagName) - -* `tagName` - A string in normalized case - -Returns the name of the all-caps constructor (like `"FOO"`) for a -tag name in normalized case (like `"foo"`). - -In addition to converting `tagName` to all-caps, hyphens (`-`) in -tag names are converted to underscores (`_`). - -Useful for code generators. - - -## HTML.knownElementNames - -An array of all known HTML5 and SVG element names in normalized case. - - -## HTML.knownSVGElementNames - -An array of all known SVG element names in normalized case. - -The `"a"` element is not included because it is primarily a non-SVG -element. - - -## HTML.voidElementNames - -An array of all "void" element names in normalized case. Void -elements are elements with a start tag and no end tag, such as BR, -HR, IMG, and INPUT. - -The HTML spec defines a closed class of void elements. - - -## HTML.isKnownElement(tagName) - -* `tagName` - A string in normalized case - -Returns whether `tagName` is a known HTML5 or SVG element. - - -## HTML.isKnownSVGElement(tagName) - -* `tagName` - A string in normalized case - -Returns whether `tagName` is the name of a known SVG element. - - -## HTML.isVoidElement(tagName) - -* `tagName` - A string in normalized case - -Returns whether `tagName` is the name of a void element. - - -## HTML.CharRef({html: ..., str: ...}) - -Represents a character reference like ` `. - -A CharRef is not required for escaping special characters like `<`, -which are automatically escaped by HTMLjs. For example, -`HTML.toHTML("<")` is `"<"`. Also, now that browsers speak -Unicode, non-ASCII characters typically do not need to be expressed -as character references either. The purpose of `CharRef` is offer -control over the generated HTML, allowing template engines to -preserve any character references that they come across. - -Constructing a CharRef requires two strings, the uninterpreted -"HTML" form and the interpreted "string" form. Both are required -to be present, and it is up to the caller to make sure the -information is accurate. - -Examples of valid CharRefs: - -* `HTML.CharRef({html: '&', str: '&'})` -* `HTML.CharRef({html: ' ', str: '\u00A0'}) - -Instance properties: `.html`, `.str` - - -## HTML.Comment(value) - -* `value` - String - -Represents an HTML Comment. For example, `HTML.Comment("foo")` represents -the comment ``. - -The value string should not contain two consecutive hyphens (`--`) or start -or end with a hyphen. If it does, the offending hyphens will be stripped -before generating HTML. - -Instance properties: `value` - - -## HTML.Raw(value) - -* `value` - String - -Represents HTML code to be inserted verbatim. `value` must consist -of a valid, complete fragment of HTML, with all tags closed and -all required end tags present. - -No security checks are performed, and no special characters are -escaped. `Raw` should not be used if there are other ways of -accomplishing the same result. HTML supplied by an application -user should not be rendered unless the user is trusted, and even -then, there could be strange results if required end tags are -missing. - -Instance properties: `value` - - -## HTML.isArray(x) - -Returns whether `x` is considered an array for the purposes of -HTMLjs. An array is an object created using `[...]` or -`new Array`. - -This function is provided because there are several common ways to -determine whether an object should be treated as an array in -JavaScript. - - -## HTML.isConstructedObject(x) - -Returns whether `x` is a "constructed object," which is (loosely -speaking) an object that was created with `new Foo` (for some `Foo`) -rather than with `{...}` (a vanilla object). Vanilla objects are used -as attribute dictionaries when constructing tags, while constructed -objects are used as children. - -For example, in `HTML.DIV({id:"foo"})`, `{id:"foo"}` is a vanilla -object. In `HTML.DIV(HTML.SPAN("text"))`, the `HTML.SPAN` is a -constructed object. - -A simple constructed object can be created as follows: - -``` -var Foo = function () {}; -var x = new Foo; // x is a constructed object -``` - -In particular, the test is that `x` is an object and `x.constructor` -is set, but on a prototype of the object, not the object itself. -The above example works because JavaScript sets -`Foo.prototype.constructor = Foo` when you create a function `Foo`. - - -## HTML.isNully(content) - -Returns true if `content` is `null`, `undefined`, an empty array, -or an array of only "nully" elements. - - -## HTML.isValidAttributeName(name) - -Returns whether `name` is a valid name for an attribute of an HTML tag -or element. `name` must: - -* Start with `:`, `_`, `A-Z` or `a-z` -* Consist only of those characters plus `-`, `.`, and `0-9`. - -Discussion: The HTML spec and the DOM API (`setAttribute`) have different -definitions of what characters are legal in an attribute. The HTML -parser is extremely permissive (allowing, for example, ``), while -`setAttribute` seems to use something like the XML grammar for names (and -throws an error if a name is invalid, making that attribute unsettable). -If we knew exactly what grammar browsers used for `setAttribute`, we could -include various Unicode ranges in what's legal. For now, we allow ASCII chars -that are known to be valid XML, valid HTML, and settable via `setAttribute`. - -See and -. - - -## HTML.flattenAttributes(attrs) - -If `attrs` is an array, the attribute dictionaries in the array are -combined into a single attributes dictionary, which is returned. -Any "nully" attribute values (see `HTML.isNully`) are ignored in -the process. If `attrs` is a single attribute dictionary, a copy -is returned with any nully attributes removed. If `attrs` is -equal to null or an empty array, `null` is returned. - -Attribute dictionaries are combined by assigning the name/value -pairs in array order, with later values overwriting previous -values. - -`attrs` must not contain any foreign objects. - - -## HTML.toHTML(content) - -* `content` - any HTMLjs content - -Returns a string of HTML generated from `content`. - -For example: - -``` -HTML.toHTML(HTML.HR()) // => "


" -``` - -Foreign objects are not allowed in `content`. To generate HTML -containing foreign objects, create a subclass of -`HTML.ToHTMLVisitor` and override `visitObject`. - - -## HTML.toText(content, textMode) - -* `content` - any HTMLjs content -* `textMode` - the type of text to generate; one of - `HTML.TEXTMODE.STRING`, `HTML.TEXTMODE.RCDATA`, or - `HTML.TEXTMODE.ATTRIBUTE` - -Generating HTML or DOM from HTMLjs content requires generating text -for attribute values and for the contents of TEXTAREA elements, -among others. The input content may contain strings, arrays, -booleans, numbers, nulls, and CharRefs. Behavior on other types -is undefined. - -The required `textMode` argument specifies the type of text to -generate: - -* `HTML.TEXTMODE.STRING` - a string with no special - escaping or encoding performed, suitable for passing to - `setAttribute` or `document.createTextNode`. -* `HTML.TEXTMODE.RCDATA` - a string with `<` and `&` encoded - as character references (and CharRefs included in their - "HTML" form), suitable for including in a string of HTML -* `HTML.TEXTMODE.ATTRIBUTE` - a string with `"` and `&` encoded - as character references (and CharRefs included in their - "HTML" form), suitable for including in an HTML attribute - value surrounded by double quotes diff --git a/packages/htmljs/html.js b/packages/htmljs/html.js deleted file mode 100644 index ce758772dc..0000000000 --- a/packages/htmljs/html.js +++ /dev/null @@ -1,268 +0,0 @@ - - -HTML.Tag = function () {}; -HTML.Tag.prototype.tagName = ''; // this will be set per Tag subclass -HTML.Tag.prototype.attrs = null; -HTML.Tag.prototype.children = Object.freeze ? Object.freeze([]) : []; -HTML.Tag.prototype.htmljsType = HTML.Tag.htmljsType = ['Tag']; - -// Given "p" create the function `HTML.P`. -var makeTagConstructor = function (tagName) { - // HTMLTag is the per-tagName constructor of a HTML.Tag subclass - var HTMLTag = function (/*arguments*/) { - // Work with or without `new`. If not called with `new`, - // perform instantiation by recursively calling this constructor. - // We can't pass varargs, so pass no args. - var instance = (this instanceof HTML.Tag) ? this : new HTMLTag; - - var i = 0; - var attrs = arguments.length && arguments[0]; - if (attrs && (typeof attrs === 'object')) { - // Treat vanilla JS object as an attributes dictionary. - if (! HTML.isConstructedObject(attrs)) { - instance.attrs = attrs; - i++; - } else if (attrs instanceof HTML.Attrs) { - var array = attrs.value; - if (array.length === 1) { - instance.attrs = array[0]; - } else if (array.length > 1) { - instance.attrs = array; - } - i++; - } - } - - - // If no children, don't create an array at all, use the prototype's - // (frozen, empty) array. This way we don't create an empty array - // every time someone creates a tag without `new` and this constructor - // calls itself with no arguments (above). - if (i < arguments.length) - instance.children = SLICE.call(arguments, i); - - return instance; - }; - HTMLTag.prototype = new HTML.Tag; - HTMLTag.prototype.constructor = HTMLTag; - HTMLTag.prototype.tagName = tagName; - - return HTMLTag; -}; - -// Not an HTMLjs node, but a wrapper to pass multiple attrs dictionaries -// to a tag (for the purpose of implementing dynamic attributes). -var Attrs = HTML.Attrs = function (/*attrs dictionaries*/) { - // Work with or without `new`. If not called with `new`, - // perform instantiation by recursively calling this constructor. - // We can't pass varargs, so pass no args. - var instance = (this instanceof Attrs) ? this : new Attrs; - - instance.value = SLICE.call(arguments); - - return instance; -}; - -////////////////////////////// KNOWN ELEMENTS - -HTML.getTag = function (tagName) { - var symbolName = HTML.getSymbolName(tagName); - if (symbolName === tagName) // all-caps tagName - throw new Error("Use the lowercase or camelCase form of '" + tagName + "' here"); - - if (! HTML[symbolName]) - HTML[symbolName] = makeTagConstructor(tagName); - - return HTML[symbolName]; -}; - -HTML.ensureTag = function (tagName) { - HTML.getTag(tagName); // don't return it -}; - -HTML.isTagEnsured = function (tagName) { - return HTML.isKnownElement(tagName); -}; - -HTML.getSymbolName = function (tagName) { - // "foo-bar" -> "FOO_BAR" - return tagName.toUpperCase().replace(/-/g, '_'); -}; - -HTML.knownElementNames = 'a abbr acronym address applet area article aside audio b base basefont bdi bdo big blockquote body br button canvas caption center cite code col colgroup command data datagrid datalist dd del details dfn dir div dl dt em embed eventsource fieldset figcaption figure font footer form frame frameset h1 h2 h3 h4 h5 h6 head header hgroup hr html i iframe img input ins isindex kbd keygen label legend li link main map mark menu meta meter nav noframes noscript object ol optgroup option output p param pre progress q rp rt ruby s samp script section select small source span strike strong style sub summary sup table tbody td textarea tfoot th thead time title tr track tt u ul var video wbr'.split(' '); -// (we add the SVG ones below) - -HTML.knownSVGElementNames = 'altGlyph altGlyphDef altGlyphItem animate animateColor animateMotion animateTransform circle clipPath color-profile cursor defs desc ellipse feBlend feColorMatrix feComponentTransfer feComposite feConvolveMatrix feDiffuseLighting feDisplacementMap feDistantLight feFlood feFuncA feFuncB feFuncG feFuncR feGaussianBlur feImage feMerge feMergeNode feMorphology feOffset fePointLight feSpecularLighting feSpotLight feTile feTurbulence filter font font-face font-face-format font-face-name font-face-src font-face-uri foreignObject g glyph glyphRef hkern image line linearGradient marker mask metadata missing-glyph path pattern polygon polyline radialGradient rect set stop style svg switch symbol text textPath title tref tspan use view vkern'.split(' '); -// Append SVG element names to list of known element names -HTML.knownElementNames = HTML.knownElementNames.concat(HTML.knownSVGElementNames); - -HTML.voidElementNames = 'area base br col command embed hr img input keygen link meta param source track wbr'.split(' '); - -// Speed up search through lists of known elements by creating internal "sets" -// of strings. -var YES = {yes:true}; -var makeSet = function (array) { - var set = {}; - for (var i = 0; i < array.length; i++) - set[array[i]] = YES; - return set; -}; -var voidElementSet = makeSet(HTML.voidElementNames); -var knownElementSet = makeSet(HTML.knownElementNames); -var knownSVGElementSet = makeSet(HTML.knownSVGElementNames); - -HTML.isKnownElement = function (tagName) { - return knownElementSet[tagName] === YES; -}; - -HTML.isKnownSVGElement = function (tagName) { - return knownSVGElementSet[tagName] === YES; -}; - -HTML.isVoidElement = function (tagName) { - return voidElementSet[tagName] === YES; -}; - - -// Ensure tags for all known elements -for (var i = 0; i < HTML.knownElementNames.length; i++) - HTML.ensureTag(HTML.knownElementNames[i]); - - -var CharRef = HTML.CharRef = function (attrs) { - if (! (this instanceof CharRef)) - // called without `new` - return new CharRef(attrs); - - if (! (attrs && attrs.html && attrs.str)) - throw new Error( - "HTML.CharRef must be constructed with ({html:..., str:...})"); - - this.html = attrs.html; - this.str = attrs.str; -}; -CharRef.prototype.htmljsType = CharRef.htmljsType = ['CharRef']; - -var Comment = HTML.Comment = function (value) { - if (! (this instanceof Comment)) - // called without `new` - return new Comment(value); - - if (typeof value !== 'string') - throw new Error('HTML.Comment must be constructed with a string'); - - this.value = value; - // Kill illegal hyphens in comment value (no way to escape them in HTML) - this.sanitizedValue = value.replace(/^-|--+|-$/g, ''); -}; -Comment.prototype.htmljsType = Comment.htmljsType = ['Comment']; - -var Raw = HTML.Raw = function (value) { - if (! (this instanceof Raw)) - // called without `new` - return new Raw(value); - - if (typeof value !== 'string') - throw new Error('HTML.Raw must be constructed with a string'); - - this.value = value; -}; -Raw.prototype.htmljsType = Raw.htmljsType = ['Raw']; - - -HTML.isArray = function (x) { - // could change this to use the more convoluted Object.prototype.toString - // approach that works when objects are passed between frames, but does - // it matter? - return (x instanceof Array); -}; - -HTML.isConstructedObject = function (x) { - // Figure out if `x` is "an instance of some class" or just a plain - // object literal. It correctly treats an object literal like - // `{ constructor: ... }` as an object literal. It won't detect - // instances of classes that lack a `constructor` property (e.g. - // if you assign to a prototype when setting up the class as in: - // `Foo = function () { ... }; Foo.prototype = { ... }`, then - // `(new Foo).constructor` is `Object`, not `Foo`). - return (x && (typeof x === 'object') && - (x.constructor !== Object) && - (typeof x.constructor === 'function') && - (x instanceof x.constructor)); -}; - -HTML.isNully = function (node) { - if (node == null) - // null or undefined - return true; - - if (HTML.isArray(node)) { - // is it an empty array or an array of all nully items? - for (var i = 0; i < node.length; i++) - if (! HTML.isNully(node[i])) - return false; - return true; - } - - return false; -}; - -HTML.isValidAttributeName = function (name) { - return /^[:_A-Za-z][:_A-Za-z0-9.\-]*/.test(name); -}; - -// If `attrs` is an array of attributes dictionaries, combines them -// into one. Removes attributes that are "nully." -HTML.flattenAttributes = function (attrs) { - if (! attrs) - return attrs; - - var isArray = HTML.isArray(attrs); - if (isArray && attrs.length === 0) - return null; - - var result = {}; - for (var i = 0, N = (isArray ? attrs.length : 1); i < N; i++) { - var oneAttrs = (isArray ? attrs[i] : attrs); - if ((typeof oneAttrs !== 'object') || - HTML.isConstructedObject(oneAttrs)) - throw new Error("Expected plain JS object as attrs, found: " + oneAttrs); - for (var name in oneAttrs) { - if (! HTML.isValidAttributeName(name)) - throw new Error("Illegal HTML attribute name: " + name); - var value = oneAttrs[name]; - if (! HTML.isNully(value)) - result[name] = value; - } - } - - return result; -}; - - - -////////////////////////////// TOHTML - -HTML.toHTML = function (content) { - return (new HTML.ToHTMLVisitor).visit(content); -}; - -// Escaping modes for outputting text when generating HTML. -HTML.TEXTMODE = { - STRING: 1, - RCDATA: 2, - ATTRIBUTE: 3 -}; - - -HTML.toText = function (content, textMode) { - if (! textMode) - throw new Error("textMode required for HTML.toText"); - if (! (textMode === HTML.TEXTMODE.STRING || - textMode === HTML.TEXTMODE.RCDATA || - textMode === HTML.TEXTMODE.ATTRIBUTE)) - throw new Error("Unknown textMode: " + textMode); - - var visitor = new HTML.ToTextVisitor({textMode: textMode});; - return visitor.visit(content); -}; diff --git a/packages/htmljs/htmljs_test.js b/packages/htmljs/htmljs_test.js deleted file mode 100644 index c0ef819fa8..0000000000 --- a/packages/htmljs/htmljs_test.js +++ /dev/null @@ -1,87 +0,0 @@ - -Tinytest.add("htmljs - getTag", function (test) { - var FOO = HTML.getTag('foo'); - test.isTrue(HTML.FOO === FOO); - var x = FOO(); - - test.equal(x.tagName, 'foo'); - test.isTrue(x instanceof HTML.FOO); - test.isTrue(x instanceof HTML.Tag); - test.equal(x.children, []); - test.equal(x.attrs, null); - - test.isTrue((new FOO) instanceof HTML.FOO); - test.isTrue((new FOO) instanceof HTML.Tag); - test.isFalse((new HTML.P) instanceof HTML.FOO); - - var result = HTML.ensureTag('Bar'); - test.equal(typeof result, 'undefined'); - var BAR = HTML.BAR; - test.equal(BAR().tagName, 'Bar'); -}); - -Tinytest.add("htmljs - construction", function (test) { - var A = HTML.getTag('a'); - var B = HTML.getTag('b'); - var C = HTML.getTag('c'); - - var a = A(0, B({q:0}, C(A(B({})), 'foo'))); - test.equal(a.tagName, 'a'); - test.equal(a.attrs, null); - test.equal(a.children.length, 2); - test.equal(a.children[0], 0); - var b = a.children[1]; - test.equal(b.tagName, 'b'); - test.equal(b.attrs, {q:0}); - test.equal(b.children.length, 1); - var c = b.children[0]; - test.equal(c.tagName, 'c'); - test.equal(c.attrs, null); - test.equal(c.children.length, 2); - test.equal(c.children[0].tagName, 'a'); - test.equal(c.children[0].attrs, null); - test.equal(c.children[0].children.length, 1); - test.equal(c.children[0].children[0].tagName, 'b'); - test.equal(c.children[0].children[0].children.length, 0); - test.equal(c.children[0].children[0].attrs, {}); - test.equal(c.children[1], 'foo'); - - var a2 = new A({m:1}, {n:2}, B(), {o:3}, 'foo'); - test.equal(a2.tagName, 'a'); - test.equal(a2.attrs, {m:1}); - test.equal(a2.children.length, 4); - test.equal(a2.children[0], {n:2}); - test.equal(a2.children[1].tagName, 'b'); - test.equal(a2.children[2], {o:3}); - test.equal(a2.children[3], 'foo'); - - // tests of HTML.isConstructedObject (indirectly) - test.equal(A({x:1}).children.length, 0); - var f = function () {}; - test.equal(A(new f).children.length, 1); - test.equal(A(new Date).children.length, 1); - test.equal(A({constructor: 'blah'}).children.length, 0); - test.equal(A({constructor: Object}).children.length, 0); - - test.equal(HTML.toHTML(HTML.CharRef({html: '&', str: '&'})), '&'); - test.throws(function () { - HTML.CharRef({html: '&'}); // no 'str' - }); -}); - -Tinytest.add("htmljs - utils", function (test) { - - test.notEqual("\u00c9".toLowerCase(), "\u00c9"); - test.equal(HTMLTools.asciiLowerCase("\u00c9"), "\u00c9"); - - test.equal(HTMLTools.asciiLowerCase("Hello There"), "hello there"); - - test.isTrue(HTML.isVoidElement("br")); - test.isFalse(HTML.isVoidElement("div")); - test.isTrue(HTML.isKnownElement("div")); - -}); - -Tinytest.add("htmljs - details", function (test) { - test.equal(HTML.toHTML(false), "false"); -}); diff --git a/packages/htmljs/package.js b/packages/htmljs/package.js deleted file mode 100644 index 4e04ef077d..0000000000 --- a/packages/htmljs/package.js +++ /dev/null @@ -1,21 +0,0 @@ -Package.describe({ - summary: "Small library for expressing HTML trees", - version: '1.0.10' -}); - -Package.onUse(function (api) { - api.use('deps'); - api.export('HTML'); - - api.addFiles(['preamble.js', - 'visitors.js', - 'html.js']); -}); - -Package.onTest(function (api) { - api.use('htmljs'); - api.use('html-tools'); - api.use('tinytest'); - api.use('underscore'); - api.addFiles(['htmljs_test.js']); -}); diff --git a/packages/htmljs/preamble.js b/packages/htmljs/preamble.js deleted file mode 100644 index 160e038041..0000000000 --- a/packages/htmljs/preamble.js +++ /dev/null @@ -1,4 +0,0 @@ -HTML = {}; - -IDENTITY = function (x) { return x; }; -SLICE = Array.prototype.slice; diff --git a/packages/htmljs/visitors.js b/packages/htmljs/visitors.js deleted file mode 100644 index 15160e759a..0000000000 --- a/packages/htmljs/visitors.js +++ /dev/null @@ -1,331 +0,0 @@ -////////////////////////////// VISITORS - -// _assign is like _.extend or the upcoming Object.assign. -// Copy src's own, enumerable properties onto tgt and return -// tgt. -var _hasOwnProperty = Object.prototype.hasOwnProperty; -var _assign = function (tgt, src) { - for (var k in src) { - if (_hasOwnProperty.call(src, k)) - tgt[k] = src[k]; - } - return tgt; -}; - -HTML.Visitor = function (props) { - _assign(this, props); -}; - -HTML.Visitor.def = function (options) { - _assign(this.prototype, options); -}; - -HTML.Visitor.extend = function (options) { - var curType = this; - var subType = function HTMLVisitorSubtype(/*arguments*/) { - HTML.Visitor.apply(this, arguments); - }; - subType.prototype = new curType; - subType.extend = curType.extend; - subType.def = curType.def; - if (options) - _assign(subType.prototype, options); - return subType; -}; - -HTML.Visitor.def({ - visit: function (content/*, ...*/) { - if (content == null) - // null or undefined. - return this.visitNull.apply(this, arguments); - - if (typeof content === 'object') { - if (content.htmljsType) { - switch (content.htmljsType) { - case HTML.Tag.htmljsType: - return this.visitTag.apply(this, arguments); - case HTML.CharRef.htmljsType: - return this.visitCharRef.apply(this, arguments); - case HTML.Comment.htmljsType: - return this.visitComment.apply(this, arguments); - case HTML.Raw.htmljsType: - return this.visitRaw.apply(this, arguments); - default: - throw new Error("Unknown htmljs type: " + content.htmljsType); - } - } - - if (HTML.isArray(content)) - return this.visitArray.apply(this, arguments); - - return this.visitObject.apply(this, arguments); - - } else if ((typeof content === 'string') || - (typeof content === 'boolean') || - (typeof content === 'number')) { - return this.visitPrimitive.apply(this, arguments); - - } else if (typeof content === 'function') { - return this.visitFunction.apply(this, arguments); - } - - throw new Error("Unexpected object in htmljs: " + content); - - }, - visitNull: function (nullOrUndefined/*, ...*/) {}, - visitPrimitive: function (stringBooleanOrNumber/*, ...*/) {}, - visitArray: function (array/*, ...*/) {}, - visitComment: function (comment/*, ...*/) {}, - visitCharRef: function (charRef/*, ...*/) {}, - visitRaw: function (raw/*, ...*/) {}, - visitTag: function (tag/*, ...*/) {}, - visitObject: function (obj/*, ...*/) { - throw new Error("Unexpected object in htmljs: " + obj); - }, - visitFunction: function (fn/*, ...*/) { - throw new Error("Unexpected function in htmljs: " + fn); - } -}); - -HTML.TransformingVisitor = HTML.Visitor.extend(); -HTML.TransformingVisitor.def({ - visitNull: IDENTITY, - visitPrimitive: IDENTITY, - visitArray: function (array/*, ...*/) { - var argsCopy = SLICE.call(arguments); - var result = array; - for (var i = 0; i < array.length; i++) { - var oldItem = array[i]; - argsCopy[0] = oldItem; - var newItem = this.visit.apply(this, argsCopy); - if (newItem !== oldItem) { - // copy `array` on write - if (result === array) - result = array.slice(); - result[i] = newItem; - } - } - return result; - }, - visitComment: IDENTITY, - visitCharRef: IDENTITY, - visitRaw: IDENTITY, - visitObject: IDENTITY, - visitFunction: IDENTITY, - visitTag: function (tag/*, ...*/) { - var oldChildren = tag.children; - var argsCopy = SLICE.call(arguments); - argsCopy[0] = oldChildren; - var newChildren = this.visitChildren.apply(this, argsCopy); - - var oldAttrs = tag.attrs; - argsCopy[0] = oldAttrs; - var newAttrs = this.visitAttributes.apply(this, argsCopy); - - if (newAttrs === oldAttrs && newChildren === oldChildren) - return tag; - - var newTag = HTML.getTag(tag.tagName).apply(null, newChildren); - newTag.attrs = newAttrs; - return newTag; - }, - visitChildren: function (children/*, ...*/) { - return this.visitArray.apply(this, arguments); - }, - // Transform the `.attrs` property of a tag, which may be a dictionary, - // an array, or in some uses, a foreign object (such as - // a template tag). - visitAttributes: function (attrs/*, ...*/) { - if (HTML.isArray(attrs)) { - var argsCopy = SLICE.call(arguments); - var result = attrs; - for (var i = 0; i < attrs.length; i++) { - var oldItem = attrs[i]; - argsCopy[0] = oldItem; - var newItem = this.visitAttributes.apply(this, argsCopy); - if (newItem !== oldItem) { - // copy on write - if (result === attrs) - result = attrs.slice(); - result[i] = newItem; - } - } - return result; - } - - if (attrs && HTML.isConstructedObject(attrs)) { - throw new Error("The basic HTML.TransformingVisitor does not support " + - "foreign objects in attributes. Define a custom " + - "visitAttributes for this case."); - } - - var oldAttrs = attrs; - var newAttrs = oldAttrs; - if (oldAttrs) { - var attrArgs = [null, null]; - attrArgs.push.apply(attrArgs, arguments); - for (var k in oldAttrs) { - var oldValue = oldAttrs[k]; - attrArgs[0] = k; - attrArgs[1] = oldValue; - var newValue = this.visitAttribute.apply(this, attrArgs); - if (newValue !== oldValue) { - // copy on write - if (newAttrs === oldAttrs) - newAttrs = _assign({}, oldAttrs); - newAttrs[k] = newValue; - } - } - } - - return newAttrs; - }, - // Transform the value of one attribute name/value in an - // attributes dictionary. - visitAttribute: function (name, value, tag/*, ...*/) { - var args = SLICE.call(arguments, 2); - args[0] = value; - return this.visit.apply(this, args); - } -}); - - -HTML.ToTextVisitor = HTML.Visitor.extend(); -HTML.ToTextVisitor.def({ - visitNull: function (nullOrUndefined) { - return ''; - }, - visitPrimitive: function (stringBooleanOrNumber) { - var str = String(stringBooleanOrNumber); - if (this.textMode === HTML.TEXTMODE.RCDATA) { - return str.replace(/&/g, '&').replace(/`), we hackishly support HTML tags in markdown - // in templates by parsing them and stringifying them. - return this.visit(this.toHTML(tag)); - }, - visitObject: function (x) { - throw new Error("Unexpected object in htmljs in toText: " + x); - }, - toHTML: function (node) { - return HTML.toHTML(node); - } -}); - - - -HTML.ToHTMLVisitor = HTML.Visitor.extend(); -HTML.ToHTMLVisitor.def({ - visitNull: function (nullOrUndefined) { - return ''; - }, - visitPrimitive: function (stringBooleanOrNumber) { - var str = String(stringBooleanOrNumber); - return str.replace(/&/g, '&').replace(/'; - }, - visitCharRef: function (charRef) { - return charRef.html; - }, - visitRaw: function (raw) { - return raw.value; - }, - visitTag: function (tag) { - var attrStrs = []; - - var tagName = tag.tagName; - var children = tag.children; - - var attrs = tag.attrs; - if (attrs) { - attrs = HTML.flattenAttributes(attrs); - for (var k in attrs) { - if (k === 'value' && tagName === 'textarea') { - children = [attrs[k], children]; - } else { - var v = this.toText(attrs[k], HTML.TEXTMODE.ATTRIBUTE); - attrStrs.push(' ' + k + '="' + v + '"'); - } - } - } - - var startTag = '<' + tagName + attrStrs.join('') + '>'; - - var childStrs = []; - var content; - if (tagName === 'textarea') { - - for (var i = 0; i < children.length; i++) - childStrs.push(this.toText(children[i], HTML.TEXTMODE.RCDATA)); - - content = childStrs.join(''); - if (content.slice(0, 1) === '\n') - // TEXTAREA will absorb a newline, so if we see one, add - // another one. - content = '\n' + content; - - } else { - for (var i = 0; i < children.length; i++) - childStrs.push(this.visit(children[i])); - - content = childStrs.join(''); - } - - var result = startTag + content; - - if (children.length || ! HTML.isVoidElement(tagName)) { - // "Void" elements like BR are the only ones that don't get a close - // tag in HTML5. They shouldn't have contents, either, so we could - // throw an error upon seeing contents here. - result += ''; - } - - return result; - }, - visitObject: function (x) { - throw new Error("Unexpected object in htmljs in toHTML: " + x); - }, - toText: function (node, textMode) { - return HTML.toText(node, textMode); - } -}); diff --git a/packages/http/README.md b/packages/http/README.md index 030b951c9d..01164d04de 100644 --- a/packages/http/README.md +++ b/packages/http/README.md @@ -1,4 +1,6 @@ # http +[Source code of released version](https://github.com/meteor/meteor/tree/master/packages/http) | [Source code of development version](https://github.com/meteor/meteor/tree/devel/packages/http) +*** `HTTP` provides an HTTP request API on the client and server. To use these functions, add the HTTP package to your project with `$ meteor add http`. diff --git a/packages/http/package.js b/packages/http/package.js index 745d6bc218..1f84883aa1 100644 --- a/packages/http/package.js +++ b/packages/http/package.js @@ -1,6 +1,6 @@ Package.describe({ summary: "Make HTTP calls to remote servers", - version: '1.2.8' + version: '1.2.10' }); Npm.depends({ diff --git a/packages/id-map/README.md b/packages/id-map/README.md index ec37b267c6..84cb801546 100644 --- a/packages/id-map/README.md +++ b/packages/id-map/README.md @@ -1 +1,5 @@ +# id-map +[Source code of released version](https://github.com/meteor/meteor/tree/master/packages/id-map) | [Source code of development version](https://github.com/meteor/meteor/tree/devel/packages/id-map) +*** + This is an internal Meteor package. \ No newline at end of file diff --git a/packages/id-map/package.js b/packages/id-map/package.js index 39c2051c39..a1931eb94f 100644 --- a/packages/id-map/package.js +++ b/packages/id-map/package.js @@ -1,6 +1,6 @@ Package.describe({ summary: "Dictionary data structure allowing non-string keys", - version: '1.0.8' + version: '1.0.9' }); Package.onUse(function (api) { diff --git a/packages/insecure/README.md b/packages/insecure/README.md index 1d8168b098..e1b992a249 100644 --- a/packages/insecure/README.md +++ b/packages/insecure/README.md @@ -1,4 +1,6 @@ # insecure +[Source code of released version](https://github.com/meteor/meteor/tree/master/packages/insecure) | [Source code of development version](https://github.com/meteor/meteor/tree/devel/packages/insecure) +*** Allow almost all collection methods, such as `insert`, `update`, and `remove`, to be called from the client. This package is useful for prototyping an app without worrying about database permissions, but should be removed as soon as the app needs to restrict database access. diff --git a/packages/jquery-waypoints/README.md b/packages/jquery-waypoints/README.md index df46727f30..c850cf0394 100644 --- a/packages/jquery-waypoints/README.md +++ b/packages/jquery-waypoints/README.md @@ -1,4 +1,6 @@ # jquery-waypoints +[Source code of released version](https://github.com/meteor/meteor/tree/master/packages/jquery-waypoints) | [Source code of development version](https://github.com/meteor/meteor/tree/devel/packages/jquery-waypoints) +*** This is a wrapper package for the JQuery Waypoints library. You can use it to add callbacks that fire when the user scrolls to certain elements on a page, for example to implement table of contents highlighting. diff --git a/packages/jquery/README.md b/packages/jquery/README.md index bf02bd488c..45e50ce962 100644 --- a/packages/jquery/README.md +++ b/packages/jquery/README.md @@ -1,4 +1,6 @@ # jquery +[Source code of released version](https://github.com/meteor/meteor/tree/master/packages/jquery) | [Source code of development version](https://github.com/meteor/meteor/tree/devel/packages/jquery) +*** [jQuery](http://jquery.com/) is a fast and concise JavaScript Library that simplifies HTML document traversing, event handling, diff --git a/packages/jquery/package.js b/packages/jquery/package.js index a6d4b4d283..30fc96b32b 100644 --- a/packages/jquery/package.js +++ b/packages/jquery/package.js @@ -5,7 +5,7 @@ Package.describe({ // patch number instead of the wrap number, we're higher than that. // In fairness, there's no way to make an RC of a new version without // bumping the patch number. - version: '1.11.9' + version: '1.11.10' }); Package.onUse(function (api) { diff --git a/packages/jshint/README.md b/packages/jshint/README.md index fb939b8a1d..ea0757e1fc 100644 --- a/packages/jshint/README.md +++ b/packages/jshint/README.md @@ -1,3 +1,7 @@ +# jshint +[Source code of released version](https://github.com/meteor/meteor/tree/master/packages/jshint) | [Source code of development version](https://github.com/meteor/meteor/tree/devel/packages/jshint) +*** + JSHint for Meteor === diff --git a/packages/jshint/package.js b/packages/jshint/package.js index 50c54dc46f..431da88126 100644 --- a/packages/jshint/package.js +++ b/packages/jshint/package.js @@ -1,6 +1,6 @@ Package.describe({ name: 'jshint', - version: '1.1.5', + version: '1.1.7', summary: 'Lint all your JavaScript files with JSHint.', documentation: 'README.md' }); diff --git a/packages/jsparse/README.md b/packages/jsparse/README.md index ec37b267c6..ba75c9e81e 100644 --- a/packages/jsparse/README.md +++ b/packages/jsparse/README.md @@ -1 +1,5 @@ +# jsparse +[Source code of released version](https://github.com/meteor/meteor/tree/master/packages/jsparse) | [Source code of development version](https://github.com/meteor/meteor/tree/devel/packages/jsparse) +*** + This is an internal Meteor package. \ No newline at end of file diff --git a/packages/jsparse/package.js b/packages/jsparse/package.js index ffe337b70a..5e3f58d85e 100644 --- a/packages/jsparse/package.js +++ b/packages/jsparse/package.js @@ -1,6 +1,6 @@ Package.describe({ summary: "Full-featured JavaScript parser", - version: "1.0.9" + version: "1.0.10" }); Package.onUse(function (api) { diff --git a/packages/launch-screen/README.md b/packages/launch-screen/README.md index 14dde4afb2..3cf5b1f31d 100644 --- a/packages/launch-screen/README.md +++ b/packages/launch-screen/README.md @@ -21,6 +21,8 @@ elements to be loaded before releasing the launch screen and showing the user the actual app. ### Additional waiting before releasing the launch screen +[Source code of released version](https://github.com/meteor/meteor/tree/master/packages/launch-screen) | [Source code of development version](https://github.com/meteor/meteor/tree/devel/packages/launch-screen) +*** To wait on more events before releasing the launch screen, call `var handle = LaunchScreen.hold()` in the top-level of the client code of your app, and when diff --git a/packages/launch-screen/package.js b/packages/launch-screen/package.js index 56f5695b4b..9184130ea7 100644 --- a/packages/launch-screen/package.js +++ b/packages/launch-screen/package.js @@ -6,11 +6,11 @@ Package.describe({ // between such packages and the build tool. name: 'launch-screen', summary: 'Default and customizable launch screen on mobile.', - version: '1.0.12' + version: '1.1.0' }); Cordova.depends({ - 'cordova-plugin-splashscreen': '3.2.1' + 'cordova-plugin-splashscreen': '4.0.0' }); Package.onUse(function(api) { diff --git a/packages/less/README.md b/packages/less/README.md index 067fe7908f..6762ed7b80 100644 --- a/packages/less/README.md +++ b/packages/less/README.md @@ -1,4 +1,6 @@ # Less +[Source code of released version](https://github.com/meteor/meteor/tree/master/packages/less) | [Source code of development version](https://github.com/meteor/meteor/tree/devel/packages/less) +*** The Less package provides a compiler build plugin for the Meteor build tool. It handles the compilation of `*.less` files to CSS. diff --git a/packages/less/package.js b/packages/less/package.js index 589d8bf262..7210938098 100644 --- a/packages/less/package.js +++ b/packages/less/package.js @@ -1,6 +1,6 @@ Package.describe({ name: 'less', - version: '2.7.4', + version: '2.7.9-beta.1', summary: 'Leaner CSS language', documentation: 'README.md' }); diff --git a/packages/livedata/README.md b/packages/livedata/README.md index ec37b267c6..c5e4fd6b2d 100644 --- a/packages/livedata/README.md +++ b/packages/livedata/README.md @@ -1 +1,5 @@ +# livedata +[Source code of released version](https://github.com/meteor/meteor/tree/master/packages/livedata) | [Source code of development version](https://github.com/meteor/meteor/tree/devel/packages/livedata) +*** + This is an internal Meteor package. \ No newline at end of file diff --git a/packages/localstorage/README.md b/packages/localstorage/README.md index ec37b267c6..d2453cb5d8 100644 --- a/packages/localstorage/README.md +++ b/packages/localstorage/README.md @@ -1 +1,5 @@ +# localstorage +[Source code of released version](https://github.com/meteor/meteor/tree/master/packages/localstorage) | [Source code of development version](https://github.com/meteor/meteor/tree/devel/packages/localstorage) +*** + This is an internal Meteor package. \ No newline at end of file diff --git a/packages/localstorage/package.js b/packages/localstorage/package.js index 0ddb80293c..3b4cc80c50 100644 --- a/packages/localstorage/package.js +++ b/packages/localstorage/package.js @@ -1,6 +1,6 @@ Package.describe({ summary: "Simulates local storage on IE 6,7 using userData", - version: "1.0.11" + version: "1.0.12" }); Package.onUse(function (api) { diff --git a/packages/logging/README.md b/packages/logging/README.md index ec37b267c6..72b2204077 100644 --- a/packages/logging/README.md +++ b/packages/logging/README.md @@ -1 +1,5 @@ +# logging +[Source code of released version](https://github.com/meteor/meteor/tree/master/packages/logging) | [Source code of development version](https://github.com/meteor/meteor/tree/devel/packages/logging) +*** + This is an internal Meteor package. \ No newline at end of file diff --git a/packages/logging/logging_test.js b/packages/logging/logging_test.js index 144dce4798..3f569b3916 100644 --- a/packages/logging/logging_test.js +++ b/packages/logging/logging_test.js @@ -190,4 +190,12 @@ Tinytest.add("logging - format", function (test) { Log.format({message: "message", time: time, level: level, file: "app.js", line: 42}), level.charAt(0).toUpperCase() + '20120908-07:06:05.004' + utcOffsetStr + ' (app.js:42) message'); }); + + test.matches(Log.format({ + message: "oyez", + time: new Date, + level: "error" + }, { + color: true + }), /oyez/); }); diff --git a/packages/logging/package.js b/packages/logging/package.js index c3a9806e24..deef99b0ca 100644 --- a/packages/logging/package.js +++ b/packages/logging/package.js @@ -1,6 +1,6 @@ Package.describe({ summary: "Logging facility.", - version: '1.1.14' + version: '1.1.16' }); Npm.depends({ @@ -12,7 +12,7 @@ Npm.strip({ }); Cordova.depends({ - 'cordova-plugin-console': '1.0.2' + 'cordova-plugin-console': '1.0.4' }); Package.onUse(function (api) { diff --git a/packages/logic-solver/README.md b/packages/logic-solver/README.md index e7654116f0..696d3f37b7 100644 --- a/packages/logic-solver/README.md +++ b/packages/logic-solver/README.md @@ -1,4 +1,6 @@ # Logic Solver +[Source code of released version](https://github.com/meteor/meteor/tree/master/packages/logic-solver) | [Source code of development version](https://github.com/meteor/meteor/tree/devel/packages/logic-solver) +*** ## Introduction diff --git a/packages/logic-solver/package.js b/packages/logic-solver/package.js index 0458ba6a97..f173dd5633 100644 --- a/packages/logic-solver/package.js +++ b/packages/logic-solver/package.js @@ -1,6 +1,6 @@ Package.describe({ summary: "General satisfiability solver for logic problems", - version: '2.0.6' + version: '2.0.7' }); Package.on_use(function (api) { diff --git a/packages/markdown/README.md b/packages/markdown/README.md index ec37b267c6..9353449870 100644 --- a/packages/markdown/README.md +++ b/packages/markdown/README.md @@ -1 +1,5 @@ +# markdown +[Source code of released version](https://github.com/meteor/meteor/tree/master/packages/markdown) | [Source code of development version](https://github.com/meteor/meteor/tree/devel/packages/markdown) +*** + This is an internal Meteor package. \ No newline at end of file diff --git a/packages/markdown/package.js b/packages/markdown/package.js index 9022229e38..0c97f5126a 100644 --- a/packages/markdown/package.js +++ b/packages/markdown/package.js @@ -2,7 +2,7 @@ Package.describe({ summary: "Markdown-to-HTML processor", - version: "1.0.10" + version: "1.0.11" }); Package.onUse(function (api) { diff --git a/packages/meetup/README.md b/packages/meetup/README.md index 1491bd3b72..0f075439bb 100644 --- a/packages/meetup/README.md +++ b/packages/meetup/README.md @@ -1,4 +1,6 @@ # meetup +[Source code of released version](https://github.com/meteor/meteor/tree/master/packages/meetup) | [Source code of development version](https://github.com/meteor/meteor/tree/devel/packages/meetup) +*** An implementation of the Meetup OAuth flow. See the [project page](https://www.meteor.com/accounts) on Meteor Accounts for more diff --git a/packages/meetup/package.js b/packages/meetup/package.js index 196c110035..ef51925996 100644 --- a/packages/meetup/package.js +++ b/packages/meetup/package.js @@ -1,13 +1,13 @@ Package.describe({ summary: "Meetup OAuth flow", - version: "1.6.4" + version: "1.6.5" }); Package.onUse(function(api) { api.use('oauth2', ['client', 'server']); api.use('oauth', ['client', 'server']); api.use('http', ['server']); - api.use('templating', 'client'); + api.use('templating@1.2.13', 'client'); api.use('underscore', 'client'); api.use('random', 'client'); api.use('service-configuration', ['client', 'server']); diff --git a/packages/meteor-base/README.md b/packages/meteor-base/README.md index 13e2e37b6e..30d609e452 100644 --- a/packages/meteor-base/README.md +++ b/packages/meteor-base/README.md @@ -1,4 +1,6 @@ # meteor-base +[Source code of released version](https://github.com/meteor/meteor/tree/master/packages/meteor-base) | [Source code of development version](https://github.com/meteor/meteor/tree/devel/packages/meteor-base) +*** A default set of packages that almost every app will have. You should only remove this package if you really, really know what you are doing. diff --git a/packages/meteor-developer/README.md b/packages/meteor-developer/README.md index ad2edf10a4..df097bba7d 100644 --- a/packages/meteor-developer/README.md +++ b/packages/meteor-developer/README.md @@ -1,4 +1,6 @@ # meteor-developer +[Source code of released version](https://github.com/meteor/meteor/tree/master/packages/meteor-developer) | [Source code of development version](https://github.com/meteor/meteor/tree/devel/packages/meteor-developer) +*** An implementation of the Meteor Developer Account OAuth flow. See the [project page](https://www.meteor.com/accounts) on Meteor Accounts for diff --git a/packages/meteor-developer/package.js b/packages/meteor-developer/package.js index 50498cc9ad..1453ce78b1 100644 --- a/packages/meteor-developer/package.js +++ b/packages/meteor-developer/package.js @@ -1,6 +1,6 @@ Package.describe({ summary: "Meteor developer accounts OAuth flow", - version: "1.1.9" + version: "1.1.10" }); Package.onUse(function (api) { @@ -8,7 +8,7 @@ Package.onUse(function (api) { api.use('oauth', ['client', 'server']); api.use('http', ['server']); api.use(['underscore', 'service-configuration'], ['client', 'server']); - api.use(['random', 'templating'], 'client'); + api.use(['random', 'templating@1.2.13'], 'client'); api.export('MeteorDeveloperAccounts'); diff --git a/packages/meteor-platform/README.md b/packages/meteor-platform/README.md index 6888d2ad5d..b1832af856 100644 --- a/packages/meteor-platform/README.md +++ b/packages/meteor-platform/README.md @@ -1,4 +1,6 @@ # meteor-platform +[Source code of released version](https://github.com/meteor/meteor/tree/master/packages/meteor-platform) | [Source code of development version](https://github.com/meteor/meteor/tree/devel/packages/meteor-platform) +*** This package used to be added to every app by `meteor create`, but is now deprecated in favor of `meteor-base` and a carefully chosen set of other packages. diff --git a/packages/meteor-tool/README.md b/packages/meteor-tool/README.md index 42f3266c17..79e97a6ffc 100644 --- a/packages/meteor-tool/README.md +++ b/packages/meteor-tool/README.md @@ -1,4 +1,6 @@ # meteor-tool +[Source code of released version](https://github.com/meteor/meteor/tree/master/packages/meteor-tool) | [Source code of development version](https://github.com/meteor/meteor/tree/devel/packages/meteor-tool) +*** This is the Meteor command-line tool. Most of the code for it is in the [tools directory](https://github.com/meteor/meteor/tree/master/tools) of the Meteor repository. diff --git a/packages/meteor-tool/package.js b/packages/meteor-tool/package.js index dca5e85d20..732a24b76d 100644 --- a/packages/meteor-tool/package.js +++ b/packages/meteor-tool/package.js @@ -1,6 +1,6 @@ Package.describe({ summary: "The Meteor command-line tool", - version: '1.4.0' + version: '1.4.3-beta.1' }); Package.includeTool(); diff --git a/packages/meteor/README.md b/packages/meteor/README.md index ec37b267c6..ff831a512a 100644 --- a/packages/meteor/README.md +++ b/packages/meteor/README.md @@ -1 +1,5 @@ +# meteor +[Source code of released version](https://github.com/meteor/meteor/tree/master/packages/meteor) | [Source code of development version](https://github.com/meteor/meteor/tree/devel/packages/meteor) +*** + This is an internal Meteor package. \ No newline at end of file diff --git a/packages/meteor/package.js b/packages/meteor/package.js index f4067cf16e..2753bc79a1 100644 --- a/packages/meteor/package.js +++ b/packages/meteor/package.js @@ -2,7 +2,7 @@ Package.describe({ summary: "Core Meteor environment", - version: '1.2.16' + version: '1.6.0' }); Package.registerBuildPlugin({ diff --git a/packages/meteor/startup_server.js b/packages/meteor/startup_server.js index 2a08396ac1..7d60372b5b 100644 --- a/packages/meteor/startup_server.js +++ b/packages/meteor/startup_server.js @@ -1,4 +1,19 @@ -Meteor.startup = function (callback) { +Meteor.startup = function startup(callback) { + if (process.env.METEOR_PROFILE) { + // Create a temporary error to capture the current stack trace. + var error = new Error("Meteor.startup"); + + // Capture the stack trace of the Meteor.startup call, excluding the + // startup stack frame itself. + Error.captureStackTrace(error, startup); + + callback.stack = error.stack + .split(/\n\s*/) // Split lines and remove leading whitespace. + .slice(0, 2) // Only include the call site. + .join(" ") // Collapse to one line. + .replace(/^Error: /, ""); // Not really an Error per se. + } + if (__meteor_bootstrap__.startupHooks) { __meteor_bootstrap__.startupHooks.push(callback); } else { diff --git a/packages/meteor/timers.js b/packages/meteor/timers.js index 455f48fc89..024844a2da 100644 --- a/packages/meteor/timers.js +++ b/packages/meteor/timers.js @@ -44,7 +44,7 @@ _.extend(Meteor, { * @memberOf Meteor * @summary Cancel a repeating function call scheduled by `Meteor.setInterval`. * @locus Anywhere - * @param {Number} id The handle returned by `Meteor.setInterval` + * @param {Object} id The handle returned by `Meteor.setInterval` */ clearInterval: function(x) { return clearInterval(x); @@ -54,7 +54,7 @@ _.extend(Meteor, { * @memberOf Meteor * @summary Cancel a function call scheduled by `Meteor.setTimeout`. * @locus Anywhere - * @param {Number} id The handle returned by `Meteor.setTimeout` + * @param {Object} id The handle returned by `Meteor.setTimeout` */ clearTimeout: function(x) { return clearTimeout(x); diff --git a/packages/meyerweb-reset/README.md b/packages/meyerweb-reset/README.md index ec37b267c6..a9bb580e4e 100644 --- a/packages/meyerweb-reset/README.md +++ b/packages/meyerweb-reset/README.md @@ -1 +1,5 @@ +# meyerweb-reset +[Source code of released version](https://github.com/meteor/meteor/tree/master/packages/meyerweb-reset) | [Source code of development version](https://github.com/meteor/meteor/tree/devel/packages/meyerweb-reset) +*** + This is an internal Meteor package. \ No newline at end of file diff --git a/packages/minifier-css/README.md b/packages/minifier-css/README.md index ec37b267c6..2505991e70 100644 --- a/packages/minifier-css/README.md +++ b/packages/minifier-css/README.md @@ -1 +1,5 @@ +# minifier-css +[Source code of released version](https://github.com/meteor/meteor/tree/master/packages/minifier-css) | [Source code of development version](https://github.com/meteor/meteor/tree/devel/packages/minifier-css) +*** + This is an internal Meteor package. \ No newline at end of file diff --git a/packages/minifier-css/minifier.js b/packages/minifier-css/minifier.js index 0994ee6fc8..ad5c202920 100644 --- a/packages/minifier-css/minifier.js +++ b/packages/minifier-css/minifier.js @@ -138,7 +138,7 @@ var rewriteRules = function (rules, mergedCssPath) { // We don't rewrite URLs starting with a protocol definition such as // http, https, or data, or those with network-path references // i.e. //img.domain.com/cat.gif - if (resource.protocol !== null || resource.href.startsWith('//')) { + if (resource.protocol !== null || resource.href.startsWith('//') || resource.href.startsWith('#')) { continue; } @@ -149,6 +149,10 @@ var rewriteRules = function (rules, mergedCssPath) { } else { absolutePath = resource.path; } + + if (resource.hash) { + absolutePath += resource.hash; + } // We used to finish the rewriting process at the absolute path step // above. But it didn't work in case the Meteor application was deployed diff --git a/packages/minifier-css/package.js b/packages/minifier-css/package.js index 3de369f110..0cd6625d20 100644 --- a/packages/minifier-css/package.js +++ b/packages/minifier-css/package.js @@ -1,6 +1,6 @@ Package.describe({ summary: "CSS minifier", - version: "1.2.13" + version: "1.2.16-beta.1" }); Npm.depends({ diff --git a/packages/minifier-css/urlrewriting-tests.js b/packages/minifier-css/urlrewriting-tests.js index 8626267909..91491acdd2 100644 --- a/packages/minifier-css/urlrewriting-tests.js +++ b/packages/minifier-css/urlrewriting-tests.js @@ -6,13 +6,13 @@ Tinytest.add("minifier-css - url rewriting when merging", function (test) { var parseOptions = { source: null, position: true }; - var t = function(relativeUrl, absoluteUrl, desc) { + function t(relativeUrl, absoluteUrl, desc) { var ast1 = CssTools.parseCss(stylesheet(relativeUrl), parseOptions); var ast2 = CssTools.parseCss(stylesheet(absoluteUrl), parseOptions); CssTools.rewriteCssUrls(ast1); test.equal(CssTools.stringifyCss(ast1), CssTools.stringifyCss(ast2), desc); - }; + } parseOptions.source = 'packages/nameOfPackage/style.css'; t('../image.png', 'packages/image.png', 'parent directory'); @@ -42,7 +42,6 @@ Tinytest.add("minifier-css - url rewriting when merging", function (test) { t('"http://i.imgur.com/fBcdJIh.gif"', '"http://i.imgur.com/fBcdJIh.gif"', 'complete quoted URL'); t('data:image/png;base64,iVBORw0K=', 'data:image/png;base64,iVBORw0K=', 'data URI'); t('http://', 'http://', 'malformed URL'); - }); Tinytest.add("minifier-css - url rewriting with media queries (ast rule recursion)", function (test) { @@ -97,3 +96,51 @@ Tinytest.add("minifier-css - url rewriting with media queries (ast rule recursio t('http://', 'http://', 'malformed URL'); }); + +Tinytest.add("minifier-css - url rewriting with hash symbols", function (test) { + var stylesheet = function(backgroundPath) { + return "body { background-image: url(" + backgroundPath + ")}" + }; + + var parseOptions = { source: null, position: true }; + + var t = function(relativeUrl, absoluteUrl, desc) { + var ast1 = CssTools.parseCss(stylesheet(relativeUrl), parseOptions); + var ast2 = CssTools.parseCss(stylesheet(absoluteUrl), parseOptions); + CssTools.rewriteCssUrls(ast1); + + test.equal(CssTools.stringifyCss(ast1), CssTools.stringifyCss(ast2), desc); + }; + + parseOptions.source = 'packages/nameOfPackage/style.css'; + t('../filters.svg#theFilterId', 'packages/filters.svg#theFilterId', 'parent directory'); + t('./../filters.svg#theFilterId', 'packages/filters.svg#theFilterId', 'parent directory'); + t('../nameOfPackage2/filters.svg#theFilterId', 'packages/nameOfPackage2/filters.svg#theFilterId', 'cousin directory'); + t('../../filters.svg#theFilterId', 'filters.svg#theFilterId', 'grand parent directory'); + t('./filters.svg#theFilterId', 'packages/nameOfPackage/filters.svg#theFilterId', 'current directory'); + t('./child/filters.svg#theFilterId', 'packages/nameOfPackage/child/filters.svg#theFilterId', 'child directory'); + t('child/filters.svg#theFilterId', 'packages/nameOfPackage/child/filters.svg#theFilterId', 'child directory'); + t('/filters.svg#theFilterId', 'filters.svg#theFilterId', 'absolute url'); + t('"/filters.svg#theFilterId"', '"filters.svg#theFilterId"', 'double quoted url'); + t("'/filters.svg#theFilterId'", "'filters.svg#theFilterId'", 'single quoted url'); + t('"./../filters.svg#theFilterId"', '"packages/filters.svg#theFilterId"', 'quoted parent directory'); + t('http://i.imgur.com/filters.svg#theFilterId', 'http://i.imgur.com/filters.svg#theFilterId', 'complete URL'); + t('"http://i.imgur.com/filters.svg#theFilterId"', '"http://i.imgur.com/filters.svg#theFilterId"', 'complete quoted URL'); + t('data:image/png;base64,iVBORw0K=#theFilterId', 'data:image/png;base64,iVBORw0K=#theFilterId', 'data URI'); + t('http://', 'http://', 'malformed URL'); + t('#theFilterId', '#theFilterId', 'URL starting with a #'); + + parseOptions.source = 'application/client/dir/other-style.css'; + t('./filters.svg#theFilterId', 'filters.svg#theFilterId', 'base path is root'); + t('./child/filters.svg#theFilterId', 'child/filters.svg#theFilterId', 'child directory from root'); + t('child/filters.svg#theFilterId', 'child/filters.svg#theFilterId', 'child directory from root'); + t('/filters.svg#theFilterId', 'filters.svg#theFilterId', 'absolute url'); + t('"/filters.svg#theFilterId"', '"filters.svg#theFilterId"', 'double quoted url'); + t("'/filters.svg#theFilterId'", "'filters.svg#theFilterId'", 'single quoted url'); + t('http://i.imgur.com/filters.svg#theFilterId', 'http://i.imgur.com/filters.svg#theFilterId', 'complete URL'); + t('"http://i.imgur.com/filters.svg#theFilterId"', '"http://i.imgur.com/filters.svg#theFilterId"', 'complete quoted URL'); + t('data:image/png;base64,iVBORw0K=#theFilterId', 'data:image/png;base64,iVBORw0K=#theFilterId', 'data URI'); + t('http://', 'http://', 'malformed URL'); + t('#theFilterId', '#theFilterId', 'URL starting with a #'); + +}); diff --git a/packages/minifier-js/.npm/package/npm-shrinkwrap.json b/packages/minifier-js/.npm/package/npm-shrinkwrap.json index 5f0105ed20..e5204dc869 100644 --- a/packages/minifier-js/.npm/package/npm-shrinkwrap.json +++ b/packages/minifier-js/.npm/package/npm-shrinkwrap.json @@ -1,60 +1,94 @@ { "dependencies": { + "align-text": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", + "from": "align-text@>=0.1.3 <0.2.0" + }, + "async": { + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz", + "from": "async@>=0.2.6 <0.3.0" + }, + "camelcase": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", + "from": "camelcase@>=1.0.2 <2.0.0" + }, + "center-align": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz", + "from": "center-align@>=0.1.1 <0.2.0" + }, + "cliui": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz", + "from": "cliui@>=2.1.0 <3.0.0" + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "from": "decamelize@>=1.0.0 <2.0.0" + }, + "is-buffer": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.4.tgz", + "from": "is-buffer@>=1.0.2 <2.0.0" + }, + "kind-of": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.0.4.tgz", + "from": "kind-of@>=3.0.2 <4.0.0" + }, + "lazy-cache": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz", + "from": "lazy-cache@>=1.0.3 <2.0.0" + }, + "longest": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", + "from": "longest@>=1.0.1 <2.0.0" + }, + "repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "from": "repeat-string@>=1.5.2 <2.0.0" + }, + "right-align": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz", + "from": "right-align@>=0.1.1 <0.2.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.1 <0.6.0" + }, "uglify-js": { - "version": "2.4.20", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.4.20.tgz", - "from": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.4.20.tgz", - "dependencies": { - "async": { - "version": "0.2.10", - "resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz", - "from": "https://registry.npmjs.org/async/-/async-0.2.10.tgz" - }, - "source-map": { - "version": "0.1.34", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.34.tgz", - "from": "https://registry.npmjs.org/source-map/-/source-map-0.1.34.tgz", - "dependencies": { - "amdefine": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-0.1.0.tgz", - "from": "https://registry.npmjs.org/amdefine/-/amdefine-0.1.0.tgz" - } - } - }, - "uglify-to-browserify": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz", - "from": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz" - }, - "yargs": { - "version": "3.5.4", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.5.4.tgz", - "from": "https://registry.npmjs.org/yargs/-/yargs-3.5.4.tgz", - "dependencies": { - "camelcase": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.0.2.tgz", - "from": "https://registry.npmjs.org/camelcase/-/camelcase-1.0.2.tgz" - }, - "decamelize": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.0.0.tgz", - "from": "https://registry.npmjs.org/decamelize/-/decamelize-1.0.0.tgz" - }, - "window-size": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz", - "from": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz" - }, - "wordwrap": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz", - "from": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz" - } - } - } - } + "version": "2.7.5", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.7.5.tgz", + "from": "uglify-js@2.7.5" + }, + "uglify-to-browserify": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz", + "from": "uglify-to-browserify@>=1.0.0 <1.1.0" + }, + "window-size": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz", + "from": "window-size@0.1.0" + }, + "wordwrap": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz", + "from": "wordwrap@0.0.2" + }, + "yargs": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", + "from": "yargs@>=3.10.0 <3.11.0" } } } diff --git a/packages/minifier-js/README.md b/packages/minifier-js/README.md index ec37b267c6..6195404f92 100644 --- a/packages/minifier-js/README.md +++ b/packages/minifier-js/README.md @@ -1 +1,5 @@ +# minifier-js +[Source code of released version](https://github.com/meteor/meteor/tree/master/packages/minifier-js) | [Source code of development version](https://github.com/meteor/meteor/tree/devel/packages/minifier-js) +*** + This is an internal Meteor package. \ No newline at end of file diff --git a/packages/minifier-js/package.js b/packages/minifier-js/package.js index afb5f8b6c9..7b74609556 100644 --- a/packages/minifier-js/package.js +++ b/packages/minifier-js/package.js @@ -1,14 +1,14 @@ Package.describe({ summary: "JavaScript minifier", - version: "1.2.13" + version: "1.2.16-beta.1" }); Npm.depends({ - "uglify-js": "2.4.20", + "uglify-js": "2.7.5" }); Npm.strip({ - "uglify-js": ["test/"], + "uglify-js": ["test/"] }); Package.onUse(function (api) { diff --git a/packages/minimongo/README.md b/packages/minimongo/README.md index c128f28f72..3258ac74c1 100644 --- a/packages/minimongo/README.md +++ b/packages/minimongo/README.md @@ -1,4 +1,6 @@ # Minimongo +[Source code of released version](https://github.com/meteor/meteor/tree/master/packages/minimongo) | [Source code of development version](https://github.com/meteor/meteor/tree/devel/packages/minimongo) +*** `minimongo` is reimplementation of (almost) the entire MongoDB API, against an in-memory JavaScript database. It is like a MongoDB emulator that runs inside diff --git a/packages/minimongo/minimongo_tests.js b/packages/minimongo/minimongo_tests.js index 5a77df7652..2898156114 100644 --- a/packages/minimongo/minimongo_tests.js +++ b/packages/minimongo/minimongo_tests.js @@ -2086,7 +2086,12 @@ Tinytest.add("minimongo - modify", function (test) { coll.update(query, mod); var actual = coll.findOne(); delete actual._id; // added by insert - test.equal(actual, expected, EJSON.stringify({input: doc, mod: mod})); + + if (typeof expected === "function") { + expected(actual, EJSON.stringify({input: doc, mod: mod})); + } else { + test.equal(actual, expected, EJSON.stringify({input: doc, mod: mod})); + } }; var modify = function (doc, mod, expected) { modifyWithQuery(doc, {}, mod, expected); @@ -2235,6 +2240,53 @@ Tinytest.add("minimongo - modify", function (test) { modify({a: {b: 2}}, {$inc: {'a.c': 10}}, {a: {b: 2, c: 10}}); exception({}, {$inc: {_id: 1}}); + // $currentDate + modify({}, {$currentDate: {a: true}}, (result, msg) => { test.instanceOf(result.a,Date,msg) }); + modify({}, {$currentDate: {a: {$type: "date"}}}, (result, msg) => { test.instanceOf(result.a,Date,msg) }); + exception({}, {$currentDate: {a: false}}); + exception({}, {$currentDate: {a: {}}}); + exception({}, {$currentDate: {a: {$type: "timestamp"}}}); + + // $min + modify({a: 1, b: 2}, {$min: {b: 1}}, {a: 1, b: 1}); + modify({a: 1, b: 2}, {$min: {b: 3}}, {a: 1, b: 2}); + modify({a: 1, b: 2}, {$min: {c: 10}}, {a: 1, b: 2, c: 10}); + exception({a: 1}, {$min: {a: '10'}}); + exception({a: 1}, {$min: {a: true}}); + exception({a: 1}, {$min: {a: [10]}}); + exception({a: '1'}, {$min: {a: 10}}); + exception({a: [1]}, {$min: {a: 10}}); + exception({a: {}}, {$min: {a: 10}}); + exception({a: false}, {$min: {a: 10}}); + exception({a: null}, {$min: {a: 10}}); + modify({a: [1, 2]}, {$min: {'a.1': 1}}, {a: [1, 1]}); + modify({a: [1, 2]}, {$min: {'a.1': 3}}, {a: [1, 2]}); + modify({a: [1, 2]}, {$min: {'a.2': 10}}, {a: [1, 2, 10]}); + modify({a: [1, 2]}, {$min: {'a.3': 10}}, {a: [1, 2, null, 10]}); + modify({a: {b: 2}}, {$min: {'a.b': 1}}, {a: {b: 1}}); + modify({a: {b: 2}}, {$min: {'a.c': 10}}, {a: {b: 2, c: 10}}); + exception({}, {$min: {_id: 1}}); + + // $max + modify({a: 1, b: 2}, {$max: {b: 1}}, {a: 1, b: 2}); + modify({a: 1, b: 2}, {$max: {b: 3}}, {a: 1, b: 3}); + modify({a: 1, b: 2}, {$max: {c: 10}}, {a: 1, b: 2, c: 10}); + exception({a: 1}, {$max: {a: '10'}}); + exception({a: 1}, {$max: {a: true}}); + exception({a: 1}, {$max: {a: [10]}}); + exception({a: '1'}, {$max: {a: 10}}); + exception({a: [1]}, {$max: {a: 10}}); + exception({a: {}}, {$max: {a: 10}}); + exception({a: false}, {$max: {a: 10}}); + exception({a: null}, {$max: {a: 10}}); + modify({a: [1, 2]}, {$max: {'a.1': 3}}, {a: [1, 3]}); + modify({a: [1, 2]}, {$max: {'a.1': 1}}, {a: [1, 2]}); + modify({a: [1, 2]}, {$max: {'a.2': 10}}, {a: [1, 2, 10]}); + modify({a: [1, 2]}, {$max: {'a.3': 10}}, {a: [1, 2, null, 10]}); + modify({a: {b: 2}}, {$max: {'a.b': 3}}, {a: {b: 3}}); + modify({a: {b: 2}}, {$max: {'a.c': 10}}, {a: {b: 2, c: 10}}); + exception({}, {$max: {_id: 1}}); + // $set modify({a: 1, b: 2}, {$set: {a: 10}}, {a: 10, b: 2}); modify({a: 1, b: 2}, {$set: {c: 10}}, {a: 1, b: 2, c: 10}); diff --git a/packages/minimongo/modify.js b/packages/minimongo/modify.js index 348e31a51e..bb140d23e9 100644 --- a/packages/minimongo/modify.js +++ b/packages/minimongo/modify.js @@ -182,6 +182,46 @@ var NO_CREATE_MODIFIERS = { }; var MODIFIERS = { + $currentDate: function (target, field, arg) { + if (typeof arg === "object" && arg.hasOwnProperty("$type")) { + if (arg.$type !== "date") { + throw MinimongoError("Minimongo does currently only support the date type in $currentDate modifiers"); + } + } else if (arg !== true) { + throw MinimongoError("Invalid $currentDate modifier"); + } + target[field] = new Date(); + }, + $min: function (target, field, arg) { + if (typeof arg !== "number") { + throw MinimongoError("Modifier $min allowed for numbers only"); + } + if (field in target) { + if (typeof target[field] !== "number") { + throw MinimongoError("Cannot apply $min modifier to non-number"); + } + if (target[field] > arg) { + target[field] = arg; + } + } else { + target[field] = arg; + } + }, + $max: function (target, field, arg) { + if (typeof arg !== "number") { + throw MinimongoError("Modifier $max allowed for numbers only"); + } + if (field in target) { + if (typeof target[field] !== "number") { + throw MinimongoError("Cannot apply $max modifier to non-number"); + } + if (target[field] < arg) { + target[field] = arg; + } + } else { + target[field] = arg; + } + }, $inc: function (target, field, arg) { if (typeof arg !== "number") throw MinimongoError("Modifier $inc allowed for numbers only"); diff --git a/packages/minimongo/package.js b/packages/minimongo/package.js index 5058b576a3..acdeaf194b 100644 --- a/packages/minimongo/package.js +++ b/packages/minimongo/package.js @@ -1,6 +1,6 @@ Package.describe({ summary: "Meteor's client-side datastore: a port of MongoDB to Javascript", - version: '1.0.17' + version: '1.0.19' }); Package.onUse(function (api) { diff --git a/packages/mobile-experience/README.md b/packages/mobile-experience/README.md index 4c0d0a980d..fa52ad557c 100644 --- a/packages/mobile-experience/README.md +++ b/packages/mobile-experience/README.md @@ -1,4 +1,6 @@ # mobile-experience +[Source code of released version](https://github.com/meteor/meteor/tree/master/packages/mobile-experience) | [Source code of development version](https://github.com/meteor/meteor/tree/devel/packages/mobile-experience) +*** A set of Cordova/PhoneGap-specific packages that set some good defaults when building for mobile. These packages only activate when you are building a native Android or iOS app. diff --git a/packages/mobile-status-bar/README.md b/packages/mobile-status-bar/README.md index 6e885f0d93..d68f68fd9a 100644 --- a/packages/mobile-status-bar/README.md +++ b/packages/mobile-status-bar/README.md @@ -1,4 +1,6 @@ # mobile-status-bar +[Source code of released version](https://github.com/meteor/meteor/tree/master/packages/mobile-status-bar) | [Source code of development version](https://github.com/meteor/meteor/tree/devel/packages/mobile-status-bar) +*** This package allows you to customize the status bar on Meteor Phonegap/Cordova apps. diff --git a/packages/mobile-status-bar/package.js b/packages/mobile-status-bar/package.js index 29e918a331..48f264f1b1 100644 --- a/packages/mobile-status-bar/package.js +++ b/packages/mobile-status-bar/package.js @@ -1,8 +1,8 @@ Package.describe({ summary: "Good defaults for the mobile status bar", - version: "1.0.12" + version: "1.0.13" }); Cordova.depends({ - 'cordova-plugin-statusbar': '2.1.2' + 'cordova-plugin-statusbar': '2.2.0' }); diff --git a/packages/modules-runtime/.npm/package/npm-shrinkwrap.json b/packages/modules-runtime/.npm/package/npm-shrinkwrap.json index 3d95f909ec..ceac639e6f 100644 --- a/packages/modules-runtime/.npm/package/npm-shrinkwrap.json +++ b/packages/modules-runtime/.npm/package/npm-shrinkwrap.json @@ -1,9 +1,9 @@ { "dependencies": { "install": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/install/-/install-0.8.1.tgz", - "from": "install@0.8.1" + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/install/-/install-0.8.2.tgz", + "from": "install@0.8.2" } } } diff --git a/packages/modules-runtime/README.md b/packages/modules-runtime/README.md index c8aa803a03..6f6d0bc761 100644 --- a/packages/modules-runtime/README.md +++ b/packages/modules-runtime/README.md @@ -1,4 +1,6 @@ ## modules-runtime +[Source code of released version](https://github.com/meteor/meteor/tree/master/packages/modules-runtime) | [Source code of development version](https://github.com/meteor/meteor/tree/devel/packages/modules-runtime) +*** This package implements the `meteorInstall` function that is used and re-exported by the `modules` package. Do not depend directly on this diff --git a/packages/modules-runtime/modules-runtime.js b/packages/modules-runtime/modules-runtime.js index b5f95185bc..30403e3fbc 100644 --- a/packages/modules-runtime/modules-runtime.js +++ b/packages/modules-runtime/modules-runtime.js @@ -4,6 +4,15 @@ var hasOwn = options.hasOwnProperty; // RegExp matching strings that don't start with a `.` or a `/`. var topLevelIdPattern = /^[^./]/; +if (typeof Profile === "function" && + process.env.METEOR_PROFILE) { + options.wrapRequire = function (require) { + return Profile(function (id) { + return "require(" + JSON.stringify(id) + ")"; + }, require); + }; +} + // This function will be called whenever a module identifier that hasn't // been installed is required. For backwards compatibility, and so that we // can require binary dependencies on the server, we implement the diff --git a/packages/modules-runtime/package.js b/packages/modules-runtime/package.js index 9ef594d341..2cf3791782 100644 --- a/packages/modules-runtime/package.js +++ b/packages/modules-runtime/package.js @@ -1,13 +1,13 @@ Package.describe({ name: "modules-runtime", - version: "0.7.5", + version: "0.7.7", summary: "CommonJS module system", git: "https://github.com/benjamn/install", documentation: "README.md" }); Npm.depends({ - install: "0.8.1" + install: "0.8.2" }); Package.onUse(function(api) { diff --git a/packages/modules/.npm/package/npm-shrinkwrap.json b/packages/modules/.npm/package/npm-shrinkwrap.json index c13812da88..eff3d98216 100644 --- a/packages/modules/.npm/package/npm-shrinkwrap.json +++ b/packages/modules/.npm/package/npm-shrinkwrap.json @@ -1,24 +1,29 @@ { "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" + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz", + "from": "acorn@>=3.3.0 <3.4.0" + }, + "acorn-es7-plugin": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/acorn-es7-plugin/-/acorn-es7-plugin-1.1.3.tgz", + "from": "acorn-es7-plugin@>=1.1.0 <1.2.0" }, "ast-types": { - "version": "0.8.17", - "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.8.17.tgz", - "from": "ast-types@>=0.8.16 <0.9.0" + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.9.2.tgz", + "from": "ast-types@>=0.9.0 <0.10.0" }, "magic-string": { - "version": "0.15.2", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.15.2.tgz", - "from": "magic-string@>=0.15.0 <0.16.0" + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.16.0.tgz", + "from": "magic-string@>=0.16.0 <0.17.0" }, "reify": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/reify/-/reify-0.3.6.tgz", - "from": "reify@0.3.6" + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/reify/-/reify-0.4.4.tgz", + "from": "reify@0.4.4" }, "vlq": { "version": "0.2.1", diff --git a/packages/modules/README.md b/packages/modules/README.md index 38574b50a8..f5f8633155 100644 --- a/packages/modules/README.md +++ b/packages/modules/README.md @@ -1,5 +1,6 @@ -# Using JavaScript modules in Meteor +# modules +Using JavaScript modules in Meteor ES2015 module support for Meteor -Read more at http://docs.meteor.com/#/full/modules \ No newline at end of file +Read more at http://docs.meteor.com/#/full/modules diff --git a/packages/modules/package.js b/packages/modules/package.js index 2315ab1943..f583cc1cc6 100644 --- a/packages/modules/package.js +++ b/packages/modules/package.js @@ -1,12 +1,12 @@ Package.describe({ name: "modules", - version: "0.7.5", + version: "0.7.8-beta.1", summary: "CommonJS module system", documentation: "README.md" }); Npm.depends({ - reify: "0.3.6" + reify: "0.4.4" }); Package.onUse(function(api) { diff --git a/packages/mongo-id/README.md b/packages/mongo-id/README.md new file mode 100644 index 0000000000..159326ff77 --- /dev/null +++ b/packages/mongo-id/README.md @@ -0,0 +1,4 @@ +# mongo-id +[Source code of released version](https://github.com/meteor/meteor/tree/master/packages/mongo-id) | [Source code of development version](https://github.com/meteor/meteor/tree/devel/packages/mongo-id) +*** + diff --git a/packages/mongo-id/package.js b/packages/mongo-id/package.js index 9a1021284d..a3fc9342e3 100644 --- a/packages/mongo-id/package.js +++ b/packages/mongo-id/package.js @@ -1,6 +1,6 @@ Package.describe({ summary: "JS simulation of MongoDB ObjectIDs", - version: '1.0.5', + version: '1.0.6', documentation: null }); diff --git a/packages/mongo-livedata/README.md b/packages/mongo-livedata/README.md index ec37b267c6..4fe9c93805 100644 --- a/packages/mongo-livedata/README.md +++ b/packages/mongo-livedata/README.md @@ -1 +1,5 @@ +# mongo-livedata +[Source code of released version](https://github.com/meteor/meteor/tree/master/packages/mongo-livedata) | [Source code of development version](https://github.com/meteor/meteor/tree/devel/packages/mongo-livedata) +*** + This is an internal Meteor package. \ No newline at end of file diff --git a/packages/mongo/.npm/package/npm-shrinkwrap.json b/packages/mongo/.npm/package/npm-shrinkwrap.json index c5840f7d36..61615e3eb5 100644 --- a/packages/mongo/.npm/package/npm-shrinkwrap.json +++ b/packages/mongo/.npm/package/npm-shrinkwrap.json @@ -3,7 +3,7 @@ "mongodb-uri": { "version": "0.9.7", "resolved": "https://registry.npmjs.org/mongodb-uri/-/mongodb-uri-0.9.7.tgz", - "from": "https://registry.npmjs.org/mongodb-uri/-/mongodb-uri-0.9.7.tgz" + "from": "mongodb-uri@0.9.7" } } } diff --git a/packages/mongo/README.md b/packages/mongo/README.md index 19c9f8e4af..701883a0a1 100644 --- a/packages/mongo/README.md +++ b/packages/mongo/README.md @@ -1,4 +1,6 @@ # mongo +[Source code of released version](https://github.com/meteor/meteor/tree/master/packages/mongo) | [Source code of development version](https://github.com/meteor/meteor/tree/devel/packages/mongo) +*** The `mongo` package is a [full stack database driver](https://www.meteor.com/full-stack-db-drivers) that provides diff --git a/packages/mongo/collection.js b/packages/mongo/collection.js index 68192fefc5..8e7d505a99 100644 --- a/packages/mongo/collection.js +++ b/packages/mongo/collection.js @@ -302,8 +302,8 @@ _.extend(Mongo.Collection.prototype, { * @param {Boolean} options.reactive (Client only) Default `true`; pass `false` to disable reactivity * @param {Function} options.transform Overrides `transform` on the [`Collection`](#collections) for this cursor. Pass `null` to disable transformation. * @param {Boolean} options.disableOplog (Server only) Pass true to disable oplog-tailing on this query. This affects the way server processes calls to `observe` on this query. Disabling the oplog can be useful when working with data that updates in large batches. - * @param {Number} options.pollingIntervalMs (Server only) How often to poll this query when observing on the server. In milliseconds. Defaults to 10 seconds. - * @param {Number} options.pollingThrottleMs (Server only) Minimum time to allow between re-polling. Increasing this will save CPU and mongo load at the expense of slower updates to users. Decreasing this is not recommended. In milliseconds. Defaults to 50 milliseconds. + * @param {Number} options.pollingIntervalMs (Server only) When oplog is disabled (through the use of `disableOplog` or when otherwise not available), the frequency (in milliseconds) of how often to poll this query when observing on the server. Defaults to 10000ms (10 seconds). + * @param {Number} options.pollingThrottleMs (Server only) When oplog is disabled (through the use of `disableOplog` or when otherwise not available), the minimum time (in milliseconds) to allow between re-polling when observing on the server. Increasing this will save CPU and mongo load at the expense of slower updates to users. Decreasing this is not recommended. Defaults to 50ms. * @returns {Mongo.Cursor} */ find: function (/* selector, options */) { @@ -704,7 +704,7 @@ Mongo.Collection.prototype._createCappedCollection = function (byteSize, maxDocu }; /** - * @summary Returns the [`Collection`](http://mongodb.github.io/node-mongodb-native/1.4/api-generated/collection.html) object corresponding to this collection from the [npm `mongodb` driver module](https://www.npmjs.com/package/mongodb) which is wrapped by `Mongo.Collection`. + * @summary Returns the [`Collection`](http://mongodb.github.io/node-mongodb-native/2.2/api/Collection.html) object corresponding to this collection from the [npm `mongodb` driver module](https://www.npmjs.com/package/mongodb) which is wrapped by `Mongo.Collection`. * @locus Server */ Mongo.Collection.prototype.rawCollection = function () { @@ -716,7 +716,7 @@ Mongo.Collection.prototype.rawCollection = function () { }; /** - * @summary Returns the [`Db`](http://mongodb.github.io/node-mongodb-native/1.4/api-generated/db.html) object corresponding to this collection's database connection from the [npm `mongodb` driver module](https://www.npmjs.com/package/mongodb) which is wrapped by `Mongo.Collection`. + * @summary Returns the [`Db`](http://mongodb.github.io/node-mongodb-native/2.2/api/Db.html) object corresponding to this collection's database connection from the [npm `mongodb` driver module](https://www.npmjs.com/package/mongodb) which is wrapped by `Mongo.Collection`. * @locus Server */ Mongo.Collection.prototype.rawDatabase = function () { diff --git a/packages/mongo/connection_options.js b/packages/mongo/connection_options.js index c061513e0f..a5e746c4ca 100644 --- a/packages/mongo/connection_options.js +++ b/packages/mongo/connection_options.js @@ -1,6 +1,6 @@ /** * @summary Allows for user specified connection options - * @example http://mongodb.github.io/node-mongodb-native/2.1/reference/connecting/connection-settings/ + * @example http://mongodb.github.io/node-mongodb-native/2.2/reference/connecting/connection-settings/ * @locus Server * @param {Object} options User specified Mongo connection options */ diff --git a/packages/mongo/mongo_driver.js b/packages/mongo/mongo_driver.js index 567c349e4c..d16b6a540f 100644 --- a/packages/mongo/mongo_driver.js +++ b/packages/mongo/mongo_driver.js @@ -130,15 +130,18 @@ MongoConnection = function (url, options) { self._observeMultiplexers = {}; self._onFailoverHook = new Hook; - var mongoOptions = _.extend({db: {safe: true}, server: {}, replSet: {}}, - Mongo._connectionOptions); - - // Set autoReconnect to true, unless passed on the URL. Why someone - // would want to set autoReconnect to false, I'm not really sure, but - // keeping this for backwards compatibility for now. - if (!(/[\?&]auto_?[rR]econnect=/.test(url))) { - mongoOptions.server.auto_reconnect = true; - } + var mongoOptions = _.extend({ + db: { safe: true }, + // http://mongodb.github.io/node-mongodb-native/2.2/api/Server.html + server: { + // Reconnect on error. + autoReconnect: true, + // Try to reconnect forever, instead of stopping after 30 tries (the + // default), with each attempt separated by 1000ms. + reconnectTries: Infinity + }, + replSet: {} + }, Mongo._connectionOptions); // Disable the native parser by default, unless specifically enabled // in the mongo URL. @@ -605,7 +608,7 @@ var isModificationMod = function (mod) { var transformResult = function (driverResult) { var meteorResult = { numberAffected: 0 }; if (driverResult) { - mongoResult = driverResult.result; + var mongoResult = driverResult.result; // On updates with upsert:true, the inserted values come as a list of // upserted values -- even with options.multi, when the upsert does insert, diff --git a/packages/mongo/package.js b/packages/mongo/package.js index 34065c55ea..20caef7701 100644 --- a/packages/mongo/package.js +++ b/packages/mongo/package.js @@ -9,7 +9,7 @@ Package.describe({ summary: "Adaptor for using MongoDB and Minimongo over DDP", - version: '1.1.10' + version: '1.1.15-beta.1' }); Npm.depends({ diff --git a/packages/non-core/blaze b/packages/non-core/blaze new file mode 160000 index 0000000000..dd65753c32 --- /dev/null +++ b/packages/non-core/blaze @@ -0,0 +1 @@ +Subproject commit dd65753c32b5535e33772ceb7ca16e998aaf6f24 diff --git a/packages/non-core/npm-bcrypt/.gitignore b/packages/non-core/npm-bcrypt/.gitignore deleted file mode 100644 index 677a6fc263..0000000000 --- a/packages/non-core/npm-bcrypt/.gitignore +++ /dev/null @@ -1 +0,0 @@ -.build* diff --git a/packages/non-core/npm-bcrypt/.npm/package/npm-shrinkwrap.json b/packages/non-core/npm-bcrypt/.npm/package/npm-shrinkwrap.json deleted file mode 100644 index 9f8bfdb477..0000000000 --- a/packages/non-core/npm-bcrypt/.npm/package/npm-shrinkwrap.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "dependencies": { - "bcrypt": { - "version": "0.8.7", - "resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-0.8.7.tgz", - "from": "bcrypt@0.8.7" - }, - "bindings": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.2.1.tgz", - "from": "bindings@1.2.1" - }, - "nan": { - "version": "2.3.5", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.3.5.tgz", - "from": "nan@2.3.5" - } - } -} diff --git a/packages/non-core/npm-bcrypt/.versions b/packages/non-core/npm-bcrypt/.versions deleted file mode 100644 index 6697cb762c..0000000000 --- a/packages/non-core/npm-bcrypt/.versions +++ /dev/null @@ -1,3 +0,0 @@ -meteor@1.1.16 -npm-bcrypt@0.8.7 -underscore@1.0.9 diff --git a/packages/non-core/npm-bcrypt/package.js b/packages/non-core/npm-bcrypt/package.js deleted file mode 100644 index ecaa1f8af1..0000000000 --- a/packages/non-core/npm-bcrypt/package.js +++ /dev/null @@ -1,14 +0,0 @@ -Package.describe({ - summary: "Wrapper around the bcrypt npm package", - version: '0.8.7', - documentation: null -}); - -Npm.depends({ - bcrypt: '0.8.7' -}); - -Package.onUse(function (api) { - api.export('NpmModuleBcrypt', 'server'); - api.addFiles('wrapper.js', 'server'); -}); diff --git a/packages/non-core/npm-bcrypt/wrapper.js b/packages/non-core/npm-bcrypt/wrapper.js deleted file mode 100644 index afff6278ff..0000000000 --- a/packages/non-core/npm-bcrypt/wrapper.js +++ /dev/null @@ -1 +0,0 @@ -NpmModuleBcrypt = Npm.require('bcrypt'); diff --git a/packages/non-core/npm-node-aes-gcm/.gitignore b/packages/non-core/npm-node-aes-gcm/.gitignore deleted file mode 100644 index 677a6fc263..0000000000 --- a/packages/non-core/npm-node-aes-gcm/.gitignore +++ /dev/null @@ -1 +0,0 @@ -.build* diff --git a/packages/non-core/npm-node-aes-gcm/.npm/package/.gitignore b/packages/non-core/npm-node-aes-gcm/.npm/package/.gitignore deleted file mode 100644 index 3c3629e647..0000000000 --- a/packages/non-core/npm-node-aes-gcm/.npm/package/.gitignore +++ /dev/null @@ -1 +0,0 @@ -node_modules diff --git a/packages/non-core/npm-node-aes-gcm/.npm/package/README b/packages/non-core/npm-node-aes-gcm/.npm/package/README deleted file mode 100644 index 3d492553a4..0000000000 --- a/packages/non-core/npm-node-aes-gcm/.npm/package/README +++ /dev/null @@ -1,7 +0,0 @@ -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. diff --git a/packages/non-core/npm-node-aes-gcm/.npm/package/npm-shrinkwrap.json b/packages/non-core/npm-node-aes-gcm/.npm/package/npm-shrinkwrap.json deleted file mode 100644 index eb10313104..0000000000 --- a/packages/non-core/npm-node-aes-gcm/.npm/package/npm-shrinkwrap.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "dependencies": { - "meteor-node-aes-gcm": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/meteor-node-aes-gcm/-/meteor-node-aes-gcm-0.1.7.tgz", - "from": "meteor-node-aes-gcm@0.1.7", - "dependencies": { - "node-aes-gcm": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/node-aes-gcm/-/node-aes-gcm-0.1.7.tgz", - "from": "node-aes-gcm@0.1.7", - "dependencies": { - "nan": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.3.3.tgz", - "from": "nan@>=2.3.0 <2.4.0" - } - } - } - } - } - } -} diff --git a/packages/non-core/npm-node-aes-gcm/.versions b/packages/non-core/npm-node-aes-gcm/.versions deleted file mode 100644 index 3e3de6dc43..0000000000 --- a/packages/non-core/npm-node-aes-gcm/.versions +++ /dev/null @@ -1,3 +0,0 @@ -meteor@1.1.16 -npm-node-aes-gcm@0.1.5_2 -underscore@1.0.9 diff --git a/packages/non-core/npm-node-aes-gcm/README.md b/packages/non-core/npm-node-aes-gcm/README.md deleted file mode 100644 index 65878320f3..0000000000 --- a/packages/non-core/npm-node-aes-gcm/README.md +++ /dev/null @@ -1,3 +0,0 @@ -## Using this package on Windows - -This package uses the [node-aes-gcm](https://github.com/xorbit/node-aes-gcm) NPM module, which requires you to have OpenSSL installed on your system to run. To install OpenSSL on Windows, use one of the binaries on [this page](http://slproweb.com/products/Win32OpenSSL.html). Don't forget to install the Visual Studio 2008 redistributables if you don't have them yet. diff --git a/packages/non-core/npm-node-aes-gcm/package.js b/packages/non-core/npm-node-aes-gcm/package.js deleted file mode 100644 index 20a82e1adc..0000000000 --- a/packages/non-core/npm-node-aes-gcm/package.js +++ /dev/null @@ -1,15 +0,0 @@ -Package.describe({ - summary: "Wrapper around the node-aes-gcm npm package", - version: '0.1.7_4', - documentation: null -}); - -Npm.depends({ - 'meteor-node-aes-gcm': '0.1.7' -}); - -Package.onUse(function (api) { - api.use("modules@0.6.1"); - api.export('NpmModuleNodeAesGcm', 'server'); - api.addFiles('wrapper.js', 'server'); -}); diff --git a/packages/non-core/npm-node-aes-gcm/wrapper.js b/packages/non-core/npm-node-aes-gcm/wrapper.js deleted file mode 100644 index 9e944ded31..0000000000 --- a/packages/non-core/npm-node-aes-gcm/wrapper.js +++ /dev/null @@ -1,14 +0,0 @@ -try { - NpmModuleNodeAesGcm = require('meteor-node-aes-gcm'); -} catch (err) { - if (process.platform === "win32" && - err.message.match(/specified module could not be found/)) { - // the user probably doesn't have OpenSSL installed. - throw new Error( -"Couldn't load the package 'meteor-node-aes-gcm'. This is probably because you " + -"don't have OpenSSL installed. See the README for details and directions: " + -"https://github.com/meteor/meteor/blob/devel/packages/non-core/npm-node-aes-gcm/README.md"); - } else { - throw err; - } -} diff --git a/packages/facebook/.gitignore b/packages/npm-bcrypt/.gitignore similarity index 100% rename from packages/facebook/.gitignore rename to packages/npm-bcrypt/.gitignore diff --git a/packages/non-core/npm-bcrypt/.npm/package/.gitignore b/packages/npm-bcrypt/.npm/package/.gitignore similarity index 100% rename from packages/non-core/npm-bcrypt/.npm/package/.gitignore rename to packages/npm-bcrypt/.npm/package/.gitignore diff --git a/packages/non-core/npm-bcrypt/.npm/package/README b/packages/npm-bcrypt/.npm/package/README similarity index 100% rename from packages/non-core/npm-bcrypt/.npm/package/README rename to packages/npm-bcrypt/.npm/package/README diff --git a/packages/npm-bcrypt/.npm/package/npm-shrinkwrap.json b/packages/npm-bcrypt/.npm/package/npm-shrinkwrap.json new file mode 100644 index 0000000000..95b0f873f6 --- /dev/null +++ b/packages/npm-bcrypt/.npm/package/npm-shrinkwrap.json @@ -0,0 +1,9 @@ +{ + "dependencies": { + "bcryptjs": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.3.0.tgz", + "from": "bcryptjs@2.3.0" + } + } +} diff --git a/packages/npm-bcrypt/.versions b/packages/npm-bcrypt/.versions new file mode 100644 index 0000000000..fe86c99b06 --- /dev/null +++ b/packages/npm-bcrypt/.versions @@ -0,0 +1,5 @@ +meteor@1.2.16 +modules@0.7.5 +modules-runtime@0.7.5 +npm-bcrypt@0.9.0 +underscore@1.0.9 diff --git a/packages/npm-bcrypt/README.md b/packages/npm-bcrypt/README.md new file mode 100644 index 0000000000..3f4ac59fff --- /dev/null +++ b/packages/npm-bcrypt/README.md @@ -0,0 +1,4 @@ +# npm-bcrypt +[Source code of released version](https://github.com/meteor/meteor/tree/master/packages/npm-bcrypt) | [Source code of development version](https://github.com/meteor/meteor/tree/devel/packages/npm-bcrypt) +*** + diff --git a/packages/npm-bcrypt/package.js b/packages/npm-bcrypt/package.js new file mode 100644 index 0000000000..b070cc4a05 --- /dev/null +++ b/packages/npm-bcrypt/package.js @@ -0,0 +1,15 @@ +Package.describe({ + summary: "Wrapper around the bcrypt npm package", + version: "0.9.2", + documentation: null +}); + +Npm.depends({ + bcryptjs: "2.3.0" +}); + +Package.onUse(function (api) { + api.use("modules@0.7.5", "server"); + api.mainModule("wrapper.js", "server"); + api.export("NpmModuleBcrypt", "server"); +}); diff --git a/packages/npm-bcrypt/wrapper.js b/packages/npm-bcrypt/wrapper.js new file mode 100644 index 0000000000..eac4ad51f0 --- /dev/null +++ b/packages/npm-bcrypt/wrapper.js @@ -0,0 +1,38 @@ +var assert = require("assert"); + +// The bcryptjs package has a slightly larger API than the native bcrypt +// package, so we stick to the smaller API for consistency. +var methods = { + compare: true, + compareSync: true, + genSalt: true, + genSaltSync: true, + getRounds: true, + hash: true, + hashSync: true +}; + +try { + // If you really need the native `bcrypt` package, then you should + // `meteor npm install --save bcrypt` into the node_modules directory in + // the root of your application. + var bcrypt = require("bcrypt"); +} catch (e) { + bcrypt = require("bcryptjs"); + console.warn([ + "Note: you are using a pure-JavaScript implementation of bcrypt.", + "While this implementation will work correctly, it is known to be", + "approximately three times slower than the native implementation.", + "In order to use the native implementation instead, run", + "", + " meteor npm install --save bcrypt", + "", + "in the root directory of your application." + ].join("\n")); +} + +exports.NpmModuleBcrypt = {}; +Object.keys(methods).forEach(function (key) { + assert.strictEqual(typeof bcrypt[key], "function"); + exports.NpmModuleBcrypt[key] = bcrypt[key]; +}); diff --git a/packages/npm-mongo/.npm/package/npm-shrinkwrap.json b/packages/npm-mongo/.npm/package/npm-shrinkwrap.json index e327314325..4493b91832 100644 --- a/packages/npm-mongo/.npm/package/npm-shrinkwrap.json +++ b/packages/npm-mongo/.npm/package/npm-shrinkwrap.json @@ -1,9 +1,14 @@ { "dependencies": { "bson": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/bson/-/bson-0.5.2.tgz", - "from": "bson@>=0.5.1 <0.6.0" + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/bson/-/bson-0.5.6.tgz", + "from": "bson@>=0.5.6 <0.6.0" + }, + "buffer-shims": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-shims/-/buffer-shims-1.0.0.tgz", + "from": "buffer-shims@>=1.0.0 <2.0.0" }, "core-util-is": { "version": "1.0.2", @@ -11,34 +16,39 @@ "from": "core-util-is@>=1.0.0 <1.1.0" }, "es6-promise": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.0.2.tgz", - "from": "es6-promise@3.0.2" + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.2.1.tgz", + "from": "es6-promise@3.2.1" }, "inherits": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", "from": "inherits@>=2.0.1 <2.1.0" }, "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "from": "isarray@0.0.1" + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "from": "isarray@>=1.0.0 <1.1.0" }, "mongodb": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-2.2.4.tgz", - "from": "mongodb@2.2.4" + "version": "2.2.11", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-2.2.11.tgz", + "from": "mongodb@2.2.11" }, "mongodb-core": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/mongodb-core/-/mongodb-core-2.0.6.tgz", - "from": "mongodb-core@2.0.6" + "version": "2.0.13", + "resolved": "https://registry.npmjs.org/mongodb-core/-/mongodb-core-2.0.13.tgz", + "from": "mongodb-core@2.0.13" + }, + "process-nextick-args": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", + "from": "process-nextick-args@>=1.0.6 <1.1.0" }, "readable-stream": { - "version": "1.0.31", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.31.tgz", - "from": "readable-stream@1.0.31" + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.1.5.tgz", + "from": "readable-stream@2.1.5" }, "require_optional": { "version": "1.0.0", @@ -59,6 +69,11 @@ "version": "0.10.31", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", "from": "string_decoder@>=0.10.0 <0.11.0" + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "from": "util-deprecate@>=1.0.1 <1.1.0" } } } diff --git a/packages/npm-mongo/README.md b/packages/npm-mongo/README.md new file mode 100644 index 0000000000..4776f34fbc --- /dev/null +++ b/packages/npm-mongo/README.md @@ -0,0 +1,4 @@ +# npm-mongo +[Source code of released version](https://github.com/meteor/meteor/tree/master/packages/npm-mongo) | [Source code of development version](https://github.com/meteor/meteor/tree/devel/packages/npm-mongo) +*** + diff --git a/packages/npm-mongo/package.js b/packages/npm-mongo/package.js index 92c312d7fe..8a21cb4846 100644 --- a/packages/npm-mongo/package.js +++ b/packages/npm-mongo/package.js @@ -3,12 +3,12 @@ Package.describe({ summary: "Wrapper around the mongo npm package", - version: '1.5.45', + version: '2.2.11_2', documentation: null }); Npm.depends({ - mongodb: "2.2.4" + mongodb: "2.2.11" }); Package.onUse(function (api) { diff --git a/packages/oauth-encryption/README.md b/packages/oauth-encryption/README.md index f989cf0cfd..ca15455569 100644 --- a/packages/oauth-encryption/README.md +++ b/packages/oauth-encryption/README.md @@ -1,4 +1,6 @@ # oauth-encryption +[Source code of released version](https://github.com/meteor/meteor/tree/master/packages/oauth-encryption) | [Source code of development version](https://github.com/meteor/meteor/tree/devel/packages/oauth-encryption) +*** Encrypts sensitive login secrets stored in the database such as a login service's application secret key and users' access tokens. diff --git a/packages/oauth-encryption/encrypt.js b/packages/oauth-encryption/encrypt.js index f0bb10fdac..d174c96ba5 100644 --- a/packages/oauth-encryption/encrypt.js +++ b/packages/oauth-encryption/encrypt.js @@ -1,12 +1,11 @@ -var crypto = Npm.require("crypto"); -// XXX We hope to be able to use the `crypto` module exclusively when -// Node supports GCM in version 0.11. -var gcm = NpmModuleNodeAesGcm; - -OAuthEncryption = {}; - +var crypto = require("crypto"); var gcmKey = null; +var OAuthEncryption = exports.OAuthEncryption = {}; +var objToStr = Object.prototype.toString; +function isString(value) { + return objToStr.call(value) === "[object String]"; +} // Node leniently ignores non-base64 characters when parsing a base64 // string, but we want to provide a more informative error message if @@ -17,7 +16,7 @@ var gcmKey = null; // Exported for the convenience of tests. // OAuthEncryption._isBase64 = function (str) { - return _.isString(str) && /^[A-Za-z0-9\+\/]*\={0,2}$/.test(str); + return isString(str) && /^[A-Za-z0-9\+\/]*\={0,2}$/.test(str); }; @@ -54,9 +53,10 @@ OAuthEncryption.loadKey = function (key) { // credentials such as access tokens from being used by a different // user. // -// We would actually like the user id to be AAD (additional -// authenticated data), but the node crypto API does not currently have -// support for specifying AAD. +// We might someday like the user id to be AAD (additional authenticated +// data), but the Node 0.10.x crypto API did not support specifying AAD, +// and it's not clear that we want to incur the compatibility issues of +// relying on that feature, even though it's now supported by Node 4. // OAuthEncryption.seal = function (data, userId) { if (! gcmKey) { @@ -67,13 +67,19 @@ OAuthEncryption.seal = function (data, userId) { data: data, userId: userId })); + var iv = crypto.randomBytes(12); - var result = gcm.encrypt(gcmKey, iv, plaintext, new Buffer([]) /* aad */); + var cipher = crypto.createCipheriv("aes-128-gcm", gcmKey, iv); + cipher.setAAD(new Buffer([])); + var chunks = [cipher.update(plaintext)]; + chunks.push(cipher.final()); + var encrypted = Buffer.concat(chunks); + return { iv: iv.toString("base64"), - ciphertext: result.ciphertext.toString("base64"), + ciphertext: encrypted.toString("base64"), algorithm: "aes-128-gcm", - authTag: result.auth_tag.toString("base64") + authTag: cipher.getAuthTag().toString("base64") }; }; @@ -96,23 +102,24 @@ OAuthEncryption.open = function (ciphertext, userId) { throw new Error(); } - var result = gcm.decrypt( + var decipher = crypto.createDecipheriv( + "aes-128-gcm", gcmKey, - new Buffer(ciphertext.iv, "base64"), - new Buffer(ciphertext.ciphertext, "base64"), - new Buffer([]), /* aad */ - new Buffer(ciphertext.authTag, "base64") + new Buffer(ciphertext.iv, "base64") ); - if (! result.auth_ok) { - throw new Error(); - } + decipher.setAAD(new Buffer([])); + decipher.setAuthTag(new Buffer(ciphertext.authTag, "base64")); + var chunks = [decipher.update( + new Buffer(ciphertext.ciphertext, "base64"))]; + chunks.push(decipher.final()); + var plaintext = Buffer.concat(chunks).toString("utf8"); var err; var data; try { - data = EJSON.parse(result.plaintext.toString()); + data = EJSON.parse(plaintext); } catch (e) { err = new Error(); } @@ -137,7 +144,7 @@ OAuthEncryption.isSealed = function (maybeCipherText) { OAuthEncryption._isBase64(maybeCipherText.iv) && OAuthEncryption._isBase64(maybeCipherText.ciphertext) && OAuthEncryption._isBase64(maybeCipherText.authTag) && - _.isString(maybeCipherText.algorithm); + isString(maybeCipherText.algorithm); }; diff --git a/packages/oauth-encryption/package.js b/packages/oauth-encryption/package.js index 14609f0702..9b622c4683 100644 --- a/packages/oauth-encryption/package.js +++ b/packages/oauth-encryption/package.js @@ -1,17 +1,13 @@ Package.describe({ summary: "Encrypt account secrets stored in the database", - version: '1.1.13' + version: '1.2.1' }); Package.onUse(function (api) { - api.use("npm-node-aes-gcm@=0.1.7_4"); - - api.export("OAuthEncryption", ["server"]); - api.use([ - "underscore", - "ejson" - ]); - api.addFiles("encrypt.js", ["server"]); + api.use("modules@0.7.5", "server"); + api.use("ejson@1.0.12", "server"); + api.mainModule("encrypt.js", "server"); + api.export("OAuthEncryption", "server"); }); Package.onTest(function (api) { diff --git a/packages/oauth/README.md b/packages/oauth/README.md index 42f3bf7ae6..8292a90a69 100644 --- a/packages/oauth/README.md +++ b/packages/oauth/README.md @@ -1,4 +1,6 @@ # oauth +[Source code of released version](https://github.com/meteor/meteor/tree/master/packages/oauth) | [Source code of development version](https://github.com/meteor/meteor/tree/devel/packages/oauth) +*** Common functionality for OAuth clients. See the [project page](https://www.meteor.com/accounts) on Meteor Accounts for more diff --git a/packages/oauth/end_of_popup_response.html b/packages/oauth/end_of_popup_response.html index 9812af83da..11cf6c5692 100644 --- a/packages/oauth/end_of_popup_response.html +++ b/packages/oauth/end_of_popup_response.html @@ -6,6 +6,6 @@

- + diff --git a/packages/oauth/end_of_redirect_response.html b/packages/oauth/end_of_redirect_response.html index af0e9885e5..bf53b766a5 100644 --- a/packages/oauth/end_of_redirect_response.html +++ b/packages/oauth/end_of_redirect_response.html @@ -1,6 +1,6 @@ - + diff --git a/packages/oauth/oauth_server.js b/packages/oauth/oauth_server.js index f5cc5d592e..cca82cb2bd 100644 --- a/packages/oauth/oauth_server.js +++ b/packages/oauth/oauth_server.js @@ -342,7 +342,10 @@ var renderEndOfLoginResponse = function (options) { throw new Error('invalid loginStyle: ' + options.loginStyle); } - var result = template.replace(/##CONFIG##/, JSON.stringify(config)); + var result = template.replace(/##CONFIG##/, JSON.stringify(config)) + .replace( + /##ROOT_URL_PATH_PREFIX##/, __meteor_runtime_config__.ROOT_URL_PATH_PREFIX + ); return "\n" + result; }; diff --git a/packages/oauth/oauth_tests.js b/packages/oauth/oauth_tests.js index dcca94ea18..bbca33dfeb 100644 --- a/packages/oauth/oauth_tests.js +++ b/packages/oauth/oauth_tests.js @@ -59,3 +59,96 @@ Tinytest.add( test.equal(OAuth._retrievePendingCredential(key, secret), cred); } ); + +Tinytest.add("oauth - _endOfLoginResponse with popup loginStyle supports unspecified ROOT_URL_PATH_PREFIX", + function (test) { + var res = { + writeHead: function () {}, + end: function (content) { + test.matches( + content, + /\/packages\/oauth\/end_of_popup_response\.js/ + ); + } + }; + var details = { + credentials: {}, + loginStyle: 'popup' + }; + OAuth._endOfLoginResponse(res, details); + } +); + +Tinytest.add("oauth - _endOfLoginResponse with popup loginStyle supports ROOT_URL_PATH_PREFIX", + function (test) { + var rootUrlPathPrefix = __meteor_runtime_config__.ROOT_URL_PATH_PREFIX; + __meteor_runtime_config__.ROOT_URL_PATH_PREFIX = '/test-root-url-prefix'; + var res = { + writeHead: function () {}, + end: function (content) { + __meteor_runtime_config__.ROOT_URL_PATH_PREFIX = rootUrlPathPrefix; + test.matches( + content, + /\/test-root-url-prefix\/packages\/oauth\/end_of_popup_response\.js/ + ); + } + }; + var details = { + credentials: {}, + loginStyle: 'popup' + }; + OAuth._endOfLoginResponse(res, details); + } +); + +Tinytest.add("oauth - _endOfLoginResponse with redirect loginStyle supports unspecified ROOT_URL_PATH_PREFIX", + function (test) { + var res = { + writeHead: function () {}, + end: function (content) { + test.matches( + content, + /\/packages\/oauth\/end_of_redirect_response\.js/ + ); + } + }; + var details = { + credentials: {}, + loginStyle: 'redirect', + query: { + state: new Buffer(JSON.stringify({ + redirectUrl: __meteor_runtime_config__.ROOT_URL + }), 'binary').toString('base64') + } + }; + OAuth._endOfLoginResponse(res, details); + } +); + + +Tinytest.add("oauth - _endOfLoginResponse with redirect loginStyle supports ROOT_URL_PATH_PREFIX", + function (test) { + var rootUrlPathPrefix = __meteor_runtime_config__.ROOT_URL_PATH_PREFIX; + __meteor_runtime_config__.ROOT_URL_PATH_PREFIX = '/test-root-url-prefix'; + var res = { + writeHead: function () {}, + end: function (content) { + __meteor_runtime_config__.ROOT_URL_PATH_PREFIX = rootUrlPathPrefix; + test.matches( + content, + /\/test-root-url-prefix\/packages\/oauth\/end_of_redirect_response\.js/ + ); + } + }; + var details = { + credentials: {}, + loginStyle: 'redirect', + query: { + state: new Buffer(JSON.stringify({ + redirectUrl: __meteor_runtime_config__.ROOT_URL + }), 'binary').toString('base64') + } + }; + OAuth._endOfLoginResponse(res, details); + } +); diff --git a/packages/oauth/package.js b/packages/oauth/package.js index 3ebf01bfb5..ed85871925 100644 --- a/packages/oauth/package.js +++ b/packages/oauth/package.js @@ -1,6 +1,6 @@ Package.describe({ summary: "Common code for OAuth-based services", - version: "1.1.11" + version: "1.1.12" }); Package.onUse(function (api) { @@ -57,5 +57,5 @@ Package.onTest(function (api) { }); Cordova.depends({ - 'cordova-plugin-inappbrowser': '1.3.0' + 'cordova-plugin-inappbrowser': '1.5.0' }); diff --git a/packages/oauth1/README.md b/packages/oauth1/README.md index 2dceba7eff..3b3205f286 100644 --- a/packages/oauth1/README.md +++ b/packages/oauth1/README.md @@ -1,4 +1,6 @@ # oauth1 +[Source code of released version](https://github.com/meteor/meteor/tree/master/packages/oauth1) | [Source code of development version](https://github.com/meteor/meteor/tree/devel/packages/oauth1) +*** Common functionality for OAuth 1 clients. See the [project page](https://www.meteor.com/accounts) on Meteor Accounts for more diff --git a/packages/oauth1/oauth1_server.js b/packages/oauth1/oauth1_server.js index 2d4794c7be..8aee39fce5 100644 --- a/packages/oauth1/oauth1_server.js +++ b/packages/oauth1/oauth1_server.js @@ -1,5 +1,26 @@ var url = Npm.require("url"); +OAuth._queryParamsWithAuthTokenUrl = function (authUrl, oauthBinding, params, whitelistedQueryParams) { + params = params || {}; + var redirectUrlObj = url.parse(authUrl, true); + + _.extend( + redirectUrlObj.query, + _.pick(params.query, whitelistedQueryParams), + { + oauth_token: oauthBinding.requestToken, + } + ); + + // Clear the `search` so it is rebuilt by Node's `url` from the `query` above. + // Using previous versions of the Node `url` module, this was just set to "" + // However, Node 6 docs seem to indicate that this should be `undefined`. + delete redirectUrlObj.search; + + // Reconstruct the URL back with provided query parameters merged with oauth_token + return url.format(redirectUrlObj); +}; + // connect middleware OAuth._requestHandlers['1'] = function (service, query, res) { var config = ServiceConfiguration.configurations.findOne({service: service.serviceName}); @@ -30,19 +51,19 @@ OAuth._requestHandlers['1'] = function (service, query, res) { oauthBinding.requestTokenSecret); // support for scope/name parameters - var redirectUrl = undefined; + var redirectUrl; + var authParams = { + query: query + }; + if(typeof urls.authenticate === "function") { - redirectUrl = urls.authenticate(oauthBinding, { - query: query - }); + redirectUrl = urls.authenticate(oauthBinding, authParams); } else { - // Parse the URL to support additional query parameters in urls.authenticate - var redirectUrlObj = url.parse(urls.authenticate, true); - redirectUrlObj.query = redirectUrlObj.query || {}; - redirectUrlObj.query.oauth_token = oauthBinding.requestToken; - redirectUrlObj.search = ''; - // Reconstruct the URL back with provided query parameters merged with oauth_token - redirectUrl = url.format(redirectUrlObj); + redirectUrl = OAuth._queryParamsWithAuthTokenUrl( + urls.authenticate, + oauthBinding, + authParams + ); } // redirect to provider login, which will redirect back to "step 2" below diff --git a/packages/oauth1/package.js b/packages/oauth1/package.js index 35cda44784..f86a19cab9 100644 --- a/packages/oauth1/package.js +++ b/packages/oauth1/package.js @@ -1,6 +1,6 @@ Package.describe({ summary: "Common code for OAuth1-based login services", - version: "1.1.10" + version: "1.1.11" }); Package.onUse(function (api) { diff --git a/packages/oauth2/README.md b/packages/oauth2/README.md index 5933c66051..294139e15d 100644 --- a/packages/oauth2/README.md +++ b/packages/oauth2/README.md @@ -1,4 +1,6 @@ # oauth2 +[Source code of released version](https://github.com/meteor/meteor/tree/master/packages/oauth2) | [Source code of development version](https://github.com/meteor/meteor/tree/devel/packages/oauth2) +*** Common functionality for OAuth 2 clients. See the [project page](https://www.meteor.com/accounts) on Meteor Accounts for more diff --git a/packages/oauth2/package.js b/packages/oauth2/package.js index bde7f326af..c73f05e6de 100644 --- a/packages/oauth2/package.js +++ b/packages/oauth2/package.js @@ -1,6 +1,6 @@ Package.describe({ summary: "Common code for OAuth2-based login services", - version: "1.1.10" + version: "1.1.11" }); Package.onUse(function (api) { diff --git a/packages/observe-sequence/README.md b/packages/observe-sequence/README.md index ec37b267c6..96c830cb40 100644 --- a/packages/observe-sequence/README.md +++ b/packages/observe-sequence/README.md @@ -1 +1,5 @@ +# observe-sequence +[Source code of released version](https://github.com/meteor/meteor/tree/master/packages/observe-sequence) | [Source code of development version](https://github.com/meteor/meteor/tree/devel/packages/observe-sequence) +*** + This is an internal Meteor package. \ No newline at end of file diff --git a/packages/observe-sequence/observe_sequence.js b/packages/observe-sequence/observe_sequence.js index 0ba750bcf5..68ec026591 100644 --- a/packages/observe-sequence/observe_sequence.js +++ b/packages/observe-sequence/observe_sequence.js @@ -94,7 +94,7 @@ ObserveSequence = { if (!seq) { seqArray = seqChangedToEmpty(lastSeqArray, callbacks); - } else if (seq instanceof Array) { + } else if (_.isArray(seq)) { seqArray = seqChangedToArray(lastSeqArray, seq, callbacks); } else if (isStoreCursor(seq)) { var result /* [seqArray, activeObserveHandle] */ = @@ -126,7 +126,7 @@ ObserveSequence = { fetch: function (seq) { if (!seq) { return []; - } else if (seq instanceof Array) { + } else if (_.isArray(seq)) { return seq; } else if (isStoreCursor(seq)) { return seq.fetch(); @@ -289,7 +289,8 @@ seqChangedToArray = function (lastSeqArray, array, callbacks) { id = "-" + item; } else if (typeof item === 'number' || typeof item === 'boolean' || - item === undefined) { + item === undefined || + item === null) { id = item; } else if (typeof item === 'object') { id = (item && ('_id' in item)) ? item._id : index; @@ -300,7 +301,7 @@ seqChangedToArray = function (lastSeqArray, array, callbacks) { var idString = idStringify(id); if (idsUsed[idString]) { - if (typeof item === 'object' && '_id' in item) + if (item && typeof item === 'object' && '_id' in item) warn("duplicate id " + id + " in", array); id = Random.id(); } else { diff --git a/packages/observe-sequence/observe_sequence_tests.js b/packages/observe-sequence/observe_sequence_tests.js index 4e126eca8f..e6a6ed823d 100644 --- a/packages/observe-sequence/observe_sequence_tests.js +++ b/packages/observe-sequence/observe_sequence_tests.js @@ -177,6 +177,15 @@ Tinytest.add('observe-sequence - array to other array, strings', function (test) ]); }); +Tinytest.add('observe-sequence - bug #7850 array with null values', function (test) { + runOneObserveSequenceTestCase(test, function () { + return [1, null]; + }, function () {}, [ + {addedAt: [1, 1, 0, null]}, + {addedAt: [null, null, 1, null]} + ]); +}); + Tinytest.add('observe-sequence - array to other array, objects without ids', function (test) { var dep = new Tracker.Dependency; var seq = [{foo: 1}, {bar: 2}]; diff --git a/packages/observe-sequence/package.js b/packages/observe-sequence/package.js index 94c4605acf..4a18677f95 100644 --- a/packages/observe-sequence/package.js +++ b/packages/observe-sequence/package.js @@ -1,14 +1,14 @@ Package.describe({ summary: "Observe changes to various sequence types such as arrays, cursors and objects", - version: "1.0.12" + version: "1.0.14" }); Package.onUse(function (api) { - api.use('tracker'); - api.use('mongo-id'); // for idStringify - api.use('diff-sequence'); - api.use('underscore'); - api.use('random'); + api.use('tracker@1.1.0'); + api.use('mongo-id@1.0.5'); // for idStringify + api.use('diff-sequence@1.0.6'); + api.use('underscore@1.0.9'); + api.use('random@1.0.10'); api.export('ObserveSequence'); api.addFiles(['observe_sequence.js']); }); diff --git a/packages/ordered-dict/README.md b/packages/ordered-dict/README.md new file mode 100644 index 0000000000..b1b82b4966 --- /dev/null +++ b/packages/ordered-dict/README.md @@ -0,0 +1,4 @@ +# ordered-dict +[Source code of released version](https://github.com/meteor/meteor/tree/master/packages/ordered-dict) | [Source code of development version](https://github.com/meteor/meteor/tree/devel/packages/ordered-dict) +*** + diff --git a/packages/ordered-dict/package.js b/packages/ordered-dict/package.js index ae17be0037..3e9af08368 100644 --- a/packages/ordered-dict/package.js +++ b/packages/ordered-dict/package.js @@ -1,6 +1,6 @@ Package.describe({ summary: "Ordered traversable dictionary with a mutable ordering", - version: '1.0.8', + version: '1.0.9', documentation: null }); diff --git a/packages/package-stats-opt-out/README.md b/packages/package-stats-opt-out/README.md index 592026d96c..dd14ffd0ce 100644 --- a/packages/package-stats-opt-out/README.md +++ b/packages/package-stats-opt-out/README.md @@ -1,4 +1,6 @@ # package-stats-opt-out +[Source code of released version](https://github.com/meteor/meteor/tree/master/packages/package-stats-opt-out) | [Source code of development version](https://github.com/meteor/meteor/tree/devel/packages/package-stats-opt-out) +*** Opt out of sending statistics about which packages your app is using. diff --git a/packages/package-version-parser/README.md b/packages/package-version-parser/README.md index 74aba151c1..3dcad42c75 100644 --- a/packages/package-version-parser/README.md +++ b/packages/package-version-parser/README.md @@ -1 +1,5 @@ +# package-version-parser +[Source code of released version](https://github.com/meteor/meteor/tree/master/packages/package-version-parser) | [Source code of development version](https://github.com/meteor/meteor/tree/devel/packages/package-version-parser) +*** + This is an internal Meteor package. diff --git a/packages/package-version-parser/package.js b/packages/package-version-parser/package.js index 94f872f794..601910f00f 100644 --- a/packages/package-version-parser/package.js +++ b/packages/package-version-parser/package.js @@ -1,6 +1,6 @@ Package.describe({ summary: "Parses Meteor Smart Package version strings", - version: "3.0.9" + version: "3.0.10" }); Package.onUse(function (api) { diff --git a/packages/preserve-inputs/README.md b/packages/preserve-inputs/README.md index ec37b267c6..b9f29d421f 100644 --- a/packages/preserve-inputs/README.md +++ b/packages/preserve-inputs/README.md @@ -1 +1,5 @@ +# preserve-inputs +[Source code of released version](https://github.com/meteor/meteor/tree/master/packages/preserve-inputs) | [Source code of development version](https://github.com/meteor/meteor/tree/devel/packages/preserve-inputs) +*** + This is an internal Meteor package. \ No newline at end of file diff --git a/packages/preserve-inputs/package.js b/packages/preserve-inputs/package.js index fa4c02bf5a..aade8591ae 100644 --- a/packages/preserve-inputs/package.js +++ b/packages/preserve-inputs/package.js @@ -1,6 +1,6 @@ Package.describe({ summary: "Deprecated package (now empty)", - version: "1.0.10" + version: "1.0.11" }); Package.onUse(function (api) { diff --git a/packages/promise/.npm/package/npm-shrinkwrap.json b/packages/promise/.npm/package/npm-shrinkwrap.json index b65eb73c37..469b5f6bbe 100644 --- a/packages/promise/.npm/package/npm-shrinkwrap.json +++ b/packages/promise/.npm/package/npm-shrinkwrap.json @@ -1,21 +1,19 @@ { "dependencies": { + "asap": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.5.tgz", + "from": "asap@>=2.0.3 <2.1.0" + }, "meteor-promise": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/meteor-promise/-/meteor-promise-0.7.2.tgz", - "from": "meteor-promise@0.7.2" + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/meteor-promise/-/meteor-promise-0.8.0.tgz", + "from": "meteor-promise@0.8.0" }, "promise": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/promise/-/promise-7.1.1.tgz", - "from": "promise@7.1.1", - "dependencies": { - "asap": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.3.tgz", - "from": "asap@>=2.0.3 <2.1.0" - } - } + "from": "promise@7.1.1" } } } diff --git a/packages/promise/README.md b/packages/promise/README.md index 8e5f62904d..50423bb186 100644 --- a/packages/promise/README.md +++ b/packages/promise/README.md @@ -1,4 +1,7 @@ # promise [![Build Status](https://travis-ci.org/meteor/promise.svg)](https://travis-ci.org/meteor/promise) +[Source code of released version](https://github.com/meteor/meteor/tree/master/packages/promise) | [Source code of development version](https://github.com/meteor/meteor/tree/devel/packages/promise) +*** + ECMAScript 2015 Promise polyfill with Fiber support The static methods `Promise.async` and `Promise.await` implement the diff --git a/packages/promise/package.js b/packages/promise/package.js index a808ffae15..b4049ed53a 100644 --- a/packages/promise/package.js +++ b/packages/promise/package.js @@ -1,18 +1,18 @@ Package.describe({ name: "promise", - version: "0.8.3", + version: "0.8.8", summary: "ECMAScript 2015 Promise polyfill with Fiber support", git: "https://github.com/meteor/promise", documentation: "README.md" }); Npm.depends({ - "meteor-promise": "0.7.2", + "meteor-promise": "0.8.0", "promise": "7.1.1" }); Package.onUse(function(api) { - api.use("modules"); + api.use("modules@0.7.6"); api.mainModule("client.js", "client"); api.mainModule("server.js", "server"); api.export("Promise"); diff --git a/packages/random/README.md b/packages/random/README.md index 380b91834e..0b0b77ebc3 100644 --- a/packages/random/README.md +++ b/packages/random/README.md @@ -1,4 +1,6 @@ # random +[Source code of released version](https://github.com/meteor/meteor/tree/master/packages/random) | [Source code of development version](https://github.com/meteor/meteor/tree/devel/packages/random) +*** The `random` package provides several functions for generating random numbers. It uses a cryptographically strong pseudorandom number generator when diff --git a/packages/rate-limit/README.md b/packages/rate-limit/README.md index 5a46421e59..1cddca4aaf 100644 --- a/packages/rate-limit/README.md +++ b/packages/rate-limit/README.md @@ -1,4 +1,6 @@ # Rate Limit +[Source code of released version](https://github.com/meteor/meteor/tree/master/packages/rate-limit) | [Source code of development version](https://github.com/meteor/meteor/tree/devel/packages/rate-limit) +*** === A Rate Limiter is a general rate limiting object that stores rules and determines whether inputs are allowed based on the rules. There is also a diff --git a/packages/rate-limit/package.js b/packages/rate-limit/package.js index 656548f146..06f92b3e2a 100644 --- a/packages/rate-limit/package.js +++ b/packages/rate-limit/package.js @@ -1,6 +1,6 @@ Package.describe({ name: 'rate-limit', - version: '1.0.5', + version: '1.0.6', // Brief, one-line summary of the package. summary: 'An algorithm for rate limiting anything', // URL to the Git repository containing the source code for this package. diff --git a/packages/reactive-dict/README.md b/packages/reactive-dict/README.md index 0f5af9b8e9..72633610cf 100644 --- a/packages/reactive-dict/README.md +++ b/packages/reactive-dict/README.md @@ -1,4 +1,6 @@ # reactive-dict +[Source code of released version](https://github.com/meteor/meteor/tree/master/packages/reactive-dict) | [Source code of development version](https://github.com/meteor/meteor/tree/devel/packages/reactive-dict) +*** This package provide `ReactiveDict`, a general-purpose reactive datatype for use with diff --git a/packages/reactive-var/README.md b/packages/reactive-var/README.md index 9dff989b35..16696efb9e 100644 --- a/packages/reactive-var/README.md +++ b/packages/reactive-var/README.md @@ -1,4 +1,6 @@ # reactive-var +[Source code of released version](https://github.com/meteor/meteor/tree/master/packages/reactive-var) | [Source code of development version](https://github.com/meteor/meteor/tree/devel/packages/reactive-var) +*** This package provide `ReactiveVar`, a general-purpose reactive datatype for use with diff --git a/packages/reactive-var/package.js b/packages/reactive-var/package.js index 5d24a2aaaf..029608a876 100644 --- a/packages/reactive-var/package.js +++ b/packages/reactive-var/package.js @@ -1,6 +1,6 @@ Package.describe({ summary: "Reactive variable", - version: '1.0.10' + version: '1.0.11' }); Package.onUse(function (api) { diff --git a/packages/reload-safetybelt/README.md b/packages/reload-safetybelt/README.md index ec37b267c6..a73157e371 100644 --- a/packages/reload-safetybelt/README.md +++ b/packages/reload-safetybelt/README.md @@ -1 +1,5 @@ +# reload-safetybelt +[Source code of released version](https://github.com/meteor/meteor/tree/master/packages/reload-safetybelt) | [Source code of development version](https://github.com/meteor/meteor/tree/devel/packages/reload-safetybelt) +*** + This is an internal Meteor package. \ No newline at end of file diff --git a/packages/reload-safetybelt/package.js b/packages/reload-safetybelt/package.js index c7f78e172c..153ed5268a 100644 --- a/packages/reload-safetybelt/package.js +++ b/packages/reload-safetybelt/package.js @@ -1,6 +1,6 @@ Package.describe({ summary: "Reload safety belt for multi-server deployments", - version: '1.0.10' + version: '1.0.11' }); Package.onUse(function (api) { diff --git a/packages/reload/README.md b/packages/reload/README.md index c8d6e6f4f6..ca025fbae0 100644 --- a/packages/reload/README.md +++ b/packages/reload/README.md @@ -1,4 +1,6 @@ # reload +[Source code of released version](https://github.com/meteor/meteor/tree/master/packages/reload) | [Source code of development version](https://github.com/meteor/meteor/tree/devel/packages/reload) +*** The `reload` package handles the process of *migrating* an app: serializing the app's state, then shutting down and restarting the app diff --git a/packages/reload/package.js b/packages/reload/package.js index 0be626208a..b1d955764f 100644 --- a/packages/reload/package.js +++ b/packages/reload/package.js @@ -1,6 +1,6 @@ Package.describe({ summary: "Reload the page while preserving application state.", - version: '1.1.10' + version: '1.1.11' }); Package.onUse(function (api) { diff --git a/packages/retry/README.md b/packages/retry/README.md index ec37b267c6..950132099e 100644 --- a/packages/retry/README.md +++ b/packages/retry/README.md @@ -1 +1,5 @@ +# retry +[Source code of released version](https://github.com/meteor/meteor/tree/master/packages/retry) | [Source code of development version](https://github.com/meteor/meteor/tree/devel/packages/retry) +*** + This is an internal Meteor package. \ No newline at end of file diff --git a/packages/retry/package.js b/packages/retry/package.js index 41a80c7efb..dfb443fe47 100644 --- a/packages/retry/package.js +++ b/packages/retry/package.js @@ -1,6 +1,6 @@ Package.describe({ summary: "Retry logic with exponential backoff", - version: '1.0.8' + version: '1.0.9' }); Package.onUse(function (api) { diff --git a/packages/routepolicy/README.md b/packages/routepolicy/README.md index 6941d9126a..c825fda7f8 100644 --- a/packages/routepolicy/README.md +++ b/packages/routepolicy/README.md @@ -1,4 +1,6 @@ # RoutePolicy +[Source code of released version](https://github.com/meteor/meteor/tree/master/packages/routepolicy) | [Source code of development version](https://github.com/meteor/meteor/tree/devel/packages/routepolicy) +*** RoutePolicy, part of [Webapp](https://www.meteor.com/webapp), is a low-level API for declaring the offline access semantics that apply to diff --git a/packages/routepolicy/package.js b/packages/routepolicy/package.js index 8aad7e6744..b443732575 100644 --- a/packages/routepolicy/package.js +++ b/packages/routepolicy/package.js @@ -1,6 +1,6 @@ Package.describe({ summary: "route policy declarations", - version: '1.0.11' + version: '1.0.12' }); Package.onUse(function (api) { diff --git a/packages/service-configuration/README.md b/packages/service-configuration/README.md index 56210e142b..59dfe29667 100644 --- a/packages/service-configuration/README.md +++ b/packages/service-configuration/README.md @@ -1,4 +1,6 @@ # service-configuration +[Source code of released version](https://github.com/meteor/meteor/tree/master/packages/service-configuration) | [Source code of development version](https://github.com/meteor/meteor/tree/devel/packages/service-configuration) +*** Configure login services. Example: diff --git a/packages/service-configuration/package.js b/packages/service-configuration/package.js index 5486424e0a..5dc8795b9a 100644 --- a/packages/service-configuration/package.js +++ b/packages/service-configuration/package.js @@ -1,6 +1,6 @@ Package.describe({ summary: "Manage the configuration for third-party services", - version: "1.0.10" + version: "1.0.11" }); Package.onUse(function(api) { diff --git a/packages/session/README.md b/packages/session/README.md index f7c1911e9d..a78f771fc9 100644 --- a/packages/session/README.md +++ b/packages/session/README.md @@ -1,4 +1,6 @@ # session +[Source code of released version](https://github.com/meteor/meteor/tree/master/packages/session) | [Source code of development version](https://github.com/meteor/meteor/tree/devel/packages/session) +*** This package provide `Session`. `Session` is a special [ReactiveDict](https://atmospherejs.com/meteor/reactive-dict) whose diff --git a/packages/session/package.js b/packages/session/package.js index 7f97d4293f..aaf70597e5 100644 --- a/packages/session/package.js +++ b/packages/session/package.js @@ -1,6 +1,6 @@ Package.describe({ summary: "Session variable", - version: '1.1.6' + version: '1.1.7' }); Package.onUse(function (api) { diff --git a/packages/sha/README.md b/packages/sha/README.md index 74afdcc59c..35890c407f 100644 --- a/packages/sha/README.md +++ b/packages/sha/README.md @@ -1,3 +1,7 @@ +# sha +[Source code of released version](https://github.com/meteor/meteor/tree/master/packages/sha) | [Source code of development version](https://github.com/meteor/meteor/tree/devel/packages/sha) +*** + An implementation of the [SHA256](https://en.wikipedia.org/wiki/SHA-2) secure hash function, available in all environments. ## Usage diff --git a/packages/sha/package.js b/packages/sha/package.js index e103a7aaef..5d77ef40a4 100644 --- a/packages/sha/package.js +++ b/packages/sha/package.js @@ -1,5 +1,5 @@ Package.describe({ - version: '1.0.8', + version: '1.0.9', summary: 'SHA256 implementation', git: 'https://github.com/meteor/meteor' }); diff --git a/packages/shell-server/README.md b/packages/shell-server/README.md new file mode 100644 index 0000000000..be30895185 --- /dev/null +++ b/packages/shell-server/README.md @@ -0,0 +1,4 @@ +# `meteor shell` + +This code used to be integrated into the Meteor command-line tool, which +made it difficult to release frequent updates. diff --git a/packages/shell-server/main.js b/packages/shell-server/main.js new file mode 100644 index 0000000000..ab044b88b4 --- /dev/null +++ b/packages/shell-server/main.js @@ -0,0 +1,7 @@ +export * from "./shell-server.js"; +import { listen } from "./shell-server.js"; + +const shellDir = process.env.METEOR_SHELL_DIR; +if (shellDir) { + listen(shellDir); +} diff --git a/packages/shell-server/package.js b/packages/shell-server/package.js new file mode 100644 index 0000000000..689b52f701 --- /dev/null +++ b/packages/shell-server/package.js @@ -0,0 +1,11 @@ +Package.describe({ + name: "shell-server", + version: "0.2.1", + summary: "Server-side component of the `meteor shell` command.", + documentation: "README.md" +}); + +Package.onUse(function(api) { + api.use("ecmascript@0.5.7", "server"); + api.mainModule("main.js", "server"); +}); diff --git a/packages/shell-server/shell-server.js b/packages/shell-server/shell-server.js new file mode 100644 index 0000000000..419fdaa4c7 --- /dev/null +++ b/packages/shell-server/shell-server.js @@ -0,0 +1,486 @@ +var assert = require("assert"); +var path = require("path"); +var stream = require("stream"); +var fs = require("fs"); +var net = require("net"); +var vm = require("vm"); +var _ = require("underscore"); +var INFO_FILE_MODE = parseInt("600", 8); // Only the owner can read or write. +var EXITING_MESSAGE = "Shell exiting..."; + +// Invoked by the server process to listen for incoming connections from +// shell clients. Each connection gets its own REPL instance. +export function listen(shellDir) { + function callback() { + new Server(shellDir).listen(); + } + + // If the server is still in the very early stages of starting up, + // Meteor.startup may not available yet. + if (typeof Meteor === "object") { + Meteor.startup(callback); + } else if (typeof __meteor_bootstrap__ === "object") { + var hooks = __meteor_bootstrap__.startupHooks; + if (hooks) { + hooks.push(callback); + } else { + // As a fallback, just call the callback asynchronously. + setImmediate(callback); + } + } +} + +// Disabling the shell causes all attached clients to disconnect and exit. +export function disable(shellDir) { + try { + // Replace info.json with a file that says the shell server is + // disabled, so that any connected shell clients will fail to + // reconnect after the server process closes their sockets. + fs.writeFileSync( + getInfoFile(shellDir), + JSON.stringify({ + status: "disabled", + reason: "Shell server has shut down." + }) + "\n", + { mode: INFO_FILE_MODE } + ); + } catch (ignored) {} +} + +class Server { + constructor(shellDir) { + var self = this; + assert.ok(self instanceof Server); + + self.shellDir = shellDir; + self.key = Math.random().toString(36).slice(2); + + self.server = net.createServer(function(socket) { + self.onConnection(socket); + }).on("error", function(err) { + console.error(err.stack); + }); + } + + listen() { + var self = this; + var infoFile = getInfoFile(self.shellDir); + + fs.unlink(infoFile, function() { + self.server.listen(0, "127.0.0.1", function() { + fs.writeFileSync(infoFile, JSON.stringify({ + status: "enabled", + port: self.server.address().port, + key: self.key + }) + "\n", { + mode: INFO_FILE_MODE + }); + }); + }); + } + + onConnection(socket) { + var self = this; + + // Make sure this function doesn't try to write anything to the socket + // after it has been closed. + socket.on("close", function() { + socket = null; + }); + + // If communication is not established within 1000ms of the first + // connection, forcibly close the socket. + var timeout = setTimeout(function() { + if (socket) { + socket.removeAllListeners("data"); + socket.end(EXITING_MESSAGE + "\n"); + } + }, 1000); + + // Let connecting clients configure certain REPL options by sending a + // JSON object over the socket. For example, only the client knows + // whether it's running a TTY or an Emacs subshell or some other kind of + // terminal, so the client must decide the value of options.terminal. + readJSONFromStream(socket, function (error, options, replInputSocket) { + clearTimeout(timeout); + + if (error) { + socket = null; + console.error(error.stack); + return; + } + + if (options.key !== self.key) { + if (socket) { + socket.end(EXITING_MESSAGE + "\n"); + } + return; + } + delete options.key; + + // Set the columns to what is being requested by the client. + if (options.columns && socket) { + socket.columns = options.columns; + } + delete options.columns; + + if (options.evaluateAndExit) { + evalCommand.call( + Object.create(null), // Dummy repl object without ._RecoverableError. + "(" + options.evaluateAndExit.command + ")", + null, // evalCommand ignores the context parameter, anyway + options.evaluateAndExit.filename || "", + function (error, result) { + if (socket) { + var message = error ? { + error: error + "", + code: 1 + } : { + result: result + }; + + // Sending back a JSON payload allows the client to + // distinguish between errors and successful results. + socket.end(JSON.stringify(message) + "\n"); + } + } + ); + return; + } + delete options.evaluateAndExit; + + // Immutable options. + _.extend(options, { + input: replInputSocket, + output: socket + }); + + // Overridable options. + _.defaults(options, { + prompt: "> ", + terminal: true, + useColors: true, + useGlobal: true, + ignoreUndefined: true, + }); + + self.startREPL(options); + }); + } + + startREPL(options) { + var self = this; + + // Make sure this function doesn't try to write anything to the output + // stream after it has been closed. + options.output.on("close", function() { + options.output = null; + }); + + var repl = self.repl = require("repl").start(options); + + // History persists across shell sessions! + self.initializeHistory(); + + // Save the global `_` object in the server. This is probably defined by the + // underscore package. It is unlikely to be the same object as the `var _ = + // require('underscore')` in this file! + var originalUnderscore = repl.context._; + + Object.defineProperty(repl.context, "_", { + // Force the global _ variable to remain bound to underscore. + get: function () { return originalUnderscore; }, + + // Expose the last REPL result as __ instead of _. + set: function(lastResult) { + repl.context.__ = lastResult; + }, + + enumerable: true, + + // Allow this property to be (re)defined more than once (e.g. each + // time the server restarts). + configurable: true + }); + + if (Package.modules) { + // Use the same `require` function and `module` object visible to the + // application. + var toBeInstalled = {}; + var shellModuleName = "meteor-shell-" + + Math.random().toString(36).slice(2) + ".js"; + + toBeInstalled[shellModuleName] = function (require, exports, module) { + repl.context.module = module; + repl.context.require = require; + + // Tab completion sometimes uses require.extensions, but only for + // the keys. + require.extensions = { + ".js": true, + ".json": true, + ".node": true, + }; + }; + + // This populates repl.context.{module,require} by evaluating the + // module defined above. + Package.modules.meteorInstall(toBeInstalled)("./" + shellModuleName); + } + + repl.context.repl = repl; + + // Some improvements to the existing help messages. + function addHelp(cmd, helpText) { + var info = repl.commands[cmd] || repl.commands["." + cmd]; + if (info) { + info.help = helpText; + } + } + addHelp("break", "Terminate current command input and display new prompt"); + addHelp("exit", "Disconnect from server and leave shell"); + addHelp("help", "Show this help information"); + + // When the REPL exits, signal the attached client to exit by sending it + // the special EXITING_MESSAGE. + repl.on("exit", function() { + if (options.output) { + options.output.write(EXITING_MESSAGE + "\n"); + options.output.end(); + } + }); + + // When the server process exits, end the output stream but do not + // signal the attached client to exit. + process.on("exit", function() { + if (options.output) { + options.output.end(); + } + }); + + // This Meteor-specific shell command rebuilds the application as if a + // change was made to server code. + repl.defineCommand("reload", { + help: "Restart the server and the shell", + action: function() { + process.exit(0); + } + }); + + // Trigger one recoverable error using the default eval function, just + // to capture the Recoverable error constructor, so that our custom + // evalCommand function can wrap recoverable errors properly. + repl.eval( + "{", null, "", + function (error) { + // Capture the Recoverable error constructor. + repl._RecoverableError = error && error.constructor; + + // Now set repl.eval to the actual evalCommand function that we want + // to use, bound to repl._domain if necessary. + repl.eval = repl._domain + ? repl._domain.bind(evalCommand) + : evalCommand; + + // Terminate the partial evaluation of the { command. + repl.commands["break"].action.call(repl); + } + ); + } + + // This function allows a persistent history of shell commands to be saved + // to and loaded from .meteor/local/shell-history. + initializeHistory() { + var self = this; + var rli = self.repl.rli; + var historyFile = getHistoryFile(self.shellDir); + var historyFd = fs.openSync(historyFile, "a+"); + var historyLines = fs.readFileSync(historyFile, "utf8").split("\n"); + var seenLines = Object.create(null); + + if (! rli.history) { + rli.history = []; + rli.historyIndex = -1; + } + + while (rli.history && historyLines.length > 0) { + var line = historyLines.pop(); + if (line && /\S/.test(line) && ! seenLines[line]) { + rli.history.push(line); + seenLines[line] = true; + } + } + + rli.addListener("line", function(line) { + if (historyFd >= 0 && /\S/.test(line)) { + fs.writeSync(historyFd, line + "\n"); + } + }); + + self.repl.on("exit", function() { + fs.closeSync(historyFd); + historyFd = -1; + }); + } +} + +function readJSONFromStream(inputStream, callback) { + var outputStream = new stream.PassThrough; + var dataSoFar = ""; + + function onData(buffer) { + var lines = buffer.toString("utf8").split("\n"); + + while (lines.length > 0) { + dataSoFar += lines.shift(); + + try { + var json = JSON.parse(dataSoFar); + } catch (error) { + if (error instanceof SyntaxError) { + continue; + } + + return finish(error); + } + + if (lines.length > 0) { + outputStream.write(lines.join("\n")); + } + + inputStream.pipe(outputStream); + + return finish(null, json); + } + } + + function onClose() { + finish(new Error("stream unexpectedly closed")); + } + + var finished = false; + function finish(error, json) { + if (! finished) { + finished = true; + inputStream.removeListener("data", onData); + inputStream.removeListener("error", finish); + inputStream.removeListener("close", onClose); + callback(error, json, outputStream); + } + } + + inputStream.on("data", onData); + inputStream.on("error", finish); + inputStream.on("close", onClose); +} + +function getInfoFile(shellDir) { + return path.join(shellDir, "info.json"); +} + +function getHistoryFile(shellDir) { + return path.join(shellDir, "history"); +} + +// Shell commands need to be executed in a Fiber in case they call into +// code that yields. Using a Promise is an even better idea, since it runs +// its callbacks in Fibers drawn from a pool, so the Fibers are recycled. +var evalCommandPromise = Promise.resolve(); + +function evalCommand(command, context, filename, callback) { + var repl = this; + + function finish(error, result) { + if (error) { + if (repl._RecoverableError && + isRecoverableError(error, repl)) { + callback(new repl._RecoverableError(error)); + } else { + callback(error); + } + } else { + callback(null, result); + } + } + + if (Package.ecmascript) { + var noParens = stripParens(command); + if (noParens !== command) { + var classMatch = /^\s*class\s+(\w+)/.exec(noParens); + if (classMatch && classMatch[1] !== "extends") { + // If the command looks like a named ES2015 class, we remove the + // extra layer of parentheses added by the REPL so that the + // command will be evaluated as a class declaration rather than as + // a named class expression. Note that you can still type (class A + // {}) explicitly to evaluate a named class expression. The REPL + // code that calls evalCommand handles named function expressions + // similarly (first with and then without parentheses), but that + // code doesn't know about ES2015 classes, which is why we have to + // handle them here. + command = noParens; + } + } + + try { + command = Package.ecmascript.ECMAScript.compileForShell(command); + } catch (error) { + finish(error); + return; + } + } + + try { + var script = new vm.Script(command, { + filename: filename, + displayErrors: false + }); + } catch (parseError) { + finish(parseError); + return; + } + + evalCommandPromise.then(function () { + finish(null, script.runInThisContext()); + }).catch(finish); +} + +function stripParens(command) { + if (command.charAt(0) === "(" && + command.charAt(command.length - 1) === ")") { + return command.slice(1, command.length - 1); + } + return command; +} + +// The bailOnIllegalToken and isRecoverableError functions are taken from +// https://github.com/nodejs/node/blob/c9e670ea2a/lib/repl.js#L1227-L1253 +function bailOnIllegalToken(parser) { + return parser._literal === null && + ! parser.blockComment && + ! parser.regExpLiteral; +} + +// If the error is that we've unexpectedly ended the input, +// then let the user try to recover by adding more input. +function isRecoverableError(e, repl) { + if (e && e.name === 'SyntaxError') { + var message = e.message; + if (message === 'Unterminated template literal' || + message === 'Missing } in template expression') { + repl._inTemplateLiteral = true; + return true; + } + + if (message.startsWith('Unexpected end of input') || + message.startsWith('missing ) after argument list') || + message.startsWith('Unexpected token')) { + return true; + } + + if (message === 'Invalid or unexpected token') { + return ! bailOnIllegalToken(repl.lineParser); + } + } + + return false; +} diff --git a/packages/showdown/README.md b/packages/showdown/README.md index ec37b267c6..e2d39d3b2f 100644 --- a/packages/showdown/README.md +++ b/packages/showdown/README.md @@ -1 +1,5 @@ +# showdown +[Source code of released version](https://github.com/meteor/meteor/tree/master/packages/showdown) | [Source code of development version](https://github.com/meteor/meteor/tree/devel/packages/showdown) +*** + This is an internal Meteor package. \ No newline at end of file diff --git a/packages/spacebars-compiler/.gitignore b/packages/spacebars-compiler/.gitignore deleted file mode 100644 index 677a6fc263..0000000000 --- a/packages/spacebars-compiler/.gitignore +++ /dev/null @@ -1 +0,0 @@ -.build* diff --git a/packages/spacebars-compiler/README.md b/packages/spacebars-compiler/README.md deleted file mode 100644 index f0720673f6..0000000000 --- a/packages/spacebars-compiler/README.md +++ /dev/null @@ -1,15 +0,0 @@ -# spacebars-compiler - -The Spacebars compiler that is invoked at build time to compile -templates to JavaScript. - -While this code is not normally ever shipped to the client, it can be -used at runtime on the server or the client by using the -`SpacebarsCompiler` symbol from this package. - -The `spacebars` package, in contrast, contains the `Spacebars` symbol -and the Spacebars runtime, which is shipped to the client as part of -the app. - -Read more about Spacebars, Blaze, and Meteor templating on the Blaze -[project page](https://www.meteor.com/blaze). diff --git a/packages/spacebars-compiler/codegen.js b/packages/spacebars-compiler/codegen.js deleted file mode 100644 index 3147a4a18a..0000000000 --- a/packages/spacebars-compiler/codegen.js +++ /dev/null @@ -1,389 +0,0 @@ -// ============================================================ -// Code-generation of template tags - -// The `CodeGen` class currently has no instance state, but in theory -// it could be useful to track per-function state, like whether we -// need to emit `var self = this` or not. -var CodeGen = SpacebarsCompiler.CodeGen = function () {}; - -var builtInBlockHelpers = SpacebarsCompiler._builtInBlockHelpers = { - 'if': 'Blaze.If', - 'unless': 'Blaze.Unless', - 'with': 'Spacebars.With', - 'each': 'Blaze.Each', - 'let': 'Blaze.Let' -}; - - -// Mapping of "macros" which, when preceded by `Template.`, expand -// to special code rather than following the lookup rules for dotted -// symbols. -var builtInTemplateMacros = { - // `view` is a local variable defined in the generated render - // function for the template in which `Template.contentBlock` or - // `Template.elseBlock` is invoked. - 'contentBlock': 'view.templateContentBlock', - 'elseBlock': 'view.templateElseBlock', - - // Confusingly, this makes `{{> Template.dynamic}}` an alias - // for `{{> __dynamic}}`, where "__dynamic" is the template that - // implements the dynamic template feature. - 'dynamic': 'Template.__dynamic', - - 'subscriptionsReady': 'view.templateInstance().subscriptionsReady()' -}; - -var additionalReservedNames = ["body", "toString", "instance", "constructor", - "toString", "toLocaleString", "valueOf", "hasOwnProperty", "isPrototypeOf", - "propertyIsEnumerable", "__defineGetter__", "__lookupGetter__", - "__defineSetter__", "__lookupSetter__", "__proto__", "dynamic", - "registerHelper", "currentData", "parentData"]; - -// A "reserved name" can't be used as a