From 68fb312696aed6cb1fbf0d41d178a38ce3b6bcad Mon Sep 17 00:00:00 2001 From: Ulric Wilfred Date: Tue, 8 Sep 2015 12:04:43 +0200 Subject: [PATCH 01/16] loading mode constants --- examples/configuration.js | 30 +++++++++++++++++++++--------- src/anm/constants.js | 23 +++++++++++++++++------ 2 files changed, 38 insertions(+), 15 deletions(-) diff --git a/examples/configuration.js b/examples/configuration.js index 95829331..df31e7c6 100644 --- a/examples/configuration.js +++ b/examples/configuration.js @@ -8,6 +8,15 @@ var currentMode; // embed, config, publish, html var shortVersion = true; +var loadingModes = [ + { value: 'rightaway', name: 'right away', description: 'loads animation just immediately when finds it\'s source (i.e. from HTML attribute)' }, + { value: 'onrequest', name: 'on request', description: 'waits for user to manually call .load() method' }, + { value: 'onplay', name: 'on play', description: 'when play button was pressed, starts loading a scene and plays it just after' }, + { value: 'onidle', name: 'on idle', description: ' waits for pause in user actions (mouse move, clicks, keyboard) to load the animation' }, + { value: 'onhover', name: 'on hover', description: 'starts loading animation when user hovered with mouse over the player canvas' }, + { value: 'wheninview', name: 'when in view', description: 'starts loading animation when Player appeares in browser viewport' } +]; + function getElm(id) { return document.getElementById(id); } function collectOptions() { @@ -133,12 +142,15 @@ function init() { 'loading': { label: 'Loading', type: 'select', create: function() { var select = document.createElement('select'); - var onPlay = document.createElement('option'); - onPlay.innerText = onPlay.textContent = 'on play'; - var onRequest = document.createElement('option'); - onRequest.innerText = onRequest.textContent = 'on request'; - select.appendChild(onPlay); - select.appendChild(onRequest); + for (var i = 0, il = loadingModes.length, mode; i < il; i++) { + var option = document.createElement('option'); + option.innerText = option.textContent = loadingModes[i].name; + select.appendChild(option); + } + select.setAttribute('title', loadingModes[0].description); + select.addEventListener('change', function() { + select.setAttribute('title', loadingModes[select.selectedIndex].description); + }); return select; }, modify: function(elm, form) { elm.selectedIndex = 0; } }, @@ -193,7 +205,7 @@ var optionsMapper = function(mode, options) { function numberOption(v) { return v; }; function colorOption(v) { return (v.indexOf('#') >= 0) ? v.slice(1) : v; }; function booleanOption(v) { return v ? '1' : '0'; }; - function loadingModeOption(v) { return (v === 1) ? 'onrequest' : 'onplay' }; + function loadingModeOption(v) { return loadingModes[v].value }; return { width: extractOption('width', 'w', 'width', numberOption), @@ -225,7 +237,7 @@ var optionsMapper = function(mode, options) { function textOption(v) { return '\'' + v + '\''; }; function colorOption(v) { return '\'' + v + '\''; }; function booleanOption(v) { return v ? 'true' : 'false'; }; - function loadingModeOption(v) { return (v === 1) ? '\'onrequest\'' : '\'onplay\'' }; + function loadingModeOption(v) { return '\'' + loadingModes[v].value + '\''; }; function thumbnailOption(v) { return v; }; return { @@ -259,7 +271,7 @@ var optionsMapper = function(mode, options) { function textOption(v) { return v; }; function numberOption(v) { return v; }; function booleanOption(v) { return v ? 'true' : 'false'; }; - function loadingModeOption(v) { return (v === 1) ? 'onrequest' : 'onplay' }; + function loadingModeOption(v) { return loadingModes[v].value }; return { width: extractOption('width', 'anm-width', numberOption), diff --git a/src/anm/constants.js b/src/anm/constants.js index 652ebc83..4971fc75 100644 --- a/src/anm/constants.js +++ b/src/anm/constants.js @@ -65,12 +65,23 @@ C.LT_URL = 4; // ### Loading modes /* ---------------- */ -C.LM_ONREQUEST = 'onrequest'; -C.LM_ONPLAY = 'onplay'; -// C.LM_ONSCROLL -// C.LM_ONSCROLLIN - -C.LM_DEFAULT = C.LM_ONREQUEST; +// some loading modes below are closely tied to `autoPlay` option: if it's set to `true`, playing starts +// immediately after loading (default is `false`) for `rightaway`, `onidle`, `onhover` and `wheninview` + +C.LM_RIGHTAWAY = 'rightaway'; // searches for an animation source where possible (i.e. HTML tag attribute) + // and, if finds it, tries to load it on player creation; if source wasn't found, + // waits for user to call .load manually as for 'onrequest' +C.LM_ONREQUEST = 'onrequest'; // waits for user to manually call .load() method; if animation source was + // passed i.e. through HTML tag attribute, waits for user to call .load() + // method without parameters and uses this URL as a source +C.LM_ONPLAY = 'onplay'; // when play button was pressed, starts loading a scene and plays it just after (overrides `autoPlay`) +C.LM_ONIDLE = 'onidle'; // waits for pause in user actions (mouse move, clicks, keyboard) to load the animation; planned to use + // requestIdleCallback in future +C.LM_ONHOVER = 'onhover'; // starts loading animation when user hovered with mouse over the player canvas +C.LM_WHENINVIEW = 'wheninview'; // starts loading animation when at least some part of canvas appears in + // user's browser viewport + +C.LM_DEFAULT = C.LM_RIGHTAWAY; // Element From fac69bb21395d4836232d43a18b963cd35eb1b4a Mon Sep 17 00:00:00 2001 From: Ulric Wilfred Date: Tue, 8 Sep 2015 12:49:09 +0200 Subject: [PATCH 02/16] tests draft for loading modes --- spec/Runner.html | 1 + spec/karma.conf.js | 3 +- spec/loading-modes.spec.js | 89 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 92 insertions(+), 1 deletion(-) create mode 100644 spec/loading-modes.spec.js diff --git a/spec/Runner.html b/spec/Runner.html index 393fa68d..c9c9f7b6 100644 --- a/spec/Runner.html +++ b/spec/Runner.html @@ -18,6 +18,7 @@ + diff --git a/spec/karma.conf.js b/spec/karma.conf.js index f859d0d3..268e0b42 100644 --- a/spec/karma.conf.js +++ b/spec/karma.conf.js @@ -19,7 +19,8 @@ module.exports = function(config) { './dist/bundle/animatron.min.js', './spec/search.spec.js', - './spec/orient-to-path.spec.js' + './spec/orient-to-path.spec.js', + './spec/loading-modes.spec.js' ], diff --git a/spec/loading-modes.spec.js b/spec/loading-modes.spec.js new file mode 100644 index 00000000..0ca00205 --- /dev/null +++ b/spec/loading-modes.spec.js @@ -0,0 +1,89 @@ +describe('loading modes', function() { + + var elmId = 'player-target', + elm; + + var player; + + beforeEach(function() { + elm = document.createElement('div'); + elm.id = elmId; + document.body.appendChild(elm); + }); + + afterEach(function() { + if (player) { + player.detach(); + } + document.body.removeChild(elm); + }); + + it('should have `rightaway` as default option', function() { + expect(anm.createPlayer(elmId).loadingMode).toBe(anm.C.LM_RIGHTAWAY); + }); + + describe('right away', function() { + + it('should automatically load a scene when source specified with attribute', function() { + + }); + + it('should automatically load a scene when source passed with forSnapshot', function() { + + }); + + it('should not load anything when player created and source wasn\'t specified', function() { + + }); + + }); + + describe('on request', function() { + + it('should not load anything when player created and source wasn\'t specified', function() { + + }); + + it('still should not load anything even when source was specified with HTML attribute', function() { + + }); + + it('still should not load anything even when source was with forSnapshot', function() { + + }); + + it('should load animation when load called manually', function() { + + }); + + it('should load animation when load called manually w/o arguments and source was specified', function() { + + }); + + }); + + describe('on play', function() { + + it('should not load anything when player was created', function() { + + }); + + it('should load animation when `load` was called manually', function() { + + }); + + it('should load animation before playing if `load` wasn\'t called before `play`', function() { + + }); + + }); + + xdescribe('onload'); + + xdescribe('onidle'); + + xdescribe('onhover'); + + xdescribe('wheninview'); + +}); From 5219ce86b56be1a7527ec07473daefa71a13eb75 Mon Sep 17 00:00:00 2001 From: Ulric Wilfred Date: Tue, 8 Sep 2015 15:44:24 +0200 Subject: [PATCH 03/16] draft for loading mode tests, p.2, karma fixtures, some new useful methods, tests fail --- doc/embedding.md | 3 +- package.json | 5 ++- spec/empty.json | 1 + spec/karma.conf.js | 8 +++- spec/loading-modes.spec.js | 77 ++++++++++++++++++++++++++++---------- src/anm/player_manager.js | 11 ++++++ src/main.js | 10 ++++- 7 files changed, 88 insertions(+), 27 deletions(-) create mode 100644 spec/empty.json diff --git a/doc/embedding.md b/doc/embedding.md index 84225106..08dd6e25 100644 --- a/doc/embedding.md +++ b/doc/embedding.md @@ -246,7 +246,8 @@ URL | `IFRAME`/`div` | JS Object | Default | Description `z`/`zoom` | `anm-zoom` | `zoom` | `1` | animation zoom `t`/`from` | `anm-start-from` | - | `0` | a time to start playing from (multiplier is 10ms, so `310` means _3s 100ms_) `p`/`at` | `anm-stop-at` | - | - | a time of animation where to stop at, when Player was initialized (multiplier is 10ms, so `310` means _3s 100ms_) -- | _`div`-only:_ `anm-src` | - | - | JSON for the animation to load from +- | _`div`-only:_ `anm-src` | - | - | JSON for the animation to load from +- | _`div`-only:_ `anm-importer` | - | `animatron` | Importer to use with this JSON `m`/`mode` | `anm-mode` | `mode` | - | (_deprecated_) a mode of a Player, one of: ... `lm`/`lmode` | `anm-loading-mode` | `loadingMode` | `onplay` | `onplay` means to start loading an animation when user clicks _Play_ button (and show _thumbnail_ before), `onrequest` means to start loading animation only when the script asked for it and expect it to be completely loaded when user clicks _Play_ button - | `anm-events` | `handleEvents` | `false` | allows animation to catch and process user mouse/keyboard events by itself (has a meaning for games or infographics) diff --git a/package.json b/package.json index 4069cdd8..930e2d62 100644 --- a/package.json +++ b/package.json @@ -70,7 +70,8 @@ "jasmine-core": "^2.3.4", "karma": "^0.12.37", "karma-chrome-launcher": "^0.2.0", - "karma-mocha-reporter": "^1.0.2", - "karma-jasmine": "^0.3.6" + "karma-fixture": "^0.2.5", + "karma-jasmine": "^0.3.6", + "karma-mocha-reporter": "^1.0.2" } } diff --git a/spec/empty.json b/spec/empty.json new file mode 100644 index 00000000..0967ef42 --- /dev/null +++ b/spec/empty.json @@ -0,0 +1 @@ +{} diff --git a/spec/karma.conf.js b/spec/karma.conf.js index 268e0b42..eaa0ccb1 100644 --- a/spec/karma.conf.js +++ b/spec/karma.conf.js @@ -11,7 +11,7 @@ module.exports = function(config) { // frameworks to use // available frameworks: https://npmjs.org/browse/keyword/karma-adapter - frameworks: ['jasmine'], + frameworks: ['jasmine', 'fixture'], // list of files / patterns to load in the browser @@ -20,7 +20,11 @@ module.exports = function(config) { './spec/search.spec.js', './spec/orient-to-path.spec.js', - './spec/loading-modes.spec.js' + './spec/loading-modes.spec.js', + + // fixtures + { pattern: 'spec/empty.json', watched: true, served: true, included: false } + ], diff --git a/spec/loading-modes.spec.js b/spec/loading-modes.spec.js index 0ca00205..e2068a6a 100644 --- a/spec/loading-modes.spec.js +++ b/spec/loading-modes.spec.js @@ -1,39 +1,76 @@ describe('loading modes', function() { - var elmId = 'player-target', - elm; + var ELEMENT_ID = 'player-target'; - var player; + var JSON_SRC = '/base/spec/empty.json'; + + function FakeImporter() {}; + FakeImporter.prototype.load = function() { return new anm.Animation(); }; + anm.importers.register('fake', FakeImporter); + + function prepareDivElement(id) { + var element = document.createElement('div'); + element.id = id; + document.body.appendChild(element); + return element; + } + + function whenDocumentReady(f, done) { + if (!done) throw new Error('`done` handler was not passed!'); + anm.engine.onDocReady(function() { f(); done(); }); + } beforeEach(function() { - elm = document.createElement('div'); - elm.id = elmId; - document.body.appendChild(elm); + //loadSpy = jasmine.createSpy('load'); }); afterEach(function() { - if (player) { - player.detach(); - } - document.body.removeChild(elm); + anm.detachAllPlayers(); // this will also detach element if players were created + //if (element && element.parentNode) document.body.removeChild(element); }); - it('should have `rightaway` as default option', function() { - expect(anm.createPlayer(elmId).loadingMode).toBe(anm.C.LM_RIGHTAWAY); + it('should have `rightaway` as default option', function(done) { + whenDocumentReady(function() { + prepareDivElement(ELEMENT_ID); + expect(anm.createPlayer(ELEMENT_ID).loadingMode).toBe(anm.C.LM_RIGHTAWAY); + }, done); }); describe('right away', function() { - it('should automatically load a scene when source specified with attribute', function() { + it('should automatically load a scene when source specified with attribute', function(done) { + whenDocumentReady(function() { + var element = prepareDivElement(ELEMENT_ID); + + element.setAttribute('anm-player-target', true); + element.setAttribute('anm-src', JSON_SRC); + element.setAttribute('anm-importer', 'fake'); + var loadSpy = jasmine.createSpy('load'); + anm.findAndInitPotentialPlayers({ 'handle': { 'load': loadSpy } }); + expect(loadSpy).toHaveBeenCalled(); + }, done); }); - it('should automatically load a scene when source passed with forSnapshot', function() { + it('should automatically load a scene when source passed with forSnapshot', function(done) { + whenDocumentReady(function() { + prepareDivElement(ELEMENT_ID); - }); + var fakeImporter = anm.importers.create('fake'); + var importLoadSpy = spyOn(fakeImporter, 'load'); + anm.Player.forSnapshot(ELEMENT_ID, JSON_SRC, fakeImporter); - it('should not load anything when player created and source wasn\'t specified', function() { + expect(importLoadSpy).toHaveBeenCalled(); + }, done); + }); + it('should not load anything when player created and source wasn\'t specified', function(done) { + whenDocumentReady(function() { + prepareDivElement(ELEMENT_ID); + var loadSpy = jasmine.createSpy('load'); + anm.createPlayer(ELEMENT_ID, { handle: { 'load': loadSpy } }); + expect(loadSpy).not.toHaveBeenCalled(); + }, done); }); }); @@ -78,12 +115,12 @@ describe('loading modes', function() { }); - xdescribe('onload'); + xdescribe('onload', function() {}); - xdescribe('onidle'); + xdescribe('onidle', function() {}); - xdescribe('onhover'); + xdescribe('onhover', function() {}); - xdescribe('wheninview'); + xdescribe('wheninview', function() {}); }); diff --git a/src/anm/player_manager.js b/src/anm/player_manager.js index b1199e91..2d4ac3fd 100644 --- a/src/anm/player_manager.js +++ b/src/anm/player_manager.js @@ -45,6 +45,17 @@ PlayerManager.prototype.getPlayer = function(cvs_id) { return this.hash[cvs_id]; }; +/** + * @method detachAll + * + * Detach all players created before + */ +PlayerManager.prototype.detachAll = function() { + for (var i = 0, il = this.instances.length; i < il; i++) { + this.instances[i].detach(); + } +} + /** * @method handleDocumentHiddenChange * @private diff --git a/src/main.js b/src/main.js index 8a24af5f..be3ffa85 100644 --- a/src/main.js +++ b/src/main.js @@ -13,15 +13,18 @@ var PUBLIC_NAMESPACE = 'anm'; var constants = require('./anm/constants.js'), engine = require('engine'), + manager = require('./anm/player_manager.js'), Player = require('./anm/player.js'); -function findAndInitPotentialPlayers() { +function findAndInitPotentialPlayers(options) { var matches = engine.findPotentialPlayers(); for (var i = 0, il = matches.length; i < il; i++) { - anm.createPlayer(matches[i]); + anm.createPlayer(matches[i], options); } } +var detachAllPlayers = manager.detachAll.bind(manager); + engine.onDocReady(findAndInitPotentialPlayers); var Element = require('./anm/animation/element.js'), @@ -76,6 +79,9 @@ var anm = { Audio: require('./anm/media/audio.js'), Video: require('./anm/media/video.js'), + findAndInitPotentialPlayers: findAndInitPotentialPlayers, + detachAllPlayers: detachAllPlayers, + interop: { playerjs: require('./anm/interop/playerjs-io.js') }, From 7a24e2d16a41d8fb8e8ebd0af41971ec332b8803 Mon Sep 17 00:00:00 2001 From: Ulric Wilfred Date: Wed, 16 Sep 2015 17:21:23 +0200 Subject: [PATCH 04/16] add debug ability to tests, fix tests themselves --- Jakefile | 25 +++++++++++++++++++++++++ spec/karma.conf.js | 8 +++++++- spec/loading-modes.spec.js | 37 +++++++++++++++++++++---------------- src/anm/loader.js | 1 + src/anm/loc.js | 1 + src/anm/player.js | 5 +++++ 6 files changed, 60 insertions(+), 17 deletions(-) diff --git a/Jakefile b/Jakefile index a3401244..36612b16 100644 --- a/Jakefile +++ b/Jakefile @@ -243,6 +243,13 @@ desc(_dfit_nl(['Run tests for the distribution.', 'Requires: `karma`, `karma-mocha-reporter`.'])); task('test', ['dist-min', 'test-dist']); +// test-debug ================================================================== + +desc(_dfit_nl(['Run tests with debug for the unminified distribution.', + 'Usage: Just call {jake test}.', + 'Requires: `karma`, `karma-mocha-reporter`.'])); +task('test-debug', ['dist', 'test-dist-debug']); + // test-dist =================================================================== desc(_dfit_nl(['Test the distribution which already exists.', @@ -260,6 +267,24 @@ task('test-dist', { async: true }, function() { complete(); }); }); +// test-dist-debug ============================================================= + +desc(_dfit_nl(['Test the unminified distribution which already exists.', + 'Usage: Just call {jake test-dist-debug}.', + 'Requires: `karma`, `karma-mocha-reporter`.'])); +task('test-dist-debug', { async: true }, function() { + _print('Running tests'); + + jake.exec([ Binaries.KARMA, 'start', + _loc(Tests.Config), + '--single-run=false', + '--debug' + ].join(' '), EXEC_OPTS, + function() { _print('Tests finished successfully'); + _print(DONE_MARKER); + complete(); }); +}); + // docs ======================================================================== desc(_dfit_nl(['Generate Docco docs and compile API documentation into '+ diff --git a/spec/karma.conf.js b/spec/karma.conf.js index eaa0ccb1..8b9f9e2e 100644 --- a/spec/karma.conf.js +++ b/spec/karma.conf.js @@ -3,6 +3,12 @@ module.exports = function(config) { + function isDebug() { + return process.argv.some(function(argument) { + argument === '--debug'; + }); + } + var options = { // base path that will be used to resolve all patterns (eg. files, exclude) @@ -16,7 +22,7 @@ module.exports = function(config) { // list of files / patterns to load in the browser files: [ - './dist/bundle/animatron.min.js', + isDebug ? './dist/bundle/animatron.js' : './dist/bundle/animatron.min.js', './spec/search.spec.js', './spec/orient-to-path.spec.js', diff --git a/spec/loading-modes.spec.js b/spec/loading-modes.spec.js index e2068a6a..f1ecb3b7 100644 --- a/spec/loading-modes.spec.js +++ b/spec/loading-modes.spec.js @@ -2,7 +2,7 @@ describe('loading modes', function() { var ELEMENT_ID = 'player-target'; - var JSON_SRC = '/base/spec/empty.json'; + var JSON_NODE_SRC = '/base/spec/empty.json'; function FakeImporter() {}; FakeImporter.prototype.load = function() { return new anm.Animation(); }; @@ -15,9 +15,8 @@ describe('loading modes', function() { return element; } - function whenDocumentReady(f, done) { - if (!done) throw new Error('`done` handler was not passed!'); - anm.engine.onDocReady(function() { f(); done(); }); + function whenDocumentReady(f) { + anm.engine.onDocReady(f); } beforeEach(function() { @@ -33,7 +32,8 @@ describe('loading modes', function() { whenDocumentReady(function() { prepareDivElement(ELEMENT_ID); expect(anm.createPlayer(ELEMENT_ID).loadingMode).toBe(anm.C.LM_RIGHTAWAY); - }, done); + done(); + }); }); describe('right away', function() { @@ -43,13 +43,14 @@ describe('loading modes', function() { var element = prepareDivElement(ELEMENT_ID); element.setAttribute('anm-player-target', true); - element.setAttribute('anm-src', JSON_SRC); + element.setAttribute('anm-src', JSON_NODE_SRC); element.setAttribute('anm-importer', 'fake'); - var loadSpy = jasmine.createSpy('load'); - anm.findAndInitPotentialPlayers({ 'handle': { 'load': loadSpy } }); - expect(loadSpy).toHaveBeenCalled(); - }, done); + anm.findAndInitPotentialPlayers({ 'handle': { 'load': function(animation) { + expect(animation).toBeDefined(); + done(); + } } }); + }); }); it('should automatically load a scene when source passed with forSnapshot', function(done) { @@ -57,11 +58,13 @@ describe('loading modes', function() { prepareDivElement(ELEMENT_ID); var fakeImporter = anm.importers.create('fake'); - var importLoadSpy = spyOn(fakeImporter, 'load'); - anm.Player.forSnapshot(ELEMENT_ID, JSON_SRC, fakeImporter); - - expect(importLoadSpy).toHaveBeenCalled(); - }, done); + var importLoadSpy = spyOn(fakeImporter, 'load').and.callThrough(); + anm.Player.forSnapshot(ELEMENT_ID, JSON_NODE_SRC, fakeImporter, function(animation) { + expect(animation).toBeDefined(); + expect(importLoadSpy).toHaveBeenCalled(); + done(); + }); + }); }); it('should not load anything when player created and source wasn\'t specified', function(done) { @@ -70,7 +73,9 @@ describe('loading modes', function() { var loadSpy = jasmine.createSpy('load'); anm.createPlayer(ELEMENT_ID, { handle: { 'load': loadSpy } }); expect(loadSpy).not.toHaveBeenCalled(); - }, done); + + done(); + }); }); }); diff --git a/src/anm/loader.js b/src/anm/loader.js index 97be13a4..76d867e0 100644 --- a/src/anm/loader.js +++ b/src/anm/loader.js @@ -71,6 +71,7 @@ Loader.loadFromUrl = function(player, url, importer, callback) { Loader.loadFromObj = function(player, object, importer, callback) { if (!importer) throw errors.player(ErrLoc.P.NO_IMPORTER_TO_LOAD_WITH, player); var anim = importer.load(object); + if (!anim) throw errors.player(ErrLoc.P.IMPORTER_RETURNED_EMPTY_ANIMATION, player); player.fire(C.S_IMPORT, importer, anim, object); Loader.loadAnimation(player, anim, callback); }; diff --git a/src/anm/loc.js b/src/anm/loc.js index 4a24126c..e7df716e 100644 --- a/src/anm/loc.js +++ b/src/anm/loc.js @@ -52,6 +52,7 @@ Errors.P.INIT_AFTER_LOAD = 'Initialization was called after loading a animation' Errors.P.SNAPSHOT_LOADING_FAILED = 'Snapshot failed to load ({0})'; Errors.P.IMPORTER_CONSTRUCTOR_PASSED = 'You\'ve passed importer constructor to snapshot loader, but not an instance! ' + 'Probably you used anm.importers.get instead of anm.importers.create.'; +Errors.P.IMPORTER_RETURNED_EMPTY_ANIMATION = 'The importer has not returned any Animation object from .load method'; Errors.P.DOM_NOT_READY = 'Document in not yet ready, please consider moving your initialization script to the bottom of your web page'; Errors.A.OBJECT_IS_NOT_ELEMENT = 'It appears that you\'ve passed not an instance of anm.Element'; Errors.A.ELEMENT_IS_REGISTERED = 'This element is already registered in animation'; diff --git a/src/anm/player.js b/src/anm/player.js index fe098798..613582bb 100644 --- a/src/anm/player.js +++ b/src/anm/player.js @@ -233,6 +233,11 @@ Player.prototype.init = function(elm, opts) { } catch(e) {} this._addOpts(opts || {}); this._postInit(); + if (opts && opts.handle) { + for (var event in opts.handle) { + this.on(event, opts.handle[event]); + } + } this._checkOpts(); /* TODO: if (this.canvas.hasAttribute('data-url')) */ From b064ac49462c2911611ae0ffbd04d37f226b96af Mon Sep 17 00:00:00 2001 From: Ulric Wilfred Date: Mon, 21 Sep 2015 14:34:32 +0200 Subject: [PATCH 05/16] removing karma fixtures, will use jasmine ajax stub instead --- package.json | 1 - spec/empty.json | 1 - spec/karma.conf.js | 5 +---- spec/loading-modes.spec.js | 25 ++++++++++++++++++++++--- 4 files changed, 23 insertions(+), 9 deletions(-) delete mode 100644 spec/empty.json diff --git a/package.json b/package.json index 930e2d62..c7820eb9 100644 --- a/package.json +++ b/package.json @@ -70,7 +70,6 @@ "jasmine-core": "^2.3.4", "karma": "^0.12.37", "karma-chrome-launcher": "^0.2.0", - "karma-fixture": "^0.2.5", "karma-jasmine": "^0.3.6", "karma-mocha-reporter": "^1.0.2" } diff --git a/spec/empty.json b/spec/empty.json deleted file mode 100644 index 0967ef42..00000000 --- a/spec/empty.json +++ /dev/null @@ -1 +0,0 @@ -{} diff --git a/spec/karma.conf.js b/spec/karma.conf.js index 8b9f9e2e..a5953cf7 100644 --- a/spec/karma.conf.js +++ b/spec/karma.conf.js @@ -26,10 +26,7 @@ module.exports = function(config) { './spec/search.spec.js', './spec/orient-to-path.spec.js', - './spec/loading-modes.spec.js', - - // fixtures - { pattern: 'spec/empty.json', watched: true, served: true, included: false } + './spec/loading-modes.spec.js' ], diff --git a/spec/loading-modes.spec.js b/spec/loading-modes.spec.js index f1ecb3b7..3470fe8b 100644 --- a/spec/loading-modes.spec.js +++ b/spec/loading-modes.spec.js @@ -38,7 +38,7 @@ describe('loading modes', function() { describe('right away', function() { - it('should automatically load a scene when source specified with attribute', function(done) { + it('should automatically load a scene when source specified with HTML attribute', function(done) { whenDocumentReady(function() { var element = prepareDivElement(ELEMENT_ID); @@ -82,12 +82,31 @@ describe('loading modes', function() { describe('on request', function() { - it('should not load anything when player created and source wasn\'t specified', function() { + it('should not load anything when player created and source wasn\'t specified', function(done) { + whenDocumentReady(function() { + prepareDivElement(ELEMENT_ID); + var loadSpy = jasmine.createSpy('load'); + anm.createPlayer(ELEMENT_ID, { loadingMode: anm.C.LM_ONREQUEST, + handle: { 'load': loadSpy } }); + expect(loadSpy).not.toHaveBeenCalled(); + done(); + }); }); - it('still should not load anything even when source was specified with HTML attribute', function() { + it('still should not load anything even when source was specified with HTML attribute', function(done) { + whenDocumentReady(function() { + var element = prepareDivElement(ELEMENT_ID); + + element.setAttribute('anm-player-target', true); + element.setAttribute('anm-src', JSON_NODE_SRC); + element.setAttribute('anm-importer', 'fake'); + anm.findAndInitPotentialPlayers({ 'handle': { 'load': function(animation) { + expect(animation).toBeDefined(); + done(); + } } }); + }); }); it('still should not load anything even when source was with forSnapshot', function() { From 3f24fd9885cd13af5e00fef1950e2cd54223c5ee Mon Sep 17 00:00:00 2001 From: Ulric Wilfred Date: Mon, 21 Sep 2015 15:45:39 +0200 Subject: [PATCH 06/16] Update Jasmine and use jasmine-ajax for tests --- package.json | 1 + spec/Runner.html | 17 +- spec/karma.conf.js | 2 +- .../MIT.LICENSE | 0 .../{jasmine-2.2.0 => jasmine-2.3.4}/boot.js | 12 +- .../console.js | 0 .../jasmine-html.js | 56 +- .../jasmine.css | 16 +- .../jasmine.js | 550 +++++++++---- .../jasmine_favicon.png | Bin spec/lib/jasmine-2.3.4/mock-ajax.js | 733 ++++++++++++++++++ spec/loading-modes.spec.js | 3 +- 12 files changed, 1201 insertions(+), 189 deletions(-) rename spec/lib/{jasmine-2.2.0 => jasmine-2.3.4}/MIT.LICENSE (100%) rename spec/lib/{jasmine-2.2.0 => jasmine-2.3.4}/boot.js (89%) mode change 100644 => 100755 rename spec/lib/{jasmine-2.2.0 => jasmine-2.3.4}/console.js (100%) mode change 100644 => 100755 rename spec/lib/{jasmine-2.2.0 => jasmine-2.3.4}/jasmine-html.js (87%) mode change 100644 => 100755 rename spec/lib/{jasmine-2.2.0 => jasmine-2.3.4}/jasmine.css (95%) mode change 100644 => 100755 rename spec/lib/{jasmine-2.2.0 => jasmine-2.3.4}/jasmine.js (86%) mode change 100644 => 100755 rename spec/lib/{jasmine-2.2.0 => jasmine-2.3.4}/jasmine_favicon.png (100%) mode change 100644 => 100755 create mode 100644 spec/lib/jasmine-2.3.4/mock-ajax.js diff --git a/package.json b/package.json index c7820eb9..702140d1 100644 --- a/package.json +++ b/package.json @@ -71,6 +71,7 @@ "karma": "^0.12.37", "karma-chrome-launcher": "^0.2.0", "karma-jasmine": "^0.3.6", + "karma-jasmine-ajax": "^0.1.13", "karma-mocha-reporter": "^1.0.2" } } diff --git a/spec/Runner.html b/spec/Runner.html index c9c9f7b6..9bc9894a 100644 --- a/spec/Runner.html +++ b/spec/Runner.html @@ -2,18 +2,19 @@ - Jasmine Spec Runner v2.2.0 + Jasmine Spec Runner v2.3.4 - - + + - - - + + + + + - - + diff --git a/spec/karma.conf.js b/spec/karma.conf.js index a5953cf7..d36a021a 100644 --- a/spec/karma.conf.js +++ b/spec/karma.conf.js @@ -17,7 +17,7 @@ module.exports = function(config) { // frameworks to use // available frameworks: https://npmjs.org/browse/keyword/karma-adapter - frameworks: ['jasmine', 'fixture'], + frameworks: ['jasmine-ajax', 'jasmine'], // list of files / patterns to load in the browser diff --git a/spec/lib/jasmine-2.2.0/MIT.LICENSE b/spec/lib/jasmine-2.3.4/MIT.LICENSE similarity index 100% rename from spec/lib/jasmine-2.2.0/MIT.LICENSE rename to spec/lib/jasmine-2.3.4/MIT.LICENSE diff --git a/spec/lib/jasmine-2.2.0/boot.js b/spec/lib/jasmine-2.3.4/boot.js old mode 100644 new mode 100755 similarity index 89% rename from spec/lib/jasmine-2.2.0/boot.js rename to spec/lib/jasmine-2.3.4/boot.js index e8ddd551..04ed64c1 --- a/spec/lib/jasmine-2.2.0/boot.js +++ b/spec/lib/jasmine-2.3.4/boot.js @@ -35,13 +35,9 @@ var jasmineInterface = jasmineRequire.interface(jasmine, env); /** - * Add all of the Jasmine global/public interface to the proper global, so a project can use the public interface directly. For example, calling `describe` in specs instead of `jasmine.getEnv().describe`. + * Add all of the Jasmine global/public interface to the global scope, so a project can use the public interface directly. For example, calling `describe` in specs instead of `jasmine.getEnv().describe`. */ - if (typeof window == "undefined" && typeof exports == "object") { - extend(exports, jasmineInterface); - } else { - extend(window, jasmineInterface); - } + extend(window, jasmineInterface); /** * ## Runner Parameters @@ -56,6 +52,9 @@ var catchingExceptions = queryString.getParam("catch"); env.catchExceptions(typeof catchingExceptions === "undefined" ? true : catchingExceptions); + var throwingExpectationFailures = queryString.getParam("throwFailures"); + env.throwOnExpectationFailure(throwingExpectationFailures); + /** * ## Reporters * The `HtmlReporter` builds all of the HTML UI for the runner page. This reporter paints the dots, stars, and x's for specs, as well as all spec names and all failures (if any). @@ -63,6 +62,7 @@ var htmlReporter = new jasmine.HtmlReporter({ env: env, onRaiseExceptionsClick: function() { queryString.navigateWithNewParam("catch", !env.catchingExceptions()); }, + onThrowExpectationsClick: function() { queryString.navigateWithNewParam("throwFailures", !env.throwingExpectationFailures()); }, addToExistingQueryString: function(key, value) { return queryString.fullStringWithNewParam(key, value); }, getContainer: function() { return document.body; }, createElement: function() { return document.createElement.apply(document, arguments); }, diff --git a/spec/lib/jasmine-2.2.0/console.js b/spec/lib/jasmine-2.3.4/console.js old mode 100644 new mode 100755 similarity index 100% rename from spec/lib/jasmine-2.2.0/console.js rename to spec/lib/jasmine-2.3.4/console.js diff --git a/spec/lib/jasmine-2.2.0/jasmine-html.js b/spec/lib/jasmine-2.3.4/jasmine-html.js old mode 100644 new mode 100755 similarity index 87% rename from spec/lib/jasmine-2.2.0/jasmine-html.js rename to spec/lib/jasmine-2.3.4/jasmine-html.js index bee5a04f..259f45ce --- a/spec/lib/jasmine-2.2.0/jasmine-html.js +++ b/spec/lib/jasmine-2.3.4/jasmine-html.js @@ -40,6 +40,7 @@ jasmineRequire.HtmlReporter = function(j$) { createElement = options.createElement, createTextNode = options.createTextNode, onRaiseExceptionsClick = options.onRaiseExceptionsClick || function() {}, + onThrowExpectationsClick = options.onThrowExpectationsClick || function() {}, addToExistingQueryString = options.addToExistingQueryString || defaultQueryString, timer = options.timer || noopTimer, results = [], @@ -145,22 +146,51 @@ jasmineRequire.HtmlReporter = function(j$) { this.jasmineDone = function() { var banner = find('.banner'); - banner.appendChild(createDom('span', {className: 'duration'}, 'finished in ' + timer.elapsed() / 1000 + 's')); - var alert = find('.alert'); + alert.appendChild(createDom('span', {className: 'duration'}, 'finished in ' + timer.elapsed() / 1000 + 's')); + + banner.appendChild( + createDom('div', { className: 'run-options' }, + createDom('span', { className: 'trigger' }, 'Options'), + createDom('div', { className: 'payload' }, + createDom('div', { className: 'exceptions' }, + createDom('input', { + className: 'raise', + id: 'raise-exceptions', + type: 'checkbox' + }), + createDom('label', { className: 'label', 'for': 'raise-exceptions' }, 'raise exceptions')), + createDom('div', { className: 'throw-failures' }, + createDom('input', { + className: 'throw', + id: 'throw-failures', + type: 'checkbox' + }), + createDom('label', { className: 'label', 'for': 'throw-failures' }, 'stop spec on expectation failure')) + ) + )); - alert.appendChild(createDom('span', { className: 'exceptions' }, - createDom('label', { className: 'label', 'for': 'raise-exceptions' }, 'raise exceptions'), - createDom('input', { - className: 'raise', - id: 'raise-exceptions', - type: 'checkbox' - }) - )); - var checkbox = find('#raise-exceptions'); + var raiseCheckbox = find('#raise-exceptions'); + + raiseCheckbox.checked = !env.catchingExceptions(); + raiseCheckbox.onclick = onRaiseExceptionsClick; - checkbox.checked = !env.catchingExceptions(); - checkbox.onclick = onRaiseExceptionsClick; + var throwCheckbox = find('#throw-failures'); + throwCheckbox.checked = env.throwingExpectationFailures(); + throwCheckbox.onclick = onThrowExpectationsClick; + + var optionsMenu = find('.run-options'), + optionsTrigger = optionsMenu.querySelector('.trigger'), + optionsPayload = optionsMenu.querySelector('.payload'), + isOpen = /\bopen\b/; + + optionsTrigger.onclick = function() { + if (isOpen.test(optionsPayload.className)) { + optionsPayload.className = optionsPayload.className.replace(isOpen, ''); + } else { + optionsPayload.className += ' open'; + } + }; if (specsExecuted < totalSpecsDefined) { var skippedMessage = 'Ran ' + specsExecuted + ' of ' + totalSpecsDefined + ' specs - run all'; diff --git a/spec/lib/jasmine-2.2.0/jasmine.css b/spec/lib/jasmine-2.3.4/jasmine.css old mode 100644 new mode 100755 similarity index 95% rename from spec/lib/jasmine-2.2.0/jasmine.css rename to spec/lib/jasmine-2.3.4/jasmine.css index ecc5f5e7..f9f4ae90 --- a/spec/lib/jasmine-2.2.0/jasmine.css +++ b/spec/lib/jasmine-2.3.4/jasmine.css @@ -8,11 +8,10 @@ body { overflow-y: scroll; } .jasmine_html-reporter .banner { position: relative; } .jasmine_html-reporter .banner .title { background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFoAAAAZCAMAAACGusnyAAACdlBMVEX/////AP+AgICqVaqAQICZM5mAVYCSSZKAQICOOY6ATYCLRouAQICJO4mSSYCIRIiPQICHPIeOR4CGQ4aMQICGPYaLRoCFQ4WKQICPPYWJRYCOQoSJQICNPoSIRICMQoSHQICHRICKQoOHQICKPoOJO4OJQYOMQICMQ4CIQYKLQICIPoKLQ4CKQICNPoKJQISMQ4KJQoSLQYKJQISLQ4KIQoSKQYKIQICIQISMQoSKQYKLQIOLQoOJQYGLQIOKQIOMQoGKQYOLQYGKQIOLQoGJQYOJQIOKQYGJQIOKQoGKQIGLQIKLQ4KKQoGLQYKJQIGKQYKJQIGKQIKJQoGKQYKLQIGKQYKLQIOJQoKKQoOJQYKKQIOJQoKKQoOKQIOLQoKKQYOLQYKJQIOKQoKKQYKKQoKJQYOKQYKLQIOKQoKLQYOKQYKLQIOJQoGKQYKJQYGJQoGKQYKLQoGLQYGKQoGJQYKKQYGJQIKKQoGJQYKLQIKKQYGLQYKKQYGKQYGKQYKJQYOKQoKJQYOKQYKLQYOLQYOKQYKLQYOKQoKKQYKKQYOKQYOJQYKKQYKLQYKKQIKKQoKKQYKKQYKKQoKJQIKKQYKLQYKKQYKKQIKKQYKKQYKKQYKKQIKKQYKJQYGLQYGKQYKKQYKKQYGKQIKKQYGKQYOJQoKKQYOLQYKKQYOKQoKKQYKKQoKKQYKKQYKJQYKLQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKJQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKLQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKmIDpEAAAA0XRSTlMAAQIDBAUGBwgJCgsMDQ4PEBESExQVFhcYGRobHB0eHyAiIyQlJycoKissLS4wMTQ1Njc4OTo7PDw+P0BCQ0RISUpLTE1OUFNUVVdYWFlaW15fYGFiY2ZnaGlqa2xtb3BxcnN0dnh5ent8fX5/gIGChIWIioyNjo+QkZOUlZaYmZqbnJ2eoKGio6WmqKmsra6vsLGztre4ubq7vL2+wMHDxMjJysvNzs/Q0dLU1tfY2dvc3t/g4eLj5ebn6Onq6+zt7u/w8vP09fb3+Pn6+/z9/vkVQXAAAAMaSURBVHhe5dXxV1N1GMfxz2ABbDgIAm5VDJOyVDIJLUMaVpBWUZUaGbmqoGpZRSiGiRWp6KoZ5AB0ZY50RImZQIlahKkMYXv/R90dBvET/rJfOr3Ouc8v99zPec59zvf56j+vYKlViSf7250X4Mr3O29Tgq08BdGB4DhcekEJ5YkQKFsgWZdtj9JpV+I8xPjLFqkrsEIqO8PHSpis36jWazcqjEsfJjkvRssVU37SdIOu4XCf5vEJPsnwJpnRNU9JmxhMk8l1gehIrq7hTFjzOD+Vf88629qKMJVNltInFeRexRQyJlNeqd1iGDlSzrIUIyXbyFfm3RYprcQRe7lqtWyGYbfc6dT0R2vmdOOkX3u55C1rP37ftiH+tDby4r/RBT0w8TyEkr+epB9XgPDmSYYWbrhCuFYaIyw3fDQAXTnSkh+ANofiHmWf9l+FY1I90FdQTetstO00o23novzVsJ7uB3/C5TkbjRwZ5JerwV4iRWq9HFbFMaK/d0TYqayRiQPuIxxS3Bu8JWU90/60tKi7vkhaznez0a/TbVOKj5CaOZh6fWG6/Lyv9B/ZLR1gw/S/fpbeVD3MCW1li6SvWDOn65tr99/uvWtBS0XDm4s1t+sOHpG0kpBKx/l77wOSnxLpcx6TXmXLTPQOKYOf9Q1dfr8/SJ2mFdCvl1Yl93DiHUZvXeLJbGSzYu5gVJ2slbSakOR8dxCq5adQ2oFLqsE9Ex3L4qQO0eOPeU5x56bypXp4onSEb5OkICX6lDat55TeoztNKQcJaakrz9KCb95oD69IKq+yKW4XPjknaS52V0TZqE2cTtXjcHSCRmUO88e+85hj3EP74i9p8pylw7lxgMDyyl6OV7ZejnjNMfatu87LxRbH0IS35gt2a4ZjmGpVBdKK3Wr6INk8jWWSGqbA55CKgjBRC6E9w78ydTg3ABS3AFV1QN0Y4Aa2pgEjWnQURj9L0ayK6R2ysEqxHUKzYnLvvyU+i9KM2JHJzE4vyZOyDcOwOsySajeLPc8sNvPJkFlyJd20wpqAzZeAfZ3oWybxd+P/3j+SG3uSBdf2VQAAAABJRU5ErkJggg==') no-repeat; background: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgoKPHN2ZwogICB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iCiAgIHhtbG5zOmNjPSJodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9ucyMiCiAgIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyIKICAgeG1sbnM6c3ZnPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxuczppbmtzY2FwZT0iaHR0cDovL3d3dy5pbmtzY2FwZS5vcmcvbmFtZXNwYWNlcy9pbmtzY2FwZSIKICAgdmVyc2lvbj0iMS4xIgogICB3aWR0aD0iNjgxLjk2MjUyIgogICBoZWlnaHQ9IjE4Ny41IgogICBpZD0ic3ZnMiIKICAgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PG1ldGFkYXRhCiAgICAgaWQ9Im1ldGFkYXRhOCI+PHJkZjpSREY+PGNjOldvcmsKICAgICAgICAgcmRmOmFib3V0PSIiPjxkYzpmb3JtYXQ+aW1hZ2Uvc3ZnK3htbDwvZGM6Zm9ybWF0PjxkYzp0eXBlCiAgICAgICAgICAgcmRmOnJlc291cmNlPSJodHRwOi8vcHVybC5vcmcvZGMvZGNtaXR5cGUvU3RpbGxJbWFnZSIgLz48L2NjOldvcms+PC9yZGY6UkRGPjwvbWV0YWRhdGE+PGRlZnMKICAgICBpZD0iZGVmczYiPjxjbGlwUGF0aAogICAgICAgaWQ9ImNsaXBQYXRoMTgiPjxwYXRoCiAgICAgICAgIGQ9Ik0gMCwxNTAwIDAsMCBsIDU0NTUuNzQsMCAwLDE1MDAgTCAwLDE1MDAgeiIKICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgaWQ9InBhdGgyMCIgLz48L2NsaXBQYXRoPjwvZGVmcz48ZwogICAgIHRyYW5zZm9ybT0ibWF0cml4KDEuMjUsMCwwLC0xLjI1LDAsMTg3LjUpIgogICAgIGlkPSJnMTAiPjxnCiAgICAgICB0cmFuc2Zvcm09InNjYWxlKDAuMSwwLjEpIgogICAgICAgaWQ9ImcxMiI+PGcKICAgICAgICAgaWQ9ImcxNCI+PGcKICAgICAgICAgICBjbGlwLXBhdGg9InVybCgjY2xpcFBhdGgxOCkiCiAgICAgICAgICAgaWQ9ImcxNiI+PHBhdGgKICAgICAgICAgICAgIGQ9Im0gMTU0NCw1OTkuNDM0IGMgMC45MiwtNDAuMzUyIDI1LjY4LC04MS42MDIgNzEuNTMsLTgxLjYwMiAyNy41MSwwIDQ3LjY4LDEyLjgzMiA2MS40NCwzNS43NTQgMTIuODMsMjIuOTMgMTIuODMsNTYuODUyIDEyLjgzLDgyLjUyNyBsIDAsMzI5LjE4NCAtNzEuNTIsMCAwLDEwNC41NDMgMjY2LjgzLDAgMCwtMTA0LjU0MyAtNzAuNiwwIDAsLTM0NC43NyBjIDAsLTU4LjY5MSAtMy42OCwtMTA0LjUzMSAtNDQuOTMsLTE1Mi4yMTggLTM2LjY4LC00Mi4xOCAtOTYuMjgsLTY2LjAyIC0xNTMuMTQsLTY2LjAyIC0xMTcuMzcsMCAtMjA3LjI0LDc3Ljk0MSAtMjAyLjY0LDE5Ny4xNDUgbCAxMzAuMiwwIgogICAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgICAgIGlkPSJwYXRoMjIiCiAgICAgICAgICAgICBzdHlsZT0iZmlsbDojOGE0MTgyO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lIiAvPjxwYXRoCiAgICAgICAgICAgICBkPSJtIDIzMDEuNCw2NjIuNjk1IGMgMCw4MC43MDMgLTY2Ljk0LDE0NS44MTMgLTE0Ny42MywxNDUuODEzIC04My40NCwwIC0xNDcuNjMsLTY4Ljc4MSAtMTQ3LjYzLC0xNTEuMzAxIDAsLTc5Ljc4NSA2Ni45NCwtMTQ1LjgwMSAxNDUuOCwtMTQ1LjgwMSA4NC4zNSwwIDE0OS40Niw2Ny44NTIgMTQ5LjQ2LDE1MS4yODkgeiBtIC0xLjgzLC0xODEuNTQ3IGMgLTM1Ljc3LC01NC4wOTcgLTkzLjUzLC03OC44NTkgLTE1Ny43MiwtNzguODU5IC0xNDAuMywwIC0yNTEuMjQsMTE2LjQ0OSAtMjUxLjI0LDI1NC45MTggMCwxNDIuMTI5IDExMy43LDI2MC40MSAyNTYuNzQsMjYwLjQxIDYzLjI3LDAgMTE4LjI5LC0yOS4zMzYgMTUyLjIyLC04Mi41MjMgbCAwLDY5LjY4NyAxNzUuMTQsMCAwLC0xMDQuNTI3IC02MS40NCwwIDAsLTI4MC41OTggNjEuNDQsMCAwLC0xMDQuNTI3IC0xNzUuMTQsMCAwLDY2LjAxOSIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDI0IgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0ibSAyNjIyLjMzLDU1Ny4yNTggYyAzLjY3LC00NC4wMTYgMzMuMDEsLTczLjM0OCA3OC44NiwtNzMuMzQ4IDMzLjkzLDAgNjYuOTMsMjMuODI0IDY2LjkzLDYwLjUwNCAwLDQ4LjYwNiAtNDUuODQsNTYuODU2IC04My40NCw2Ni45NDEgLTg1LjI4LDIyLjAwNCAtMTc4LjgxLDQ4LjYwNiAtMTc4LjgxLDE1NS44NzkgMCw5My41MzYgNzguODYsMTQ3LjYzMyAxNjUuOTgsMTQ3LjYzMyA0NCwwIDgzLjQzLC05LjE3NiAxMTAuOTQsLTQ0LjAwOCBsIDAsMzMuOTIyIDgyLjUzLDAgMCwtMTMyLjk2NSAtMTA4LjIxLDAgYyAtMS44MywzNC44NTYgLTI4LjQyLDU3Ljc3NCAtNjMuMjYsNTcuNzc0IC0zMC4yNiwwIC02Mi4zNSwtMTcuNDIyIC02Mi4zNSwtNTEuMzQ4IDAsLTQ1Ljg0NyA0NC45MywtNTUuOTMgODAuNjksLTY0LjE4IDg4LjAyLC0yMC4xNzUgMTgyLjQ3LC00Ny42OTUgMTgyLjQ3LC0xNTcuNzM0IDAsLTk5LjAyNyAtODMuNDQsLTE1NC4wMzkgLTE3NS4xMywtMTU0LjAzOSAtNDkuNTMsMCAtOTQuNDYsMTUuNTgyIC0xMjYuNTUsNTMuMTggbCAwLC00MC4zNCAtODUuMjcsMCAwLDE0Mi4xMjkgMTE0LjYyLDAiCiAgICAgICAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgICAgICAgaWQ9InBhdGgyNiIKICAgICAgICAgICAgIHN0eWxlPSJmaWxsOiM4YTQxODI7ZmlsbC1vcGFjaXR5OjE7ZmlsbC1ydWxlOm5vbnplcm87c3Ryb2tlOm5vbmUiIC8+PHBhdGgKICAgICAgICAgICAgIGQ9Im0gMjk4OC4xOCw4MDAuMjU0IC02My4yNiwwIDAsMTA0LjUyNyAxNjUuMDUsMCAwLC03My4zNTUgYyAzMS4xOCw1MS4zNDcgNzguODYsODUuMjc3IDE0MS4yMSw4NS4yNzcgNjcuODUsMCAxMjQuNzEsLTQxLjI1OCAxNTIuMjEsLTEwMi42OTkgMjYuNiw2Mi4zNTEgOTIuNjIsMTAyLjY5OSAxNjAuNDcsMTAyLjY5OSA1My4xOSwwIDEwNS40NiwtMjIgMTQxLjIxLC02Mi4zNTEgMzguNTIsLTQ0LjkzOCAzOC41MiwtOTMuNTMyIDM4LjUyLC0xNDkuNDU3IGwgMCwtMTg1LjIzOSA2My4yNywwIDAsLTEwNC41MjcgLTIzOC40MiwwIDAsMTA0LjUyNyA2My4yOCwwIDAsMTU3LjcxNSBjIDAsMzIuMTAyIDAsNjAuNTI3IC0xNC42Nyw4OC45NTcgLTE4LjM0LDI2LjU4MiAtNDguNjEsNDAuMzQ0IC03OS43Nyw0MC4zNDQgLTMwLjI2LDAgLTYzLjI4LC0xMi44NDQgLTgyLjUzLC0zNi42NzIgLTIyLjkzLC0yOS4zNTUgLTIyLjkzLC01Ni44NjMgLTIyLjkzLC05Mi42MjkgbCAwLC0xNTcuNzE1IDYzLjI3LDAgMCwtMTA0LjUyNyAtMjM4LjQxLDAgMCwxMDQuNTI3IDYzLjI4LDAgMCwxNTAuMzgzIGMgMCwyOS4zNDggMCw2Ni4wMjMgLTE0LjY3LDkxLjY5OSAtMTUuNTksMjkuMzM2IC00Ny42OSw0NC45MzQgLTgwLjcsNDQuOTM0IC0zMS4xOCwwIC01Ny43NywtMTEuMDA4IC03Ny45NCwtMzUuNzc0IC0yNC43NywtMzAuMjUzIC0yNi42LC02Mi4zNDMgLTI2LjYsLTk5Ljk0MSBsIDAsLTE1MS4zMDEgNjMuMjcsMCAwLC0xMDQuNTI3IC0yMzguNCwwIDAsMTA0LjUyNyA2My4yNiwwIDAsMjgwLjU5OCIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDI4IgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0ibSAzOTk4LjY2LDk1MS41NDcgLTExMS44NywwIDAsMTE4LjI5MyAxMTEuODcsMCAwLC0xMTguMjkzIHogbSAwLC00MzEuODkxIDYzLjI3LDAgMCwtMTA0LjUyNyAtMjM5LjMzLDAgMCwxMDQuNTI3IDY0LjE5LDAgMCwyODAuNTk4IC02My4yNywwIDAsMTA0LjUyNyAxNzUuMTQsMCAwLC0zODUuMTI1IgogICAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgICAgIGlkPSJwYXRoMzAiCiAgICAgICAgICAgICBzdHlsZT0iZmlsbDojOGE0MTgyO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lIiAvPjxwYXRoCiAgICAgICAgICAgICBkPSJtIDQxNTkuMTIsODAwLjI1NCAtNjMuMjcsMCAwLDEwNC41MjcgMTc1LjE0LDAgMCwtNjkuNjg3IGMgMjkuMzUsNTQuMTAxIDg0LjM2LDgwLjY5OSAxNDQuODcsODAuNjk5IDUzLjE5LDAgMTA1LjQ1LC0yMi4wMTYgMTQxLjIyLC02MC41MjcgNDAuMzQsLTQ0LjkzNCA0MS4yNiwtODguMDMyIDQxLjI2LC0xNDMuOTU3IGwgMCwtMTkxLjY1MyA2My4yNywwIDAsLTEwNC41MjcgLTIzOC40LDAgMCwxMDQuNTI3IDYzLjI2LDAgMCwxNTguNjM3IGMgMCwzMC4yNjIgMCw2MS40MzQgLTE5LjI2LDg4LjAzNSAtMjAuMTcsMjYuNTgyIC01My4xOCwzOS40MTQgLTg2LjE5LDM5LjQxNCAtMzMuOTMsMCAtNjguNzcsLTEzLjc1IC04OC45NCwtNDEuMjUgLTIxLjA5LC0yNy41IC0yMS4wOSwtNjkuNjg3IC0yMS4wOSwtMTAyLjcwNyBsIDAsLTE0Mi4xMjkgNjMuMjYsMCAwLC0xMDQuNTI3IC0yMzguNCwwIDAsMTA0LjUyNyA2My4yNywwIDAsMjgwLjU5OCIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDMyIgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0ibSA1MDgyLjQ4LDcwMy45NjUgYyAtMTkuMjQsNzAuNjA1IC04MS42LDExNS41NDcgLTE1NC4wNCwxMTUuNTQ3IC02Ni4wNCwwIC0xMjkuMywtNTEuMzQ4IC0xNDMuMDUsLTExNS41NDcgbCAyOTcuMDksMCB6IG0gODUuMjcsLTE0NC44ODMgYyAtMzguNTEsLTkzLjUyMyAtMTI5LjI3LC0xNTYuNzkzIC0yMzEuMDUsLTE1Ni43OTMgLTE0My4wNywwIC0yNTcuNjgsMTExLjg3MSAtMjU3LjY4LDI1NS44MzYgMCwxNDQuODgzIDEwOS4xMiwyNjEuMzI4IDI1NC45MSwyNjEuMzI4IDY3Ljg3LDAgMTM1LjcyLC0zMC4yNTggMTgzLjM5LC03OC44NjMgNDguNjIsLTUxLjM0NCA2OC43OSwtMTEzLjY5NSA2OC43OSwtMTgzLjM4MyBsIC0zLjY3LC0zOS40MzQgLTM5Ni4xMywwIGMgMTQuNjcsLTY3Ljg2MyA3Ny4wMywtMTE3LjM2MyAxNDYuNzIsLTExNy4zNjMgNDguNTksMCA5MC43NiwxOC4zMjggMTE4LjI4LDU4LjY3MiBsIDExNi40NCwwIgogICAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgICAgIGlkPSJwYXRoMzQiCiAgICAgICAgICAgICBzdHlsZT0iZmlsbDojOGE0MTgyO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lIiAvPjxwYXRoCiAgICAgICAgICAgICBkPSJtIDY5MC44OTUsODUwLjcwMyA5MC43NSwwIDIyLjU0MywzMS4wMzUgMCwyNDMuMTIyIC0xMzUuODI5LDAgMCwtMjQzLjE0MSAyMi41MzYsLTMxLjAxNiIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDM2IgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0ibSA2MzIuMzk1LDc0Mi4yNTggMjguMDM5LDg2LjMwNCAtMjIuNTUxLDMxLjA0IC0yMzEuMjIzLDc1LjEyOCAtNDEuOTc2LC0xMjkuMTgzIDIzMS4yNTcsLTc1LjEzNyAzNi40NTQsMTEuODQ4IgogICAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgICAgIGlkPSJwYXRoMzgiCiAgICAgICAgICAgICBzdHlsZT0iZmlsbDojOGE0MTgyO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lIiAvPjxwYXRoCiAgICAgICAgICAgICBkPSJtIDcxNy40NDksNjUzLjEwNSAtNzMuNDEsNTMuMzYgLTM2LjQ4OCwtMTEuODc1IC0xNDIuOTAzLC0xOTYuNjkyIDEwOS44ODMsLTc5LjgyOCAxNDIuOTE4LDE5Ni43MDMgMCwzOC4zMzIiCiAgICAgICAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgICAgICAgaWQ9InBhdGg0MCIKICAgICAgICAgICAgIHN0eWxlPSJmaWxsOiM4YTQxODI7ZmlsbC1vcGFjaXR5OjE7ZmlsbC1ydWxlOm5vbnplcm87c3Ryb2tlOm5vbmUiIC8+PHBhdGgKICAgICAgICAgICAgIGQ9Im0gODI4LjUyLDcwNi40NjUgLTczLjQyNiwtNTMuMzQgMC4wMTEsLTM4LjM1OSBMIDg5OC4wMDQsNDE4LjA3IDEwMDcuOSw0OTcuODk4IDg2NC45NzMsNjk0LjYwOSA4MjguNTIsNzA2LjQ2NSIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDQyIgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0ibSA4MTIuMDg2LDgyOC41ODYgMjguMDU1LC04Ni4zMiAzNi40ODQsLTExLjgzNiAyMzEuMjI1LDc1LjExNyAtNDEuOTcsMTI5LjE4MyAtMjMxLjIzOSwtNzUuMTQgLTIyLjU1NSwtMzEuMDA0IgogICAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgICAgIGlkPSJwYXRoNDQiCiAgICAgICAgICAgICBzdHlsZT0iZmlsbDojOGE0MTgyO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lIiAvPjxwYXRoCiAgICAgICAgICAgICBkPSJtIDczNi4zMDEsMTMzNS44OCBjIC0zMjMuMDQ3LDAgLTU4NS44NzUsLTI2Mi43OCAtNTg1Ljg3NSwtNTg1Ljc4MiAwLC0zMjMuMTE4IDI2Mi44MjgsLTU4NS45NzcgNTg1Ljg3NSwtNTg1Ljk3NyAzMjMuMDE5LDAgNTg1LjgwOSwyNjIuODU5IDU4NS44MDksNTg1Ljk3NyAwLDMyMy4wMDIgLTI2Mi43OSw1ODUuNzgyIC01ODUuODA5LDU4NS43ODIgbCAwLDAgeiBtIDAsLTExOC42MSBjIDI1Ny45NzIsMCA0NjcuMTg5LC0yMDkuMTMgNDY3LjE4OSwtNDY3LjE3MiAwLC0yNTguMTI5IC0yMDkuMjE3LC00NjcuMzQ4IC00NjcuMTg5LC00NjcuMzQ4IC0yNTguMDc0LDAgLTQ2Ny4yNTQsMjA5LjIxOSAtNDY3LjI1NCw0NjcuMzQ4IDAsMjU4LjA0MiAyMDkuMTgsNDY3LjE3MiA0NjcuMjU0LDQ2Ny4xNzIiCiAgICAgICAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgICAgICAgaWQ9InBhdGg0NiIKICAgICAgICAgICAgIHN0eWxlPSJmaWxsOiM4YTQxODI7ZmlsbC1vcGFjaXR5OjE7ZmlsbC1ydWxlOm5vbnplcm87c3Ryb2tlOm5vbmUiIC8+PHBhdGgKICAgICAgICAgICAgIGQ9Im0gMTA5MS4xMyw2MTkuODgzIC0xNzUuNzcxLDU3LjEyMSAxMS42MjksMzUuODA4IDE3NS43NjIsLTU3LjEyMSAtMTEuNjIsLTM1LjgwOCIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDQ4IgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0iTSA4NjYuOTU3LDkwMi4wNzQgODM2LjUsOTI0LjE5OSA5NDUuMTIxLDEwNzMuNzMgOTc1LjU4NiwxMDUxLjYxIDg2Ni45NTcsOTAyLjA3NCIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDUwIgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0iTSA2MDcuNDY1LDkwMy40NDUgNDk4Ljg1NSwxMDUyLjk3IDUyOS4zMiwxMDc1LjEgNjM3LjkzLDkyNS41NjYgNjA3LjQ2NSw5MDMuNDQ1IgogICAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgICAgIGlkPSJwYXRoNTIiCiAgICAgICAgICAgICBzdHlsZT0iZmlsbDojOGE0MTgyO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lIiAvPjxwYXRoCiAgICAgICAgICAgICBkPSJtIDM4MC42ODgsNjIyLjEyOSAtMTEuNjI2LDM1LjgwMSAxNzUuNzU4LDU3LjA5IDExLjYyMSwtMzUuODAxIC0xNzUuNzUzLC01Ny4wOSIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDU0IgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0ibSA3MTYuMjg5LDM3Ni41OSAzNy42NDA2LDAgMCwxODQuODE2IC0zNy42NDA2LDAgMCwtMTg0LjgxNiB6IgogICAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgICAgIGlkPSJwYXRoNTYiCiAgICAgICAgICAgICBzdHlsZT0iZmlsbDojOGE0MTgyO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lIiAvPjwvZz48L2c+PC9nPjwvZz48L3N2Zz4=') no-repeat, none; -moz-background-size: 100%; -o-background-size: 100%; -webkit-background-size: 100%; background-size: 100%; display: block; float: left; width: 90px; height: 25px; } .jasmine_html-reporter .banner .version { margin-left: 14px; position: relative; top: 6px; } -.jasmine_html-reporter .banner .duration { position: absolute; right: 14px; top: 6px; } .jasmine_html-reporter #jasmine_content { position: fixed; right: 100%; } .jasmine_html-reporter .version { color: #aaa; } .jasmine_html-reporter .banner { margin-top: 14px; } -.jasmine_html-reporter .duration { color: #aaa; float: right; } +.jasmine_html-reporter .duration { color: #fff; float: right; line-height: 28px; padding-right: 9px; } .jasmine_html-reporter .symbol-summary { overflow: hidden; *zoom: 1; margin: 14px 0; } .jasmine_html-reporter .symbol-summary li { display: inline-block; height: 8px; width: 14px; font-size: 16px; } .jasmine_html-reporter .symbol-summary li.passed { font-size: 14px; } @@ -25,7 +24,10 @@ body { overflow-y: scroll; } .jasmine_html-reporter .symbol-summary li.pending:before { color: #ba9d37; content: "*"; } .jasmine_html-reporter .symbol-summary li.empty { font-size: 14px; } .jasmine_html-reporter .symbol-summary li.empty:before { color: #ba9d37; content: "\02022"; } -.jasmine_html-reporter .exceptions { color: #fff; float: right; margin-top: 5px; margin-right: 5px; } +.jasmine_html-reporter .run-options { float: right; margin-right: 5px; border: 1px solid #8a4182; color: #8a4182; position: relative; line-height: 20px; } +.jasmine_html-reporter .run-options .trigger { cursor: pointer; padding: 8px 16px; } +.jasmine_html-reporter .run-options .payload { position: absolute; display: none; right: -1px; border: 1px solid #8a4182; background-color: #eee; white-space: nowrap; padding: 4px 8px; } +.jasmine_html-reporter .run-options .payload.open { display: block; } .jasmine_html-reporter .bar { line-height: 28px; font-size: 14px; display: block; color: #eee; } .jasmine_html-reporter .bar.failed { background-color: #ca3a11; } .jasmine_html-reporter .bar.passed { background-color: #007069; } @@ -36,14 +38,7 @@ body { overflow-y: scroll; } .jasmine_html-reporter .bar a { color: white; } .jasmine_html-reporter.spec-list .bar.menu.failure-list, .jasmine_html-reporter.spec-list .results .failures { display: none; } .jasmine_html-reporter.failure-list .bar.menu.spec-list, .jasmine_html-reporter.failure-list .summary { display: none; } -.jasmine_html-reporter .running-alert { background-color: #666; } .jasmine_html-reporter .results { margin-top: 14px; } -.jasmine_html-reporter.showDetails .summaryMenuItem { font-weight: normal; text-decoration: inherit; } -.jasmine_html-reporter.showDetails .summaryMenuItem:hover { text-decoration: underline; } -.jasmine_html-reporter.showDetails .detailsMenuItem { font-weight: bold; text-decoration: underline; } -.jasmine_html-reporter.showDetails .summary { display: none; } -.jasmine_html-reporter.showDetails #details { display: block; } -.jasmine_html-reporter .summaryMenuItem { font-weight: bold; text-decoration: underline; } .jasmine_html-reporter .summary { margin-top: 14px; } .jasmine_html-reporter .summary ul { list-style-type: none; margin-left: 14px; padding-top: 0; padding-left: 0; } .jasmine_html-reporter .summary ul.suite { margin-top: 7px; margin-bottom: 7px; } @@ -51,6 +46,7 @@ body { overflow-y: scroll; } .jasmine_html-reporter .summary li.failed a { color: #ca3a11; } .jasmine_html-reporter .summary li.empty a { color: #ba9d37; } .jasmine_html-reporter .summary li.pending a { color: #ba9d37; } +.jasmine_html-reporter .summary li.disabled a { color: #bababa; } .jasmine_html-reporter .description + .suite { margin-top: 0; } .jasmine_html-reporter .suite { margin-top: 14px; } .jasmine_html-reporter .suite a { color: #333; } diff --git a/spec/lib/jasmine-2.2.0/jasmine.js b/spec/lib/jasmine-2.3.4/jasmine.js old mode 100644 new mode 100755 similarity index 86% rename from spec/lib/jasmine-2.2.0/jasmine.js rename to spec/lib/jasmine-2.3.4/jasmine.js index 6bf3f02a..312d591e --- a/spec/lib/jasmine-2.2.0/jasmine.js +++ b/spec/lib/jasmine-2.3.4/jasmine.js @@ -42,7 +42,8 @@ var getJasmineRequireObj = (function (jasmineGlobal) { jRequire.base(j$, jasmineGlobal); j$.util = jRequire.util(); - j$.Any = jRequire.Any(); + j$.errors = jRequire.errors(); + j$.Any = jRequire.Any(j$); j$.Anything = jRequire.Anything(j$); j$.CallTracker = jRequire.CallTracker(); j$.MockDate = jRequire.MockDate(); @@ -63,8 +64,9 @@ var getJasmineRequireObj = (function (jasmineGlobal) { j$.SpyRegistry = jRequire.SpyRegistry(j$); j$.SpyStrategy = jRequire.SpyStrategy(); j$.StringMatching = jRequire.StringMatching(j$); - j$.Suite = jRequire.Suite(); + j$.Suite = jRequire.Suite(j$); j$.Timer = jRequire.Timer(); + j$.TreeProcessor = jRequire.TreeProcessor(); j$.version = jRequire.version(); j$.matchers = jRequire.requireMatchers(jRequire, j$); @@ -302,6 +304,7 @@ getJasmineRequireObj().Spec = function(j$) { this.expectationResultFactory = attrs.expectationResultFactory || function() { }; this.queueRunnerFactory = attrs.queueRunnerFactory || function() {}; this.catchingExceptions = attrs.catchingExceptions || function() { return true; }; + this.throwOnExpectationFailure = !!attrs.throwOnExpectationFailure; if (!this.queueableFn.fn) { this.pend(); @@ -317,12 +320,16 @@ getJasmineRequireObj().Spec = function(j$) { }; } - Spec.prototype.addExpectationResult = function(passed, data) { + Spec.prototype.addExpectationResult = function(passed, data, isError) { var expectationResult = this.expectationResultFactory(data); if (passed) { this.result.passedExpectations.push(expectationResult); } else { this.result.failedExpectations.push(expectationResult); + + if (this.throwOnExpectationFailure && !isError) { + throw new j$.errors.ExpectationFailed(); + } } }; @@ -330,13 +337,13 @@ getJasmineRequireObj().Spec = function(j$) { return this.expectationFactory(actual, this); }; - Spec.prototype.execute = function(onComplete) { + Spec.prototype.execute = function(onComplete, enabled) { var self = this; this.onStart(this); - if (this.markedPending || this.disabled) { - complete(); + if (!this.isExecutable() || this.markedPending || enabled === false) { + complete(enabled); return; } @@ -350,8 +357,8 @@ getJasmineRequireObj().Spec = function(j$) { userContext: this.userContext() }); - function complete() { - self.result.status = self.status(); + function complete(enabledAgain) { + self.result.status = self.status(enabledAgain); self.resultCallback(self.result); if (onComplete) { @@ -366,13 +373,17 @@ getJasmineRequireObj().Spec = function(j$) { return; } + if (e instanceof j$.errors.ExpectationFailed) { + return; + } + this.addExpectationResult(false, { matcherName: '', passed: false, expected: '', actual: '', error: e - }); + }, true); }; Spec.prototype.disable = function() { @@ -386,8 +397,13 @@ getJasmineRequireObj().Spec = function(j$) { } }; - Spec.prototype.status = function() { - if (this.disabled) { + Spec.prototype.getResult = function() { + this.result.status = this.status(); + return this.result; + }; + + Spec.prototype.status = function(enabled) { + if (this.disabled || enabled === false) { return 'disabled'; } @@ -403,7 +419,7 @@ getJasmineRequireObj().Spec = function(j$) { }; Spec.prototype.isExecutable = function() { - return !this.disabled && !this.markedPending; + return !this.disabled; }; Spec.prototype.getFullName = function() { @@ -444,7 +460,7 @@ getJasmineRequireObj().Env = function(j$) { var realSetTimeout = j$.getGlobal().setTimeout; var realClearTimeout = j$.getGlobal().clearTimeout; - this.clock = new j$.Clock(global, new j$.DelayedFunctionScheduler(), new j$.MockDate(global)); + this.clock = new j$.Clock(global, function () { return new j$.DelayedFunctionScheduler(); }, new j$.MockDate(global)); var runnableLookupTable = {}; var runnableResources = {}; @@ -452,6 +468,7 @@ getJasmineRequireObj().Env = function(j$) { var currentSpec = null; var currentlyExecutingSuites = []; var currentDeclarationSuite = null; + var throwOnExpectationFailure = false; var currentSuite = function() { return currentlyExecutingSuites[currentlyExecutingSuites.length - 1]; @@ -533,27 +550,21 @@ getJasmineRequireObj().Env = function(j$) { delete runnableResources[id]; }; - var beforeAndAfterFns = function(suite, runnablesExplictlySet) { + var beforeAndAfterFns = function(suite) { return function() { var befores = [], - afters = [], - beforeAlls = [], - afterAlls = []; + afters = []; while(suite) { befores = befores.concat(suite.beforeFns); afters = afters.concat(suite.afterFns); - if (runnablesExplictlySet()) { - beforeAlls = beforeAlls.concat(suite.beforeAllFns); - afterAlls = afterAlls.concat(suite.afterAllFns); - } - suite = suite.parentSuite; } + return { - befores: beforeAlls.reverse().concat(befores.reverse()), - afters: afters.concat(afterAlls) + befores: befores.reverse(), + afters: afters }; }; }; @@ -599,10 +610,18 @@ getJasmineRequireObj().Env = function(j$) { return j$.Spec.isPendingSpecException(e) || catchExceptions; }; + this.throwOnExpectationFailure = function(value) { + throwOnExpectationFailure = !!value; + }; + + this.throwingExpectationFailures = function() { + return throwOnExpectationFailure; + }; + var queueRunnerFactory = function(options) { options.catchException = catchException; options.clearStack = options.clearStack || clearStack; - options.timer = {setTimeout: realSetTimeout, clearTimeout: realClearTimeout}; + options.timeout = {setTimeout: realSetTimeout, clearTimeout: realClearTimeout}; options.fail = self.fail; new j$.QueueRunner(options).execute(); @@ -623,26 +642,40 @@ getJasmineRequireObj().Env = function(j$) { }; this.execute = function(runnablesToRun) { - if(runnablesToRun) { - runnablesExplictlySet = true; - } else if (focusedRunnables.length) { - runnablesExplictlySet = true; - runnablesToRun = focusedRunnables; - } else { - runnablesToRun = [topSuite.id]; + if(!runnablesToRun) { + if (focusedRunnables.length) { + runnablesToRun = focusedRunnables; + } else { + runnablesToRun = [topSuite.id]; + } } + var processor = new j$.TreeProcessor({ + tree: topSuite, + runnableIds: runnablesToRun, + queueRunnerFactory: queueRunnerFactory, + nodeStart: function(suite) { + currentlyExecutingSuites.push(suite); + defaultResourcesForRunnable(suite.id, suite.parentSuite.id); + reporter.suiteStarted(suite.result); + }, + nodeComplete: function(suite, result) { + if (!suite.disabled) { + clearResourcesForRunnable(suite.id); + } + currentlyExecutingSuites.pop(); + reporter.suiteDone(result); + } + }); - var allFns = []; - for(var i = 0; i < runnablesToRun.length; i++) { - var runnable = runnableLookupTable[runnablesToRun[i]]; - allFns.push((function(runnable) { return { fn: function(done) { runnable.execute(done); } }; })(runnable)); + if(!processor.processTree().valid) { + throw new Error('Invalid order: would cause a beforeAll or afterAll to be run multiple times'); } reporter.jasmineStarted({ totalSpecsDefined: totalSpecsDefined }); - queueRunnerFactory({queueableFns: allFns, onComplete: reporter.jasmineDone}); + processor.execute(reporter.jasmineDone); }; this.addReporter = function(reporterToAdd) { @@ -666,28 +699,13 @@ getJasmineRequireObj().Env = function(j$) { id: getNextSuiteId(), description: description, parentSuite: currentDeclarationSuite, - queueRunner: queueRunnerFactory, - onStart: suiteStarted, expectationFactory: expectationFactory, expectationResultFactory: expectationResultFactory, - runnablesExplictlySetGetter: runnablesExplictlySetGetter, - resultCallback: function(attrs) { - if (!suite.disabled) { - clearResourcesForRunnable(suite.id); - } - currentlyExecutingSuites.pop(); - reporter.suiteDone(attrs); - } + throwOnExpectationFailure: throwOnExpectationFailure }); runnableLookupTable[suite.id] = suite; return suite; - - function suiteStarted(suite) { - currentlyExecutingSuites.push(suite); - defaultResourcesForRunnable(suite.id, suite.parentSuite.id); - reporter.suiteStarted(suite.result); - } }; this.describe = function(description, specDefinitions) { @@ -759,17 +777,11 @@ getJasmineRequireObj().Env = function(j$) { } } - var runnablesExplictlySet = false; - - var runnablesExplictlySetGetter = function(){ - return runnablesExplictlySet; - }; - var specFactory = function(description, fn, suite, timeout) { totalSpecsDefined++; var spec = new j$.Spec({ id: getNextSpecId(), - beforeAndAfterFns: beforeAndAfterFns(suite, runnablesExplictlySetGetter), + beforeAndAfterFns: beforeAndAfterFns(suite), expectationFactory: expectationFactory, resultCallback: specResultCallback, getSpecName: function(spec) { @@ -783,7 +795,8 @@ getJasmineRequireObj().Env = function(j$) { queueableFn: { fn: fn, timeout: function() { return timeout || j$.DEFAULT_TIMEOUT_INTERVAL; } - } + }, + throwOnExpectationFailure: throwOnExpectationFailure }); runnableLookupTable[spec.id] = spec; @@ -1023,7 +1036,7 @@ getJasmineRequireObj().CallTracker = function() { }; getJasmineRequireObj().Clock = function() { - function Clock(global, delayedFunctionScheduler, mockDate) { + function Clock(global, delayedFunctionSchedulerFactory, mockDate) { var self = this, realTimingFunctions = { setTimeout: global.setTimeout, @@ -1038,19 +1051,24 @@ getJasmineRequireObj().Clock = function() { clearInterval: clearInterval }, installed = false, + delayedFunctionScheduler, timer; self.install = function() { + if(!originalTimingFunctionsIntact()) { + throw new Error('Jasmine Clock was unable to install over custom global timer functions. Is the clock already installed?'); + } replace(global, fakeTimingFunctions); timer = fakeTimingFunctions; + delayedFunctionScheduler = delayedFunctionSchedulerFactory(); installed = true; return self; }; self.uninstall = function() { - delayedFunctionScheduler.reset(); + delayedFunctionScheduler = null; mockDate.uninstall(); replace(global, realTimingFunctions); @@ -1058,6 +1076,15 @@ getJasmineRequireObj().Clock = function() { installed = false; }; + self.withMock = function(closure) { + this.install(); + try { + closure(); + } finally { + this.uninstall(); + } + }; + self.mockDate = function(initialDate) { mockDate.install(initialDate); }; @@ -1101,6 +1128,13 @@ getJasmineRequireObj().Clock = function() { return self; + function originalTimingFunctionsIntact() { + return global.setTimeout === realTimingFunctions.setTimeout && + global.clearTimeout === realTimingFunctions.clearTimeout && + global.setInterval === realTimingFunctions.setInterval && + global.clearInterval === realTimingFunctions.clearInterval; + } + function legacyIE() { //if these methods are polyfilled, apply will be present return !(realTimingFunctions.setTimeout || realTimingFunctions.setInterval).apply; @@ -1210,13 +1244,6 @@ getJasmineRequireObj().DelayedFunctionScheduler = function() { } }; - self.reset = function() { - currentTime = 0; - scheduledLookup = []; - scheduledFunctions = {}; - delayedFnCount = 0; - }; - return self; function indexOfFirstToPass(array, testFn) { @@ -1642,6 +1669,23 @@ getJasmineRequireObj().pp = function(j$) { if(array.length > length){ this.append(', ...'); } + + var self = this; + var first = array.length === 0; + this.iterateObject(array, function(property, isGetter) { + if (property.match(/^\d+$/)) { + return; + } + + if (first) { + first = false; + } else { + self.append(', '); + } + + self.formatProperty(array, property, isGetter); + }); + this.append(' ]'); }; @@ -1664,18 +1708,22 @@ getJasmineRequireObj().pp = function(j$) { self.append(', '); } - self.append(property); - self.append(': '); - if (isGetter) { - self.append(''); - } else { - self.format(obj[property]); - } + self.formatProperty(obj, property, isGetter); }); this.append(' })'); }; + StringPrettyPrinter.prototype.formatProperty = function(obj, property, isGetter) { + this.append(property); + this.append(': '); + if (isGetter) { + this.append(''); + } else { + this.format(obj[property]); + } + }; + StringPrettyPrinter.prototype.append = function(value) { this.string += value; }; @@ -1706,7 +1754,7 @@ getJasmineRequireObj().QueueRunner = function(j$) { this.onException = attrs.onException || function() {}; this.catchException = attrs.catchException || function() { return true; }; this.userContext = attrs.userContext || {}; - this.timer = attrs.timeout || {setTimeout: setTimeout, clearTimeout: clearTimeout}; + this.timeout = attrs.timeout || {setTimeout: setTimeout, clearTimeout: clearTimeout}; this.fail = attrs.fail || function() {}; } @@ -1746,7 +1794,7 @@ getJasmineRequireObj().QueueRunner = function(j$) { function attemptAsync(queueableFn) { var clearTimeout = function () { - Function.prototype.apply.apply(self.timer.clearTimeout, [j$.getGlobal(), [timeoutId]]); + Function.prototype.apply.apply(self.timeout.clearTimeout, [j$.getGlobal(), [timeoutId]]); }, next = once(function () { clearTimeout(timeoutId); @@ -1760,7 +1808,7 @@ getJasmineRequireObj().QueueRunner = function(j$) { }; if (queueableFn.timeout) { - timeoutId = Function.prototype.apply.apply(self.timer.setTimeout, [j$.getGlobal(), [function() { + timeoutId = Function.prototype.apply.apply(self.timeout.setTimeout, [j$.getGlobal(), [function() { var error = new Error('Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL.'); onException(error, queueableFn); next(); @@ -1938,24 +1986,20 @@ getJasmineRequireObj().SpyStrategy = function() { return SpyStrategy; }; -getJasmineRequireObj().Suite = function() { +getJasmineRequireObj().Suite = function(j$) { function Suite(attrs) { this.env = attrs.env; this.id = attrs.id; this.parentSuite = attrs.parentSuite; this.description = attrs.description; - this.onStart = attrs.onStart || function() {}; - this.resultCallback = attrs.resultCallback || function() {}; - this.clearStack = attrs.clearStack || function(fn) {fn();}; this.expectationFactory = attrs.expectationFactory; this.expectationResultFactory = attrs.expectationResultFactory; - this.runnablesExplictlySetGetter = attrs.runnablesExplictlySetGetter || function() {}; + this.throwOnExpectationFailure = !!attrs.throwOnExpectationFailure; this.beforeFns = []; this.afterFns = []; this.beforeAllFns = []; this.afterAllFns = []; - this.queueRunner = attrs.queueRunner || function() {}; this.disabled = false; this.children = []; @@ -2018,51 +2062,17 @@ getJasmineRequireObj().Suite = function() { } }; - Suite.prototype.execute = function(onComplete) { - var self = this; - - this.onStart(this); - - if (this.disabled) { - complete(); - return; - } - - var allFns = []; - - for (var i = 0; i < this.children.length; i++) { - allFns.push(wrapChildAsAsync(this.children[i])); - } - - if (this.isExecutable()) { - allFns = this.beforeAllFns.concat(allFns); - allFns = allFns.concat(this.afterAllFns); - } - - this.queueRunner({ - queueableFns: allFns, - onComplete: complete, - userContext: this.sharedUserContext(), - onException: function() { self.onException.apply(self, arguments); } - }); - - function complete() { - self.result.status = self.status(); - self.resultCallback(self.result); - - if (onComplete) { - onComplete(); - } - } + Suite.prototype.isExecutable = function() { + return !this.disabled; + }; - function wrapChildAsAsync(child) { - return { fn: function(done) { child.execute(done); } }; - } + Suite.prototype.canBeReentered = function() { + return this.beforeAllFns.length === 0 && this.afterAllFns.length === 0; }; - Suite.prototype.isExecutable = function() { - var runnablesExplicitlySet = this.runnablesExplictlySetGetter(); - return !runnablesExplicitlySet && hasExecutableChild(this.children); + Suite.prototype.getResult = function() { + this.result.status = this.status(); + return this.result; }; Suite.prototype.sharedUserContext = function() { @@ -2078,6 +2088,10 @@ getJasmineRequireObj().Suite = function() { }; Suite.prototype.onException = function() { + if (arguments[0] instanceof j$.errors.ExpectationFailed) { + return; + } + if(isAfterAll(this.children)) { var data = { matcherName: '', @@ -2099,10 +2113,17 @@ getJasmineRequireObj().Suite = function() { if(isAfterAll(this.children) && isFailure(arguments)){ var data = arguments[1]; this.result.failedExpectations.push(this.expectationResultFactory(data)); + if(this.throwOnExpectationFailure) { + throw new j$.errors.ExpectationFailed(); + } } else { for (var i = 0; i < this.children.length; i++) { var child = this.children[i]; - child.addExpectationResult.apply(child, arguments); + try { + child.addExpectationResult.apply(child, arguments); + } catch(e) { + // keep going + } } } }; @@ -2115,17 +2136,6 @@ getJasmineRequireObj().Suite = function() { return !args[0]; } - function hasExecutableChild(children) { - var foundActive = false; - for (var i = 0; i < children.length; i++) { - if (children[i].isExecutable()) { - foundActive = true; - break; - } - } - return foundActive; - } - function clone(obj) { var clonedObj = {}; for (var prop in obj) { @@ -2167,7 +2177,211 @@ getJasmineRequireObj().Timer = function() { return Timer; }; -getJasmineRequireObj().Any = function() { +getJasmineRequireObj().TreeProcessor = function() { + function TreeProcessor(attrs) { + var tree = attrs.tree, + runnableIds = attrs.runnableIds, + queueRunnerFactory = attrs.queueRunnerFactory, + nodeStart = attrs.nodeStart || function() {}, + nodeComplete = attrs.nodeComplete || function() {}, + stats = { valid: true }, + processed = false, + defaultMin = Infinity, + defaultMax = 1 - Infinity; + + this.processTree = function() { + processNode(tree, false); + processed = true; + return stats; + }; + + this.execute = function(done) { + if (!processed) { + this.processTree(); + } + + if (!stats.valid) { + throw 'invalid order'; + } + + var childFns = wrapChildren(tree, 0); + + queueRunnerFactory({ + queueableFns: childFns, + userContext: tree.sharedUserContext(), + onException: function() { + tree.onException.apply(tree, arguments); + }, + onComplete: done + }); + }; + + function runnableIndex(id) { + for (var i = 0; i < runnableIds.length; i++) { + if (runnableIds[i] === id) { + return i; + } + } + } + + function processNode(node, parentEnabled) { + var executableIndex = runnableIndex(node.id); + + if (executableIndex !== undefined) { + parentEnabled = true; + } + + parentEnabled = parentEnabled && node.isExecutable(); + + if (!node.children) { + stats[node.id] = { + executable: parentEnabled && node.isExecutable(), + segments: [{ + index: 0, + owner: node, + nodes: [node], + min: startingMin(executableIndex), + max: startingMax(executableIndex) + }] + }; + } else { + var hasExecutableChild = false; + + for (var i = 0; i < node.children.length; i++) { + var child = node.children[i]; + + processNode(child, parentEnabled); + + if (!stats.valid) { + return; + } + + var childStats = stats[child.id]; + + hasExecutableChild = hasExecutableChild || childStats.executable; + } + + stats[node.id] = { + executable: hasExecutableChild + }; + + segmentChildren(node, stats[node.id], executableIndex); + + if (!node.canBeReentered() && stats[node.id].segments.length > 1) { + stats = { valid: false }; + } + } + } + + function startingMin(executableIndex) { + return executableIndex === undefined ? defaultMin : executableIndex; + } + + function startingMax(executableIndex) { + return executableIndex === undefined ? defaultMax : executableIndex; + } + + function segmentChildren(node, nodeStats, executableIndex) { + var currentSegment = { index: 0, owner: node, nodes: [], min: startingMin(executableIndex), max: startingMax(executableIndex) }, + result = [currentSegment], + lastMax = defaultMax, + orderedChildSegments = orderChildSegments(node.children); + + function isSegmentBoundary(minIndex) { + return lastMax !== defaultMax && minIndex !== defaultMin && lastMax < minIndex - 1; + } + + for (var i = 0; i < orderedChildSegments.length; i++) { + var childSegment = orderedChildSegments[i], + maxIndex = childSegment.max, + minIndex = childSegment.min; + + if (isSegmentBoundary(minIndex)) { + currentSegment = {index: result.length, owner: node, nodes: [], min: defaultMin, max: defaultMax}; + result.push(currentSegment); + } + + currentSegment.nodes.push(childSegment); + currentSegment.min = Math.min(currentSegment.min, minIndex); + currentSegment.max = Math.max(currentSegment.max, maxIndex); + lastMax = maxIndex; + } + + nodeStats.segments = result; + } + + function orderChildSegments(children) { + var specifiedOrder = [], + unspecifiedOrder = []; + + for (var i = 0; i < children.length; i++) { + var child = children[i], + segments = stats[child.id].segments; + + for (var j = 0; j < segments.length; j++) { + var seg = segments[j]; + + if (seg.min === defaultMin) { + unspecifiedOrder.push(seg); + } else { + specifiedOrder.push(seg); + } + } + } + + specifiedOrder.sort(function(a, b) { + return a.min - b.min; + }); + + return specifiedOrder.concat(unspecifiedOrder); + } + + function executeNode(node, segmentNumber) { + if (node.children) { + return { + fn: function(done) { + nodeStart(node); + + queueRunnerFactory({ + onComplete: function() { + nodeComplete(node, node.getResult()); + done(); + }, + queueableFns: wrapChildren(node, segmentNumber), + userContext: node.sharedUserContext(), + onException: function() { + node.onException.apply(node, arguments); + } + }); + } + }; + } else { + return { + fn: function(done) { node.execute(done, stats[node.id].executable); } + }; + } + } + + function wrapChildren(node, segmentNumber) { + var result = [], + segmentChildren = stats[node.id].segments[segmentNumber].nodes; + + for (var i = 0; i < segmentChildren.length; i++) { + result.push(executeNode(segmentChildren[i].owner, segmentChildren[i].index)); + } + + if (!stats[node.id].executable) { + return result; + } + + return node.beforeAllFns.concat(result).concat(node.afterAllFns); + } + } + + return TreeProcessor; +}; + +getJasmineRequireObj().Any = function(j$) { function Any(expectedObject) { this.expectedObject = expectedObject; @@ -2198,7 +2412,7 @@ getJasmineRequireObj().Any = function() { }; Any.prototype.jasmineToString = function() { - return ''; + return ''; }; return Any; @@ -2251,11 +2465,35 @@ getJasmineRequireObj().ObjectContaining = function(j$) { this.sample = sample; } + function getPrototype(obj) { + if (Object.getPrototypeOf) { + return Object.getPrototypeOf(obj); + } + + if (obj.constructor.prototype == obj) { + return null; + } + + return obj.constructor.prototype; + } + + function hasProperty(obj, property) { + if (!obj) { + return false; + } + + if (Object.prototype.hasOwnProperty.call(obj, property)) { + return true; + } + + return hasProperty(getPrototype(obj), property); + } + ObjectContaining.prototype.asymmetricMatch = function(other) { if (typeof(this.sample) !== 'object') { throw new Error('You must provide an object to objectContaining, not \''+this.sample+'\'.'); } for (var property in this.sample) { - if (!Object.prototype.hasOwnProperty.call(other, property) || + if (!hasProperty(other, property) || !j$.matchersUtil.equals(this.sample[property], other[property])) { return false; } @@ -2292,6 +2530,16 @@ getJasmineRequireObj().StringMatching = function(j$) { return StringMatching; }; +getJasmineRequireObj().errors = function() { + function ExpectationFailed() {} + + ExpectationFailed.prototype = new Error(); + ExpectationFailed.prototype.constructor = ExpectationFailed; + + return { + ExpectationFailed: ExpectationFailed + }; +}; getJasmineRequireObj().matchersUtil = function(j$) { // TODO: what to do about jasmine.pp not being inject? move to JSON.stringify? gut PrettyPrinter? @@ -2461,11 +2709,13 @@ getJasmineRequireObj().matchersUtil = function(j$) { if (result) { // Objects with different constructors are not equivalent, but `Object`s - // from different frames are. - var aCtor = a.constructor, bCtor = b.constructor; - if (aCtor !== bCtor && !(isFunction(aCtor) && (aCtor instanceof aCtor) && - isFunction(bCtor) && (bCtor instanceof bCtor))) { - return false; + // or `Array`s from different frames are. + if (className !== '[object Array]') { + var aCtor = a.constructor, bCtor = b.constructor; + if (aCtor !== bCtor && !(isFunction(aCtor) && aCtor instanceof aCtor && + isFunction(bCtor) && bCtor instanceof bCtor)) { + return false; + } } // Deep compare objects. for (var key in a) { @@ -2939,7 +3189,7 @@ getJasmineRequireObj().toThrowError = function(j$) { return expected === null && errorType === null; }, matches: function(error) { - return (errorType === null || error.constructor === errorType) && + return (errorType === null || error instanceof errorType) && (expected === null || messageMatch(error.message)); } }; @@ -3044,5 +3294,5 @@ getJasmineRequireObj().interface = function(jasmine, env) { }; getJasmineRequireObj().version = function() { - return '2.2.0'; + return '2.3.4'; }; diff --git a/spec/lib/jasmine-2.2.0/jasmine_favicon.png b/spec/lib/jasmine-2.3.4/jasmine_favicon.png old mode 100644 new mode 100755 similarity index 100% rename from spec/lib/jasmine-2.2.0/jasmine_favicon.png rename to spec/lib/jasmine-2.3.4/jasmine_favicon.png diff --git a/spec/lib/jasmine-2.3.4/mock-ajax.js b/spec/lib/jasmine-2.3.4/mock-ajax.js new file mode 100644 index 00000000..64398005 --- /dev/null +++ b/spec/lib/jasmine-2.3.4/mock-ajax.js @@ -0,0 +1,733 @@ +/* + +Jasmine-Ajax - v3.2.0: a set of helpers for testing AJAX requests under the Jasmine +BDD framework for JavaScript. + +http://github.com/jasmine/jasmine-ajax + +Jasmine Home page: http://jasmine.github.io/ + +Copyright (c) 2008-2015 Pivotal Labs + +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. + +*/ + +getJasmineRequireObj().ajax = function(jRequire) { + var $ajax = {}; + + $ajax.RequestStub = jRequire.AjaxRequestStub(); + $ajax.RequestTracker = jRequire.AjaxRequestTracker(); + $ajax.StubTracker = jRequire.AjaxStubTracker(); + $ajax.ParamParser = jRequire.AjaxParamParser(); + $ajax.event = jRequire.AjaxEvent(); + $ajax.eventBus = jRequire.AjaxEventBus($ajax.event); + $ajax.fakeRequest = jRequire.AjaxFakeRequest($ajax.eventBus); + $ajax.MockAjax = jRequire.MockAjax($ajax); + + return $ajax.MockAjax; +}; + +getJasmineRequireObj().AjaxEvent = function() { + function now() { + return new Date().getTime(); + } + + function noop() { + } + + // Event object + // https://dom.spec.whatwg.org/#concept-event + function XMLHttpRequestEvent(xhr, type) { + this.type = type; + this.bubbles = false; + this.cancelable = false; + this.timeStamp = now(); + + this.isTrusted = false; + this.defaultPrevented = false; + + // Event phase should be "AT_TARGET" + // https://dom.spec.whatwg.org/#dom-event-at_target + this.eventPhase = 2; + + this.target = xhr; + this.currentTarget = xhr; + } + + XMLHttpRequestEvent.prototype.preventDefault = noop; + XMLHttpRequestEvent.prototype.stopPropagation = noop; + XMLHttpRequestEvent.prototype.stopImmediatePropagation = noop; + + function XMLHttpRequestProgressEvent() { + XMLHttpRequestEvent.apply(this, arguments); + + this.lengthComputable = false; + this.loaded = 0; + this.total = 0; + } + + // Extend prototype + XMLHttpRequestProgressEvent.prototype = XMLHttpRequestEvent.prototype; + + return { + event: function(xhr, type) { + return new XMLHttpRequestEvent(xhr, type); + }, + + progressEvent: function(xhr, type) { + return new XMLHttpRequestProgressEvent(xhr, type); + } + }; +}; +getJasmineRequireObj().AjaxEventBus = function(eventFactory) { + function EventBus(source) { + this.eventList = {}; + this.source = source; + } + + function ensureEvent(eventList, name) { + eventList[name] = eventList[name] || []; + return eventList[name]; + } + + function findIndex(list, thing) { + if (list.indexOf) { + return list.indexOf(thing); + } + + for(var i = 0; i < list.length; i++) { + if (thing === list[i]) { + return i; + } + } + + return -1; + } + + EventBus.prototype.addEventListener = function(event, callback) { + ensureEvent(this.eventList, event).push(callback); + }; + + EventBus.prototype.removeEventListener = function(event, callback) { + var index = findIndex(this.eventList[event], callback); + + if (index >= 0) { + this.eventList[event].splice(index, 1); + } + }; + + EventBus.prototype.trigger = function(event) { + var evt; + + // Event 'readystatechange' is should be a simple event. + // Others are progress event. + // https://xhr.spec.whatwg.org/#events + if (event === 'readystatechange') { + evt = eventFactory.event(this.source, event); + } else { + evt = eventFactory.progressEvent(this.source, event); + } + + var eventListeners = this.eventList[event]; + + if (eventListeners) { + for (var i = 0; i < eventListeners.length; i++) { + eventListeners[i].call(this.source, evt); + } + } + }; + + return function(source) { + return new EventBus(source); + }; +}; + +getJasmineRequireObj().AjaxFakeRequest = function(eventBusFactory) { + function extend(destination, source, propertiesToSkip) { + propertiesToSkip = propertiesToSkip || []; + for (var property in source) { + if (!arrayContains(propertiesToSkip, property)) { + destination[property] = source[property]; + } + } + return destination; + } + + function arrayContains(arr, item) { + for (var i = 0; i < arr.length; i++) { + if (arr[i] === item) { + return true; + } + } + return false; + } + + function wrapProgressEvent(xhr, eventName) { + return function() { + if (xhr[eventName]) { + xhr[eventName].apply(xhr, arguments); + } + }; + } + + function initializeEvents(xhr) { + xhr.eventBus.addEventListener('readystatechange', wrapProgressEvent(xhr, 'onreadystatechange')); + xhr.eventBus.addEventListener('loadstart', wrapProgressEvent(xhr, 'onloadstart')); + xhr.eventBus.addEventListener('load', wrapProgressEvent(xhr, 'onload')); + xhr.eventBus.addEventListener('loadend', wrapProgressEvent(xhr, 'onloadend')); + xhr.eventBus.addEventListener('progress', wrapProgressEvent(xhr, 'onprogress')); + xhr.eventBus.addEventListener('error', wrapProgressEvent(xhr, 'onerror')); + xhr.eventBus.addEventListener('abort', wrapProgressEvent(xhr, 'onabort')); + xhr.eventBus.addEventListener('timeout', wrapProgressEvent(xhr, 'ontimeout')); + } + + function unconvertibleResponseTypeMessage(type) { + var msg = [ + "Can't build XHR.response for XHR.responseType of '", + type, + "'.", + "XHR.response must be explicitly stubbed" + ]; + return msg.join(' '); + } + + function fakeRequest(global, requestTracker, stubTracker, paramParser) { + function FakeXMLHttpRequest() { + requestTracker.track(this); + this.eventBus = eventBusFactory(this); + initializeEvents(this); + this.requestHeaders = {}; + this.overriddenMimeType = null; + } + + function findHeader(name, headers) { + name = name.toLowerCase(); + for (var header in headers) { + if (header.toLowerCase() === name) { + return headers[header]; + } + } + } + + function normalizeHeaders(rawHeaders, contentType) { + var headers = []; + + if (rawHeaders) { + if (rawHeaders instanceof Array) { + headers = rawHeaders; + } else { + for (var headerName in rawHeaders) { + if (rawHeaders.hasOwnProperty(headerName)) { + headers.push({ name: headerName, value: rawHeaders[headerName] }); + } + } + } + } else { + headers.push({ name: "Content-Type", value: contentType || "application/json" }); + } + + return headers; + } + + function parseXml(xmlText, contentType) { + if (global.DOMParser) { + return (new global.DOMParser()).parseFromString(xmlText, 'text/xml'); + } else { + var xml = new global.ActiveXObject("Microsoft.XMLDOM"); + xml.async = "false"; + xml.loadXML(xmlText); + return xml; + } + } + + var xmlParsables = ['text/xml', 'application/xml']; + + function getResponseXml(responseText, contentType) { + if (arrayContains(xmlParsables, contentType.toLowerCase())) { + return parseXml(responseText, contentType); + } else if (contentType.match(/\+xml$/)) { + return parseXml(responseText, 'text/xml'); + } + return null; + } + + var iePropertiesThatCannotBeCopied = ['responseBody', 'responseText', 'responseXML', 'status', 'statusText', 'responseTimeout']; + extend(FakeXMLHttpRequest.prototype, new global.XMLHttpRequest(), iePropertiesThatCannotBeCopied); + extend(FakeXMLHttpRequest.prototype, { + open: function() { + this.method = arguments[0]; + this.url = arguments[1]; + this.username = arguments[3]; + this.password = arguments[4]; + this.readyState = 1; + this.eventBus.trigger('readystatechange'); + }, + + setRequestHeader: function(header, value) { + if(this.requestHeaders.hasOwnProperty(header)) { + this.requestHeaders[header] = [this.requestHeaders[header], value].join(', '); + } else { + this.requestHeaders[header] = value; + } + }, + + overrideMimeType: function(mime) { + this.overriddenMimeType = mime; + }, + + abort: function() { + this.readyState = 0; + this.status = 0; + this.statusText = "abort"; + this.eventBus.trigger('readystatechange'); + this.eventBus.trigger('progress'); + this.eventBus.trigger('abort'); + this.eventBus.trigger('loadend'); + }, + + readyState: 0, + + onloadstart: null, + onprogress: null, + onabort: null, + onerror: null, + onload: null, + ontimeout: null, + onloadend: null, + onreadystatechange: null, + + addEventListener: function() { + this.eventBus.addEventListener.apply(this.eventBus, arguments); + }, + + removeEventListener: function(event, callback) { + this.eventBus.removeEventListener.apply(this.eventBus, arguments); + }, + + status: null, + + send: function(data) { + this.params = data; + this.eventBus.trigger('loadstart'); + + var stub = stubTracker.findStub(this.url, data, this.method); + if (stub) { + if (stub.isReturn()) { + this.respondWith(stub); + } else if (stub.isError()) { + this.responseError(); + } else if (stub.isTimeout()) { + this.responseTimeout(); + } + } + }, + + contentType: function() { + return findHeader('content-type', this.requestHeaders); + }, + + data: function() { + if (!this.params) { + return {}; + } + + return paramParser.findParser(this).parse(this.params); + }, + + getResponseHeader: function(name) { + name = name.toLowerCase(); + var resultHeader; + for(var i = 0; i < this.responseHeaders.length; i++) { + var header = this.responseHeaders[i]; + if (name === header.name.toLowerCase()) { + if (resultHeader) { + resultHeader = [resultHeader, header.value].join(', '); + } else { + resultHeader = header.value; + } + } + } + return resultHeader; + }, + + getAllResponseHeaders: function() { + var responseHeaders = []; + for (var i = 0; i < this.responseHeaders.length; i++) { + responseHeaders.push(this.responseHeaders[i].name + ': ' + + this.responseHeaders[i].value); + } + return responseHeaders.join('\r\n') + '\r\n'; + }, + + responseText: null, + response: null, + responseType: null, + + responseValue: function() { + switch(this.responseType) { + case null: + case "": + case "text": + return this.readyState >= 3 ? this.responseText : ""; + case "json": + return JSON.parse(this.responseText); + case "arraybuffer": + throw unconvertibleResponseTypeMessage('arraybuffer'); + case "blob": + throw unconvertibleResponseTypeMessage('blob'); + case "document": + return this.responseXML; + } + }, + + + respondWith: function(response) { + if (this.readyState === 4) { + throw new Error("FakeXMLHttpRequest already completed"); + } + + this.status = response.status; + this.statusText = response.statusText || ""; + this.responseHeaders = normalizeHeaders(response.responseHeaders, response.contentType); + this.readyState = 2; + this.eventBus.trigger('readystatechange'); + + this.responseText = response.responseText || ""; + this.responseType = response.responseType || ""; + this.readyState = 4; + this.responseXML = getResponseXml(response.responseText, this.getResponseHeader('content-type') || ''); + if (this.responseXML) { + this.responseType = 'document'; + } + + if ('response' in response) { + this.response = response.response; + } else { + this.response = this.responseValue(); + } + + this.eventBus.trigger('readystatechange'); + this.eventBus.trigger('progress'); + this.eventBus.trigger('load'); + this.eventBus.trigger('loadend'); + }, + + responseTimeout: function() { + if (this.readyState === 4) { + throw new Error("FakeXMLHttpRequest already completed"); + } + this.readyState = 4; + jasmine.clock().tick(30000); + this.eventBus.trigger('readystatechange'); + this.eventBus.trigger('progress'); + this.eventBus.trigger('timeout'); + this.eventBus.trigger('loadend'); + }, + + responseError: function() { + if (this.readyState === 4) { + throw new Error("FakeXMLHttpRequest already completed"); + } + this.readyState = 4; + this.eventBus.trigger('readystatechange'); + this.eventBus.trigger('progress'); + this.eventBus.trigger('error'); + this.eventBus.trigger('loadend'); + } + }); + + return FakeXMLHttpRequest; + } + + return fakeRequest; +}; + +getJasmineRequireObj().MockAjax = function($ajax) { + function MockAjax(global) { + var requestTracker = new $ajax.RequestTracker(), + stubTracker = new $ajax.StubTracker(), + paramParser = new $ajax.ParamParser(), + realAjaxFunction = global.XMLHttpRequest, + mockAjaxFunction = $ajax.fakeRequest(global, requestTracker, stubTracker, paramParser); + + this.install = function() { + if (global.XMLHttpRequest === mockAjaxFunction) { + throw "MockAjax is already installed."; + } + + global.XMLHttpRequest = mockAjaxFunction; + }; + + this.uninstall = function() { + global.XMLHttpRequest = realAjaxFunction; + + this.stubs.reset(); + this.requests.reset(); + paramParser.reset(); + }; + + this.stubRequest = function(url, data, method) { + var stub = new $ajax.RequestStub(url, data, method); + stubTracker.addStub(stub); + return stub; + }; + + this.withMock = function(closure) { + this.install(); + try { + closure(); + } finally { + this.uninstall(); + } + }; + + this.addCustomParamParser = function(parser) { + paramParser.add(parser); + }; + + this.requests = requestTracker; + this.stubs = stubTracker; + } + + return MockAjax; +}; + +getJasmineRequireObj().AjaxParamParser = function() { + function ParamParser() { + var defaults = [ + { + test: function(xhr) { + return (/^application\/json/).test(xhr.contentType()); + }, + parse: function jsonParser(paramString) { + return JSON.parse(paramString); + } + }, + { + test: function(xhr) { + return true; + }, + parse: function naiveParser(paramString) { + var data = {}; + var params = paramString.split('&'); + + for (var i = 0; i < params.length; ++i) { + var kv = params[i].replace(/\+/g, ' ').split('='); + var key = decodeURIComponent(kv[0]); + data[key] = data[key] || []; + data[key].push(decodeURIComponent(kv[1])); + } + return data; + } + } + ]; + var paramParsers = []; + + this.add = function(parser) { + paramParsers.unshift(parser); + }; + + this.findParser = function(xhr) { + for(var i in paramParsers) { + var parser = paramParsers[i]; + if (parser.test(xhr)) { + return parser; + } + } + }; + + this.reset = function() { + paramParsers = []; + for(var i in defaults) { + paramParsers.push(defaults[i]); + } + }; + + this.reset(); + } + + return ParamParser; +}; + +getJasmineRequireObj().AjaxRequestStub = function() { + var RETURN = 0, + ERROR = 1, + TIMEOUT = 2; + + function RequestStub(url, stubData, method) { + var normalizeQuery = function(query) { + return query ? query.split('&').sort().join('&') : undefined; + }; + + if (url instanceof RegExp) { + this.url = url; + this.query = undefined; + } else { + var split = url.split('?'); + this.url = split[0]; + this.query = split.length > 1 ? normalizeQuery(split[1]) : undefined; + } + + this.data = (stubData instanceof RegExp) ? stubData : normalizeQuery(stubData); + this.method = method; + + this.andReturn = function(options) { + this.action = RETURN; + this.status = options.status || 200; + + this.contentType = options.contentType; + this.response = options.response; + this.responseText = options.responseText; + this.responseHeaders = options.responseHeaders; + }; + + this.isReturn = function() { + return this.action === RETURN; + }; + + this.andError = function() { + this.action = ERROR; + }; + + this.isError = function() { + return this.action === ERROR; + }; + + this.andTimeout = function() { + this.action = TIMEOUT; + }; + + this.isTimeout = function() { + return this.action === TIMEOUT; + }; + + this.matches = function(fullUrl, data, method) { + var urlMatches = false; + fullUrl = fullUrl.toString(); + if (this.url instanceof RegExp) { + urlMatches = this.url.test(fullUrl); + } else { + var urlSplit = fullUrl.split('?'), + url = urlSplit[0], + query = urlSplit[1]; + urlMatches = this.url === url && this.query === normalizeQuery(query); + } + var dataMatches = false; + if (this.data instanceof RegExp) { + dataMatches = this.data.test(data); + } else { + dataMatches = !this.data || this.data === normalizeQuery(data); + } + return urlMatches && dataMatches && (!this.method || this.method === method); + }; + } + + return RequestStub; +}; + +getJasmineRequireObj().AjaxRequestTracker = function() { + function RequestTracker() { + var requests = []; + + this.track = function(request) { + requests.push(request); + }; + + this.first = function() { + return requests[0]; + }; + + this.count = function() { + return requests.length; + }; + + this.reset = function() { + requests = []; + }; + + this.mostRecent = function() { + return requests[requests.length - 1]; + }; + + this.at = function(index) { + return requests[index]; + }; + + this.filter = function(url_to_match) { + var matching_requests = []; + + for (var i = 0; i < requests.length; i++) { + if (url_to_match instanceof RegExp && + url_to_match.test(requests[i].url)) { + matching_requests.push(requests[i]); + } else if (url_to_match instanceof Function && + url_to_match(requests[i])) { + matching_requests.push(requests[i]); + } else { + if (requests[i].url === url_to_match) { + matching_requests.push(requests[i]); + } + } + } + + return matching_requests; + }; + } + + return RequestTracker; +}; + +getJasmineRequireObj().AjaxStubTracker = function() { + function StubTracker() { + var stubs = []; + + this.addStub = function(stub) { + stubs.push(stub); + }; + + this.reset = function() { + stubs = []; + }; + + this.findStub = function(url, data, method) { + for (var i = stubs.length - 1; i >= 0; i--) { + var stub = stubs[i]; + if (stub.matches(url, data, method)) { + return stub; + } + } + }; + } + + return StubTracker; +}; + +(function() { + var jRequire = getJasmineRequireObj(), + MockAjax = jRequire.ajax(jRequire); + if (typeof window === "undefined" && typeof exports === "object") { + exports.MockAjax = MockAjax; + jasmine.Ajax = new MockAjax(exports); + } else { + window.MockAjax = MockAjax; + jasmine.Ajax = new MockAjax(window); + } +}()); diff --git a/spec/loading-modes.spec.js b/spec/loading-modes.spec.js index 3470fe8b..f507b77b 100644 --- a/spec/loading-modes.spec.js +++ b/spec/loading-modes.spec.js @@ -20,12 +20,13 @@ describe('loading modes', function() { } beforeEach(function() { - //loadSpy = jasmine.createSpy('load'); + jasmine.Ajax.install(); }); afterEach(function() { anm.detachAllPlayers(); // this will also detach element if players were created //if (element && element.parentNode) document.body.removeChild(element); + jasmine.Ajax.uninstall(); }); it('should have `rightaway` as default option', function(done) { From dfb0e66067396fb58a7ea4d5e2fb9b6c1a78ff34 Mon Sep 17 00:00:00 2001 From: Ulric Wilfred Date: Mon, 21 Sep 2015 16:47:50 +0200 Subject: [PATCH 07/16] implementing tests for loading modes, p.1 --- spec/loading-modes.spec.js | 79 ++++++++++++++++++++++---------------- 1 file changed, 46 insertions(+), 33 deletions(-) diff --git a/spec/loading-modes.spec.js b/spec/loading-modes.spec.js index f507b77b..57652040 100644 --- a/spec/loading-modes.spec.js +++ b/spec/loading-modes.spec.js @@ -2,7 +2,7 @@ describe('loading modes', function() { var ELEMENT_ID = 'player-target'; - var JSON_NODE_SRC = '/base/spec/empty.json'; + var JSON_SRC = './spec/empty.json'; function FakeImporter() {}; FakeImporter.prototype.load = function() { return new anm.Animation(); }; @@ -19,6 +19,16 @@ describe('loading modes', function() { anm.engine.onDocReady(f); } + function prepareJsonRequestStub() { + jasmine.Ajax.stubRequest(JSON_SRC).andReturn({ + "responseText": '{}' + }); + } + + function lastAjaxCall() { + return jasmine.Ajax.requests.mostRecent(); + } + beforeEach(function() { jasmine.Ajax.install(); }); @@ -29,53 +39,61 @@ describe('loading modes', function() { jasmine.Ajax.uninstall(); }); - it('should have `rightaway` as default option', function(done) { + it('should have `rightaway` as default option', function() { whenDocumentReady(function() { prepareDivElement(ELEMENT_ID); expect(anm.createPlayer(ELEMENT_ID).loadingMode).toBe(anm.C.LM_RIGHTAWAY); - done(); + }); + }); + + xit('should fallback to `rightaway` if loadingMode is unknown', function() { + whenDocumentReady(function() { + prepareDivElement(ELEMENT_ID); + var player = anm.createPlayer(ELEMENT_ID, { loadingMode: 'foobarbuz' }); + expect(player.loadingMode).toBe(anm.C.LM_RIGHTAWAY); }); }); describe('right away', function() { - it('should automatically load a scene when source specified with HTML attribute', function(done) { + it('should automatically load a scene when source specified with HTML attribute', function() { whenDocumentReady(function() { + prepareJsonRequestStub(); var element = prepareDivElement(ELEMENT_ID); element.setAttribute('anm-player-target', true); - element.setAttribute('anm-src', JSON_NODE_SRC); + element.setAttribute('anm-src', JSON_SRC); element.setAttribute('anm-importer', 'fake'); - anm.findAndInitPotentialPlayers({ 'handle': { 'load': function(animation) { - expect(animation).toBeDefined(); - done(); - } } }); + anm.findAndInitPotentialPlayers(); + + var lastCall = lastAjaxCall(); + expect(lastCall).toBeDefined(); + if (lastCall) { expect(lastCall.url).toBe(JSON_SRC) }; }); }); - it('should automatically load a scene when source passed with forSnapshot', function(done) { + it('should automatically load a scene when source passed with forSnapshot', function() { whenDocumentReady(function() { + prepareJsonRequestStub(); prepareDivElement(ELEMENT_ID); var fakeImporter = anm.importers.create('fake'); var importLoadSpy = spyOn(fakeImporter, 'load').and.callThrough(); - anm.Player.forSnapshot(ELEMENT_ID, JSON_NODE_SRC, fakeImporter, function(animation) { - expect(animation).toBeDefined(); - expect(importLoadSpy).toHaveBeenCalled(); - done(); - }); + anm.Player.forSnapshot(ELEMENT_ID, JSON_SRC, fakeImporter); + + expect(importLoadSpy).toHaveBeenCalled(); + expect(lastAjaxCall()).toBeDefined(); }); }); - it('should not load anything when player created and source wasn\'t specified', function(done) { + it('should not load anything when player created and source wasn\'t specified', function() { whenDocumentReady(function() { + prepareJsonRequestStub(); prepareDivElement(ELEMENT_ID); - var loadSpy = jasmine.createSpy('load'); - anm.createPlayer(ELEMENT_ID, { handle: { 'load': loadSpy } }); - expect(loadSpy).not.toHaveBeenCalled(); - done(); + anm.createPlayer(ELEMENT_ID); + expect(lastAjaxCall()).not.toBeDefined(); }); }); @@ -83,34 +101,29 @@ describe('loading modes', function() { describe('on request', function() { - it('should not load anything when player created and source wasn\'t specified', function(done) { + it('should not load anything when player created and source wasn\'t specified', function() { whenDocumentReady(function() { prepareDivElement(ELEMENT_ID); var loadSpy = jasmine.createSpy('load'); - anm.createPlayer(ELEMENT_ID, { loadingMode: anm.C.LM_ONREQUEST, - handle: { 'load': loadSpy } }); - expect(loadSpy).not.toHaveBeenCalled(); - - done(); + anm.createPlayer(ELEMENT_ID, { loadingMode: anm.C.LM_ONREQUEST }); + expect(lastAjaxCall()).not.toBeDefined(); }); }); - it('still should not load anything even when source was specified with HTML attribute', function(done) { + it('still should not load anything even when source was specified with HTML attribute', function() { whenDocumentReady(function() { var element = prepareDivElement(ELEMENT_ID); element.setAttribute('anm-player-target', true); - element.setAttribute('anm-src', JSON_NODE_SRC); + element.setAttribute('anm-src', JSON_SRC); element.setAttribute('anm-importer', 'fake'); - anm.findAndInitPotentialPlayers({ 'handle': { 'load': function(animation) { - expect(animation).toBeDefined(); - done(); - } } }); + anm.findAndInitPotentialPlayers({ loadingMode: anm.C.LM_ONREQUEST }); + expect(lastAjaxCall()).not.toBeDefined(); }); }); - it('still should not load anything even when source was with forSnapshot', function() { + it('still should not load anything even when source was passed with forSnapshot', function() { }); From 8ba2d4483df4ef038a35c88e1c2fc6f9b3ce5fd6 Mon Sep 17 00:00:00 2001 From: Ulric Wilfred Date: Mon, 21 Sep 2015 17:36:20 +0200 Subject: [PATCH 08/16] implementing tests for loading modes, p.2 --- spec/configuration.spec.js | 0 spec/loading-modes.spec.js | 89 +++++++++++++++++++++++++++++++++++--- src/anm/player_manager.js | 10 +++++ src/main.js | 2 + 4 files changed, 96 insertions(+), 5 deletions(-) create mode 100644 spec/configuration.spec.js diff --git a/spec/configuration.spec.js b/spec/configuration.spec.js new file mode 100644 index 00000000..e69de29b diff --git a/spec/loading-modes.spec.js b/spec/loading-modes.spec.js index 57652040..e4ad1f04 100644 --- a/spec/loading-modes.spec.js +++ b/spec/loading-modes.spec.js @@ -35,6 +35,7 @@ describe('loading modes', function() { afterEach(function() { anm.detachAllPlayers(); // this will also detach element if players were created + anm.forgetAllPlayers(); //if (element && element.parentNode) document.body.removeChild(element); jasmine.Ajax.uninstall(); }); @@ -97,14 +98,16 @@ describe('loading modes', function() { }); }); + xit('autoPlay', function() {}); + }); describe('on request', function() { it('should not load anything when player created and source wasn\'t specified', function() { whenDocumentReady(function() { + prepareJsonRequestStub(); prepareDivElement(ELEMENT_ID); - var loadSpy = jasmine.createSpy('load'); anm.createPlayer(ELEMENT_ID, { loadingMode: anm.C.LM_ONREQUEST }); expect(lastAjaxCall()).not.toBeDefined(); }); @@ -112,6 +115,7 @@ describe('loading modes', function() { it('still should not load anything even when source was specified with HTML attribute', function() { whenDocumentReady(function() { + prepareJsonRequestStub(); var element = prepareDivElement(ELEMENT_ID); element.setAttribute('anm-player-target', true); @@ -124,14 +128,77 @@ describe('loading modes', function() { }); it('still should not load anything even when source was passed with forSnapshot', function() { + whenDocumentReady(function() { + prepareJsonRequestStub(); + prepareDivElement(ELEMENT_ID); + var fakeImporter = anm.importers.create('fake'); + var importLoadSpy = spyOn(fakeImporter, 'load').and.callThrough(); + anm.Player.forSnapshot(ELEMENT_ID, JSON_SRC, fakeImporter); + + expect(importLoadSpy).not.toHaveBeenCalled(); + expect(lastAjaxCall()).not.toBeDefined(); + }); }); it('should load animation when load called manually', function() { + whenDocumentReady(function() { + prepareJsonRequestStub(); + prepareDivElement(ELEMENT_ID); + var player = anm.createPlayer(ELEMENT_ID, { loadingMode: anm.C.LM_ONREQUEST }); + var fakeImporter = anm.importers.create('fake'); + player.load(JSON_SRC, fakeImporter); + expect(lastAjaxCall()).toBeDefined(); + }); + }); + + it('should load animation when load called manually w/o arguments and source was specified via HTML attribute', function() { + whenDocumentReady(function() { + prepareJsonRequestStub(); + var element = prepareDivElement(ELEMENT_ID); + + element.setAttribute('anm-player-target', true); + element.setAttribute('anm-src', JSON_SRC); + element.setAttribute('anm-importer', 'fake'); + + anm.findAndInitPotentialPlayers({ loadingMode: anm.C.LM_ONREQUEST }); + + anm.player_manager.instances[0].load(); + expect(lastAjaxCall()).toBeDefined(); + }); + }); + + it('should load animation when load called manually w/o arguments and source was provided with forSnapshot', function() { + whenDocumentReady(function() { + prepareJsonRequestStub(); + prepareDivElement(ELEMENT_ID); + + var fakeImporter = anm.importers.create('fake'); + var importLoadSpy = spyOn(fakeImporter, 'load').and.callThrough(); + anm.Player.forSnapshot(ELEMENT_ID, JSON_SRC, fakeImporter); + + expect(importLoadSpy).not.toHaveBeenCalled(); + expect(lastAjaxCall()).not.toBeDefined(); + }); + }); + + it('if autoPlay is off, should not play animation after a call to `load` even when source was specified with HTML attributes', function() { }); - it('should load animation when load called manually w/o arguments and source was specified', function() { + it('if autoPlay is off, should not play animation after a call to `load` even when source was passed with forSnapshot call', function() { + + }); + + it('if autoPlay is on, should automatically play animation just after a call to `load`', function() { + + }); + + it('if autoPlay is on and source was specified with HTML attributes, should automatically play animation just after a call to `load`', function() { + + }); + + it('if autoPlay is on and source was passed with forSnapshot call, should automatically play animation just after a call to `load`', function() { }); @@ -147,13 +214,25 @@ describe('loading modes', function() { }); - it('should load animation before playing if `load` wasn\'t called before `play`', function() { + it('should automatically load and play animation on `play` call when source was specified with HTML attribute', function() { }); - }); + it('should automatically load and play animation on `play` call when source was passed with forSnapshot', function() { + + }); - xdescribe('onload', function() {}); + it('if `load` was called before `play`, should postpone it to `play` call', function() { + + }); + + it('should fail if `load` wasn\'t called before `play` and no source was specified', function() { + + }); + + // autoPlay option should not affect a call + + }); xdescribe('onidle', function() {}); diff --git a/src/anm/player_manager.js b/src/anm/player_manager.js index 96ddd692..328f5b9d 100644 --- a/src/anm/player_manager.js +++ b/src/anm/player_manager.js @@ -56,6 +56,16 @@ PlayerManager.prototype.detachAll = function() { } } +/** + * @method forgetAll + * + * Clear the data collected about all players instances created before + */ +PlayerManager.prototype.forgetAll = function() { + this.hash = {}; + this.instances = []; +} + /** * @method handleDocumentHiddenChange * @private diff --git a/src/main.js b/src/main.js index 35033794..40feb5b2 100644 --- a/src/main.js +++ b/src/main.js @@ -24,6 +24,7 @@ function findAndInitPotentialPlayers(options) { } var detachAllPlayers = manager.detachAll.bind(manager); +var forgetAllPlayers = manager.forgetAll.bind(manager); engine.onDocReady(findAndInitPotentialPlayers); @@ -83,6 +84,7 @@ var anm = { findAndInitPotentialPlayers: findAndInitPotentialPlayers, detachAllPlayers: detachAllPlayers, + forgetAllPlayers: forgetAllPlayers, interop: { playerjs: require('./anm/interop/playerjs-io.js') From c8f1a6cc314b2aa5bde148793e2f7d398984ec6c Mon Sep 17 00:00:00 2001 From: Ulric Wilfred Date: Mon, 21 Sep 2015 19:11:41 +0200 Subject: [PATCH 09/16] implementing tests for loading modes, p.3 --- spec/loading-modes.spec.js | 94 +++++++++++++++++++++++++++++++++++--- 1 file changed, 88 insertions(+), 6 deletions(-) diff --git a/spec/loading-modes.spec.js b/spec/loading-modes.spec.js index e4ad1f04..ee83c15a 100644 --- a/spec/loading-modes.spec.js +++ b/spec/loading-modes.spec.js @@ -40,6 +40,13 @@ describe('loading modes', function() { jasmine.Ajax.uninstall(); }); + it('`autoPlay` is off by default', function() { + whenDocumentReady(function() { + prepareDivElement(ELEMENT_ID); + expect(anm.createPlayer(ELEMENT_ID).autoPlay).toBeFalsy(); + }); + }); + it('should have `rightaway` as default option', function() { whenDocumentReady(function() { prepareDivElement(ELEMENT_ID); @@ -57,6 +64,17 @@ describe('loading modes', function() { describe('right away', function() { + it('should indeed load animation passed to `load` call', function() { + whenDocumentReady(function() { + prepareJsonRequestStub(); + prepareDivElement(ELEMENT_ID); + var player = anm.createPlayer(ELEMENT_ID); + var fakeImporter = anm.importers.create('fake'); + player.load(JSON_SRC, fakeImporter); + expect(lastAjaxCall()).toBeDefined(); + }); + }); + it('should automatically load a scene when source specified with HTML attribute', function() { whenDocumentReady(function() { prepareJsonRequestStub(); @@ -98,7 +116,65 @@ describe('loading modes', function() { }); }); - xit('autoPlay', function() {}); + it('when autoPlay is on, yet should not play if no source was specified', function() { + whenDocumentReady(function() { + prepareDivElement(ELEMENT_ID); + + var player = anm.createPlayer(ELEMENT_ID, { autoPlay: true }); + playSpy = spyOn(player, 'play'); + expect(playSpy).not.toHaveBeenCalled(); + }); + }); + + it('when autoPlay is on, should automatically play animation passed to a `load` call', function() { + whenDocumentReady(function() { + prepareJsonRequestStub(); + prepareDivElement(ELEMENT_ID); + var player = anm.createPlayer(ELEMENT_ID, { autoPlay: true }); + playSpy = spyOn(player, 'play'); + var fakeImporter = anm.importers.create('fake'); + player.load(JSON_SRC, fakeImporter); + expect(playSpy).toHaveBeenCalled(); + }); + }); + + it('when autoPlay is on, should automatically load and play animation specified with HTML attribute', function() { + whenDocumentReady(function() { + prepareJsonRequestStub(); + var element = prepareDivElement(ELEMENT_ID); + + element.setAttribute('anm-player-target', true); + element.setAttribute('anm-src', JSON_SRC); + element.setAttribute('anm-importer', 'fake'); + + var playSpy = jasmine.createSpy('play'); + anm.findAndInitPotentialPlayers({ + autoPlay: true, + handle: { 'play': playSpy } + }); + + expect(lastAjaxCall()).toBeDefined(); + expect(playSpy).toHaveBeenCalled(); + }); + }); + + it('when autoPlay is on, should automatically load and play animation passed with forSnapshot method', function() { + whenDocumentReady(function() { + prepareJsonRequestStub(); + prepareDivElement(ELEMENT_ID); + + var fakeImporter = anm.importers.create('fake'); + var importLoadSpy = spyOn(fakeImporter, 'load').and.callThrough(); + var playSpy = jasmine.createSpy('play'); + anm.Player.forSnapshot(ELEMENT_ID, JSON_SRC, fakeImporter, { + autoPlay: true, + handle: { 'play': playSpy } + }); + + expect(importLoadSpy).toHaveBeenCalled(); + expect(playSpy).toHaveBeenCalled(); + }); + }); }); @@ -206,11 +282,11 @@ describe('loading modes', function() { describe('on play', function() { - it('should not load anything when player was created', function() { + it('should not load anything when player was created and source wasn\'t specified', function() { }); - it('should load animation when `load` was called manually', function() { + it('if `load` was called before `play`, should postpone it to `play` call', function() { }); @@ -222,15 +298,21 @@ describe('loading modes', function() { }); - it('if `load` was called before `play`, should postpone it to `play` call', function() { + it('should fail if `load` wasn\'t called before `play` and no source was specified', function() { }); - it('should fail if `load` wasn\'t called before `play` and no source was specified', function() { + it('if autoPlay is on, should automatically load and play animation just after a call to `load`', function() { }); - // autoPlay option should not affect a call + it('if autoPlay is on and source was specified with HTML attributes, should automatically play animation right away', function() { + + }); + + it('if autoPlay is on and source was passed with forSnapshot call, should automatically play animation right away', function() { + + }); }); From ecee4fe51724271e80f45422116c82272617bc54 Mon Sep 17 00:00:00 2001 From: Ulric Wilfred Date: Mon, 21 Sep 2015 20:23:10 +0200 Subject: [PATCH 10/16] implementing tests for loading modes, p.4 --- spec/loading-modes.spec.js | 151 +++++++++++++++++++++++++++++++++---- 1 file changed, 136 insertions(+), 15 deletions(-) diff --git a/spec/loading-modes.spec.js b/spec/loading-modes.spec.js index ee83c15a..b24d9bc7 100644 --- a/spec/loading-modes.spec.js +++ b/spec/loading-modes.spec.js @@ -64,6 +64,16 @@ describe('loading modes', function() { describe('right away', function() { + it('should not load anything when player created and source wasn\'t specified', function() { + whenDocumentReady(function() { + prepareJsonRequestStub(); + prepareDivElement(ELEMENT_ID); + + anm.createPlayer(ELEMENT_ID); + expect(lastAjaxCall()).not.toBeDefined(); + }); + }); + it('should indeed load animation passed to `load` call', function() { whenDocumentReady(function() { prepareJsonRequestStub(); @@ -98,21 +108,53 @@ describe('loading modes', function() { prepareDivElement(ELEMENT_ID); var fakeImporter = anm.importers.create('fake'); - var importLoadSpy = spyOn(fakeImporter, 'load').and.callThrough(); anm.Player.forSnapshot(ELEMENT_ID, JSON_SRC, fakeImporter); - expect(importLoadSpy).toHaveBeenCalled(); expect(lastAjaxCall()).toBeDefined(); }); }); - it('should not load anything when player created and source wasn\'t specified', function() { + it('since autoPlay is off, should not play animation after a call to `load`', function() { whenDocumentReady(function() { prepareJsonRequestStub(); prepareDivElement(ELEMENT_ID); + var player = anm.createPlayer(ELEMENT_ID); + playSpy = spyOn(player, 'play'); + var fakeImporter = anm.importers.create('fake'); + player.load(JSON_SRC, fakeImporter); + expect(playSpy).not.toHaveBeenCalled(); + }); + }); - anm.createPlayer(ELEMENT_ID); - expect(lastAjaxCall()).not.toBeDefined(); + it('since autoPlay is off, should not play animation even when source was specified with HTML attributes', function() { + whenDocumentReady(function() { + prepareJsonRequestStub(); + var element = prepareDivElement(ELEMENT_ID); + + element.setAttribute('anm-player-target', true); + element.setAttribute('anm-src', JSON_SRC); + element.setAttribute('anm-importer', 'fake'); + + var playSpy = jasmine.createSpy('play'); + anm.findAndInitPotentialPlayers({ + handle: { 'play': playSpy } + }); + + expect(playSpy).not.toHaveBeenCalled(); + }); + }); + + it('since autoPlay is off, should not play animation even when source was passed with forSnapshot call', function() { + whenDocumentReady(function() { + prepareJsonRequestStub(); + prepareDivElement(ELEMENT_ID); + + var fakeImporter = anm.importers.create('fake'); + var playSpy = jasmine.createSpy('play'); + var player = anm.Player.forSnapshot(ELEMENT_ID, JSON_SRC, fakeImporter, { + handle: { 'play': playSpy } + }); + expect(playSpy).not.toHaveBeenLoaded(); }); }); @@ -153,7 +195,6 @@ describe('loading modes', function() { handle: { 'play': playSpy } }); - expect(lastAjaxCall()).toBeDefined(); expect(playSpy).toHaveBeenCalled(); }); }); @@ -164,14 +205,12 @@ describe('loading modes', function() { prepareDivElement(ELEMENT_ID); var fakeImporter = anm.importers.create('fake'); - var importLoadSpy = spyOn(fakeImporter, 'load').and.callThrough(); var playSpy = jasmine.createSpy('play'); anm.Player.forSnapshot(ELEMENT_ID, JSON_SRC, fakeImporter, { autoPlay: true, handle: { 'play': playSpy } }); - expect(importLoadSpy).toHaveBeenCalled(); expect(playSpy).toHaveBeenCalled(); }); }); @@ -210,7 +249,9 @@ describe('loading modes', function() { var fakeImporter = anm.importers.create('fake'); var importLoadSpy = spyOn(fakeImporter, 'load').and.callThrough(); - anm.Player.forSnapshot(ELEMENT_ID, JSON_SRC, fakeImporter); + anm.Player.forSnapshot(ELEMENT_ID, JSON_SRC, fakeImporter, { + loadingMode: anm.C.LM_ONREQUEST + }); expect(importLoadSpy).not.toHaveBeenCalled(); expect(lastAjaxCall()).not.toBeDefined(); @@ -239,6 +280,7 @@ describe('loading modes', function() { anm.findAndInitPotentialPlayers({ loadingMode: anm.C.LM_ONREQUEST }); + expect(lastAjaxCall()).not.toBeDefined(); anm.player_manager.instances[0].load(); expect(lastAjaxCall()).toBeDefined(); }); @@ -250,32 +292,111 @@ describe('loading modes', function() { prepareDivElement(ELEMENT_ID); var fakeImporter = anm.importers.create('fake'); - var importLoadSpy = spyOn(fakeImporter, 'load').and.callThrough(); - anm.Player.forSnapshot(ELEMENT_ID, JSON_SRC, fakeImporter); + var player = anm.Player.forSnapshot(ELEMENT_ID, JSON_SRC, fakeImporter, { + loadingMode: anm.C.LM_ONREQUEST + }); - expect(importLoadSpy).not.toHaveBeenCalled(); expect(lastAjaxCall()).not.toBeDefined(); + player.load(); + expect(lastAjaxCall()).toBeDefined(); }); }); - it('if autoPlay is off, should not play animation after a call to `load` even when source was specified with HTML attributes', function() { + it('since autoPlay is off, should not play animation after a call to `load`', function() { + whenDocumentReady(function() { + prepareJsonRequestStub(); + prepareDivElement(ELEMENT_ID); + var player = anm.createPlayer(ELEMENT_ID, { + loadingMode: anm.C.LM_ONREQUEST + }); + playSpy = spyOn(player, 'play'); + var fakeImporter = anm.importers.create('fake'); + player.load(JSON_SRC, fakeImporter); + expect(playSpy).not.toHaveBeenCalled(); + }); + }); + + it('since autoPlay is off, should not play animation after a call to `load` even when source was specified with HTML attributes', function() { + whenDocumentReady(function() { + prepareJsonRequestStub(); + var element = prepareDivElement(ELEMENT_ID); + + element.setAttribute('anm-player-target', true); + element.setAttribute('anm-src', JSON_SRC); + element.setAttribute('anm-importer', 'fake'); + + var playSpy = jasmine.createSpy('play'); + anm.findAndInitPotentialPlayers({ + loadingMode: anm.C.LM_ONREQUEST, + handle: { 'play': playSpy } + }); + expect(playSpy).not.toHaveBeenCalled(); + }); }); - it('if autoPlay is off, should not play animation after a call to `load` even when source was passed with forSnapshot call', function() { + it('since autoPlay is off, should not play animation after a call to `load` even when source was passed with forSnapshot call', function() { + whenDocumentReady(function() { + prepareJsonRequestStub(); + prepareDivElement(ELEMENT_ID); + var fakeImporter = anm.importers.create('fake'); + var player = anm.Player.forSnapshot(ELEMENT_ID, JSON_SRC, fakeImporter, { + loadingMode: anm.C.LM_ONREQUEST + }); + playSpy = spyOn(player, 'play'); + player.load(); + expect(playSpy).not.toHaveBeenCalled(); + }); }); it('if autoPlay is on, should automatically play animation just after a call to `load`', function() { - + whenDocumentReady(function() { + prepareJsonRequestStub(); + prepareDivElement(ELEMENT_ID); + var player = anm.createPlayer(ELEMENT_ID, { + loadingMode: anm.C.LM_ONREQUEST, + autoPlay: true + }); + playSpy = spyOn(player, 'play'); + var fakeImporter = anm.importers.create('fake'); + player.load(JSON_SRC, fakeImporter); + expect(playSpy).toHaveBeenCalled(); + }); }); it('if autoPlay is on and source was specified with HTML attributes, should automatically play animation just after a call to `load`', function() { + whenDocumentReady(function() { + prepareJsonRequestStub(); + var element = prepareDivElement(ELEMENT_ID); + + element.setAttribute('anm-player-target', true); + element.setAttribute('anm-src', JSON_SRC); + element.setAttribute('anm-importer', 'fake'); + var playSpy = jasmine.createSpy('play'); + anm.findAndInitPotentialPlayers({ + loadingMode: anm.C.LM_ONREQUEST, + autoPlay: true, handle: { 'play': playSpy } + }); + anm.player_manager.instances[0].load(); + expect(playSpy).toHaveBeenCalled(); + }); }); it('if autoPlay is on and source was passed with forSnapshot call, should automatically play animation just after a call to `load`', function() { + whenDocumentReady(function() { + prepareJsonRequestStub(); + prepareDivElement(ELEMENT_ID); + var fakeImporter = anm.importers.create('fake'); + var player = anm.Player.forSnapshot(ELEMENT_ID, JSON_SRC, fakeImporter, { + loadingMode: anm.C.LM_ONREQUEST + }); + playSpy = spyOn(player, 'play'); + player.load(); + expect(playSpy).toHaveBeenCalled(); + }); }); }); From 601c0fd546a6ee7a90a95ee95a35ddc56e5a1278 Mon Sep 17 00:00:00 2001 From: Ulric Wilfred Date: Tue, 22 Sep 2015 19:30:57 +0200 Subject: [PATCH 11/16] more configuration, docs and tests --- doc/embedding.md | 33 ++++++++++++++++++++++++++++++++- examples/configuration.js | 13 +++++++++---- spec/loading-modes.spec.js | 2 -- src/anm/constants.js | 28 +++++++++++++++++++--------- 4 files changed, 60 insertions(+), 16 deletions(-) diff --git a/doc/embedding.md b/doc/embedding.md index 08dd6e25..9cf96499 100644 --- a/doc/embedding.md +++ b/doc/embedding.md @@ -249,7 +249,8 @@ URL | `IFRAME`/`div` | JS Object | Default | Description - | _`div`-only:_ `anm-src` | - | - | JSON for the animation to load from - | _`div`-only:_ `anm-importer` | - | `animatron` | Importer to use with this JSON `m`/`mode` | `anm-mode` | `mode` | - | (_deprecated_) a mode of a Player, one of: ... -`lm`/`lmode` | `anm-loading-mode` | `loadingMode` | `onplay` | `onplay` means to start loading an animation when user clicks _Play_ button (and show _thumbnail_ before), `onrequest` means to start loading animation only when the script asked for it and expect it to be completely loaded when user clicks _Play_ button +`lm`/`lmode` | `anm-loading-mode` | `loadingMode` | `rightaway` | see [section below][lmodes-pmodes] +`pm`/`pmode` | `anm-playing-mode` | `playingMode` | `onrequest` | see [section below][lmodes-pmodes] - | `anm-events` | `handleEvents` | `false` | allows animation to catch and process user mouse/keyboard events by itself (has a meaning for games or infographics) - | `anm-debug` | `debug` | `false` | show debug information like FPS and paths/bounds of objects `bg`/`bgcolor` | `anm-bg-color` | `bgColor` | `transparent` | set background color of an animation (if it is set, it can't be overriden), format is `#00ff00` @@ -263,6 +264,35 @@ URL | `IFRAME`/`div` | JS Object | Default | Description - | `anm-scene-size` | `forceSceneSize` | `false` | always override user-specified Player size with a size of a scene, so when scene loaded, Player will resize itself, if sizes don't match `me`/`errors` | `anm-mute-errors` | `muteErrors` | `false` | do not stop playing if some errors happened during the playing process, just log them +## Loading Modes and Playing Modes + +Loading Mode | Playing Mode | `autoPlay` | HTML attr. | `forSnaphot`/manual load | Result +-------------|--------------|------------|------------|-----------|--- +`rightaway` | `onrequest` | `false` | none | yes | +`rightaway` | `onrequest` | `true` | none | yes | +`rightaway` | `onrequest` | `false` | has | - | +`rightaway` | `onrequest` | `true` | has | - | +`onrequest` | `onrequest` | `false` | none | yes | +`onrequest` | `onrequest` | `true` | none | yes | +`onrequest` | `onrequest` | `false` | has | - | +`onrequest` | `onrequest` | `true` | has | - | +`onplay` | `onrequest` | `false` | none | yes | +`onplay` | `onrequest` | `true` | none | yes | +`onplay` | `onrequest` | `false` | has | - | +`onplay` | `onrequest` | `true` | has | - | +`rightaway` | `onhover` | any | has | - | +`rightaway` | `onhover` | any | none | yes | +`rightaway` | `wheninview` | any | has | - | +`rightaway` | `wheninview` | any | none | yes | +`onrequest` | `onhover` | any | has | - | +`onrequest` | `onhover` | any | none | yes | +`onrequest` | `wheninview` | any | has | - | +`onrequest` | `wheninview` | any | none | yes | +`onplay` | `onhover` | any | has | - | +`onplay` | `onhover` | any | none | yes | +`onplay` | `wheninview` | any | has | - | +`onplay` | `wheninview` | any | none | yes | + [permanent]: https://github.com/Animatron/player/blob/docs/doc/embedding.md [iframe]: #iframe @@ -274,3 +304,4 @@ URL | `IFRAME`/`div` | JS Object | Default | Description [adding-events]: #adding-events [create-player]: #custom-scene-with-createplayer [for-snapshot]: #snapshot-with-forsnapshot +[lmodes-pmodes]: #loading-modes-and-playing-modes diff --git a/examples/configuration.js b/examples/configuration.js index df31e7c6..541d6222 100644 --- a/examples/configuration.js +++ b/examples/configuration.js @@ -11,10 +11,15 @@ var shortVersion = true; var loadingModes = [ { value: 'rightaway', name: 'right away', description: 'loads animation just immediately when finds it\'s source (i.e. from HTML attribute)' }, { value: 'onrequest', name: 'on request', description: 'waits for user to manually call .load() method' }, - { value: 'onplay', name: 'on play', description: 'when play button was pressed, starts loading a scene and plays it just after' }, - { value: 'onidle', name: 'on idle', description: ' waits for pause in user actions (mouse move, clicks, keyboard) to load the animation' }, - { value: 'onhover', name: 'on hover', description: 'starts loading animation when user hovered with mouse over the player canvas' }, - { value: 'wheninview', name: 'when in view', description: 'starts loading animation when Player appeares in browser viewport' } + { value: 'onplay', name: 'on play', description: 'when play button was pressed, starts loading a scene and plays it just after' }/*, + { value: 'onidle', name: 'on idle', description: ' waits for pause in user actions (mouse move, clicks, keyboard) to load the animation' },*/ +]; + +var playingModes = [ + { value: 'onrequest', name: 'on request', description: 'same to autoPlay: false, waits for user to manually call .play() method' }, + { value: 'rightaway', name: 'right away', description: 'same to autoPlay: true' }, + { value: 'onhover', name: 'on hover', description: 'starts playing animation when user hovered with mouse over the player canvas' }, + { value: 'wheninview', name: 'when in view', description: 'starts playing animation when Player appeares in browser viewport' } ]; function getElm(id) { return document.getElementById(id); } diff --git a/spec/loading-modes.spec.js b/spec/loading-modes.spec.js index b24d9bc7..5f4e8dd2 100644 --- a/spec/loading-modes.spec.js +++ b/spec/loading-modes.spec.js @@ -439,8 +439,6 @@ describe('loading modes', function() { xdescribe('onidle', function() {}); - xdescribe('onhover', function() {}); - xdescribe('wheninview', function() {}); }); diff --git a/src/anm/constants.js b/src/anm/constants.js index 4971fc75..a5228d19 100644 --- a/src/anm/constants.js +++ b/src/anm/constants.js @@ -65,24 +65,34 @@ C.LT_URL = 4; // ### Loading modes /* ---------------- */ -// some loading modes below are closely tied to `autoPlay` option: if it's set to `true`, playing starts -// immediately after loading (default is `false`) for `rightaway`, `onidle`, `onhover` and `wheninview` +// loading modes below are closely tied to `autoPlay` option: if it's set to `true`, playing starts +// immediately after loading (default is `false`) + +// there are also playing modes below; C.LM_RIGHTAWAY = 'rightaway'; // searches for an animation source where possible (i.e. HTML tag attribute) // and, if finds it, tries to load it on player creation; if source wasn't found, - // waits for user to call .load manually as for 'onrequest' -C.LM_ONREQUEST = 'onrequest'; // waits for user to manually call .load() method; if animation source was - // passed i.e. through HTML tag attribute, waits for user to call .load() + // waits for user to call `.load` manually as for 'onrequest' +C.LM_ONREQUEST = 'onrequest'; // waits for user to manually call `.load` method; if animation source was + // passed i.e. through HTML tag attribute, waits for user to call `.load` // method without parameters and uses this URL as a source -C.LM_ONPLAY = 'onplay'; // when play button was pressed, starts loading a scene and plays it just after (overrides `autoPlay`) +C.LM_ONPLAY = 'onplay'; // when play button was pressed or `.play` method was called, starts loading a scene and plays it just after C.LM_ONIDLE = 'onidle'; // waits for pause in user actions (mouse move, clicks, keyboard) to load the animation; planned to use // requestIdleCallback in future -C.LM_ONHOVER = 'onhover'; // starts loading animation when user hovered with mouse over the player canvas -C.LM_WHENINVIEW = 'wheninview'; // starts loading animation when at least some part of canvas appears in - // user's browser viewport C.LM_DEFAULT = C.LM_RIGHTAWAY; +// ### Playing modes +/* ---------------- */ + +// playing modes are overriden if `autoPlay` == `true` + +C.LM_ONREQUEST = 'onrequest'; // waits for user to manually call `play` method or press play button +C.PM_ONHOVER = 'onhover'; // starts playing animation when user hovered with mouse over the player canvas +C.PM_WHENINVIEW = 'wheninview'; // starts loading animation when at least some part of canvas appears in + // user's browser viewport + +C.PM_DEFAULT = C.PM_ONREQUEST; // Element // ----------------------------------------------------------------------------- From 54c03d33e5daa35a61fc9c3ba808f95eded54f40 Mon Sep 17 00:00:00 2001 From: Ulric Wilfred Date: Tue, 22 Sep 2015 21:40:14 +0200 Subject: [PATCH 12/16] more loading modes docs --- doc/embedding.md | 71 ++++++++++++++++++++++++++++++++---------------- 1 file changed, 47 insertions(+), 24 deletions(-) diff --git a/doc/embedding.md b/doc/embedding.md index 9cf96499..6f0c7103 100644 --- a/doc/embedding.md +++ b/doc/embedding.md @@ -266,32 +266,55 @@ URL | `IFRAME`/`div` | JS Object | Default | Description ## Loading Modes and Playing Modes +Most times, you'll only need `autoPlay` option. But in some cases you may wish to configure loading and playing precisely. + +First, some quick tips: + +* if you want scene to load and play immediately when you specified source, just set `autoPlay` to `true`, no loading / playing mode needed; +* if you want to postpone loading even when you specified animation source with HTML attribute, set `loadingMode` to `onrequest`; +* if you want loading to always automatically happen before playing (i.e. to load scene just when user pressed Play button or you called `play` method, and play it then), set `loadingMode` to `onplay`; +* if you want loading to happen in background i.e. when you specified source with HTML attribute, but player to start only when user hovers over it, set `playingMode` to `onhover`, leave `loadingMode` being default; ...only when user scrolled down to it — set `playingMode` to `wheninview`; + +Loading modes are: + +* `rightaway` _(default)_ — searches for an animation source where possible (i.e. HTML tag attribute) and, if finds it, tries to load it on player creation; if source wasn't found, waits for user to call `.load` manually as for 'onrequest'; +* `onrequest` — waits for user to manually call `.load` method; if animation source was passed i.e. through HTML tag attribute, waits for user to call `.load` method without parameters and uses this URL as a source; this allows user to completely control a moment of loading; if `.load` method was called with some values, this call cancels postponed load and overrides it; +* `onplay` — when play button was pressed or `.play` method was called, automatically starts loading a scene and plays it just after; even if scene was passed with HTML attributes, waits for `.play` call; +* `onidle` — not yet implemented; + +Playing modes are: + +* `onrequest` _(default)_ — waits for user to manually call `.play` method or press play button; +* `onhover` — starts playing animation (if loaded before) when user hovered with mouse over the player canvas; +* `wheninview` — starts playing animation (if loaded before) when at least some part of canvas appears in user's browser viewport; + + Loading Mode | Playing Mode | `autoPlay` | HTML attr. | `forSnaphot`/manual load | Result -------------|--------------|------------|------------|-----------|--- -`rightaway` | `onrequest` | `false` | none | yes | -`rightaway` | `onrequest` | `true` | none | yes | -`rightaway` | `onrequest` | `false` | has | - | -`rightaway` | `onrequest` | `true` | has | - | -`onrequest` | `onrequest` | `false` | none | yes | -`onrequest` | `onrequest` | `true` | none | yes | -`onrequest` | `onrequest` | `false` | has | - | -`onrequest` | `onrequest` | `true` | has | - | -`onplay` | `onrequest` | `false` | none | yes | -`onplay` | `onrequest` | `true` | none | yes | -`onplay` | `onrequest` | `false` | has | - | -`onplay` | `onrequest` | `true` | has | - | -`rightaway` | `onhover` | any | has | - | -`rightaway` | `onhover` | any | none | yes | -`rightaway` | `wheninview` | any | has | - | -`rightaway` | `wheninview` | any | none | yes | -`onrequest` | `onhover` | any | has | - | -`onrequest` | `onhover` | any | none | yes | -`onrequest` | `wheninview` | any | has | - | -`onrequest` | `wheninview` | any | none | yes | -`onplay` | `onhover` | any | has | - | -`onplay` | `onhover` | any | none | yes | -`onplay` | `wheninview` | any | has | - | -`onplay` | `wheninview` | any | none | yes | +`rightaway` | `onrequest` | `false` | none | yes | loads a scene from `.load` call and waits for a call to `.play` method (or play button to be pressed) +`rightaway` | `onrequest` | `true` | none | yes | loads a scene from `.load` call and immediately starts playing it +`rightaway` | `onrequest` | `false` | has | - | immediately loads a scene specified in HTML attributes and waits for a call to `.play` method (or play button to be pressed) +`rightaway` | `onrequest` | `true` | has | - | immediately loads a scene specified in HTML attribute and then starts playing it +`onrequest` | `onrequest` | `false` | none | yes | loads a scene from `.load` call and waits for a call to `.play` method (or play button to be pressed) +`onrequest` | `onrequest` | `true` | none | yes | loads a scene from `.load` call and immediately starts playing it +`onrequest` | `onrequest` | `false` | has | - | waits for user to call `.load` method w/o attributes, then loads scene (specified in HTML attributes) and waits for a call to `.play` method (or play button to be pressed) +`onrequest` | `onrequest` | `true` | has | - | waits for user to call `.load` method w/o attributes, then loads scene (specified in HTML attributes) and immediately plays it +`onplay` | `onrequest` | `false` | none | yes | do not loads the scene passed with a `.load` call, but postpones loading to a next call to `.play` method (or play button to be pressed), then loads and plays it just after that +`onplay` | `onrequest` | `true` | none | yes | do not loads the scene passed with a `.load` call, but postpones it to a call to `.play` method, but since it is called immediately, loads and plays the scene as soon as Player ready to do so +`onplay` | `onrequest` | `false` | has | - | do not loads the scene specified with HTML attributes, but postpones loading to a next call to `.play` method (or play button to be pressed), then loads and plays it just after that +`onplay` | `onrequest` | `true` | has | - | do not loads the scene specified with HTML attributes, but postpones it to a call to `.play` method, but since it is called immediately, loads and plays the scene as soon as Player ready to do so +`rightaway` | `onhover` | any | has | - | immediately loads a scene specified in HTML attributes and waits for user to move mouse over a Player to start playing +`rightaway` | `onhover` | any | none | yes | loads a scene from a `.load` call and waits for user to move mouse over a Player to start playing +`rightaway` | `wheninview` | any | has | - | immediately loads a scene specified in HTML attributes and waits for user to scroll down to a Player to start playing +`rightaway` | `wheninview` | any | none | yes | loads a scene from a `.load` call and waits for user to scroll down to a Player to start playing +`onplay` | `onhover` | any | has | - | do not loads the scene specified with HTML attributes, but postpones loading to a moment when user will move mouse over the Player, then loads and plays it just after that +`onplay` | `onhover` | any | none | yes | do not loads the scene passed with a `.load` call, but postpones loading to a moment when user will move mouse over the Player, then loads and plays it just after that +`onplay` | `wheninview` | any | has | - | do not loads the scene specified with HTML attributes, but postpones loading to a moment when user will scroll down to the Player position, then loads and plays it just after that +`onplay` | `wheninview` | any | none | yes | do not loads the scene passed with a `.load` call, but postpones loading to a moment when user will scroll down to the Player position, then loads and plays it just after that +`onrequest` | `onhover` | any | has | - | same as `onplay`/`onhover`, since `.play` method is called on mouse hover +`onrequest` | `onhover` | any | none | yes | same as `onplay`/`onhover`, since `.play` method is called on mouse hover +`onrequest` | `wheninview` | any | has | - | same as `onplay`/`wheninview`, since `.play` method is called on scroll down +`onrequest` | `wheninview` | any | none | yes | same as `onplay`/`wheninview`, since `.play` method is called on scroll down [permanent]: https://github.com/Animatron/player/blob/docs/doc/embedding.md From 7cc8508a24e80103f9b3eba80d3858aae8477350 Mon Sep 17 00:00:00 2001 From: Ulric Wilfred Date: Tue, 22 Sep 2015 21:52:24 +0200 Subject: [PATCH 13/16] add playing modes to configuration page --- examples/configuration.js | 22 ++++++++++++++++++++++ spec/playing-modes.spec.js | 7 +++++++ 2 files changed, 29 insertions(+) create mode 100644 spec/playing-modes.spec.js diff --git a/examples/configuration.js b/examples/configuration.js index 541d6222..2693c356 100644 --- a/examples/configuration.js +++ b/examples/configuration.js @@ -39,6 +39,7 @@ function collectOptions() { if (!getElm('opts-bg-color').disabled) options.bgColor = getElm('opts-bg-color').value; if (!getElm('opts-ribbons').disabled) options.ribbonsColor = getElm('opts-ribbons').value; if (!getElm('opts-loading').disabled) options.loadingMode = getElm('opts-loading').selectedIndex; + if (!getElm('opts-playing').disabled) options.playingMode = getElm('opts-playing').selectedIndex; if (!getElm('opts-thumbnail').disabled) options.thumbnail = getElm('opts-thumbnail').value; //if (!getElm('opts-images').disabled) options.imagesEnabled = getElm('opts-images').checked; if (!getElm('opts-audio').disabled) options.audioEnabled = getElm('opts-audio').checked; @@ -159,6 +160,21 @@ function init() { return select; }, modify: function(elm, form) { elm.selectedIndex = 0; } }, + 'playing': { label: 'Playing', type: 'select', + create: function() { + var select = document.createElement('select'); + for (var i = 0, il = playingModes.length, mode; i < il; i++) { + var option = document.createElement('option'); + option.innerText = option.textContent = playingModes[i].name; + select.appendChild(option); + } + select.setAttribute('title', playingModes[0].description); + select.addEventListener('change', function() { + select.setAttribute('title', playingModes[select.selectedIndex].description); + }); + return select; + }, + modify: function(elm, form) { elm.selectedIndex = 0; } }, 'thumbnail': { label: 'Thumbnail', type: 'text', modify: function(elm, form) { elm.value = defaultThumbnail; } }, 'audio': { label: 'Audio', type: 'checkbox', modify: function(elm, form) { elm.checked = true; } }, 'shadows': { label: 'Shadows', type: 'checkbox', modify: function(elm, form) { elm.checked = true; } }, @@ -211,6 +227,7 @@ var optionsMapper = function(mode, options) { function colorOption(v) { return (v.indexOf('#') >= 0) ? v.slice(1) : v; }; function booleanOption(v) { return v ? '1' : '0'; }; function loadingModeOption(v) { return loadingModes[v].value }; + function playingModeOption(v) { return playingModes[v].value }; return { width: extractOption('width', 'w', 'width', numberOption), @@ -224,6 +241,7 @@ var optionsMapper = function(mode, options) { startFrom: extractOption('startFrom', 't', 'from', parseTime), stopAt: extractOption('stopAt', 'p', 'at', parseTime), loadingMode: extractOption('loadingMode', 'lm', 'lmode', loadingModeOption), + playingMode: extractOption('playingMode', 'pm', 'pmode', playingModeOption), bgColor: extractOption('bgColor', 'bg', 'bgcolor', colorOption), ribbonsColor: extractOption('ribbonsColor', 'rc', 'ribcolor', colorOption), audioEnabled: extractOption('audioEnabled', 's', 'audio', booleanOption), @@ -243,6 +261,7 @@ var optionsMapper = function(mode, options) { function colorOption(v) { return '\'' + v + '\''; }; function booleanOption(v) { return v ? 'true' : 'false'; }; function loadingModeOption(v) { return '\'' + loadingModes[v].value + '\''; }; + function playingModeOption(v) { return '\'' + playingModes[v].value + '\''; }; function thumbnailOption(v) { return v; }; return { @@ -255,6 +274,7 @@ var optionsMapper = function(mode, options) { speed: extractOption('speed', numberOption), zoom: extractOption('zoom', numberOption), loadingMode: extractOption('loadingMode', loadingModeOption), + playingMode: extractOption('playingMode', playingModeOption), bgColor: extractOption('bgColor', colorOption), ribbonsColor: extractOption('ribbonsColor', colorOption), thumbnail: extractOption('thumbnail', textOption), @@ -277,6 +297,7 @@ var optionsMapper = function(mode, options) { function numberOption(v) { return v; }; function booleanOption(v) { return v ? 'true' : 'false'; }; function loadingModeOption(v) { return loadingModes[v].value }; + function playingModeOption(v) { return playingModes[v].value }; return { width: extractOption('width', 'anm-width', numberOption), @@ -290,6 +311,7 @@ var optionsMapper = function(mode, options) { startFrom: extractOption('startFrom', 'anm-start-from', parseTime), stopAt: extractOption('stopAt', 'anm-stop-at', parseTime), loadingMode: extractOption('loadingMode', 'anm-loading-mode', loadingModeOption), + playingMode: extractOption('playingMode', 'anm-playing-mode', playingModeOption), bgColor: extractOption('bgColor', 'anm-bg-color', colorOption), ribbonsColor: extractOption('ribbonsColor', 'anm-rib-color', colorOption), thumbnail: extractOption('thumbnail', 'anm-thumbnail', textOption), diff --git a/spec/playing-modes.spec.js b/spec/playing-modes.spec.js new file mode 100644 index 00000000..1c54c464 --- /dev/null +++ b/spec/playing-modes.spec.js @@ -0,0 +1,7 @@ +xdescribe('playing modes', function() { + + xdescribe('onhover', function() {}); + + xdescribe('wheninview', function() {}); + +}); From db5bad23e20a369f754f104afd6cfd11cda95974 Mon Sep 17 00:00:00 2001 From: Ulric Wilfred Date: Thu, 24 Sep 2015 22:12:22 +0200 Subject: [PATCH 14/16] test auto-play mode --- spec/loading-modes.spec.js | 85 +++++++++++++++++++++++++++++++++++--- src/anm/player.js | 6 ++- 2 files changed, 83 insertions(+), 8 deletions(-) diff --git a/spec/loading-modes.spec.js b/spec/loading-modes.spec.js index 5f4e8dd2..a68fb148 100644 --- a/spec/loading-modes.spec.js +++ b/spec/loading-modes.spec.js @@ -221,10 +221,11 @@ describe('loading modes', function() { it('should not load anything when player created and source wasn\'t specified', function() { whenDocumentReady(function() { - prepareJsonRequestStub(); prepareDivElement(ELEMENT_ID); - anm.createPlayer(ELEMENT_ID, { loadingMode: anm.C.LM_ONREQUEST }); - expect(lastAjaxCall()).not.toBeDefined(); + var loadSpy = jasmine.createSpy('load'); + anm.createPlayer(ELEMENT_ID, { loadingMode: anm.C.LM_ONREQUEST, + handle: { load: loadSpy } }}); + expect(loadSpy).not.toHaveBeenCalled(); }); }); @@ -404,22 +405,66 @@ describe('loading modes', function() { describe('on play', function() { it('should not load anything when player was created and source wasn\'t specified', function() { - + whenDocumentReady(function() { + prepareDivElement(ELEMENT_ID); + var loadSpy = jasmine.createSpy('load'); + anm.createPlayer(ELEMENT_ID, { loadingMode: anm.C.LM_ONPLAY, + handle: { load: loadSpy } }); + expect(loadSpy).not.toHaveBeenCalled(); + }); }); it('if `load` was called before `play`, should postpone it to `play` call', function() { - + whenDocumentReady(function() { + prepareJsonRequestStub(); + prepareDivElement(ELEMENT_ID); + var player = anm.createPlayer(ELEMENT_ID, { + loadingMode: anm.C.LM_ONPLAY + }); + playSpy = spyOn(player, 'play'); + var fakeImporter = anm.importers.create('fake'); + player.load(JSON_SRC, fakeImporter); + expect(lastAjaxCall()).not.toBeDefined(); + expect(playSpy).not.toHaveBeenCalled(); + player.play(); + expect(lastAjaxCall()).toBeDefined(); + }); }); it('should automatically load and play animation on `play` call when source was specified with HTML attribute', function() { + whenDocumentReady(function() { + prepareJsonRequestStub(); + var element = prepareDivElement(ELEMENT_ID); + + element.setAttribute('anm-player-target', true); + element.setAttribute('anm-src', JSON_SRC); + element.setAttribute('anm-importer', 'fake'); + anm.findAndInitPotentialPlayers({ + loadingMode: anm.C.LM_ONPLAY, + }); + expect(lastAjaxCall()).not.toBeDefined(); + anm.player_manager.instances[0].play(); + expect(lastAjaxCall()).toBeDefined(); + }); }); it('should automatically load and play animation on `play` call when source was passed with forSnapshot', function() { + whenDocumentReady(function() { + prepareJsonRequestStub(); + prepareDivElement(ELEMENT_ID); + var fakeImporter = anm.importers.create('fake'); + var player = anm.Player.forSnapshot(ELEMENT_ID, JSON_SRC, fakeImporter, { + loadingMode: anm.C.LM_ONPLAY + }); + expect(lastAjaxCall()).not.toBeDefined(); + player.play(); + expect(lastAjaxCall()).toBeDefined(); + }); }); - it('should fail if `load` wasn\'t called before `play` and no source was specified', function() { + xit('should fail if `load` wasn\'t called before `play` and no source was specified', function() { }); @@ -428,11 +473,39 @@ describe('loading modes', function() { }); it('if autoPlay is on and source was specified with HTML attributes, should automatically play animation right away', function() { + whenDocumentReady(function() { + prepareJsonRequestStub(); + var element = prepareDivElement(ELEMENT_ID); + + element.setAttribute('anm-player-target', true); + element.setAttribute('anm-src', JSON_SRC); + element.setAttribute('anm-importer', 'fake'); + anm.findAndInitPotentialPlayers({ + loadingMode: anm.C.LM_ONPLAY, + autoPlay: true + }); + expect(lastAjaxCall()).not.toBeDefined(); + anm.player_manager.instances[0].play(); + expect(lastAjaxCall()).toBeDefined(); + }); }); it('if autoPlay is on and source was passed with forSnapshot call, should automatically play animation right away', function() { + whenDocumentReady(function() { + prepareJsonRequestStub(); + prepareDivElement(ELEMENT_ID); + var fakeImporter = anm.importers.create('fake'); + var playSpy = jasmine.createSpy('play'); + var player = anm.Player.forSnapshot(ELEMENT_ID, JSON_SRC, fakeImporter, { + loadingMode: anm.C.LM_ONPLAY, + autoPlay: true, + handle: { play: playSpy } + }); + expect(lastAjaxCall()).toBeDefined(); + expect(playSpy).toHaveBeenCalled(); + }); }); }); diff --git a/src/anm/player.js b/src/anm/player.js index 613582bb..3d854357 100644 --- a/src/anm/player.js +++ b/src/anm/player.js @@ -127,7 +127,8 @@ Player.DEFAULT_CONFIGURATION = { 'debug': false, 'controlsEnabled': undefined, // undefined means 'auto' 'controlsInvisible': undefined, 'infoEnabled': undefined, // undefined means 'auto' - 'loadingMode': C.LM_DEFAULT, // undefined means 'auto' + 'loadingMode': C.LM_DEFAULT, + 'playingMode': C.PM_DEFAULT, 'thumbnail': undefined, 'bgColor': undefined, 'ribbonsColor': undefined, @@ -173,7 +174,8 @@ Player.EMPTY_BG = 'rgba(0,0,0,.05)'; * controlsEnabled: undefined, // undefined means 'auto' * infoEnabled: undefined, // undefined means 'auto' * handleEvents: undefined, // undefined means 'auto' - * loadingMode: undefined, // undefined means 'auto' + * loadingMode: C.LM_DEFAULT, // see loading modes description in constants.js + * playingMode: C.PM_DEFAULT, // see playing modes description in constants.js * thumbnail: undefined, * forceAnimationSize: false, * muteErrors: false From 2d1278f6fba978b2dda359dc469caee8209574a1 Mon Sep 17 00:00:00 2001 From: Ulric Wilfred Date: Fri, 25 Sep 2015 13:29:22 +0200 Subject: [PATCH 15/16] cut out previous loadingMode logic --- spec/loading-modes.spec.js | 17 ++++++++-- src/anm/player.js | 68 ++++++-------------------------------- 2 files changed, 26 insertions(+), 59 deletions(-) diff --git a/spec/loading-modes.spec.js b/spec/loading-modes.spec.js index a68fb148..c2c38a6d 100644 --- a/spec/loading-modes.spec.js +++ b/spec/loading-modes.spec.js @@ -224,7 +224,7 @@ describe('loading modes', function() { prepareDivElement(ELEMENT_ID); var loadSpy = jasmine.createSpy('load'); anm.createPlayer(ELEMENT_ID, { loadingMode: anm.C.LM_ONREQUEST, - handle: { load: loadSpy } }}); + handle: { load: loadSpy } }); expect(loadSpy).not.toHaveBeenCalled(); }); }); @@ -469,7 +469,20 @@ describe('loading modes', function() { }); it('if autoPlay is on, should automatically load and play animation just after a call to `load`', function() { - + whenDocumentReady(function() { + prepareJsonRequestStub(); + prepareDivElement(ELEMENT_ID); + var player = anm.createPlayer(ELEMENT_ID, { + loadingMode: anm.C.LM_ONPLAY + }); + playSpy = spyOn(player, 'play'); + var fakeImporter = anm.importers.create('fake'); + expect(lastAjaxCall()).not.toBeDefined(); + expect(playSpy).not.toHaveBeenCalled(); + player.load(JSON_SRC, fakeImporter); + expect(lastAjaxCall()).toBeDefined(); + expect(playSpy).toHaveBeenCalled(); + }); }); it('if autoPlay is on and source was specified with HTML attributes, should automatically play animation right away', function() { diff --git a/src/anm/player.js b/src/anm/player.js index 3d854357..82594092 100644 --- a/src/anm/player.js +++ b/src/anm/player.js @@ -322,21 +322,6 @@ Player.prototype.load = function(arg1, arg2, arg3, arg4) { callback = arg3; } - if ((player.loadingMode == C.LM_ONPLAY) && - !player._playLock) { // if play lock is set, we should just load an animation normally, since - // it was requested after the call to 'play', or else it was called by user - // FIXME: may be playLock was set by player and user calls this method - // while some animation is already loading - if (player._postponedLoad) throw errors.player(ErrLoc.P.LOAD_WAS_ALREADY_POSTPONED, player); - player._lastReceivedAnimationId = null; - // this kind of postponed call is different from the ones below (_clearPostpones and _postpone), - // since this one is related to loading mode, rather than calling later some methods which - // were called during the process of loading (and were required to be called when it was finished). - player._postponedLoad = [ object, duration, importer, callback ]; - player.stop(); - return; - } - // if player was loading resources already when .load() was called, inside the ._reset() method // postpones will be cleared and loaders cancelled @@ -467,34 +452,6 @@ Player.prototype.play = function(from, speed, stopAfter) { if (player.infiniteDuration) return; // it's ok to skip this call if it's some dynamic animation (FIXME?) } - if ((player.loadingMode === C.LM_ONPLAY) && !player._lastReceivedAnimationId) { - if (player._playLock) return; // we already loading something - // use _postponedLoad with _playLock flag set - // call play when loading was finished - player._playLock = true; - var loadArgs = player._postponedLoad, - playArgs = arguments; - if (!loadArgs) throw errors.player(ErrLoc.P.NO_LOAD_CALL_BEFORE_PLAY, player); - var loadCallback = loadArgs[3]; - var afterLoad = function() { - if (loadCallback) loadCallback.apply(player, arguments); - player._postponedLoad = null; - player._playLock = false; - player._lastReceivedAnimationId = player.anim.id; - Player.prototype.play.apply(player, playArgs); - }; - loadArgs[3] = afterLoad; // substitute callback with our variant which calls the previous one - Player.prototype.load.apply(player, loadArgs); - return; - } - - if ((player.loadingMode === C.LM_ONREQUEST) && - (state.happens === C.RES_LOADING)) { player._postpone('play', arguments); - return; } // if player loads remote resources just now, - // postpone this task and exit. postponed tasks - // will be called when all remote resources were - // finished loading - // reassigns var to ensure proper function is used //__nextFrame = engine.getRequestFrameFunc(); //__stopAnim = engine.getCancelFrameFunc(); @@ -565,8 +522,7 @@ Player.prototype.stop = function() { // postpone this task and exit. postponed tasks // will be called when all remote resources were // finished loading - if ((state.happens === C.RES_LOADING) && - (player.loadingMode === C.LM_ONREQUEST)) { + if (state.happens === C.RES_LOADING) { player._postpone('stop', arguments); return; } @@ -582,8 +538,7 @@ Player.prototype.stop = function() { var anim = player.anim; - if (anim || ((player.loadingMode == C.LM_ONPLAY) && - player._postponedLoad)) { + if (anim) { state.happens = C.STOPPED; player._drawStill(); player.fire(C.S_CHANGE_STATE, C.STOPPED); @@ -613,8 +568,7 @@ Player.prototype.pause = function() { // postpone this task and exit. postponed tasks // will be called when all remote resources were // finished loading - if ((player.state.happens === C.RES_LOADING) && - (player.loadingMode === C.LM_ONREQUEST)) { + if (player.state.happens === C.RES_LOADING) { player._postpone('pause', arguments); return player; } @@ -731,6 +685,8 @@ Player.prototype._addOpts = function(opts) { this.loadingMode = is.defined(opts.loadingMode) ? opts.loadingMode : this.loadingMode; + this.playingMode = is.defined(opts.playingMode) ? + opts.playingMode : this.playingMode; this.audioEnabled = is.defined(opts.audioEnabled) ? opts.audioEnabled : this.audioEnabled; this.globalVolume = is.defined(opts.volume) ? @@ -886,12 +842,11 @@ Player.prototype.forceRedraw = function() { */ Player.prototype.drawAt = function(time) { if (time === Player.NO_TIME) throw errors.player(ErrLoc.P.PASSED_TIME_VALUE_IS_NO_TIME, this); - if ((this.state.happens === C.RES_LOADING) && - (this.loadingMode === C.LM_ONREQUEST)) { this._postpone('drawAt', arguments); - return; } // if player loads remote resources just now, - // postpone this task and exit. postponed tasks - // will be called when all remote resources were - // finished loading + if (this.state.happens === C.RES_LOADING) { this._postpone('drawAt', arguments); + return; } // if player loads remote resources just now, + // postpone this task and exit. postponed tasks + // will be called when all remote resources were + // finished loading if ((time < 0) || (!this.infiniteDuration && (time > this.anim.duration))) { throw errors.player(utils.strf(ErrLoc.P.PASSED_TIME_NOT_IN_RANGE, [time]), this); } @@ -1377,8 +1332,7 @@ Player.prototype._reset = function() { // clear postponed tasks if player started to load remote resources, // they are not required since new animation is loading in the player now // or it is being detached - if ((this.loadingMode === C.LM_ONREQUEST) && - (state.happens === C.RES_LOADING)) { + if (state.happens === C.RES_LOADING) { this._clearPostpones(); resourceManager.cancel(this.id); } From 0300285d24acd63caf03560e2d4d8eaf44e277d9 Mon Sep 17 00:00:00 2001 From: Ulric Wilfred Date: Fri, 25 Sep 2015 17:59:37 +0200 Subject: [PATCH 16/16] implementing loading modes, p.1 --- src/anm/constants.js | 6 +++++- src/anm/player.js | 44 ++++++++++++++++++++++++++-------------- src/engine/dom-engine.js | 9 ++++---- 3 files changed, 39 insertions(+), 20 deletions(-) diff --git a/src/anm/constants.js b/src/anm/constants.js index a5228d19..2ea816de 100644 --- a/src/anm/constants.js +++ b/src/anm/constants.js @@ -82,18 +82,22 @@ C.LM_ONIDLE = 'onidle'; // waits for pause in user actions (mouse move, clicks, C.LM_DEFAULT = C.LM_RIGHTAWAY; +C.LOADING_MODES = [ C.LM_RIGHTAWAY, C.LM_ONREQUEST, C.LM_ONPLAY, C.LM_ONIDLE ]; + // ### Playing modes /* ---------------- */ // playing modes are overriden if `autoPlay` == `true` -C.LM_ONREQUEST = 'onrequest'; // waits for user to manually call `play` method or press play button +C.PM_ONREQUEST = 'onrequest'; // waits for user to manually call `play` method or press play button C.PM_ONHOVER = 'onhover'; // starts playing animation when user hovered with mouse over the player canvas C.PM_WHENINVIEW = 'wheninview'; // starts loading animation when at least some part of canvas appears in // user's browser viewport C.PM_DEFAULT = C.PM_ONREQUEST; +C.PLAYING_MODES = [ C.PM_ONREQUEST, C.PM_ONHOVER, C.PM_WHENINVIEW ]; + // Element // ----------------------------------------------------------------------------- diff --git a/src/anm/player.js b/src/anm/player.js index 82594092..65fb806a 100644 --- a/src/anm/player.js +++ b/src/anm/player.js @@ -244,6 +244,8 @@ Player.prototype.init = function(elm, opts) { /* TODO: if (this.canvas.hasAttribute('data-url')) */ playerManager.fire(C.S_NEW_PLAYER, this); + this._checkPreparedSource(); // if scene was specified with HTML attributes, + // and loading mode matches, will load it immediately return this; }; @@ -279,6 +281,10 @@ Player.prototype.load = function(arg1, arg2, arg3, arg4) { throw errors.player(ErrLoc.P.COULD_NOT_LOAD_WHILE_PLAYING, player); } + if (this.loadingMode === C.LM_ONPLAY) { + this._postponedLoad = arguments; return; + } + /* object */ /* object, callback */ /* object, importer */ @@ -679,14 +685,17 @@ Player.prototype._addOpts = function(opts) { this.width = opts.width || this.width; this.height = opts.height || this.height; + this.loadingMode = is.defined(opts.loadingMode) + ? (C.LOADING_MODES.indexOf(opts.loadingMode) >= 0) ? opts.loadingMode : C.LM_DEFAULT) + : this.loadingMode; + this.playingMode = is.defined(opts.playingMode) + ? (C.PLAYING_MODES.indexOf(opts.playingMode) >= 0) ? opts.playingMode : C.PM_DEFAULT) + : this.playingMode; + this.ribbonsColor = opts.ribbonsColor || this.ribbonsColor; this.thumbnailSrc = opts.thumbnail || this.thumbnailSrc; - this.loadingMode = is.defined(opts.loadingMode) ? - opts.loadingMode : this.loadingMode; - this.playingMode = is.defined(opts.playingMode) ? - opts.playingMode : this.playingMode; this.audioEnabled = is.defined(opts.audioEnabled) ? opts.audioEnabled : this.audioEnabled; this.globalVolume = is.defined(opts.volume) ? @@ -757,16 +766,9 @@ Player.prototype._checkOpts = function() { // initial state of the player, called from constuctor Player.prototype._postInit = function() { this.stop(); - /* TODO: load some default information into player */ - var to_load = engine.hasUrlToLoad(this.wrapper); - if (!to_load.url) to_load = engine.hasUrlToLoad(this.canvas); - if (to_load.url) { - var importer = null; - if (to_load.importer_id && anm.importers.isAccessible(to_load.importer_id)) { - importer = anm.importers.create(to_load.importer_id); - } - this.load(to_load.url, importer); - } + // this._prepared_src will be null if there is no url to load + this._prepared_src = engine.hasUrlToLoad(this.wrapper); + if (!this._prepared_src) this._prepared_src = engine.hasUrlToLoad(this.canvas); }; /** @@ -1640,7 +1642,7 @@ Player.createState = function(player) { Player.forSnapshot = function(elm_id, snapshot_url, importer, callback, alt_opts) { var player = new Player(); player.init(elm_id, alt_opts); - player.load(snapshot_url, importer, callback); + if (player.loadingMode === C.LM_RIGHTAWAY) player.load(snapshot_url, importer, callback); return player; }; @@ -1686,4 +1688,16 @@ Player.prototype._applyTimeOptionsIfSet = function() { } } +Player.prototype._checkPreparedSource = function() { + if (this._prepared_src && (this.loadingMode === C.LM_RIGHTAWAY)) { + var url = this._prepared_src.url, + importer_id = this._prepared_src.importer_id; + var importer = null; + if (importer_id && anm.importers.isAccessible(importer_id)) { + importer = anm.importers.create(importer_id); + } + this.load(url, importer); + } +} + module.exports = Player; diff --git a/src/engine/dom-engine.js b/src/engine/dom-engine.js index afcdaeae..7b3ba128 100644 --- a/src/engine/dom-engine.js +++ b/src/engine/dom-engine.js @@ -595,10 +595,11 @@ $DE.checkPlayerCanvas = function(cvs) { }; $DE.hasUrlToLoad = function(elm) { - return { - url: elm.getAttribute(URL_ATTR) || elm.getAttribute(SNAPSHOT_URL_ATTR), - importer_id: elm.getAttribute(IMPORTER_ATTR) - }; + var url = elm.getAttribute(URL_ATTR) || elm.getAttribute(SNAPSHOT_URL_ATTR); + var importer_id = elm.getAttribute(IMPORTER_ATTR); + return (url || importer_id) ? { + url: url, importer_id: importer_id + } : null; }; $DE.setTabIndex = function(cvs, idx) {