Merge branch 'devel' into concepts

This commit is contained in:
Sashko Stubailo
2014-10-16 13:37:19 -07:00
322 changed files with 8823 additions and 5239 deletions

View File

@@ -10,35 +10,35 @@
GITHUB: aldeed <eric@dairystatedesigns.com>
GITHUB: AlexeyMK <alexey@alexeymk.com>
GITHUB: apendua <apendua@gmail.com>
GITHUB: Cangit <fredricendrerud@gmail.com>
GITHUB: DenisGorbachev <Denis.Gorbachev@faster-than-wind.ru>
GITHUB: EOT <eot@gmx.at>
GITHUB: FooBarWidget <honglilai@gmail.com>
GITHUB: Maxhodges <Max@whiterabbitpress.com>
GITHUB: OyoKooN <nathan@sxnlabs.com>
GITHUB: RobertLowe <robert@iblargz.com>
GITHUB: ansman <nicklas@ansman.se>
GITHUB: apendua <apendua@gmail.com>
GITHUB: awwx <andrew.wilcox@gmail.com>
GITHUB: babenzele <tim.p.phillips@gmail.com>
GITHUB: Cangit <fredricendrerud@gmail.com>
GITHUB: cmather <mather.chris@gmail.com>
GITHUB: codeinthehole <david.winterbottom@gmail.com>
GITHUB: cryptoquick <faustianightmare@gmail.com>
GITHUB: dandv <ddascalescu+github@gmail.com>
GITHUB: davegonzalez <gonzalez.dalex@gmail.com>
GITHUB: DenisGorbachev <Denis.Gorbachev@faster-than-wind.ru>
GITHUB: ducdigital <duc@ducdigital.com>
GITHUB: duckspeaker <gallo.j@gmail.com>
GITHUB: emgee3 <hello@gravitronic.com>
GITHUB: EOT <eot@gmx.at>
GITHUB: felixrabe <felix@rabe.io>
GITHUB: FooBarWidget <honglilai@gmail.com>
GITHUB: FredericoC <frederico.carvalho@3stack.com.au>
GITHUB: Gaelan <gbs@canishe.com>
GITHUB: icellan <icellan@icellan.com>
GITHUB: ImtiazMajeed <imtiazmaj@gmail.com>
GITHUB: jacott <geoffjacobsen@gmail.com>
GITHUB: jfhamlin <jfhamlin@gmail.com>
GITHUB: jbruni <contato@jbruni.com.br>
GITHUB: justinsb <justin@fathomdb.com>
GITHUB: jfhamlin <jfhamlin@gmail.com>
GITHUB: jperl <perl.jonathan@gmail.com>
GITHUB: kentonv <temporal@gmail.com>
GITHUB: marcandre <github@marc-andre.ca>
GITHUB: mart-jansink <m.jansink@gmail.com>
GITHUB: Maxhodges <Max@whiterabbitpress.com>
GITHUB: MaximDubrovin <maxxdubrovin@gmail.com>
GITHUB: meawoppl <meawoppl@gmail.com>
GITHUB: meonkeys <haircut@gmail.com>
@@ -49,14 +49,22 @@ GITHUB: mizzao <mizzao@gmail.com>
GITHUB: mquandalle <maxime.quandalle@gmail.com>
GITHUB: nathan-muir <ndmuir@gmail.com>
GITHUB: Neftedollar <oildollar@gmail.com>
GITHUB: OyoKooN <nathan@sxnlabs.com>
GITHUB: paulswartz <paulswartz@gmail.com>
GITHUB: Pent <jon@empire5design.com>
GITHUB: prapicault <pascal@rapicault.net>
GITHUB: prapicault <pascal@rapicorp.com>
GITHUB: pscanf <paolo.scanferla@mondora.com>
GITHUB: queso <joshua.owens@gmail.com>
GITHUB: rcy <rcyeske@gmail.com>
GITHUB: RichardLitt <richard.littauer@gmail.com>
GITHUB: rdickert <robert.dickert@gmail.com>
GITHUB: rgould <rwgould@gmail.com>
GITHUB: RichardLitt <richard.littauer@gmail.com>
GITHUB: richguan <guan.rich@gmail.com>
GITHUB: rick-golden-healthagen <rick.golden@healthagen.com>
GITHUB: rissem <rissem@gmail.com>
GITHUB: RobertLowe <robert@iblargz.com>
GITHUB: rosh93 <rosh93@gmail.com>
GITHUB: ryw <ry@rywalker.com>
GITHUB: rzymek <rzymek@gmail.com>
GITHUB: sdarnell <stephen@darnell.plus.com>
@@ -64,6 +72,7 @@ GITHUB: subhog <hubert@orlikarnia.com>
GITHUB: tbjers <torgny@xorcode.com>
GITHUB: thatneat <thatneat@users.noreply.github.com>
GITHUB: timhaines <tmhaines@gmail.com>
GITHUB: timoabend <timo.abend@liquidmotions.de>
GITHUB: tmeasday <tom@thesnail.org>
GITHUB: twhy <tom.wanghaiyi@gmail.com>
GITHUB: waitingkuo <waitingkuo0527@gmail.com>
@@ -73,8 +82,8 @@ GITHUB: zol <zol@percolatestudio.com>
METEOR: arbesfeld <arbesfeld@gmail.com>
METEOR: avital <avital@thewe.net>
METEOR: ben <ben@meteor.com>
METEOR: ben <bn@cs.stanford.edu>
METEOR: benjamn <ben@meteor.com>
METEOR: benjamn <bn@cs.stanford.edu>
METEOR: debergalis <matt@meteor.com>
METEOR: dgreensp <dgreenspan@alum.mit.edu>
METEOR: ekatek <ekate@meteor.com>
@@ -83,9 +92,11 @@ METEOR: estark37 <estark37@gmail.com>
METEOR: glasser <glasser@meteor.com>
METEOR: glasser <glasser@davidglasser.net>
METEOR: gschmidt <geoff@geoffschmidt.com>
METEOR: justinsb <justin@fathomdb.com>
METEOR: karayu <lele.yu@gmail.com>
METEOR: mariapacana <maria.pacana@gmail.com>
METEOR: n1mmy <nim@meteor.com>
METEOR: sixolet <naomi@meteor.com>
METEOR: Slava <slava@meteor.com>
METEOR: Slava <imslavko@gmail.com>
METEOR: stubailo <sashko@mit.edu>

View File

