vulcan 0.6.1 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/vulcan/cli.rb +20 -10
- data/lib/vulcan/version.rb +1 -1
- data/server/Procfile +1 -1
- data/server/bin/web +10 -0
- data/server/lib/cloudant.coffee +20 -0
- data/server/lib/logger.coffee +17 -0
- data/server/lib/on.coffee +10 -0
- data/server/lib/spawner.coffee +65 -0
- data/server/node_modules/coffee-script/CNAME +1 -0
- data/server/node_modules/coffee-script/LICENSE +22 -0
- data/server/node_modules/coffee-script/README +51 -0
- data/server/node_modules/coffee-script/Rakefile +78 -0
- data/server/node_modules/coffee-script/bin/cake +7 -0
- data/server/node_modules/coffee-script/bin/coffee +7 -0
- data/server/node_modules/coffee-script/extras/jsl.conf +44 -0
- data/server/node_modules/coffee-script/lib/coffee-script/browser.js +92 -0
- data/server/node_modules/coffee-script/lib/coffee-script/cake.js +111 -0
- data/server/node_modules/coffee-script/lib/coffee-script/coffee-script.js +167 -0
- data/server/node_modules/coffee-script/lib/coffee-script/command.js +500 -0
- data/server/node_modules/coffee-script/lib/coffee-script/grammar.js +606 -0
- data/server/node_modules/coffee-script/lib/coffee-script/helpers.js +77 -0
- data/server/node_modules/coffee-script/lib/coffee-script/index.js +11 -0
- data/server/node_modules/coffee-script/lib/coffee-script/lexer.js +788 -0
- data/server/node_modules/coffee-script/lib/coffee-script/nodes.js +2986 -0
- data/server/node_modules/coffee-script/lib/coffee-script/optparse.js +138 -0
- data/server/node_modules/coffee-script/lib/coffee-script/parser.js +683 -0
- data/server/node_modules/coffee-script/lib/coffee-script/repl.js +261 -0
- data/server/node_modules/coffee-script/lib/coffee-script/rewriter.js +349 -0
- data/server/node_modules/coffee-script/lib/coffee-script/scope.js +146 -0
- data/server/node_modules/coffee-script/package.json +55 -0
- data/server/node_modules/connect-form/History.md +0 -6
- data/server/node_modules/connect-form/lib/connect-form.js +2 -4
- data/server/node_modules/connect-form/node_modules/formidable/Readme.md +64 -36
- data/server/node_modules/connect-form/node_modules/formidable/lib/incoming_form.js +5 -1
- data/server/node_modules/connect-form/node_modules/formidable/package.json +20 -6
- data/server/node_modules/connect-form/node_modules/formidable/test/common.js +5 -6
- data/server/node_modules/connect-form/node_modules/formidable/test/fixture/file/funkyfilename.txt +1 -0
- data/server/node_modules/connect-form/node_modules/formidable/test/fixture/js/special-chars-in-filename.js +1 -1
- data/server/node_modules/connect-form/node_modules/formidable/test/{slow → integration}/test-fixtures.js +38 -33
- data/server/node_modules/connect-form/node_modules/formidable/test/legacy/simple/test-incoming-form.js +11 -0
- data/server/node_modules/connect-form/node_modules/formidable/test/run.js +1 -6
- data/server/node_modules/connect-form/node_modules/formidable/test/unit/test-incoming-form.js +63 -0
- data/server/node_modules/connect-form/package.json +27 -5
- data/server/node_modules/cradle/README.md +10 -10
- data/server/node_modules/cradle/lib/cradle.js +117 -523
- data/server/node_modules/cradle/lib/cradle/database/attachments.js +120 -0
- data/server/node_modules/cradle/lib/cradle/database/changes.js +56 -0
- data/server/node_modules/cradle/lib/cradle/database/documents.js +215 -0
- data/server/node_modules/cradle/lib/cradle/database/index.js +65 -0
- data/server/node_modules/cradle/lib/cradle/database/views.js +125 -0
- data/server/node_modules/cradle/node_modules/follow/LICENSE +202 -0
- data/server/node_modules/cradle/node_modules/follow/README.md +164 -0
- data/server/node_modules/cradle/node_modules/follow/Rakefile +54 -0
- data/server/node_modules/cradle/node_modules/follow/api.js +35 -0
- data/server/node_modules/cradle/node_modules/follow/browser/eventemitter2.js +453 -0
- data/server/node_modules/cradle/node_modules/follow/browser/export.js +78 -0
- data/server/node_modules/cradle/node_modules/follow/browser/index.html +14 -0
- data/server/node_modules/cradle/node_modules/follow/browser/jquery-1.6.1.min.js +18 -0
- data/server/node_modules/cradle/node_modules/follow/browser/log4js.js +46 -0
- data/server/node_modules/cradle/node_modules/follow/browser/main.js +92 -0
- data/server/node_modules/cradle/node_modules/follow/browser/querystring.js +28 -0
- data/server/node_modules/cradle/node_modules/follow/browser/request.jquery.js +237 -0
- data/server/node_modules/cradle/node_modules/follow/browser/require.js +33 -0
- data/server/node_modules/cradle/node_modules/follow/browser/util.js +28 -0
- data/server/node_modules/cradle/node_modules/follow/cli.js +101 -0
- data/server/node_modules/cradle/node_modules/follow/lib/feed.js +556 -0
- data/server/node_modules/cradle/node_modules/follow/lib/index.js +66 -0
- data/server/node_modules/cradle/node_modules/follow/lib/stream.js +305 -0
- data/server/node_modules/cradle/node_modules/follow/node_modules/request/LICENSE +55 -0
- data/server/node_modules/cradle/node_modules/follow/node_modules/request/README.md +285 -0
- data/server/node_modules/cradle/node_modules/follow/node_modules/request/main.js +618 -0
- data/server/node_modules/cradle/node_modules/follow/node_modules/request/mimetypes.js +146 -0
- data/server/node_modules/cradle/node_modules/follow/node_modules/request/oauth.js +34 -0
- data/server/node_modules/cradle/node_modules/follow/node_modules/request/package.json +42 -0
- data/server/node_modules/cradle/node_modules/follow/node_modules/request/tests/googledoodle.png +0 -0
- data/server/node_modules/cradle/node_modules/follow/node_modules/request/tests/run.sh +6 -0
- data/server/node_modules/cradle/node_modules/follow/node_modules/request/tests/server.js +57 -0
- data/server/node_modules/cradle/node_modules/follow/node_modules/request/tests/test-body.js +90 -0
- data/server/node_modules/cradle/node_modules/follow/node_modules/request/tests/test-cookie.js +29 -0
- data/server/node_modules/cradle/node_modules/follow/node_modules/request/tests/test-cookiejar.js +90 -0
- data/server/node_modules/cradle/node_modules/follow/node_modules/request/tests/test-errors.js +30 -0
- data/server/node_modules/cradle/node_modules/follow/node_modules/request/tests/test-oauth.js +109 -0
- data/server/node_modules/cradle/node_modules/follow/node_modules/request/tests/test-pipes.js +167 -0
- data/server/node_modules/cradle/node_modules/follow/node_modules/request/tests/test-proxy.js +39 -0
- data/server/node_modules/cradle/node_modules/follow/node_modules/request/tests/test-timeout.js +87 -0
- data/server/node_modules/cradle/node_modules/follow/node_modules/request/uuid.js +19 -0
- data/server/node_modules/cradle/node_modules/follow/node_modules/request/vendor/cookie/index.js +55 -0
- data/server/node_modules/cradle/node_modules/follow/node_modules/request/vendor/cookie/jar.js +72 -0
- data/server/node_modules/cradle/node_modules/follow/package.json +45 -0
- data/server/node_modules/cradle/node_modules/follow/test/couch.js +153 -0
- data/server/node_modules/cradle/node_modules/follow/test/follow.js +136 -0
- data/server/node_modules/cradle/node_modules/follow/test/issues.js +178 -0
- data/server/node_modules/cradle/node_modules/follow/test/issues/10.js +24 -0
- data/server/node_modules/cradle/node_modules/follow/test/stream.js +493 -0
- data/server/node_modules/cradle/node_modules/request/LICENSE +55 -0
- data/server/node_modules/cradle/node_modules/request/README.md +287 -0
- data/server/node_modules/cradle/node_modules/request/forever.js +103 -0
- data/server/node_modules/cradle/node_modules/request/main.js +913 -0
- data/server/node_modules/cradle/node_modules/request/mimetypes.js +152 -0
- data/server/node_modules/cradle/node_modules/request/oauth.js +34 -0
- data/server/node_modules/cradle/node_modules/request/package.json +42 -0
- data/server/node_modules/cradle/node_modules/request/tests/googledoodle.png +0 -0
- data/server/node_modules/cradle/node_modules/request/tests/run.js +38 -0
- data/server/node_modules/cradle/node_modules/request/tests/server.js +82 -0
- data/server/node_modules/cradle/node_modules/request/tests/squid.conf +77 -0
- data/server/node_modules/cradle/node_modules/request/tests/ssl/ca/ca.cnf +20 -0
- data/server/node_modules/cradle/node_modules/request/tests/ssl/ca/ca.crl +0 -0
- data/server/node_modules/cradle/node_modules/request/tests/ssl/ca/ca.crt +17 -0
- data/server/node_modules/cradle/node_modules/request/tests/ssl/ca/ca.csr +13 -0
- data/server/node_modules/cradle/node_modules/request/tests/ssl/ca/ca.key +18 -0
- data/server/node_modules/cradle/node_modules/request/tests/ssl/ca/ca.srl +1 -0
- data/server/node_modules/cradle/node_modules/request/tests/ssl/ca/server.cnf +19 -0
- data/server/node_modules/cradle/node_modules/request/tests/ssl/ca/server.crt +16 -0
- data/server/node_modules/cradle/node_modules/request/tests/ssl/ca/server.csr +11 -0
- data/server/node_modules/cradle/node_modules/request/tests/ssl/ca/server.js +28 -0
- data/server/node_modules/cradle/node_modules/request/tests/ssl/ca/server.key +9 -0
- data/server/node_modules/cradle/node_modules/request/tests/ssl/npm-ca.crt +16 -0
- data/server/node_modules/cradle/node_modules/request/tests/ssl/test.crt +15 -0
- data/server/node_modules/cradle/node_modules/request/tests/ssl/test.key +15 -0
- data/server/node_modules/cradle/node_modules/request/tests/test-body.js +95 -0
- data/server/node_modules/cradle/node_modules/request/tests/test-cookie.js +29 -0
- data/server/node_modules/cradle/node_modules/request/tests/test-cookiejar.js +90 -0
- data/server/node_modules/cradle/node_modules/request/tests/test-defaults.js +68 -0
- data/server/node_modules/cradle/node_modules/request/tests/test-errors.js +37 -0
- data/server/node_modules/cradle/node_modules/request/tests/test-headers.js +52 -0
- data/server/node_modules/cradle/node_modules/request/tests/test-httpModule.js +94 -0
- data/server/node_modules/cradle/node_modules/request/tests/test-https-strict.js +97 -0
- data/server/node_modules/cradle/node_modules/request/tests/test-https.js +86 -0
- data/server/node_modules/cradle/node_modules/request/tests/test-oauth.js +117 -0
- data/server/node_modules/cradle/node_modules/request/tests/test-params.js +92 -0
- data/server/node_modules/cradle/node_modules/request/tests/test-pipes.js +202 -0
- data/server/node_modules/cradle/node_modules/request/tests/test-proxy.js +39 -0
- data/server/node_modules/cradle/node_modules/request/tests/test-qs.js +28 -0
- data/server/node_modules/cradle/node_modules/request/tests/test-redirect.js +154 -0
- data/server/node_modules/cradle/node_modules/request/tests/test-timeout.js +87 -0
- data/server/node_modules/cradle/node_modules/request/tests/test-toJSON.js +14 -0
- data/server/node_modules/cradle/node_modules/request/tests/test-tunnel.js +61 -0
- data/server/node_modules/cradle/node_modules/request/tunnel.js +229 -0
- data/server/node_modules/cradle/node_modules/request/uuid.js +19 -0
- data/server/node_modules/cradle/node_modules/request/vendor/cookie/index.js +65 -0
- data/server/node_modules/cradle/node_modules/request/vendor/cookie/jar.js +72 -0
- data/server/node_modules/cradle/node_modules/vargs/package.json +33 -10
- data/server/node_modules/cradle/package.json +50 -12
- data/server/node_modules/cradle/test/cache-test.js +1 -4
- data/server/node_modules/cradle/test/connection-test.js +179 -0
- data/server/node_modules/cradle/test/database-attachment-test.js +344 -0
- data/server/node_modules/cradle/test/database-cache-test.js +132 -0
- data/server/node_modules/cradle/test/database-test.js +219 -0
- data/server/node_modules/cradle/test/database-view-test.js +141 -0
- data/server/node_modules/cradle/test/fixtures/databases.json +28 -1
- data/server/node_modules/cradle/test/helpers/seed.js +14 -5
- data/server/node_modules/cradle/test/response-test.js +1 -1
- data/server/node_modules/express/History.md +16 -0
- data/server/node_modules/express/bin/express +7 -6
- data/server/node_modules/express/lib-cov/application.js +510 -0
- data/server/node_modules/express/lib-cov/express.js +65 -0
- data/server/node_modules/express/lib-cov/middleware.js +54 -0
- data/server/node_modules/express/lib-cov/request.js +225 -0
- data/server/node_modules/express/lib-cov/response.js +611 -0
- data/server/node_modules/express/lib-cov/router/collection.js +40 -0
- data/server/node_modules/express/lib-cov/router/index.js +515 -0
- data/server/node_modules/express/lib-cov/router/methods.js +9 -0
- data/server/node_modules/express/lib-cov/router/route.js +68 -0
- data/server/node_modules/express/lib-cov/utils.js +151 -0
- data/server/node_modules/express/lib-cov/view.js +81 -0
- data/server/node_modules/express/lib/express.js +1 -1
- data/server/node_modules/express/lib/http.js +1 -2
- data/server/node_modules/express/lib/request.js +2 -2
- data/server/node_modules/express/lib/router/methods.js +10 -1
- data/server/node_modules/express/node_modules/connect/lib/connect.js +1 -1
- data/server/node_modules/express/node_modules/connect/lib/http.js +3 -2
- data/server/node_modules/express/node_modules/connect/lib/middleware/limit.js +0 -2
- data/server/node_modules/express/node_modules/connect/lib/middleware/session.js +1 -2
- data/server/node_modules/express/node_modules/connect/node_modules/formidable/Readme.md +42 -25
- data/server/node_modules/express/node_modules/connect/node_modules/formidable/lib/incoming_form.js +1 -0
- data/server/node_modules/express/node_modules/connect/node_modules/formidable/package.json +14 -3
- data/server/node_modules/express/node_modules/connect/node_modules/formidable/test/legacy/simple/test-incoming-form.js +11 -0
- data/server/node_modules/express/node_modules/connect/package.json +35 -7
- data/server/node_modules/express/node_modules/mime/package.json +30 -8
- data/server/node_modules/express/node_modules/mkdirp/README.markdown +35 -2
- data/server/node_modules/express/node_modules/mkdirp/examples/pow.js +1 -1
- data/server/node_modules/express/node_modules/mkdirp/index.js +72 -13
- data/server/node_modules/express/node_modules/mkdirp/package.json +39 -21
- data/server/node_modules/express/node_modules/mkdirp/test/chmod.js +38 -0
- data/server/node_modules/express/node_modules/mkdirp/test/clobber.js +37 -0
- data/server/node_modules/express/node_modules/mkdirp/test/perm.js +32 -0
- data/server/node_modules/express/node_modules/mkdirp/test/perm_sync.js +39 -0
- data/server/node_modules/express/node_modules/mkdirp/test/sync.js +27 -0
- data/server/node_modules/express/node_modules/mkdirp/test/umask.js +28 -0
- data/server/node_modules/express/node_modules/mkdirp/test/umask_sync.js +27 -0
- data/server/node_modules/express/node_modules/qs/History.md +10 -0
- data/server/node_modules/express/node_modules/qs/Readme.md +9 -2
- data/server/node_modules/express/node_modules/qs/examples.js +3 -0
- data/server/node_modules/express/node_modules/qs/lib/querystring.js +8 -6
- data/server/node_modules/express/node_modules/qs/package.json +26 -8
- data/server/node_modules/express/node_modules/qs/test/parse.js +13 -1
- data/server/node_modules/express/node_modules/qs/test/stringify.js +45 -37
- data/server/node_modules/express/package.json +55 -16
- data/server/node_modules/express/test.js +41 -0
- data/server/node_modules/knox/package.json +26 -4
- data/server/node_modules/node-uuid/package.json +40 -11
- data/server/node_modules/node-uuid/test/test.js +1 -1
- data/server/node_modules/nodemon/README.md +120 -0
- data/server/node_modules/nodemon/nodemon.js +518 -0
- data/server/node_modules/nodemon/nodemonignore.example +11 -0
- data/server/node_modules/nodemon/package.json +49 -0
- data/server/node_modules/restler/README.md +144 -94
- data/server/node_modules/restler/lib/multipartform.js +2 -0
- data/server/node_modules/restler/lib/restler.js +218 -61
- data/server/node_modules/restler/package.json +35 -8
- data/server/node_modules/restler/test/all.js +6 -1
- data/server/node_modules/restler/test/restler.js +624 -118
- data/server/package.json +14 -10
- data/server/web.coffee +64 -0
- data/server/web.js +15 -3
- metadata +170 -57
- data/server/index.js +0 -14
- data/server/node_modules/connect-form/LICENSE +0 -22
- data/server/node_modules/connect-form/index.js +0 -100
- data/server/node_modules/connect-form/node_modules/formidable/test/fast/test-incoming-form.js +0 -45
- data/server/node_modules/connect-form/node_modules/formidable/test/fixture/http/no-filename/generic.http +0 -13
- data/server/node_modules/connect-form/node_modules/formidable/test/fixture/http/special-chars-in-filename/osx-chrome-13.http +0 -26
- data/server/node_modules/connect-form/node_modules/formidable/test/fixture/http/special-chars-in-filename/osx-firefox-3.6.http +0 -24
- data/server/node_modules/connect-form/node_modules/formidable/test/fixture/http/special-chars-in-filename/osx-safari-5.http +0 -23
- data/server/node_modules/connect-form/node_modules/formidable/test/fixture/http/special-chars-in-filename/xp-chrome-12.http +0 -24
- data/server/node_modules/connect-form/node_modules/formidable/test/fixture/http/special-chars-in-filename/xp-ie-7.http +0 -22
- data/server/node_modules/connect-form/node_modules/formidable/test/fixture/http/special-chars-in-filename/xp-ie-8.http +0 -22
- data/server/node_modules/connect-form/node_modules/formidable/test/fixture/http/special-chars-in-filename/xp-safari-5.http +0 -22
- data/server/node_modules/connect-form/node_modules/formidable/test/fixture/multi_video.upload +0 -0
- data/server/node_modules/cradle/Makefile +0 -10
- data/server/node_modules/cradle/test/cradle-test.js +0 -650
- data/server/node_modules/express/node_modules/connect/node_modules/formidable/test/fixture/http/no-filename/generic.http +0 -13
- data/server/node_modules/express/node_modules/connect/node_modules/formidable/test/fixture/http/special-chars-in-filename/osx-chrome-13.http +0 -26
- data/server/node_modules/express/node_modules/connect/node_modules/formidable/test/fixture/http/special-chars-in-filename/osx-firefox-3.6.http +0 -24
- data/server/node_modules/express/node_modules/connect/node_modules/formidable/test/fixture/http/special-chars-in-filename/osx-safari-5.http +0 -23
- data/server/node_modules/express/node_modules/connect/node_modules/formidable/test/fixture/http/special-chars-in-filename/xp-chrome-12.http +0 -24
- data/server/node_modules/express/node_modules/connect/node_modules/formidable/test/fixture/http/special-chars-in-filename/xp-ie-7.http +0 -22
- data/server/node_modules/express/node_modules/connect/node_modules/formidable/test/fixture/http/special-chars-in-filename/xp-ie-8.http +0 -22
- data/server/node_modules/express/node_modules/connect/node_modules/formidable/test/fixture/http/special-chars-in-filename/xp-safari-5.http +0 -22
- data/server/node_modules/express/node_modules/connect/node_modules/formidable/test/fixture/multi_video.upload +0 -0
- data/server/node_modules/express/node_modules/connect/test.js +0 -52
- data/server/node_modules/express/testing/foo/app.js +0 -35
- data/server/node_modules/express/testing/foo/package.json +0 -9
- data/server/node_modules/express/testing/foo/public/stylesheets/style.css +0 -8
- data/server/node_modules/express/testing/foo/routes/index.js +0 -10
- data/server/node_modules/express/testing/foo/views/index.jade +0 -2
- data/server/node_modules/express/testing/foo/views/layout.jade +0 -6
- data/server/node_modules/express/testing/index.js +0 -43
- data/server/node_modules/express/testing/public/test.txt +0 -2971
- data/server/node_modules/express/testing/views/page.html +0 -1
- data/server/node_modules/express/testing/views/page.jade +0 -3
- data/server/node_modules/express/testing/views/test.md +0 -1
- data/server/node_modules/express/testing/views/user/index.jade +0 -1
- data/server/node_modules/express/testing/views/user/list.jade +0 -1
- data/server/node_modules/knox/lib/knox/mime/index.js +0 -308
- data/server/node_modules/knox/lib/knox/mime/test.js +0 -59
- data/server/node_modules/node-uuid/test/benchmark-native +0 -0
- data/server/node_modules/on/index.js +0 -13
- data/server/node_modules/restler/test/test_helper.js +0 -163
- data/server/node_modules/spawner/index.js +0 -106
@@ -0,0 +1,33 @@
|
|
1
|
+
/*
|
2
|
+
RequireJS 0.26.0 Copyright (c) 2010-2011, The Dojo Foundation All Rights Reserved.
|
3
|
+
Available via the MIT or new BSD license.
|
4
|
+
see: http://github.com/jrburke/requirejs for details
|
5
|
+
*/
|
6
|
+
var requirejs,require,define;
|
7
|
+
(function(){function M(a){return $.call(a)==="[object Function]"}function E(a){return $.call(a)==="[object Array]"}function V(a,c,g){for(var e in c)if(!(e in J)&&(!(e in a)||g))a[e]=c[e];return d}function R(a,c,d){a=Error(c+"\nhttp://requirejs.org/docs/errors.html#"+a);if(d)a.originalError=d;return a}function aa(a,c,d){var e,x,j;for(e=0;j=c[e];e++){j=typeof j==="string"?{name:j}:j;x=j.location;if(d&&(!x||x.indexOf("/")!==0&&x.indexOf(":")===-1))x=d+"/"+(x||j.name);a[j.name]={name:j.name,location:x||
|
8
|
+
j.name,main:(j.main||"main").replace(fa,"").replace(ba,"")}}}function W(a,d){a.holdReady?a.holdReady(d):d?a.readyWait+=1:a.ready(!0)}function ga(a){function c(b,h){var n,o;if(b&&b.charAt(0)==="."&&h){p.pkgs[h]?h=[h]:(h=h.split("/"),h=h.slice(0,h.length-1));n=b=h.concat(b.split("/"));var a;for(o=0;a=n[o];o++)if(a===".")n.splice(o,1),o-=1;else if(a==="..")if(o===1&&(n[2]===".."||n[0]===".."))break;else o>0&&(n.splice(o-1,2),o-=2);o=p.pkgs[n=b[0]];b=b.join("/");o&&b===n+"/"+o.main&&(b=n)}return b}function g(b,
|
9
|
+
h){var n=b?b.indexOf("!"):-1,o=null,a=h?h.name:null,ha=b,g,l;n!==-1&&(o=b.substring(0,n),b=b.substring(n+1,b.length));o&&(o=c(o,a));b&&(g=o?(n=m[o])?n.normalize?n.normalize(b,function(b){return c(b,a)}):c(b,a):"__$p"+a+"@"+(b||""):c(b,a),l=E[g],l||(l=d.toModuleUrl?d.toModuleUrl(f,g,h):f.nameToUrl(g,null,h),E[g]=l));return{prefix:o,name:g,parentMap:h,url:l,originalName:ha,fullName:o?o+"!"+(g||""):g}}function e(){var b=!0,h=p.priorityWait,n,a;if(h){for(a=0;n=h[a];a++)if(!s[n]){b=!1;break}b&&delete p.priorityWait}return b}
|
10
|
+
function x(b){return function(h){b.exports=h}}function j(b,h,n){return function(){var a=[].concat(ia.call(arguments,0)),d;if(n&&M(d=a[a.length-1]))d.__requireJsBuild=!0;a.push(h);return b.apply(null,a)}}function q(b,h){var a=j(f.require,b,h);V(a,{nameToUrl:j(f.nameToUrl,b),toUrl:j(f.toUrl,b),defined:j(f.requireDefined,b),specified:j(f.requireSpecified,b),ready:d.ready,isBrowser:d.isBrowser});if(d.paths)a.paths=d.paths;return a}function v(b){var h=b.prefix,a=b.fullName;y[a]||a in m||(h&&!K[h]&&(K[h]=
|
11
|
+
void 0,(S[h]||(S[h]=[])).push(b),(t[h]||(t[h]=[])).push({onDep:function(b){if(b===h){var a,n,d,c,f,e,j=S[h];if(j)for(d=0;a=j[d];d++)if(b=a.fullName,a=g(a.originalName,a.parentMap),a=a.fullName,n=t[b]||[],c=t[a],a!==b){b in y&&(delete y[b],y[a]=!0);t[a]=c?c.concat(n):n;delete t[b];for(c=0;c<n.length;c++){e=n[c].depArray;for(f=0;f<e.length;f++)e[f]===b&&(e[f]=a)}}delete S[h]}}}),v(g(h))),f.paused.push(b))}function w(b){var h,a,c;h=b.callback;var k=b.fullName,e=[],j=b.depArray;if(h&&M(h)){if(j)for(h=
|
12
|
+
0;h<j.length;h++)e.push(b.deps[j[h]]);if(p.catchError.define)try{a=d.execCb(k,b.callback,e,m[k])}catch(l){c=l}else a=d.execCb(k,b.callback,e,m[k]);if(k)b.cjsModule&&b.cjsModule.exports!==void 0?a=m[k]=b.cjsModule.exports:a===void 0&&b.usingExports?a=m[k]:m[k]=a}else k&&(a=m[k]=h);if(F[b.waitId])delete F[b.waitId],b.isDone=!0,f.waitCount-=1,f.waitCount===0&&(I=[]);if(c)return a=(k?g(k).url:"")||c.fileName||c.sourceURL,c=R("defineerror",'Error evaluating module "'+k+'" at location "'+a+'":\n'+c+"\nfileName:"+
|
13
|
+
a+"\nlineNumber: "+(c.lineNumber||c.line),c),c.moduleName=k,d.onError(c);if(k&&(c=t[k])){for(h=0;h<c.length;h++)c[h].onDep(k,a);delete t[k]}}function z(b,a,c,d){var b=g(b,d),k=b.name,e=b.fullName,j={},l={waitId:k||ja+Q++,depCount:0,depMax:0,prefix:b.prefix,name:k,fullName:e,deps:{},depArray:a,callback:c,onDep:function(b,a){b in l.deps||(l.deps[b]=a,l.depCount+=1,l.depCount===l.depMax&&w(l))}},i,r;if(e){if(e in m||s[e]===!0||e==="jquery"&&p.jQuery&&p.jQuery!==c().fn.jquery)return;y[e]=!0;s[e]=!0;e===
|
14
|
+
"jquery"&&c&&T(c())}for(c=0;c<a.length;c++)if(i=a[c])i=g(i,k?b:d),r=i.fullName,a[c]=r,r==="require"?l.deps[r]=q(b):r==="exports"?(l.deps[r]=m[e]={},l.usingExports=!0):r==="module"?(l.cjsModule=i=l.deps[r]={id:k,uri:k?f.nameToUrl(k,null,d):void 0,exports:m[e]},i.setExports=x(i)):r in m&&!(r in F)?l.deps[r]=m[r]:j[r]||(l.depMax+=1,v(i),(t[r]||(t[r]=[])).push(l),j[r]=!0);l.depCount===l.depMax?w(l):(F[l.waitId]=l,I.push(l),f.waitCount+=1)}function u(b){z.apply(null,b);s[b[0]]=!0}function C(b,a){if(!b.isDone){var c=
|
15
|
+
b.fullName,d=b.depArray,f,e;if(c){if(a[c])return m[c];a[c]=!0}for(e=0;e<d.length;e++)if((f=d[e])&&!b.deps[f]&&F[f])b.onDep(f,C(F[f],a));return c?m[c]:void 0}}function A(){var b=p.waitSeconds*1E3,a=b&&f.startTime+b<(new Date).getTime(),b="",c=!1,g=!1,k;if(!(f.pausedCount>0)){if(p.priorityWait)if(e())G();else return;for(k in s)if(!(k in J)&&(c=!0,!s[k]))if(a)b+=k+" ";else{g=!0;break}if(c||f.waitCount){if(a&&b)return k=R("timeout","Load timeout for modules: "+b),k.requireType="timeout",k.requireModules=
|
16
|
+
b,d.onError(k);if(g||f.scriptCount){if((B||ca)&&!X)X=setTimeout(function(){X=0;A()},50)}else{if(f.waitCount){for(H=0;b=I[H];H++)C(b,{});Y<5&&(Y+=1,A())}Y=0;d.checkReadyState()}}}}function D(b,a){var c=a.name,e=a.fullName,g;if(!(e in m||e in s))K[b]||(K[b]=m[b]),s[e]||(s[e]=!1),g=function(g){if(d.onPluginLoad)d.onPluginLoad(f,b,c,g);w({prefix:a.prefix,name:a.name,fullName:a.fullName,callback:function(){return g}});s[e]=!0},g.fromText=function(b,a){var c=N;f.loaded[b]=!1;f.scriptCount+=1;c&&(N=!1);
|
17
|
+
d.exec(a);c&&(N=!0);f.completeLoad(b)},K[b].load(c,q(a.parentMap,!0),g,p)}function L(b){b.prefix&&b.name&&b.name.indexOf("__$p")===0&&m[b.prefix]&&(b=g(b.originalName,b.parentMap));var a=b.prefix,c=b.fullName,e=f.urlFetched;!y[c]&&!s[c]&&(y[c]=!0,a?m[a]?D(a,b):(O[a]||(O[a]=[],(t[a]||(t[a]=[])).push({onDep:function(b){if(b===a){for(var c,d=O[a],b=0;b<d.length;b++)c=d[b],D(a,g(c.originalName,c.parentMap));delete O[a]}}})),O[a].push(b)):e[b.url]||(d.load(f,c,b.url),e[b.url]=!0))}var f,G,p={waitSeconds:7,
|
18
|
+
baseUrl:i.baseUrl||"./",paths:{},pkgs:{},catchError:{}},P=[],y={require:!0,exports:!0,module:!0},E={},m={},s={},F={},I=[],Q=0,t={},K={},O={},Z=0,S={};T=function(b){if(!f.jQuery&&(b=b||(typeof jQuery!=="undefined"?jQuery:null))&&!(p.jQuery&&b.fn.jquery!==p.jQuery)&&("holdReady"in b||"readyWait"in b))if(f.jQuery=b,u(["jquery",[],function(){return jQuery}]),f.scriptCount)W(b,!0),f.jQueryIncremented=!0};G=function(){var b,a,c;Z+=1;if(f.scriptCount<=0)f.scriptCount=0;for(;P.length;)if(b=P.shift(),b[0]===
|
19
|
+
null)return d.onError(R("mismatch","Mismatched anonymous define() module: "+b[b.length-1]));else u(b);if(!p.priorityWait||e())for(;f.paused.length;){c=f.paused;f.pausedCount+=c.length;f.paused=[];for(a=0;b=c[a];a++)L(b);f.startTime=(new Date).getTime();f.pausedCount-=c.length}Z===1&&A();Z-=1};f={contextName:a,config:p,defQueue:P,waiting:F,waitCount:0,specified:y,loaded:s,urlMap:E,scriptCount:0,urlFetched:{},defined:m,paused:[],pausedCount:0,plugins:K,managerCallbacks:t,makeModuleMap:g,normalize:c,
|
20
|
+
configure:function(b){var a,c,e;b.baseUrl&&b.baseUrl.charAt(b.baseUrl.length-1)!=="/"&&(b.baseUrl+="/");a=p.paths;e=p.pkgs;V(p,b,!0);if(b.paths){for(c in b.paths)c in J||(a[c]=b.paths[c]);p.paths=a}if((a=b.packagePaths)||b.packages){if(a)for(c in a)c in J||aa(e,a[c],c);b.packages&&aa(e,b.packages);p.pkgs=e}if(b.priority)c=f.requireWait,f.requireWait=!1,f.takeGlobalQueue(),G(),f.require(b.priority),G(),f.requireWait=c,p.priorityWait=b.priority;if(b.deps||b.callback)f.require(b.deps||[],b.callback);
|
21
|
+
b.ready&&d.ready(b.ready)},requireDefined:function(b,a){return g(b,a).fullName in m},requireSpecified:function(b,a){return g(b,a).fullName in y},require:function(b,c,e){if(typeof b==="string"){if(d.get)return d.get(f,b,c);c=g(b,c);b=c.fullName;return!(b in m)?d.onError(R("notloaded","Module name '"+c.fullName+"' has not been loaded yet for context: "+a)):m[b]}z(null,b,c,e);if(!f.requireWait)for(;!f.scriptCount&&f.paused.length;)f.takeGlobalQueue(),G();return f.require},takeGlobalQueue:function(){U.length&&
|
22
|
+
(ka.apply(f.defQueue,[f.defQueue.length-1,0].concat(U)),U=[])},completeLoad:function(b){var a;for(f.takeGlobalQueue();P.length;)if(a=P.shift(),a[0]===null){a[0]=b;break}else if(a[0]===b)break;else u(a),a=null;a?u(a):u([b,[],b==="jquery"&&typeof jQuery!=="undefined"?function(){return jQuery}:null]);s[b]=!0;T();d.isAsync&&(f.scriptCount-=1);G();d.isAsync||(f.scriptCount-=1)},toUrl:function(b,a){var c=b.lastIndexOf("."),d=null;c!==-1&&(d=b.substring(c,b.length),b=b.substring(0,c));return f.nameToUrl(b,
|
23
|
+
d,a)},nameToUrl:function(b,a,e){var g,j,i,m,l=f.config,b=c(b,e&&e.fullName);if(d.jsExtRegExp.test(b))a=b+(a?a:"");else{g=l.paths;j=l.pkgs;e=b.split("/");for(m=e.length;m>0;m--)if(i=e.slice(0,m).join("/"),g[i]){e.splice(0,m,g[i]);break}else if(i=j[i]){b=b===i.name?i.location+"/"+i.main:i.location;e.splice(0,m,b);break}a=e.join("/")+(a||".js");a=(a.charAt(0)==="/"||a.match(/^\w+:/)?"":l.baseUrl)+a}return l.urlArgs?a+((a.indexOf("?")===-1?"?":"&")+l.urlArgs):a}};f.jQueryCheck=T;f.resume=G;return f}function la(){var a,
|
24
|
+
c,d;if(C&&C.readyState==="interactive")return C;a=document.getElementsByTagName("script");for(c=a.length-1;c>-1&&(d=a[c]);c--)if(d.readyState==="interactive")return C=d;return null}var ma=/(\/\*([\s\S]*?)\*\/|\/\/(.*)$)/mg,na=/require\(\s*["']([^'"\s]+)["']\s*\)/g,fa=/^\.\//,ba=/\.js$/,$=Object.prototype.toString,q=Array.prototype,ia=q.slice,ka=q.splice,B=!!(typeof window!=="undefined"&&navigator&&document),ca=!B&&typeof importScripts!=="undefined",oa=B&&navigator.platform==="PLAYSTATION 3"?/^complete$/:
|
25
|
+
/^(complete|loaded)$/,da=typeof opera!=="undefined"&&opera.toString()==="[object Opera]",ja="_r@@",J={},z={},U=[],C=null,Y=0,N=!1,d,q={},I,i,u,L,v,A,D,H,Q,ea,w,T,X;if(typeof define==="undefined"){if(typeof requirejs!=="undefined")if(M(requirejs))return;else q=requirejs,requirejs=void 0;typeof require!=="undefined"&&!M(require)&&(q=require,require=void 0);d=requirejs=function(a,c,d){var e="_",i;!E(a)&&typeof a!=="string"&&(i=a,E(c)?(a=c,c=d):a=[]);if(i&&i.context)e=i.context;d=z[e]||(z[e]=ga(e));i&&
|
26
|
+
d.configure(i);return d.require(a,c)};d.config=function(a){return d(a)};typeof require==="undefined"&&(require=d);d.toUrl=function(a){return z._.toUrl(a)};d.version="0.26.0";d.isArray=E;d.isFunction=M;d.mixin=V;d.jsExtRegExp=/^\/|:|\?|\.js$/;i=d.s={contexts:z,skipAsync:{},isPageLoaded:!B,readyCalls:[]};if(d.isAsync=d.isBrowser=B)if(u=i.head=document.getElementsByTagName("head")[0],L=document.getElementsByTagName("base")[0])u=i.head=L.parentNode;d.onError=function(a){throw a;};d.load=function(a,c,
|
27
|
+
g){var e=a.loaded;e[c]||(e[c]=!1);a.scriptCount+=1;d.attach(g,a,c);if(a.jQuery&&!a.jQueryIncremented)W(a.jQuery,!0),a.jQueryIncremented=!0};define=d.def=function(a,c,g){var e,i;typeof a!=="string"&&(g=c,c=a,a=null);d.isArray(c)||(g=c,c=[]);!a&&!c.length&&d.isFunction(g)&&g.length&&(g.toString().replace(ma,"").replace(na,function(a,d){c.push(d)}),c=(g.length===1?["require"]:["require","exports","module"]).concat(c));if(N&&(e=I||la()))a||(a=e.getAttribute("data-requiremodule")),i=z[e.getAttribute("data-requirecontext")];
|
28
|
+
(i?i.defQueue:U).push([a,c,g])};define.amd={multiversion:!0,plugins:!0,jQuery:!0};d.exec=function(a){return eval(a)};d.execCb=function(a,c,d,e){return c.apply(e,d)};d.onScriptLoad=function(a){var c=a.currentTarget||a.srcElement,g;if(a.type==="load"||oa.test(c.readyState))C=null,a=c.getAttribute("data-requirecontext"),g=c.getAttribute("data-requiremodule"),z[a].completeLoad(g),c.detachEvent&&!da?c.detachEvent("onreadystatechange",d.onScriptLoad):c.removeEventListener("load",d.onScriptLoad,!1)};d.attach=
|
29
|
+
function(a,c,g,e,q){var j;if(B)return e=e||d.onScriptLoad,j=c&&c.config&&c.config.xhtml?document.createElementNS("http://www.w3.org/1999/xhtml","html:script"):document.createElement("script"),j.type=q||"text/javascript",j.charset="utf-8",j.async=!i.skipAsync[a],c&&j.setAttribute("data-requirecontext",c.contextName),j.setAttribute("data-requiremodule",g),j.attachEvent&&!da?(N=!0,j.attachEvent("onreadystatechange",e)):j.addEventListener("load",e,!1),j.src=a,I=j,L?u.insertBefore(j,L):u.appendChild(j),
|
30
|
+
I=null,j;else if(ca)e=c.loaded,e[g]=!1,importScripts(a),c.completeLoad(g);return null};if(B){v=document.getElementsByTagName("script");for(H=v.length-1;H>-1&&(A=v[H]);H--){if(!u)u=A.parentNode;if(D=A.getAttribute("data-main")){if(!q.baseUrl)v=D.split("/"),A=v.pop(),v=v.length?v.join("/")+"/":"./",q.baseUrl=v,D=A.replace(ba,"");q.deps=q.deps?q.deps.concat(D):[D];break}}}i.baseUrl=q.baseUrl;d.pageLoaded=function(){if(!i.isPageLoaded){i.isPageLoaded=!0;Q&&clearInterval(Q);if(ea)document.readyState="complete";
|
31
|
+
d.callReady()}};d.checkReadyState=function(){var a=i.contexts,c;for(c in a)if(!(c in J)&&a[c].waitCount)return;i.isDone=!0;d.callReady()};d.callReady=function(){var a=i.readyCalls,c,d,e;if(i.isPageLoaded&&i.isDone){if(a.length){i.readyCalls=[];for(c=0;d=a[c];c++)d()}a=i.contexts;for(e in a)if(!(e in J)&&(c=a[e],c.jQueryIncremented))W(c.jQuery,!1),c.jQueryIncremented=!1}};d.ready=function(a){i.isPageLoaded&&i.isDone?a():i.readyCalls.push(a);return d};if(B){if(document.addEventListener){if(document.addEventListener("DOMContentLoaded",
|
32
|
+
d.pageLoaded,!1),window.addEventListener("load",d.pageLoaded,!1),!document.readyState)ea=!0,document.readyState="loading"}else window.attachEvent&&(window.attachEvent("onload",d.pageLoaded),self===self.top&&(Q=setInterval(function(){try{document.body&&(document.documentElement.doScroll("left"),d.pageLoaded())}catch(a){}},30)));document.readyState==="complete"&&d.pageLoaded()}d(q);if(d.isAsync&&typeof setTimeout!=="undefined")w=i.contexts[q.context||"_"],w.requireWait=!0,setTimeout(function(){w.requireWait=
|
33
|
+
!1;w.takeGlobalQueue();w.jQueryCheck();w.scriptCount||w.resume();d.checkReadyState()},0)}})();
|
@@ -0,0 +1,28 @@
|
|
1
|
+
define([], function() {
|
2
|
+
var exports = {};
|
3
|
+
|
4
|
+
exports.inspect = JSON.stringify;
|
5
|
+
|
6
|
+
// Copy from Node
|
7
|
+
/**
|
8
|
+
* Inherit the prototype methods from one constructor into another.
|
9
|
+
*
|
10
|
+
* The Function.prototype.inherits from lang.js rewritten as a standalone
|
11
|
+
* function (not on Function.prototype). NOTE: If this file is to be loaded
|
12
|
+
* during bootstrapping this function needs to be revritten using some native
|
13
|
+
* functions as prototype setup using normal JavaScript does not work as
|
14
|
+
* expected during bootstrapping (see mirror.js in r114903).
|
15
|
+
*
|
16
|
+
* @param {function} ctor Constructor function which needs to inherit the
|
17
|
+
* prototype.
|
18
|
+
* @param {function} superCtor Constructor function to inherit prototype from.
|
19
|
+
*/
|
20
|
+
exports.inherits = function(ctor, superCtor) {
|
21
|
+
ctor.super_ = superCtor;
|
22
|
+
ctor.prototype = Object.create(superCtor.prototype, {
|
23
|
+
constructor: { value: ctor, enumerable: false }
|
24
|
+
});
|
25
|
+
};
|
26
|
+
|
27
|
+
return exports;
|
28
|
+
})
|
@@ -0,0 +1,101 @@
|
|
1
|
+
#!/usr/bin/env node
|
2
|
+
// The follow command-line interface.
|
3
|
+
//
|
4
|
+
// Copyright 2011 Iris Couch
|
5
|
+
//
|
6
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
+
// you may not use this file except in compliance with the License.
|
8
|
+
// You may obtain a copy of the License at
|
9
|
+
//
|
10
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
//
|
12
|
+
// Unless required by applicable law or agreed to in writing, software
|
13
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
+
// See the License for the specific language governing permissions and
|
16
|
+
// limitations under the License.
|
17
|
+
|
18
|
+
var lib = require('./lib')
|
19
|
+
, couch_changes = require('./api')
|
20
|
+
;
|
21
|
+
|
22
|
+
function puts(str) {
|
23
|
+
process.stdout.write(str + "\n");
|
24
|
+
}
|
25
|
+
|
26
|
+
function main() {
|
27
|
+
var db = require.isBrowser ? (process.env.db || '/_users') : process.argv[2];
|
28
|
+
puts('Watching: ' + db);
|
29
|
+
|
30
|
+
var feed = new couch_changes.Feed();
|
31
|
+
feed.db = db;
|
32
|
+
feed.since = (process.env.since === 'now') ? 'now' : parseInt(process.env.since || '0');
|
33
|
+
|
34
|
+
feed.heartbeat = (process.env.heartbeat || '3000').replace(/s$/, '000');
|
35
|
+
feed.heartbeat = parseInt(feed.heartbeat);
|
36
|
+
|
37
|
+
if(require.isBrowser)
|
38
|
+
feed.feed = 'longpoll';
|
39
|
+
if(process.env.host)
|
40
|
+
feed.headers.host = process.env.host;
|
41
|
+
if(process.env.inactivity)
|
42
|
+
feed.inactivity_ms = parseInt(process.env.inactivity);
|
43
|
+
if(process.env.limit)
|
44
|
+
feed.limit = parseInt(process.env.limit);
|
45
|
+
|
46
|
+
feed.query_params.pid = process.pid;
|
47
|
+
feed.filter = process.env.filter || example_filter;
|
48
|
+
function example_filter(doc, req) {
|
49
|
+
// This is a local filter. It runs on the client side.
|
50
|
+
var label = 'Filter ' + (req.query.pid || '::');
|
51
|
+
|
52
|
+
if(process.env.show_doc)
|
53
|
+
console.log(label + ' doc: ' + JSON.stringify(doc));
|
54
|
+
if(process.env.show_req)
|
55
|
+
console.log(label + ' for ' + doc._id + ' req: ' + JSON.stringify(req));
|
56
|
+
return true;
|
57
|
+
}
|
58
|
+
|
59
|
+
feed.on('confirm', function() {
|
60
|
+
puts('Database confirmed: ' + db);
|
61
|
+
})
|
62
|
+
|
63
|
+
feed.on('change', function(change) {
|
64
|
+
puts('Change:' + JSON.stringify(change));
|
65
|
+
})
|
66
|
+
|
67
|
+
feed.on('timeout', function(state) {
|
68
|
+
var seconds = state.elapsed_ms / 1000;
|
69
|
+
var hb = state.heartbeat / 1000;
|
70
|
+
puts('Timeout after ' + seconds + 's inactive, heartbeat=' + hb + 's');
|
71
|
+
})
|
72
|
+
|
73
|
+
feed.on('retry', function(state) {
|
74
|
+
if(require.isBrowser)
|
75
|
+
puts('Long polling since ' + state.since);
|
76
|
+
else
|
77
|
+
puts('Retry since ' + state.since + ' after ' + state.after + 'ms');
|
78
|
+
})
|
79
|
+
|
80
|
+
feed.on('response', function() {
|
81
|
+
puts('Streaming response:');
|
82
|
+
})
|
83
|
+
|
84
|
+
feed.on('error', function(er) {
|
85
|
+
//console.error(er);
|
86
|
+
console.error('Changes error ============\n' + er.stack);
|
87
|
+
setTimeout(function() { process.exit(0) }, 100);
|
88
|
+
})
|
89
|
+
|
90
|
+
process.on('uncaughtException', function(er) {
|
91
|
+
puts('========= UNCAUGHT EXCEPTION; This is bad');
|
92
|
+
puts(er.stack);
|
93
|
+
setTimeout(function() { process.exit(1) }, 100);
|
94
|
+
})
|
95
|
+
|
96
|
+
feed.follow();
|
97
|
+
}
|
98
|
+
|
99
|
+
exports.main = main;
|
100
|
+
if(!require.isBrowser && process.argv[1] == module.filename)
|
101
|
+
main();
|
@@ -0,0 +1,556 @@
|
|
1
|
+
// Core routines for event emitters
|
2
|
+
//
|
3
|
+
// Copyright 2011 Iris Couch
|
4
|
+
//
|
5
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
6
|
+
// you may not use this file except in compliance with the License.
|
7
|
+
// You may obtain a copy of the License at
|
8
|
+
//
|
9
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
10
|
+
//
|
11
|
+
// Unless required by applicable law or agreed to in writing, software
|
12
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
13
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
14
|
+
// See the License for the specific language governing permissions and
|
15
|
+
// limitations under the License.
|
16
|
+
|
17
|
+
var lib = require('../lib')
|
18
|
+
, util = require('util')
|
19
|
+
, events = require('events')
|
20
|
+
, request = require('request')
|
21
|
+
, Changes = require('./stream').Changes
|
22
|
+
, querystring = require('querystring')
|
23
|
+
|
24
|
+
// Use the library timeout functions, primarily so the test suite can catch errors.
|
25
|
+
var setTimeout = lib.setTimeout
|
26
|
+
, clearTimeout = lib.clearTimeout
|
27
|
+
|
28
|
+
var DEFAULT_HEARTBEAT = 30000;
|
29
|
+
var HEARTBEAT_TIMEOUT_COEFFICIENT = 1.25; // E.g. heartbeat 1000ms would trigger a timeout after 1250ms of no heartbeat.
|
30
|
+
var DEFAULT_MAX_RETRY_SECONDS = 60 * 60;
|
31
|
+
var INITIAL_RETRY_DELAY = 1000;
|
32
|
+
|
33
|
+
var FEED_PARAMETERS = ['since', 'limit', 'feed', 'heartbeat', 'filter', 'include_docs'];
|
34
|
+
|
35
|
+
var EventEmitter = events.EventEmitter2 || events.EventEmitter;
|
36
|
+
|
37
|
+
|
38
|
+
util.inherits(Feed, EventEmitter);
|
39
|
+
function Feed (opts) {
|
40
|
+
var self = this;
|
41
|
+
EventEmitter.call(self);
|
42
|
+
|
43
|
+
self.feed = 'continuous';
|
44
|
+
self.heartbeat = DEFAULT_HEARTBEAT;
|
45
|
+
self.max_retry_seconds = DEFAULT_MAX_RETRY_SECONDS;
|
46
|
+
self.inactivity_ms = null;
|
47
|
+
|
48
|
+
self.headers = {};
|
49
|
+
self.request = {}; // Extra options for potentially future versions of request. The caller can supply them.
|
50
|
+
|
51
|
+
self.since = 0;
|
52
|
+
self.caught_up = false
|
53
|
+
self.retry_delay = INITIAL_RETRY_DELAY; // ms
|
54
|
+
|
55
|
+
self.query_params = {}; // Extra `req.query` values for filter functions
|
56
|
+
|
57
|
+
opts = opts || {};
|
58
|
+
if(typeof opts === 'string')
|
59
|
+
opts = {'db': opts};
|
60
|
+
Object.keys(opts).forEach(function(key) {
|
61
|
+
self[key] = opts[key];
|
62
|
+
})
|
63
|
+
|
64
|
+
self.pending = { request : null
|
65
|
+
, activity_at : null
|
66
|
+
};
|
67
|
+
} // Feed
|
68
|
+
|
69
|
+
Feed.prototype.start =
|
70
|
+
Feed.prototype.follow = function follow_feed() {
|
71
|
+
var self = this;
|
72
|
+
|
73
|
+
if(!self.db)
|
74
|
+
throw new Error('Database URL required');
|
75
|
+
|
76
|
+
if(self.feed !== 'continuous' && self.feed !== 'longpoll')
|
77
|
+
throw new Error('The only valid feed options are "continuous" and "longpoll"');
|
78
|
+
|
79
|
+
if(typeof self.heartbeat !== 'number')
|
80
|
+
throw new Error('Required "heartbeat" value');
|
81
|
+
|
82
|
+
self.log = lib.log4js.getLogger(self.db);
|
83
|
+
self.log.setLevel(process.env.follow_log_level || "info");
|
84
|
+
|
85
|
+
self.emit('start');
|
86
|
+
return self.confirm();
|
87
|
+
}
|
88
|
+
|
89
|
+
Feed.prototype.confirm = function confirm_feed() {
|
90
|
+
var self = this;
|
91
|
+
|
92
|
+
self.db_safe = lib.scrub_creds(self.db);
|
93
|
+
|
94
|
+
self.log.debug('Checking database: ' + self.db_safe);
|
95
|
+
|
96
|
+
var confirm_timeout = self.heartbeat * 3; // Give it time to look up the name, connect, etc.
|
97
|
+
var timeout_id = setTimeout(function() {
|
98
|
+
return self.die(new Error('Timeout confirming database: ' + self.db_safe));
|
99
|
+
}, confirm_timeout);
|
100
|
+
|
101
|
+
var headers = lib.JP(lib.JS(self.headers));
|
102
|
+
headers.accept = 'application/json';
|
103
|
+
|
104
|
+
var req = request({'uri':self.db, 'headers':headers}, db_response)
|
105
|
+
self.emit('confirm_request', req)
|
106
|
+
|
107
|
+
function db_response(er, resp, body) {
|
108
|
+
clearTimeout(timeout_id);
|
109
|
+
|
110
|
+
if(er)
|
111
|
+
return self.die(er);
|
112
|
+
|
113
|
+
var db;
|
114
|
+
try {
|
115
|
+
db = JSON.parse(body)
|
116
|
+
} catch(json_er) {
|
117
|
+
return self.emit('error', json_er)
|
118
|
+
}
|
119
|
+
|
120
|
+
if(!db.db_name || !db.instance_start_time)
|
121
|
+
return self.emit('error', new Error('Bad DB response: ' + body));
|
122
|
+
|
123
|
+
self.original_db_seq = db.update_seq
|
124
|
+
self.log.debug('Confirmed db: ' + self.db_safe);
|
125
|
+
self.emit('confirm', db);
|
126
|
+
|
127
|
+
if(self.since == 'now') {
|
128
|
+
self.log.debug('Query since "now" is the same as query since -1')
|
129
|
+
self.since = -1
|
130
|
+
}
|
131
|
+
|
132
|
+
if(self.since < 0) {
|
133
|
+
self.log.debug('Query since '+self.since+' will start at ' + (db.update_seq + self.since + 1))
|
134
|
+
self.since = db.update_seq + self.since + 1
|
135
|
+
}
|
136
|
+
|
137
|
+
// If the next change would come after the current update_seq, just fake a catchup event now.
|
138
|
+
if(self.original_db_seq == self.since) {
|
139
|
+
self.caught_up = true
|
140
|
+
self.emit('catchup', db.update_seq)
|
141
|
+
}
|
142
|
+
|
143
|
+
return self.query();
|
144
|
+
}
|
145
|
+
}
|
146
|
+
|
147
|
+
Feed.prototype.query = function query_feed() {
|
148
|
+
var self = this;
|
149
|
+
|
150
|
+
var query_params = JSON.parse(JSON.stringify(self.query_params));
|
151
|
+
|
152
|
+
FEED_PARAMETERS.forEach(function(key) {
|
153
|
+
if(key in self)
|
154
|
+
query_params[key] = self[key];
|
155
|
+
})
|
156
|
+
|
157
|
+
if(typeof query_params.filter !== 'string')
|
158
|
+
delete query_params.filter;
|
159
|
+
|
160
|
+
if(typeof self.filter === 'function' && !query_params.include_docs) {
|
161
|
+
self.log.debug('Enabling include_docs for client-side filter');
|
162
|
+
query_params.include_docs = true;
|
163
|
+
}
|
164
|
+
|
165
|
+
// Limit the response size for longpoll.
|
166
|
+
var poll_size = 100;
|
167
|
+
if(query_params.feed == 'longpoll' && (!query_params.limit || query_params.limit > poll_size))
|
168
|
+
query_params.limit = poll_size;
|
169
|
+
|
170
|
+
var feed_url = self.db + '/_changes?' + querystring.stringify(query_params);
|
171
|
+
|
172
|
+
self.headers.accept = self.headers.accept || 'application/json';
|
173
|
+
var req = { method : 'GET'
|
174
|
+
, uri : feed_url
|
175
|
+
, headers: self.headers
|
176
|
+
, encoding: 'utf-8'
|
177
|
+
, onResponse: on_feed_response
|
178
|
+
}
|
179
|
+
|
180
|
+
req.changes_query = query_params;
|
181
|
+
Object.keys(self.request).forEach(function(key) {
|
182
|
+
req[key] = self.request[key];
|
183
|
+
})
|
184
|
+
|
185
|
+
var now = new Date
|
186
|
+
, feed_ts = lib.JDUP(now)
|
187
|
+
, feed_id = process.env.follow_debug ? feed_ts.match(/\.(\d\d\d)Z$/)[1] : feed_ts
|
188
|
+
|
189
|
+
self.log.debug('Feed query ' + feed_id + ': ' + lib.scrub_creds(feed_url))
|
190
|
+
var feed_request = request(req)
|
191
|
+
feed_request.on('response', function() {
|
192
|
+
self.log.debug('Remove feed from agent pool: ' + feed_id)
|
193
|
+
feed_request.req.socket.emit('agentRemove')
|
194
|
+
})
|
195
|
+
|
196
|
+
// The response headers must arrive within one heartbeat.
|
197
|
+
var response_timer = setTimeout(response_timed_out, self.heartbeat)
|
198
|
+
, timed_out = false
|
199
|
+
|
200
|
+
return self.emit('query', feed_request)
|
201
|
+
|
202
|
+
function response_timed_out() {
|
203
|
+
self.log.debug('Feed response timed out: ' + feed_id)
|
204
|
+
timed_out = true
|
205
|
+
return self.retry()
|
206
|
+
}
|
207
|
+
|
208
|
+
function on_feed_response(er, resp, body) {
|
209
|
+
clearTimeout(response_timer)
|
210
|
+
|
211
|
+
if((resp !== undefined && resp.body) || body)
|
212
|
+
return self.die(new Error('Cannot handle a body in the feed response: ' + lib.JS(resp.body || body)))
|
213
|
+
|
214
|
+
if(timed_out) {
|
215
|
+
self.log.debug('Ignoring late response: ' + feed_id);
|
216
|
+
return destroy_response(resp);
|
217
|
+
}
|
218
|
+
|
219
|
+
if(er) {
|
220
|
+
self.log.debug('Request error ' + feed_id + ': ' + er.stack);
|
221
|
+
destroy_response(resp);
|
222
|
+
return self.retry();
|
223
|
+
}
|
224
|
+
|
225
|
+
if(resp.statusCode !== 200) {
|
226
|
+
self.log.debug('Bad changes response ' + feed_id + ': ' + resp.statusCode);
|
227
|
+
destroy_response(resp);
|
228
|
+
return self.retry();
|
229
|
+
}
|
230
|
+
|
231
|
+
self.log.debug('Good response: ' + feed_id);
|
232
|
+
self.retry_delay = INITIAL_RETRY_DELAY;
|
233
|
+
|
234
|
+
self.emit('response', resp);
|
235
|
+
|
236
|
+
var changes_stream = new Changes
|
237
|
+
changes_stream.log = lib.log4js.getLogger('stream ' + self.db)
|
238
|
+
changes_stream.log.setLevel(self.log.level.levelStr)
|
239
|
+
changes_stream.feed = self.feed
|
240
|
+
feed_request.pipe(changes_stream)
|
241
|
+
|
242
|
+
changes_stream.created_at = now
|
243
|
+
changes_stream.id = function() { return feed_id }
|
244
|
+
return self.prep(changes_stream)
|
245
|
+
}
|
246
|
+
}
|
247
|
+
|
248
|
+
Feed.prototype.prep = function prep_request(changes_stream) {
|
249
|
+
var self = this;
|
250
|
+
|
251
|
+
var now = new Date;
|
252
|
+
self.pending.request = changes_stream;
|
253
|
+
self.pending.activity_at = now;
|
254
|
+
self.pending.wait_timer = null;
|
255
|
+
|
256
|
+
// The inactivity timer is for time between *changes*, or time between the
|
257
|
+
// initial connection and the first change. Therefore it goes here.
|
258
|
+
self.change_at = now;
|
259
|
+
if(self.inactivity_ms) {
|
260
|
+
clearTimeout(self.inactivity_timer);
|
261
|
+
self.inactivity_timer = setTimeout(function() { self.on_inactivity() }, self.inactivity_ms);
|
262
|
+
}
|
263
|
+
|
264
|
+
changes_stream.on('heartbeat', handler_for('heartbeat'))
|
265
|
+
changes_stream.on('error', handler_for('error'))
|
266
|
+
changes_stream.on('data', handler_for('data'))
|
267
|
+
changes_stream.on('end', handler_for('end'))
|
268
|
+
|
269
|
+
return self.wait();
|
270
|
+
|
271
|
+
function handler_for(ev) {
|
272
|
+
var name = 'on_couch_' + ev;
|
273
|
+
var inner_handler = self[name];
|
274
|
+
|
275
|
+
return handle_confirmed_req_event;
|
276
|
+
function handle_confirmed_req_event() {
|
277
|
+
if(self.pending.request === changes_stream)
|
278
|
+
return inner_handler.apply(self, arguments);
|
279
|
+
|
280
|
+
if(!changes_stream.created_at)
|
281
|
+
return self.die(new Error("Received data from unknown request")); // Pretty sure this is impossible.
|
282
|
+
|
283
|
+
var s_to_now = (new Date() - changes_stream.created_at) / 1000;
|
284
|
+
var s_to_req = '[no req]';
|
285
|
+
if(self.pending.request)
|
286
|
+
s_to_req = (self.pending.request.created_at - changes_stream.created_at) / 1000;
|
287
|
+
|
288
|
+
var msg = ': ' + changes_stream.id() + ' to_req=' + s_to_req + 's, to_now=' + s_to_now + 's';
|
289
|
+
|
290
|
+
if(ev == 'end' || ev == 'data' || ev == 'heartbeat') {
|
291
|
+
self.log.debug('Old "' + ev + '": ' + changes_stream.id())
|
292
|
+
return destroy_req(changes_stream)
|
293
|
+
}
|
294
|
+
|
295
|
+
self.log.warn('Old "'+ev+'"' + msg);
|
296
|
+
}
|
297
|
+
}
|
298
|
+
}
|
299
|
+
|
300
|
+
Feed.prototype.wait = function wait_for_event() {
|
301
|
+
var self = this;
|
302
|
+
self.emit('wait');
|
303
|
+
|
304
|
+
if(self.pending.wait_timer)
|
305
|
+
return self.die(new Error('wait() called but there is already a wait_timer: ' + self.pending.wait_timer));
|
306
|
+
|
307
|
+
var timeout_ms = self.heartbeat * HEARTBEAT_TIMEOUT_COEFFICIENT;
|
308
|
+
var req_id = self.pending.request && self.pending.request.id()
|
309
|
+
var msg = 'Req ' + req_id + ' timeout=' + timeout_ms;
|
310
|
+
if(self.inactivity_ms)
|
311
|
+
msg += ', inactivity=' + self.inactivity_ms;
|
312
|
+
msg += ': ' + self.db_safe;
|
313
|
+
|
314
|
+
self.log.debug(msg);
|
315
|
+
self.pending.wait_timer = setTimeout(function() { self.on_timeout() }, timeout_ms);
|
316
|
+
}
|
317
|
+
|
318
|
+
Feed.prototype.got_activity = function() {
|
319
|
+
var self = this
|
320
|
+
|
321
|
+
if(! self.pending.wait_timer)
|
322
|
+
return self.die(new Error('Cannot find wait timer'))
|
323
|
+
|
324
|
+
clearTimeout(self.pending.wait_timer)
|
325
|
+
self.pending.wait_timer = null
|
326
|
+
self.pending.activity_at = new Date
|
327
|
+
}
|
328
|
+
|
329
|
+
Feed.prototype.on_couch_heartbeat = function on_couch_heartbeat() {
|
330
|
+
var self = this
|
331
|
+
|
332
|
+
self.got_activity()
|
333
|
+
if(self.dead)
|
334
|
+
return self.log.debug('Skip heartbeat processing for dead feed')
|
335
|
+
|
336
|
+
self.emit('heartbeat')
|
337
|
+
|
338
|
+
if(self.dead)
|
339
|
+
return self.log.debug('No wait: heartbeat listener stopped this feed')
|
340
|
+
self.wait()
|
341
|
+
}
|
342
|
+
|
343
|
+
Feed.prototype.on_couch_data = function on_couch_data(change) {
|
344
|
+
var self = this;
|
345
|
+
self.log.debug('Data from ' + self.pending.request.id());
|
346
|
+
|
347
|
+
self.got_activity()
|
348
|
+
if(self.dead)
|
349
|
+
return self.log.debug('Skip data processing for dead feed')
|
350
|
+
|
351
|
+
// The changes stream guarantees that this data is valid JSON.
|
352
|
+
change = JSON.parse(change)
|
353
|
+
|
354
|
+
//self.log.debug('Object:\n' + util.inspect(change));
|
355
|
+
if('last_seq' in change) {
|
356
|
+
self.log.warn('Stopping upon receiving a final message: ' + JSON.stringify(change))
|
357
|
+
var del_er = new Error('Database deleted after change: ' + change.last_seq)
|
358
|
+
del_er.deleted = true
|
359
|
+
del_er.last_seq = change.last_seq
|
360
|
+
return self.die(del_er)
|
361
|
+
}
|
362
|
+
|
363
|
+
if(!change.seq)
|
364
|
+
return self.die(new Error('Change has no .seq field: ' + JSON.stringify(change)))
|
365
|
+
|
366
|
+
self.on_change(change)
|
367
|
+
|
368
|
+
// on_change() might work its way all the way to a "change" event, and the listener
|
369
|
+
// might call .stop(), which means among other things that no more events are desired.
|
370
|
+
// The die() code sets a self.dead flag to indicate this.
|
371
|
+
if(self.dead)
|
372
|
+
return self.log.debug('No wait: change listener stopped this feed')
|
373
|
+
self.wait()
|
374
|
+
}
|
375
|
+
|
376
|
+
Feed.prototype.on_timeout = function on_timeout() {
|
377
|
+
var self = this;
|
378
|
+
self.log.debug('Timeout')
|
379
|
+
|
380
|
+
var now = new Date;
|
381
|
+
var elapsed_ms = now - self.pending.activity_at;
|
382
|
+
|
383
|
+
self.emit('timeout', {elapsed_ms:elapsed_ms, heartbeat:self.heartbeat, id:self.pending.request.id()});
|
384
|
+
|
385
|
+
/*
|
386
|
+
var msg = ' for timeout after ' + elapsed_ms + 'ms; heartbeat=' + self.heartbeat;
|
387
|
+
if(!self.pending.request.id)
|
388
|
+
self.log.warn('Closing req (no id) ' + msg + ' req=' + util.inspect(self.pending.request));
|
389
|
+
else
|
390
|
+
self.log.warn('Closing req ' + self.pending.request.id() + msg);
|
391
|
+
*/
|
392
|
+
|
393
|
+
destroy_req(self.pending.request);
|
394
|
+
self.retry()
|
395
|
+
}
|
396
|
+
|
397
|
+
Feed.prototype.retry = function retry() {
|
398
|
+
var self = this;
|
399
|
+
|
400
|
+
clearTimeout(self.pending.wait_timer);
|
401
|
+
self.pending.wait_timer = null;
|
402
|
+
|
403
|
+
self.log.debug('Retry since=' + self.since + ' after ' + self.retry_delay + 'ms ')
|
404
|
+
self.emit('retry', {since:self.since, after:self.retry_delay, db:self.db_safe});
|
405
|
+
|
406
|
+
self.retry_timer = setTimeout(function() { self.query() }, self.retry_delay);
|
407
|
+
|
408
|
+
var max_retry_ms = self.max_retry_seconds * 1000;
|
409
|
+
self.retry_delay *= 2;
|
410
|
+
if(self.retry_delay > max_retry_ms)
|
411
|
+
self.retry_delay = max_retry_ms;
|
412
|
+
}
|
413
|
+
|
414
|
+
Feed.prototype.on_couch_end = function on_couch_end() {
|
415
|
+
var self = this;
|
416
|
+
|
417
|
+
self.log.debug('Changes feed ended ' + self.pending.request.id());
|
418
|
+
self.pending.request = null;
|
419
|
+
return self.retry();
|
420
|
+
}
|
421
|
+
|
422
|
+
Feed.prototype.on_couch_error = function on_couch_error(er) {
|
423
|
+
var self = this;
|
424
|
+
|
425
|
+
self.log.debug('Changes query eror: ' + lib.JS(er.stack));
|
426
|
+
return self.retry();
|
427
|
+
}
|
428
|
+
|
429
|
+
Feed.prototype.stop = function(val) {
|
430
|
+
var self = this
|
431
|
+
self.log.debug('Stop')
|
432
|
+
|
433
|
+
// Die with no errors.
|
434
|
+
self.die()
|
435
|
+
self.emit('stop', val);
|
436
|
+
}
|
437
|
+
|
438
|
+
Feed.prototype.die = function(er) {
|
439
|
+
var self = this;
|
440
|
+
|
441
|
+
if(er)
|
442
|
+
self.log.fatal('Fatal error: ' + er.stack);
|
443
|
+
|
444
|
+
// Warn code executing later that death has occured.
|
445
|
+
self.dead = true
|
446
|
+
|
447
|
+
clearTimeout(self.retry_timer)
|
448
|
+
clearTimeout(self.inactivity_timer)
|
449
|
+
clearTimeout(self.pending.wait_timer)
|
450
|
+
|
451
|
+
self.inactivity_timer = null
|
452
|
+
self.pending.wait_timer = null
|
453
|
+
|
454
|
+
var req = self.pending.request;
|
455
|
+
self.pending.request = null;
|
456
|
+
if(req) {
|
457
|
+
self.log.debug('Destroying req ' + req.id());
|
458
|
+
destroy_req(req);
|
459
|
+
}
|
460
|
+
|
461
|
+
if(er)
|
462
|
+
self.emit('error', er);
|
463
|
+
}
|
464
|
+
|
465
|
+
Feed.prototype.on_change = function on_change(change) {
|
466
|
+
var self = this;
|
467
|
+
|
468
|
+
if(!change.seq)
|
469
|
+
return self.die(new Error('No seq value in change: ' + lib.JS(change)));
|
470
|
+
|
471
|
+
if(change.seq <= self.since) {
|
472
|
+
self.log.debug('Bad seq value ' + change.seq + ' since=' + self.since);
|
473
|
+
return destroy_req(self.pending.request);
|
474
|
+
}
|
475
|
+
|
476
|
+
if(!self.caught_up && change.seq >= self.original_db_seq) {
|
477
|
+
self.caught_up = true
|
478
|
+
self.emit('catchup', change.seq)
|
479
|
+
}
|
480
|
+
|
481
|
+
if(typeof self.filter !== 'function')
|
482
|
+
return self.on_good_change(change);
|
483
|
+
|
484
|
+
if(!change.doc)
|
485
|
+
return self.die(new Error('Internal filter needs .doc in change ' + change.seq));
|
486
|
+
|
487
|
+
// Don't let the filter mutate the real data.
|
488
|
+
var doc = lib.JDUP(change.doc);
|
489
|
+
var req = lib.JDUP({'query': self.pending.request.changes_query});
|
490
|
+
|
491
|
+
var result = false;
|
492
|
+
try {
|
493
|
+
result = self.filter.apply(null, [doc, req]);
|
494
|
+
} catch (er) {
|
495
|
+
self.log.debug('Filter error', er);
|
496
|
+
}
|
497
|
+
|
498
|
+
result = (result && true) || false;
|
499
|
+
if(result) {
|
500
|
+
self.log.debug('Builtin filter PASS for change: ' + change.seq);
|
501
|
+
return self.on_good_change(change);
|
502
|
+
} else
|
503
|
+
self.log.debug('Builtin filter FAIL for change: ' + change.seq);
|
504
|
+
}
|
505
|
+
|
506
|
+
Feed.prototype.on_good_change = function on_good_change(change) {
|
507
|
+
var self = this;
|
508
|
+
|
509
|
+
if(self.inactivity_ms && !self.inactivity_timer)
|
510
|
+
return self.die(new Error('Cannot find inactivity timer during change'));
|
511
|
+
|
512
|
+
clearTimeout(self.inactivity_timer);
|
513
|
+
self.inactivity_timer = null;
|
514
|
+
if(self.inactivity_ms)
|
515
|
+
self.inactivity_timer = setTimeout(function() { self.on_inactivity() }, self.inactivity_ms);
|
516
|
+
|
517
|
+
self.change_at = new Date;
|
518
|
+
self.since = change.seq;
|
519
|
+
self.emit('change', change);
|
520
|
+
}
|
521
|
+
|
522
|
+
Feed.prototype.on_inactivity = function on_inactivity() {
|
523
|
+
var self = this;
|
524
|
+
var now = new Date;
|
525
|
+
var elapsed_ms = now - self.change_at;
|
526
|
+
var elapsed_s = elapsed_ms / 1000;
|
527
|
+
|
528
|
+
return self.die(new Error('Req ' + self.pending.request.id() + ' made no changes for ' + elapsed_s + 's'));
|
529
|
+
}
|
530
|
+
|
531
|
+
module.exports = { "Feed" : Feed
|
532
|
+
};
|
533
|
+
|
534
|
+
|
535
|
+
/*
|
536
|
+
* Utilities
|
537
|
+
*/
|
538
|
+
|
539
|
+
function destroy_req(req) {
|
540
|
+
if(req)
|
541
|
+
destroy_response(req.response)
|
542
|
+
|
543
|
+
if(req && typeof req.destroy == 'function')
|
544
|
+
req.destroy()
|
545
|
+
}
|
546
|
+
|
547
|
+
function destroy_response(response) {
|
548
|
+
if(!response)
|
549
|
+
return;
|
550
|
+
|
551
|
+
if(typeof response.abort === 'function')
|
552
|
+
response.abort();
|
553
|
+
|
554
|
+
if(response.connection)
|
555
|
+
response.connection.destroy();
|
556
|
+
}
|