Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Merge branch 'release-0.5.3'

  • Loading branch information...
commit 182e1dcd81916bb47e74a9e4cdba07cebd39fe3b 2 parents 067113a + a9a7179
@glasser glasser authored
Showing with 3,521 additions and 3,633 deletions.
  1. +3 −0  .gitignore
  2. +75 −0 History.md
  3. +53 −7 admin/cli-test.sh
  4. +53 −0 admin/copy-release-from-jenkins.sh
  5. +1 −1  admin/debian/changelog
  6. +11 −0 admin/find-new-npm-versions.sh
  7. +11 −2 admin/generate-dev-bundle.sh
  8. +1 −1  admin/increment-version.js
  9. +1 −1  admin/install-s3.sh
  10. +3 −3 admin/manifest.json
  11. +1 −1  admin/meteor.spec
  12. +3 −0  admin/node.sh
  13. +8 −8 app/lib/bundler.js
  14. +1 −1  app/lib/files.js
  15. +67 −0 app/lib/mongo_exit_codes.js
  16. +47 −44 app/lib/mongo_runner.js
  17. +16 −16 app/lib/packages.js
  18. +1 −1  app/lib/project.js
  19. +0 −1,200 app/lib/third/underscore.js
  20. +7 −4 app/lib/updater.js
  21. +58 −21 app/meteor/deploy.js
  22. +591 −565 app/meteor/meteor.js
  23. +2 −2 app/meteor/post-upgrade.js
  24. +151 −24 app/meteor/run.js
  25. +1 −1  app/meteor/update.js
  26. +1 −3 app/server/server.js
  27. +0 −1,200 app/server/underscore.js
  28. +37 −5 docs/client/api.html
  29. +25 −4 docs/client/api.js
  30. +14 −0 docs/client/commandline.html
  31. +1 −0  docs/client/docs.css
  32. +1 −1  docs/client/docs.html
  33. +3 −2 docs/client/docs.js
  34. +1 −1  examples/parties/client/parties.css
  35. 0  examples/parties/public/{soma.jpeg → soma.png}
  36. +1 −1  examples/unfinished/accounts-ui-viewer/accounts-ui-viewer.html
  37. +0 −58 examples/unfinished/benchmark/benchmark-scenarios.js
  38. +6 −6 examples/unfinished/benchmark/benchmark.js
  39. +3 −1 examples/unfinished/benchmark/run-local.sh
  40. +27 −0 examples/unfinished/benchmark/scenarios/README.md
  41. +9 −0 examples/unfinished/benchmark/scenarios/bigdata-static.json
  42. +10 −0 examples/unfinished/benchmark/scenarios/bigdata-updates.json
  43. +13 −0 examples/unfinished/benchmark/scenarios/default.json
  44. +7 −0 examples/unfinished/benchmark/scenarios/nodata.json
  45. +1 −0  examples/unfinished/jsparse-docs/.meteor/.gitignore
  46. +6 −0 examples/unfinished/jsparse-docs/.meteor/packages
  47. +42 −0 examples/unfinished/jsparse-docs/jsparse-docs.css
  48. +279 −0 examples/unfinished/jsparse-docs/jsparse-docs.html
  49. +67 −0 examples/unfinished/jsparse-docs/jsparse-docs.js
  50. +1 −1  examples/wordplay/server/game.js
  51. +3 −2 meteor
  52. +4 −1 packages/accounts-base/accounts_server.js
  53. +4 −2 packages/accounts-base/accounts_tests.js
  54. +0 −1  packages/accounts-facebook/facebook_client.js
  55. +3 −0  packages/accounts-facebook/facebook_login_button.css
  56. +1 −1  packages/accounts-facebook/package.js
  57. +3 −0  packages/accounts-github/github_login_button.css
  58. +1 −1  packages/accounts-github/package.js
  59. +8 −8 packages/accounts-google/google_client.js
  60. +1 −1  packages/accounts-google/google_configure.js
  61. +3 −0  packages/accounts-google/google_login_button.css
  62. +27 −8 packages/accounts-google/google_server.js
  63. +1 −1  packages/accounts-google/package.js
  64. +30 −14 packages/accounts-oauth1-helper/oauth1_binding.js
  65. +0 −1  packages/accounts-password/password_client.js
  66. +1 −1  packages/accounts-twitter/package.js
  67. +0 −1  packages/accounts-twitter/twitter_client.js
  68. +3 −0  packages/accounts-twitter/twitter_login_button.css
  69. +1 −1  packages/accounts-twitter/twitter_server.js
  70. +17 −2 packages/accounts-ui-unstyled/accounts_ui.js
  71. +6 −1 packages/accounts-ui-unstyled/accounts_ui_tests.js
  72. +43 −10 packages/accounts-ui-unstyled/login_buttons.js
  73. +2 −1  packages/accounts-ui-unstyled/login_buttons_dialogs.js
  74. +20 −0 packages/accounts-ui-unstyled/login_buttons_dropdown.html
  75. +1 −0  packages/accounts-ui-unstyled/login_buttons_dropdown.js
  76. +0 −21 packages/accounts-ui-unstyled/login_buttons_images.css
  77. +2 −0  packages/accounts-ui-unstyled/login_buttons_single.js
  78. +0 −1  packages/accounts-ui-unstyled/package.js
  79. +1 −1  packages/accounts-ui/login_buttons.less
  80. +1 −1  packages/accounts-ui/package.js
  81. +1 −1  packages/accounts-weibo/package.js
  82. +0 −1  packages/accounts-weibo/weibo_client.js
  83. +3 −0  packages/accounts-weibo/weibo_login_button.css
  84. +85 −9 packages/domutils/domutils.js
  85. +13 −1 packages/domutils/domutils_tests.js
  86. +9 −4 packages/http/httpcall_client.js
  87. +6 −1 packages/http/httpcall_common.js
  88. +5 −4 packages/http/httpcall_server.js
  89. +18 −1 packages/http/httpcall_tests.js
  90. +35 −9 packages/jsparse/lexer.js
  91. +30 −7 packages/jsparse/parser.js
  92. +71 −7 packages/jsparse/parser_tests.js
  93. +6 −6 packages/livedata/client_convenience.js
  94. +11 −9 packages/livedata/livedata_connection.js
  95. +43 −0 packages/livedata/livedata_connection_tests.js
  96. +4 −2 packages/livedata/server_convenience.js
  97. +12 −2 packages/liverange/liverange.js
  98. +7 −7 packages/madewith/madewith.js
  99. +12 −1 packages/meteor/fiber_helpers.js
  100. +9 −0 packages/meteor/server_environment.js
  101. +112 −32 packages/minimongo/minimongo.js
  102. +438 −110 packages/minimongo/minimongo_tests.js
  103. +26 −16 packages/minimongo/selector.js
  104. +14 −6 packages/minimongo/sort.js
  105. +1 −1  packages/minimongo/uuid.js
  106. +37 −0 packages/mongo-livedata/collection.js
  107. +15 −24 packages/mongo-livedata/mongo_driver.js
  108. +65 −6 packages/mongo-livedata/mongo_livedata_tests.js
  109. +111 −30 packages/spark/patch.js
  110. +13 −2 packages/spark/patch_tests.js
  111. +54 −2 packages/spark/spark.js
  112. +219 −31 packages/spark/spark_tests.js
  113. +40 −13 packages/stream/stream_client.js
  114. +10 −10 packages/stream/stream_tests.js
  115. +12 −2 packages/test-helpers/canonicalize_html.js
  116. +6 −0 packages/test-in-browser/driver.html
  117. +18 −1 packages/test-in-browser/driver.js
  118. +29 −0 packages/tinytest/tinytest.js
  119. +3 −1 packages/tinytest/tinytest_client.js
  120. +9 −5 packages/tinytest/tinytest_server.js
  121. +1 −1  packages/uuid/uuid.js
