mirror of
https://github.com/meteor/meteor.git
synced 2026-05-02 03:01:46 -04:00
Merge commit 'cca623' into devel
This commit is contained in:
@@ -61,7 +61,7 @@ run_save_node_bin: &run_save_node_bin
|
||||
build_machine_environment: &build_machine_environment
|
||||
# Specify that we want an actual machine (ala Circle 1.0), not a Docker image.
|
||||
docker:
|
||||
- image: meteor/circleci
|
||||
- image: meteor/circleci:android-28-node-12
|
||||
environment:
|
||||
# This multiplier scales the waitSecs for selftests.
|
||||
TIMEOUT_SCALE_FACTOR: 8
|
||||
@@ -93,8 +93,8 @@ build_machine_environment: &build_machine_environment
|
||||
SELF_TEST_TOOL_NODE_FLAGS: " "
|
||||
|
||||
# Variables for load-balancing
|
||||
NUM_GROUPS: 11
|
||||
RUNNING_AVG_LENGTH: 5
|
||||
NUM_GROUPS: 12
|
||||
RUNNING_AVG_LENGTH: 6
|
||||
|
||||
jobs:
|
||||
Get Ready:
|
||||
@@ -128,8 +128,9 @@ jobs:
|
||||
- package-npm-deps-cache-group2-v3-
|
||||
- restore_cache:
|
||||
keys:
|
||||
- v2-other-deps-cache-{{ .Branch }}-{{ .Revision }}
|
||||
- v2-other-deps-cache-{{ .Branch }}-
|
||||
- v5-other-deps-cache-{{ .Branch }}-{{ checksum "meteor" }}-{{ .Revision }}
|
||||
- v5-other-deps-cache-{{ .Branch }}-{{ checksum "meteor" }}-
|
||||
- v5-other-deps-cache-{{ .Branch }}-
|
||||
- restore_cache:
|
||||
keys:
|
||||
- v1-test-groups-{{ .Branch }}
|
||||
@@ -147,6 +148,10 @@ jobs:
|
||||
name: Get Ready
|
||||
command: |
|
||||
eval $PRE_TEST_COMMANDS;
|
||||
pushd tools
|
||||
# Ensure that meteor/tools has no TypeScript errors.
|
||||
../meteor npx tsc --noEmit
|
||||
popd
|
||||
./meteor --get-ready
|
||||
# shouldn't take longer than 20 minutes
|
||||
no_output_timeout: 20m
|
||||
@@ -180,7 +185,6 @@ jobs:
|
||||
'add debugOnly and prodOnly packages' \
|
||||
--retries ${METEOR_SELF_TEST_RETRIES} \
|
||||
--headless \
|
||||
--phantom
|
||||
no_output_timeout: 20m
|
||||
- run:
|
||||
name: "Running self-test (Custom Warehouse Tests)"
|
||||
@@ -190,7 +194,6 @@ jobs:
|
||||
--retries ${METEOR_SELF_TEST_RETRIES} \
|
||||
--exclude "${SELF_TEST_EXCLUDE}" \
|
||||
--headless \
|
||||
--phantom \
|
||||
--with-tag "custom-warehouse"
|
||||
no_output_timeout: 20m
|
||||
- run:
|
||||
@@ -227,7 +230,6 @@ jobs:
|
||||
--retries ${METEOR_SELF_TEST_RETRIES} \
|
||||
--exclude "${SELF_TEST_EXCLUDE}" \
|
||||
--headless \
|
||||
--phantom \
|
||||
--junit ./tmp/results/junit/0.xml \
|
||||
--without-tag "custom-warehouse"
|
||||
no_output_timeout: 20m
|
||||
@@ -268,7 +270,6 @@ jobs:
|
||||
--retries ${METEOR_SELF_TEST_RETRIES} \
|
||||
--exclude "${SELF_TEST_EXCLUDE}" \
|
||||
--headless \
|
||||
--phantom \
|
||||
--junit ./tmp/results/junit/1.xml \
|
||||
--without-tag "custom-warehouse"
|
||||
no_output_timeout: 20m
|
||||
@@ -309,7 +310,6 @@ jobs:
|
||||
--retries ${METEOR_SELF_TEST_RETRIES} \
|
||||
--exclude "${SELF_TEST_EXCLUDE}" \
|
||||
--headless \
|
||||
--phantom \
|
||||
--junit ./tmp/results/junit/2.xml \
|
||||
--without-tag "custom-warehouse"
|
||||
no_output_timeout: 20m
|
||||
@@ -350,7 +350,6 @@ jobs:
|
||||
--retries ${METEOR_SELF_TEST_RETRIES} \
|
||||
--exclude "${SELF_TEST_EXCLUDE}" \
|
||||
--headless \
|
||||
--phantom \
|
||||
--junit ./tmp/results/junit/3.xml \
|
||||
--without-tag "custom-warehouse"
|
||||
no_output_timeout: 20m
|
||||
@@ -391,7 +390,6 @@ jobs:
|
||||
--retries ${METEOR_SELF_TEST_RETRIES} \
|
||||
--exclude "${SELF_TEST_EXCLUDE}" \
|
||||
--headless \
|
||||
--phantom \
|
||||
--junit ./tmp/results/junit/4.xml \
|
||||
--without-tag "custom-warehouse"
|
||||
no_output_timeout: 20m
|
||||
@@ -432,7 +430,6 @@ jobs:
|
||||
--retries ${METEOR_SELF_TEST_RETRIES} \
|
||||
--exclude "${SELF_TEST_EXCLUDE}" \
|
||||
--headless \
|
||||
--phantom \
|
||||
--junit ./tmp/results/junit/5.xml \
|
||||
--without-tag "custom-warehouse"
|
||||
no_output_timeout: 20m
|
||||
@@ -473,7 +470,6 @@ jobs:
|
||||
--retries ${METEOR_SELF_TEST_RETRIES} \
|
||||
--exclude "${SELF_TEST_EXCLUDE}" \
|
||||
--headless \
|
||||
--phantom \
|
||||
--junit ./tmp/results/junit/6.xml \
|
||||
--without-tag "custom-warehouse"
|
||||
no_output_timeout: 20m
|
||||
@@ -514,7 +510,6 @@ jobs:
|
||||
--retries ${METEOR_SELF_TEST_RETRIES} \
|
||||
--exclude "${SELF_TEST_EXCLUDE}" \
|
||||
--headless \
|
||||
--phantom \
|
||||
--junit ./tmp/results/junit/7.xml \
|
||||
--without-tag "custom-warehouse"
|
||||
no_output_timeout: 20m
|
||||
@@ -555,7 +550,6 @@ jobs:
|
||||
--retries ${METEOR_SELF_TEST_RETRIES} \
|
||||
--exclude "${SELF_TEST_EXCLUDE}" \
|
||||
--headless \
|
||||
--phantom \
|
||||
--junit ./tmp/results/junit/8.xml \
|
||||
--without-tag "custom-warehouse"
|
||||
no_output_timeout: 20m
|
||||
@@ -596,7 +590,6 @@ jobs:
|
||||
--retries ${METEOR_SELF_TEST_RETRIES} \
|
||||
--exclude "${SELF_TEST_EXCLUDE}" \
|
||||
--headless \
|
||||
--phantom \
|
||||
--junit ./tmp/results/junit/9.xml \
|
||||
--without-tag "custom-warehouse"
|
||||
no_output_timeout: 20m
|
||||
@@ -637,7 +630,6 @@ jobs:
|
||||
--retries ${METEOR_SELF_TEST_RETRIES} \
|
||||
--exclude "${SELF_TEST_EXCLUDE}" \
|
||||
--headless \
|
||||
--phantom \
|
||||
--junit ./tmp/results/junit/10.xml \
|
||||
--without-tag "custom-warehouse"
|
||||
no_output_timeout: 20m
|
||||
@@ -655,6 +647,47 @@ jobs:
|
||||
- store_artifacts:
|
||||
path: /tmp/memuse.txt
|
||||
|
||||
Test Group 11:
|
||||
<<: *build_machine_environment
|
||||
steps:
|
||||
- run:
|
||||
<<: *run_log_mem_use
|
||||
- run:
|
||||
<<: *run_env_change
|
||||
- attach_workspace:
|
||||
at: .
|
||||
- run:
|
||||
name: "Print environment"
|
||||
command: printenv
|
||||
- run:
|
||||
name: "Running self-test (Browserstack Test)"
|
||||
command: |
|
||||
TEST_GROUP='css hot code push|custom minifier - devel vs prod|versioning hot code push|javascript hot code push|add packages client archs';
|
||||
echo $TEST_GROUP;
|
||||
eval $PRE_TEST_COMMANDS;
|
||||
./meteor self-test \
|
||||
"$TEST_GROUP" \
|
||||
--browserstack \
|
||||
--retries ${METEOR_SELF_TEST_RETRIES} \
|
||||
--exclude "${SELF_TEST_EXCLUDE}" \
|
||||
--headless \
|
||||
--junit ./tmp/results/junit/11.xml \
|
||||
--without-tag "custom-warehouse"
|
||||
no_output_timeout: 40m
|
||||
- run:
|
||||
<<: *run_save_node_bin
|
||||
- store_test_results:
|
||||
path: ./tmp/results
|
||||
- persist_to_workspace:
|
||||
root: .
|
||||
paths: ./tmp/results/junit
|
||||
- store_artifacts:
|
||||
path: ./tmp/results
|
||||
- store_artifacts:
|
||||
path: /tmp/core_dumps
|
||||
- store_artifacts:
|
||||
path: /tmp/memuse.txt
|
||||
|
||||
# Test the JSDoc declarations which live within this codebase against the
|
||||
# Meteor Docs (https://github.com/meteor/docs) repository, where they'll
|
||||
# eventually be consumed. This test aims to provide an early warning of
|
||||
@@ -664,14 +697,14 @@ jobs:
|
||||
Docs:
|
||||
docker:
|
||||
# This Node version should match that in the meteor/docs CircleCI config.
|
||||
- image: circleci/node:8
|
||||
- image: meteor/circleci:android-28-node-12
|
||||
environment:
|
||||
CHECKOUT_METEOR_DOCS: /home/circleci/test_docs
|
||||
steps:
|
||||
- run:
|
||||
name: Cloning "meteor/docs" Repository's "master" branch
|
||||
name: Cloning "meteor/docs" Repository's "update-to-meteor-1.9" branch
|
||||
command: |
|
||||
git clone https://github.com/meteor/docs.git ${CHECKOUT_METEOR_DOCS}
|
||||
git clone --branch update-to-meteor-1.9 https://github.com/meteor/docs.git ${CHECKOUT_METEOR_DOCS}
|
||||
# The "docs" repository normally brings in the Meteor code as a Git
|
||||
# submodule checked out into the "code" directory. As the goal of this
|
||||
# test is to run it against the _current_ repository's code, we'll move
|
||||
@@ -764,7 +797,7 @@ jobs:
|
||||
- packages/fetch/.npm/package/node_modules
|
||||
- packages/non-core/mongo-decimal/.npm/package/node_modules
|
||||
- save_cache:
|
||||
key: v2-other-deps-cache-{{ .Branch }}-{{ .Revision }}
|
||||
key: v5-other-deps-cache-{{ .Branch }}-{{ checksum "meteor" }}-{{ .Revision }}
|
||||
paths:
|
||||
- ".babel-cache"
|
||||
- ".meteor"
|
||||
@@ -811,6 +844,9 @@ workflows:
|
||||
- Test Group 10:
|
||||
requires:
|
||||
- Get Ready
|
||||
- Test Group 11:
|
||||
requires:
|
||||
- Get Ready
|
||||
- Clean Up:
|
||||
requires:
|
||||
- Isolated Tests
|
||||
@@ -825,3 +861,4 @@ workflows:
|
||||
- Test Group 8
|
||||
- Test Group 9
|
||||
- Test Group 10
|
||||
- Test Group 11
|
||||
|
||||
@@ -27,19 +27,18 @@ tools/deploy.js
|
||||
tools/fiber-helpers.js
|
||||
tools/fs/files.js
|
||||
tools/fs/mini-files.js
|
||||
tools/func-utils.js
|
||||
tools/http-helpers.js
|
||||
tools/inspector.js
|
||||
tools/index.js
|
||||
tools/mongo-exit-codes.js
|
||||
tools/processes.js
|
||||
tools/progress.js
|
||||
tools/mongo-exit-codes.ts
|
||||
tools/processes.ts
|
||||
tools/progress.ts
|
||||
tools/project-context.js
|
||||
tools/runners/run-log.js
|
||||
tools/fs/safe-pathwatcher.js
|
||||
tools/selftest.js
|
||||
tools/service-connection.js
|
||||
tools/shell-client.js
|
||||
tools/shell-client.ts
|
||||
tools/stats.js
|
||||
tools/test-utils.js
|
||||
tools/upgraders.js
|
||||
|
||||
11
.github/ISSUE_TEMPLATE.md
vendored
11
.github/ISSUE_TEMPLATE.md
vendored
@@ -1,7 +1,6 @@
|
||||
<!--
|
||||
* ❓ Questions?
|
||||
Use Stack Overflow (http://stackoverflow.com/questions/tagged/meteor)
|
||||
or start a discussion on the Meteor forums (https://forums.meteor.com/).
|
||||
Start a discussion on the Meteor forums (https://forums.meteor.com/) or post a message on (Community Slack)[https://join.slack.com/t/meteor-community/shared_invite/enQtODA0NTU2Nzk5MTA3LWY5NGMxMWRjZDgzYWMyMTEyYTQ3MTcwZmU2YjM5MTY3MjJkZjQ0NWRjOGZlYmIxZjFlYTA5Mjg4OTk3ODRiOTc]
|
||||
* 💡 Feature requests?
|
||||
Visit the feature request repository (https://github.com/meteor/meteor-feature-requests).
|
||||
* ❗️ Bug?
|
||||
@@ -18,8 +17,9 @@ https://github.com/meteor/meteor/blob/devel/CONTRIBUTING.md#reporting-a-bug-in-m
|
||||
- [ ] The operating system you're running Meteor on.
|
||||
- [ ] The expected behavior.
|
||||
- [ ] The actual behavior.
|
||||
- [ ] A **simple** reproduction!
|
||||
(example: A GitHub repository that anyone can clone to observe the problem.)
|
||||
- [ ] A **simple** reproduction! (Must include the Github repository and steps to reproduce the issue on it.)
|
||||
|
||||
If you don't include a reproduction the issue is probably going to be closed.
|
||||
|
||||
### Independent packages
|
||||
|
||||
@@ -28,5 +28,6 @@ Please ensure your issue is opened in the appropriate repository:
|
||||
* Feature Requests: https://github.com/meteor/meteor-feature-requests
|
||||
* Blaze: https://github.com/meteor/blaze/
|
||||
* Docs: https://github.com/meteor/docs/
|
||||
* Guide: https://github.com/meteor/guide
|
||||
* Guide: https://github.com/meteor/guide/
|
||||
* Galaxy Guide: https://github.com/meteor/galaxy-docs/
|
||||
-->
|
||||
|
||||
2
.mailmap
2
.mailmap
@@ -13,7 +13,7 @@
|
||||
# A command-line way to get the GitHub username for an author:
|
||||
# scripts/admin/find-author-github.sh 'User Name <foo@bar.com>'
|
||||
# (Note that this script always outputs GITHUB so you should manually
|
||||
# check to see if they are an MDG employee!)
|
||||
# check to see if they are an Meteor Software employee!)
|
||||
|
||||
GITHUB: 0a- <a@0a.io>
|
||||
GITHUB: adnissen <andrew_nissen@yahoo.com>
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
language: node_js
|
||||
node_js:
|
||||
- "8.11.1"
|
||||
- "12.16.1"
|
||||
cache:
|
||||
directories:
|
||||
- ".meteor"
|
||||
- ".babel-cache"
|
||||
script: TEST_PACKAGES_EXCLUDE="less" phantom=false ./packages/test-in-console/run.sh
|
||||
script:
|
||||
- export TEST_PACKAGES_EXCLUDE="less"
|
||||
- export phantom=false
|
||||
- travis_retry ./packages/test-in-console/run.sh
|
||||
sudo: false
|
||||
env:
|
||||
- CXX=g++-4.8
|
||||
|
||||
120
CONTRIBUTING.md
120
CONTRIBUTING.md
@@ -17,75 +17,65 @@ There are many ways to contribute to the Meteor Project. Here’s a list of tech
|
||||
- [Reviewing pull requests](CONTRIBUTING.md#reviewer)
|
||||
- [Maintaining a community package](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/).
|
||||
There are also several ways to contribute to the Meteor Project outside of GitHub, like organizing or speaking at [Meetups](https://forums.meteor.com/c/meetups) and events and helping to moderate our [forums](https://forums.meteor.com/).
|
||||
|
||||
If you can think of any changes to the project, [documentation](https://github.com/meteor/docs), or [guide](https://github.com/meteor/guide) that would improve the contributor experience, let us know by opening an issue in the correct repository!
|
||||
|
||||
### Finding work
|
||||
|
||||
We curate specific issues that would make great pull requests for community contributors by applying the `pull-requests-encouraged` label ([bugs](https://github.com/meteor/meteor/issues?q=is%3Aopen+is%3Aissue+label%3Apull-requests-encouraged) / [feature requests](https://github.com/meteor/meteor-feature-requests/issues?q=is%3Aopen+is%3Aissue+label%3Apull-requests-encouraged)).
|
||||
Are you new here? Please check our issues `good-first-issue`: [core](https://github.com/meteor/meteor/labels/good%20first%20issue), [docs](https://github.com/meteor/docs/labels/good%20first%20issue), [guide](https://github.com/meteor/guide/labels/good%20first%20issue).
|
||||
|
||||
Issues which *also* have the `confirmed` label ([bugs](https://github.com/meteor/meteor/issues?q=is%3Aissue%20is%3Aopen%20label%3Apull-requests-encouraged%20label%3Aconfirmed) / [feature requests](https://github.com/meteor/meteor-feature-requests/issues?q=is%3Aissue%20is%3Aopen%20label%3Apull-requests-encouraged%20label%3Aconfirmed)) are considered to have their details clear enough to begin working on.
|
||||
We curate specific issues that would make great pull requests for community contributors by applying the `ready` label.
|
||||
|
||||
Any issue which does not have the `confirmed` label still requires discussion on implementation details but input and positive commentary is welcome! Any pull request opened on an issue which is not `confirmed` is still welcome, however the pull-request is more likely to be sent back for reworking than a `confirmed` issue. If in doubt about the best way to implement something, please create additional conversation on the issue.
|
||||
Any issue which does not have the `ready` label still requires discussion on implementation details but input and positive commentary is welcome! Any pull request opened on an issue which is not `confirmed` is still welcome, however the pull-request is more likely to be sent back for reworking than a `ready` issue.
|
||||
|
||||
Please note that `pull-requests-encouraged` issues with low activity will often be closed without being implemented. These issues are tagged with an additional [`not-implemented`](https://github.com/meteor/meteor/issues?utf8=✓&q=label%3Apull-requests-encouraged+label%3Anot-implemented) label, and can still be considered good candidates to work on. If you're interested in working on a closed and `not-implemented` issue, please let us know by posting on that issue.
|
||||
If in doubt about the best way to implement something, please create additional conversation on the issue. You can also reach one of the [core committers](https://github.com/meteor/meteor/blob/devel/CONTRIBUTING.md#core-committer) and they will help you to find something interesting to work on.
|
||||
|
||||
### 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.
|
||||
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.
|
||||
Issue Triagers are members of the community that help every week with Meteor’s open issues and bug reports.
|
||||
|
||||
Current Issue Triagers:
|
||||
- [@hwillson](https://github.com/hwillson)
|
||||
- [@laosb](https://github.com/laosb)
|
||||
- [@abernix](https://github.com/abernix)
|
||||
- [@lorensr](https://github.com/lorensr)
|
||||
- [meteor](https://github.com/meteor/meteor)
|
||||
- [@klaussner](https://github.com/klaussner)
|
||||
|
||||
- [docs](https://github.com/meteor/docs) / [guide](https://github.com/meteor/guide)
|
||||
- [@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.
|
||||
Reviwers are members of the community that help with Pull Requests reviews.
|
||||
|
||||
Current Reviewers:
|
||||
- [@hwillson](https://github.com/hwillson)
|
||||
- [@lorensr](https://github.com/lorensr)
|
||||
- [@abernix](https://github.com/abernix)
|
||||
- [meteor](https://github.com/meteor/meteor)
|
||||
- [@klaussner](https://github.com/klaussner)
|
||||
- [@zodern](https://github.com/zodern)
|
||||
- [@StorytellerCZ](https://github.com/StorytellerCZ)
|
||||
- [@sebakerckhof](https://github.com/sebakerckhof)
|
||||
- [@filipenevola](https://github.com/filipenevola)
|
||||
- [@renanccastro](https://github.com/renanccastro)
|
||||
|
||||
- [docs](https://github.com/meteor/docs) / [guide](https://github.com/meteor/guide)
|
||||
- [@lorensr](https://github.com/lorensr)
|
||||
- [@filipenevola](https://github.com/filipenevola)
|
||||
- [@renanccastro](https://github.com/renanccastro)
|
||||
|
||||
#### 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)
|
||||
The contributors with commit access to meteor/meteor are employees of Meteor Software Ltd or community members who have distinguished themselves in other contribution areas. If you want to become a core committer please start writing PRs.
|
||||
|
||||
Current Core Committers:
|
||||
- [@abernix](https://github.com/abernix)
|
||||
- [@glasser](https://github.com/glasser)
|
||||
- [@stubailo](https://github.com/stubailo)
|
||||
- [@filipenevola](https://github.com/filipenevola)
|
||||
- [@renanccastro](https://github.com/renanccastro)
|
||||
- [@denihs](https://github.com/denihs)
|
||||
|
||||
#### Documentation Maintainer
|
||||
#### Developer Evangelist
|
||||
|
||||
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:
|
||||
- [@abernix](https://github.com/abernix)
|
||||
- [@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 work-flow. 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)
|
||||
- [@filipenevola](https://github.com/filipenevola) (Feel free to reach him out on [Twitter](https://twitter.com/FilipeNevola))
|
||||
|
||||
### Tracking project work
|
||||
|
||||
@@ -104,14 +94,16 @@ isn't a security risk, please file a report in
|
||||
> will page the security team.
|
||||
|
||||
A Meteor app has many moving parts, and it's often difficult to
|
||||
reproduce a bug based on just a few lines of code. So your report
|
||||
should include a reproduction recipe. By making it as easy as possible
|
||||
reproduce a bug based on just a few lines of code. So your report
|
||||
should include a link to a repository with a reproduction. By making it as easy as possible
|
||||
for others to reproduce your bug, you make it easier for your bug to be
|
||||
fixed. **It's likely that without a reproduction, contributors won't look into fixing your issue and it will end up being closed.**
|
||||
fixed.
|
||||
|
||||
**A single code snippet is _not_ a reproduction recipe and neither is an entire application.**
|
||||
**It's likely that without a reproduction, contributors won't look into fixing your issue and it will end up being closed.**
|
||||
|
||||
A reproduction recipe works like this:
|
||||
**A single code snippet is _not_ a reproduction and neither is an entire application.**
|
||||
|
||||
A reproduction works like this:
|
||||
|
||||
* Create a new Meteor app that displays the bug with as little code as
|
||||
possible. Try to delete any code that is unrelated to the precise bug
|
||||
@@ -137,6 +129,8 @@ A reproduction recipe works like this:
|
||||
|
||||
* Mention what operating system you're using and what browser (if any).
|
||||
|
||||
If you can't provide a reproduction make this very clear in the issue and explain why that is the case.
|
||||
|
||||
If you want to submit a pull request that fixes your bug, that's even
|
||||
better. We love getting bugfix pull requests. Just make sure they're
|
||||
written with the [correct style](DEVELOPMENT.md#code-style) and *come with tests*. Read further down
|
||||
@@ -147,9 +141,7 @@ for more details on proposing changes to core code.
|
||||
Feature requests are tracked in the [meteor/meteor-feature-requests](https://github.com/meteor/meteor-feature-requests) repository, and include a label that corresponds to the Meteor subproject that they are a part of.
|
||||
|
||||
Meteor is a big project with [many sub-projects](https://github.com/meteor/meteor/tree/devel/packages).
|
||||
There aren't as many [core developers (we're hiring!)](https://www.meteor.io/jobs/)
|
||||
as there are sub-projects, so we're not able to work on every single sub-project every
|
||||
month. We use our [roadmap](Roadmap.md) to communicate the high-level features we're currently prioritizing.
|
||||
Community is welcome to help in all the sub-projects. We use our [roadmap](Roadmap.md) to communicate the high-level features we're currently prioritizing.
|
||||
|
||||
Every additional feature adds a maintenance cost in addition to its value. This
|
||||
cost starts with the work of writing the feature or reviewing a community pull
|
||||
@@ -168,9 +160,11 @@ Finally, you can show your support for (or against!) features by using [GitHub r
|
||||
|
||||
A great way to contribute to Meteor is by helping keep the issues in the repository clean and well organized. This process is called 'issue triage' and the steps are described [here](ISSUE_TRIAGE.md).
|
||||
|
||||
Learn how we use GitHub labels [here](LABELS.md)
|
||||
|
||||
## Documentation
|
||||
|
||||
If you'd like to contribute to Meteor's documentation, head over to https://github.com/meteor/docs and create issues or pull requests there.
|
||||
If you'd like to contribute to Meteor's documentation, head over to https://github.com/meteor/docs or https://github.com/meteor/guide and create issues or pull requests there.
|
||||
|
||||
## Blaze
|
||||
|
||||
@@ -181,7 +175,7 @@ Blaze lives in its [own repository](https://github.com/meteor/blaze/) with its o
|
||||
Eventually you may want to change something in a core Meteor package, or
|
||||
in the `meteor` command line tool. These changes have the highest
|
||||
standards for API design, for the names of symbols, for documentation,
|
||||
and for the code itself. Be prepared for a lot of work!
|
||||
and for the code itself.
|
||||
|
||||
It may take some study to get comfortable with Meteor's core architecture. Each core package is
|
||||
designed to stand separately. At the same time, all the parts of core fit together to make the
|
||||
@@ -220,22 +214,22 @@ For more information about how to work with Meteor core, take a look at the [Dev
|
||||
|
||||
### Proposing your change
|
||||
|
||||
You'll have the best chance of getting a change into core if you can build consensus in the community for it. Start by creating a well specified feature request as a Github issue, in the [meteor/meteor-feature-requests](https://github.com/meteor/meteor-feature-requests) repository.
|
||||
You'll have the best chance of getting a change into core if you can build consensus in the community for it or if it is listed in the [roadmap](https://github.com/meteor/meteor/blob/devel/Roadmap.md). Start by creating a well specified feature request as a Github issue, in the [meteor/meteor-feature-requests](https://github.com/meteor/meteor-feature-requests) repository.
|
||||
|
||||
Help drive discussion and advocate for your feature on the Github ticket (and perhaps the forums). The higher the demand for the feature and the greater the clarity of it's specification will determine the likelihood of a core contributor prioritizing your feature by flagging it with the `pull-requests-encouraged` label.
|
||||
Help drive discussion and advocate for your feature on the Github ticket (and perhaps the forums). The higher the demand for the feature and the greater the clarity of it's specification will determine the likelihood of a core contributor prioritizing your feature by flagging it with the `ready` label.
|
||||
|
||||
Split features up into smaller, logically separate chunks. It is unlikely that large and complicated PRs will be merged.
|
||||
|
||||
Once your feature has been labelled with `pull-requests-encouraged`, leave a comment letting people know you're working on it and you can begin work on the code.
|
||||
Once your feature has been labelled with `ready`, leave a comment letting people know you're working on it and you can begin work on the code. We have the label `in-development` to track the items in progress.
|
||||
|
||||
### Submitting pull requests
|
||||
|
||||
Once you've come up with a good design, go ahead and submit a pull request (PR). If your PR isn't against a bug with the `confirmed` label or a feature request with the `pull-requests-encouraged` label, don't expect your PR to be merged unless it's a trivial and obvious fix (e.g. documentation). When submitting a PR, please follow these guidelines:
|
||||
Once you've come up with a good design, go ahead and submit a pull request (PR). When submitting a PR, please follow these guidelines:
|
||||
|
||||
* Sign the [contributor's agreement](http://contribute.meteor.com/).
|
||||
* Sign the CLA (the bot will ask you do to this in the PR).
|
||||
|
||||
* Base all your work off of the **devel** branch. The **devel** branch
|
||||
is where active development happens. **We do not merge pull requests
|
||||
is where active development happens. **We do not merge pull requests
|
||||
directly into master.**
|
||||
|
||||
* Name your branch to match the feature/bug fix that you are
|
||||
@@ -249,12 +243,14 @@ Once you've come up with a good design, go ahead and submit a pull request (PR).
|
||||
[code contributions](DEVELOPMENT.md#code-style)
|
||||
and
|
||||
[commit messages](DEVELOPMENT.md#commit-messages)
|
||||
|
||||
* Bump the version of the changed package accordingly
|
||||
* If your changes are ok to be released without a whole new Meteor version bump just the patch, for example, 2.4.5 will become 2.4.6.
|
||||
* If your changes need a new Meteor version because they are affecting many parts or they depend on changes in the meteor-tool bump the minor, for example, 2.4.5 will become 2.5.0.
|
||||
* If your change is a major rewrite then bump the major, for example, 2.4.5 will become 3.0.0.
|
||||
* If you bump anything that is not the patch you will need to wait a new Meteor version to have your changes available. This is how Meteor core packages work.
|
||||
|
||||
* Be sure your author field in git is properly filled out with your full name
|
||||
and email address so we can credit you.
|
||||
|
||||
### Need help with your pull request?
|
||||
|
||||
If you need help with a pull request, you should start by asking questions in the issue which it pertains to. If you feel that your pull request is almost ready or needs feedback which can only be demonstrated with code, go ahead and open a pull-request with as much progress as possible. By including a "[Work in Progress]" note in the subject, project contributors will know you need help!
|
||||
|
||||
Submitting a pull request is no guarantee it will be accepted, but contributors will do their best to help move your pull request toward release.
|
||||
* You can submit PRs that are not ready yet, submit them as Draft on GitHub and explain what is left and also if you need help.
|
||||
|
||||
@@ -65,6 +65,7 @@ When `meteor` is run from a checkout, a `dev_bundle` is automatically downloaded
|
||||
* Node.js version
|
||||
* npm version
|
||||
* MongoDB version
|
||||
* TypeScript version
|
||||
* Packages [used by `meteor-tool`](scripts/dev-bundle-tool-package.js)
|
||||
* Packages [used by the server bundle](scripts/dev-bundle-server-package.js)
|
||||
|
||||
@@ -94,15 +95,15 @@ This will generate a new tarball (`dev_bundle_<Platform>_<arch>_<version>.tar.gz
|
||||
|
||||
### Submitting "Dev Bundle" Pull Requests
|
||||
|
||||
It's important to note that while `dev_bundle` pull requests are accepted/reviewed, a new `dev_bundle` can only be published to MDG's Meteor infrastructure by an MDG staff member. This means that the build tool and package tests of submitted `dev_bundle` pull requests will always initially fail (since the new `dev_bundle` hasn't yet been built/published by MDG, which means it can't be downloaded by Meteor's continuous integration environment).
|
||||
It's important to note that while `dev_bundle` pull requests are accepted/reviewed, a new `dev_bundle` can only be published to Meteor Software's Meteor infrastructure by an Meteor Software staff member. This means that the build tool and package tests of submitted `dev_bundle` pull requests will always initially fail (since the new `dev_bundle` hasn't yet been built/published by Meteor Software, which means it can't be downloaded by Meteor's continuous integration environment).
|
||||
|
||||
Pull requests that contain `dev_bundle` changes will be noted by repo collaborators, and a request to have a new `dev_bundle` built/published will be forwarded to MDG.
|
||||
Pull requests that contain `dev_bundle` changes will be noted by repo collaborators, and a request to have a new `dev_bundle` built/published will be forwarded to Meteor Software.
|
||||
|
||||
## Additional documentation
|
||||
|
||||
The Meteor core is best documented within the code itself, however, many components also have a `README.md` in their respective directories.
|
||||
|
||||
Some compartmentalized portions of Meteor are broken into packages ([see a list of packages](packages/)) and they almost all have a `README.md` within their directory. For example, [`ddp`](packages/ddp/README.md), [`ecmascript`](packages/ecmascript/README.md) and [`tinytest`](packages/tinytest/README.md).
|
||||
Some compartmentalized portions of Meteor are broken into packages ([see a list of packages](packages/)) and almost all of them have a `README.md` within their directory. For example, [`ddp`](packages/ddp/README.md), [`ecmascript`](packages/ecmascript/README.md) and [`tinytest`](packages/tinytest/README.md).
|
||||
|
||||
For the rest, try looking nearby for a `README.md`. For example, [`isobuild`](tools/isobuild/README.md) or [`cordova`](tools/cordova/README.md).
|
||||
|
||||
@@ -110,10 +111,10 @@ For the rest, try looking nearby for a `README.md`. For example, [`isobuild`](t
|
||||
|
||||
### Test against the local meteor copy
|
||||
|
||||
When running any of tests, be sure run them against the checked-out copy of Meteor instead of
|
||||
When running any tests, be sure to run them against the checked-out copy of Meteor instead of
|
||||
the globally-installed version. This means ensuring that the command is `path-to-meteor-checkout/meteor` and not just `meteor`.
|
||||
|
||||
This is important so that tests are run against the version in development and not the stable (installed) Meteor release.
|
||||
This is important so that tests are run against your local development version and not the stable (installed) Meteor release.
|
||||
|
||||
### Running tests on Meteor core
|
||||
|
||||
@@ -142,7 +143,7 @@ While TinyTest and the `test-packages` command can be used to test internal Mete
|
||||
|
||||
#### Listing available tests
|
||||
|
||||
To see a list of the tests which are included in the self-test system, list them with the `--list` option:
|
||||
To see a list of tests included in the self-test system, use the `--list` option:
|
||||
|
||||
./meteor self-test --list
|
||||
|
||||
@@ -162,6 +163,13 @@ In a similar way to the method of specifying which tests TO run, there is a way
|
||||
|
||||
Simply remove the `--list` flag to actually run the matching tests.
|
||||
|
||||
#### Avoiding retries
|
||||
|
||||
On CI we want to retry the tests to avoid false failures but in development can take some time if you retry every time a test is failing. So to avoid retries use:
|
||||
|
||||
./meteor self-test --retries 0
|
||||
|
||||
|
||||
#### More reading
|
||||
|
||||
For even more details on how to run Meteor Tool "self tests", please refer to the [Testing section of the Meteor Tool README](https://github.com/meteor/meteor/blob/master/tools/README.md#testing).
|
||||
@@ -182,7 +190,7 @@ Since Meteor is a free, open-source project, you can run tests in the context of
|
||||
|
||||
To enable CircleCI for your development:
|
||||
|
||||
1. Make sure you have an account with [CircleCI](https://circleci.com)
|
||||
0. Make sure you have an account with [CircleCI](https://circleci.com)
|
||||
0. Make sure you have [forked](https://help.github.com/articles/fork-a-repo/) [Meteor](https://github.com/meteor/meteor) into your own GitHub account.
|
||||
0. Go to the [Add Projects](https://circleci.com/add-projects) page on CircleCI.
|
||||
0. On the left, click on your GitHub username.
|
||||
@@ -201,7 +209,7 @@ To enable CircleCI for your development:
|
||||
|
||||
## Commit messages
|
||||
|
||||
Good commit messages are very important and you should make sure to explain what is changing and why. The commit message should include:
|
||||
Good commit messages are very important and you should make sure to explain what is changing and why. The commit message should include:
|
||||
|
||||
* A short and helpful commit title (maximum 80 characters).
|
||||
* A commit description which clearly explains the change if it's not super-obvious by the title. Some description always helps!
|
||||
|
||||
779
History.md
779
History.md
@@ -1,4 +1,744 @@
|
||||
## v.NEXT
|
||||
## vNEXT, unreleased
|
||||
|
||||
### Breaking changes
|
||||
|
||||
N/A
|
||||
|
||||
### Migration steps
|
||||
|
||||
N/A
|
||||
|
||||
### Changes
|
||||
|
||||
## v1.12, 2020-04-12
|
||||
|
||||
### Breaking changes
|
||||
|
||||
- When importing types, you might need to use the "type" qualifier, like so:
|
||||
```js
|
||||
import { Point } from 'react-easy-crop/types';
|
||||
```
|
||||
to
|
||||
```ts
|
||||
import type { Point } from 'react-easy-crop/types';
|
||||
```
|
||||
Because now emitDecoratorsMetadata is enabled.
|
||||
|
||||
- Refer to typescript breaking changes before migrating your existing project, from 3.7.6 to 4.1.2: https://github.com/Microsoft/TypeScript/wiki/Breaking-Changes
|
||||
|
||||
### Migration steps
|
||||
|
||||
N/A
|
||||
|
||||
### Changes
|
||||
|
||||
#### Highlights
|
||||
- TypeScript update from 3.7.6 to 4.1.2.
|
||||
- enables decorators and metadata reflection. Important: these are stage 2 features so be aware that breaking changes could be introduced before they reach stage 3.
|
||||
|
||||
#### Meteor Version Release
|
||||
* `meteor-tool@1.12`
|
||||
- updates TypeScript to 4.1.2. [#11225](https://github.com/meteor/meteor/pull/11225) and [#11255](https://github.com/meteor/meteor/pull/11255)
|
||||
- adds new options for `meteor list` command (TODO pending link to updated doc). [#11165](https://github.com/meteor/meteor/pull/11165)
|
||||
- supports Cordova add plugin command working again with plugin id or plugin name in the git URL as it was before Meteor 1.11. [#11202](https://github.com/meteor/meteor/pull/11202)
|
||||
- avoids MiTM by downloading through https. [#11188](https://github.com/meteor/meteor/pull/11188)
|
||||
|
||||
* `meteor-babel@7.10.7`
|
||||
- updates TypeScript to 4.1.2 and enables decorators and metadata reflection. [#11225](https://github.com/meteor/meteor/pull/11225) and [#11255](https://github.com/meteor/meteor/pull/11255)
|
||||
|
||||
* `minimongo@1.6.1`
|
||||
- fixes a null reference exception, if an array contains null values while compiling a fields projection. [#10499](https://github.com/meteor/meteor/pull/10499).
|
||||
|
||||
* `accounts-password@1.6.3`
|
||||
- adds a new function `createUserVerifyingEmail` (TODO pending link to updated doc). [#11080](https://github.com/meteor/meteor/pull/11080)
|
||||
- fixes a typo. [#11182](https://github.com/meteor/meteor/pull/11182)
|
||||
|
||||
* `browser-content-policy@1.1.1`
|
||||
- adds support to nonce
|
||||
```js
|
||||
BrowserPolicy.content.allowScriptOrigin(`nonce-${nonce}`);
|
||||
```
|
||||
|
||||
* `accounts-ui@1.3.2`
|
||||
- follow accounts-ui-unstyled release
|
||||
|
||||
* `accounts-ui-unstyled@1.4.3`
|
||||
- fixes the login form would send the server two login requests
|
||||
- fixes the "forgot password" form would not only send the email but also refresh the page
|
||||
|
||||
* `dynamic-import@0.5.4`
|
||||
- fixes prefetching errors. [#11209](https://github.com/meteor/meteor/pull/11209)
|
||||
- adds the option for dynamic-imports to fetch from the current origin instead of the absolute URL. [#11105](https://github.com/meteor/meteor/pull/11105)
|
||||
|
||||
* `mongo-decimal@0.1.2`
|
||||
- updates npm dependency `decimal.js` to v10.2.1
|
||||
|
||||
* `accounts-base@1.7.1`
|
||||
- adds the ability to define default user fields published on login. [#11118](https://github.com/meteor/meteor/pull/11118)
|
||||
|
||||
* `standard-minifier-css@1.7.0`
|
||||
- modernize and update dependencies. [#11196](https://github.com/meteor/meteor/pull/11196)
|
||||
|
||||
|
||||
#### Independent Releases
|
||||
* `facebook-oauth@1.7.3`
|
||||
- is now using Facebook GraphAPI v8. [#11160](https://github.com/meteor/meteor/pull/11160)
|
||||
|
||||
## v1.11.1, 2020-09-16
|
||||
|
||||
### Breaking changes
|
||||
|
||||
N/A
|
||||
|
||||
### Migration steps
|
||||
|
||||
N/A
|
||||
|
||||
### Changes
|
||||
|
||||
* `--apollo` skeleton was missing client cache setup [more](https://github.com/meteor/meteor/pull/11146)
|
||||
|
||||
* `--vue` skeleton was updated to use proper folder structure [more](https://github.com/meteor/meteor/pull/11174)
|
||||
|
||||
* All skeletons got their `npm` dependencies updated. [more](https://github.com/meteor/meteor/pull/11172)
|
||||
|
||||
* Node.js has been updated to version [12.18.4](https://nodejs.org/en/blog/release/v12.18.4/), this is a [security release](https://nodejs.org/en/blog/vulnerability/september-2020-security-releases/)
|
||||
|
||||
* Updated npm to version 6.14.8 [more](https://blog.npmjs.org/post/626732790304686080/release-6148)
|
||||
|
||||
* `npm-mongo` version 3.8.1 was published, updating `mongodb` to [3.6.2](https://github.com/mongodb/node-mongodb-native/releases/tag/v3.6.2) [more](https://github.com/advisories/GHSA-pp7h-53gx-mx7r)
|
||||
|
||||
* Updated PostCSS from 7.0.31 to 7.0.32 [more](https://github.com/meteor/meteor/issues/10682)
|
||||
|
||||
* Allow android-webview-video-poster [more](https://github.com/meteor/meteor/pull/11159)
|
||||
|
||||
## v1.11, 2020-08-18
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* `email` package dependencies have been update and package version has been bumped to 2.0.0
|
||||
There is a potential breaking change as the underlying package started to use `dns.resolve()`
|
||||
instead of `dns.lookup()` which might be breaking on some environments.
|
||||
See [nodemailer changelog](https://github.com/nodemailer/nodemailer/blob/master/CHANGELOG.md) for more information.
|
||||
|
||||
* (Added later) Cordova add plugin is not working with plugin name in the git URL when the plugin id was different than the name in the config.xml. Fixed on [#11202](https://github.com/meteor/meteor/pull/11202)
|
||||
|
||||
### Migration steps
|
||||
|
||||
N/A
|
||||
|
||||
### Changes
|
||||
|
||||
* `meteor create --apollo` is now available thanks to [@StorytellerCZ](https://github.com/StorytellerCZ). PR [#11119](https://github.com/meteor/meteor/pull/11119)
|
||||
|
||||
* `meteor create --vue` is now available thanks to [@chris-visser](https://github.com/chris-visser). PR [#11086](https://github.com/meteor/meteor/pull/11086)
|
||||
|
||||
* `--cache-build` option is now available on `meteor deploy` command and you can use it safely all the time if you are using a Git repository to run your deploy. This is helpful if your upload is failing then you can retry just the upload and also if you deploy the same bundle to multiple environments. [Read more](https://galaxy-guide.meteor.com/deploy-guide.html#cache-build).
|
||||
|
||||
* Multiple optimizations in build performance, many of them for Windows thanks to [@zodern](https://github.com/zodern). PRs [#10838](https://github.com/meteor/meteor/pull/10838), [#11114](https://github.com/meteor/meteor/pull/11114), [#11115](https://github.com/meteor/meteor/pull/11115), [#11102](https://github.com/meteor/meteor/pull/11102), [#10839](https://github.com/meteor/meteor/pull/10839)
|
||||
|
||||
* Fixes error when removing cordova plugin that depends on cli variables. PR [#10976](https://github.com/meteor/meteor/pull/11052)
|
||||
|
||||
* `email` package now exposes `hookSend` that runs before emails are send.
|
||||
|
||||
* Node.js has been updated to version
|
||||
[12.18.3](https://nodejs.org/en/blog/release/v12.18.3/)
|
||||
|
||||
* Updated npm to version 6.14.5
|
||||
|
||||
* `mongodb` driver npm dependency has been updated to 3.6.0
|
||||
|
||||
* The version of MongoDB used by Meteor in development has been updated
|
||||
from 4.2.5 to 4.2.8
|
||||
|
||||
## v1.10.2, 2020-04-21
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* The `babel-compiler` package, used by both `ecmascript` and
|
||||
`typescript`, no longer supports stripping [Flow](https://flow.org/)
|
||||
type annotations by default, which may be a breaking change if your
|
||||
application (or Meteor package) relied on Flow syntax.
|
||||
|
||||
### Migration steps
|
||||
|
||||
* If you still need Babel's Flow plugins, you can install them with npm
|
||||
and then enable them with a custom `.babelrc` file in your application's
|
||||
(or package's) root directory:
|
||||
```json
|
||||
{
|
||||
"plugins": [
|
||||
"@babel/plugin-syntax-flow",
|
||||
"@babel/plugin-transform-flow-strip-types"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Changes
|
||||
|
||||
* Adds support to override MongoDB options via Meteor settings. Code PR
|
||||
[#10976](https://github.com/meteor/meteor/pull/10976), Docs PR
|
||||
[#662](https://github.com/meteor/docs/pull/662)
|
||||
|
||||
* The `meteor-babel` npm package has been updated to version 7.9.0.
|
||||
|
||||
* The `typescript` npm package has been updated to version 3.8.3.
|
||||
|
||||
* To pass Node command line flags to the server node instance,
|
||||
now it is recommended to use `SERVER_NODE_OPTIONS` instead of `NODE_OPTIONS`.
|
||||
Since Meteor 0.5.3, Meteor allowed to pass node command line flags via the `NODE_OPTIONS`
|
||||
environment variable.
|
||||
However, since Node version 8 / Meteor 1.6 this has become a default node
|
||||
envar with the same behavior. The side effect is that this now also affects
|
||||
Meteor tool. The command line parameters could already be set separately
|
||||
via the `TOOL_NODE_FLAGS` envar. This is now also possible (again) for the server.
|
||||
|
||||
* The version of MongoDB used by Meteor in development has been updated from
|
||||
4.2.1 to 4.2.5.
|
||||
[PR #11020](https://github.com/meteor/meteor/pull/11020)
|
||||
|
||||
* The `url` package now provides an isomorphic implentation of the [WHATWG `url()`
|
||||
API](https://url.spec.whatwg.org/).
|
||||
While remaining backwards compatible, you can now also import `URL` and `URLSearchParams` from `meteor/url`.
|
||||
These will work for both modern and legacy browsers as well as node.
|
||||
|
||||
|
||||
## v1.10.1, 2020-03-12
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* Cordova has been updated from version 7 to 9. We recommend that you test
|
||||
your features that are taking advantage of Cordova plugins to be sure
|
||||
they are still working as expected.
|
||||
|
||||
* WKWebViewOnly is set by default now as true so if you are relying on
|
||||
UIWebView or plugins that are using UIWebView APIs you probably want to
|
||||
set it as false, you can do this by calling
|
||||
`App.setPreference('WKWebViewOnly', false);` in your mobile-config.js. But we
|
||||
don't recommend turning this into false because
|
||||
[Apple have said](https://developer.apple.com/news/?id=12232019b) they are
|
||||
going to reject apps using UIWebView.
|
||||
|
||||
* Because MongoDB since 3.4 no longer supports 32-bit Windows, Meteor 1.10 has
|
||||
also dropped support for 32-bit Windows. In other words, Meteor 1.10 supports
|
||||
64-bit Mac, Windows 64-bit, and Linux 64-bit.
|
||||
|
||||
### Migration Steps
|
||||
* If you get `Unexpected mongo exit code 62. Restarting.` when starting your local
|
||||
MongoDB, you can either reset your project (`meteor reset`)
|
||||
(if you don't care about your local data)
|
||||
or you will need to update the feature compatibility version of your local MongoDB:
|
||||
|
||||
1. Downgrade your app to earlier version of Meteor `meteor update --release 1.9.2`
|
||||
2. Start your application
|
||||
3. While your application is running open a new terminal window, navigate to the
|
||||
app directory and open `mongo` shell: `meteor mongo`
|
||||
4. Use: `db.adminCommand({ getParameter: 1, featureCompatibilityVersion: 1 })` to
|
||||
check the current feature compatibility.
|
||||
5. If the returned version is less than 4.0 update like this:
|
||||
`db.adminCommand({ setFeatureCompatibilityVersion: "4.2" })`
|
||||
6. You can now stop your app and update to Meteor 1.10.
|
||||
|
||||
For more information about this, check out [MongoDB documentation](https://docs.mongodb.com/manual/release-notes/4.2-upgrade-standalone/).
|
||||
|
||||
### Changes
|
||||
|
||||
* The version of MongoDB used by Meteor in development has been updated
|
||||
from 4.0.6 to 4.2.1, and the `mongodb` driver package has been updated
|
||||
from 3.2.7 to 3.5.4, thanks to [@klaussner](https://github.com/klaussner).
|
||||
[Feature #361](https://github.com/meteor/meteor-feature-requests/issues/361)
|
||||
[PR #10723](https://github.com/meteor/meteor/pull/10723)
|
||||
|
||||
* The `npm` command-line tool used by the `meteor npm` command (and by
|
||||
Meteor internally) has been updated to version 6.14.0, and our
|
||||
[fork](https://github.com/meteor/pacote/tree/v9.5.12-meteor) of its
|
||||
`pacote` dependency has been updated to version 9.5.12.
|
||||
|
||||
* Cordova was updated from version 7 to 9
|
||||
* cordova-lib from 7.1.0 to 9.0.1 [release notes](https://github.com/apache/cordova-lib/blob/master/RELEASENOTES.md)
|
||||
* cordova-common from 2.1.1 to 3.2.1 [release notes](https://github.com/apache/cordova-common/blob/master/RELEASENOTES.md)
|
||||
* cordova-android from 7.1.4 to 8.1.0 [release notes](https://github.com/apache/cordova-android/blob/master/RELEASENOTES.md)
|
||||
* cordova-ios from 4.5.5 to 5.1.1 [release notes](https://github.com/apache/cordova-ios/blob/master/RELEASENOTES.md)
|
||||
* cordova-plugin-wkwebview-engine from 1.1.4 to 1.2.1 [release notes](https://github.com/apache/cordova-plugin-wkwebview-engine/blob/master/RELEASENOTES.md#121-jul-20-2019)
|
||||
* cordova-plugin-whitelist from 1.3.3 to 1.3.4 [release notes](https://github.com/apache/cordova-plugin-whitelist/blob/master/RELEASENOTES.md#134-jun-19-2019)
|
||||
* cordova-plugin-splashscreen (included by mobile-experience > launch-screen)
|
||||
from 4.1.0 to 5.0.3 [release notes](https://github.com/apache/cordova-plugin-splashscreen/blob/master/RELEASENOTES.md#503-may-09-2019)
|
||||
* cordova-plugin-statusbar (included by mobile-experience > mobile-status-bar)
|
||||
from 2.3.0 to 2.4.3 [release notes](https://github.com/apache/cordova-plugin-statusbar/blob/master/RELEASENOTES.md#243-jun-19-2019)
|
||||
* On iOS WKWebViewOnly is set by default now as true.
|
||||
* On iOS the Swift version is now set by default to `5` this change can make
|
||||
your app to produce some warnings if your plugins are using old Swift code.
|
||||
You can override the Swift version using
|
||||
`App.setPreference('SwiftVersion', 4.2);` but we don't recommend that.
|
||||
|
||||
* New command to ensure that Cordova dependencies are installed. Usage:
|
||||
`meteor ensure-cordova-dependencies`. Meteor handles this automatically but in
|
||||
some cases, like running in a CI, is useful to install them in advance.
|
||||
|
||||
* You can now pass an `--exclude-archs` option to the `meteor run` and
|
||||
`meteor test` commands to temporarily disable building certain web
|
||||
architectures. For example, `meteor run --exclude-archs web.browser.legacy`.
|
||||
Multiple architectures should be separated by commas. This option can be
|
||||
used to improve (re)build times if you're not actively testing the
|
||||
excluded architectures during development.
|
||||
[Feature #333](https://github.com/meteor/meteor-feature-requests/issues/333),
|
||||
[PR #10824](https://github.com/meteor/meteor/pull/10824)
|
||||
|
||||
* `meteor create --react app` and `--typescript` now use `useTracker` hook instead of
|
||||
`withTracker` HOC, it also uses `function` components instead of `classes`.
|
||||
|
||||
## v1.9.3, 2020-03-09
|
||||
|
||||
### Breaking changes
|
||||
* The MongoDB `retryWrites` option now defaults to `true` (it previously defaulted to false). Users of database services that don't support retryWrites will experience a fatal error due to this.
|
||||
|
||||
### Migration Steps
|
||||
* If you get the error `MongoError: This MongoDB deployment does not support retryable writes. Please add retryWrites=false to your connection string.`, append `retryWrites=false` to your MongoDB connection string.
|
||||
|
||||
### Changes
|
||||
* `mongodb` driver package has been updated
|
||||
from 3.2.7 to 3.5.4 [#10961](https://github.com/meteor/meteor/pull/10961)
|
||||
|
||||
## v1.9.2, 2020-02-20
|
||||
|
||||
### Breaking changes
|
||||
N/A
|
||||
|
||||
### Migration Steps
|
||||
N/A
|
||||
|
||||
### Changes
|
||||
|
||||
* Node.js has been updated to version
|
||||
[12.16.1](https://nodejs.org/en/blog/release/v12.16.1/), fixing several unintended
|
||||
[regressions](https://github.com/nodejs/node/blob/master/doc/changelogs/CHANGELOG_V12.md#12.16.1)
|
||||
introduced in 12.16.0.
|
||||
|
||||
* The `meteor-babel` npm package has been updated to version 7.8.2.
|
||||
|
||||
* The `typescript` npm package has been updated to version 3.7.5.
|
||||
|
||||
## v1.9.1, 2020-02-18
|
||||
|
||||
### Breaking changes
|
||||
|
||||
N/A
|
||||
|
||||
### Migration Steps
|
||||
N/A
|
||||
|
||||
### Changes
|
||||
|
||||
* Node.js has been updated to version
|
||||
12.16.0 from 12.14.0, which includes
|
||||
security updates and small changes:
|
||||
* [12.16.0](https://nodejs.org/en/blog/release/v12.16.0/)
|
||||
* Updated V8 to [release v7.8](https://v8.dev/blog/v8-release-78) which includes improvements in performance, for example, object destructuring now is as fast as the equivalent variable assignment.
|
||||
* [12.15.0](https://nodejs.org/en/blog/release/v12.15.0/)
|
||||
|
||||
* `cursor.observeChanges` now accepts a second options argument.
|
||||
If your observer functions do not mutate the passed arguments, you can specify
|
||||
`{ nonMutatingCallbacks: true }`, which improves performance by reducing
|
||||
the amount of data copies.
|
||||
|
||||
## v1.9, 2020-01-09
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* Because Node.js 12 no longer supports 32-bit Linux, Meteor 1.9 has also
|
||||
dropped support for 32-bit Linux. In other words, Meteor 1.9 supports
|
||||
64-bit Mac, Windows, and Linux, as well as 32-bit Windows.
|
||||
|
||||
### Migration Steps
|
||||
N/A
|
||||
|
||||
### Changes
|
||||
|
||||
* Node.js has been updated to version
|
||||
[12.14.0](https://nodejs.org/en/blog/release/v12.14.0/), which includes
|
||||
several major Node.js versions since 8.17.0 (used by Meteor 1.8.3):
|
||||
* [12.0.0](https://nodejs.org/en/blog/release/v12.0.0/)
|
||||
* [11.0.0](https://nodejs.org/en/blog/release/v10.0.0/)
|
||||
* [10.0.0](https://nodejs.org/en/blog/release/v10.0.0/)
|
||||
* [9.0.0](https://nodejs.org/en/blog/release/v9.0.0/)
|
||||
|
||||
* The `fibers` npm package has been updated to version 4.0.3, which
|
||||
includes [changes](https://github.com/laverdet/node-fibers/pull/429)
|
||||
that may drastically reduce garbage collection pressure resulting from
|
||||
heavy `Fiber` usage.
|
||||
|
||||
* The `pathwatcher` npm package has been updated to use a fork of version
|
||||
8.0.2, with [PR #128](https://github.com/atom/node-pathwatcher/pull/128)
|
||||
applied.
|
||||
|
||||
* The `sqlite3` npm package has been updated to version 4.1.0.
|
||||
|
||||
* The `node-gyp` npm package has been updated to version 6.0.1, and
|
||||
`node-pre-gyp` has been updated to version 0.14.0.
|
||||
|
||||
* The feature that restarts the application up to two times if it crashes
|
||||
on startup has been removed.
|
||||
[Feature #335](https://github.com/meteor/meteor-feature-requests/issues/335)
|
||||
[PR #10345](https://github.com/meteor/meteor/pull/10345)
|
||||
|
||||
* Facebook OAuth has been updated to call v5 API endpoints. [PR #10738](https://github.com/meteor/meteor/pull/10738)
|
||||
|
||||
### Changes
|
||||
|
||||
* `Meteor.user()`, `Meteor.findUserByEmail()` and `Meteor.findUserByUserName()` can take a new
|
||||
`options` parameter which can be used to limit the returned fields. Useful for minimizing
|
||||
DB bandwidth on the server and avoiding unnecessary reactive UI updates on the client.
|
||||
[Issue #10469](https://github.com/meteor/meteor/issues/10469)
|
||||
|
||||
* `Accounts.config()` has a new option `defaultFieldSelector` which will apply to all
|
||||
`Meteor.user()` and `Meteor.findUserBy...()` functions without explicit field selectors, and
|
||||
also to all `onLogin`, `onLogout` and `onLoginFailure` callbacks. This is useful if you store
|
||||
large data on the user document (e.g. a growing list of transactions) which do no need to be
|
||||
retrieved from the DB whenever you or a package author call `Meteor.user()` without limiting the
|
||||
fields. [Issue #10469](https://github.com/meteor/meteor/issues/10469)
|
||||
|
||||
* Lots of internal calls to `Meteor.user()` without field specifiers in `accounts-base` and
|
||||
`accounts-password` packages have been optimized with explicit field selectors to only
|
||||
the fields needed by the functions they are in.
|
||||
[Issue #10469](https://github.com/meteor/meteor/issues/10469)
|
||||
|
||||
## v1.8.3, 2019-12-19
|
||||
|
||||
### Migration Steps
|
||||
|
||||
* If your application uses `blaze-html-templates`, the Meteor `jquery`
|
||||
package will be automatically installed in your `.meteor/packages` file
|
||||
when you update to Meteor 1.8.3. However, this new version of the Meteor
|
||||
`jquery` package no longer bundles its own copy of the `jquery` npm
|
||||
implementation, so you may need to install `jquery` from npm by running
|
||||
```sh
|
||||
meteor npm i jquery
|
||||
```
|
||||
in your application directory. Symptoms of not installing jquery include
|
||||
a blank browser window, with helpful error messages in the console.
|
||||
|
||||
### Changes
|
||||
|
||||
* Node has been updated to version
|
||||
[8.17.0](https://nodejs.org/en/blog/release/v8.17.0/).
|
||||
|
||||
* The `npm` npm package has been updated to version 6.13.4, and our
|
||||
[fork](https://github.com/meteor/pacote/tree/v9.5.11-meteor) of its
|
||||
`pacote` dependency has been updated to version 9.5.11, an important
|
||||
[security release](https://nodejs.org/en/blog/vulnerability/december-2019-security-releases/).
|
||||
|
||||
* Prior to Meteor 1.8.3, installing the `jquery` package from npm along
|
||||
with the Meteor `jquery` package could result in bundling jQuery twice.
|
||||
Thanks to [PR #10498](https://github.com/meteor/meteor/pull/10498), the
|
||||
Meteor `jquery` package will no longer provide its own copy of jQuery,
|
||||
but will simply display a warning in the console if the `jquery` npm
|
||||
package cannot be found in your `node_modules` directory. If you are
|
||||
using `blaze` in your application, updating to Meteor 1.8.3 will
|
||||
automatically add this new version of the Meteor `jquery` package to
|
||||
your application if you were not already using it (thanks to
|
||||
[PR #10801](https://github.com/meteor/meteor/pull/10801)), but you might
|
||||
need to run `meteor npm i jquery` manually, so that `blaze` can import
|
||||
`jquery` from your `node_modules` directory.
|
||||
|
||||
* The `meteor-babel` npm package has been updated to version 7.7.5.
|
||||
|
||||
* The `typescript` npm package has been updated to version 3.7.3.
|
||||
|
||||
## v1.8.2, 2019-11-14
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* Module-level variable declarations named `require` or `exports` are no
|
||||
longer automatically renamed, so they may collide with module function
|
||||
parameters of the same name, leading to errors like
|
||||
`Uncaught SyntaxError: Identifier 'exports' has already been declared`.
|
||||
See [this comment](https://github.com/meteor/meteor/pull/10522#issuecomment-535535056)
|
||||
by [@SimonSimCity](https://github.com/SimonSimCity).
|
||||
|
||||
* `Plugin.fs` methods are now always sync and no longer accept a callback.
|
||||
|
||||
### Migration Steps
|
||||
|
||||
* Be sure to update the `@babel/runtime` npm package to its latest version
|
||||
(currently 7.7.2):
|
||||
```sh
|
||||
meteor npm install @babel/runtime@latest
|
||||
```
|
||||
|
||||
* New Meteor applications now depend on `meteor-node-stubs@1.0.0`, so it
|
||||
may be a good idea to update to the same major version:
|
||||
```sh
|
||||
meteor npm install meteor-node-stubs@next
|
||||
```
|
||||
|
||||
* If you are the author of any Meteor packages, and you encounter errors
|
||||
when using those packages in a Meteor 1.8.2 application (for example,
|
||||
`module.watch` being undefined), we recommend that you bump the minor
|
||||
version of your package and republish it using Meteor 1.8.2, so
|
||||
Meteor 1.8.2 applications will automatically use the new version of the
|
||||
package, as compiled by Meteor 1.8.2:
|
||||
```sh
|
||||
cd path/to/your/package
|
||||
# Add api.versionsFrom("1.8.2") to Package.onUse in package.js...
|
||||
meteor --release 1.8.2 publish
|
||||
```
|
||||
This may not be necessary for all packages, especially those that have
|
||||
been recently republished using Meteor 1.8.1, or local packages in the
|
||||
`packages/` directory (which are always recompiled from source).
|
||||
However, republishing packages is a general solution to a wide variety
|
||||
of package versioning and compilation problems, and package authors can
|
||||
make their users' lives easier by handling these issues proactively.
|
||||
|
||||
### Changes
|
||||
|
||||
* Node has been updated to version
|
||||
[8.16.2](https://nodejs.org/en/blog/release/v8.16.2/).
|
||||
|
||||
* The `npm` npm package has been updated to version 6.13.0, and our
|
||||
[fork](https://github.com/meteor/pacote/tree/v9.5.9-meteor) of its
|
||||
`pacote` dependency has been updated to version 9.5.9.
|
||||
|
||||
* New Meteor applications now include an official `typescript` package,
|
||||
supporting TypeScript compilation of `.ts` and `.tsx` modules, which can
|
||||
be added to existing apps by running `meteor add typescript`.
|
||||
|
||||
* New TypeScript-based Meteor applications can be created by running
|
||||
```sh
|
||||
meteor create --typescript new-typescript-app
|
||||
```
|
||||
This app skeleton contains a recommended tsconfig.json file, and should
|
||||
serve as a reference for how to make TypeScript and Meteor work together
|
||||
(to the best of our current knowledge).
|
||||
[PR #10695](https://github.com/meteor/meteor/pull/10695)
|
||||
|
||||
* When bundling modern client code, the Meteor module system now prefers
|
||||
the `"module"` field in `package.json` (if defined) over the `"main"`
|
||||
field, which should unlock various `import`/`export`-based optimizations
|
||||
such as tree shaking in future versions of Meteor. As before, server
|
||||
code uses only the `"main"` field, like Node.js, and legacy client code
|
||||
prefers `"browser"`, `"main"`, and then `"module"`.
|
||||
[PR #10541](https://github.com/meteor/meteor/pull/10541),
|
||||
[PR #10765](https://github.com/meteor/meteor/pull/10765).
|
||||
|
||||
* ECMAScript module syntax (`import`, `export`, and dynamic `import()`) is
|
||||
now supported by default everywhere, including in modules imported from
|
||||
`node_modules`, thanks to the [Reify](https://github.com/benjamn/reify)
|
||||
compiler.
|
||||
|
||||
* If you need to import code from `node_modules` that uses modern syntax
|
||||
beyond module syntax, it is now possible to enable recompilation for
|
||||
specific npm packages using the `meteor.nodeModules.recompile` option in
|
||||
your application's `package.json` file.
|
||||
See [PR #10603](https://github.com/meteor/meteor/pull/10603) for further
|
||||
explanation.
|
||||
|
||||
* The Meteor build process is now able to detect whether files changed in
|
||||
development were actually used by the server bundle, so that a full
|
||||
server restart can be avoided when no files used by the server bundle
|
||||
have changed. Client-only refreshes are typically much faster than
|
||||
server restarts. Run `meteor add autoupdate` to enable client refreshes,
|
||||
if you are not already using the `autoupdate` package.
|
||||
[Issue #10449](https://github.com/meteor/meteor/issues/10449)
|
||||
[PR #10686](https://github.com/meteor/meteor/pull/10686)
|
||||
|
||||
* The `mongodb` npm package used by the `npm-mongo` Meteor package has
|
||||
been updated to version 3.2.7.
|
||||
|
||||
* The `meteor-babel` npm package has been updated to version 7.7.0,
|
||||
enabling compilation of the `meteor/tools` codebase with TypeScript
|
||||
(specifically, version 3.7.2 of the `typescript` npm package).
|
||||
|
||||
* The `reify` npm package has been updated to version 0.20.12.
|
||||
|
||||
* The `core-js` npm package used by `ecmascript-runtime-client` and
|
||||
`ecmascript-runtime-server` has been updated to version 3.2.1.
|
||||
|
||||
* The `terser` npm package used by `minifier-js` (and indirectly by
|
||||
`standard-minifier-js`) has been updated to version 4.3.1.
|
||||
|
||||
* The `node-gyp` npm package has been updated to version 5.0.1, and
|
||||
`node-pre-gyp` has been updated to 0.13.0.
|
||||
|
||||
* The `optimism` npm package has been updated to version 0.11.3, which
|
||||
enables caching of thrown exceptions as well as ordinary results, in
|
||||
addition to performance improvements.
|
||||
|
||||
* The `pathwatcher` npm package has been updated to version 8.1.0.
|
||||
|
||||
* The `underscore` npm package installed in the Meteor dev bundle (for use
|
||||
by the `meteor/tools` codebase) has been updated from version 1.5.2 to
|
||||
version 1.9.1, and `@types/underscore` has been installed for better
|
||||
TypeScript support.
|
||||
|
||||
* In addition to the `.js` and `.jsx` file extensions, the `ecmascript`
|
||||
compiler plugin now automatically handles JavaScript modules with the
|
||||
`.mjs` file extension.
|
||||
|
||||
* Add `--cordova-server-port` option to override local port where Cordova will
|
||||
serve static resources, which is useful when multiple Cordova apps are built
|
||||
from the same application source code, since by default the port is generated
|
||||
using the ID from the application's `.meteor/.id` file.
|
||||
|
||||
* The `--test-app-path <directory>` option for `meteor test-packages` and
|
||||
`meteor test` now accepts relative paths as well as absolute paths.
|
||||
|
||||
## v1.8.1, 2019-04-03
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* Although we are not aware of any specific backwards incompatibilities,
|
||||
the major upgrade of `cordova-android` from 6.4.0 to 7.1.4 likely
|
||||
deserves extra attention, if you use Cordova to build Android apps.
|
||||
|
||||
### Migration Steps
|
||||
N/A
|
||||
|
||||
### Changes
|
||||
|
||||
* Node has been updated from version 8.11.4 to version
|
||||
[8.15.1](https://nodejs.org/en/blog/release/v8.15.1/), an important
|
||||
[security release](https://nodejs.org/en/blog/vulnerability/february-2019-security-releases/),
|
||||
which includes the changes from four other minor releases:
|
||||
* [8.15.0](https://nodejs.org/en/blog/release/v8.15.0/)
|
||||
* [8.14.0](https://nodejs.org/en/blog/release/v8.14.0/), an important
|
||||
[security release](https://nodejs.org/en/blog/vulnerability/november-2018-security-releases/)
|
||||
* [8.12.0](https://nodejs.org/en/blog/release/v8.12.0/)
|
||||
* [8.13.0](https://nodejs.org/en/blog/release/v8.13.0/)
|
||||
|
||||
> Note: While Node 8.12.0 included changes that may improve the
|
||||
performance of Meteor apps, there have been reports of CPU usage spikes
|
||||
in production due to excessive garbage collection, so this version of
|
||||
Meteor should be considered experimental until those problems have been
|
||||
fixed. [Issue #10216](https://github.com/meteor/meteor/issues/10216)
|
||||
|
||||
* The `npm` tool has been upgraded to version
|
||||
[6.9.0](https://github.com/npm/cli/releases/tag/v6.9.0), and our
|
||||
[fork](https://github.com/meteor/pacote/tree/v9.5.0-meteor) of its
|
||||
`pacote` dependency has been updated to version 9.5.0.
|
||||
|
||||
* Mongo has been upgraded to version 4.0.6 for 64-bit systems (was 4.0.2),
|
||||
and 3.2.22 for 32-bit systems (was 3.2.19). The `mongodb` npm package
|
||||
used by `npm-mongo` has been updated to version 3.1.13 (was 3.1.6).
|
||||
|
||||
* The `fibers` npm package has been updated to version 3.1.1, a major
|
||||
update from version 2.0.0. Building this version of `fibers` requires a
|
||||
C++11 compiler, unlike previous versions. If you deploy your Meteor app
|
||||
manually (without using Galaxy), you may need to update the version of
|
||||
`g++` used when running `npm install` in the `bundle/programs/server`
|
||||
directory.
|
||||
|
||||
* The `meteor-babel` npm package has been updated to version 7.3.4.
|
||||
|
||||
* Cordova Hot Code Push mechanism is now switching versions explicitly with
|
||||
call to `WebAppLocalServer.switchToPendingVersion` instead of trying to
|
||||
switch every time a browser reload is detected. If you use any third
|
||||
party package or have your own HCP routines implemented be sure to call
|
||||
it before forcing a browser reload. If you use the automatic reload from
|
||||
the `Reload` meteor package you do not need to do anything.
|
||||
[cordova-plugin-meteor-webapp PR #62](https://github.com/meteor/cordova-plugin-meteor-webapp/pull/62)
|
||||
|
||||
* Multiple Cordova-related bugs have been fixed, including Xcode 10 build
|
||||
incompatibilities and hot code push errors due to duplicated
|
||||
images/assets. [PR #10339](https://github.com/meteor/meteor/pull/10339)
|
||||
|
||||
* The `cordova-android` and `cordova-ios` npm dependencies have been
|
||||
updated to 7.1.4 (from 6.4.0) and 4.5.5 (from 4.5.4), respectively.
|
||||
|
||||
* Build performance has improved (especially on Windows) thanks to
|
||||
additional caching implemented by [@zodern](https://github.com/zodern)
|
||||
in PRs [#10399](https://github.com/meteor/meteor/pull/10399),
|
||||
[#10452](https://github.com/meteor/meteor/pull/10452),
|
||||
[#10453](https://github.com/meteor/meteor/pull/10453), and
|
||||
[#10454](https://github.com/meteor/meteor/pull/10454).
|
||||
|
||||
* The `meteor mongo` command no longer uses the `--quiet` option, so the
|
||||
normal startup text will be displayed, albeit without the banner about
|
||||
Mongo's free monitoring service. See this
|
||||
[MongoDB Jira issue](https://jira.mongodb.org/browse/SERVER-38862)
|
||||
for more details.
|
||||
|
||||
* In Meteor packages, `client/` and `server/` directories no longer have
|
||||
any special meaning. In application code, `client/` directories are
|
||||
ignored during the server build, and `server/` directories are ignored
|
||||
during the client build, as before. This special behavior previously
|
||||
applied to packages as well, but has now been removed.
|
||||
[Issue #10393](https://github.com/meteor/meteor/issues/10393)
|
||||
[PR #10414](https://github.com/meteor/meteor/pull/10414)
|
||||
|
||||
* If your application is using Git for version control, the current Git
|
||||
commit hash will now be exposed via the `Meteor.gitCommitHash` property
|
||||
while the app is running (in both server and client code), and also via
|
||||
the `"gitCommitHash"` property in the `star.json` file located in the
|
||||
root directory of builds produced by `meteor build`, for consumption by
|
||||
deployment tools. If you are not using Git, neither property will be
|
||||
defined. [PR #10442](https://github.com/meteor/meteor/pull/10442)
|
||||
|
||||
* The Meteor Tool now uses a more reliable method (the MongoDB
|
||||
[`isMaster` command](https://docs.mongodb.com/manual/reference/command/isMaster/))
|
||||
to detect when the local development database has started and is ready to
|
||||
accept read and write operations.
|
||||
[PR #10500](https://github.com/meteor/meteor/pull/10500)
|
||||
|
||||
* Setting the `x-no-compression` request header will prevent the `webapp`
|
||||
package from compressing responses with `gzip`, which may be useful if
|
||||
your Meteor app is behind a proxy that compresses resources with another
|
||||
compression algorithm, such as [brotli](https://github.com/google/brotli).
|
||||
[PR #10378](https://github.com/meteor/meteor/pull/10378)
|
||||
|
||||
## v1.8.0.2, 2019-01-07
|
||||
|
||||
### Breaking changes
|
||||
N/A
|
||||
|
||||
### Migration steps
|
||||
N/A
|
||||
|
||||
### Changes
|
||||
|
||||
* The [React tutorial](https://www.meteor.com/tutorials/react/creating-an-app)
|
||||
has been updated to address a number of inaccuracies due to changes in
|
||||
recent Meteor releases that were not fully incorporated back into the
|
||||
tutorial. As a reminder, Meteor now supports a `meteor create --react`
|
||||
command that can be used to create a new React-based app quickly.
|
||||
|
||||
* Fixed a bug where modules named with `*.app-tests.js` (or `*.tests.js`)
|
||||
file extensions sometimes could not be imported by the
|
||||
`meteor.testModule` entry point when running the `meteor test` command
|
||||
(or `meteor test --full-app`).
|
||||
[PR #10402](https://github.com/meteor/meteor/pull/10402)
|
||||
|
||||
* The `meteor-promise` package has been updated to version 0.8.7, which
|
||||
includes a [commit](https://github.com/meteor/promise/commit/bbe4f0d20b70417950381aea112993c4cc8c1168)
|
||||
that should prevent memory leaks when excess fibers are discarded from
|
||||
the `Fiber` pool.
|
||||
|
||||
* The `meteor-babel` npm package has been updated to version 7.2.0,
|
||||
improving source maps for applications with custom `.babelrc` files.
|
||||
|
||||
## v1.8.0.1, 2018-11-23
|
||||
|
||||
### Breaking changes
|
||||
N/A
|
||||
|
||||
### Migration steps
|
||||
N/A
|
||||
|
||||
### Changes
|
||||
|
||||
* The `useragent` npm package used by `webapp` and (indirectly) by the
|
||||
`modern-browsers` package has been updated from 2.2.1 to 2.3.0. The
|
||||
`chromium` browser name has been aliased to use the same minimum modern
|
||||
version as `chrome`, and browser names are now processed
|
||||
case-insensitively by the `modern-browsers` package.
|
||||
[PR #10334](https://github.com/meteor/meteor/pull/10334)
|
||||
|
||||
* Fixed a module caching bug that allowed `findImportedModuleIdentifiers`
|
||||
to return the same identifiers for the modern and legacy versions of a
|
||||
given module, even if the set of imported modules is different (for
|
||||
example, because Babel injects fewer `@babel/runtime/...` imports into
|
||||
modern code). Now the caching is always based on the SHA-1 hash of the
|
||||
_generated_ code, rather than trusting the hash provided by compiler
|
||||
plugins. [PR #10330](https://github.com/meteor/meteor/pull/10330)
|
||||
|
||||
## v1.8, 2018-10-08
|
||||
|
||||
@@ -8,7 +748,6 @@ N/A
|
||||
### Migration Steps
|
||||
|
||||
* Update the `@babel/runtime` npm package to version 7.0.0 or later:
|
||||
|
||||
```sh
|
||||
meteor npm install @babel/runtime@latest
|
||||
```
|
||||
@@ -21,9 +760,13 @@ N/A
|
||||
planning a quick follow-up Meteor 1.8.1 release, which can be obtained
|
||||
by running the command
|
||||
```bash
|
||||
meteor update --release 1.8.1
|
||||
meteor update --release 1.8.1-beta.n
|
||||
```
|
||||
where `-beta.n` is the latest beta release according to the
|
||||
[releases](https://github.com/meteor/meteor/releases) page (currently
|
||||
`-beta.6`).
|
||||
[Issue #10216](https://github.com/meteor/meteor/issues/10216)
|
||||
[PR #10248](https://github.com/meteor/meteor/pull/10248)
|
||||
|
||||
* Meteor 1.7 introduced a new client bundle called `web.browser.legacy` in
|
||||
addition to the `web.browser` (modern) and `web.cordova` bundles.
|
||||
@@ -383,7 +1126,7 @@ N/A
|
||||
|
||||
### Migration Steps
|
||||
|
||||
* Update `@babel/runtime` (as well as other Babel-related packages) and
|
||||
* Update `@babel/runtime` (as well as other Babel-related packages) and
|
||||
`meteor-node-stubs` to their latest versions:
|
||||
```sh
|
||||
meteor npm install @babel/runtime@latest meteor-node-stubs@latest
|
||||
@@ -731,6 +1474,20 @@ N/A
|
||||
[Feature #24](https://github.com/meteor/meteor-feature-requests/issues/24)
|
||||
[PR #9657](https://github.com/meteor/meteor/pull/9657)
|
||||
|
||||
## v1.6.1.4, 2018-08-16
|
||||
|
||||
### Breaking changes
|
||||
N/A
|
||||
|
||||
### Migration Steps
|
||||
N/A
|
||||
|
||||
### Changes
|
||||
|
||||
* Node has been updated to version
|
||||
[8.11.4](https://nodejs.org/en/blog/release/v8.11.4/), an important
|
||||
[security release](https://nodejs.org/en/blog/vulnerability/august-2018-security-releases/).
|
||||
|
||||
## v1.6.1.3, 2018-06-16
|
||||
|
||||
### Breaking changes
|
||||
@@ -768,7 +1525,7 @@ N/A
|
||||
N/A
|
||||
|
||||
### Migration Steps
|
||||
* Update `@babel/runtime` npm package and any custom Babel plugin enabled in
|
||||
* Update `@babel/runtime` npm package and any custom Babel plugin enabled in
|
||||
`.babelrc`
|
||||
```sh
|
||||
meteor npm install @babel/runtime@latest
|
||||
@@ -803,7 +1560,7 @@ N/A
|
||||
values are not first converted to `null`, when inserted/updated. `undefined`
|
||||
values are now removed from all Mongo queries and insert/update documents.
|
||||
|
||||
This is a potentially breaking change if you are upgrading an existing app
|
||||
This is a potentially breaking change if you are upgrading an existing app
|
||||
from an earlier version of Meteor.
|
||||
|
||||
For example:
|
||||
@@ -813,11 +1570,11 @@ N/A
|
||||
userId: currentUser._id // undefined
|
||||
});
|
||||
```
|
||||
Assuming there are no documents in the `privateUserData` collection with
|
||||
`userId: null`, in Meteor versions prior to 1.6.1 this query will return
|
||||
zero documents. From Meteor 1.6.1 onwards, this query will now return
|
||||
_every_ document in the collection. It is highly recommend you review all
|
||||
your existing queries to ensure that any potential usage of `undefined` in
|
||||
Assuming there are no documents in the `privateUserData` collection with
|
||||
`userId: null`, in Meteor versions prior to 1.6.1 this query will return
|
||||
zero documents. From Meteor 1.6.1 onwards, this query will now return
|
||||
_every_ document in the collection. It is highly recommend you review all
|
||||
your existing queries to ensure that any potential usage of `undefined` in
|
||||
query objects won't lead to problems.
|
||||
|
||||
### Migration Steps
|
||||
|
||||
@@ -13,7 +13,7 @@ We would love to have more contributors who are willing to help out with triagin
|
||||
- [Impact](#impact)
|
||||
- [Issues ready to claim](#issues-ready-to-claim)
|
||||
|
||||
## Issue lifecycle
|
||||
## Lifecycle
|
||||
|
||||
All issues follow the flow outlined below. Your job as an issue maintainer is to work with the requester and others within the community towards the goal of having an issue either become 'claimable' or closed. Read on for more details on the process.
|
||||
|
||||
@@ -24,73 +24,34 @@ The first step is in determining whether the issue is a bug, help question or fe
|
||||
### Bugs
|
||||
|
||||
1. Duplicates should be closed and marked as such.
|
||||
2. Add the `bug` label and `Project:*` labels that apply (a best guess on the `Project:` is fine; sometimes it's hard to tell exactly which project the issue falls under).
|
||||
2. Add the `Type:Bug` label and `Project:*` labels that apply (a best guess on the `Project:` is fine; sometimes it's hard to tell exactly which project the issue falls under).
|
||||
3. Bugs should have a high-quality reproduction as described [here](CONTRIBUTING.md#reporting-bug). You may need to help the reporter reduce their bug to a minimal reproduction. Leave the issue open.
|
||||
4. A reproduction should be confirmed by at least one person other than the original reporter. Run the reproduction and validate that the bug exists; then make a note of your findings on the issue. If a reproduction is supplied but doesn't work, add the `can't-reproduce` label and make a comment describing what happened.
|
||||
5. Finally, once you've confirmed the reproduction add the `confirmed` label and [classify](#classification) the issue (removing the `can't-reproduce` label if it exists).
|
||||
|
||||
#### Bug issue lifespan
|
||||
|
||||
To help keep issues in this repository under control, and make sure the most important problems are visible to maintainers, unresolved issues (lacking recent activity) should be closed after a certain amount of time has elapsed.
|
||||
|
||||
##### Issues labelled with `pull-requests-encouraged`
|
||||
|
||||
- Open `pull-requests-encouraged` issues should be closed after one month of inactivity, unless someone has clearly identified that they are interested in working on the issue.
|
||||
- When closing, the `not-implemented` label should be added.
|
||||
- A message similar to the following should be included:
|
||||
|
||||
> While we think resolving this issue would be a great addition to the Meteor project, we're going to close it for now due to inactivity. If anyone comes across this issue in the future, and is interested in working on resolving it, please let us know by posting here and we'll consider re-opening this issue. Thanks!
|
||||
|
||||
##### Issues labelled with `bug` and `confirmed`
|
||||
|
||||
- Open `bug` + `confirmed` issues should be closed after two months of inactivity, unless someone has clearly identified that they are interested in working on the issue.
|
||||
- Triagers should do everything possible to help get `bug` + `confirmed` issues to `pull-requests-encouraged`. This means helping clearly identify where the problem is, pointing towards parts of the codebase that someone might want to look into, documenting what a potential solution looks like, etc.
|
||||
|
||||
##### All other issues
|
||||
|
||||
- All open issues that can’t be labelled as `bug` + `confirmed` and/or `pull-requests-encouraged`, should be closed after one month of inactivity.
|
||||
- Triagers should do everything possible to help get `bug` + `confirmed` issues to `pull-requests-encouraged`.
|
||||
4. A reproduction should be confirmed by at least one person other than the original reporter. Run the reproduction and validate that the bug exists; then make a note of your findings on the issue. If a reproduction is supplied but doesn't work, make a comment describing what happened.
|
||||
5. Finally, add a status label and also classification, read more about labels [here](./LABELS.md).
|
||||
|
||||
### Help questions
|
||||
|
||||
[Stack Overflow](http://stackoverflow.com/questions/tagged/meteor) and our [forums](https://forums.meteor.com/c/help) are the place to ask for help on using the framework. Close issues that are help requests and politely refer the author to the above locations.
|
||||
Our [forums](https://forums.meteor.com/c/help) or Community Slack are the place to ask for help on using the framework. Close issues that are help requests and politely refer the author to the above locations.
|
||||
|
||||
### Feature requests
|
||||
|
||||
1. For reasons described [here](CONTRIBUTING.md#feature-requests), we would prefer features to be built as separate packages. If the feature can clearly be built as a package, explain this to the requester and close the issue.
|
||||
> - If the feature could be built as a package and serves a particular need, encourage the user to contribute it themselves.
|
||||
>- If the underlying issue could be better solved by existing technology, encourage them to seek help in the [forums](https://forums.meteor.com/c/help) or on [Stack Overflow](http://stackoverflow.com/questions/tagged/meteor).
|
||||
>- If the underlying issue could be better solved by existing technology, encourage them to seek help in the [forums](https://forums.meteor.com/c/help).
|
||||
2. If you haven't closed the issue, add `Project:*` labels that apply (a best guess on the `Project:` is fine, sometimes it's hard to tell exactly which project the issue falls under).
|
||||
3. If it's not possible to build the feature as a package (as you identified in step 1), explore whether creating hooks in core would make it possible to do so. If it would, redefine the issue as a request to create those hooks.
|
||||
4. Work with the requester and others in the community to build a clear specification for the feature and update the issue description accordingly.
|
||||
5. Finally, add the `confirmed` label and [classify](#classification) the issue.
|
||||
5. Finally, add a status label and also classification, read more about labels [here](./LABELS.md).
|
||||
|
||||
Core contributors may add the `pull-requests-encouraged` label to feature requests. This indicates the feature is aligned with the project roadmap and a high-quality pull request will almost certainly be merged.
|
||||
### Is it ready?
|
||||
|
||||
<h2 id="classification">Classification</h2>
|
||||
|
||||
Assign a classification (via GH labels) that enables the community to determine how to prioritize which issues to work on. The classification is based on *Severity x Impact* .
|
||||
|
||||
### Severity
|
||||
_[Severity:has-workaround, Severity:production, Severity:blocks-development]_
|
||||
|
||||
- If there is a workaround, apply the `Severity:has-workaround` label.
|
||||
- If the issue affects production apps, apply the `Severity:production` label.
|
||||
- If the issue blocks development (e.g `meteor run` is broken), apply the `Severity:blocks-development` label.
|
||||
|
||||
### Impact
|
||||
_[Impact:few, Impact:some, Impact:most]_
|
||||
|
||||
This is a somewhat subjective label and is interpreted in conjunction with Github's upvotes. As a general guideline:
|
||||
|
||||
- `Impact:few` issues would go unnoticed by almost all users, apart from those using a very niche feature, or a feature in an unusual way.
|
||||
- `Impact:some` issues would impact users using a feature that is commonly but not universally used.
|
||||
- `Impact:most` issues would impact more or less every user of the framework.
|
||||
- If the desired fixed or solution is already clear add the label `ready`.
|
||||
- If discussions are still necessary add the label `in-discussion` and start the discussion.
|
||||
|
||||
## Issues ready to claim
|
||||
|
||||
This state indicates that bugs/feature requests have reached the level of quality
|
||||
required for a contributor to begin writing code against (you can easily filter for [bugs](https://github.com/meteor/meteor/labels/confirmed) or [feature requests](https://github.com/meteor/meteor-feature-requests/labels/confirmed) that are ready to claim, by using the `confirmed` label).
|
||||
required for a contributor to begin writing code against (you can easily filter for [Ready](https://github.com/meteor/meteor/labels/ready))
|
||||
|
||||
Although this should have already been done by this stage, ensure the issue is
|
||||
correctly labeled and the title/description have been updated to reflect an
|
||||
|
||||
63
LABELS.md
Normal file
63
LABELS.md
Normal file
@@ -0,0 +1,63 @@
|
||||
## Labels
|
||||
|
||||
Labels are used to organize our issues and PRs.
|
||||
|
||||
We should change the labels of issues and PRs when its status changes.
|
||||
|
||||
### Status Labels
|
||||
Labels to indicate the status of a specific issue or PR. These are the most important labels as they tell us in which stage a specific item is at the moment at a glance.
|
||||
|
||||
#### Stage 0
|
||||
- `triage`: Waiting triage
|
||||
- New items receive this label [automatically](https://probot.github.io/apps/triage-new-issues/)
|
||||
|
||||
#### Stage 1
|
||||
- `confirmed`: We want to fix or implement it
|
||||
- `not-ready`: Something is missing, we are not able to work on this issue yet
|
||||
- `in-discussion`: We are still discussing how to solve or implement it
|
||||
- `needs-reproduction`: We can't reproduce so it's blocked
|
||||
|
||||
#### Stage 2
|
||||
- `ready`: We've decided how to solve or implement it
|
||||
- `in-development`: We are already working on it
|
||||
|
||||
#### Stage 3
|
||||
- `pending-tests`: Tests are not passing, stuck or we need new tests
|
||||
- `waiting-feedback`: It's implemented but we need feedback that it is working as expected
|
||||
|
||||
### Classification Labels
|
||||
|
||||
Assign a classification (via GH labels) that enables the community to determine how to prioritize which issues to work on. The classification is based on *Severity x Impact* .
|
||||
|
||||
#### Severity
|
||||
_[Severity:has-workaround, Severity:production, Severity:blocks-development]_
|
||||
|
||||
- If there is a workaround, apply the `Severity:has-workaround` label.
|
||||
- If the issue affects production apps, apply the `Severity:production` label.
|
||||
- If the issue blocks development (e.g `meteor run` is broken), apply the `Severity:blocks-development` label.
|
||||
|
||||
#### Impact
|
||||
_[Impact:few, Impact:some, Impact:most]_
|
||||
|
||||
This is a somewhat subjective label and is interpreted in conjunction with Github's upvotes. As a general guideline:
|
||||
|
||||
- `Impact:few` issues would go unnoticed by almost all users, apart from those using a very niche feature, or a feature in an unusual way.
|
||||
- `Impact:some` issues would impact users using a feature that is commonly but not universally used.
|
||||
- `Impact:most` issues would impact more or less every user of the framework.
|
||||
|
||||
#### Type
|
||||
_[Type:Bug, Type:Feature]_
|
||||
|
||||
As a general guideline:
|
||||
|
||||
- `Type:Bug` a problem is happening because of an issue in Meteor code.
|
||||
- `Type:Feature` a new behavior or functionality is desired.
|
||||
|
||||
## Project Labels
|
||||
|
||||
They start with `Project:` and they are used to inform the parts of Meteor that are involved in this item.
|
||||
|
||||
## Special Labels
|
||||
|
||||
- `good-first-issue`: Used to indicate items friendly to beginners in Meteor
|
||||
- `hacktoberfest-accepted`: Used to indicate items accepted for [Hacktoberfest](https://hacktoberfest.digitalocean.com/hacktoberfest-update)
|
||||
2
LICENSE
2
LICENSE
@@ -1,6 +1,6 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2011 - 2018 Meteor Development Group, Inc.
|
||||
Copyright (c) 2011 - present Meteor Software Ltd.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -841,6 +841,13 @@ xmlbuilder: http://github.com/oozcitak/xmlbuilder-js
|
||||
Copyright (c) 2013 Ozgur Ozcitak
|
||||
|
||||
|
||||
----------
|
||||
xmlbuilder2: http://github.com/oozcitak/xmlbuilder2
|
||||
----------
|
||||
|
||||
Copyright (c) 2013 Ozgur Ozcitak
|
||||
|
||||
|
||||
----------
|
||||
xmldom: https://github.com/jindw/xmldom
|
||||
----------
|
||||
|
||||
14
README.md
14
README.md
@@ -2,7 +2,6 @@
|
||||
|
||||
[](https://travis-ci.org/meteor/meteor)
|
||||
[](https://circleci.com/gh/meteor/meteor/tree/devel)
|
||||
[](https://www.browserstack.com/automate/public-build/S2FjOVpYTlk1eHVxdkdrR24ra0JXaXBDaVA3WjJGejdkbThaWGRnWVJvWT0tLTlQMEdYM2NJbDJOYUd3RTc3RVVGQ2c9PQ==--9e2143718a0c216274cdacef7cd5a3d12950bcb8)
|
||||
|
||||
Meteor is an ultra-simple environment for building modern web
|
||||
applications.
|
||||
@@ -14,9 +13,11 @@ With Meteor you write apps:
|
||||
* using your choice of popular open-source libraries
|
||||
|
||||
Try a getting started tutorial:
|
||||
* [React](https://www.meteor.com/tutorials/react/creating-an-app)
|
||||
* [React](https://react-tutorial.meteor.com) - new
|
||||
* [Blaze](https://www.meteor.com/tutorials/blaze/creating-an-app)
|
||||
* [Angular](https://www.meteor.com/tutorials/angular/creating-an-app)
|
||||
* [Vue](https://www.meteor.com/tutorials/vue/creating-an-app)
|
||||
* [Svelte](https://www.meteor.com/tutorials/svelte/creating-an-app)
|
||||
|
||||
Next, read the [guide](https://guide.meteor.com) and the [documentation](https://docs.meteor.com/).
|
||||
|
||||
@@ -47,9 +48,11 @@ meteor
|
||||
|
||||
Building an application with Meteor?
|
||||
|
||||
* Announcement list: sign up at http://www.meteor.com/
|
||||
* Having problems? Ask for help at: http://stackoverflow.com/questions/tagged/meteor
|
||||
* Deploy on Galaxy hosting: https://www.meteor.com/hosting
|
||||
* Announcement list: sign up at https://www.meteor.com/
|
||||
* Discussion forums: https://forums.meteor.com/
|
||||
* Join the Meteor community Slack by clicking this [invite link](https://join.slack.com/t/meteor-community/shared_invite/enQtODA0NTU2Nzk5MTA3LWY5NGMxMWRjZDgzYWMyMTEyYTQ3MTcwZmU2YjM5MTY3MjJkZjQ0NWRjOGZlYmIxZjFlYTA5Mjg4OTk3ODRiOTc).
|
||||
|
||||
|
||||
Interested in helping or contributing to Meteor? These resources will help:
|
||||
|
||||
@@ -58,9 +61,6 @@ Interested in helping or contributing to Meteor? These resources will help:
|
||||
* [Feature requests](https://github.com/meteor/meteor-feature-requests/)
|
||||
* [Issue tracker](https://github.com/meteor/meteor/issues)
|
||||
|
||||
We are hiring! Visit [meteor.io/jobs](https://www.meteor.io/jobs/) to
|
||||
learn more about working full-time on the Meteor project.
|
||||
|
||||
## Uninstalling Meteor
|
||||
|
||||
Aside from a short launcher shell script, Meteor installs itself inside your
|
||||
|
||||
324
Roadmap.md
324
Roadmap.md
@@ -1,188 +1,250 @@
|
||||
|
||||
|
||||
# Meteor Roadmap
|
||||
|
||||
**Up to date as of October 9, 2018**
|
||||
**Up to date as of Aug 21, 2020**
|
||||
|
||||
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.
|
||||
This document describes the high-level features and actions for the Meteor project in the near- to medium-term future. This roadmap was built based on community feedback and to improve areas where Meteor is already strong. The description of many items include sentences and ideas from Meteor community members.
|
||||
|
||||
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.
|
||||
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 every quarter.
|
||||
|
||||
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.
|
||||
Contributors are encouraged to focus their efforts on work that aligns with the roadmap then we can work together in these areas.
|
||||
|
||||
## Out of the box support for advanced React features
|
||||
PRs to the roadmap are welcome. If you are willing to contribute please open a PR explaining your ideas and what you would be able to do yourself.
|
||||
|
||||
React is the most popular way to build UIs in JavaScript today, and a great companion to the rest of the features provided by Meteor. Meteor's zero-configuration environment provides a great opportunity to make features React apps depend on work out of the box. This includes features like:
|
||||
Ideally, every item in this roadmap should have at least two leaders, leaders are people that are interested in the item and would like to help. If you are interested please open a PR including yourself and describing how do you want to help.
|
||||
|
||||
1. Automatic selection of development vs. production build of React (completed)
|
||||
2. Abstraction for isomorphic server-side rendering ([ongoing](https://github.com/meteor/meteor/blob/devel/packages/server-render/README.md))
|
||||
3. Integration of [dynamic imports](https://blog.meteor.com/dynamic-imports-in-meteor-1-5-c6130419c3cd) with React SSR
|
||||
4. Full support for optimized CSS-in-JS features of libraries like [styled-components](https://www.styled-components.com/)
|
||||
## Core
|
||||
|
||||
We think Meteor has a clear set of benefits when compared to other popular React frameworks like Create React App and Next.js.
|
||||
### Tree Shaking
|
||||
- Leaders: [Renan Castro](https://github.com/renanccastro) / [Filipe Névola](https://github.com/filipenevola)
|
||||
- Status: In Progress
|
||||
- PRs: [#11107](https://github.com/meteor/meteor/pull/11107)
|
||||
|
||||
## Remove blockers to Meteor adoption
|
||||
Implement tree shaking / dead code elimination, which involves pruning the dependency tree while scanning imports in the `ImportScanner`. We believe it should be possible to treat values like `Meteor.isProduction` and `Meteor.isServer` as constants during this process, and eliminate those branches if their conditions are false (as in https://github.com/meteor/meteor/pull/10056).
|
||||
|
||||
### Make the `meteor` command-line tool installable from npm
|
||||
### Service worker build target
|
||||
- Leaders: <you?>
|
||||
- Status: -
|
||||
- PRs: -
|
||||
|
||||
Installing `meteor` from npm would enable developers to use it as build tool for npm-based projects, and would simplify the Meteor release process by getting rid of the "dev bundle" (essentially the npm dependencies of the command-line tool).
|
||||
A proper service worker build target. Regular Web Workers can be built from a function.toString() but service-workers require an actual server route.
|
||||
|
||||
The biggest blockers to this project are
|
||||
### Ultra-thin Meteor
|
||||
- Leaders: [Ruither Borba](https://github.com/delki8) / [Christian Klaussner](https://github.com/klaussner)
|
||||
- Status: In Progress
|
||||
- PRs: https://github.com/meteor/meteor/pull/11034 https://github.com/meteor/meteor/pull/11106
|
||||
|
||||
1. deciding whether/how to preserve Meteor release versions, and
|
||||
2. changing the API of the package server so that you don't have to download the entire package database locally.
|
||||
### Page load performance improvements
|
||||
- Leaders: [Seba Kerckhof](https://github.com/sebakerckhof)
|
||||
- Status: -
|
||||
- PRs: -
|
||||
|
||||
## Page load performance improvements
|
||||
Make sure we are not delivering any dependency that is not used ([Issue #10701](https://github.com/meteor/meteor/issues/10701), [Issue #10702](https://github.com/meteor/meteor/issues/10702), [Issue #10704](https://github.com/meteor/meteor/issues/10704), [PR #10792](https://github.com/meteor/meteor/pull/10792))
|
||||
|
||||
*Ongoing*
|
||||
### Improve Rebuild performance
|
||||
- Leaders: [zodern](https://github.com/zodern) / [Marcelo T Prado](https://github.com/MarceloPrado)
|
||||
- Status: -
|
||||
- PRs: -
|
||||
|
||||
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).
|
||||
|
||||
### Making large dependencies optional
|
||||
|
||||
Making it possible to remove (or dynamically load) large dependencies like `jquery`, `underscore`, and `minimongo` will have a significant impact on bundle sizes.
|
||||
|
||||
### More aggressive client caching
|
||||
|
||||
Using `Cache-Control: immutable` to cache the initial JavaScript bundle will reduce the amortized cost of downloading the initial JavaScript bundle in newer browsers.
|
||||
|
||||
Dynamic `import(...)` benefits dramatically from storing previously-received module versions in [IndexedDB](https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API), though it may be possible to use IndexedDB in a faster way.
|
||||
|
||||
### Better dead code elimination
|
||||
|
||||
Although Meteor minifies JavaScript in production, and modules that are never imported are not included in the client bundle, Meteor could do a better job of removing code within modules that will never be used, according to static analysis. The static syntax of ECMAScript 2015 `import` and `export` declarations should make this analysis easier.
|
||||
Explore ideas to improve rebuild time such as split main client bundle into several bundles, split the server bundle into several bundles, store less file content in memory, option to disabling the legacy build (at least in dev mode), etc
|
||||
|
||||
|
||||
## Full transition to npm
|
||||
### Hot Module Replacement
|
||||
- Leaders: [zodern](https://github.com/zodern)
|
||||
- Status: In Progress
|
||||
- PRs: https://github.com/meteor/meteor/pull/11117
|
||||
|
||||
*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.*
|
||||
HMR in Meteor, we already have a work in progress here, you can provide feedback already, check the PR.
|
||||
|
||||
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.
|
||||
### Transition as much as possible to NPM
|
||||
- Leaders: <you?>
|
||||
- Status: -
|
||||
- PRs:
|
||||
- PoC using base64 package as example [#10996](https://github.com/meteor/meteor/pull/10996)
|
||||
|
||||
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.
|
||||
Migrate packages that do not depend on Meteor exclusive features to NPM and we also continue to encourage new packages to be published as NPM packages when possible.
|
||||
|
||||
## Cordova
|
||||
### Cordova documentation
|
||||
- Leaders: [Filipe Névola](https://github.com/filipenevola)
|
||||
- Status: In Progress
|
||||
- PRs: https://github.com/meteor/meteor/pull/11072
|
||||
|
||||
## GraphQL Data: SQL, REST, performance
|
||||
Provide an example with mobile native configurations already in place such as `mobile-config.js`, sample assets, Fastlane scripts, etc. Also improve docs and guide ([Forums post](https://forums.meteor.com/t/lets-create-the-ultimate-cordova-hot-code-push-faq-doc/50500)).
|
||||
|
||||
*Status: In progress at [http://dev.apollodata.com/](http://dev.apollodata.com/)*
|
||||
## DB
|
||||
### Minimongo secondary index support
|
||||
- Leaders: [Brian Mulhall](https://github.com/BrianMulhall)
|
||||
- Status: -
|
||||
- PRs: -
|
||||
|
||||
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.
|
||||
Improve index support for Minimongo to enable better performance in the client for databases with thousands of documents. ([Issue #10703](https://github.com/meteor/meteor/issues/10703))
|
||||
|
||||
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.
|
||||
## Documentation
|
||||
### Step-by-step guide
|
||||
- Leaders: <you?>
|
||||
- Status: -
|
||||
- PRs: -
|
||||
|
||||
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.
|
||||
Provide a nice and friendly introduction for people that are learning Meteor.
|
||||
|
||||
# **Recently completed**
|
||||
### Update Angular Tutorial
|
||||
- Leaders: [Brian Mulhall](https://github.com/BrianMulhall)
|
||||
- Status: -
|
||||
- PRs: -
|
||||
|
||||
## Different JS bundles for modern versus legacy browsers
|
||||
Angular tutorial should reflect latest best practices for using Meteor and Angular together.
|
||||
|
||||
*Pull request: https://github.com/meteor/meteor/pull/9439*
|
||||
React tutorial should reflect latest best practices for using Meteor and React together.
|
||||
|
||||
Despite amazing progress in the latest versions of popular web browsers to support the vast majority of the ECMAScript specification, most web applications are still forced to compile their JavaScript for the oldest browsers they want to support, which means native support for the latest features is usually off-limits.
|
||||
### PWA documentation
|
||||
- Leaders: [Filipe Névola](https://github.com/filipenevola)
|
||||
- Status: In Progress
|
||||
- PRs: -
|
||||
|
||||
Starting in Meteor 1.6.2, Meteor will build two different client JS bundles, one for modern browsers (`web.browser`) and another for legacy browsers (`web.browser.legacy` and `web.cordova`), in addition to the server bundle which targets Node 8. Package authors can use these architectures to include files only in legacy browsers, or only in modern browsers, while also setting minimum browser versions for the native features they require. As of this writing, modern browsers are loosely defined as any browsers with native support for `async` functions and `await` expressions, which represents [more than 80% of web usage today](https://caniuse.com/#feat=async-functions).
|
||||
Provide an example with PWA configurations already in place such as `manifest`, service worker, Open Graph meta tags, etc. Also improve docs and guide.
|
||||
|
||||
While it was tempting to compile even more bundles for different categories of browser support, the reality of the web today is that most users have access to self-updating "evergreen" browsers, with nearly complete ECMAScript support, and the market share of evergreen browsers is only going to increase with time. For everyone else, Meteor will automatically provide the same level of compilation provided to everyone by Meteor 1.6.1 and before. It's also a lot easier to test two different bundles in representative browsers than it is to test a whole matrix of possibilities.
|
||||
### SSR documentation
|
||||
- Leaders: [Kevin Newman](https://github.com/CaptainN) / [Eric Burel](https://github.com/eric-burel)
|
||||
- Status: -
|
||||
- PRs: -
|
||||
|
||||
As a result of these changes, a typical new Meteor app will have a modern client JS bundle that is one quarter to one third the size of the legacy JS bundle. A new app created with `meteor create --release 1.6.2-beta.12 --minimal new-app` will have a modern JS bundle just 15KB in size (minified + gzip), for example.
|
||||
Provide a skeleton with SSR configurations already in place.
|
||||
|
||||
Relevant issues:
|
||||
- https://github.com/meteor/meteor-feature-requests/issues/174
|
||||
|
||||
### Tests documentation
|
||||
- Leaders: [Simon Schick](https://github.com/SimonSimCity) / [Florian Bienefelt](https://github.com/Floriferous)
|
||||
- Status: -
|
||||
- PRs: -
|
||||
|
||||
Provide samples on how to run tests in Meteor these samples should include unit tests and also cypress tests.
|
||||
|
||||
### Svelte
|
||||
- Leaders: [Brian Mulhall](https://github.com/BrianMulhall)
|
||||
- Status: In Progress
|
||||
- PRs: https://github.com/meteor/simple-todos-svelte
|
||||
|
||||
Tutorial is ready. We want a create command (--svelte) yet and more docs.
|
||||
|
||||
### Third-party tools with their own build steps
|
||||
|
||||
- Leaders: <you?>
|
||||
- Status: -
|
||||
- PRs: -
|
||||
|
||||
Remove limitations that prevent using third-party tools with their own build steps, such as Storybook or Jest.
|
||||
|
||||
Relevant discussions:
|
||||
- https://github.com/meteor/meteor/pull/10811#issuecomment-564726713
|
||||
- https://github.com/storybookjs/storybook/issues/5975
|
||||
|
||||
## Recently completed
|
||||
### Vue.js
|
||||
- Leaders: [Brian Mulhall](https://github.com/BrianMulhall)
|
||||
- Status: shipped in August 2020
|
||||
- PRs: https://github.com/meteor/simple-todos-vue
|
||||
|
||||
Tutorial is ready and create command meteor create --vue
|
||||
|
||||
### Apollo
|
||||
- Leaders: [Jan Dvorak](https://github.com/StorytellerCZ)
|
||||
- Status: shipped in August 2020
|
||||
- PRs: https://github.com/meteor/meteor/pull/11119
|
||||
|
||||
Apollo skeleton, meteor create --apollo
|
||||
|
||||
### Performance improvements on Windows
|
||||
- Leaders: [zodern](https://github.com/zodern)
|
||||
- Status: shipped in August 2020
|
||||
- PRs: https://github.com/meteor/meteor/pull/10838 https://github.com/meteor/meteor/pull/11114 https://github.com/meteor/meteor/pull/11115 https://github.com/meteor/meteor/pull/11102
|
||||
|
||||
Explore ideas to improve performance on Windows such as build in place.
|
||||
|
||||
### Update React Tutorial
|
||||
- Leaders: [Leonardo Venturini](https://github.com/leonardoventurini) / [Brian Mulhall](https://github.com/BrianMulhall)
|
||||
- Status: shipped in July 2020
|
||||
- PRs: https://github.com/meteor/simple-todos-react
|
||||
|
||||
### React Native
|
||||
- Leaders: [Nathaniel Dsouza](https://github.com/TheRealNate)
|
||||
- Status: shipped in June 2020
|
||||
- PRs: https://github.com/meteor/guide/pull/1041 https://github.com/meteor/guide/pull/1039 https://github.com/meteor/guide/pull/1035
|
||||
|
||||
Guide is ready ([check here](https://guide.meteor.com/react-native.html)).
|
||||
|
||||
### Update Blaze Tutorial
|
||||
- Leaders: [Jan Küster](https://github.com/jankapunkt), [Harry Adel](https://github.com/harryadelb), [Brian Mulhall](https://github.com/BrianMulhall)
|
||||
- Status: shipped in April 2020
|
||||
- PRs: https://github.com/meteor/tutorials/pull/200 https://github.com/meteor/tutorials/pull/199
|
||||
|
||||
Blaze tutorial should reflect latest best practices.
|
||||
|
||||
### Update MongoDB driver
|
||||
- Leaders: [Christian Klaussner](https://github.com/klaussner)
|
||||
- Status: shipped in Meteor 1.10.1
|
||||
- PRs: https://github.com/meteor/meteor/pull/10861 / https://github.com/meteor/meteor/pull/10723
|
||||
|
||||
Update to Mongodb driver from 3.2.7 to 3.5.4, this version is compatible with MongoDB 4.2.
|
||||
|
||||
### Update Cordova to 9
|
||||
- Leaders: [Filipe Névola](https://github.com/filipenevola) / [Renan Castro](https://github.com/renanccastro)
|
||||
- Status: shipped in Meteor 1.10.1
|
||||
- PRs: https://github.com/meteor/meteor/pull/10861 / https://github.com/meteor/meteor/pull/10810 / https://github.com/meteor/meteor/pull/10861
|
||||
|
||||
Update Cordoba lib and its dependencies to latest (version 9)
|
||||
|
||||
### Update to Node.js 12
|
||||
- Leaders: [Ben Newman](https://github.com/benjamn)
|
||||
- Status: shipped in Meteor 1.9.
|
||||
- PRs: https://github.com/meteor/meteor/pull/10527
|
||||
|
||||
Since Node.js 12 is scheduled to become the LTS version on October 1st, 2019, Meteor 1.9 will update the Node.js version used by Meteor from 8.16.1 (in Meteor 1.8.2) to 12.10.0 (the most recent current version).
|
||||
|
||||
### Different JS bundles for modern versus legacy browsers
|
||||
|
||||
- Status: shipped in Meteor 1.6.2.
|
||||
- PRs: https://github.com/meteor/meteor/pull/9439
|
||||
|
||||
### Eliminate the need for an `imports` directory
|
||||
|
||||
*Status: possible using `meteor.mainModule` in `package.json` in `1.6.2-beta.12`.*
|
||||
*Pull requests: https://github.com/meteor/meteor/pull/9690, https://github.com/meteor/meteor/pull/9714, https://github.com/meteor/meteor/pull/9715*
|
||||
- Status: shipped in Meteor 1.6.2.
|
||||
- PRs: https://github.com/meteor/meteor/pull/9690, https://github.com/meteor/meteor/pull/9714, https://github.com/meteor/meteor/pull/9715
|
||||
|
||||
When Meteor 1.3 first introduced a module system based on [CommonJS](http://wiki.commonjs.org/wiki/Modules/1.1) and [ECMAScript module syntax](2ality.com/2014/09/es6-modules-final.html), we had to provide a way for developers to migrate their apps from the old ways of loading code, whereby all files were evaluated eagerly during application startup.
|
||||
### Make Mongo more optional
|
||||
|
||||
The best solution at the time was to introduce a special `imports` directory to contain modules that should be loaded lazily (rather than eagerly), when first imported.
|
||||
- Status: shipped in Meteor 1.6.2.
|
||||
- PRs: https://github.com/meteor/meteor/pull/8999
|
||||
|
||||
Most other Node applications work this way by default: every module is lazy, and therefore must be imported by another module, and evaluation starts with one "entry point" module (typically specified by the `"main"` field in `package.json`).
|
||||
### Upgrade to Node 8
|
||||
|
||||
It should be possible for Meteor apps to opt into this behavior, and optionally get rid of their special `imports` directories. The mechanism for opting in will very likely involve putting something in your `package.json` file that specifies entry point modules for both client and server.
|
||||
- Status: shipped in Meteor 1.6.
|
||||
- PRs: https://github.com/meteor/meteor/pull/8728
|
||||
|
||||
## Make Mongo more optional
|
||||
### Upgrade to npm 5
|
||||
|
||||
*Pull request: https://github.com/meteor/meteor/pull/8999*
|
||||
- Status: shipped in Meteor 1.6
|
||||
|
||||
Meteor has depended on Mongo for as long as the Meteor project has existed. However, we care deeply about supporting other data storage systems (especially via [GraphQL](https://www.apollodata.com/)), and would like to make it possible to avoid using Mongo altogether.
|
||||
### Dynamic `import(...)`
|
||||
|
||||
Since Meteor 1.6.2-beta.9, `meteor create --minimal minimal-app` will create an app with very few packages, without any dependency on Mongo.
|
||||
- Status: shipped in Meteor 1.5
|
||||
|
||||
### Support the latest stable version of Node
|
||||
### Rebuild performance improvements
|
||||
|
||||
*Tracking pull request: https://github.com/meteor/meteor/pull/8728*
|
||||
- Status: shipped in Meteor 1.4.2
|
||||
|
||||
See [above](https://github.com/meteor/meteor/blob/devel/Roadmap.md#upgrade-to-node-8). Developers deserve to use the latest underlying technologies, and Meteor is uniquely able to smooth over any rough edges in early/experimental versions of technologies like Node. A number of developers are already using beta versions of Meteor 1.6 to deploy their apps, because the benefits outweigh the risks for them. Just as Meteor 1.5 climbed to more than 50% usage in less than two months, we expect Meteor 1.6 to become the most widely used version of Meteor soon after its release.
|
||||
### MongoDB updates
|
||||
|
||||
## Upgrade to Node 8
|
||||
- Status: shipped in Meteor 1.4
|
||||
|
||||
*Status: shipped in Meteor 1.6.*
|
||||
*Pull request: https://github.com/meteor/meteor/pull/8728*
|
||||
### Support for Node 4 and beyond
|
||||
|
||||
Upgrading Node will allow Meteor to take better advantage of native support for new ECMAScript features on the server, which should speed up build performance and also improve runtime performance, thanks to performance improvements in Node itself.
|
||||
- Status: shipped in Meteor 1.4
|
||||
|
||||
Perhaps even more importantly, newer versions of Node support a vastly improved debugging experience. Not only can you use native Chrome DevTools and many other debugging clients (WebStorm, VS Code, etc.) to debug your app (no more [`node-inspector`](https://www.npmjs.com/package/node-inspector)), but also the Node process runs at full speed while debugging, so you don't have to wait as long for problems to manifest themselves.
|
||||
### View Layer
|
||||
|
||||
## Upgrade to npm 5
|
||||
- Status: Blaze split into new repository and can be published independently as of 1.4.2
|
||||
|
||||
*Status: implemented since `1.6-beta.4`.*
|
||||
|
||||
It’s been an interesting year for npm clients. Once unrivaled as the tool of choice for installing npm packages, the `npm` command-line tool faced some serious competition starting last September from an innovative tool called `yarn`, which promised fast, reproducible installs based on `yarn.lock` files.
|
||||
|
||||
The popularity of `yarn` led Meteor to support `meteor yarn` in addition to `meteor npm` (though you had to `meteor npm install --global yarn` first, so `npm` still had an advantage). Our own Galaxy Server and Optics apps, which are built with Meteor, switched over to `yarn` soon after its release. The appeal was undeniable.
|
||||
|
||||
This competition was a good thing for JavaScript developers, first because yarn solved some long-standing problems with `npm`, and later because `npm@5` responded by shipping its own implementation of some similar features, with `package-lock.json` files and automatic addition of npm install-ed packages to `package.json`.
|
||||
|
||||
These improvements to `npm` will benefit all Meteor developers, even those who keep using `yarn`, because package dependencies specified with `Npm.depends` are automatically installed using `npm`, and `npm@5` performs those installations much faster and more reliably.
|
||||
|
||||
Meteor is careful to remain agnostic about how you choose to populate your `node_modules` directories, so we fully expect that `meteor npm` and `meteor yarn` will remain equally good alternatives for that purpose.
|
||||
|
||||
## Dynamic `import(...)`
|
||||
|
||||
*Status: Shipped in 1.5*
|
||||
|
||||
The banner feature of this effort will be first-class support for [dynamic `import(...)`](https://github.com/tc39/proposal-dynamic-import), which enables asynchronous module fetching.
|
||||
|
||||
Read the recent [blog post](https://blog.meteor.com/meteor-1-5-react-loadable-f029a320e59c) for an overview of how this system will work in Meteor 1.5.
|
||||
|
||||
Remaining work can be found [here](https://github.com/meteor/meteor/blob/release-1.5/packages/dynamic-import/TODO.md), though not all of those ideas will necessarily block the initial 1.5 release.
|
||||
|
||||
|
||||
## 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
|
||||
### Other
|
||||
|
||||
For more completed items, refer to the project history here: https://github.com/meteor/meteor/blob/devel/History.md
|
||||
|
||||
@@ -43,5 +43,5 @@ on_failure:
|
||||
|
||||
cache:
|
||||
- dev_bundle -> meteor
|
||||
- .babel-cache
|
||||
- .babel-cache -> meteor
|
||||
- .meteor
|
||||
|
||||
17
examples/.gitignore
vendored
17
examples/.gitignore
vendored
@@ -1,17 +0,0 @@
|
||||
# Each individual example should include 'local' in foo/.meteor/.gitignore.
|
||||
# However, we also include this top-level .gitignore so that the following
|
||||
# situation:
|
||||
# $ git checkout some-branch-with-a-new-example
|
||||
# $ (cd examples/unfinished/new-example; meteor)
|
||||
# $ git checkout devel
|
||||
# doesn't leave you with tons of files in
|
||||
# examples/unfinished/new-example/.meteor/local showing up in your 'git status'.
|
||||
|
||||
*/.meteor/local
|
||||
*/*/.meteor/local
|
||||
|
||||
# We don't want to check example project id files into the main meteor
|
||||
# repo... but we do want users who create apps from the examples to check
|
||||
# *their* project id files into their repos, so we put this ignore line
|
||||
# here rather than in the examples/FOO/.meteor/.gitignore.
|
||||
*/.meteor/.id
|
||||
@@ -1 +0,0 @@
|
||||
0.6.0
|
||||
@@ -1,8 +0,0 @@
|
||||
# Meteor packages used by this project, one per line.
|
||||
#
|
||||
# 'meteor add' and 'meteor remove' will edit this file for you,
|
||||
# but you can also edit it by hand.
|
||||
|
||||
standard-app-packages
|
||||
insecure
|
||||
preserve-inputs
|
||||
@@ -1 +0,0 @@
|
||||
0.7.0.1
|
||||
@@ -1,15 +0,0 @@
|
||||
<head>
|
||||
<title>Client Info</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
{{> info}}
|
||||
</body>
|
||||
|
||||
<template name="info">
|
||||
<h1>Client Info</h1>
|
||||
|
||||
<p>Information about this client available on the server:</p>
|
||||
|
||||
<pre>{{info}}</pre>
|
||||
</template>
|
||||
@@ -1,19 +0,0 @@
|
||||
if (Meteor.isServer) {
|
||||
Meteor.publish("clientInfo", function () {
|
||||
var self = this;
|
||||
self.added("clientInfo", "info", {
|
||||
clientAddress: self.connection.clientAddress,
|
||||
httpHeaders: self.connection.httpHeaders
|
||||
});
|
||||
self.ready();
|
||||
});
|
||||
}
|
||||
|
||||
if (Meteor.isClient) {
|
||||
Meteor.subscribe("clientInfo");
|
||||
var ClientInfo = new Mongo.Collection("clientInfo");
|
||||
|
||||
Template.info.info = function () {
|
||||
return EJSON.stringify(ClientInfo.findOne("info"), {indent: true});
|
||||
};
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
0.6.0
|
||||
@@ -1,6 +0,0 @@
|
||||
# Meteor packages used by this project, one per line.
|
||||
#
|
||||
# 'meteor add' and 'meteor remove' will edit this file for you,
|
||||
# but you can also edit it by hand.
|
||||
|
||||
standard-app-packages
|
||||
@@ -1,13 +0,0 @@
|
||||
# Defer in Inactive Tab
|
||||
|
||||
Tests that `Meteor.defer` works in an inactive tab in iOS Safari.
|
||||
|
||||
(`setTimeout` and `setInterval` events aren't delivered to inactive
|
||||
tabs in iOS Safari until they become active again).
|
||||
|
||||
Sadly we have to run the test manually because scripts aren't allowed
|
||||
to open windows themselves except in response to user events.
|
||||
|
||||
This test will not run on Chrome for iOS because the storage event is
|
||||
not implemented in that browser. Also doesn't attempt to run on
|
||||
versions of IE that don't support `window.addEventListener`.
|
||||
@@ -1,52 +0,0 @@
|
||||
<head>
|
||||
<title>defer in inactive tab</title>
|
||||
<meta name="viewport" content="width=device-width">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
{{> route}}
|
||||
</body>
|
||||
|
||||
<template name="route">
|
||||
{{#if isParent}}
|
||||
{{> parent}}
|
||||
{{else}}
|
||||
{{> child}}
|
||||
{{/if}}
|
||||
</template>
|
||||
|
||||
<template name="parent">
|
||||
<h1>Test Defer in Inactive Tab</h1>
|
||||
|
||||
<p>
|
||||
Step one: open second tab:
|
||||
<button id="openTab">Open Tab</button>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Step two: run test:
|
||||
<button id="runTest">Run Test</button>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
In a successful test the test status will immediately change to
|
||||
"test successful". (If you switch to the child tab yourself and
|
||||
that makes the test claim to be successful, that's actually an
|
||||
invalid test because you're letting the child tab become the
|
||||
active tab).
|
||||
</p>
|
||||
|
||||
<p style="padding: 1em; outline: 1px solid gray">
|
||||
Test status: <b>{{testStatus}}</b>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
After the test has run successfully you can close the child tab.
|
||||
</p>
|
||||
|
||||
</template>
|
||||
|
||||
<template name="child">
|
||||
<p>This is the child.</p>
|
||||
<p>Switch back to the first tab and run the test.</p>
|
||||
</template>
|
||||
@@ -1,57 +0,0 @@
|
||||
if (Meteor.isClient) {
|
||||
|
||||
var isParent = (window.location.pathname === '/');
|
||||
var isChild = ! isParent;
|
||||
|
||||
Template.route.isParent = function () {
|
||||
return isParent;
|
||||
};
|
||||
|
||||
Template.parent.testStatus = function () {
|
||||
return Session.get('testStatus');
|
||||
};
|
||||
|
||||
Template.parent.events({
|
||||
'click #openTab': function () {
|
||||
window.open('/child');
|
||||
},
|
||||
|
||||
'click #runTest': function () {
|
||||
if (localStorage.getItem('ping') === '!' ||
|
||||
localStorage.getItem('pong') === '!') {
|
||||
Session.set('testStatus', 'Test already run. Close the second tab (if open), refresh this page, and run again.');
|
||||
}
|
||||
else {
|
||||
localStorage.setItem('ping', '!');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (isParent) {
|
||||
Session.set('testStatus', '');
|
||||
|
||||
Meteor.startup(function () {
|
||||
localStorage.setItem('ping', null);
|
||||
localStorage.setItem('pong', null);
|
||||
});
|
||||
window.addEventListener('storage', function (event) {
|
||||
if (event.key === 'pong' && event.newValue === '!') {
|
||||
Session.set('testStatus', 'test successful');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (isChild) {
|
||||
window.addEventListener('storage', function (event) {
|
||||
if (event.key === 'ping' && event.newValue === '!') {
|
||||
// If we used setTimeout here in iOS Safari it wouldn't
|
||||
// work (unless we switched tabs) because setTimeout and
|
||||
// setInterval events don't fire in inactive tabs.
|
||||
Meteor.defer(function () {
|
||||
localStorage.setItem('pong', '!');
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
# Meteor packages used by this project, one per line.
|
||||
#
|
||||
# 'meteor add' and 'meteor remove' will edit this file for you,
|
||||
# but you can also edit it by hand.
|
||||
|
||||
standard-app-packages
|
||||
autopublish
|
||||
insecure
|
||||
preserve-inputs
|
||||
ui
|
||||
@@ -1,18 +0,0 @@
|
||||
/* CSS declarations go here */
|
||||
|
||||
|
||||
* { margin: 0; padding: 0 }
|
||||
|
||||
#grid td {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
vertical-align: middle;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.color0 { background: #eee; }
|
||||
.color1 { background: #d8f; }
|
||||
.color2 { background: #8c3; }
|
||||
.color3 { background: #39d; }
|
||||
.color4 { background: #d96; }
|
||||
.color5 { background: #a95; }
|
||||
@@ -1,7 +0,0 @@
|
||||
<head>
|
||||
<title>domrange-grid</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
</body>
|
||||
@@ -1,105 +0,0 @@
|
||||
if (Meteor.isClient) {
|
||||
Meteor.startup(function () {
|
||||
var N = 10;
|
||||
var numColors = 6;
|
||||
var colors = [];
|
||||
for(var z = 0; z < numColors; z++)
|
||||
colors[z] = z;
|
||||
|
||||
var guid = 1;
|
||||
|
||||
var table = $('<table id="grid"></table>');
|
||||
$(table).appendTo("body");
|
||||
var rows = [];
|
||||
var tableContent = new UI.DomRange;
|
||||
var makeCell = function (row) {
|
||||
var cells = row.cells;
|
||||
var tr = row.dom.elements()[0];
|
||||
var cell = {color: Random.choice(colors),
|
||||
guid: String(guid++)};
|
||||
cell.dom = new UI.DomRange(cell);
|
||||
cells.push(cell);
|
||||
cell.dom.add(cell.guid, $('<td class="color' +
|
||||
cell.color + '">' +
|
||||
cell.color + '</td>'));
|
||||
row.content.add(cell.guid, cell);
|
||||
};
|
||||
var makeRow = function () {
|
||||
var row = {cells: [], guid: String(guid++),
|
||||
content: new UI.DomRange};
|
||||
row.dom = new UI.DomRange(row);
|
||||
rows.push(row);
|
||||
tableContent.add(row.guid, row);
|
||||
var tr = $("<tr></tr>")[0];
|
||||
row.dom.add(tr);
|
||||
UI.DomRange.insert(row.content, tr);
|
||||
var cells = row.cells;
|
||||
for(var c = 0; c < N; c++)
|
||||
makeCell(row);
|
||||
};
|
||||
for (var r = 0; r < N; r++)
|
||||
makeRow();
|
||||
|
||||
UI.DomRange.insert(tableContent, table[0]);
|
||||
|
||||
$(document).on('keydown', function (evt) {
|
||||
var deltaN = 0;
|
||||
var deltaC = 0;
|
||||
if (evt.which === 38) {
|
||||
deltaN = 1; // up
|
||||
} else if (evt.which === 40) {
|
||||
deltaN = -1; // down
|
||||
} else if (evt.which === 37) {
|
||||
deltaC = -1; // left
|
||||
} else if (evt.which === 39) {
|
||||
deltaC = 1; // right
|
||||
} else if (evt.which === 32) {
|
||||
// spacebar
|
||||
var row0 = rows.shift();
|
||||
rows.push(row0);
|
||||
tableContent.moveBefore(row0.guid, null);
|
||||
for (var i = 0; i < rows.length; i++) {
|
||||
var row = rows[i];
|
||||
var cell0 = row.cells.shift();
|
||||
row.cells.push(cell0);
|
||||
row.content.moveBefore(cell0.guid, null);
|
||||
}
|
||||
}
|
||||
|
||||
if (deltaN === 1) {
|
||||
N += 1;
|
||||
for (var i = 0; i < N - 1; i++)
|
||||
// lengthen old rows
|
||||
makeCell(rows[i]);
|
||||
makeRow();
|
||||
} else if (deltaN === -1) {
|
||||
if (N === 0)
|
||||
return;
|
||||
N -= 1;
|
||||
tableContent.remove(rows[N].guid);
|
||||
rows.length = N;
|
||||
for (var i = 0; i < N; i++) {
|
||||
var row = rows[i];
|
||||
row.content.remove(row.cells[N].guid);
|
||||
rows[i].cells.length = N;
|
||||
}
|
||||
}
|
||||
|
||||
if (deltaC) {
|
||||
for (var r = 0; r < N; r++) {
|
||||
var row = rows[r];
|
||||
for (var c = 0; c < N; c++) {
|
||||
var cell = row.cells[c];
|
||||
var td = cell.dom.elements()[0];
|
||||
var color =
|
||||
(cell.color =
|
||||
(cell.color + deltaC + numColors)
|
||||
% numColors);
|
||||
td.innerHTML = color;
|
||||
td.className = 'color' + color;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
# Meteor packages used by this project, one per line.
|
||||
#
|
||||
# 'meteor add' and 'meteor remove' will edit this file for you,
|
||||
# but you can also edit it by hand.
|
||||
|
||||
preserve-inputs
|
||||
accounts-google
|
||||
standard-app-packages
|
||||
@@ -1,32 +0,0 @@
|
||||
|
||||
* { padding: 0; margin: 0; }
|
||||
|
||||
#main {
|
||||
margin: 50px;
|
||||
text-align: center;
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
.msgDiv {
|
||||
margin: 30px;
|
||||
}
|
||||
|
||||
a {
|
||||
padding: 5px;
|
||||
background: #ddd;
|
||||
}
|
||||
|
||||
#readme {
|
||||
margin: 20px;
|
||||
border: 1px solid #ccc;
|
||||
width: 500px;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 16px;
|
||||
}
|
||||
|
||||
ul {
|
||||
margin: 16px;
|
||||
padding-left: 30px;
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
<head>
|
||||
<title>login-demo</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="main">
|
||||
{{> main}}
|
||||
</div>
|
||||
<div id="readme">
|
||||
<p>This is a minimal app where you need to log in to see the database. There's also a loading screen while logging in and until the initial data is loaded.</p>
|
||||
<p>There are three top-level screens corresponding to the three possible states of the app:</p>
|
||||
<ul>
|
||||
<li>Logging in / Loading — when <code>{{loggingIn}}</code> is true</li>
|
||||
<li>Logged in — when there is a <code>{{currentUser}}</code></li>
|
||||
<li>Logged out — otherwise</li>
|
||||
</ul>
|
||||
<p>If you reload the page while logged in, you'll start in the "logging in" state and see the "Loading..." message until the data loads. Because logging in doesn't complete until all subscriptions have been rerun and finished loading, and the app only serves data when you're logged in, the "logging in" state encompasses loading the initial data for all subscriptions and is the only loading screen we need.</p>
|
||||
<p>To configure this app for Google auth, the easiest way is to add the <code>accounts-ui</code> package, add <code>{{> loginButtons}}</code> to the end of the body, and use the configuration wizard.</p>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
<template name="main">
|
||||
{{#if loggingIn}}
|
||||
<div class="loading">Loading...</div>
|
||||
{{else}}
|
||||
{{#if currentUser}}
|
||||
<div class="msgDiv">
|
||||
Signed in as: {{currentUser.services.google.email}}
|
||||
</div>
|
||||
<a href="#" id="logout">Sign out</a>
|
||||
{{else}}
|
||||
<a href="#" id="login">Sign In With Google</a>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
<div class="msgDiv">
|
||||
Client can see {{numGizmos}} gizmos.
|
||||
</div>
|
||||
</template>
|
||||
@@ -1,57 +0,0 @@
|
||||
Gizmos = new Mongo.Collection("gizmos");
|
||||
|
||||
if (Meteor.isClient) {
|
||||
|
||||
var allGizmos = Meteor.subscribe("allGizmos");
|
||||
|
||||
Template.main.numGizmos = function () {
|
||||
return Gizmos.find().count();
|
||||
};
|
||||
|
||||
Template.main.events({
|
||||
'click #login': function (evt) {
|
||||
Meteor.loginWithGoogle(function (err) {
|
||||
if (err)
|
||||
Meteor._debug(err);
|
||||
});
|
||||
evt.preventDefault();
|
||||
},
|
||||
'click #logout': function (evt) {
|
||||
Meteor.logout(function (err) {
|
||||
if (err)
|
||||
Meteor._debug(err);
|
||||
});
|
||||
evt.preventDefault();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (Meteor.isServer) {
|
||||
|
||||
Meteor.startup(function () {
|
||||
// populate the Gizmos collection if it's empty on startup
|
||||
if (Gizmos.find().count() === 0) {
|
||||
for (var i = 0; i < 1000; i++)
|
||||
Gizmos.insert({ name: "Gizmo " + i });
|
||||
}
|
||||
});
|
||||
|
||||
Meteor.publish("allGizmos", function () {
|
||||
// Only publish the Gizmos if user is logged in.
|
||||
var user = this.userId && Meteor.users.findOne(this.userId);
|
||||
if (user) {
|
||||
// potentially put other conditions on user here...
|
||||
return Gizmos.find({});
|
||||
}
|
||||
return [];
|
||||
});
|
||||
|
||||
Meteor.publish(null, function () {
|
||||
// If logged in, autopublish the current user's Google email
|
||||
// to the client (which isn't published by default).
|
||||
return this.userId &&
|
||||
Meteor.users.find(this.userId,
|
||||
{fields: {'services.google.email': 1}});
|
||||
});
|
||||
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
# This file contains information which helps Meteor properly upgrade your
|
||||
# app when you run 'meteor update'. You should check it into version control
|
||||
# with your project.
|
||||
|
||||
notices-for-0.9.0
|
||||
notices-for-0.9.1
|
||||
0.9.4-platform-file
|
||||
@@ -1,14 +0,0 @@
|
||||
# Meteor packages used by this project, one per line.
|
||||
#
|
||||
# 'meteor add' and 'meteor remove' will edit this file for you,
|
||||
# but you can also edit it by hand.
|
||||
|
||||
standard-app-packages
|
||||
accounts-ui
|
||||
accounts-password
|
||||
d3
|
||||
bootstrap
|
||||
email
|
||||
accounts-facebook
|
||||
accounts-twitter
|
||||
audit-argument-checks
|
||||
@@ -1 +0,0 @@
|
||||
METEOR@0.9.4
|
||||
@@ -1,72 +0,0 @@
|
||||
accounts-base@1.1.2
|
||||
accounts-facebook@1.0.2
|
||||
accounts-oauth@1.1.2
|
||||
accounts-password@1.0.3
|
||||
accounts-twitter@1.0.2
|
||||
accounts-ui-unstyled@1.1.3
|
||||
accounts-ui@1.1.2
|
||||
application-configuration@1.0.3
|
||||
audit-argument-checks@1.0.1
|
||||
autoupdate@1.1.2
|
||||
base64@1.0.1
|
||||
binary-heap@1.0.1
|
||||
blaze-tools@1.0.1
|
||||
blaze@2.0.2
|
||||
boilerplate-generator@1.0.1
|
||||
bootstrap@1.0.1
|
||||
callback-hook@1.0.1
|
||||
check@1.0.2
|
||||
ctl-helper@1.0.4
|
||||
ctl@1.0.2
|
||||
d3@1.0.0
|
||||
ddp@1.0.10
|
||||
deps@1.0.5
|
||||
ejson@1.0.4
|
||||
email@1.0.4
|
||||
facebook@1.1.1
|
||||
fastclick@1.0.1
|
||||
follower-livedata@1.0.2
|
||||
geojson-utils@1.0.1
|
||||
html-tools@1.0.2
|
||||
htmljs@1.0.2
|
||||
http@1.0.7
|
||||
id-map@1.0.1
|
||||
jquery@1.0.1
|
||||
json@1.0.1
|
||||
less@1.0.10
|
||||
livedata@1.0.11
|
||||
localstorage@1.0.1
|
||||
logging@1.0.4
|
||||
meteor-platform@1.1.2
|
||||
meteor@1.1.2
|
||||
minifiers@1.1.1
|
||||
minimongo@1.0.4
|
||||
mobile-status-bar@1.0.1
|
||||
mongo@1.0.7
|
||||
npm-bcrypt@0.7.7
|
||||
oauth1@1.1.1
|
||||
oauth2@1.1.1
|
||||
oauth@1.1.1
|
||||
observe-sequence@1.0.3
|
||||
ordered-dict@1.0.1
|
||||
random@1.0.1
|
||||
reactive-dict@1.0.4
|
||||
reactive-var@1.0.3
|
||||
reload@1.1.1
|
||||
retry@1.0.1
|
||||
routepolicy@1.0.2
|
||||
service-configuration@1.0.2
|
||||
session@1.0.3
|
||||
sha@1.0.1
|
||||
spacebars-compiler@1.0.3
|
||||
spacebars@1.0.3
|
||||
srp@1.0.1
|
||||
standard-app-packages@1.0.3
|
||||
templating@1.0.8
|
||||
tracker@1.0.3
|
||||
twitter@1.1.1
|
||||
ui@1.0.4
|
||||
underscore@1.0.1
|
||||
url@1.0.1
|
||||
webapp-hashing@1.0.1
|
||||
webapp@1.1.3
|
||||
@@ -1,285 +0,0 @@
|
||||
// All Tomorrow's Parties -- client
|
||||
|
||||
Meteor.subscribe("directory");
|
||||
Meteor.subscribe("parties");
|
||||
|
||||
// If no party selected, or if the selected party was deleted, select one.
|
||||
Meteor.startup(function () {
|
||||
Tracker.autorun(function () {
|
||||
var selected = Session.get("selected");
|
||||
if (! selected || ! Parties.findOne(selected)) {
|
||||
var party = Parties.findOne();
|
||||
if (party)
|
||||
Session.set("selected", party._id);
|
||||
else
|
||||
Session.set("selected", null);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Party details sidebar
|
||||
|
||||
Template.details.helpers({
|
||||
party: function () {
|
||||
return Parties.findOne(Session.get("selected"));
|
||||
},
|
||||
anyParties: function () {
|
||||
return Parties.find().count() > 0;
|
||||
},
|
||||
creatorName: function () {
|
||||
var owner = Meteor.users.findOne(this.owner);
|
||||
if (owner._id === Meteor.userId())
|
||||
return "me";
|
||||
return displayName(owner);
|
||||
},
|
||||
canRemove: function () {
|
||||
return this.owner === Meteor.userId() && attending(this) === 0;
|
||||
},
|
||||
maybeChosen: function (what) {
|
||||
var myRsvp = _.find(this.rsvps, function (r) {
|
||||
return r.user === Meteor.userId();
|
||||
}) || {};
|
||||
|
||||
return what == myRsvp.rsvp ? "chosen btn-inverse" : "";
|
||||
}
|
||||
});
|
||||
|
||||
Template.details.events({
|
||||
'click .rsvp_yes': function () {
|
||||
Meteor.call("rsvp", Session.get("selected"), "yes");
|
||||
return false;
|
||||
},
|
||||
'click .rsvp_maybe': function () {
|
||||
Meteor.call("rsvp", Session.get("selected"), "maybe");
|
||||
return false;
|
||||
},
|
||||
'click .rsvp_no': function () {
|
||||
Meteor.call("rsvp", Session.get("selected"), "no");
|
||||
return false;
|
||||
},
|
||||
'click .invite': function () {
|
||||
openInviteDialog();
|
||||
return false;
|
||||
},
|
||||
'click .remove': function () {
|
||||
Parties.remove(this._id);
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Party attendance widget
|
||||
|
||||
Template.attendance.helpers({
|
||||
rsvpName: function () {
|
||||
var user = Meteor.users.findOne(this.user);
|
||||
return displayName(user);
|
||||
},
|
||||
|
||||
outstandingInvitations: function () {
|
||||
var party = Parties.findOne(this._id);
|
||||
return Meteor.users.find({$and: [
|
||||
{_id: {$in: party.invited}}, // they're invited
|
||||
{_id: {$nin: _.pluck(party.rsvps, 'user')}} // but haven't RSVP'd
|
||||
]});
|
||||
},
|
||||
|
||||
invitationName: function () {
|
||||
return displayName(this);
|
||||
},
|
||||
|
||||
rsvpIs: function (what) {
|
||||
return this.rsvp === what;
|
||||
},
|
||||
|
||||
nobody: function () {
|
||||
return ! this.public && (this.rsvps.length + this.invited.length === 0);
|
||||
},
|
||||
|
||||
canInvite: function () {
|
||||
return ! this.public && this.owner === Meteor.userId();
|
||||
}
|
||||
});
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Map display
|
||||
|
||||
// Use jquery to get the position clicked relative to the map element.
|
||||
var coordsRelativeToElement = function (element, event) {
|
||||
var offset = $(element).offset();
|
||||
var x = event.pageX - offset.left;
|
||||
var y = event.pageY - offset.top;
|
||||
return { x: x, y: y };
|
||||
};
|
||||
|
||||
Template.map.events({
|
||||
'mousedown circle, mousedown text': function (event, template) {
|
||||
Session.set("selected", event.currentTarget.id);
|
||||
},
|
||||
'dblclick .map': function (event, template) {
|
||||
if (! Meteor.userId()) // must be logged in to create events
|
||||
return;
|
||||
var coords = coordsRelativeToElement(event.currentTarget, event);
|
||||
openCreateDialog(coords.x / 500, coords.y / 500);
|
||||
}
|
||||
});
|
||||
|
||||
Template.map.onRendered(function () {
|
||||
var self = this;
|
||||
self.node = self.find("svg");
|
||||
|
||||
if (! self.handle) {
|
||||
self.handle = Tracker.autorun(function () {
|
||||
var selected = Session.get('selected');
|
||||
var selectedParty = selected && Parties.findOne(selected);
|
||||
var radius = function (party) {
|
||||
return 10 + Math.sqrt(attending(party)) * 10;
|
||||
};
|
||||
|
||||
// Draw a circle for each party
|
||||
var updateCircles = function (group) {
|
||||
group.attr("id", function (party) { return party._id; })
|
||||
.attr("cx", function (party) { return party.x * 500; })
|
||||
.attr("cy", function (party) { return party.y * 500; })
|
||||
.attr("r", radius)
|
||||
.attr("class", function (party) {
|
||||
return party.public ? "public" : "private";
|
||||
})
|
||||
.style('opacity', function (party) {
|
||||
return selected === party._id ? 1 : 0.6;
|
||||
});
|
||||
};
|
||||
|
||||
var circles = d3.select(self.node).select(".circles").selectAll("circle")
|
||||
.data(Parties.find().fetch(), function (party) { return party._id; });
|
||||
|
||||
updateCircles(circles.enter().append("circle"));
|
||||
updateCircles(circles.transition().duration(250).ease("cubic-out"));
|
||||
circles.exit().transition().duration(250).attr("r", 0).remove();
|
||||
|
||||
// Label each with the current attendance count
|
||||
var updateLabels = function (group) {
|
||||
group.attr("id", function (party) { return party._id; })
|
||||
.text(function (party) {return attending(party) || '';})
|
||||
.attr("x", function (party) { return party.x * 500; })
|
||||
.attr("y", function (party) { return party.y * 500 + radius(party)/2 })
|
||||
.style('font-size', function (party) {
|
||||
return radius(party) * 1.25 + "px";
|
||||
});
|
||||
};
|
||||
|
||||
var labels = d3.select(self.node).select(".labels").selectAll("text")
|
||||
.data(Parties.find().fetch(), function (party) { return party._id; });
|
||||
|
||||
updateLabels(labels.enter().append("text"));
|
||||
updateLabels(labels.transition().duration(250).ease("cubic-out"));
|
||||
labels.exit().remove();
|
||||
|
||||
// Draw a dashed circle around the currently selected party, if any
|
||||
var callout = d3.select(self.node).select("circle.callout")
|
||||
.transition().duration(250).ease("cubic-out");
|
||||
if (selectedParty)
|
||||
callout.attr("cx", selectedParty.x * 500)
|
||||
.attr("cy", selectedParty.y * 500)
|
||||
.attr("r", radius(selectedParty) + 10)
|
||||
.attr("class", "callout")
|
||||
.attr("display", '');
|
||||
else
|
||||
callout.attr("display", 'none');
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
Template.map.onDestroyed = function () {
|
||||
this.handle && this.handle.stop();
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Create Party dialog
|
||||
|
||||
var openCreateDialog = function (x, y) {
|
||||
Session.set("createCoords", {x: x, y: y});
|
||||
Session.set("createError", null);
|
||||
Session.set("showCreateDialog", true);
|
||||
};
|
||||
|
||||
Template.page.helpers({
|
||||
showCreateDialog: function () {
|
||||
return Session.get("showCreateDialog");
|
||||
}
|
||||
});
|
||||
|
||||
Template.createDialog.events({
|
||||
'click .save': function (event, template) {
|
||||
var title = template.find(".title").value;
|
||||
var description = template.find(".description").value;
|
||||
var public = ! template.find(".private").checked;
|
||||
var coords = Session.get("createCoords");
|
||||
|
||||
if (title.length && description.length) {
|
||||
var id = createParty({
|
||||
title: title,
|
||||
description: description,
|
||||
x: coords.x,
|
||||
y: coords.y,
|
||||
public: public
|
||||
});
|
||||
|
||||
Session.set("selected", id);
|
||||
if (! public && Meteor.users.find().count() > 1)
|
||||
openInviteDialog();
|
||||
Session.set("showCreateDialog", false);
|
||||
} else {
|
||||
Session.set("createError",
|
||||
"It needs a title and a description, or why bother?");
|
||||
}
|
||||
},
|
||||
|
||||
'click .cancel': function () {
|
||||
Session.set("showCreateDialog", false);
|
||||
}
|
||||
});
|
||||
|
||||
Template.createDialog.helpers({
|
||||
error: function () {
|
||||
return Session.get("createError");
|
||||
}
|
||||
});
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Invite dialog
|
||||
|
||||
var openInviteDialog = function () {
|
||||
Session.set("showInviteDialog", true);
|
||||
};
|
||||
|
||||
Template.page.helpers({
|
||||
showInviteDialog: function () {
|
||||
return Session.get("showInviteDialog");
|
||||
}
|
||||
});
|
||||
|
||||
Template.inviteDialog.events({
|
||||
'click .invite': function (event, template) {
|
||||
Meteor.call('invite', Session.get("selected"), this._id);
|
||||
},
|
||||
'click .done': function (event, template) {
|
||||
Session.set("showInviteDialog", false);
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
Template.inviteDialog.helpers({
|
||||
uninvited: function () {
|
||||
var party = Parties.findOne(Session.get("selected"));
|
||||
if (! party)
|
||||
return []; // party hasn't loaded yet
|
||||
return Meteor.users.find({$nor: [{_id: {$in: party.invited}},
|
||||
{_id: party.owner}]});
|
||||
},
|
||||
|
||||
displayName: function () {
|
||||
return displayName(this);
|
||||
}
|
||||
});
|
||||
@@ -1,81 +0,0 @@
|
||||
.header {
|
||||
padding: 20px 0;
|
||||
}
|
||||
|
||||
.details {
|
||||
margin-top: -18px;
|
||||
}
|
||||
|
||||
.mask {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
background-color: #000000;
|
||||
opacity: .4;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.invite-row .invite {
|
||||
margin: 10px 10px 10px 0;
|
||||
}
|
||||
|
||||
.rsvp-buttons {
|
||||
text-align: center;
|
||||
margin: 40px 0 40px 0;
|
||||
}
|
||||
|
||||
.description {
|
||||
margin: 20px 0 20px 0;
|
||||
}
|
||||
|
||||
.attendance .who {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.attendance .invite {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
input.chosen {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.map {
|
||||
position: relative;
|
||||
background-image: url('/soma.png');
|
||||
background-position: -20px -20px;
|
||||
width: 500px;
|
||||
height: 500px;
|
||||
}
|
||||
|
||||
.map circle.public {
|
||||
fill: #49AFCD;
|
||||
}
|
||||
|
||||
.map circle.private {
|
||||
fill: #DA4F49;
|
||||
}
|
||||
|
||||
.map text {
|
||||
text-anchor: middle;
|
||||
fill: white;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.map circle.callout {
|
||||
stroke-width: 5px;
|
||||
stroke-dasharray: 9, 5;
|
||||
stroke-opacity: .8;
|
||||
fill: none;
|
||||
stroke: red;
|
||||
}
|
||||
|
||||
.attribution {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
background-color: white;
|
||||
padding: 3px;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
@@ -1,216 +0,0 @@
|
||||
<head>
|
||||
<title>All Tomorrow's Parties</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
{{> page}}
|
||||
</body>
|
||||
|
||||
<template name="page">
|
||||
{{#if showCreateDialog}}
|
||||
{{> createDialog}}
|
||||
{{/if}}
|
||||
|
||||
{{#if showInviteDialog}}
|
||||
{{> inviteDialog}}
|
||||
{{/if}}
|
||||
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="span1"> </div>
|
||||
|
||||
<div class="span10">
|
||||
<div class="header row">
|
||||
<div class="span5">
|
||||
<h3 style="margin-bottom: 0px">All Tomorrow's Parties</h3>
|
||||
</div>
|
||||
<div class="span5">
|
||||
<div style="float: right">
|
||||
{{> loginButtons align="right"}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="span6">
|
||||
{{> map}}
|
||||
{{#if currentUser}}
|
||||
<div class="pagination-centered">
|
||||
<em><small>Double click the map to post a party!</small></em>
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
<div class="span4">
|
||||
{{> details}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="span1"> </div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template name="map">
|
||||
<div class="map">
|
||||
<svg width="500" height="500">
|
||||
<circle class="callout" cx=-100 cy=-100></circle>
|
||||
<g class="circles"></g>
|
||||
<g class="labels"></g>
|
||||
</svg>
|
||||
<div>
|
||||
<small class="attribution muted">©
|
||||
<a href="http://www.openstreetmap.org/?lat=37.78212&lon=-122.40146&zoom=15&layers=M"
|
||||
target="_blank">OpenStreetMap</a> contributors</small>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template name="details">
|
||||
<div class="details">
|
||||
{{#if party}}
|
||||
{{#with party}}
|
||||
<h1>{{title}}</h1>
|
||||
|
||||
<div class="description">{{description}}</div>
|
||||
|
||||
{{> attendance}}
|
||||
|
||||
<div class="rsvp-buttons">
|
||||
{{#if currentUser}}
|
||||
<input type="button" value="I'm going!"
|
||||
class="btn btn-small rsvp_yes {{maybeChosen "yes"}}">
|
||||
<input type="button" value="Maybe"
|
||||
class="btn btn-small rsvp_maybe {{maybeChosen "maybe"}}">
|
||||
<input type="button" value="No"
|
||||
class="btn btn-small rsvp_no {{maybeChosen "no"}}">
|
||||
{{else}}
|
||||
<i>Sign in to RSVP for this party.</i>
|
||||
{{/if}}
|
||||
<p><small>Posted by {{creatorName}}</small></p>
|
||||
</div>
|
||||
|
||||
{{#if canRemove}}
|
||||
<div class="alert alert-info"><small>
|
||||
You posted this party and nobody is signed up to go, so if
|
||||
you like, you could
|
||||
<b><a href="#" class="remove">delete this listing</a></b>.
|
||||
</small></div>
|
||||
{{/if}}
|
||||
{{/with}}
|
||||
{{else}}
|
||||
<h1 class="muted pagination-centered">
|
||||
{{#if anyParties}}
|
||||
Click a party to select it
|
||||
{{else}}
|
||||
Sign in and double click the map to post a party
|
||||
{{/if}}
|
||||
</h1>
|
||||
{{/if}}
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template name="attendance">
|
||||
<div class="attendance well well-small">
|
||||
<div class="muted who"><b>Who</b></div>
|
||||
{{#if public}}
|
||||
<div>
|
||||
<b>Everyone</b>
|
||||
<span class="label label-inverse pull-right">Invited</span>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{#each rsvps}}
|
||||
<div>
|
||||
{{rsvpName}}
|
||||
{{#if rsvpIs "yes"}}
|
||||
<span class="label label-success pull-right">Going</span>
|
||||
{{/if}}
|
||||
{{#if rsvpIs "maybe"}}
|
||||
<span class="label label-info pull-right">Maybe</span>
|
||||
{{/if}}
|
||||
{{#if rsvpIs "no"}}
|
||||
<span class="label label pull-right">No</span>
|
||||
{{/if}}
|
||||
</div>
|
||||
{{/each}}
|
||||
|
||||
{{#unless public}}
|
||||
{{#each outstandingInvitations}}
|
||||
<div>
|
||||
{{invitationName}}
|
||||
<span class="label label-inverse pull-right">Invited</span>
|
||||
</div>
|
||||
{{/each}}
|
||||
{{/unless}}
|
||||
|
||||
{{#if nobody}}
|
||||
<div>Nobody.</div>
|
||||
{{/if}}
|
||||
|
||||
{{#if canInvite}}
|
||||
<div class="invite">
|
||||
<a href="#" class="btn btn-mini invite">Invite people</a>
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template name="createDialog">
|
||||
<div class="mask"> </div>
|
||||
<div class="modal">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close cancel">×</button>
|
||||
<h3>Add party</h3>
|
||||
</div>
|
||||
|
||||
<div class="modal-body">
|
||||
{{#if error}}
|
||||
<div class="alert alert-error">{{error}}</div>
|
||||
{{/if}}
|
||||
|
||||
<label>Title</label>
|
||||
<input type="text" class="title span5">
|
||||
|
||||
<label>Description</label>
|
||||
<textarea class="description span5"></textarea>
|
||||
|
||||
<label class="checkbox">
|
||||
<input type="checkbox" class="private">
|
||||
Private party — invitees only
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="modal-footer">
|
||||
<a href="#" class="btn cancel">Cancel</a>
|
||||
<a href="#" class="btn btn-primary save">Add party</a>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template name="inviteDialog">
|
||||
<div class="mask"> </div>
|
||||
<div class="modal">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close done">×</button>
|
||||
<h3>Invite people</h3>
|
||||
</div>
|
||||
|
||||
<div class="modal-body">
|
||||
{{#each uninvited}}
|
||||
<div class="invite-row">
|
||||
<a href="#" class="btn invite">Invite</a>
|
||||
{{displayName}}
|
||||
</div>
|
||||
{{else}}
|
||||
Everyone on the site has already been invited.
|
||||
{{/each}}
|
||||
</div>
|
||||
|
||||
<div class="modal-footer">
|
||||
<a href="#" class="btn btn-primary done">Done</a>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -1,183 +0,0 @@
|
||||
// All Tomorrow's Parties -- data model
|
||||
// Loaded on both the client and the server
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Parties
|
||||
|
||||
/*
|
||||
Each party is represented by a document in the Parties collection:
|
||||
owner: user id
|
||||
x, y: Number (screen coordinates in the interval [0, 1])
|
||||
title, description: String
|
||||
public: Boolean
|
||||
invited: Array of user id's that are invited (only if !public)
|
||||
rsvps: Array of objects like {user: userId, rsvp: "yes"} (or "no"/"maybe")
|
||||
*/
|
||||
Parties = new Mongo.Collection("parties");
|
||||
|
||||
Parties.allow({
|
||||
insert: function (userId, party) {
|
||||
return false; // no cowboy inserts -- use createParty method
|
||||
},
|
||||
update: function (userId, party, fields, modifier) {
|
||||
if (userId !== party.owner)
|
||||
return false; // not the owner
|
||||
|
||||
var allowed = ["title", "description", "x", "y"];
|
||||
if (_.difference(fields, allowed).length)
|
||||
return false; // tried to write to forbidden field
|
||||
|
||||
// A good improvement would be to validate the type of the new
|
||||
// value of the field (and if a string, the length.) In the
|
||||
// future Meteor will have a schema system to makes that easier.
|
||||
return true;
|
||||
},
|
||||
remove: function (userId, party) {
|
||||
// You can only remove parties that you created and nobody is going to.
|
||||
return party.owner === userId && attending(party) === 0;
|
||||
}
|
||||
});
|
||||
|
||||
attending = function (party) {
|
||||
return (_.groupBy(party.rsvps, 'rsvp').yes || []).length;
|
||||
};
|
||||
|
||||
var NonEmptyString = Match.Where(function (x) {
|
||||
check(x, String);
|
||||
return x.length !== 0;
|
||||
});
|
||||
|
||||
var Coordinate = Match.Where(function (x) {
|
||||
check(x, Number);
|
||||
return x >= 0 && x <= 1;
|
||||
});
|
||||
|
||||
createParty = function (options) {
|
||||
var id = Random.id();
|
||||
Meteor.call('createParty', _.extend({ _id: id }, options));
|
||||
return id;
|
||||
};
|
||||
|
||||
Meteor.methods({
|
||||
// options should include: title, description, x, y, public
|
||||
createParty: function (options) {
|
||||
check(options, {
|
||||
title: NonEmptyString,
|
||||
description: NonEmptyString,
|
||||
x: Coordinate,
|
||||
y: Coordinate,
|
||||
public: Match.Optional(Boolean),
|
||||
_id: Match.Optional(NonEmptyString)
|
||||
});
|
||||
|
||||
if (options.title.length > 100)
|
||||
throw new Meteor.Error(413, "Title too long");
|
||||
if (options.description.length > 1000)
|
||||
throw new Meteor.Error(413, "Description too long");
|
||||
if (! this.userId)
|
||||
throw new Meteor.Error(403, "You must be logged in");
|
||||
|
||||
var id = options._id || Random.id();
|
||||
Parties.insert({
|
||||
_id: id,
|
||||
owner: this.userId,
|
||||
x: options.x,
|
||||
y: options.y,
|
||||
title: options.title,
|
||||
description: options.description,
|
||||
public: !! options.public,
|
||||
invited: [],
|
||||
rsvps: []
|
||||
});
|
||||
return id;
|
||||
},
|
||||
|
||||
invite: function (partyId, userId) {
|
||||
check(partyId, String);
|
||||
check(userId, String);
|
||||
var party = Parties.findOne(partyId);
|
||||
if (! party || party.owner !== this.userId)
|
||||
throw new Meteor.Error(404, "No such party");
|
||||
if (party.public)
|
||||
throw new Meteor.Error(400,
|
||||
"That party is public. No need to invite people.");
|
||||
if (userId !== party.owner && ! _.contains(party.invited, userId)) {
|
||||
Parties.update(partyId, { $addToSet: { invited: userId } });
|
||||
|
||||
var from = contactEmail(Meteor.users.findOne(this.userId));
|
||||
var to = contactEmail(Meteor.users.findOne(userId));
|
||||
if (Meteor.isServer && to) {
|
||||
// This code only runs on the server. If you didn't want clients
|
||||
// to be able to see it, you could move it to a separate file.
|
||||
Email.send({
|
||||
from: "noreply@example.com",
|
||||
to: to,
|
||||
replyTo: from || undefined,
|
||||
subject: "PARTY: " + party.title,
|
||||
text:
|
||||
"Hey, I just invited you to '" + party.title + "' on All Tomorrow's Parties." +
|
||||
"\n\nCome check it out: " + Meteor.absoluteUrl() + "\n"
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
rsvp: function (partyId, rsvp) {
|
||||
check(partyId, String);
|
||||
check(rsvp, String);
|
||||
if (! this.userId)
|
||||
throw new Meteor.Error(403, "You must be logged in to RSVP");
|
||||
if (! _.contains(['yes', 'no', 'maybe'], rsvp))
|
||||
throw new Meteor.Error(400, "Invalid RSVP");
|
||||
var party = Parties.findOne(partyId);
|
||||
if (! party)
|
||||
throw new Meteor.Error(404, "No such party");
|
||||
if (! party.public && party.owner !== this.userId &&
|
||||
!_.contains(party.invited, this.userId))
|
||||
// private, but let's not tell this to the user
|
||||
throw new Meteor.Error(403, "No such party");
|
||||
|
||||
var rsvpIndex = _.indexOf(_.pluck(party.rsvps, 'user'), this.userId);
|
||||
if (rsvpIndex !== -1) {
|
||||
// update existing rsvp entry
|
||||
|
||||
if (Meteor.isServer) {
|
||||
// update the appropriate rsvp entry with $
|
||||
Parties.update(
|
||||
{_id: partyId, "rsvps.user": this.userId},
|
||||
{$set: {"rsvps.$.rsvp": rsvp}});
|
||||
} else {
|
||||
// minimongo doesn't yet support $ in modifier. as a temporary
|
||||
// workaround, make a modifier that uses an index. this is
|
||||
// safe on the client since there's only one thread.
|
||||
var modifier = {$set: {}};
|
||||
modifier.$set["rsvps." + rsvpIndex + ".rsvp"] = rsvp;
|
||||
Parties.update(partyId, modifier);
|
||||
}
|
||||
|
||||
// Possible improvement: send email to the other people that are
|
||||
// coming to the party.
|
||||
} else {
|
||||
// add new rsvp entry
|
||||
Parties.update(partyId,
|
||||
{$push: {rsvps: {user: this.userId, rsvp: rsvp}}});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Users
|
||||
|
||||
displayName = function (user) {
|
||||
if (user.profile && user.profile.name)
|
||||
return user.profile.name;
|
||||
return user.emails[0].address;
|
||||
};
|
||||
|
||||
var contactEmail = function (user) {
|
||||
if (user.emails && user.emails.length)
|
||||
return user.emails[0].address;
|
||||
if (user.services && user.services.facebook && user.services.facebook.email)
|
||||
return user.services.facebook.email;
|
||||
return null;
|
||||
};
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 126 KiB |
@@ -1,10 +0,0 @@
|
||||
// All Tomorrow's Parties -- server
|
||||
|
||||
Meteor.publish("directory", function () {
|
||||
return Meteor.users.find({}, {fields: {emails: 1, profile: 1}});
|
||||
});
|
||||
|
||||
Meteor.publish("parties", function () {
|
||||
return Parties.find(
|
||||
{$or: [{"public": true}, {invited: this.userId}, {owner: this.userId}]});
|
||||
});
|
||||
@@ -1,9 +0,0 @@
|
||||
# Meteor packages used by this project, one per line.
|
||||
#
|
||||
# 'meteor add' and 'meteor remove' will edit this file for you,
|
||||
# but you can also edit it by hand.
|
||||
|
||||
insecure
|
||||
preserve-inputs
|
||||
random
|
||||
standard-app-packages
|
||||
@@ -1 +0,0 @@
|
||||
0.6.0
|
||||
@@ -1,35 +0,0 @@
|
||||
<head>
|
||||
<title>quiescence</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
{{> clock}}
|
||||
{{> updated}}
|
||||
{{> stream}}
|
||||
</body>
|
||||
|
||||
<template name="clock">
|
||||
<h1>Current time</h1>
|
||||
<p>{{time}}</p>
|
||||
</template>
|
||||
|
||||
<template name="updated">
|
||||
<h1>Update object</h1>
|
||||
<p>The magic number is {{magic}}. Click the button to set it to a random
|
||||
integer in the client stub. The server will add 0.5 the that integer.</p>
|
||||
<button id='update-button'>Update</button>
|
||||
</template>
|
||||
|
||||
<template name="stream">
|
||||
<h1>Stream in results</h1>
|
||||
<p>Click the button. Note that the results stream in instead of appearing all
|
||||
at once. Note that the clock continues to tick while the streaming method
|
||||
is running. Note that the "update" button above works while the streaming
|
||||
method is running (ie, the server's "add 0.5" is processed).</p>
|
||||
<button id='stream-button'>Stream</button>
|
||||
<ul>
|
||||
{{#each results}}
|
||||
<li>{{text}}</li>
|
||||
{{/each}}
|
||||
</ul>
|
||||
</template>
|
||||
@@ -1,96 +0,0 @@
|
||||
Time = new Mongo.Collection("time");
|
||||
Results = new Mongo.Collection("results");
|
||||
Magic = new Mongo.Collection("magic");
|
||||
|
||||
if (Meteor.isServer) {
|
||||
Meteor.publish("time", function () {
|
||||
var self = this;
|
||||
var publishTime = function () {
|
||||
var when = + new Date;
|
||||
self.changed("time", "now", {timestamp: when});
|
||||
};
|
||||
self.added("time", "now", {});
|
||||
publishTime();
|
||||
self.ready();
|
||||
var interval = Meteor.setInterval(publishTime, 1000);
|
||||
self.onStop(function () {
|
||||
Meteor.clearInterval(interval);
|
||||
});
|
||||
});
|
||||
Meteor.publish("results", function () {
|
||||
return Results.find();
|
||||
});
|
||||
Meteor.publish("magic", function () {
|
||||
return Magic.find();
|
||||
});
|
||||
|
||||
Meteor.startup(function () {
|
||||
if (Magic.find().count() === 0) {
|
||||
Magic.insert({number: 42});
|
||||
}
|
||||
});
|
||||
|
||||
var Fiber = Npm.require('fibers');
|
||||
|
||||
var sleep = function (ms) {
|
||||
var fiber = Fiber.current;
|
||||
setTimeout(function() {
|
||||
fiber.run();
|
||||
}, ms);
|
||||
Fiber.yield();
|
||||
};
|
||||
|
||||
Meteor.methods({
|
||||
getResults: function () {
|
||||
this.unblock();
|
||||
Results.remove({});
|
||||
for (var i = 0; i < 5; ++i) {
|
||||
sleep(1000);
|
||||
Results.insert({i: i, text: 'result ' + i});
|
||||
}
|
||||
}});
|
||||
} else {
|
||||
Meteor.subscribe("time");
|
||||
Meteor.subscribe("results");
|
||||
Meteor.subscribe("magic");
|
||||
|
||||
Template.clock.time = function () {
|
||||
var now = Time.findOne('now');
|
||||
if (!now)
|
||||
return "(loading)";
|
||||
return new Date(now.timestamp).toTimeString();
|
||||
};
|
||||
|
||||
Template.updated.magic = function () {
|
||||
var singleton = Magic.findOne();
|
||||
if (!singleton)
|
||||
return "(loading)";
|
||||
return singleton.number;
|
||||
};
|
||||
Template.updated.events({
|
||||
'click #update-button': function () {
|
||||
var num = Math.round(Random.fraction()*100);
|
||||
Meteor.call('setMagic', num);
|
||||
}
|
||||
});
|
||||
|
||||
Template.stream.events({
|
||||
'click #stream-button': function () {
|
||||
Meteor.call('getResults');
|
||||
}
|
||||
});
|
||||
|
||||
Template.stream.results = function () {
|
||||
return Results.find({}, {sort: ['i']});
|
||||
};
|
||||
}
|
||||
|
||||
Meteor.methods({
|
||||
setMagic: function (num) {
|
||||
if (this.isSimulation) {
|
||||
Magic.update({}, {$set: {number: num}});
|
||||
} else {
|
||||
Magic.update({}, {$set: {number: num + 0.5}});
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -1 +0,0 @@
|
||||
local
|
||||
@@ -1,7 +0,0 @@
|
||||
# Meteor packages used by this project, one per line.
|
||||
#
|
||||
# 'meteor add' and 'meteor remove' will edit this file for you,
|
||||
# but you can also edit it by hand.
|
||||
|
||||
autopublish
|
||||
standard-app-packages
|
||||
@@ -1 +0,0 @@
|
||||
0.6.0
|
||||
7034
examples/other/template-demo/client/d3.v2.js
vendored
7034
examples/other/template-demo/client/d3.v2.js
vendored
File diff suppressed because it is too large
Load Diff
@@ -1,44 +0,0 @@
|
||||
body {
|
||||
font-family: 'Helvetica Neue', Helvetica, Arial, san-serif;
|
||||
width: 600px;
|
||||
margin: auto;
|
||||
padding: 25px 50px;
|
||||
border: 5px dashed #ccc;
|
||||
border-style: none dashed;
|
||||
}
|
||||
|
||||
h2 {
|
||||
margin-top: 50px;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.clearboth {
|
||||
clear: both;
|
||||
}
|
||||
|
||||
@-webkit-keyframes spinForward {
|
||||
from {-webkit-transform: rotate(0deg);}
|
||||
to {-webkit-transform: rotate(360deg);}
|
||||
}
|
||||
|
||||
@-webkit-keyframes spinBackward {
|
||||
from {-webkit-transform: rotate(360deg);}
|
||||
to {-webkit-transform: rotate(0deg);}
|
||||
}
|
||||
|
||||
.spinner {
|
||||
width: 100px;
|
||||
border: 2px solid black;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
background: white;
|
||||
}
|
||||
|
||||
.circles {
|
||||
float: left;
|
||||
padding-right: 20px;
|
||||
}
|
||||
|
||||
.circles svg {
|
||||
border: 2px solid #333;
|
||||
}
|
||||
@@ -1,183 +0,0 @@
|
||||
<head>
|
||||
<title>Advanced Template Demo</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
{{> page}}
|
||||
</body>
|
||||
|
||||
<template name="page">
|
||||
<h1>Advanced Template Demo</h1>
|
||||
<p>
|
||||
This demo shows off the advanced features of Meteor's optional
|
||||
Spark-based templating system, including constant regions, node
|
||||
preservation, per-template state, and template lifecycle
|
||||
callbacks.
|
||||
</p>
|
||||
|
||||
{{> preserveDemo }}
|
||||
{{> constantDemo }}
|
||||
{{> stateDemo }}
|
||||
{{> d3Demo }}
|
||||
</template>
|
||||
|
||||
<template name="preserveDemo">
|
||||
<h2>Element preservation</h2>
|
||||
|
||||
<input type="button" value="X++" class="x">
|
||||
|
||||
<p>
|
||||
Elements can be <em>preserved</em>, meaning that they will not be
|
||||
disturbed even as their attributes, children, or siblings
|
||||
change. In this example, when you press the X++ button, the CSS
|
||||
animation continues uninterrupted.
|
||||
</p>
|
||||
|
||||
|
||||
X={{x}}<br>
|
||||
<div class="spinner" style="-webkit-animation: {{spinAnim}} 2s infinite linear">
|
||||
X={{x}}
|
||||
</div>
|
||||
<div>
|
||||
<input type="checkbox" class="spinforward" {{spinForwardChecked}}>
|
||||
Spin Forward
|
||||
</div>
|
||||
X={{x}}
|
||||
</template>
|
||||
|
||||
<template name="constantDemo">
|
||||
<h2>Constant regions</h2>
|
||||
|
||||
<div>
|
||||
<input type="button" value="X++" class="x"> <br>
|
||||
<input type="checkbox" class="remove" which="1" {{checked 1}}>
|
||||
Remove map 1<br>
|
||||
<input type="checkbox" class="remove" which="2" {{checked 2}}>
|
||||
Remove map 2
|
||||
</div>
|
||||
<br>
|
||||
|
||||
<p>
|
||||
Parts of a template can be marked as <em>constant</em>, meaning
|
||||
that Meteor will leave the entire region alone (even as its
|
||||
siblings change.) This is great for embedding non-Meteor
|
||||
widgets. Try scrolling the two Google Maps embeds below. When you
|
||||
press X++, the maps stay where they are.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Try using the checkboxes to remove either or both of the
|
||||
maps. When you remove a map, Spark tracks the <em>identity</em> of
|
||||
the constant regions so that it knows which DOM nodes to keep and
|
||||
which DOM nodes to throw away. In the case of the Handlebars
|
||||
package, the identity is based on the actual template call stack
|
||||
that rendered the constant region.
|
||||
</p>
|
||||
|
||||
X={{x}}<br>
|
||||
|
||||
{{#if show 1}}
|
||||
{{#constant}}
|
||||
<div style="float: left; padding-right: 20px;">
|
||||
<iframe width="290" height="290" frameborder="0" scrolling="no" marginheight="0" marginwidth="0" src="https://maps.google.com/maps?f=q&source=s_q&hl=en&geocode=&q=140+10th+Street,+San+Francisco,+CA&aq=0&oq=140+10th+s&sll=37.7577,-122.4376&sspn=0.166931,0.329247&ie=UTF8&hq=&hnear=140+10th+St,+San+Francisco,+California+94103&t=m&ll=37.774921,-122.415419&spn=0.013569,0.017252&z=14&iwloc=A&output=embed"></iframe><br /><small><a href="https://maps.google.com/maps?f=q&source=embed&hl=en&geocode=&q=140+10th+Street,+San+Francisco,+CA&aq=0&oq=140+10th+s&sll=37.7577,-122.4376&sspn=0.166931,0.329247&ie=UTF8&hq=&hnear=140+10th+St,+San+Francisco,+California+94103&t=m&ll=37.774921,-122.415419&spn=0.013569,0.017252&z=14&iwloc=A" style="color:#0000FF;text-align:left">View Larger Map</a></small>
|
||||
</div>
|
||||
{{/constant}}
|
||||
{{/if}}
|
||||
|
||||
{{#if show 2}}
|
||||
{{#constant}}
|
||||
<div>
|
||||
<iframe width="290" height="290" frameborder="0" scrolling="no" marginheight="0" marginwidth="0" src="https://maps.google.com/maps?f=q&source=s_q&hl=en&geocode=&q=880+Harrison+Street,+San+Francisco,+CA&aq=0&oq=880+harrison&sll=37.7577,-122.4376&sspn=0.166931,0.329247&ie=UTF8&hq=&hnear=880+Harrison+St,+San+Francisco,+California+94107&t=m&ll=37.779534,-122.411213&spn=0.013568,0.01708&z=14&iwloc=A&output=embed"></iframe><br /><small><a href="https://maps.google.com/maps?f=q&source=embed&hl=en&geocode=&q=880+Harrison+Street,+San+Francisco,+CA&aq=0&oq=880+harrison&sll=37.7577,-122.4376&sspn=0.166931,0.329247&ie=UTF8&hq=&hnear=880+Harrison+St,+San+Francisco,+California+94107&t=m&ll=37.779534,-122.411213&spn=0.013568,0.01708&z=14&iwloc=A" style="color:#0000FF;text-align:left">View Larger Map</a></small>
|
||||
</div>
|
||||
{{/constant}}
|
||||
{{/if}}
|
||||
|
||||
<div class="clearboth"> </div>
|
||||
|
||||
X={{x}}
|
||||
</template>
|
||||
|
||||
<template name="stateDemo">
|
||||
<h2>Template callbacks</h2>
|
||||
|
||||
<p>
|
||||
<input type="button" value="X++" class="x">
|
||||
<input type="button" value="Y++" class="y">
|
||||
<input type="button" value="Z++" class="z">
|
||||
</p>
|
||||
|
||||
<p>
|
||||
You can get a <em>created</em> callback when a template is
|
||||
initially rendered; a <em>rendered</em> when a template is placed on
|
||||
the screen and when any part of the template is redrawn; and
|
||||
a <em>destroyed</em> callback when a template is taken across the
|
||||
screen. All of these callbacks receive a common <em>template state
|
||||
object</em> in 'this' which allows you to attach data to each
|
||||
particular instance of a template.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
In this case, <em>created</em> is used to create a new JavaScript
|
||||
timer that updates the text of a <span> element every
|
||||
second. <em>rendered</em> is used to find the <span> when it
|
||||
appears on the screen, and update the pointer when the
|
||||
<span> is redraw (say, when you press Y++ — since it
|
||||
is not marked to be preserved.) <em>destroyed</em> is used to cancel
|
||||
the timer when the template goes off the screen.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
The template state is used to hold the current time count and a
|
||||
reference to the <span> object to update. That's why there
|
||||
can be multiple copies of the same template, each with a different
|
||||
value for the counter.
|
||||
</p>
|
||||
|
||||
X={{x}}<br>
|
||||
<input type="button" value="Create a timer" class="create"><br>
|
||||
{{#each timers}}
|
||||
<div>
|
||||
{{> timer}}
|
||||
Z={{z}}
|
||||
</div>
|
||||
{{/each}}
|
||||
X={{x}}
|
||||
</template>
|
||||
|
||||
<template name="timer">
|
||||
<span class="elapsed"></span>
|
||||
<input type="button" value="Reset" class="reset">
|
||||
<input type="button" value="Delete" class="delete">
|
||||
Y={{y}}
|
||||
</template>
|
||||
|
||||
<template name="d3Demo">
|
||||
<h2>Simple d3.js integration</h2>
|
||||
<p>
|
||||
Meteor fits naturally with the popular d3.js data visualization
|
||||
library by Michael Bostock. Just set up d3 from your
|
||||
template's <em>rendered</em> callback. With Meteor, you can pass
|
||||
data directly out of a Mongo query into d3, and your d3
|
||||
visualization will update in realtime, with no extra code! Try
|
||||
opening this page in two browser windows.
|
||||
</p>
|
||||
|
||||
{{> circles left}}
|
||||
{{> circles right}}
|
||||
<div class="clearboth"> </div>
|
||||
|
||||
</template>
|
||||
|
||||
<template name="circles">
|
||||
<div class="circles">
|
||||
{{#constant}}
|
||||
<svg width="272" height="272"></svg>
|
||||
{{/constant}}
|
||||
<br>
|
||||
{{count}} circles<br>
|
||||
<input type="button" value="Add" class="add">
|
||||
<input type="button" value="Remove" class="remove" {{disabled}}>
|
||||
<input type="button" value="Scram" class="scram">
|
||||
<input type="button" value="Clear" class="clear">
|
||||
</div>
|
||||
</template>
|
||||
@@ -1,273 +0,0 @@
|
||||
Timers = new Mongo.Collection(null);
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
if (! Session.get("x")) {
|
||||
Session.set("x", 1);
|
||||
}
|
||||
|
||||
if (! Session.get("y")) {
|
||||
Session.set("y", 1);
|
||||
}
|
||||
|
||||
if (! Session.get("z")) {
|
||||
Session.set("z", 1);
|
||||
}
|
||||
|
||||
Template.preserveDemo.x =
|
||||
Template.constantDemo.x =
|
||||
Template.stateDemo.x =
|
||||
function () {
|
||||
return Session.get("x");
|
||||
};
|
||||
|
||||
Template.timer.y = function () {
|
||||
return Session.get("y");
|
||||
};
|
||||
|
||||
Template.stateDemo.z =
|
||||
function () {
|
||||
return Session.get("z");
|
||||
};
|
||||
|
||||
Template.page.events({
|
||||
'click input.x': function () {
|
||||
Session.set("x", Session.get("x") + 1);
|
||||
},
|
||||
|
||||
'click input.y': function () {
|
||||
Session.set("y", Session.get("y") + 1);
|
||||
},
|
||||
|
||||
'click input.z': function () {
|
||||
Session.set("z", Session.get("z") + 1);
|
||||
}
|
||||
});
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
if (typeof Session.get("spinForward") !== 'boolean') {
|
||||
Session.set("spinForward", true);
|
||||
}
|
||||
|
||||
Template.preserveDemo.preserve([ '.spinner', '.spinforward' ]);
|
||||
|
||||
Template.preserveDemo.spinForwardChecked = function () {
|
||||
return Session.get('spinForward') ? 'checked' : '';
|
||||
};
|
||||
|
||||
Template.preserveDemo.spinAnim = function () {
|
||||
return Session.get('spinForward') ? 'spinForward' : 'spinBackward';
|
||||
};
|
||||
|
||||
Template.preserveDemo.events({
|
||||
'change .spinforward' : function (event) {
|
||||
Session.set('spinForward', event.currentTarget.checked);
|
||||
}
|
||||
});
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
Template.constantDemo.checked = function (which) {
|
||||
return Session.get('mapchecked' + which) ? 'checked' : '';
|
||||
};
|
||||
|
||||
Template.constantDemo.show = function (which) {
|
||||
return ! Session.get('mapchecked' + which);
|
||||
};
|
||||
|
||||
Template.constantDemo.events({
|
||||
'change .remove' : function (event) {
|
||||
var tgt = event.currentTarget;
|
||||
Session.set('mapchecked' + tgt.getAttribute("which"), tgt.checked);
|
||||
}
|
||||
});
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
Template.stateDemo.events({
|
||||
'click .create': function () {
|
||||
Timers.insert({});
|
||||
}
|
||||
});
|
||||
|
||||
Template.stateDemo.timers = function () {
|
||||
return Timers.find();
|
||||
};
|
||||
|
||||
Template.timer.events({
|
||||
'click .reset': function (event, template) {
|
||||
template.elapsed = 0;
|
||||
updateTimer(template);
|
||||
},
|
||||
'click .delete': function () {
|
||||
Timers.remove(this._id);
|
||||
}
|
||||
});
|
||||
|
||||
var updateTimer = function (timer) {
|
||||
timer.node.innerHTML = timer.elapsed + " second" +
|
||||
((timer.elapsed === 1) ? "" : "s");
|
||||
};
|
||||
|
||||
Template.timer.onCreated(function () {
|
||||
var self = this;
|
||||
self.elapsed = 0;
|
||||
self.node = null;
|
||||
});
|
||||
|
||||
Template.timer.onRendered(function () {
|
||||
var self = this;
|
||||
self.node = this.find(".elapsed");
|
||||
updateTimer(self);
|
||||
|
||||
if (! self.timer) {
|
||||
var tick = function () {
|
||||
self.elapsed++;
|
||||
self.timer = setTimeout(tick, 1000);
|
||||
updateTimer(self);
|
||||
};
|
||||
tick();
|
||||
}
|
||||
});
|
||||
|
||||
Template.timer.onDestroyed(function () {
|
||||
clearInterval(this.timer);
|
||||
});
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
Template.d3Demo.left = function () {
|
||||
return { group: "left" };
|
||||
};
|
||||
|
||||
Template.d3Demo.right = function () {
|
||||
return { group: "right" };
|
||||
};
|
||||
|
||||
Template.circles.events({
|
||||
'mousedown circle': function (evt, template) {
|
||||
Session.set("selectedCircle:" + this.group, evt.currentTarget.id);
|
||||
},
|
||||
'click .add': function () {
|
||||
Circles.insert({x: Random.fraction(), y: Random.fraction(),
|
||||
r: Random.fraction() * .1 + .02,
|
||||
color: {
|
||||
r: Random.fraction(),
|
||||
g: Random.fraction(),
|
||||
b: Random.fraction()
|
||||
},
|
||||
group: this.group
|
||||
});
|
||||
},
|
||||
'click .remove': function () {
|
||||
var selected = Session.get("selectedCircle:" + this.group);
|
||||
if (selected) {
|
||||
Circles.remove(selected);
|
||||
Session.set("selectedCircle:" + this.group, null);
|
||||
}
|
||||
},
|
||||
'click .scram': function () {
|
||||
Circles.find({group: this.group}).forEach(function (r) {
|
||||
Circles.update(r._id, {
|
||||
$set: {
|
||||
x: Random.fraction(), y: Random.fraction(), r: Random.fraction() * .1 + .02
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
'click .clear': function () {
|
||||
Circles.remove({group: this.group});
|
||||
}
|
||||
});
|
||||
|
||||
var colorToString = function (color) {
|
||||
var f = function (x) { return Math.floor(x * 256); };
|
||||
return "rgb(" + f(color.r) + "," +
|
||||
+ f(color.g) + "," + + f(color.b) + ")";
|
||||
};
|
||||
|
||||
Template.circles.count = function () {
|
||||
return Circles.find({group: this.group}).count();
|
||||
};
|
||||
|
||||
Template.circles.disabled = function () {
|
||||
return Session.get("selectedCircle:" + this.group) ?
|
||||
'' : 'disabled';
|
||||
};
|
||||
|
||||
Template.circles.onCreated(function () {
|
||||
});
|
||||
|
||||
Template.circles.onRendered(function () {
|
||||
var self = this;
|
||||
self.node = self.find("svg");
|
||||
|
||||
var data = self.data;
|
||||
|
||||
if (! self.handle) {
|
||||
d3.select(self.node).append("rect");
|
||||
self.handle = Deps.autorun(function () {
|
||||
var circle = d3.select(self.node).selectAll("circle")
|
||||
.data(Circles.find({group: data.group}).fetch(),
|
||||
function (d) { return d._id; });
|
||||
|
||||
circle.enter().append("circle")
|
||||
.attr("id", function (d) {
|
||||
return d._id;
|
||||
})
|
||||
.attr("cx", function (d) {
|
||||
return d.x * 272;
|
||||
})
|
||||
.attr("cy", function (d) {
|
||||
return d.y * 272;
|
||||
})
|
||||
.attr("r", 50)
|
||||
.style("fill", function (d) {
|
||||
return colorToString(d.color);
|
||||
})
|
||||
.style("opacity", 0);
|
||||
|
||||
circle.transition()
|
||||
.duration(250)
|
||||
.attr("cx", function (d) {
|
||||
return d.x * 272;
|
||||
})
|
||||
.attr("cy", function (d) {
|
||||
return d.y * 272;
|
||||
})
|
||||
.attr("r", function (d) {
|
||||
return d.r * 272;
|
||||
})
|
||||
.style("fill", function (d) {
|
||||
return colorToString(d.color);
|
||||
})
|
||||
.style("opacity", .9)
|
||||
.ease("cubic-out");
|
||||
|
||||
circle.exit().transition()
|
||||
.duration(250)
|
||||
.attr("r", 0)
|
||||
.remove();
|
||||
|
||||
var selectionId = Session.get("selectedCircle:" + data.group);
|
||||
var s = selectionId && Circles.findOne(selectionId);
|
||||
var rect = d3.select(self.node).select("rect");
|
||||
if (s)
|
||||
rect.attr("x", (s.x - s.r) * 272)
|
||||
.attr("y", (s.y - s.r) * 272)
|
||||
.attr("width", s.r * 2 * 272)
|
||||
.attr("height", s.r * 2 * 272)
|
||||
.attr("display", '')
|
||||
.style("fill", "none")
|
||||
.style("stroke", "red")
|
||||
.style("stroke-width", 3);
|
||||
else
|
||||
rect.attr("display", 'none');
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
Template.circles.onDestroyed(function () {
|
||||
this.handle && this.handle.stop();
|
||||
});
|
||||
@@ -1 +0,0 @@
|
||||
Circles = new Mongo.Collection("circles");
|
||||
@@ -1,7 +0,0 @@
|
||||
# This file contains information which helps Meteor properly upgrade your
|
||||
# app when you run 'meteor update'. You should check it into version control
|
||||
# with your project.
|
||||
|
||||
notices-for-0.9.0
|
||||
notices-for-0.9.1
|
||||
0.9.4-platform-file
|
||||
1
examples/other/wordplay/.meteor/.gitignore
vendored
1
examples/other/wordplay/.meteor/.gitignore
vendored
@@ -1 +0,0 @@
|
||||
local
|
||||
@@ -1,8 +0,0 @@
|
||||
# Meteor packages used by this project, one per line.
|
||||
#
|
||||
# 'meteor add' and 'meteor remove' will edit this file for you,
|
||||
# but you can also edit it by hand.
|
||||
|
||||
standard-app-packages
|
||||
insecure
|
||||
jquery
|
||||
@@ -1 +0,0 @@
|
||||
METEOR@0.9.4
|
||||
@@ -1,51 +0,0 @@
|
||||
application-configuration@1.0.3
|
||||
autoupdate@1.1.2
|
||||
base64@1.0.1
|
||||
binary-heap@1.0.1
|
||||
blaze-tools@1.0.1
|
||||
blaze@2.0.2
|
||||
boilerplate-generator@1.0.1
|
||||
callback-hook@1.0.1
|
||||
check@1.0.2
|
||||
ctl-helper@1.0.4
|
||||
ctl@1.0.2
|
||||
ddp@1.0.10
|
||||
deps@1.0.5
|
||||
ejson@1.0.4
|
||||
fastclick@1.0.1
|
||||
follower-livedata@1.0.2
|
||||
geojson-utils@1.0.1
|
||||
html-tools@1.0.2
|
||||
htmljs@1.0.2
|
||||
http@1.0.7
|
||||
id-map@1.0.1
|
||||
insecure@1.0.1
|
||||
jquery@1.0.1
|
||||
json@1.0.1
|
||||
livedata@1.0.11
|
||||
logging@1.0.4
|
||||
meteor-platform@1.1.2
|
||||
meteor@1.1.2
|
||||
minifiers@1.1.1
|
||||
minimongo@1.0.4
|
||||
mobile-status-bar@1.0.1
|
||||
mongo@1.0.7
|
||||
observe-sequence@1.0.3
|
||||
ordered-dict@1.0.1
|
||||
random@1.0.1
|
||||
reactive-dict@1.0.4
|
||||
reactive-var@1.0.3
|
||||
reload@1.1.1
|
||||
retry@1.0.1
|
||||
routepolicy@1.0.2
|
||||
session@1.0.3
|
||||
spacebars-compiler@1.0.3
|
||||
spacebars@1.0.3
|
||||
standard-app-packages@1.0.3
|
||||
templating@1.0.8
|
||||
tracker@1.0.3
|
||||
ui@1.0.4
|
||||
underscore@1.0.1
|
||||
url@1.0.1
|
||||
webapp-hashing@1.0.1
|
||||
webapp@1.1.3
|
||||
@@ -1,12 +0,0 @@
|
||||
TODOS
|
||||
strip spaces on input box
|
||||
focus input on game start
|
||||
styling
|
||||
eliminate extra divs
|
||||
|
||||
POSSIBLE EXTENSIONS
|
||||
publish remaining words at end of game
|
||||
UI that works on touch devices sans keyboard
|
||||
spinny while word is getting scored
|
||||
support clicking on board instead of text box
|
||||
|
||||
@@ -1,180 +0,0 @@
|
||||
body {
|
||||
margin: 0px;
|
||||
background-color: #f4f4f4;
|
||||
font-family: Helvetica, Arial, sans-serif;
|
||||
}
|
||||
|
||||
/* base styles */
|
||||
|
||||
input {
|
||||
height: 50px;
|
||||
width: 300px;
|
||||
font-size: 2em;
|
||||
border: 2px solid black;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
button {
|
||||
position: relative;
|
||||
bottom: 3px;
|
||||
margin: 10px;
|
||||
height: 50px;
|
||||
background-color:#E6EFC2;
|
||||
border:1px solid #dedede;
|
||||
font-weight:bold;
|
||||
cursor:pointer;
|
||||
font-size: 1.5em;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
background-color:#D6DFb2;
|
||||
border:1px solid #C6D880;
|
||||
}
|
||||
|
||||
button:active {
|
||||
background-color:#529214;
|
||||
border:1px solid #529214;
|
||||
color:#fff;
|
||||
}
|
||||
|
||||
/*******/
|
||||
|
||||
#main {
|
||||
margin: 20px;
|
||||
}
|
||||
|
||||
#left {
|
||||
float: left;
|
||||
width: 40%;
|
||||
}
|
||||
|
||||
#right {
|
||||
float: left;
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
#clock {
|
||||
width: 100%;
|
||||
height: 100px;
|
||||
text-align: center;
|
||||
font-size: 3em;
|
||||
}
|
||||
|
||||
#board {
|
||||
margin: auto;
|
||||
border:4px solid #999999;
|
||||
border-radius: 4px;
|
||||
-moz-border-radius: 4px;
|
||||
padding:2px;
|
||||
|
||||
width:400px;
|
||||
height:400px;
|
||||
background-color:#999999;
|
||||
}
|
||||
|
||||
.square {
|
||||
cursor: pointer;
|
||||
width:84px;
|
||||
height:84px;
|
||||
border:4px solid #eeeee8;
|
||||
border-radius: 4px;
|
||||
-moz-border-radius: 4px;
|
||||
margin: 4px;
|
||||
float:left;
|
||||
text-align: center;
|
||||
background-color:#eeeee8;
|
||||
font-size: 65px;
|
||||
}
|
||||
|
||||
.square.last_in_path {
|
||||
color: #ff0000;
|
||||
}
|
||||
|
||||
.square.in_path {
|
||||
color: #990000;
|
||||
}
|
||||
|
||||
#login {
|
||||
margin: 100px auto;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#login .error {
|
||||
color: red;
|
||||
}
|
||||
|
||||
#lobby {
|
||||
margin: 100px auto;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#postgame {
|
||||
height: 100px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#scratchpad {
|
||||
height: 100px;
|
||||
}
|
||||
|
||||
#scratchpad input {
|
||||
width: 70%;
|
||||
}
|
||||
|
||||
#scratchpad h1 {
|
||||
float: left;
|
||||
}
|
||||
|
||||
#scores {
|
||||
float: left;
|
||||
width: 100%;
|
||||
background-color: #eeeee8;
|
||||
border: 1px solid black;
|
||||
padding: 0.5em;
|
||||
}
|
||||
|
||||
#scores .player {
|
||||
float: left;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#scores .header {
|
||||
font-size: 1.25em;
|
||||
}
|
||||
|
||||
#scores .unnamed {
|
||||
color: #444;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
#scores .winner {
|
||||
background-color: yellow;
|
||||
}
|
||||
|
||||
#scores .winner_text {
|
||||
float: right;
|
||||
}
|
||||
|
||||
#scores .word {
|
||||
float: left;
|
||||
font-size: 1.25em;
|
||||
padding: 0.25em;
|
||||
margin: 0.5em;
|
||||
border: 1px solid #030;
|
||||
}
|
||||
|
||||
#scores .word.good {
|
||||
background-color: #0a0;
|
||||
}
|
||||
|
||||
#scores .word.bad {
|
||||
text-decoration: line-through;
|
||||
}
|
||||
|
||||
#scores .word span.score {
|
||||
width: 1em;
|
||||
}
|
||||
|
||||
#scores .word.bad span.score {
|
||||
background-image: 'circle-ball-dark-antialiased.gif';
|
||||
}
|
||||
@@ -1,136 +0,0 @@
|
||||
<head>
|
||||
<title>Word play!</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
{{> page}}
|
||||
</body>
|
||||
|
||||
<template name="page">
|
||||
<div id="main">
|
||||
<div id="left">
|
||||
{{> board }}
|
||||
</div>
|
||||
<div id="right">
|
||||
{{> lobby }}
|
||||
{{> scratchpad }}
|
||||
{{> postgame }}
|
||||
{{> scores }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template name="board">
|
||||
<div id="clock">
|
||||
{{ clock }}
|
||||
</div>
|
||||
<div id="board">
|
||||
<div>
|
||||
<div class="square {{ selected 0 }}">{{ square 0 }}</div>
|
||||
<div class="square {{ selected 1 }}">{{ square 1 }}</div>
|
||||
<div class="square {{ selected 2 }}">{{ square 2 }}</div>
|
||||
<div class="square {{ selected 3 }}">{{ square 3 }}</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="square {{ selected 4 }}">{{ square 4 }}</div>
|
||||
<div class="square {{ selected 5 }}">{{ square 5 }}</div>
|
||||
<div class="square {{ selected 6 }}">{{ square 6 }}</div>
|
||||
<div class="square {{ selected 7 }}">{{ square 7 }}</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="square {{ selected 8 }}">{{ square 8 }}</div>
|
||||
<div class="square {{ selected 9 }}">{{ square 9 }}</div>
|
||||
<div class="square {{ selected 10 }}">{{ square 10 }}</div>
|
||||
<div class="square {{ selected 11 }}">{{ square 11 }}</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="square {{ selected 12 }}">{{ square 12 }}</div>
|
||||
<div class="square {{ selected 13 }}">{{ square 13 }}</div>
|
||||
<div class="square {{ selected 14 }}">{{ square 14 }}</div>
|
||||
<div class="square {{ selected 15 }}">{{ square 15 }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template name="lobby">
|
||||
<div>
|
||||
{{#if show }}
|
||||
<div id="lobby">
|
||||
<h1>What's your name?</h1>
|
||||
<input id="myname" type="text" />
|
||||
{{#if count}}
|
||||
<h1>{{count}} other players are in the lobby:</h1>
|
||||
{{#each waiting }}
|
||||
<div class="player">{{name}}</div>
|
||||
{{/each}}
|
||||
{{/if}}
|
||||
<div>
|
||||
<button id="startgame" class="startgame" {{disabled}}>
|
||||
{{#if count}} It's on! {{else}} Play solo {{/if}}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template name="scratchpad">
|
||||
{{#if show}}
|
||||
<div id="scratchpad">
|
||||
<input id="scratchpad_input" type="text" />
|
||||
<button name="submit" class="submit">Submit</button>
|
||||
</div>
|
||||
{{/if}}
|
||||
</template>
|
||||
|
||||
<template name="postgame">
|
||||
<div>
|
||||
{{#if show}}
|
||||
<div id="postgame">
|
||||
<button name="backtolobby" class="lobby">Back to lobby</button>
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template name="scores">
|
||||
<div>
|
||||
{{#if show}}
|
||||
<div id="scores">
|
||||
{{#each players}}
|
||||
{{> player }}
|
||||
{{/each}}
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template name="player">
|
||||
<div class="player">
|
||||
<div class="header {{winner}}">
|
||||
{{#if name}}
|
||||
{{name}}
|
||||
{{else}}
|
||||
<span class="unnamed">no name</span>
|
||||
{{/if}}
|
||||
<span class="score">{{total_score}}</span>
|
||||
{{#if winner}}
|
||||
<span class="winner_text">Winner!</span>
|
||||
{{/if}}
|
||||
</div>
|
||||
{{> words}}
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template name="words">
|
||||
<div class="words">
|
||||
{{#each words}}
|
||||
<div id="word_{{_id}}" class="word {{state}}">
|
||||
<span class="score">
|
||||
{{score}}
|
||||
</span>
|
||||
{{word}}
|
||||
</div>
|
||||
{{/each}}
|
||||
</div>
|
||||
</template>
|
||||
@@ -1,252 +0,0 @@
|
||||
////////// Main client application logic //////////
|
||||
|
||||
//////
|
||||
////// Utility functions
|
||||
//////
|
||||
|
||||
var player = function () {
|
||||
return Players.findOne(Session.get('player_id'));
|
||||
};
|
||||
|
||||
var game = function () {
|
||||
var me = player();
|
||||
return me && me.game_id && Games.findOne(me.game_id);
|
||||
};
|
||||
|
||||
var set_selected_positions = function (word) {
|
||||
var paths = paths_for_word(game().board, word.toUpperCase());
|
||||
var in_a_path = [];
|
||||
var last_in_a_path = [];
|
||||
|
||||
for (var i = 0; i < paths.length; i++) {
|
||||
in_a_path = in_a_path.concat(paths[i]);
|
||||
last_in_a_path.push(paths[i].slice(-1)[0]);
|
||||
}
|
||||
|
||||
for (var pos = 0; pos < 16; pos++) {
|
||||
if (_.indexOf(last_in_a_path, pos) !== -1)
|
||||
Session.set('selected_' + pos, 'last_in_path');
|
||||
else if (_.indexOf(in_a_path, pos) !== -1)
|
||||
Session.set('selected_' + pos, 'in_path');
|
||||
else
|
||||
Session.set('selected_' + pos, false);
|
||||
}
|
||||
};
|
||||
|
||||
var clear_selected_positions = function () {
|
||||
for (var pos = 0; pos < 16; pos++)
|
||||
Session.set('selected_' + pos, false);
|
||||
};
|
||||
|
||||
//////
|
||||
////// lobby template: shows everyone not currently playing, and
|
||||
////// offers a button to start a fresh game.
|
||||
//////
|
||||
|
||||
Template.lobby.helpers({
|
||||
show: function () {
|
||||
// only show lobby if we're not in a game
|
||||
return !game();
|
||||
},
|
||||
|
||||
waiting: function () {
|
||||
var players = Players.find({_id: {$ne: Session.get('player_id')},
|
||||
name: {$ne: ''},
|
||||
game_id: {$exists: false}});
|
||||
|
||||
return players;
|
||||
},
|
||||
|
||||
count: function () {
|
||||
var players = Players.find({_id: {$ne: Session.get('player_id')},
|
||||
name: {$ne: ''},
|
||||
game_id: {$exists: false}});
|
||||
|
||||
return players.count();
|
||||
},
|
||||
|
||||
disabled: function () {
|
||||
var me = player();
|
||||
if (me && me.name)
|
||||
return '';
|
||||
return 'disabled';
|
||||
}
|
||||
});
|
||||
|
||||
var trim = function (string) { return string.replace(/^\s+|\s+$/g, ''); };
|
||||
|
||||
Template.lobby.events({
|
||||
'keyup input#myname': function (evt) {
|
||||
var name = trim($('#lobby input#myname').val());
|
||||
Players.update(Session.get('player_id'), {$set: {name: name}});
|
||||
},
|
||||
'click button.startgame': function () {
|
||||
Meteor.call('start_new_game');
|
||||
}
|
||||
});
|
||||
|
||||
//////
|
||||
////// board template: renders the board and the clock given the
|
||||
////// current game. if there is no game, show a splash screen.
|
||||
//////
|
||||
var SPLASH = ['','','','',
|
||||
'W', 'O', 'R', 'D',
|
||||
'P', 'L', 'A', 'Y',
|
||||
'','','',''];
|
||||
|
||||
Template.board.helpers({
|
||||
square: function (i) {
|
||||
var g = game();
|
||||
return g && g.board && g.board[i] || SPLASH[i];
|
||||
},
|
||||
|
||||
selected: function (i) {
|
||||
return Session.get('selected_' + i);
|
||||
},
|
||||
|
||||
clock: function () {
|
||||
var clock = game() && game().clock;
|
||||
|
||||
if (!clock || clock === 0)
|
||||
return;
|
||||
|
||||
// format into M:SS
|
||||
var min = Math.floor(clock / 60);
|
||||
var sec = clock % 60;
|
||||
return min + ':' + (sec < 10 ? ('0' + sec) : sec);
|
||||
}
|
||||
});
|
||||
|
||||
Template.board.events({
|
||||
'click .square': function (evt) {
|
||||
var textbox = $('#scratchpad input');
|
||||
// Note: Getting the letter out of the DOM is kind of a hack
|
||||
var letter = evt.target.textContent || evt.target.innerText;
|
||||
textbox.val(textbox.val() + letter);
|
||||
textbox.focus();
|
||||
}
|
||||
});
|
||||
|
||||
//////
|
||||
////// scratchpad is where we enter new words.
|
||||
//////
|
||||
|
||||
Template.scratchpad.helpers({
|
||||
show: function () {
|
||||
return game() && game().clock > 0;
|
||||
}
|
||||
});
|
||||
|
||||
Template.scratchpad.events({
|
||||
'click button, keyup input': function (evt) {
|
||||
var textbox = $('#scratchpad input');
|
||||
// if we clicked the button or hit enter
|
||||
if ((evt.type === "click" || (evt.type === "keyup" && evt.which === 13))
|
||||
&& textbox.val()) {
|
||||
var word_id = Words.insert({player_id: Session.get('player_id'),
|
||||
game_id: game() && game()._id,
|
||||
word: textbox.val().toUpperCase(),
|
||||
state: 'pending'});
|
||||
Meteor.call('score_word', word_id);
|
||||
textbox.val('');
|
||||
textbox.focus();
|
||||
clear_selected_positions();
|
||||
} else {
|
||||
set_selected_positions(textbox.val());
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Template.postgame.helpers({
|
||||
show: function () {
|
||||
return game() && game().clock === 0;
|
||||
}
|
||||
});
|
||||
|
||||
Template.postgame.events({
|
||||
'click button': function (evt) {
|
||||
Players.update(Session.get('player_id'), {$set: {game_id: null}});
|
||||
}
|
||||
});
|
||||
|
||||
//////
|
||||
////// scores shows everyone's score and word list.
|
||||
//////
|
||||
|
||||
Template.scores.helpers({
|
||||
show: function () {
|
||||
return !!game();
|
||||
},
|
||||
|
||||
players: function () {
|
||||
return game() && game().players;
|
||||
}
|
||||
});
|
||||
|
||||
Template.player.helpers({
|
||||
winner: function () {
|
||||
var g = game();
|
||||
if (g.winners && _.include(g.winners, this._id))
|
||||
return 'winner';
|
||||
return '';
|
||||
},
|
||||
|
||||
total_score: function () {
|
||||
var words = Words.find({game_id: game() && game()._id,
|
||||
player_id: this._id});
|
||||
|
||||
var score = 0;
|
||||
words.forEach(function (word) {
|
||||
if (word.score)
|
||||
score += word.score;
|
||||
});
|
||||
return score;
|
||||
}
|
||||
});
|
||||
|
||||
Template.words.helpers({
|
||||
words: function () {
|
||||
return Words.find({game_id: game() && game()._id,
|
||||
player_id: this._id});
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
//////
|
||||
////// Initialization
|
||||
//////
|
||||
|
||||
Meteor.startup(function () {
|
||||
// Allocate a new player id.
|
||||
//
|
||||
// XXX this does not handle hot reload. In the reload case,
|
||||
// Session.get('player_id') will return a real id. We should check for
|
||||
// a pre-existing player, and if it exists, make sure the server still
|
||||
// knows about us.
|
||||
var player_id = Players.insert({name: '', idle: false});
|
||||
Session.set('player_id', player_id);
|
||||
|
||||
// subscribe to all the players, the game i'm in, and all
|
||||
// the words in that game.
|
||||
Tracker.autorun(function () {
|
||||
Meteor.subscribe('players');
|
||||
|
||||
if (Session.get('player_id')) {
|
||||
var me = player();
|
||||
if (me && me.game_id) {
|
||||
Meteor.subscribe('games', me.game_id);
|
||||
Meteor.subscribe('words', me.game_id, Session.get('player_id'));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// send keepalives so the server can tell when we go away.
|
||||
//
|
||||
// XXX this is not a great idiom. meteor server does not yet have a
|
||||
// way to expose connection status to user code. Once it does, this
|
||||
// code can go away.
|
||||
Meteor.setInterval(function() {
|
||||
if (Meteor.status().connected)
|
||||
Meteor.call('keepalive', Session.get('player_id'));
|
||||
}, 20*1000);
|
||||
});
|
||||
@@ -1,158 +0,0 @@
|
||||
////////// Shared code (client and server) //////////
|
||||
|
||||
Games = new Mongo.Collection('games');
|
||||
// { board: ['A','I',...], clock: 60,
|
||||
// players: [{player_id, name}], winners: [player_id] }
|
||||
|
||||
Words = new Mongo.Collection('words');
|
||||
// {player_id: 10, game_id: 123, word: 'hello', state: 'good', score: 4}
|
||||
|
||||
Players = new Mongo.Collection('players');
|
||||
// {name: 'matt', game_id: 123}
|
||||
|
||||
// 6 faces per die, 16 dice. Q really means Qu.
|
||||
var DICE = ['PCHOAS', 'OATTOW', 'LRYTTE', 'VTHRWE',
|
||||
'EGHWNE', 'SEOTIS', 'ANAEEG', 'IDSYTT',
|
||||
'MTOICU', 'AFPKFS', 'XLDERI', 'ENSIEU',
|
||||
'YLDEVR', 'ZNRNHL', 'NMIQHU', 'OBBAOJ'];
|
||||
|
||||
var DICTIONARY = null;
|
||||
|
||||
// board is an array of length 16, in row-major order. ADJACENCIES
|
||||
// lists the board positions adjacent to each board position.
|
||||
var ADJACENCIES = [
|
||||
[1,4,5],
|
||||
[0,2,4,5,6],
|
||||
[1,3,5,6,7],
|
||||
[2,6,7],
|
||||
[0,1,5,8,9],
|
||||
[0,1,2,4,6,8,9,10],
|
||||
[1,2,3,5,7,9,10,11],
|
||||
[2,3,6,10,11],
|
||||
[4,5,9,12,13],
|
||||
[4,5,6,8,10,12,13,14],
|
||||
[5,6,7,9,11,13,14,15],
|
||||
[6,7,10,14,15],
|
||||
[8,9,13],
|
||||
[8,9,10,12,14],
|
||||
[9,10,11,13,15],
|
||||
[10,11,14]
|
||||
];
|
||||
|
||||
// generate a new random selection of letters.
|
||||
new_board = function () {
|
||||
var board = [];
|
||||
var i;
|
||||
|
||||
// pick random letter from each die
|
||||
for (i = 0; i < 16; i += 1) {
|
||||
board[i] = Random.choice(DICE[i]);
|
||||
}
|
||||
|
||||
// knuth shuffle
|
||||
for (i = 15; i > 0; i -= 1) {
|
||||
var j = Math.floor(Math.random() * (i + 1));
|
||||
var tmp = board[i];
|
||||
board[i] = board[j];
|
||||
board[j] = tmp;
|
||||
}
|
||||
|
||||
return board;
|
||||
};
|
||||
|
||||
// returns an array of valid paths to make the specified word on the
|
||||
// board. each path is an array of board positions 0-15. a valid
|
||||
// path can use each position only once, and each position must be
|
||||
// adjacent to the previous position.
|
||||
paths_for_word = function (board, word) {
|
||||
var valid_paths = [];
|
||||
|
||||
var check_path = function (word, path, positions_to_try) {
|
||||
// base case: the whole word has been consumed. path is valid.
|
||||
if (word.length === 0) {
|
||||
valid_paths.push(path);
|
||||
return;
|
||||
}
|
||||
|
||||
// otherwise, try to match each available position against the
|
||||
// first letter of the word, avoiding any positions that are
|
||||
// already used by the path. for each of those matches, descend
|
||||
// recursively, passing the remainder of the word, the accumulated
|
||||
// path, and the positions adjacent to the match.
|
||||
|
||||
for (var i = 0; i < positions_to_try.length; i++) {
|
||||
var pos = positions_to_try[i];
|
||||
if (board[pos] === word[0] && _.indexOf(path, pos) === -1)
|
||||
check_path(word.slice(1), // cdr of word
|
||||
path.concat([pos]), // append matching loc to path
|
||||
ADJACENCIES[pos]); // only look at surrounding tiles
|
||||
}
|
||||
};
|
||||
|
||||
// start recursive search w/ full word, empty path, and all tiles
|
||||
// available for the first letter.
|
||||
check_path(word, [], [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]);
|
||||
|
||||
return valid_paths;
|
||||
};
|
||||
|
||||
Meteor.methods({
|
||||
score_word: function (word_id) {
|
||||
check(word_id, String);
|
||||
var word = Words.findOne(word_id);
|
||||
var game = Games.findOne(word.game_id);
|
||||
|
||||
// client and server can both check that the game has time remaining, and
|
||||
// that the word is at least three chars, isn't already used, and is
|
||||
// possible to make on the board.
|
||||
if (game.clock === 0
|
||||
|| !word.word
|
||||
|| word.word.length < 3
|
||||
|| Words.find({game_id: word.game_id, word: word.word}).count() > 1
|
||||
|| paths_for_word(game.board, word.word).length === 0) {
|
||||
Words.update(word._id, {$set: {score: 0, state: 'bad'}});
|
||||
return;
|
||||
}
|
||||
|
||||
// now only on the server, check against dictionary and score it.
|
||||
if (Meteor.isServer) {
|
||||
if (_.has(DICTIONARY, word.word.toLowerCase())) {
|
||||
var score = Math.pow(2, word.word.length - 3);
|
||||
Words.update(word._id, {$set: {score: score, state: 'good'}});
|
||||
} else {
|
||||
Words.update(word._id, {$set: {score: 0, state: 'bad'}});
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
if (Meteor.isServer) {
|
||||
DICTIONARY = {};
|
||||
_.each(Assets.getText("enable2k.txt").split("\n"), function (line) {
|
||||
// Skip blanks and comment lines
|
||||
if (line && line.indexOf("//") !== 0) {
|
||||
DICTIONARY[line] = true;
|
||||
}
|
||||
});
|
||||
|
||||
// publish all the non-idle players.
|
||||
Meteor.publish('players', function () {
|
||||
return Players.find({idle: false});
|
||||
});
|
||||
|
||||
// publish single games
|
||||
Meteor.publish('games', function (id) {
|
||||
check(id, String);
|
||||
return Games.find({_id: id});
|
||||
});
|
||||
|
||||
// publish all my words and opponents' words that the server has
|
||||
// scored as good.
|
||||
Meteor.publish('words', function (game_id, player_id) {
|
||||
check(game_id, String);
|
||||
check(player_id, String);
|
||||
return Words.find({$or: [{game_id: game_id, state: 'good'},
|
||||
{player_id: player_id}]});
|
||||
});
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
Binary file not shown.
|
Before Width: | Height: | Size: 691 B |
@@ -1,70 +0,0 @@
|
||||
////////// Server only logic //////////
|
||||
|
||||
Meteor.methods({
|
||||
start_new_game: function () {
|
||||
// create a new game w/ fresh board
|
||||
var game_id = Games.insert({board: new_board(),
|
||||
clock: 120});
|
||||
|
||||
// move everyone who is ready in the lobby to the game
|
||||
Players.update({game_id: null, idle: false, name: {$ne: ''}},
|
||||
{$set: {game_id: game_id}},
|
||||
{multi: true});
|
||||
// Save a record of who is in the game, so when they leave we can
|
||||
// still show them.
|
||||
var p = Players.find({game_id: game_id},
|
||||
{fields: {_id: true, name: true}}).fetch();
|
||||
Games.update({_id: game_id}, {$set: {players: p}});
|
||||
|
||||
|
||||
// wind down the game clock
|
||||
var clock = 120;
|
||||
var interval = Meteor.setInterval(function () {
|
||||
clock -= 1;
|
||||
Games.update(game_id, {$set: {clock: clock}});
|
||||
|
||||
// end of game
|
||||
if (clock === 0) {
|
||||
// stop the clock
|
||||
Meteor.clearInterval(interval);
|
||||
// declare zero or more winners
|
||||
var scores = {};
|
||||
Words.find({game_id: game_id}).forEach(function (word) {
|
||||
if (!scores[word.player_id])
|
||||
scores[word.player_id] = 0;
|
||||
scores[word.player_id] += word.score;
|
||||
});
|
||||
var high_score = _.max(scores);
|
||||
var winners = [];
|
||||
_.each(scores, function (score, player_id) {
|
||||
if (score === high_score)
|
||||
winners.push(player_id);
|
||||
});
|
||||
Games.update(game_id, {$set: {winners: winners}});
|
||||
}
|
||||
}, 1000);
|
||||
|
||||
return game_id;
|
||||
},
|
||||
|
||||
|
||||
keepalive: function (player_id) {
|
||||
check(player_id, String);
|
||||
Players.update({_id: player_id},
|
||||
{$set: {last_keepalive: (new Date()).getTime(),
|
||||
idle: false}});
|
||||
}
|
||||
});
|
||||
|
||||
Meteor.setInterval(function () {
|
||||
var now = (new Date()).getTime();
|
||||
var idle_threshold = now - 70*1000; // 70 sec
|
||||
var remove_threshold = now - 60*60*1000; // 1hr
|
||||
|
||||
Players.update({last_keepalive: {$lt: idle_threshold}},
|
||||
{$set: {idle: true}});
|
||||
|
||||
// XXX need to deal with people coming back!
|
||||
// Players.remove({$lt: {last_keepalive: remove_threshold}});
|
||||
|
||||
}, 30*1000);
|
||||
@@ -1,113 +0,0 @@
|
||||
require("./words.js");
|
||||
|
||||
var BOGGLE_DICE = ['pchoas', 'oattow', 'lrytte', 'vthrwe',
|
||||
'eghwne', 'seotis', 'anaeeg', 'idsytt',
|
||||
'mtoicu', 'afpkfs', 'xlderi', 'ensieu',
|
||||
'yldevr', 'znrnhl', 'nmiqhu', 'obbaoj'];
|
||||
|
||||
var FLAGS = [0x1, 0x2, 0x4, 0x8,
|
||||
0x10, 0x20, 0x40, 0x80,
|
||||
0x100, 0x200, 0x400, 0x800,
|
||||
0x1000, 0x2000, 0x4000, 0x8000];
|
||||
|
||||
var MASKS = {};
|
||||
|
||||
// generate masks for all one, two, three, and four-letter combinations
|
||||
var make_masks = function () {
|
||||
var mask_count = 0;
|
||||
|
||||
// recursive helper
|
||||
var check_mask = function (word, index, used) {
|
||||
for (var i = 0; i < 16; i += 1) {
|
||||
for (var j = 0; j < 6; j += 1) {
|
||||
// if die i is still available, and it has a letter that
|
||||
// matches what we need, we can make use of this.
|
||||
if (!(used & FLAGS[i]) && (BOGGLE_DICE[i][j] === word[index])) {
|
||||
if (word.length === index + 1) {
|
||||
// this is the end of the word, we have a valid mask!
|
||||
if (!MASKS[word])
|
||||
MASKS[word] = [];
|
||||
if (MASKS[word].indexOf(used | FLAGS[i]) === -1) {
|
||||
MASKS[word].push(used | FLAGS[i]);
|
||||
mask_count += 1;
|
||||
}
|
||||
// console.log("MASK", word, used | FLAGS[i]);
|
||||
} else {
|
||||
// descend into searching rest of word w/ remaining dice.
|
||||
check_mask(word,
|
||||
index + 1,
|
||||
used | FLAGS[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
process.stderr.write("CALCULATING MASKS FOR ");
|
||||
for (var a = 97; a <= 97; a += 1) {
|
||||
process.stderr.write(String.fromCharCode(a));
|
||||
check_mask(String.fromCharCode(a), 0, 0x0);
|
||||
for (var b = 97; b <= 122; b += 1) {
|
||||
check_mask(String.fromCharCode(a,b), 0, 0x0);
|
||||
for (var c = 97; c <= 122; c += 1) {
|
||||
check_mask(String.fromCharCode(a,b,c), 0, 0x0);
|
||||
for (var d = 97; d <= 122; d += 1) {
|
||||
check_mask(String.fromCharCode(a,b,c,d), 0, 0x0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
process.stderr.write(" DONE [" + mask_count + " MASKS]\n");
|
||||
};
|
||||
|
||||
make_masks();
|
||||
|
||||
function check (word, index, used) {
|
||||
//console.log('CHECK', word, index, used);
|
||||
|
||||
var length;
|
||||
var masks;
|
||||
|
||||
// check up to 4 chars at a time
|
||||
length = (word.length - index > 4) ? 4 : word.length - index;
|
||||
masks = MASKS[word.slice(index, length + index)] || [];
|
||||
|
||||
for (var i = 0; i < masks.length; i += 1) {
|
||||
if (!(used & masks[i])) {
|
||||
// masks[i] has no overlap w/ tiles we already consumed.
|
||||
if (word.length === index + length)
|
||||
// we consumed the whole word.
|
||||
return true;
|
||||
else if (check(word, index + length, used | masks[i]))
|
||||
// some descendant consumed the whole word
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// none of the available masks returned true. there's no match.
|
||||
return false;
|
||||
};
|
||||
|
||||
var dict_len = DICTIONARY.length
|
||||
for (var i = 0; i < dict_len; i+=1) {
|
||||
var word = DICTIONARY[i];
|
||||
|
||||
// reject words that have q followed by non-u. those can't be made
|
||||
// in boggle. otherwise, strip the q -- our dictionary won't
|
||||
// include the u.
|
||||
|
||||
if (word.match(/q/)) {
|
||||
if (word.match(/q[^u]/)) {
|
||||
process.stderr.write('Q REJECT ' + word + '\n');
|
||||
continue;
|
||||
} else {
|
||||
word = word.replace('qu', 'q');
|
||||
process.stderr.write('Q REPLACED ' + word + '\n');
|
||||
}
|
||||
}
|
||||
|
||||
if (check(word, 0, 0x0))
|
||||
console.log(word);
|
||||
else
|
||||
process.stderr.write('REJECT ' + DICTIONARY[i] + '\n');
|
||||
};
|
||||
@@ -1 +0,0 @@
|
||||
local
|
||||
@@ -1,18 +0,0 @@
|
||||
# Meteor packages used by this project, one per line.
|
||||
#
|
||||
# 'meteor add' and 'meteor remove' will edit this file for you,
|
||||
# but you can also edit it by hand.
|
||||
|
||||
autopublish
|
||||
insecure
|
||||
preserve-inputs
|
||||
accounts-ui
|
||||
less
|
||||
accounts-google
|
||||
accounts-github
|
||||
accounts-password
|
||||
accounts-facebook
|
||||
standard-app-packages
|
||||
facebook-config-ui
|
||||
github-config-ui
|
||||
google-config-ui
|
||||
@@ -1 +0,0 @@
|
||||
0.6.1
|
||||
@@ -1,131 +0,0 @@
|
||||
<head>
|
||||
<title>accounts-ui-viewer</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
{{> page}}
|
||||
</body>
|
||||
|
||||
<template name="radio">
|
||||
<span class="radio">
|
||||
<input id="{{key}}:{{value}}" {{maybeChecked}} type="radio" name="{{key}}" value="{{value}}" />
|
||||
<label for="{{key}}:{{value}}">{{label}}</label>
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<template name="button">
|
||||
<button>{{label}}</button>
|
||||
</template>
|
||||
|
||||
<template name="page">
|
||||
<div id="controlpane">
|
||||
<div class="group">
|
||||
<h3>Dropdown align edge:</h3>
|
||||
{{> radio key="alignRight" value="false" label="Left"}}
|
||||
{{> radio key="alignRight" value="true" label="Right"}}
|
||||
</div>
|
||||
<div class="group">
|
||||
<h3>Positioning:</h3>
|
||||
{{> radio key="positioning" value="relative" label="Relative"}}
|
||||
{{> radio key="positioning" value="absolute" label="Absolute"}}
|
||||
{{> radio key="positioning" value="floatRight" label="Float:right"}}
|
||||
{{> radio key="positioning" value="inline" label="Inline"}}
|
||||
</div>
|
||||
<div class="group">
|
||||
<h3>How many third-party services?</h3>
|
||||
{{> radio key="numServices" value="0" label="0"}}
|
||||
{{> radio key="numServices" value="1" label="1"}}
|
||||
{{> radio key="numServices" value="2" label="2"}}
|
||||
{{> radio key="numServices" value="3" label="3"}}
|
||||
</div>
|
||||
<div class="group">
|
||||
<h3>Has password accounts?</h3>
|
||||
{{> radio key="hasPasswords" value="false" label="No"}}
|
||||
{{> radio key="hasPasswords" value="true" label="Yes"}}
|
||||
</div>
|
||||
<div class="group">
|
||||
<h3>Password sign-up fields:</h3>
|
||||
{{> radio key="signupFields" value="EMAIL_ONLY" label="Email"}}
|
||||
{{> radio key="signupFields" value="USERNAME_ONLY" label="Username"}}
|
||||
{{> radio key="signupFields" value="USERNAME_AND_EMAIL" label="Username & Email"}}
|
||||
{{> radio key="signupFields" value="USERNAME_AND_OPTIONAL_EMAIL" label="Username & Optional Email"}}
|
||||
</div>
|
||||
<div class="group">
|
||||
<h3>Fake-Configure:</h3>
|
||||
{{> button key="fakeConfig" value="facebook" label="Facebook"}}
|
||||
{{> button key="fakeConfig" value="github" label="GitHub"}}
|
||||
{{> button key="fakeConfig" value="google" label="Google"}}
|
||||
</div>
|
||||
<div class="group">
|
||||
<h3>Show Configure Dialog:</h3>
|
||||
{{> button key="showConfig" value="facebook" label="Facebook"}}
|
||||
{{> button key="showConfig" value="github" label="GitHub"}}
|
||||
{{> button key="showConfig" value="google" label="Google"}}
|
||||
</div>
|
||||
<div class="group">
|
||||
<h3>Unconfigure:</h3>
|
||||
{{> button key="unconfig" value="facebook" label="Facebook"}}
|
||||
{{> button key="unconfig" value="github" label="GitHub"}}
|
||||
{{> button key="unconfig" value="google" label="Google"}}
|
||||
</div>
|
||||
<div class="group">
|
||||
<h3>Messages:</h3>
|
||||
{{> button key="messages" value="error" label="Error"}}
|
||||
{{> button key="messages" value="info" label="Info"}}
|
||||
{{> button key="messages" value="clear" label="Clear"}}
|
||||
</div>
|
||||
<div class="group">
|
||||
<h3>Signing in/out</h3>
|
||||
{{> button key="sign" value="in" label="Fake sign-in"}}
|
||||
{{> button key="sign" value="out" label="Sign out"}}
|
||||
</div>
|
||||
<div class="group">
|
||||
<h3>Logged-out Views</h3>
|
||||
{{> button key="lov" value="signIn" label="Sign In"}}
|
||||
{{> button key="lov" value="createAccount" label="Create Account"}}
|
||||
{{> button key="lov" value="forgotPassword" label="Forgot Password"}}
|
||||
</div>
|
||||
<div class="group">
|
||||
<h3>Logged-in Views</h3>
|
||||
{{> button key="liv" value="accountButtons" label="Account Buttons"}}
|
||||
{{> button key="liv" value="changePassword" label="Change Password"}}
|
||||
{{> button key="liv" value="messageOnly" label="Message Only"}}
|
||||
</div>
|
||||
<div class="group">
|
||||
<h3>Other Modals</h3>
|
||||
{{> button key="modals" value="resetPassword" label="Reset Password"}}
|
||||
{{> button key="modals" value="enrollAccount" label="Enroll Account"}}
|
||||
{{> button key="modals" value="justVerifiedEmail" label="Verified Email"}}
|
||||
</div>
|
||||
<div class="group">
|
||||
<h3>Logging-in Spinner</h3>
|
||||
{{> radio key="fakeLoggingIn" value="false" label="Off"}}
|
||||
{{> radio key="fakeLoggingIn" value="true" label="Pretend loggingIn=true"}}
|
||||
</div>
|
||||
<div class="group">
|
||||
<h3>Background Color</h3>
|
||||
{{> radio key="bgcolor" value="white" label="White"}}
|
||||
{{> radio key="bgcolor" value="black" label="Black"}}
|
||||
{{> radio key="bgcolor" value="red" label="Red"}}
|
||||
</div>
|
||||
</div>
|
||||
{{#with settings}}
|
||||
<div id="previewpane" class="{{settingsClass}}" style="background:{{bgcolor}}">
|
||||
<div id="preview-wrapper">
|
||||
{{#if match "positioning:inline"}}
|
||||
Here is a place to sign in, yay!
|
||||
{{/if}}
|
||||
{{> loginButtons align=dropdownAlign}}
|
||||
{{#if match "positioning:inline"}}
|
||||
Isn't that great?
|
||||
{{/if}}
|
||||
</div>
|
||||
<div id="pos-indicator"></div>
|
||||
{{#unless match "positioning:absolute"}}
|
||||
<div style="clear:both">
|
||||
A line that shouldn't move when logging in and logging out
|
||||
</div>
|
||||
{{/unless}}
|
||||
</div>
|
||||
{{/with}}
|
||||
</template>
|
||||
@@ -1,221 +0,0 @@
|
||||
|
||||
Meteor.users.allow({ update: () => true });
|
||||
|
||||
const { ServiceConfiguration } = Package['service-configuration'];
|
||||
|
||||
Meteor.methods({
|
||||
'removeService': service => ServiceConfiguration.configurations.remove({ service }),
|
||||
})
|
||||
|
||||
if (Meteor.isClient) {
|
||||
|
||||
Accounts.STASH = { ...Accounts };
|
||||
Accounts.STASH.loggingIn = Meteor.loggingIn;
|
||||
|
||||
const handleSetting = (key, value) => {
|
||||
if (key === "numServices") {
|
||||
const registeredServices = Accounts.oauth.serviceNames();
|
||||
['facebook', 'github', 'google'].forEach((serv, i) => {
|
||||
if (i < value && !registeredServices.includes(serv)) {
|
||||
Accounts.oauth.registerService(serv);
|
||||
} else if (i >= value && registeredServices.includes(serv)) {
|
||||
Accounts.oauth.unregisterService(serv);
|
||||
}
|
||||
});
|
||||
} else if (key === "hasPasswords") {
|
||||
Package['accounts-password'] = value ? {} : null;
|
||||
const user = Meteor.user();
|
||||
if (user) {
|
||||
if (! value) {
|
||||
// make sure we have no username if "app" has no passwords
|
||||
Meteor.users.update(Meteor.userId(),
|
||||
{ $unset: { username: 1 }});
|
||||
} else {
|
||||
// make sure we have a username
|
||||
Meteor.users.update(Meteor.userId(),
|
||||
{ $set: { username: Random.id() }});
|
||||
}
|
||||
}
|
||||
} else if (key === "signupFields") {
|
||||
Accounts.ui._options.passwordSignupFields = value;
|
||||
} else if (key === "fakeLoggingIn") {
|
||||
Meteor.loggingIn = (value ? () => true :
|
||||
Accounts.STASH.loggingIn);
|
||||
}
|
||||
};
|
||||
|
||||
const settings = Session.get('settings');
|
||||
if (! settings) {
|
||||
Session.set('settings', {
|
||||
alignRight: false,
|
||||
positioning: "relative",
|
||||
numServices: 3,
|
||||
hasPasswords: true,
|
||||
signupFields: 'EMAIL_ONLY',
|
||||
fakeLoggingIn: false,
|
||||
bgcolor: 'white'
|
||||
});
|
||||
} else {
|
||||
Object.keys(settings).forEach(key => handleSetting(key, settings[key]));
|
||||
}
|
||||
|
||||
Template.page.helpers({
|
||||
settings: () => Session.get('settings'),
|
||||
settingsClass: () => {
|
||||
var settings = Session.get('settings');
|
||||
var classes = [];
|
||||
if (settings.positioning)
|
||||
classes.push('positioning-' + settings.positioning.toLowerCase());
|
||||
return classes.join(' ');
|
||||
},
|
||||
match: kv => {
|
||||
kv = keyValueFromId(kv);
|
||||
if (! kv)
|
||||
return false;
|
||||
|
||||
return Session.get('settings')[kv[0]] === kv[1];
|
||||
},
|
||||
dropdownAlign: function() {
|
||||
var settings = this;
|
||||
return settings.alignRight ? 'right' : 'left';
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
var keyValueFromId = function (id) {
|
||||
var match;
|
||||
if (id && (match = /^(.*?):(.*)$/.exec(id))) {
|
||||
var key = match[1];
|
||||
var value = castValue(match[2]);
|
||||
return [key, value];
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
const castValue = value => {
|
||||
if (value === "false")
|
||||
value = false;
|
||||
else if (value === "true")
|
||||
value = true;
|
||||
else if (/^[0-9]+$/.test(value))
|
||||
value = Number(value);
|
||||
return value;
|
||||
};
|
||||
|
||||
Template.radio.helpers({
|
||||
maybeChecked: function() {
|
||||
var curValue = Session.get('settings')[this.key];
|
||||
if (castValue(this.value) === curValue)
|
||||
return 'checked';
|
||||
return '';
|
||||
},
|
||||
});
|
||||
|
||||
const fakeLogin = callback => {
|
||||
Accounts.createUser(
|
||||
{username: Random.id(),
|
||||
password: "password",
|
||||
profile: { name: "Joe Schmoe" }},
|
||||
() => {
|
||||
var user = Meteor.user();
|
||||
if (! user)
|
||||
return;
|
||||
// delete our username if we are in a mode
|
||||
// where there aren't usernames/emails/passwords
|
||||
// (only third-party auth) so that there is no
|
||||
// "Change Password" button when signed in
|
||||
if (! Session.get('settings').hasPasswords)
|
||||
Meteor.users.update(Meteor.userId(),
|
||||
{ $unset: { username: 1 }});
|
||||
callback();
|
||||
});
|
||||
};
|
||||
|
||||
const exitFlows = () => {
|
||||
Accounts._loginButtonsSession.set('inSignupFlow', false);
|
||||
Accounts._loginButtonsSession.set('inForgotPasswordFlow', false);
|
||||
Accounts._loginButtonsSession.set('inChangePasswordFlow', false);
|
||||
Accounts._loginButtonsSession.set('inMessageOnlyFlow', false);
|
||||
};
|
||||
|
||||
Template.page.events({
|
||||
'change #controlpane input[type=radio]': event => {
|
||||
const input = event.currentTarget;
|
||||
let keyValue;
|
||||
if (input && input.id && (keyValue = keyValueFromId(input.id))) {
|
||||
const key = keyValue[0];
|
||||
const value = keyValue[1];
|
||||
if (value === "false")
|
||||
value = false;
|
||||
else if (value === "true")
|
||||
value = true;
|
||||
const settings = Session.get('settings');
|
||||
settings[key] = value;
|
||||
Session.set('settings', settings);
|
||||
|
||||
handleSetting(key, value);
|
||||
}
|
||||
},
|
||||
'click #controlpane button': function (event) {
|
||||
const { ServiceConfiguration } = Package['service-configuration'];
|
||||
if (this.key === "fakeConfig") {
|
||||
const service = this.value;
|
||||
if (! ServiceConfiguration.configurations.findOne({ service }))
|
||||
ServiceConfiguration.configurations.insert(
|
||||
{ service, fake: true });
|
||||
} else if (this.key === "unconfig") {
|
||||
const service = this.value;
|
||||
Meteor.call('removeService', service);
|
||||
} else if (this.key === "messages") {
|
||||
if (this.value === "error") {
|
||||
Accounts._loginButtonsSession.errorMessage('An error occurred! Gee golly gosh.');
|
||||
} else if (this.value === "info") {
|
||||
Accounts._loginButtonsSession.infoMessage('Here is some information that is crucial.');
|
||||
} else if (this.value === "clear") {
|
||||
Accounts._loginButtonsSession.resetMessages();
|
||||
}
|
||||
} else if (this.key === "sign") {
|
||||
if (this.value === 'in') {
|
||||
// create a random new user
|
||||
fakeLogin(function () {
|
||||
Accounts._loginButtonsSession.closeDropdown();
|
||||
});
|
||||
} else if (this.value === 'out') {
|
||||
Meteor.logout();
|
||||
}
|
||||
} else if (this.key === "showConfig") {
|
||||
Accounts._loginButtonsSession.configureService(this.value);
|
||||
} else if (this.key === "lov") {
|
||||
exitFlows();
|
||||
Accounts._loginButtonsSession.set("dropdownVisible", true);
|
||||
if (Meteor.userId())
|
||||
Meteor.logout();
|
||||
if (this.value === "createAccount")
|
||||
Accounts._loginButtonsSession.set("inSignupFlow", true);
|
||||
else if (this.value === "forgotPassword")
|
||||
Accounts._loginButtonsSession.set("inForgotPasswordFlow", true);
|
||||
} else if (this.key === "liv") {
|
||||
exitFlows();
|
||||
Accounts._loginButtonsSession.set("dropdownVisible", true);
|
||||
if (! Meteor.userId())
|
||||
fakeLogin(() => {});
|
||||
if (this.value === "changePassword")
|
||||
Accounts._loginButtonsSession.set("inChangePasswordFlow", true);
|
||||
else if (this.value === "messageOnly")
|
||||
Accounts._loginButtonsSession.set("inMessageOnlyFlow", true);
|
||||
} else if (this.key === "modals") {
|
||||
const { value } = this;
|
||||
[
|
||||
'resetPasswordToken',
|
||||
'enrollAccountToken',
|
||||
'justVerifiedEmail'
|
||||
].forEach(k => {
|
||||
Accounts._loginButtonsSession.set(
|
||||
k, k.indexOf(value) >= 0 ? 'foo' : null
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
@@ -1,108 +0,0 @@
|
||||
html, body { height: 100%; }
|
||||
|
||||
#controlpane {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
width: 299px;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
|
||||
background: #eee;
|
||||
border-right: 1px solid #999;
|
||||
|
||||
overflow: auto;
|
||||
|
||||
h3 {
|
||||
border-top: 1px solid #999;
|
||||
font-size: 85%;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.group {
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
input[type=radio] {
|
||||
margin-left: 5px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
label {
|
||||
padding-left: 3px;
|
||||
}
|
||||
}
|
||||
|
||||
#previewpane {
|
||||
position: absolute;
|
||||
left: 300px;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
|
||||
#preview-wrapper {
|
||||
margin: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.radio {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.positioning-floatright {
|
||||
#login-buttons {
|
||||
float: right;
|
||||
margin-right: 180px;
|
||||
}
|
||||
|
||||
#pos-indicator {
|
||||
display: block;
|
||||
top: 0;
|
||||
right: 0;
|
||||
width: 200px;
|
||||
height: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.positioning-relative {
|
||||
#login-buttons {
|
||||
position: relative;
|
||||
left: 150px;
|
||||
top: 20px;
|
||||
}
|
||||
|
||||
#pos-indicator {
|
||||
display: block;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 170px;
|
||||
height: 40px;
|
||||
}
|
||||
}
|
||||
|
||||
.positioning-absolute {
|
||||
#login-buttons {
|
||||
position: absolute;
|
||||
left: 170px;
|
||||
top: 40px;
|
||||
}
|
||||
|
||||
#pos-indicator {
|
||||
display: block;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 170px;
|
||||
height: 40px;
|
||||
}
|
||||
}
|
||||
|
||||
#pos-indicator {
|
||||
position: absolute;
|
||||
background: #eec;
|
||||
display: none;
|
||||
}
|
||||
|
||||
a { color: blue; }
|
||||
|
||||
button { padding: 4px;
|
||||
margin-bottom: 4px; // for when buttons wrap
|
||||
}
|
||||
682
examples/unfinished/accounts-ui-viewer/package-lock.json
generated
682
examples/unfinished/accounts-ui-viewer/package-lock.json
generated
@@ -1,682 +0,0 @@
|
||||
{
|
||||
"requires": true,
|
||||
"lockfileVersion": 1,
|
||||
"dependencies": {
|
||||
"@babel/runtime": {
|
||||
"version": "7.0.0-beta.38",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.0.0-beta.38.tgz",
|
||||
"integrity": "sha512-ZvPtlcvH2ZRzr1U5pkmCE7U3RIun3Nf29XHem47aScmJgMuL06ulkp+4oPBee3QrUVFErDjwNWtC67BzNuxLVw==",
|
||||
"requires": {
|
||||
"core-js": "2.5.3",
|
||||
"regenerator-runtime": "0.11.1"
|
||||
}
|
||||
},
|
||||
"core-js": {
|
||||
"version": "2.5.3",
|
||||
"resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.3.tgz",
|
||||
"integrity": "sha1-isw4NFgk8W2DZbfJtCWRaOjtYD4="
|
||||
},
|
||||
"meteor-node-stubs": {
|
||||
"version": "0.3.2",
|
||||
"resolved": "https://registry.npmjs.org/meteor-node-stubs/-/meteor-node-stubs-0.3.2.tgz",
|
||||
"integrity": "sha512-l93SS/HutbqBRJODO2m7hup8cYI2acF5bB39+ZvP2BX8HMmCSCXeFH7v0sr4hD7zrVvHQA5UqS0pcDYKn0VM6g==",
|
||||
"requires": {
|
||||
"assert": "1.4.1",
|
||||
"browserify-zlib": "0.1.4",
|
||||
"buffer": "4.9.1",
|
||||
"console-browserify": "1.1.0",
|
||||
"constants-browserify": "1.0.0",
|
||||
"crypto-browserify": "3.11.1",
|
||||
"domain-browser": "1.1.7",
|
||||
"events": "1.1.1",
|
||||
"http-browserify": "1.7.0",
|
||||
"https-browserify": "0.0.1",
|
||||
"os-browserify": "0.2.1",
|
||||
"path-browserify": "0.0.0",
|
||||
"process": "0.11.10",
|
||||
"punycode": "1.4.1",
|
||||
"querystring-es3": "0.2.1",
|
||||
"readable-stream": "git+https://github.com/meteor/readable-stream.git#d64a64aa6061b9b6855feff4d09e58fb3b2e4502",
|
||||
"stream-browserify": "2.0.1",
|
||||
"string_decoder": "1.0.3",
|
||||
"timers-browserify": "1.4.2",
|
||||
"tty-browserify": "0.0.0",
|
||||
"url": "0.11.0",
|
||||
"util": "0.10.3",
|
||||
"vm-browserify": "0.0.4"
|
||||
},
|
||||
"dependencies": {
|
||||
"Base64": {
|
||||
"version": "0.2.1",
|
||||
"resolved": "https://registry.npmjs.org/Base64/-/Base64-0.2.1.tgz",
|
||||
"integrity": "sha1-ujpCMHCOGGcFBl5mur3Uw1z2ACg="
|
||||
},
|
||||
"asn1.js": {
|
||||
"version": "4.9.1",
|
||||
"resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.9.1.tgz",
|
||||
"integrity": "sha1-SLokC0WpKA6UdImQull9IWYX/UA=",
|
||||
"requires": {
|
||||
"bn.js": "4.11.8",
|
||||
"inherits": "2.0.1",
|
||||
"minimalistic-assert": "1.0.0"
|
||||
}
|
||||
},
|
||||
"assert": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/assert/-/assert-1.4.1.tgz",
|
||||
"integrity": "sha1-mZEtWRg2tab1s0XA8H7vwI/GXZE=",
|
||||
"requires": {
|
||||
"util": "0.10.3"
|
||||
}
|
||||
},
|
||||
"balanced-match": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
|
||||
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c="
|
||||
},
|
||||
"base64-js": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.2.1.tgz",
|
||||
"integrity": "sha512-dwVUVIXsBZXwTuwnXI9RK8sBmgq09NDHzyR9SAph9eqk76gKK2JSQmZARC2zRC81JC2QTtxD0ARU5qTS25gIGw=="
|
||||
},
|
||||
"bn.js": {
|
||||
"version": "4.11.8",
|
||||
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz",
|
||||
"integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA=="
|
||||
},
|
||||
"brace-expansion": {
|
||||
"version": "1.1.8",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz",
|
||||
"integrity": "sha1-wHshHHyVLsH479Uad+8NHTmQopI=",
|
||||
"requires": {
|
||||
"balanced-match": "1.0.0",
|
||||
"concat-map": "0.0.1"
|
||||
}
|
||||
},
|
||||
"brorand": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz",
|
||||
"integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8="
|
||||
},
|
||||
"browserify-aes": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.1.0.tgz",
|
||||
"integrity": "sha512-W2bIMLYoZ9oow7TyePpMJk9l9LY7O3R61a/68bVCDOtnJynnwe3ZeW2IzzSkrQnPKNdJrxVDn3ALZNisSBwb7g==",
|
||||
"requires": {
|
||||
"buffer-xor": "1.0.3",
|
||||
"cipher-base": "1.0.4",
|
||||
"create-hash": "1.1.3",
|
||||
"evp_bytestokey": "1.0.3",
|
||||
"inherits": "2.0.1",
|
||||
"safe-buffer": "5.1.1"
|
||||
}
|
||||
},
|
||||
"browserify-cipher": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.0.tgz",
|
||||
"integrity": "sha1-mYgkSHS/XtTijalWZtzWasj8Njo=",
|
||||
"requires": {
|
||||
"browserify-aes": "1.1.0",
|
||||
"browserify-des": "1.0.0",
|
||||
"evp_bytestokey": "1.0.3"
|
||||
}
|
||||
},
|
||||
"browserify-des": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.0.tgz",
|
||||
"integrity": "sha1-2qJ3cXRwki7S/hhZQRihdUOXId0=",
|
||||
"requires": {
|
||||
"cipher-base": "1.0.4",
|
||||
"des.js": "1.0.0",
|
||||
"inherits": "2.0.1"
|
||||
}
|
||||
},
|
||||
"browserify-rsa": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz",
|
||||
"integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=",
|
||||
"requires": {
|
||||
"bn.js": "4.11.8",
|
||||
"randombytes": "2.0.5"
|
||||
}
|
||||
},
|
||||
"browserify-sign": {
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.0.4.tgz",
|
||||
"integrity": "sha1-qk62jl17ZYuqa/alfmMMvXqT0pg=",
|
||||
"requires": {
|
||||
"bn.js": "4.11.8",
|
||||
"browserify-rsa": "4.0.1",
|
||||
"create-hash": "1.1.3",
|
||||
"create-hmac": "1.1.6",
|
||||
"elliptic": "6.4.0",
|
||||
"inherits": "2.0.1",
|
||||
"parse-asn1": "5.1.0"
|
||||
}
|
||||
},
|
||||
"browserify-zlib": {
|
||||
"version": "0.1.4",
|
||||
"resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.1.4.tgz",
|
||||
"integrity": "sha1-uzX4pRn2AOD6a4SFJByXnQFB+y0=",
|
||||
"requires": {
|
||||
"pako": "0.2.9"
|
||||
}
|
||||
},
|
||||
"buffer": {
|
||||
"version": "4.9.1",
|
||||
"resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz",
|
||||
"integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=",
|
||||
"requires": {
|
||||
"base64-js": "1.2.1",
|
||||
"ieee754": "1.1.8",
|
||||
"isarray": "1.0.0"
|
||||
}
|
||||
},
|
||||
"buffer-xor": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz",
|
||||
"integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk="
|
||||
},
|
||||
"cipher-base": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz",
|
||||
"integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==",
|
||||
"requires": {
|
||||
"inherits": "2.0.1",
|
||||
"safe-buffer": "5.1.1"
|
||||
}
|
||||
},
|
||||
"concat-map": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
||||
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
|
||||
},
|
||||
"console-browserify": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz",
|
||||
"integrity": "sha1-8CQcRXMKn8YyOyBtvzjtx0HQuxA=",
|
||||
"requires": {
|
||||
"date-now": "0.1.4"
|
||||
}
|
||||
},
|
||||
"constants-browserify": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz",
|
||||
"integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U="
|
||||
},
|
||||
"create-ecdh": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.0.tgz",
|
||||
"integrity": "sha1-iIxyNZbN92EvZJgjPuvXo1MBc30=",
|
||||
"requires": {
|
||||
"bn.js": "4.11.8",
|
||||
"elliptic": "6.4.0"
|
||||
}
|
||||
},
|
||||
"create-hash": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.1.3.tgz",
|
||||
"integrity": "sha1-YGBCrIuSYnUPSDyt2rD1gZFy2P0=",
|
||||
"requires": {
|
||||
"cipher-base": "1.0.4",
|
||||
"inherits": "2.0.1",
|
||||
"ripemd160": "2.0.1",
|
||||
"sha.js": "2.4.9"
|
||||
}
|
||||
},
|
||||
"create-hmac": {
|
||||
"version": "1.1.6",
|
||||
"resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.6.tgz",
|
||||
"integrity": "sha1-rLniIaThe9sHbpBlfEK5PjcmzwY=",
|
||||
"requires": {
|
||||
"cipher-base": "1.0.4",
|
||||
"create-hash": "1.1.3",
|
||||
"inherits": "2.0.1",
|
||||
"ripemd160": "2.0.1",
|
||||
"safe-buffer": "5.1.1",
|
||||
"sha.js": "2.4.9"
|
||||
}
|
||||
},
|
||||
"crypto-browserify": {
|
||||
"version": "3.11.1",
|
||||
"resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.11.1.tgz",
|
||||
"integrity": "sha512-Na7ZlwCOqoaW5RwUK1WpXws2kv8mNhWdTlzob0UXulk6G9BDbyiJaGTYBIX61Ozn9l1EPPJpICZb4DaOpT9NlQ==",
|
||||
"requires": {
|
||||
"browserify-cipher": "1.0.0",
|
||||
"browserify-sign": "4.0.4",
|
||||
"create-ecdh": "4.0.0",
|
||||
"create-hash": "1.1.3",
|
||||
"create-hmac": "1.1.6",
|
||||
"diffie-hellman": "5.0.2",
|
||||
"inherits": "2.0.1",
|
||||
"pbkdf2": "3.0.14",
|
||||
"public-encrypt": "4.0.0",
|
||||
"randombytes": "2.0.5"
|
||||
}
|
||||
},
|
||||
"date-now": {
|
||||
"version": "0.1.4",
|
||||
"resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz",
|
||||
"integrity": "sha1-6vQ5/U1ISK105cx9vvIAZyueNFs="
|
||||
},
|
||||
"des.js": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.0.tgz",
|
||||
"integrity": "sha1-wHTS4qpqipoH29YfmhXCzYPsjsw=",
|
||||
"requires": {
|
||||
"inherits": "2.0.1",
|
||||
"minimalistic-assert": "1.0.0"
|
||||
}
|
||||
},
|
||||
"diffie-hellman": {
|
||||
"version": "5.0.2",
|
||||
"resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.2.tgz",
|
||||
"integrity": "sha1-tYNXOScM/ias9jIJn97SoH8gnl4=",
|
||||
"requires": {
|
||||
"bn.js": "4.11.8",
|
||||
"miller-rabin": "4.0.1",
|
||||
"randombytes": "2.0.5"
|
||||
}
|
||||
},
|
||||
"domain-browser": {
|
||||
"version": "1.1.7",
|
||||
"resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.1.7.tgz",
|
||||
"integrity": "sha1-hnqksJP6oF8d4IwG9NeyH9+GmLw="
|
||||
},
|
||||
"elliptic": {
|
||||
"version": "6.4.0",
|
||||
"resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.4.0.tgz",
|
||||
"integrity": "sha1-ysmvh2LIWDYYcAPI3+GT5eLq5d8=",
|
||||
"requires": {
|
||||
"bn.js": "4.11.8",
|
||||
"brorand": "1.1.0",
|
||||
"hash.js": "1.1.3",
|
||||
"hmac-drbg": "1.0.1",
|
||||
"inherits": "2.0.1",
|
||||
"minimalistic-assert": "1.0.0",
|
||||
"minimalistic-crypto-utils": "1.0.1"
|
||||
}
|
||||
},
|
||||
"events": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz",
|
||||
"integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ="
|
||||
},
|
||||
"evp_bytestokey": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz",
|
||||
"integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==",
|
||||
"requires": {
|
||||
"md5.js": "1.3.4",
|
||||
"safe-buffer": "5.1.1"
|
||||
}
|
||||
},
|
||||
"fs.realpath": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
|
||||
"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
|
||||
},
|
||||
"glob": {
|
||||
"version": "7.1.2",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz",
|
||||
"integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==",
|
||||
"requires": {
|
||||
"fs.realpath": "1.0.0",
|
||||
"inflight": "1.0.6",
|
||||
"inherits": "2.0.1",
|
||||
"minimatch": "3.0.4",
|
||||
"once": "1.4.0",
|
||||
"path-is-absolute": "1.0.1"
|
||||
}
|
||||
},
|
||||
"hash-base": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/hash-base/-/hash-base-2.0.2.tgz",
|
||||
"integrity": "sha1-ZuodhW206KVHDK32/OI65SRO8uE=",
|
||||
"requires": {
|
||||
"inherits": "2.0.1"
|
||||
}
|
||||
},
|
||||
"hash.js": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.3.tgz",
|
||||
"integrity": "sha512-/UETyP0W22QILqS+6HowevwhEFJ3MBJnwTf75Qob9Wz9t0DPuisL8kW8YZMK62dHAKE1c1p+gY1TtOLY+USEHA==",
|
||||
"requires": {
|
||||
"inherits": "2.0.3",
|
||||
"minimalistic-assert": "1.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"inherits": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
|
||||
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
|
||||
}
|
||||
}
|
||||
},
|
||||
"hmac-drbg": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz",
|
||||
"integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=",
|
||||
"requires": {
|
||||
"hash.js": "1.1.3",
|
||||
"minimalistic-assert": "1.0.0",
|
||||
"minimalistic-crypto-utils": "1.0.1"
|
||||
}
|
||||
},
|
||||
"http-browserify": {
|
||||
"version": "1.7.0",
|
||||
"resolved": "https://registry.npmjs.org/http-browserify/-/http-browserify-1.7.0.tgz",
|
||||
"integrity": "sha1-M3la3nLfiKz7/TZ3PO/tp2RzWyA=",
|
||||
"requires": {
|
||||
"Base64": "0.2.1",
|
||||
"inherits": "2.0.1"
|
||||
}
|
||||
},
|
||||
"https-browserify": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-0.0.1.tgz",
|
||||
"integrity": "sha1-P5E2XKvmC3ftDruiS0VOPgnZWoI="
|
||||
},
|
||||
"ieee754": {
|
||||
"version": "1.1.8",
|
||||
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.8.tgz",
|
||||
"integrity": "sha1-vjPUCsEO8ZJnAfbwii2G+/0a0+Q="
|
||||
},
|
||||
"indexof": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz",
|
||||
"integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10="
|
||||
},
|
||||
"inflight": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
|
||||
"integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
|
||||
"requires": {
|
||||
"once": "1.4.0",
|
||||
"wrappy": "1.0.2"
|
||||
}
|
||||
},
|
||||
"inherits": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz",
|
||||
"integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE="
|
||||
},
|
||||
"isarray": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
|
||||
"integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
|
||||
},
|
||||
"md5.js": {
|
||||
"version": "1.3.4",
|
||||
"resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.4.tgz",
|
||||
"integrity": "sha1-6b296UogpawYsENA/Fdk1bCdkB0=",
|
||||
"requires": {
|
||||
"hash-base": "3.0.4",
|
||||
"inherits": "2.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"hash-base": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz",
|
||||
"integrity": "sha1-X8hoaEfs1zSZQDMZprCj8/auSRg=",
|
||||
"requires": {
|
||||
"inherits": "2.0.1",
|
||||
"safe-buffer": "5.1.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"miller-rabin": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz",
|
||||
"integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==",
|
||||
"requires": {
|
||||
"bn.js": "4.11.8",
|
||||
"brorand": "1.1.0"
|
||||
}
|
||||
},
|
||||
"minimalistic-assert": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.0.tgz",
|
||||
"integrity": "sha1-cCvi3aazf0g2vLP121ZkG2Sh09M="
|
||||
},
|
||||
"minimalistic-crypto-utils": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz",
|
||||
"integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo="
|
||||
},
|
||||
"minimatch": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
|
||||
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
|
||||
"requires": {
|
||||
"brace-expansion": "1.1.8"
|
||||
}
|
||||
},
|
||||
"once": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
||||
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
|
||||
"requires": {
|
||||
"wrappy": "1.0.2"
|
||||
}
|
||||
},
|
||||
"os-browserify": {
|
||||
"version": "0.2.1",
|
||||
"resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.2.1.tgz",
|
||||
"integrity": "sha1-Y/xMzuXS13Y9Jrv4YBB45sLgBE8="
|
||||
},
|
||||
"pako": {
|
||||
"version": "0.2.9",
|
||||
"resolved": "https://registry.npmjs.org/pako/-/pako-0.2.9.tgz",
|
||||
"integrity": "sha1-8/dSL073gjSNqBYbrZ7P1Rv4OnU="
|
||||
},
|
||||
"parse-asn1": {
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.0.tgz",
|
||||
"integrity": "sha1-N8T5t+06tlx0gXtfJICTf7+XxxI=",
|
||||
"requires": {
|
||||
"asn1.js": "4.9.1",
|
||||
"browserify-aes": "1.1.0",
|
||||
"create-hash": "1.1.3",
|
||||
"evp_bytestokey": "1.0.3",
|
||||
"pbkdf2": "3.0.14"
|
||||
}
|
||||
},
|
||||
"path-browserify": {
|
||||
"version": "0.0.0",
|
||||
"resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.0.tgz",
|
||||
"integrity": "sha1-oLhwcpquIUAFt9UDLsLLuw+0RRo="
|
||||
},
|
||||
"path-is-absolute": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
|
||||
"integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18="
|
||||
},
|
||||
"pbkdf2": {
|
||||
"version": "3.0.14",
|
||||
"resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.14.tgz",
|
||||
"integrity": "sha512-gjsZW9O34fm0R7PaLHRJmLLVfSoesxztjPjE9o6R+qtVJij90ltg1joIovN9GKrRW3t1PzhDDG3UMEMFfZ+1wA==",
|
||||
"requires": {
|
||||
"create-hash": "1.1.3",
|
||||
"create-hmac": "1.1.6",
|
||||
"ripemd160": "2.0.1",
|
||||
"safe-buffer": "5.1.1",
|
||||
"sha.js": "2.4.9"
|
||||
}
|
||||
},
|
||||
"process": {
|
||||
"version": "0.11.10",
|
||||
"resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
|
||||
"integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI="
|
||||
},
|
||||
"process-nextick-args": {
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz",
|
||||
"integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M="
|
||||
},
|
||||
"public-encrypt": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.0.tgz",
|
||||
"integrity": "sha1-OfaZ86RlYN1eusvKaTyvfGXBjMY=",
|
||||
"requires": {
|
||||
"bn.js": "4.11.8",
|
||||
"browserify-rsa": "4.0.1",
|
||||
"create-hash": "1.1.3",
|
||||
"parse-asn1": "5.1.0",
|
||||
"randombytes": "2.0.5"
|
||||
}
|
||||
},
|
||||
"punycode": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
|
||||
"integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4="
|
||||
},
|
||||
"querystring": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz",
|
||||
"integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA="
|
||||
},
|
||||
"querystring-es3": {
|
||||
"version": "0.2.1",
|
||||
"resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz",
|
||||
"integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM="
|
||||
},
|
||||
"randombytes": {
|
||||
"version": "2.0.5",
|
||||
"resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.0.5.tgz",
|
||||
"integrity": "sha512-8T7Zn1AhMsQ/HI1SjcCfT/t4ii3eAqco3yOcSzS4mozsOz69lHLsoMXmF9nZgnFanYscnSlUSgs8uZyKzpE6kg==",
|
||||
"requires": {
|
||||
"safe-buffer": "5.1.1"
|
||||
}
|
||||
},
|
||||
"readable-stream": {
|
||||
"version": "git+https://github.com/meteor/readable-stream.git#d64a64aa6061b9b6855feff4d09e58fb3b2e4502",
|
||||
"requires": {
|
||||
"inherits": "2.0.3",
|
||||
"isarray": "1.0.0",
|
||||
"process-nextick-args": "1.0.7",
|
||||
"safe-buffer": "5.1.1",
|
||||
"string_decoder": "1.0.3",
|
||||
"util-deprecate": "1.0.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"inherits": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
|
||||
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
|
||||
}
|
||||
}
|
||||
},
|
||||
"rimraf": {
|
||||
"version": "2.6.2",
|
||||
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz",
|
||||
"integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==",
|
||||
"requires": {
|
||||
"glob": "7.1.2"
|
||||
}
|
||||
},
|
||||
"ripemd160": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.1.tgz",
|
||||
"integrity": "sha1-D0WEKVxTo2KK9+bXmsohzlfRxuc=",
|
||||
"requires": {
|
||||
"hash-base": "2.0.2",
|
||||
"inherits": "2.0.1"
|
||||
}
|
||||
},
|
||||
"safe-buffer": {
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz",
|
||||
"integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg=="
|
||||
},
|
||||
"sha.js": {
|
||||
"version": "2.4.9",
|
||||
"resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.9.tgz",
|
||||
"integrity": "sha512-G8zektVqbiPHrylgew9Zg1VRB1L/DtXNUVAM6q4QLy8NE3qtHlFXTf8VLL4k1Yl6c7NMjtZUTdXV+X44nFaT6A==",
|
||||
"requires": {
|
||||
"inherits": "2.0.1",
|
||||
"safe-buffer": "5.1.1"
|
||||
}
|
||||
},
|
||||
"stream-browserify": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.1.tgz",
|
||||
"integrity": "sha1-ZiZu5fm9uZQKTkUUyvtDu3Hlyds=",
|
||||
"requires": {
|
||||
"inherits": "2.0.1",
|
||||
"readable-stream": "git+https://github.com/meteor/readable-stream.git#d64a64aa6061b9b6855feff4d09e58fb3b2e4502"
|
||||
}
|
||||
},
|
||||
"string_decoder": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz",
|
||||
"integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==",
|
||||
"requires": {
|
||||
"safe-buffer": "5.1.1"
|
||||
}
|
||||
},
|
||||
"timers-browserify": {
|
||||
"version": "1.4.2",
|
||||
"resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-1.4.2.tgz",
|
||||
"integrity": "sha1-ycWLV1voQHN1y14kYtrO50NZ9B0=",
|
||||
"requires": {
|
||||
"process": "0.11.10"
|
||||
}
|
||||
},
|
||||
"tty-browserify": {
|
||||
"version": "0.0.0",
|
||||
"resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz",
|
||||
"integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY="
|
||||
},
|
||||
"url": {
|
||||
"version": "0.11.0",
|
||||
"resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz",
|
||||
"integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=",
|
||||
"requires": {
|
||||
"punycode": "1.3.2",
|
||||
"querystring": "0.2.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"punycode": {
|
||||
"version": "1.3.2",
|
||||
"resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz",
|
||||
"integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0="
|
||||
}
|
||||
}
|
||||
},
|
||||
"util": {
|
||||
"version": "0.10.3",
|
||||
"resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz",
|
||||
"integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=",
|
||||
"requires": {
|
||||
"inherits": "2.0.1"
|
||||
}
|
||||
},
|
||||
"util-deprecate": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||
"integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
|
||||
},
|
||||
"vm-browserify": {
|
||||
"version": "0.0.4",
|
||||
"resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-0.0.4.tgz",
|
||||
"integrity": "sha1-XX6kW7755Kb/ZflUOOCofDV9WnM=",
|
||||
"requires": {
|
||||
"indexof": "0.0.1"
|
||||
}
|
||||
},
|
||||
"wrappy": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
||||
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
|
||||
}
|
||||
}
|
||||
},
|
||||
"regenerator-runtime": {
|
||||
"version": "0.11.1",
|
||||
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz",
|
||||
"integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg=="
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
{
|
||||
"name": "accounts-ui-viewer",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"start": "meteor run"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.0.0-beta.38",
|
||||
"meteor-node-stubs": "^0.3.2"
|
||||
}
|
||||
}
|
||||
1
examples/unfinished/atoms/.meteor/.gitignore
vendored
1
examples/unfinished/atoms/.meteor/.gitignore
vendored
@@ -1 +0,0 @@
|
||||
local
|
||||
@@ -1,9 +0,0 @@
|
||||
# Meteor packages used by this project, one per line.
|
||||
#
|
||||
# 'meteor add' and 'meteor remove' will edit this file for you,
|
||||
# but you can also edit it by hand.
|
||||
|
||||
standard-app-packages
|
||||
autopublish
|
||||
insecure
|
||||
underscore
|
||||
@@ -1,12 +0,0 @@
|
||||
g[class=atom] circle {
|
||||
stroke: black;
|
||||
stroke-width: 3px;
|
||||
}
|
||||
|
||||
g[class=atom] text {
|
||||
font-family: Arial, sans-serif;
|
||||
font-size: 24px;
|
||||
fill: black;
|
||||
font-weight: bold;
|
||||
text-anchor: middle;
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
<head>
|
||||
<title>Atoms</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="atoms">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="500" height="500" viewBox="0 0 500 500">
|
||||
{{#atom x=100 y=100 color="#ffff00"}}O{{/atom}}
|
||||
{{> hydrogen x=150 y=100}}
|
||||
{{#giantatom x=250 y=100 color="#ff9999"}}My{{/giantatom}}
|
||||
</svg>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
<template name="atom">
|
||||
<g class="atom">
|
||||
<circle style="fill: {{#if color}}{{color}}{{else}}white{{/if}}" cx={{x}} cy={{y}} r={{#if r}}{{r}}{{else}}20{{/if}} />
|
||||
<text x={{x}} y={{textY}}>{{> UI.contentBlock}}</text>
|
||||
</g>
|
||||
</template>
|
||||
|
||||
<template name="hydrogen">
|
||||
{{#atom x=x y=y r=r color="#00ffff"}}H{{/atom}}
|
||||
</template>
|
||||
|
||||
<template name="giantatom">
|
||||
{{#atom x=x y=y r=50 color=color}}{{> UI.contentBlock}}{{/atom}}
|
||||
</template>
|
||||
@@ -1,5 +0,0 @@
|
||||
if (Meteor.isClient) {
|
||||
Template.atom.textY = function () {
|
||||
return this.y + 8;
|
||||
};
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
local
|
||||
@@ -1,9 +0,0 @@
|
||||
# Meteor packages used by this project, one per line.
|
||||
#
|
||||
# 'meteor add' and 'meteor remove' will edit this file for you,
|
||||
# but you can also edit it by hand.
|
||||
|
||||
underscore
|
||||
jquery
|
||||
jquery-layout
|
||||
standard-app-packages
|
||||
@@ -1 +0,0 @@
|
||||
0.6.0
|
||||
@@ -1,26 +0,0 @@
|
||||
#room-list .room.selected {
|
||||
color: white;
|
||||
background-color: black;
|
||||
}
|
||||
|
||||
.room .name {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.room .delete {
|
||||
float: right;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.room:hover .delete {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.add-room {
|
||||
margin-top: 20px;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.add-room:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
<body>
|
||||
|
||||
<div class="ui-layout-center" id="chat-view">
|
||||
{{> center_pane }}
|
||||
</div>
|
||||
<div class="ui-layout-east">East</div>
|
||||
<div class="ui-layout-west">
|
||||
{{> room_list}}
|
||||
{{> add_room}}
|
||||
</div>
|
||||
<div class="ui-layout-north">Azrael</div>
|
||||
|
||||
</body>
|
||||
|
||||
<template name="room_list">
|
||||
<div id="room-list">
|
||||
{{#each rooms}}
|
||||
{{> room}}
|
||||
{{/each}}
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template name="add_room">
|
||||
<div class="add-room">
|
||||
Create new room
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template name="room">
|
||||
<div class="room {{maybe_selected}}">
|
||||
{{#if editing}}
|
||||
<input id="room_name_input" value="{{name}}">
|
||||
{{else}}
|
||||
<div class="name">{{name}}</div>
|
||||
<div class="delete">(x)</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template name="center_pane">
|
||||
<div id="center-pane">
|
||||
{{#if any_room_selected}}
|
||||
<div id="chat">
|
||||
{{#each messages}}
|
||||
{{> chat_message}}
|
||||
{{else}}
|
||||
No chat yet!
|
||||
{{/each}}
|
||||
</div>
|
||||
<input id="chat-entry">
|
||||
{{else}}
|
||||
No room selected
|
||||
{{/if}}
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template name="chat_message">
|
||||
<div>
|
||||
{{username}}: {{message}}
|
||||
</div>
|
||||
</template>
|
||||
@@ -1,128 +0,0 @@
|
||||
Meteor.subscribe('rooms');
|
||||
|
||||
Session.set('current_room', null);
|
||||
Session.set('editing_room_name', false);
|
||||
|
||||
Deps.autorun(function () {
|
||||
var room_id = Session.get('current_room');
|
||||
if (room_id) Meteor.subscribe('room-detail', room_id);
|
||||
});
|
||||
|
||||
// XXX would be nice to eliminate this function and have people just
|
||||
// call Session.set("current_room", foo) directly instead
|
||||
var selectRoom = function (room_id) {
|
||||
// XXX pushstate
|
||||
var room = Rooms.find(room_id);
|
||||
Session.set('current_room', room_id);
|
||||
};
|
||||
|
||||
Meteor.startup(function () {
|
||||
$('body').layout({applyDefaultStyles: true})
|
||||
});
|
||||
|
||||
Template.room_list.rooms = function () {
|
||||
// XXX it would be nice if this were find instead of findLive (ie,
|
||||
// if they were unified in some sane way)
|
||||
return Rooms.findLive({}, {sort: {name: 1}});
|
||||
};
|
||||
|
||||
Template.add_room.events = {
|
||||
'click': function () {
|
||||
// XXX should put up dialog to get name
|
||||
// XXX should support automatically set created/updated timestamps
|
||||
var room_id = Rooms.insert({name: "New room",
|
||||
// XXX horrid syntax
|
||||
created: (new Date()).getTime()});
|
||||
selectRoom(room_id);
|
||||
// XXX XXX XXX this fails to work -- it leaves edit mode after
|
||||
// 1RTT. what happens is, the server echos the insert back to us,
|
||||
// and that is currently wired up to trigger a changed event on
|
||||
// the findlive, which redraws the element, which triggers blur,
|
||||
// which causes us to set editing_room_name to false.
|
||||
//
|
||||
// one option is to have the rendering function (maybe in a
|
||||
// post-render routine?) decide if it currently wants
|
||||
// focus. (should that be within the recomputation envelope, I
|
||||
// wonder?)
|
||||
//
|
||||
// another is to suppress blur on rerender. probably the only
|
||||
// principled way to do this is to narrow the scope of the
|
||||
// rerender to not include the <input>.
|
||||
//
|
||||
// [No idea if the comment above is still current]
|
||||
Session.set('editing_room_name', true);
|
||||
Deps.flush();
|
||||
$('#room_name_input').focus();
|
||||
}
|
||||
};
|
||||
|
||||
Template.room.events = {
|
||||
'mousedown': function (evt) {
|
||||
selectRoom(this._id);
|
||||
},
|
||||
'dblclick': function (evt) {
|
||||
Session.set('editing_room_name', true);
|
||||
// XXX XXX doesn't generalize.. the element might very reasonably
|
||||
// not have a unique id. may need a different strategy..
|
||||
Deps.flush();
|
||||
$('#room_name_input').focus();
|
||||
},
|
||||
'blur input': function (evt) {
|
||||
Session.set('editing_room_name', false);
|
||||
},
|
||||
'keypress input': function (evt) {
|
||||
// XXX should really have a binding/validator-based pattern
|
||||
// XXX check to see this pattern works if you are saving
|
||||
// continuously (on every keystroke)
|
||||
var value = $(evt.target).val();
|
||||
if (evt.which === 13 && value.length)
|
||||
Rooms.update(this._id, {$set: {name: value}});
|
||||
if (evt.which === 13 || evt.which === 27)
|
||||
Session.set('editing_room_name', false);
|
||||
},
|
||||
// If you make this event be click (rather than mousedown), then
|
||||
// delete doesn't work if the room isn't already selected. what
|
||||
// happens is, the mousedown triggers the selection, which redraws
|
||||
// the room, meaning that the elements are replaced out from under
|
||||
// the event, and the click event is lost.. bleh. needs
|
||||
// reconsideration.
|
||||
'mousedown .delete': function (evt) {
|
||||
Rooms.remove('rooms', this._id);
|
||||
Session.set('current_room', null);
|
||||
},
|
||||
};
|
||||
|
||||
Template.room.editing = function (options) {
|
||||
// Check current_room first, before editing_room_name, to minimize
|
||||
// number of redraws
|
||||
return (Session.equals('current_room', this._id) &&
|
||||
Session.equals('editing_room_name', true));
|
||||
};
|
||||
|
||||
Template.room.maybe_selected = function () {
|
||||
return Session.equals('current_room', this._id) ? "selected" : "";
|
||||
};
|
||||
|
||||
Template.center_pane.messages = function () {
|
||||
return Chat.findLive({room: Session.get("current_room")},
|
||||
{sort: {created: 1}});
|
||||
};
|
||||
|
||||
Template.center_pane.any_room_selected = function () {
|
||||
return !Session.equals('current_room', null);
|
||||
};
|
||||
|
||||
Template.center_pane.events = {
|
||||
'keydown #chat-entry': function (evt) {
|
||||
if (evt.which === 13) {
|
||||
var room_id = Session.get('current_room');
|
||||
if (!room_id)
|
||||
return;
|
||||
|
||||
Chat.insert({room: room_id, message: $(evt.target).val(),
|
||||
username: "someone",
|
||||
created: (new Date()).getTime()});
|
||||
$(evt.target).val('');
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -1,21 +0,0 @@
|
||||
// XXX it is actually very dangerous to store times as Number. use
|
||||
// Date type once it's implemented in minimongo
|
||||
Rooms = new Mongo.Collection("rooms");
|
||||
//Rooms.schema({name: String, created: Number});
|
||||
|
||||
Chat = new Mongo.Collection("chat");
|
||||
/*
|
||||
Chat.schema({room: String, message: String,
|
||||
username: String, created: Number});
|
||||
*/
|
||||
|
||||
if (Meteor.isServer) {
|
||||
Meteor.publish('rooms', function () {
|
||||
return Rooms.find();
|
||||
});
|
||||
|
||||
// XXX should limit to just a certain amount of recent chat ..
|
||||
Meteor.publish('room-detail', function (room) {
|
||||
return Chat.find({room: room});
|
||||
});
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
local
|
||||
@@ -1,10 +0,0 @@
|
||||
# Meteor packages used by this project, one per line.
|
||||
#
|
||||
# 'meteor add' and 'meteor remove' will edit this file for you,
|
||||
# but you can also edit it by hand.
|
||||
|
||||
insecure
|
||||
preserve-inputs
|
||||
bootstrap
|
||||
random
|
||||
standard-app-packages
|
||||
@@ -1 +0,0 @@
|
||||
0.6.5.1
|
||||
@@ -1 +0,0 @@
|
||||
/* CSS declarations go here */
|
||||
@@ -1,23 +0,0 @@
|
||||
<head>
|
||||
<title>benchmark</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
{{> status}}
|
||||
|
||||
{{> params}}
|
||||
</body>
|
||||
|
||||
<template name="status">
|
||||
<p>Status: {{status}}</p>
|
||||
<p>Update Rate: {{updateRate}}</p>
|
||||
</template>
|
||||
|
||||
<template name="params">
|
||||
<dl>
|
||||
{{#each params}}
|
||||
<dt>{{key}}</dt><dd>{{value}}</dd>
|
||||
{{/each}}
|
||||
</dl>
|
||||
</template>
|
||||
|
||||
@@ -1,244 +0,0 @@
|
||||
|
||||
// Pick scenario from settings.
|
||||
// XXX settings now has public. could move stuff there and avoid this.
|
||||
var PARAMS = {};
|
||||
if (Meteor.isServer) {
|
||||
if (!Meteor.settings.params)
|
||||
throw new Error("Must set scenario with Meteor.settings");
|
||||
__meteor_runtime_config__.PARAMS = PARAMS = Meteor.settings.params;
|
||||
} else {
|
||||
PARAMS = __meteor_runtime_config__.PARAMS;
|
||||
}
|
||||
|
||||
|
||||
// id for this client or server.
|
||||
var processId = Random.id();
|
||||
console.log("processId", processId);
|
||||
|
||||
|
||||
//////////////////////////////
|
||||
// Helper Functions
|
||||
//////////////////////////////
|
||||
|
||||
var random = function (n) {
|
||||
return Math.floor(Random.fraction() * n);
|
||||
};
|
||||
|
||||
var randomChars =
|
||||
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'.split('');
|
||||
var randomString = function (length) {
|
||||
// XXX make more efficient
|
||||
var ret = '';
|
||||
_.times(length, function () {
|
||||
ret += Random.choice(randomChars);
|
||||
});
|
||||
return ret;
|
||||
};
|
||||
|
||||
var preCall = function (name) {
|
||||
console.log('> ' + name);
|
||||
};
|
||||
|
||||
var postCall = function (name) {
|
||||
return function (err, callback) {
|
||||
console.log('< ' + name + ' ' + (err ? 'ERR' : 'OK'));
|
||||
};
|
||||
};
|
||||
|
||||
var pickCollection = function () {
|
||||
return Random.choice(Collections);
|
||||
};
|
||||
|
||||
var generateDoc = function () {
|
||||
var ret = {};
|
||||
ret.fromProcess = processId;
|
||||
_.times(PARAMS.documentNumFields, function (n) {
|
||||
ret['Field' + n] = randomString(PARAMS.documentSize/PARAMS.documentNumFields);
|
||||
});
|
||||
|
||||
return ret;
|
||||
};
|
||||
|
||||
|
||||
//////////////////////////////
|
||||
// Data
|
||||
//////////////////////////////
|
||||
|
||||
|
||||
var Collections = [];
|
||||
_.times(PARAMS.numCollections, function (n) {
|
||||
Collections.push(new Mongo.Collection("Collection" + n));
|
||||
});
|
||||
|
||||
|
||||
if (Meteor.isServer) {
|
||||
|
||||
// Make sure we have indexes. Helps mongo CPU usage.
|
||||
Meteor.startup(function () {
|
||||
_.each(Collections, function (C) {
|
||||
C._ensureIndex({toProcess: 1});
|
||||
C._ensureIndex({fromProcess: 1});
|
||||
C._ensureIndex({when: 1});
|
||||
});
|
||||
});
|
||||
|
||||
// periodic db check. generate a client list.
|
||||
var currentClients = [];
|
||||
var totalDocs = 0;
|
||||
Meteor.setInterval(function () {
|
||||
var newClients = {};
|
||||
var newTotal = 0;
|
||||
// XXX hardcoded time
|
||||
var since = +(new Date) - 1000*PARAMS.insertsPerSecond * 5;
|
||||
_.each(Collections, function (C) {
|
||||
_.each(C.find({when: {$gt: since}}, {fields: {fromProcess: 1, when: 1}}).fetch(), function (d) {
|
||||
newTotal += 1;
|
||||
if (d.fromProcess && d.when > since)
|
||||
newClients[d.fromProcess] = true;
|
||||
});
|
||||
});
|
||||
currentClients = _.keys(newClients);
|
||||
totalDocs = newTotal;
|
||||
}, 3*1000); // XXX hardcoded time
|
||||
|
||||
// periodic document cleanup.
|
||||
if (PARAMS.maxAgeSeconds) {
|
||||
Meteor.setInterval(function () {
|
||||
var when = +(new Date) - PARAMS.maxAgeSeconds*1000;
|
||||
_.each(Collections, function (C) {
|
||||
preCall('removeMaxAge');
|
||||
C.remove({when: {$lt: when}}, postCall('removeMaxAge'));
|
||||
});
|
||||
// Clear out 5% of the DB each time, steady state. XXX parameterize?
|
||||
}, 1000*PARAMS.maxAgeSeconds / 20);
|
||||
}
|
||||
|
||||
Meteor.publish("data", function (collection, process) {
|
||||
check(collection, Number);
|
||||
check(process, String);
|
||||
var C = Collections[collection];
|
||||
return C.find({toProcess: process});
|
||||
});
|
||||
|
||||
Meteor.methods({
|
||||
'insert': function (doc) {
|
||||
check(doc, Object);
|
||||
check(doc.fromProcess, String);
|
||||
// pick a random destination. send to ourselves if there is no one
|
||||
// else. by having an entry in the db, we'll end up in the target
|
||||
// list.
|
||||
doc.toProcess = Random.choice(currentClients) || doc.fromProcess;
|
||||
|
||||
doc.when = +(new Date);
|
||||
|
||||
var C = pickCollection();
|
||||
preCall('insert');
|
||||
C.insert(doc, postCall('insert'));
|
||||
},
|
||||
update: function (processId, field, value) {
|
||||
check([processId, field, value], [String]);
|
||||
var modifer = {};
|
||||
modifer[field] = value; // XXX injection attack?
|
||||
|
||||
var C = pickCollection();
|
||||
// update one message.
|
||||
preCall('update');
|
||||
C.update({fromProcess: processId}, {$set: modifer}, {multi: false}, postCall('update'));
|
||||
},
|
||||
remove: function (processId) {
|
||||
check(processId, String);
|
||||
var C = pickCollection();
|
||||
// remove one message.
|
||||
var obj = C.findOne({fromProcess: processId});
|
||||
if (obj) {
|
||||
preCall('remove');
|
||||
C.remove(obj._id, postCall('remove'));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// XXX publish stats
|
||||
// - currentClients.length
|
||||
// - serverId
|
||||
// - num ddp sessions
|
||||
// - total documents
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (Meteor.isClient) {
|
||||
// sub to data
|
||||
_.times(PARAMS.numCollections, function (n) {
|
||||
Meteor.subscribe("data", n, processId);
|
||||
});
|
||||
|
||||
// templates
|
||||
Template.params.params = function () {
|
||||
return _.map(PARAMS, function (v, k) {
|
||||
return {key: k, value: v};
|
||||
});
|
||||
};
|
||||
|
||||
Template.status.status = function () {
|
||||
return Meteor.status().status;
|
||||
};
|
||||
|
||||
Template.status.updateRate = function () {
|
||||
return (Session.get('updateAvgs') || []).join(", ");
|
||||
};
|
||||
|
||||
// XXX count of how many docs are in local collection?
|
||||
|
||||
|
||||
// do stuff periodically
|
||||
|
||||
if (PARAMS.insertsPerSecond) {
|
||||
Meteor.setInterval(function () {
|
||||
Meteor.call('insert', generateDoc());
|
||||
}, 1000 / PARAMS.insertsPerSecond);
|
||||
}
|
||||
|
||||
if (PARAMS.updatesPerSecond) {
|
||||
Meteor.setInterval(function () {
|
||||
Meteor.call('update',
|
||||
processId,
|
||||
'Field' + random(PARAMS.documentNumFields),
|
||||
randomString(PARAMS.documentSize/PARAMS.documentNumFields)
|
||||
);
|
||||
}, 1000 / PARAMS.updatesPerSecond);
|
||||
}
|
||||
|
||||
if (PARAMS.removesPerSecond) {
|
||||
Meteor.setInterval(function () {
|
||||
Meteor.call('remove', processId);
|
||||
}, 1000 / PARAMS.removesPerSecond);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// XXX very rough per client update rate. we need to measure this
|
||||
// better. ideally, on the server we could get the global update rate
|
||||
var updateCount = 0;
|
||||
var updateHistories = {1: [], 10: [], 100: [], 1000: []};
|
||||
var updateFunc = function () { updateCount += 1; };
|
||||
_.each(Collections, function (C) {
|
||||
C.find({}).observeChanges({
|
||||
added: updateFunc, changed: updateFunc, removed: updateFunc
|
||||
});
|
||||
});
|
||||
Meteor.setInterval(function () {
|
||||
_.each(updateHistories, function (h, max) {
|
||||
h.push(updateCount);
|
||||
if (h.length > max)
|
||||
h.shift();
|
||||
});
|
||||
Session.set('updateAvgs', _.map(updateHistories, function (h) {
|
||||
return _.reduce(h, function(memo, num) {
|
||||
return memo + num;
|
||||
}, 0) / h.length;
|
||||
}));;
|
||||
updateCount = 0;
|
||||
}, 1000);
|
||||
|
||||
}
|
||||
@@ -1,84 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
PORT=9000
|
||||
if [ -z "$NUM_CLIENTS" ]; then
|
||||
NUM_CLIENTS=10
|
||||
fi
|
||||
if [ -z "$DURATION" ]; then
|
||||
DURATION=120
|
||||
fi
|
||||
REPORT_INTERVAL=10
|
||||
|
||||
set -e
|
||||
trap 'echo "FAILED. Killing: $(jobs -pr)" ; for pid in "$(jobs -pr)"; do kill $pid ; done' EXIT
|
||||
|
||||
PROJDIR=`dirname $0`
|
||||
cd "$PROJDIR"
|
||||
PROJDIR=`pwd`
|
||||
|
||||
SCENARIO="${1:-default}"
|
||||
|
||||
# clean up from previous runs
|
||||
# XXX this is gross!
|
||||
pkill -f "$PROJDIR/.meteor/local/db" || true
|
||||
../../../meteor reset || true
|
||||
|
||||
# start the benchmark app
|
||||
../../../meteor --production --settings "scenarios/${SCENARIO}.json" --port ${PORT} &
|
||||
OUTER_PID=$!
|
||||
|
||||
echo "Waiting for server to come up"
|
||||
function wait_for_port {
|
||||
local N=0
|
||||
while ! curl -v "$1" 2>&1 | grep ' 200 ' > /dev/null ; do
|
||||
sleep 1
|
||||
N=$(($N+1))
|
||||
if [ $N -ge $2 ] ; then
|
||||
curl -v "$1" || true
|
||||
echo "Timed out waiting for port $1"
|
||||
exit 2
|
||||
fi
|
||||
done
|
||||
}
|
||||
wait_for_port "http://localhost:${PORT}" 60
|
||||
|
||||
|
||||
echo "Starting phantoms"
|
||||
# start a bunch of phantomjs processes
|
||||
PHANTOMSCRIPT=`mktemp -t benchmark-XXXXXXXX`
|
||||
cat > "$PHANTOMSCRIPT" <<EOF
|
||||
var page = require('webpage').create();
|
||||
var url = 'http://localhost:$PORT';
|
||||
page.open(url);
|
||||
EOF
|
||||
for ((i = 0 ; i < $NUM_CLIENTS ; i++)) ; do
|
||||
# sleep between each phantom start both to provide a smoother ramp
|
||||
# to the benchmark and because otherwise their PRNGs get set to the
|
||||
# same seed and you get duplicate key errors!
|
||||
sleep 2
|
||||
phantomjs "$PHANTOMSCRIPT" &
|
||||
done
|
||||
|
||||
ps -o cputime,ppid,args | grep " $OUTER_PID " | grep main.js || true
|
||||
for ((i = 0 ; i < $DURATION/$REPORT_INTERVAL ; i++)) ; do
|
||||
sleep $REPORT_INTERVAL
|
||||
ps -o cputime,ppid,args | grep " $OUTER_PID " | grep main.js || true
|
||||
done
|
||||
|
||||
# print totals of all processes (outer, mongo, inner)
|
||||
echo
|
||||
echo TOTALS
|
||||
ps -o cputime,pid,ppid,args | grep " $OUTER_PID " | grep -v grep || true
|
||||
|
||||
|
||||
# cleanup
|
||||
trap - EXIT
|
||||
for pid in "$(jobs -pr)"; do
|
||||
# not sure why we need both, but it seems to help clean up rogue
|
||||
# mongo and phantomjs processes.
|
||||
kill -INT $pid
|
||||
kill $pid
|
||||
done
|
||||
rm "$PHANTOMSCRIPT"
|
||||
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
Parameters for simulation:
|
||||
|
||||
- numCollections
|
||||
how many collections to spread the documents over
|
||||
|
||||
- maxAgeSeconds: How long to leave documents in the database. This,
|
||||
combined with all the various rates, determines the steady state
|
||||
database size. In seconds. falsy to disable.
|
||||
|
||||
Per-client action rates:
|
||||
- insertsPerSecond
|
||||
- updatesPerSecond
|
||||
- removesPerSecond
|
||||
|
||||
- documentSize: bytes of randomness per document.
|
||||
// XXX make this a random distribution?
|
||||
- documentNumFields: how many fields of randomness per document.
|
||||
@@ -1,11 +0,0 @@
|
||||
{
|
||||
"params": {
|
||||
"numCollections": 1,
|
||||
"maxAgeSeconds": 60,
|
||||
"insertsPerSecond": 1,
|
||||
"updatesPerSecond": 1,
|
||||
"removesPerSecond": 0.1,
|
||||
"documentSize": 1024,
|
||||
"documentNumFields": 8
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
{
|
||||
"params": {
|
||||
"numCollections": 1,
|
||||
"maxAgeSeconds": 60,
|
||||
"insertsPerSecond": 5,
|
||||
"updatesPerSecond": 5,
|
||||
"removesPerSecond": 1,
|
||||
"documentSize": 128,
|
||||
"documentNumFields": 2
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user