@@ -1,26 +1,126 @@
## v.NEXT
## v0.9.4
### New Features
* The new `meteor debug` command and `--debug-port` command line option
to `meteor run` allow you to easily use node-inspector to debug your
server-side code. Add a `debugger` statement to your code to create a
breakpoint.
* Add new a `meteor run --test` command that runs
[Velocity](https://github.com/meteor-velocity/velocity) tests in your
app .
* Add new callbacks `Accounts.onResetPasswordLink`,
`Accounts.onEnrollmentLink`, and `Accounts.onEmailVerificationLink`
that make it easier to build custom user interfaces on top of the
accounts system. These callbacks should be registered before
`Meteor.startup` fires, and will be called if the URL matches a link
in an email sent by `Accounts.resetPassword`, etc. See
https://docs.meteor.com/#Accounts-onResetPasswordLink.
* A new configuration file for mobile apps,
`<APP>/mobile-config.js`. This allows you to set app metadata, icons,
splash screens, preferences, and PhoneGap/Cordova plugin settings
without needing a `cordova_build_override` directory. See
https://docs.meteor.com/#mobileconfigjs.
### API Changes
* Rename `{{> UI.dynamic}}` to `{{> Template.dynamic}}`, and likewise
with `UI.contentBlock` and `UI.elseBlock`.
with `UI.contentBlock` and `UI.elseBlock`. The UI namespace is no
longer used anywhere except for backwards compatibility.
* Deprecate the `Template.someTemplate.myHelper = ...` syntax in favor
of `Template.someTemplate.helpers(...)`. Using the older syntax still
works, but prints a deprecation warning to the console.
* `Package.registerBuildPlugin` its associated functions have been added
to the public API, cleaned up, and documented. The new function is
identical to the earlier _transitional_registerBuildPlugin except for
minor backwards- compatible API changes. See
https://docs.meteor.com/#Package-registerBuildPlugin
* Rename the `showdown` package to `markdown`.
* Deprecate the `amplify`, `backbone`, `bootstrap`, and `d3` integration
packages in favor of community alternatives. These packages will no
longer be maintained by MDG.
* Rename the `showdown` package to `markdown`.
* Deprecate the `Template.someTemplate.myHelper = ...` syntax in favor
of `Template.someTemplate.helpers(...)`. Using the older syntax still
works, but it prints a deprecation warning to the console.
### Tool Changes
* Improved output from `meteor build` to make it easier to publish
mobile apps to the App Store and Play Store. See the wiki pages for
instructions on how to publish your
[iOS](https://github.com/meteor/meteor/wiki/How-to-submit-your-iOS-app-to-App-Store)
and
[Android](https://github.com/meteor/meteor/wiki/How-to-submit-your-Android-app-to-Play-Store)
apps.
* Packages can now be marked as debug-mode only by adding `debugOnly:
true` to `Package.describe`. Debug-only packages are not included in
the app when it is bundled for production (`meteor build` or `meteor
run --production`). This allows package authors to build packages
specifically for testing and debugging without increasing the size of
the resulting app bundle or causing apps to ship with debug
functionality built in.
* Rework the process for installing mobile development SDKs. There is
now a `meteor install-sdk` command that automatically install what
software it can and points to documentation for the parts that
require manual installation.
* The `.meteor/cordova-platforms` file has been renamed to
`.meteor/platforms` and now includes the default `server` and
`browser` platforms. The default platforms can't currently be removed
from a project, though this will be possible in the future. The old
file will be automatically migrated to the new one when the app is run
with Meteor 0.9.4 or above.
* The `unipackage.json` file inside downloaded packages has been renamed
to `isopack.json` and has an improved forwards-compatible format. To
maintain backwards compatibility with previous releases, packages will
be built with both files.
* The local package metadata cache now uses SQLite, which is much faster
than the previous implementation. This improves `meteor` command line
tool startup time.
* The constraint solver used by the client to find compatible versions
of packages is now much faster.
* The `--port` option to `meteor run` now requires a numeric port
(e.g. `meteor run --port example.com` is no longer valid).
* The `--mobile-port` option `meteor run` has been reworked. The option
is now `--mobile-server` in `meteor run` and `--server` in `meteor
build`. `--server` is required for `meteor build` in apps with mobile
platforms installed. `--mobile-server` defaults to an automatically
detected IP address on port 3000, and `--server` requires a hostname
but defaults to port 80 if a port is not specified.
* Operations that take longer than a few seconds (e.g. downloading
packages, installing the Android SDK, etc) now show a progress bar.
### Bug Fixes
* Fix behavior of ROOT_URL with path ending in `/`.
* Fix source maps when using a ROOT_URL with a path. #2627
* --port now requires a port (e.g. `meteor run --port example.com` is
not valid). XXX --mobile-port deprecated in favor of --mobile-server
option for 'meteor run' and '--server' for 'meteor build'. --server
is required for meteor build. describe defaults for --mobile-server.
* Change the mechanism that the Meteor tool uses to clean up app server
processes. The new mechanism is more resilient to slow app bundles and
other CPU-intensive tasks. #2536, #2588.
Patches by Github users cryptoquick, Gaelan, jperl, meonkeys, mitar,
mquandalle, prapicault, pscanf, richguan, rick-golden-healthagen,
rissem, rosh93, rzymek, and timoabend
## v0.9.3.1
@@ -29,8 +129,6 @@
* Allow more than one dash in package versions. #2715
* Fix `meteor update` on an app built from a checkout version
of Meteor.
## v0.9.3

View File

@@ -884,6 +884,7 @@ Modifications made by the following entities are licensed as above:
accepts: https://github.com/jshttp/accepts
merge-descriptors: https://github.com/component/merge-descriptors
type-is: https://github.com/jshttp/type-is
mime-types: https://github.com/jshttp/mime-types
----------
Copyright (c) 2013 Jonathan Ong me@jongleberry.com
@@ -947,6 +948,35 @@ utils-merge: https://github.com/jaredhanson/utils-merge
Copyright (c) 2013 Jared Hanson
----------
bl: https://github.com/rvagg/bl
----------
Copyright (c) 2014 bl contributors
bl contributors listed at https://github.com/rvagg/bl#contributors
----------
lodash-node: https://github.com/lodash/lodash-node
----------
Copyright 2012-2014 The Dojo Foundation <http://dojofoundation.org/>
Based on Underscore.js 1.6.0, copyright 2009-2014 Jeremy Ashkenas,
DocumentCloud and Investigative Reporters & Editors <http://underscorejs.org/>
----------
netroute: https://github.com/indutny/node-netroute
----------
Copyright Fedor Indutny, 2012.
----------
stringstream: https://github.com/mhart/StringStream
----------
Copyright 2012 Michael Hart (michael.hart.au@gmail.com)
==============
@@ -1229,6 +1259,99 @@ Copyright (C) 2013 David Nolen and contributors
BSD Licenses
============
----------
tar-pack: https://github.com/ForbesLindesay/tar-pack
----------
Copyright (c) 2014, Forbes Lindesay
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
----------
sqlite3: https://github.com/mapbox/node-sqlite3
----------
Copyright (c) MapBox
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
- Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright notice, this
list of conditions and the following disclaimer in the documentation and/or
other materials provided with the distribution.
- Neither the name "MapBox" nor the names of its contributors may be
used to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
----------
node-pre-gyp: https://github.com/mapbox/node-pre-gyp
----------
Copyright (c), Mapbox
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of node-pre-gyp nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
----------
uglify-js: https://github.com/mishoo/UglifyJS2
----------

View File

@@ -4,3 +4,4 @@
notices-for-0.9.0
notices-for-0.9.1
0.9.4-platform-file

View File

@@ -7,9 +7,11 @@ standard-app-packages
jquery
underscore
showdown
code-prettify
jquery-waypoints
less
spiderable
appcache
reload-safetybelt
simple:markdown-templating
simple:highlight.js

2
docs/.meteor/platforms Normal file
View File

@@ -0,0 +1,2 @@
server
browser

View File

@@ -1 +1 @@
METEOR@0.9.3.1
METEOR@0.9.4

View File

@@ -1,58 +1,60 @@
appcache@1.0.1
application-configuration@1.0.2
autoupdate@1.1.1
base64@1.0.0
binary-heap@1.0.0
blaze-tools@1.0.0
blaze@2.0.1
boilerplate-generator@1.0.0
callback-hook@1.0.0
check@1.0.1
code-prettify@1.0.0
coffeescript@1.0.3
ctl-helper@1.0.3
ctl@1.0.1
ddp@1.0.9
deps@1.0.4
ejson@1.0.3
fastclick@1.0.0
follower-livedata@1.0.1
geojson-utils@1.0.0
html-tools@1.0.1
htmljs@1.0.1
http@1.0.6
id-map@1.0.0
jquery-waypoints@1.0.0
jquery@1.0.0
json@1.0.0
less@1.0.9
livedata@1.0.10
logging@1.0.3
meteor-platform@1.1.1
meteor@1.1.1
minifiers@1.1.0
minimongo@1.0.3
mobile-status-bar@1.0.0
mongo@1.0.6
observe-sequence@1.0.2
ordered-dict@1.0.0
random@1.0.0
reactive-dict@1.0.3
reactive-var@1.0.2
reload-safetybelt@1.0.0
reload@1.1.0
retry@1.0.0
routepolicy@1.0.1
session@1.0.2
showdown@1.0.1
spacebars-compiler@1.0.2
spacebars@1.0.2
spiderable@1.0.3
standard-app-packages@1.0.2
templating@1.0.7
tracker@1.0.2
ui@1.0.3
underscore@1.0.0
url@1.0.0
webapp-hashing@1.0.0
webapp@1.1.2
appcache@1.0.2
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
coffeescript@1.0.4
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
jquery-waypoints@1.0.1
jquery@1.0.1
json@1.0.1
less@1.0.10
livedata@1.0.11
logging@1.0.4
markdown@1.0.2
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-safetybelt@1.0.1
reload@1.1.1
retry@1.0.1
routepolicy@1.0.2
session@1.0.3
showdown@1.0.2
simple:highlight.js@1.0.1
simple:markdown-templating@1.2.5
spacebars-compiler@1.0.3
spacebars@1.0.3
spiderable@1.0.4
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

File diff suppressed because it is too large Load Diff

31
docs/client/api.md Normal file
View File

@@ -0,0 +1,31 @@
{{#template name="api"}}
<h1 id="api">The Meteor API</h1>
Your JavaScript code can run in two environments: the *client* (browser), and
the *server* (a [Node.js](http://nodejs.org/) container on a server). For each
function in this API reference, we'll indicate if the function is available just
on the client, just on the server, or *Anywhere*.
{{> api_core}}
{{> api_pubsub}}
{{> api_methods}}
{{> api_check}}
{{> api_connections}}
{{> api_collections}}
{{> api_session}}
{{> api_accounts}}
{{> api_passwords}}
{{> api_templates}}
{{> api_blaze}}
{{> api_timers}}
{{> api_tracker}}
{{> api_reactive_var}}
{{> api_ejson}}
{{> api_http}}
{{> api_email}}
{{> api_assets}}
{{> api_packagejs}}
{{> api_mobile_config}}
{{/template}}

384
docs/client/api/accounts.md Normal file
View File

@@ -0,0 +1,384 @@
{{#template name="api_accounts"}}
<h2 id="accounts_api"><span>Accounts</span></h2>
The Meteor Accounts system builds on top of the `userId` support in
[`publish`](#publish_userId) and [`methods`](#method_userId). The core
packages add the concept of user documents stored in the database, and
additional packages add [secure password
authentication](#accounts_passwords), [integration with third party
login services](#meteor_loginwithexternalservice), and a [pre-built user
interface](#accountsui).
The basic Accounts system is in the `accounts-base` package, but
applications typically include this automatically by adding one of the
login provider packages: `accounts-password`, `accounts-facebook`,
`accounts-github`, `accounts-google`, `accounts-meetup`,
`accounts-twitter`, or `accounts-weibo`.
{{> autoApiBox "Meteor.user"}}
Retrieves the user record for the current user from
the [`Meteor.users`](#meteor_users) collection.
On the client, this will be the subset of the fields in the document that
are published from the server (other fields won't be available on the
client). By default the server publishes `username`, `emails`, and
`profile`. See [`Meteor.users`](#meteor_users) for more on
the fields used in user documents.
{{> autoApiBox "Meteor.userId"}}
{{> autoApiBox "Meteor.users"}}
This collection contains one document per registered user. Here's an example
user document:
{
_id: "bbca5d6a-2156-41c4-89da-0329e8c99a4f", // Meteor.userId()
username: "cool_kid_13", // unique name
emails: [
// each email address can only belong to one user.
{ address: "cool@example.com", verified: true },
{ address: "another@different.com", verified: false }
],
createdAt: Wed Aug 21 2013 15:16:52 GMT-0700 (PDT),
profile: {
// The profile is writable by the user by default.
name: "Joe Schmoe"
},
services: {
facebook: {
id: "709050", // facebook id
accessToken: "AAACCgdX7G2...AbV9AZDZD"
},
resume: {
loginTokens: [
{ token: "97e8c205-c7e4-47c9-9bea-8e2ccc0694cd",
when: 1349761684048 }
]
}
}
}
A user document can contain any data you want to store about a user. Meteor
treats the following fields specially:
- `username`: a unique String identifying the user.
- `emails`: an Array of Objects with keys `address` and `verified`;
an email address may belong to at most one user. `verified` is
a Boolean which is true if the user has [verified the
address](#accounts_verifyemail) with a token sent over email.
- `createdAt`: the Date at which the user document was created.
- `profile`: an Object which (by default) the user can create
and update with any data.
- `services`: an Object containing data used by particular
login services. For example, its `reset` field contains
tokens used by [forgot password](#accounts_forgotpassword) links,
and its `resume` field contains tokens used to keep you
logged in between sessions.
Like all [Mongo.Collection](#collections)s, you can access all
documents on the server, but only those specifically published by the server are
available on the client.
By default, the current user's `username`, `emails` and `profile` are
published to the client. You can publish additional fields for the
current user with:
// server
Meteor.publish("userData", function () {
if (this.userId) {
return Meteor.users.find({_id: this.userId},
{fields: {'other': 1, 'things': 1}});
} else {
this.ready();
}
});
// client
Meteor.subscribe("userData");
If the autopublish package is installed, information about all users
on the system is published to all clients. This includes `username`,
`profile`, and any fields in `services` that are meant to be public
(eg `services.facebook.id`,
`services.twitter.screenName`). Additionally, when using autopublish
more information is published for the currently logged in user,
including access tokens. This allows making API calls directly from
the client for services that allow this.
Users are by default allowed to specify their own `profile` field with
[`Accounts.createUser`](#accounts_createuser) and modify it with
`Meteor.users.update`. To allow users to edit additional fields, use
[`Meteor.users.allow`](#allow). To forbid users from making any modifications to
their user document:
Meteor.users.deny({update: function () { return true; }});
{{> autoApiBox "Meteor.loggingIn"}}
For example, [the `accounts-ui` package](#accountsui) uses this to display an
animation while the login request is being processed.
{{> autoApiBox "Meteor.logout"}}
{{> autoApiBox "Meteor.logoutOtherClients"}}
For example, when called in a user's browser, connections in that browser
remain logged in, but any other browsers or DDP clients logged in as that user
will be logged out.
{{> autoApiBox "Meteor.loginWithPassword"}}
This function is provided by the `accounts-password` package. See the
[Passwords](#accounts_passwords) section below.
{{> autoApiBox "Meteor.loginWith<ExternalService>"}}
Available functions are:
* `Meteor.loginWithMeteorDeveloperAccount`
* `Meteor.loginWithFacebook`
* `Meteor.loginWithGithub`
* `Meteor.loginWithGoogle`
* `Meteor.loginWithMeetup`
* `Meteor.loginWithTwitter`
* `Meteor.loginWithWeibo`
These functions initiate the login process with an external
service (eg: Facebook, Google, etc), using OAuth. When called they open a new pop-up
window that loads the provider's login page. Once the user has logged in
with the provider, the pop-up window is closed and the Meteor client
logs in to the Meteor server with the information provided by the external
service.
<a id="requestpermissions" name="requestpermissions"></a>
In addition to identifying the user to your application, some services
have APIs that allow you to take action on behalf of the user. To
request specific permissions from the user, pass the
`requestPermissions` option the login function. This will cause the user
to be presented with an additional page in the pop-up dialog to permit
access to their data. The user's `accessToken` &mdash; with permissions
to access the service's API &mdash; is stored in the `services` field of
the user document. The supported values for `requestPermissions` differ
for each login service and are documented on their respective developer
sites:
- Facebook: <http://developers.facebook.com/docs/authentication/permissions/>
- GitHub: <http://developer.github.com/v3/oauth/#scopes>
- Google: <https://developers.google.com/accounts/docs/OAuth2Login#scopeparameter>
- Meetup: <http://www.meetup.com/meetup_api/auth/#oauth2-scopes>
- Twitter, Weibo, Meteor developer accounts: `requestPermissions` currently not supported
External login services typically require registering and configuring
your application before use. The easiest way to do this is with the
[`accounts-ui` package](#accountsui) which presents a step-by-step guide
to configuring each service. However, the data can be also be entered
manually in the `ServiceConfiguration.configurations` collection, which
is exported by the `service-configuration` package. For example, after
running `meteor add service-configuration` in your app:
// first, remove configuration entry in case service is already configured
ServiceConfiguration.configurations.remove({
service: "weibo"
});
ServiceConfiguration.configurations.insert({
service: "weibo",
clientId: "1292962797",
loginStyle: "popup",
secret: "75a730b58f5691de5522789070c319bc"
});
Each external service has its own login provider package and login function. For
example, to support GitHub login, run `$ meteor add accounts-github` and use the
`Meteor.loginWithGithub` function:
Meteor.loginWithGithub({
requestPermissions: ['user', 'public_repo']
}, function (err) {
if (err)
Session.set('errorMessage', err.reason || 'Unknown error');
});
Login service configuration is sent from the server to the client over DDP when
your app starts up; you may not call the login function until the configuration
is loaded. The function `Accounts.loginServicesConfigured()` is a reactive data
source that will return true once the login service is configured; you should
not make login buttons visible or active until it is true.
{{> autoApiBox "currentUser"}}
{{> autoApiBox "loggingIn"}}
{{> autoApiBox "Accounts.config"}}
{{> autoApiBox "Accounts.ui.config"}}
Example:
Accounts.ui.config({
requestPermissions: {
facebook: ['user_likes'],
github: ['user', 'repo']
},
requestOfflineToken: {
google: true
},
passwordSignupFields: 'USERNAME_AND_OPTIONAL_EMAIL'
});
{{> autoApiBox "Accounts.validateNewUser"}}
This can be called multiple times. If any of the functions return `false` or
throw an error, the new user creation is aborted. To set a specific error
message (which will be displayed by [`accounts-ui`](#accountsui)), throw a new
[`Meteor.Error`](#meteor_error).
Example:
// Validate username, sending a specific error message on failure.
Accounts.validateNewUser(function (user) {
if (user.username && user.username.length >= 3)
return true;
throw new Meteor.Error(403, "Username must have at least 3 characters");
});
// Validate username, without a specific error message.
Accounts.validateNewUser(function (user) {
return user.username !== "root";
});
If the user is being created as part of a login attempt from a client (eg,
calling [`Accounts.createUser`](#accounts_createuser) from the client, or
[logging in for the first time with an external
service](#meteor_loginwithexternalservice)), these callbacks are called *before*
the [`Accounts.validateLoginAttempt`](#accounts_validateloginattempt)
callbacks. If these callbacks succeed but those fail, the user will still be
created but the connection will not be logged in as that user.
{{> autoApiBox "Accounts.onCreateUser"}}
Use this when you need to do more than simply accept or reject new user
creation. With this function you can programatically control the
contents of new user documents.
The function you pass will be called with two arguments: `options` and
`user`. The `options` argument comes
from [`Accounts.createUser`](#accounts_createuser) for
password-based users or from an external service login flow. `options` may come
from an untrusted client so make sure to validate any values you read from
it. The `user` argument is created on the server and contains a
proposed user object with all the automatically generated fields
required for the user to log in.
The function should return the user document (either the one passed in or a
newly-created object) with whatever modifications are desired. The returned
document is inserted directly into the [`Meteor.users`](#meteor_users) collection.
The default create user function simply copies `options.profile` into
the new user document. Calling `onCreateUser` overrides the default
hook. This can only be called once.
Example:
<!-- XXX replace d6 with _.random once we have underscore 1.4.2 -->
// Support for playing D&D: Roll 3d6 for dexterity
Accounts.onCreateUser(function(options, user) {
var d6 = function () { return Math.floor(Random.fraction() * 6) + 1; };
user.dexterity = d6() + d6() + d6();
// We still want the default hook's 'profile' behavior.
if (options.profile)
user.profile = options.profile;
return user;
});
{{> autoApiBox "Accounts.validateLoginAttempt"}}
Call `validateLoginAttempt` with a callback to be called on login
attempts. It returns an object with a single method, `stop`. Calling
`stop()` unregisters the callback.
When a login attempt is made, the registered validate login callbacks
are called with a single argument, the attempt info object:
<dl class="objdesc">
{{#dtdd name="type" type="String"}}
The service name, such as "password" or "twitter".
{{/dtdd}}
{{#dtdd name="allowed" type="Boolean"}}
Whether this login is allowed and will be successful (if not aborted
by any of the validateLoginAttempt callbacks). False if the login
will not succeed (for example, an invalid password or the login was
aborted by a previous validateLoginAttempt callback).
{{/dtdd}}
{{#dtdd name="error" type="Exception"}}
When `allowed` is false, the exception describing why the login
failed. It will be a `Meteor.Error` for failures reported to the
user (such as invalid password), and can be a another kind of
exception for internal errors.
{{/dtdd}}
{{#dtdd name="user" type="Object"}}
When it is known which user was attempting to login, the Meteor user
object. This will always be present for successful logins.
{{/dtdd}}
{{#dtdd name="connection" type="Object"}}
The `connection` object the request came in on. See
[`Meteor.onConnection`](#meteor_onconnection) for details.
{{/dtdd}}
{{#dtdd name="methodName" type="String"}}
The name of the Meteor method being used to login.
{{/dtdd}}
{{#dtdd name="methodArguments" type="Array"}}
An array of the arguments passed to the login method.
{{/dtdd}}
</dl>
A validate login callback must return a truthy value for the login to
proceed. If the callback returns a falsy value or throws an
exception, the login is aborted. Throwing a `Meteor.Error` will
report the error reason to the user.
All registered validate login callbacks are called, even if one of the callbacks
aborts the login. The later callbacks will see the `allowed` field set to
`false` since the login will now not be successful. This allows later callbacks
to override an error from a previous callback; for example, you could override
the "Incorrect password" error with a different message.
Validate login callbacks that aren't explicitly trying to override a previous
error generally have no need to run if the attempt has already been determined
to fail, and should start with
if (!attempt.allowed)
return false;
{{> autoApiBox "Accounts.onLogin"}}
See description of [Accounts.onLoginFailure](#accounts_onloginfailure)
for details.
{{> autoApiBox "Accounts.onLoginFailure"}}
Either the `onLogin` or the `onLoginFailure` callbacks will be called
for each login attempt. The `onLogin` callbacks are called after the
user has been successfully logged in. The `onLoginFailure` callbacks are
called after a login attempt is denied.
These functions return an object with a single method, `stop`. Calling
`stop()` unregisters the callback.
The callbacks are called with a single argument, the same attempt info
object as [`validateLoginAttempt`](#accounts_validateloginattempt).
{{/template}}

20
docs/client/api/assets.md Normal file
View File

@@ -0,0 +1,20 @@
{{#template name="api_assets"}}
<h2 id="assets"><span>Assets</span></h2>
`Assets` allows server code in a Meteor application to access static server
assets, which are located in the `private` subdirectory of an application's
tree. Assets are not processed as source files and are copied directly
into your application's bundle.
{{> autoApiBox "Assets.getText"}}
{{> autoApiBox "Assets.getBinary"}}
Static server assets are included by placing them in the application's `private`
subdirectory. For example, if an application's `private` subdirectory includes a
directory called `nested` with a file called `data.txt` inside it, then server
code can read `data.txt` by running:
var data = Assets.getText('nested/data.txt');
{{/template}}

307
docs/client/api/blaze.md Normal file
View File

@@ -0,0 +1,307 @@
{{#template name="api_blaze"}}
<h2 id="blaze"><span>Blaze</span></h2>
Blaze is the package that makes reactive templates possible.
You can use the Blaze API directly in order to render templates programmatically
and manipulate "Views," the building blocks of reactive templates.
{{> autoApiBox "Blaze.render"}}
When you render a template, the
template's [`created`](#template_created) callback is invoked
immediately, before evaluating the content of the template.
The [`rendered`](#template_rendered) callback is
invoked after the View is rendered and inserted into the DOM.
The rendered template
will update reactively in response to data changes until the View is
removed using [`Blaze.remove`](#blaze_remove) or the View's
parent element is removed by Meteor or jQuery.
{{#warning}}
If the View is removed by some other mechanism
besides Meteor or jQuery (which Meteor integrates with by default),
the View may continue to update indefinitely. Most users will not need to
manually render templates and insert them into the DOM, but if you do,
be mindful to always call [`Blaze.remove`](#blaze_remove) when the View is
no longer needed.
{{/warning}}
{{> autoApiBox "Blaze.renderWithData"}}
`Blaze.renderWithData(Template.myTemplate, data)` is essentially the same as
`Blaze.render(Blaze.With(data, function () { return Template.myTemplate; }))`.
{{> autoApiBox "Blaze.remove"}}
Use `Blaze.remove` to remove a template or View previously inserted with
`Blaze.render`, in such a way that any behaviors attached to the DOM by
Meteor are cleaned up. The rendered template or View is now considered
["destroyed"](#template_destroyed), along with all nested templates and
Views. In addition, any data assigned via
jQuery to the DOM nodes is removed, as if the nodes were passed to
jQuery's `$(...).remove()`.
As mentioned in [`Blaze.render`](#blaze_render), it is important to "remove"
all content rendered via `Blaze.render` using `Blaze.remove`, unless the
parent node of `renderedView` is removed by a Meteor reactive
update or with jQuery.
`Blaze.remove` can be used even if the DOM nodes in question have already
been removed from the document, to tell Blaze to stop tracking and
updating these nodes.
{{> autoApiBox "Blaze.getData"}}
{{> autoApiBox "Blaze.toHTML"}}
Rendering a template to HTML loses all fine-grained reactivity. The
normal way to render a template is to either include it from another
template (`{{dstache}}> myTemplate}}`) or render and insert it
programmatically using `Blaze.render`. Only occasionally
is generating HTML useful.
Because `Blaze.toHTML` returns a string, it is not able to update the DOM
in response to reactive data changes. Instead, any reactive data
changes will invalidate the current Computation if there is one
(for example, an autorun that is the caller of `Blaze.toHTML`).
{{> autoApiBox "Blaze.toHTMLWithData"}}
{{> autoApiBox "Blaze.View"}}
Behind every template or part of a template &mdash; a template tag, say, like `{{dstache}}foo}}` or `{{dstache}}#if}}` &mdash; is
a View object, which is a reactively updating region of DOM.
Most applications do not need to be aware of these Views, but they offer a
way to understand and customize Meteor's rendering behavior for more
advanced applications and packages.
You can obtain a View object by calling [`Blaze.render`](#blaze_render) on a
template, or by accessing [`template.view`](#template_view) on a template
instance.
At the heart of a View is an [autorun](#tracker_autorun) that calls the View's
`renderFunction`, uses the result to create DOM nodes, and replaces the
contents of the View with these new DOM nodes. A View's content may consist
of any number of consecutive DOM nodes (though if it is zero, a placeholder
node such as a comment or an empty text node is automatically supplied). Any
reactive dependency established by `renderFunction` causes a full recalculation
of the View's contents when the dependency is invalidated. Templates, however,
are compiled in such a way that they do not have top-level dependencies and so
will only ever render once, while their parts may re-render many times.
When a `Blaze.View` is constructed by calling the constructor, no hooks
are fired and no rendering is performed. In particular, the View is
not yet considered to be "created." Only when the View is actually
used, by a call to `Blaze.render` or `Blaze.toHTML` or by inclusion in
another View, is it "created," right before it is rendered for the
first time. When a View is created, its `.parentView` is set if
appropriate, and then the `onViewCreated` hook is fired. The term
"unrendered View" means a newly constructed View that has not been
"created" or rendered.
The "current View" is kept in [`Blaze.currentView`](#blaze_currentview) and
is set during View rendering, callbacks, autoruns, and template event
handlers. It affects calls such as [`Template.currentData()`](#template_currentdata).
The following properties and methods are available on Blaze.View:
<dl class="objdesc">
{{#dtdd name="name" type="String" id="view_name"}}
The name of this type of View. View names may be used to identify
particular kinds of Views in code, but more often they simply aid in
debugging and comprehensibility of the View tree. Views generated
by Meteor have names like "Template.foo" and "if".
{{/dtdd}}
{{#dtdd name="parentView" type="View or null" id="view_parentview"}}
The enclosing View that caused this View to be rendered, if any.
{{/dtdd}}
{{#dtdd name="isCreated" type="Boolean" id="view_iscreated"}}
True if this View has been called on to be rendered by `Blaze.render`
or `Blaze.toHTML` or another View. Once it becomes true, never
becomes false again. A "created" View's `.parentView` has been
set to its final value. `isCreated` is set to true before
`onViewCreated` hooks are called.
{{/dtdd}}
{{#dtdd name="isRendered" type="Boolean" id="view_isrendered"}}
True if this View has been rendered to DOM by `Blaze.render` or
by the rendering of an enclosing View. Conversion to HTML by
`Blaze.toHTML` doesn't count. Once true, never becomes false.
{{/dtdd}}
{{#dtdd name="isDestroyed" type="Boolean" id="view_isdestroyed"}}
True if this View has been destroyed, such as by `Blaze.remove()` or
by a reactive update that removes it. A destroyed View's autoruns
have been stopped, and its DOM nodes have generally been cleaned
of all Meteor reactivity and possibly dismantled.
{{/dtdd}}
{{#dtdd name="renderCount" type="Integer" id="view_rendercount"}}
The number of times the View has been rendered, including the
current time is the View is in the process of being rendered
or re-rendered.
{{/dtdd}}
{{#dtdd name="autorun(runFunc)" id="view_autorun"}}
Like [`Tracker.autorun`](#tracker_autorun), except that the autorun is
automatically stopped when the View is destroyed, and the
[current View](#blaze_currentview) is always set when running `runFunc`.
There is no relationship to the View's internal autorun or render
cycle. In `runFunc`, the View is bound to `this`.
{{/dtdd}}
{{#dtdd name="onViewCreated(func)" id="view_onviewcreated"}}
If the View hasn't been created yet, calls `func` when the View
is created. In `func`, the View is bound to `this`.
This hook is the basis for the [`created`](#template_created)
template callback.
{{/dtdd}}
{{#dtdd name="onViewReady(func)" id="view_onviewready"}}
Calls `func` when the View is rendered and inserted into the DOM,
after waiting for the end of
[flush time](#tracker_afterflush). Does not fire if the View
is destroyed at any point before it would fire.
May fire multiple times (if the View re-renders).
In `func`, the View is bound to `this`.
This hook is the basis for the [`rendered`](#template_rendered)
template callback.
{{/dtdd}}
{{#dtdd name="onViewDestroyed(func)" id="view_onviewdestroyed"}}
If the View hasn't been destroyed yet, calls `func` when the
View is destroyed. A View may be destroyed without ever becoming
"ready." In `func`, the View is bound to `this`.
This hook is the basis for the [`destroyed`](#template_destroyed)
template callback.
{{/dtdd}}
{{#dtdd name="firstNode()" type="DOM node" id="view_firstnode"}}
The first node of the View's rendered content. Note that this may
be a text node. Requires that the View be rendered.
If the View rendered to zero DOM nodes, it may be a placeholder
node (comment or text node). The DOM extent of a View consists
of the nodes between `view.firstNode()` and `view.lastNode()`,
inclusive.
{{/dtdd}}
{{#dtdd name="lastNode()" type="DOM node" id="view_lastnode"}}
The last node of the View's rendered content.
See [`firstNode()`](#view_firstnode).
{{/dtdd}}
{{#dtdd name="template" type="Template" id="view_template"}}
For Views created by invoking templates, the original Template
object. For example, `Blaze.render(Template.foo).template === Template.foo`.
{{/dtdd}}
{{#dtdd name="templateInstance()" type="Template instance"
id="view_templateinstance"}} For Views created by invoking templates,
returns the [template instance](#template_inst) object for this
particular View. For example, in a [`created`](#template_created)
callback, `this.view.templateInstance() === this`.
Template instance objects have fields like `data`, `firstNode`, and
`lastNode` which are not reactive and which are also not automatically
kept up to date. Calling `templateInstance()` causes these fields to
be updated.
{{/dtdd}}
</dl>
{{> autoApiBox "Blaze.currentView"}}
The "current view" is used by [`Template.currentData()`](#template_currentdata) and
[`Template.instance()`](#template_instance) to determine
the contextually relevant data context and template instance.
{{> autoApiBox "Blaze.getView"}}
If you don't specify an `element`, there must be a current View or an
error will be thrown. This is in contrast to
[`Blaze.currentView`](#blaze_currentview).
{{> autoApiBox "Blaze.With"}}
Returns an unrendered View object you can pass to `Blaze.render`.
Unlike `{{dstache}}#with}}` (as used in templates), `Blaze.With` has no "else" case, and
a falsy value for the data context will not prevent the content from
rendering.
{{> autoApiBox "Blaze.If"}}
Returns an unrendered View object you can pass to `Blaze.render`.
Matches the behavior of `{{dstache}}#if}}` in templates.
{{> autoApiBox "Blaze.Unless"}}
Returns an unrendered View object you can pass to `Blaze.render`.
Matches the behavior of `{{dstache}}#unless}}` in templates.
{{> autoApiBox "Blaze.Each"}}
Returns an unrendered View object you can pass to `Blaze.render`.
Matches the behavior of `{{dstache}}#each}}` in templates.
{{> autoApiBox "Blaze.Template"}}
Templates defined by the template compiler, such as `Template.myTemplate`,
are objects of type `Blaze.Template` (aliased as `Template`).
In addition to methods like `events` and `helpers`, documented as part of
the [Template API](#templates_api), the following fields and methods are
present on template objects:
<dl class="objdesc">
{{#dtdd name="viewName" type="String" id="template_viewname"}}
Same as the constructor argument.
{{/dtdd}}
{{#dtdd name="renderFunction" type="Function" id="template_renderfunction"}}
Same as the constructor argument.
{{/dtdd}}
{{#dtdd name="constructView()" id="template_constructview"}}
Constructs and returns an unrendered View object. This method is invoked
by Meteor whenever the template is used, such as by `Blaze.render` or by
`{{dstache}}> foo}}` where `foo` resolves to a Template object.
`constructView()` constructs a View using `viewName` and `renderFunction`
as constructor arguments, and then configures it as a template
View, setting up `view.template`, `view.templateInstance()`, event maps, and so on.
{{/dtdd}}
</dl>
{{> autoApiBox "Blaze.isTemplate"}}
{{> apiBoxTitle name="Renderable Content" id="renderable_content"}}
A value is *renderable content* if it is one of the following:
* A [template object](#templates_api) like `Template.myTemplate`
* An unrendered [View](#blaze_view) object, like the return value of `Blaze.With`
* `null` or `undefined`
{{#note}}
Internally, renderable content includes objects representing HTML tags
as well, but these objects are not yet part of the officially-supported,
public API.
{{/note}}
{{/template}}

138
docs/client/api/check.md Normal file
View File

@@ -0,0 +1,138 @@
{{#template name="api_check"}}
<h2 id="check_package"><span>Check</span></h2>
The `check` package includes pattern checking functions useful for checking
the types and structure of variables and an [extensible
library of patterns](#matchpatterns) to specify which types you are expecting.
{{> autoApiBox "check"}}
Meteor methods and publish functions take arbitrary [EJSON](#ejson) types as
arguments, but most arguments are expected to be of a particular type. `check`
is a lightweight function for checking that arguments and other
values are of the expected type. For example:
Meteor.publish("chats-in-room", function (roomId) {
// Make sure roomId is a string, not an arbitrary mongo selector object.
check(roomId, String);
return Chats.find({room: roomId});
});
Meteor.methods({addChat: function (roomId, message) {
check(roomId, String);
check(message, {
text: String,
timestamp: Date,
// Optional, but if present must be an array of strings.
tags: Match.Optional([String])
});
// ... do something with the message ...
}});
If the match fails, `check` throws a `Match.Error` describing how it failed. If
this error gets sent over the wire to the client, it will appear only as
`Meteor.Error(400, "Match Failed")`. The failure details will be written to the
server logs but not revealed to the client.
{{> autoApiBox "Match.test"}}
`Match.test` can be used to identify if a variable has a certain structure.
```js
// will return true for {foo: 1, bar: "hello"} or similar
Match.test(value, {foo: Match.Integer, bar: String});
// will return true if value is a string
Match.test(value, String);
// will return true if value is a String or an array of Numbers
Match.test(value, Match.OneOf(String, [Number]));
```
This can be useful if you have a function that accepts several different kinds
of objects, and you want to determine which was passed in.
{{> apiBoxTitle name="Match Patterns" id="matchpatterns"}}
The following patterns can be used as pattern arguments to
[`check`](#check) and `Match.test`:
<dl>
{{#dtdd "<code>Match.Any</code>"}}
Matches any value.
{{/dtdd}}
{{#dtdd "<code>String</code>, <code>Number</code>, <code>Boolean</code>, <code>undefined</code>, <code>null</code>"}}
Matches a primitive of the given type.
{{/dtdd}}
{{#dtdd "<code>Match.Integer</code>"}}
Matches a signed 32-bit integer. Doesn't match `Infinity`, `-Infinity`, or `NaN`.
{{/dtdd}}
{{#dtdd "<code>[<em>pattern</em>]</code>"}}
A one-element array matches an array of elements, each of which match
*pattern*. For example, `[Number]` matches a (possibly empty) array of numbers;
`[Match.Any]` matches any array.
{{/dtdd}}
{{#dtdd "<code>{<em>key1</em>: <em>pattern1</em>, <em>key2</em>: <em>pattern2</em>, ...}</code>"}}
Matches an Object with the given keys, with values matching the given patterns.
If any *pattern* is a `Match.Optional`, that key does not need to exist
in the object. The value may not contain any keys not listed in the pattern.
The value must be a plain Object with no special prototype.
{{/dtdd}}
{{#dtdd "<code>Match.ObjectIncluding({<em>key1</em>: <em>pattern1</em>, <em>key2</em>: <em>pattern2</em>, ...})</code>"}}
Matches an Object with the given keys; the value may also have other keys
with arbitrary values.
{{/dtdd}}
{{#dtdd "<code>Object</code>"}}
Matches any plain Object with any keys; equivalent to
`Match.ObjectIncluding({})`.
{{/dtdd}}
{{#dtdd "<code>Match.Optional(<em>pattern</em>)</code>"}} Matches either
`undefined` or something that matches pattern. If used in an object this matches
only if the key is not set as opposed to the value being set to `undefined`.
// In an object
var pat = { name: Match.Optional(String) };
check({ name: "something" }, pat) // OK
check({}, pat) // OK
check({ name: undefined }, pat) // Throws an exception
// Outside an object
check(undefined, Match.Optional(String)); // OK
{{/dtdd}}
{{#dtdd "<code>Match.OneOf(<em>pattern1</em>, <em>pattern2</em>, ...)</code>"}}
Matches any value that matches at least one of the provided patterns.
{{/dtdd}}
{{#dtdd "Any constructor function (eg, <code>Date</code>)"}}
Matches any element that is an instance of that type.
{{/dtdd}}
{{#dtdd "<code>Match.Where(<em>condition</em>)</code>"}}
Calls the function *condition* with the value as the argument. If *condition*
returns true, this matches. If *condition* throws a `Match.Error` or returns
false, this fails. If *condition* throws any other error, that error is thrown
from the call to `check` or `Match.test`. Examples:
check(buffer, Match.Where(EJSON.isBinary));
NonEmptyString = Match.Where(function (x) {
check(x, String);
return x.length > 0;
});
check(arg, NonEmptyString);
{{/dtdd}}
</dl>
{{/template}}

View File

@@ -0,0 +1,827 @@
{{#template name="api_collections"}}
<h2 id="collections"><span>Collections</span></h2>
Meteor stores data in *collections*. To get started, declare a
collection with `new Mongo.Collection`.
{{> autoApiBox "Mongo.Collection"}}
Calling this function is analogous to declaring a model in a traditional ORM
(Object-Relation Mapper)-centric framework. It sets up a *collection* (a storage
space for records, or "documents") that can be used to store a particular type
of information, like users, posts, scores, todo items, or whatever matters to
your application. Each document is a EJSON object. It includes an `_id`
property whose value is unique in the collection, which Meteor will set when you
first create the document.
// common code on client and server declares a DDP-managed mongo
// collection.
Chatrooms = new Mongo.Collection("chatrooms");
Messages = new Mongo.Collection("messages");
The function returns an object with methods to [`insert`](#insert)
documents in the collection, [`update`](#update) their properties, and
[`remove`](#remove) them, and to [`find`](#find) the documents in the
collection that match arbitrary criteria. The way these methods work is
compatible with the popular Mongo database API. The same database API
works on both the client and the server (see below).
// return array of my messages
var myMessages = Messages.find({userId: Session.get('myUserId')}).fetch();
// create a new message
Messages.insert({text: "Hello, world!"});
// mark my first message as "important"
Messages.update(myMessages[0]._id, {$set: {important: true}});
If you pass a `name` when you create the collection, then you are
declaring a persistent collection &mdash; one that is stored on the
server and seen by all users. Client code and server code can both
access the same collection using the same API.
Specifically, when you pass a `name`, here's what happens:
* On the server (if you do not specify a `connection`), a collection with that
name is created on a backend Mongo server. When you call methods on that
collection on the server, they translate directly into normal Mongo operations
(after checking that they match your [access control rules](#allow)).
* On the client (and on the server if you specify a `connection`), a Minimongo
instance is created. Minimongo is essentially an in-memory, non-persistent
implementation of Mongo in pure JavaScript. It serves as a local cache that
stores just the subset of the database that this client is working with. Queries
([`find`](#find)) on these collections are served directly out of this cache,
without talking to the server.
* When you write to the database on the client ([`insert`](#insert),
[`update`](#update), [`remove`](#remove)), the command is executed locally
immediately, and, simultaneously, it's sent to the server and executed
there too. This happens via [stubs](#meteor_methods), because writes are
implemented as methods.
{{#note}}
When, on the server, you write to a collection which has a specified
`connection` to another server, it sends the corresponding method to the other
server and receives the changed values back from it over DDP. Unlike on the
client, it does not execute the write locally first.
{{/note}}
If you pass `null` as the `name`, then you're creating a local
collection. It's not synchronized anywhere; it's just a local scratchpad
that supports Mongo-style [`find`](#find), [`insert`](#insert),
[`update`](#update), and [`remove`](#remove) operations. (On both the
client and the server, this scratchpad is implemented using Minimongo.)
By default, Meteor automatically publishes every document in your
collection to each connected client. To turn this behavior off, remove
the `autopublish` package:
$ meteor remove autopublish
and instead call [`Meteor.publish`](#meteor_publish) to specify which parts of
your collection should be published to which users.
// Create a collection called Posts and put a document in it. The
// document will be immediately visible in the local copy of the
// collection. It will be written to the server-side database
// a fraction of a second later, and a fraction of a second
// after that, it will be synchronized down to any other clients
// that are subscribed to a query that includes it (see
// Meteor.subscribe and autopublish)
Posts = new Mongo.Collection("posts");
Posts.insert({title: "Hello world", body: "First post"});
// Changes are visible immediately -- no waiting for a round trip to
// the server.
assert(Posts.find().count() === 1);
// Create a temporary, local collection. It works just like any other
// collection, but it doesn't send changes to the server, and it
// can't receive any data from subscriptions.
Scratchpad = new Mongo.Collection;
for (var i = 0; i < 10; i++)
Scratchpad.insert({number: i * 2});
assert(Scratchpad.find({number: {$lt: 9}}).count() === 5);
Generally, you'll assign `Mongo.Collection` objects in your app to global
variables. You can only create one `Mongo.Collection` object for each
underlying Mongo collection.
If you specify a `transform` option to the `Collection` or any of its retrieval
methods, documents are passed through the `transform` function before being
returned or passed to callbacks. This allows you to add methods or otherwise
modify the contents of your collection from their database representation. You
can also specify `transform` on a particular `find`, `findOne`, `allow`, or
`deny` call. Transform functions must return an object and they may not change
the value of the document's `_id` field (though it's OK to leave it out).
// An Animal class that takes a document in its constructor
Animal = function (doc) {
_.extend(this, doc);
};
_.extend(Animal.prototype, {
makeNoise: function () {
console.log(this.sound);
}
});
// Define a Collection that uses Animal as its document
Animals = new Mongo.Collection("Animals", {
transform: function (doc) { return new Animal(doc); }
});
// Create an Animal and call its makeNoise method
Animals.insert({name: "raptor", sound: "roar"});
Animals.findOne({name: "raptor"}).makeNoise(); // prints "roar"
`transform` functions are not called reactively. If you want to add a
dynamically changing attribute to an object, do it with a function that computes
the value at the time it's called, not by computing the attribute at `transform`
time.
{{#warning}}
In this release, Minimongo has some limitations:
* `$pull` in modifiers can only accept certain kinds
of selectors.
* `findAndModify`, aggregate functions, and
map/reduce aren't supported.
All of these will be addressed in a future release. For full
Minimongo release notes, see packages/minimongo/NOTES
in the repository.
{{/warning}}
{{#warning}}
Minimongo doesn't currently have indexes. It's rare for this to be an
issue, since it's unusual for a client to have enough data that an
index is worthwhile.
{{/warning}}
{{> autoApiBox "Mongo.Collection#find"}}
`find` returns a cursor. It does not immediately access the database or return
documents. Cursors provide `fetch` to return all matching documents, `map` and
`forEach` to iterate over all matching documents, and `observe` and
`observeChanges` to register callbacks when the set of matching documents
changes.
{{#warning}}
Collection cursors are not query snapshots. If the database changes
between calling `Collection.find` and fetching the
results of the cursor, or while fetching results from the cursor,
those changes may or may not appear in the result set.
{{/warning}}
Cursors are a reactive data source. On the client, the first time you retrieve a
cursor's documents with `fetch`, `map`, or `forEach` inside a
reactive computation (eg, a template or
[`autorun`](#tracker_autorun)), Meteor will register a
dependency on the underlying data. Any change to the collection that
changes the documents in a cursor will trigger a recomputation. To
disable this behavior, pass `{reactive: false}` as an option to
`find`.
Note that when `fields` are specified, only changes to the included
fields will trigger callbacks in `observe`, `observeChanges` and
invalidations in reactive computations using this cursor. Careful use
of `fields` allows for more fine-grained reactivity for computations
that don't depend on an entire document.
{{> autoApiBox "Mongo.Collection#findOne"}}
Equivalent to `find(selector, options).fetch()[0]` with
`options.limit = 1`.
{{> autoApiBox "Mongo.Collection#insert"}}
Add a document to the collection. A document is just an object, and
its fields can contain any combination of EJSON-compatible datatypes
(arrays, objects, numbers, strings, `null`, true, and false).
`insert` will generate a unique ID for the object you pass, insert it
in the database, and return the ID. When `insert` is called from
untrusted client code, it will be allowed only if passes any
applicable [`allow`](#allow) and [`deny`](#deny) rules.
On the server, if you don't provide a callback, then `insert` blocks
until the database acknowledges the write, or throws an exception if
something went wrong. If you do provide a callback, `insert` still
returns the ID immediately. Once the insert completes (or fails), the
callback is called with error and result arguments. In an error case,
`result` is undefined. If the insert is successful, `error` is
undefined and `result` is the new document ID.
On the client, `insert` never blocks. If you do not provide a callback
and the insert fails on the server, then Meteor will log a warning to
the console. If you provide a callback, Meteor will call that function
with `error` and `result` arguments. In an error case, `result` is
undefined. If the insert is successful, `error` is undefined and
`result` is the new document ID.
Example:
var groceriesId = Lists.insert({name: "Groceries"});
Items.insert({list: groceriesId, name: "Watercress"});
Items.insert({list: groceriesId, name: "Persimmons"});
{{> autoApiBox "Mongo.Collection#update"}}
Modify documents that match `selector` according to `modifier` (see
[modifier documentation](#modifiers)).
The behavior of `update` differs depending on whether it is called by
trusted or untrusted code. Trusted code includes server code and
method code. Untrusted code includes client-side code such as event
handlers and a browser's JavaScript console.
- Trusted code can modify multiple documents at once by setting
`multi` to true, and can use an arbitrary [Mongo
selector](#selectors) to find the documents to modify. It bypasses
any access control rules set up by [`allow`](#allow) and
[`deny`](#deny). The number of affected documents will be returned
from the `update` call if you don't pass a callback.
- Untrusted code can only modify a single document at once, specified
by its `_id`. The modification is allowed only after checking any
applicable [`allow`](#allow) and [`deny`](#deny) rules. The number
of affected documents will be returned to the callback. Untrusted
code cannot perform upserts, except in insecure mode.
On the server, if you don't provide a callback, then `update` blocks
until the database acknowledges the write, or throws an exception if
something went wrong. If you do provide a callback, `update` returns
immediately. Once the update completes, the callback is called with a
single error argument in the case of failure, or a second argument
indicating the number of affected documents if the update was successful.
On the client, `update` never blocks. If you do not provide a callback
and the update fails on the server, then Meteor will log a warning to
the console. If you provide a callback, Meteor will call that function
with an error argument if there was an error, or a second argument
indicating the number of affected documents if the update was successful.
Client example:
// When the givePoints button in the admin dashboard is pressed,
// give 5 points to the current player. The new score will be
// immediately visible on everyone's screens.
Template.adminDashboard.events({
'click .givePoints': function () {
Players.update(Session.get("currentPlayer"), {$inc: {score: 5}});
}
});
Server example:
// Give the "Winner" badge to each user with a score greater than
// 10. If they are logged in and their badge list is visible on the
// screen, it will update automatically as they watch.
Meteor.methods({
declareWinners: function () {
Players.update({score: {$gt: 10}},
{$addToSet: {badges: "Winner"}},
{multi: true});
}
});
You can use `update` to perform a Mongo upsert by setting the `upsert`
option to true. You can also use the [`upsert`](#upsert) method to perform an
upsert that returns the _id of the document that was inserted (if there was one)
in addition to the number of affected documents.
{{> autoApiBox "Mongo.Collection#upsert"}}
Modify documents that match `selector` according to `modifier`, or insert
a document if no documents were modified. `upsert` is the same as calling
`update` with the `upsert` option set to true, except that the return
value of `upsert` is an object that contain the keys `numberAffected`
and `insertedId`. (`update` returns only the number of affected documents.)
{{> autoApiBox "Mongo.Collection#remove"}}
Find all of the documents that match `selector` and delete them from
the collection.
The behavior of `remove` differs depending on whether it is called by
trusted or untrusted code. Trusted code includes server code and
method code. Untrusted code includes client-side code such as event
handlers and a browser's JavaScript console.
- Trusted code can use an arbitrary [Mongo selector](#selectors) to
find the documents to remove, and can remove more than one document
at once by passing a selector that matches multiple documents. It
bypasses any access control rules set up by [`allow`](#allow) and
[`deny`](#deny). The number of removed documents will be returned
from `remove` if you don't pass a callback.
As a safety measure, if `selector` is omitted (or is `undefined`),
no documents will be removed. Set `selector` to `{}` if you really
want to remove all documents from your collection.
- Untrusted code can only remove a single document at a time,
specified by its `_id`. The document is removed only after checking
any applicable [`allow`](#allow) and [`deny`](#deny) rules. The
number of removed documents will be returned to the callback.
On the server, if you don't provide a callback, then `remove` blocks
until the database acknowledges the write and then returns the number
of removed documents, or throws an exception if
something went wrong. If you do provide a callback, `remove` returns
immediately. Once the remove completes, the callback is called with a
single error argument in the case of failure, or a second argument
indicating the number of removed documents if the remove was successful.
On the client, `remove` never blocks. If you do not provide a callback
and the remove fails on the server, then Meteor will log a warning to the
console. If you provide a callback, Meteor will call that function with an
error argument if there was an error, or a second argument indicating the number
of removed documents if the remove was successful.
Client example:
// When the remove button is clicked on a chat message, delete
// that message.
Template.chat.events({
'click .remove': function () {
Messages.remove(this._id);
}
});
Server example:
// When the server starts, clear the log, and delete all players
// with a karma of less than -2.
Meteor.startup(function () {
if (Meteor.isServer) {
Logs.remove({});
Players.remove({karma: {$lt: -2}});
}
});
{{> autoApiBox "Mongo.Collection#allow"}}
When a client calls `insert`, `update`, or `remove` on a collection, the
collection's `allow` and [`deny`](#deny) callbacks are called
on the server to determine if the write should be allowed. If at least
one `allow` callback allows the write, and no `deny` callbacks deny the
write, then the write is allowed to proceed.
These checks are run only when a client tries to write to the database
directly, for example by calling `update` from inside an event
handler. Server code is trusted and isn't subject to `allow` and `deny`
restrictions. That includes methods that are called with `Meteor.call`
&mdash; they are expected to do their own access checking rather than
relying on `allow` and `deny`.
You can call `allow` as many times as you like, and each call can
include any combination of `insert`, `update`, and `remove`
functions. The functions should return `true` if they think the
operation should be allowed. Otherwise they should return `false`, or
nothing at all (`undefined`). In that case Meteor will continue
searching through any other `allow` rules on the collection.
The available callbacks are:
<dl class="callbacks">
{{#dtdd "insert(userId, doc)"}}
The user `userId` wants to insert the document `doc` into the
collection. Return `true` if this should be allowed.
`doc` will contain the `_id` field if one was explicitly set by the client, or
if there is an active `transform`. You can use this to prevent users from
specifying arbitrary `_id` fields.
{{/dtdd}}
{{#dtdd "update(userId, doc, fieldNames, modifier)"}}
The user `userId` wants to update a document `doc`. (`doc` is the
current version of the document from the database, without the
proposed update.) Return `true` to permit the change.
`fieldNames` is an array of the (top-level) fields in `doc` that the
client wants to modify, for example
`['name',`&nbsp;`'score']`.
`modifier` is the raw Mongo modifier that
the client wants to execute; for example,
`{$set: {'name.first': "Alice"}, $inc: {score: 1}}`.
Only Mongo modifiers are supported (operations like `$set` and `$push`).
If the user tries to replace the entire document rather than use
$-modifiers, the request will be denied without checking the `allow`
functions.
{{/dtdd}}
{{#dtdd "remove(userId, doc)"}}
The user `userId` wants to remove `doc` from the database. Return
`true` to permit this.
{{/dtdd}}
</dl>
When calling `update` or `remove` Meteor will by default fetch the
entire document `doc` from the database. If you have large documents
you may wish to fetch only the fields that are actually used by your
functions. Accomplish this by setting `fetch` to an array of field
names to retrieve.
Example:
// Create a collection where users can only modify documents that
// they own. Ownership is tracked by an 'owner' field on each
// document. All documents must be owned by the user that created
// them and ownership can't be changed. Only a document's owner
// is allowed to delete it, and the 'locked' attribute can be
// set on a document to prevent its accidental deletion.
Posts = new Mongo.Collection("posts");
Posts.allow({
insert: function (userId, doc) {
// the user must be logged in, and the document must be owned by the user
return (userId && doc.owner === userId);
},
update: function (userId, doc, fields, modifier) {
// can only change your own documents
return doc.owner === userId;
},
remove: function (userId, doc) {
// can only remove your own documents
return doc.owner === userId;
},
fetch: ['owner']
});
Posts.deny({
update: function (userId, docs, fields, modifier) {
// can't change owners
return _.contains(fields, 'owner');
},
remove: function (userId, doc) {
// can't remove locked documents
return doc.locked;
},
fetch: ['locked'] // no need to fetch 'owner'
});
If you never set up any `allow` rules on a collection then all client
writes to the collection will be denied, and it will only be possible to
write to the collection from server-side code. In this case you will
have to create a method for each possible write that clients are allowed
to do. You'll then call these methods with `Meteor.call` rather than
having the clients call `insert`, `update`, and `remove` directly on the
collection.
Meteor also has a special "insecure mode" for quickly prototyping new
applications. In insecure mode, if you haven't set up any `allow` or `deny`
rules on a collection, then all users have full write access to the
collection. This is the only effect of insecure mode. If you call `allow` or
`deny` at all on a collection, even `Posts.allow({})`, then access is checked
just like normal on that collection. __New Meteor projects start in insecure
mode by default.__ To turn it off just run `$ meteor remove insecure`.
{{> autoApiBox "Mongo.Collection#deny"}}
This works just like [`allow`](#allow), except it lets you
make sure that certain writes are definitely denied, even if there is an
`allow` rule that says that they should be permitted.
When a client tries to write to a collection, the Meteor server first
checks the collection's `deny` rules. If none of them return true then
it checks the collection's `allow` rules. Meteor allows the write only
if no `deny` rules return `true` and at least one `allow` rule returns
`true`.
<h2 id="mongo_cursor"><span>Cursors</span></h2>
To create a cursor, use [`find`](#find). To access the documents in a
cursor, use [`forEach`](#foreach), [`map`](#map), or [`fetch`](#fetch).
{{> autoApiBox "Mongo.Cursor#forEach"}}
This interface is compatible with [Array.forEach](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach).
When called from a reactive computation, `forEach` registers dependencies on
the matching documents.
Examples:
// Print the titles of the five top-scoring posts
var topPosts = Posts.find({}, {sort: {score: -1}, limit: 5});
var count = 0;
topPosts.forEach(function (post) {
console.log("Title of post " + count + ": " + post.title);
count += 1;
});
{{> autoApiBox "Mongo.Cursor#map"}}
This interface is compatible with [Array.map](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map).
When called from a reactive computation, `map` registers dependencies on
the matching documents.
<!-- The following is not yet implemented, but users shouldn't assume
sequential execution anyway because that will break. -->
On the server, if `callback` yields, other calls to `callback` may occur while
the first call is waiting. If strict sequential execution is necessary, use
`forEach` instead.
{{> autoApiBox "Mongo.Cursor#fetch"}}
When called from a reactive computation, `fetch` registers dependencies on
the matching documents.
{{> autoApiBox "Mongo.Cursor#count"}}
Unlike the other functions, `count` registers a dependency only on the
number of matching documents. (Updates that just change or reorder the
documents in the result set will not trigger a recomputation.)
{{> autoApiBox "Mongo.Cursor#observe"}}
Establishes a *live query* that invokes callbacks when the result of
the query changes. The callbacks receive the entire contents of the
document that was affected, as well as its old contents, if
applicable. If you only need to receive the fields that changed, see
[`observeChanges`](#observe_changes).
`callbacks` may have the following functions as properties:
<dl class="callbacks">
<dt><span class="name">added(document)</span> <span class="or">or</span></dt>
<dt><span class="name">addedAt(document, atIndex, before)</span></dt>
<dd>
{{#markdown}}
A new document `document` entered the result set. The new document
appears at position `atIndex`. It is immediately before the document
whose `_id` is `before`. `before` will be `null` if the new document
is at the end of the results.
{{/markdown}}
</dd>
<dt><span class="name">changed(newDocument, oldDocument)
<span class="or">or</span></span></dt>
<dt><span class="name">changedAt(newDocument, oldDocument, atIndex)</span></dt>
<dd>
{{#markdown}}
The contents of a document were previously `oldDocument` and are now
`newDocument`. The position of the changed document is `atIndex`.
{{/markdown}}
</dd>
<dt><span class="name">removed(oldDocument)</span>
<span class="or">or</span></dt>
<dt><span class="name">removedAt(oldDocument, atIndex)</span></dt>
<dd>
{{#markdown}}
The document `oldDocument` is no longer in the result set. It used to be at position `atIndex`.
{{/markdown}}
</dd>
{{#dtdd "movedTo(document, fromIndex, toIndex, before)"}}
A document changed its position in the result set, from `fromIndex` to `toIndex`
(which is before the document with id `before`). Its current contents is
`document`.
{{/dtdd}}
</dl>
Use `added`, `changed`, and `removed` when you don't care about the
order of the documents in the result set. They are more efficient than
`addedAt`, `changedAt`, and `removedAt`.
Before `observe` returns, `added` (or `addedAt`) will be called zero
or more times to deliver the initial results of the query.
`observe` returns a live query handle, which is an object with a `stop` method.
Call `stop` with no arguments to stop calling the callback functions and tear
down the query. **The query will run forever until you call this.** If
`observe` is called from a `Tracker.autorun` computation, it is automatically
stopped when the computation is rerun or stopped.
(If the cursor was created with the option `reactive` set to false, it will
only deliver the initial results and will not call any further callbacks;
it is not necessary to call `stop` on the handle.)
{{> autoApiBox "Mongo.Cursor#observeChanges"}}
Establishes a *live query* that invokes callbacks when the result of
the query changes. In contrast to [`observe`](#observe),
`observeChanges` provides only the difference between the old and new
result set, not the entire contents of the document that changed.
`callbacks` may have the following functions as properties:
<dl class="callbacks">
<dt><span class="name">added(id, fields)</span>
<span class="or">or</span></dt>
<dt><span class="name">addedBefore(id, fields, before)</span></dt>
<dd>
{{#markdown}}
A new document entered the result set. It has the `id` and `fields`
specified. `fields` contains all fields of the document excluding the
`_id` field. The new document is before the document identified by
`before`, or at the end if `before` is `null`.
{{/markdown}}
</dd>
{{#dtdd "changed(id, fields)"}}
The document identified by `id` has changed. `fields` contains the
changed fields with their new values. If a field was removed from the
document then it will be present in `fields` with a value of
`undefined`.
{{/dtdd}}
{{#dtdd "movedBefore(id, before)"}}
The document identified by `id` changed its position in the ordered result set,
and now appears before the document identified by `before`.
{{/dtdd}}
{{#dtdd "removed(id)"}}
The document identified by `id` was removed from the result set.
{{/dtdd}}
</dl>
`observeChanges` is significantly more efficient if you do not use
`addedBefore` or `movedBefore`.
Before `observeChanges` returns, `added` (or `addedBefore`) will be called
zero or more times to deliver the initial results of the query.
`observeChanges` returns a live query handle, which is an object with a `stop`
method. Call `stop` with no arguments to stop calling the callback functions
and tear down the query. **The query will run forever until you call this.**
If
`observeChanges` is called from a `Tracker.autorun` computation, it is automatically
stopped when the computation is rerun or stopped.
(If the cursor was created with the option `reactive` set to false, it will
only deliver the initial results and will not call any further callbacks;
it is not necessary to call `stop` on the handle.)
{{#note}}
Unlike `observe`, `observeChanges` does not provide absolute position
information (that is, `atIndex` positions rather than `before`
positions.) This is for efficiency.
{{/note}}
Example:
// Keep track of how many administrators are online.
var count = 0;
var query = Users.find({admin: true, onlineNow: true});
var handle = query.observeChanges({
added: function (id, user) {
count++;
console.log(user.name + " brings the total to " + count + " admins.");
},
removed: function () {
count--;
console.log("Lost one. We're now down to " + count + " admins.");
}
});
// After five seconds, stop keeping the count.
setTimeout(function () {handle.stop();}, 5000);
{{> autoApiBox "Mongo.ObjectID"}}
`Mongo.ObjectID` follows the same API as the [Node MongoDB driver
`ObjectID`](http://mongodb.github.com/node-mongodb-native/api-bson-generated/objectid.html)
class. Note that you must use the `equals` method (or [`EJSON.equals`](#ejson_equals)) to
compare them; the `===` operator will not work. If you are writing generic code
that needs to deal with `_id` fields that may be either strings or `ObjectID`s, use
[`EJSON.equals`](#ejson_equals) instead of `===` to compare them.
{{#note}}
`ObjectID` values created by Meteor will not have meaningful answers to their `getTimestamp`
method, since Meteor currently constructs them fully randomly.
{{/note}}
{{> apiBoxTitle name="Mongo-Style Selectors" id="selectors"}}
The simplest selectors are just a string or
[`Mongo.ObjectID`](#mongo_object_id). These selectors match the
document with that value in its `_id` field.
A slightly more complex form of selector is an object containing a set of keys
that must match in a document:
// Matches all documents where deleted is false
{deleted: false}
// Matches all documents where the name and cognomen are as given
{name: "Rhialto", cognomen: "the Marvelous"}
// Matches every document
{}
But they can also contain more complicated tests:
// Matches documents where age is greater than 18
{age: {$gt: 18}}
// Also matches documents where tags is an array containing "popular"
{tags: "popular"}
// Matches documents where fruit is one of three possibilities
{fruit: {$in: ["peach", "plum", "pear"]}}
See the [complete
documentation](http://docs.mongodb.org/manual/reference/operator/).
{{> apiBoxTitle name="Mongo-Style Modifiers" id="modifiers"}}
A modifier is an object that describes how to update a document in
place by changing some of its fields. Some examples:
// Set the 'admin' property on the document to true
{$set: {admin: true}}
// Add 2 to the 'votes' property, and add "Traz"
// to the end of the 'supporters' array
{$inc: {votes: 2}, $push: {supporters: "Traz"}}
But if a modifier doesn't contain any $-operators, then it is instead
interpreted as a literal document, and completely replaces whatever was
previously in the database. (Literal document modifiers are not currently
supported by [validated updates](#allow).)
// Find the document with id "123", and completely replace it.
Users.update({_id: "123"}, {name: "Alice", friends: ["Bob"]});
See the [full list of
modifiers](http://docs.mongodb.org/manual/reference/operator/update/).
{{> apiBoxTitle name="Sort Specifiers" id="sortspecifiers"}}
Sorts may be specified using your choice of several syntaxes:
// All of these do the same thing (sort in ascending order by
// key "a", breaking ties in descending order of key "b")
[["a", "asc"], ["b", "desc"]]
["a", ["b", "desc"]]
{a: 1, b: -1}
The last form will only work if your JavaScript implementation
preserves the order of keys in objects. Most do, most of the time, but
it's up to you to be sure.
{{> apiBoxTitle name="Field Specifiers" id="fieldspecifiers"}}
Queries can specify a particular set of fields to include or exclude from the
result object.
To exclude specific fields from the result objects, the field specifier is a
dictionary whose keys are field names and whose values are `0`. All unspecified
fields are included.
// Users.find({}, {fields: {password: 0, hash: 0}})
To include only specific fields in the result documents, use `1` as
the value. The `_id` field is still included in the result.
// Users.find({}, {fields: {firstname: 1, lastname: 1}})
With one exception, it is not possible to mix inclusion and exclusion styles:
the keys must either be all 1 or all 0. The exception is that you may specify
`_id: 0` in an inclusion specifier, which will leave `_id` out of the result
object as well. However, such field specifiers can not be used with
[`observeChanges`](#observe_changes), [`observe`](#observe), cursors returned
from a [publish function](#meteor_publish), or cursors used in
`{{dstache}}#each}}` in a template. They may be used with [`fetch`](#fetch),
[`findOne`](#findone), [`forEach`](#foreach), and [`map`](#map).
<a href="http://docs.mongodb.org/manual/reference/operator/projection/">Field
operators</a> such as `$` and `$elemMatch` are not available on the client side
yet.
A more advanced example:
Users.insert({ alterEgos: [{ name: "Kira", alliance: "murderer" },
{ name: "L", alliance: "police" }],
name: "Yagami Light" });
Users.findOne({}, { fields: { 'alterEgos.name': 1, _id: 0 } });
// returns { alterEgos: [{ name: "Kira" }, { name: "L" }] }
See <a href="http://docs.mongodb.org/manual/tutorial/project-fields-from-query-results/#projection">
the MongoDB docs</a> for details of the nested field rules and array behavior.
{{/template}}

View File

@@ -0,0 +1,168 @@
{{#template name="api_connections"}}
<h2 id="connections"><span>Server connections</span></h2>
These functions manage and inspect the network connection between the
Meteor client and server.
{{> autoApiBox "Meteor.status"}}
This method returns the status of the connection between the client and
the server. The return value is an object with the following fields:
<dl class="objdesc">
{{#dtdd name="connected" type="Boolean"}}
True if currently connected to the server. If false, changes and
method invocations will be queued up until the connection is
reestablished.
{{/dtdd}}
{{#dtdd name="status" type="String"}}
Describes the current reconnection status. The possible
values are `connected` (the connection is up and
running), `connecting` (disconnected and trying to open a
new connection), `failed` (permanently failed to connect; e.g., the client
and server support different versions of DDP), `waiting` (failed
to connect and waiting to try to reconnect) and `offline` (user has disconnected the connection).
{{/dtdd}}
{{#dtdd name="retryCount" type="Number"}}
The number of times the client has tried to reconnect since the
connection was lost. 0 when connected.
{{/dtdd}}
{{#dtdd name="retryTime" type="Number or undefined"}}
The estimated time of the next reconnection attempt. To turn this
into an interval until the next reconnection, use
`retryTime - (new Date()).getTime()`. This key will
be set only when `status` is `waiting`.
{{/dtdd}}
{{#dtdd name="reason" type="String or undefined"}}
If `status` is `failed`, a description of why the connection failed.
{{/dtdd}}
</dl>
Instead of using callbacks to notify you on changes, this is
a [reactive](#reactivity) data source. You can use it in a
[template](#livehtmltemplates) or [computation](#tracker_autorun)
to get realtime updates.
{{> autoApiBox "Meteor.reconnect"}}
{{> autoApiBox "Meteor.disconnect"}}
Call this method to disconnect from the server and stop all
live data updates. While the client is disconnected it will not receive
updates to collections, method calls will be queued until the
connection is reestablished, and hot code push will be disabled.
Call [Meteor.reconnect](#meteor_reconnect) to reestablish the connection
and resume data transfer.
This can be used to save battery on mobile devices when real time
updates are not required.
{{> autoApiBox "Meteor.onConnection"}}
`onConnection` returns an object with a single method `stop`. Calling
`stop` unregisters the callback, so that this callback will no longer
be called on new connections.
The callback is called with a single argument, the server-side
`connection` representing the connection from the client. This object
contains the following fields:
<dl class="objdesc">
{{#dtdd name="id" type="String"}}
A globally unique id for this connection.
{{/dtdd}}
{{#dtdd name="close" type="Function"}}
Close this DDP connection. The client is free to reconnect, but will
receive a different connection with a new `id` if it does.
{{/dtdd}}
{{#dtdd name="onClose" type="Function"}}
Register a callback to be called when the connection is closed. If the
connection is already closed, the callback will be called immediately.
{{/dtdd}}
{{#dtdd name="clientAddress" type="String"}}
The IP address of the client in dotted form (such as `"127.0.0.1"`).
If you're running your Meteor server behind a proxy (so that clients
are connecting to the proxy instead of to your server directly),
you'll need to set the `HTTP_FORWARDED_COUNT` environment variable
for the correct IP address to be reported by `clientAddress`.
Set `HTTP_FORWARDED_COUNT` to an integer representing the number of
proxies in front of your server. For example, you'd set it to `"1"`
when your server was behind one proxy.
{{/dtdd}}
{{#dtdd name="httpHeaders" type="Object"}}
When the connection came in over an HTTP transport (such as with
Meteor's default SockJS implementation), this field contains
whitelisted HTTP headers.
Cookies are deliberately excluded from the headers as they are a
security risk for this transport. For details and alternatives, see
the [SockJS
documentation](https://github.com/sockjs/sockjs-node#authorisation).
{{/dtdd}}
</dl>
{{#note}}
Currently when a client reconnects to the server (such as after
temporarily losing its Internet connection), it will get a new
connection each time. The `onConnection` callbacks will be called
again, and the new connection will have a new connection `id`.
In the future, when client reconnection is fully implemented,
reconnecting from the client will reconnect to the same connection on
the server: the `onConnection` callback won't be called for that
connection again, and the connection will still have the same
connection `id`.
{{/note}}
{{> autoApiBox "DDP.connect"}}
To call methods on another Meteor application or subscribe to its data
sets, call `DDP.connect` with the URL of the application.
`DDP.connect` returns an object which provides:
* `subscribe` -
Subscribe to a record set. See
[Meteor.subscribe](#meteor_subscribe).
* `call` -
Invoke a method. See [Meteor.call](#meteor_call).
* `apply` -
Invoke a method with an argument array. See
[Meteor.apply](#meteor_apply).
* `methods` -
Define client-only stubs for methods defined on the remote server. See
[Meteor.methods](#meteor_methods).
* `status` -
Get the current connection status. See
[Meteor.status](#meteor_status).
* `reconnect` -
See [Meteor.reconnect](#meteor_reconnect).
* `disconnect` -
See [Meteor.disconnect](#meteor_disconnect).
* `onReconnect` - Set this to a function to be called as the first step of
reconnecting. This function can call methods which will be executed before
any other outstanding methods. For example, this can be used to re-establish
the appropriate authentication context on the new connection.
By default, clients open a connection to the server from which they're loaded.
When you call `Meteor.subscribe`, `Meteor.status`, `Meteor.call`, and
`Meteor.apply`, you are using a connection back to that default
server.
{{/template}}

47
docs/client/api/core.md Normal file
View File

@@ -0,0 +1,47 @@
{{#template name="api_core"}}
<h2 id="core"><span>Meteor Core</span></h2>
{{> autoApiBox "Meteor.isClient"}}
{{> autoApiBox "Meteor.isServer"}}
{{#note}}
`Meteor.isServer` can be used to limit where code runs, but it does not
prevent code from being sent to the client. Any sensitive code that you
don't want served to the client, such as code containing passwords or
authentication mechanisms, should be kept in the `server` directory.
{{/note}}
{{> autoApiBox "Meteor.isCordova"}}
{{> autoApiBox "Meteor.startup"}}
On a server, the function will run as soon as the server process is
finished starting. On a client, the function will run as soon as the DOM
is ready.
The `startup` callbacks are called in the same order as the calls to
`Meteor.startup` were made.
On a client, `startup` callbacks from packages will be called
first, followed by `<body>` templates from your `.html` files,
followed by your application code.
// On server startup, if the database is empty, create some initial data.
if (Meteor.isServer) {
Meteor.startup(function () {
if (Rooms.find().count() === 0) {
Rooms.insert({name: "Initial room"});
}
});
}
{{> autoApiBox "Meteor.wrapAsync"}}
{{> autoApiBox "Meteor.absoluteUrl"}}
{{> autoApiBox "Meteor.settings"}}
{{> autoApiBox "Meteor.release"}}
{{/template}}

90
docs/client/api/ejson.md Normal file
View File

@@ -0,0 +1,90 @@
{{#template name="api_ejson"}}
<h2 id="ejson"><span>EJSON</span></h2>
EJSON is an extension of JSON to support more types. It supports all JSON-safe
types, as well as:
- **Date** (JavaScript `Date`)
- **Binary** (JavaScript `Uint8Array` or the
result of [`EJSON.newBinary`](#ejson_new_binary))
- **User-defined types** (see [`EJSON.addType`](#ejson_add_type). For example,
[`Mongo.ObjectID`](#mongo_object_id) is implemented this way.)
All EJSON serializations are also valid JSON. For example an object with a date
and a binary buffer would be serialized in EJSON as:
{
"d": {"$date": 1358205756553},
"b": {"$binary": "c3VyZS4="}
}
Meteor supports all built-in EJSON data types in publishers, method arguments
and results, Mongo databases, and [`Session`](#session) variables.
{{> autoApiBox "EJSON.parse"}}
{{> autoApiBox "EJSON.stringify"}}
{{> autoApiBox "EJSON.fromJSONValue"}}
{{> autoApiBox "EJSON.toJSONValue"}}
{{> autoApiBox "EJSON.equals"}}
{{> autoApiBox "EJSON.clone"}}
{{> autoApiBox "EJSON.newBinary"}}
Buffers of binary data are represented by `Uint8Array` instances on JavaScript
platforms that support them. On implementations of JavaScript that do not
support `Uint8Array`, binary data buffers are represented by standard arrays
containing numbers ranging from 0 to 255, and the `$Uint8ArrayPolyfill` key
set to `true`.
{{> autoApiBox "EJSON.isBinary"}}
{{> autoApiBox "EJSON.addType"}}
When you add a type to EJSON, Meteor will be able to use that type in:
- publishing objects of your type if you pass them to publish handlers.
- allowing your type in the return values or arguments to
[methods](#methods_header).
- storing your type client-side in Minimongo.
- allowing your type in [`Session`](#session) variables.
Instances of your type must implement [`typeName`](#ejson_type_typeName) and
[`toJSONValue`](#ejson_type_toJSONValue) methods, and may implement
[`clone`](#ejson_type_clone) and [`equals`](#ejson_type_equals) methods if the
default implementations are not sufficient.
{{> autoApiBox "EJSON.CustomType#typeName"}}
{{> autoApiBox "EJSON.CustomType#toJSONValue"}}
For example, the `toJSONValue` method for
[`Mongo.ObjectID`](#mongo_object_id) could be:
function () {
return this.toHexString();
};
{{> autoApiBox "EJSON.CustomType#clone"}}
If your type does not have a `clone` method, `EJSON.clone` will use
[`toJSONValue`](#ejson_type_toJSONValue) and the factory instead.
{{> autoApiBox "EJSON.CustomType#equals"}}
The `equals` method should define an [equivalence
relation](http://en.wikipedia.org/wiki/Equivalence_relation). It should have
the following properties:
- *Reflexivity* - for any instance `a`: `a.equals(a)` must be true.
- *Symmetry* - for any two instances `a` and `b`: `a.equals(b)` if and only if `b.equals(a)`.
- *Transitivity* - for any three instances `a`, `b`, and `c`: `a.equals(b)` and `b.equals(c)` implies `a.equals(c)`.
If your type does not have an `equals` method, `EJSON.equals` will compare the
result of calling [`toJSONValue`](#ejson_type_toJSONValue) instead.
{{/template}}

57
docs/client/api/email.md Normal file
View File

@@ -0,0 +1,57 @@
{{#template name="api_email"}}
<h2 id="email"><span>Email</span></h2>
The `email` package allows sending email from a Meteor app. To use it, add the
package to your project with `$ meteor add email`.
The server reads from the `MAIL_URL` environment variable to determine how to
send mail. Currently, Meteor supports sending mail over SMTP; the `MAIL_URL`
environment variable should be of the form
`smtp://USERNAME:PASSWORD@HOST:PORT/`. For apps deployed with `meteor deploy`,
`MAIL_URL` defaults to an account (provided by
[Mailgun](http://www.mailgun.com/)) which allows apps to send up to 200 emails
per day; you may override this default by assigning to `process.env.MAIL_URL`
before your first call to `Email.send`.
If `MAIL_URL` is not set (eg, when running your application locally),
`Email.send` outputs the message to standard output instead.
{{> autoApiBox "Email.send"}}
You must provide the `from` option and at least one of `to`, `cc`, and `bcc`;
all other options are optional.
`Email.send` only works on the server. Here is an example of how a
client could use a server method call to send an email. (In an actual
application, you'd need to be careful to limit the emails that a
client could send, to prevent your server from being used as a relay
by spammers.)
// In your server code: define a method that the client can call
Meteor.methods({
sendEmail: function (to, from, subject, text) {
check([to, from, subject, text], [String]);
// Let other method calls from the same client start running,
// without waiting for the email sending to complete.
this.unblock();
Email.send({
to: to,
from: from,
subject: subject,
text: text
});
}
});
// In your client code: asynchronously send an email
Meteor.call('sendEmail',
'alice@example.com',
'bob@example.com',
'Hello from Meteor!',
'This is a test of Email.send.');
{{/template}}

102
docs/client/api/http.md Normal file
View File

@@ -0,0 +1,102 @@
{{#template name="api_http"}}
<h2 id="http"><span>HTTP</span></h2>
`HTTP` provides an HTTP request API on the client and server. To use
these functions, add the HTTP package to your project with `$ meteor add http`.
{{> autoApiBox "HTTP.call"}}
This function initiates an HTTP request to a remote server.
On the server, this function can be run either synchronously or
asynchronously. If the callback is omitted, it runs synchronously
and the results are returned once the request completes successfully.
If the request was not successful, an error is thrown.
This is
useful when making server-to-server HTTP API calls from within Meteor
methods, as the method can succeed or fail based on the results of the
synchronous HTTP call. In this case, consider using
[`this.unblock()`](#method_unblock) to allow other methods on the same
connection to run in
the mean time. On the client, this function must be used
asynchronously by passing a callback.
Both HTTP and HTTPS protocols are supported. The `url` argument must be
an absolute URL including protocol and host name on the server, but may be
relative to the current host on the client. The `query` option
replaces the query string of `url`. Parameters specified in `params`
that are put in the URL are appended to any query string.
For example, with a `url` of `"/path?query"` and
`params` of `{foo:"bar"}`, the final URL will be `"/path?query&foo=bar"`.
The `params` are put in the URL or the request body, depending on the
type of request. In the case of request with no bodies, like GET and
HEAD, the parameters will always go in the URL. For a POST or other
type of request, the parameters will be encoded into the body with a
standard `x-www-form-urlencoded` content type, unless the `content`
or `data` option is used to specify a body, in which case the
parameters will be appended to the URL instead.
When run in asynchronous mode, the callback receives two arguments,
`error` and `result`. The
`error` argument will contain an Error if the request fails in any
way, including a network error, time-out, or an HTTP status code in
the 400 or 500 range. In case of a 4xx/5xx HTTP status code, the
`response` property on `error` matches the contents of the result
object. When run in synchronous mode, either `result` is returned
from the function, or `error` is thrown.
Contents of the result object:
<dl class="objdesc">
<dt><span class="name">statusCode</span>
<span class="type">Number</span></dt>
<dd>Numeric HTTP result status code, or <code>null</code> on error.</dd>
<dt><span class="name">content</span>
<span class="type">String</span></dt>
<dd>The body of the HTTP response as a string.</dd>
<dt><span class="name">data</span>
<span class="type">Object or <code>null</code></span></dt>
<dd>If the response headers indicate JSON content, this contains the body of the document parsed as a JSON object.</dd>
<dt><span class="name">headers</span>
<span class="type">Object</span></dt>
<dd>A dictionary of HTTP headers from the response.</dd>
</dl>
Example server method:
Meteor.methods({checkTwitter: function (userId) {
check(userId, String);
this.unblock();
try {
var result = HTTP.call("GET", "http://api.twitter.com/xyz",
{params: {user: userId}});
return true;
} catch (e) {
// Got a network error, time-out or HTTP error in the 400 or 500 range.
return false;
}
}});
Example asynchronous HTTP call:
HTTP.call("POST", "http://api.twitter.com/xyz",
{data: {some: "json", stuff: 1}},
function (error, result) {
if (!error) {
Session.set("twizzled", true);
}
});
{{> autoApiBox "HTTP.get"}}
{{> autoApiBox "HTTP.post"}}
{{> autoApiBox "HTTP.put"}}
{{> autoApiBox "HTTP.del"}}
{{/template}}

167
docs/client/api/methods.md Normal file
View File

@@ -0,0 +1,167 @@
{{#template name="api_methods"}}
<h2 id="methods_header"><span>Methods</span></h2>
Methods are remote functions that Meteor clients can invoke.
{{> autoApiBox "Meteor.methods"}}
Example:
Meteor.methods({
foo: function (arg1, arg2) {
check(arg1, String);
check(arg2, [Number]);
// .. do stuff ..
if (you want to throw an error)
throw new Meteor.Error("pants-not-found", "Can't find my pants");
return "some return value";
},
bar: function () {
// .. do other stuff ..
return "baz";
}
});
Calling `methods` on the server defines functions that can be called remotely by
clients. They should return an [EJSON](#ejson)-able value or throw an
exception. Inside your method invocation, `this` is bound to a method
invocation object, which provides the following:
* `isSimulation`: a boolean value, true if this invocation is a stub.
* `unblock`: when called, allows the next method from this client to
begin running.
* `userId`: the id of the current user.
* `setUserId`: a function that associates the current client with a user.
* `connection`: on the server, the [connection](#meteor_onconnection) this method call was received on.
Calling `methods` on the client defines *stub* functions associated with
server methods of the same name. You don't have to define a stub for
your method if you don't want to. In that case, method calls are just
like remote procedure calls in other systems, and you'll have to wait
for the results from the server.
If you do define a stub, when a client invokes a server method it will
also run its stub in parallel. On the client, the return value of a
stub is ignored. Stubs are run for their side-effects: they are
intended to *simulate* the result of what the server's method will do,
but without waiting for the round trip delay. If a stub throws an
exception it will be logged to the console.
You use methods all the time, because the database mutators
([`insert`](#insert), [`update`](#update), [`remove`](#remove)) are implemented
as methods. When you call any of these functions on the client, you're invoking
their stub version that update the local cache, and sending the same write
request to the server. When the server responds, the client updates the local
cache with the writes that actually occurred on the server.
Since methods usually expect particular types as arguments,
use [`check`](#check) liberally to ensure your method arguments have
the correct [types and structure](#matchpatterns).
{{> autoApiBox "MethodInvocation#userId"}}
The user id is an arbitrary string &mdash; typically the id of the user record
in the database. You can set it with the `setUserId` function. If you're using
the [Meteor accounts system](#accounts_api) then this is handled for you.
{{> autoApiBox "MethodInvocation#setUserId"}}
Call this function to change the currently logged in user on the
connection that made this method call. This simply sets the value of
`userId` for future method calls received on this connection. Pass
`null` to log out the connection.
If you are using the [built-in Meteor accounts system](#accounts_api) then this
should correspond to the `_id` field of a document in the
[`Meteor.users`](#meteor_users) collection.
`setUserId` is not retroactive. It affects the current method call and
any future method calls on the connection. Any previous method calls on
this connection will still see the value of `userId` that was in effect
when they started.
{{> autoApiBox "MethodInvocation#isSimulation"}}
{{> autoApiBox "MethodInvocation#unblock"}}
On the server, methods from a given client run one at a time. The N+1th
invocation from a client won't start until the Nth invocation
returns. However, you can change this by calling `this.unblock`. This
will allow the N+1th invocation to start running in a new fiber.
{{> autoApiBox "MethodInvocation#connection"}}
{{> autoApiBox "Meteor.Error"}}
If you want to return an error from a method, throw an exception. Methods can
throw any kind of exception. But `Meteor.Error` is the only kind of error that
a server will send to the client. If a method function throws a different
exception, then it will be mapped to a sanitized version on the
wire. Specifically, if the `sanitizedError` field on the thrown error is set to
a `Meteor.Error`, then that error will be sent to the client. Otherwise, if no
sanitized version is available, the client gets
`Meteor.Error(500, 'Internal server error')`.
{{> autoApiBox "Meteor.call"}}
This is how to invoke a method. It will run the method on the server. If a
stub is available, it will also run the stub on the client. (See also
[`Meteor.apply`](#meteor_apply), which is identical to `Meteor.call` except that
you specify the parameters as an array instead of as separate arguments and you
can specify a few options controlling how the method is executed.)
If you include a callback function as the last argument (which can't be
an argument to the method, since functions aren't serializable), the
method will run asynchronously: it will return nothing in particular and
will not throw an exception. When the method is complete (which may or
may not happen before `Meteor.call` returns), the callback will be
called with two arguments: `error` and `result`. If an error was thrown,
then `error` will be the exception object. Otherwise, `error` will be
undefined and the return value (possibly undefined) will be in `result`.
// async call
Meteor.call('foo', 1, 2, function (error, result) { ... } );
If you do not pass a callback on the server, the method invocation will
block until the method is complete. It will eventually return the
return value of the method, or it will throw an exception if the method
threw an exception. (Possibly mapped to 500 Server Error if the
exception happened remotely and it was not a `Meteor.Error` exception.)
// sync call
var result = Meteor.call('foo', 1, 2);
On the client, if you do not pass a callback and you are not inside a
stub, `call` will return `undefined`, and you will have no way to get
the return value of the method. That is because the client doesn't have
fibers, so there is not actually any way it can block on the remote
execution of a method.
Finally, if you are inside a stub on the client and call another
method, the other method is not executed (no RPC is generated, nothing
"real" happens). If that other method has a stub, that stub stands in
for the method and is executed. The method call's return value is the
return value of the stub function. The client has no problem executing
a stub synchronously, and that is why it's okay for the client to use
the synchronous `Meteor.call` form from inside a method body, as
described earlier.
Meteor tracks the database writes performed by methods, both on the client and
the server, and does not invoke `asyncCallback` until all of the server's writes
replace the stub's writes in the local cache. In some cases, there can be a lag
between the method's return value being available and the writes being visible:
for example, if another method still outstanding wrote to the same document, the
local cache may not be up to date until the other method finishes as well. If
you want to process the method's result as soon as it arrives from the server,
even if the method's writes are not available yet, you can specify an
`onResultReceived` callback to [`Meteor.apply`](#meteor_apply).
{{> autoApiBox "Meteor.apply"}}
`Meteor.apply` is just like `Meteor.call`, except that the method arguments are
passed as an array rather than directly as arguments, and you can specify
options about how the client executes the method.
{{/template}}

View File

@@ -0,0 +1,57 @@
{{#template name="api_mobile_config"}}
<h2 id="mobileconfigjs"><span>Mobile Config File</span></h2>
If your Meteor application targets mobile platforms such as iOS or
Android, you can configure your app's metadata and build process
in a special top-level file called
`mobile-config.js` which is *not* included in your application and is used only
for this configuration.
The code snippet below is an example `mobile-config.js` file. The rest of this
section will explain the specific API commands in greater detail.
```javascript
// This section sets up some basic app metadata,
// the entire section is optional.
App.info({
id: 'com.example.matt.uber',
name: 'über',
description: 'Get über power in one button click',
author: 'Matt Development Group',
email: 'contact@example.com',
website: 'http://example.com'
});
// Set up resources such as icons and launch screens.
App.icons({
'iphone': 'icons/icon-60.png',
'iphone_2x': 'icons/icon-60@2x.png',
// ... more screen sizes and platforms ...
});
App.launchScreens({
'iphone': 'splash/Default~iphone.png',
'iphone_2x': 'splash/Default@2x~iphone.png',
// ... more screen sizes and platforms ...
});
// Set PhoneGap/Cordova preferences
App.setPreference('BackgroundColor', '0xff0000ff');
App.setPreference('HideKeyboardFormAccessoryBar', true);
// Pass preferences for a particular PhoneGap/Cordova plugin
App.configurePlugin('com.phonegap.plugins.facebookconnect', {
APP_ID: '1234567890',
API_KEY: 'supersecretapikey'
});
```
{{> autoApiBox "App.info"}}
{{> autoApiBox "App.setPreference"}}
{{> autoApiBox "App.configurePlugin"}}
{{> autoApiBox "App.icons"}}
{{> autoApiBox "App.launchScreens"}}
{{/template}}

View File

@@ -0,0 +1,122 @@
{{#template name="api_packagejs"}}
<h2 id="packagejs"><span>Package.js</span></h2>
{{#markdown}} A package is a directory containing a package.js file, which
contains roughly three major sections: a basic description, a package
definition, and a test definition. By default, the directory name is the name of
the package.
The `package.js` file below is an example of how to use the packaging API. The
rest of this section will explain the specific API commands in greater detail.
/* Information about this package */
Package.describe({
// Short two-sentence summary.
summary: "What this does",
// Version number.
version: "1.0.0",
// Optional. Default is package directory name.
name: "username:package-name",
// Optional github URL to your source repository.
git: "https://github.com/something/something.git",
});
/* This defines your actual package */
Package.onUse(function (api) {
// If no version is specified for an 'api.use' dependency, use the
// one defined in Meteor 0.9.0.
api.versionsFrom('0.9.0');
// Use Underscore package, but only on the server.
// Version not specified, so it will be as of Meteor 0.9.0.
api.use('underscore', 'server');
// Use application-configuration package, version 1.0.0 or newer.
api.use('application-configuration@1.0.0');
// Give users of this package access to the Templating package.
api.imply('templating')
// Export the object 'Email' to packages or apps that use this package.
api.export('Email', 'server');
// Specify the source code for the package.
api.addFiles('email.js', 'server');
});
/* This defines the tests for the package */
Package.onTest(function (api) {
// Sets up a dependency on this package
api.use('username:package-name');
// Allows you to use the 'tinytest' framework
api.use('tinytest@1.0.0');
// Specify the source code for the package tests
api.addFiles('email_tests.js', 'server');
});
/* This lets you use npm packages in your package*/
Npm.depends({
simplesmtp: "0.3.10",
"stream-buffers": "0.2.5"});
Build plugins are created with `_transitional_registerBuildPlugin`, an API that is
very much in flux. See the coffeescript package for an
example. Build plugins are fully-fledged Meteor programs in their own right and
have their own namespace, package dependencies, source files and npm
requirements.
<h3 id="packagedescription"><span>Package Description</span></h3>
Provide basic package information with `Package.describe(options)`. To publish a
package, you must define `summary` and `version`.
{{/markdown}}
{{> autoApiBox "Package.describe"}}
<h3 id="packagedefinition"><span>Package Definition</span></h3>
{{#markdown}}
Define dependencies and expose package methods with the
`Package.onUse` handler. This section lets you define what packages your package
depends on, what packages are implied by your package, and what object your
package is exported to.
{{/markdown}}
{{> autoApiBox "Package.onUse"}}
{{> autoApiBox "PackageAPI#versionsFrom" }}
{{> autoApiBox "PackageAPI#use" }}
{{> autoApiBox "PackageAPI#imply" }}
{{> autoApiBox "PackageAPI#export" }}
{{> autoApiBox "PackageAPI#addFiles" }}
<h3 id="packagetests"><span>Unit Tests</span></h3>
{{#markdown}}
Set up your tests with the `Package.onTest` handler, which has an interface
that's parallel to that of the `onUse` handler. The tests will need to depend on
the package that you have just created. For example, if your package is the
`email` package, you have to call `api.use('email')` in order to test the
package.
If you used `meteor create` to set up your package, Meteor will create the
required scaffolding in `package.js`, and you'll only need to add unit test code
in the `_test.js` file that was created.
{{/markdown}}
{{> autoApiBox "Package.onTest"}}
<h3><span>External Packages and Plugins</span></h3>
{{#markdown}}
Meteor packages can include NPM packages and Cordova plugins by using
`Npm.depends` and `Cordova.depends` in the `package.js` file.
{{/markdown}}
{{> autoApiBox "Npm.depends"}}
{{> autoApiBox "Npm.require"}}
{{> autoApiBox "Cordova.depends"}}
{{> autoApiBox "Package.registerBuildPlugin"}}
{{> autoApiBox "Plugin.registerSourceHandler"}}
{{/template}}

View File

@@ -0,0 +1,148 @@
{{#template name="api_passwords"}}
<h2 id="accounts_passwords"><span>Passwords</span></h2>
The `accounts-password` package contains a full system for password-based
authentication. In addition to the basic username and password-based
sign-in process, it also supports email-based sign-in including
address verification and password recovery emails.
The Meteor server stores passwords using the
[bcrypt](http://en.wikipedia.org/wiki/Bcrypt) algorithm. This helps
protect against embarrassing password leaks if the server's database is
compromised.
To add password support to your application, run `$ meteor add
accounts-password`. You can construct your own user interface using the
functions below, or use the [`accounts-ui` package](#accountsui) to
include a turn-key user interface for password-based sign-in.
{{> autoApiBox "Accounts.createUser"}}
On the client, this function logs in as the newly created user on
successful completion. On the server, it returns the newly created user
id.
On the client, you must pass `password` and at least one of `username` or
`email` &mdash; enough information for the user to be able to log in again
later. On the server, you do not need to specify `password`, but the user will
not be able to log in until it has a password (eg, set with
[`Accounts.setPassword`](#accounts_setpassword)).
To create an account without a password on the server and still let the
user pick their own password, call `createUser` with the `email` option
and then
call [`Accounts.sendEnrollmentEmail`](#accounts_sendenrollmentemail). This
will send the user an email with a link to set their initial password.
By default the `profile` option is added directly to the new user document. To
override this behavior, use [`Accounts.onCreateUser`](#accounts_oncreateuser).
This function is only used for creating users with passwords. The external
service login flows do not use this function.
{{> autoApiBox "Accounts.changePassword"}}
{{> autoApiBox "Accounts.forgotPassword"}}
This triggers a call
to [`Accounts.sendResetPasswordEmail`](#accounts_sendresetpasswordemail)
on the server. When the user visits the link in this email, the callback
registered with [`Accounts.onResetPasswordLink`](#Accounts-onResetPasswordLink)
will be called.
If you are using the [`accounts-ui` package](#accountsui), this is handled
automatically. Otherwise, it is your responsiblity to prompt the user for the
new password and call `resetPassword`.
{{> autoApiBox "Accounts.resetPassword"}}
This function accepts tokens passed into the callbacks registered with
[`Accounts.onResetPasswordLink`](#Accounts-onResetPasswordLink) and
[`Accounts.onEnrollmentLink`](#Accounts-onEnrollmentLink).
{{> autoApiBox "Accounts.setPassword"}}
{{> autoApiBox "Accounts.verifyEmail"}}
This function accepts tokens passed into the callback registered with
[`Accounts.onEmailVerificationLink`](#Accounts-onEmailVerificationLink).
{{> autoApiBox "Accounts.sendResetPasswordEmail"}}
When the user visits the link in this email, the callback registered with
[`Accounts.onResetPasswordLink`](#Accounts-onResetPasswordLink) will be called.
To customize the contents of the email, see
[`Accounts.emailTemplates`](#accounts_emailtemplates).
{{> autoApiBox "Accounts.sendEnrollmentEmail"}}
When the user visits the link in this email, the callback registered with
[`Accounts.onEnrollmentLink`](#Accounts-onEnrollmentLink) will be called.
To customize the contents of the email, see
[`Accounts.emailTemplates`](#accounts_emailtemplates).
{{> autoApiBox "Accounts.sendVerificationEmail"}}
When the user visits the link in this email, the callback registered with
[`Accounts.onEmailVerificationLink`](#Accounts-onEmailVerificationLink) will
be called.
To customize the contents of the email, see
[`Accounts.emailTemplates`](#accounts_emailtemplates).
{{> autoApiBox "Accounts.onResetPasswordLink"}}
{{> autoApiBox "Accounts.onEnrollmentLink"}}
{{> autoApiBox "Accounts.onEmailVerificationLink"}}
{{> autoApiBox "Accounts.emailTemplates"}}
This is an `Object` with several fields that are used to generate text/html
for the emails sent by `sendResetPasswordEmail`, `sendEnrollmentEmail`,
and `sendVerificationEmail`.
Override fields of the object by assigning to them:
- `from`: A `String` with an [RFC5322](http://tools.ietf.org/html/rfc5322) From
address. By default, the email is sent from `no-reply@meteor.com`. If you
wish to receive email from users asking for help with their account, be sure
to set this to an email address that you can receive email at.
- `siteName`: The public name of your application. Defaults to the DNS name of
the application (eg: `awesome.meteor.com`).
- `resetPassword`: An `Object` with two fields:
- `resetPassword.subject`: A `Function` that takes a user object and returns
a `String` for the subject line of a reset password email.
- `resetPassword.text`: A `Function` that takes a user object and a url, and
returns the body text for a reset password email.
- `resetPassword.html`: An optional `Function` that takes a user object and a
url, and returns the body html for a reset password email.
- `enrollAccount`: Same as `resetPassword`, but for initial password setup for
new accounts.
- `verifyEmail`: Same as `resetPassword`, but for verifying the users email
address.
Example:
Accounts.emailTemplates.siteName = "AwesomeSite";
Accounts.emailTemplates.from = "AwesomeSite Admin <accounts@example.com>";
Accounts.emailTemplates.enrollAccount.subject = function (user) {
return "Welcome to Awesome Town, " + user.profile.name;
};
Accounts.emailTemplates.enrollAccount.text = function (user, url) {
return "You have been selected to participate in building a better future!"
+ " To activate your account, simply click the link below:\n\n"
+ url;
};
{{/template}}

221
docs/client/api/pubsub.md Normal file
View File

@@ -0,0 +1,221 @@
{{#template name="api_pubsub"}}
<h2 id="publishandsubscribe"><span>Publish and subscribe</span></h2>
These functions control how Meteor servers publish sets of records and
how clients can subscribe to those sets.
{{> autoApiBox "Meteor.publish"}}
To publish records to clients, call `Meteor.publish` on the server with
two parameters: the name of the record set, and a *publish function*
that Meteor will call each time a client subscribes to the name.
Publish functions can return a
[`Collection.Cursor`](#mongo_cursor), in which case Meteor
will publish that cursor's documents to each subscribed client. You can
also return an array of `Collection.Cursor`s, in which case Meteor will
publish all of the cursors.
{{#warning}}
If you return multiple cursors in an array, they currently must all be from
different collections. We hope to lift this restriction in a future release.
{{/warning}}
// server: publish the rooms collection, minus secret info.
Meteor.publish("rooms", function () {
return Rooms.find({}, {fields: {secretInfo: 0}});
});
// ... and publish secret info for rooms where the logged-in user
// is an admin. If the client subscribes to both streams, the records
// are merged together into the same documents in the Rooms collection.
Meteor.publish("adminSecretInfo", function () {
return Rooms.find({admin: this.userId}, {fields: {secretInfo: 1}});
});
// publish dependent documents and simulate joins
Meteor.publish("roomAndMessages", function (roomId) {
check(roomId, String);
return [
Rooms.find({_id: roomId}, {fields: {secretInfo: 0}}),
Messages.find({roomId: roomId})
];
});
Alternatively, a publish function can directly control its published record set
by calling the functions [`added`](#publish_added) (to add a new document to the
published record set), [`changed`](#publish_changed) (to change or clear some
fields on a document already in the published record set), and
[`removed`](#publish_removed) (to remove documents from the published record
set). These methods are provided by `this` in your publish function.
If a publish function does not return a cursor or array of cursors, it is
assumed to be using the low-level `added`/`changed`/`removed` interface, and it
**must also call [`ready`](#publish_ready) once the initial record set is
complete**.
Example:
// server: publish the current size of a collection
Meteor.publish("counts-by-room", function (roomId) {
var self = this;
check(roomId, String);
var count = 0;
var initializing = true;
// observeChanges only returns after the initial `added` callbacks
// have run. Until then, we don't want to send a lot of
// `self.changed()` messages - hence tracking the
// `initializing` state.
var handle = Messages.find({roomId: roomId}).observeChanges({
added: function (id) {
count++;
if (!initializing)
self.changed("counts", roomId, {count: count});
},
removed: function (id) {
count--;
self.changed("counts", roomId, {count: count});
}
// don't care about changed
});
// Instead, we'll send one `self.added()` message right after
// observeChanges has returned, and mark the subscription as
// ready.
initializing = false;
self.added("counts", roomId, {count: count});
self.ready();
// Stop observing the cursor when client unsubs.
// Stopping a subscription automatically takes
// care of sending the client any removed messages.
self.onStop(function () {
handle.stop();
});
});
// client: declare collection to hold count object
Counts = new Mongo.Collection("counts");
// client: subscribe to the count for the current room
Tracker.autorun(function () {
Meteor.subscribe("counts-by-room", Session.get("roomId"));
});
// client: use the new collection
console.log("Current room has " +
Counts.findOne(Session.get("roomId")).count +
" messages.");
// server: sometimes publish a query, sometimes publish nothing
Meteor.publish("secretData", function () {
if (this.userId === 'superuser') {
return SecretData.find();
} else {
// Declare that no data is being published. If you leave this line
// out, Meteor will never consider the subscription ready because
// it thinks you're using the added/changed/removed interface where
// you have to explicitly call this.ready().
return [];
}
});
Since publish functions usually expect particular types as arguments,
use [`check`](#check) liberally to ensure the arguments have
the correct [types and structure](#matchpatterns).
{{#warning}}
Meteor will emit a warning message if you call `Meteor.publish` in a
project that includes the `autopublish` package. Your publish function
will still work.
{{/warning}}
{{> autoApiBox "Subscription#userId"}}
This is constant. However, if the logged-in user changes, the publish
function is rerun with the new value.
{{> autoApiBox "Subscription#added"}}
{{> autoApiBox "Subscription#changed"}}
{{> autoApiBox "Subscription#removed"}}
{{> autoApiBox "Subscription#ready"}}
{{> autoApiBox "Subscription#onStop"}}
If you call [`observe`](#observe) or [`observeChanges`](#observe_changes) in your
publish handler, this is the place to stop the observes.
{{> autoApiBox "Subscription#error"}}
{{> autoApiBox "Subscription#stop"}}
{{> autoApiBox "Subscription#connection"}}
{{> autoApiBox "Meteor.subscribe"}}
When you subscribe to a record set, it tells the server to send records to the
client. The client stores these records in local [Minimongo
collections](#mongo_collection), with the same name as the `collection`
argument used in the publish handler's [`added`](#publish_added),
[`changed`](#publish_changed), and [`removed`](#publish_removed)
callbacks. Meteor will queue incoming records until you declare the
[`Mongo.Collection`](#mongo_collection) on the client with the matching
collection name.
// okay to subscribe (and possibly receive data) before declaring
// the client collection that will hold it. assume "allplayers"
// publishes data from server's "players" collection.
Meteor.subscribe("allplayers");
...
// client queues incoming players records until ...
...
Players = new Mongo.Collection("players");
The client will see a document if the document is currently in the published
record set of any of its subscriptions.
The `onReady` callback is called with no arguments when the server
[marks the subscription as ready](#publish_ready). The `onError` callback is
called with a [`Meteor.Error`](#meteor_error) if the subscription fails or is
terminated by the server.
`Meteor.subscribe` returns a subscription handle, which is an object with the
following methods:
<dl class="callbacks">
{{#dtdd "stop()"}}
Cancel the subscription. This will typically result in the server directing the
client to remove the subscription's data from the client's cache.
{{/dtdd}}
{{#dtdd "ready()"}}
True if the server has [marked the subscription as ready](#publish_ready). A
reactive data source.
{{/dtdd}}
</dl>
If you call `Meteor.subscribe` within a [reactive computation](#reactivity),
for example using
[`Tracker.autorun`](#tracker_autorun), the subscription will automatically be
cancelled when the computation is invalidated or stopped; it's not necessary
to call `stop` on
subscriptions made from inside `autorun`. However, if the next iteration
of your run function subscribes to the same record set (same name and
parameters), Meteor is smart enough to skip a wasteful
unsubscribe/resubscribe. For example:
Tracker.autorun(function () {
Meteor.subscribe("chat", {room: Session.get("current-room")});
Meteor.subscribe("privateMessages");
});
This subscribes you to the chat messages in the current room and to your private
messages. When you change rooms by calling `Session.set("current-room",
"new-room")`, Meteor will subscribe to the new room's chat messages,
unsubscribe from the original room's chat messages, and continue to
stay subscribed to your private messages.
If more than one subscription sends conflicting values for a field (same
collection name, document ID, and field name), then the value on the client will
be one of the published values, chosen arbitrarily.
{{/template}}

View File

@@ -0,0 +1,41 @@
{{#template name="api_reactive_var"}}
<h2 id="reactivevar_pkg"><span>ReactiveVar</span></h2>
To use `ReactiveVar`, add the `reactive-var` package to your project with
`$ meteor add reactive-var`.
{{> autoApiBox "ReactiveVar"}}
A ReactiveVar holds a single value that can be get and set, such that calling
`set` will invalidate any Computations that called `get`, according to the
usual contract for reactive data sources.
A ReactiveVar is similar to a Session variable, with a few differences:
* ReactiveVars don't have global names, like the "foo" in `Session.get("foo")`.
Instead, they may be created and used locally, for example attached to a
template instance, as in: `this.foo.get()`.
* ReactiveVars are not automatically migrated across hot code pushes,
whereas Session state is.
* ReactiveVars can hold any value, while Session variables are limited to
JSON or EJSON.
An important property of ReactiveVars &mdash; which is sometimes a
reason for using one &mdash; is that setting the value to the same
value as before has no effect; it does not trigger any invalidations.
So if one autorun sets a ReactiveVar, and another autorun gets the
ReactiveVar, a re-run of the first autorun won't necessarily trigger
the second. By default, only primitive values are compared this way,
while calling `set` on an argument that is an *object* (not a
primitive) always counts as a change. You can configure this behavior
using the `equalsFunc` argument.
{{> autoApiBox "ReactiveVar#get"}}
{{> autoApiBox "ReactiveVar#set"}}
{{/template}}

112
docs/client/api/session.md Normal file
View File

@@ -0,0 +1,112 @@
{{#template name="api_session"}}
<h2 id="session"><span>Session</span></h2>
`Session` provides a global object on the client that you can use to
store an arbitrary set of key-value pairs. Use it to store things like
the currently selected item in a list.
What's special about `Session` is that it's reactive. If
you call [`Session.get`](#session_get)`("currentList")`
from inside a template, the template will automatically be rerendered
whenever [`Session.set`](#session_set)`("currentList", x)` is called.
{{> autoApiBox "Session.set"}}
Example:
Tracker.autorun(function () {
Meteor.subscribe("chat-history", {room: Session.get("currentRoomId")});
});
// Causes the function passed to Tracker.autorun to be re-run, so
// that the chat-history subscription is moved to the room "home".
Session.set("currentRoomId", "home");
{{> autoApiBox "Session.setDefault"}}
This is useful in initialization code, to avoid re-initializing a session
variable every time a new version of your app is loaded.
{{> autoApiBox "Session.get"}}
Example:
// in main.html
{{lt}}template name="main">
<p>We've always been at war with {{dstache}}theEnemy}}.</p>
{{lt}}/template>
// in main.js
Template.main.helpers({
theEnemy: function () {
return Session.get("enemy");
}
});
Session.set("enemy", "Eastasia");
// Page will say "We've always been at war with Eastasia"
Session.set("enemy", "Eurasia");
// Page will change to say "We've always been at war with Eurasia"
{{> autoApiBox "Session.equals"}}
If value is a scalar, then these two expressions do the same thing:
(1) Session.get("key") === value
(2) Session.equals("key", value)
... but the second one is always better. It triggers fewer invalidations
(template redraws), making your program more efficient.
Example:
{{lt}}template name="postsView">
{{dstache}}! Show a dynamically updating list of items. Let the user click on an
item to select it. The selected item is given a CSS class so it
can be rendered differently. }}
{{dstache}}#each posts}}
{{dstache}}> postItem }}
{{dstache}}/each}}
{{lt}}/template>
{{lt}}template name="postItem">
<div class="{{dstache}}postClass}}">{{dstache}}title}}</div>
{{lt}}/template>
///// in JS file
Template.postsView.helpers({
posts: function() {
return Posts.find();
}
});
Template.postItem.helpers({
postClass: function() {
return Session.equals("selectedPost", this._id) ?
"selected" : "";
}
});
Template.postItem.events({
'click': function() {
Session.set("selectedPost", this._id);
}
});
// Using Session.equals here means that when the user clicks
// on an item and changes the selection, only the newly selected
// and the newly unselected items are re-rendered.
//
// If Session.get had been used instead of Session.equals, then
// when the selection changed, all the items would be re-rendered.
For object and array session values, you cannot use `Session.equals`; instead,
you need to use the `underscore` package and write
`_.isEqual(Session.get(key), value)`.
{{/template}}

View File

@@ -0,0 +1,364 @@
{{#template name="api_templates"}}
<h2 id="templates_api"><span>Templates</span></h2>
When you write a template as `<{{! }}template name="foo"> ... <{{!
}}/template>` in an HTML file in your app, Meteor generates a
"template object" named `Template.foo`.
The same template may occur many times on a page, and these
occurrences are called template instances. Template instances have a
life cycle of being created, put into the document, and later taken
out of the document and destroyed. Meteor manages these stages for
you, including determining when a template instance has been removed
or replaced and should be cleaned up. You can associate data with a
template instance, and you can access its DOM nodes when it is in the
document.
{{> autoApiBox "Template#events"}}
Declare event handlers for instances of this template. Multiple calls add
new event handlers in addition to the existing ones.
See [Event Maps](#eventmaps) for a detailed description of the event
map format and how event handling works in Meteor.
{{> autoApiBox "Template#helpers"}}
Each template has a local dictionary of helpers that are made available to it,
and this call specifies helpers to add to the template's dictionary.
Example:
Template.myTemplate.helpers({
foo: function () {
return Session.get("foo");
}
});
Now you can invoke this helper with `{{dstache}}foo}}` in the template defined
with `<{{! }}template name="myTemplate">`.
To create a helper that can be used in any template, use
[`Template.registerHelper`](#template_registerhelper).
{{> autoApiBox "Template#rendered"}}
This callback is called once when an instance of Template.*myTemplate* is
rendered into DOM nodes and put into the document for the first time.
In the body of the callback, `this` is a [template
instance](#template_inst) object that is unique to this occurrence of
the template and persists across re-renderings. Use the `created` and
`destroyed` callbacks to perform initialization or clean-up on the
object.
Because your template has been rendered, you can use functions like
[`this.findAll`](#template_findAll) which look at its DOM nodes.
{{> autoApiBox "Template#created"}}
This callback is called before your template's logic is evaluated for the first
time. Inside the callback, `this` is the new [template
instance](#template_inst) object. Properties you set on this object will be
visible from the `rendered` and `destroyed` callbacks and from event handlers.
This callback fires once and is the first callback to fire. Every
`created` has a corresponding `destroyed`; that is, if you get a
`created` callback with a certain template instance object in `this`,
you will eventually get a `destroyed` callback for the same object.
`created` is a useful way to set up values on template instance that are
read from template helpers using `Template.instance()`.
{{> autoApiBox "Template#destroyed"}}
This callback is called when an occurrence of a template is taken off
the page for any reason and not replaced with a re-rendering. Inside
the callback, `this` is the [template instance](#template_inst) object
being destroyed.
This callback is most useful for cleaning up or undoing any external effects of
`created` or `rendered`. It fires once and is the last callback to fire.
<h2 id="template_inst"><span>Template instances</span></h2>
A template instance object represents an occurrence of a template in
the document. It can be used to access the DOM and it can be
assigned properties that persist as the template is reactively updated.
Template instance objects are found as the value of `this` in the
`created`, `rendered`, and `destroyed` template callbacks, and as an
argument to event handlers. You can access the current template instance
from helpers using [`Template.instance()`](#template_instance).
In addition to the properties and functions described below, you can assign
additional properties of your choice to the object. Use the
[`created`](#template_created) and [`destroyed`](#template_destroyed) callbacks
to perform initialization or clean-up on the object.
You can only access `findAll`, `find`, `firstNode`, and `lastNode`
from the `rendered` callback and event handlers, not from `created`
and `destroyed`, because they require the template instance to be
in the DOM.
Template instance objects are `instanceof Blaze.TemplateInstance`.
{{> autoApiBox "Blaze.TemplateInstance#findAll"}}
`template.findAll` returns an array of DOM elements matching `selector`.
{{> autoApiBox "Blaze.TemplateInstance#$"}}
`template.$` returns a [jQuery object](http://api.jquery.com/Types/#jQuery) of
those same elements. jQuery objects are similar to arrays, with
additional methods defined by the jQuery library.
The template instance serves as the document root for the selector. Only
elements inside the template and its sub-templates can match parts of
the selector.
{{> autoApiBox "Blaze.TemplateInstance#find"}}
Returns one DOM element matching `selector`, or `null` if there are no
such elements.
The template instance serves as the document root for the selector. Only
elements inside the template and its sub-templates can match parts of
the selector.
{{> autoApiBox "Blaze.TemplateInstance#firstNode"}}
The two nodes `firstNode` and `lastNode` indicate the extent of the
rendered template in the DOM. The rendered template includes these
nodes, their intervening siblings, and their descendents. These two
nodes are siblings (they have the same parent), and `lastNode` comes
after `firstNode`, or else they are the same node.
{{> autoApiBox "Blaze.TemplateInstance#lastNode"}}
{{> autoApiBox "Blaze.TemplateInstance#data"}}
This property provides access to the data context at the top level of
the template. It is updated each time the template is re-rendered.
Access is read-only and non-reactive.
{{> autoApiBox "Blaze.TemplateInstance#autorun"}}
You can use `this.autorun` from a [`created`](#template_created) or
[`rendered`](#template_rendered) callback to reactively update the DOM
or the template instance. The Computation is automatically stopped
when the template is destroyed.
Alias for `template.view.autorun`.
{{> autoApiBox "Blaze.TemplateInstance#view"}}
{{> autoApiBox "Template.registerHelper"}}
{{> autoApiBox "Template.instance"}}
{{> autoApiBox "Template.currentData"}}
{{> autoApiBox "Template.parentData"}}
For example, `Template.parentData(0)` is equivalent to `Template.currentData()`. `Template.parentData(2)`
is equivalent to `{{dstache}}../..}}` in a template.
{{> autoApiBox "Template.body"}}
You can define helpers and event maps on `Template.body` just like on
any `Template.myTemplate` object.
Helpers on `Template.body` are only available in the `<body>` tags of
your app. To register a global helper, use
[Template.registerHelper](#template_registerhelper).
Event maps on `Template.body` don't apply to elements added to the
body via `Blaze.render`, jQuery, or the DOM API, or to the body element
itself. To handle events on the body, window, or document, use jQuery
or the DOM API.
{{> autoApiBox "Template.dynamic"}}
`Template.dynamic` allows you to include a template by name, where the name
may be calculated by a helper and may change reactively. The `data`
argument is optional, and if it is omitted, the current data context
is used.
For example, if there is a template named "foo", `{{dstache}}> Template.dynamic
template="foo"}}` is equivalent to `{{dstache}}> foo}}`.
{{> apiBoxTitle name="Event Maps" id="eventmaps"}}
An event map is an object where
the properties specify a set of events to handle, and the values are
the handlers for those events. The property can be in one of several
forms:
<dl>
{{#dtdd "<em>eventtype</em>"}}
Matches a particular type of event, such as 'click'.
{{/dtdd}}
{{#dtdd "<em>eventtype selector</em>"}}
Matches a particular type of event, but only when it appears on
an element that matches a certain CSS selector.
{{/dtdd}}
{{#dtdd "<em>event1, event2</em>"}}
To handle more than one type of event with the same function, use a
comma-separated list.
{{/dtdd}}
</dl>
The handler function receives two arguments: `event`, an object with
information about the event, and `template`, a [template
instance](#template_inst) for the template where the handler is
defined. The handler also receives some additional context data in
`this`, depending on the context of the current element handling the
event. In a template, an element's context is the
data context where that element occurs, which is set by
block helpers such as `#with` and `#each`.
Example:
{
// Fires when any element is clicked
'click': function (event) { ... },
// Fires when any element with the 'accept' class is clicked
'click .accept': function (event) { ... },
// Fires when 'accept' is clicked or focused, or a key is pressed
'click .accept, focus .accept, keypress': function (event) { ... }
}
Most events bubble up the document tree from their originating
element. For example, `'click p'` catches a click anywhere in a
paragraph, even if the click originated on a link, span, or some other
element inside the paragraph. The originating element of the event
is available as the `target` property, while the element that matched
the selector and is currently handling it is called `currentTarget`.
{
'click p': function (event) {
var paragraph = event.currentTarget; // always a P
var clickedElement = event.target; // could be the P or a child element
}
}
If a selector matches multiple elements that an event bubbles to, it
will be called multiple times, for example in the case of `'click
div'` or `'click *'`. If no selector is given, the handler
will only be called once, on the original target element.
The following properties and methods are available on the event object
passed to handlers:
<dl class="objdesc">
{{#dtdd name="type" type="String"}}
The event's type, such as "click", "blur" or "keypress".
{{/dtdd}}
{{#dtdd name="target" type="DOM Element"}}
The element that originated the event.
{{/dtdd}}
{{#dtdd name="currentTarget" type="DOM Element"}}
The element currently handling the event. This is the element that
matched the selector in the event map. For events that bubble, it may
be `target` or an ancestor of `target`, and its value changes as the
event bubbles.
{{/dtdd}}
{{#dtdd name="which" type="Number"}}
For mouse events, the number of the mouse button (1=left, 2=middle, 3=right).
For key events, a character or key code.
{{/dtdd}}
{{#dtdd "stopPropagation()"}}
Prevent the event from propagating (bubbling) up to other elements.
Other event handlers matching the same element are still fired, in
this and other event maps.
{{/dtdd}}
{{#dtdd "stopImmediatePropagation()"}}
Prevent all additional event handlers from being run on this event,
including other handlers in this event map, handlers reached by
bubbling, and handlers in other event maps.
{{/dtdd}}
{{#dtdd "preventDefault()"}}
Prevents the action the browser would normally take in response to this
event, such as following a link or submitting a form. Further handlers
are still called, but cannot reverse the effect.
{{/dtdd}}
{{#dtdd "isPropagationStopped()"}}
Returns whether `stopPropagation()` has been called for this event.
{{/dtdd}}
{{#dtdd "isImmediatePropagationStopped()"}}
Returns whether `stopImmediatePropagation()` has been called for this event.
{{/dtdd}}
{{#dtdd "isDefaultPrevented()"}}
Returns whether `preventDefault()` has been called for this event.
{{/dtdd}}
</dl>
Returning `false` from a handler is the same as calling
both `stopImmediatePropagation` and `preventDefault` on the event.
Event types and their uses include:
<dl class="objdesc">
{{#dtdd "<code>click</code>"}}
Mouse click on any element, including a link, button, form control, or div.
Use `preventDefault()` to prevent a clicked link from being followed.
Some ways of activating an element from the keyboard also fire `click`.
{{/dtdd}}
{{#dtdd "<code>dblclick</code>"}}
Double-click.
{{/dtdd}}
{{#dtdd "<code>focus, blur</code>"}}
A text input field or other form control gains or loses focus. You
can make any element focusable by giving it a `tabindex` property.
Browsers differ on whether links, checkboxes, and radio buttons are
natively focusable. These events do not bubble.
{{/dtdd}}
{{#dtdd "<code>change</code>"}}
A checkbox or radio button changes state. For text fields, use
`blur` or key events to respond to changes.
{{/dtdd}}
{{#dtdd "<code>mouseenter, mouseleave</code>"}} The pointer enters or
leaves the bounds of an element. These events do not bubble.
{{/dtdd}}
{{#dtdd "<code>mousedown, mouseup</code>"}}
The mouse button is newly down or up.
{{/dtdd}}
{{#dtdd "<code>keydown, keypress, keyup</code>"}}
The user presses a keyboard key. `keypress` is most useful for
catching typing in text fields, while `keydown` and `keyup` can be
used for arrow keys or modifier keys.
{{/dtdd}}
</dl>
Other DOM events are available as well, but for the events above,
Meteor has taken some care to ensure that they work uniformly in all
browsers.
{{/template}}

25
docs/client/api/timers.md Normal file
View File

@@ -0,0 +1,25 @@
{{#template name="api_timers"}}
<h2 id="timers"><span>Timers</span></h2>
Meteor uses global environment variables
to keep track of things like the current request's user. To make sure
these variables have the right values, you need to use
`Meteor.setTimeout` instead of `setTimeout` and `Meteor.setInterval`
instead of `setInterval`.
These functions work just like their native JavaScript equivalents.
If you call the native function, you'll get an error stating that Meteor
code must always run within a Fiber, and advising to use
`Meteor.bindEnvironment`.
{{> autoApiBox "Meteor.setTimeout"}}
Returns a handle that can be used by `Meteor.clearTimeout`.
{{> autoApiBox "Meteor.setInterval"}}
Returns a handle that can be used by `Meteor.clearInterval`.
{{> autoApiBox "Meteor.clearTimeout"}}
{{> autoApiBox "Meteor.clearInterval"}}
{{/template}}

313
docs/client/api/tracker.md Normal file
View File

@@ -0,0 +1,313 @@
{{#template name="api_tracker"}}
<h2 id="tracker"><span>Tracker</span></h2>
Meteor has a simple dependency tracking system which allows it to
automatically rerun templates and other computations whenever
[`Session`](#session) variables, database queries, and other data
sources change.
Unlike most other systems, you don't have to manually declare these
dependencies &mdash; it "just works". The mechanism is simple and
efficient. When you call a function that supports reactive updates
(such as a database query), it automatically saves the current
Computation object, if any (representing, for example, the current
template being rendered). Later, when the data changes, the function
can "invalidate" the Computation, causing it to rerun (rerendering the
template).
Applications will find [`Tracker.autorun`](#tracker_autorun) useful, while more
advanced facilities such as `Tracker.Dependency` and `onInvalidate`
callbacks are intended primarily for package authors implementing new
reactive data sources.
To learn more about how Tracker works and to explore advanced ways to use it,
visit the <a href="http://manual.meteor.com/#tracker"> Tracker</a> chapter in the
<a href="http://manual.meteor.com/">Meteor Manual</a>, which describes it in
complete detail.
{{> autoApiBox "Tracker.autorun" }}
`Tracker.autorun` allows you to run a function that depends on reactive data
sources, in such a way that if there are changes to the data later,
the function will be rerun.
For example, you can monitor a cursor (which is a reactive data
source) and aggregate it into a session variable:
Tracker.autorun(function () {
var oldest = _.max(Monkeys.find().fetch(), function (monkey) {
return monkey.age;
});
if (oldest)
Session.set("oldest", oldest.name);
});
Or you can wait for a session variable to have a certain value, and do
something the first time it does, calling `stop` on the computation to
prevent further rerunning:
Tracker.autorun(function (c) {
if (! Session.equals("shouldAlert", true))
return;
c.stop();
alert("Oh no!");
});
The function is invoked immediately, at which point it may alert and
stop right away if `shouldAlert` is already true. If not, the
function is run again when `shouldAlert` becomes true.
A change to a data dependency does not cause an immediate rerun, but
rather "invalidates" the computation, causing it to rerun the next
time a flush occurs. A flush will occur automatically as soon as
the system is idle if there are invalidated computations. You can
also use [`Tracker.flush`](#tracker_flush) to cause an immediate flush of
all pending reruns.
If you nest calls to `Tracker.autorun`, then when the outer call stops or
reruns, the inner call will stop automatically. Subscriptions and
observers are also automatically stopped when used as part of a
computation that is rerun, allowing new ones to be established. See
[`Meteor.subscribe`](#meteor_subscribe) for more information about
subscriptions and reactivity.
If the initial run of an autorun throws an exception, the computation
is automatically stopped and won't be rerun.
{{> autoApiBox "Tracker.flush" }}
Normally, when you make changes (like writing to the database),
their impact (like updating the DOM) is delayed until the system is
idle. This keeps things predictable &mdash; you can know that the DOM
won't go changing out from under your code as it runs. It's also one
of the things that makes Meteor fast.
`Tracker.flush` forces all of the pending reactive updates to complete.
For example, if an event handler changes a Session
variable that will cause part of the user interface to rerender, the
handler can call `flush` to perform the rerender immediately and then
access the resulting DOM.
An automatic flush occurs whenever the system is idle which performs
exactly the same work as `Tracker.flush`. The flushing process consists
of rerunning any invalidated computations. If additional
invalidations happen while flushing, they are processed as part of the
same flush until there is no more work to be done. Callbacks
registered with [`Tracker.afterFlush`](#tracker_afterflush) are called
after processing outstanding invalidations.
It is illegal to call `flush` from inside a `flush` or from a running
computation.
The <a href="http://manual.meteor.com/#tracker-theflushcycle">Meteor Manual</a>
describes the motivation for the flush cycle and the guarantees made by
`Tracker.flush` and `Tracker.afterFlush`.
{{> autoApiBox "Tracker.nonreactive" }}
Calls `func` with `Tracker.currentComputation` temporarily set to `null`
and returns `func`'s own return value. If `func` accesses reactive data
sources, these data sources will never cause a rerun of the enclosing
computation.
{{> autoApiBox "Tracker.active" }}
This value is useful for data source implementations to determine
whether they are being accessed reactively or not.
{{> autoApiBox "Tracker.currentComputation" }}
It's very rare to need to access `currentComputation` directly. The
current computation is used implicitly by
[`Tracker.active`](#tracker_active) (which tests whether there is one),
[`dependency.depend()`](#dependency_depend) (which registers that it depends on a
dependency), and [`Tracker.onInvalidate`](#tracker_oninvalidate) (which
registers a callback with it).
{{> autoApiBox "Tracker.onInvalidate" }}
See [*`computation`*`.onInvalidate`](#computation_oninvalidate) for more
details.
{{> autoApiBox "Tracker.afterFlush" }}
Functions scheduled by multiple calls to `afterFlush` are guaranteed
to run in the order that `afterFlush` was called. Functions are
guaranteed to be called at a time when there are no invalidated
computations that need rerunning. This means that if an `afterFlush`
function invalidates a computation, that computation will be rerun
before any other `afterFlush` functions are called.
<h2 id="tracker_computation"><span>Tracker.Computation</span></h2>
A Computation object represents code that is repeatedly rerun in
response to reactive data changes. Computations don't have return
values; they just perform actions, such as rerendering a template on
the screen. Computations are created using [`Tracker.autorun`](#tracker_autorun).
Use [`stop`](#computation_stop) to prevent further rerunning of a
computation.
Each time a computation runs, it may access various reactive data
sources that serve as inputs to the computation, which are called its
dependencies. At some future time, one of these dependencies may
trigger the computation to be rerun by invalidating it. When this
happens, the dependencies are cleared, and the computation is
scheduled to be rerun at flush time.
The *current computation*
([`Tracker.currentComputation`](#tracker_currentcomputation)) is the
computation that is currently being run or rerun (computed), and the
one that gains a dependency when a reactive data source is accessed.
Data sources are responsible for tracking these dependencies using
[`Tracker.Dependency`](#tracker_dependency) objects.
Invalidating a computation sets its `invalidated` property to true
and immediately calls all of the computation's `onInvalidate`
callbacks. When a flush occurs, if the computation has been invalidated
and not stopped, then the computation is rerun by setting the
`invalidated` property to `false` and calling the original function
that was passed to `Tracker.autorun`. A flush will occur when the current
code finishes running, or sooner if `Tracker.flush` is called.
Stopping a computation invalidates it (if it is valid) for the purpose
of calling callbacks, but ensures that it will never be rerun.
Example:
// if we're in a computation, then perform some clean-up
// when the current computation is invalidated (rerun or
// stopped)
if (Tracker.active) {
Tracker.onInvalidate(function () {
x.destroy();
y.finalize();
});
}
{{> autoApiBox "Tracker.Computation#stop"}}
Stopping a computation is irreversible and guarantees that it will
never be rerun. You can stop a computation at any time, including
from the computation's own run function. Stopping a computation that
is already stopped has no effect.
Stopping a computation causes its `onInvalidate` callbacks to run
immediately if it is not currently invalidated.
Nested computations are stopped automatically when their enclosing
computation is rerun.
{{> autoApiBox "Tracker.Computation#invalidate" }}
Invalidating a computation marks it to be rerun at
[flush time](#tracker_flush), at
which point the computation becomes valid again. It is rare to
invalidate a computation manually, because reactive data sources
invalidate their calling computations when they change. Reactive data
sources in turn perform this invalidation using one or more
[`Tracker.Dependency`](#tracker_dependency) objects.
Invalidating a computation immediately calls all `onInvalidate`
callbacks registered on it. Invalidating a computation that is
currently invalidated or is stopped has no effect. A computation can
invalidate itself, but if it continues to do so indefinitely, the
result will be an infinite loop.
{{> autoApiBox "Tracker.Computation#onInvalidate" }}
`onInvalidate` registers a one-time callback that either fires
immediately or as soon as the computation is next invalidated or
stopped. It is used by reactive data sources to clean up resources or
break dependencies when a computation is rerun or stopped.
To get a callback after a computation has been recomputed, you can
call [`Tracker.afterFlush`](#tracker_afterflush) from `onInvalidate`.
{{> autoApiBox "Tracker.Computation#stopped" }}
{{> autoApiBox "Tracker.Computation#invalidated" }}
This property is initially false. It is set to true by `stop()` and
`invalidate()`. It is reset to false when the computation is
recomputed at flush time.
{{> autoApiBox "Tracker.Computation#firstRun" }}
This property is a convenience to support the common pattern where a
computation has logic specific to the first run.
<h2 id="tracker_dependency"><span>Tracker.Dependency</span></h2>
A Dependency represents an atomic unit of reactive data that a
computation might depend on. Reactive data sources such as Session or
Minimongo internally create different Dependency objects for different
pieces of data, each of which may be depended on by multiple
computations. When the data changes, the computations are
invalidated.
Dependencies don't store data, they just track the set of computations to
invalidate if something changes. Typically, a data value will be
accompanied by a Dependency object that tracks the computations that depend
on it, as in this example:
var weather = "sunny";
var weatherDep = new Tracker.Dependency;
var getWeather = function () {
weatherDep.depend()
return weather;
};
var setWeather = function (w) {
weather = w;
// (could add logic here to only call changed()
// if the new value is different from the old)
weatherDep.changed();
};
This example implements a weather data source with a simple getter and
setter. The getter records that the current computation depends on
the `weatherDep` dependency using `depend()`, while the setter
signals the dependency to invalidate all dependent computations by
calling `changed()`.
The reason Dependencies do not store data themselves is that it can be
useful to associate multiple Dependencies with the same piece of data.
For example, one Dependency might represent the result of a database
query, while another might represent just the number of documents in
the result. A Dependency could represent whether the weather is sunny
or not, or whether the temperature is above freezing.
[`Session.equals`](#session_equals) is implemented this way for
efficiency. When you call `Session.equals("weather", "sunny")`, the
current computation is made to depend on an internal Dependency that
does not change if the weather goes from, say, "rainy" to "cloudy".
Conceptually, the only two things a Dependency can do are gain a
dependent and change.
A Dependency's dependent computations are always valid (they have
`invalidated === false`). If a dependent is invalidated at any time,
either by the Dependency itself or some other way, it is immediately
removed.
See the <a href="http://manual.meteor.com/#tracker-reactivevaluewithdependency">
Meteor Manual</a> to learn how to create a reactive data source using
Tracker.Dependency.
{{> autoApiBox "Tracker.Dependency#changed" }}
{{> autoApiBox "Tracker.Dependency#depend" }}
`dep.depend()` is used in reactive data source implementations to record
the fact that `dep` is being accessed from the current computation.
{{> autoApiBox "Tracker.Dependency#hasDependents" }}
For reactive data sources that create many internal Dependencies,
this function is useful to determine whether a particular Dependency is
still tracking any dependency relationships or if it can be cleaned up
to save memory.
{{/template}}

View File

@@ -149,7 +149,7 @@ can add multiple packages with one command
Optionally, adds version constraints. Running `meteor add package@1.1.0` will
add the package at version `1.1.0` or higher (but not `2.0.0` or higher). If you
want to use version `1.1.0` exactly, use `meteor add package@=1.1.0`. You can also
'or' constraints together: for example, '=1.0.0 || =2.0.1' means either 1.0.0 (exactly)
'or' constraints together: for example, `meteor add 'package@=1.0.0 || =2.0.1'` means either 1.0.0 (exactly)
or 2.0.1 (exactly).
To remove a version constraint for a specific package, run `meteor add` again

View File

@@ -89,7 +89,7 @@ server code but not served to the client, like private data files.
There are more assets to consider on the client side. Meteor
gathers all JavaScript files in your tree, with the exception of
the `server`, `public`, and `private` subdirectories, for the
the `server`, `tests`, `public`, and `private` subdirectories, for the
client. It minifies this bundle and serves it to each new client.
You're free to use a single JavaScript file for your entire application, or
create a nested tree of separate files, or anything in between.
@@ -100,7 +100,7 @@ executed without being wrapped in a new variable scope. This means
that each top-level `var` defines a global variable. In addition,
these files are executed before other client-side JavaScript files.
Files outside the `client`, `server` and `tests` subdirectories are loaded on
Files outside the `client`, `server`, `tests`, `public` and `private` subdirectories are loaded on
both the client and the server! That's the place for model definitions and
other functions. Meteor provides the variables [`isClient`](#meteor_isclient) and
[`isServer`](#meteor_isserver) so that your code can alter its behavior depending
@@ -427,7 +427,7 @@ Meteor templates when compiled.
{{#note}}
Today, the only templating system that ships with Meteor is Spacebars, though
our community has created packages for other languages such as
[Jade](https://atmospherejs.com/package/jade).
[Jade](https://atmospherejs.com/mquandalle/jade).
{{/note}}
To define templates, create a file in your project with the `.html`
@@ -793,9 +793,10 @@ things). However, we will still write the new versions.json file.
Meteor uses extended semver versioning for its packages: that means that the version
number has three parts separated by dots: major version, minor version and patch version
(for example: 1.2.3) with an optional pre-release version. You can read more about it here
(www.semver.org). Additionally, because some meteor packages wrap external libraries,
Meteor supports the convention of using _ to denote a wrap number.
(for example: 1.2.3) with an optional pre-release version. You can read more about it on
[semver.org](http://www.semver.org).
Additionally, because some meteor packages wrap external libraries,
Meteor supports the convention of using `_` to denote a wrap number.
You can read more about [`package.js`](#packagejs) files in the API
section.

File diff suppressed because one or more lines are too long

View File

@@ -42,8 +42,12 @@ pre {
code {
font-family: monospace;
font-size: 1.1em;
white-space: pre;
font-size: 1.2em;
}
pre code {
font-size: 1.1em;
}
ul {
@@ -392,23 +396,7 @@ h3.api-title {
}
#main pre {
-moz-border-radius: 10px;
-webkit-border-radius: 10px;
-o-border-radius: 10px;
-ms-border-radius: 10px;
-khtml-border-radius: 10px;
border-radius: 10px;
border-color: #777;
border-style: solid;
background-color: white;
border-width: 1px;
padding: 5px;
margin: 1em 0 1em 0;
}
#main pre, #main pre code { /* override inline code font and white-space*/
font-size: 12px;
font-weight: bold;
margin: 1em 0;
}
#main a.selflink:link, #main a.selflink:visited, #main a.selflink:hover {

View File

@@ -1,16 +1,12 @@
var release = Meteor.release ? "0.9.4" : "(checkout)";
Template.headline.helpers({
release: function () {
return Meteor.release ? "0.9.4-pre.11" : "(checkout)";
return release;
}
});
Meteor.startup(function () {
// XXX this is broken by the new multi-page layout. Also, it was
// broken before the multi-page layout because it had illegible
// colors. Just turn it off for now. We'll fix it and turn it on
// later.
// prettyPrint();
//mixpanel tracking
mixpanel.track('docs');
@@ -103,7 +99,7 @@ var hideMenu = function () {
};
var toc = [
{name: "Meteor " + Template.headline.release(), id: "top"}, [
{name: "Meteor " + release, id: "top"}, [
"Quick start",
"Seven principles",
"Resources"
@@ -402,7 +398,7 @@ var toc = [
{name: "mobile-config.js", id: "mobileconfigjs"}, [
{name: "App.info", id: "App-info"},
{name: "App.set", id: "App-set"},
{name: "App.setPreference", id: "App-setPreference"},
{name: "App.configurePlugin", id: "App-configurePlugin"},
{name: "App.icons", id: "App-icons"},
{name: "App.launchScreens", id: "App-launchScreens"}
@@ -411,17 +407,13 @@ var toc = [
"Packages", [ [
"accounts-ui",
"appcache",
"audit-argument-checks",
"browser-policy",
"coffeescript",
"fastclick",
"force-ssl",
"jquery",
"less",
"oauth-encryption",
"random",
"spiderable",
"stylus",
"showdown",
"underscore",

View File

@@ -43,29 +43,29 @@ platforms](https://github.com/meteor/meteor/wiki/Supported-Platforms).
Install Meteor:
<pre>
```
$ curl https://install.meteor.com | /bin/sh
</pre>
```
Create a project:
<pre>
```
$ meteor create myapp
</pre>
```
Run it locally:
<pre>
```
$ cd myapp
$ meteor
=&gt; Meteor server running on: http://localhost:3000/
</pre>
# Meteor server running on: http://localhost:3000/
```
Unleash it on the world (on a free server we provide):
<pre>
```
$ meteor deploy myapp.meteor.com
</pre>
```
<h2 id="sevenprinciples">Seven Principles of Meteor</h2>

View File

@@ -19,17 +19,13 @@ and removed with:
Meteor Development Group maintains the following packages:
{{> pkg_accounts_ui}}
{{> pkg_appcache}}
{{> pkg_audit_argument_checks}}
{{> pkg_browser_policy}}
{{> pkg_coffeescript}}
{{> pkg_fastclick}}
{{> pkg_force_ssl}}
{{> pkg_jquery}}
{{> pkg_less}}
{{> pkg_oauth_encryption}}
{{> pkg_random}}
{{> pkg_spiderable}}
{{> pkg_stylus}}
{{> pkg_showdown}}
{{> pkg_underscore}}

View File

@@ -4,3 +4,4 @@
notices-for-0.9.0
notices-for-0.9.1
0.9.4-platform-file

View File

@@ -0,0 +1,2 @@
server
browser

View File

@@ -1 +1 @@
METEOR@0.9.3
METEOR@0.9.4

View File

@@ -1,52 +1,52 @@
application-configuration@1.0.2
autopublish@1.0.0
autoupdate@1.1.1
base64@1.0.0
binary-heap@1.0.0
blaze-tools@1.0.0
blaze@2.0.1
boilerplate-generator@1.0.0
callback-hook@1.0.0
check@1.0.1
ctl-helper@1.0.3
ctl@1.0.1
ddp@1.0.9
deps@1.0.4
ejson@1.0.3
fastclick@1.0.0
follower-livedata@1.0.1
geojson-utils@1.0.0
html-tools@1.0.1
htmljs@1.0.1
http@1.0.6
id-map@1.0.0
insecure@1.0.0
jquery@1.0.0
json@1.0.0
livedata@1.0.10
logging@1.0.3
meteor-platform@1.1.1
meteor@1.1.1
minifiers@1.1.0
minimongo@1.0.3
mobile-status-bar@1.0.0
mongo@1.0.6
observe-sequence@1.0.2
ordered-dict@1.0.0
random@1.0.0
reactive-dict@1.0.3
reactive-var@1.0.2
reload@1.1.0
retry@1.0.0
routepolicy@1.0.1
session@1.0.2
spacebars-compiler@1.0.2
spacebars@1.0.2
standard-app-packages@1.0.2
templating@1.0.7
tracker@1.0.2
ui@1.0.3
underscore@1.0.0
url@1.0.0
webapp-hashing@1.0.0
webapp@1.1.2
application-configuration@1.0.3
autopublish@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
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

View File

@@ -4,3 +4,4 @@
notices-for-0.9.0
notices-for-0.9.1
0.9.4-platform-file

View File

@@ -0,0 +1,2 @@
server
browser

View File

@@ -1 +1 @@
METEOR@0.9.3
METEOR@0.9.4

View File

@@ -1,52 +1,52 @@
application-configuration@1.0.2
autopublish@1.0.0
autoupdate@1.1.1
base64@1.0.0
binary-heap@1.0.0
blaze-tools@1.0.0
blaze@2.0.1
boilerplate-generator@1.0.0
callback-hook@1.0.0
check@1.0.1
ctl-helper@1.0.3
ctl@1.0.1
ddp@1.0.9
deps@1.0.4
ejson@1.0.3
fastclick@1.0.0
follower-livedata@1.0.1
geojson-utils@1.0.0
html-tools@1.0.1
htmljs@1.0.1
http@1.0.6
id-map@1.0.0
insecure@1.0.0
jquery@1.0.0
json@1.0.0
livedata@1.0.10
logging@1.0.3
meteor-platform@1.1.1
meteor@1.1.1
minifiers@1.1.0
minimongo@1.0.3
mobile-status-bar@1.0.0
mongo@1.0.6
observe-sequence@1.0.2
ordered-dict@1.0.0
random@1.0.0
reactive-dict@1.0.3
reactive-var@1.0.2
reload@1.1.0
retry@1.0.0
routepolicy@1.0.1
session@1.0.2
spacebars-compiler@1.0.2
spacebars@1.0.2
standard-app-packages@1.0.2
templating@1.0.7
tracker@1.0.2
ui@1.0.3
underscore@1.0.0
url@1.0.0
webapp-hashing@1.0.0
webapp@1.1.2
application-configuration@1.0.3
autopublish@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
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

View File

@@ -4,3 +4,4 @@
notices-for-0.9.0
notices-for-0.9.1
0.9.4-platform-file

View File

@@ -0,0 +1,2 @@
server
browser

View File

@@ -1 +1 @@
METEOR@0.9.3
METEOR@0.9.4

View File

@@ -1,72 +1,72 @@
accounts-base@1.1.1
accounts-facebook@1.0.1
accounts-oauth@1.1.1
accounts-password@1.0.2
accounts-twitter@1.0.1
accounts-ui-unstyled@1.1.2
accounts-ui@1.1.1
application-configuration@1.0.2
audit-argument-checks@1.0.0
autoupdate@1.1.1
base64@1.0.0
binary-heap@1.0.0
blaze-tools@1.0.0
blaze@2.0.1
boilerplate-generator@1.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.0
check@1.0.1
ctl-helper@1.0.3
ctl@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.9
deps@1.0.4
ejson@1.0.3
email@1.0.3
facebook@1.1.0
fastclick@1.0.0
follower-livedata@1.0.1
geojson-utils@1.0.0
html-tools@1.0.1
htmljs@1.0.1
http@1.0.6
id-map@1.0.0
jquery@1.0.0
json@1.0.0
less@1.0.9
livedata@1.0.10
localstorage@1.0.0
logging@1.0.3
meteor-platform@1.1.1
meteor@1.1.1
minifiers@1.1.0
minimongo@1.0.3
mobile-status-bar@1.0.0
mongo@1.0.6
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.0
oauth2@1.1.0
oauth@1.1.0
observe-sequence@1.0.2
ordered-dict@1.0.0
random@1.0.0
reactive-dict@1.0.3
reactive-var@1.0.2
reload@1.1.0
retry@1.0.0
routepolicy@1.0.1
service-configuration@1.0.1
session@1.0.2
sha@1.0.0
spacebars-compiler@1.0.2
spacebars@1.0.2
srp@1.0.0
standard-app-packages@1.0.2
templating@1.0.7
tracker@1.0.2
twitter@1.1.0
ui@1.0.3
underscore@1.0.0
url@1.0.0
webapp-hashing@1.0.0
webapp@1.1.2
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

View File

@@ -5,7 +5,7 @@ Meteor.subscribe("parties");
// If no party selected, or if the selected party was deleted, select one.
Meteor.startup(function () {
Deps.autorun(function () {
Tracker.autorun(function () {
var selected = Session.get("selected");
if (! selected || ! Parties.findOne(selected)) {
var party = Parties.findOne();
@@ -130,7 +130,7 @@ Template.map.rendered = function () {
self.node = self.find("svg");
if (! self.handle) {
self.handle = Deps.autorun(function () {
self.handle = Tracker.autorun(function () {
var selected = Session.get('selected');
var selectedParty = selected && Parties.findOne(selected);
var radius = function (party) {

View File

@@ -8,6 +8,3 @@ insecure
less
iron:router
accounts-password
fastclick
noorderstorm:hammer

View File

@@ -1 +1 @@
METEOR@0.9.4-pre.8
METEOR@0.9.4

View File

@@ -1,65 +1,65 @@
accounts-base@1.1.2-pre.2
accounts-password@1.0.3-pre.2
application-configuration@1.0.3-pre.2
autoupdate@1.1.2-pre.3
base64@1.0.1-pre.2
binary-heap@1.0.1-pre.2
blaze-tools@1.0.1-pre.2
blaze@2.0.2-pre.2
boilerplate-generator@1.0.1-pre.3
callback-hook@1.0.1-pre.2
check@1.0.2-pre.2
ctl-helper@1.0.4-pre.2
ctl@1.0.2-pre.2
ddp@1.0.10-pre.2
deps@1.0.5-pre.2
ejson@1.0.4-pre.2
email@1.0.4-pre.2
fastclick@1.0.1-pre.3
follower-livedata@1.0.2-pre.2
geojson-utils@1.0.1-pre.2
html-tools@1.0.2-pre.2
htmljs@1.0.2-pre.3
http@1.0.7-pre.2
id-map@1.0.1-pre.2
insecure@1.0.1-pre.2
accounts-base@1.1.2
accounts-password@1.0.3
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
email@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
iron:core@0.3.4
iron:dynamic-template@0.4.1
iron:layout@0.4.1
iron:router@0.9.4
jquery@1.0.1-pre.2
json@1.0.1-pre.2
less@1.0.10-pre.3
livedata@1.0.11-pre.2
localstorage@1.0.1-pre.2
logging@1.0.4-pre.2
meteor-platform@1.1.2-pre.3
meteor@1.1.2-pre.3
minifiers@1.1.1-pre.2
minimongo@1.0.4-pre.3
mobile-status-bar@1.0.1-pre.2
mongo@1.0.7-pre.2
noorderstorm:hammer@0.1.3
jquery@1.0.1
json@1.0.1
launch-screen@1.0.0
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
observe-sequence@1.0.3-pre.2
ordered-dict@1.0.1-pre.2
random@1.0.1-pre.2
reactive-dict@1.0.4-pre.2
reactive-var@1.0.3-pre.2
reload@1.1.1-pre.2
retry@1.0.1-pre.2
routepolicy@1.0.2-pre.2
service-configuration@1.0.2-pre.2
session@1.0.3-pre.2
sha@1.0.1-pre.2
spacebars-compiler@1.0.3-pre.2
spacebars@1.0.3-pre.2
srp@1.0.1-pre.1
standard-app-packages@1.0.3-pre.2
templating@1.0.8-pre.3
tracker@1.0.3-pre.2
ui@1.0.4-pre.2
underscore@1.0.1-pre.2
url@1.0.1-pre.2
webapp-hashing@1.0.1-pre.2
webapp@1.1.3-pre.3
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
ui@1.0.4
underscore@1.0.1
url@1.0.1
webapp-hashing@1.0.1
webapp@1.1.3

View File

@@ -0,0 +1,82 @@
/**
* jQuery Plugin to obtain touch gestures from iPhone, iPod Touch and iPad, should also work with Android mobile phones (not tested yet!)
* Common usage: wipe images (left and right to show the previous or next image)
*
* @author Andreas Waltl, netCU Internetagentur (http://www.netcu.de)
* @version 1.1.1 (9th December 2010) - fix bug (older IE's had problems)
* @version 1.1 (1st September 2010) - support wipe up and wipe down
* @version 1.0 (15th July 2010)
*/
(function($) {
$.fn.touchwipe = function(settings) {
var config = {
min_move_x: 20,
min_move_y: 20,
wipeLeft: function() { },
wipeRight: function() { },
wipeUp: function() { },
wipeDown: function() { },
preventDefaultEvents: true
};
if (settings) $.extend(config, settings);
this.each(function() {
var startX;
var startY;
var isMoving = false;
function cancelTouch() {
this.removeEventListener('touchmove', onTouchMove);
startX = null;
isMoving = false;
}
function onTouchMove(e) {
if(config.preventDefaultEvents) {
e.preventDefault();
}
if(isMoving) {
var x = e.touches[0].pageX;
var y = e.touches[0].pageY;
var dx = startX - x;
var dy = startY - y;
if(Math.abs(dx) >= config.min_move_x) {
cancelTouch();
if(dx > 0) {
config.wipeLeft();
}
else {
config.wipeRight();
}
}
else if(Math.abs(dy) >= config.min_move_y) {
cancelTouch();
if(dy > 0) {
config.wipeDown();
}
else {
config.wipeUp();
}
}
}
}
function onTouchStart(e)
{
if (e.touches.length == 1) {
startX = e.touches[0].pageX;
startY = e.touches[0].pageY;
isMoving = true;
this.addEventListener('touchmove', onTouchMove, false);
}
}
if ('ontouchstart' in document.documentElement) {
this.addEventListener('touchstart', onTouchStart, false);
}
});
return this;
};
})(jQuery);

View File

@@ -1,10 +1,10 @@
@font-face {
font-family: 'todos';
src:url('icon/todos.eot?o4fu7p');
src:url('icon/todos.eot?#iefixo4fu7p') format('embedded-opentype'),
url('icon/todos.woff?o4fu7p') format('woff'),
url('icon/todos.ttf?o4fu7p') format('truetype'),
url('icon/todos.svg?o4fu7p#todos') format('svg');
src:url('icon/todos.eot?-5w3um4');
src:url('icon/todos.eot?#iefix5w3um4') format('embedded-opentype'),
url('icon/todos.woff?5w3um4') format('woff'),
url('icon/todos.ttf?5w3um4') format('truetype'),
url('icon/todos.svg?5w3um4#todos') format('svg');
font-weight: normal;
font-style: normal;
}
@@ -23,9 +23,13 @@
-moz-osx-font-smoothing: grayscale;
}
.icon-unlock:before {
content: "\e600";
}
.icon-user-add:before {
content: "\e604";
}
.icon-cog:before {
content: "\e606";
}
@@ -44,6 +48,9 @@
.icon-close:before {
content: "\e60c";
}
.icon-cross:before {
content: "\e60d";
}
.icon-sync:before {
content: "\e60e";
}
@@ -53,6 +60,9 @@
.icon-check:before {
content: "\e612";
}
.icon-share:before {
content: "\e617";
}
.icon-email:before {
content: "\e619";
}

View File

@@ -37,6 +37,10 @@ body {
background: @color-tertiary;
opacity: 1;
@media screen and (min-width: 40em) {
left: @menu-width;
}
.content-scrollable {
.position(absolute, 0, 0, 0, 0);
.transform(translate3d(0, 0, 0));
@@ -48,13 +52,14 @@ body {
.menu-open & {
.transform(translate3d(@menu-width, 0, 0));
opacity: .85;
}
left: 0;
// Show menu on desktop, negate .menu-open
@media screen and (min-width: 40em) {
.transform(translate3d(0, 0, 0)); //reset transform and use position properties instead
left: @menu-width;
opacity: 1;
@media screen and (min-width: 40em) {
// Show menu on desktop, negate .menu-open
.transform(translate3d(0, 0, 0)); //reset transform and use position properties instead
opacity: 1;
left: @menu-width;
}
}
}

View File

@@ -85,6 +85,11 @@
color: @color-empty;
.count-list { background: @color-primary; }
}
.cordova &:hover {
// Prevent hover states from being noticeable on Cordova apps
color: rgba(255,255,255,.4);
}
}
}
}

View File

@@ -1,29 +1,29 @@
@import 'util/reset.lessimport';
@import 'util/reset.import.less';
// Mixins & utilities
@import 'util/helpers.lessimport';
@import 'util/lesshat.lessimport';
@import 'util/text.lessimport';
@import 'util/typography.lessimport';
@import 'util/variables.lessimport';
@import 'util/helpers.import.less';
@import 'util/lesshat.import.less';
@import 'util/text.import.less';
@import 'util/typography.import.less';
@import 'util/variables.import.less';
// Global namespace
@import 'globals/base.lessimport';
@import 'globals/button.lessimport';
@import 'globals/form.lessimport';
@import 'globals/icon.lessimport';
@import 'globals/layout.lessimport';
@import 'globals/link.lessimport';
@import 'globals/menu.lessimport';
@import 'globals/nav.lessimport';
@import 'globals/base.import.less';
@import 'globals/button.import.less';
@import 'globals/form.import.less';
@import 'globals/icon.import.less';
@import 'globals/layout.import.less';
@import 'globals/link.import.less';
@import 'globals/menu.import.less';
@import 'globals/nav.import.less';
// Global templates
@import 'globals/list-items.lessimport';
@import 'globals/message.lessimport';
@import 'globals/notification.lessimport';
@import 'globals/list-items.import.less';
@import 'globals/message.import.less';
@import 'globals/notification.import.less';
// Templates
@import '../templates/lists-show.lessimport';
@import '../templates/auth.lessimport';
@import '../templates/app-not-found.lessimport';
@import '../templates/loading.lessimport';
@import '../templates/lists-show.import.less';
@import '../templates/auth.import.less';
@import '../templates/app-not-found.import.less';
@import '../templates/loading.import.less';

View File

@@ -10,7 +10,7 @@
{{else}}
<span class="icon-arrow-down"></span>
{{/if}}
{{email}}
{{emailLocalPart}}
</a>
{{#if userMenuOpen}}
<a class="js-logout btn-secondary">Logout</a>
@@ -31,10 +31,10 @@
{{#if userId}}
<span class="icon-lock"></span>
{{/if}}
{{name}}
{{#if incompleteCount}}
<span class="count-list">{{incompleteCount}}</span>
{{/if}}
{{name}}
</a>
{{/each}}

View File

@@ -4,21 +4,33 @@ Session.setDefault(MENU_KEY, false);
var USER_MENU_KEY = 'userMenuOpen';
Session.setDefault(USER_MENU_KEY, false);
var SHOW_CONNECTION_ISSUE_KEY = 'showConnectionIssue';
Session.setDefault(SHOW_CONNECTION_ISSUE_KEY, false);
var CONNECTION_ISSUE_TIMEOUT = 1000;
Meteor.startup(function () {
// set up a swipe left / right handler
$(document.body).touchwipe({
wipeLeft: function () {
Session.set(MENU_KEY, false);
},
wipeRight: function () {
Session.set(MENU_KEY, true);
},
preventDefaultEvents: false
});
// Don't show the connection error box unless we haven't connected within
// 1 second of app starting
setTimeout(function () {
Session.set(SHOW_CONNECTION_ISSUE_KEY, true);
}, CONNECTION_ISSUE_TIMEOUT);
});
Template.appBody.rendered = function() {
if (Meteor.isCordova) {
// set up a swipe left / right handler
this.hammer = new Hammer(this.find('#container'));
this.hammer.on('swipeleft swiperight', function(event) {
if (event.gesture.direction === 'right') {
Session.set(MENU_KEY, true);
} else if (event.gesture.direction === 'left') {
Session.set(MENU_KEY, false);
}
});
}
this.find('#content-container')._uihooks = {
insertElement: function(node, next) {;
insertElement: function(node, next) {
$(node)
.hide()
.insertBefore(next)
@@ -32,12 +44,6 @@ Template.appBody.rendered = function() {
};
};
Template.appBody.destroyed = function() {
if (Meteor.isCordova) {
this.hammer.destroy();
}
};
Template.appBody.helpers({
// We use #each on an array of one item so that the "list" template is
// removed and a new copy is added when changing lists, which is
@@ -52,8 +58,9 @@ Template.appBody.helpers({
cordova: function() {
return Meteor.isCordova && 'cordova';
},
email: function() {
return Meteor.user().emails[0].address;
emailLocalPart: function() {
var email = Meteor.user().emails[0].address;
return email.substring(0, email.indexOf('@'));
},
userMenuOpen: function() {
return Session.get(USER_MENU_KEY);
@@ -68,7 +75,11 @@ Template.appBody.helpers({
}
},
connected: function() {
return Meteor.status().connected;
if (Session.get(SHOW_CONNECTION_ISSUE_KEY)) {
return Meteor.status().connected;
} else {
return true;
}
}
});

View File

@@ -34,10 +34,9 @@ Template.listsShow.helpers({
var editList = function(list, template) {
Session.set(EDITING_KEY, true);
// wait for the template to redraw based on the reactive change
Tracker.afterFlush(function() {
template.$('.js-edit-form input[type=text]').focus();
});
// force the template to redraw based on the reactive change
Tracker.flush();
template.$('.js-edit-form input[type=text]').focus();
};
var saveList = function(list, template) {
@@ -117,15 +116,13 @@ Template.listsShow.events({
'change .list-edit': function(event, template) {
if ($(event.target).val() === 'edit') {
editList(this, template);
} else if ($(event.target).val() === 'delete') {
if (! deleteList(this, template)) {
// reset the select
event.target.selectedIndex = 0;
}
deleteList(this, template);
} else {
toggleListPrivacy(this, template);
}
event.target.selectedIndex = 0;
},
'click .js-edit-list': function(event, template) {

View File

@@ -18,22 +18,32 @@ Router.configure({
}
});
if (Meteor.isClient) {
var launchScreenHandle = LaunchScreen.hold();
}
Router.map(function() {
this.route('join');
this.route('signin');
this.route('listsShow', {
path: '/lists/:_id',
// subscribe to todos before the page is rendered but don't wait on the
// subscribe to todos before the page is rendered but don't wait on the
// subscription, we'll just render the items as they arrive
onBeforeAction: function() {
onBeforeAction: function () {
this.todosHandle = Meteor.subscribe('todos', this.params._id);
},
data: function() {
data: function () {
return Lists.findOne(this.params._id);
},
action: function () {
this.render();
if (Meteor.isClient) {
launchScreenHandle.release();
}
}
});
this.route('home', {
path: '/',
action: function() {
@@ -45,4 +55,4 @@ Router.map(function() {
if (Meteor.isClient) {
Router.onBeforeAction('loading', {except: ['join', 'signin']});
Router.onBeforeAction('dataNotFound', {except: ['join', 'signin']});
}
}

View File

@@ -7,13 +7,21 @@ App.info({
});
App.icons({
// iOS
'iphone': 'resources/icons/icon-60.png',
'iphone-2x': 'resources/icons/icon-60@2x.png',
'iphone_2x': 'resources/icons/icon-60@2x.png',
'ipad': 'resources/icons/icon-72.png',
'ipad-2x': 'resources/icons/icon-72@2x.png'
'ipad_2x': 'resources/icons/icon-72@2x.png',
// Android - XXX these are the same as iOS for now
'android_ldpi': 'resources/icons/icon-60.png',
'android_mdpi': 'resources/icons/icon-60.png',
'android_hdpi': 'resources/icons/icon-72.png',
'android_xhdpi': 'resources/icons/icon-72@2x.png'
});
App.launchScreens({
// iOS
'iphone': 'resources/splash/Default~iphone.png',
'iphone_2x': 'resources/splash/Default@2x~iphone.png',
'iphone5': 'resources/splash/Default-568h@2x~iphone.png',

Binary file not shown.

View File

@@ -8,15 +8,18 @@
<missing-glyph horiz-adv-x="512" />
<glyph unicode="&#x20;" d="" horiz-adv-x="256" />
<glyph unicode="&#xe600;" d="M448.016 22.235v233.749c0 0.994-0.097 1.963-0.236 2.917-0.055 0.372-0.095 0.749-0.17 1.113-0.761 3.72-2.568 7.050-5.082 9.704-0.126 0.133-0.24 0.277-0.37 0.407-0.248 0.247-0.517 0.472-0.776 0.706-0.215 0.193-0.42 0.4-0.644 0.585-0.145 0.12-0.304 0.223-0.452 0.338-1.253 0.977-2.622 1.808-4.088 2.467-0.372 0.167-0.752 0.316-1.135 0.461-2.194 0.828-4.562 1.302-7.046 1.302h-276.092l0.001 27.925c0 57.388 46.688 104.075 104.075 104.075 43.019 0 80.017-26.237 95.849-63.548 0.006-0.014 0.015-0.022 0.021-0.036 2.989-7.283 10.142-12.416 18.502-12.416 11.046 0 20 8.953 20 20 0 2.355-0.429 4.605-1.176 6.705-0.091 0.347-0.21 0.719-0.391 1.146-21.833 51.791-73.070 88.148-132.806 88.148-79.564 0-144.065-64.497-144.075-144.060 0 0 0-0.001 0-0.003v-27.938h-27.425c-10.355 0-18.873-7.871-19.896-17.955-0.068-0.672-0.104-1.354-0.104-2.045v-231.666c-0.309-1.397-0.484-2.844-0.484-4.334 0-11.047 8.955-20 20-20h344.135c11.046 0 20 8.955 20 20-0.001 0.764-0.052 1.514-0.135 2.253zM104.5 235.984h303.516v-196h-303.516v196zM288.016 158.99c0 17.673-14.327 32-32 32s-32-14.327-32-32c0-10.134 4.716-19.161 12.067-25.024v-28.988c-0.002-11.046 8.953-20 20-20 11.045 0.002 20 8.956 19.998 20l0.001 29.093c7.275 5.866 11.934 14.847 11.934 24.919z" />
<glyph unicode="&#xe604;" d="M195.538 36.414c46.458 0 91.505 9.090 133.89 27.016 7.282 3.080 14.427 6.408 21.44 9.959v7.527c0 46.996-97.17 89.854-123.19 93.994-8.079 1.285-8.264 23.498-8.264 23.498s23.738 23.497 28.911 55.097c13.916 0 22.513 33.593 8.594 45.412 0.582 12.441 17.889 97.67-69.732 97.67s-70.313-85.229-69.734-97.67c-13.917-11.819-5.323-45.412 8.595-45.412 5.171-31.6 28.909-55.097 28.909-55.097s-0.184-22.213-8.263-23.498c-25.743-4.096-121.126-46.097-123.14-92.506 12.25-7.078 24.956-13.416 38.098-18.975 42.383-17.925 87.429-27.015 133.886-27.015zM455.032 312.5h-40.004v40.005c0.001 11.045-8.953 19.996-19.998 19.996-11.044 0-19.999-8.953-19.999-19.998v-40.003h-40.001c-11.047 0-20-8.954-19.999-20-0.001-11.045 8.952-19.998 19.998-19.998h40.002v-40.001c0-11.047 8.954-20 20-20s19.998 8.953 19.998 19.998v40.003h40.005c11.045-0.001 19.997 8.953 19.997 19.998s-8.954 20-19.999 20z" />
<glyph unicode="&#xe606;" d="M198.229 404.257l13.884-13.884c11.64-11.645 27.131-18.058 43.621-18.058 0.159 0 0.318 0 0.479 0.002 0.146-0.001 0.29-0.002 0.436-0.002 16.495 0 31.988 6.415 43.61 18.049l13.805 13.802c9.792-3.147 19.291-7.096 28.446-11.821v-19.988c0-16.439 6.703-32.402 18.421-43.944 11.536-11.692 27.483-18.381 43.902-18.381l19.721-0.001c4.584-8.953 8.426-18.231 11.507-27.787l-13.838-13.838c-11.611-11.612-18.159-27.621-18.047-44.048-0.124-16.442 6.422-32.467 18.047-44.095l13.964-13.964c-3.149-9.852-7.106-19.406-11.852-28.615l-19.492-0.001c-0.004 0-0.006 0-0.011 0-16.42 0-32.37-6.69-43.907-18.388-11.715-11.54-18.415-27.5-18.415-43.938v-19.558c-9.19-4.743-18.728-8.703-28.561-11.857l-13.674 13.673c-11.639 11.65-27.132 18.065-43.627 18.065-0.146 0-0.29-0.001-0.436-0.002-0.159 0.002-0.318 0.002-0.479 0.002-16.489 0-31.981-6.413-43.614-18.051l-13.776-13.777c-9.937 3.169-19.572 7.16-28.854 11.951l-0.001 19.065c0.006 16.427-6.684 32.382-18.386 43.923-11.541 11.715-27.502 18.417-43.939 18.417h-19.253c-4.888 9.423-8.949 19.21-12.164 29.312l13.745 13.745c11.623 11.624 18.169 27.648 18.044 44.091 0.112 16.427-6.436 32.436-18.046 44.047l-13.615 13.616c3.145 9.806 7.091 19.314 11.818 28.483h19.471c16.439 0 32.401 6.701 43.942 18.419 11.699 11.543 18.389 27.497 18.383 43.905l0.001 19.514c9.246 4.773 18.844 8.753 28.74 11.917zM210.743 449.896c-29.966-5.973-57.829-17.758-82.376-34.134l-0.002-42.933c0.002-5.838-2.443-11.097-6.353-14.84-3.743-3.915-9.006-6.364-14.851-6.364h-42.903c-16.312-24.473-28.066-52.242-34.044-82.102l30.195-30.197c4.128-4.128 6.117-9.575 5.999-14.984 0.123-5.415-1.867-10.869-5.999-15.002l-30.281-30.28c5.985-30.201 17.87-58.276 34.413-82.974h42.62c5.845 0 11.107-2.45 14.851-6.365 3.909-3.741 6.354-8.999 6.353-14.838l0.002-42.502c24.581-16.398 52.488-28.193 82.503-34.158l30.327 30.328c4.014 4.016 9.275 6.007 14.536 6.007 0.156 0 0.31-0.002 0.465-0.005 0.149 0.003 0.301 0.005 0.45 0.005 5.263 0 10.523-1.991 14.535-6.007l30.271-30.267c29.89 5.984 57.688 17.756 82.178 34.095v42.978c0 5.845 2.45 11.107 6.365 14.851 3.743 3.909 9.001 6.354 14.839 6.353l42.938 0.002c16.334 24.516 28.091 52.337 34.052 82.252l-30.525 30.526c-4.133 4.133-6.122 9.587-6 15.002-0.118 5.409 1.871 10.856 6 14.984l30.441 30.444c-5.955 29.572-17.579 57.089-33.685 81.379l-43.222 0.002c-0.002 0-0.004 0-0.004 0-5.837 0-11.094 2.445-14.835 6.354-3.915 3.743-6.365 9.006-6.365 14.851v43.408c-24.456 16.316-52.209 28.079-82.053 34.070l-30.396-30.392c-4.012-4.016-9.273-6.007-14.535-6.007-0.15 0-0.301 0.002-0.45 0.006-0.154-0.004-0.309-0.006-0.465-0.006-5.261 0-10.522 1.991-14.536 6.007l-30.453 30.453zM255.928 322.764c-54.507 0-98.693-44.186-98.693-98.692s44.187-98.691 98.693-98.691v-0.067c0.373 0 0.746-0.006 1.12-0.002 7.403 0.080 14.607 0.971 21.528 2.591 0.511 0.119 0.966 0.248 1.397 0.381 8.664 2.494 14.97 10.506 14.869 19.958-0.121 11.355-9.423 20.463-20.778 20.343-1.498-0.016-2.951-0.201-4.354-0.521-0.045-0.008-0.089 0-0.133-0.011-4.090-0.995-8.354-1.548-12.736-1.612-0.293 0.037-0.595 0.062-0.913 0.062-31.743 0-57.571 25.825-57.571 57.569s25.828 57.571 57.571 57.571c31.744 0 57.57-25.826 57.57-57.571 0-4.462-0.514-8.808-1.482-12.979-0.010-0.045-0.004-0.089-0.010-0.134-0.305-1.406-0.474-2.862-0.474-4.36 0-11.355 9.204-20.561 20.561-20.561 9.453 0 17.396 6.389 19.798 15.079 0.131 0.433 0.254 0.89 0.368 1.402 1.546 6.938 2.361 14.15 2.361 21.553 0 54.506-44.186 98.692-98.692 98.692z" />
<glyph unicode="&#xe607;" d="M256 111.438c-11.047-0.001-20.002 8.953-20 20v120c-0.002 11.046 8.953 20.001 20 20.001v0c11.045-0.001 20-8.954 20-20.001l-0.002-120.001c0.002-11.044-8.953-19.999-19.998-19.999v0zM459.75 367.396h-107.75v60.040c0 10.172-7.596 18.548-17.418 19.816-0.848 0.108-1.705 0.186-2.582 0.186h-152.002c-0.877 0-1.734-0.076-2.582-0.186-9.822-1.27-17.418-9.646-17.418-19.814v-60.042h-107.998c-11.045 0-20-8.956-20-20.001s8.955-20 20-20h13.608l26.958-307.957h0.107c0-11.046 8.955-20 20-20h286.686c11.047 0 20 8.955 20 20h0.074l26.958 307.957h13.358c11.045 0 20 8.955 20 20s-8.954 20.001-19.999 20.001zM199.998 407.437h112.002v-40.041h-112.002v40.041zM381.031 39.438h-250.062l-25.207 287.956h300.477l-25.208-287.956zM336.002 111.438c11.045 0.001 20 8.955 19.998 19.999l0.002 120.001c0 11.047-8.955 20-20 20.001-11.047 0-20.002-8.955-20-20.001v-120c-0.002-11.047 8.953-20.001 20-20zM175.998 111.438c11.045 0.001 20 8.955 19.998 19.999l0.002 120.001c0 11.047-8.955 20-20 20.001-11.047 0-20.002-8.955-20-20.001v-120c-0.002-11.047 8.953-20.001 20-20zM256 111.438c11.045 0.001 20 8.955 19.998 19.999l0.002 120.001c0 11.047-8.955 20-20 20.001-11.047 0-20.002-8.955-20-20.001v-120c-0.002-11.047 8.953-20.001 20-20z" />
<glyph unicode="&#xe607;" d="M459.75 367.396h-107.75v60.040c0 10.172-7.596 18.548-17.418 19.816-0.848 0.108-1.705 0.186-2.582 0.186h-152.002c-0.877 0-1.734-0.076-2.582-0.186-9.822-1.27-17.418-9.646-17.418-19.814v-60.042h-107.998c-11.045 0-20-8.956-20-20.001s8.955-20 20-20h13.608l26.958-307.957h0.107c0-11.046 8.955-20 20-20h286.686c11.047 0 20 8.955 20 20h0.074l26.958 307.957h13.358c11.045 0 20 8.955 20 20s-8.954 20.001-19.999 20.001zM199.998 407.437h112.002v-40.041h-112.002v40.041zM381.031 39.438h-250.062l-25.207 287.956h300.477l-25.208-287.956zM336.002 111.438c11.045 0.001 20 8.955 19.998 19.999l0.002 120.001c0 11.047-8.955 20-20 20.001-11.047 0-20.002-8.955-20-20.001v-120c-0.002-11.047 8.953-20.001 20-20zM175.998 111.438c11.045 0.001 20 8.955 19.998 19.999l0.002 120.001c0 11.047-8.955 20-20 20.001-11.047 0-20.002-8.955-20-20.001v-120c-0.002-11.047 8.953-20.001 20-20zM256 111.438c11.045 0.001 20 8.955 19.998 19.999l0.002 120.001c0 11.047-8.955 20-20 20.001-11.047 0-20.002-8.955-20-20.001v-120c-0.002-11.047 8.953-20.001 20-20z" />
<glyph unicode="&#xe608;" d="M474.28 348.312c6.352 6.354 7.536 15.918 3.555 23.468-0.92 1.762-2.116 3.416-3.596 4.896-0.143 0.143-0.297 0.264-0.443 0.4l-64.659 64.317c-0.024 0.024-0.044 0.053-0.069 0.077-3.906 3.906-9.025 5.858-14.146 5.857-5.121 0.002-10.242-1.951-14.148-5.857-0.032-0.033-0.060-0.068-0.092-0.102l-318.084-318.084c-2.934-2.567-5.105-5.979-6.149-9.874l-23.803-88.832c-1.85-6.902 0.124-14.267 5.176-19.319 3.801-3.8 8.908-5.857 14.144-5.857 1.726 0 3.465 0.223 5.176 0.682l88.832 23.803c3.648 0.978 6.866 2.952 9.37 5.608l260.743 260.742c0.563 0.452 1.111 0.935 1.634 1.457 0.521 0.522 1.003 1.069 1.455 1.632l53.875 53.874c0.418 0.355 0.834 0.716 1.229 1.112zM403.45 334.17l-36.796 36.601 28.305 28.305 36.797-36.6-28.306-28.306zM140.783 71.502l-36.7 36.7 234.286 234.284 36.797-36.601-234.383-234.383zM80.248 47.686l5.871 21.911 16.040-16.040-21.911-5.871z" />
<glyph unicode="&#xe60a;" d="M256 408c49.148 0 95.354-19.14 130.107-53.892 34.753-34.754 53.893-80.961 53.893-130.108 0-49.148-19.14-95.354-53.893-130.107s-80.959-53.893-130.107-53.893c-49.147 0-95.354 19.14-130.108 53.893-34.752 34.754-53.892 80.959-53.892 130.107s19.14 95.354 53.892 130.108c34.754 34.752 80.961 53.892 130.108 53.892zM256 448c-123.712 0-224-100.288-224-224s100.288-224 224-224 224 100.288 224 224-100.288 224-224 224v0zM364.501 243.5h-88.004v88.004c0.001 11.045-8.953 19.997-19.998 19.997-11.044 0-19.999-8.954-19.999-19.999v-88.002h-88c-11.047 0-20-8.954-19.999-20-0.001-11.045 8.952-19.998 19.998-19.998h88.001v-88.002c0-11.047 8.954-20 20-19.999 11.045-0.001 19.998 8.952 19.998 19.998v88.003h88.006c11.045-0.001 19.997 8.953 19.997 19.998s-8.955 20-20 20z" />
<glyph unicode="&#xe60b;" d="M460.501 243.5h-184.004v184.002c0.001 11.045-8.953 19.998-19.998 19.998-11.044 0-19.999-8.955-19.999-20v-184h-184.001c-11.047 0-20-8.954-19.999-20-0.001-11.045 8.952-19.998 19.998-19.998l184.002 0.001v-184.005c0-11.047 8.954-20 20-19.998 11.045-0.002 19.998 8.951 19.998 19.998v184.005h184.005c11.045-0.001 19.997 8.953 19.997 19.998 0 11.044-8.954 19.999-19.999 19.999z" />
<glyph unicode="&#xe60c;" d="M346.511 161.067c7.81-7.811 7.809-20.475 0-28.283-7.811-7.811-20.473-7.812-28.281-0.001l-62.23 62.229-62.228-62.229c-7.811-7.811-20.472-7.811-28.281 0-7.812 7.811-7.812 20.473-0.001 28.284l62.227 62.228-62.226 62.225c-7.811 7.811-7.811 20.473 0 28.282 7.81 7.812 20.472 7.812 28.284 0.001l62.225-62.225 62.227 62.226c7.811 7.81 20.475 7.81 28.283 0 7.811-7.81 7.812-20.472 0.001-28.28l-62.228-62.229 62.228-62.228zM414.392 65.608c-87.478-87.478-229.307-87.478-316.783 0-87.479 87.478-87.479 229.307 0 316.783 87.477 87.479 229.306 87.479 316.783 0 87.477-87.476 87.477-229.306 0-316.783zM440 224.001c-0.001 49.148-19.14 95.354-53.893 130.106s-80.96 53.893-130.107 53.894c-49.148-0.001-95.355-19.141-130.108-53.893-34.753-34.754-53.891-80.959-53.893-130.107 0.001-49.148 19.141-95.355 53.893-130.108 34.754-34.753 80.959-53.892 130.108-53.892 49.148 0 95.354 19.139 130.107 53.892s53.892 80.96 53.893 130.108z" />
<glyph unicode="&#xe60d;" d="M506.643 2.142l-221.86 221.858 221.859 221.859c7.811 7.811 7.81 20.474-0.002 28.283-7.809 7.811-20.471 7.811-28.281 0l-221.858-221.859-221.86 221.859c-7.81 7.812-20.473 7.811-28.283 0s-7.811-20.472 0-28.283l221.861-221.859-221.861-221.862c-7.811-7.809-7.81-20.471 0-28.28s20.474-7.811 28.283 0l221.86 221.859 221.861-221.859c7.809-7.812 20.471-7.811 28.28 0 7.81 7.81 7.811 20.473 0.001 28.284z" />
<glyph unicode="&#xe60e;" d="M499.391 271.027c-7.811 7.811-20.475 7.811-28.283 0l-10.712-10.712c-18.178 96.329-102.752 169.196-204.364 169.196-80.473 0-150.266-45.701-184.855-112.564-2.398-4.639-2.137-4.182-2.502-4.998-0.018-0.039-0.035-0.078-0.053-0.117-0.008-0.016-0.012-0.029-0.020-0.045l0.002-0.002c-0.992-2.373-1.543-4.975-1.543-7.707 0-11.047 8.955-20 20-20 7.656 0 14.299 4.305 17.66 10.621l0.004-0.002c8.094 16.695 19.006 32.102 32.512 45.607 31.73 31.732 73.92 49.207 118.795 49.207 44.873 0 87.062-17.475 118.793-49.207 22.796-22.794 38.222-50.988 45.108-81.697l-13.073 13.074c-7.811 7.812-20.475 7.811-28.285 0-7.812-7.811-7.812-20.475 0-28.285l46.15-46.148c4.020-4.021 9.324-5.955 14.592-5.838 5.264-0.113 10.562 1.82 14.576 5.838l45.5 45.494c7.81 7.811 7.808 20.475-0.002 28.285zM424.996 158.945c-7.656 0-14.299-4.305-17.662-10.621l-0.004 0.002c-8.092-16.697-19.004-32.104-32.51-45.609-31.732-31.73-73.92-49.205-118.795-49.205-44.873 0-87.062 17.475-118.793 49.205-24.116 24.116-39.99 54.273-46.224 87.058l14.646-14.646c7.811-7.811 20.475-7.809 28.283 0.002 7.812 7.811 7.812 20.475 0 28.283l-46.148 46.15c-4.021 4.020-9.326 5.955-14.594 5.836-5.262 0.115-10.561-1.82-14.576-5.836l-45.498-45.496c-7.811-7.811-7.809-20.475 0-28.285 7.811-7.811 20.475-7.811 28.285 0l9.739 9.739c17.046-97.722 102.28-172.011 204.88-172.011 80.473 0 150.266 45.701 184.854 112.562 2.4 4.641 2.139 4.182 2.504 5 0.016 0.039 0.035 0.078 0.053 0.117 0.006 0.016 0.012 0.027 0.020 0.045l-0.004 0.002c0.992 2.371 1.545 4.975 1.545 7.707-0.001 11.046-8.956 20.001-20.001 20.001zM73.203 207.58l0.412-0.412-0.7 0.124 0.288 0.288z" />
<glyph unicode="&#xe610;" d="M448.016 22.235v233.749c0 9.551-6.701 17.526-15.655 19.513-0.105 0.023-0.207 0.060-0.314 0.081-0.222 0.045-0.452 0.065-0.677 0.104-1.093 0.185-2.208 0.303-3.354 0.303h-27.951l0.001 27.925h0.010c0 79.57-64.505 144.075-144.075 144.075-79.564 0-144.065-64.496-144.075-144.059v-27.942h-27.426c-10.355 0-18.873-7.871-19.896-17.955-0.068-0.672-0.104-1.354-0.104-2.045v-231.666c-0.309-1.396-0.484-2.844-0.484-4.334 0-11.047 8.955-20 20-20h344.134c11.046 0 20 8.955 20 20 0 0.762-0.051 1.512-0.134 2.251zM104.5 235.984h303.516v-196h-303.516v196zM151.925 303.909c0 57.388 46.688 104.075 104.075 104.075 57.295 0 103.921-46.537 104.071-103.798 0-0.042-0.006-0.082-0.006-0.124v-28.078h-208.141l0.001 27.925zM276.082 134.071l-0.001-29.094c0.002-11.044-8.953-19.998-19.998-19.999-11.047-0.001-20.002 8.953-20 20v28.987c-7.352 5.863-12.067 14.891-12.067 25.024 0 17.673 14.327 32 32 32s32-14.327 32-32c0-10.071-4.659-19.052-11.934-24.918z" />
<glyph unicode="&#xe612;" d="M474.299 380.125c-7.811 7.809-20.475 7.809-28.285 0l-267.822-267.822-112.030 112.031c-7.812 7.812-20.477 7.81-28.285 0-7.812-7.81-7.812-20.474 0-28.284l125.787-125.787c0.126-0.133 0.236-0.275 0.367-0.406 3.91-3.91 9.036-5.862 14.161-5.857 5.125-0.005 10.251 1.947 14.161 5.857 0.124 0.124 0.228 0.259 0.348 0.385l281.598 281.6c7.81 7.81 7.81 20.471 0 28.283z" />
<glyph unicode="&#xe617;" d="M447.999 20.078v279.922c0 11.045-8.955 20-20 20h-89.329c-11.047 0-20-8.957-20-20.002s8.955-20 20-20h69.329v-239.998h-303.999v239.998h68.665c11.046 0 20 8.955 20 20s-8.954 20-20 20.002h-89.33c-11.046 0-20-8.957-20-20.002 0-1.743 0.246-3.424 0.665-5.037v-274.961c0-11.047 8.955-20 20-20h344.003c11.045 0 20 8.955 20 20 0 0.026-0.004 0.052-0.004 0.078zM332.811 379.917l-62.226 62.225c-4.020 4.021-9.325 5.956-14.593 5.837-0.806 0.017-1.611 0-2.414-0.080-0.279-0.027-0.549-0.080-0.825-0.119-0.371-0.052-0.742-0.096-1.109-0.169-0.328-0.065-0.645-0.155-0.966-0.236-0.311-0.078-0.623-0.146-0.93-0.239-0.32-0.098-0.63-0.218-0.943-0.33-0.302-0.108-0.605-0.207-0.902-0.33-0.299-0.124-0.585-0.27-0.877-0.407-0.299-0.141-0.6-0.273-0.893-0.43-0.29-0.155-0.566-0.332-0.847-0.5-0.277-0.166-0.558-0.322-0.828-0.503-0.334-0.224-0.65-0.471-0.97-0.713-0.198-0.151-0.404-0.288-0.598-0.447-0.524-0.429-1.027-0.883-1.504-1.363l-62.199-62.195c-7.811-7.811-7.809-20.475 0.001-28.285 7.811-7.811 20.475-7.811 28.284 0l28.082 28.083v-219.713c0-11.047 8.956-20 20.001-20s20 8.955 20 20v220.601l28.971-28.97c7.811-7.812 20.474-7.811 28.284 0 7.813 7.808 7.813 20.472 0.001 28.283z" />
<glyph unicode="&#xe619;" d="M480.016 345.999c0.374 5.020-1.123 10.182-4.613 14.342-2.992 3.566-6.969 5.811-11.197 6.697-0.065 0.014-0.134 0.033-0.198 0.047-0.541 0.109-1.086 0.188-1.636 0.25-0.125 0.016-0.246 0.039-0.371 0.051-0.514 0.049-1.026 0.068-1.545 0.078-0.146 0.002-0.291 0.021-0.438 0.021h-408.004c-10.708 0-19.424-8.422-19.949-19-0.017-0.314-0.046-0.629-0.048-0.945 0-0.018-0.003-0.037-0.003-0.057 0-0.025 0.004-0.051 0.004-0.076v-247.846c0-0.027-0.004-0.053-0.004-0.078 0-11.045 8.955-20 20-20h408.002c6.904 0 12.99 3.498 16.585 8.818 0.050 0.074 0.091 0.156 0.14 0.229 0.303 0.465 0.598 0.934 0.861 1.42 0.023 0.041 0.040 0.086 0.062 0.129 1.496 2.803 2.352 6.004 2.352 9.402v0 0 246.518zM72.016 304.922l169.973-140.594c3.284-3.217 7.459-5.084 11.782-5.561 0.080-0.012 0.16-0.025 0.24-0.033 0.419-0.041 0.838-0.057 1.259-0.070 0.248-0.010 0.497-0.029 0.746-0.029s0.498 0.021 0.746 0.029c0.421 0.014 0.84 0.029 1.259 0.070 0.080 0.008 0.16 0.021 0.24 0.033 4.323 0.479 8.498 2.344 11.782 5.561l169.973 140.604v-185.447h-368v185.437zM107.533 327.485h296.955l-148.473-122.711-148.482 122.711z" />
<glyph unicode="&#xe623;" d="M256.012 352c5.118 0 10.235-1.952 14.141-5.856l172.077-172.049c7.812-7.81 7.812-20.473 0.003-28.285-7.811-7.811-20.474-7.812-28.284-0.002l-157.938 157.91-157.96-157.934c-7.812-7.809-20.476-7.809-28.284 0.002-7.81 7.812-7.809 20.475 0.003 28.285l172.102 172.072c3.904 3.905 9.022 5.857 14.14 5.857z" />
<glyph unicode="&#xe626;" d="M255.988 95.999c-5.118 0-10.235 1.952-14.141 5.856l-172.077 172.049c-7.812 7.81-7.812 20.473-0.003 28.285 7.811 7.811 20.474 7.812 28.284 0.002l157.938-157.91 157.961 157.934c7.812 7.809 20.476 7.808 28.284-0.002 7.81-7.812 7.809-20.475-0.003-28.285l-172.102-172.072c-3.905-3.905-9.023-5.857-14.141-5.857z" />

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

View File

@@ -4,3 +4,4 @@
notices-for-0.9.0
notices-for-0.9.1
0.9.4-platform-file

View File

@@ -0,0 +1,2 @@
server
browser

View File

@@ -1 +1 @@
METEOR@0.9.3
METEOR@0.9.4

View File

@@ -1,51 +1,51 @@
application-configuration@1.0.2
autoupdate@1.1.1
base64@1.0.0
binary-heap@1.0.0
blaze-tools@1.0.0
blaze@2.0.1
boilerplate-generator@1.0.0
callback-hook@1.0.0
check@1.0.1
ctl-helper@1.0.3
ctl@1.0.1
ddp@1.0.9
deps@1.0.4
ejson@1.0.3
fastclick@1.0.0
follower-livedata@1.0.1
geojson-utils@1.0.0
html-tools@1.0.1
htmljs@1.0.1
http@1.0.6
id-map@1.0.0
insecure@1.0.0
jquery@1.0.0
json@1.0.0
livedata@1.0.10
logging@1.0.3
meteor-platform@1.1.1
meteor@1.1.1
minifiers@1.1.0
minimongo@1.0.3
mobile-status-bar@1.0.0
mongo@1.0.6
observe-sequence@1.0.2
ordered-dict@1.0.0
random@1.0.0
reactive-dict@1.0.3
reactive-var@1.0.2
reload@1.1.0
retry@1.0.0
routepolicy@1.0.1
session@1.0.2
spacebars-compiler@1.0.2
spacebars@1.0.2
standard-app-packages@1.0.2
templating@1.0.7
tracker@1.0.2
ui@1.0.3
underscore@1.0.0
url@1.0.0
webapp-hashing@1.0.0
webapp@1.1.2
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

View File

@@ -228,7 +228,7 @@ Meteor.startup(function () {
// subscribe to all the players, the game i'm in, and all
// the words in that game.
Deps.autorun(function () {
Tracker.autorun(function () {
Meteor.subscribe('players');
if (Session.get('player_id')) {

2
meteor
View File

@@ -1,6 +1,6 @@
#!/bin/bash
BUNDLE_VERSION=0.3.58
BUNDLE_VERSION=0.3.59
# OS Check. Put here because here is where we download the precompiled
# bundles that are arch specific.

View File

@@ -1,6 +1,6 @@
Package.describe({
summary: "A user account system",
version: "1.1.2-pre.2"
version: "1.1.2"
});
Package.on_use(function (api) {

View File

@@ -80,6 +80,7 @@ AccountsTest = {
* 2. `done`: A function to call when the password reset UI flow is complete. The normal
* login process is suspended until this function is called, so that the
* password for user A can be reset even if user B was logged in.
* @locus Client
*/
Accounts.onResetPasswordLink = function (callback) {
if (accountsCallbacks["reset-password"]) {
@@ -104,6 +105,7 @@ Accounts.onResetPasswordLink = function (callback) {
* The normal login process is suspended until this function is called, so
* that the user can be notified that they are verifying their email before
* being logged in.
* @locus Client
*/
Accounts.onEmailVerificationLink = function (callback) {
if (accountsCallbacks["verify-email"]) {
@@ -128,6 +130,7 @@ Accounts.onEmailVerificationLink = function (callback) {
* 2. `done`: A function to call when the enrollment UI flow is complete.
* The normal login process is suspended until this function is called, so that
* user A can be enrolled even if user B was logged in.
* @locus Client
*/
Accounts.onEnrollmentLink = function (callback) {
if (accountsCallbacks["enroll-account"]) {

View File

@@ -1,6 +1,6 @@
Package.describe({
summary: "Login service for Facebook accounts",
version: "1.0.2-pre.2"
version: "1.0.2"
});
Package.on_use(function(api) {

View File

@@ -1,6 +1,6 @@
Package.describe({
summary: "Login service for Github accounts",
version: "1.0.2-pre.2"
version: "1.0.2"
});
Package.on_use(function(api) {

View File

@@ -1,6 +1,6 @@
Package.describe({
summary: "Login service for Google accounts",
version: "1.0.2-pre.2"
version: "1.0.2"
});
Package.on_use(function(api) {

View File

@@ -1,6 +1,6 @@
Package.describe({
summary: "Login service for Meetup accounts",
version: "1.0.2-pre.2"
version: "1.0.2"
});
Package.on_use(function(api) {

View File

@@ -1,6 +1,6 @@
Package.describe({
summary: "Login service for Meteor developer accounts",
version: "1.0.2-pre.2"
version: "1.0.2"
});
Package.on_use(function (api) {

View File

@@ -1,6 +1,6 @@
Package.describe({
summary: "Common code for OAuth-based login services",
version: "1.1.2-pre.2"
version: "1.1.2"
});
Package.on_use(function (api) {

Some files were not shown because too many files have changed in this diff Show More