View
3  .gitignore
@@ -6,3 +6,6 @@
\#*\#
.\#*
.idea
+*.iml
+*.sublime-project
+*.sublime-workspace
View
75 History.md
@@ -1,6 +1,81 @@
## vNEXT
+## v0.5.3
+
+* Add `--settings` argument to `meteor deploy` and `meteor run`. This
+ allows you to specify deployment-specific information made available
+ to server code in the variable `Meteor.settings`.
+
+* Support unlimited open tabs in a single browser. Work around the
+ browser per-hostname connection limit by using randomized hostnames
+ for deployed apps. #131
+
+* minimongo improvements:
+ * Allow observing cursors with `skip` or `limit`. #528
+ * Allow sorting on `dotted.sub.keys`. #533
+ * Allow querying specific array elements (`foo.1.bar`).
+ * `$and`, `$or`, and `$nor` no longer accept empty arrays (for consistency
+ with Mongo)
+
+* Re-rendering a template with Spark no longer reverts changes made by
+ users to a `preserve`d form element. Instead, the newly rendered value
+ is only applied if it is different from the previously rendered value.
+ Additionally, <INPUT> elements with type other than TEXT can now have
+ reactive values (eg, the labels on submit buttons can now be
+ reactive). #510 #514 #523 #537 #558
+
+* Support JavaScript RegExp objects in selectors in Collection write
+ methods on the client, eg `myCollection.remove({foo: /bar/})`. #346
+
+* `meteor` command-line improvements:
+ * Improve error message when mongod fails to start.
+ * The `NODE_OPTIONS` environment variable can be used to pass command-line
+ flags to node (eg, `--debug` or `--debug-brk` to enable the debugger).
+ * Die with error if an app name is mistakenly passed to `meteor reset`.
+
+* Add support for "offline" access tokens with Google login. #464 #525
+
+* Don't remove `serviceData` fields from previous logins when logging in
+ with an external service.
+
+* Improve `OAuth1Binding` to allow making authenticated API calls to
+ OAuth1 providers (eg Twitter). #539
+
+* New login providers automatically work with `{{loginButtons}}` without
+ needing to edit the `accounts-ui-unstyled` package. #572
+
+* Use `Content-Type: application/json` by default when sending JSON data
+ with `Meteor.http`.
+
+* Improvements to `jsparse`: hex literals, keywords as property names, ES5 line
+ continuations, trailing commas in object literals, line numbers in error
+ messages, decimal literals starting with `.`, regex character classes with
+ slashes.
+
+* Spark improvements:
+ * Improve rendering of <SELECT> elements on IE. #496
+ * Don't lose nested data contexts in IE9/10 after two seconds. #458
+ * Don't print a stack trace if DOM nodes are manually removed
+ from the document without calling `Spark.finalize`. #392
+
+* Always use the `autoReconnect` flag when connecting to Mongo. #425
+
+* Fix server-side `observe` with no `added` callback. #589
+
+* Fix re-sending method calls on reconnect. #538
+
+* Remove deprecated `/sockjs` URL support from `Meteor.connect`.
+
+* Avoid losing a few bits of randomness in UUID v4 creation. #519
+
+* Update clean-css package from 0.8.2 to 0.8.3, fixing minification of `0%`
+ values in `hsl` colors. #515
+
+Patches contributed by GitHub users Ed-von-Schleck, egtann, jwulf, lvbreda,
+martin-naumann, meawoppl, nwmartin, timhaines, and zealoushacker.
+
+
## v0.5.2
* Fix 0.5.1 regression: Cursor `observe` works during server startup. #507
View
60 admin/cli-test.sh
@@ -1,9 +1,19 @@
#!/bin/bash
-# NOTE: by default this tests the installed meteor, not the one in your
-# working copy.
+# NOTE: by default this tests the working copy, not the installed meteor.
+# To test the installed meteor, pass in --global
-METEOR=/usr/local/bin/meteor
+cd `dirname $0`
+METEOR=`pwd`/../meteor
+
+if [ -z "$NODE" ]; then
+ NODE=`pwd`/node.sh
+fi
+
+#If this ever takes more options, use getopt
+if [ "$1" == "--global" ]; then
+ METEOR=meteor
+fi
DIR=`mktemp -d -t meteor-cli-test-XXXXXXXX`
trap 'echo FAILED ; rm -rf "$DIR" >/dev/null 2>&1' EXIT
@@ -71,7 +81,8 @@ echo "... run"
MONGOMARK='--bind_ip 127.0.0.1 --smallfiles --port 9102'
# kill any old test meteor
# there is probably a better way to do this, but it is at least portable across macos and linux
-ps ax | grep -e 'meteor.js -p 9100' | grep -v grep | awk '{print $1}' | xargs kill
+# (the || true is needed on linux, whose xargs will invoke kill even with no args)
+ps ax | grep -e 'meteor.js -p 9100' | grep -v grep | awk '{print $1}' | xargs kill || true
! $METEOR mongo > /dev/null 2>&1
$METEOR reset > /dev/null 2>&1
@@ -83,7 +94,7 @@ PORT=9100
$METEOR -p $PORT > /dev/null 2>&1 &
METEOR_PID=$!
-sleep 1 # XXX XXX lame
+sleep 2 # XXX XXX lame
test -d .meteor/local/db
ps ax | grep -e "$MONGOMARK" | grep -v grep > /dev/null
@@ -105,16 +116,51 @@ echo "... rerun"
$METEOR -p $PORT > /dev/null 2>&1 &
METEOR_PID=$!
-sleep 1 # XXX XXX lame
+sleep 2 # XXX XXX lame
ps ax | grep -e "$MONGOMARK" | grep -v grep > /dev/null
curl -s "http://localhost:$PORT" > /dev/null
kill $METEOR_PID
-ps ax | grep -e "$MONGOMARK" | grep -v grep | awk '{print $1}' | xargs kill
+sleep 10 # XXX XXX lame. have to wait for inner app to die via keepalive!
+
+ps ax | grep -e "$MONGOMARK" | grep -v grep | awk '{print $1}' | xargs kill || true
+sleep 2 # need to make sure these kills take effect
+
+echo "... mongo message"
+
+# Run a server on the same port as mongod, so that mongod fails to start up. Rig
+# it so that a single connection will cause it to exit.
+$NODE -e 'require("net").createServer(function(){process.exit(0)}).listen('$PORT'+2, "127.0.0.1")' &
+
+sleep 1
+
+$METEOR -p $PORT > error.txt || true
+
+grep 'port was closed' error.txt > /dev/null
+
+# Kill the server by connecting to it.
+$NODE -e 'require("net").connect({host:"127.0.0.1",port:'$PORT'+2},function(){process.exit(0);})'
+
+echo "... settings"
+cat > settings.json <<EOF
+{ "foo" : "bar",
+ "baz" : "quux"
+}
+EOF
+cat > settings.js <<EOF
+if (Meteor.isServer) {
+ Meteor.startup(function () {
+ if (!Meteor.settings) process.exit(1);
+ if (Meteor.settings.foo !== "bar") process.exit(1);
+ process.exit(0);
+ });
+}
+EOF
+$METEOR -p $PORT --settings='settings.json' --once > /dev/null
# XXX more tests here!
View
53 admin/copy-release-from-jenkins.sh
@@ -0,0 +1,53 @@
+#!/bin/bash
+
+# Requires s3cmd to be installed and an appropriate ~/.s3cfg.
+# Usage:
+# admin/copy-release-from-jenkins.sh [--prod] BUILDNUMBER
+# where BUILDNUMBER is the small integer Jenkins build number.
+
+set -e
+set -u
+
+cd `dirname $0`
+
+TARGET="s3://com.meteor.static/test/"
+TEST=no
+if [ $# -ge 1 -a $1 = '--prod' ]; then
+ shift
+ TARGET="s3://com.meteor.static/"
+else
+ TEST=yes
+fi
+
+if [ $# -ne 1 ]; then
+ echo "usage: $0 [--prod] jenkins-build-number" 1>&2
+ exit 1
+fi
+
+DIRNAME=$(s3cmd ls s3://com.meteor.jenkins/ | perl -nle 'print $1 if m!/(release-.+--'$1'--.+)/!')
+
+if [ -z "$DIRNAME" ]; then
+ echo "build not found" 1>&2
+ exit 1
+fi
+
+echo Found build $DIRNAME
+
+# Check to make sure the proper number of each kind of file is there.
+s3cmd ls s3://com.meteor.jenkins/$DIRNAME/ | \
+ perl -nle '++$RPM if /\.rpm/; ++$DEB if /\.deb/; ++$TAR if /\.tar\.gz/; ++$DIR if /DIR/; END { exit !($RPM == 2 && $DEB == 2 && $TAR == 3 && $DIR == 1) }'
+
+echo Copying to $TARGET
+s3cmd -P cp -r s3://com.meteor.jenkins/$DIRNAME/ $TARGET
+
+if [ $TEST = 'yes' ]; then
+ echo Uploading modified install-s3.sh and manifest.json
+
+ OUTDIR=$(mktemp -dt meteor-crfj)
+ perl -pe 's!https://d3sqy0vbqsdhku.cloudfront.net!https://s3.amazonaws.com/com.meteor.static/test!g' install-s3.sh >$OUTDIR/install-s3.sh
+ perl -pe 's!https://d3sqy0vbqsdhku.cloudfront.net!https://s3.amazonaws.com/com.meteor.static/test!g' manifest.json >$OUTDIR/manifest.json
+
+ cd $OUTDIR
+ s3cmd -P put install-s3.sh s3://com.meteor.static/test/update/
+ s3cmd -P put manifest.json s3://com.meteor.static/test/update/
+fi
View
2  admin/debian/changelog
@@ -1,4 +1,4 @@
-meteor (0.5.2-1) unstable; urgency=low
+meteor (0.5.3-1) unstable; urgency=low
* Automated debian build.
View
11 admin/find-new-npm-versions.sh
@@ -0,0 +1,11 @@
+#!/bin/bash
+BASEDIR=`dirname $0`
+cat $BASEDIR/generate-dev-bundle.sh | grep "npm install" | sed "s/npm install //" | sed "s/@.*//" | while read PACKAGE
+do
+ CURRENT_VERSION=`cat $BASEDIR/generate-dev-bundle.sh | grep "npm install $PACKAGE" | sed "s/npm install //" | sed "s/.*@//"`
+ LATEST_VERSION=`$BASEDIR/../dev_bundle/bin/npm info $PACKAGE version 2> /dev/null`
+ if [ "$CURRENT_VERSION" != "$LATEST_VERSION" ]
+ then
+ echo "$PACKAGE -- current version: $CURRENT_VERSION, latest version: $LATEST_VERSION"
+ fi
+done
View
13 admin/generate-dev-bundle.sh
@@ -3,7 +3,7 @@
set -e
set -u
-BUNDLE_VERSION=0.2.8
+BUNDLE_VERSION=0.2.12
UNAME=$(uname)
ARCH=$(uname -m)
@@ -89,7 +89,7 @@ npm install semver@1.1.0
npm install handlebars@1.0.7
npm install mongodb@1.1.11
npm install uglify-js@1.3.4
-npm install clean-css@0.8.2
+npm install clean-css@0.8.3
npm install useragent@1.1.0
npm install request@2.12.0
npm install simplesmtp@0.1.25
@@ -97,6 +97,8 @@ npm install stream-buffers@0.2.3
npm install keypress@0.1.0
npm install sockjs@0.3.4
npm install http-proxy@0.8.5
+npm install underscore@1.4.2
+npm install tar@0.1.14
# progress 0.1.0 has a regression where it opens stdin and thus does not
# allow the node process to exit cleanly. See
@@ -107,6 +109,13 @@ npm install progress@0.0.5
# which make the dev bundle much bigger. We need a better solution.
npm install mailcomposer@0.1.15
+# Use our version of fstream with a bug fixed. Also have tar use it.
+# See https://github.com/isaacs/fstream/pull/11 .
+npm install https://github.com/meteor/fstream/tarball/91c56e7
+cd tar/node_modules
+npm install https://github.com/meteor/fstream/tarball/91c56e7
+cd ../..
+
# If you update the version of fibers in the dev bundle, also update the "npm
# install" command in docs/client/concepts.html.
npm install fibers@0.6.9
View
2  admin/increment-version.js
@@ -7,7 +7,7 @@ var semver = require('semver');
var optimist = require('optimist');
var updater = require(path.join(__dirname, '..', 'app', 'lib', 'updater.js'));
-var _ = require(path.join(__dirname, '..', 'app', 'lib', 'third', 'underscore.js'));
+var _ = require('underscore');
// What files to update. Relative to project root.
var UPDATE_FILES = [path.join('app', 'lib', 'updater.js'),
View
2  admin/install-s3.sh
@@ -5,7 +5,7 @@
## example.
URLBASE="https://d3sqy0vbqsdhku.cloudfront.net"
-VERSION="0.5.2"
+VERSION="0.5.3"
PKGVERSION="${VERSION}-1"
UNAME=`uname`
View
6 admin/manifest.json
@@ -1,6 +1,6 @@
{
- "version": "0.5.2",
- "deb_version": "0.5.2-1",
- "rpm_version": "0.5.2-1",
+ "version": "0.5.3",
+ "deb_version": "0.5.3-1",
+ "rpm_version": "0.5.3-1",
"urlbase": "https://d3sqy0vbqsdhku.cloudfront.net"
}
View
2  admin/meteor.spec
@@ -5,7 +5,7 @@
Summary: Meteor platform and JavaScript application server
Vendor: Meteor
Name: meteor
-Version: 0.5.2
+Version: 0.5.3
Release: 1
License: MIT
Group: Networking/WWW
View
3  admin/node.sh
@@ -20,6 +20,7 @@ if [ "$EMACS" == t ]; then
fi
"$TOPDIR/dev_bundle/bin/node" "$@"
+EXITSTATUS=$?
# Node sets stdin to non-blocking, which causes Emacs shell to die after it
# exits. Work around this by setting stdin to blocking again.
@@ -27,3 +28,5 @@ if [ "$EMACS" == t ]; then
perl -MFcntl=F_GETFL,F_SETFL,O_NONBLOCK -e \
'fcntl(STDIN, F_SETFL, ~O_NONBLOCK & fcntl(STDIN, F_GETFL, 0))'
fi
+
+exit $EXITSTATUS
View
16 app/lib/bundler.js
@@ -31,7 +31,7 @@ var crypto = require('crypto');
var fs = require('fs');
var uglify = require('uglify-js');
var cleanCSS = require('clean-css');
-var _ = require(path.join(__dirname, 'third', 'underscore.js'));
+var _ = require('underscore');
// files to ignore when bundling. node has no globs, so use regexps
var ignore_files = [
@@ -143,7 +143,7 @@ _.extend(PackageInstance.prototype, {
// should be the extension of the file without a leading dot.)
get_source_handler: function (extension) {
var self = this;
- var candidates = []
+ var candidates = [];
if (extension in self.pkg.extensions)
candidates.push(self.pkg.extensions[extension]);
@@ -277,7 +277,7 @@ var Bundle = function () {
_.each(where, function (w) {
if (options.type === "js") {
if (!options.path)
- throw new Error("Must specify path")
+ throw new Error("Must specify path");
if (w === "client" || w === "server") {
self.files[w][options.path] = data;
@@ -292,7 +292,7 @@ var Bundle = function () {
// that appear in the server directories in an app tree
return;
if (!options.path)
- throw new Error("Must specify path")
+ throw new Error("Must specify path");
self.files.client[options.path] = data;
self.css.push(options.path);
} else if (options.type === "head" || options.type === "body") {
@@ -358,8 +358,8 @@ _.extend(Bundle.prototype, {
// XXX detect circular dependencies and print an error. (not sure
// what the current code will do)
- if (pkg.on_use)
- pkg.on_use(inst.api, where);
+ if (pkg.on_use_handler)
+ pkg.on_use_handler(inst.api, where);
},
include_tests: function (pkg) {
@@ -369,8 +369,8 @@ _.extend(Bundle.prototype, {
self.tests_included[pkg.id] = true;
var inst = self._get_instance(pkg);
- if (inst.pkg.on_test)
- inst.pkg.on_test(inst.api);
+ if (inst.pkg.on_test_handler)
+ inst.pkg.on_test_handler(inst.api);
},
// Minify the bundle
View
2  app/lib/files.js
@@ -1,6 +1,6 @@
var fs = require("fs");
var path = require('path');
-var _ = require(path.join(__dirname, 'third', 'underscore.js'));
+var _ = require('underscore');
var files = module.exports = {
// A sort comparator to order files into load order.
View
67 app/lib/mongo_exit_codes.js
@@ -0,0 +1,67 @@
+// MongoDB exit codes. This replicates information in
+// https://github.com/mongodb/docs/blob/master/source/reference/exit-codes.txt
+// but in a javascript dictionary instead of just a text file.
+
+// Explanations have been rewritten, not copied, for license reasons.
+
+
+var path = require("path");
+var _ = require('underscore');
+
+exports.Codes = {
+ 0 : { code: 0,
+ symbol: "EXIT_CLEAN",
+ longText: "MongoDB exited cleanly"
+ },
+ 2 : { code: 2,
+ symbol: "EXIT_BADOPTIONS",
+ longText: "MongoDB was started with erroneous or incompatible command line options"
+ },
+ 3 : { code: 3,
+ symbol: "EXIT_REPLICATION_ERROR",
+ longText: "There was an inconsistency between hostnames specified\n" +
+ "on the command line compared with hostnames stored in local.sources"
+ },
+ 4 : { code: 4,
+ symbol: "EXIT_NEED_UPGRADE",
+ longText: "MongoDB needs to upgrade to use this database"
+ },
+ 5 : { code: 5,
+ symbol: "EXIT_SHARDING_ERROR",
+ longText: "A moveChunk operation failed"
+ },
+ 12 : { code: 12,
+ symbol: "EXIT_KILL",
+ longText: "The MongoDB process was killed, on Windows"
+ },
+ 14 : { code: 14,
+ symbol: "EXIT_ABRUPT",
+ longText: "Unspecified unrecoverable error. Exit was not clean"
+ },
+ 20 : { code: 20,
+ symbol: "EXIT_NTSERVICE_ERROR",
+ longText: "Error managing NT Service on Windows"
+ },
+ 45 : { code: 45,
+ symbol: "EXIT_FS",
+ longText: "MongoDB cannot open or obtain a lock on a file"
+ },
+ 47 : { code: 47,
+ symbol: "EXIT_CLOCK_SKEW",
+ longText: "MongoDB exited due to excess clock skew"
+ },
+ 48 : { code: 48,
+ symbol: "EXIT_NET_ERROR",
+ longText: "MongoDB exited because its port was closed, or was already\n" +
+ "taken by a previous instance of MongoDB"
+ },
+ 100 : { code: 100,
+ symbol: "EXIT_UNCAUGHT",
+ longText: "MongoDB had an unspecified uncaught exception.\n" +
+ "Check to make sure that MongoDB is able to write to its database directory."
+ }
+};
+
+_.each(exports.Codes, function (value) {
+ exports[value.symbol] = value;
+});
View
91 app/lib/mongo_runner.js
@@ -1,10 +1,10 @@
var fs = require("fs");
var path = require("path");
-var spawn = require('child_process').spawn;
+var child_process = require('child_process');
var files = require(path.join(__dirname, '..', 'lib', 'files.js'));
-var _ = require(path.join('..', 'lib', 'third', 'underscore.js'));
+var _ = require('underscore');
/** Internal.
@@ -15,37 +15,34 @@ var _ = require(path.join('..', 'lib', 'third', 'underscore.js'));
*/
var find_mongo_pids = function (app_dir, port, callback) {
// 'ps ax' should be standard across all MacOS and Linux.
- var proc = spawn('ps', ['ax']);
- var data = '';
- proc.stdout.on('data', function (d) {
- data += d;
- });
-
- proc.on('exit', function (code, signal) {
- if (code === 0) {
- var pids = [];
-
- _.each(data.split('\n'), function (ps_line) {
- // matches mongos we start.
- var m = ps_line.match(/^\s*(\d+).+mongod .+--port (\d+) --dbpath (.+)(?:\/|\\)\.meteor(?:\/|\\)local(?:\/|\\)db\s*$/);
- if (m && m.length === 4) {
- var found_pid = parseInt(m[1]);
- var found_port = parseInt(m[2]);
- var found_path = m[3];
-
- if ( (!port || port === found_port) &&
- (!app_dir || app_dir === found_path)) {
- pids.push({
- pid: found_pid, port: found_port, app_dir: found_path});
+ child_process.exec('ps ax',
+ function (error, stdout, stderr) {
+ if (error) {
+ callback({reason: error});
+ } else if (stderr) {
+ callback({reason: 'ps produced stderr ' + stderr});
+ } else {
+ var pids = [];
+
+ _.each(stdout.split('\n'), function (ps_line) {
+ // matches mongos we start.
+ var m = ps_line.match(/^\s*(\d+).+mongod .+--port (\d+) --dbpath (.+)(?:\/|\\)\.meteor(?:\/|\\)local(?:\/|\\)db\s*$/);
+ if (m && m.length === 4) {
+ var found_pid = parseInt(m[1]);
+ var found_port = parseInt(m[2]);
+ var found_path = m[3];
+
+ if ( (!port || port === found_port) &&
+ (!app_dir || app_dir === found_path)) {
+ pids.push({
+ pid: found_pid, port: found_port, app_dir: found_path});
+ }
}
- }
- });
+ });
- callback(null, pids);
- } else {
- callback({reason: 'ps exit code ' + code});
- }
- });
+ callback(null, pids);
+ }
+ });
};
@@ -131,17 +128,24 @@ var find_mongo_and_kill_it_dead = function (port, callback) {
};
exports.launch_mongo = function (app_dir, port, launch_callback, on_exit_callback) {
+ var handle = {stop: function (callback) { callback(); } };
launch_callback = launch_callback || function () {};
on_exit_callback = on_exit_callback || function () {};
// If we are passed an external mongo, assume it is launched and never
// exits. Matches code in run.js:exports.run.
+
+ // Since it is externally managed, asking it to actually stop would be
+ // impolite, so our stoppable handle is a noop
if (process.env.MONGO_URL) {
launch_callback();
- return;
+ return handle;
}
- var mongod_path = path.join(files.get_dev_bundle(), 'mongodb', 'bin', 'mongod');
+ var mongod_path = path.join(files.get_dev_bundle(),
+ 'mongodb',
+ 'bin',
+ 'mongod');
// store data in app_dir
var data_path = path.join(app_dir, '.meteor', 'local', 'db');
@@ -155,21 +159,21 @@ exports.launch_mongo = function (app_dir, port, launch_callback, on_exit_callbac
return;
}
- var proc = spawn(mongod_path, [
+ var proc = child_process.spawn(mongod_path, [
'--bind_ip', '127.0.0.1',
'--smallfiles',
'--port', port,
'--dbpath', data_path
]);
+ handle.stop = function (callback) {
+ var tries = 0;
+ var exited = false;
+ proc.removeListener('exit', on_exit_callback);
+ proc.kill('SIGINT');
+ callback && callback(err);
+ };
- proc.on('exit', function (code, signal) {
- on_exit_callback(code, signal);
- });
-
- // proc.stderr.setEncoding('utf8');
- // proc.stderr.on('data', function (data) {
- // process.stdout.write(data);
- // });
+ proc.on('exit', on_exit_callback);
proc.stdout.setEncoding('utf8');
proc.stdout.on('data', function (data) {
@@ -178,6 +182,5 @@ exports.launch_mongo = function (app_dir, port, launch_callback, on_exit_callbac
launch_callback();
});
});
-
+ return handle;
};
-
View
32 app/lib/packages.js
@@ -1,5 +1,5 @@
var path = require('path');
-var _ = require(path.join(__dirname, 'third', 'underscore.js'));
+var _ = require('underscore');
var files = require(path.join(__dirname, 'files.js'));
var fs = require('fs');
@@ -36,14 +36,14 @@ var Package = function () {
// package metadata, from describe()
self.metadata = {};
- self.on_use = null;
- self.on_test = null;
+ self.on_use_handler = null;
+ self.on_test_handler = null;
// registered source file handlers
self.extensions = {};
// functions that can be called when the package is scanned
- self.api = {
+ self.declarationFuncs = {
// keys
// - summary: for 'meteor list'
// - internal: if true, hide in list
@@ -60,15 +60,15 @@ var Package = function () {
},
on_use: function (f) {
- if (self.on_use)
+ if (self.on_use_handler)
throw new Error("A package may have only one on_use handler");
- self.on_use = f;
+ self.on_use_handler = f;
},
on_test: function (f) {
- if (self.on_test)
+ if (self.on_test_handler)
throw new Error("A package may have only one on_test handler");
- self.on_test = f;
+ self.on_test_handler = f;
},
register_extension: function (extension, callback) {
@@ -86,10 +86,10 @@ _.extend(Package.prototype, {
self.name = name;
self.source_root = files.get_package_dir(name);
self.serve_root = path.join(path.sep, 'packages', name);
-
+
if (!self.source_root)
throw new Error("The package named " + self.name + " does not exist.");
-
+
var fullpath = path.join(self.source_root, 'package.js');
var code = fs.readFileSync(fullpath).toString();
// \n is necessary in case final line is a //-comment
@@ -104,7 +104,7 @@ _.extend(Package.prototype, {
// 'templating' use this to load other code to run at
// bundle-time. and to pull in, eg, 'fs' and 'path' to access
// the file system
- func(self.api, require);
+ func(self.declarationFuncs, require);
},
init_from_app_dir: function (app_dir, ignore_files) {
@@ -124,7 +124,7 @@ _.extend(Package.prototype, {
});
};
- self.api.on_use(function (api) {
+ self.declarationFuncs.on_use(function (api) {
// -- Packages --
// standard client packages (for now), for the classic meteor
@@ -140,7 +140,7 @@ _.extend(Package.prototype, {
api.add_files(sources_except(api, "client"), "server");
});
- self.api.on_test(function (api) {
+ self.declarationFuncs.on_test(function (api) {
api.use(self);
api.add_files(sources_except(api, "server", true), "client");
api.add_files(sources_except(api, "client", true), "server");
@@ -202,7 +202,7 @@ _.extend(Package.prototype, {
self.source_root = null;
self.serve_root = null;
- self.api.on_test(function (api) {
+ self.declarationFuncs.on_test(function (api) {
_.each(fs.readdirSync(collection_dir), function (name) {
// only take things that are actually packages
if (files.is_package_dir(path.join(collection_dir, name)))
@@ -269,13 +269,13 @@ var packages = module.exports = {
// a package object.
list: function () {
var ret = {};
-
+
_.each(files.get_package_dirs(), function(dir) {
_.each(fs.readdirSync(dir), function (name) {
// skip .meteor directory
if (fs.existsSync(path.join(dir, name, 'package.js')))
ret[name] = packages.get(name);
- });
+ });
})
return ret;
View
2  app/lib/project.js
@@ -1,6 +1,6 @@
var fs = require('fs');
var path = require('path');
-var _ = require(path.join(__dirname, 'third', 'underscore.js'));
+var _ = require('underscore');
var project = module.exports = {
View
1,200 app/lib/third/underscore.js
@@ -1,1200 +0,0 @@
-// Underscore.js 1.4.2
-// http://underscorejs.org
-// (c) 2009-2012 Jeremy Ashkenas, DocumentCloud Inc.
-// Underscore may be freely distributed under the MIT license.
-
-(function() {
-
- // Baseline setup
- // --------------
-
- // Establish the root object, `window` in the browser, or `global` on the server.
- var root = this;
-
- // Save the previous value of the `_` variable.
- var previousUnderscore = root._;
-
- // Establish the object that gets returned to break out of a loop iteration.
- var breaker = {};
-
- // Save bytes in the minified (but not gzipped) version:
- var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = Function.prototype;
-
- // Create quick reference variables for speed access to core prototypes.
- var push = ArrayProto.push,
- slice = ArrayProto.slice,
- concat = ArrayProto.concat,
- unshift = ArrayProto.unshift,
- toString = ObjProto.toString,
- hasOwnProperty = ObjProto.hasOwnProperty;
-
- // All **ECMAScript 5** native function implementations that we hope to use
- // are declared here.
- var
- nativeForEach = ArrayProto.forEach,
- nativeMap = ArrayProto.map,
- nativeReduce = ArrayProto.reduce,
- nativeReduceRight = ArrayProto.reduceRight,
- nativeFilter = ArrayProto.filter,
- nativeEvery = ArrayProto.every,
- nativeSome = ArrayProto.some,
- nativeIndexOf = ArrayProto.indexOf,
- nativeLastIndexOf = ArrayProto.lastIndexOf,
- nativeIsArray = Array.isArray,
- nativeKeys = Object.keys,
- nativeBind = FuncProto.bind;
-
- // Create a safe reference to the Underscore object for use below.
- var _ = function(obj) {
- if (obj instanceof _) return obj;
- if (!(this instanceof _)) return new _(obj);
- this._wrapped = obj;
- };
-
- // Export the Underscore object for **Node.js**, with
- // backwards-compatibility for the old `require()` API. If we're in
- // the browser, add `_` as a global object via a string identifier,
- // for Closure Compiler "advanced" mode.
- if (typeof exports !== 'undefined') {
- if (typeof module !== 'undefined' && module.exports) {
- exports = module.exports = _;
- }
- exports._ = _;
- } else {
- root['_'] = _;
- }
-
- // Current version.
- _.VERSION = '1.4.2';
-
- // Collection Functions
- // --------------------
-
- // The cornerstone, an `each` implementation, aka `forEach`.
- // Handles objects with the built-in `forEach`, arrays, and raw objects.
- // Delegates to **ECMAScript 5**'s native `forEach` if available.
- var each = _.each = _.forEach = function(obj, iterator, context) {
- if (obj == null) return;
- if (nativeForEach && obj.forEach === nativeForEach) {
- obj.forEach(iterator, context);
- } else if (obj.length === +obj.length) {
- for (var i = 0, l = obj.length; i < l; i++) {
- if (iterator.call(context, obj[i], i, obj) === breaker) return;
- }
- } else {
- for (var key in obj) {
- if (_.has(obj, key)) {
- if (iterator.call(context, obj[key], key, obj) === breaker) return;
- }
- }
- }
- };
-
- // Return the results of applying the iterator to each element.
- // Delegates to **ECMAScript 5**'s native `map` if available.
- _.map = _.collect = function(obj, iterator, context) {
- var results = [];
- if (obj == null) return results;
- if (nativeMap && obj.map === nativeMap) return obj.map(iterator, context);
- each(obj, function(value, index, list) {
- results[results.length] = iterator.call(context, value, index, list);
- });
- return results;
- };
-
- // **Reduce** builds up a single result from a list of values, aka `inject`,
- // or `foldl`. Delegates to **ECMAScript 5**'s native `reduce` if available.
- _.reduce = _.foldl = _.inject = function(obj, iterator, memo, context) {
- var initial = arguments.length > 2;
- if (obj == null) obj = [];
- if (nativeReduce && obj.reduce === nativeReduce) {
- if (context) iterator = _.bind(iterator, context);
- return initial ? obj.reduce(iterator, memo) : obj.reduce(iterator);
- }
- each(obj, function(value, index, list) {
- if (!initial) {
- memo = value;
- initial = true;
- } else {
- memo = iterator.call(context, memo, value, index, list);
- }
- });
- if (!initial) throw new TypeError('Reduce of empty array with no initial value');
- return memo;
- };
-
- // The right-associative version of reduce, also known as `foldr`.
- // Delegates to **ECMAScript 5**'s native `reduceRight` if available.
- _.reduceRight = _.foldr = function(obj, iterator, memo, context) {
- var initial = arguments.length > 2;
- if (obj == null) obj = [];
- if (nativeReduceRight && obj.reduceRight === nativeReduceRight) {
- if (context) iterator = _.bind(iterator, context);
- return arguments.length > 2 ? obj.reduceRight(iterator, memo) : obj.reduceRight(iterator);
- }
- var length = obj.length;
- if (length !== +length) {
- var keys = _.keys(obj);
- length = keys.length;
- }
- each(obj, function(value, index, list) {
- index = keys ? keys[--length] : --length;
- if (!initial) {
- memo = obj[index];
- initial = true;
- } else {
- memo = iterator.call(context, memo, obj[index], index, list);
- }
- });
- if (!initial) throw new TypeError('Reduce of empty array with no initial value');
- return memo;
- };
-
- // Return the first value which passes a truth test. Aliased as `detect`.
- _.find = _.detect = function(obj, iterator, context) {
- var result;
- any(obj, function(value, index, list) {
- if (iterator.call(context, value, index, list)) {
- result = value;
- return true;
- }
- });
- return result;
- };
-
- // Return all the elements that pass a truth test.
- // Delegates to **ECMAScript 5**'s native `filter` if available.
- // Aliased as `select`.
- _.filter = _.select = function(obj, iterator, context) {
- var results = [];
- if (obj == null) return results;
- if (nativeFilter && obj.filter === nativeFilter) return obj.filter(iterator, context);
- each(obj, function(value, index, list) {
- if (iterator.call(context, value, index, list)) results[results.length] = value;
- });
- return results;
- };
-
- // Return all the elements for which a truth test fails.
- _.reject = function(obj, iterator, context) {
- var results = [];
- if (obj == null) return results;
- each(obj, function(value, index, list) {
- if (!iterator.call(context, value, index, list)) results[results.length] = value;
- });
- return results;
- };
-
- // Determine whether all of the elements match a truth test.
- // Delegates to **ECMAScript 5**'s native `every` if available.
- // Aliased as `all`.
- _.every = _.all = function(obj, iterator, context) {
- iterator || (iterator = _.identity);
- var result = true;
- if (obj == null) return result;
- if (nativeEvery && obj.every === nativeEvery) return obj.every(iterator, context);
- each(obj, function(value, index, list) {
- if (!(result = result && iterator.call(context, value, index, list))) return breaker;
- });
- return !!result;
- };
-
- // Determine if at least one element in the object matches a truth test.
- // Delegates to **ECMAScript 5**'s native `some` if available.
- // Aliased as `any`.
- var any = _.some = _.any = function(obj, iterator, context) {
- iterator || (iterator = _.identity);
- var result = false;
- if (obj == null) return result;
- if (nativeSome && obj.some === nativeSome) return obj.some(iterator, context);
- each(obj, function(value, index, list) {
- if (result || (result = iterator.call(context, value, index, list))) return breaker;
- });
- return !!result;
- };
-
- // Determine if the array or object contains a given value (using `===`).
- // Aliased as `include`.
- _.contains = _.include = function(obj, target) {
- var found = false;
- if (obj == null) return found;
- if (nativeIndexOf && obj.indexOf === nativeIndexOf) return obj.indexOf(target) != -1;
- found = any(obj, function(value) {
- return value === target;
- });
- return found;
- };
-
- // Invoke a method (with arguments) on every item in a collection.
- _.invoke = function(obj, method) {
- var args = slice.call(arguments, 2);
- return _.map(obj, function(value) {
- return (_.isFunction(method) ? method : value[method]).apply(value, args);
- });
- };
-
- // Convenience version of a common use case of `map`: fetching a property.
- _.pluck = function(obj, key) {
- return _.map(obj, function(value){ return value[key]; });
- };
-
- // Convenience version of a common use case of `filter`: selecting only objects
- // with specific `key:value` pairs.
- _.where = function(obj, attrs) {
- if (_.isEmpty(attrs)) return [];
- return _.filter(obj, function(value) {
- for (var key in attrs) {
- if (attrs[key] !== value[key]) return false;
- }
- return true;
- });
- };
-
- // Return the maximum element or (element-based computation).
- // Can't optimize arrays of integers longer than 65,535 elements.
- // See: https://bugs.webkit.org/show_bug.cgi?id=80797
- _.max = function(obj, iterator, context) {
- if (!iterator && _.isArray(obj) && obj[0] === +obj[0] && obj.length < 65535) {
- return Math.max.apply(Math, obj);
- }
- if (!iterator && _.isEmpty(obj)) return -Infinity;
- var result = {computed : -Infinity};
- each(obj, function(value, index, list) {
- var computed = iterator ? iterator.call(context, value, index, list) : value;
- computed >= result.computed && (result = {value : value, computed : computed});
- });
- return result.value;
- };
-
- // Return the minimum element (or element-based computation).
- _.min = function(obj, iterator, context) {
- if (!iterator && _.isArray(obj) && obj[0] === +obj[0] && obj.length < 65535) {
- return Math.min.apply(Math, obj);
- }
- if (!iterator && _.isEmpty(obj)) return Infinity;
- var result = {computed : Infinity};
- each(obj, function(value, index, list) {
- var computed = iterator ? iterator.call(context, value, index, list) : value;
- computed < result.computed && (result = {value : value, computed : computed});
- });
- return result.value;
- };
-
- // Shuffle an array.
- _.shuffle = function(obj) {
- var rand;
- var index = 0;
- var shuffled = [];
- each(obj, function(value) {
- rand = _.random(index++);
- shuffled[index - 1] = shuffled[rand];
- shuffled[rand] = value;
- });
- return shuffled;
- };
-
- // An internal function to generate lookup iterators.
- var lookupIterator = function(value) {
- return _.isFunction(value) ? value : function(obj){ return obj[value]; };
- };
-
- // Sort the object's values by a criterion produced by an iterator.
- _.sortBy = function(obj, value, context) {
- var iterator = lookupIterator(value);
- return _.pluck(_.map(obj, function(value, index, list) {
- return {
- value : value,
- index : index,
- criteria : iterator.call(context, value, index, list)
- };
- }).sort(function(left, right) {
- var a = left.criteria;
- var b = right.criteria;
- if (a !== b) {
- if (a > b || a === void 0) return 1;
- if (a < b || b === void 0) return -1;
- }
- return left.index < right.index ? -1 : 1;
- }), 'value');
- };
-
- // An internal function used for aggregate "group by" operations.
- var group = function(obj, value, context, behavior) {
- var result = {};
- var iterator = lookupIterator(value);
- each(obj, function(value, index) {
- var key = iterator.call(context, value, index, obj);
- behavior(result, key, value);
- });
- return result;
- };
-
- // Groups the object's values by a criterion. Pass either a string attribute
- // to group by, or a function that returns the criterion.
- _.groupBy = function(obj, value, context) {
- return group(obj, value, context, function(result, key, value) {
- (_.has(result, key) ? result[key] : (result[key] = [])).push(value);
- });
- };
-
- // Counts instances of an object that group by a certain criterion. Pass
- // either a string attribute to count by, or a function that returns the
- // criterion.
- _.countBy = function(obj, value, context) {
- return group(obj, value, context, function(result, key, value) {
- if (!_.has(result, key)) result[key] = 0;
- result[key]++;
- });
- };
-
- // Use a comparator function to figure out the smallest index at which
- // an object should be inserted so as to maintain order. Uses binary search.
- _.sortedIndex = function(array, obj, iterator, context) {
- iterator = iterator == null ? _.identity : lookupIterator(iterator);
- var value = iterator.call(context, obj);
- var low = 0, high = array.length;
- while (low < high) {
- var mid = (low + high) >>> 1;
- iterator.call(context, array[mid]) < value ? low = mid + 1 : high = mid;
- }
- return low;
- };
-
- // Safely convert anything iterable into a real, live array.
- _.toArray = function(obj) {
- if (!obj) return [];
- if (obj.length === +obj.length) return slice.call(obj);
- return _.values(obj);
- };
-
- // Return the number of elements in an object.
- _.size = function(obj) {
- return (obj.length === +obj.length) ? obj.length : _.keys(obj).length;
- };
-
- // Array Functions
- // ---------------
-
- // Get the first element of an array. Passing **n** will return the first N
- // values in the array. Aliased as `head` and `take`. The **guard** check
- // allows it to work with `_.map`.
- _.first = _.head = _.take = function(array, n, guard) {
- return (n != null) && !guard ? slice.call(array, 0, n) : array[0];
- };
-
- // Returns everything but the last entry of the array. Especially useful on
- // the arguments object. Passing **n** will return all the values in
- // the array, excluding the last N. The **guard** check allows it to work with
- // `_.map`.
- _.initial = function(array, n, guard) {
- return slice.call(array, 0, array.length - ((n == null) || guard ? 1 : n));
- };
-
- // Get the last element of an array. Passing **n** will return the last N
- // values in the array. The **guard** check allows it to work with `_.map`.
- _.last = function(array, n, guard) {
- if ((n != null) && !guard) {
- return slice.call(array, Math.max(array.length - n, 0));
- } else {
- return array[array.length - 1];
- }
- };
-
- // Returns everything but the first entry of the array. Aliased as `tail` and `drop`.
- // Especially useful on the arguments object. Passing an **n** will return
- // the rest N values in the array. The **guard**
- // check allows it to work with `_.map`.
- _.rest = _.tail = _.drop = function(array, n, guard) {
- return slice.call(array, (n == null) || guard ? 1 : n);
- };
-
- // Trim out all falsy values from an array.
- _.compact = function(array) {
- return _.filter(array, function(value){ return !!value; });
- };
-
- // Internal implementation of a recursive `flatten` function.
- var flatten = function(input, shallow, output) {
- each(input, function(value) {
- if (_.isArray(value)) {
- shallow ? push.apply(output, value) : flatten(value, shallow, output);
- } else {
- output.push(value);
- }
- });
- return output;
- };
-
- // Return a completely flattened version of an array.
- _.flatten = function(array, shallow) {
- return flatten(array, shallow, []);
- };
-
- // Return a version of the array that does not contain the specified value(s).
- _.without = function(array) {
- return _.difference(array, slice.call(arguments, 1));
- };
-
- // Produce a duplicate-free version of the array. If the array has already
- // been sorted, you have the option of using a faster algorithm.
- // Aliased as `unique`.
- _.uniq = _.unique = function(array, isSorted, iterator, context) {
- var initial = iterator ? _.map(array, iterator, context) : array;
- var results = [];
- var seen = [];
- each(initial, function(value, index) {
- if (isSorted ? (!index || seen[seen.length - 1] !== value) : !_.contains(seen, value)) {
- seen.push(value);
- results.push(array[index]);
- }
- });
- return results;
- };
-
- // Produce an array that contains the union: each distinct element from all of
- // the passed-in arrays.
- _.union = function() {
- return _.uniq(concat.apply(ArrayProto, arguments));
- };
-
- // Produce an array that contains every item shared between all the
- // passed-in arrays.
- _.intersection = function(array) {
- var rest = slice.call(arguments, 1);
- return _.filter(_.uniq(array), function(item) {
- return _.every(rest, function(other) {
- return _.indexOf(other, item) >= 0;
- });
- });
- };
-
- // Take the difference between one array and a number of other arrays.
- // Only the elements present in just the first array will remain.
- _.difference = function(array) {
- var rest = concat.apply(ArrayProto, slice.call(arguments, 1));
- return _.filter(array, function(value){ return !_.contains(rest, value); });
- };
-
- // Zip together multiple lists into a single array -- elements that share
- // an index go together.
- _.zip = function() {
- var args = slice.call(arguments);
- var length = _.max(_.pluck(args, 'length'));
- var results = new Array(length);
- for (var i = 0; i < length; i++) {
- results[i] = _.pluck(args, "" + i);
- }
- return results;
- };
-
- // Converts lists into objects. Pass either a single array of `[key, value]`
- // pairs, or two parallel arrays of the same length -- one of keys, and one of
- // the corresponding values.
- _.object = function(list, values) {
- var result = {};
- for (var i = 0, l = list.length; i < l; i++) {
- if (values) {
- result[list[i]] = values[i];
- } else {
- result[list[i][0]] = list[i][1];
- }
- }
- return result;
- };
-
- // If the browser doesn't supply us with indexOf (I'm looking at you, **MSIE**),
- // we need this function. Return the position of the first occurrence of an
- // item in an array, or -1 if the item is not included in the array.
- // Delegates to **ECMAScript 5**'s native `indexOf` if available.
- // If the array is large and already in sort order, pass `true`
- // for **isSorted** to use binary search.
- _.indexOf = function(array, item, isSorted) {
- if (array == null) return -1;
- var i = 0, l = array.length;
- if (isSorted) {
- if (typeof isSorted == 'number') {
- i = (isSorted < 0 ? Math.max(0, l + isSorted) : isSorted);
- } else {
- i = _.sortedIndex(array, item);
- return array[i] === item ? i : -1;
- }
- }
- if (nativeIndexOf && array.indexOf === nativeIndexOf) return array.indexOf(item, isSorted);
- for (; i < l; i++) if (array[i] === item) return i;
- return -1;
- };
-
- // Delegates to **ECMAScript 5**'s native `lastIndexOf` if available.
- _.lastIndexOf = function(array, item, from) {
- if (array == null) return -1;
- var hasIndex = from != null;
- if (nativeLastIndexOf && array.lastIndexOf === nativeLastIndexOf) {
- return hasIndex ? array.lastIndexOf(item, from) : array.lastIndexOf(item);
- }
- var i = (hasIndex ? from : array.length);
- while (i--) if (array[i] === item) return i;
- return -1;
- };
-
- // Generate an integer Array containing an arithmetic progression. A port of
- // the native Python `range()` function. See
- // [the Python documentation](http://docs.python.org/library/functions.html#range).
- _.range = function(start, stop, step) {
- if (arguments.length <= 1) {
- stop = start || 0;
- start = 0;
- }
- step = arguments[2] || 1;
-
- var len = Math.max(Math.ceil((stop - start) / step), 0);
- var idx = 0;
- var range = new Array(len);
-
- while(idx < len) {
- range[idx++] = start;
- start += step;
- }
-
- return range;
- };
-
- // Function (ahem) Functions
- // ------------------
-
- // Reusable constructor function for prototype setting.
- var ctor = function(){};
-
- // Create a function bound to a given object (assigning `this`, and arguments,
- // optionally). Binding with arguments is also known as `curry`.
- // Delegates to **ECMAScript 5**'s native `Function.bind` if available.
- // We check for `func.bind` first, to fail fast when `func` is undefined.
- _.bind = function bind(func, context) {
- var bound, args;
- if (func.bind === nativeBind && nativeBind) return nativeBind.apply(func, slice.call(arguments, 1));
- if (!_.isFunction(func)) throw new TypeError;
- args = slice.call(arguments, 2);
- return bound = function() {
- if (!(this instanceof bound)) return func.apply(context, args.concat(slice.call(arguments)));
- ctor.prototype = func.prototype;
- var self = new ctor;
- var result = func.apply(self, args.concat(slice.call(arguments)));
- if (Object(result) === result) return result;
- return self;
- };
- };
-
- // Bind all of an object's methods to that object. Useful for ensuring that
- // all callbacks defined on an object belong to it.
- _.bindAll = function(obj) {
- var funcs = slice.call(arguments, 1);
- if (funcs.length == 0) funcs = _.functions(obj);
- each(funcs, function(f) { obj[f] = _.bind(obj[f], obj); });
- return obj;
- };
-
- // Memoize an expensive function by storing its results.
- _.memoize = function(func, hasher) {
- var memo = {};
- hasher || (hasher = _.identity);
- return function() {
- var key = hasher.apply(this, arguments);
- return _.has(memo, key) ? memo[key] : (memo[key] = func.apply(this, arguments));
- };
- };
-
- // Delays a function for the given number of milliseconds, and then calls
- // it with the arguments supplied.
- _.delay = function(func, wait) {
- var args = slice.call(arguments, 2);
- return setTimeout(function(){ return func.apply(null, args); }, wait);
- };
-
- // Defers a function, scheduling it to run after the current call stack has
- // cleared.
- _.defer = function(func) {
- return _.delay.apply(_, [func, 1].concat(slice.call(arguments, 1)));
- };
-
- // Returns a function, that, when invoked, will only be triggered at most once
- // during a given window of time.
- _.throttle = function(func, wait) {
- var context, args, timeout, throttling, more, result;
- var whenDone = _.debounce(function(){ more = throttling = false; }, wait);
- return function() {
- context = this; args = arguments;
- var later = function() {
- timeout = null;
- if (more) {
- result = func.apply(context, args);
- }
- whenDone();
- };
- if (!timeout) timeout = setTimeout(later, wait);
- if (throttling) {
- more = true;
- } else {
- throttling = true;
- result = func.apply(context, args);
- }
- whenDone();
- return result;
- };
- };
-
- // Returns a function, that, as long as it continues to be invoked, will not
- // be triggered. The function will be called after it stops being called for
- // N milliseconds. If `immediate` is passed, trigger the function on the
- // leading edge, instead of the trailing.
- _.debounce = function(func, wait, immediate) {
- var timeout, result;
- return function() {
- var context = this, args = arguments;
- var later = function() {
- timeout = null;
- if (!immediate) result = func.apply(context, args);
- };
- var callNow = immediate && !timeout;
- clearTimeout(timeout);
- timeout = setTimeout(later, wait);
- if (callNow) result = func.apply(context, args);
- return result;
- };
- };
-
- // Returns a function that will be executed at most one time, no matter how
- // often you call it. Useful for lazy initialization.
- _.once = function(func) {
- var ran = false, memo;
- return function() {
- if (ran) return memo;
- ran = true;
- memo = func.apply(this, arguments);
- func = null;
- return memo;
- };
- };
-
- // Returns the first function passed as an argument to the second,
- // allowing you to adjust arguments, run code before and after, and
- // conditionally execute the original function.
- _.wrap = function(func, wrapper) {
- return function() {
- var args = [func];
- push.apply(args, arguments);
- return wrapper.apply(this, args);
- };
- };
-
- // Returns a function that is the composition of a list of functions, each
- // consuming the return value of the function that follows.
- _.compose = function() {
- var funcs = arguments;
- return function() {
- var args = arguments;
- for (var i = funcs.length - 1; i >= 0; i--) {
- args = [funcs[i].apply(this, args)];
- }
- return args[0];
- };
- };
-
- // Returns a function that will only be executed after being called N times.
- _.after = function(times, func) {
- if (times <= 0) return func();
- return function() {
- if (--times < 1) {
- return func.apply(this, arguments);
- }
- };
- };
-
- // Object Functions
- // ----------------
-
- // Retrieve the names of an object's properties.
- // Delegates to **ECMAScript 5**'s native `Object.keys`
- _.keys = nativeKeys || function(obj) {
- if (obj !== Object(obj)) throw new TypeError('Invalid object');
- var keys = [];
- for (var key in obj) if (_.has(obj, key)) keys[keys.length] = key;
- return keys;
- };
-
- // Retrieve the values of an object's properties.
- _.values = function(obj) {
- var values = [];
- for (var key in obj) if (_.has(obj, key)) values.push(obj[key]);
- return values;
- };
-
- // Convert an object into a list of `[key, value]` pairs.
- _.pairs = function(obj) {
- var pairs = [];
- for (var key in obj) if (_.has(obj, key)) pairs.push([key, obj[key]]);
- return pairs;
- };
-
- // Invert the keys and values of an object. The values must be serializable.
- _.invert = function(obj) {
- var result = {};
- for (var key in obj) if (_.has(obj, key)) result[obj[key]] = key;
- return result;
- };
-
- // Return a sorted list of the function names available on the object.
- // Aliased as `methods`
- _.functions = _.methods = function(obj) {
- var names = [];
- for (var key in obj) {
- if (_.isFunction(obj[key])) names.push(key);
- }
- return names.sort();
- };
-
- // Extend a given object with all the properties in passed-in object(s).
- _.extend = function(obj) {
- each(slice.call(arguments, 1), function(source) {
- for (var prop in source) {
- obj[prop] = source[prop];
- }
- });
- return obj;
- };
-
- // Return a copy of the object only containing the whitelisted properties.
- _.pick = function(obj) {
- var copy = {};
- var keys = concat.apply(ArrayProto, slice.call(arguments, 1));
- each(keys, function(key) {
- if (key in obj) copy[key] = obj[key];
- });
- return copy;
- };
-
- // Return a copy of the object without the blacklisted properties.
- _.omit = function(obj) {
- var copy = {};
- var keys = concat.apply(ArrayProto, slice.call(arguments, 1));
- for (var key in obj) {
- if (!_.contains(keys, key)) copy[key] = obj[key];
- }
- return copy;
- };
-
- // Fill in a given object with default properties.
- _.defaults = function(obj) {
- each(slice.call(arguments, 1), function(source) {
- for (var prop in source) {
- if (obj[prop] == null) obj[prop] = source[prop];
- }
- });
- return obj;
- };
-
- // Create a (shallow-cloned) duplicate of an object.
- _.clone = function(obj) {
- if (!_.isObject(obj)) return obj;
- return _.isArray(obj) ? obj.slice() : _.extend({}, obj);
- };
-
- // Invokes interceptor with the obj, and then returns obj.
- // The primary purpose of this method is to "tap into" a method chain, in
- // order to perform operations on intermediate results within the chain.
- _.tap = function(obj, interceptor) {
- interceptor(obj);
- return obj;
- };
-
- // Internal recursive comparison function for `isEqual`.
- var eq = function(a, b, aStack, bStack) {
- // Identical objects are equal. `0 === -0`, but they aren't identical.
- // See the Harmony `egal` proposal: http://wiki.ecmascript.org/doku.php?id=harmony:egal.
- if (a === b) return a !== 0 || 1 / a == 1 / b;
- // A strict comparison is necessary because `null == undefined`.
- if (a == null || b == null) return a === b;
- // Unwrap any wrapped objects.
- if (a instanceof _) a = a._wrapped;
- if (b instanceof _) b = b._wrapped;
- // Compare `[[Class]]` names.
- var className = toString.call(a);
- if (className != toString.call(b)) return false;
- switch (className) {
- // Strings, numbers, dates, and booleans are compared by value.
- case '[object String]':
- // Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is
- // equivalent to `new String("5")`.
- return a == String(b);
- case '[object Number]':
- // `NaN`s are equivalent, but non-reflexive. An `egal` comparison is performed for
- // other numeric values.
- return a != +a ? b != +b : (a == 0 ? 1 / a == 1 / b : a == +b);
- case '[object Date]':
- case '[object Boolean]':
- // Coerce dates and booleans to numeric primitive values. Dates are compared by their
- // millisecond representations. Note that invalid dates with millisecond representations
- // of `NaN` are not equivalent.
- return +a == +b;
- // RegExps are compared by their source patterns and flags.
- case '[object RegExp]':
- return a.source == b.source &&
- a.global == b.global &&
- a.multiline == b.multiline &&
- a.ignoreCase == b.ignoreCase;
- }
- if (typeof a != 'object' || typeof b != 'object') return false;
- // Assume equality for cyclic structures. The algorithm for detecting cyclic
- // structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`.
- var length = aStack.length;
- while (length--) {
- // Linear search. Performance is inversely proportional to the number of
- // unique nested structures.
- if (aStack[length] == a) return bStack[length] == b;
- }
- // Add the first object to the stack of traversed objects.
- aStack.push(a);
- bStack.push(b);
- var size = 0, result = true;
- // Recursively compare objects and arrays.
- if (className == '[object Array]') {
- // Compare array lengths to determine if a deep comparison is necessary.
- size = a.length;
- result = size == b.length;
- if (result) {
- // Deep compare the contents, ignoring non-numeric properties.
- while (size--) {
- if (!(result = eq(a[size], b[size], aStack, bStack))) break;
- }
- }
- } else {
- // Objects with different constructors are not equivalent, but `Object`s
- // from different frames are.
- var aCtor = a.constructor, bCtor = b.constructor;
- if (aCtor !== bCtor && !(_.isFunction(aCtor) && (aCtor instanceof aCtor) &&
- _.isFunction(bCtor) && (bCtor instanceof bCtor))) {
- return false;
- }
- // Deep compare objects.
- for (var key in a) {
- if (_.has(a, key)) {
- // Count the expected number of properties.
- size++;
- // Deep compare each member.
- if (!(result = _.has(b, key) && eq(a[key], b[key], aStack, bStack))) break;
- }
- }
- // Ensure that both objects contain the same number of properties.
- if (result) {
- for (key in b) {
- if (_.has(b, key) && !(size--)) break;
- }
- result = !size;
- }
- }
- // Remove the first object from the stack of traversed objects.
- aStack.pop();
- bStack.pop();
- return result;
- };
-
- // Perform a deep comparison to check if two objects are equal.
- _.isEqual = function(a, b) {
- return eq(a, b, [], []);
- };
-
- // Is a given array, string, or object empty?
- // An "empty" object has no enumerable own-properties.
- _.isEmpty = function(obj) {
- if (obj == null) return true;
- if (_.isArray(obj) || _.isString(obj)) return obj.length === 0;
- for (var key in obj) if (_.has(obj, key)) return false;
- return true;
- };
-
- // Is a given value a DOM element?
- _.isElement = function(obj) {
- return !!(obj && obj.nodeType === 1);
- };
-
- // Is a given value an array?
- // Delegates to ECMA5's native Array.isArray
- _.isArray = nativeIsArray || function(obj) {
- return toString.call(obj) == '[object Array]';
- };
-
- // Is a given variable an object?
- _.isObject = function(obj) {
- return obj === Object(obj);
- };
-
- // Add some isType methods: isArguments, isFunction, isString, isNumber, isDate, isRegExp.
- each(['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp'], function(name) {
- _['is' + name] = function(obj) {
- return toString.call(obj) == '[object ' + name + ']';
- };
- });
-
- // Define a fallback version of the method in browsers (ahem, IE), where
- // there isn't any inspectable "Arguments" type.
- if (!_.isArguments(arguments)) {
- _.isArguments = function(obj) {
- return !!(obj && _.has(obj, 'callee'));
- };
- }
-
- // Optimize `isFunction` if appropriate.
- if (typeof (/./) !== 'function') {
- _.isFunction = function(obj) {
- return typeof obj === 'function';
- };
- }
-
- // Is a given object a finite number?
- _.isFinite = function(obj) {
- return _.isNumber(obj) && isFinite(obj);
- };
-
- // Is the given value `NaN`? (NaN is the only number which does not equal itself).
- _.isNaN = function(obj) {
- return _.isNumber(obj) && obj != +obj;
- };
-
- // Is a given value a boolean?
- _.isBoolean = function(obj) {
- return obj === true || obj === false || toString.call(obj) == '[object Boolean]';
- };
-
- // Is a given value equal to null?
- _.isNull = function(obj) {
- return obj === null;
- };
-
- // Is a given variable undefined?
- _.isUndefined = function(obj) {
- return obj === void 0;
- };
-
- // Shortcut function for checking if an object has a given property directly
- // on itself (in other words, not on a prototype).
- _.has = function(obj, key) {
- return hasOwnProperty.call(obj, key);
- };
-
- // Utility Functions
- // -----------------
-
- // Run Underscore.js in *noConflict* mode, returning the `_` variable to its
- // previous owner. Returns a reference to the Underscore object.
- _.noConflict = function() {
- root._ = previousUnderscore;
- return this;
- };
-
- // Keep the identity function around for default iterators.
- _.identity = function(value) {
- return value;
- };
-
- // Run a function **n** times.
- _.times = function(n, iterator, context) {
- for (var i = 0; i < n; i++) iterator.call(context, i);
- };
-
- // Return a random integer between min and max (inclusive).
- _.random = function(min, max) {
- if (max == null) {
- max = min;
- min = 0;
- }
- return min + (0 | Math.random() * (max - min + 1));
- };
-
- // List of HTML entities for escaping.
- var entityMap = {
- escape: {
- '&': '&amp;',
- '<': '&lt;',
- '>': '&gt;',
- '"': '&quot;',
- "'": '&#x27;',
- '/': '&#x2F;'
- }
- };
- entityMap.unescape = _.invert(entityMap.escape);
-
- // Regexes containing the keys and values listed immediately above.
- var entityRegexes = {
- escape: new RegExp('[' + _.keys(entityMap.escape).join('') + ']', 'g'),
- unescape: new RegExp('(' + _.keys(entityMap.unescape).join('|') + ')', 'g')
- };
-
- // Functions for escaping and unescaping strings to/from HTML interpolation.
- _.each(['escape', 'unescape'], function(method) {
- _[method] = function(string) {
- if (string == null) return '';
- return ('' + string).replace(entityRegexes[method], function(match) {
- return entityMap[method][match];
- });
- };
- });
-
- // If the value of the named property is a function then invoke it;
- // otherwise, return it.
- _.result = function(object, property) {
- if (object == null) return null;
- var value = object[property];
- return _.isFunction(value) ? value.call(object) : value;
- };
-
- // Add your own custom functions to the Underscore object.
- _.mixin = function(obj) {
- each(_.functions(obj), function(name){
- var func = _[name] = obj[name];
- _.prototype[name] = function() {
- var args = [this._wrapped];
- push.apply(args, arguments);
- return result.call(this, func.apply(_, args));
- };
- });
- };
-
- // Generate a unique integer id (unique within the entire client session).
- // Useful for temporary DOM ids.
- var idCounter = 0;
- _.uniqueId = function(prefix) {
- var id = idCounter++;
- return prefix ? prefix + id : id;
- };
-
- // By default, Underscore uses ERB-style template delimiters, change the
- // following template settings to use alternative delimiters.
- _.templateSettings = {
- evaluate : /<%([\s\S]+?)%>/g,
- interpolate : /<%=([\s\S]+?)%>/g,
- escape : /<%-([\s\S]+?)%>/g
- };
-
- // When customizing `templateSettings`, if you don't want to define an
- // interpolation, evaluation or escaping regex, we need one that is
- // guaranteed not to match.
- var noMatch = /(.)^/;
-
- // Certain characters need to be escaped so that they can be put into a
- // string literal.
- var escapes = {
- "'": "'",
- '\\': '\\',
- '\r': 'r',
- '\n': 'n',
- '\t': 't',
- '\u2028': 'u2028',
- '\u2029': 'u2029'
- };
-