diff --git a/.gitignore b/.gitignore
new file mode 100644
index 00000000..5455e0c0
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+node_modules
+.sass-cache
diff --git a/.jamignore b/.jamignore
index 7fee82d3..81881c3e 100644
--- a/.jamignore
+++ b/.jamignore
@@ -1 +1,9 @@
-actionscript
+.*
+*.json
+*.md
+*.txt
+artwork
+examples
+lib
+skin/circle.player
+src
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 00000000..4140ac4e
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,94 @@
+# Change Log
+All notable changes to this project will be documented in this file.
+
+## 2.9.2 - 2014-12-14
+### Added
+- New Feature: Implemented Pull Request [Introduce sass skins](https://github.com/happyworm/jPlayer/pull/260) by [nervo](https://github.com/nervo).
+- Bug Fix: Fixed [Example Demo-04 does not work](https://github.com/happyworm/jPlayer/issues/274).
+
+### Changed
+- jPlayer Repository Refactor: The skins source files are now in `src/skin` and the build skins files in the `dist/skin` folder.
+- jPlayer Repository Refactor: All the circle player specific files are now in the `lib/circle-player` folder.
+- jPlayer Repository Refactor: Changed the html `examples` file extentions from `htm` to `html` for @Laurian.
+
+
+## 2.9.1 - 2014-12-09
+### Added
+- Bug Fix: Fixed [Bug in IE8](https://github.com/happyworm/jPlayer/issues/269) reported by Denis.
+
+
+## 2.9.0 - 2014-11-27
+### Added
+- New Feature: Merged Pull Request [Composer support](https://github.com/happyworm/jPlayer/pull/235) by [thormeier](https://github.com/thormeier).
+- New Feature: Merged Pull Request [Add Aurora.js solution](https://github.com/happyworm/jPlayer/pull/246) by [Afterster](https://github.com/Afterster).
+
+
+## 2.8.4 - 2014-11-24
+### Added
+- Bug fix: Merged Pull Request [Add support for native fullscreen api in Internet explorer](https://github.com/happyworm/jPlayer/pull/213) by [mattfawcett](https://github.com/mattfawcett).
+- Bug fix: Merged Pull Request [Chrome on android mobile supports full screen](https://github.com/happyworm/jPlayer/pull/207) by [mattfawcett](https://github.com/mattfawcett).
+- Bug fix: Merged Pull Request [Automatically destroy removed instances](https://github.com/happyworm/jPlayer/pull/150) by [sterlinghirsh](https://github.com/sterlinghirsh).
+
+
+## 2.8.3 - 2014-11-20
+### Added
+- Bug fix: Merged Pull Request to [Return good ratio in Flash players when file loaded but no total length](https://github.com/happyworm/jPlayer/pull/185) by [Afterster](https://github.com/Afterster).
+- Bug fix: Merged Pull Request to [fix for wrong mousemove event on Chrome browser](https://github.com/happyworm/jPlayer/pull/217) by [HobieCat](https://github.com/HobieCat).
+- Bug fix: Merged Pull Request [Browser-compatibility fix for data URI scheme](https://github.com/happyworm/jPlayer/pull/239) by [smidgen](https://github.com/smidgen).
+
+
+## 2.8.2 - 2014-11-19
+### Added
+- Package Fix: The un-minified source is now also added to the `dist` folder.
+- New Feature: Merged Pull Request to add [commonJS support](https://github.com/happyworm/jPlayer/pull/257) by [nervo](https://github.com/nervo).
+- Docs: Added CHANGELOG.md and gave details back to 2.7.1
+- Docs: Added MIGRATION.md as placeholder for migration details.
+- Bug Fix: Fixed Internet Explorer key bindings. Removed [`document.activeElement`](https://developer.mozilla.org/en-US/docs/Web/API/document.activeElement) useage from the key bindings code.
+- New Feature: The key bindings option `key` value may now be a number for [`event.which`](http://api.jquery.com/event.which/) and a string for [`event.key`](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent.key) comparison.
+- Default Options: Added to the default key bindings:
+ - **l** loop toggle
+ - **s** shuffle toggle (playlist)
+
+### Changed
+- Package Fix: Renamed the built folder to `dist`, which is more appropriate than the previous naming of `js`.
+- Default Options: The default key bindings have been changed to:
+ - **p** play/pause toggle
+ - **f** full/restore screen toggle
+ - **m** mute/unmute toggle
+ - **,** decrease volume
+ - **.** increase volume
+ - **[** previous item (playlist)
+ - **]** next item (playlist)
+
+### Removed
+- Package Fix: The old `js` build folder.
+
+
+## 2.8.1 - 2014-11-13
+### Added
+- Skin Fix: Added the CSS3 rule to disable the default Firefox focus highlighting. Fixed both the Blue Monday and Pink Flag skins.
+- Bug Fix: Fixed the media title being displayed in iOS Control Center when there is no GUI title element.
+
+
+## 2.8.0 - 2014-11-11
+### Added
+- ARIA Feature: The `autoBlur` option will now either `blur()` or `focus()`. This helps maintain cross-browser behaviour when a user clicks on a GUI button.
+- New Feature: Added the `noVolume` state class for when the volume controls are being hidden due to the `noVolume option`. This helps GUI design by enabling a CSS rule to hide the volume controls.
+- jPlayer Repository Refactor: Added all download content to the repository and added a grunt build system. The example demos work within the repository, and use the built (minified) jPlayer files.
+
+## Changed
+- Refactor: Renamed the SWF file from `Jplayer.swf` to `jquery.jplayer.swf`
+- Refactor: The Flash `jquery.jplayer.swf` file is now compiled using the Flex compiler in the `grunt-mxmlc` node.js module.
+- Skins: The skins are now designed for ARIA. Please use the options `{useStateClassSkin: true, autoBlur: false}`
+
+## Removed
+- Refactor: Refactored the Flash ActionScript, removing the `TraceOut` class from the `Jplayer.as` code and the `happyworm` package.
+
+
+## 2.7.1 - 2014-09-19
+### Added
+- Bug Fix: Fixed the legacy Android fix to work with latest Android. This moved the android fix code to the `loadeddata` event from the `progress` event.
+
+
+## 2.7.0 - 2014-09-01
+For older changes, view these [release notes](http://jplayer.org/latest/release-notes/).
diff --git a/CLA.md b/CLA.md
new file mode 100644
index 00000000..a1cd89cc
--- /dev/null
+++ b/CLA.md
@@ -0,0 +1,19 @@
+# Contributor License Agreement.
+
+*The document below clarifies the terms under which You, the person listed below, may make "Contributions" (software, bug fixes, configuration changes, documentation, or any other materials) to the project at [https://github.com/happyworm/jPlayer](https://github.com/happyworm/jPlayer). This license protects You, [Happyworm Ltd.](http://happyworm.com) and licensees; it does not change your rights to use your own Contributions for any other purpose. Please complete the following information about You and the Contributions. If you have questions about these terms, please [contact us](mailto:hello@happyworm.com).*
+
+## You and Happyworm Ltd. agree:
+
+You grant to Happyworm Ltd. a non-exclusive, irrevocable, worldwide, royalty-free, sublicenseable, transferable license under all of Your relevant intellectual property rights (including copyright, patent, and any other rights), to use, copy, prepare derivative works of, distribute and publicly perform and display the Contributions on any licensing terms, including without limitation:
+
+open source licenses like the MIT license;
+binary, proprietary, or commercial licenses.
+Except for the licenses granted herein, You reserve all right, title, and interest in and to the Contribution.
+
+You are able to grant us these rights. You represent that You are legally entitled to grant the above license. If Your employer has rights to intellectual property that You create, You represent that You have received permission to make the Contributions on behalf of that employer, or that Your employer has waived such rights for the Contributions.
+
+The Contributions are your original work. You represent that the Contributions are Your original works of authorship, and to Your knowledge, no other person claims, or has the right to claim, any right in any invention or patent related to the Contributions. You also represent that You are not legally obligated, whether by entering into an agreement or otherwise, in any way that conflicts with the terms of this license. For example, if you have signed an agreement requiring you to assign the intellectual property rights in the Contributions to an employer or customer, that would conflict with the terms of this license.
+
+We determine the code that is in our project. You understand that the decision to include the Contribution in any project or source repository is entirely that of the Happyworm Ltd., and this agreement does not guarantee that the Contributions will be included in any product.
+
+No Implied Warranties. Happyworm Ltd. acknowledges that, except as explicitly described in this Agreement, the Contribution is provided on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 00000000..653e5e9c
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,22 @@
+# Contributing
+
+jPlayer is an open source project and you are welcome to contribute.
+
+
+## Code Style
+
+Please follow the [jQuery Style Guide](http://contribute.jquery.org/style-guide/),
+in particular the [JavaScript Style Guide](http://contribute.jquery.org/style-guide/js/).
+
+
+## Pull Requests
+
+* Make a branch in your fork. For example, **patch-1**
+* Apply your changes to that branch
+* **Do not include built files in the Pull Request**
+* Make a Pull Request to the **master** branch
+
+
+## Accept the CLA
+
+Contributors must [sign the Contributor License Agreement](https://www.clahub.com/agreements/happyworm/jPlayer).
diff --git a/Gruntfile.js b/Gruntfile.js
new file mode 100644
index 00000000..8aa774e2
--- /dev/null
+++ b/Gruntfile.js
@@ -0,0 +1,157 @@
+module.exports = function(grunt) {
+
+ grunt.initConfig({
+ pkg: grunt.file.readJSON('package.json'),
+
+ // Using concat to copy the source. In future, we plan to split the source up, making the concat more appropriate.
+
+ concat: {
+ jplayer: {
+ files: {
+ 'dist/jplayer/jquery.jplayer.js': ['src/javascript/jplayer/jquery.jplayer.js']
+ }
+ },
+ playlist: {
+ files: {
+ 'dist/add-on/jplayer.playlist.js': ['src/javascript/add-on/jplayer.playlist.js']
+ }
+ },
+ inspector: {
+ files: {
+ 'dist/add-on/jquery.jplayer.inspector.js': ['src/javascript/add-on/jquery.jplayer.inspector.js']
+ }
+ },
+ popcorn: {
+ files: {
+ 'dist/popcorn/popcorn.jplayer.js': ['src/javascript/popcorn/popcorn.jplayer.js']
+ }
+ }
+ },
+
+ uglify: {
+ options: {
+ // maxLineLen: 0 // Generates the output on a single line
+ },
+ jplayer: {
+ options: {
+ banner: '/*! jPlayer <%= pkg.version %> for jQuery ~ (c) 2009-<%= grunt.template.today("yyyy") %> <%= pkg.organization %> ~ <%= pkg.license %> License */\n'
+ },
+ files: {
+ 'dist/jplayer/jquery.jplayer.min.js': ['dist/jplayer/jquery.jplayer.js']
+ }
+ },
+ playlist: {
+ options: {
+ banner: '/*! jPlayerPlaylist for jPlayer <%= pkg.version %> ~ (c) 2009-<%= grunt.template.today("yyyy") %> <%= pkg.organization %> ~ <%= pkg.license %> License */\n'
+ },
+ files: {
+ 'dist/add-on/jplayer.playlist.min.js': ['dist/add-on/jplayer.playlist.js']
+ }
+ },
+ inspector: {
+ options: {
+ banner: '/*! jPlayerInspector for jPlayer <%= pkg.version %> ~ (c) 2009-<%= grunt.template.today("yyyy") %> <%= pkg.organization %> ~ <%= pkg.license %> License */\n'
+ },
+ files: {
+ 'dist/add-on/jquery.jplayer.inspector.min.js': ['dist/add-on/jquery.jplayer.inspector.js']
+ }
+ },
+ popcorn: {
+ options: {
+ banner: '/*! Popcorn Player for jPlayer <%= pkg.version %> ~ (c) 2009-<%= grunt.template.today("yyyy") %> <%= pkg.organization %> ~ <%= pkg.license %> License */\n'
+ },
+ files: {
+ 'dist/popcorn/popcorn.jplayer.min.js': ['dist/popcorn/popcorn.jplayer.js']
+ }
+ }
+ },
+
+ sass: {
+ options: {
+ sourcemap: 'none',
+ style: 'nested'
+ },
+ "blue.monday": {
+ options: {
+ banner: '/*! Blue Monday Skin for jPlayer <%= pkg.version %> ~ (c) 2009-<%= grunt.template.today("yyyy") %> <%= pkg.organization %> ~ <%= pkg.license %> License */\n'
+ },
+ files: {
+ 'dist/skin/blue.monday/css/jplayer.blue.monday.css': 'src/skin/blue.monday/scss/jplayer.blue.monday.scss'
+ }
+ },
+ "pink.flag": {
+ options: {
+ banner: '/*! Pink Flag Skin for jPlayer <%= pkg.version %> ~ (c) 2009-<%= grunt.template.today("yyyy") %> <%= pkg.organization %> ~ <%= pkg.license %> License */\n'
+ },
+ files: {
+ 'dist/skin/pink.flag/css/jplayer.pink.flag.css': 'src/skin/pink.flag/scss/jplayer.pink.flag.scss'
+ }
+ }
+ },
+
+ cssmin: {
+ skins: {
+ files: {
+ 'dist/skin/blue.monday/css/jplayer.blue.monday.min.css': ['dist/skin/blue.monday/css/jplayer.blue.monday.css'],
+ 'dist/skin/pink.flag/css/jplayer.pink.flag.min.css': ['dist/skin/pink.flag/css/jplayer.pink.flag.css']
+ }
+ },
+ },
+
+ copy: {
+ skins: {
+ files: [
+ {expand: true, cwd: 'src/skin/blue.monday/', src: ['image/**', 'mustache/**'], dest: 'dist/skin/blue.monday/'},
+ {expand: true, cwd: 'src/skin/pink.flag/', src: ['image/**', 'mustache/**'], dest: 'dist/skin/pink.flag/'}
+ ]
+ },
+ },
+
+ jshint: {
+
+ test: {
+ src: [
+ 'Gruntfile.js',
+ '*.json',
+ 'src/javascript/**/*.js',
+ '!**/jquery.jplayer.inspector.js' // The inspector does not pass jshint, and this will be addressed in due course.
+ ]
+ },
+
+ // jQuery linting guide http://contribute.jquery.org/style-guide/js/#linting
+ // docs http://www/jshint.com/docs/
+ options: {
+ // Using .jshintrc files for the options.
+ jshintrc: true
+ }
+ },
+
+ mxmlc: {
+ options: {
+ rawConfig: '-static-link-runtime-shared-libraries=true'
+ },
+ jplayer: {
+ files: {
+ // Compile and give the SWF a filename like the JavaScript filenames. Important as it is the jPlayer code.
+ 'dist/jplayer/jquery.jplayer.swf': ['src/actionscript/Jplayer.as']
+ }
+ }
+ }
+ });
+
+ grunt.loadNpmTasks('grunt-contrib-jshint');
+ grunt.loadNpmTasks('grunt-contrib-concat');
+ grunt.loadNpmTasks('grunt-contrib-copy');
+ grunt.loadNpmTasks('grunt-contrib-cssmin');
+ grunt.loadNpmTasks('grunt-contrib-uglify');
+ grunt.loadNpmTasks('grunt-contrib-sass');
+ grunt.loadNpmTasks('grunt-mxmlc');
+
+ grunt.registerTask('default', ['test', 'build']);
+
+ grunt.registerTask('test', ['jshint']);
+ grunt.registerTask('build', ['js', 'swf', 'css']);
+ grunt.registerTask('js', ['concat', 'uglify']);
+ grunt.registerTask('swf', ['mxmlc']);
+ grunt.registerTask('css', ['sass', 'cssmin', 'copy:skins']);
+};
diff --git a/MIGRATION.md b/MIGRATION.md
new file mode 100644
index 00000000..087fbff3
--- /dev/null
+++ b/MIGRATION.md
@@ -0,0 +1,51 @@
+# Migration
+All notable changes that affect the backwards compatability of this project will be documented in this file.
+
+## 2.9.2 - 2014-12-14
+### Changed
+- jPlayer Repository Refactor: The skins source files are now in `src/skin` and the build skins files in the `dist/skin` folder.
+- jPlayer Repository Refactor: All the circle player specific files are now in the `lib/circle-player` folder.
+- jPlayer Repository Refactor: Changed the html `examples` file extentions from `htm` to `html` for @Laurian.
+
+
+## 2.8.2 - 2014-11-19
+
+The author appologises for breaking the [Semantic Versioning](http://semver.org/) rules again in this update.
+
+### Added
+- Package Fix: The un-minified source is now also added to the `dist` folder.
+
+### Changed
+- Package Fix: Renamed the built folder to `dist`, which is more appropriate than the previous naming of `js`.
+- Default Options: The default key bindings have been changed to:
+ - **p** play/pause toggle
+ - **f** full/restore screen toggle
+ - **m** mute/unmute toggle
+ - **,** decrease volume
+ - **.** increase volume
+ - **[** previous item (playlist)
+ - **]** next item (playlist)
+
+### Removed
+- Package Fix: The old `js` build folder.
+
+
+## 2.8.0 - 2014-11-11
+
+The author appologises for breaking the [Semantic Versioning](http://semver.org/) rules and only raising the `MINOR` version, instead of correctly increamenting the `MAJOR` version.
+
+### Added
+- jPlayer Repository Refactor: Added all download content to the repository and added a grunt build system. The example demos work within the repository, and use the built (minified) jPlayer files.
+
+## Changed
+- Refactor: Renamed the SWF file from `Jplayer.swf` to `jquery.jplayer.swf`
+- Refactor: The Flash `jquery.jplayer.swf` file is now compiled using the Flex compiler in the `grunt-mxmlc` node.js module.
+- Skins: The skins are now designed for ARIA. Please use the options `{useStateClassSkin: true, autoBlur: false}`
+
+## Removed
+- Refactor: Refactored the Flash ActionScript, removing the `TraceOut` class from the `Jplayer.as` code and the `happyworm` package.
+
+
+## 2.7.1 - 2014-09-19
+
+This was the last version that used the old repository structure.
\ No newline at end of file
diff --git a/README.md b/README.md
index a1b30116..4ce7308e 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,7 @@
# [jPlayer](http://jplayer.org/) : HTML5 Audio & Video for [jQuery](http://jquery.com/)
+[Gitter](https://gitter.im/happyworm/jPlayer?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
+
Support for [Zepto](http://zeptojs.com/) 1.0+ compiled with the data module.
## What is jPlayer?
@@ -19,6 +21,32 @@ Support for [Zepto](http://zeptojs.com/) 1.0+ compiled with the data module.
_(*) Optional counterpart formats to increase HTML5 cross-browser support._
+## Bower Install
+* simple install using `bower install jplayer`
+* see
'+(d.visible?"Hide":"Show")+' jPlayer Inspector
jPlayer events that have occurred over the past 1 second:
(Backgrounds: Never occurred Occurred before Occurred Multiple occurrences reset)
Update jPlayer Inspector
This jPlayer instance is running in your browser where:
";for(i=0;i"+c+" solution is",a(this).data("jPlayerInspector").jPlayer.data("jPlayer")[c].used){b+=" being used and will support:";for(format in a(this).data("jPlayerInspector").jPlayer.data("jPlayer")[c].support)a(this).data("jPlayerInspector").jPlayer.data("jPlayer")[c].support[format]&&(b+=" "+format);b+="
"}else b+=" not required
"}b+="
status.formatType = '"+d+"'
",b+=d?"Browser canPlay('"+a.jPlayer.prototype.format[d].codec+"')":"
status.src = '"+a(this).data("jPlayerInspector").jPlayer.data("jPlayer").status.src+"'
status.media = {
";for(prop in a(this).data("jPlayerInspector").jPlayer.data("jPlayer").status.media)b+=" "+prop+": "+a(this).data("jPlayerInspector").jPlayer.data("jPlayer").status.media[prop]+"
";b+="};
",b+="status.videoWidth = '"+a(this).data("jPlayerInspector").jPlayer.data("jPlayer").status.videoWidth+"'",b+=" | status.videoHeight = '"+a(this).data("jPlayerInspector").jPlayer.data("jPlayer").status.videoHeight+"'",b+="status.width = '"+a(this).data("jPlayerInspector").jPlayer.data("jPlayer").status.width+"'",b+=" | status.height = '"+a(this).data("jPlayerInspector").jPlayer.data("jPlayer").status.height+"'",b+="
htmlElement.audio.canPlayType = "+typeof a(this).data("jPlayerInspector").jPlayer.data("jPlayer").htmlElement.audio.canPlayType+"htmlElement.video.canPlayType = "+typeof a(this).data("jPlayerInspector").jPlayer.data("jPlayer").htmlElement.video.canPlayType+""),b+="",b+="This instance is using the constructor options:$('#"+a(this).data("jPlayerInspector").jPlayer.data("jPlayer").internal.self.id+"').jPlayer({
swfPath: '"+a(this).data("jPlayerInspector").jPlayer.jPlayer("option","swfPath")+"',
solution: '"+a(this).data("jPlayerInspector").jPlayer.jPlayer("option","solution")+"',
supplied: '"+a(this).data("jPlayerInspector").jPlayer.jPlayer("option","supplied")+"',
preload: '"+a(this).data("jPlayerInspector").jPlayer.jPlayer("option","preload")+"',
volume: "+a(this).data("jPlayerInspector").jPlayer.jPlayer("option","volume")+",
muted: "+a(this).data("jPlayerInspector").jPlayer.jPlayer("option","muted")+",
backgroundColor: '"+a(this).data("jPlayerInspector").jPlayer.jPlayer("option","backgroundColor")+"',
cssSelectorAncestor: '"+a(this).data("jPlayerInspector").jPlayer.jPlayer("option","cssSelectorAncestor")+"',
cssSelector: {";var e=a(this).data("jPlayerInspector").jPlayer.jPlayer("option","cssSelector");for(prop in e)b+="
"+prop+": '"+a(this).data("jPlayerInspector").jPlayer.jPlayer("option","cssSelector."+prop)+"',";return b=b.slice(0,-1),b+="
},
errorAlerts: "+a(this).data("jPlayerInspector").jPlayer.jPlayer("option","errorAlerts")+",
warningAlerts: "+a(this).data("jPlayerInspector").jPlayer.jPlayer("option","warningAlerts")+"
});
jPlayer is "+(a(this).data("jPlayerInspector").jPlayer.data("jPlayer").status.paused?"paused":"playing")+" at time: "+Math.floor(10*a(this).data("jPlayerInspector").jPlayer.data("jPlayer").status.currentTime)/10+"s. (d: "+Math.floor(10*a(this).data("jPlayerInspector").jPlayer.data("jPlayer").status.duration)/10+"s, sp: "+Math.floor(a(this).data("jPlayerInspector").jPlayer.data("jPlayer").status.seekPercent)+"%, cpr: "+Math.floor(a(this).data("jPlayerInspector").jPlayer.data("jPlayer").status.currentPercentRelative)+"%, cpa: "+Math.floor(a(this).data("jPlayerInspector").jPlayer.data("jPlayer").status.currentPercentAbsolute)+"%)
"),this}};a.fn.jPlayerInspector=function(b){return c[b]?c[b].apply(this,Array.prototype.slice.call(arguments,1)):"object"!=typeof b&&b?void a.error("Method "+b+" does not exist on jQuery.jPlayerInspector"):c.init.apply(this,arguments)}}(jQuery); \ No newline at end of file diff --git a/jquery.jplayer/jquery.jplayer.js b/dist/jplayer/jquery.jplayer.js similarity index 83% rename from jquery.jplayer/jquery.jplayer.js rename to dist/jplayer/jquery.jplayer.js index 69dd8d89..842f31b6 100644 --- a/jquery.jplayer/jquery.jplayer.js +++ b/dist/jplayer/jquery.jplayer.js @@ -2,21 +2,17 @@ * jPlayer Plugin for jQuery JavaScript Library * http://www.jplayer.org * - * Copyright (c) 2009 - 2013 Happyworm Ltd + * Copyright (c) 2009 - 2014 Happyworm Ltd * Licensed under the MIT license. * http://opensource.org/licenses/MIT * * Author: Mark J Panaghiston - * Version: 2.5.2 - * Date: 15th December 2013 + * Version: 2.9.2 + * Date: 14th December 2014 */ -/* Code verified using http://www.jshint.com/ */ -/*jshint asi:false, bitwise:false, boss:false, browser:true, curly:true, debug:false, eqeqeq:true, eqnull:false, evil:false, forin:false, immed:false, jquery:true, laxbreak:false, newcap:true, noarg:true, noempty:true, nonew:true, onevar:false, passfail:false, plusplus:false, regexp:false, undef:true, sub:false, strict:false, white:false, smarttabs:true */ -/*global define:false, ActiveXObject:false, alert:false */ - /* Support for Zepto 1.0 compiled with optional data module. - * For AMD support, you will need to manually switch the 2 lines in the code below. + * For AMD or NODE/CommonJS support, you will need to manually switch the related 2 lines in the code below. * Search terms: "jQuery Switch" and "Zepto Switch" */ @@ -25,6 +21,10 @@ // AMD. Register as an anonymous module. define(['jquery'], factory); // jQuery Switch // define(['zepto'], factory); // Zepto Switch + } else if (typeof exports === 'object') { + // Node/CommonJS + factory(require('jquery')); // jQuery Switch + //factory(require('zepto')); // Zepto Switch } else { // Browser globals if(root.jQuery) { // Use jQuery if available @@ -113,6 +113,7 @@ $.each( [ 'ready', + 'setmedia', // Fires when the media is set 'flashreset', // Similar to the ready event if the Flash solution is set to display:none and then shown again or if it's reloaded for another reason by the browser. For example, using CSS position:fixed on Firefox for the full screen feature. 'resize', // Occurs when the size changes through a full/restore screen operation or if the size/sizeFull options are changed. 'repeat', // Occurs when the repeat status changes. Usually through clicks on the repeat button of the interface. @@ -159,7 +160,7 @@ // "play", // jPlayer uses internally before bubbling. // "pause", // jPlayer uses internally before bubbling. "loadedmetadata", - "loadeddata", + // "loadeddata", // jPlayer uses internally before bubbling. // "waiting", // jPlayer uses internally before bubbling. // "playing", // jPlayer uses internally before bubbling. "canplay", @@ -174,6 +175,7 @@ ]; $.jPlayer.pause = function() { + $.jPlayer.prototype.destroyRemoved(); $.each($.jPlayer.prototype.instances, function(i, element) { if(element.data("jPlayer").status.srcSet) { // Check that media is set otherwise would cause error event. element.jPlayer("pause"); @@ -352,13 +354,22 @@ 'webkitExitFullscreen', '', '' + ], + ms: [ + '', + 'msFullscreenElement', + 'msRequestFullscreen', + 'msExitFullscreen', + 'MSFullscreenChange', + 'MSFullscreenError' ] }, specOrder = [ 'w3c', 'moz', 'webkit', - 'webkitVideo' + 'webkitVideo', + 'ms' ], fs, i, il; @@ -367,7 +378,8 @@ w3c: !!d[spec.w3c[0]], moz: !!d[spec.moz[0]], webkit: typeof d[spec.webkit[3]] === 'function', - webkitVideo: typeof v[spec.webkitVideo[2]] === 'function' + webkitVideo: typeof v[spec.webkitVideo[2]] === 'function', + ms: typeof v[spec.ms[2]] === 'function' }, used: {} }; @@ -391,7 +403,7 @@ return elem[s[1]]; }, requestFullscreen: function(elem) { - return elem[s[2]](); + return elem[s[2]](); // Chrome and Opera want parameter (Element.ALLOW_KEYBOARD_INPUT) but Safari fails if flag used. }, exitFullscreen: function(elem) { elem = elem ? elem : d; // Video element required for webkitVideo @@ -423,11 +435,10 @@ $.jPlayer.focus = null; // The list of element node names to ignore with key controls. - $.jPlayer.keyIgnoreElementNames = "INPUT TEXTAREA"; + $.jPlayer.keyIgnoreElementNames = "A INPUT TEXTAREA SELECT BUTTON"; // The function that deals with key presses. var keyBindings = function(event) { - var f = $.jPlayer.focus, ignoreKey; @@ -445,7 +456,11 @@ // See if the key pressed matches any of the bindings. $.each(f.options.keyBindings, function(action, binding) { // The binding could be a null when the default has been disabled. ie., 1st clause in if() - if(binding && event.which === binding.key && $.isFunction(binding.fn)) { + if( + (binding && $.isFunction(binding.fn)) && + ((typeof binding.key === 'number' && event.which === binding.key) || + (typeof binding.key === 'string' && event.key === binding.key)) + ) { event.preventDefault(); // Key being used by jPlayer, so prevent default operation. binding.fn(f); return false; // exit each. @@ -470,17 +485,21 @@ $.jPlayer.prototype = { count: 0, // Static Variable: Change it via prototype. version: { // Static Object - script: "2.5.2", - needFlash: "2.5.2", + script: "2.9.2", + needFlash: "2.9.0", flash: "unknown" }, options: { // Instanced in $.jPlayer() constructor - swfPath: "js", // Path to Jplayer.swf. Can be relative, absolute or server root relative. - solution: "html, flash", // Valid solutions: html, flash. Order defines priority. 1st is highest, + swfPath: "js", // Path to jquery.jplayer.swf. Can be relative, absolute or server root relative. + solution: "html, flash", // Valid solutions: html, flash, aurora. Order defines priority. 1st is highest, supplied: "mp3", // Defines which formats jPlayer will try and support and the priority by the order. 1st is highest, + auroraFormats: "wav", // List the aurora.js codecs being loaded externally. Its core supports "wav". Specify format in jPlayer context. EG., The aac.js codec gives the "m4a" format. preload: 'metadata', // HTML5 Spec values: none, metadata, auto. volume: 0.8, // The volume. Number 0 to 1. muted: false, + remainingDuration: false, // When true, the remaining time is shown in the duration GUI element. + toggleDuration: false, // When true, clicks on the duration toggle between the duration and remaining display. + captureDuration: true, // When true, clicks on the duration are captured and no longer propagate up the DOM. playbackRate: 1, defaultPlaybackRate: 1, minPlaybackRate: 0.5, @@ -504,6 +523,7 @@ playbackRateBarValue: ".jp-playback-rate-bar-value", currentTime: ".jp-current-time", duration: ".jp-duration", + title: ".jp-title", fullScreen: ".jp-full-screen", // * restoreScreen: ".jp-restore-screen", // * repeat: ".jp-repeat", @@ -511,6 +531,16 @@ gui: ".jp-gui", // The interface used with autohide feature. noSolution: ".jp-no-solution" // For error feedback when jPlayer cannot find a solution. }, + stateClass: { // Classes added to the cssSelectorAncestor to indicate the state. + playing: "jp-state-playing", + seeking: "jp-state-seeking", + muted: "jp-state-muted", + looped: "jp-state-looped", + fullScreen: "jp-state-full-screen", + noVolume: "jp-state-no-volume" + }, + useStateClassSkin: false, // A state class skin relies on the state classes to change the visual appearance. The single control toggles the effect, for example: play then pause, mute then unmute. + autoBlur: true, // GUI control handlers will drop focus after clicks. smoothPlayBar: false, // Smooths the play bar transitions, which affects clicks and short media with big changes per second. fullScreen: false, // Native Full Screen fullWindow: false, @@ -541,7 +571,7 @@ iphone: /iphone/, ipod: /ipod/, android_pad: /android [0-3]\.(?!.*?mobile)/, - android_phone: /android.*?mobile/, + android_phone: /(?=.*android)(?!.*chrome)(?=.*mobile)/, blackberry: /blackberry/, windows_ce: /windows ce/, iemobile: /iemobile/, @@ -569,7 +599,7 @@ // The parameter, f = $.jPlayer.focus, will be checked truethy before attempting to call any of these functions. // Properties may be added to this object, in key/fn pairs, to enable other key controls. EG, for the playlist add-on. play: { - key: 32, // space + key: 80, // p fn: function(f) { if(f.status.paused) { f.play(); @@ -579,7 +609,7 @@ } }, fullScreen: { - key: 13, // enter + key: 70, // f fn: function(f) { if(f.status.video || f.options.audioFullScreen) { f._setOption("fullScreen", !f.options.fullScreen); @@ -587,22 +617,28 @@ } }, muted: { - key: 8, // backspace + key: 77, // m fn: function(f) { f._muted(!f.options.muted); } }, volumeUp: { - key: 38, // UP + key: 190, // . fn: function(f) { f.volume(f.options.volume + 0.1); } }, volumeDown: { - key: 40, // DOWN + key: 188, // , fn: function(f) { f.volume(f.options.volume - 0.1); } + }, + loop: { + key: 76, // l + fn: function(f) { + f._loop(!f.options.loop); + } } }, verticalVolume: false, // Calculate volume from the bottom of the volume bar. Default is from the left. Also volume affects either width or height. @@ -655,6 +691,7 @@ currentPercentAbsolute: 0, currentTime: 0, duration: 0, + remaining: 0, videoWidth: 0, // Intrinsic width of the video in pixels. videoHeight: 0, // Intrinsic height of the video in pixels. readyState: 0, @@ -679,16 +716,18 @@ // domNode: undefined // htmlDlyCmdId: undefined // autohideId: undefined + // mouse: undefined // cmdsIgnored }, solution: { // Static Object: Defines the solutions built in jPlayer. html: true, + aurora: true, flash: true }, // 'MPEG-4 support' : canPlayType('video/mp4; codecs="mp4v.20.8"') format: { // Static Object mp3: { - codec: 'audio/mpeg; codecs="mp3"', + codec: 'audio/mpeg', flashCanPlay: true, media: 'audio' }, @@ -794,6 +833,17 @@ $.jPlayer.focus = this; } + // A fix for Android where older (2.3) and even some 4.x devices fail to work when changing the *audio* SRC and then playing immediately. + this.androidFix = { + setMedia: false, // True when media set + play: false, // True when a progress event will instruct the media to play + pause: false, // True when a progress event will instruct the media to pause at a time. + time: NaN // The play(time) parameter + }; + if($.jPlayer.platform.android) { + this.options.preload = this.options.preload !== 'auto' ? 'metadata' : 'auto'; // Default to metadata, but allow auto. + } + this.formats = []; // Array based on supplied string option. Order defines priority. this.solutions = []; // Array based on solution string option. Order defines priority. this.require = {}; // Which media types are required: video, audio. @@ -802,6 +852,9 @@ this.html = {}; // In _init()'s this.desired code and setmedia(): Accessed via this[solution], where solution from this.solutions array. this.html.audio = {}; this.html.video = {}; + this.aurora = {}; // In _init()'s this.desired code and setmedia(): Accessed via this[solution], where solution from this.solutions array. + this.aurora.formats = []; + this.aurora.properties = []; this.flash = {}; // In _init()'s this.desired code and setmedia(): Accessed via this[solution], where solution from this.solutions array. this.css = {}; @@ -845,6 +898,23 @@ } } }); + + // Create Aurora.js formats array + $.each(this.options.auroraFormats.toLowerCase().split(","), function(index1, value1) { + var format = value1.replace(/^\s+|\s+$/g, ""); //trim + if(self.format[format]) { // Check format is valid. + var dupFound = false; + $.each(self.aurora.formats, function(index2, value2) { // Check for duplicates + if(format === value2) { + dupFound = true; + return false; + } + }); + if(!dupFound) { + self.aurora.formats.push(format); + } + } + }); this.internal.instance = "jp_" + this.count; this.instances[this.internal.instance] = this.element; @@ -869,7 +939,7 @@ this.internal.flash = $.extend({}, { id: this.options.idPrefix + "_flash_" + this.count, jq: undefined, - swf: this.options.swfPath + (this.options.swfPath.toLowerCase().slice(-4) !== ".swf" ? (this.options.swfPath && this.options.swfPath.slice(-1) !== "/" ? "/" : "") + "Jplayer.swf" : "") + swf: this.options.swfPath + (this.options.swfPath.toLowerCase().slice(-4) !== ".swf" ? (this.options.swfPath && this.options.swfPath.slice(-1) !== "/" ? "/" : "") + "jquery.jplayer.swf" : "") }); this.internal.poster = $.extend({}, { id: this.options.idPrefix + "_poster_" + this.count, @@ -951,12 +1021,15 @@ this.flash.available = this._checkForFlash(10.1); this.html.canPlay = {}; + this.aurora.canPlay = {}; this.flash.canPlay = {}; $.each(this.formats, function(priority, format) { self.html.canPlay[format] = self.html[self.format[format].media].available && "" !== self.htmlElement[self.format[format].media].canPlayType(self.format[format].codec); + self.aurora.canPlay[format] = ($.inArray(format, self.aurora.formats) > -1); self.flash.canPlay[format] = self.format[format].flashCanPlay && self.flash.available; }); this.html.desired = false; + this.aurora.desired = false; this.flash.desired = false; $.each(this.solutions, function(solutionPriority, solution) { if(solutionPriority === 0) { @@ -978,13 +1051,16 @@ }); // This is what jPlayer will support, based on solution and supplied. this.html.support = {}; + this.aurora.support = {}; this.flash.support = {}; $.each(this.formats, function(priority, format) { self.html.support[format] = self.html.canPlay[format] && self.html.desired; + self.aurora.support[format] = self.aurora.canPlay[format] && self.aurora.desired; self.flash.support[format] = self.flash.canPlay[format] && self.flash.desired; }); // If jPlayer is supporting any format in a solution, then the solution is used. this.html.used = false; + this.aurora.used = false; this.flash.used = false; $.each(this.solutions, function(solutionPriority, solution) { $.each(self.formats, function(formatPriority, format) { @@ -1002,8 +1078,8 @@ // Set up the css selectors for the control and feedback entities. this._cssSelectorAncestor(this.options.cssSelectorAncestor); - // If neither html nor flash are being used by this browser, then media playback is not possible. Trigger an error event. - if(!(this.html.used || this.flash.used)) { + // If neither html nor aurora nor flash are being used by this browser, then media playback is not possible. Trigger an error event. + if(!(this.html.used || this.aurora.used || this.flash.used)) { this._error( { type: $.jPlayer.error.NO_SOLUTION, context: "{solution:'" + this.options.solution + "', supplied:'" + this.options.supplied + "'}", @@ -1103,13 +1179,18 @@ }); } } + + // Add the Aurora.js solution if being used. + if(this.aurora.used) { + // Aurora.js player need to be created for each media, see setMedia function. + } // Create the bridge that emulates the HTML Media element on the jPlayer DIV if( this.options.emulateHtml ) { this._emulateHtmlBridge(); } - if(this.html.used && !this.flash.used) { // If only HTML, then emulate flash ready() call after 100ms. + if((this.html.used || this.aurora.used) && !this.flash.used) { // If only HTML, then emulate flash ready() call after 100ms. setTimeout( function() { self.internal.ready = true; self.version.flash = "n/a"; @@ -1170,6 +1251,17 @@ delete this.instances[this.internal.instance]; // Clear the instance on the static instance object }, + destroyRemoved: function() { // Destroy any instances that have gone away. + var self = this; + $.each(this.instances, function(i, element) { + if(self.element !== element) { // Do not destroy this instance. + if(!element.data("jPlayer")) { // Check that element is a real jPlayer. + element.jPlayer("destroy"); + delete self.instances[i]; + } + } + }); + }, enable: function() { // Plan to implement // options.disabled = false }, @@ -1266,6 +1358,20 @@ self._trigger($.jPlayer.event.progress); } }, false); + mediaElement.addEventListener("loadeddata", function() { + if(entity.gate) { + self.androidFix.setMedia = false; // Disable the fix after the first progress event. + if(self.androidFix.play) { // Play Android audio - performing the fix. + self.androidFix.play = false; + self.play(self.androidFix.time); + } + if(self.androidFix.pause) { // Pause Android audio at time - performing the fix. + self.androidFix.pause = false; + self.pause(self.androidFix.time); + } + self._trigger($.jPlayer.event.loadeddata); + } + }, false); mediaElement.addEventListener("timeupdate", function() { if(entity.gate) { self._getHtmlStatus(mediaElement); @@ -1392,6 +1498,77 @@ }, false); }); }, + _addAuroraEventListeners : function(player, entity) { + var self = this; + //player.preload = this.options.preload; + //player.muted = this.options.muted; + player.volume = this.options.volume * 100; + + // Create the event listeners + // Only want the active entity to affect jPlayer and bubble events. + // Using entity.gate so that object is referenced and gate property always current + + player.on("progress", function() { + if(entity.gate) { + if(self.internal.cmdsIgnored && this.readyState > 0) { // Detect iOS executed the command + self.internal.cmdsIgnored = false; + } + self._getAuroraStatus(player); + self._updateInterface(); + self._trigger($.jPlayer.event.progress); + // Progress with song duration, we estimate timeupdate need to be triggered too. + if (player.duration > 0) { + self._trigger($.jPlayer.event.timeupdate); + } + } + }, false); + player.on("ready", function() { + if(entity.gate) { + self._trigger($.jPlayer.event.loadeddata); + } + }, false); + player.on("duration", function() { + if(entity.gate) { + self._getAuroraStatus(player); + self._updateInterface(); + self._trigger($.jPlayer.event.durationchange); + } + }, false); + player.on("end", function() { + if(entity.gate) { + // Order of the next few commands are important. Change the time and then pause. + self._updateButtons(false); + self._getAuroraStatus(player, true); + self._updateInterface(); + self._trigger($.jPlayer.event.ended); + } + }, false); + player.on("error", function() { + if(entity.gate) { + self._updateButtons(false); + self._seeked(); + if(self.status.srcSet) { // Deals with case of clearMedia() causing an error event. + self.status.waitForLoad = true; // Allows the load operation to try again. + self.status.waitForPlay = true; // Reset since a play was captured. + if(self.status.video && !self.status.nativeVideoControls) { + self.internal.video.jq.css({'width':'0px', 'height':'0px'}); + } + if(self._validString(self.status.media.poster) && !self.status.nativeVideoControls) { + self.internal.poster.jq.show(); + } + if(self.css.jq.videoPlay.length) { + self.css.jq.videoPlay.show(); + } + self._error( { + type: $.jPlayer.error.URL, + context: self.status.src, // this.src shows absolute urls. Want context to show the url given. + message: $.jPlayer.errorMsg.URL, + hint: $.jPlayer.errorHint.URL + }); + } + } + }, false); + }, _getHtmlStatus: function(media, override) { var ct = 0, cpa = 0, sp = 0, cpr = 0; @@ -1422,6 +1599,8 @@ this.status.currentPercentAbsolute = cpa; this.status.currentTime = ct; + this.status.remaining = this.status.duration - this.status.currentTime; + this.status.videoWidth = media.videoWidth; this.status.videoHeight = media.videoHeight; @@ -1430,6 +1609,39 @@ this.status.playbackRate = media.playbackRate; this.status.ended = media.ended; }, + _getAuroraStatus: function(player, override) { + var ct = 0, cpa = 0, sp = 0, cpr = 0; + + this.status.duration = player.duration / 1000; + + ct = player.currentTime / 1000; + cpa = (this.status.duration > 0) ? 100 * ct / this.status.duration : 0; + if(player.buffered > 0) { + sp = (this.status.duration > 0) ? (player.buffered * this.status.duration) / this.status.duration : 100; + cpr = (this.status.duration > 0) ? ct / (player.buffered * this.status.duration) : 0; + } else { + sp = 100; + cpr = cpa; + } + + if(override) { + ct = 0; + cpr = 0; + cpa = 0; + } + + this.status.seekPercent = sp; + this.status.currentPercentRelative = cpr; + this.status.currentPercentAbsolute = cpa; + this.status.currentTime = ct; + + this.status.remaining = this.status.duration - this.status.currentTime; + + this.status.readyState = 4; // status.readyState; + this.status.networkState = 0; // status.networkState; + this.status.playbackRate = 1; // status.playbackRate; + this.status.ended = false; // status.ended; + }, _resetStatus: function() { this.status = $.extend({}, this.status, $.jPlayer.prototype.status); // Maintains the status properties that persist through a reset. }, @@ -1440,6 +1652,7 @@ event.jPlayer.options = $.extend(true, {}, this.options); // Deep copy event.jPlayer.status = $.extend(true, {}, this.status); // Deep copy event.jPlayer.html = $.extend(true, {}, this.html); // Deep copy + event.jPlayer.aurora = $.extend(true, {}, this.aurora); // Deep copy event.jPlayer.flash = $.extend(true, {}, this.flash); // Deep copy if(error) { event.jPlayer.error = $.extend({}, error); @@ -1571,6 +1784,7 @@ this.status.currentPercentAbsolute = status.currentPercentAbsolute; this.status.currentTime = status.currentTime; this.status.duration = status.duration; + this.status.remaining = status.duration - status.currentTime; this.status.videoWidth = status.videoWidth; this.status.videoHeight = status.videoHeight; @@ -1587,6 +1801,23 @@ } else { this.status.paused = !playing; } + // Apply the state classes. (For the useStateClassSkin:true option) + if(playing) { + this.addStateClass('playing'); + } else { + this.removeStateClass('playing'); + } + if(!this.status.noFullWindow && this.options.fullWindow) { + this.addStateClass('fullScreen'); + } else { + this.removeStateClass('fullScreen'); + } + if(this.options.loop) { + this.addStateClass('looped'); + } else { + this.removeStateClass('looped'); + } + // Toggle the GUI element pairs. (For the useStateClassSkin:false option) if(this.css.jq.play.length && this.css.jq.pause.length) { if(playing) { this.css.jq.play.hide(); @@ -1631,11 +1862,33 @@ this.css.jq.playBar.width(this.status.currentPercentRelative+"%"); } } + var currentTimeText = ''; if(this.css.jq.currentTime.length) { - this.css.jq.currentTime.text(this._convertTime(this.status.currentTime)); + currentTimeText = this._convertTime(this.status.currentTime); + if(currentTimeText !== this.css.jq.currentTime.text()) { + this.css.jq.currentTime.text(this._convertTime(this.status.currentTime)); + } } + var durationText = '', + duration = this.status.duration, + remaining = this.status.remaining; if(this.css.jq.duration.length) { - this.css.jq.duration.text(this._convertTime(this.status.duration)); + if(typeof this.status.media.duration === 'string') { + durationText = this.status.media.duration; + } else { + if(typeof this.status.media.duration === 'number') { + duration = this.status.media.duration; + remaining = duration - this.status.currentTime; + } + if(this.options.remainingDuration) { + durationText = (remaining > 0 ? '-' : '') + this._convertTime(remaining); + } else { + durationText = this._convertTime(duration); + } + } + if(durationText !== this.css.jq.duration.text()) { + this.css.jq.duration.text(durationText); + } } }, _convertTime: ConvertTime.prototype.time, @@ -1643,19 +1896,23 @@ if(this.css.jq.seekBar.length) { this.css.jq.seekBar.addClass("jp-seeking-bg"); } + this.addStateClass('seeking'); }, _seeked: function() { if(this.css.jq.seekBar.length) { this.css.jq.seekBar.removeClass("jp-seeking-bg"); } + this.removeStateClass('seeking'); }, _resetGate: function() { this.html.audio.gate = false; this.html.video.gate = false; + this.aurora.gate = false; this.flash.gate = false; }, _resetActive: function() { this.html.active = false; + this.aurora.active = false; this.flash.active = false; }, _escapeHtml: function(s) { @@ -1669,12 +1926,22 @@ _absoluteMediaUrls: function(media) { var self = this; $.each(media, function(type, url) { - if(self.format[type]) { + if(url && self.format[type] && url.substr(0, 5) !== "data:") { media[type] = self._qualifyURL(url); } }); return media; }, + addStateClass: function(state) { + if(this.ancestorJq.length) { + this.ancestorJq.addClass(this.options.stateClass[state]); + } + }, + removeStateClass: function(state) { + if(this.ancestorJq.length) { + this.ancestorJq.removeClass(this.options.stateClass[state]); + } + }, setMedia: function(media) { /* media[format] = String: URL of format. Must contain all of the supplied option's video or audio formats. @@ -1691,6 +1958,11 @@ this._resetGate(); this._resetActive(); + // Clear the Android Fix. + this.androidFix.setMedia = false; + this.androidFix.play = false; + this.androidFix.pause = false; + // Convert all media URLs to absolute URLs. media = this._absoluteMediaUrls(media); @@ -1699,6 +1971,7 @@ $.each(self.solutions, function(solutionPriority, solution) { if(self[solution].support[format] && self._validString(media[format])) { // Format supported in solution and url given for format. var isHtml = solution === 'html'; + var isAurora = solution === 'aurora'; if(isVideo) { if(isHtml) { @@ -1719,6 +1992,15 @@ self.html.audio.gate = true; self._html_setAudio(media); self.html.active = true; + + // Setup the Android Fix - Only for HTML audio. + if($.jPlayer.platform.android) { + self.androidFix.setMedia = true; + } + } else if(isAurora) { + self.aurora.gate = true; + self._aurora_setAudio(media); + self.aurora.active = true; } else { self.flash.gate = true; self._flash_setAudio(media); @@ -1752,10 +2034,22 @@ } } } + if(typeof media.title === 'string') { + if(this.css.jq.title.length) { + this.css.jq.title.html(media.title); + } + if(this.htmlElement.audio) { + this.htmlElement.audio.setAttribute('title', media.title); + } + if(this.htmlElement.video) { + this.htmlElement.video.setAttribute('title', media.title); + } + } this.status.srcSet = true; this.status.media = $.extend({}, media); this._updateButtons(false); this._updateInterface(); + this._trigger($.jPlayer.event.setmedia); } else { // jPlayer cannot support any formats provided in this browser // Send an error event this._error( { @@ -1777,6 +2071,8 @@ if(this.html.active) { this._html_resetMedia(); + } else if(this.aurora.active) { + this._aurora_resetMedia(); } else if(this.flash.active) { this._flash_resetMedia(); } @@ -1786,6 +2082,8 @@ if(this.html.active) { this._html_clearMedia(); + } else if(this.aurora.active) { + this._aurora_clearMedia(); } else if(this.flash.active) { this._flash_clearMedia(); } @@ -1797,6 +2095,8 @@ if(this.status.srcSet) { if(this.html.active) { this._html_load(); + } else if(this.aurora.active) { + this._aurora_load(); } else if(this.flash.active) { this._flash_load(); } @@ -1810,16 +2110,23 @@ } }, play: function(time) { - time = (typeof time === "number") ? time : NaN; // Remove jQuery event from click handler - if(this.status.srcSet) { - this.focus(); - if(this.html.active) { - this._html_play(time); - } else if(this.flash.active) { - this._flash_play(time); - } + var guiAction = typeof time === "object"; // Flags GUI click events so we know this was not a direct command, but an action taken by the user on the GUI. + if(guiAction && this.options.useStateClassSkin && !this.status.paused) { + this.pause(time); // The time would be the click event, but passing it over so info is not lost. } else { - this._urlNotSetError("play"); + time = (typeof time === "number") ? time : NaN; // Remove jQuery event from click handler + if(this.status.srcSet) { + this.focus(); + if(this.html.active) { + this._html_play(time); + } else if(this.aurora.active) { + this._aurora_play(time); + } else if(this.flash.active) { + this._flash_play(time); + } + } else { + this._urlNotSetError("play"); + } } }, videoPlay: function() { // Handles clicks on the play button over the video poster @@ -1830,6 +2137,8 @@ if(this.status.srcSet) { if(this.html.active) { this._html_pause(time); + } else if(this.aurora.active) { + this._aurora_pause(time); } else if(this.flash.active) { this._flash_pause(time); } @@ -1849,6 +2158,7 @@ args.splice(1, 1); // Remove the conditions from the arguments } + $.jPlayer.prototype.destroyRemoved(); $.each(this.instances, function() { // Remember that "this" is the instance's "element" in the $.each() loop. if(self.element !== this) { // Do not tell my instance. @@ -1868,6 +2178,8 @@ if(this.status.srcSet) { if(this.html.active) { this._html_pause(0); + } else if(this.aurora.active) { + this._aurora_pause(0); } else if(this.flash.active) { this._flash_pause(0); } @@ -1880,6 +2192,8 @@ if(this.status.srcSet) { if(this.html.active) { this._html_playHead(p); + } else if(this.aurora.active) { + this._aurora_playHead(p); } else if(this.flash.active) { this._flash_playHead(p); } @@ -1901,6 +2215,9 @@ if(this.html.used) { this._html_setProperty('muted', muted); } + if(this.aurora.used) { + this._aurora_mute(muted); + } if(this.flash.used) { this._flash_mute(muted); } @@ -1913,8 +2230,13 @@ } }, mute: function(mute) { // mute is either: undefined (true), an event object (true) or a boolean (muted). - mute = mute === undefined ? true : !!mute; - this._muted(mute); + var guiAction = typeof mute === "object"; // Flags GUI click events so we know this was not a direct command, but an action taken by the user on the GUI. + if(guiAction && this.options.useStateClassSkin && this.options.muted) { + this._muted(false); + } else { + mute = mute === undefined ? true : !!mute; + this._muted(mute); + } }, unmute: function(unmute) { // unmute is either: undefined (true), an event object (true) or a boolean (!muted). unmute = unmute === undefined ? true : !!unmute; @@ -1924,6 +2246,11 @@ if(mute === undefined) { mute = this.options.muted; } + if(mute) { + this.addStateClass('muted'); + } else { + this.removeStateClass('muted'); + } if(this.css.jq.mute.length && this.css.jq.unmute.length) { if(this.status.noVolume) { this.css.jq.mute.hide(); @@ -1953,6 +2280,9 @@ if(this.html.used) { this._html_setProperty('volume', v); } + if(this.aurora.used) { + this._aurora_volume(v); + } if(this.flash.used) { this._flash_volume(v); } @@ -1989,6 +2319,7 @@ v = this.options.muted ? 0 : v; if(this.status.noVolume) { + this.addStateClass('noVolume'); if(this.css.jq.volumeBar.length) { this.css.jq.volumeBar.hide(); } @@ -1999,6 +2330,7 @@ this.css.jq.volumeMax.hide(); } } else { + this.removeStateClass('noVolume'); if(this.css.jq.volumeBar.length) { this.css.jq.volumeBar.show(); } @@ -2062,7 +2394,11 @@ var handler = function(e) { e.preventDefault(); self[fn](e); - $(this).blur(); + if(self.options.autoBlur) { + $(this).blur(); + } else { + $(this).focus(); // Force focus for ARIA. + } }; this.css.jq[fn].bind("click.jPlayer", handler); // Using jPlayer namespace } @@ -2092,6 +2428,14 @@ }); } }, + duration: function(e) { + if(this.options.toggleDuration) { + if(this.options.captureDuration) { + e.stopPropagation(); + } + this._setOption("remainingDuration", !this.options.remainingDuration); + } + }, seekBar: function(e) { // Handles clicks on the seekBar if(this.css.jq.seekBar.length) { // Using $(e.currentTarget) to enable multiple seek bars @@ -2145,8 +2489,13 @@ } } }, - repeat: function() { // Handle clicks on the repeat button - this._loop(true); + repeat: function(event) { // Handle clicks on the repeat button + var guiAction = typeof event === "object"; // Flags GUI click events so we know this was not a direct command, but an action taken by the user on the GUI. + if(guiAction && this.options.useStateClassSkin && this.options.loop) { + this._loop(false); + } else { + this._loop(true); + } }, repeatOff: function() { // Handle clicks on the repeatOff button this._loop(false); @@ -2313,6 +2662,13 @@ case "loop" : this._loop(value); break; + case "remainingDuration" : + this.options[key] = value; + this._updateInterface(); + break; + case "toggleDuration" : + this.options[key] = value; + break; case "nativeVideoControls" : this.options[key] = $.extend({}, this.options[key], value); // store a merged copy of it, incase not all properties changed. this.status.nativeVideoControls = this._uaBlocklist(this.options.nativeVideoControls); @@ -2357,6 +2713,9 @@ case "audioFullScreen" : this.options[key] = value; break; + case "autoBlur" : + this.options[key] = value; + break; } return this; @@ -2413,13 +2772,31 @@ event = "mousemove.jPlayer", namespace = ".jPlayerAutohide", eventType = event + namespace, - handler = function() { - self.css.jq.gui.fadeIn(self.options.autohide.fadeIn, function() { - clearTimeout(self.internal.autohideId); - self.internal.autohideId = setTimeout( function() { - self.css.jq.gui.fadeOut(self.options.autohide.fadeOut); - }, self.options.autohide.hold); - }); + handler = function(event) { + var moved = false, + deltaX, deltaY; + if(typeof self.internal.mouse !== "undefined") { + //get the change from last position to this position + deltaX = self.internal.mouse.x - event.pageX; + deltaY = self.internal.mouse.y - event.pageY; + moved = (Math.floor(deltaX) > 0) || (Math.floor(deltaY)>0); + } else { + moved = true; + } + // store current position for next method execution + self.internal.mouse = { + x : event.pageX, + y : event.pageY + }; + // if mouse has been actually moved, do the gui fadeIn/fadeOut + if (moved) { + self.css.jq.gui.fadeIn(self.options.autohide.fadeIn, function() { + clearTimeout(self.internal.autohideId); + self.internal.autohideId = setTimeout( function() { + self.css.jq.gui.fadeOut(self.options.autohide.fadeOut); + }, self.options.autohide.hold); + }); + } }; if(this.css.jq.gui.length) { @@ -2430,6 +2807,8 @@ // Removes the fadeOut operation from the fadeIn callback. clearTimeout(this.internal.autohideId); + // undefine mouse + delete this.internal.mouse; this.element.unbind(namespace); this.css.jq.gui.unbind(namespace); @@ -2447,8 +2826,13 @@ } } }, - fullScreen: function() { - this._setOption("fullScreen", true); + fullScreen: function(event) { + var guiAction = typeof event === "object"; // Flags GUI click events so we know this was not a direct command, but an action taken by the user on the GUI. + if(guiAction && this.options.useStateClassSkin && this.options.fullScreen) { + this._setOption("fullScreen", false); + } else { + this._setOption("fullScreen", true); + } }, restoreScreen: function() { this._setOption("fullScreen", false); @@ -2474,7 +2858,7 @@ _fullscreenRemoveEventListeners: function() { var fs = $.jPlayer.nativeFeatures.fullscreen; if(this.internal.fullscreenchangeHandler) { - document.addEventListener(fs.event.fullscreenchange, this.internal.fullscreenchangeHandler, false); + document.removeEventListener(fs.event.fullscreenchange, this.internal.fullscreenchangeHandler, false); } }, _fullscreenchange: function() { @@ -2590,9 +2974,16 @@ var self = this, media = this.htmlElement.media; + this.androidFix.pause = false; // Cancel the pause fix. + this._html_load(); // Loads if required and clears any delayed commands. - if(!isNaN(time)) { + // Setup the Android Fix. + if(this.androidFix.setMedia) { + this.androidFix.play = true; + this.androidFix.time = time; + + } else if(!isNaN(time)) { // Attempt to play it, since iOS has been ignoring commands if(this.internal.cmdsIgnored) { @@ -2622,7 +3013,9 @@ _html_pause: function(time) { var self = this, media = this.htmlElement.media; - + + this.androidFix.play = false; // Cancel the play fix. + if(time > 0) { // We do not want the stop() command, which does pause(0), causing a load operation. this._html_load(); // Loads if required and clears any delayed commands. } else { @@ -2632,7 +3025,12 @@ // Order of these commands is important for Safari (Win) and IE9. Pause then change currentTime. media.pause(); - if(!isNaN(time)) { + // Setup the Android Fix. + if(this.androidFix.setMedia) { + this.androidFix.pause = true; + this.androidFix.time = time; + + } else if(!isNaN(time)) { try { if(!media.seekable || typeof media.seekable === "object" && media.seekable.length > 0) { media.currentTime = time; @@ -2656,6 +3054,8 @@ this._html_load(); // Loads if required and clears any delayed commands. + // This playHead() method needs a refactor to apply the android fix. + try { if(typeof media.seekable === "object" && media.seekable.length > 0) { media.currentTime = percent * media.seekable.end(media.seekable.length-1) / 100; @@ -2694,6 +3094,99 @@ this.htmlElement.video[property] = value; } }, + _aurora_setAudio: function(media) { + var self = this; + + // Always finds a format due to checks in setMedia() + $.each(this.formats, function(priority, format) { + if(self.aurora.support[format] && media[format]) { + self.status.src = media[format]; + self.status.format[format] = true; + self.status.formatType = format; + + return false; + } + }); + + this.aurora.player = new AV.Player.fromURL(this.status.src); + this._addAuroraEventListeners(this.aurora.player, this.aurora); + + if(this.options.preload === 'auto') { + this._aurora_load(); + this.status.waitForLoad = false; + } + }, + _aurora_resetMedia: function() { + if (this.aurora.player) { + this.aurora.player.stop(); + } + }, + _aurora_clearMedia: function() { + // Nothing to clear. + }, + _aurora_load: function() { + if(this.status.waitForLoad) { + this.status.waitForLoad = false; + this.aurora.player.preload(); + } + }, + _aurora_play: function(time) { + if (!this.status.waitForLoad) { + if (!isNaN(time)) { + this.aurora.player.seek(time); + } + } + if (!this.aurora.player.playing) { + this.aurora.player.play(); + } + this.status.waitForLoad = false; + this._aurora_checkWaitForPlay(); + + // No event from the player, update UI now. + this._updateButtons(true); + this._trigger($.jPlayer.event.play); + }, + _aurora_pause: function(time) { + if (!isNaN(time)) { + this.aurora.player.seek(time * 1000); + } + this.aurora.player.pause(); + + if(time > 0) { // Avoids a setMedia() followed by stop() or pause(0) hiding the video play button. + this._aurora_checkWaitForPlay(); + } + + // No event from the player, update UI now. + this._updateButtons(false); + this._trigger($.jPlayer.event.pause); + }, + _aurora_playHead: function(percent) { + if(this.aurora.player.duration > 0) { + // The seek() sould be in milliseconds, but the only codec that works with seek (aac.js) uses seconds. + this.aurora.player.seek(percent * this.aurora.player.duration / 100); // Using seconds + } + + if(!this.status.waitForLoad) { + this._aurora_checkWaitForPlay(); + } + }, + _aurora_checkWaitForPlay: function() { + if(this.status.waitForPlay) { + this.status.waitForPlay = false; + } + }, + _aurora_volume: function(v) { + this.aurora.player.volume = v * 100; + }, + _aurora_mute: function(m) { + if (m) { + this.aurora.properties.lastvolume = this.aurora.player.volume; + this.aurora.player.volume = 0; + } else { + this.aurora.player.volume = this.aurora.properties.lastvolume; + } + this.aurora.properties.muted = m; + }, _flash_setAudio: function(media) { var self = this; try { @@ -2899,8 +3392,8 @@ var msg = "jPlayer " + this.version.script + " : id='" + this.internal.self.id +"' : " + message; if(!this.options.consoleAlerts) { alert(msg); - } else if(console && console.log) { - console.log(msg); + } else if(window.console && window.console.log) { + window.console.log(msg); } }, _emulateHtmlBridge: function() { diff --git a/dist/jplayer/jquery.jplayer.min.js b/dist/jplayer/jquery.jplayer.min.js new file mode 100644 index 00000000..99f64d71 --- /dev/null +++ b/dist/jplayer/jquery.jplayer.min.js @@ -0,0 +1,3 @@ +/*! jPlayer 2.9.2 for jQuery ~ (c) 2009-2014 Happyworm Ltd ~ MIT License */ +!function(a,b){"function"==typeof define&&define.amd?define(["jquery"],b):b("object"==typeof exports?require("jquery"):a.jQuery?a.jQuery:a.Zepto)}(this,function(a,b){a.fn.jPlayer=function(c){var d="jPlayer",e="string"==typeof c,f=Array.prototype.slice.call(arguments,1),g=this;return c=!e&&f.length?a.extend.apply(null,[!0,c].concat(f)):c,e&&"_"===c.charAt(0)?g:(this.each(e?function(){var e=a(this).data(d),h=e&&a.isFunction(e[c])?e[c].apply(e,f):e;return h!==e&&h!==b?(g=h,!1):void 0}:function(){var b=a(this).data(d);b?b.option(c||{}):a(this).data(d,new a.jPlayer(c,this))}),g)},a.jPlayer=function(b,c){if(arguments.length){this.element=a(c),this.options=a.extend(!0,{},this.options,b);var d=this;this.element.bind("remove.jPlayer",function(){d.destroy()}),this._init()}},"function"!=typeof a.fn.stop&&(a.fn.stop=function(){}),a.jPlayer.emulateMethods="load play pause",a.jPlayer.emulateStatus="src readyState networkState currentTime duration paused ended playbackRate",a.jPlayer.emulateOptions="muted volume",a.jPlayer.reservedEvent="ready flashreset resize repeat error warning",a.jPlayer.event={},a.each(["ready","setmedia","flashreset","resize","repeat","click","error","warning","loadstart","progress","suspend","abort","emptied","stalled","play","pause","loadedmetadata","loadeddata","waiting","playing","canplay","canplaythrough","seeking","seeked","timeupdate","ended","ratechange","durationchange","volumechange"],function(){a.jPlayer.event[this]="jPlayer_"+this}),a.jPlayer.htmlEvent=["loadstart","abort","emptied","stalled","loadedmetadata","canplay","canplaythrough"],a.jPlayer.pause=function(){a.jPlayer.prototype.destroyRemoved(),a.each(a.jPlayer.prototype.instances,function(a,b){b.data("jPlayer").status.srcSet&&b.jPlayer("pause")})},a.jPlayer.timeFormat={showHour:!1,showMin:!0,showSec:!0,padHour:!1,padMin:!0,padSec:!0,sepHour:":",sepMin:":",sepSec:""};var c=function(){this.init()};c.prototype={init:function(){this.options={timeFormat:a.jPlayer.timeFormat}},time:function(a){a=a&&"number"==typeof a?a:0;var b=new Date(1e3*a),c=b.getUTCHours(),d=this.options.timeFormat.showHour?b.getUTCMinutes():b.getUTCMinutes()+60*c,e=this.options.timeFormat.showMin?b.getUTCSeconds():b.getUTCSeconds()+60*d,f=this.options.timeFormat.padHour&&10>c?"0"+c:c,g=this.options.timeFormat.padMin&&10>d?"0"+d:d,h=this.options.timeFormat.padSec&&10>e?"0"+e:e,i="";return i+=this.options.timeFormat.showHour?f+this.options.timeFormat.sepHour:"",i+=this.options.timeFormat.showMin?g+this.options.timeFormat.sepMin:"",i+=this.options.timeFormat.showSec?h+this.options.timeFormat.sepSec:""}};var d=new c;a.jPlayer.convertTime=function(a){return d.time(a)},a.jPlayer.uaBrowser=function(a){var b=a.toLowerCase(),c=/(webkit)[ \/]([\w.]+)/,d=/(opera)(?:.*version)?[ \/]([\w.]+)/,e=/(msie) ([\w.]+)/,f=/(mozilla)(?:.*? rv:([\w.]+))?/,g=c.exec(b)||d.exec(b)||e.exec(b)||b.indexOf("compatible")<0&&f.exec(b)||[];return{browser:g[1]||"",version:g[2]||"0"}},a.jPlayer.uaPlatform=function(a){var b=a.toLowerCase(),c=/(ipad|iphone|ipod|android|blackberry|playbook|windows ce|webos)/,d=/(ipad|playbook)/,e=/(android)/,f=/(mobile)/,g=c.exec(b)||[],h=d.exec(b)||!f.exec(b)&&e.exec(b)||[];return g[1]&&(g[1]=g[1].replace(/\s/g,"_")),{platform:g[1]||"",tablet:h[1]||""}},a.jPlayer.browser={},a.jPlayer.platform={};var e=a.jPlayer.uaBrowser(navigator.userAgent);e.browser&&(a.jPlayer.browser[e.browser]=!0,a.jPlayer.browser.version=e.version);var f=a.jPlayer.uaPlatform(navigator.userAgent);f.platform&&(a.jPlayer.platform[f.platform]=!0,a.jPlayer.platform.mobile=!f.tablet,a.jPlayer.platform.tablet=!!f.tablet),a.jPlayer.getDocMode=function(){var b;return a.jPlayer.browser.msie&&(document.documentMode?b=document.documentMode:(b=5,document.compatMode&&"CSS1Compat"===document.compatMode&&(b=7))),b},a.jPlayer.browser.documentMode=a.jPlayer.getDocMode(),a.jPlayer.nativeFeatures={init:function(){var a,b,c,d=document,e=d.createElement("video"),f={w3c:["fullscreenEnabled","fullscreenElement","requestFullscreen","exitFullscreen","fullscreenchange","fullscreenerror"],moz:["mozFullScreenEnabled","mozFullScreenElement","mozRequestFullScreen","mozCancelFullScreen","mozfullscreenchange","mozfullscreenerror"],webkit:["","webkitCurrentFullScreenElement","webkitRequestFullScreen","webkitCancelFullScreen","webkitfullscreenchange",""],webkitVideo:["webkitSupportsFullscreen","webkitDisplayingFullscreen","webkitEnterFullscreen","webkitExitFullscreen","",""],ms:["","msFullscreenElement","msRequestFullscreen","msExitFullscreen","MSFullscreenChange","MSFullscreenError"]},g=["w3c","moz","webkit","webkitVideo","ms"];for(this.fullscreen=a={support:{w3c:!!d[f.w3c[0]],moz:!!d[f.moz[0]],webkit:"function"==typeof d[f.webkit[3]],webkitVideo:"function"==typeof e[f.webkitVideo[2]],ms:"function"==typeof e[f.ms[2]]},used:{}},b=0,c=g.length;c>b;b++){var h=g[b];if(a.support[h]){a.spec=h,a.used[h]=!0;break}}if(a.spec){var i=f[a.spec];a.api={fullscreenEnabled:!0,fullscreenElement:function(a){return a=a?a:d,a[i[1]]},requestFullscreen:function(a){return a[i[2]]()},exitFullscreen:function(a){return a=a?a:d,a[i[3]]()}},a.event={fullscreenchange:i[4],fullscreenerror:i[5]}}else a.api={fullscreenEnabled:!1,fullscreenElement:function(){return null},requestFullscreen:function(){},exitFullscreen:function(){}},a.event={}}},a.jPlayer.nativeFeatures.init(),a.jPlayer.focus=null,a.jPlayer.keyIgnoreElementNames="A INPUT TEXTAREA SELECT BUTTON";var g=function(b){var c,d=a.jPlayer.focus;d&&(a.each(a.jPlayer.keyIgnoreElementNames.split(/\s+/g),function(a,d){return b.target.nodeName.toUpperCase()===d.toUpperCase()?(c=!0,!1):void 0}),c||a.each(d.options.keyBindings,function(c,e){return e&&a.isFunction(e.fn)&&("number"==typeof e.key&&b.which===e.key||"string"==typeof e.key&&b.key===e.key)?(b.preventDefault(),e.fn(d),!1):void 0}))};a.jPlayer.keys=function(b){var c="keydown.jPlayer";a(document.documentElement).unbind(c),b&&a(document.documentElement).bind(c,g)},a.jPlayer.keys(!0),a.jPlayer.prototype={count:0,version:{script:"2.9.2",needFlash:"2.9.0",flash:"unknown"},options:{swfPath:"js",solution:"html, flash",supplied:"mp3",auroraFormats:"wav",preload:"metadata",volume:.8,muted:!1,remainingDuration:!1,toggleDuration:!1,captureDuration:!0,playbackRate:1,defaultPlaybackRate:1,minPlaybackRate:.5,maxPlaybackRate:4,wmode:"opaque",backgroundColor:"#000000",cssSelectorAncestor:"#jp_container_1",cssSelector:{videoPlay:".jp-video-play",play:".jp-play",pause:".jp-pause",stop:".jp-stop",seekBar:".jp-seek-bar",playBar:".jp-play-bar",mute:".jp-mute",unmute:".jp-unmute",volumeBar:".jp-volume-bar",volumeBarValue:".jp-volume-bar-value",volumeMax:".jp-volume-max",playbackRateBar:".jp-playback-rate-bar",playbackRateBarValue:".jp-playback-rate-bar-value",currentTime:".jp-current-time",duration:".jp-duration",title:".jp-title",fullScreen:".jp-full-screen",restoreScreen:".jp-restore-screen",repeat:".jp-repeat",repeatOff:".jp-repeat-off",gui:".jp-gui",noSolution:".jp-no-solution"},stateClass:{playing:"jp-state-playing",seeking:"jp-state-seeking",muted:"jp-state-muted",looped:"jp-state-looped",fullScreen:"jp-state-full-screen",noVolume:"jp-state-no-volume"},useStateClassSkin:!1,autoBlur:!0,smoothPlayBar:!1,fullScreen:!1,fullWindow:!1,autohide:{restored:!1,full:!0,fadeIn:200,fadeOut:600,hold:1e3},loop:!1,repeat:function(b){b.jPlayer.options.loop?a(this).unbind(".jPlayerRepeat").bind(a.jPlayer.event.ended+".jPlayer.jPlayerRepeat",function(){a(this).jPlayer("play")}):a(this).unbind(".jPlayerRepeat")},nativeVideoControls:{},noFullWindow:{msie:/msie [0-6]\./,ipad:/ipad.*?os [0-4]\./,iphone:/iphone/,ipod:/ipod/,android_pad:/android [0-3]\.(?!.*?mobile)/,android_phone:/(?=.*android)(?!.*chrome)(?=.*mobile)/,blackberry:/blackberry/,windows_ce:/windows ce/,iemobile:/iemobile/,webos:/webos/},noVolume:{ipad:/ipad/,iphone:/iphone/,ipod:/ipod/,android_pad:/android(?!.*?mobile)/,android_phone:/android.*?mobile/,blackberry:/blackberry/,windows_ce:/windows ce/,iemobile:/iemobile/,webos:/webos/,playbook:/playbook/},timeFormat:{},keyEnabled:!1,audioFullScreen:!1,keyBindings:{play:{key:80,fn:function(a){a.status.paused?a.play():a.pause()}},fullScreen:{key:70,fn:function(a){(a.status.video||a.options.audioFullScreen)&&a._setOption("fullScreen",!a.options.fullScreen)}},muted:{key:77,fn:function(a){a._muted(!a.options.muted)}},volumeUp:{key:190,fn:function(a){a.volume(a.options.volume+.1)}},volumeDown:{key:188,fn:function(a){a.volume(a.options.volume-.1)}},loop:{key:76,fn:function(a){a._loop(!a.options.loop)}}},verticalVolume:!1,verticalPlaybackRate:!1,globalVolume:!1,idPrefix:"jp",noConflict:"jQuery",emulateHtml:!1,consoleAlerts:!0,errorAlerts:!1,warningAlerts:!1},optionsAudio:{size:{width:"0px",height:"0px",cssClass:""},sizeFull:{width:"0px",height:"0px",cssClass:""}},optionsVideo:{size:{width:"480px",height:"270px",cssClass:"jp-video-270p"},sizeFull:{width:"100%",height:"100%",cssClass:"jp-video-full"}},instances:{},status:{src:"",media:{},paused:!0,format:{},formatType:"",waitForPlay:!0,waitForLoad:!0,srcSet:!1,video:!1,seekPercent:0,currentPercentRelative:0,currentPercentAbsolute:0,currentTime:0,duration:0,remaining:0,videoWidth:0,videoHeight:0,readyState:0,networkState:0,playbackRate:1,ended:0},internal:{ready:!1},solution:{html:!0,aurora:!0,flash:!0},format:{mp3:{codec:"audio/mpeg",flashCanPlay:!0,media:"audio"},m4a:{codec:'audio/mp4; codecs="mp4a.40.2"',flashCanPlay:!0,media:"audio"},m3u8a:{codec:'application/vnd.apple.mpegurl; codecs="mp4a.40.2"',flashCanPlay:!1,media:"audio"},m3ua:{codec:"audio/mpegurl",flashCanPlay:!1,media:"audio"},oga:{codec:'audio/ogg; codecs="vorbis, opus"',flashCanPlay:!1,media:"audio"},flac:{codec:"audio/x-flac",flashCanPlay:!1,media:"audio"},wav:{codec:'audio/wav; codecs="1"',flashCanPlay:!1,media:"audio"},webma:{codec:'audio/webm; codecs="vorbis"',flashCanPlay:!1,media:"audio"},fla:{codec:"audio/x-flv",flashCanPlay:!0,media:"audio"},rtmpa:{codec:'audio/rtmp; codecs="rtmp"',flashCanPlay:!0,media:"audio"},m4v:{codec:'video/mp4; codecs="avc1.42E01E, mp4a.40.2"',flashCanPlay:!0,media:"video"},m3u8v:{codec:'application/vnd.apple.mpegurl; codecs="avc1.42E01E, mp4a.40.2"',flashCanPlay:!1,media:"video"},m3uv:{codec:"audio/mpegurl",flashCanPlay:!1,media:"video"},ogv:{codec:'video/ogg; codecs="theora, vorbis"',flashCanPlay:!1,media:"video"},webmv:{codec:'video/webm; codecs="vorbis, vp8"',flashCanPlay:!1,media:"video"},flv:{codec:"video/x-flv",flashCanPlay:!0,media:"video"},rtmpv:{codec:'video/rtmp; codecs="rtmp"',flashCanPlay:!0,media:"video"}},_init:function(){var c=this;if(this.element.empty(),this.status=a.extend({},this.status),this.internal=a.extend({},this.internal),this.options.timeFormat=a.extend({},a.jPlayer.timeFormat,this.options.timeFormat),this.internal.cmdsIgnored=a.jPlayer.platform.ipad||a.jPlayer.platform.iphone||a.jPlayer.platform.ipod,this.internal.domNode=this.element.get(0),this.options.keyEnabled&&!a.jPlayer.focus&&(a.jPlayer.focus=this),this.androidFix={setMedia:!1,play:!1,pause:!1,time:0/0},a.jPlayer.platform.android&&(this.options.preload="auto"!==this.options.preload?"metadata":"auto"),this.formats=[],this.solutions=[],this.require={},this.htmlElement={},this.html={},this.html.audio={},this.html.video={},this.aurora={},this.aurora.formats=[],this.aurora.properties=[],this.flash={},this.css={},this.css.cs={},this.css.jq={},this.ancestorJq=[],this.options.volume=this._limitValue(this.options.volume,0,1),a.each(this.options.supplied.toLowerCase().split(","),function(b,d){var e=d.replace(/^\s+|\s+$/g,"");if(c.format[e]){var f=!1;a.each(c.formats,function(a,b){return e===b?(f=!0,!1):void 0}),f||c.formats.push(e)}}),a.each(this.options.solution.toLowerCase().split(","),function(b,d){var e=d.replace(/^\s+|\s+$/g,"");if(c.solution[e]){var f=!1;a.each(c.solutions,function(a,b){return e===b?(f=!0,!1):void 0}),f||c.solutions.push(e)}}),a.each(this.options.auroraFormats.toLowerCase().split(","),function(b,d){var e=d.replace(/^\s+|\s+$/g,"");if(c.format[e]){var f=!1;a.each(c.aurora.formats,function(a,b){return e===b?(f=!0,!1):void 0}),f||c.aurora.formats.push(e)}}),this.internal.instance="jp_"+this.count,this.instances[this.internal.instance]=this.element,this.element.attr("id")||this.element.attr("id",this.options.idPrefix+"_jplayer_"+this.count),this.internal.self=a.extend({},{id:this.element.attr("id"),jq:this.element}),this.internal.audio=a.extend({},{id:this.options.idPrefix+"_audio_"+this.count,jq:b}),this.internal.video=a.extend({},{id:this.options.idPrefix+"_video_"+this.count,jq:b}),this.internal.flash=a.extend({},{id:this.options.idPrefix+"_flash_"+this.count,jq:b,swf:this.options.swfPath+(".swf"!==this.options.swfPath.toLowerCase().slice(-4)?(this.options.swfPath&&"/"!==this.options.swfPath.slice(-1)?"/":"")+"jquery.jplayer.swf":"")}),this.internal.poster=a.extend({},{id:this.options.idPrefix+"_poster_"+this.count,jq:b}),a.each(a.jPlayer.event,function(a,d){c.options[a]!==b&&(c.element.bind(d+".jPlayer",c.options[a]),c.options[a]=b)}),this.require.audio=!1,this.require.video=!1,a.each(this.formats,function(a,b){c.require[c.format[b].media]=!0}),this.options=this.require.video?a.extend(!0,{},this.optionsVideo,this.options):a.extend(!0,{},this.optionsAudio,this.options),this._setSize(),this.status.nativeVideoControls=this._uaBlocklist(this.options.nativeVideoControls),this.status.noFullWindow=this._uaBlocklist(this.options.noFullWindow),this.status.noVolume=this._uaBlocklist(this.options.noVolume),a.jPlayer.nativeFeatures.fullscreen.api.fullscreenEnabled&&this._fullscreenAddEventListeners(),this._restrictNativeVideoControls(),this.htmlElement.poster=document.createElement("img"),this.htmlElement.poster.id=this.internal.poster.id,this.htmlElement.poster.onload=function(){(!c.status.video||c.status.waitForPlay)&&c.internal.poster.jq.show()},this.element.append(this.htmlElement.poster),this.internal.poster.jq=a("#"+this.internal.poster.id),this.internal.poster.jq.css({width:this.status.width,height:this.status.height}),this.internal.poster.jq.hide(),this.internal.poster.jq.bind("click.jPlayer",function(){c._trigger(a.jPlayer.event.click)}),this.html.audio.available=!1,this.require.audio&&(this.htmlElement.audio=document.createElement("audio"),this.htmlElement.audio.id=this.internal.audio.id,this.html.audio.available=!!this.htmlElement.audio.canPlayType&&this._testCanPlayType(this.htmlElement.audio)),this.html.video.available=!1,this.require.video&&(this.htmlElement.video=document.createElement("video"),this.htmlElement.video.id=this.internal.video.id,this.html.video.available=!!this.htmlElement.video.canPlayType&&this._testCanPlayType(this.htmlElement.video)),this.flash.available=this._checkForFlash(10.1),this.html.canPlay={},this.aurora.canPlay={},this.flash.canPlay={},a.each(this.formats,function(b,d){c.html.canPlay[d]=c.html[c.format[d].media].available&&""!==c.htmlElement[c.format[d].media].canPlayType(c.format[d].codec),c.aurora.canPlay[d]=a.inArray(d,c.aurora.formats)>-1,c.flash.canPlay[d]=c.format[d].flashCanPlay&&c.flash.available}),this.html.desired=!1,this.aurora.desired=!1,this.flash.desired=!1,a.each(this.solutions,function(b,d){if(0===b)c[d].desired=!0;else{var e=!1,f=!1;a.each(c.formats,function(a,b){c[c.solutions[0]].canPlay[b]&&("video"===c.format[b].media?f=!0:e=!0)}),c[d].desired=c.require.audio&&!e||c.require.video&&!f}}),this.html.support={},this.aurora.support={},this.flash.support={},a.each(this.formats,function(a,b){c.html.support[b]=c.html.canPlay[b]&&c.html.desired,c.aurora.support[b]=c.aurora.canPlay[b]&&c.aurora.desired,c.flash.support[b]=c.flash.canPlay[b]&&c.flash.desired}),this.html.used=!1,this.aurora.used=!1,this.flash.used=!1,a.each(this.solutions,function(b,d){a.each(c.formats,function(a,b){return c[d].support[b]?(c[d].used=!0,!1):void 0})}),this._resetActive(),this._resetGate(),this._cssSelectorAncestor(this.options.cssSelectorAncestor),this.html.used||this.aurora.used||this.flash.used?this.css.jq.noSolution.length&&this.css.jq.noSolution.hide():(this._error({type:a.jPlayer.error.NO_SOLUTION,context:"{solution:'"+this.options.solution+"', supplied:'"+this.options.supplied+"'}",message:a.jPlayer.errorMsg.NO_SOLUTION,hint:a.jPlayer.errorHint.NO_SOLUTION}),this.css.jq.noSolution.length&&this.css.jq.noSolution.show()),this.flash.used){var d,e="jQuery="+encodeURI(this.options.noConflict)+"&id="+encodeURI(this.internal.self.id)+"&vol="+this.options.volume+"&muted="+this.options.muted;if(a.jPlayer.browser.msie&&(Number(a.jPlayer.browser.version)<9||a.jPlayer.browser.documentMode<9)){var f='',g=['','','','',''];d=document.createElement(f);for(var h=0;h
+ setPlaylist( [Audio Mix] | [Video Mix] | [Media Mix] )
+
+ Miaow audio: add( {Bubble} | | {Tempered Song} | {Lentement} )
+ The Stark Palace audio: add( {Cro Magnon Man} | {Your Face} | {Cyber Sonnet} )
+ Various video: add( {Big Buck Bunny} | {Incredibles} | {Finding Nemo} )
+
+ remove( -2 | -1 | 0 | 1 | 2 )
+ | shuffle( false | true )
+
+ select( -2 | -1 | 0 | 1 | 2 )
+ | next() | previous()
+
+ play( -2 | -1 | 0 | 1 | 2 )
+ | pause()
+
+ option( "autoPlay", false | true ) Default: false
+ option( "enableRemoveControls", false | true ) Default: false
+ option( "displayTime", 0 | 'fast' | 'slow' | 2000 ) Default: 'slow'
+ option( "addTime", 0 | 'fast' | 'slow' | 2000 ) Default: 'fast'
+ option( "removeTime", 0 | 'fast' | 'slow' | 2000 ) Default: 'fast'
+ option( "shuffleTime", 0 | 'fast' | 'slow' | 2000 ) Default: 'slow'
+
+
+ Equivalent Effect: add(Your Face, true) == add(Your Face) then play(-1)
+
+ Avoid code like: remove(2) then remove(3)
+ Because the second command will only work if the remove animation time, removeTime, is zero.
+ Even then, it will look like it removes the 3rd and 5th items from the original playlist before both commands executed.
+ This is because the remove(2) removes the 3rd item and then remove(3) removes the 4th item, which was the 5th item before the 3rd item was removed.
+ To remove the 3rd and 4th items, you'd use remove(2) and then remove(2) again.
+ The remove() method returns a true when successful, a false when ignored, which allows you to know whether it worked or not.
+
Screen size is set through CSS to: 270p {width:"480px"; height:"270px";}
+ + FLV Test from youtube + +
+ Use a constructor option below to instance jPlayer.
+
Otherwise the commands and artwork interface do nothing.
+ jPlayer( {solution:"html, flash"} | {solution:"flash, html"} | currentTest )
+
Use jPlayer("destroy") to destroy the jPlayer instance before changing the instance above. Otherwise they try and change the options, which do not support such an action.
+
+
jPlayer("option", "cssSelector", "play", "broken" | "play", "" | "play", "fixed")
+ jPlayer("option", "cssSelectorAncestor", "broken" | "" | "fixed")
+ size option object to that for (270p | 360p) | Set fullScreen option to (false | true)
+ sizeFull option object to that for (270p | 360p | full) | restoreScreen | fullScreen
+ autohide option object to that for (neither | full | restored | both) | (slow | fast | instant)
+ loop option to (true | false)
+
+ nativeVideoControls (all | none)
+ | noFullWindow (all | none)
+ | noVolume (all | none)
+
+ keyEnabled (true | false)
+ keyBindings (default | alert | disable play/space)
+
+ The player below is set up to play a single audio track. It is included here to test any interactions between the players. In particular the pauseOthers command.
+ + ++ : + nothing + at + of , which is + +
+ +
+ setPlaylist( [Audio Mix] | [Video Mix] | [Media Mix] )
+
+ Miaow audio: add( {Bubble} | | {Tempered Song} | {Lentement} )
+ The Stark Palace audio: add( {Cro Magnon Man} | {Your Face} | {Cyber Sonnet} )
+ Various video: add( {Big Buck Bunny} | {Incredibles} | {Finding Nemo} )
+
+ remove( -2 | -1 | 0 | 1 | 2 )
+ | shuffle( false | true )
+
+ select( -2 | -1 | 0 | 1 | 2 )
+ | next() | previous()
+
+ play( -2 | -1 | 0 | 1 | 2 )
+ | pause()
+
+ option( "autoPlay", false | true ) Default: false
+ option( "enableRemoveControls", false | true ) Default: false
+ option( "displayTime", 0 | 'fast' | 'slow' | 2000 ) Default: 'slow'
+ option( "addTime", 0 | 'fast' | 'slow' | 2000 ) Default: 'fast'
+ option( "removeTime", 0 | 'fast' | 'slow' | 2000 ) Default: 'fast'
+ option( "shuffleTime", 0 | 'fast' | 'slow' | 2000 ) Default: 'slow'
+
+
+ Equivalent Effect: add(Your Face, true) == add(Your Face) then play(-1)
+
+ Avoid code like: remove(2) then remove(3)
+ Because the second command will only work if the remove animation time, removeTime, is zero.
+ Even then, it will look like it removes the 3rd and 5th items from the original playlist before both commands executed.
+ This is because the remove(2) removes the 3rd item and then remove(3) removes the 4th item, which was the 5th item before the 3rd item was removed.
+ To remove the 3rd and 4th items, you'd use remove(2) and then remove(2) again.
+ The remove() method returns a true when successful, a false when ignored, which allows you to know whether it worked or not.
+
Screen size is set through CSS to: 270p {width:"480px"; height:"270px";}
+ + FLV Test from youtube + +
+ Use a constructor option below to instance jPlayer.
+
Otherwise the commands and artwork interface do nothing.
+ jPlayer( {solution:"html, flash"} | {solution:"flash, html"} | currentTest )
+
Use jPlayer("destroy") to destroy the jPlayer instance before changing the instance above. Otherwise they try and change the options, which do not support such an action.
+
+
jPlayer("option", "cssSelector", "play", "broken" | "play", "" | "play", "fixed")
+ jPlayer("option", "cssSelectorAncestor", "broken" | "" | "fixed")
+ size option object to that for (270p | 360p) | Set fullScreen option to (false | true)
+ sizeFull option object to that for (270p | 360p | full) | restoreScreen | fullScreen
+ autohide option object to that for (neither | full | restored | both) | (slow | fast | instant)
+ loop option to (true | false)
+
+ nativeVideoControls (all | none)
+ | noFullWindow (all | none)
+ | noVolume (all | none)
+
+ keyEnabled (true | false)
+ keyBindings (default | alert | disable play/space)
+
+ The player below is set up to play a single audio track. It is included here to test any interactions between the players. In particular the pauseOthers command.
+ + +>>0){G=p-a|0;c[1260>>2]=G;H=c[1272>>2]|0;c[1272>>2]=H+a;c[H+(a+4)>>2]=G|1;c[H+4>>2]=a|3;H=H+8|0;i=b;return H|0}do{if((c[430]|0)==0){p=ia(30)|0;if((p+ -1&p|0)==0){c[1728>>2]=p;c[1724>>2]=p;c[1732>>2]=-1;c[1736>>2]=-1;c[1740>>2]=0;c[1692>>2]=0;c[430]=(ma(0)|0)&-16^1431655768;break}else{ja()}}}while(0);w=a+48|0;p=c[1728>>2]|0;x=a+47|0;z=p+x|0;p=0-p|0;v=z&p;if(!(v>>>0>a>>>0)){H=0;i=b;return H|0}A=c[1688>>2]|0;if((A|0)!=0?(G=c[1680>>2]|0,H=G+v|0,H>>>0<=G>>>0|H>>>0>A>>>0):0){H=0;i=b;return H|0}d:do{if((c[1692>>2]&4|0)==0){B=c[1272>>2]|0;e:do{if((B|0)!=0){A=1696|0;while(1){C=c[A>>2]|0;if(!(C>>>0>B>>>0)?(y=A+4|0,(C+(c[y>>2]|0)|0)>>>0>B>>>0):0){break}A=c[A+8>>2]|0;if((A|0)==0){o=182;break e}}if((A|0)!=0){B=z-(c[1260>>2]|0)&p;if(B>>>0<2147483647){p=la(B|0)|0;A=(p|0)==((c[A>>2]|0)+(c[y>>2]|0)|0);y=p;z=B;p=A?p:-1;A=A?B:0;o=191}else{A=0}}else{o=182}}else{o=182}}while(0);do{if((o|0)==182){p=la(0)|0;if((p|0)!=(-1|0)){z=p;A=c[1724>>2]|0;y=A+ -1|0;if((y&z|0)==0){A=v}else{A=v-z+(y+z&0-A)|0}y=c[1680>>2]|0;z=y+A|0;if(A>>>0>a>>>0&A>>>0<2147483647){H=c[1688>>2]|0;if((H|0)!=0?z>>>0<=y>>>0|z>>>0>H>>>0:0){A=0;break}y=la(A|0)|0;o=(y|0)==(p|0);z=A;p=o?p:-1;A=o?A:0;o=191}else{A=0}}else{A=0}}}while(0);f:do{if((o|0)==191){o=0-z|0;if((p|0)!=(-1|0)){q=A;o=202;break d}do{if((y|0)!=(-1|0)&z>>>0<2147483647&z>>>0 >>0:0){c[u>>2]=s+q;d=(c[1260>>2]|0)+q|0;e=r+8|0;if((e&7|0)==0){e=0}else{e=0-e&7}H=d-e|0;c[1272>>2]=r+e;c[1260>>2]=H;c[r+(e+4)>>2]=H|1;c[r+(d+4)>>2]=40;c[1276>>2]=c[1736>>2];break}if(p>>>0<(c[1264>>2]|0)>>>0){c[1264>>2]=p}t=p+q|0;s=1696|0;while(1){if((c[s>>2]|0)==(t|0)){o=224;break}u=c[s+8>>2]|0;if((u|0)==0){break}else{s=u}}if((o|0)==224?(c[s+12>>2]&8|0)==0:0){c[s>>2]=p;h=s+4|0;c[h>>2]=(c[h>>2]|0)+q;h=p+8|0;if((h&7|0)==0){h=0}else{h=0-h&7}j=p+(q+8)|0;if((j&7|0)==0){n=0}else{n=0-j&7}o=p+(n+q)|0;j=h+a|0;k=p+j|0;m=o-(p+h)-a|0;c[p+(h+4)>>2]=a|3;h:do{if((o|0)!=(c[1272>>2]|0)){if((o|0)==(c[1268>>2]|0)){H=(c[1256>>2]|0)+m|0;c[1256>>2]=H;c[1268>>2]=k;c[p+(j+4)>>2]=H|1;c[p+(H+j)>>2]=H;break}r=q+4|0;t=c[p+(r+n)>>2]|0;if((t&3|0)==1){a=t&-8;s=t>>>3;i:do{if(!(t>>>0<256)){l=c[p+((n|24)+q)>>2]|0;u=c[p+(q+12+n)>>2]|0;do{if((u|0)==(o|0)){u=n|16;t=p+(r+u)|0;s=c[t>>2]|0;if((s|0)==0){t=p+(u+q)|0;s=c[t>>2]|0;if((s|0)==0){g=0;break}}while(1){u=s+20|0;v=c[u>>2]|0;if((v|0)!=0){s=v;t=u;continue}u=s+16|0;v=c[u>>2]|0;if((v|0)==0){break}else{s=v;t=u}}if(t>>>0<(c[1264>>2]|0)>>>0){ja()}else{c[t>>2]=0;g=s;break}}else{t=c[p+((n|8)+q)>>2]|0;if(t>>>0<(c[1264>>2]|0)>>>0){ja()}v=t+12|0;if((c[v>>2]|0)!=(o|0)){ja()}s=u+8|0;if((c[s>>2]|0)==(o|0)){c[v>>2]=u;c[s>>2]=t;g=u;break}else{ja()}}}while(0);if((l|0)==0){break}t=c[p+(q+28+n)>>2]|0;s=1552+(t<<2)|0;do{if((o|0)!=(c[s>>2]|0)){if(l>>>0<(c[1264>>2]|0)>>>0){ja()}s=l+16|0;if((c[s>>2]|0)==(o|0)){c[s>>2]=g}else{c[l+20>>2]=g}if((g|0)==0){break i}}else{c[s>>2]=g;if((g|0)!=0){break}c[1252>>2]=c[1252>>2]&~(1< >>(o>>>0))&n|h<>>0):0)?(r=s-t|0,q=r>>>0>(a+40|0)>>>0,q):0){p=t;q=q?r:A;o=202}if((o|0)==202){r=(c[1680>>2]|0)+q|0;c[1680>>2]=r;if(r>>>0>(c[1684>>2]|0)>>>0){c[1684>>2]=r}r=c[1272>>2]|0;g:do{if((r|0)!=0){v=1696|0;while(1){t=c[v>>2]|0;u=v+4|0;s=c[u>>2]|0;if((p|0)==(t+s|0)){o=214;break}w=c[v+8>>2]|0;if((w|0)==0){break}else{v=w}}if(((o|0)==214?(c[v+12>>2]&8|0)==0:0)?r>>>0>=t>>>0&r>>>0>>0<(c[1264>>2]|0)>>>0){ja()}s=g+8|0;if((c[s>>2]|0)==(o|0)){l=s;break}ja()}}while(0);c[r+12>>2]=g;c[l>>2]=r}}while(0);o=p+((a|n)+q)|0;m=a+m|0}g=o+4|0;c[g>>2]=c[g>>2]&-2;c[p+(j+4)>>2]=m|1;c[p+(m+j)>>2]=m;g=m>>>3;if(m>>>0<256){m=g<<1;d=1288+(m<<2)|0;l=c[312]|0;g=1<>>0){ja()}if((l|0)==(c[1268>>2]|0)){e=a+(b+4)|0;if((c[e>>2]&3|0)!=3){e=l;n=m;break}c[1256>>2]=m;c[e>>2]=c[e>>2]&-2;c[a+(4-p)>>2]=m|1;c[h>>2]=m;i=d;return}s=p>>>3;if(p>>>0<256){e=c[a+(8-p)>>2]|0;n=c[a+(12-p)>>2]|0;o=1288+(s<<1<<2)|0;if((e|0)!=(o|0)){if(e>>>0
>>0){ja()}if((c[e+12>>2]|0)!=(l|0)){ja()}}if((n|0)==(e|0)){c[312]=c[312]&~(1<
>>0>>0){ja()}o=n+8|0;if((c[o>>2]|0)==(l|0)){r=o}else{ja()}}else{r=n+8|0}c[e+12>>2]=n;c[r>>2]=e;e=l;n=m;break}r=c[a+(24-p)>>2]|0;t=c[a+(12-p)>>2]|0;do{if((t|0)==(l|0)){u=16-p|0;t=a+(u+4)|0;s=c[t>>2]|0;if((s|0)==0){t=a+u|0;s=c[t>>2]|0;if((s|0)==0){o=0;break}}while(1){u=s+20|0;v=c[u>>2]|0;if((v|0)!=0){s=v;t=u;continue}v=s+16|0;u=c[v>>2]|0;if((u|0)==0){break}else{s=u;t=v}}if(t>>>0
>>0){ja()}else{c[t>>2]=0;o=s;break}}else{s=c[a+(8-p)>>2]|0;if(s>>>0
>>0){ja()}u=s+12|0;if((c[u>>2]|0)!=(l|0)){ja()}q=t+8|0;if((c[q>>2]|0)==(l|0)){c[u>>2]=t;c[q>>2]=s;o=t;break}else{ja()}}}while(0);if((r|0)!=0){q=c[a+(28-p)>>2]|0;s=1552+(q<<2)|0;if((l|0)==(c[s>>2]|0)){c[s>>2]=o;if((o|0)==0){c[1252>>2]=c[1252>>2]&~(1<
>>0<(c[1264>>2]|0)>>>0){ja()}q=r+16|0;if((c[q>>2]|0)==(l|0)){c[q>>2]=o}else{c[r+20>>2]=o}if((o|0)==0){e=l;n=m;break}}if(o>>>0<(c[1264>>2]|0)>>>0){ja()}c[o+24>>2]=r;p=16-p|0;q=c[a+p>>2]|0;do{if((q|0)!=0){if(q>>>0<(c[1264>>2]|0)>>>0){ja()}else{c[o+16>>2]=q;c[q+24>>2]=o;break}}}while(0);p=c[a+(p+4)>>2]|0;if((p|0)!=0){if(p>>>0<(c[1264>>2]|0)>>>0){ja()}else{c[o+20>>2]=p;c[p+24>>2]=o;e=l;n=m;break}}else{e=l;n=m}}else{e=l;n=m}}else{e=a;n=b}}while(0);l=c[1264>>2]|0;if(h>>>0