├── .gitignore ├── Gruntfile.js ├── HISTORY.md ├── LICENSE ├── README.md ├── bower.json ├── bower_components └── assert │ ├── .bower.json │ ├── HISTORY.md │ ├── README.md │ ├── assert.js │ ├── component.json │ └── test │ ├── index.html │ └── test-assert.js ├── dist ├── videojs-playlists.js └── videojs-playlists.min.js ├── example └── index.html ├── lib └── videojs-playlists.js ├── package.json └── test ├── index.html └── videojs-playlists.test.js /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | components 4 | reports 5 | .idea -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | module.exports = function(grunt) { 2 | grunt.initConfig({ 3 | info: grunt.file.readJSON('bower.json'), 4 | meta: { 5 | banner: '/*!\n'+ 6 | ' * <%= info.name %> - <%= info.description %>\n'+ 7 | ' * v<%= info.version %>\n'+ 8 | ' * <%= info.homepage %>\n'+ 9 | ' * copyright <%= info.copyright %> <%= grunt.template.today("yyyy") %>\n'+ 10 | ' * <%= info.license %> License\n'+ 11 | '*/\n' 12 | }, 13 | jshint: { 14 | main: [ 15 | 'Gruntfile.js', 16 | 'bower.json', 17 | 'lib/**/*.js', 18 | 'test/*.js' 19 | ] 20 | }, 21 | concat: { 22 | options: { 23 | banner: '<%= meta.banner %>' 24 | }, 25 | dist: { 26 | src: 'lib/videojs-playlists.js', 27 | dest: 'dist/videojs-playlists.js' 28 | } 29 | }, 30 | uglify: { 31 | options: { 32 | banner: '<%= meta.banner %>' 33 | }, 34 | dist: { 35 | src: 'dist/videojs-playlists.js', 36 | dest: 'dist/videojs-playlists.min.js' 37 | } 38 | }, 39 | watch: { 40 | main: { 41 | files: '<%= jshint.main %>', 42 | tasks: 'default', 43 | options: { 44 | livereload: true 45 | } 46 | }, 47 | examples: { 48 | files: [ 49 | 'example/*' 50 | ], 51 | options: { 52 | livereload: true 53 | } 54 | }, 55 | ci: { 56 | files: [ 57 | 'test/index.html' 58 | ], 59 | tasks: 'default' 60 | } 61 | }, 62 | mocha: { 63 | all: { 64 | src: 'test/index.html', 65 | options: { 66 | run: true 67 | } 68 | } 69 | }, 70 | plato: { 71 | main: { 72 | files: { 73 | 'reports': ['lib/*.js'] 74 | } 75 | } 76 | }, 77 | connect: { 78 | server:{ 79 | port: 8000, 80 | base: '.' 81 | }, 82 | plato: { 83 | port: 8000, 84 | base: 'reports', 85 | options: { 86 | keepalive: true 87 | } 88 | } 89 | } 90 | }); 91 | grunt.loadNpmTasks('grunt-contrib-jshint'); 92 | grunt.loadNpmTasks('grunt-contrib-concat'); 93 | grunt.loadNpmTasks('grunt-contrib-watch'); 94 | grunt.loadNpmTasks('grunt-contrib-uglify'); 95 | grunt.loadNpmTasks('grunt-contrib-connect'); 96 | grunt.loadNpmTasks('grunt-mocha'); 97 | grunt.loadNpmTasks('grunt-plato'); 98 | grunt.registerTask('default', ['jshint', 'concat', 'uglify']); 99 | grunt.registerTask('dev', ['connect:server', 'watch']); 100 | grunt.registerTask('ci', ['connect:server', 'watch:ci']); 101 | grunt.registerTask('reports', ['plato', 'connect:plato']); 102 | }; 103 | -------------------------------------------------------------------------------- /HISTORY.md: -------------------------------------------------------------------------------- 1 | 0.2.0 / 2013-11-20 2 | ================== 3 | 4 | * Fixed issue which avoided the playlist to work with latest versions of videojs 5 | 6 | 7 | 0.1.1 / 2013-08-26 8 | ================== 9 | 10 | * Fixed issue with iOS devices in which the plugin won't work 11 | 12 | 0.1.0 / 2013-07-08 13 | ================== 14 | 15 | * added example, updated gruntfile 16 | * added option to pass in function to get video source (for use with s3 tokens) 17 | * Resume on finish & next/prev 18 | * Addding demo to readme 19 | * removed example in favor of demo 20 | * changed alerts to console.logs 21 | * Created demo 22 | * initial commit 23 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | (The MIT License) 2 | 3 | Copyright (c) 2013 Antonio Laguna 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | 'Software'), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 21 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 22 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | #videojs-playlists 2 | 3 | Sexy Playlists for VideoJS. 4 | 5 | ##Installation 6 | 7 | ###Bower 8 | 9 | `bower install videojs-playlists` 10 | 11 | ###Manual Download 12 | 13 | - [Development]() 14 | - [Production]() 15 | 16 | ##Usage 17 | 18 | ### Initialize playList 19 | 20 | In order to initialize `playList` you need to pass an array of videos with this structure: 21 | 22 | ```js 23 | videos = [ 24 | { 25 | src : [ 26 | 'http://stream.flowplayer.org/bauhaus/624x260.webm', 27 | 'http://stream.flowplayer.org/bauhaus/624x260.mp4', 28 | 'http://stream.flowplayer.org/bauhaus/624x260.ogv' 29 | ], 30 | poster : '', 31 | title : 'Whales' 32 | }, 33 | { 34 | src : [ 35 | 'http://vjs.zencdn.net/v/oceans.mp4', 36 | 'http://vjs.zencdn.net/v/oceans.webm' 37 | ], 38 | poster : 'http://www.videojs.com/img/poster.jpg', 39 | title : 'Ocean' 40 | } 41 | ]; 42 | ``` 43 | 44 | Now, when videos plays they automatically jump to the next one. You also gain a couple of methods 45 | 46 | ### Jump to video 47 | 48 | Use `player.playList(index)` to jump to a video into the playlist. 49 | 50 | ### next 51 | 52 | VideoJS receives a `next()` function which put in place the next video. 53 | 54 | ### prev 55 | 56 | VideoJS receives a `prev()` function which put in place the previous video. 57 | 58 | ### Events 59 | 60 | 61 | 62 | 63 | 64 | 65 |
NameDescription
nextFired when you use the `next()` function or when one video finish and the next starts.
prevFired when you use the `prev()` function.
lastVideoEndedFired when the playlist has finished.
66 | 67 | A [demo](http://belelros.github.io/videojs-playLists/) is now available to showcase what you can create with this plugin. 68 | 69 | ##Pending 70 | 71 | Pass video parameter to `next` and `prev` events. That should need to rewrite the trigger function from videojs since 72 | doesn't allow passing events. 73 | 74 | As a workaround, the `player.pl.current` is updated with the actual index and `player.pl.currentVideo` contains the 75 | video object. 76 | 77 | ##Development 78 | 79 | ###Requirements 80 | 81 | - node and npm 82 | - bower `npm install -g bower` 83 | - grunt `npm install -g grunt-cli` 84 | 85 | ###Setup 86 | 87 | - `npm install` 88 | - `bower install` 89 | 90 | ###Run 91 | 92 | `grunt dev` 93 | 94 | or for just running tests on file changes: 95 | 96 | `grunt ci` 97 | 98 | ###Tests 99 | 100 | `grunt mocha` -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "videojs-playlists", 3 | "description": "Playlists done right for Videojs", 4 | "version": "0.2.0", 5 | "homepage": "", 6 | "license": "MIT", 7 | "copyright": "Antonio Laguna", 8 | "main": "dist/videojs-playlists.js", 9 | "dependencies": { 10 | }, 11 | "devDependencies": { 12 | "assert": "*" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /bower_components/assert/.bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "assert", 3 | "version": "0.0.1", 4 | "main": "./assert.js", 5 | "homepage": "https://github.com/jgallen23/assert", 6 | "_release": "0.0.1", 7 | "_resolution": { 8 | "type": "version", 9 | "tag": "0.0.1", 10 | "commit": "d713c8c0d007c22985f92bf4c0aa1ab5688ce187" 11 | }, 12 | "_source": "git://github.com/jgallen23/assert.git", 13 | "_target": "*", 14 | "_originalSource": "assert" 15 | } -------------------------------------------------------------------------------- /bower_components/assert/HISTORY.md: -------------------------------------------------------------------------------- 1 | 2 | 0.0.1 / 2012-12-06 3 | ================== 4 | 5 | * added component.json for bower 6 | * add Object.create comatibile for issue #1 7 | * update assert to v0.8.1 8 | * fix README.md 9 | * to make it perfect: s/the both-sides/both sides/ 10 | * Assert to assert 11 | * updates README 12 | * add README.md 13 | * add index.html for browser test 14 | * fix replace for browser 15 | * mod copyright 16 | * add common object instead of require('common') 17 | * semmicolon 18 | * add test-assert.js 19 | * remove case of buffer(browser dosen't has) 20 | * define util.inherits instead of require('util') 21 | * export assert to global or module 22 | * modefy license 23 | * initial commit 24 | -------------------------------------------------------------------------------- /bower_components/assert/README.md: -------------------------------------------------------------------------------- 1 | # assert.js 2 | 3 | assert.js is a port of the Node.js standard assertion library for the browser. 4 | The original code and tests are from Node.js, and have been modified to be browser compatible. 5 | 6 | For example, you can use it with [Mocha](http://visionmedia.github.com/mocha/) to perform tests 7 | on the **both sides** (server-side and client-side). Mocha does not supply it's own assertion library. 8 | 9 | ## run the same tests on both the client-side and server-side 10 | 11 | You can use the standard assert module when running mocha on Node.js. 12 | 13 | The same tests will run in the browser if you use this library. 14 | 15 | ## how to use 16 | 17 | ```html 18 | 19 | 20 | 21 | ``` 22 | 23 | ## running test of this library 24 | 25 | ### browser 26 | open ```test/index.html``` in your browser, 27 | and see the console. 28 | 29 | ### node.js 30 | 31 | ```shell 32 | > node test/test-assert.js 33 | All OK 34 | ``` 35 | 36 | ## license 37 | 38 | MIT (same as Node.js) -------------------------------------------------------------------------------- /bower_components/assert/assert.js: -------------------------------------------------------------------------------- 1 | // http://wiki.commonjs.org/wiki/Unit_Testing/1.0 2 | // 3 | // THIS IS NOT TESTED NOR LIKELY TO WORK OUTSIDE V8! 4 | // 5 | // Copyright (c) 2011 Jxck 6 | // 7 | // Originally from node.js (http://nodejs.org) 8 | // Copyright Joyent, Inc. 9 | // 10 | // Permission is hereby granted, free of charge, to any person obtaining a copy 11 | // of this software and associated documentation files (the 'Software'), to 12 | // deal in the Software without restriction, including without limitation the 13 | // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 14 | // sell copies of the Software, and to permit persons to whom the Software is 15 | // furnished to do so, subject to the following conditions: 16 | // 17 | // The above copyright notice and this permission notice shall be included in 18 | // all copies or substantial portions of the Software. 19 | // 20 | // THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | // AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 24 | // ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 25 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 26 | 27 | (function(global) { 28 | 29 | // Object.create compatible in IE 30 | var create = Object.create || function(p) { 31 | if (!p) throw Error('no type'); 32 | function f() {}; 33 | f.prototype = p; 34 | return new f(); 35 | }; 36 | 37 | // UTILITY 38 | var util = { 39 | inherits: function(ctor, superCtor) { 40 | ctor.super_ = superCtor; 41 | ctor.prototype = create(superCtor.prototype, { 42 | constructor: { 43 | value: ctor, 44 | enumerable: false, 45 | writable: true, 46 | configurable: true 47 | } 48 | }); 49 | } 50 | }; 51 | 52 | var pSlice = Array.prototype.slice; 53 | 54 | // 1. The assert module provides functions that throw 55 | // AssertionError's when particular conditions are not met. The 56 | // assert module must conform to the following interface. 57 | 58 | var assert = ok; 59 | 60 | global['assert'] = assert; 61 | 62 | if (typeof module === 'object' && typeof module.exports === 'object') { 63 | module.exports = assert; 64 | }; 65 | 66 | // 2. The AssertionError is defined in assert. 67 | // new assert.AssertionError({ message: message, 68 | // actual: actual, 69 | // expected: expected }) 70 | 71 | assert.AssertionError = function AssertionError(options) { 72 | this.name = 'AssertionError'; 73 | this.message = options.message; 74 | this.actual = options.actual; 75 | this.expected = options.expected; 76 | this.operator = options.operator; 77 | var stackStartFunction = options.stackStartFunction || fail; 78 | 79 | if (Error.captureStackTrace) { 80 | Error.captureStackTrace(this, stackStartFunction); 81 | } 82 | }; 83 | util.inherits(assert.AssertionError, Error); 84 | 85 | function replacer(key, value) { 86 | if (value === undefined) { 87 | return '' + value; 88 | } 89 | if (typeof value === 'number' && (isNaN(value) || !isFinite(value))) { 90 | return value.toString(); 91 | } 92 | if (typeof value === 'function' || value instanceof RegExp) { 93 | return value.toString(); 94 | } 95 | return value; 96 | } 97 | 98 | function truncate(s, n) { 99 | if (typeof s == 'string') { 100 | return s.length < n ? s : s.slice(0, n); 101 | } else { 102 | return s; 103 | } 104 | } 105 | 106 | assert.AssertionError.prototype.toString = function() { 107 | if (this.message) { 108 | return [this.name + ':', this.message].join(' '); 109 | } else { 110 | return [ 111 | this.name + ':', 112 | truncate(JSON.stringify(this.actual, replacer), 128), 113 | this.operator, 114 | truncate(JSON.stringify(this.expected, replacer), 128) 115 | ].join(' '); 116 | } 117 | }; 118 | 119 | // assert.AssertionError instanceof Error 120 | 121 | assert.AssertionError.__proto__ = Error.prototype; 122 | 123 | // At present only the three keys mentioned above are used and 124 | // understood by the spec. Implementations or sub modules can pass 125 | // other keys to the AssertionError's constructor - they will be 126 | // ignored. 127 | 128 | // 3. All of the following functions must throw an AssertionError 129 | // when a corresponding condition is not met, with a message that 130 | // may be undefined if not provided. All assertion methods provide 131 | // both the actual and expected values to the assertion error for 132 | // display purposes. 133 | 134 | function fail(actual, expected, message, operator, stackStartFunction) { 135 | throw new assert.AssertionError({ 136 | message: message, 137 | actual: actual, 138 | expected: expected, 139 | operator: operator, 140 | stackStartFunction: stackStartFunction 141 | }); 142 | } 143 | 144 | // EXTENSION! allows for well behaved errors defined elsewhere. 145 | assert.fail = fail; 146 | 147 | // 4. Pure assertion tests whether a value is truthy, as determined 148 | // by !!guard. 149 | // assert.ok(guard, message_opt); 150 | // This statement is equivalent to assert.equal(true, !!guard, 151 | // message_opt);. To test strictly for the value true, use 152 | // assert.strictEqual(true, guard, message_opt);. 153 | 154 | function ok(value, message) { 155 | if (!!!value) fail(value, true, message, '==', assert.ok); 156 | } 157 | assert.ok = ok; 158 | 159 | // 5. The equality assertion tests shallow, coercive equality with 160 | // ==. 161 | // assert.equal(actual, expected, message_opt); 162 | 163 | assert.equal = function equal(actual, expected, message) { 164 | if (actual != expected) fail(actual, expected, message, '==', assert.equal); 165 | }; 166 | 167 | // 6. The non-equality assertion tests for whether two objects are not equal 168 | // with != assert.notEqual(actual, expected, message_opt); 169 | 170 | assert.notEqual = function notEqual(actual, expected, message) { 171 | if (actual == expected) { 172 | fail(actual, expected, message, '!=', assert.notEqual); 173 | } 174 | }; 175 | 176 | // 7. The equivalence assertion tests a deep equality relation. 177 | // assert.deepEqual(actual, expected, message_opt); 178 | 179 | assert.deepEqual = function deepEqual(actual, expected, message) { 180 | if (!_deepEqual(actual, expected)) { 181 | fail(actual, expected, message, 'deepEqual', assert.deepEqual); 182 | } 183 | }; 184 | 185 | function _deepEqual(actual, expected) { 186 | // 7.1. All identical values are equivalent, as determined by ===. 187 | if (actual === expected) { 188 | return true; 189 | 190 | // } else if (Buffer.isBuffer(actual) && Buffer.isBuffer(expected)) { 191 | // if (actual.length != expected.length) return false; 192 | // 193 | // for (var i = 0; i < actual.length; i++) { 194 | // if (actual[i] !== expected[i]) return false; 195 | // } 196 | // 197 | // return true; 198 | // 199 | // 7.2. If the expected value is a Date object, the actual value is 200 | // equivalent if it is also a Date object that refers to the same time. 201 | } else if (actual instanceof Date && expected instanceof Date) { 202 | return actual.getTime() === expected.getTime(); 203 | 204 | // 7.3 If the expected value is a RegExp object, the actual value is 205 | // equivalent if it is also a RegExp object with the same source and 206 | // properties (`global`, `multiline`, `lastIndex`, `ignoreCase`). 207 | } else if (actual instanceof RegExp && expected instanceof RegExp) { 208 | return actual.source === expected.source && 209 | actual.global === expected.global && 210 | actual.multiline === expected.multiline && 211 | actual.lastIndex === expected.lastIndex && 212 | actual.ignoreCase === expected.ignoreCase; 213 | 214 | // 7.4. Other pairs that do not both pass typeof value == 'object', 215 | // equivalence is determined by ==. 216 | } else if (typeof actual != 'object' && typeof expected != 'object') { 217 | return actual == expected; 218 | 219 | // 7.5 For all other Object pairs, including Array objects, equivalence is 220 | // determined by having the same number of owned properties (as verified 221 | // with Object.prototype.hasOwnProperty.call), the same set of keys 222 | // (although not necessarily the same order), equivalent values for every 223 | // corresponding key, and an identical 'prototype' property. Note: this 224 | // accounts for both named and indexed properties on Arrays. 225 | } else { 226 | return objEquiv(actual, expected); 227 | } 228 | } 229 | 230 | function isUndefinedOrNull(value) { 231 | return value === null || value === undefined; 232 | } 233 | 234 | function isArguments(object) { 235 | return Object.prototype.toString.call(object) == '[object Arguments]'; 236 | } 237 | 238 | function objEquiv(a, b) { 239 | if (isUndefinedOrNull(a) || isUndefinedOrNull(b)) 240 | return false; 241 | // an identical 'prototype' property. 242 | if (a.prototype !== b.prototype) return false; 243 | //~~~I've managed to break Object.keys through screwy arguments passing. 244 | // Converting to array solves the problem. 245 | if (isArguments(a)) { 246 | if (!isArguments(b)) { 247 | return false; 248 | } 249 | a = pSlice.call(a); 250 | b = pSlice.call(b); 251 | return _deepEqual(a, b); 252 | } 253 | try { 254 | var ka = Object.keys(a), 255 | kb = Object.keys(b), 256 | key, i; 257 | } catch (e) {//happens when one is a string literal and the other isn't 258 | return false; 259 | } 260 | // having the same number of owned properties (keys incorporates 261 | // hasOwnProperty) 262 | if (ka.length != kb.length) 263 | return false; 264 | //the same set of keys (although not necessarily the same order), 265 | ka.sort(); 266 | kb.sort(); 267 | //~~~cheap key test 268 | for (i = ka.length - 1; i >= 0; i--) { 269 | if (ka[i] != kb[i]) 270 | return false; 271 | } 272 | //equivalent values for every corresponding key, and 273 | //~~~possibly expensive deep test 274 | for (i = ka.length - 1; i >= 0; i--) { 275 | key = ka[i]; 276 | if (!_deepEqual(a[key], b[key])) return false; 277 | } 278 | return true; 279 | } 280 | 281 | // 8. The non-equivalence assertion tests for any deep inequality. 282 | // assert.notDeepEqual(actual, expected, message_opt); 283 | 284 | assert.notDeepEqual = function notDeepEqual(actual, expected, message) { 285 | if (_deepEqual(actual, expected)) { 286 | fail(actual, expected, message, 'notDeepEqual', assert.notDeepEqual); 287 | } 288 | }; 289 | 290 | // 9. The strict equality assertion tests strict equality, as determined by ===. 291 | // assert.strictEqual(actual, expected, message_opt); 292 | 293 | assert.strictEqual = function strictEqual(actual, expected, message) { 294 | if (actual !== expected) { 295 | fail(actual, expected, message, '===', assert.strictEqual); 296 | } 297 | }; 298 | 299 | // 10. The strict non-equality assertion tests for strict inequality, as 300 | // determined by !==. assert.notStrictEqual(actual, expected, message_opt); 301 | 302 | assert.notStrictEqual = function notStrictEqual(actual, expected, message) { 303 | if (actual === expected) { 304 | fail(actual, expected, message, '!==', assert.notStrictEqual); 305 | } 306 | }; 307 | 308 | function expectedException(actual, expected) { 309 | if (!actual || !expected) { 310 | return false; 311 | } 312 | 313 | if (expected instanceof RegExp) { 314 | return expected.test(actual); 315 | } else if (actual instanceof expected) { 316 | return true; 317 | } else if (expected.call({}, actual) === true) { 318 | return true; 319 | } 320 | 321 | return false; 322 | } 323 | 324 | function _throws(shouldThrow, block, expected, message) { 325 | var actual; 326 | 327 | if (typeof expected === 'string') { 328 | message = expected; 329 | expected = null; 330 | } 331 | 332 | try { 333 | block(); 334 | } catch (e) { 335 | actual = e; 336 | } 337 | 338 | message = (expected && expected.name ? ' (' + expected.name + ').' : '.') + 339 | (message ? ' ' + message : '.'); 340 | 341 | if (shouldThrow && !actual) { 342 | fail('Missing expected exception' + message); 343 | } 344 | 345 | if (!shouldThrow && expectedException(actual, expected)) { 346 | fail('Got unwanted exception' + message); 347 | } 348 | 349 | if ((shouldThrow && actual && expected && 350 | !expectedException(actual, expected)) || (!shouldThrow && actual)) { 351 | throw actual; 352 | } 353 | } 354 | 355 | // 11. Expected to throw an error: 356 | // assert.throws(block, Error_opt, message_opt); 357 | 358 | assert.throws = function(block, /*optional*/error, /*optional*/message) { 359 | _throws.apply(this, [true].concat(pSlice.call(arguments))); 360 | }; 361 | 362 | // EXTENSION! This is annoying to write outside this module. 363 | assert.doesNotThrow = function(block, /*optional*/error, /*optional*/message) { 364 | _throws.apply(this, [false].concat(pSlice.call(arguments))); 365 | }; 366 | 367 | assert.ifError = function(err) { if (err) {throw err;}}; 368 | 369 | })(this); 370 | 371 | -------------------------------------------------------------------------------- /bower_components/assert/component.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "assert", 3 | "version": "0.0.1", 4 | "main": "./assert.js" 5 | } 6 | -------------------------------------------------------------------------------- /bower_components/assert/test/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Test Assert 5 | 6 | 7 | 8 | 9 |

Test Assert

10 |

Please see the console output

11 |

If all test has passed, you can see All OK

12 | 13 | 14 | -------------------------------------------------------------------------------- /bower_components/assert/test/test-assert.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2011 Jxck 2 | // 3 | // Originally from node.js (http://nodejs.org) 4 | // Copyright Joyent, Inc. and other Node contributors. 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a 7 | // copy of this software and associated documentation files (the 8 | // "Software"), to deal in the Software without restriction, including 9 | // without limitation the rights to use, copy, modify, merge, publish, 10 | // distribute, sublicense, and/or sell copies of the Software, and to permit 11 | // persons to whom the Software is furnished to do so, subject to the 12 | // following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included 15 | // in all copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 18 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 20 | // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 21 | // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 22 | // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 23 | // USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | 25 | if(typeof require === 'function') { 26 | var assert = require('../assert'); 27 | var a = require('../assert'); 28 | } else { 29 | var a = assert; 30 | }; 31 | 32 | var common = { 33 | protoCtrChain: function(o) { 34 | var result = []; 35 | for (; o; o = o.__proto__) { result.push(o.constructor); } 36 | return result.join(); 37 | }, 38 | indirectInstanceOf: function(obj, cls) { 39 | if (obj instanceof cls) { return true; } 40 | var clsChain = protoCtrChain(cls.prototype); 41 | var objChain = protoCtrChain(obj); 42 | return objChain.slice(-clsChain.length) === clsChain; 43 | } 44 | }; 45 | 46 | function makeBlock(f) { 47 | var args = Array.prototype.slice.call(arguments, 1); 48 | return function() { 49 | return f.apply(this, args); 50 | }; 51 | } 52 | 53 | assert.ok(common.indirectInstanceOf(a.AssertionError.prototype, Error), 54 | 'a.AssertionError instanceof Error'); 55 | 56 | assert.throws(makeBlock(a, false), a.AssertionError, 'ok(false)'); 57 | 58 | assert.doesNotThrow(makeBlock(a, true), a.AssertionError, 'ok(true)'); 59 | 60 | assert.doesNotThrow(makeBlock(a, 'test', 'ok(\'test\')')); 61 | 62 | assert.throws(makeBlock(a.ok, false), 63 | a.AssertionError, 'ok(false)'); 64 | 65 | assert.doesNotThrow(makeBlock(a.ok, true), 66 | a.AssertionError, 'ok(true)'); 67 | 68 | assert.doesNotThrow(makeBlock(a.ok, 'test'), 'ok(\'test\')'); 69 | 70 | assert.throws(makeBlock(a.equal, true, false), a.AssertionError, 'equal'); 71 | 72 | assert.doesNotThrow(makeBlock(a.equal, null, null), 'equal'); 73 | 74 | assert.doesNotThrow(makeBlock(a.equal, undefined, undefined), 'equal'); 75 | 76 | assert.doesNotThrow(makeBlock(a.equal, null, undefined), 'equal'); 77 | 78 | assert.doesNotThrow(makeBlock(a.equal, true, true), 'equal'); 79 | 80 | assert.doesNotThrow(makeBlock(a.equal, 2, '2'), 'equal'); 81 | 82 | assert.doesNotThrow(makeBlock(a.notEqual, true, false), 'notEqual'); 83 | 84 | assert.throws(makeBlock(a.notEqual, true, true), 85 | a.AssertionError, 'notEqual'); 86 | 87 | assert.throws(makeBlock(a.strictEqual, 2, '2'), 88 | a.AssertionError, 'strictEqual'); 89 | 90 | assert.throws(makeBlock(a.strictEqual, null, undefined), 91 | a.AssertionError, 'strictEqual'); 92 | 93 | assert.doesNotThrow(makeBlock(a.notStrictEqual, 2, '2'), 'notStrictEqual'); 94 | 95 | // deepEquals joy! 96 | // 7.2 97 | assert.doesNotThrow(makeBlock(a.deepEqual, new Date(2000, 3, 14), 98 | new Date(2000, 3, 14)), 'deepEqual date'); 99 | 100 | assert.throws(makeBlock(a.deepEqual, new Date(), new Date(2000, 3, 14)), 101 | a.AssertionError, 102 | 'deepEqual date'); 103 | 104 | // 7.3 105 | assert.doesNotThrow(makeBlock(a.deepEqual, /a/, /a/)); 106 | assert.doesNotThrow(makeBlock(a.deepEqual, /a/g, /a/g)); 107 | assert.doesNotThrow(makeBlock(a.deepEqual, /a/i, /a/i)); 108 | assert.doesNotThrow(makeBlock(a.deepEqual, /a/m, /a/m)); 109 | assert.doesNotThrow(makeBlock(a.deepEqual, /a/igm, /a/igm)); 110 | assert.throws(makeBlock(a.deepEqual, /ab/, /a/)); 111 | assert.throws(makeBlock(a.deepEqual, /a/g, /a/)); 112 | assert.throws(makeBlock(a.deepEqual, /a/i, /a/)); 113 | assert.throws(makeBlock(a.deepEqual, /a/m, /a/)); 114 | assert.throws(makeBlock(a.deepEqual, /a/igm, /a/im)); 115 | 116 | var re1 = /a/; 117 | re1.lastIndex = 3; 118 | assert.throws(makeBlock(a.deepEqual, re1, /a/)); 119 | 120 | 121 | // 7.4 122 | assert.doesNotThrow(makeBlock(a.deepEqual, 4, '4'), 'deepEqual == check'); 123 | assert.doesNotThrow(makeBlock(a.deepEqual, true, 1), 'deepEqual == check'); 124 | assert.throws(makeBlock(a.deepEqual, 4, '5'), 125 | a.AssertionError, 126 | 'deepEqual == check'); 127 | 128 | // 7.5 129 | // having the same number of owned properties && the same set of keys 130 | assert.doesNotThrow(makeBlock(a.deepEqual, {a: 4}, {a: 4})); 131 | assert.doesNotThrow(makeBlock(a.deepEqual, {a: 4, b: '2'}, {a: 4, b: '2'})); 132 | assert.doesNotThrow(makeBlock(a.deepEqual, [4], ['4'])); 133 | assert.throws(makeBlock(a.deepEqual, {a: 4}, {a: 4, b: true}), 134 | a.AssertionError); 135 | assert.doesNotThrow(makeBlock(a.deepEqual, ['a'], {0: 'a'})); 136 | //(although not necessarily the same order), 137 | assert.doesNotThrow(makeBlock(a.deepEqual, {a: 4, b: '1'}, {b: '1', a: 4})); 138 | var a1 = [1, 2, 3]; 139 | var a2 = [1, 2, 3]; 140 | a1.a = 'test'; 141 | a1.b = true; 142 | a2.b = true; 143 | a2.a = 'test'; 144 | assert.throws(makeBlock(a.deepEqual, Object.keys(a1), Object.keys(a2)), 145 | a.AssertionError); 146 | assert.doesNotThrow(makeBlock(a.deepEqual, a1, a2)); 147 | 148 | // having an identical prototype property 149 | var nbRoot = { 150 | toString: function() { return this.first + ' ' + this.last; } 151 | }; 152 | 153 | function nameBuilder(first, last) { 154 | this.first = first; 155 | this.last = last; 156 | return this; 157 | } 158 | nameBuilder.prototype = nbRoot; 159 | 160 | function nameBuilder2(first, last) { 161 | this.first = first; 162 | this.last = last; 163 | return this; 164 | } 165 | nameBuilder2.prototype = nbRoot; 166 | 167 | var nb1 = new nameBuilder('Ryan', 'Dahl'); 168 | var nb2 = new nameBuilder2('Ryan', 'Dahl'); 169 | 170 | assert.doesNotThrow(makeBlock(a.deepEqual, nb1, nb2)); 171 | 172 | nameBuilder2.prototype = Object; 173 | nb2 = new nameBuilder2('Ryan', 'Dahl'); 174 | assert.throws(makeBlock(a.deepEqual, nb1, nb2), a.AssertionError); 175 | 176 | // String literal + object blew up my implementation... 177 | assert.throws(makeBlock(a.deepEqual, 'a', {}), a.AssertionError); 178 | 179 | // Testing the throwing 180 | function thrower(errorConstructor) { 181 | throw new errorConstructor('test'); 182 | } 183 | var aethrow = makeBlock(thrower, a.AssertionError); 184 | aethrow = makeBlock(thrower, a.AssertionError); 185 | 186 | // the basic calls work 187 | assert.throws(makeBlock(thrower, a.AssertionError), 188 | a.AssertionError, 'message'); 189 | assert.throws(makeBlock(thrower, a.AssertionError), a.AssertionError); 190 | assert.throws(makeBlock(thrower, a.AssertionError)); 191 | 192 | // if not passing an error, catch all. 193 | assert.throws(makeBlock(thrower, TypeError)); 194 | 195 | // when passing a type, only catch errors of the appropriate type 196 | var threw = false; 197 | try { 198 | a.throws(makeBlock(thrower, TypeError), a.AssertionError); 199 | } catch (e) { 200 | threw = true; 201 | assert.ok(e instanceof TypeError, 'type'); 202 | } 203 | assert.equal(true, threw, 204 | 'a.throws with an explicit error is eating extra errors', 205 | a.AssertionError); 206 | threw = false; 207 | 208 | // doesNotThrow should pass through all errors 209 | try { 210 | a.doesNotThrow(makeBlock(thrower, TypeError), a.AssertionError); 211 | } catch (e) { 212 | threw = true; 213 | assert.ok(e instanceof TypeError); 214 | } 215 | assert.equal(true, threw, 216 | 'a.doesNotThrow with an explicit error is eating extra errors'); 217 | 218 | // key difference is that throwing our correct error makes an assertion error 219 | try { 220 | a.doesNotThrow(makeBlock(thrower, TypeError), TypeError); 221 | } catch (e) { 222 | threw = true; 223 | assert.ok(e instanceof a.AssertionError); 224 | } 225 | assert.equal(true, threw, 226 | 'a.doesNotThrow is not catching type matching errors'); 227 | 228 | assert.throws(function() {assert.ifError(new Error('test error'))}); 229 | assert.doesNotThrow(function() {assert.ifError(null)}); 230 | assert.doesNotThrow(function() {assert.ifError()}); 231 | 232 | // make sure that validating using constructor really works 233 | threw = false; 234 | try { 235 | assert.throws( 236 | function() { 237 | throw ({}); 238 | }, 239 | Array 240 | ); 241 | } catch (e) { 242 | threw = true; 243 | } 244 | assert.ok(threw, 'wrong constructor validation'); 245 | 246 | // use a RegExp to validate error message 247 | a.throws(makeBlock(thrower, TypeError), /test/); 248 | 249 | // use a fn to validate error object 250 | a.throws(makeBlock(thrower, TypeError), function(err) { 251 | if ((err instanceof TypeError) && /test/.test(err)) { 252 | return true; 253 | } 254 | }); 255 | 256 | 257 | // GH-207. Make sure deepEqual doesn't loop forever on circular refs 258 | 259 | var b = {}; 260 | b.b = b; 261 | 262 | var c = {}; 263 | c.b = c; 264 | 265 | var gotError = false; 266 | try { 267 | assert.deepEqual(b, c); 268 | } catch (e) { 269 | gotError = true; 270 | } 271 | 272 | console.log('All OK'); 273 | assert.ok(gotError); 274 | 275 | 276 | // #217 277 | function testAssertionMessage(actual, expected) { 278 | try { 279 | assert.equal(actual, ''); 280 | } catch (e) { 281 | assert.equal(e.toString(), 282 | ['AssertionError:', expected, '==', '""'].join(' ')); 283 | } 284 | } 285 | testAssertionMessage(undefined, '"undefined"'); 286 | testAssertionMessage(null, 'null'); 287 | testAssertionMessage(true, 'true'); 288 | testAssertionMessage(false, 'false'); 289 | testAssertionMessage(0, '0'); 290 | testAssertionMessage(100, '100'); 291 | testAssertionMessage(NaN, '"NaN"'); 292 | testAssertionMessage(Infinity, '"Infinity"'); 293 | testAssertionMessage(-Infinity, '"-Infinity"'); 294 | testAssertionMessage('', '""'); 295 | testAssertionMessage('foo', '"foo"'); 296 | testAssertionMessage([], '[]'); 297 | testAssertionMessage([1, 2, 3], '[1,2,3]'); 298 | testAssertionMessage(/a/, '"/a/"'); 299 | testAssertionMessage(/abc/gim, '"/abc/gim"'); 300 | testAssertionMessage(function f() {}, '"function f() {}"'); 301 | testAssertionMessage({}, '{}'); 302 | testAssertionMessage({a: undefined, b: null}, '{"a":"undefined","b":null}'); 303 | testAssertionMessage({a: NaN, b: Infinity, c: -Infinity}, 304 | '{"a":"NaN","b":"Infinity","c":"-Infinity"}'); 305 | 306 | -------------------------------------------------------------------------------- /dist/videojs-playlists.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * videojs-playlists - Playlists done right for Videojs 3 | * v0.1.1 4 | * 5 | * copyright Antonio Laguna 2013 6 | * MIT License 7 | */ 8 | //videojs-playlists.js 9 | function playList(options,arg){ 10 | var player = this; 11 | player.pl = player.pl || {}; 12 | var index = parseInt(options,10); 13 | 14 | player.pl._guessVideoType = function(video){ 15 | var videoTypes = { 16 | 'webm' : 'video/webm', 17 | 'mp4' : 'video/mp4', 18 | 'ogv' : 'video/ogg' 19 | }; 20 | var extension = video.split('.').pop(); 21 | 22 | return videoTypes[extension] || ''; 23 | }; 24 | 25 | player.pl.init = function(videos, options) { 26 | options = options || {}; 27 | player.pl.videos = []; 28 | player.pl.current = 0; 29 | player.on('ended', player.pl._videoEnd); 30 | 31 | if (options.getVideoSource) { 32 | player.pl.getVideoSource = options.getVideoSource; 33 | } 34 | 35 | player.pl._addVideos(videos); 36 | }; 37 | 38 | player.pl._updatePoster = function(posterURL) { 39 | player.poster(posterURL); 40 | player.removeChild(player.posterImage); 41 | player.posterImage = player.addChild("posterImage"); 42 | }; 43 | 44 | player.pl._addVideos = function(videos){ 45 | for (var i = 0, length = videos.length; i < length; i++){ 46 | var aux = []; 47 | for (var j = 0, len = videos[i].src.length; j < len; j++){ 48 | aux.push({ 49 | type : player.pl._guessVideoType(videos[i].src[j]), 50 | src : videos[i].src[j] 51 | }); 52 | } 53 | videos[i].src = aux; 54 | player.pl.videos.push(videos[i]); 55 | } 56 | }; 57 | 58 | player.pl._nextPrev = function(func){ 59 | var comparison, addendum; 60 | 61 | if (func === 'next'){ 62 | comparison = player.pl.videos.length -1; 63 | addendum = 1; 64 | } 65 | else { 66 | comparison = 0; 67 | addendum = -1; 68 | } 69 | 70 | if (player.pl.current !== comparison){ 71 | var newIndex = player.pl.current + addendum; 72 | player.pl._setVideo(newIndex); 73 | player.trigger(func, [player.pl.videos[newIndex]]); 74 | } 75 | }; 76 | 77 | player.pl._setVideo = function(index){ 78 | if (index < player.pl.videos.length){ 79 | player.pl.current = index; 80 | player.pl.currentVideo = player.pl.videos[index]; 81 | 82 | if (!player.paused()){ 83 | player.pl._resumeVideo(); 84 | } 85 | 86 | if (player.pl.getVideoSource) { 87 | player.pl.getVideoSource(player.pl.videos[index], function(src, poster) { 88 | player.pl._setVideoSource(src, poster); 89 | }); 90 | } else { 91 | player.pl._setVideoSource(player.pl.videos[index].src, player.pl.videos[index].poster); 92 | } 93 | } 94 | }; 95 | 96 | player.pl._setVideoSource = function(src, poster) { 97 | player.src(src); 98 | player.pl._updatePoster(poster); 99 | }; 100 | 101 | player.pl._resumeVideo = function(){ 102 | player.one('loadstart',function(){ 103 | player.play(); 104 | }); 105 | }; 106 | 107 | player.pl._videoEnd = function(){ 108 | if (player.pl.current === player.pl.videos.length -1){ 109 | player.trigger('lastVideoEnded'); 110 | } 111 | else { 112 | player.pl._resumeVideo(); 113 | player.next(); 114 | } 115 | }; 116 | 117 | if (options instanceof Array){ 118 | player.pl.init(options, arg); 119 | player.pl._setVideo(0); 120 | return player; 121 | } 122 | else if (index === index){ // NaN 123 | player.pl._setVideo(index); 124 | return player; 125 | } 126 | else if (typeof options === 'string' && typeof player.pl[options] !== 'undefined'){ 127 | player.pl[options].apply(player); 128 | return player; 129 | } 130 | } 131 | 132 | videojs.Player.prototype.next = function(){ 133 | this.pl._nextPrev('next'); 134 | return this; 135 | }; 136 | videojs.Player.prototype.prev = function(){ 137 | this.pl._nextPrev('prev'); 138 | return this; 139 | }; 140 | 141 | videojs.plugin('playList', playList); 142 | -------------------------------------------------------------------------------- /dist/videojs-playlists.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * videojs-playlists - Playlists done right for Videojs 3 | * v0.1.1 4 | * 5 | * copyright Antonio Laguna 2013 6 | * MIT License 7 | */ 8 | function playList(a,b){var c=this;c.pl=c.pl||{};var d=parseInt(a,10);return c.pl._guessVideoType=function(a){var b={webm:"video/webm",mp4:"video/mp4",ogv:"video/ogg"},c=a.split(".").pop();return b[c]||""},c.pl.init=function(a,b){b=b||{},c.pl.videos=[],c.pl.current=0,c.on("ended",c.pl._videoEnd),b.getVideoSource&&(c.pl.getVideoSource=b.getVideoSource),c.pl._addVideos(a)},c.pl._updatePoster=function(a){c.poster(a),c.removeChild(c.posterImage),c.posterImage=c.addChild("posterImage")},c.pl._addVideos=function(a){for(var b=0,d=a.length;d>b;b++){for(var e=[],f=0,g=a[b].src.length;g>f;f++)e.push({type:c.pl._guessVideoType(a[b].src[f]),src:a[b].src[f]});a[b].src=e,c.pl.videos.push(a[b])}},c.pl._nextPrev=function(a){var b,d;if("next"===a?(b=c.pl.videos.length-1,d=1):(b=0,d=-1),c.pl.current!==b){var e=c.pl.current+d;c.pl._setVideo(e),c.trigger(a,[c.pl.videos[e]])}},c.pl._setVideo=function(a){a 2 | 3 | 4 | 5 | videojs-playlists demo 6 | 7 | 8 | 9 | 10 |

11 | 12 | 13 | 14 | 15 | 16 | 17 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /lib/videojs-playlists.js: -------------------------------------------------------------------------------- 1 | //videojs-playlists.js 2 | function playList(options,arg){ 3 | var player = this; 4 | player.pl = player.pl || {}; 5 | var index = parseInt(options,10); 6 | 7 | player.pl._guessVideoType = function(video){ 8 | var videoTypes = { 9 | 'webm' : 'video/webm', 10 | 'mp4' : 'video/mp4', 11 | 'ogv' : 'video/ogg' 12 | }; 13 | var extension = video.split('.').pop(); 14 | 15 | return videoTypes[extension] || ''; 16 | }; 17 | 18 | player.pl.init = function(videos, options) { 19 | options = options || {}; 20 | player.pl.videos = []; 21 | player.pl.current = 0; 22 | player.on('ended', player.pl._videoEnd); 23 | 24 | if (options.getVideoSource) { 25 | player.pl.getVideoSource = options.getVideoSource; 26 | } 27 | 28 | player.pl._addVideos(videos); 29 | }; 30 | 31 | player.pl._updatePoster = function(posterURL) { 32 | player.poster(posterURL); 33 | player.removeChild(player.posterImage); 34 | player.posterImage = player.addChild("posterImage"); 35 | }; 36 | 37 | player.pl._addVideos = function(videos){ 38 | for (var i = 0, length = videos.length; i < length; i++){ 39 | var aux = []; 40 | for (var j = 0, len = videos[i].src.length; j < len; j++){ 41 | aux.push({ 42 | type : player.pl._guessVideoType(videos[i].src[j]), 43 | src : videos[i].src[j] 44 | }); 45 | } 46 | videos[i].src = aux; 47 | player.pl.videos.push(videos[i]); 48 | } 49 | }; 50 | 51 | player.pl._nextPrev = function(func){ 52 | var comparison, addendum; 53 | 54 | if (func === 'next'){ 55 | comparison = player.pl.videos.length -1; 56 | addendum = 1; 57 | } 58 | else { 59 | comparison = 0; 60 | addendum = -1; 61 | } 62 | 63 | if (player.pl.current !== comparison){ 64 | var newIndex = player.pl.current + addendum; 65 | player.pl._setVideo(newIndex); 66 | player.trigger(func, [player.pl.videos[newIndex]]); 67 | } 68 | }; 69 | 70 | player.pl._setVideo = function(index){ 71 | if (index < player.pl.videos.length){ 72 | player.pl.current = index; 73 | player.pl.currentVideo = player.pl.videos[index]; 74 | 75 | if (!player.paused()){ 76 | player.pl._resumeVideo(); 77 | } 78 | 79 | if (player.pl.getVideoSource) { 80 | player.pl.getVideoSource(player.pl.videos[index], function(src, poster) { 81 | player.pl._setVideoSource(src, poster); 82 | }); 83 | } else { 84 | player.pl._setVideoSource(player.pl.videos[index].src, player.pl.videos[index].poster); 85 | } 86 | } 87 | }; 88 | 89 | player.pl._setVideoSource = function(src, poster) { 90 | player.src(src); 91 | player.pl._updatePoster(poster); 92 | }; 93 | 94 | player.pl._resumeVideo = function(){ 95 | player.one('loadstart',function(){ 96 | player.play(); 97 | }); 98 | }; 99 | 100 | player.pl._videoEnd = function(){ 101 | if (player.pl.current === player.pl.videos.length -1){ 102 | player.trigger('lastVideoEnded'); 103 | } 104 | else { 105 | player.pl._resumeVideo(); 106 | player.next(); 107 | } 108 | }; 109 | 110 | if (options instanceof Array){ 111 | player.pl.init(options, arg); 112 | player.pl._setVideo(0); 113 | return player; 114 | } 115 | else if (index === index){ // NaN 116 | player.pl._setVideo(index); 117 | return player; 118 | } 119 | else if (typeof options === 'string' && typeof player.pl[options] !== 'undefined'){ 120 | player.pl[options].apply(player); 121 | return player; 122 | } 123 | } 124 | 125 | videojs.Player.prototype.next = function(){ 126 | this.pl._nextPrev('next'); 127 | return this; 128 | }; 129 | videojs.Player.prototype.prev = function(){ 130 | this.pl._nextPrev('prev'); 131 | return this; 132 | }; 133 | 134 | videojs.plugin('playList', playList); 135 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "videojs-playlists", 3 | "version": "0.2.0", 4 | "private": true, 5 | "devDependencies": { 6 | "mocha": "1.10.0", 7 | "grunt": "~0.4.1", 8 | "grunt-reloadr": "~0.1.2", 9 | "grunt-mocha": "~0.3.0", 10 | "grunt-contrib-concat": "~0.3.0", 11 | "grunt-contrib-uglify": "~0.2.0", 12 | "grunt-contrib-jshint": "~0.5.4", 13 | "grunt-contrib-connect": "~0.3.0", 14 | "grunt-contrib-watch": "~0.4.3", 15 | "grunt-plato": "~0.2.0" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /test/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | videojs-playlists Tests 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 16 | 17 | 18 |
19 | 22 | 27 | 28 | 29 | 32 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /test/videojs-playlists.test.js: -------------------------------------------------------------------------------- 1 | 2 | suite('videojs-playlists', function() { 3 | var player, videos, index; 4 | suiteSetup(function(){ 5 | videos = [ 6 | { 7 | src : [ 8 | 'http://stream.flowplayer.org/bauhaus/624x260.webm', 9 | 'http://stream.flowplayer.org/bauhaus/624x260.mp4', 10 | 'http://stream.flowplayer.org/bauhaus/624x260.ogv' 11 | ], 12 | poster : 'http://flowplayer.org/media/img/demos/functional.jpg', 13 | title : 'Whales' 14 | }, 15 | { 16 | src : [ 17 | 'http://vjs.zencdn.net/v/oceans.mp4', 18 | 'http://vjs.zencdn.net/v/oceans.webm' 19 | ], 20 | poster : 'http://www.videojs.com/img/poster.jpg', 21 | title : 'Whales' 22 | } 23 | ]; 24 | player = videojs("example_video_1"); 25 | player.playList(videos); 26 | }); 27 | 28 | suite('#_init()', function(){ 29 | test('should have same videos stored after videos have been loaded',function(){ 30 | assert.equal(player.pl.videos.length,videos.length); 31 | }); 32 | test('current video should be 0 after init',function(){ 33 | assert.equal(player.pl.current,0); 34 | }); 35 | }); 36 | suite('#playList(index)',function(){ 37 | suiteSetup(function(){ 38 | index = 1; 39 | player.playList(index); 40 | }); 41 | test('current should change on index passing',function(){ 42 | assert.equal(player.pl.current,index); 43 | }); 44 | test('poster should match video poster',function(){ 45 | var poster = $('.vjs-poster').css('background-image').replace('url(','').replace(')',''); 46 | assert.equal(poster,videos[index].poster); 47 | }); 48 | }); 49 | suite('general',function(){ 50 | setup(function(){ 51 | index = 0; 52 | player.playList(index); 53 | }); 54 | test('next video should autostart',function(done){ 55 | player.one('loadedmetadata',function(){ 56 | var duration = player.duration(); 57 | player.currentTime(duration); 58 | }); 59 | player.one('next',function(){ 60 | done(); 61 | }); 62 | }); 63 | test('last video should fire event',function(done){ 64 | player.playList(1); 65 | player.one('loadedmetadata',function(){ 66 | var duration = player.duration(); 67 | player.currentTime(duration); 68 | }); 69 | player.one('lastVideoEnded',function(){ 70 | done(); 71 | }); 72 | }); 73 | }); 74 | suite('#next()',function(){ 75 | setup(function(){ 76 | index = 0; 77 | player.playList(index); 78 | }); 79 | test('calling next increase the current video index',function(done){ 80 | var currentVideo = player.pl.current; 81 | player.one('next',function(){ 82 | assert.equal(player.pl.current,currentVideo+1); 83 | done(); 84 | }); 85 | player.next(); 86 | }); 87 | test('calling next should fire a \'next\' event',function(done){ 88 | player.one('next',function(){ 89 | done(); 90 | }); 91 | player.next(); 92 | }); 93 | }); 94 | 95 | suite('#prev()',function(){ 96 | setup(function(){ 97 | index = 1; 98 | player.playList(index); 99 | }); 100 | test('calling prev decrease the current video index',function(done){ 101 | var currentVideo = player.pl.current; 102 | player.one('prev',function(){ 103 | assert.equal(player.pl.current,currentVideo-1); 104 | done(); 105 | }); 106 | player.prev(); 107 | }); 108 | test('calling prev should fire a \'prev\' event',function(done){ 109 | player.one('prev',function(){ 110 | done(); 111 | }); 112 | player.prev(); 113 | }); 114 | }); 115 | }); 116 | --------------------------------------------------------------------------------