From bf901f42e8e44390394573a229a26a4f14d720cc Mon Sep 17 00:00:00 2001 From: Mark Bennett Date: Tue, 2 Apr 2013 18:15:37 -0600 Subject: [PATCH] Check-in components. It's an anti-pattern but I want to be able to deploy with GH pages. --- .gitignore | 1 - components/q/.coverignore | 1 + components/q/.gitignore | 9 + components/q/.jshintrc | 24 + components/q/.mailmap | 2 + components/q/.npmignore | 24 + components/q/.travis.yml | 4 + components/q/CHANGES.md | 632 ++++ components/q/LICENSE | 19 + components/q/README.md | 777 +++++ components/q/VERSIONS.md | 18 + components/q/component.json | 8 + components/q/design/README.js | 1026 +++++++ components/q/design/q0.js | 22 + components/q/design/q1.js | 26 + components/q/design/q2.js | 33 + components/q/design/q3.js | 30 + components/q/design/q4.js | 48 + components/q/design/q5.js | 56 + components/q/design/q6.js | 64 + components/q/design/q7.js | 126 + components/q/examples/all.js | 20 + components/q/examples/async-generators/0.html | 43 + .../q/examples/async-generators/1-return.html | 36 + .../async-generators/2-error-propagation.html | 37 + .../async-generators/3-wishful-thinking.html | 36 + .../q/examples/async-generators/4-shim.html | 36 + .../q/examples/async-generators/README.md | 66 + components/q/examples/delay.js | 13 + components/q/minify | 2 + components/q/package.json | 69 + components/q/q.js | 1481 ++++++++++ components/q/q.min.js | 18 + components/q/q.min.js.gz | Bin 0 -> 3135 bytes components/q/queue.js | 35 + components/q/ref_send.md | 26 + components/q/spec/aplus-adapter.js | 15 + .../q/spec/lib/jasmine-1.2.0/MIT.LICENSE | 20 + .../q/spec/lib/jasmine-1.2.0/jasmine-html.js | 616 ++++ .../q/spec/lib/jasmine-1.2.0/jasmine.css | 81 + .../q/spec/lib/jasmine-1.2.0/jasmine.js | 2529 +++++++++++++++++ components/q/spec/lib/jasmine-promise.js | 91 + components/q/spec/q-spec.html | 52 + components/q/spec/q-spec.js | 2332 +++++++++++++++ components/q/spec/queue-spec.js | 180 ++ components/reveal.js/.gitignore | 6 + components/reveal.js/.travis.yml | 5 + components/reveal.js/Gruntfile.js | 104 + components/reveal.js/LICENSE | 19 + components/reveal.js/README.md | 469 +++ components/reveal.js/component.json | 9 + components/reveal.js/css/print/paper.css | 176 ++ components/reveal.js/css/print/pdf.css | 164 ++ components/reveal.js/css/reveal.css | 1261 ++++++++ components/reveal.js/css/reveal.min.css | 7 + components/reveal.js/css/theme/README.md | 23 + components/reveal.js/css/theme/beige.css | 142 + components/reveal.js/css/theme/default.css | 142 + components/reveal.js/css/theme/moon.css | 142 + components/reveal.js/css/theme/night.css | 130 + components/reveal.js/css/theme/serif.css | 130 + components/reveal.js/css/theme/simple.css | 132 + components/reveal.js/css/theme/sky.css | 136 + components/reveal.js/css/theme/solarized.css | 142 + .../reveal.js/css/theme/source/beige.scss | 50 + .../reveal.js/css/theme/source/default.scss | 42 + .../reveal.js/css/theme/source/moon.scss | 68 + .../reveal.js/css/theme/source/night.scss | 35 + .../reveal.js/css/theme/source/serif.scss | 33 + .../reveal.js/css/theme/source/simple.scss | 38 + .../reveal.js/css/theme/source/sky.scss | 41 + .../reveal.js/css/theme/source/solarized.scss | 74 + .../reveal.js/css/theme/template/mixins.scss | 29 + .../css/theme/template/settings.scss | 33 + .../reveal.js/css/theme/template/theme.scss | 163 ++ components/reveal.js/index.html | 378 +++ components/reveal.js/js/reveal.js | 2087 ++++++++++++++ components/reveal.js/js/reveal.min.js | 8 + components/reveal.js/lib/css/zenburn.css | 115 + .../lib/font/league_gothic-webfont.eot | Bin 0 -> 18485 bytes .../lib/font/league_gothic-webfont.svg | 230 ++ .../lib/font/league_gothic-webfont.ttf | Bin 0 -> 42324 bytes .../lib/font/league_gothic-webfont.woff | Bin 0 -> 21288 bytes .../reveal.js/lib/font/league_gothic_license | 2 + components/reveal.js/lib/js/classList.js | 2 + components/reveal.js/lib/js/head.min.js | 8 + components/reveal.js/lib/js/html5shiv.js | 7 + components/reveal.js/package.json | 43 + .../reveal.js/plugin/highlight/highlight.js | 9 + .../reveal.js/plugin/markdown/example.html | 97 + .../reveal.js/plugin/markdown/example.md | 29 + .../reveal.js/plugin/markdown/markdown.js | 161 ++ .../reveal.js/plugin/markdown/showdown.js | 62 + .../reveal.js/plugin/multiplex/client.js | 13 + .../reveal.js/plugin/multiplex/index.js | 55 + .../reveal.js/plugin/multiplex/master.js | 32 + .../reveal.js/plugin/notes-server/client.js | 57 + .../reveal.js/plugin/notes-server/index.js | 58 + .../reveal.js/plugin/notes-server/notes.html | 139 + components/reveal.js/plugin/notes/notes.html | 252 ++ components/reveal.js/plugin/notes/notes.js | 100 + .../reveal.js/plugin/postmessage/example.html | 39 + .../plugin/postmessage/postmessage.js | 42 + .../reveal.js/plugin/print-pdf/print-pdf.js | 39 + .../reveal.js/plugin/remotes/remotes.js | 30 + components/reveal.js/plugin/search/search.js | 196 ++ components/reveal.js/plugin/zoom-js/zoom.js | 256 ++ 107 files changed, 19274 insertions(+), 1 deletion(-) create mode 100644 components/q/.coverignore create mode 100644 components/q/.gitignore create mode 100644 components/q/.jshintrc create mode 100644 components/q/.mailmap create mode 100644 components/q/.npmignore create mode 100644 components/q/.travis.yml create mode 100644 components/q/CHANGES.md create mode 100644 components/q/LICENSE create mode 100644 components/q/README.md create mode 100644 components/q/VERSIONS.md create mode 100644 components/q/component.json create mode 100644 components/q/design/README.js create mode 100644 components/q/design/q0.js create mode 100644 components/q/design/q1.js create mode 100644 components/q/design/q2.js create mode 100644 components/q/design/q3.js create mode 100644 components/q/design/q4.js create mode 100644 components/q/design/q5.js create mode 100644 components/q/design/q6.js create mode 100644 components/q/design/q7.js create mode 100644 components/q/examples/all.js create mode 100644 components/q/examples/async-generators/0.html create mode 100644 components/q/examples/async-generators/1-return.html create mode 100644 components/q/examples/async-generators/2-error-propagation.html create mode 100644 components/q/examples/async-generators/3-wishful-thinking.html create mode 100644 components/q/examples/async-generators/4-shim.html create mode 100644 components/q/examples/async-generators/README.md create mode 100644 components/q/examples/delay.js create mode 100644 components/q/minify create mode 100644 components/q/package.json create mode 100644 components/q/q.js create mode 100644 components/q/q.min.js create mode 100644 components/q/q.min.js.gz create mode 100644 components/q/queue.js create mode 100644 components/q/ref_send.md create mode 100644 components/q/spec/aplus-adapter.js create mode 100644 components/q/spec/lib/jasmine-1.2.0/MIT.LICENSE create mode 100644 components/q/spec/lib/jasmine-1.2.0/jasmine-html.js create mode 100644 components/q/spec/lib/jasmine-1.2.0/jasmine.css create mode 100644 components/q/spec/lib/jasmine-1.2.0/jasmine.js create mode 100644 components/q/spec/lib/jasmine-promise.js create mode 100644 components/q/spec/q-spec.html create mode 100644 components/q/spec/q-spec.js create mode 100644 components/q/spec/queue-spec.js create mode 100644 components/reveal.js/.gitignore create mode 100644 components/reveal.js/.travis.yml create mode 100644 components/reveal.js/Gruntfile.js create mode 100644 components/reveal.js/LICENSE create mode 100644 components/reveal.js/README.md create mode 100644 components/reveal.js/component.json create mode 100644 components/reveal.js/css/print/paper.css create mode 100644 components/reveal.js/css/print/pdf.css create mode 100644 components/reveal.js/css/reveal.css create mode 100644 components/reveal.js/css/reveal.min.css create mode 100644 components/reveal.js/css/theme/README.md create mode 100644 components/reveal.js/css/theme/beige.css create mode 100644 components/reveal.js/css/theme/default.css create mode 100644 components/reveal.js/css/theme/moon.css create mode 100644 components/reveal.js/css/theme/night.css create mode 100644 components/reveal.js/css/theme/serif.css create mode 100644 components/reveal.js/css/theme/simple.css create mode 100644 components/reveal.js/css/theme/sky.css create mode 100644 components/reveal.js/css/theme/solarized.css create mode 100644 components/reveal.js/css/theme/source/beige.scss create mode 100644 components/reveal.js/css/theme/source/default.scss create mode 100644 components/reveal.js/css/theme/source/moon.scss create mode 100644 components/reveal.js/css/theme/source/night.scss create mode 100644 components/reveal.js/css/theme/source/serif.scss create mode 100644 components/reveal.js/css/theme/source/simple.scss create mode 100644 components/reveal.js/css/theme/source/sky.scss create mode 100644 components/reveal.js/css/theme/source/solarized.scss create mode 100644 components/reveal.js/css/theme/template/mixins.scss create mode 100644 components/reveal.js/css/theme/template/settings.scss create mode 100644 components/reveal.js/css/theme/template/theme.scss create mode 100644 components/reveal.js/index.html create mode 100644 components/reveal.js/js/reveal.js create mode 100644 components/reveal.js/js/reveal.min.js create mode 100644 components/reveal.js/lib/css/zenburn.css create mode 100755 components/reveal.js/lib/font/league_gothic-webfont.eot create mode 100644 components/reveal.js/lib/font/league_gothic-webfont.svg create mode 100644 components/reveal.js/lib/font/league_gothic-webfont.ttf create mode 100644 components/reveal.js/lib/font/league_gothic-webfont.woff create mode 100644 components/reveal.js/lib/font/league_gothic_license create mode 100644 components/reveal.js/lib/js/classList.js create mode 100644 components/reveal.js/lib/js/head.min.js create mode 100644 components/reveal.js/lib/js/html5shiv.js create mode 100644 components/reveal.js/package.json create mode 100644 components/reveal.js/plugin/highlight/highlight.js create mode 100644 components/reveal.js/plugin/markdown/example.html create mode 100644 components/reveal.js/plugin/markdown/example.md create mode 100644 components/reveal.js/plugin/markdown/markdown.js create mode 100644 components/reveal.js/plugin/markdown/showdown.js create mode 100644 components/reveal.js/plugin/multiplex/client.js create mode 100644 components/reveal.js/plugin/multiplex/index.js create mode 100644 components/reveal.js/plugin/multiplex/master.js create mode 100644 components/reveal.js/plugin/notes-server/client.js create mode 100644 components/reveal.js/plugin/notes-server/index.js create mode 100644 components/reveal.js/plugin/notes-server/notes.html create mode 100644 components/reveal.js/plugin/notes/notes.html create mode 100644 components/reveal.js/plugin/notes/notes.js create mode 100644 components/reveal.js/plugin/postmessage/example.html create mode 100644 components/reveal.js/plugin/postmessage/postmessage.js create mode 100644 components/reveal.js/plugin/print-pdf/print-pdf.js create mode 100644 components/reveal.js/plugin/remotes/remotes.js create mode 100644 components/reveal.js/plugin/search/search.js create mode 100644 components/reveal.js/plugin/zoom-js/zoom.js diff --git a/.gitignore b/.gitignore index ff5551a..3c3629e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1 @@ node_modules -components diff --git a/components/q/.coverignore b/components/q/.coverignore new file mode 100644 index 0000000..1564ca0 --- /dev/null +++ b/components/q/.coverignore @@ -0,0 +1 @@ +spec/ diff --git a/components/q/.gitignore b/components/q/.gitignore new file mode 100644 index 0000000..2116335 --- /dev/null +++ b/components/q/.gitignore @@ -0,0 +1,9 @@ +node_modules +npm-debug.log +CHANGES.html +README.html +.tmp + +.coverage_data/ +.coverage_debug/ +cover_html/ diff --git a/components/q/.jshintrc b/components/q/.jshintrc new file mode 100644 index 0000000..a9b2adf --- /dev/null +++ b/components/q/.jshintrc @@ -0,0 +1,24 @@ +{ + "browser": true, + "node": true, + + "curly": true, + "eqeqeq": true, + "newcap": true, + "noarg": true, + "nonew": true, + "quotmark": "double", + "trailing": true, + "undef": true, + "unused": true, + + "globals": { + "bootstrap": false, + "cajaVM": false, + "define": false, + "Q": true, + "ReturnValue": false, + "ses": false, + "setImmediate": false + } +} diff --git a/components/q/.mailmap b/components/q/.mailmap new file mode 100644 index 0000000..281850a --- /dev/null +++ b/components/q/.mailmap @@ -0,0 +1,2 @@ +Domenic Denicola +Kris Kowal diff --git a/components/q/.npmignore b/components/q/.npmignore new file mode 100644 index 0000000..069dc12 --- /dev/null +++ b/components/q/.npmignore @@ -0,0 +1,24 @@ +node_modules +npm-debug.log +CHANGES.html +README.html + +.coverage_data/ +.coverage_debug/ +.tmp +cover_html/ + +design +examples +spec +.coverignore +.jshintrc +.mailmap +.npmignore +.travis.yml +CHANGES.md +VERSIONS.md +q.min.js +q.min.js.gz +ref_send.md +minify diff --git a/components/q/.travis.yml b/components/q/.travis.yml new file mode 100644 index 0000000..895dbd3 --- /dev/null +++ b/components/q/.travis.yml @@ -0,0 +1,4 @@ +language: node_js +node_js: + - 0.6 + - 0.8 diff --git a/components/q/CHANGES.md b/components/q/CHANGES.md new file mode 100644 index 0000000..fa94613 --- /dev/null +++ b/components/q/CHANGES.md @@ -0,0 +1,632 @@ + + +## 0.9.2 + + - Pass through progress notifications when using `timeout`. #229 @omares + - Pass through progress notifications when using `delay`. + - Fix `nbind` to actually bind the `thisArg`. #232 @davidpadbury + +## 0.9.1 + + - Made the AMD detection compatible with the RequireJS optimizer's `namespace` + option. #225 @terinjokes + - Fix side effects from `valueOf`, and thus from `isFulfilled`, `isRejected`, + and `isPending`. #226 @benjamn + +## 0.9.0 + +This release removes many layers of deprecated methods and brings Q closer to +alignment with Mark Miller’s TC39 [strawman][] for concurrency. At the same +time, it fixes many bugs and adds a few features around error handling. Finally, +it comes with an updated and comprehensive [API Reference][]. + +[strawman]: http://wiki.ecmascript.org/doku.php?id=strawman:concurrency +[API Reference]: https://github.com/kriskowal/q/wiki/API-Reference + +### API Cleanup + +The following deprecated or undocumented methods have been removed. +Their replacements are listed here: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
0.8.x method0.9 replacement
Q.refQ
call, apply, bind (*)fcall/invoke, fapply/post, fbind
ncall, napply (*)nfcall/ninvoke, nfapply/npost
enddone
putset
nodenbind
nendnodeify
isResolvedisPending
deferred.nodedeferred.makeNodeResolver
Method, senderdispatcher
senddispatch
view, viewInfo(none)
+ + +(*) Use of ``thisp`` is discouraged. For calling methods, use ``post`` or +``invoke``. + +### Alignment with the Concurrency Strawman + +- Q now exports a `Q(value)` function, an alias for `resolve`. + `Q.call`, `Q.apply`, and `Q.bind` were removed to make room for the + same methods on the function prototype. +- `invoke` has been aliased to `send` in all its forms. +- `post` with no method name acts like `fapply`. + +### Error Handling + +- Long stack traces can be turned off by setting `Q.stackJumpLimit` to zero. + In the future, this property will be used to fine tune how many stack jumps + are retained in long stack traces; for now, anything nonzero is treated as + one (since Q only tracks one stack jump at the moment, see #144). #168 +- In Node.js, if there are unhandled rejections when the process exits, they + are output to the console. #115 + +### Other + +- `delete` and `set` (née `put`) no longer have a fulfillment value. +- Q promises are no longer frozen, which + [helps with performance](http://code.google.com/p/v8/issues/detail?id=1858). +- `thenReject` is now included, as a counterpart to `thenResolve`. +- The included browser `nextTick` shim is now faster. #195 @rkatic. + +### Bug Fixes + +- Q now works in Internet Explorer 10. #186 @ForbesLindesay +- `fbind` no longer hard-binds the returned function's `this` to `undefined`. + #202 +- `Q.reject` no longer leaks memory. #148 +- `npost` with no arguments now works. #207 +- `allResolved` now works with non-Q promises ("thenables"). #179 +- `keys` behavior is now correct even in browsers without native + `Object.keys`. #192 @rkatic +- `isRejected` and the `exception` property now work correctly if the + rejection reason is falsy. #198 + +### Internals and Advanced + +- The internal interface for a promise now uses + `dispatchPromise(resolve, op, operands)` instead of `sendPromise(op, + resolve, ...operands)`, which reduces the cases where Q needs to do + argument slicing. +- The internal protocol uses different operands. "put" is now "set". + "del" is now "delete". "view" and "viewInfo" have been removed. +- `Q.fulfill` has been added. It is distinct from `Q.resolve` in that + it does not pass promises through, nor coerces promises from other + systems. The promise becomes the fulfillment value. This is only + recommended for use when trying to fulfill a promise with an object that has + a `then` function that is at the same time not a promise. + +## 0.8.12 +- Treat foreign promises as unresolved in `Q.isFulfilled`; this lets `Q.all` + work on arrays containing foreign promises. #154 +- Fix minor incompliances with the [Promises/A+ spec][] and [test suite][]. #157 + #158 + +[Promises/A+ spec]: http://promises-aplus.github.com/promises-spec/ +[test suite]: https://github.com/promises-aplus/promises-tests + +## 0.8.11 + + - Added ``nfcall``, ``nfapply``, and ``nfbind`` as ``thisp``-less versions of + ``ncall``, ``napply``, and ``nbind``. The latter are now deprecated. #142 + - Long stack traces no longer cause linearly-growing memory usage when chaining + promises together. #111 + - Inspecting ``error.stack`` in a rejection handler will now give a long stack + trace. #103 + - Fixed ``Q.timeout`` to clear its timeout handle when the promise is rejected; + previously, it kept the event loop alive until the timeout period expired. + #145 @dfilatov + - Added `q/queue` module, which exports an infinite promise queue + constructor. + +## 0.8.10 + + - Added ``done`` as a replacement for ``end``, taking the usual fulfillment, + rejection, and progress handlers. It's essentially equivalent to + ``then(f, r, p).end()``. + - Added ``Q.onerror``, a settable error trap that you can use to get full stack + traces for uncaught errors. #94 + - Added ``thenResolve`` as a shortcut for returning a constant value once a + promise is fulfilled. #108 @ForbesLindesay + - Various tweaks to progress notification, including propagation and + transformation of progress values and only forwarding a single progress + object. + - Renamed ``nend`` to ``nodeify``. It no longer returns an always-fulfilled + promise when a Node callback is passed. + - ``deferred.resolve`` and ``deferred.reject`` no longer (sometimes) return + ``deferred.promise``. + - Fixed stack traces getting mangled if they hit ``end`` twice. #116 #121 @ef4 + - Fixed ``ninvoke`` and ``npost`` to work on promises for objects with Node + methods. #134 + - Fixed accidental coercion of objects with nontrivial ``valueOf`` methods, + like ``Date``s, by the promise's ``valueOf`` method. #135 + - Fixed ``spread`` not calling the passed rejection handler if given a rejected + promise. + +## 0.8.9 + + - Added ``nend`` + - Added preliminary progress notification support, via + ``promise.then(onFulfilled, onRejected, onProgress)``, + ``promise.progress(onProgress)``, and ``deferred.notify(...progressData)``. + - Made ``put`` and ``del`` return the object acted upon for easier chaining. + #84 + - Fixed coercion cycles with cooperating promises. #106 + +## 0.8.7 + + - Support [Montage Require](http://github.com/kriskowal/mr) + +## 0.8.6 + + - Fixed ``npost`` and ``ninvoke`` to pass the correct ``thisp``. #74 + - Fixed various cases involving unorthodox rejection reasons. #73 #90 + @ef4 + - Fixed double-resolving of misbehaved custom promises. #75 + - Sped up ``Q.all`` for arrays contain already-resolved promises or scalar + values. @ForbesLindesay + - Made stack trace filtering work when concatenating assets. #93 @ef4 + - Added warnings for deprecated methods. @ForbesLindesay + - Added ``.npmignore`` file so that dependent packages get a slimmer + ``node_modules`` directory. + +## 0.8.5 + + - Added preliminary support for long traces (@domenic) + - Added ``fapply``, ``fcall``, ``fbind`` for non-thisp + promised function calls. + - Added ``return`` for async generators, where generators + are implemented. + - Rejected promises now have an "exception" property. If an object + isRejected(object), then object.valueOf().exception will + be the wrapped error. + - Added Jasmine specifications + - Support Internet Explorers 7–9 (with multiple bug fixes @domenic) + - Support Firefox 12 + - Support Safari 5.1.5 + - Support Chrome 18 + +## 0.8.4 + + - WARNING: ``promise.timeout`` is now rejected with an ``Error`` object + and the message now includes the duration of the timeout in + miliseconds. This doesn't constitute (in my opinion) a + backward-incompatibility since it is a change of an undocumented and + unspecified public behavior, but if you happened to depend on the + exception being a string, you will need to revise your code. + - Added ``deferred.makeNodeResolver()`` to replace the more cryptic + ``deferred.node()`` method. + - Added experimental ``Q.promise(maker(resolve, reject))`` to make a + promise inside a callback, such that thrown exceptions in the + callback are converted and the resolver and rejecter are arguments. + This is a shorthand for making a deferred directly and inspired by + @gozala’s stream constructor pattern and the Microsoft Windows Metro + Promise constructor interface. + - Added experimental ``Q.begin()`` that is intended to kick off chains + of ``.then`` so that each of these can be reordered without having to + edit the new and former first step. + +## 0.8.3 + + - Added ``isFulfilled``, ``isRejected``, and ``isResolved`` + to the promise prototype. + - Added ``allResolved`` for waiting for every promise to either be + fulfilled or rejected, without propagating an error. @utvara #53 + - Added ``Q.bind`` as a method to transform functions that + return and throw into promise-returning functions. See + [an example](https://gist.github.com/1782808). @domenic + - Renamed ``node`` export to ``nbind``, and added ``napply`` to + complete the set. ``node`` remains as deprecated. @domenic #58 + - Renamed ``Method`` export to ``sender``. ``Method`` + remains as deprecated and will be removed in the next + major version since I expect it has very little usage. + - Added browser console message indicating a live list of + unhandled errors. + - Added support for ``msSetImmediate`` (IE10) or ``setImmediate`` + (available via [polyfill](https://github.com/NobleJS/setImmediate)) + as a browser-side ``nextTick`` implementation. #44 #50 #59 + - Stopped using the event-queue dependency, which was in place for + Narwhal support: now directly using ``process.nextTick``. + - WARNING: EXPERIMENTAL: added ``finally`` alias for ``fin``, ``catch`` + alias for ``fail``, ``try`` alias for ``call``, and ``delete`` alias + for ``del``. These properties are enquoted in the library for + cross-browser compatibility, but may be used as property names in + modern engines. + +## 0.8.2 + + - Deprecated ``ref`` in favor of ``resolve`` as recommended by + @domenic. + - Update event-queue dependency. + +## 0.8.1 + + - Fixed Opera bug. #35 @cadorn + - Fixed ``Q.all([])`` #32 @domenic + +## 0.8.0 + + - WARNING: ``enqueue`` removed. Use ``nextTick`` instead. + This is more consistent with NodeJS and (subjectively) + more explicit and intuitive. + - WARNING: ``def`` removed. Use ``master`` instead. The + term ``def`` was too confusing to new users. + - WARNING: ``spy`` removed in favor of ``fin``. + - WARNING: ``wait`` removed. Do ``all(args).get(0)`` instead. + - WARNING: ``join`` removed. Do ``all(args).spread(callback)`` instead. + - WARNING: Removed the ``Q`` function module.exports alias + for ``Q.ref``. It conflicts with ``Q.apply`` in weird + ways, making it uncallable. + - Revised ``delay`` so that it accepts both ``(value, + timeout)`` and ``(timeout)`` variations based on + arguments length. + - Added ``ref().spread(cb(...args))``, a variant of + ``then`` that spreads an array across multiple arguments. + Useful with ``all()``. + - Added ``defer().node()`` Node callback generator. The + callback accepts ``(error, value)`` or ``(error, + ...values)``. For multiple value arguments, the + fulfillment value is an array, useful in conjunction with + ``spread``. + - Added ``node`` and ``ncall``, both with the signature + ``(fun, thisp_opt, ...args)``. The former is a decorator + and the latter calls immediately. ``node`` optional + binds and partially applies. ``ncall`` can bind and pass + arguments. + +## 0.7.2 + + - Fixed thenable promise assimilation. + +## 0.7.1 + + - Stopped shimming ``Array.prototype.reduce``. The + enumerable property has bad side-effects. Libraries that + depend on this (for example, QQ) will need to be revised. + +## 0.7.0 - BACKWARD INCOMPATIBILITY + + - WARNING: Removed ``report`` and ``asap`` + - WARNING: The ``callback`` argument of the ``fin`` + function no longer receives any arguments. Thus, it can + be used to call functions that should not receive + arguments on resolution. Use ``when``, ``then``, or + ``fail`` if you need a value. + - IMPORTANT: Fixed a bug in the use of ``MessageChannel`` + for ``nextTick``. + - Renamed ``enqueue`` to ``nextTick``. + - Added experimental ``view`` and ``viewInfo`` for creating + views of promises either when or before they're + fulfilled. + - Shims are now externally applied so subsequent scripts or + dependees can use them. + - Improved minification results. + - Improved readability. + +## 0.6.0 - BACKWARD INCOMPATIBILITY + + - WARNING: In practice, the implementation of ``spy`` and + the name ``fin`` were useful. I've removed the old + ``fin`` implementation and renamed/aliased ``spy``. + - The "q" module now exports its ``ref`` function as a "Q" + constructor, with module systems that support exports + assignment including NodeJS, RequireJS, and when used as + a `` + + + +
+ + diff --git a/components/q/examples/async-generators/1-return.html b/components/q/examples/async-generators/1-return.html new file mode 100644 index 0000000..277fa7e --- /dev/null +++ b/components/q/examples/async-generators/1-return.html @@ -0,0 +1,36 @@ + + + + + + + + +
+ + diff --git a/components/q/examples/async-generators/2-error-propagation.html b/components/q/examples/async-generators/2-error-propagation.html new file mode 100644 index 0000000..efadf7e --- /dev/null +++ b/components/q/examples/async-generators/2-error-propagation.html @@ -0,0 +1,37 @@ + + + + + + + + +
+ + diff --git a/components/q/examples/async-generators/3-wishful-thinking.html b/components/q/examples/async-generators/3-wishful-thinking.html new file mode 100644 index 0000000..3341ce7 --- /dev/null +++ b/components/q/examples/async-generators/3-wishful-thinking.html @@ -0,0 +1,36 @@ + + + + + + + + +
+ + diff --git a/components/q/examples/async-generators/4-shim.html b/components/q/examples/async-generators/4-shim.html new file mode 100644 index 0000000..e01d898 --- /dev/null +++ b/components/q/examples/async-generators/4-shim.html @@ -0,0 +1,36 @@ + + + + + + + + +
+ + diff --git a/components/q/examples/async-generators/README.md b/components/q/examples/async-generators/README.md new file mode 100644 index 0000000..c342e61 --- /dev/null +++ b/components/q/examples/async-generators/README.md @@ -0,0 +1,66 @@ + +/!\ Warning: The behavior described here is likely to be quickly +obseleted by developments in standardization and implementation. Tread +with care. + +Q has an ``async`` function. This can be used to +decorate a generator function such that ``yield`` is +effectively equivalent to ``await`` or ``defer`` syntax as +supported by languages like Go and, reportedly, C#3. + +Generator functions are presently only supported by SpiderMonkey, but +they are (with some changes) on standards track, and very similar down +to details to generators in Python. + + function count() { + var i = 0; + while (true) { + yield i++; + } + } + + var counter = count(); + count.next() === 0; + count.next() === 1; + count.next() === 2; + +``yield`` can also return a value, if the ``send`` method of +a generator is used instead of ``next``. + + var buffer = (function () { + var x; + while (true) { + x = yield x; + } + }()); + + buffer.send(1) === undefined + buffer.send("a") === 1 + buffer.send(2) === "a" + buffer.next() === 2 + buffer.next() === undefined + buffer.next() === undefined + +We can use ``yield`` to wait for a promise to resolve. + + var eventualAdd = Q.async(function (oneP, twoP) { + var one = yield oneP; + var two = yield twoP; + return one + two; + }); + + eventualAdd(eventualOne, eventualTwo) + .then(function (three) { + three === 3; + }); + +Or, at least we could. For now, SpiderMonkey does not allow +return values in generators. When they do, ``Q.async`` will +properly fulfill the result of eventualAdd. Until then, +``eventualAdd`` will resolve to ``undefined`` when the job +is done, when the generator throws ``StopIteration``. + +As a stop-gap, you can fake the return value by tacking a +``value`` property on an explicitly thrown +``StopIteration``, as in Example 1, in this directory. + diff --git a/components/q/examples/delay.js b/components/q/examples/delay.js new file mode 100644 index 0000000..6871f95 --- /dev/null +++ b/components/q/examples/delay.js @@ -0,0 +1,13 @@ + +var Q = require("q"); + +var delay = function (delay) { + var d = Q.defer(); + setTimeout(d.resolve, delay); + return d.promise; +}; + +Q.when(delay(1000), function () { + console.log('Hello, World!'); +}); + diff --git a/components/q/minify b/components/q/minify new file mode 100644 index 0000000..898dc3e --- /dev/null +++ b/components/q/minify @@ -0,0 +1,2 @@ +closure < q.js > q.min.js +gzip < q.min.js > q.min.js.gz diff --git a/components/q/package.json b/components/q/package.json new file mode 100644 index 0000000..7148c99 --- /dev/null +++ b/components/q/package.json @@ -0,0 +1,69 @@ +{ + "name": "q", + "version": "0.9.2", + "description": "A library for promises (CommonJS/Promises/A,B,D)", + "homepage": "https://github.com/kriskowal/q", + "author": "Kris Kowal (https://github.com/kriskowal)", + "keywords": [ + "q", + "promise", + "promises", + "promises-a", + "promises-a-plus", + "deferred", + "future", + "async", + "flow control", + "fluent", + "browser", + "node" + ], + "contributors": [ + "Kris Kowal (https://github.com/kriskowal)", + "Irakli Gozalishvili (http://jeditoolkit.com)", + "Domenic Denicola (http://domenicdenicola.com)" + ], + "bugs": { + "mail": "kris@cixar.com", + "url": "http://github.com/kriskowal/q/issues" + }, + "licenses": [ + { + "type": "MIT", + "url": "http://github.com/kriskowal/q/raw/master/LICENSE" + } + ], + "main": "q.js", + "repository": { + "type": "git", + "url": "git://github.com/kriskowal/q.git" + }, + "engines": { + "node": ">=0.6.0", + "teleport": ">=0.2.0" + }, + "dependencies": {}, + "devDependencies": { + "jshint": ">=1.1.0", + "cover": "*", + "jasmine-node": "1.2.2", + "opener": "*", + "promises-aplus-tests": "1.x" + }, + "scripts": { + "test": "jasmine-node spec && promises-aplus-tests spec/aplus-adapter", + "test-browser": "opener spec/q-spec.html", + "lint": "jshint q.js", + "cover": "cover run node_modules/jasmine-node/bin/jasmine-node spec && cover report html && opener cover_html/index.html" + }, + "overlay": { + "teleport": { + "dependencies": { + "system": ">=0.0.4" + } + } + }, + "directories": { + "test": "./spec" + } +} diff --git a/components/q/q.js b/components/q/q.js new file mode 100644 index 0000000..2a644aa --- /dev/null +++ b/components/q/q.js @@ -0,0 +1,1481 @@ +// vim:ts=4:sts=4:sw=4: +/*! + * + * Copyright 2009-2012 Kris Kowal under the terms of the MIT + * license found at http://github.com/kriskowal/q/raw/master/LICENSE + * + * With parts by Tyler Close + * Copyright 2007-2009 Tyler Close under the terms of the MIT X license found + * at http://www.opensource.org/licenses/mit-license.html + * Forked at ref_send.js version: 2009-05-11 + * + * With parts by Mark Miller + * Copyright (C) 2011 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +(function (definition) { + // Turn off strict mode for this function so we can assign to global.Q + /*jshint strict: false*/ + + // This file will function properly as a + + + + + + + + + + + + + + + + diff --git a/components/q/spec/q-spec.js b/components/q/spec/q-spec.js new file mode 100644 index 0000000..a69a92e --- /dev/null +++ b/components/q/spec/q-spec.js @@ -0,0 +1,2332 @@ +"use strict"; +/*jshint newcap: false*/ +/*global Q: true, describe: false, it: false, expect: false, beforeEach: false, + afterEach: false, require: false, jasmine: false, waitsFor: false, + runs: false */ + +if (typeof Q === "undefined" && typeof require !== "undefined") { + // For Node compatibility. + global.Q = require("../q"); + require("./lib/jasmine-promise"); +} + +var REASON = "this is not an error, but it might show up in the console"; + +// In browsers that support strict mode, it'll be `undefined`; otherwise, the global. +var calledAsFunctionThis = (function () { return this; }()); + +afterEach(function () { + Q.onerror = null; +}); + +describe("Q function", function () { + it("should result in a fulfilled promise when given a value", function () { + expect(Q(5).isFulfilled()).toBe(true); + }); + + it("should be the identity when given promise", function () { + var f = Q.fulfill(5); + var r = Q.reject(new Error("aaargh")); + var p = Q.promise(); + + expect(Q(f)).toBe(f); + expect(Q(r)).toBe(r); + expect(Q(p)).toBe(p); + }); +}); + +describe("defer and when", function () { + + it("resolve before when", function () { + var turn = 0; + var deferred = Q.defer(); + deferred.resolve(10); + var promise = Q.when(deferred.promise, function (value) { + expect(turn).toEqual(1); + expect(value).toEqual(10); + }); + turn++; + return promise; + }); + + it("reject before when", function () { + var turn = 0; + var deferred = Q.defer(); + deferred.reject(-1); + var promise = Q.when(deferred.promise, function () { + expect(true).toBe(false); + }, function (value) { + expect(turn).toEqual(1); + expect(value).toEqual(-1); + }); + turn++; + return promise; + }); + + it("when before resolve", function () { + var turn = 0; + var deferred = Q.defer(); + var promise = deferred.promise.then(function (value) { + expect(turn).toEqual(2); + expect(value).toEqual(10); + turn++; + }); + Q.nextTick(function () { + expect(turn).toEqual(1); + deferred.resolve(10); + turn++; + }); + turn++; + return promise; + }); + + it("when before reject", function () { + var turn = 0; + var deferred = Q.defer(); + var promise = deferred.promise.then(function () { + expect(true).toBe(false); + }, function (value) { + expect(turn).toEqual(2); + expect(value).toEqual(-1); + turn++; + }); + Q.nextTick(function () { + expect(turn).toEqual(1); + deferred.reject(-1); + turn++; + }); + turn++; + return promise; + }); + + it("resolves multiple observers", function (done) { + var nextTurn = false; + + var resolution = 'Taram pam param!'; + var deferred = Q.defer(); + var count = 10; + var i = 0; + + function resolve(value) { + i++; + expect(value).toBe(resolution); + expect(nextTurn).toBe(true); + if (i === count) { + done(); + } + } + + while (++i <= count) { + Q.when(deferred.promise, resolve); + } + + deferred.resolve(resolution); + i = 0; + nextTurn = true; + }); + + it("observers called even after throw", function () { + var threw = false; + var deferred = Q.defer(); + Q.when(deferred.promise, function () { + threw = true; + throw new Error(REASON); + }); + var promise = Q.when(deferred.promise, function (value) { + expect(value).toEqual(10); + }, function () { + expect("not").toEqual("here"); + }); + deferred.resolve(10); + return promise; + }); + + it("returns `undefined` from the deferred's methods", function () { + expect(Q.defer().resolve()).toBe(undefined); + expect(Q.defer().reject()).toBe(undefined); + }); + +}); + +describe("always next tick", function () { + + it("generated by `resolve`", function () { + var turn = 0; + var promise = Q.when(Q.resolve(), function () { + expect(turn).toEqual(1); + }); + turn++; + return promise; + }); + + it("generated by `reject`", function () { + var turn = 0; + var promise = Q.when(Q.reject(), function () { + expect(true).toBe(false); + }, function () { + expect(turn).toEqual(1); + }); + turn++; + return promise; + }); + +}); + +describe("progress", function () { + + it("calls a single progress listener", function () { + var progressed = false; + var deferred = Q.defer(); + + var promise = Q.when( + deferred.promise, + function () { + expect(progressed).toBe(true); + }, + function () { + expect(true).toBe(false); + }, + function () { + progressed = true; + } + ); + + deferred.notify(); + deferred.resolve(); + + return promise; + }); + + it("calls multiple progress listeners", function () { + var progressed1 = false; + var progressed2 = false; + var deferred = Q.defer(); + var promise = Q.when( + deferred.promise, + function () { + expect(progressed1).toBe(true); + expect(progressed2).toBe(true); + }, + function () { + expect(true).toBe(false); + }, + function () { + progressed1 = true; + } + ); + Q.when(deferred.promise, null, null, function () { + progressed2 = true; + }); + + deferred.notify(); + deferred.resolve(); + + return promise; + }); + + it("calls all progress listeners even if one throws", function () { + var progressed1 = false; + var progressed2 = false; + var progressed3 = false; + var deferred = Q.defer(); + var promise = Q.when( + deferred.promise, + function () { + expect(progressed1).toBe(true); + expect(progressed2).toBe(true); + expect(progressed3).toBe(true); + }, + function () { + expect(true).toBe(false); + }, + function () { + progressed1 = true; + } + ); + + Q.onerror = function () { }; + + Q.when(deferred.promise, null, null, function () { + progressed2 = true; + throw new Error("just a test, ok if it shows up in the console"); + }); + Q.when(deferred.promise, null, null, function () { + progressed3 = true; + }); + + deferred.notify(); + deferred.resolve(); + + return promise; + }); + + it("calls the progress listener even if later rejected", function () { + var progressed = false; + var deferred = Q.defer(); + var promise = Q.when( + deferred.promise, + function () { + expect(true).toBe(false); + }, + function () { + expect(progressed).toEqual(true); + }, + function () { + progressed = true; + } + ); + + deferred.notify(); + deferred.reject(); + + return promise; + }); + + it("calls the progress listener with the notify values", function () { + var progressValues = []; + var desiredProgressValues = [{}, {}, "foo", 5]; + var deferred = Q.defer(); + var promise = Q.when( + deferred.promise, + function () { + for (var i = 0; i < desiredProgressValues.length; ++i) { + var desired = desiredProgressValues[i]; + var actual = progressValues[i]; + expect(actual).toBe(desired); + } + }, + function () { + expect(true).toBe(false); + }, + function (value) { + progressValues.push(value); + } + ); + + for (var i = 0; i < desiredProgressValues.length; ++i) { + deferred.notify(desiredProgressValues[i]); + } + deferred.resolve(); + + return promise; + }); + + it("does not call the progress listener if notify is called after fulfillment", function () { + var deferred = Q.defer(); + var called = false; + + Q.when(deferred.promise, null, null, function () { + called = true; + }); + + deferred.resolve(); + deferred.notify(); + + return Q.delay(10).then(function () { + expect(called).toBe(false); + }); + }); + + it("does not call the progress listener if notify is called after rejection", function () { + var deferred = Q.defer(); + var called = false; + + Q.when(deferred.promise, null, null, function () { + called = true; + }); + + deferred.reject(); + deferred.notify(); + + return Q.delay(10).then(function () { + expect(called).toBe(false); + }); + }); + + it("should not save and re-emit progress notifications", function () { + var deferred = Q.defer(); + var progressValues = []; + + deferred.notify(1); + + var promise = Q.when( + deferred.promise, + function () { + expect(progressValues).toEqual([2]); + }, + function () { + expect(true).toBe(false); + }, + function (progressValue) { + progressValues.push(progressValue); + } + ); + + deferred.notify(2); + deferred.resolve(); + + return promise; + }); + + it("should allow attaching progress listeners w/ .progress", function () { + var progressed = false; + var deferred = Q.defer(); + + deferred.promise.progress(function () { + progressed = true; + }); + + deferred.notify(); + deferred.resolve(); + + return deferred.promise; + }); + + it("should allow attaching progress listeners w/ Q.progress", function () { + var progressed = false; + var deferred = Q.defer(); + + Q.progress(deferred.promise, function () { + progressed = true; + }); + + deferred.notify(); + deferred.resolve(); + + return deferred.promise; + }); + + it("should call the progress listener with undefined context", function () { + var progressed = false; + var progressContext = {}; + var deferred = Q.defer(); + var promise = Q.when( + deferred.promise, + function () { + expect(progressed).toBe(true); + expect(progressContext).toBe(calledAsFunctionThis); + }, + function () { + expect(true).toBe(false); + }, + function () { + progressed = true; + progressContext = this; + } + ); + + deferred.notify(); + deferred.resolve(); + + return promise; + }); + + it("should forward only the first notify argument to listeners", function () { + var progressValueArrays = []; + var deferred = Q.defer(); + + var promise = Q.when( + deferred.promise, + function () { + expect(progressValueArrays).toEqual([[1], [2], [4]]); + }, + function () { + expect(true).toBe(false); + }, + function () { + var args = Array.prototype.slice.call(arguments); + progressValueArrays.push(args); + } + ); + + deferred.notify(1); + deferred.notify(2, 3); + deferred.notify(4, 5, 6); + deferred.resolve(); + + return promise; + }); + + it("should work with .then as well", function () { + var progressed = false; + var deferred = Q.defer(); + + var promise = deferred.promise.then( + function () { + expect(progressed).toBe(true); + }, + function () { + expect(true).toBe(false); + }, + function () { + progressed = true; + } + ); + + deferred.notify(); + deferred.resolve(); + + return promise; + }); + + it("should re-throw all errors thrown by listeners to Q.onerror", function () { + var theError = new Error("boo!"); + + var def = Q.defer(); + def.promise.progress(function () { + throw theError; + }); + + var deferred = Q.defer(); + Q.onerror = function (error) { + expect(error).toBe(theError); + deferred.resolve(); + }; + Q.delay(100).then(deferred.reject); + + def.notify(); + + return deferred.promise; + }); +}); + +describe("promises for objects", function () { + + describe("get", function () { + + it("fulfills a promise", function () { + var deferred = Q.defer(); + deferred.resolve({a: 1}); + return deferred.promise.get("a") + .then(function (a) { + expect(a).toBe(1); + }); + }); + + it("propagates a rejection", function () { + var exception = new Error("boo!"); + return Q.fcall(function () { + throw exception; + }) + .get("a") + .then(function () { + expect("be").toBe("not to be"); + }, function (_exception) { + expect(_exception).toBe(exception); + }); + }); + + }); + + describe("set", function () { + + it("fulfills a promise", function () { + var object = {}; + return Q.resolve(object) + .set('a', 1) + .then(function (result) { + expect(result).toBe(undefined); + expect(object.a).toBe(1); + }); + }); + + it("propagates a rejection", function () { + var exception = new Error("Gah!"); + return Q.reject(exception) + .set("a", 1) + .then(function (result) { + expect("frozen over").toBe("quite warm"); + }, function (_exception) { + expect(_exception).toBe(exception); + }); + }); + + }); + + describe("del", function () { + + it("fulfills a promise", function () { + var object = {a: 10}; + return Q.fcall(function () { + return object; + }) + .del('a') + .then(function (result) { + expect('a' in object).toBe(false); + expect(result).toBe(void 0); + }, function (exception) { + expect("up").toBe("down"); + }); + }); + + it("propagates a rejection", function () { + var exception = new Error("hah-hah"); + return Q.fcall(function () { + throw exception; + }) + .del('a') + .then(function () { + expect(true).toBe(false); + }, function (_exception) { + expect(_exception).toBe(exception); + }); + }); + + }); + + describe("post", function () { + + it("fulfills a promise", function () { + var subject = { + a: function a(value) { + this._a = value; + return 1 + value; + } + }; + return Q.when(Q.post(subject, 'a', [1]), function (two) { + expect(subject._a).toBe(1); + expect(two).toBe(2); + }); + }); + + it("works as apply when given no name", function () { + return Q.resolve(function (a, b, c) { + return a + b + c; + }) + .post(undefined, [1, 2, 3]) + .then(function (sum) { + expect(sum).toEqual(6); + }); + }); + + }); + + describe("send", function () { + + it("fulfills a promise", function () { + var foo; + var subject = { + foo: function (_bar) { + return _bar; + }, + bar: function (_foo, _bar) { + foo = _foo; + return this.foo(_bar); + } + }; + return Q.send(subject, 'bar', 1, 2) + .then(function (two) { + expect(foo).toEqual(1); + expect(two).toEqual(2); + }); + }); + + it("is rejected for undefined method", function () { + var subject = {}; + return Q.resolve(subject) + .send('foo') + .then(function () { + expect("here").toEqual("not here"); + }, function (exception) { + }); + }); + + it("is rejected for undefined object", function () { + return Q.resolve() + .send('foo') + .then(function () { + expect("here").toEqual("not here"); + }, function (exception) { + }); + }); + + }); + + describe("keys", function () { + + function Klass (a, b) { + this.a = a; + this.b = b; + } + Klass.prototype.notOwn = 1; + + it("fulfills a promise", function () { + return Q.keys(new Klass(10, 20)) + .then(function (keys) { + expect(keys.sort()).toEqual(['a', 'b']); + }); + }); + + }); + +}); + +describe("promises for functions", function () { + + describe("fapply", function () { + it("fulfills a promise using arguments", function () { + return Q.resolve(function (a, b, c) { + return a + b + c; + }) + .fapply([1, 2, 3]) + .then(function (sum) { + expect(sum).toEqual(6); + }); + }); + }); + + describe("fcall", function () { + it("fulfills a promise using arguments", function () { + return Q.resolve(function (a, b, c) { + return a + b + c; + }) + .fcall(1, 2, 3) + .then(function (sum) { + expect(sum).toEqual(6); + }); + }); + }); + + describe("fbind", function () { + + it("accepts a promise for a function", function () { + return Q.fbind(Q.resolve(function (high, low) { + return high - low; + })) + (2, 1) + .then(function (difference) { + expect(difference).toEqual(1); + }); + }); + + it("chains partial application on a promise for a function", function () { + return Q.resolve(function (a, b) { + return a * b; + }) + .fbind(2)(3) + .then(function (product) { + expect(product).toEqual(6); + }); + }); + + it("returns a fulfilled promise", function () { + var result = {}; + var bound = Q.fbind(function () { + return result; + }); + return bound() + .then(function (_result) { + expect(_result).toBe(result); + }); + }); + + it("returns a rejected promise from a thrown error", function () { + var exception = new Error("Boo!"); + var bound = Q.fbind(function () { + throw exception; + }); + return bound() + .then(function () { + expect("flying pigs").toBe("swillin' pigs"); + }, function (_exception) { + expect(_exception).toBe(exception); + }); + }); + + it("passes arguments through", function () { + var x = {}, y = {}; + var bound = Q.fbind(function (a, b) { + expect(a).toBe(x); + expect(b).toBe(y); + }); + return bound(x, y); + }); + + it("passes and also partially applies arguments", function () { + var x = {}, y = {}; + var bound = Q.fbind(function (a, b) { + expect(a).toBe(x); + expect(b).toBe(y); + }, x); + return bound(y); + }); + + it("doesn't bind `this`", function () { + var theThis = { me: "this" }; + var bound = Q.fbind(function () { + expect(this).toBe(theThis); + }); + + return bound.call(theThis); + }); + + }); + +}); + +describe("valueOf", function () { + + it("of fulfillment", function () { + expect(Q.resolve(10).valueOf()).toEqual(10); + }); + + it("of rejection", function () { + var error = new Error("In your face."); + var rejection = Q.reject(error); + expect(rejection.valueOf()).toBe(rejection); + expect(rejection.valueOf().exception).toBe(error); + }); + + it("of deferred", function () { + var deferred = Q.defer(); + expect(deferred.promise.valueOf()).toBe(deferred.promise); + }); + + it("of deferred rejection", function () { + var deferred = Q.defer(); + var error = new Error("Rejected!"); + var rejection = Q.reject(error); + deferred.resolve(rejection); + expect(deferred.promise.valueOf()).toBe(rejection); + expect(deferred.promise.valueOf().exception).toBe(error); + }); + + it("of deferred fulfillment", function () { + var deferred = Q.defer(); + deferred.fulfill(10); + expect(deferred.promise.valueOf()).toBe(10); + }); + + it("of deferred deferred", function () { + var a = Q.defer(); + var b = Q.defer(); + a.resolve(b.promise); + expect(a.promise.valueOf()).toBe(b.promise); + }); + + it("should not convert `Date` instances to milliseconds", function () { + var promise = Q.resolve(new Date(2012, 10, 4)); + expect(promise.valueOf()).toEqual(new Date(2012, 10, 4)); + }); + +}); + +describe("promise states", function () { + + it("of fulfilled value", function () { + expect(Q.isFulfilled(void 0)).toBe(true); + expect(Q.isRejected(false)).toBe(false); + expect(Q.isPending(true)).toBe(false); + }); + + it("of fulfillment", function () { + var promise = Q.resolve(10); + expect(Q.isFulfilled(promise)).toBe(true); + expect(promise.isFulfilled()).toBe(true); + expect(Q.isRejected(promise)).toBe(false); + expect(promise.isRejected()).toBe(false); + expect(Q.isPending(promise)).toBe(false); + expect(promise.isPending()).toBe(false); + }); + + it("of rejection", function () { + var error = new Error("Oh, snap."); + var promise = Q.reject(error); + expect(promise.isFulfilled()).toBe(false); + expect(promise.isRejected()).toBe(true); + expect(promise.isPending()).toBe(false); + }); + + it("of rejection with a falsy value", function () { + var promise = Q.reject(undefined); + expect(promise.isFulfilled()).toBe(false); + expect(promise.isRejected()).toBe(true); + expect(promise.isPending()).toBe(false); + }); + + it("of deferred", function () { + var deferred = Q.defer(); + var promise = deferred.promise; + expect(promise.isFulfilled()).toBe(false); + expect(promise.isRejected()).toBe(false); + expect(promise.isPending()).toBe(true); + }); + + it("of deferred rejection", function () { + var deferred = Q.defer(); + var rejection = Q.reject(new Error("Rejected!")); + deferred.resolve(rejection); + var promise = deferred.promise; + expect(promise.isFulfilled()).toBe(false); + expect(promise.isRejected()).toBe(true); + expect(promise.isPending()).toBe(false); + }); + + it("of deferred fulfillment", function () { + var deferred = Q.defer(); + deferred.resolve(10); + var promise = deferred.promise; + expect(promise.isFulfilled()).toBe(true); + expect(promise.isRejected()).toBe(false); + expect(promise.isPending()).toBe(false); + }); + + it("of deferred deferred", function () { + var a = Q.defer(); + var b = Q.defer(); + a.resolve(b.promise); + var promise = a.promise; + expect(promise.isFulfilled()).toBe(false); + expect(promise.isRejected()).toBe(false); + expect(promise.isPending()).toBe(true); + }); + + it("of isFulfilled side effects", function () { + var deferred = Q.defer(); + var finished = false; + + waitsFor(function () { + return finished; + }); + + var parentPromise = deferred.promise; + + var childPromise = parentPromise.then(function (value) { + expect(parentPromise.isFulfilled()).toBe(true); + expect(childPromise.isFulfilled()).toBe(false); + + return parentPromise.then(function (value) { + finished = true; + return value + 1; + }); + }); + + deferred.resolve(1); + + runs(function () { + expect(childPromise.isPending()).toBe(false); + expect(childPromise.isRejected()).toBe(false); + expect(childPromise.isFulfilled()).toBe(true); + expect(childPromise.valueOf()).toBe(2); + }); + }); + +}); + +describe("propagation", function () { + + it("propagate through then with no callback", function () { + return Q.resolve(10) + .then() + .then(function (ten) { + expect(ten).toBe(10); + }); + }); + + it("propagate through then with modifying callback", function () { + return Q.resolve(10) + .then(function (ten) { + return ten + 10; + }) + .then(function (twen) { + expect(twen).toBe(20); + }); + }); + + it("errback recovers from exception", function () { + var error = new Error("Bah!"); + return Q.reject(error) + .then(null, function (_error) { + expect(_error).toBe(error); + return 10; + }) + .then(function (value) { + expect(value).toBe(10); + }); + }); + + it("rejection propagates through then with no errback", function () { + var error = new Error("Foolish mortals!"); + return Q.reject(error) + .then() + .then(null, function (_error) { + expect(_error).toBe(error); + }); + }); + + it("rejection intercepted and rethrown", function () { + var error = new Error("Foolish mortals!"); + var nextError = new Error("Silly humans!"); + return Q.reject(error) + .fail(function () { + throw nextError; + }) + .then(null, function (_error) { + expect(_error).toBe(nextError); + }); + }); + + it("resolution is forwarded through deferred promise", function () { + var a = Q.defer(); + var b = Q.defer(); + a.resolve(b.promise); + b.resolve(10); + return a.promise.then(function (eh) { + expect(eh).toEqual(10); + }); + }); + + it("should propagate progress by default", function () { + var d = Q.defer(); + + var progressValues = []; + var promise = d.promise + .then() + .then( + function () { + expect(progressValues).toEqual([1]); + }, + function () { + expect(true).toBe(false); + }, + function (progressValue) { + progressValues.push(progressValue); + } + ); + + d.notify(1); + d.resolve(); + + return promise; + }); + + it("should allow translation of progress in the progressback", function () { + var d = Q.defer(); + + var progressValues = []; + var promise = d.promise + .progress(function (p) { + return p + 5; + }) + .then( + function () { + expect(progressValues).toEqual([10]); + }, + function () { + expect(true).toBe(false); + }, + function (progressValue) { + progressValues.push(progressValue); + } + ); + + d.notify(5); + d.resolve(); + + return promise; + }); + + + it("should stop progress propagation if an error is thrown", function () { + var def = Q.defer(); + var p2 = def.promise.progress(function () { + throw new Error("boo!"); + }); + + Q.onerror = function () { /* just swallow it for this test */ }; + + var progressValues = []; + var result = p2.then( + function () { + expect(progressValues).toEqual([]); + }, + function () { + expect(true).toBe(false); + }, + function (progressValue) { + progressValues.push(progressValue); + } + ); + + def.notify(); + def.resolve(); + return result; + }); +}); + +describe("all", function () { + + it("resolves when passed an empty array", function () { + return Q.all([]); + }); + + it("resolves after any constituent promise is rejected", function () { + var toResolve = Q.defer(); // never resolve + var toReject = Q.defer(); + var promises = [toResolve.promise, toReject.promise]; + var promise = Q.all(promises); + + toReject.reject(new Error("Rejected")); + + return Q.delay(250) + .then(function () { + expect(promise.isRejected()).toBe(true); + }) + .timeout(1000); + }); + + it("resolves foreign promises", function () { + var normal = Q.resolve(1); + var foreign = { then: function (f) { f(2); } }; + + return Q.all([normal, foreign]) + .then(function (result) { + expect(result).toEqual([1, 2]); + }); + }); + + it("resolves when passed an sparse array", function () { + var toResolve = Q.defer(); + var promises = []; + promises[0] = Q.resolve(0); + promises[2] = toResolve.promise; + var promise = Q.all(promises); + + toResolve.resolve(2); + + return promise.then(function (result) { + expect(result).toEqual([0, void 0, 2]); + }); + }); + +}); + +describe("allResolved", function () { + + it("normalizes all given values to promises", function () { + return Q.allResolved([1, Q.resolve(2), Q.reject(3)]) + .then(function (promises) { + expect(Q.isPromise(promises[0])).toBe(true); + expect(Q.isPromise(promises[1])).toBe(true); + expect(Q.isPromise(promises[2])).toBe(true); + }); + }); + + it("fulfillment even when one given promise is rejected", function () { + return Q.allResolved([1, Q.resolve(2), Q.reject(3)]) + .then(null, function () { + expect("flying pigs").toBe("flightless pigs"); + }); + }); + + it("the state and quantity of promises to be correct", function () { + return Q.allResolved([1, Q.resolve(2), Q.reject(3)]) + .then(function (promises) { + expect(promises.length).toEqual(3); + + expect(Q.isPromise(promises[0])).toBe(true); + expect(Q.isPromise(promises[1])).toBe(true); + expect(Q.isPromise(promises[2])).toBe(true); + + expect(Q.isPending(promises[0])).toBe(false); + expect(Q.isPending(promises[1])).toBe(false); + expect(Q.isPending(promises[2])).toBe(false); + + expect(Q.isFulfilled(promises[0])).toBe(true); + expect(Q.isFulfilled(promises[1])).toBe(true); + expect(Q.isRejected(promises[2])).toBe(true); + + expect(promises[0].valueOf()).toEqual(1); + expect(promises[1].valueOf()).toEqual(2); + }); + }); + + it("is resolved after every constituent promise is resolved", function () { + var toResolve = Q.defer(); + var toReject = Q.defer(); + var promises = [toResolve.promise, toReject.promise]; + var resolved; + var rejected; + + Q.fcall(function () { + toReject.reject(); + rejected = true; + }) + .then(function () { + toResolve.resolve(); + resolved = true; + }); + + return Q.allResolved(promises) + .then(function (promises) { + expect(resolved).toBe(true); + expect(rejected).toBe(true); + }); + }); + +}); + +describe("spread", function () { + + it("spreads values across arguments", function () { + return Q.spread([1, 2, 3], function (a, b, c) { + expect(b).toBe(2); + }); + }); + + it("spreads promises for arrays across arguments", function () { + return Q.resolve([Q.resolve(10)]) + .spread(function (value) { + expect(value).toEqual(10); + }); + }); + + it("spreads arrays of promises across arguments", function () { + var deferredA = Q.defer(); + var deferredB = Q.defer(); + + var promise = Q.spread([deferredA.promise, deferredB.promise], + function (a, b) { + expect(a).toEqual(10); + expect(b).toEqual(20); + }); + + Q.delay(5).then(function () { + deferredA.resolve(10); + }); + Q.delay(10).then(function () { + deferredB.resolve(20); + }); + + return promise; + }); + + it("calls the errback when given a rejected promise", function () { + var err = new Error(); + return Q.spread([Q.resolve(10), Q.reject(err)], + function () { + expect(true).toBe(false); + }, + function (actual) { + expect(actual).toBe(err); + } + ); + }); + +}); + +describe("fin", function () { + + var exception1 = new Error("boo!"); + var exception2 = new TypeError("evil!"); + + describe("when the promise is fulfilled", function () { + + it("should call the callback", function () { + var called = false; + + return Q.resolve("foo") + .fin(function () { + called = true; + }) + .then(function () { + expect(called).toBe(true); + }); + }); + + it("should fulfill with the original value", function () { + return Q.resolve("foo") + .fin(function () { + return "bar"; + }) + .then(function (result) { + expect(result).toBe("foo"); + }); + }); + + describe("when the callback returns a promise", function () { + + describe("that is fulfilled", function () { + it("should fulfill with the original reason after that promise resolves", function () { + var promise = Q.delay(250); + + return Q.resolve("foo") + .fin(function () { + return promise; + }) + .then(function (result) { + expect(Q.isPending(promise)).toBe(false); + expect(result).toBe("foo"); + }); + }); + }); + + describe("that is rejected", function () { + it("should reject with this new rejection reason", function () { + return Q.resolve("foo") + .fin(function () { + return Q.reject(exception1); + }) + .then(function () { + expect(false).toBe(true); + }, + function (exception) { + expect(exception).toBe(exception1); + }); + }); + }); + + }); + + describe("when the callback throws an exception", function () { + it("should reject with this new exception", function () { + return Q.resolve("foo") + .fin(function () { + throw exception1; + }) + .then(function () { + expect(false).toBe(true); + }, + function (exception) { + expect(exception).toBe(exception1); + }); + }); + }); + + }); + + describe("when the promise is rejected", function () { + + it("should call the callback", function () { + var called = false; + + return Q.reject(exception1) + .fin(function () { + called = true; + }) + .then(function () { + expect(called).toBe(true); + }, function () { + expect(called).toBe(true); + }); + }); + + it("should reject with the original reason", function () { + return Q.reject(exception1) + .fin(function () { + return "bar"; + }) + .then(function (result) { + expect(false).toBe(true); + }, + function (exception) { + expect(exception).toBe(exception1); + }); + }); + + describe("when the callback returns a promise", function () { + + describe("that is fulfilled", function () { + it("should reject with the original reason after that promise resolves", function () { + var promise = Q.delay(250); + + return Q.reject(exception1) + .fin(function () { + return promise; + }) + .then(function (result) { + expect(false).toBe(true); + }, + function (exception) { + expect(exception).toBe(exception1); + expect(Q.isPending(promise)).toBe(false); + }); + }); + }); + + describe("that is rejected", function () { + it("should reject with the new reason", function () { + var newException = new TypeError("evil!"); + + return Q.reject(exception1) + .fin(function () { + return Q.reject(exception2); + }) + .then(function (result) { + expect(false).toBe(true); + }, + function (exception) { + expect(exception).toBe(exception2); + }); + }); + }); + + }); + + describe("when the callback throws an exception", function () { + it("should reject with this new exception", function () { + return Q.reject(exception1) + .fin(function () { + throw exception2; + }) + .then(function () { + expect(false).toBe(true); + }, + function (exception) { + expect(exception).toBe(exception2); + }); + }); + }); + + }); + +}); + +describe("done", function () { + describe("when the promise is fulfilled", function () { + describe("and the callback does not throw", function () { + it("should call the callback and return nothing", function () { + var called = false; + + var promise = Q.resolve(); + + var returnValue = promise.done(function () { + called = true; + }); + + return promise.fail(function () { }).fin(function () { + expect(called).toBe(true); + expect(returnValue).toBe(undefined); + }); + }); + }); + + describe("and the callback throws", function () { + it("should rethrow that error in the next turn and return nothing", function () { + var turn = 0; + Q.nextTick(function () { + ++turn; + }); + + var returnValue = Q.resolve().done( + function () { + throw "foo"; + } + ); + + var deferred = Q.defer(); + Q.onerror = function (error) { + expect(turn).toBe(1); + expect(error).toBe("foo"); + expect(returnValue).toBe(undefined); + deferred.resolve(); + }; + Q.delay(100).then(deferred.reject); + + return deferred.promise; + }); + }); + }); + + describe("when the promise is rejected", function () { + describe("and the errback handles it", function () { + it("should call the errback and return nothing", function () { + var called = false; + + var promise = Q.reject(new Error()); + + var returnValue = promise.done( + function () { }, + function () { + called = true; + } + ); + + return promise.fail(function () { }).fin(function () { + expect(called).toBe(true); + expect(returnValue).toBe(undefined); + }); + }); + }); + + describe("and the errback throws", function () { + it("should rethrow that error in the next turn and return nothing", function () { + var turn = 0; + Q.nextTick(function () { + ++turn; + }); + + var returnValue = Q.reject("bar").done( + null, + function () { + throw "foo"; + } + ); + + var deferred = Q.defer(); + Q.onerror = function (error) { + expect(turn).toBe(1); + expect(error).toBe("foo"); + expect(returnValue).toBe(undefined); + deferred.resolve(); + }; + Q.delay(100).then(deferred.reject); + + return deferred.promise; + }); + }); + + describe("and there is no errback", function () { + it("should throw the original error in the next turn", function () { + var turn = 0; + Q.nextTick(function () { + ++turn; + }); + + var returnValue = Q.reject("bar").done(); + + var deferred = Q.defer(); + Q.onerror = function (error) { + expect(turn).toBe(1); + expect(error).toBe("bar"); + expect(returnValue).toBe(undefined); + deferred.resolve(); + }; + Q.delay(10).then(deferred.reject); + + return deferred.promise; + }); + }); + }); + + it("should attach a progress listener", function () { + var deferred = Q.defer(); + + var spy = jasmine.createSpy(); + deferred.promise.done(null, null, spy); + + deferred.notify(10); + deferred.resolve(); + + return deferred.promise.then(function () { + expect(spy).toHaveBeenCalledWith(10); + }); + }); +}); + +describe("timeout", function () { + it("should do nothing if the promise fulfills quickly", function () { + return Q.delay(10).timeout(200); + }); + + it("should do nothing if the promise rejects quickly", function () { + var goodError = new Error("haha!"); + return Q.delay(10) + .then(function () { + throw goodError; + }) + .timeout(200) + .then(undefined, function (error) { + expect(error).toBe(goodError); + }); + }); + + it("should reject with a timeout error if the promise is too slow", function () { + var goodError = new Error("haha!"); + return Q.delay(100) + .timeout(10) + .then( + function () { + expect(true).toBe(false); + }, + function (error) { + expect(/time/i.test(error.message)).toBe(true); + } + ); + }); + + it("should pass through progress notifications", function () { + var deferred = Q.defer(); + + var progressValsSeen = []; + var promise = Q.timeout(deferred.promise, 100).then(function () { + expect(progressValsSeen).toEqual([1, 2, 3]); + }, undefined, function (progressVal) { + progressValsSeen.push(progressVal); + }); + + Q.delay(5).then(function () { deferred.notify(1); }); + Q.delay(15).then(function () { deferred.notify(2); }); + Q.delay(25).then(function () { deferred.notify(3); }); + Q.delay(35).then(function () { deferred.resolve(); }); + + return promise; + }); +}); + +describe("delay", function () { + it("should delay fulfillment", function () { + var promise = Q.resolve(5).delay(50); + + setTimeout(function () { + expect(promise.isPending()).toBe(true); + }, 40); + + return promise; + }); + + it("should delay rejection", function () { + var promise = Q.reject(5).delay(50); + + setTimeout(function () { + expect(promise.isPending()).toBe(true); + }, 40); + + return promise.then(undefined, function () { }); + }); + + it("should treat a single argument as a time", function () { + var promise = Q.delay(50); + + setTimeout(function () { + expect(promise.isPending()).toBe(true); + }, 40); + + return promise; + }); + + it("should treat two arguments as a value + a time", function () { + var promise = Q.delay("what", 50); + + setTimeout(function () { + expect(promise.isPending()).toBe(true); + }, 40); + + return promise.then(function (value) { + expect(value).toBe("what"); + }); + }); + + it("should delegate to faster passed promises, slowing them down", function () { + var promise1 = Q.delay("what", 30); + var promise2 = Q.delay(promise1, 50); + + setTimeout(function () { + expect(promise1.isPending()).toBe(false); + expect(promise2.isPending()).toBe(true); + }, 40); + + return promise2.then(function (value) { + expect(value).toBe("what"); + }); + }); + + it("should delegate to slower passed promises, staying at their speed", function () { + var promise1 = Q.delay("what", 70); + var promise2 = Q.delay(promise1, 50); + + setTimeout(function () { + expect(promise1.isPending()).toBe(true); + expect(promise2.isPending()).toBe(true); + }, 60); + + return promise2.then(function (value) { + expect(value).toBe("what"); + }); + }); + + it("should pass through progress notifications from passed promises", function () { + var deferred = Q.defer(); + + var progressValsSeen = []; + var promise = Q.delay(deferred.promise, 100).then(function () { + expect(progressValsSeen).toEqual([1, 2, 3]); + }, undefined, function (progressVal) { + progressValsSeen.push(progressVal); + }); + + Q.delay(5).then(function () { deferred.notify(1); }); + Q.delay(15).then(function () { deferred.notify(2); }); + Q.delay(25).then(function () { deferred.notify(3); }); + Q.delay(35).then(function () { deferred.resolve(); }); + + return promise; + }); +}); + +describe("thenResolve", function () { + describe("Resolving with a non-thenable value", function () { + it("returns a promise for that object once the promise is resolved", function () { + var waited = false; + return Q.delay(20) + .then(function () { + waited = true; + }) + .thenResolve('foo') + .then(function (val) { + expect(waited).toBe(true); + expect(val).toBe('foo'); + }); + }); + + describe("based off a rejected promise", function () { + it("does nothing, letting the rejection flow through", function () { + return Q.reject('boo') + .thenResolve('foo') + .then( + function () { + expect(true).toBe(false); + }, + function (reason) { + expect(reason).toBe('boo'); + } + ); + }); + }); + }); + + describe("Resolving with an promise", function () { + it("returns a promise for the result of that promise once the promise is resolved", function () { + var waited = false; + return Q.delay(20) + .then(function () { + waited = true; + }) + .thenResolve(Q.resolve('foo')) + .then(function (val) { + expect(waited).toBe(true); + expect(val).toBe('foo'); + }); + }); + }); +}); + +describe("thenReject", function () { + describe("Rejecting with a reason", function () { + it("returns a promise rejected with that object once the original promise is resolved", function () { + var waited = false; + return Q.delay(20) + .then(function () { + waited = true; + }) + .thenReject('foo') + .then( + function () { + expect(true).toBe(false); + }, + function (reason) { + expect(waited).toBe(true); + expect(reason).toBe('foo'); + } + ); + }); + + describe("based off a rejected promise", function () { + it("does nothing, letting the rejection flow through", function () { + return Q.reject('boo') + .thenResolve('foo') + .then( + function () { + expect(true).toBe(false); + }, + function (reason) { + expect(reason).toBe('boo'); + } + ); + }); + }); + }); +}); + + +describe("thenables", function () { + + it("assimilates a thenable with fulfillment with resolve", function () { + return Q.resolve({ + then: function (resolved) { + resolved(10); + } + }) + .then(function (ten) { + expect(ten).toEqual(10); + }) + .then(function (undefined) { + expect(undefined).toEqual(void 0); + }); + }); + + it("assimilates a thenable with progress and fulfillment (using resolve)", function () { + var progressValueArrays = []; + return Q.resolve({ + then: function (fulfilled, rejected, progressed) { + Q.nextTick(function () { + progressed(1, 2); + progressed(3, 4, 5); + fulfilled(); + }); + } + }) + .progress(function () { + progressValueArrays.push(Array.prototype.slice.call(arguments)); + }) + .then(function () { + expect(progressValueArrays).toEqual([[1], [3]]); + }); + }); + + it("assimilates a thenable with progress and fulfillment (using when)", function () { + var progressValueArrays = []; + return Q.when({ + then: function (fulfilled, rejected, progressed) { + Q.nextTick(function () { + progressed(1, 2); + progressed(3, 4, 5); + fulfilled(); + }); + } + }) + .progress(function () { + progressValueArrays.push(Array.prototype.slice.call(arguments)); + }) + .then(function () { + expect(progressValueArrays).toEqual([[1], [3]]); + }); + }); + + it("flows fulfillment into a promise pipeline", function () { + return Q.resolve({ + then: function (resolved) { + resolved([10]); + } + }) + .get(0) + .then(function (ten) { + expect(ten).toEqual(10); + }); + }); + + it("assimilates a thenable in allResolved", function () { + return Q.allResolved([ + {then: function (win, fail) { + win(10); + }} + ]) + .then(function (promises) { + expect(promises[0].isPending()).toBe(false); + expect(promises[0].isFulfilled()).toBe(true); + expect(promises[0].isRejected()).toBe(false); + }); + }); + + it("assimilates a pending thenable in allResolved", function () { + return Q.allResolved([ + {then: function (win, fail) { + setTimeout(function () { + win(10); + }, 100); + }} + ]) + .then(function (promises) { + expect(promises[0].isPending()).toBe(false); + expect(promises[0].isFulfilled()).toBe(true); + expect(promises[0].isRejected()).toBe(false); + }); + }); + +}); + +describe("node support", function () { + + var exception = new Error("That is not your favorite color."); + + var obj = { + method: function (a, b, c, callback) { + callback(null, a + b + c); + }, + thispChecker: function (callback) { + callback(null, this === obj); + }, + errorCallbacker: function (a, b, c, callback) { + callback(exception); + }, + errorThrower: function (a, b, c, callback) { + throw exception; + } + }; + + describe("nfapply", function (done) { + + it("fulfills with callback result", function () { + return Q.nfapply(function (a, b, c, callback) { + callback(null, a + b + c); + }, [1, 2, 3]) + .then(function (sum) { + expect(sum).toEqual(6); + }); + }); + + it("rejects with callback error", function () { + var exception = new Error("That is not your favorite color."); + return Q.nfapply(function (a, b, c, callback) { + callback(exception); + }, [1, 2, 3]) + .then(function (sum) { + expect(true).toBe(false); + }, function (_exception) { + expect(_exception).toBe(exception); + }); + }); + + }); + + describe("nfcall", function () { + it("fulfills with callback result", function () { + return Q.nfcall(function (a, b, c, callback) { + callback(null, a + b + c); + }, 1, 2, 3) + .then(function (sum) { + expect(sum).toEqual(6); + }); + }); + + it("rejects with callback error", function () { + var exception = new Error("That is not your favorite color."); + return Q.nfcall(function (a, b, c, callback) { + callback(exception); + }, 1, 2, 3) + .then(function (sum) { + expect(true).toBe(false); + }, function (_exception) { + expect(_exception).toBe(exception); + }); + }); + + }); + + describe("nfbind", function () { + + it("mixes partial application with complete application", function () { + return Q.nfbind(function (a, b, c, d, callback) { + callback(null, a + b + c + d); + }, 1, 2).call({}, 3, 4) + .then(function (ten) { + expect(ten).toBe(10); + }); + }); + + }); + + describe("nbind", function () { + + it("binds this, and mixes partial application with complete application", function () { + return Q.nbind(function (a, b, c, callback) { + console.log(this, arguments); + callback(null, this + a + b + c); + }, 1, 2).call(3 /* effectively ignored as fn bound to 1 */, 4, 5) + .then(function (twelve) { + expect(twelve).toBe(12); + }); + }); + + it("second arg binds this", function() { + var expectedThis = { test: null }; + + return Q.nbind(function(callback) { + callback(null, this); + }, expectedThis).call() + .then(function(actualThis) { + expect(actualThis).toEqual(expectedThis); + }); + }); + + }); + describe("npost", function (done) { + + it("fulfills with callback result", function () { + return Q.npost(obj, "method", [1, 2, 3]) + .then(function (sum) { + expect(sum).toEqual(6); + }); + }); + + it("gets the correct thisp", function () { + return Q.npost(obj, "thispChecker", []) + .then(function (result) { + expect(result).toBe(true); + }); + }); + + it("rejects with callback error", function () { + return Q.npost(obj, "errorCallbacker", [1, 2, 3]) + .then(function (sum) { + expect("blue").toBe("no, yellow!"); + }, function (_exception) { + expect(_exception).toBe(exception); + }); + }); + + it("rejects with thrown error", function () { + return Q.npost(obj, "errorThrower", [1, 2, 3]) + .then(function (sum) { + expect(true).toBe(false); + }, function (_exception) { + expect(_exception).toBe(exception); + }); + }); + + it("works on promises for objects with Node methods", function () { + return Q.resolve(obj) + .npost("method", [1, 2, 3]) + .then(function (sum) { + expect(sum).toEqual(6); + }); + }); + + }); + + describe("nsend", function (done) { + + it("fulfills with callback result", function () { + return Q.nsend(obj, "method", 1, 2, 3) + .then(function (sum) { + expect(sum).toEqual(6); + }); + }); + + it("gets the correct thisp", function () { + return Q.nsend(obj, "thispChecker") + .then(function (result) { + expect(result).toBe(true); + }); + }); + + it("rejects with callback error", function () { + return Q.nsend(obj, "errorCallbacker", 1, 2, 3) + .then(function (sum) { + expect("blue").toBe("no, yellow!"); + }, function (_exception) { + expect(_exception).toBe(exception); + }); + }); + + it("rejects with thrown error", function () { + return Q.nsend(obj, "errorThrower", 1, 2, 3) + .then(function (sum) { + expect(true).toBe(false); + }, function (_exception) { + expect(_exception).toBe(exception); + }); + }); + + it("works on promises for objects with Node methods", function () { + return Q.resolve(obj) + .nsend("method", 1, 2, 3) + .then(function (sum) { + expect(sum).toEqual(6); + }); + }); + + }); + + describe("deferred.makeNodeResolver", function () { + + it("fulfills a promise with a single callback argument", function () { + var deferred = Q.defer(); + var callback = deferred.makeNodeResolver(); + callback(null, 10); + return deferred.promise.then(function (value) { + expect(value).toBe(10); + }); + }); + + it("fulfills a promise with multiple callback arguments", function () { + var deferred = Q.defer(); + var callback = deferred.makeNodeResolver(); + callback(null, 10, 20); + return deferred.promise.then(function (value) { + expect(value).toEqual([10, 20]); + }); + }); + + it("rejects a promise", function () { + var deferred = Q.defer(); + var callback = deferred.makeNodeResolver(); + var exception = new Error("Holy Exception of Anitoch"); + callback(exception); + return deferred.promise.then(function (value) { + expect(5).toBe(3); + }, function (_exception) { + expect(_exception).toBe(exception); + }); + }); + + }); + + describe("nodeify", function () { + + it("calls back with a resolution", function () { + var spy = jasmine.createSpy(); + Q.resolve(10).nodeify(spy); + waitsFor(function () { + return spy.argsForCall.length; + }); + runs(function () { + expect(spy.argsForCall).toEqual([[null, 10]]); + }); + }); + + it("calls back with an error", function () { + var spy = jasmine.createSpy(); + Q.reject(10).nodeify(spy); + waitsFor(function () { + return spy.argsForCall.length; + }); + runs(function () { + expect(spy.argsForCall).toEqual([[10]]); + }); + }); + + it("forwards a promise", function () { + return Q.resolve(10).nodeify().then(function (ten) { + expect(ten).toBe(10); + }); + }); + + }); + +}); + +if (typeof require === "function") { + var domain; + try { + domain = require("domain"); + } catch (e) { } + + if (domain) { + var EventEmitter = require("events").EventEmitter; + + describe("node domain support", function () { + var d; + + beforeEach(function () { + d = domain.create(); + }); + afterEach(function() { + d.dispose(); + }); + + it("should work for non-promise async inside a promise handler", + function (done) { + var error = new Error("should be caught by the domain"); + + d.run(function () { + Q.resolve().then(function () { + setTimeout(function () { + throw error; + }, 10); + }); + }); + + var errorTimeout = setTimeout(function () { + done(new Error("Wasn't caught")); + }, 100); + + d.on("error", function (theError) { + expect(theError).toBe(error); + clearTimeout(errorTimeout); + done(); + }); + }); + + it("should transfer errors from `done` into the domain", + function (done) { + var error = new Error("should be caught by the domain"); + + d.run(function () { + Q.reject(error).done(); + }); + + var errorTimeout = setTimeout(function () { + done(new Error("Wasn't caught")); + }, 100); + + d.on("error", function (theError) { + expect(theError).toBe(error); + clearTimeout(errorTimeout); + done(); + }); + }); + + it("should take care of re-used event emitters", function (done) { + // See discussion in https://github.com/kriskowal/q/issues/120 + var error = new Error("should be caught by the domain"); + + var e = new EventEmitter(); + + d.run(function () { + callAsync().done(); + }); + setTimeout(function () { + e.emit("beep"); + }, 100); + + var errorTimeout = setTimeout(function () { + done(new Error("Wasn't caught")); + }, 500); + + d.on("error", function (theError) { + expect(theError).toBe(error); + clearTimeout(errorTimeout); + done(); + }); + + function callAsync() { + var def = Q.defer(); + e.once("beep", function () { + def.reject(error); + }); + return def.promise; + } + }); + }); + } +} + +describe("decorator functions", function () { + describe("promised", function () { + var exception = new Error("That is not the meaning of life."); + it("resolves promised arguments", function () { + var sum = Q.promised(function add(a, b) { + return a + b; + }); + return sum(Q.resolve(4), Q.resolve(5)).then(function (sum) { + expect(sum).toEqual(9); + }); + }); + it("resolves promised `this`", function () { + var inc = Q.promised(function inc(a) { + return this + a; + }); + return inc.call(Q.resolve(4), Q.resolve(5)).then(function (sum) { + expect(sum).toEqual(9); + }); + }); + it("is rejected if an argument is rejected", function () { + var sum = Q.promised(function add(a, b) { + return a + b; + }); + return sum(Q.reject(exception), Q.resolve(4)).then(function () { + expect(4).toEqual(42); + }, function (_exception) { + expect(_exception).toBe(exception); + }); + }); + it("is rejected if `this` is rejected", function () { + var inc = Q.promised(function inc(a) { + return this + a; + }); + return inc.call(Q.reject(exception), Q.resolve(4)).then(function () { + expect(4).toEqual(42); + }, function (_exception) { + expect(_exception).toBe(exception); + }); + }); + }); +}); + +describe("stack trace formatting", function () { + it("doesn't mangle a stack trace that gets handled twice", function () { + var d1 = Q.defer(); + var d2 = Q.defer(); + var captured = []; + d1.promise.done(); + d2.promise.done(); + + Q.onerror = function (err) { + captured.push(err.stack); + }; + + var error = new Error("boom!"); + d1.reject(error); + d2.reject(error); + + return Q.all([d1.promise.fail(function () {}), d2.promise.fail(function () { })]) + .then(function () { + expect(captured[0]).toEqual(captured[1]); + }); + }); +}); + +describe("possible regressions", function () { + + describe("gh-9", function () { + it("treats falsy values as resolved values without error", function () { + expect(Q.isPending(null)).toEqual(false); + expect(Q.isPending(void 0)).toEqual(false); + expect(Q.isPending(false)).toEqual(false); + expect(Q.isPending()).toEqual(false); + }); + }); + + describe("gh-22", function () { + it("ensures that the array prototype is intact", function () { + var keys = []; + for (var key in []) { + keys.push(key); + } + expect(keys.length).toBe(0); + }); + }); + + describe("gh-73", function () { + it("does not choke on non-error rejection reasons", function () { + Q.reject(REASON).done(); + + var deferred = Q.defer(); + + Q.onerror = function (error) { + expect(error).toBe(REASON); + deferred.resolve(); + }; + Q.delay(10).then(deferred.reject); + + return deferred.promise; + }); + }); + + describe("gh-90", function () { + it("does not choke on rejection reasons with an undefined `stack`", function () { + var error = new RangeError(REASON); + error.stack = undefined; + Q.reject(error).done(); + + var deferred = Q.defer(); + + Q.onerror = function (theError) { + expect(theError).toBe(error); + deferred.resolve(); + }; + Q.delay(10).then(deferred.reject); + + return deferred.promise; + }); + }); + + describe("gh-75", function () { + it("does not double-resolve misbehaved promises", function () { + var badPromise = Q.makePromise({ + post: function () { return "hello"; } + }); + + var resolutions = 0; + function onResolution() { + ++resolutions; + } + + return Q.when(badPromise, onResolution, onResolution).then(function () { + expect(resolutions).toBe(1); + }); + }); + }); + +}); diff --git a/components/q/spec/queue-spec.js b/components/q/spec/queue-spec.js new file mode 100644 index 0000000..4cec14e --- /dev/null +++ b/components/q/spec/queue-spec.js @@ -0,0 +1,180 @@ + +var Q = require("../q"); +var Queue = require("../queue"); + +global.Q = Q; +require("./lib/jasmine-promise"); + +describe("queue", function () { + + it("should enqueue then dequeue", function () { + var queue = Queue(); + queue.put(1); + return queue.get().then(function (value) { + expect(value).toBe(1); + }); + }); + + it("should dequeue then enqueue", function () { + var queue = Queue(); + var promise = queue.get().then(function (value) { + expect(value).toBe(1); + }); + queue.put(1); + return promise; + }); + + it("should stream", function () { + var queue = Queue(); + + Q.try(function () { + return Q.delay(20).then(function () { + queue.put(1); + }) + }) + .then(function () { + return Q.delay(20).then(function () { + queue.put(2); + }) + }) + .then(function () { + return Q.delay(20).then(function () { + queue.put(3); + }) + }) + .done(); + + return Q.try(function () { + return queue.get() + .then(function (value) { + expect(value).toBe(1); + }); + }) + .then(function () { + return queue.get() + .then(function (value) { + expect(value).toBe(2); + }); + }) + .then(function () { + return queue.get() + .then(function (value) { + expect(value).toBe(3); + }); + }) + + }); + + it("should be order agnostic", function () { + var queue = Queue(); + + Q.try(function () { + return Q.delay(20).then(function () { + queue.put(1); + }) + }) + .then(function () { + return Q.delay(20).then(function () { + queue.put(2); + }) + }) + .then(function () { + return Q.delay(20).then(function () { + queue.put(3); + }) + }) + .done(); + + return Q.all([ + queue.get() + .then(function (value) { + expect(value).toBe(1); + }), + queue.get() + .then(function (value) { + expect(value).toBe(2); + }), + queue.get() + .then(function (value) { + expect(value).toBe(3); + }) + ]); + }); + + it("should close", function () { + + var queue = Queue(); + + Q.try(function () { + return Q.delay(20).then(function () { + queue.put(1); + }) + }) + .then(function () { + return Q.delay(20).then(function () { + queue.put(2); + }) + }) + .then(function () { + queue.close(); + }) + .done(); + + return Q.try(function () { + return queue.get() + .then(function (value) { + expect(value).toBe(1); + }); + }) + .then(function () { + return queue.get() + .then(function (value) { + expect(value).toBe(2); + }); + }) + .then(function () { + return queue.get() + .then(function (value) { + expect(false).toBe(true); // should not get here + }); + }) + .catch(function (error) { + expect(error.message).toBe("Can't get value from closed queue"); + return queue.get(); + }) + .catch(function (error) { + expect(error.message).toBe("Can't get value from closed queue"); + }) + .then(function () { + return queue.closed; + }) + .then(function (error) { + expect(error.message).toBe("Can't get value from closed queue"); + }) + }); + + it("should close with alternate error", function () { + + var queue = Queue(); + queue.close(new Error("Alternate reason")); + + return Q.try(function () { + return queue.get(); + }) + .catch(function (error) { + expect(error.message).toBe("Alternate reason"); + return queue.get(); + }) + .catch(function (error) { + expect(error.message).toBe("Alternate reason"); + }) + .then(function () { + return queue.closed; + }) + .then(function (error) { + expect(error.message).toBe("Alternate reason"); + }) + }); + +}); + diff --git a/components/reveal.js/.gitignore b/components/reveal.js/.gitignore new file mode 100644 index 0000000..9ffdbc7 --- /dev/null +++ b/components/reveal.js/.gitignore @@ -0,0 +1,6 @@ +.DS_Store +.svn +log/*.log +tmp/** +node_modules/ +.sass-cache \ No newline at end of file diff --git a/components/reveal.js/.travis.yml b/components/reveal.js/.travis.yml new file mode 100644 index 0000000..2d6cd8f --- /dev/null +++ b/components/reveal.js/.travis.yml @@ -0,0 +1,5 @@ +language: node_js +node_js: + - 0.8 +before_script: + - npm install -g grunt-cli \ No newline at end of file diff --git a/components/reveal.js/Gruntfile.js b/components/reveal.js/Gruntfile.js new file mode 100644 index 0000000..6fa4014 --- /dev/null +++ b/components/reveal.js/Gruntfile.js @@ -0,0 +1,104 @@ +/* global module:false */ +module.exports = function(grunt) { + + // Project configuration + grunt.initConfig({ + pkg: grunt.file.readJSON('package.json'), + meta: { + banner: + '/*!\n' + + ' * reveal.js <%= pkg.version %> (<%= grunt.template.today("yyyy-mm-dd, HH:MM") %>)\n' + + ' * http://lab.hakim.se/reveal-js\n' + + ' * MIT licensed\n' + + ' *\n' + + ' * Copyright (C) 2013 Hakim El Hattab, http://hakim.se\n' + + ' */' + }, + + // Tests will be added soon + qunit: { + files: [ 'test/**/*.html' ] + }, + + uglify: { + options: { + banner: '<%= meta.banner %>\n' + }, + build: { + src: 'js/reveal.js', + dest: 'js/reveal.min.js' + } + }, + + cssmin: { + compress: { + files: { + 'css/reveal.min.css': [ 'css/reveal.css' ] + } + } + }, + + sass: { + main: { + files: { + 'css/theme/default.css': 'css/theme/source/default.scss', + 'css/theme/beige.css': 'css/theme/source/beige.scss', + 'css/theme/night.css': 'css/theme/source/night.scss', + 'css/theme/serif.css': 'css/theme/source/serif.scss', + 'css/theme/simple.css': 'css/theme/source/simple.scss', + 'css/theme/sky.css': 'css/theme/source/sky.scss', + 'css/theme/moon.css': 'css/theme/source/moon.scss', + 'css/theme/solarized.css': 'css/theme/source/solarized.scss' + } + } + }, + + jshint: { + options: { + curly: false, + eqeqeq: true, + immed: true, + latedef: true, + newcap: true, + noarg: true, + sub: true, + undef: true, + eqnull: true, + browser: true, + expr: true, + globals: { + head: false, + module: false, + console: false + } + }, + files: [ 'Gruntfile.js', 'js/reveal.js' ] + }, + + watch: { + main: { + files: [ 'Gruntfile.js', 'js/reveal.js', 'css/reveal.css' ], + tasks: 'default' + }, + theme: { + files: [ 'css/theme/source/*.scss', 'css/theme/template/*.scss' ], + tasks: 'themes' + } + } + + }); + + // Dependencies + grunt.loadNpmTasks( 'grunt-contrib-jshint' ); + grunt.loadNpmTasks( 'grunt-contrib-cssmin' ); + grunt.loadNpmTasks( 'grunt-contrib-uglify' ); + grunt.loadNpmTasks( 'grunt-contrib-watch' ); + grunt.loadNpmTasks( 'grunt-contrib-sass' ); + + // Default task + grunt.registerTask( 'default', [ 'jshint', 'cssmin', 'uglify' ] ); + + // Theme task + grunt.registerTask( 'themes', [ 'sass' ] ); + +}; diff --git a/components/reveal.js/LICENSE b/components/reveal.js/LICENSE new file mode 100644 index 0000000..e1e8bf7 --- /dev/null +++ b/components/reveal.js/LICENSE @@ -0,0 +1,19 @@ +Copyright (C) 2013 Hakim El Hattab, http://hakim.se + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. \ No newline at end of file diff --git a/components/reveal.js/README.md b/components/reveal.js/README.md new file mode 100644 index 0000000..827ac67 --- /dev/null +++ b/components/reveal.js/README.md @@ -0,0 +1,469 @@ +# reveal.js [![Build Status](https://travis-ci.org/hakimel/reveal.js.png?branch=master)](https://travis-ci.org/hakimel/reveal.js) + +A framework for easily creating beautiful presentations using HTML. [Check out the live demo](http://lab.hakim.se/reveal-js/). + +reveal.js comes with a broad range of features including [nested slides](https://github.com/hakimel/reveal.js#markup), [markdown contents](https://github.com/hakimel/reveal.js#markdown), [PDF export](https://github.com/hakimel/reveal.js#pdf-export), [speaker notes](https://github.com/hakimel/reveal.js#speaker-notes) and a [JavaScript API](https://github.com/hakimel/reveal.js#api). It's best viewed in a browser with support for CSS 3D transforms but [fallbacks](https://github.com/hakimel/reveal.js/wiki/Browser-Support) are available to make sure your presentation can still be viewed elsewhere. + + +#### More reading in the Wiki: +- [Changelog](https://github.com/hakimel/reveal.js/wiki/Changelog): Up-to-date version history. +- [Examples](https://github.com/hakimel/reveal.js/wiki/Example-Presentations): Presentations created with reveal.js, add your own! +- [Browser Support](https://github.com/hakimel/reveal.js/wiki/Browser-Support): Explanation of browser support and fallbacks. + +## rvl.io + +Slides are written using HTML or markdown but there's also an online editor for those of you who prefer a more traditional user interface. Give it a try at [www.rvl.io](http://www.rvl.io). + + +## Instructions + +### Markup + +Markup heirarchy needs to be ``
`` where the ``
`` represents one slide and can be repeated indefinitely. If you place multiple ``
``'s inside of another ``
`` they will be shown as vertical slides. The first of the vertical slides is the "root" of the others (at the top), and it will be included in the horizontal sequence. For example: + +```html +
+
+
Single Horizontal Slide
+
+
Vertical Slide 1
+
Vertical Slide 2
+
+
+
+``` + +### Markdown + +It's possible to write your slides using Markdown. To enable Markdown, add the ```data-markdown``` attribute to your ```
``` elements and wrap the contents in a ``` +
+``` + +#### External Markdown + +You can write your content as a separate file and have reveal.js load it at runtime. Note the separator arguments which determine how slides are delimited in the external file. + +```html +
+``` + +### Configuration + +At the end of your page you need to initialize reveal by running the following code. Note that all config values are optional and will default as specified below. + +```javascript +Reveal.initialize({ + + // Display controls in the bottom right corner + controls: true, + + // Display a presentation progress bar + progress: true, + + // Push each slide change to the browser history + history: false, + + // Enable keyboard shortcuts for navigation + keyboard: true, + + // Enable the slide overview mode + overview: true, + + // Vertical centering of slides + center: true, + + // Loop the presentation + loop: false, + + // Change the presentation direction to be RTL + rtl: false, + + // Number of milliseconds between automatically proceeding to the + // next slide, disabled when set to 0, this value can be overwritten + // by using a data-autoslide attribute on your slides + autoSlide: 0, + + // Enable slide navigation via mouse wheel + mouseWheel: false, + + // Apply a 3D roll to links on hover + rollingLinks: true, + + // Transition style + transition: 'default' // default/cube/page/concave/zoom/linear/fade/none + +}); +``` + +Note that the new default vertical centering option will break compatibility with slides that were using transitions with backgrounds (`cube` and `page`). To restore the previous behavior, set `center` to `false`. + +The configuration can be updated after initialization using the ```configure``` method: + +```javascript +// Turn autoSlide off +Reveal.configure({ autoSlide: 0 }); + +// Start auto-sliding every 5s +Reveal.configure({ autoSlide: 5000 }); +``` + + +### Presentation Size + +All presentations have a normal size, that is the resolution at which they are authored. The framework will automatically scale presentations uniformly based on this size to ensure that everything fits on any given display or viewport. + +See below for a list of configuration options related to sizing, including default values: + +```javascript +Reveal.initialize({ + + ... + + // The "normal" size of the presentation, aspect ratio will be preserved + // when the presentation is scaled to fit different resolutions. Can be + // specified using percentage units. + width: 960, + height: 700, + + // Factor of the display size that should remain empty around the content + margin: 0.1, + + // Bounds for smallest/largest possible scale to apply to content + minScale: 0.2, + maxScale: 1.0 + +}); +``` + + +### Dependencies + +Reveal.js doesn't _rely_ on any third party scripts to work but a few optional libraries are included by default. These libraries are loaded as dependencies in the order they appear, for example: + +```javascript +Reveal.initialize({ + dependencies: [ + // Cross-browser shim that fully implements classList - https://github.com/eligrey/classList.js/ + { src: 'lib/js/classList.js', condition: function() { return !document.body.classList; } }, + + // Interpret Markdown in
elements + { src: 'plugin/markdown/showdown.js', condition: function() { return !!document.querySelector( '[data-markdown]' ); } }, + { src: 'plugin/markdown/markdown.js', condition: function() { return !!document.querySelector( '[data-markdown]' ); } }, + + // Syntax highlight for elements + { src: 'plugin/highlight/highlight.js', async: true, callback: function() { hljs.initHighlightingOnLoad(); } }, + + // Zoom in and out with Alt+click + { src: 'plugin/zoom-js/zoom.js', async: true, condition: function() { return !!document.body.classList; } }, + + // Speaker notes + { src: 'plugin/notes/notes.js', async: true, condition: function() { return !!document.body.classList; } }, + + // Remote control your reveal.js presentation using a touch device + { src: 'plugin/remotes/remotes.js', async: true, condition: function() { return !!document.body.classList; } } + ] +}); +``` + +You can add your own extensions using the same syntax. The following properties are available for each dependency object: +- **src**: Path to the script to load +- **async**: [optional] Flags if the script should load after reveal.js has started, defaults to false +- **callback**: [optional] Function to execute when the script has loaded +- **condition**: [optional] Function which must return true for the script to be loaded + + +### API + +The ``Reveal`` class provides a minimal JavaScript API for controlling navigation and reading state: + +```javascript +// Navigation +Reveal.slide( indexh, indexv, indexf ); +Reveal.left(); +Reveal.right(); +Reveal.up(); +Reveal.down(); +Reveal.prev(); +Reveal.next(); +Reveal.prevFragment(); +Reveal.nextFragment(); +Reveal.toggleOverview(); + +// Retrieves the previous and current slide elements +Reveal.getPreviousSlide(); +Reveal.getCurrentSlide(); + +Reveal.getIndices(); // { h: 0, v: 0 } } +``` + +### States + +If you set ``data-state="somestate"`` on a slide ``
``, "somestate" will be applied as a class on the document element when that slide is opened. This allows you to apply broad style changes to the page based on the active slide. + +Furthermore you can also listen to these changes in state via JavaScript: + +```javascript +Reveal.addEventListener( 'somestate', function() { + // TODO: Sprinkle magic +}, false ); +``` + +### Ready event + +The 'ready' event is fired when reveal.js has loaded all (synchronous) dependencies and is ready to start navigating. + +```javascript +Reveal.addEventListener( 'ready', function( event ) { + // event.currentSlide, event.indexh, event.indexv +} ); +``` + +### Slide change event + +An 'slidechanged' event is fired each time the slide is changed (regardless of state). The event object holds the index values of the current slide as well as a reference to the previous and current slide HTML nodes. + +Some libraries, like MathJax (see [#226](https://github.com/hakimel/reveal.js/issues/226#issuecomment-10261609)), get confused by the transforms and display states of slides. Often times, this can be fixed by calling their update or render function from this callback. + +```javascript +Reveal.addEventListener( 'slidechanged', function( event ) { + // event.previousSlide, event.currentSlide, event.indexh, event.indexv +} ); +``` + +### Internal links + +It's easy to link between slides. The first example below targets the index of another slide whereas the second targets a slide with an ID attribute (```
```): + +```html +Link +Link +``` + +You can also add relative navigation links, similar to the built in reveal.js controls, by appending one of the following classes on any element. Note that each element is automatically given an ```enabled``` class when it's a valid navigation route based on the current slide. + +```html + + + + + + +``` + + +### Fragments +Fragments are used to highlight individual elements on a slide. Every elmement with the class ```fragment``` will be stepped through before moving on to the next slide. Here's an example: http://lab.hakim.se/reveal-js/#/16 + +The default fragment style is to start out invisible and fade in. This style can be changed by appending a different class to the fragment: + +```html +
+

grow

+

shrink

+

roll-in

+

fade-out

+

highlight-red

+

highlight-green

+

highlight-blue

+
+``` + +Multiple fragments can be applied to the same element sequentially by wrapping it, this will fade in the text on the first step and fade it back out on the second. + +```html +
+ + I'll fade in, then out + +
+``` + +The display order of fragments can be controlled using the ```data-fragment-index``` attribute. + +```html +
+

Appears last

+

Appears first

+

Appears second

+
+``` + +### Fragment events + +When a slide fragment is either shown or hidden reveal.js will dispatch an event. + +```javascript +Reveal.addEventListener( 'fragmentshown', function( event ) { + // event.fragment = the fragment DOM element +} ); +Reveal.addEventListener( 'fragmenthidden', function( event ) { + // event.fragment = the fragment DOM element +} ); +``` + +### Code syntax highlighting + +By default, Reveal is configured with [highlight.js](http://softwaremaniacs.org/soft/highlight/en/) for code syntax highlighting. Below is an example with clojure code that will be syntax highlighted: + +```html +
+

+(def lazy-fib
+  (concat
+   [0 1]
+   ((fn rfib [a b]
+        (lazy-cons (+ a b) (rfib b (+ a b)))) 0 1)))
+	
+
+``` + + +### Overview mode + +Press "Esc" key to toggle the overview mode on and off. While you're in this mode, you can still navigate between slides, +as if you were at 1,000 feet above your presentation. The overview mode comes with a few API hooks: + +```javascript +Reveal.addEventListener( 'overviewshown', function( event ) { /* ... */ } ); +Reveal.addEventListener( 'overviewhidden', function( event ) { /* ... */ } ); + +// Toggle the overview mode programmatically +Reveal.toggleOverview(); +``` + +### Fullscreen mode +Just press »F« on your keyboard to show your presentation in fullscreen mode. Press the »ESC« key to exit fullscreen mode. + + +## PDF Export + +Presentations can be exported to PDF via a special print stylesheet. This feature requires that you use [Google Chrome](http://google.com/chrome). +Here's an example of an exported presentation that's been uploaded to SlideShare: http://www.slideshare.net/hakimel/revealjs-13872948. + +1. Open your presentation with [css/print/pdf.css](https://github.com/hakimel/reveal.js/blob/master/css/print/pdf.css) included on the page. The default index HTML lets you add *print-pdf* anywhere in the query to include the stylesheet, for example: [lab.hakim.se/reveal-js?print-pdf](http://lab.hakim.se/reveal-js?print-pdf). +2. Open the in-browser print dialog (CMD+P). +3. Change the **Destination** setting to **Save as PDF**. +4. Change the **Layout** to **Landscape**. +5. Change the **Margins** to **None**. +6. Click **Save**. + +![Chrome Print Settings](https://s3.amazonaws.com/hakim-static/reveal-js/pdf-print-settings.png) + + +## Speaker Notes + +reveal.js comes with a speaker notes plugin which can be used to present per-slide notes in a separate browser window. The notes window also gives you a preview of the next upcoming slide so it may be helpful even if you haven't written any notes. Append ```?notes``` to the presentation URL or press the 's' key on your keyboard to open the notes window. + +By default notes are written using standard HTML, see below, but you can add a ```data-markdown``` attribute to the ```